28 lines
3.6 KiB
JavaScript
28 lines
3.6 KiB
JavaScript
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import assert from 'node:assert/strict'
|
|
import crypto from 'node:crypto'
|
|
import {spawnSync} from 'node:child_process'
|
|
const root=await fs.mkdtemp(path.resolve('.tmp/rea-cpp-binary-fixture-')),target=path.join(root,'target'),checks=[]
|
|
await fs.mkdir(path.join(target,'avatars'),{recursive:true})
|
|
const png=Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a6hoAAAAASUVORK5CYII=','base64'),hash=b=>crypto.createHash('sha256').update(b).digest('hex')
|
|
await fs.writeFile(path.join(target,'avatars/expert.png'),png)
|
|
function cli(command,args){return spawnSync(process.execPath,['.tmp/rea-css-runtime/scripts/rea.mjs',command,...args],{encoding:'utf8',maxBuffer:32*1024*1024})}
|
|
function inspect(){const r=cli('inspect-selected-directory-text-root',[target]);assert.equal(r.status,0,r.stderr);return JSON.parse(r.stdout).normalized_result}
|
|
function run(pin,selection){return cli('export-selected-directory-binary',[target,pin,JSON.stringify(selection)])}
|
|
function reject(label,r,pattern){assert.notEqual(r.status,0,label);assert.match(r.stderr,pattern,label);checks.push({label,pass:true})}
|
|
let inventory=inspect(),pin=inventory.root_sha256,selected=[{path:'avatars/expert.png',sha256:hash(png)}],r=run(pin,selected)
|
|
assert.equal(r.status,0,r.stderr);const evidence=JSON.parse(r.stdout),f=evidence.normalized_result.files[0]
|
|
assert.equal(evidence.operation,'export_selected_directory_binary');assert.equal(f.media_type,'image/png');assert.equal(f.encoding,'base64');assert.equal(f.sha256,hash(png));assert.deepEqual(Buffer.from(f.data_base64,'base64'),png);assert.equal(f.size_bytes,png.length);assert.equal('text' in f,false);checks.push({label:'actual own PNG public CLI export is exact bytes/SHA/type and base64 Evidence',pass:true})
|
|
reject('wrong root SHA',run('0'.repeat(64),selected),/root SHA mismatch/)
|
|
reject('wrong selected PNG SHA',run(pin,[{...selected[0],sha256:'0'.repeat(64)}]),/selected PNG SHA mismatch/)
|
|
reject('path traversal',run(pin,[{path:'../expert.png',sha256:hash(png)}]),/canonical/)
|
|
reject('unsupported binary extension',run(pin,[{path:'avatars/expert.jpg',sha256:hash(png)}]),/canonical/)
|
|
reject('missing selected member',run(pin,[{path:'avatars/missing.png',sha256:hash(png)}]),/absent/)
|
|
reject('duplicate paths',run(pin,[...selected,...selected]),/Duplicate/)
|
|
reject('required explicit image SHA',run(pin,[{path:'avatars/expert.png'}]),/SHA required/)
|
|
await fs.writeFile(path.join(target,'avatars/fake.png'),'not an image');inventory=inspect();reject('PNG signature validated',run(inventory.root_sha256,[{path:'avatars/fake.png',sha256:hash('not an image')}]),/signature/)
|
|
const large=Buffer.alloc(8*1024*1024+1,97);png.subarray(0,8).copy(large);await fs.writeFile(path.join(target,'avatars/large.png'),large);inventory=inspect();reject('binary per-file budget',run(inventory.root_sha256,[{path:'avatars/large.png',sha256:hash(large)}]),/byte budget/)
|
|
await fs.rm(path.join(target,'avatars/large.png'));const exact=large.subarray(0,8*1024*1024);for(const name of['one','two'])await fs.writeFile(path.join(target,'avatars/'+name+'.png'),exact);inventory=inspect();reject('binary aggregate budget',run(inventory.root_sha256,[{path:'avatars/one.png',sha256:hash(exact)},{path:'avatars/two.png',sha256:hash(exact)},...selected]),/byte budget/)
|
|
await fs.writeFile(path.join(root,'report.json'),JSON.stringify({result:'PASS',checks,evidenceId:evidence.evidence_id},null,2));await fs.writeFile(path.join(root,'evidence.json'),JSON.stringify(evidence,null,2));console.log(JSON.stringify({result:'PASS',root,checks:checks.length,evidenceId:evidence.evidence_id}))
|