Skip to content

Refuse non-string policy arguments and judge every result output in the reference Guardian - #499

Open
drk-ram wants to merge 1 commit into
GenAI-Security-Project:integrationfrom
drk-ram:refimpl/guardian-non-string-fail-closed-469
Open

drk-ram wants to merge 1 commit into
GenAI-Security-Project:integrationfrom
drk-ram:refimpl/guardian-non-string-fail-closed-469

Conversation

@drk-ram

@drk-ram drk-ram commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

What changed

The reference Guardian now refuses a tool argument it cannot judge as a string, and judges every output of a result instead of only outputs[0].

  • assemblePreToolCallSnapshot refuses a policy-target value, or a url, endpoint, host or domain value, that is not a string (array, object, number, boolean, null). The step ends in the existing evaluation_failed deny, the same path a missing target takes, instead of reaching AGT as an undefined rule and an allow.
  • The result gate cuts a result into one single-output snapshot per string leaf (assemblePostToolCallUnits), evaluates each with the unchanged post_tool_call policy, and returns the most restrictive decision (deny, ask, modify, allow). Each redaction is addressed to the JSON pointer of the leaf it came from, so ["ok", "token=..."] and [{"stdout": "token=..."}] are redacted at /outputs/1/value and /outputs/0/value/stdout.
  • A result with more than 256 strings, or nested deeper than 32 levels, is refused rather than partly examined. A result with no string anywhere is evaluated once as before.

policy/ is untouched, so verify:pin is unaffected.

Which issue does this implement

Closes #469

  • This is an editorial correction (typo, grammar, link, formatting) with no change in
    meaning, so it needs no issue

Base branch

  • integration, because this touches the specification, schemas, a reference
    implementation, an adapter, tests, or CI
  • main, because every changed path is on the documentation lane allowlist in
    CONTRIBUTING.md

Type of change

  • Specification change (schema, hooks, events, AgBOM)
  • Reference implementation or adapter
  • Documentation
  • Tooling or CI
  • Governance (licensing, security policy, contributor docs)

I tested this

Run from reference-implementations/agt with Bun 1.4.4 on Windows x64 and ACS_OPA_PATH pointing at the installed OPA binary.

  • I synced my branch with the base branch before opening this (branch is on bba1b84, the current integration head)
  • uv run pytest -v passes on my machine (not run: no specification or Python file changed)
  • uv run mkdocs build --strict passes on my machine (not run: no docs page changed)

Results:

  • bun test packages/guardian/test/non-string-values.test.ts packages/guardian/test/result-output-units.test.ts: 19 pass, 0 fail. On origin/integration source the same non-string-values.test.ts is 11 fail, 5 pass.
  • bun run typecheck: clean.
  • bun test (whole tree): the same 89 to 90 failures as on origin/integration before this change, all Windows-environment cases (loopback proxy variables, file modes 0600/0700, repo-root path stripping, README banner lines). Nothing outside them changed.

Checklist

  • Commits are signed off with git commit -s (required by the DCO)
  • Prose follows STYLE.md
  • No secrets, tokens, or internal URLs in the diff

Security

  • This change has no security impact

This closes a fail-open: the same deployment denied rm -rf / as a string and allowed it as ["rm","-rf","/"], and left a secret in a second or structured output unredacted. The change moves both cases to deny or redact. Nothing here is an unreported vulnerability beyond what #469 already describes.

…he reference Guardian

A tool argument the gates read (the policy target, url, endpoint, host, domain) that was not a string left the gate rule undefined, which AGT answers as an allow. The request assembler now refuses such a value, so the step ends in the evaluation_failed deny. The result gate judged only outputs[0].value; it now cuts the result into one single-output snapshot per string leaf, evaluates each, and returns the most restrictive decision with each redaction addressed to its own JSON pointer. A result with more than 256 strings, or nested deeper than 32 levels, is refused.

Closes GenAI-Security-Project#469

Signed-off-by: Raghuvar Karthik Durga <177662415+drk-ram@users.noreply.github.com>
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

Thanks for this. It is queued rather than ignored. A lead has not reviewed this pull request yet. A lead will read it when they have time, and we can't promise a date. Nothing here is rejected. If a check below fails, please fix it. A pull request that waits a long time may be closed, and you can reopen it later.

@acs-board-reconciler acs-board-reconciler Bot moved this from Needs triage to In progress in ACS Project Tracker Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:needs-triage Not yet triaged. Applied by the issue forms

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

[Bug] The reference Guardian allows a call whose policy-target argument is not a string, and redacts only the first output of a result

1 participant