fix(platformctl): extend apply output redaction #637

Merged
pdurlej merged 1 commit from codex/m06-extend-apply-redaction into main 2026-05-30 14:17:45 +02:00
Collaborator

Canary status: missing — fire canary 3+3 manually before merge

Canary Context Pack

Product story

Apply output is consumed by operators and agents after remote execution. It must preserve enough diagnostics to debug while never exposing common secret spellings from remote stdout/stderr.

What changed

Extended _redact_remote_output to redact additional secret-looking fields: api_key, API-key, passwd, JSON secret/token fields, and URL query token parameters.

Why it changed

Issue #200 follows the redaction-first M06 chain. #199 removed raw remote commands from emitted apply results; this PR tightens stdout/stderr redaction before provenance/adversarial work builds on it.

Files touched

  • control-plane/platformctl/apply.py
  • control-plane/platformctl/tests/test_apply_phase3.py

Relevant context

  • Issue #200: REDACT-EXTEND-01
  • Issue #194 grind order: redaction before provenance/adversarial matrix

Runtime evidence

No runtime mutation. Local validation only.

Known constraints

Remote transport still receives raw process output from the actual command; this PR only changes persisted/emitted apply output redaction.

Explicit out-of-scope

  • No command scrub changes (#206 next)
  • No provenance/adversarial matrix changes (#192/#193/#194 later)
  • No runtime apply or service restart

Requested decision

Approve merge if tests and Patchwarden checks are green.

Merge blockers

  • Secret-like values appear in emitted apply result/status artifacts
  • Existing apply tests regress

Spec sources read

  • Forgejo issue #200
  • docs/forgejo-agent-operations.md
  • control-plane/platformctl/apply.py
  • control-plane/platformctl/tests/test_apply_phase3.py
  • control-plane/platformctl/tests/test_apply_env_file.py

Validation

  • PYTHONPATH=control-plane control-plane/.venv/bin/python -m pytest control-plane/platformctl/tests/test_apply_phase3.py control-plane/platformctl/tests/test_apply_env_file.py — 67 passed
  • PYTHONPATH=control-plane control-plane/.venv/bin/python -m platformctl.cli validate all --json — passed

Closes #200

Canary status: missing — fire canary 3+3 manually before merge ## Canary Context Pack ### Product story Apply output is consumed by operators and agents after remote execution. It must preserve enough diagnostics to debug while never exposing common secret spellings from remote stdout/stderr. ### What changed Extended `_redact_remote_output` to redact additional secret-looking fields: `api_key`, `API-key`, `passwd`, JSON secret/token fields, and URL query token parameters. ### Why it changed Issue #200 follows the redaction-first M06 chain. #199 removed raw remote commands from emitted apply results; this PR tightens stdout/stderr redaction before provenance/adversarial work builds on it. ### Files touched - `control-plane/platformctl/apply.py` - `control-plane/platformctl/tests/test_apply_phase3.py` ### Relevant context - Issue #200: REDACT-EXTEND-01 - Issue #194 grind order: redaction before provenance/adversarial matrix ### Runtime evidence No runtime mutation. Local validation only. ### Known constraints Remote transport still receives raw process output from the actual command; this PR only changes persisted/emitted apply output redaction. ### Explicit out-of-scope - No command scrub changes (#206 next) - No provenance/adversarial matrix changes (#192/#193/#194 later) - No runtime apply or service restart ### Requested decision Approve merge if tests and Patchwarden checks are green. ### Merge blockers - Secret-like values appear in emitted apply result/status artifacts - Existing apply tests regress ## Spec sources read - Forgejo issue #200 - `docs/forgejo-agent-operations.md` - `control-plane/platformctl/apply.py` - `control-plane/platformctl/tests/test_apply_phase3.py` - `control-plane/platformctl/tests/test_apply_env_file.py` ## Validation - `PYTHONPATH=control-plane control-plane/.venv/bin/python -m pytest control-plane/platformctl/tests/test_apply_phase3.py control-plane/platformctl/tests/test_apply_env_file.py` — 67 passed - `PYTHONPATH=control-plane control-plane/.venv/bin/python -m platformctl.cli validate all --json` — passed Closes #200
fix(platformctl): extend apply output redaction
All checks were successful
patchwarden-client-dry-run / collect-diff (pull_request) Successful in 5s
platformctl plan / auto-apply scope (pull_request) Successful in 23s
pyfallow / Pyfallow gate (control-plane) (pull_request) Successful in 21s
python-ci / Python 3.12 (pull_request) Successful in 46s
patchwarden-client-dry-run / dry-run (pull_request) Successful in 23s
canary-required / collect-diff (pull_request) Successful in 5s
python-ci / Python 3.11 (pull_request) Successful in 44s
python-ci / Python 3.13 (pull_request) Successful in 46s
base-is-main / guard (pull_request) Successful in 1s
canary-required / canary (pull_request) Successful in 15s
patchwarden-pr-sanity / sanity (pull_request) Successful in 3m49s
patchwarden-pr-sanity / collect-diff (pull_request) Successful in 6s
51c9b7e6b1
Author
Collaborator

Patchwarden PR sanity

  • Status: advisory_findings
  • PR: 637
  • Commit: 51c9b7e6b19d5615dbf4befe5fa2ac7d79fe4035
  • Security-sensitive label: present
  • Authority: advisory model review plus deterministic blockers only
  • 3+3 canary: still alive; this does not replace it

Deterministic findings

  • info sensitive-path-touched Sensitive path touched — control-plane/platformctl/apply.py
    • Evidence: control-plane/platformctl/apply.py
    • Next: Route through the existing 3+3/risk-tier process; model review remains advisory.

Model reviewers

global-glm / glm-5.1:cloud

  • Status: ok

  • Verdict: OK

  • medium Nested JSON objects not properly redacted

    • Evidence: In control-plane/platformctl/apply.py, the JSON regex rf'(?i)("{_REMOTE_SECRET_FIELD_RE}"\s*:\s*)("(?:[^"\\]|\\.)*"|[^,\s}}]+)' matches either quoted strings or unquoted values, but not nested objects. For input like {"secret":{"key":"value
    • Next: Consider adding a fallback regex that matches any value after a secret field name (including nested objects), or document this limitation explicitly. A greedy match like [^}]+ or [^,}]+ after the colon could catch nested structures.
  • medium Test coverage gaps for edge cases

    • Evidence: In control-plane/platformctl/tests/test_apply_phase3.py, the parametrized test cases cover simple key=value pairs, quoted JSON strings, and URL parameters, but not nested JSON objects, values with spaces (e.g., password=hunter 2 would becom
    • Next: Add test cases for: nested JSON objects like {"secret":{"nested":"value"}}, values with spaces like password=hunter 2, and unquoted JSON values like {"secret":123} to validate redaction behavior comprehensively.

global-deepseek / deepseek-v4-pro:cloud

  • Status: ok

  • Verdict: NOT_OK

  • blocker General key-value redaction pattern leaks partial secrets due to restrictive value character class

    • Evidence: In control-plane/platformctl/apply.py, the new general redaction regex [^\s&#,}}]+replaces the previous\S+. This stops at &, #, ,, }, causing incomplete redaction for values containing these characters. For example, token=a`
    • Next: Change the value-matching part of the general pattern to be at least as inclusive as the original \S+, or define a safe boundary that does not leak partial secrets. Add test cases for values containing &, #, ,, }, and other common delimiters.
  • medium JSON redaction does not handle nested objects or arrays as secret values

    • Evidence: In control-plane/platformctl/apply.py, the JSON redaction regex only matches simple scalar values (strings, numbers, booleans, null). If a secret field contains a nested object or array, e.g., {"token": {"nested": "secret"}}, the value
    • Next: Either extend the JSON pattern to redact the entire value when it is an object or array (e.g., by matching balanced braces), or document this limitation explicitly and ensure no such structured secrets appear in remote output.

redteam / kimi-k2.6:cloud

  • Status: ok

  • Verdict: NOT_OK

  • high Redaction bypass for prefixed password/api_key/passwd/authorization fields

    • Evidence: control-plane/platformctl/apply.py introduces _REMOTE_SECRET_FIELD_RE with a negative lookbehind (?<![A-Za-z0-9_-]) on the key-value regex, but only the token/secret branch allows a [A-Za-z0-9]+[_-] prefix. The password, passwd, authorizati
    • Next: Apply the prefix pattern (?:[A-Za-z0-9]+[_-])? uniformly to all keywords in _REMOTE_SECRET_FIELD_RE, or replace the lookbehind with a boundary assertion that does not exclude common separators.

Policy notes

  • GLM 5.1 + DeepSeek V4 Pro are the operator-required model mix for this bot.
  • Optional red-team model is enabled only when PLATFORMCTL_PR_SANITY_REDTEAM_MODEL is configured.
  • Auto-merge is not enabled here.
<!-- patchwarden-pr-sanity:pdurlej/platform:PR-637 --> # Patchwarden PR sanity - Status: `advisory_findings` - PR: `637` - Commit: `51c9b7e6b19d5615dbf4befe5fa2ac7d79fe4035` - Security-sensitive label: `present` - Authority: advisory model review plus deterministic blockers only - 3+3 canary: still alive; this does not replace it ## Deterministic findings - **`info` `sensitive-path-touched`** Sensitive path touched — `control-plane/platformctl/apply.py` - Evidence: `control-plane/platformctl/apply.py` - Next: Route through the existing 3+3/risk-tier process; model review remains advisory. ## Model reviewers ### `global-glm` / `glm-5.1:cloud` - Status: `ok` - Verdict: `OK` - **`medium`** Nested JSON objects not properly redacted - Evidence: `In control-plane/platformctl/apply.py, the JSON regex rf'(?i)("{_REMOTE_SECRET_FIELD_RE}"\s*:\s*)("(?:[^"\\]|\\.)*"|[^,\s}}]+)' matches either quoted strings or unquoted values, but not nested objects. For input like {"secret":{"key":"value` - Next: Consider adding a fallback regex that matches any value after a secret field name (including nested objects), or document this limitation explicitly. A greedy match like [^}]+ or [^,}]+ after the colon could catch nested structures. - **`medium`** Test coverage gaps for edge cases - Evidence: `In control-plane/platformctl/tests/test_apply_phase3.py, the parametrized test cases cover simple key=value pairs, quoted JSON strings, and URL parameters, but not nested JSON objects, values with spaces (e.g., password=hunter 2 would becom` - Next: Add test cases for: nested JSON objects like {"secret":{"nested":"value"}}, values with spaces like password=hunter 2, and unquoted JSON values like {"secret":123} to validate redaction behavior comprehensively. ### `global-deepseek` / `deepseek-v4-pro:cloud` - Status: `ok` - Verdict: `NOT_OK` - **`blocker`** General key-value redaction pattern leaks partial secrets due to restrictive value character class - Evidence: `In `control-plane/platformctl/apply.py`, the new general redaction regex `[^\s&#,}}]+` replaces the previous `\S+`. This stops at `&`, `#`, `,`, `}`, causing incomplete redaction for values containing these characters. For example, `token=a` - Next: Change the value-matching part of the general pattern to be at least as inclusive as the original `\S+`, or define a safe boundary that does not leak partial secrets. Add test cases for values containing `&`, `#`, `,`, `}`, and other common delimiters. - **`medium`** JSON redaction does not handle nested objects or arrays as secret values - Evidence: `In `control-plane/platformctl/apply.py`, the JSON redaction regex only matches simple scalar values (strings, numbers, booleans, null). If a secret field contains a nested object or array, e.g., `{"token": {"nested": "secret"}}`, the value ` - Next: Either extend the JSON pattern to redact the entire value when it is an object or array (e.g., by matching balanced braces), or document this limitation explicitly and ensure no such structured secrets appear in remote output. ### `redteam` / `kimi-k2.6:cloud` - Status: `ok` - Verdict: `NOT_OK` - **`high`** Redaction bypass for prefixed password/api_key/passwd/authorization fields - Evidence: `control-plane/platformctl/apply.py introduces _REMOTE_SECRET_FIELD_RE with a negative lookbehind (?<![A-Za-z0-9_-]) on the key-value regex, but only the token/secret branch allows a [A-Za-z0-9]+[_-] prefix. The password, passwd, authorizati` - Next: Apply the prefix pattern (?:[A-Za-z0-9]+[_-])? uniformly to all keywords in _REMOTE_SECRET_FIELD_RE, or replace the lookbehind with a boundary assertion that does not exclude common separators. ## Policy notes - GLM 5.1 + DeepSeek V4 Pro are the operator-required model mix for this bot. - Optional red-team model is enabled only when `PLATFORMCTL_PR_SANITY_REDTEAM_MODEL` is configured. - Auto-merge is not enabled here.
pdurlej approved these changes 2026-05-30 14:17:44 +02:00
pdurlej left a comment

Owner-authorized admin approval after green checks. Scope: #200 extends apply output redaction patterns; no runtime mutation and no secrets printed.

Owner-authorized admin approval after green checks. Scope: #200 extends apply output redaction patterns; no runtime mutation and no secrets printed.
pdurlej referenced this pull request from a commit 2026-05-30 14:17:45 +02:00
Sign in to join this conversation.
No reviewers
No labels
W6d-automerge-calibration
agent/claude-code
agent/codex
agent/hermes
agent/iskra
agent/ollama
agent/patchwarden
automerge-candidate
class/security-sensitive
cutover-gate
dependency/blocked
dependency/blocks-others
dependency/cross-repo
dependency/needs-confirmation
domain:agents
domain:ci
domain:docs
domain:forgejo
domain:infra
domain:memory
domain:runtime
domain:signal
domain:ux
flow/architecture
flow/blocked
flow/deployed
flow/done
flow/implementation
flow/intake
flow/maintained
flow/observed
flow/ready
flow/refining
flow/retired
flow/review
iterating
judge/codex-candidate
judge/hermes-candidate
judge/low-confidence
judge/needs-refinement
judge/operator-needed
judge/p0
judge/p1
judge/p2
judge/p3
judge/park
judge/patchwarden-candidate
judge/stale-priority
kind/adr
kind/bug
kind/chore
kind/feature
kind/infra
kind/ops
kind/refactor
kind/research
large-impact
merge/auto
merge/manual
merge/manual-dependency-conflict
merge/manual-failing-tests
merge/manual-merge-conflict
merge/manual-missing-review
merge/manual-operator-preference
merge/manual-red-zone
merge/manual-security-sensitive
merge/manual-unclear-scope
merge/manual-unknown
meta
mode:operator-only
mode:patchwarden-iskra-approved
mode:safe-auto
needs-operator-decision
needs-triage
not-ready
observed/erroring
observed/needs-followup
observed/pending
observed/retire-candidate
observed/unused
observed/used
operator-emotional
owner-attention
phase/02
phase/03
priority:p0
priority:p1
priority:p2
priority:p3
proposed
ready-for-agent
ready-for-operator
recovery
review:claude-reviewed
review:codex-reviewed
review:dziadek-reviewed
review:needs-human
risk/exposure
risk/process
risk/product
risk/runtime
safety:external-write
safety:no-prod-mutation
safety:prod-impact
safety:secret-touch
size/large
size/medium
size/small
size/tiny
size/unknown
source/adr
source/agent-generated
source/manual
source/operator-chat
source/voice-note
status:blocked
status:codex-ready
status:merged:pending-evidence
status:needs-evidence
status:operator-needed
status:parked
tier/full
tier/lite
tier/stacked
tier:0-platform-substrate
tier:1-iskra-value-layer
tier:2-tools-products-modules
type:bug
type:chore
type:docs
type:feat
type:policy
type:research
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pdurlej/platform!637
No description provided.