Repository navigation
π fix(crux): bound agentstep prompt_path and answer file reads - #17362
Conversation
The step service bounds every other caller- or agent-shaped input (inline prompt, schema, signals, request body) but read two workspace files whole into host memory: the run owner's prompt_path file and the codex answer file the sandboxed agent writes. Either could make the crux server allocate without limit from inside a contained run. Both reads now go through readBounded, which stops at MaxPromptFileBytes (16 MiB): an oversize prompt_path is a RequestError naming the bound; an oversize answer file is reported to the backend as a failed read, which codexBackend.Parse already treats as no file and falls back to the last agent_message on stdout. Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of No conflicting open PRs or same-file overlaps were found. |
There was a problem hiding this comment.
Read the diff against v5 plus the callers; this looks correct to me.
readBounded(src/crux/agentstep/agentstep.go:355-367) uses the standardLimitReader(max+1)idiom and returns a sentinel;validate(agentstep.go:175-177) maps it to a*RequestErrorbefore the existingescapes from parentbranch, so the symlink refusal at:180is unchanged.- The answer-file path is safe to fail soft:
codexBackend.Parse(backend.go:293-296) only takes the file whenerr == nil, so an oversize file falls through to the lastagent_messagefrom stdout exactly as a missing file already did.processBackend/claudeBackendignorereadFile, so nothing else observes the new error. rootinrunAttemptis still theos.OpenRoot(host.Workspace)atagentstep.go:277withdefer root.Close(), so the bounded read keeps the symlink containment.- Both tests exercise the bound with sparse files and assert the two different failure shapes (400 vs stdout fallback); the second test's answer-file write lands under
.crux/steps, whichroot.MkdirAllat:282creates before launch.
Body matches the diff. Optional, not blocking: src/docs/design/crux-work-services.md:479 documents prompt as "β€ 1 MiB" and the 400 table at :542 lists the request-error cases β a short note that prompt_path is bounded at 16 MiB would keep that page current.
β hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88
|
Hive status β updated in place; edits do not notify. Full history is in the Activity Monitor. Review findings from Read the base tree (
One docs-currency suggestion, not a blocker: |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Thank you for your contribution! Your PR has been merged. We'd love to hear how your experience was: share feedback |
Security Fix
The step service (
src/crux/agentstep) bounds every other caller- or agent-shaped input β inline prompt, schema, signals, request body β but read two workspace files whole into the crux server's memory: the run owner'sprompt_pathfile and the codex answer file the sandboxed agent writes. From inside a contained run either could make the host allocate without limit, which is the boundary the package'sos.OpenRootwork exists to hold.Both reads now go through one
readBoundedhelper that stops at a new exportedMaxPromptFileBytes(16 MiB β 16Γ the inline bound, so nobody usingprompt_pathfor a large prompt today is affected;MaxPromptBytesis unchanged):prompt_pathis a*RequestErrornaming the bound, the same shape as the inline-prompt refusal;codexBackend.Parsealready treats as "no file" and falls back to the lastagent_messageon stdout β no schema, status or wire change.Tests use sparse files (
os.Truncate) so they exercise the bound without 16 MiB of writes; both fail on the previous code. Changelog fragment undersecurity.Fixes #17361
Filed by sec-check agent (ACMM L6 β full mode)
β hive: agent=sec-check backend=copilot model=claude-fable-5.1 copilot=1.0.88