Skip to content

fix(security): neutralize CWE-117 log forging in StructuredFormatter - #1263

Closed
groupthinking wants to merge 1 commit into
mainfrom
claude/determined-maxwell-6q4s34
Closed

fix(security): neutralize CWE-117 log forging in StructuredFormatter#1263
groupthinking wants to merge 1 commit into
mainfrom
claude/determined-maxwell-6q4s34

Conversation

@groupthinking

@groupthinking groupthinking commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Canonical issue

Progresses #898 (CWE-117 log-injection hardening). Addresses the confirmed, previously-unmitigated exc_info finding raised on #810 — this is the central, formatter-level remediation that complements #810's per-sink message sanitization (not competing: #810 scrubs individual router.py sinks; this scrubs the final rendered record for every logger).

De-duplicated (2026-08-02): an earlier revision of this PR also carried the code_generator.py info-disclosure fix. That fix is owned by the canonical Sentinel PR #1259 — I force-pushed to drop those commits, so this PR is now scoped solely to the StructuredFormatter CWE-117 fix and does not compete with #1259.

Outcome

A user-controlled value containing \r\n (or other line separators) can no longer forge or corrupt a log line — not via the message, not via an exc_info traceback, and not via structured extra fields. One logging call renders as exactly one physical line.

Scope

Risk

  • Risk level: low
  • Failure mode: multi-line tracebacks now render on a single physical line (newlines escaped to \n). Content is fully preserved and greppable; separators are escaped, not dropped.
  • Rollback: revert the logging_config.py hunk; formatter reverts to prior passthrough behavior.

Verification

  • Focused tests — tests/unit/test_logging_config_crlf.py: 7 passed on head d88875d (message forging, exc_info traceback forging, and 5 Unicode/control separators), asserted against rendered handler output.
  • Required CI — pending on head d88875d.
  • Review threads resolved — none open.

Standalone evidence:

MESSAGE  lines=1  forged-standalone=False  content-kept=True
EXC_INFO forged-standalone=False  has_CR=False  traceback-present=True

Production evidence

Not applicable to runtime behavior: Python-only logging-format change. Vercel builds the Next.js apps/web root and does not exercise this path (exact-head preview was Ready on the prior revision).

Agent handoff

Unattended-run disclosure: produced by a scheduled, non-interactive remediation pass. Not authorized to merge to protected main autonomously, and the agent-completion/truth-gate needs provenance an unattended run can't supply. Expected terminal state: HALTED(awaiting_owner) — code complete and verified; only sign-off/merge remain.

Agent provenance

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, v0 Aug 2, 2026 6:31pm

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • [‘architecture-gap’, ‘bug’, ‘ci-cd’, ‘ci/cd’, ‘copilot-rabbit’, ‘documentation’, ‘duplicate’, ‘enhancement’, ‘frontend’, ‘github_actions’, ‘good first issue’, ‘help wanted’, ‘high-priority’, ‘invalid’, ‘javascript’, ‘ml-model’, ‘needs-triage’, ‘pipeline-critical’, ‘placeholder-code’, ‘priority:high’, ‘python’, ‘python:uv’, ‘question’, ‘styling’, ‘tests’, ‘v0’]

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d85a2f3d-0816-44f1-9135-43d790589dbc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Owner Author

CI triage (head 689740a)

Sorting the red checks by whether they're actionable here:

Check Cause Disposition
label PR had no qualifying label Fixed — added python, security, tests (accurate to the diff); re-triggers the gate
validate / Canonical issue and evidence GitHub API rate-limit 403 (x-ratelimit-remaining: 0, resets ~18:32 UTC) — transient infra, not the diff Clears on re-run after reset
dependency-review Repo-level: "Dependency review is not supported… ensure Dependency graph is enabled" — fails on every PR, and this diff changes no dependencies Not fixable in-PR; needs repo Settings → Security
agent-completion/truth-gate, Agent completion enforcement Provenance gate needs a frozen pre-dispatch intent + trusted terminal agent result Human/owner gate — see below

Code status is green: focused suite tests/unit/test_logging_config_crlf.py = 7 passed, and the exact-head Vercel preview is Ready. The CWE-117 fix and the two Sentinel code-gen fixes are complete and verified.

Genuine blocker (unattended-run honest disclosure): this was produced by a scheduled, non-interactive pass. I'm not authorized to merge to protected main autonomously, and the agent-completion/truth-gate requires provenance an unattended run can't supply. So this PR is HALTED(awaiting_owner) — the remaining work is owner sign-off + merge, not more code.

Staged next command for the owner once approved:

gh pr merge 1263 --repo groupthinking/EventRelay --squash

Generated by Claude Code

