fix: second review exception (docs credential check reads every docs path whatever its class; the live SVG reader never opens a symlink or non-regular file)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-11-03 20:35:19 +00:00
1 parent b8d991bf7d
commit c33df57a94
6 files changed
+19 -4

No files matched your search

+1 -1
View File
@@ -317,7 +317,7 @@ export function collectLive(root: string, base: string): Collected {
}
const consumed = scanConsumers(root, [...diff.perPath.map(e => e.path), ...diff.deleted], [mergeBase], true, root, errors);
const drawings = drawingsAmong(diff.perPath, root, p => {
try { return fs.statSync(path.join(root, p)).size <= DRAWING_READ_CAP ? fs.readFileSync(path.join(root, p)) : null; } catch { return null; }
try { const st = fs.lstatSync(path.join(root, p)); return st.isFile() && st.size <= DRAWING_READ_CAP ? fs.readFileSync(path.join(root, p)) : null; } catch { return null; }
});
const executables = executablesAmong(diff.perPath, root, e => {
try {
+1 -1
View File
@@ -288,7 +288,7 @@ export function classifyDiff(i: ClassInput): ClassResult {
if (i.registry?.error) errors.push(i.registry.error);
}
const riskHits = CLASS_RANK[cls] >= CLASS_RANK.code ? matchRisk({ codePaths: i.paths.filter(p => pathClass(i, p) === 'code'), executables: i.executables, added: i.added, removed: i.removed, scopeAuth: s.SCOPE_AUTH === 'true', ...(i.riskPaths?.length ? { isRiskPath: (p: string) => matchesAny(i.riskPaths!, p) && !DATA_PATH.test(p) } : {}) }) : new Map<RiskClass, string>();
const docSecret = i.paths.find(p => pathClass(i, p) === 'docs' && DOC_PATH.test(p) && (i.added?.[p] ?? []).some(l => DOC_SECRET_LINE.some(r => r.test(l))));
const docSecret = i.paths.find(p => DOC_PATH.test(p) && (i.added?.[p] ?? []).some(l => DOC_SECRET_LINE.some(r => r.test(l))));
if (docSecret && !riskHits.has('security')) riskHits.set('security', `docs that can print a credential: ${docSecret}`);
const byRank = (a: RiskClass, b: RiskClass) => RISK_CLASSES.indexOf(a) - RISK_CLASSES.indexOf(b);
const detected = [...riskHits.keys()].sort(byRank);
+1 -1
View File
@@ -1 +1 @@
{"sha":"2c08a34c47094a808963e87e6a5eb95e8529d6f68b1896174b8dfa613562468e","replay":"GSTA-258-L7"}
{"sha":"6688963750c14f03cdbb249dcaa27ebd9e9c2c62170f4e142ec8ded28af010a1","replay":"GSTA-258-L7"}
+1 -1
View File
@@ -1 +1 @@
{"replay":"GSTA-258-L7","justification":"GSTA-258 L7 planner no-loss check (GST-1177, Addenda 1 and 7): the change only adds triggers (executables stay code; seal, sealed, custody and egress path tokens; redaction calls (camelCase or snake_case), redaction or outside-voice imports (from, side-effect, require, dynamic and Python forms) and SECRET or CREDENTIAL constants set to a regex literal, a regex list or new RegExp are security; a deleted file is never an executable, and an executable or symlink is code with its lines read whatever its docs, data or config name) and removes none; over the last 200 first-parent main commits to bf2cab34c5, planReview and collectCommit from origin/main and from this branch give the same depth, class, risk classes, next step and dispatches on 195, and the other 5 only raise: ed5c948eb6 (#3921, the GST-1164 secret-egress fix) and c197a139b7 (#3918, approval text in public PR bodies) and 514daebf5f (#3569, lib/sealed-guard.ts) go light to full on security, while 90a170c941 and 71acd83aa6 gain security at unchanged full depth and reviewers; no commit's depth drops ; reruns after the pass 1 and Step 11 review fixes give the same 200 rows (GStack Minder drive handoffs/GSTA-258/L7-planner-noloss/, l7-200-v2.tsv, l7-200-v3.tsv)"}
{"replay":"GSTA-258-L7","justification":"GSTA-258 L7 planner no-loss check (GST-1177, Addenda 1 and 7): the change only adds triggers (executables stay code; seal, sealed, custody and egress path tokens; redaction calls (camelCase or snake_case), redaction or outside-voice imports (from, side-effect, require, dynamic and Python forms) and SECRET or CREDENTIAL constants set to a regex literal, a regex list or new RegExp are security; the docs credential check reads every docs path whatever its class; a deleted file is never an executable, and an executable or symlink is code with its lines read whatever its docs, data or config name) and removes none; over the last 200 first-parent main commits to bf2cab34c5, planReview and collectCommit from origin/main and from this branch give the same depth, class, risk classes, next step and dispatches on 194, and the other 6 only raise: ed5c948eb6 (#3921, the GST-1164 secret-egress fix) and c197a139b7 (#3918, approval text in public PR bodies) and 514daebf5f (#3569, lib/sealed-guard.ts) go light to full on security, while 90a170c941, 71acd83aa6 and 216493fb32 gain security at unchanged full depth and reviewers; no commit's depth drops ; reruns after the pass 1 and Step 11 fixes give the same rows, and after the second-exception fixes 216493fb32 also gains security at unchanged depth and readers (GStack Minder drive handoffs/GSTA-258/L7-planner-noloss/, l7-200-v2.tsv, l7-200-v3.tsv, l7-200-v4.tsv)"}
+12
View File
@@ -147,6 +147,18 @@ describe('GSTA-258 L7 Addendum 1 and 7: executables among data-named paths', ()
expect([d.class, d.risk.includes('security'), d.depth]).toEqual(['code', true, 'full']);
}
});
// Value: protects=collectLive never opens a symlinked SVG (GStack Minder second exception on #3972); fails_when=a tracked
// art.svg linked to a FIFO blocks the drawing read; seam=drawingsAmong's live reader
test('a symlinked SVG is not read as a drawing in collectLive', () => {
const r = repo({});
const fifo = path.join(r.dir, '.git', 'fifo');
spawnSync('mkfifo', [fifo], { timeout: 5_000 });
fs.symlinkSync(fifo, path.join(r.dir, 'art.svg'));
r.commit({});
const t0 = Date.now();
expect(collectLive(r.dir, 'main').drawings).toEqual([]);
expect(Date.now() - t0).toBeLessThan(20_000);
}, 30_000);
});
describe('collectLive: the working tree', () => {
+3
View File
@@ -36,6 +36,9 @@ describe('HARN-1 false triggers (harness-evals#231 on 1.93.7.0): data, config an
expect(c.class).toBe('code');
expect(c.reasons).toEqual(expect.arrayContaining(['code: scripts/logs/prune']));
for (const p of exe) expect(classifyDiff({ paths: [p], gateTagged: 0, executables: [p] }).class).toBe('code');
const txt = 'scripts/logs/prune.txt';
const printsKey = classifyDiff({ paths: [txt], gateTagged: 0, executables: [txt], added: { [txt]: ['curl -H "Authorization: Bearer abcdefgh12345678"'] } });
expect([printsKey.class, printsKey.risk]).toEqual(['code', ['security']]);
expect(classifyDiff({ paths: ['logs/run.txt', 'docs/r/logs/ready'], gateTagged: 0, executables: [] }).class).toBe('docs');
});
// Value: protects=sealed custody and outside-model prompt egress plan full depth (GST-1177: harness-evals#237, gstack #3921);