Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/workflows/sandbox-log-redaction-quality-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Sandbox Log Redaction Quality CI

on:
pull_request:
branches: [main]
paths:
- ".github/workflows/sandbox-log-redaction-quality-ci.yml"
- "CHANGELOG.md"
- "docs/doctoring/sandbox-log-redaction.md"
- "scripts/ci/redact_sensitive_log.py"
- "scripts/ci/sandboxed_verify.py"
- "scripts/ci/sandboxed_web_e2e.py"
- "tests/test_command_wrapper_redaction.py"
- "tests/test_opencode_security_boundaries.py"
- "tests/test_sandboxed_verify.py"
- "tests/test_sandboxed_web_e2e.py"
- "tests/test_sandboxed_log_redaction_regression.py"

permissions:
contents: read

concurrency:
group: sandbox-log-redaction-quality-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
exact-head-redaction-contract:
if: github.event_name != 'pull_request' || github.event.action != 'closed'
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout exact source revision
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.14"

- name: Install exact hash-verified test dependencies
env:
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PIP_NO_INPUT: "1"
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
cat >"${RUNNER_TEMP}/sandbox-redaction-quality-requirements.txt" <<'EOF'
coverage==7.15.2 --hash=sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f
iniconfig==2.1.0 --hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760
packaging==26.2 --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e
pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
pytest==9.1.1 --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c
EOF
python -m pip install \
--only-binary=:all: \
--require-hashes \
-r "${RUNNER_TEMP}/sandbox-redaction-quality-requirements.txt"

- name: Verify fail-closed sandbox redaction contract
env:
STRIX_TEST_PROCESS_TIMEOUT_SECONDS: "3"
STRIX_TEST_FAKE_SLEEP_SECONDS: "5"
shell: bash --noprofile --norc -e -o pipefail {0}
run: |
test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}"
python -m coverage run --branch -m pytest \
tests/test_command_wrapper_redaction.py \
tests/test_opencode_security_boundaries.py \
tests/test_sandboxed_verify.py \
tests/test_sandboxed_web_e2e.py \
tests/test_sandboxed_log_redaction_regression.py \
-q
python -m coverage report \
--include='scripts/ci/redact_sensitive_log.py,scripts/ci/sandboxed_verify.py,scripts/ci/sandboxed_web_e2e.py' \
--fail-under=100
python - <<'PY'
import ast
from pathlib import Path

missing = []
for filename in (
"scripts/ci/redact_sensitive_log.py",
"scripts/ci/sandboxed_verify.py",
"scripts/ci/sandboxed_web_e2e.py",
):
tree = ast.parse(Path(filename).read_text(encoding="utf-8"), filename=filename)
for node in tree.body:
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
if not node.name.startswith("_") and ast.get_docstring(node) is None:
missing.append(f"{filename}:{node.lineno}:{node.name}")
if missing:
raise SystemExit("public docstrings missing: " + ", ".join(missing))
PY
python -m pytest tests -q
bash scripts/ci/test_strix_quick_gate.sh
python -m compileall -q \
scripts/ci/redact_sensitive_log.py \
scripts/ci/sandboxed_verify.py \
scripts/ci/sandboxed_web_e2e.py \
tests/test_command_wrapper_redaction.py \
tests/test_opencode_security_boundaries.py \
tests/test_sandboxed_verify.py \
tests/test_sandboxed_web_e2e.py \
tests/test_sandboxed_log_redaction_regression.py
git diff --exit-code
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Redacted opaque Docker/Podman login credentials inside bounded GNU `env` split-string and supported shell `-c` wrapper operands with one shared literal matcher, input/token/work budget, and four-level depth boundary; malformed or compound grammar fails closed while valid `--password-stdin` registries, env unset/chdir operands, Docker publish ports, SSH ports, and unrelated `login` arguments remain visible.
- Redacted credential-shaped stdout, stderr, timeout evidence, and bounded backend/frontend service-log tails emitted by sandboxed verification and web-E2E subprocesses before those values become CI or review evidence. The boundary now canonicalizes safe ANSI styling and fails closed on single- or multiline rendering controls, scans JSON keys and opaque string values without corrupting scalar types or result schemas, preserves benign metadata and diagnostic domains, removes authorization headers, URL userinfo, and private-key blocks, protects raw and escaped explicitly allowed values before setup and tail selection, handles separated credential options, preserves markers and line boundaries, falls back safely on excessive JSON nesting, and parses high-volume diagnostics within bounded time. Normal executed argv, child exit, timeout, readiness, and network behavior remains unchanged; ambiguous short or fixed-evidence-colliding allowed values and setup/launch exceptions return redacted code `126` evidence before a raw traceback can escape.
- Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics.
- Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed.
- Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped.
Expand Down
Loading
Loading