fix(cyber): grade the pentest on the breach + make the single-tool loop work with real models#358
Open
larstalian wants to merge 4 commits into
Open
fix(cyber): grade the pentest on the breach + make the single-tool loop work with real models#358larstalian wants to merge 4 commits into
larstalian wants to merge 4 commits into
Conversation
…e tool calls
Two defects made the core run_agent / rLLM scaffold ungradeable for cyber:
1. No submit channel. check_success grades the flag from solver_root/result.json
(the TRL submit tool writes it); the single-tool `finish` recorded the answer
but never wrote it. A *perfect* breach scored success=False, reward capped at
1/3, matched_flag always 0 — zero GRPO gradient on the actual objective.
Fix: add a `submit` capability to the SDK filesystem-runtime surface (the
write-side of the existing _read_result) and route the finish answer to it.
Cyber grading now also accepts a bare-flag submission, not just {"flag": ...}.
2. Native tool calls dropped. parse_action only matched markdown ```bash fences,
so a function-calling model's native <function=bash><parameter=command>... form
fell through to a give-up `finish`, killing rollouts at turn ~2. Fix: parse
that form too; last recognized action across both forms wins.
Verified end to end against NVIDIA Nemotron-3-Nano-30B over the real episode loop:
before, every rollout self-terminated by turn 2; after, the model runs the full
turn budget and submissions reach the grader.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The consequence verifier already decides whether a world is real by whether the guarded flag leaks — and admission guarantees a benign request can't trigger that leak, so leak ⟺ exploit. But the episode grader still gated success on the agent submitting the flag string, so that signal was never wired into the reward: a perfect breach scored 0.333 / success=False (proven against a live 30B). Grade success on the leak (the consequence verifier's own signal). An exact submission still counts, so a submit-tool harness and a pure-shell agent grade identically; `matched_flag` stays as the strict-submission subgoal for eval-mode comparability. This is the XBOW / RL-pentest convention (validate actual exploitation) rather than the CTF flag-submission convention — fitting a training gym, and it means a pure-shell agent needs no submit step at all. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ure shell With success now graded on the breach (the flag crossing the wire), the cyber agent needs no submit step. Remove the `submit` tool from the TRL env, the reference-breach demo, the pool/adaptive rounds, and the Strands any-agent cell — the policy acts with one `shell` tool (CodeAct), and the consequence verifier grades the leak. The reward table collapses to the honest two rungs it now has (0.333 reached -> 1.0 breached); the stale §5 output is cleared for re-run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…utputs Executed the whole notebook under real Docker (CONTAINER backing) on current code and committed the genuine outputs: the §5 two-rung reward (0.333 reached -> 1.000 breached) under the pure-shell agent, a live LLM-generated novel vuln class (`like_wildcard_injection`) the consequence gate admitted, an LLM-authored exploit the same gate verified + re-seeded, a real GRPO step (rewards/reward_func/mean 0.3333 — the honest "0.5B can't breach"), and the pool extending up under a solver / softening under a stuck agent. No cell errors. Serialized in the standard per-line format so unedited cells are untouched vs main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Driving a real model (NVIDIA Nemotron‑3‑Nano‑30B) through the core
run_agent/arun_agentloop surfaced that the single‑tool scaffold couldn't actually be scored for cyber. The headline is the grading change; the rest follows from it.1. Grade on the breach, not a flag submission (the real fix)
The consequence verifier already decides whether a world is real by whether the guarded flag leaks, and admission guarantees a benign request can't trigger that leak — so leak ⟺ exploit. But the episode grader still gated success on the agent submitting the flag string, so that leak signal was never wired into the reward. Proven against the live 30B: a perfect breach (
leaked_secret_ids: ['secret_flag']) scoredreward=0.333 / success=False,matched_flag=0on every rollout — zero GRPO gradient on the objective.WebappPentest.check_successnow grades success on the leak (ok = secret_leaked or matched). An exact submission still counts, so a submit‑tool harness and a pure‑shell agent grade identically;matched_flagstays as the strict‑submission subgoal for eval‑mode comparability.This follows the XBOW / RL‑pentest convention — validate actual exploitation — over the CTF flag‑submission convention (Cybench, NYU CTF, InterCode‑CTF). The latter fits an eval benchmark; OpenRange is a self‑verifying training gym, where effect‑based grading is both standard and more rigorous (the admission gate removes the "incidental discovery" worry that justifies CTF's print≠submit rule). Proof after the change: an agent that runs the exploit and finishes with prose (no flag) now scores
1.0 / success=True— "flag leaked in a response (breach confirmed)."2. Honour native tool calls (else rollouts die at turn ~2)
parse_actiononly matched```bashfences; a function‑calling model emits its native<function=bash><parameter=command>…form, which fell through to the give‑upfinishfallback, killing rollouts at turn ~2 with the model's actions discarded. It now reads that form too; the last recognized action across both forms wins. Fence behaviour is unchanged.3. Drop the cyber submit tool — the agent is pure shell (CodeAct)
With grading on the breach, the cyber agent needs no submit step. The TRL notebook (
examples/trl_grpo_cyber.ipynb) now drops thesubmittool everywhere — the env, the reference‑breach demo, the pool/adaptive rounds, the Strands any‑agent cell — so the policy acts with oneshelltool. The reward table collapses to the honest two rungs it now has (0.333reached →1.0breached).A generic
submitcapability stays on the SDK filesystem‑runtime surface (the write‑side of_read_result), routed fromfinish— it's the secondary/eval path (theor matchedbranch, the rLLMrun_agentloop, SWE‑style "write your answer" tasks), no longer required for cyber. Cyber grading accepts a bare flag, not only{"flag": ...}.Tests
test_agent_harness.py: real exploit + leak passes with no submission; native tool‑call form parsed; last‑action‑wins both orders; bare/JSON/non‑flag/empty submissions;submit()beforereset()rejected.openrange.agentat 100% branch coverage.test_cyber_staged_generation.py: rewritten to assert the breach grades as success without a submission (was: "leak doesn't change the reward").submit, stale §5 output cleared (live cells need Docker to re‑run, per the notebook). All integration, no test doubles. ruff + ruff‑format + mypy(strict) clean..rulesauditRoot‑cause fix (the leak signal existed but was unwired); comments are genuine WHY only; removed a dead
if Nonebranch in favour ofassert(matching the SDK's_read_result); no WHAT/phase markers.SOTA sources
Cybench, NYU CTF Bench, InterCode‑CTF (flag‑submission eval); XBOW (exploitation‑validation); RL‑for‑pentest (effect/state‑based reward).
🤖 Generated with Claude Code