Scrub CR/LF and other line separators (VT, FF, NEL, U+2028, U+2029) from the
fully rendered log record in StructuredFormatter.format(). Because the scrub
runs on the final formatted string, it covers the message, exc_info traceback
text (including str(exc)), and structured `extra` fields at once — closing the
log-forging vector that inline message sanitizers miss at exc_info /
logger.exception sinks.

Separators are escaped rather than dropped, so content stays greppable while
one logging call is guaranteed to render as one physical line.

Adds tests/unit/test_logging_config_crlf.py asserting the *rendered* handler
output (not a sanitizer return value) cannot be used to forge a standalone log
line via message, exc_info traceback, or Unicode line separators.

Addresses the confirmed unmitigated finding on #810 (CWE-117); this is the
central formatter-level remediation that complements #810's per-sink message
sanitization. Progresses #898.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDtvQR6b5JXkAStGbTCTxL
@groupthinking
groupthinking force-pushed the claude/determined-maxwell-6q4s34 branch from 689740a to d88875d Compare August 2, 2026 18:29
@groupthinking groupthinking changed the title fix(security): central CWE-117 log-forging fix in StructuredFormatter + Sentinel code-gen hardening fix(security): neutralize CWE-117 log forging in StructuredFormatter Aug 2, 2026

Copy link
Copy Markdown
Owner Author

Update — de-duplicated against #1259 (head now d88875d)

A pull_request.synchronize on #1259 surfaced that this PR's two Sentinel commits were a duplicate of #1259's canonical code_generator.py info-disclosure fix (identical diff). I force-pushed to drop those commits, so #1263 is now scoped to a single concern — the StructuredFormatter CWE-117 fix — and no longer competes with #1259.

  • git diff vs main: only logging_config.py (+ new test_logging_config_crlf.py).
  • Focused tests still 7 passed on d88875d.

CI on the new head is red for the same reasons already triaged above — unchanged by this rework:

  • validate / Canonical issue and evidence → GitHub App API rate-limit 403 (transient, installation-wide).
  • dependency-review → repo has Dependency Graph disabled (fails on every PR; this diff changes no deps).
  • agent-completion/truth-gate, Agent completion enforcementprovenance gate needing a human/owner.
  • CodeRabbit "review skipped: required labels" is stalepython, security, tests are applied and are in its required set.

Terminal state unchanged: HALTED(awaiting_owner) — code complete and verified; only owner sign-off + merge remain. Staged merge command:

gh pr merge 1263 --repo groupthinking/EventRelay --squash

Generated by Claude Code

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Agent Completion Truth Gate: BLOCKED

Reasons: invalid_payload

Machine-readable verdict
{
  "details": {
    "invalid_fields": [
      "policy.agent_login",
      "policy.run_id"
    ]
  },
  "reasons": [
    "invalid_payload"
  ],
  "verdict": "blocked"
}

Workflow evidence

Copy link
Copy Markdown
Owner Author

Truth-gate invalid_payload — root cause confirmed (not a fixable payload bug)

The gate's verdict names invalid_fields: ["policy.agent_login", "policy.run_id"]. I traced this to scripts/ci/agent_completion_gate.py:45-48, which requires both to be non-empty strings.

Key point: these fields are not sourced from the PR body's agent-lock-manifest. My declared values (agent_login: claude, run_id: session_…) are non-empty and would pass that string check. They come through empty because the enforcement harness derives them from a trusted-provenance lookup — a verified agent-lock event cross-checked against the repo's trusted_check_app_slugs / trusted_label_actors. This PR was opened by an unattended scheduled session with no registered trusted-agent identity, so the lookup yields nothing → invalid_payload.

Therefore this is not a fixable formatting issue. Making policy.agent_login/run_id "valid" would require asserting a trusted-agent identity/run this session doesn't hold — i.e. impersonating the gate, which I will not do (per this repo's own guidance on #810: "do not weaken or impersonate the gate").

Disposition is final for an autonomous run: HALTED(awaiting_owner). The code is complete and verified (single-file CWE-117 fix, 7/7 focused tests, Vercel preview Ready, de-duplicated against #1259). What remains is an owner action the gate is specifically designed to require — dispatch/approve this through a trusted agent lane, or human-review and merge:

gh pr merge 1263 --repo groupthinking/EventRelay --squash

I've stopped echoing the repeating CI webhooks at head d88875d; they're all the same infra/provenance blockers documented above.


Generated by Claude Code

@groupthinking

Copy link
Copy Markdown
Owner Author

Closing as strictly dominated — no unique coverage.

Empirically compared against the other five PRs in this CWE-117 cluster by pushing every control/separator codepoint through StructuredFormatter.format() at each PR head. This PR neutralizes {LF, CR, VT, FF, NEL, LS, PS}, which is a strict subset of what #1255, #1265, #1266 and #1270 each already neutralize. There is no codepoint this PR covers that another does not.

Full comparison matrix and the consolidation plan: #1270 (comment).

Reopen if I have missed a non-coverage reason to keep this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants