row 188 (#1686, P19 409, 410): stack-release T54, the proof file checks on a carried pair and a proof file inside the repository (Darkwing; Filbert approve 29433; Sage gate passed)
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
import test, {after} from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {execFileSync,spawn,spawnSync} from 'node:child_process';
|
||||
import {mkdtempSync,mkdirSync,writeFileSync,readFileSync,appendFileSync,cpSync,rmSync,chmodSync,existsSync,statSync,lstatSync,symlinkSync,readdirSync,realpathSync} from 'node:fs';
|
||||
import {mkdtempSync,mkdirSync,writeFileSync,readFileSync,appendFileSync,cpSync,rmSync,chmodSync,existsSync,statSync,lstatSync,symlinkSync,linkSync,readdirSync,realpathSync} from 'node:fs';
|
||||
import {createServer} from 'node:http';
|
||||
import {createHash} from 'node:crypto';
|
||||
import {DatabaseSync} from 'node:sqlite';
|
||||
@@ -1449,9 +1449,9 @@ print(n,flush=True)`;
|
||||
// A subtest that fails before noLeak leaves its watches running; they end with the file.
|
||||
const WATCHERS=new Set();
|
||||
after(()=>{for(const ch of WATCHERS)ch.kill('SIGKILL')});
|
||||
// A proof file under e.root, outside the data root, with its read watch running.
|
||||
async function proofFile(e,name='proof.187',mode=0o600){
|
||||
const f=join(e.root,name);writeFileSync(f,SENTINEL+'\n',{mode});chmodSync(f,mode);
|
||||
// A proof file under dir (e.root, outside the data root, unless given), with its read watch running.
|
||||
async function proofFile(e,name='proof.187',mode=0o600,dir=e.root){
|
||||
const f=join(dir,name);writeFileSync(f,SENTINEL+'\n',{mode});chmodSync(f,mode);
|
||||
const ch=spawn('python3',['-I','-c',WATCH,f],{stdio:['pipe','pipe','inherit']});
|
||||
let out='';ch.stdout.on('data',(d)=>{out+=d});
|
||||
await new Promise((res,rej)=>{ch.stdout.on('data',()=>{if(out.startsWith('ready\n'))res()});ch.on('error',rej);ch.on('close',(c)=>rej(new Error(`inotify watch exited ${c}`)))});
|
||||
@@ -1650,3 +1650,58 @@ test('T53 row 187: activate sets, carries and drops the proof pair',async(t)=>{
|
||||
await noLeak(e);
|
||||
});
|
||||
});
|
||||
|
||||
// Row 188 (#1686, P19 409 and 410): the checks on a carried pair, and a proof file inside REPO.
|
||||
// A file gone from its path keeps a hard link under e.root, so the inode and its read watch
|
||||
// outlive the path: inotify sends IN_IGNORED, which the watch would count, when a watched inode goes.
|
||||
const carried=['proof: carried, header x-console-proof'];
|
||||
test('T54 row 188: a carried pair gets the proof file checks, and a proof file inside the repository refuses',async(t)=>{
|
||||
const changes=[
|
||||
['chmod 644',(e,f)=>{chmodSync(f,0o644);return 'has mode 644, not 600 or 400'}],
|
||||
['a symlink to a mode-600 file',async(e,f)=>{const g=await proofFile(e,'swap.188');linkSync(f,join(e.root,'kept.188'));rmSync(f);symlinkSync(g,f);return 'is a symlink'}],
|
||||
['removal',(e,f)=>{linkSync(f,join(e.root,'kept.188'));rmSync(f);return 'cannot be found'}],
|
||||
];
|
||||
for(const [name,change] of changes){
|
||||
await t.test(`test 1: an activate that carries the pair refuses after ${name}, in the plan and the run, before anything stops`,async(t)=>{
|
||||
const e=await mk(t),base=await seedPreview(e),f=await proofFile(e);
|
||||
const r=await prun(e,'activate',B,'acme',...pair(f));assert.equal(r.code,0,r.out+r.err);
|
||||
const why=`--proof-file ${f} ${await change(e,f)}`;
|
||||
const out=await planOnly(e,3,'activate',C1,'acme');
|
||||
assert.deepEqual(proofLines(out),carried);assert.deepEqual(out.filter((l)=>l.startsWith('refuses ')),[`refuses proof-file-invalid: ${why}`]);
|
||||
await refused(e,['activate',C1,'acme'],'proof-file-invalid',why);
|
||||
assert.equal(pointer(e).copy,B);assert.deepEqual(pointer(e).console,[...base,...pair(f)]);
|
||||
await noLeak(e);
|
||||
});
|
||||
}
|
||||
await t.test('test 2: a rollback that carries the pair refuses after chmod 644, in the plan and the run, before anything stops',async(t)=>{
|
||||
const e=await mk(t),base=await seedPreview(e),f=await proofFile(e);
|
||||
const r=await prun(e,'activate',C1,'acme',...pair(f));assert.equal(r.code,0,r.out+r.err);
|
||||
chmodSync(f,0o644);const why=`--proof-file ${f} has mode 644, not 600 or 400`;
|
||||
const out=await planOnly(e,3,'rollback','acme');
|
||||
assert.ok(out.includes(`to: ${B}`),out.join('\n'));
|
||||
assert.deepEqual(proofLines(out),carried);assert.deepEqual(out.filter((l)=>l.startsWith('refuses ')),[`refuses proof-file-invalid: ${why}`]);
|
||||
await refused(e,['rollback','acme'],'proof-file-invalid',why);
|
||||
assert.equal(pointer(e).copy,C1);assert.deepEqual(pointer(e).console,[...base,...pair(f)]);
|
||||
await noLeak(e);
|
||||
});
|
||||
// Tests 3 and 4: a mode-600 file of this uid, so only the realpath check can refuse it.
|
||||
const inRepo=async(t,e,dir)=>{t.after(()=>rmSync(dir,{recursive:true,force:true}));mkdirSync(dir);return proofFile(e,'proof.188',0o600,dir)};
|
||||
await t.test('test 3: a proof file inside the repository refuses, in the plan and the run, before anything stops',async(t)=>{
|
||||
const e=await mk(t);await seedPreview(e);const f=await inRepo(t,e,join(repo,'proof-r188'));
|
||||
const why=`--proof-file ${f} is inside the repository or the copies`;
|
||||
const out=await planOnly(e,3,'activate',C1,'acme',...pair(f));
|
||||
assert.deepEqual(out.filter((l)=>l.startsWith('refuses ')),[`refuses proof-file-invalid: ${why}`]);
|
||||
await refused(e,['activate',C1,'acme',...pair(f)],'proof-file-invalid',why);
|
||||
await noLeak(e);
|
||||
});
|
||||
await t.test('test 4: a path outside the repository through a directory symlink into it refuses on its realpath',async(t)=>{
|
||||
const e=await mk(t);await seedPreview(e);await inRepo(t,e,join(repo,'proof-r188'));
|
||||
const link=join(e.root,'repo-link.188');symlinkSync(join(repo,'proof-r188'),link);
|
||||
const f=join(link,'proof.188');assert.ok(!lstatSync(f).isSymbolicLink());assert.ok(!f.startsWith(`${repo}/`));
|
||||
const why=`--proof-file ${f} is inside the repository or the copies`;
|
||||
const out=await planOnly(e,3,'activate',C1,'acme',...pair(f));
|
||||
assert.deepEqual(out.filter((l)=>l.startsWith('refuses ')),[`refuses proof-file-invalid: ${why}`]);
|
||||
await refused(e,['activate',C1,'acme',...pair(f)],'proof-file-invalid',why);
|
||||
await noLeak(e);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user