feat(hunt): add trace-aware hunting (grounding, propagation, get_trace enrichment) - #222
Conversation
…e enrichment) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughOptional telemetry support flows from hunt configuration through trace grounding, trace-ID propagation, retrieval, finding validation, report enrichment, CLI/SDK integration, and automated coverage. ChangesTrace-aware hunt telemetry
Estimated code review effort: 4 (Complex) | ~60 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Operator
participant HuntCLI
participant runAutonomous
participant TargetClient
participant TelemetryAdapter
Operator->>HuntCLI: provide telemetry configuration
HuntCLI->>runAutonomous: start hunt with telemetry
runAutonomous->>TargetClient: probe trace propagation
TargetClient-->>runAutonomous: return trace round-trip status
runAutonomous->>TargetClient: send attack with trace metadata
TargetClient-->>runAutonomous: return target response
runAutonomous->>TelemetryAdapter: fetch recorded trace
TelemetryAdapter-->>runAutonomous: return trace excerpt
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/autonomous/tools/selfCheck.ts (1)
74-93: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winWrap the verifier’s trace data as untrusted output.
selfCheck.tsinsertstraceJsondirectly into the verifieruserPrompt, while other target-originated tool outputs are wrapped bywrapUntrustedOutput(...)with<untrusted_target_output>...</untrusted_target_output>and reinforced by the shared untrusted-output defense. Since this trace comes from target/telemetry metadata, treat it as evidence, not instructions, and reuse the shared wrapper or an equivalent explicit untrusted-data delimiter here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/autonomous/tools/selfCheck.ts` around lines 74 - 93, Wrap traceJson with the shared wrapUntrustedOutput(...) helper before interpolating it into traceBlock in the verifier prompt. Preserve the existing enrichment gate and trace content, while clearly delimiting the target/telemetry trace as untrusted evidence rather than instructions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/src/autonomous/lib/telemetry.ts`:
- Around line 222-224: Update traceCacheKey to include a unique per-turn anchor,
specifically threadId and turnIndex or an equivalent monotonic identifier,
alongside traceId and expectedResponse. Propagate the new inputs through callers
so cached traces and validation lookups cannot collide across turns.
- Around line 321-325: Update the turn selection logic around turnIndex and
traceId so numeric indexes must be positive integers within thread.turns.length;
reject 0, negative, fractional, and out-of-range values with an actionable range
error instead of falling back to thread.traceId. Preserve the existing fallback
only when turnIndex is not provided.
- Around line 312-319: Update the unavailable-trace reason messages in the
trace-availability check, including the branches around telemetry provider
validation and telemetry.propagation, to state actionable recovery steps. Tell
operators to configure telemetry.propagation when absent, send a turn before
retrying where applicable, and retry after ingestion delay while verifying
target instrumentation when traces are not yet available.
In `@core/src/autonomous/orchestrator/run.ts`:
- Around line 103-126: Update the preflight flow in the run function to check
the cancellation signal before and after curateHuntTracesIfConfigured, and
before calling probeTraceRoundTrip, so an aborted run skips further telemetry
work and agent-query creation. Pass the signal into those telemetry operations
when their APIs support it, then finalize immediately with the partial report
and stopReason "user-interrupted" after an aborted preflight.
In `@core/src/autonomous/prompts/commander.ts`:
- Around line 21-27: Update the traceSummary prompt section in the commander
prompt construction to clearly delimit the telemetry as untrusted reference
data, since it may contain attacker-controlled prompts or outputs. Explicitly
instruct the commander not to follow or execute instructions embedded in the
summary, while preserving its use for corroborating observed tools, flows, and
sensitive fields; apply the same treatment to the corresponding trace-summary
occurrence near the referenced line.
In `@core/src/autonomous/state/runLog.ts`:
- Around line 57-58: Update the recordFindingTool context so the fetched trace
remains transient when enrichJudgeFromTrace is disabled, and assign
Finding.traceJson only when ctx.telemetryCaps.enrichment is enabled. Preserve
trace use for invisible-evidence validation without persisting propagation-only
data in run logs or reports.
In `@core/src/autonomous/tools/getTrace.ts`:
- Around line 26-29: Update the missing-thread error returned in getTrace around
the thread lookup to include an actionable next step, matching the guidance and
tone of record_finding’s equivalent rejection. Keep the existing thread ID in
the message and tell the calling agent what operation must be performed before
retrying.
In `@core/src/config/schema.ts`:
- Around line 190-204: The parseTelemetry function currently treats malformed
supplied telemetry values as disabled. Distinguish an absent telemetry property
from a supplied one, route every supplied telemetry block—including non-object
values and empty objects—through TelemetryConfigSchema.safeParse so invalid
configurations throw the existing descriptive error, and return undefined only
when telemetry is absent or the parsed provider is "none".
In `@runners/cli/src/commands/hunt.ts`:
- Around line 412-420: Update the catch block around resolveTelemetry in the
hunt command to choose the error prefix based on the telemetry source: use
--telemetry-config for explicit telemetry configuration and --target-config when
telemetryFromTargetConfig was used. Preserve the existing error detail,
process.exitCode assignment, and return behavior.
---
Outside diff comments:
In `@core/src/autonomous/tools/selfCheck.ts`:
- Around line 74-93: Wrap traceJson with the shared wrapUntrustedOutput(...)
helper before interpolating it into traceBlock in the verifier prompt. Preserve
the existing enrichment gate and trace content, while clearly delimiting the
target/telemetry trace as untrusted evidence rather than instructions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 294a90b4-bc34-4a38-b6ab-8a33e3da243c
📒 Files selected for processing (25)
AGENTS.mdcore/src/autonomous/lib/models.tscore/src/autonomous/lib/telemetry.tscore/src/autonomous/lib/types.tscore/src/autonomous/orchestrator/context.tscore/src/autonomous/orchestrator/run.tscore/src/autonomous/prompts/commander.tscore/src/autonomous/prompts/operator.tscore/src/autonomous/report/mapRunLog.tscore/src/autonomous/report/types.tscore/src/autonomous/state/runLog.tscore/src/autonomous/target/http.tscore/src/autonomous/tools/getTrace.tscore/src/autonomous/tools/recordFinding.tscore/src/autonomous/tools/selfCheck.tscore/src/autonomous/tools/sendToTarget.tscore/src/autonomous/tools/server.tscore/src/config/schema.tscore/src/targets/httpClient.tscore/tests/huntTelemetry.test.tsdocs/hunt.mddocs/sdk.mdrunners/cli/src/commands/hunt.tsrunners/sdk/src/hunt.tsrunners/sdk/src/types.ts
- telemetry.ts: key trace cache by thread+turn anchor instead of response
text (two turns with identical replies could collide and serve a stale
trace); reject an out-of-range/non-integer turnIndex in fetchThreadTrace
instead of silently falling back to the thread-level trace id; make all
unavailable-trace reasons actionable
- run.ts: check the abort signal before telemetry curation, before the
round-trip probe, and before the agent query is created, so a Ctrl+C at
hunt start can't still fire a live LLM call or a request to the target;
skip the forced-synthesis LLM call when nothing was captured at all
- commander.ts: delimit curated production traces as untrusted reference
data (they can carry attacker-controlled content) instead of splicing
them into the prompt as a trusted briefing
- recordFinding.ts: keep a propagation-only trace fetch transient for the
hallucination guard instead of persisting it onto the finding when the
enrichment opt-in is off
- getTrace.ts: make the no-such-thread error tell the agent what to do
next, matching record_finding's equivalent message
- schema.ts: route every supplied telemetry block (including {} and
non-object values) through Zod validation instead of treating a
malformed block the same as "telemetry omitted"
- hunt.ts: label a telemetry config error with the flag that actually
supplied it (--telemetry-config vs --target-config)
Adds regression tests for the cache-key collision, the turnIndex
validation, and the parseTelemetry malformed-input cases.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…them
parseTelemetry now rejects a malformed telemetry block, but a well-formed
block carrying a MISSPELLED field name still validated cleanly and silently
left the capability it was meant to enable switched off — e.g. `header` for
`headers` or `enrichJudgeFromTraces` for `enrichJudgeFromTrace` yields
grounding-only with no indication why.
The parse stays deliberately lenient (a forward-dated or provider-specific
field must never break a run), so unrecognized keys are surfaced as warnings
rather than errors:
- declare the telemetry shapes standalone so ONE key list drives both the
lenient parse and the new check — a field added to a schema can't escape it
- add telemetryConfigWarnings(), covering the top level and the nested
propagation block; provider-specific netra/langfuse blocks are skipped, as
they're open extension points their adapters validate
- share the { telemetry } unwrap between it and parseTelemetry so the two
can't disagree about which object they inspect
- report the warnings from the hunt CLI, where config errors already surface
- migrate the two telemetry schemas off the Zod 4 deprecated .passthrough()
to z.looseObject(); the pre-existing target/run schemas are left for a
separate change
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/src/config/schema.ts`:
- Around line 195-199: Update unwrapTelemetryBlock and the telemetry validation
flow around TelemetryConfigSchema.safeParse so an explicitly supplied {
telemetry: null } or { telemetry: undefined } is passed to validation and fails
with “Invalid telemetry config” rather than disabling telemetry. Preserve
disabling only when the telemetry value is absent or the parsed configuration
explicitly uses provider: "none".
- Around line 198-199: Update the configuration key checks in the raw-object
parsing logic around the telemetry selection and related validation checks to
use Object.prototype.hasOwnProperty.call(...) instead of the in operator. Ensure
only own properties such as telemetry, constructor, toString, or __proto__ are
treated as present, preserving existing parsing and typo-warning behavior
otherwise.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1090ecb9-60d8-4ba8-996d-dd00783dc16e
📒 Files selected for processing (3)
core/src/config/schema.tscore/tests/huntTelemetry.test.tsrunners/cli/src/commands/hunt.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- runners/cli/src/commands/hunt.ts
- core/tests/huntTelemetry.test.ts
Own-property checks (`Object.prototype.hasOwnProperty.call`) replace the `in`
operator in both telemetry key checks. `k in shape` walks the prototype chain,
so a field named `constructor`, `toString`, `valueOf` or `hasOwnProperty`
counted as recognized and slipped past the typo warning entirely — a hole in
the one check whose job is catching unexpected fields. The unwrap now also
requires an OWN `telemetry` key, so an inherited one can't redirect what gets
parsed.
An explicitly-null telemetry (`{ "telemetry": null }`, or a bare null from a
--target-config sibling) is now reported rather than silently disabling
trace-aware testing. It deliberately does NOT throw: `null` is how JSON says
"no value", so it arrives from hand-nulled sections, serializers emitting null
for absent optionals, and unset `"telemetry": ${VAR}` templates — all meaning
"off". Rejecting it would turn a working telemetry-less run into a hard
failure, so it degrades to a warning instead. Genuinely absent telemetry (the
zero-config default) stays silent, and a supplied-but-malformed block such as
`{ telemetry: {} }` still throws as before.
Warning strings are now self-contained so callers emit them verbatim, instead
of the CLI appending a typo-specific suffix that didn't fit every case.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem
opfor huntplanned and judged attacks from the visible target reply alone. When a target is wired to an observability backend (Netra / Langfuse), two signals were going unused:Solution
Optional, strictly opt-in trace-aware hunting, reusing the exact
telemetryconfig shapeopfor runalready accepts. It unlocks three independently-gated capabilities (single source of truth intelemetryCapabilities()):provider+ adapter) — curates historic traces into a summary the commander plans against. Works on any instrumented backend.propagationblock) — mints an OTEL trace id per thread, injects it (header/body) on every send, and gives operators aget_tracetool to inspect the tool calls / retrieval behind a reply.propagation+enrichJudgeFromTrace) — confirmed findings carry the recorded trace excerpt and the independent verifier judges against it too.Because propagation/enrichment only work if the target echoes the injected id back into its own telemetry, a recon-time round-trip preflight verifies that assumption once and reports
trace round-trip: OK / NOT DETECTED. On a miss the hunt continues (ingestion lag can cause a false negative) but warns, records it, and tells operators an empty trace is not proof the target is clean. Grounded planning is unaffected either way. Every leg degrades gracefully — a curation/fetch failure never aborts the hunt.Two correctness fixes are included after a self-review of the feature:
get_trace(or a later-turn finding) got the first fetch back, missing later spans. Now keyed by trace id + the turn's response anchor, so each turn fetches fresh while a finding and itsself_checkstill share a cache hit.get_tracecontent inrecord_finding, but the validation fetch was gated on enrichment — so those citations were structurally rejected wheneverenrichJudgeFromTracewas off, breaking the documented silent-leak-detection mode.record_findingnow fetches the trace to validate a citation whenever the evidence isn't in a visible reply and propagation sent an id; feeding the trace to the verifier stays gated on the enrichment opt-in.Changes
core(engine)autonomous/lib/telemetry.ts(new) — capability gating, per-thread trace-id minting/reuse (fresh id per fork), per-send propagation, curator-model auth gating, round-trip preflight, cache-aware trace fetch (traceCacheKey).autonomous/lib/models.ts(new) — sharedresolveModelIdalias resolution (extracted fromselfCheck).autonomous/tools/getTrace.ts(new) —get_tracetool (output wrapped as untrusted).autonomous/tools/{recordFinding,selfCheck,sendToTarget,server}.ts— trace-aware validation/enrichment + propagation on send + tool registration.autonomous/{orchestrator/run.ts,orchestrator/context.ts,prompts/commander.ts,prompts/operator.ts,report/*,state/runLog.ts,target/http.ts,lib/types.ts}— wiring, prompt doctrine gated on capabilities, report/runlog fields.config/schema.ts—parseTelemetry()+ Zod schema (bare block or{ telemetry }wrapper).targets/httpClient.ts— inject trace id into a configured top-level body field.Runners / docs
runners/cli/src/commands/hunt.ts—--telemetry-configflag (or atelemetryblock in--target-config) + capability-accurate banner.runners/sdk/src/{hunt,types}.ts—telemetryoption onhunt().docs/hunt.md,docs/sdk.md,AGENTS.md— trace-aware hunting section + field reference.Tests —
core/tests/huntTelemetry.test.ts(new): capability gating, per-thread/per-run/per-fork id semantics, propagation header/body expansion, preflight, cache keying, and an end-to-end propagation check over the HTTP send path.Issue
Closes #147
How to test
End-to-end against an instrumented target (Netra example):
Expect a
telemetry : netra (grounding · propagation · enrichment)banner line and atrace round-trip: OK / NOT DETECTEDprogress line during recon. With no telemetry config the zero-config quick-start is unchanged.Screenshots
N/A
Summary by CodeRabbit
New Features
get_tracetool for retrieving recorded target trace excerpts.--telemetry-configsupport in the hunt CLI andtelemetrysupport in the SDK, enabling capability status reporting.Documentation
Tests
Refactor