Skip to content

fix(security): address open CodeQL code-scanning alerts#1152

Merged
Aaronontheweb merged 2 commits into
netclaw-dev:devfrom
Aaronontheweb:claude-wt-sec-fixes
May 22, 2026
Merged

fix(security): address open CodeQL code-scanning alerts#1152
Aaronontheweb merged 2 commits into
netclaw-dev:devfrom
Aaronontheweb:claude-wt-sec-fixes

Conversation

@Aaronontheweb
Copy link
Copy Markdown
Collaborator

Summary

Triaged every open CodeQL alert on the repo and addressed each as a real fix or a documented dismissal. Two commits:

  • fix(security): address open CodeQL alerts — the primary fixes.
  • fix(security): tighten log/telemetry sanitization from review — follow-ups from a post-fix code review.

Real fixes (CodeQL should auto-resolve these as fixed on the next scan)

  • Daemon management: Windows cross-platform support #21 actions/missing-workflow-permissions — added permissions: contents: read to .github/workflows/website-rebuild.yml. The job only dispatches an external workflow via a fine-grained PAT, so the workflow's own GITHUB_TOKEN needs nothing.
  • Daemon + thin client architecture #22 cs/log-forging in WebhookEndpointRouteBuilderExtensions.cs — the URL-path-decoded route value is now sanitized via the existing SanitizeWebhookId allowlist before being passed to both the logger.LogWarning call and WebhookTelemetry.RecordRouteNotFound. (The second call site was caught in review — the metric tag was the same log-forging surface and additionally an unbounded high-cardinality vector.)
  • Bump docfx from 2.78.3 to 2.78.5 #18 cs/log-forging in DaemonLifecycleNotifier.cs — the HTTP-tainted reason (from ShutdownDaemonRequest.Reason) is now run through a new SanitizeReason helper before being logged or copied into alert context/summary. The helper:
    • Strips characters that act as line terminators downstream: anything char.IsControl matches (Cc category, including CR/LF/NUL/ESC), plus U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR (Zl/Zp — missed by IsControl but split-on by JSON-line readers and many log shippers).
    • Drops sanitized chars rather than replacing them with space, so reason=ok\nlevel=critical becomes reason=oklevel=critical instead of an attacker-friendly reason=ok level=critical.
    • Caps at 200 chars, backing off one position if the cut would orphan a high surrogate.
    • Symmetric application in NotifyCrashing even though current callers pass code-side constants — closes off future regressions.
  • TUI polish: paste debounce, status bar, crash logging, tool timers #17 cs/path-injection in ReminderDefinitionStore.csGetPath already used Uri.EscapeDataString (which neutralizes path separators), but CodeQL doesn't recognize that as a sanitizer. Added an explicit base-directory containment check on the canonicalized path; throws ArgumentException if anything ever escapes. Belt-and-suspenders + makes the scanner happy. New [Theory] regression test covers ../../etc/passwd, backslash traversal, and absolute paths.

Dismissed as false positives (with per-alert explanatory comments)

Test plan

- workflows: add `permissions: contents: read` to website-rebuild.yml
  so the rebuild job no longer inherits the repo's default GITHUB_TOKEN
  scope (cs-actions/missing-workflow-permissions, alert netclaw-dev#21).
- webhooks: sanitize the raw route value via SanitizeWebhookId before
  logging the route_not_found case — ASP.NET URL-decodes path segments,
  so a caller could otherwise inject CR/LF into log lines
  (cs/log-forging, alert netclaw-dev#22).
- lifecycle: add a SanitizeReason helper to DaemonLifecycleNotifier that
  strips control chars and caps length at 200, and apply it in
  NotifyShutdown (HTTP-tainted via ShutdownDaemonRequest.Reason) and
  NotifyCrashing (cs/log-forging, alert netclaw-dev#18).
- reminders: harden ReminderDefinitionStore.GetPath with an explicit
  base-directory containment check on top of the existing
  Uri.EscapeDataString encoding, and add a regression test covering
  traversal, backslash, and absolute-path ids (cs/path-injection,
  alert netclaw-dev#17).
Follow-ups from the post-CodeQL code review:

- webhooks: sanitize `route` once and pass the safe value to both
  WebhookTelemetry.RecordRouteNotFound and the log line, not just the log.
  The metric tag was the same log-forging surface and additionally an
  unbounded high-cardinality vector against the `route` dimension.
- lifecycle: widen the SanitizeReason predicate from `char.IsControl` to
  also strip U+2028 (LINE SEPARATOR) and U+2029 (PARAGRAPH SEPARATOR);
  these are categories Zl/Zp (not Cc) so IsControl misses them, but JSON-
  line readers and many log shippers still split on them.
- lifecycle: strip sanitized chars rather than replacing them with space,
  so an attacker-controlled `reason=ok\nlevel=critical` collapses to
  `reason=oklevel=critical` instead of `reason=ok level=critical` — a
  space would have been a plausible field separator to key=value log
  parsers.
- lifecycle: when truncating reason at 200 chars, back off one position
  if the cut would orphan a high surrogate, so downstream UTF-8 encoders
  don't emit U+FFFD or throw.
- tests: cover the four behaviors above with a Theory across CR/LF, NUL,
  U+2028, U+2029, and a surrogate-pair-boundary truncation case.
@Aaronontheweb Aaronontheweb merged commit 7d5bf23 into netclaw-dev:dev May 22, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant