Skip to content

fix(databases-on-aws): harden DSQL functional eval harness - #262

Merged
anwesham-lab merged 5 commits into
awslabs:mainfrom
davidrz15:fix-dsql-functional-eval-harness
Aug 31, 2026
Merged

fix(databases-on-aws): harden DSQL functional eval harness#262
anwesham-lab merged 5 commits into
awslabs:mainfrom
davidrz15:fix-dsql-functional-eval-harness

Conversation

@davidrz15

@davidrz15 davidrz15 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • migrate the DSQL functional corpora to a versioned schema with explicit regex or LLM grading
  • fail closed around subject isolation, tool/result correlation, transact blocking, redaction, and artifact promotion
  • close review gaps around bounded deterministic scans, process identity, output ownership/locking, descriptor-relative staging, and unsafe grader false positives
  • preserve trusted artifact/recovery metadata while bounding adversarial escaped-key and nested-secret redaction
  • add focused regression coverage and document the supported runner workflows

This is split from #261 so the eval harness hardening can be reviewed independently from the foreign key skill content.

Testing

  • mise run test:python (125 passed)
  • mise run fmt:check
  • mise run build (lint, formatting, tests, Bandit, Semgrep, Gitleaks, Checkov, and Grype)
  • complete DSQL authoring/security self-review using Code Review and PR Review Toolkit lenses
  • git diff --check

Review follow-ups resolved

  • bounded adversarial DDL, sensitive-key, escaped-key, and mapping-collision scans
  • correlated the real transact.sql_list payload with lint source/fixed SQL and rejected comment-based bypasses
  • rejected contradictory legacy safety claims plus literal f-string, %, and + SQL interpolation
  • disabled print-mode prompt suggestions and rejected nested-CLI option injection through model arguments
  • hardened process cleanup, output-directory and lock inode identity, descriptor-relative staging, and promotion recovery
  • preserved trusted artifact schema/control-plane fields while redacting untrusted values and deeply escaped credentials
  • replaced the interpolation detector's catastrophic backtracking pattern with bounded lookaheads
  • restored escaped-separator redaction and preserved evidence after unterminated escaped assignments
  • made output-lock and staging cleanup idempotent under injected unlock/removal failures
  • restricted trusted artifact-value bypasses to scalar constants

Known limitation

macOS does not expose a supported primitive that reliably tracks a rapid double-fork plus setsid() escape. The runner improves kqueue/libproc error handling and cleanup, but does not claim that unsupported case is fully contained.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.

anwesham-lab
anwesham-lab previously approved these changes Aug 28, 2026

@anwesham-lab anwesham-lab left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

largely lgtm, leaving a comment for follow ups we should make, but they can be picked up after merged and I'd like to get the FK support in ASAP as priority

@anwesham-lab

anwesham-lab commented Aug 28, 2026

Copy link
Copy Markdown
Member

Reviewed this as the harness half of the #261 split. The split is clean and the blocking findings from #261 are genuinely fixed — no FK content leaked in (0 FK-related additions across the corpora diffs), so this reviews independently as intended. Recommending merge, with one small fix worth taking here and two follow-ups.

Verified fixed, at head 8bf388cf5f8b0764a457ba02a2bce1214b332980:

#261 Fix in this PR
--bare stripped the Skill tool, so the suite graded the bare model Flag removed entirely, plus _dsql_skill_loaded() correlating the Skill tool_use_id to a non-error result
.claude/.mcp.json didn't exist; two documented commands exited 1 Defaults to the shipped config; where the path appears it's documented as gitignored / operator-supplied
Hardcoded --max-turns 10; exhaustion misfiled as infrastructure CLI flag, and error_max_turns reclassified as truncated with an explicit warning
Missing/misspelled llm_judge silently fell back to keyword grading Required grader key through a Grader enum that raises on anything else
Missing expectations graded nothing and exited 0 EvalSchemaError on empty/missing, plus skill_name validation
overall_pass_rate computed only over graded assertions requested_total / graded_total split with truncated_failures
Unresolved --eval-ids warned and exited 0 Now raises eval IDs not found: [...]

The provenance block (corpus_sha256, plugin_tree_sha256, selected_eval_ids) and blocking mcp__aurora-dsql__transact behind a generated guard plugin are good additions beyond what was asked for.


Smallest follow-up, could be in this PR: redaction corrupts UUIDs

In _redact_text the 12-digit account rule fires inside UUIDs before UUID_VALUE (L2339) can match, destroying the shape; PAYMENT_CARD_VALUE's \d[ -]*? then eats the hyphenated leading groups. Executed against this branch:

00000000-0000-0000-0000-000000000000  ->  <redacted-payment-card>-0000-<redacted-account>
550e8400-e29b-41d4-a716-446655440000  ->  550e8400-e29b-41d4-a716-<redacted-account>
550e8400-e29b-41d4-a716-4466554400ab  ->  <redacted-uuid>          # only this one is correct

Only UUIDs with a hex letter in the final group redact cleanly — including the repo's own all-zeros example convention, which comes out as garbage. Since the DSQL evals are built on example UUIDs, artifacts and judge evidence carry mangled placeholders.

Scope check before anyone worries: this is an evidence-quality bug, not a grading bug. I confirmed DDL structure survives redaction intact — column types and constraint clauses pass through — so no eval passes or fails wrongly because of it. Fix is to run UUID_VALUE.sub ahead of the account and payment-card rules, or anchor those two to reject matches adjacent to --delimited hex groups.

Two things I suspected and disproved by testing, so they need no action: PAYMENT_CARD_VALUE is not a ReDoS risk (0.000s at 36 digits), and redaction does not damage the DDL that assertions actually grade.

Follow-up 1: decompose the test suite

test_run_functional_evals.py is 3,237 lines and collects 4 tests — no classes, no parametrize. That's down from 19 focused tests in #261, which is why mise run test:python reports 69 rather than main's 84.

To be clear about severity, since "4 tests" reads worse than it is: I measured coverage, and those 4 tests exercise 83% of 2,832 statements in the runner. The fixes above are genuinely protected against regression. The cost is diagnosability — a failure reports one of four ~800-line tests like test_grading_correlates_tools_redacts_evidence_and_fails_closed, and the first failed assert hides everything downstream. Real maintenance cost, but refactoring work on a suite that demonstrably covers the code, so it doesn't need to gate this merge.

Follow-up 2: REDACTION_KEY stability

REDACTION_KEY = os.urandom(32) is regenerated per process, so SQL-literal placeholders differ between runs and artifacts aren't diffable run-to-run. Deriving it from corpus_sha256 would keep the pseudonymization property while making artifacts comparable — which is most of the value of writing them.

Non-blocking, but cheaper to answer now than later

605 → 6,335 lines in the runner. Most of it earns its place; _ProcessTreeMonitor is the part I'd question — 322 lines of kqueue + libproc via ctypes + Linux prctl(37) subreaper + /proc scraping to track descendants escaping the process group, when the subject is claude -p launched by a maintainer on their own machine rather than hostile code. The PR body also concedes it doesn't contain the double-fork + setsid() case it's built for, so it's carrying permanent complexity for a guarantee it explicitly doesn't provide. Same question for the hand-rolled JSON limits and the SSN / payment-card / JWT / cookie classes, which don't appear in DSQL eval traffic.

Raising it now only because it's the one item that gets harder after merge — nobody volunteers to delete working code, and everyone who touches this file next pays the comprehension tax. Entirely a maintainer judgment call, not a merge condition. The transact block and credential redaction should stay either way.


Note on scope: this was a targeted pass — I verified the #261 findings, measured coverage, and executed the redaction paths, rather than the full sub-agent roster #261 got. That was deliberate since this is just tooling/CI, a test suite, and one consumer.

anwesham-lab
anwesham-lab previously approved these changes Aug 28, 2026

@theagenticguy theagenticguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed with a focus on path traversal and OS/network-level risk in the new harness. Two things I could not fault: no corpus field reaches a filesystem path (eval_id is type-checked as a non-negative int at 3801-3808 and is the only corpus value used as a path component), and no corpus content reaches argv (prompt and judge payload both go over stdin at 1187/1721, shell= is never used). The promotion path's O_NOFOLLOW + dir_fd + flock + inode identity discipline looks right.

