mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-19 19:32:18 +02:00
v1.87.0.0 feat: add verified CSO audits and replayable repair bundles (#2852)
* feat(cso): add verified audits and replayable repair bundles * fix(cso): harden qualification and setup boundaries * fix(cso): assemble security canaries at runtime * fix(cso): bound release proof and maintenance work Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): require complete evaluation reports Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): replay expired snapshots from supplied source Co-Authored-By: OpenAI Codex <noreply@openai.com> * test(cso): synchronize DNS cancellation assertion Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore(ship): exempt repository owner from liveness proof Co-Authored-By: OpenAI Codex <noreply@openai.com> * test(cso): make recheck retention overlap deterministic Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: bump version and changelog (v1.85.0.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): pass native release gates Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: move release to v1.86.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): resolve rechecks by finding Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: move release to v1.87.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): pass macOS and Windows release gates Normalize BSD wc output, compare Windows paths by filesystem identity, preserve portable snapshot race coverage, and narrow POSIX-only Windows fixtures. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): harden native verification gates * fix(cso): refine Windows native diagnostics * test(cso): isolate Windows Git startup failure * test(cso): stabilize Windows native diagnostics * fix(cso): support hardened Git on Windows * fix(cso): close final verification gaps * test(cso): bound cold Docker fixture setup * fix(cso): restore cross-platform free-suite gates --------- Co-authored-by: OpenAI Codex <noreply@openai.com>
This commit is contained in:
co-authored by
OpenAI Codex
parent
9f81911136
commit
4a3c6a8a3c
+92
-89
@@ -1,107 +1,110 @@
|
||||
/**
|
||||
* cso security-guidance preservation test.
|
||||
*
|
||||
* cso carries load-bearing security prose: OWASP Top 10 mappings, STRIDE
|
||||
* threat-model phrasing, mode dispatch, and false-positive-filtering exceptions
|
||||
* that must NOT be auto-discarded.
|
||||
*
|
||||
* cso is now carved (skeleton SKILL.md + sections/audit-phases.md). The
|
||||
* scope-dependent audit phases (2-11) moved to the section; the mode dispatch
|
||||
* (## Arguments, ## Mode Resolution), the always-run phases (0, 1), and the
|
||||
* FP-filtering exceptions (Phase 12) stay always-loaded in the skeleton.
|
||||
*
|
||||
* Two distinct guarantees (codex outside-voice #5 — earliest-use, not loose
|
||||
* substrings):
|
||||
* 1. PRESERVATION — the security phrases survive somewhere in the union
|
||||
* (skeleton + sections); a carve relocates, it never drops.
|
||||
* 2. ALWAYS-LOADED CONTRACT — dispatch + FP-filtering directives stay in the
|
||||
* skeleton, and mode dispatch precedes any STOP-Read (a directive that
|
||||
* decides which sections to read can't sit behind the STOP that reads them).
|
||||
*/
|
||||
|
||||
/** Security-critical CSO routing/proof policy must remain in the always-loaded controller. */
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const REPO_ROOT = path.resolve(import.meta.dir, '..');
|
||||
const CSO_DIR = path.join(REPO_ROOT, 'cso');
|
||||
const CSO_SKELETON = path.join(CSO_DIR, 'SKILL.md');
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const controller = () => fs.readFileSync(path.join(ROOT, 'cso/SKILL.md'), 'utf8');
|
||||
const template = () => fs.readFileSync(path.join(ROOT, 'cso/SKILL.md.tmpl'), 'utf8');
|
||||
const domain = () => fs.readFileSync(path.join(ROOT, 'cso/sections/audit-phases.md'), 'utf8');
|
||||
|
||||
function readSkeleton(): string {
|
||||
return fs.readFileSync(CSO_SKELETON, 'utf-8');
|
||||
}
|
||||
function readUnion(): string {
|
||||
let text = readSkeleton();
|
||||
const dir = path.join(CSO_DIR, 'sections');
|
||||
if (fs.existsSync(dir)) {
|
||||
for (const f of fs.readdirSync(dir).sort()) {
|
||||
if (f.endsWith('.md') && !f.endsWith('.md.tmpl')) {
|
||||
text += '\n' + fs.readFileSync(path.join(dir, f), 'utf-8');
|
||||
}
|
||||
describe('CSO v3 always-loaded policy', () => {
|
||||
test('dispatch and execution boundaries are available before a host section read', () => {
|
||||
const text = controller();
|
||||
const stop = text.indexOf('> **STOP.**');
|
||||
for (const directive of ['## Arguments', '## Mode Resolution', '**Private startup.**', 'untrusted evidence']) {
|
||||
expect(text.indexOf(directive)).toBeGreaterThan(-1);
|
||||
if (stop >= 0) expect(text.indexOf(directive)).toBeLessThan(stop);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// Security content that must survive the carve (checked against the UNION).
|
||||
const MUST_PRESERVE_PHRASES = ['OWASP', 'STRIDE', 'daily', 'comprehensive', 'confidence', 'verif'];
|
||||
|
||||
describe('cso skill preserves load-bearing security guidance', () => {
|
||||
test('cso skeleton exists and is non-trivial', () => {
|
||||
expect(fs.existsSync(CSO_SKELETON)).toBe(true);
|
||||
// Skeleton stays substantial: dispatch + always-run phases + FP filtering +
|
||||
// report phases are all always-loaded. Under 30 KB means too much moved out.
|
||||
expect(readSkeleton().length).toBeGreaterThan(30_000);
|
||||
for (const phase of [0, 1, 12, 13, 14]) expect(text).toContain(`### Phase ${phase}:`);
|
||||
});
|
||||
|
||||
test('security phrases survive in the union (skeleton + sections)', () => {
|
||||
const union = readUnion().toLowerCase();
|
||||
const missing = MUST_PRESERVE_PHRASES.filter((p) => !union.includes(p.toLowerCase()));
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`cso union is missing required security phrases: ${missing.join(', ')}. ` +
|
||||
`These are load-bearing. A carve relocates them; it must not drop them.`,
|
||||
);
|
||||
test('every scope and lifecycle entrypoint survives host generation', () => {
|
||||
const text = controller();
|
||||
for (const flag of ['--infra', '--code', '--skills', '--supply-chain', '--owasp', '--scope', '--diff', '--base', '--budget', '--offline', '--comprehensive', '--doctor', '--resume', '--replay', '--recheck']) {
|
||||
expect(text).toContain(flag);
|
||||
}
|
||||
expect(text).toContain('mutually exclusive');
|
||||
for (const command of ['start', 'doctor', 'resume', 'replay', 'recheck', 'inspect', 'read', 'history', 'scan', 'import-sarif', 'submit', 'verify', 'finish', 'import-v2', 'inspect-v2', 'schema']) {
|
||||
expect(text).toContain(`gstack-cso ${command}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('ALWAYS-LOADED: mode dispatch + FP-filtering stay in the skeleton', () => {
|
||||
const skeleton = readSkeleton();
|
||||
// Dispatch must be always-loaded — the agent resolves scope before reading sections.
|
||||
expect(skeleton).toContain('## Arguments');
|
||||
expect(skeleton).toContain('## Mode Resolution');
|
||||
// FP-filtering with its critical exceptions is mandatory and must not be on-demand.
|
||||
expect(skeleton).toContain('Phase 12');
|
||||
// The "SKILL.md files are NOT documentation" exception is a must-not-miss
|
||||
// security directive (skill supply-chain findings); it stays always-loaded.
|
||||
expect(skeleton).toContain('NOT documentation');
|
||||
});
|
||||
|
||||
test('EARLIEST-USE: mode dispatch precedes any STOP-Read directive (codex #6)', () => {
|
||||
const skeleton = readSkeleton();
|
||||
const stop = skeleton.indexOf('> **STOP.**');
|
||||
const modeRes = skeleton.indexOf('## Mode Resolution');
|
||||
const args = skeleton.indexOf('## Arguments');
|
||||
expect(modeRes).toBeGreaterThan(-1);
|
||||
expect(args).toBeGreaterThan(-1);
|
||||
if (stop >= 0) {
|
||||
// A dispatch directive stranded after the STOP can't govern which sections to read.
|
||||
expect(args).toBeLessThan(stop);
|
||||
expect(modeRes).toBeLessThan(stop);
|
||||
test('CSO cannot acquire shared preamble execution or content-export hooks', () => {
|
||||
for (const forbidden of ['{{PREAMBLE}}', '{{GBRAIN_CONTEXT_LOAD}}', '{{GBRAIN_SAVE_RESULTS}}', '{{LEARNINGS_SEARCH}}', '{{LEARNINGS_LOG}}', '{{CONFIDENCE_CALIBRATION}}']) {
|
||||
expect(template()).not.toContain(forbidden);
|
||||
}
|
||||
const text = controller();
|
||||
expect(text).not.toContain('gstack-skill-start');
|
||||
expect(text).not.toContain('gstack-telemetry-log');
|
||||
expect(text).not.toContain('gstack-review-log');
|
||||
expect(text).toContain('Do not send findings, source, secrets, harnesses, or bundles');
|
||||
expect(text).toContain('trusted installed gstack distribution');
|
||||
});
|
||||
|
||||
test('cso catalog trim landed (frontmatter description ≤ 200 chars)', () => {
|
||||
const content = readSkeleton();
|
||||
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
expect(fmMatch).not.toBeNull();
|
||||
const desc = fmMatch![1].match(/^description:\s+(.+)$/m);
|
||||
expect(desc).not.toBeNull();
|
||||
expect(desc![1].trim().length).toBeLessThanOrEqual(200);
|
||||
expect(desc![1]).toContain('(gstack)');
|
||||
test('static review cannot acquire tested or current-source closure labels', () => {
|
||||
const text = controller();
|
||||
for (const proof of ['CSO evidence rubric', 'identical security assertion', 'legitimate control passes', 'pristine second copy', 'before/after configuration and dependency closures', 'boundary-replacing mocks', 'legacy review evidence', 'new evidence covering the same root cause']) {
|
||||
expect(text).toContain(proof);
|
||||
}
|
||||
expect(text).toContain('Keep finding evidence, reproduction outcome, patch validation, test-completion assurance, review assurance, and current-source closure separate');
|
||||
expect(text).toContain('cannot establish that an application booted or a repair passed tests');
|
||||
});
|
||||
|
||||
test('cso routing prose moved to "## When to invoke" body section', () => {
|
||||
expect(readSkeleton()).toContain('## When to invoke this skill');
|
||||
test('runtime and scanner execution claims require qualified catalog profiles', () => {
|
||||
const text = controller();
|
||||
expect(text).toContain('Static assessment remains available without runtime or scanner profiles');
|
||||
expect(text).toContain('matching qualified runtime catalog profile');
|
||||
expect(text).toContain('Project-test completion remains `self_reported`');
|
||||
expect(text).toContain('target code shares that process and can forge reporter output or terminate the runner');
|
||||
expect(text).toContain('The `tested` state remains reserved until a target-independent completion witness exists');
|
||||
expect(text).toContain('show assertion, test-completion, and review assurance exactly as recorded');
|
||||
expect(domain()).toContain('matching qualified scanner catalog profile');
|
||||
});
|
||||
|
||||
test('sensitive helper control files stay out of the audited working tree', () => {
|
||||
const text = controller();
|
||||
for (const proof of ['Private control files', 'umask 077', 'mode-`0700`', 'mode-`0600`', 'outside the audited repository', 'remove each control file immediately']) expect(text).toContain(proof);
|
||||
for (const proof of ['Audited-source access invariant', "only with that run's `inspect`, `read`, and `history`", 'exact `path` from `inspect`', '`displayPath` is only a redacted label', 'Never use host `Read`/`Glob`/`Grep`', 'direct reads bypass']) expect(text).toContain(proof);
|
||||
for (const proof of ["copy every record's `domain` and `scope` exactly", 'a new scope leaves the planned scope unassessed', 'Only helper commands may update helper-owned records']) expect(text).toContain(proof);
|
||||
});
|
||||
|
||||
test('supported findings are persisted and surfaced before the final report', () => {
|
||||
const text = controller();
|
||||
expect(text).toContain('Invoke `start` exactly once');
|
||||
expect(text).toContain('never call `start` again');
|
||||
expect(text).toContain('same ID');
|
||||
expect(text).toContain('submit it to the helper **and surface it to the user immediately**');
|
||||
expect(text).toContain('do not wait for the final report');
|
||||
expect(text).toContain('if the run is interrupted');
|
||||
});
|
||||
|
||||
test('empty, partial, redaction failure, persistence failure and expiry stay truthful', () => {
|
||||
const text = controller();
|
||||
for (const contract of ['**complete**, **partial**, or **not assessed**', 'No supported findings in the assessed scope.', 'PERSISTENCE_FAILED', 'MISSING_INPUT', 'withhold the payload entirely', 'one bounded correction attempt', 'seven days', 'thirty days']) {
|
||||
expect(text).toContain(contract);
|
||||
}
|
||||
expect(text).toContain('Completeness is independent of finding count');
|
||||
expect(text).toContain('outside synchronization allowlists');
|
||||
});
|
||||
|
||||
test('obsolete blanket exclusions and certainty scores are absent', () => {
|
||||
const text = controller() + domain();
|
||||
for (const obsolete of ['8/10 confidence gate', '2/10 confidence gate', 'devDependency CVEs are MEDIUM max', "gstack's own skills are trusted", 'User content in the user-message position of an AI conversation is NOT prompt injection', 'pull_request_target` without PR ref checkout is safe', 'Hard exclusions — automatically discard']) {
|
||||
expect(text).not.toContain(obsolete);
|
||||
}
|
||||
expect(text).toContain('Unknown reachability remains');
|
||||
expect(text).toContain('sequential challenge; independent agent unavailable');
|
||||
});
|
||||
|
||||
test('controller stays compact without moving proof policy behind a section read', () => {
|
||||
expect(Buffer.byteLength(template())).toBeLessThan(18_000);
|
||||
expect(Buffer.byteLength(controller())).toBeLessThan(20_000);
|
||||
expect(Buffer.byteLength(controller() + domain())).toBeLessThan(36_000);
|
||||
const frontmatter = controller().match(/^---\n([\s\S]*?)\n---/)![1];
|
||||
const description = frontmatter.match(/^description:\s+(.+)$/m)![1].trim();
|
||||
expect(description.length).toBeLessThanOrEqual(200);
|
||||
expect(description).toContain('(gstack)');
|
||||
expect(controller()).toContain('## When to invoke this skill');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user