feat(taint): stage 1 of argument provenance — the tap records hosts fetched content named
The "derived from untrusted content" half of ActGov's invariant (no outbound action whose target came from untrusted content). Observed only; no rule reads it yet. - the tap runs a node extractor only when a payload could be a fetch (WebFetch, WebSearch, curl/wget in command position) and appends the response's URL hosts, minus the agent's own target, to untrusted-hosts.txt beside the tap — a path hook-files already protects - capped at 500, deduplicated, and the tap still always exits 0 - both tiers drain it per finished phase into a taint.hosts event - shell-tested against the generated hook with the real node; the test caught `grep -r curl docs` being read as a fetch Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
509b7ceb89
commit
597e76b261
@@ -682,6 +682,21 @@ async fn drain_finished_container_phases(pool: &PgPool) -> Result<(), String> {
|
||||
)
|
||||
.await;
|
||||
}
|
||||
// What fetched content named. Observed, not enforced — see
|
||||
// docs/TASK-PERMISSION-AND-TAINT.md, piece 2, stage 1.
|
||||
let hosts = crate::container_tool_hooks::drain_taint(&docker, &container).await;
|
||||
if !hosts.is_empty() {
|
||||
crate::mission_events::record(
|
||||
pool,
|
||||
crate::mission_events::MissionEvent::new(
|
||||
mission_id,
|
||||
crate::container_tool_hooks::TAINT_HOSTS,
|
||||
)
|
||||
.phase(phase_id)
|
||||
.detail(crate::container_tool_hooks::taint_detail(&hosts, "container")),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let tools = crate::container_tool_hooks::drain(&docker, &container).await;
|
||||
if tools.is_empty() {
|
||||
continue;
|
||||
@@ -1854,6 +1869,19 @@ async fn launch_microvm_phase(
|
||||
// vocabulary: `gate.inert` when it gave up parsing and allowed
|
||||
// calls unchecked, `gate.denied` per call it refused. The guest
|
||||
// wrote both files from day one; this is the first reader.
|
||||
if !o.taint_hosts.is_empty() {
|
||||
crate::mission_events::record(
|
||||
&pool2,
|
||||
crate::mission_events::MissionEvent::new(
|
||||
mission_id,
|
||||
crate::container_tool_hooks::TAINT_HOSTS,
|
||||
)
|
||||
.phase(phase_id)
|
||||
.run(run_id)
|
||||
.detail(crate::container_tool_hooks::taint_detail(&o.taint_hosts, "microvm")),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if let Some(g) = &o.tool_gate {
|
||||
if g.inert > 0 {
|
||||
crate::mission_events::record(
|
||||
|
||||
Reference in New Issue
Block a user