Phase 6: recover, AST fix-suggester + DLP simulation + polish¶
Status: done (local, 2026-09-05). 6 fix-suggester unit tests green.
The "recover" pillar: when the policy fires, suggest the exact fix. The
fix-suggester walks the agent's tool-call code with Python's ast, finds
calls to external / LLM sinks whose arguments are not routed through the
SDK's Phase 4 egress gate (i.e. not wrapped by an @instrument_tool-decorated
tool), and emits the exact one-line fix. An LLM narrates the finding,
but the suggestion is grounded in the AST, which is more defensible (and
cheaper) than asking a model to propose a free-form patch.
Pieces¶
taintward/fix_suggester/suggest.py_dotted_namereconstructs the called function (requests.post,openai.ChatCompletion.create, and so on)._classify_sink: external (*.post/*.put/*.patch/*.delete/*.urlopen) or LLM (dotted name containingopenai/anthropicand ending.create).- a parent map assigns each
Callits true enclosing function (no double counting); a call is UNGUARDED if its enclosing function isn't decorated with@instrument_tool. Findingfeedsnarrate, which produces a grounded explanation. The SigNoz MCP read (signoz_search_tracesfor similar past violations, to prioritize fixes) is a hook for when the MCP server plus a model key are configured.- CLI:
python taintward/fix_suggester/suggest.py <agent_source.py> [...]. taintward/fix_suggester/sample_buggy_agent.py: a deliberately-leaky agent (rawrequests.post,openai.ChatCompletion.create, a module-levelrequests.put, plus a safe internal helper) for the suggester to analyze.- DLP simulation mode:
TAINTWARD_DRY_RUN=1makes the SDK log the violation (taintward.dry_run=true) WITHOUT redacting args, an audit/dry-run mode for safe rollout (see what would be flagged before enforcing). - RAG:
rag_retrieveis already masked at the SDK egress gate (Phase 4: rag goes to[PII]/[SECRET], since embeddings are invertible, OWASP LLM08:2025). No separate redact-before-embed step is needed because the gate already redacts before the tool runs.
Verified¶
$ python taintward/fix_suggester/suggest.py taintward/fix_suggester/sample_buggy_agent.py
sample_buggy_agent.py:15: `requests.post` is an unguarded external sink inside `send_to_auditor`. ... fix: redact = redact_payload(payload, instr.DEST_EXTERNAL) ...
sample_buggy_agent.py:20: `openai.ChatCompletion.create` is an unguarded llm sink inside `summarize_with_llm`. ... fix: redact = redact_payload(prompt/record, instr.DEST_LLM) ...
sample_buggy_agent.py:29: `requests.put` is an unguarded external sink inside `<module>`. ...
The safe internal helper is not flagged.
Tests + project¶
tests/test_fix_suggester.py: 6 green (external POST, LLM create, module-level PUT, safe-internal ignored, fix mentionsredact_payload, narration grounded). Full suite 45 python green.- README updated with the full 6-phase pipeline and how to run it.
The full arc (detect → propagate → decide → redact → record → recover)¶
| phase | does | replaces |
|---|---|---|
| 1 | formal DOE model + SigNoz substrate | (foundation) |
| 2 | runtime taint on OTel (baggage + re-detection) | AgentRaft's Φ (LLM-judged propagation) |
| 3 | Rego policy in the collector | AgentRaft's D_nec 3-LLM committee |
| 4 | reversible context-aware redaction | static field-name masking |
| 5 | tamper-evident Merkle evidence + lineage/SLO dashboards | metrics turned into verifiable Art. 30/15 records |
| 6 | AST-grounded fix-suggester + DLP dry-run | (AgentRaft has no recovery step) |