Four blocking findings inline. Each performance and redaction claim was measured by importing the module and calling the real function on this branch, not inferred from pattern shape; timings are in the comments.

Please double-check these before I re-review — I would value a second opinion on two points specifically:

  1. The _create_table_bodies timings (comment on line 3552) are from adversarial input ("create table t(" repeated). Do you consider a 2 MB unbalanced-DDL answer reachable in practice for a regex grader eval, or is the realistic ceiling much lower than the constant permits?
  2. Whether signature/sig genuinely belong in _is_sensitive_key (line 2033) given _redact_tool_result_value's allowlist already covers the tool-result path. My concern is the artifact and judge paths, which are denylist-only — worth confirming that reading is right.

Non-blocking notes I did not comment on, happy to file separately if useful: env secrets under 12 chars are skipped inside mapping keys (2445); a bare 40-char AWS secret access key has no free-text detector (AWS_ENV_CREDENTIAL at 1928 requires a NAME= prefix); --model/--judge-model accept a leading - and become CLI flags; os.killpg at 659/697 runs after the child is already reaped at 951-957, so it can signal a recycled PID, and signal_known/wait_known drop the (pid, start_time) identity the Linux tracker already collects at 542-549; expanduser() at 1246 raises RuntimeError (not OSError) on an unknown ~user prefix, which aborts the whole run.

The known-limitation note about macOS double-fork plus setsid() is the right call to document rather than paper over.

Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated

@theagenticguy theagenticguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested edits for three of the five blocking findings, each applied locally against 0d501ce and verified before posting. The harness suite passes with all three in place (python3 -m pytest -q test_run_functional_evals.py, 4 passed).

Two of my blocking comments I am withdrawing as written, because your own test suite documents intent that my suggestions would have broken. I tried both and they failed:

  1. Line 4053 (hoisting the MAX_REDACTION_INPUT guard out of the LLM_JUDGE branch) — this breaks test_grading_correlates_tools_redacts_evidence_and_fails_closed:2044, which feeds a 2 * MAX_ARTIFACT_TEXT answer with a forbidden CREATE INDEX buried in the middle and requires the regex grader to fail it. Grading long transcripts deterministically is deliberate, so the guard is correctly judge-only. The quadratic scanners are still the real problem; the line 3552 suggestion below fixes them at the source instead, which is the better place anyway.

  2. Line 5358 (gating the sibling sweep on the ownership marker) — this breaks test_main_covers_both_graders_artifacts_and_incomplete_runs:2689, which creates an unmarked .results.run-injected sibling and requires cleanup to remove it. Marker-gating cannot work here: a staging dir abandoned mid-crash may never have been marked. I still think deleting unmarked directories in an unowned parent is a sharp edge, but the fix has to preserve crash cleanup — perhaps confining staging to a subdirectory of the leased output dir, or matching on a per-run random suffix the runner records. Your call on the shape; I withdraw my specific suggestion.

The other two blocking findings (os.killpg on a reaped PID, and expanduser raising RuntimeError) I have left as prose rather than suggestions, since both fixes touch control flow where you will have better context than I do.

Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py
@anwesham-lab

Copy link
Copy Markdown
Member

I'll pick up blocking comments separately, to strengthen evals and run evals locally and add to report in a comment in review of FKs to simultaneously unblock that workstream.

@anwesham-lab

anwesham-lab commented Aug 31, 2026

Copy link
Copy Markdown
Member

Implemented the requested review follow-ups in fe72bb8.

Resolved:

  • bounded adversarial CREATE TABLE and sensitive-key scans;
  • added SigV4 signature / sig / hmac redaction without losing benign oversized-key evidence;
  • skipped process-group signals after the root is reaped and validated Linux PID start-time identity in fallback cleanup;
  • handled Path.expanduser() RuntimeError as a fail-closed Read-scope violation;
  • moved staging under the owned output directory, removed the parent sibling sweep, and made marker/recovery cleanup relative to the leased directory descriptor;
  • rejected explicit f-string injection and combined-DDL transaction guidance in deterministic grading.

The two withdrawn suggestions remain unchanged: the complete semantic-redaction limit is still judge-only, and unmarked crash staging is not marker-gated.

Verification:

  • mise run test:python — 81 passed
  • mise run build — passed
  • direct Bandit scan of the modified runner/tests — no findings
  • complete 20-area DSQL review and security roster — completed
  • git diff --check — clean

Working on running complete automated fleets across various judges on security and other areas.

@anwesham-lab

anwesham-lab commented Aug 31, 2026

Copy link
Copy Markdown
Member

Final DSQL authoring/security convergence review completed at 4476945.

The contributor branch was rebased onto newer main after review. The DSQL
files at rebased head 4476945 are byte-identical to reviewed local head
fd3af00; only commit IDs changed.

# Confidence Area Finding Disposition Commit / SHA
1 99 grader-security Real transact payloads use sql_list; string-only correlation could miss unsafe execution Fixed with per-statement fail-closed correlation and comment canonicalization 82f175a
2 99 grader-security Contradictory legacy claims and literal f-string / % / + interpolation could pass safety assertions Fixed with contradiction checks and structural interpolation detection 82f175a
3 99 output-safety Lock replacement, path replacement, and reserved-prefix recovery could permit concurrency bugs or delete/strand data Fixed with directory+file inode locking, descriptor-relative staging, validated recovery state, and recoverable internal preparation names 82f175a
4 99 redaction Trusted schema/control-plane fields could be corrupted; nested or adversarial escaped values could leak or consume superlinear time Fixed with trusted structural fields, linear escaped-key scans, deep nested-secret handling, and bounded collision logic 82f175a, 4476945
5 98 process/CLI Prompt-suggestion events, nested CLI option values, startup-control environment variables, and SIGQUIT cleanup were not fully fail-closed Fixed with explicit CLI flags/validation and cleanup coverage 82f175a
6 99 docs/comments Recovery and advisory-lock documentation overstated sibling cleanup and arbitrary-writer exclusion Corrected 82f175a

Verification on the reviewed SHA:

  • mise run test:python116 passed
  • mise run buildpassed
  • Bandit — no findings
  • Gitleaks — no leaks
  • Checkov — 71 passed, 0 failed
  • Grype — no vulnerabilities
  • complete 20-area Code Review / PR Review Toolkit roster plus final convergence pass
  • git diff --check — clean

No unresolved finding at the review procedure's confidence threshold remains. The documented macOS rapid double-fork plus setsid() limitation remains intentionally scoped as previously agreed.

