mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
fix: CI run/job ids are not phone numbers; context-recovery fixture gets the full runtime bin
pii.phone.e164 redacted the run id in the published eval report's fetch command (--run 37235771700), breaking the command it prints. Digit-only spans in a run/job flag, gh run argument, actions/runs/ or /job/ URL segment, or run_id/job_id key are now exempt; the same digits elsewhere still report. context-recovery-artifacts: the fixture's ./bin/ lacked gstack-skill-start and gstack-context-recovery (the preamble's runtime scripts since v1.71), so the agent spent its 10 turns hunting for them and hit error_max_turns with no final message (run 37235771700, 11 turns). Link the whole runtime bin; diagnostic rerun passed in 6 turns.
This commit is contained in:
1 parent
0bddbd0947
commit
c245d1bc22
4 files changed
+62
-15
No files matched your search
+24
-2
@@ -320,6 +320,26 @@ export function isNumericMetadataValue(span: string, match: RegExpExecArray): bo
|
|||||||
return NUMERIC_METADATA_KEY.test(input.slice(Math.max(lineStart, start - 80), start));
|
return NUMERIC_METADATA_KEY.test(input.slice(Math.max(lineStart, start - 80), start));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitHub Actions run and job ids are bare 10-12 digit integers, so
|
||||||
|
* pii.phone.e164 reads them as phone numbers. A published eval report prints
|
||||||
|
* the run id inside the command the reader copies
|
||||||
|
* (`bun run eval:pass-rates --run 37235771700`), and redacting it breaks that
|
||||||
|
* command. Only the position decides: a run/job flag (`--run 37235771700`,
|
||||||
|
* `--job=111534615007`), a `gh run view|watch|rerun|download|cancel`
|
||||||
|
* argument, an `actions/runs/<id>` or `/job/<id>` URL segment, or a
|
||||||
|
* `run_id` / `job_id` key. The same digits anywhere else still report.
|
||||||
|
*/
|
||||||
|
const CI_ID_POSITION_BEFORE =
|
||||||
|
/(?:--(?:run|job)(?:-id)?(?:[ \t]+|[ \t]*=[ \t]*)|\bgh[ \t]+run[ \t]+(?:view|watch|rerun|download|cancel)[ \t]+|\/actions\/runs\/|\/jobs?\/|(?:run|job)_id["']?[ \t]*[:=][ \t]*["']?)$/i;
|
||||||
|
export function isCiRunIdentifier(span: string, match: RegExpExecArray): boolean {
|
||||||
|
if (!/^\d+$/.test(span)) return false;
|
||||||
|
const input = match.input ?? "";
|
||||||
|
const { start } = spanBounds(match);
|
||||||
|
const lineStart = input.lastIndexOf("\n", start - 1) + 1;
|
||||||
|
return CI_ID_POSITION_BEFORE.test(input.slice(Math.max(lineStart, start - 80), start));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A four-part version (MAJOR.MINOR.PATCH.BUILD: .NET assembly versions,
|
* A four-part version (MAJOR.MINOR.PATCH.BUILD: .NET assembly versions,
|
||||||
* gstack's own VERSION) is byte-for-byte a dotted quad, and `1.128.1.0` is a
|
* gstack's own VERSION) is byte-for-byte a dotted quad, and `1.128.1.0` is a
|
||||||
@@ -892,14 +912,16 @@ export const PATTERNS: RedactPattern[] = [
|
|||||||
// A digit-only UUID's hyphen groups read as national phone formatting, and
|
// A digit-only UUID's hyphen groups read as national phone formatting, and
|
||||||
// so do a county tax-map parcel ID (see looksLikeParcelId), vector
|
// so do a county tax-map parcel ID (see looksLikeParcelId), vector
|
||||||
// coordinates (looksLikeDecimalCoordinates) and seed/nonce/timestamp JSON
|
// coordinates (looksLikeDecimalCoordinates) and seed/nonce/timestamp JSON
|
||||||
// values (isNumericMetadataValue).
|
// values (isNumericMetadataValue), and GitHub Actions run and job ids in
|
||||||
|
// their id positions (isCiRunIdentifier).
|
||||||
validate: (span, match) =>
|
validate: (span, match) =>
|
||||||
!insideUuid(match) &&
|
!insideUuid(match) &&
|
||||||
span.replace(/\D/g, "").length >= 10 &&
|
span.replace(/\D/g, "").length >= 10 &&
|
||||||
!looksLikeCompactTimestamp(span) &&
|
!looksLikeCompactTimestamp(span) &&
|
||||||
!looksLikeParcelId(span, match) &&
|
!looksLikeParcelId(span, match) &&
|
||||||
!looksLikeDecimalCoordinates(span) &&
|
!looksLikeDecimalCoordinates(span) &&
|
||||||
!isNumericMetadataValue(span, match),
|
!isNumericMetadataValue(span, match) &&
|
||||||
|
!isCiRunIdentifier(span, match),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "pii.ssn",
|
id: "pii.ssn",
|
||||||
|
|||||||
@@ -75,3 +75,14 @@ describe('sanitizeFixedFenceLine (lines a workflow wraps in a fixed fence)', ()
|
|||||||
expect(sanitizeFixedFenceLine('z'.repeat(10 * 1024))).toHaveLength(PUBLISHED_TEXT_MAX);
|
expect(sanitizeFixedFenceLine('z'.repeat(10 * 1024))).toHaveLength(PUBLISHED_TEXT_MAX);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('id positions survive redaction', () => {
|
||||||
|
test('the eval report fetch command keeps its run id (PR #3033 comment)', () => {
|
||||||
|
const line = '✗ context-recovery-artifacts rule FAIL assertion — expect(received).toBeGreaterThanOrEqual(expected) · cause assertion Expected: >= 1 · Received: 0 [slice 10, attempt 1] evidence: paid-slice-10-a1/shards/skill-e2e-session-intelligence (fetch: bun run eval:pass-rates --run 37235771700 --case context-recovery-artifacts) after a repair: bun run scripts/test-paid-shards.ts --tier gate --case context-recovery-artifacts';
|
||||||
|
expect(sanitizeFixedFenceLine(line)).toBe(line);
|
||||||
|
expect(publishedFence([line])[1]).toBe(line);
|
||||||
|
});
|
||||||
|
test('a phone number in published text is still redacted', () => {
|
||||||
|
expect(sanitizeFixedFenceLine('call +1 415 555 0123 now')).not.toContain('415 555 0123');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -191,3 +191,26 @@ describe("db.url_with_password: postgres:postgres only on a loopback host (#2913
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("pii.phone.e164: GitHub Actions run and job ids in their id positions", () => {
|
||||||
|
table(
|
||||||
|
"pii.phone.e164",
|
||||||
|
"MEDIUM",
|
||||||
|
[
|
||||||
|
"bun run eval:pass-rates --run 37235771700 --case context-recovery-artifacts",
|
||||||
|
"bun run eval:pass-rates --run=37235771700",
|
||||||
|
"gh run view 37235771700 --job 111534615007 --log",
|
||||||
|
"gh run download 37235771700 -n paid-slice-10-a1",
|
||||||
|
"https://github.com/garrytan/gstack/actions/runs/37235771700/job/111534615007",
|
||||||
|
"GITHUB_RUN_ID=37235771700",
|
||||||
|
'{"run_id": 37235771700}',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"call 37235771700 tomorrow",
|
||||||
|
"--phone 37235771700",
|
||||||
|
"https://example.com/contact/37235771700",
|
||||||
|
'{"phone_id": 37235771700}',
|
||||||
|
"run 4155550123 laps",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -40,19 +40,10 @@ describeIfSelected('Session Intelligence E2E', [
|
|||||||
run('git', ['add', '.']);
|
run('git', ['add', '.']);
|
||||||
run('git', ['commit', '-m', 'initial']);
|
run('git', ['commit', '-m', 'initial']);
|
||||||
|
|
||||||
// Copy bin scripts needed by timeline and checkpoint
|
// The prompts send every bin call to ./bin/, and the skill preambles run
|
||||||
const binDir = path.join(workDir, 'bin');
|
// gstack-skill-start and gstack-context-recovery, which call further bin
|
||||||
fs.mkdirSync(binDir, { recursive: true });
|
// scripts, so ./bin/ is the whole runtime bin directory.
|
||||||
for (const script of [
|
fs.symlinkSync(path.join(ROOT, 'bin'), path.join(workDir, 'bin'), 'dir');
|
||||||
'gstack-timeline-log', 'gstack-timeline-read', 'gstack-slug', 'gstack-state-root.sh', 'gstack-remote-identity.sh',
|
|
||||||
'gstack-learnings-log', 'gstack-learnings-search',
|
|
||||||
]) {
|
|
||||||
const src = path.join(ROOT, 'bin', script);
|
|
||||||
if (fs.existsSync(src)) {
|
|
||||||
fs.copyFileSync(src, path.join(binDir, script));
|
|
||||||
fs.chmodSync(path.join(binDir, script), 0o755);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute slug (same logic as gstack-slug without git remote)
|
// Compute slug (same logic as gstack-slug without git remote)
|
||||||
slug = path.basename(workDir).replace(/[^a-zA-Z0-9._-]/g, '');
|
slug = path.basename(workDir).replace(/[^a-zA-Z0-9._-]/g, '');
|
||||||
|
|||||||
Reference in new issue
Block a user