software-defence-factory 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/factory/executor.mjs +14 -3
- package/package.json +1 -1
- package/scripts/probe-platform.mjs +14 -0
package/factory/executor.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, lstatSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, lstatSync, chmodSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { ROOT, configAt, run, save, json, digest, instanceLabel, stopContainers } from './lib.mjs';
|
|
@@ -44,6 +44,15 @@ function safeRead(path) {
|
|
|
44
44
|
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024) throw new Error('Expected a regular report under 1 MiB');
|
|
45
45
|
return readFileSync(path, 'utf8');
|
|
46
46
|
}
|
|
47
|
+
// Called only after the container has stopped. Builds may leave read-only cache
|
|
48
|
+
// directories; make owned directories traversable without following symlinks.
|
|
49
|
+
function removeScratch(path) {
|
|
50
|
+
if (lstatSync(path).isDirectory()) {
|
|
51
|
+
chmodSync(path, 0o700);
|
|
52
|
+
for (const name of readdirSync(path)) removeScratch(join(path, name));
|
|
53
|
+
}
|
|
54
|
+
rmSync(path, { recursive: true, force: true });
|
|
55
|
+
}
|
|
47
56
|
async function container(mode, input, command, writable = false, credentials = false) {
|
|
48
57
|
const name = `sdf-${instanceLabel(state)}-${attempt}-${mode}`;
|
|
49
58
|
const reportDir = join(folder, attempt, mode);
|
|
@@ -84,8 +93,10 @@ async function container(mode, input, command, writable = false, credentials = f
|
|
|
84
93
|
const probe = run('docker',['ps','-aq','--filter',`name=^/${name}$`]);
|
|
85
94
|
if (probe) throw error;
|
|
86
95
|
}
|
|
87
|
-
|
|
88
|
-
if (
|
|
96
|
+
let cleanupError;
|
|
97
|
+
try { if (scratch) removeScratch(scratch); } catch (error) { cleanupError = error; }
|
|
98
|
+
if (code !== 0) throw new Error(`${mode} exited ${code}; private log: ${logPath}${cleanupError ? `; scratch cleanup requires recovery: ${cleanupError.message}` : ''}`);
|
|
99
|
+
if (cleanupError) throw cleanupError;
|
|
89
100
|
return reportDir;
|
|
90
101
|
}
|
|
91
102
|
function brief(instruction) {
|
package/package.json
CHANGED
|
@@ -45,6 +45,20 @@ try {
|
|
|
45
45
|
await cli('approve',native.id);await waitState(native.id,'succeeded');setConfig({});
|
|
46
46
|
record('native build scratch exceeds 1 GiB without changing the candidate; limits and cleanup verified');
|
|
47
47
|
|
|
48
|
+
for (const exitCode of [0, 17]) {
|
|
49
|
+
setConfig({check:`mkdir -p cache/nested && echo cache > cache/nested/value && ln -s /workspace cache/candidate && chmod 000 cache/nested cache && exit ${exitCode}`});
|
|
50
|
+
const readonly=await work(`Synthetic read-only cache cleanup, exit ${exitCode}`);
|
|
51
|
+
await waitState(readonly.id,exitCode===0?'awaiting_approval':'failed');
|
|
52
|
+
const run=(await snapshot(readonly.id)).runs.find(r=>r.command==='verify');
|
|
53
|
+
assert(!existsSync(join(state,'jobs',readonly.id,run.id,'check-workspace')));
|
|
54
|
+
assert.equal(readFileSync(join(state,'jobs',readonly.id,'checkout/value.txt'),'utf8'),'fixed\n');
|
|
55
|
+
assert.equal(readFileSync(join(original.repo,'value.txt'),'utf8'),'broken\n');
|
|
56
|
+
if(exitCode===0){await cli('approve',readonly.id);await waitState(readonly.id,'succeeded');}
|
|
57
|
+
else assert.match(run.error,/verify exited 17/);
|
|
58
|
+
record(`read-only cache cleanup after exit ${exitCode}; candidate symlink not followed and exit preserved`);
|
|
59
|
+
}
|
|
60
|
+
setConfig({});
|
|
61
|
+
|
|
48
62
|
const changed=await work('Synthetic changed revision guard');await waitState(changed.id,'awaiting_approval');
|
|
49
63
|
writeFileSync(join(state,'jobs',changed.id,'checkout/value.txt'),'changed after review\n');
|
|
50
64
|
await cli('approve',changed.id);await waitState(changed.id,'failed');
|