davidrz15 and others added 2 commits August 31, 2026 12:08
Make functional eval runs fail closed around subprocess containment, transact gating, deterministic grading, artifact redaction, and output promotion. Validate all supported corpora under the versioned schema and add focused regression coverage for the harness safety boundaries.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the [project license](https://github.com/awslabs/agent-plugins/blob/main/LICENSE).
Bound adversarial SQL and redaction scans, cover SigV4 signature names, and avoid signaling reaped or recycled process identities. Fail closed on unresolvable Read paths, preserve unrelated sibling paths, and keep output recovery bound to the leased directory inode.

Add focused regressions for each boundary and reject unsafe f-string and combined-DDL grading false positives.
@anwesham-lab
anwesham-lab force-pushed the fix-dsql-functional-eval-harness branch from fd3af00 to 4476945 Compare August 31, 2026 19:08
anwesham-lab
anwesham-lab previously approved these changes Aug 31, 2026
@anwesham-lab
anwesham-lab dismissed theagenticguy’s stale review August 31, 2026 19:28

blocking changes addressed

@anwesham-lab
anwesham-lab enabled auto-merge August 31, 2026 19:30
@anwesham-lab

Copy link
Copy Markdown
Member

Complete 20+ agent review-fleet report

Reviewed head: 4476945
Result: every merge-relevant finding at the review procedure's confidence
threshold is fixed, explicitly withdrawn by the reviewer, or documented as an
accepted platform limitation. All eight GitHub review threads are resolved.

The contributor branch was rebased after the review commits were pushed. The
DSQL eval files at 4476945 were compared byte-for-byte with reviewed local
head fd3af00 and are identical; only commit IDs changed.

Fleet coverage

# Review lens Coverage and outcome
1 Repository instructions Verified worktree isolation, mise workflow, dependency boundaries, required full build, and no unrelated changes. Complete.
2 Shallow correctness Found and fixed real transact.sql_list correlation, unrelated-SQL lint acceptance, literal % / + interpolation, and fail-open malformed payloads.
3 History / blame Checked the intent behind process containment, long-answer regex grading, crash recovery, and the documented macOS limitation. No stale semantic regression remains.
4 Prior PR comments Revalidated every theagenticguy inline and review-body item against the final head. All active requests are fixed; withdrawn suggestions remain intentionally unapplied.
5 Comment analyzer Corrected stale advisory-lock, snapshot, sibling-staging, and Darwin NOTE_TRACK wording.
6 Silent-failure hunter Fixed ignored judge errors, ambiguous/malformed transact input, comment-based lint bypasses, promotion recovery failures, and failure-reporting gaps.
7 Code simplification Removed or avoided duplicate assertion/recovery behavior where it could hide production paths; no blocking simplification finding remains.
8 PR test analyzer Added focused tests for every validated boundary. Runner coverage is now 51 focused tests; repository total is 116 tests.
9 Type-design analyzer Hardened artifact schemas, trusted control-plane fields, CLI argument validation, promotion state, and invalid-state rejection.
10 General correctness reviewer Confirmed the real MCP transact shape, lint-result ordering, per-statement correlation, and fail-closed execution rules.
11 Independent quality reviewer Fixed Claude CLI prompt_suggestion compatibility and source-SQL correlation; no remaining high-confidence quality finding.
12 Security reviewer Reviewed path traversal, symlink/TOCTOU, lock replacement, process identity, environment startup controls, redaction, data loss, and CPU/memory bounds. All validated findings fixed.
13 Mainline / PR drift Verified the split remains independent of unrelated FK skill content and corrected documentation drift introduced by review fixes.
14 PR body auditor Updated scope, review follow-ups, test totals, security gates, and known limitation to match the final diff.
15 Python idiom reviewer No additional non-duplicative correctness finding after the fixes.
16 DSQL authoring-style reviewer Checked grader choice, assertion wording, corpus placement, and prescribed DSQL terminology. No remaining blocking style issue.
17 Independent AGENTS.md compliance Confirmed worktree use, mise commands, no new dependencies, clean manifests/cross-references, and full-build compliance.
18 Reviewer-comment resolution Classified every review item as fixed, withdrawn, accepted limitation, or non-blocking observation. Eight of eight threads resolved.
19 Commit / squash audit Verified intentional commits only, correct merge base, contributor statement, preserved authorship, and no generated/whitespace noise.
20 Deferred-findings adjudicator Revalidated disputed findings and promoted only reproducible, customer-relevant issues into the fix set.
21 Final Code Review convergence Re-reviewed the complete follow-up diff; no additional high-confidence correctness or repository-instruction finding.
22 Final security convergence Found and fixed descriptor-path staging, structural redaction, SQL-comment correlation, deeply escaped credentials, and crash-preparation recovery.
23 Final tests / comments / types convergence Confirmed direct regression coverage and documentation consistency after the last security fixes.

Validated findings and dispositions

Area Finding Final disposition Rebased commit
Deterministic grading Adversarial unbalanced CREATE TABLE input caused repeated tail scans Total scan budget; malformed/budget-exhausted input fails closed 9477195
Secret classification Oversized keys and SigV4 signature / sig / hmac handling were incomplete Linear sensitive-key classification and structured/free-text regressions 9477195, 82f175a, 4476945
Tool correlation Grader modeled transact as sql rather than required sql_list Validate nonempty sql_list; correlate every statement to source/fixed lint SQL 82f175a
Lint safety Unrelated SQL or SQL prefixed with comments could bypass lint gates Prompt-SQL correlation plus SQL comment canonicalization 82f175a
Unsafe SQL Contradictory legacy prose and literal f-string / % / + construction could pass Contradiction rejection and structural interpolation detection 82f175a
Process containment Reaped/recycled PIDs, lost Linux identity, SIGQUIT, and refresh failures Root-reaped guard, start-time identity, complete termination-signal handling, fail-closed tracking 9477195, 82f175a
CLI compatibility Unknown-user expanduser, leading-dash model values, and post-result prompt suggestions Resolve errors fail closed, model arguments reject option-like values, prompt suggestions disabled 9477195, 82f175a
Environment isolation Passed startup-control variables could execute before containment Reject PYTHONPATH, loader injection variables, NODE_OPTIONS, and equivalent controls 82f175a
Output concurrency Replacing the visible lock file allowed separate lock inodes Lock both output-directory and visible lock-file inodes; continuously verify identity 82f175a
Staging TOCTOU Path-based staging could follow a replaced output path Descriptor-relative staging and artifact writes throughout 82f175a
Recovery/data loss Reserved-prefix directories could be deleted without valid runner state or stranded before state publication Validate journals/markers, preserve unknown entries, use recoverable internal preparation names 82f175a
Artifact integrity Environment-secret redaction could rename required schema/control-plane keys and values Preserve trusted structural keys/enums while redacting untrusted leaves 82f175a
Redaction completeness Deeply escaped credentials and truncation boundaries could leak fragments Forward scanners, deep nested handling, redact-before-truncate ordering 82f175a, 4476945
Redaction performance Escaped JSON, oversized decoded keys, and mapping collisions were superlinear Linear escaped-key/token scans, collision counters, lazy regex iteration, timing regressions 82f175a, 4476945
Documentation README overstated arbitrary-writer exclusion and sibling cleanup Document cooperating-runner locks, internal staging only, and exact snapshot scope 82f175a

Reviewer-specific accounting

  • All active blocking requests from theagenticguy are implemented.
  • The requested total DDL scan budget and SigV4 classification are included.
  • Reaped-PID signaling and expanduser() failure handling are included.
  • The unsafe parent-sibling sweep was replaced with descriptor-relative internal
    staging and recovery.
  • Two proposed edits were explicitly withdrawn by the reviewer:
    • hoisting the complete-answer redaction bound into deterministic grading;
    • marker-gating the old sibling sweep.
  • The macOS rapid double-fork plus setsid() case remains an explicitly
    documented platform limitation, as agreed.
  • The non-blocking observation about detecting a context-free 40-character AWS
    secret access key was considered but not implemented because that shape has no
    reliable distinguishing context and would create broad false positives. Named,
    structured, environment, provider-token, access-key-ID, and SigV4 secrets are
    covered.

Verification

  • mise run test:python116 passed
  • mise run buildpassed
  • git diff --check — clean
  • Build workflow — passed
  • CodeQL (actions, javascript-typescript) — passed
  • Dependency Review — passed
  • Bandit — passed
  • Semgrep / Semgrep OSS — passed
  • Gitleaks — passed
  • Checkov — passed
  • Grype — passed
  • ClamAV — passed
  • Zizmor — passed
  • SonarQube — passed

Only lightweight PR-title, contributor-statement, and merge-status jobs remain
queued; the substantive build and security matrix is green. Human re-review is
still requested from theagenticguy.

Conclusion: the 20+ agent review fleet is complete, every validated
merge-relevant finding is addressed, and no additional code change is required
from this review.

amaksimo
amaksimo previously approved these changes Aug 31, 2026

@theagenticguy theagenticguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the updated diff at 4476945. All five of my blocking findings and the six smaller notes are fixed, and I verified each one rather than taking the diff's word for it.

What I confirmed resolved, by measurement on this branch:

finding evidence
quadratic _create_table_bodies 43.51 s -> 0.031 s at 100 KB; 0.125 s at 500 KB; real DDL verdicts unchanged
unbounded key into SENSITIVE_KEY 8.14 s -> 0.022 s at 30 KB
SigV4 names not sensitive X-Amz-Signature/sig/hmac now True, signal/design/assignment still False, free text and artifacts both redact
sibling sweep in an unowned parent the parent sweep is gone entirely; staging moved inside the leased dir via DescriptorTemporaryDirectory, and a planted .results.run-user-notes now survives
killpg on a reaped PID, stale PID in signal_known returncode is None guard added; signal_known re-checks (pid, start_time) against /proc
expanduser RuntimeError, --model leading dash, --pass-env startup controls, timeout bound all handled (_model_argument, MAX_TIMEOUT_SECONDS, UNSAFE_PASSTHROUGH_ENVIRONMENT)

I also checked the new guard tests actually fail when the invariant breaks, rather than passing vacuously. Removing the scan budget, stripping SigV4 coverage from all five places, dropping the returncode guard, and reverting the RuntimeError handler each produced the matching failure. test_output_setup_preserves_unowned_sibling_runs is a nice reversal of the test that blocked my earlier suggestion — that resolved the tension cleanly, and moving staging under the lease is a better answer than the marker-gating I proposed.

That said, the revision is 1,174 changed runner lines, and reviewing the genuinely new code turned up four regressions I would block on, plus one hardening note. Three carry suggestions I applied and measured locally; the suite stays green (python3 -m pytest -q test_run_functional_evals.py, 51 passed) with all of them in place.

The one I would look at first is line 3686: the new _has_unsafe_sql_interpolation reintroduces exactly the unbounded-quadratic-scan-over-model-text pattern that this revision just fixed in _create_table_bodies, and it is reachable from the shipped safe_query_evals.json. Since that makes two independent instances, a cheap structural guard may be worth more than fixing them one at a time: bound the deterministic grading input once at the top of grade_eval (a text[:MAX_REDACTION_INPUT] local for the scanners, keeping raw text where anti-truncation matters), or add a test that asserts every registered rule grades a 2 MB adversarial answer within a few seconds.

CI is green on all substantive scans at this head (bandit, semgrep, Semgrep OSS, gitleaks, grype, checkov, clamav, sonarqube, dependency-review, zizmor, build); the four remaining checks are queued housekeeping.

Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py
Comment thread tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py Outdated
@anwesham-lab
anwesham-lab dismissed stale reviews from amaksimo and themself via f71c3c3 August 31, 2026 20:33
auto-merge was automatically disabled August 31, 2026 20:33

Head branch was pushed to by a user without write access

@theagenticguy theagenticguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All five follow-up findings are fixed at f71c3c3. Verified by running the real functions on this branch rather than reading the diff:

finding before after
_has_unsafe_sql_interpolation scan cost 9.04 s at 78.6 KiB 0.018 s at 78.6 KiB, 0.101 s at 449 KiB
escaped separator (\:) leak secret passed through <redacted-secret>
unterminated escaped assignment 96 of 132 chars silently dropped value redacted, trailing answer preserved
TRUSTED_ARTIFACT_VALUE_KEYS subtree nested dict/list written verbatim nested values redacted, scalar status still preserved
OutputDirectoryLease.close() fd lifecycle injected unlock EIO left a stale fd that a second close() closed out from under an unrelated file close() survives the EIO, both fields land at -1, unrelated fd intact

Detection is preserved on the regex rewrite: f-string, + concatenation, and % formatting all still return True, and a parameterised %s with cur.execute(sql, (...)) still returns False.

The twelve new guard tests are not vacuous. I reverted each of the five invariants in place and the matching test failed every time (test_unsafe_sql_interpolation_scan_is_bounded, test_escaped_sensitive_assignment_allows_escaped_separator, test_unterminated_escaped_assignment_preserves_trailing_answer, test_unterminated_escaped_password_redacts_value_and_preserves_tail, test_trusted_artifact_value_bypass_applies_only_to_scalars). Suite is 60 passed on a clean tree.

Correction to my severity ranking

My previous review led with the scan-cost finding and called it the thing to fix first. That ranking was wrong, and I want it on the record before this merges so the next reader does not inherit it.

I had assumed a CI or service threat model. This harness is neither. No workflow invokes run_functional_evals.py; CI only runs its unit tests via pytest tools plugins. Every documented invocation writes to /tmp, no raw artifact is committed anywhere in the repo, the corpus is in-repo under a CODEOWNERS rule covering *, and reaching a live cluster additionally requires the operator's own credentials plus an --mcp-config the README already labels as needing review. There is no multi-tenancy, no shared runner, and a human in front of every run.

Under that model:

  1. The transact guard is still the sharpest surface. Real credentials against a real cluster, write tool allowlisted, denial delegated to a hook. The shlex.quote fix in the prior revision addressed my specific complaint, and this context confirms the concern rather than softening it.
  2. The transcript truncation bug should have been ranked above the scan cost. The transcript is the product: a maintainer runs an eval, it fails, they read transcript.json. Silently deleting the tail while grading used the full text means the artifact and the verdict disagree with no marker, in the one file a human will read.
  3. The two redaction gaps hold at medium, but for a narrower reason than I gave. There is no live exfiltration path. What they defeat is the README's own control, which is a human reviewing artifacts before publication, and a plain X-Amz-Signature=<64 hex> is what a skimming reader misses.
  4. The fd lifecycle bug is local and single-process. Low.
  5. The scan cost is hygiene, not a blocker. Triggering it needs the subject model to emit several hundred kilobytes of adversarially shaped text in response to a reviewed prompt, and the cost is a maintainer waiting and pressing Ctrl-C. Worth bounding, which you did; not worth blocking on.

Bounding the deterministic grading input once in grade_eval is still worth doing as hygiene, since this was the second instance of the same unbounded-scan shape. test_unsafe_sql_interpolation_scan_is_bounded and test_create_table_body_scan_is_bounded do run in CI, so the class now has regression coverage either way.

Not approving yet only because CI for f71c3c3 is still queued. No outstanding code objections from me.

@theagenticguy theagenticguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@anwesham-lab
anwesham-lab added this pull request to the merge queue Aug 31, 2026
Merged via the queue into awslabs:main with commit 1ffbdbd Aug 31, 2026
42 of 50 checks passed
anwesham-lab added a commit to mchenjh/agent-plugins that referenced this pull request Sep 1, 2026
)

