Skip to content

Bound sandbox subprocess output and long-running service logs #766

Description

@seonghobae

Outcome

Central sandbox verification remains usable and fail-closed when a repository command or E2E service emits excessive output. Parent memory and retained log files are capped before evidence redaction, while a bounded final diagnostic suffix remains available for review.

Problem

sandboxed_verify.py and sandboxed_web_e2e.py currently use subprocess.PIPE/communicate()-style capture for short-lived commands, which can buffer an unbounded stream in memory. Long-running E2E services write to ordinary files without a quota, and tail_text() reads the entire file before selecting the final lines. A malicious or defective repository process can therefore exhaust runner memory or disk before the redaction boundary executes.

Scope

  • add one reusable POSIX bounded-subprocess module that continuously drains stdout/stderr on background threads into fixed-size final-suffix buffers;
  • terminate the isolated process group on the first stream overflow;
  • redirect neither application file writes nor build artifacts through process-wide RLIMIT_FSIZE, because that limit would incorrectly constrain every regular file created by the child;
  • cap each short-lived command stream at an explicit byte budget and return a stable output-limit result;
  • capture long-running backend/frontend combined logs through the same bounded drainer and persist only the bounded rendered suffix;
  • read only a bounded suffix from service evidence files using seek-from-end, then preserve the final line limit and redaction;
  • retain structured argv, shell=False, process-group cleanup, timeouts, exit status, output redaction, evidence notes, and environment scrubbing;
  • keep a dedicated non-POSIX fail-closed contract rather than silently running without isolated process-group termination;
  • add realistic tests with child processes that exceed stdout, stderr, and service-log budgets, plus normal Unicode and partial-codepoint suffix cases;
  • maintain 100% statement/branch coverage and complete production docstrings;
  • document Python subprocess pipe semantics, CWE-770, NIST SSDF, limitations, and rollback using APA 7 references;
  • update CHANGELOG.md.

Acceptance

  • excessive output cannot grow retained per-stream memory or service evidence files beyond the declared budget;
  • the parent continuously drains pipes, avoiding child deadlock on full OS pipe buffers;
  • output-limit termination has stable exit code 123 and bounded redacted evidence;
  • timeout remains 124 and readiness failure remains 125 unless a more specific output limit occurred;
  • ordinary commands and services preserve current behavior and may create normal build/test artifacts larger than the output budget;
  • exact-head central tests, 100% coverage/docstrings, Secret Scan, Semgrep, CodeQL, Python Security, Security Scan, OpenCode, Noema, CodeRabbit, independent approval, and branch protection pass;
  • no reviewer identity, OpenCode/Noema/Strix credential, NVIDIA NIM secret name, or scheduled-agent key scheme changes.

Standards basis

  • Python 3.14 subprocess structured-argument, pipe, file-descriptor, timeout, and process-group semantics;
  • CWE-770 resource allocation without limits or throttling;
  • NIST SP 800-218 SSDF 1.1.

Stack after #764.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions