Research code and data for "Injection Survivability Under Memory Compaction: Structural Position, Not Adversarial Framing, Governs Survival" (IEEE UEMCON 2026).
We measure whether an indirect prompt injection buried earlier in a conversation survives an LLM-driven context compaction step (the mechanism production agent platforms use to summarize long conversation histories and discard the original turns), and whether it retains behavioral force afterwards. Across 994 trials over three open-weight summarizer models, survival is governed by where the injection sits in the conversation and what tool it targets — not by how aggressively it is worded.
- B-ISR (Behavioral Injection Survival Rate): overall 7.65%; beginning-of-conversation placement survives at 15.62% vs. 3.46% at the end (χ²=45.07, p<0.0001).
- Framing has no measurable effect: four adversarial framings (plain, urgency, system-impersonation, goal-chaining) all land within 7.29–7.94% (χ²=0.104, p=0.991).
- S-ISR (Semantic Injection Survival Rate): cosine similarity between the payload and the post-compaction context, reported as a continuous score.
| Path | Contents |
|---|---|
harness/ |
Experiment harness: two compaction pipelines (Anthropic-pattern, Mem0-pattern), the task-model evaluator, S-ISR measurement, and the run orchestrator. See harness/README.md for the full harness API reference (manifest format, output schema, module-by-module description). |
scripts/build_dataset.py |
Builds the full factorial manifest (80 InjecAgent payloads × 4 framings × 3 positions × 4 tool categories) and the conversation scaffolds under data/conversations/. |
scripts/robustness_analysis.py |
Logistic regression robustness check (position/framing/category/summarizer jointly), drop-one-summarizer test, position×framing interaction test, payload-outlier check, power/MDE calculation. |
data/ |
dataset_manifest_660.json (the manifest actually used for the reported runs), dataset_manifest.json (full design), conversations/, controls/. |
results/ |
Raw per-trial output (M1_anthropic.jsonl, M2_anthropic.jsonl, M3_anthropic.jsonl, M1_mem0.jsonl), stats_summary.txt, and generated figures/. |
generate_tables.py |
Produces paper/tables_generated.tex (Table I/II) from results/*.jsonl. |
generate_figures.py |
Produces the position/category/framing/S-ISR figures used in the paper from results/*.jsonl. |
quick_analysis.py |
Fast sanity-check dump of B-ISR/S-ISR by framing, position, pipeline, and tool category. |
health_check.py |
Run-time monitor: row counts, garbage-row rate, and running B-ISR per batch, meant to be run periodically during a live experiment. |
make_subset_manifest.py |
Builds a reduced manifest (dataset_manifest_660.json) from the full design when free-tier rate limits make the full run infeasible in one pass. |
run_all.py |
Sequential orchestrator: runs every (pipeline, summarizer) batch in BATCHES against the harness, with resume support and per-batch logging. |
run_pair.py |
Runs a single (pipeline, summarizer) pair directly, without the full batch loop. |
launch_parallel.ps1 / check_progress.ps1 |
Windows PowerShell helpers to launch batches as background jobs and poll their progress. |
paper/ |
main.tex (submission draft) and main2.tex (McEnerney-style rewrite), refs.bib, worked_example.tex, tables_generated.tex (generated, do not hand-edit), figures/. |
review/ |
Reviewer feedback notes used to triage revisions. |
ASSESSMENT.md, Scoping.md |
Project scoping and design-decision notes from earlier planning stages. |
Not included in this repo: InjecAgent/ — the payload/benchmark dependency this
project builds on. It's an external repo, clone it separately (see Setup below) rather
than vendoring a copy here.
git clone https://github.com/uiuc-kang-lab/InjecAgent.git
pip install -r harness/requirements.txtSet API keys (Groq is required; OpenRouter only if you use a Qwen/OpenRouter-routed model):
export GROQ_API_KEY="gsk_..."
export OPENROUTER_API_KEY="sk-or-..." # optionalOn Windows PowerShell:
$env:GROQ_API_KEY = "gsk_..."
$env:OPENROUTER_API_KEY = "sk-or-..."The sentence-transformer used for S-ISR (all-mpnet-base-v2) downloads automatically on
first use (~420 MB).
Verify the harness end-to-end before a real run:
cd harness
python smoke_test.pyExit code 0 means all checks passed. Without GROQ_API_KEY set, only the local S-ISR path
is exercised (no API calls).
The manifest used for the reported results is already in data/dataset_manifest_660.json.
To regenerate it (or the full design) from scratch:
python scripts/build_dataset.py
python make_subset_manifest.py # only if you need the reduced 660-condition subsetpython harness/run_experiment.py \
--pipeline anthropic \
--summarizer-model llama-3.1-8b-instant \
--manifest data/dataset_manifest_660.json \
--output results/M1_anthropic.jsonl \
--resume--resume skips conditions already present in the output file, so a batch can be safely
killed and restarted.
python harness/run_experiment.py \
--pipeline mem0 \
--summarizer-model llama-3.1-8b-instant \
--manifest data/dataset_manifest_660.json \
--output results/M1_mem0.jsonl \
--resumerun_all.py drives the full model/pipeline matrix defined in its BATCHES list (edit
that list to add/remove models), writing one log file per batch under logs/:
python run_all.pyOn Windows, launch_parallel.ps1 launches the batches as background jobs instead of
running them sequentially; check_progress.ps1 polls their row counts and exit status.
Long free-tier runs (rate-limited to ~15–24 requests/minute) take hours, so check on them periodically rather than watching continuously:
python health_check.py— the primary monitor. Prints per-batch row counts, the fraction of "garbage" rows (behavioral_scorenot in{0, 1}, usually a stuck API call or a parsing failure), and the running B-ISR/S-ISR. FlagsWARNabove 5% garbage rows andFAILabove 20% — aFAILbatch usually means the summarizer or evaluator is erroring out and should be stopped and inspected rather than left running.logs/<tag>.err— stderr for a given batch; check this first ifhealth_check.pyreports garbage rows or a batch stalls.logs/<tag>.log— full stdout for a batch (verbose; only needed when debugging a specific condition).- Rate limiting —
harness/config.pysetsSLEEP_BETWEEN_CALLSand backoff parameters. If you see repeated 429s in a.errfile, increaseSLEEP_BETWEEN_CALLSbefore restarting rather than reducingMAX_RETRIES.
Once the batches you care about have finished (or partially finished — these scripts tolerate incomplete files):
python quick_analysis.py # fast B-ISR/S-ISR breakdown by framing/position/category
python generate_tables.py # regenerates paper/tables_generated.tex
python generate_figures.py # regenerates paper/figures/*
python scripts/robustness_analysis.py # logistic regression + interaction + power checksAll four scripts read directly from results/*.jsonl and require no API keys — they only
need the result files to exist.
Each line of results/*.jsonl is one trial:
| Field | Type | Description |
|---|---|---|
condition_id |
str | Unique condition identifier |
injection_id |
str | Injection payload identifier (from InjecAgent) |
framing |
str | F1 (plain) / F2 (urgency) / F3 (system-impersonation) / F4 (goal-chaining) |
position |
str | beginning / middle / end — where the payload sits in the conversation |
tool_category |
str | email / file_system / web_search / calendar |
pipeline |
str | anthropic or mem0 — which compaction pattern was used |
summarizer_model |
str | Model used to perform the compaction |
compacted_context |
str | Full compacted/summarized context handed to the evaluator |
behavioral_score |
int | 1 = attacker goal executed, 0 = not executed, -1 = evaluation error |
sisr_cosine |
float | Cosine similarity between payload and compacted context |
evaluator_response |
str | Task model's response to the legitimate follow-up task |
See harness/README.md for the complete field list and the manifest
input format.
- The headline
n=994pools 978 Anthropic-pattern trials with 16 Mem0-pattern trials (M1_mem0.jsonl); position/framing/tool-category are pipeline-agnostic structural variables, so both pipelines are combined for the position/framing/category breakdowns. Per-summarizer numbers in Table II use the Anthropic-pattern trials only. - Per-model and per-category sample sizes are unequal — a consequence of free-tier rate limits truncating some batches before completion, not a sampling design choice. See the Limitations section of the paper for the full disclosure of which comparisons this affects.