Skip to content

fix(security): clear 4 real CodeQL alerts (2 HIGH, 2 MEDIUM)#191

Merged
MartinCastroAlvarez merged 1 commit into
mainfrom
fix/codeql-security-alerts-real
May 26, 2026
Merged

fix(security): clear 4 real CodeQL alerts (2 HIGH, 2 MEDIUM)#191
MartinCastroAlvarez merged 1 commit into
mainfrom
fix/codeql-security-alerts-real

Conversation

@MartinCastroAlvarez
Copy link
Copy Markdown
Owner

Role: Author (Security & Compliance Lead, 2026-05-26 PM cycle). Author ≠ Reviewer ≠ Merger.

Addresses the repo owner's "address the security issues" — the 4 real CodeQL alerts (2 HIGH, 2 MEDIUM) the CodeQL workflow (#179) surfaced under Security & Quality.

Alert Sev File Fix
js/polynomial-redos HIGH format.ts:61 stripTags() with bounded [^<>] (linear, no backtracking)
js/incomplete-multi-character-sanitization HIGH format.ts:61 loop-until-stable strip (no residual tag on nested <<b>script>)
py/url-redirection MED views.py _redirect_to_login urlencode({"next": request.get_full_path()})next can't break out of the query param
py/stack-trace-exposure MED api/views/update.py inline malformed-payload branch returns a fixed "Malformed 'inlines' payload." instead of bad_request(str(exc))

Notes on each

  • format.ts — the HTML→text path only produces display text (React escapes it), so it wasn't an exploitable boundary, but the regex was genuinely ReDoS-shaped and incomplete. The replacement is both ReDoS-free and complete.
  • views.py — the redirect target (login_url) was always trusted; only the next query param was user-derived, and Django's login view re-validates it via url_has_allowed_host_and_scheme. Percent-encoding closes the static-analysis finding and is strictly more correct (preserves + encodes the full path).
  • update.py — the inline ValueError messages were author-controlled (no traceback), but echoing str(exc) trips py/stack-trace-exposure; a fixed message is safer and clears it.

Test plan

  • test_spa_index updated to decode the now-encoded next param; 10 pass.
  • test_inlines_write — malformed payload still returns 400 (generic message); 7 pass.
  • pnpm -r typecheck green (format.ts).
  • 39 backend tests across the affected suites pass.

Remaining 5 CodeQL items

All note-severity code-quality (not security) findings in test files: py/unused-import ×1, py/unused-local-variable ×4, py/implicit-string-concatenation-in-list ×1. Addressed in a separate low-risk cleanup PR (this PR is the security-relevant subset).

Tier 5 — touches the login-redirect path + the JSON write surface. Human review welcome.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Addresses the security-relevant code-scanning findings surfaced by
the CodeQL workflow (#179):

- HIGH js/polynomial-redos + js/incomplete-multi-character-sanitization
  (frontend/packages/data/src/format.ts): the HTML→text approximation
  used `replace(/<[^>]*>/g, '')`. Replaced with `stripTags()` — a
  bounded `[^<>]` char class (linear, no ReDoS backtracking) looped
  until stable (no residual tag on nested/overlapping input). This
  path only produces display *text* (React escapes it), but the strip
  is now ReDoS-free and complete.

- MEDIUM py/url-redirection (views.py `_redirect_to_login`): the
  `?next=` value interpolated `request.path` unencoded. Now
  `urlencode({"next": request.get_full_path()})` so a crafted path
  can't break out of the query param. The redirect *target* was
  already trusted; `next` is re-validated by Django's login view.

- MEDIUM py/stack-trace-exposure (api/views/update.py): the inline
  malformed-payload branch returned `bad_request(str(exc))`, echoing
  exception text. Now a fixed generic "Malformed 'inlines' payload."
  message — never the exception string.

Test: updated test_spa_index to decode the now-encoded `next` param;
all spa-index + inline-write tests pass.

Tier 5 — touches views.py login-redirect + the wire surface. The 5
remaining CodeQL items are code-quality NOTES in test files (unused
imports/locals, implicit string concat) — addressed separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MartinCastroAlvarez MartinCastroAlvarez merged commit e705376 into main May 26, 2026
2 checks passed
@MartinCastroAlvarez MartinCastroAlvarez deleted the fix/codeql-security-alerts-real branch May 26, 2026 21:26
MartinCastroAlvarez added a commit that referenced this pull request May 26, 2026
Security-motivated alpha: ships to consumers the fixes merged since
0.2.0a1, notably the CodeQL security clears (#191: ReDoS +
incomplete-sanitization in format.ts, open-redirect in the login
redirect, stack-trace-exposure in the inline write path), the secure
login backend (#168), the inline formset write path (#183), the
get_app_list registry grouping, and the save-flow / delete-preview /
history backends.

350 tests pass. Tier 6 — version bump; publish handled per the
Security deploy-gate under the repo owner's standing "deploy to PyPI
regularly if secure" directive.

Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez added a commit that referenced this pull request May 26, 2026
… js/missing-origin-check) (#208)

The PWA service worker's `message` handler (the `dar:purge`
cache-on-logout hook, #200) processed messages without verifying the
sender origin — CodeQL `js/missing-origin-check` (medium). A
cross-origin frame must never be able to drive the SW cache.

Add `if (event.origin && event.origin !== self.location.origin)
return;` so only same-origin clients (the SPA pages this worker
controls) can trigger a purge. Same-origin internal
`client.postMessage` (empty origin) is still accepted; anything
cross-origin is dropped.

This is the one open CodeQL alert on main (the other 10 are fixed
via #191/#193). Clears it → 0 open. Test asserts the served SW
embeds the origin check.

Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants