Skip to content

Move ASI06 from uncovered to partly addressed - #128

Merged
arpanghoshal merged 2 commits into
mainfrom
owasp-asi06-partial
Sep 8, 2026
Merged

arpanghoshal merged 2 commits into
mainfrom
owasp-asi06-partial

Conversation

@arpanghoshal

Copy link
Copy Markdown
Member

What

ASI06:2026 Memory & Context Poisoning moves from the "Not covered by CTRLRun" table into the partly-addressed one. The disclaimer and SPEC-v0.4.md §6.1 go from four uncovered entries to three.

Docs and one test. No src/ change.

Why

The mapping treated two entries with the same downstream shape differently.

ASI01 Agent Goal Hijack is partial, on the argument that G1 and G6 constrain what a hijacked agent can do: the action must still be named in the policy, and an approval granted for one action cannot be spent on another. The not-covered half then says CTRLRun never sees the prompt, the plan or the reasoning.

ASI06 was flatly out of scope, on the reason that CTRLRun never reads a model's memory, context or prompt — which is the same sentence, and it did not stop ASI01 being partial. A poisoned context and a hijacked goal both surface the same way: an agent proposing actions through the normal path with a valid principal, bounded by the same two guarantees.

There is a real distinction, and it is now written in the not-covered half where it belongs rather than used to keep the whole entry out: poisoning most often corrupts the arguments of an action the agent is genuinely entitled to take — the right operation against the wrong record — and that is precisely where the kernel has least to say. Policy conditions, resource patterns and v0.6 data scope reach part of it; nothing reaches an identifier an attacker chose.

The guard fired, and it was weaker than it looked

test_T121_the_four_uncovered_entries_are_listed_by_name exists to stop the coverage claim growing quietly, and it did stop this — but only on one of its two checks.

assert code in not_covered passed throughout the edit. A partly addressed entry appears in that half too, by the design test_T121_a_partly_addressed_entry_appears_in_both_halves asserts on the next line, so the membership check cannot distinguish "uncovered" from "partial". The literal string "Four of the ten entries are not addressed by CTRLRun at all" was carrying the test by itself.

That is mutation pattern 1 from CLAUDE.md — a guard that cannot fire independently of another. The discriminator that actually holds is that a fully uncovered entry is cited by no guarantee row, and it has to be tested against the guarantee table rather than the half containing it: that half opens by enumerating all ten entries, so a membership test against it is true of every code.

A first attempt asserted code not in mapping and failed for exactly that reason, which is how the enumeration table got noticed.

Verification

  • Doc edited first, without the test, to confirm the tripwire fires — it did, on the sentence
  • Mutation both ways: citing ASI05 from a guarantee row fails the test; removing it passes
  • 4426 passed, 47 skipped — full suite
  • ruff check clean

Review note

This changes a published claim about what the product defends against. CLAUDE.md puts v0.4's trust-claim items under independent review, so this wants a read from a session that did not write it before it merges — specifically on whether the ASI01-parity argument actually holds, since that is the whole basis of the move.

The mapping treated two entries with the same downstream shape
differently. ASI01 Agent Goal Hijack is partial because G1 and G6
constrain what a hijacked agent can do — the action must still be named
in the policy, and an approval granted for one action cannot be spent on
another. ASI06 Memory & Context Poisoning was flatly out of scope, on the
reason that CTRLRun never reads a model's memory, context or prompt.

That reason is equally true of ASI01, where it did not stop the row being
partial. A poisoned context and a hijacked goal both surface as an agent
proposing actions through the normal path with a valid principal, and the
same two guarantees bound both.

So ASI06 joins the partial table, cited by the same two guarantees. The
not-covered half carries the distinction that does hold: poisoning more
often corrupts the arguments of an action the agent is entitled to take —
the right operation against the wrong record — and that is where the
kernel has least to say. Policy conditions, resource patterns and data
scope reach part of it; nothing reaches an identifier an attacker chose.

The disclaimer and SPEC-v0.4 §6.1 go from four uncovered to three.

T121's membership check was subsumed and is now not
--------------------------------------------------

Only the hardcoded sentence caught this edit. `code in not_covered`
passed throughout, because a partly addressed entry appears in that half
too — by the design the very next test asserts — so the set membership
check could not tell "uncovered" from "partial" and the literal string
was carrying the test alone.

The discriminator that holds is that a fully uncovered entry is cited by
no guarantee row, tested against the guarantee table rather than the half
containing it: that half opens by enumerating all ten entries, so a
membership test against it is true of every code and says nothing.

Mutation-checked both ways — citing ASI05 from a guarantee row now fails
the test, and removing the citation passes it.
@mintlify

mintlify Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
ctrlrun 🟢 Ready View Preview Sep 7, 2026, 10:02 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

# Conflicts:
#	docs/docs/OWASP-AGENTIC-TOP10.md
@arpanghoshal
arpanghoshal merged commit 34a61a8 into main Sep 8, 2026
11 checks passed
@arpanghoshal
arpanghoshal deleted the owasp-asi06-partial branch September 8, 2026 07:47
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.

1 participant