ACE-040: safety regression corpus — the F9 done-bar - #121
Conversation
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
One adversarial corpus that locks every F9 control: it drives each attack class through the REAL
execute_sql tool and asserts its expected Envelope, on BOTH surfaces (stdio subprocess + in-process
HTTP) and BOTH model paths (file-served: SQLite + disk YAML; DB-served: Postgres + model_store), plus
a read-only role FLOOR that proves the database rejects a write with the app gate bypassed. Test-only
+ CI; it locks the controls built in ACE-035..039 (does not change them).
- tests/safety/corpus.py — the single canonical source: the demo SCHEMA (the harness derives BOTH the
semantic model and the physical datasource from it) + CASES mapping each class to its outcome.
Classes: integrity→permission, object-scope→table_out_of_scope/select_star/column_out_of_scope
(incl. hidden in UNION/EXCEPT arms), fail-closed→unscopable_sql, recon→recon, availability→bounded
(row-cap truncate+flag), governed→ok (no false refusals).
- tests/e2e/harness.py — ONE copy of the stdio + HTTP drivers + the model/datasource builders
(test_safety_envelope.py imports them; drivers single-sourced, no duplication).
- tests/e2e/conftest.py — surface (stdio|http) + file_safety_env + the Postgres fixtures
(pg_admin with an AGAMI_IT_PG_REQUIRED sentinel so an all-skip can't pass green, create_ro_role,
db_safety_env, pg_ro_conn). Serial-only (shared role/table); the role password is derived from env.
- tests/e2e/test_safety_corpus.py — the corpus over {surface} × {file, DB} paths.
- tests/e2e/test_role_floor_pg.py — writes on a RAW agami_ro connection (app gate bypassed) →
Postgres raises InsufficientPrivilege; a SELECT succeeds.
- .github/workflows/ci.yml — a new integration-pg job (a postgres:16 service, trust auth — no
password literal) runs the DB-dependent tests; the sentinel makes an unavailable DB fail, not skip.
Consolidation is additive (per sign-off): the corpus is the new end-to-end bar + single-sourced
vectors; the existing unit gate-tests stay as the fine-grained layer. Reviewed (1 must-fix fixed,
nits dispositioned). File path verified green locally; the Postgres path is first-run-in-CI (no local
Docker). Make integration-pg a required check.
Spec: ACE-040
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1accd3f to
c8cb377
Compare
|
Closing: this PR's base branch no longer leads anywhere. Its base is ACE-040 is not abandoned; it is unblocked as of today. Its two remaining dependencies both shipped: ACE-036 ( What the re-authored version inherits from here: the test files themselves, and the sentinel defect #161 identified — enforcement per call-site, so renaming one test silently drops 102 of 108 vectors. Per the governance remediation plan §4, that must be fixed in the port, not after it. What it will need to add, because |
Summary
Spec: ACE-040— the F9 done-bar. One adversarial safety regression corpus that drives every attack class through the REALexecute_sqltool and asserts its expectedEnvelope, on both surfaces (stdio subprocess + in-process HTTP) and both model paths (file-served: SQLite + disk YAML; DB-served: Postgres +model_store), plus a read-only role floor that proves the DB rejects a write with the app gate bypassed. This is the regression bar a future change must not drop below. Test-only + CI — nosrcchanges; it locks the controls built in ACE-035…039.Stacked on #120 (ACE-039) — base is
ACE-039-recon-error-hardening; retarget tomainonce the stack lands.Changes
tests/safety/corpus.py— the single canonical source: the demoSCHEMA(the harness derives BOTH the semantic model and the physical datasource from it) +CASESmapping each class to its outcome. Classes: integrity→permission, object-scope→table_out_of_scope/select_star/column_out_of_scope(incl. hidden inUNION/EXCEPTarms), fail-closed→unscopable_sql, recon→recon, availability→bounded (row-cap truncate+flag), governed→ok(no false refusals).tests/e2e/harness.py— ONE copy of the stdio + HTTP drivers + the model/datasource builders (test_safety_envelope.pynow imports them — drivers single-sourced, no duplication).tests/e2e/conftest.py—surface(stdio|http) +file_safety_env+ the Postgres fixtures (pg_admin,create_ro_role,db_safety_env,pg_ro_conn).tests/e2e/test_safety_corpus.py— the corpus over{surface} × {file, DB}paths.tests/e2e/test_role_floor_pg.py— writes on a RAWagami_roconnection (app gate bypassed) → Postgres raisesInsufficientPrivilege; aSELECTsucceeds..github/workflows/ci.yml— a newintegration-pgjob (apostgres:16service — the repo's first CI service) runs the DB-dependent tests; anAGAMI_IT_PG_REQUIREDsentinel makes an unavailable DB fail (not skip) so an all-skip can't pass green.Verification
uv run dev.py checkpasses; 58 corpus cases (29 × both surfaces) + the envelope tests. Full suite 1752 passed / 62 skipped.integration-pgCI job is their first real run. The fixtures were written against the compose fixture's known creds and read as if debugging; a focused security review traced the guard order per case, confirmed the role-floor genuinely bypasses the app gate, and confirmed the corpus is non-vacuous (a regressed control degrades each case to a failure, never a silent pass).Reviewer notes / follow-ups
integration-pga required check (branch protection — outside this diff). The sentinel guarantees it can't pass green on an all-skip, but it must actually be required to gate merges.integration-pgrun.Checklist
Spec: ACE-040on every commit; diff stays in spec scope (declined a PII/sensitive_columnscase — spec assigns PII to F10's corpus)test_safety_envelope.pygreenuv run dev.py checkgreen (file path); review dispositioned (1 must-fix fixed)integration-pggreen in CI (first run) + made a required check🤖 Generated with Claude Code