traverse

adversarial audit

The index is the attack surface

“We have not evaluated ObjectGraph against adversarial document authors who might craft misleading ::dense blocks or ::index entries to manipulate agent routing.”
— arXiv:2604.27820, Limitations

This page is that evaluation. It is not a new class of attack — it is the same shape as prompt injection and retrieval-corpus poisoning, untrusted text steering a control decision. What is new is only the measurement against this format.

why it works

::indexLLM-generatedUNTRUSTEDagent routes on this
::denseLLM-generatedUNTRUSTEDagent routes on this
::fullcopied verbatimtrustedagent may never read it

The transpiler copies content verbatim but generates the routing metadata. So the cheapest thing in the document to corrupt is also the only thing the agent trusts — and the evidence that would expose the lie sits in the layer the index exists to help you skip.

Results

Each attack applied to every question in the benchmark (48 applicable of 52). “Routing changed” means the top-ranked node moved away from the correct one.

AttackRouting changedCaught at query timeCaught in CI
Keyword hijack
Copy the target node's routing keywords onto an unrelated node, so search_index ranks the decoy first.
100%0%100%
Confidence inflation
Raise a decoy's self-declared conf to 1.0 and lower the correct node's, inverting the ranking.
33%0%0%
Omission
Delete the correct node's row from ::index. The content still exists in the file, but search_index can never surface it, so the agent answers as though it does not exist.
100%38%100%
Dense lie
Rewrite the ::dense summary so it misdescribes the node it belongs to, corrupting the routing decision at Pass 2.
0%98%52%

the finding

A keyword hijack changes routing on 100% of the benchmark and is detected at query time 0% of the time.

There is no collision to spot, no implausible confidence, and the ranking is decisive. Every signal available in the cheap path reports a healthy document. This is a property of the format, not a gap in this implementation: the only disconfirming evidence lives in ::full, which is exactly what the index exists to let you avoid reading.

So the trust boundary cannot be defended from inside the cheap path. It has to be defended at authoring or CI time, where reading the whole document once is affordable — and there, the same attack is caught 100% of the time.

The obvious mitigation does not work

The first thing anyone reaches for is “resolve the top few candidates instead of just the top one”. Below is how often the correct node is still inside the top K after each attack. Breadth buys very little, and it never rescues an omission — a node removed from the index is unreachable at any K, because it is not there to rank.

AttackK = 1K = 2K = 3
Keyword hijack0%25%35%
Confidence inflation67%83%88%
Omission0%0%0%
Dense lie100%100%100%

Reading more candidates costs more tokens to arrive at the same wrong answer.

The two defences

Query time — confidence gating

Escalates to reading ::full when routing looks untrustworthy: no match, implausible uniform confidence, keyword collision, or a ::dense block inconsistent with its own index row.

8%
false positives
+2.6%
token overhead

Catches dense lies well. Catches hijacks not at all.

CI time — content grounding

Reads every node and checks that its routing metadata is actually grounded in its own content: orphaned nodes, phantom index rows, dense/full divergence, keywords that appear nowhere in the node they index.

0
findings on clean corpus
100%
hijacks caught

Runs once per commit, not once per query. The right place to pay for trust.

What is still unsolved

disclosure

Every attack here is applied to documents authored for this repository. Nothing targets a deployed system, and no live service was tested. This is an extension of a limitation the format's own authors published, not an unsolicited security disclosure.

attack definitions

Keyword hijack

Copy the target node's routing keywords onto an unrelated node, so search_index ranks the decoy first.

tell: Two nodes claim near-identical keywords while their ::full content is unrelated.

Confidence inflation

Raise a decoy's self-declared conf to 1.0 and lower the correct node's, inverting the ranking.

tell: conf is author-declared and unverifiable. A document where everything is 1.00 is not more trustworthy, it is less.

Omission

Delete the correct node's row from ::index. The content still exists in the file, but search_index can never surface it, so the agent answers as though it does not exist.

tell: A node body with no matching index row. The parser reports this; nothing in the protocol requires the agent to notice.

Dense lie

Rewrite the ::dense summary so it misdescribes the node it belongs to, corrupting the routing decision at Pass 2.

tell: ::dense and ::full disagree. Detectable only by reading the ::full the summary exists to help you avoid reading.