Technology Aug 29, 2026 · 8 min read

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

agent-harness-defense v0.2.0: dual-lattice IFC for LLM agent privilege escalation 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...

DE
DEV Community
by Fenix
agent-harness-defense v0.2.0: dual-lattice IFC for LLM agent privilege escalation

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

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.

The problem

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:

  • Instruction privilege escalation — harnesses assemble per-invocation context and can elevate low-privilege content (repo text, tool output) to a higher instruction level, making the agent obey what it would refuse at the original level (Girrens & Wang, arXiv:2608.27234 — SPA).
  • Non-decaying loop state — autonomous loops re-initialize their safety monitor every trajectory, so attacker evidence fragmented across iterations is never seen in any single window; a monitor retaining cross-iteration state separates true from false positives, trajectory-scoped monitors do not (arXiv:2608.27141).

The harnesses themselves are evaluated across 13 attack objectives in 6 real
coding-agent frameworks (arXiv:2608.27299).

What it is

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:

  • Confidentiality — is it secret?
  • Integrity — do we trust who put it there?

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.

How the engine works

The planner emits one Plan per query. evaluate_plan applies a componentwise
lattice join over the depends_on graph:

  • Confidentiality = max — the result is as sensitive as the most sensitive operand.
  • Integrity = mintaint does not wash: an UNTRUSTED value joined with a SYSTEM intent stays UNTRUSTED, which blocks the no-upgrade rule.

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.

  • A write that depends on an untrusted read inherits UNTRUSTED integrity and is denied (no-upgrade).
  • A write sourced from env.SECRET to a public sink is denied (no-downgrade).
  • A write that depends only on SYSTEM/USER input is admitted.

The case v0.1 missed (AC-EVAL-1)

The public Signetry/eval IPI corpus models the agent writing the malicious
artifact to disk
. We add INCIDENT_REPORT_INJECTION: the plan reads an
untrusted README.md and writes incident-report.md sourced from env.SECRET,
depending on the read. The v0.1 heuristic — five hard-coded trigger phrases — does
not fire, because the planted README contains none of them. The dual-lattice
IFC denies the step on both axes (integrity + confidentiality) because the
write transitively inherits the README's UNTRUSTED label and carries a SECRET to
a public sink.

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.

The bug the audit caught

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.

What it does NOT do (read this before you integrate it)

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

  1. It does not extract the plan itself. The Plan (what the agent will read, write, and where each value comes from) must be supplied explicitly by the caller. Nothing observes a real LLM acting and builds that plan automatically. That integration is on the user — and today there is not even an example of how to do it with a real framework (LangChain, an MCP harness, etc.). This is the largest gap between "library" and "usable out-of-the-box defense."
  2. Propagation is over what is declared, not over real content. If the 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 test guard, not something that runs in production against a live agent.
  3. Small evaluation corpus. Three scenarios, all with fairly literal attack text in English. No evidence it resists phrasing variation, other languages, or subtler attacks.
  4. No real cross-iteration persistence. The roadmap calls this v0.3: today, if an agent makes several calls in a row, each evaluation is independent except for the accumulated signal of LoopStateMonitor — which is still the substring heuristic, not the lattice.
  5. It has never run against a real agent or seen production traffic. The code is well-tested in software-engineering terms (tests, CI, audit), but zero flight hours against real traffic.

Is it "production-ready"? Two honest axes

  • Engineering hygiene: yes, solid. Correct AGPL license with consistent attribution, SPDX headers, real CI that actually fails when something breaks, 23 tests that are not vacuous (I checked explicitly, not just trusted them to pass), honest documentation of what is missing (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.
  • As a turnkey product for the community to run in production: not yet — and saying so costs credibility, not the opposite. Present it as what it is: a research prototype with a rigorous audit, a reference implementation of IFC defense for agents that demonstrates the concept and documents its own limits — not "install this and your agent is safe." The first person who tries to wire it to a real harness (without building their own Plan generator) hits a wall, and that burns credibility fast.

What would move it toward usable (in order of impact)

  1. An example adapter with a real harness — even a minimal tool-calling wrapper over the Anthropic/OpenAI API showing how to build the Plan from real agent calls. Without it, "just use it" is an empty promise.
  2. An explicit threat model document: what it protects, what it does not, what it assumes of the caller. For a security tool this is nearly as important as the code.
  3. A larger evaluation corpus — phrasing variations, other languages, longer multi-step attacks.
  4. A 5-minute quickstart in the README, separate from the API reference — today you read ifc.py to learn how to use it.

How it is verified

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:

  • Teeth assert (test_admission.py) — fails if obey() does not land the artifact on disk.
  • Drift guard (assert_plan_matches_materialized) — fails if the declared Plan diverges from what the agent actually wrote.
  • Regression guard (test_eval_catches_regression.py) — monkey-patches evaluate_plan to admit everything and asserts the guard observes the broken boundary.

Results (clean runner, fresh venv)

Gate Result
pytest 23 passed (0.29s)
ruff check clean
ruff format --check clean
bandit -r agent_harness_defense -ll clean (B108 suppressed, justified)

Try it

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

Links

Ú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.

DE
Source

This article was originally published by DEV Community and written by Fenix.

Read original article on DEV Community
Back to Discover

Reading List