55 lines
5.8 KiB
JavaScript
55 lines
5.8 KiB
JavaScript
import test from 'node:test';
|
|||
|
|
import assert from 'node:assert/strict';
|
||
|
|
import fs from 'node:fs';
|
||
|
|
import path from 'node:path';
|
||
|
|
import os from 'node:os';
|
||
|
|
import {spawn} from 'node:child_process';
|
||
|
|
import {once} from 'node:events';
|
||
|
|
import {createRequire} from 'node:module';
|
||
|
|
const require=createRequire(import.meta.url);
|
||
|
|
const {acquireUploadLease}=require('../apps/browser/upload-lease.cjs');
|
||
|
|
const options={pythonPath:path.resolve('.runtime/windows/python/python.exe'),helperPath:path.resolve('apps/inspector/upload_lease.py')};
|
||
|
|
test('Windows immutable upload snapshot locks write, rename and hard-link overwrite until explicit release',{skip:process.platform!=='win32'},async()=>{
|
||
|
|
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'safetynet-upload-lease-'));
|
||
|
|
const file=path.join(dir,'safe.bin'),alias=path.join(dir,'alias.bin');
|
||
|
|
fs.writeFileSync(file,Buffer.from('synthetic-upload'));fs.linkSync(file,alias);
|
||
|
|
let lease;
|
||
|
|
try{
|
||
|
|
lease=await acquireUploadLease({...options,paths:[file],maxBytes:4096});
|
||
|
|
assert.equal(lease.files[0].bytes.toString(),'synthetic-upload');assert.equal(lease.isAlive(),true);
|
||
|
|
assert.throws(()=>fs.writeFileSync(file,'changed'));assert.throws(()=>fs.writeFileSync(alias,'changed'));assert.throws(()=>fs.renameSync(file,path.join(dir,'moved.bin')));
|
||
|
|
await lease.release();lease=null;fs.writeFileSync(file,'released');assert.equal(fs.readFileSync(alias,'utf8'),'released');
|
||
|
|
}finally{if(lease)await lease.release();assert.ok(path.resolve(dir).startsWith(path.resolve(os.tmpdir())+path.sep+'safetynet-upload-lease-'));fs.rmSync(dir,{recursive:true,force:true});}
|
||
|
|
});
|
||
|
|
test('broker refuses UNC, device, ADS and noncanonical upload paths before spawn',async()=>{
|
||
|
|
for(const value of ['\\\\server\\share\\a','\\\\?\\C:\\file','C:\\file:stream','C:\\folder\\..\\file'])await assert.rejects(acquireUploadLease({...options,paths:[value]}),{code:'upload-lease-refused'});
|
||
|
|
});
|
||
|
|
test('parent handle copies survive helper death and trusted explicit cleanup releases them',{skip:process.platform!=='win32'},async()=>{
|
||
|
|
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'safetynet-upload-lease-death-')),file=path.join(dir,'safe.bin');fs.writeFileSync(file,'snapshot');
|
||
|
|
const start=()=>spawn(options.pythonPath,['-I','-B',options.helperPath],{windowsHide:true,stdio:['pipe','pipe','pipe'],env:{SystemRoot:process.env.SystemRoot,WINDIR:process.env.WINDIR}});
|
||
|
|
const child=start();let text='';
|
||
|
|
const ready=new Promise((resolve,reject)=>{child.stdout.on('data',c=>{text+=c;if(text.includes('\n'))resolve(JSON.parse(text.split('\n')[0]));});child.on('error',reject);});
|
||
|
|
child.stdin.write(JSON.stringify({version:'safetynet-upload-lease-v1',paths:[file],maxBytes:1024,parentPid:process.pid})+'\n');
|
||
|
|
const receipt=await ready;assert.equal(receipt.ok,true);const exited=once(child,'exit');child.kill();await exited;
|
||
|
|
assert.throws(()=>fs.writeFileSync(file,'changed'));assert.throws(()=>fs.renameSync(file,path.join(dir,'moved.bin')));
|
||
|
|
const cleanup=start();let output='';cleanup.stdout.on('data',c=>output+=c);const cleaned=once(cleanup,'exit');cleanup.stdin.end(JSON.stringify({version:'safetynet-upload-lease-v1',parentPid:process.pid,closeHandles:receipt.handles})+'\n');
|
||
|
|
const [code]=await cleaned;assert.equal(code,0);assert.equal(JSON.parse(output).released,true);fs.writeFileSync(file,'released');
|
||
|
|
assert.ok(path.resolve(dir).startsWith(path.resolve(os.tmpdir())+path.sep+'safetynet-upload-lease-death-'));fs.rmSync(dir,{recursive:true,force:true});
|
||
|
|
});
|
||
|
|
test('existing writable memory mapping is refused even after creator file descriptor closes',{skip:process.platform!=='win32'},async()=>{
|
||
|
|
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'safetynet-upload-lease-mapping-')),file=path.join(dir,'safe.bin');fs.writeFileSync(file,'snapshot');
|
||
|
|
const code="import sys,json,mmap\np=json.loads(sys.stdin.readline())\nf=open(p,'r+b')\nm=mmap.mmap(f.fileno(),0,access=mmap.ACCESS_WRITE)\nf.close()\nprint('ready',flush=True)\nsys.stdin.readline()\nm.close()";
|
||
|
|
const mapper=spawn(options.pythonPath,['-I','-B','-c',code],{windowsHide:true,stdio:['pipe','pipe','pipe']});
|
||
|
|
const ready=once(mapper.stdout,'data');mapper.stdin.write(JSON.stringify(file)+'\n');await ready;
|
||
|
|
try{await assert.rejects(acquireUploadLease({...options,paths:[file],maxBytes:1024}),{code:'upload-lease-refused'});}finally{const exited=once(mapper,'exit');mapper.stdin.end('release\n');await exited;assert.ok(path.resolve(dir).startsWith(path.resolve(os.tmpdir())+path.sep+'safetynet-upload-lease-mapping-'));fs.rmSync(dir,{recursive:true,force:true});}
|
||
|
|
const lease=await acquireUploadLease({...options,paths:[path.resolve('README.md')],maxBytes:1048576});await lease.release();
|
||
|
|
});
|
||
|
|
test('junction ancestor, oversize and preexisting writable descriptor are refused without disabling later valid leases',{skip:process.platform!=='win32'},async()=>{
|
||
|
|
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'safetynet-upload-lease-negative-')),target=path.join(dir,'target'),junction=path.join(dir,'junction');fs.mkdirSync(target);const file=path.join(target,'safe.bin');fs.writeFileSync(file,'snapshot');fs.symlinkSync(target,junction,'junction');
|
||
|
|
try{
|
||
|
|
await assert.rejects(acquireUploadLease({...options,paths:[path.join(junction,'safe.bin')],maxBytes:1024}),{code:'upload-lease-refused'});
|
||
|
|
await assert.rejects(acquireUploadLease({...options,paths:[file],maxBytes:1}),{code:'upload-lease-refused'});
|
||
|
|
const fd=fs.openSync(file,'r+');try{await assert.rejects(acquireUploadLease({...options,paths:[file],maxBytes:1024}),{code:'upload-lease-refused'});}finally{fs.closeSync(fd);}
|
||
|
|
const lease=await acquireUploadLease({...options,paths:[file],maxBytes:1024});await lease.release();await assert.rejects(lease.release(),{code:'upload-lease-refused'});
|
||
|
|
}finally{fs.unlinkSync(junction);assert.ok(path.resolve(dir).startsWith(path.resolve(os.tmpdir())+path.sep+'safetynet-upload-lease-negative-'));fs.rmSync(dir,{recursive:true,force:true});}
|
||
|
|
});
|