# agent-harness-defense v0.2.0: dual-lattice IFC for LLM agent privilege escalation

> Source: <https://dev.to/magopredator/agent-harness-defense-v020-dual-lattice-ifc-for-llm-agent-privilege-escalation-46jh>
> Published: 2026-08-29 07:23:28+00:00

An open, offline-verifiable admission layer that stops instruction-privilege

escalation in coding agents by enforcing a plan-first information-flow policy.

A research prototype with a rigorous audit — not a turnkey production defense.

LLM coding agents run over untrusted web pages, docs and tool output while

holding authority over security-sensitive resources (deploy keys, CI configs,

secret stores). Two structural gaps are documented in the literature:

The harnesses themselves are evaluated across 13 attack objectives in 6 real

coding-agent frameworks ([arXiv:2608.27299](https://arxiv.org/abs/2608.27299)).

`agent-harness-defense`

is an **admission layer**: before a change is applied,

you call `run_admission()`

with an explicit description of what the agent

proposes to do, and it returns a verdict — admit this, deny that, and why. It is

*not* a runtime firewall watching a live agent. It is a library you (or the

harness you integrate) invoke.

The decision core is a **dual-lattice information-flow control (IFC)** engine.

Each datum in the plan carries two labels:

When an action depends on low-integrity data (a repo README, say), the action

inherits that distrust *even if the content contains no trigger word*. That is

exactly what lets it catch prompt injection that the v0.1 heuristic (trigger

phrases) could not see. As a second layer, the v0.1 heuristic (trigger phrases +

forbidden paths) is retained as a **backup signal**, not the primary mechanism.

The planner emits one `Plan`

per query. `evaluate_plan`

applies a componentwise

lattice join over the `depends_on`

graph:

`SourceTag`

(SYSTEM / USER / TOOL_RESULT / REPO_TEXT / ENV / DATA) is the

per-fragment origin label. Reads are classified by path: reading repo text

yields UNTRUSTED integrity (the prompt-injection surface); reading a system file

yields SYSTEM.

`write`

that depends on an untrusted `read`

inherits UNTRUSTED integrity and
is `write`

sourced from `env.SECRET`

to a public sink is `write`

that depends only on SYSTEM/USER input is admitted.The public `Signetry/eval`

IPI corpus models the agent *writing the malicious
artifact to disk*. We add

`INCIDENT_REPORT_INJECTION`

: the plan reads an`README.md`

and writes `incident-report.md`

sourced from `env.SECRET`

,This is verified, not asserted: `test_v01_would_have_missed_this`

re-runs the v0.1

scan over the materialized repo and confirms `escalation_detected is False`

— the

eval is non-vacuous. The suite covers 3 scenarios: the 2 Signetry IPI scenarios

(README injection, CLAUDE.md scope expansion) plus this own secret-leak scenario.

Each one has a test proving v0.1 would have let it through and the new engine does not.

An independent audit (fresh clone, clean venv) reproduced a real defect introduced

during the build: `_step_initial_label`

returned `(PUBLIC, SYSTEM)`

for *every*

read, so propagation through `depends_on`

only worked via magic prefixes baked

into `value_source`

. The fix (`_classify_read_path`

) derives the read label from

the path. A regression test (`test_read_propagates_untrusted_via_depends_on`

) now

fails if the bug returns. The audit also flagged a CI regression the fix caused

(a `bandit`

B108 and a `ruff format`

miss), both closed before merge.

This is the part most security posts omit. Stated plainly:

`Plan`

(what the agent will read,
write, and where each value comes from) must be supplied `Plan`

says step B depends on step A, the engine propagates the label. But nothing
analyzes disk to detect "this file literally cites that other file" on its own.
If the caller declares dependencies wrong, the engine cannot know — which is why
`assert_plan_matches_materialized`

exists, but it is a `LoopStateMonitor`

— which is still the substring heuristic,
not the lattice.`KNOWN_ISSUES.md`

does not whitewash
anything), and a real audit trail where a propagation bug was found and fixed
before publish. That already puts the repo above the median of security projects
shipped to GitHub without external scrutiny.`Plan`

generator) hits a wall, and that
burns credibility fast.`Plan`

from real agent
calls. Without it, "just use it" is an empty promise.`ifc.py`

to learn how to use it.All claims are reproducible offline. The suite models both Signetry IPI scenarios

faithfully: the agent's `obey()`

step *writes the attack artifacts to disk*, so the

defense is exercised on real materialized state, not a mock. Three guard rails keep

the eval honest:

`test_admission.py`

) — fails if `obey()`

does not land the
artifact on disk.`assert_plan_matches_materialized`

) — fails if the declared
`Plan`

diverges from what the agent actually wrote.`test_eval_catches_regression.py`

) — monkey-patches
`evaluate_plan`

to admit everything and asserts the guard observes the broken
boundary.| Gate | Result |
|---|---|
`pytest` |
23 passed (0.29s) |
`ruff check` |
clean |
`ruff format --check` |
clean |
`bandit -r agent_harness_defense -ll` |
clean (B108 suppressed, justified) |

The package is not on PyPI — install from the repo:

```
git clone https://github.com/amurlaniakea/agent-harness-defense
cd agent-harness-defense
pip install -e ".[dev]"      # ed-itable install; [dev] pulls pytest/ruff/bandit
pytest                      # 23 tests
ahd eval                   # run bundled IPI + AC-EVAL-1 scenarios
ahd run REPO --plan plan.yaml   # evaluate a declarative Plan
```

*Última actualización: 2026-08-29 — correcciones de instalación: el paquete no está en PyPI, instalar desde el repo con pip install -e ".[dev]"; el tag v0.2.0 tiene Release de GitHub con notas.*

*License: AGPL-3.0-or-later — Pedro Sordo Martínez*

*Implementation → independent audit on a clean clone → merge gated on green CI.
Prototype, not a turnkey defense: read "What it does NOT do" before integrating.*