* feat(databases-on-aws): add native DSQL foreign key support

Part of the foreign key rollout with:
- awslabs/aurora-dsql-orms#598
- awslabs/aurora-dsql-tools#147

* fix(databases-on-aws): align native DSQL foreign key guidance

Present foreign keys as normal native DSQL functionality and remove retired application-layer replacement framing.

Correct tenant-scoped optional relationship semantics, DSQL post-creation validation, OCC and SQLSTATE boundaries, deferral transaction scope, referential-action limits, and shared-parent modeling.

Make table recreation relationship-safe with a schema-aware pre-create FK and dependent-view gate, single handling for self-references, exact restoration, a write fence, explicit destructive confirmation, and phase-specific recovery.

Replace obsolete UNIQUE table recreation with documented async-index promotion, protect referenced keys during primary-key and AUTO_INCREMENT migrations, and correct focused MySQL, ORM, lint, and routing guidance.

Expand the functional corpus to 19 prompts and 80 assertions covering dependency preflight, self-FKs, direct UNIQUE promotion, referenced-primary-key preservation, tenant nullability, and recovery.

* fix(databases-on-aws): use direct constraint alterations

Document direct CHECK, UNIQUE, constraint, default, and DROP NOT NULL operations while keeping table recreation only for true structural changes.\n\nSimplify generic table recreation to a dependency guard and user-approved bespoke plan, retain SELECT FOR UPDATE for write-skew decisions with OCC retry, and use concise foreign-key-constraint terminology.\n\nRebase FK eval semantics onto the schema-v2 harness from awslabs#262, remove the obsolete application-layer FK eval, use semantic grading for lint preservation assertions, and retain separate DSQL Lint follow-up work.

---------

Co-authored-by: Anwesha Mukherjee <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants