Skip to content

codeql: close js/xss-through-dom alerts #709-#714 #716-#718 - #70

Open
natechadwick-intsof wants to merge 1 commit into
mainfrom
bugfix/xss-through-dom-inline-sanitizers
Open

codeql: close js/xss-through-dom alerts #709-#714 #716-#718#70
natechadwick-intsof wants to merge 1 commit into
mainfrom
bugfix/xss-through-dom-inline-sanitizers

Conversation

@natechadwick-intsof

@natechadwick-intsof natechadwick-intsof commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes 9 open js/xss-through-dom alerts (#709–#714, #716–#718) on delivery/common/js/views/Perc*View.js href and window.location sinks by routing each computed href through the URL constructor and gating the sink on parsedUrl.protocol not being javascript:, vbscript:, or data:. The sink then receives parsedUrl.href (the URL object's serialized form) rather than the string-concatenated value.

Background

PRs #60 and #62 added an in-repo percSafeUrl() helper and wrapped every flagged sink in a percSafeUrl(...) call. .github/workflows/codeql.yml explicitly notes that GHA rejects local model-pack paths in the workflow packs: input, so GHAS does not model the in-repo helper as a sanitizer barrier. PR #62 acknowledged this and shipped sink-line // codeql[rule-id] suppressions; 9 alerts (#709–#714, #716–#718) stayed open after the merge, plus 3 (#707, #708, #715) closed because the .text()-only path through the link text removed the taint.

Two earlier iterations of this PR tried inline sanitizers CodeQL still didn't recognize as barriers:

  1. Regex test with if-reassign/^\s*(?:javascript|vbscript|data)\s*:/i.test(href) then if (matches) { href = "#"; }. The first CodeQL check still flagged all 9 sinks; xss-through-dom doesn't model phi-node reassignment from a RegExpTest as a barrier.
  2. String.prototype.indexOf guard inside the safe branchif (href.indexOf("javascript:") === -1 && ...) { sink(href); }. The 9 sinks were still flagged; indexOf === -1 against a literal scheme isn't on the recognized sanitizer list for this rule in the CodeQL version GHA runs.

This PR uses the pattern CodeQL's xss-through-dom library explicitly recognizes: the URL constructor + protocol check. A single-sink test (only PercTagListView.js migrated first) dropped the CodeQL check from 9 alerts to 8, confirming the pattern; the remaining 8 sinks are migrated in this push.

Pattern

// Before:
var tagHref = blogIndexPage + "?filter=" + encodeURIComponent(tag) + encodedQuery;
$(this).attr("href", percSafeUrl(tagHref));

// After:
var tagHref = blogIndexPage + "?filter=" + encodeURIComponent(tag) + encodedQuery;
var tagUrl;
try {
    tagUrl = new URL(tagHref, blogIndexPage);
} catch (e) {
    tagUrl = null;
}
if (tagUrl
    && tagUrl.protocol !== "javascript:"
    && tagUrl.protocol !== "vbscript:"
    && tagUrl.protocol !== "data:") {
    $(this).attr("href", tagUrl.href);
} else {
    $(this).attr("href", "#");
}

PercRegistrationView.js falls back to "/" (matching the original percSafeUrl helper's behavior) because window.location = "#" is not a meaningful redirect target. The other four files fall back to "#".

Changes

Source (5 files, 9 sinks):

  • delivery/common/js/views/PercBlogPostView.js — drop percSafeUrl helper; URL constructor on tagHref (#716) and categoryHref (#717).
  • delivery/common/js/views/PercArchiveListView.js — drop percSafeUrl helper; URL constructor on the hierarchical year href (#709), hierarchical month href (#710), and flat-list month href (#711).
  • delivery/common/js/views/PercCategoryListView.js — drop percSafeUrl helper; URL constructor on the parseNode href (#712).
  • delivery/common/js/views/PercRegistrationView.js — drop percSafeUrl helper; URL constructor on both window.location sinks (confirmation-page redirect #714, rvkey redirect #613); fallback to "/".
  • delivery/common/js/views/PercTagListView.js — drop percSafeUrl helper; URL constructor on tagHref (#718).

Docs:

  • docs/ai-generated/tasks/8.1.x-codeql-baseline/suppressions.md — drop the 9 js/xss-through-dom rows for the alerts being fixed plus 3 stale rows for the alerts PR codeql: close delivery-view residuals #705-#715 #62 already closed (#707, #708, #715). The corresponding // codeql[js/xss-through-dom] sink-line markers are gone from the source, so the verify-suppressions.py greps would otherwise fail.
  • docs/ai-generated/tasks/8.1.x-codeql-baseline/triage.md — keep the 9 rows (alerts are still open in alerts.md until the next GHAS scan dismisses them) but update notes to describe the actual code fix and the post-fix line numbers; row count still matches alerts.md so verify-triage-inventory.py stays green.
  • docs/ai-generated/tasks/8.1.x-codeql-baseline/clusters.md — cluster row updated to point at this PR and note the URL-constructor pattern; per-alert line numbers refreshed.

Verification

  • node --check on each modified file — syntax OK
  • scripts/verify-suppressions.py — PASS
  • scripts/verify-triage-inventory.py — PASS (9 == 9; 9 open + 0 ready-to-close)
  • scripts/verify-valid-fixes.py — PASS
  • CodeQL check (single-sink test, PercTagListView.js only) — 9 → 8 alerts, confirming the URL-constructor pattern is recognized.

JDK 1.8.0 compatible (JS only). No CHANGELOG.md entry per AGENTS.md.

Follow-up

After this PR merges, the next CodeQL scan (push to main triggers .github/workflows/codeql.yml) should dismiss the 9 alerts in alerts.md. A follow-up "regenerate triage + clusters" PR (matching the pattern of #61) will then drop the 9 rows from triage.md and the js/xss-through-dom row from clusters.md.

Co-Authored by MiniMax Code (MiniMax-M3) with agent mavis.

Comment thread delivery/common/js/views/PercArchiveListView.js Fixed
Comment thread delivery/common/js/views/PercArchiveListView.js Fixed
Comment thread delivery/common/js/views/PercArchiveListView.js Fixed
Comment thread delivery/common/js/views/PercBlogPostView.js Fixed
Comment thread delivery/common/js/views/PercBlogPostView.js Fixed
Comment thread delivery/common/js/views/PercCategoryListView.js Fixed
Comment thread delivery/common/js/views/PercRegistrationView.js Fixed
Comment thread delivery/common/js/views/PercRegistrationView.js Fixed
Comment thread delivery/common/js/views/PercTagListView.js Fixed
@natechadwick-intsof
natechadwick-intsof force-pushed the bugfix/xss-through-dom-inline-sanitizers branch from 48af0be to 6f39887 Compare August 25, 2026 21:31
Comment thread delivery/common/js/views/PercArchiveListView.js Fixed
Comment thread delivery/common/js/views/PercArchiveListView.js Fixed
Comment thread delivery/common/js/views/PercArchiveListView.js Fixed
Comment thread delivery/common/js/views/PercBlogPostView.js Fixed
Comment thread delivery/common/js/views/PercBlogPostView.js Fixed
Comment thread delivery/common/js/views/PercCategoryListView.js Fixed
Comment thread delivery/common/js/views/PercRegistrationView.js Fixed
Comment thread delivery/common/js/views/PercRegistrationView.js Fixed
Comment thread delivery/common/js/views/PercTagListView.js Fixed
@natechadwick-intsof
natechadwick-intsof force-pushed the bugfix/xss-through-dom-inline-sanitizers branch from 6f39887 to 7656994 Compare August 25, 2026 23:04
9 open js/xss-through-dom alerts on delivery/common/js/views/Perc*View.js
href / window.location sinks closed by routing the value through the
URL constructor before the sink.

PRs #60 and #62 added an in-repo percSafeUrl() helper, but
.github/workflows/codeql.yml explicitly notes that GHA rejects local
model-pack paths in the workflow packs: input, so GHAS does not model
the in-repo helper as a sanitizer barrier. Two earlier iterations of
this PR tried sanitizers CodeQL still didn't recognize:

  1. inline /^\\s*(?:javascript|vbscript|data)\\s*:/i regex test
     on the computed href (if-reassign pattern). CodeQL's xss-through-dom
     library doesn't model phi-node reassignment from a RegExpTest as
     a barrier — the first CodeQL push still flagged all 9 sinks.
  2. inline String.prototype.indexOf === -1 guard placed inside the
     if-true branch (safer by the JS semantics, still not recognized).
     CodeQL's sanitizer library tracks String.prototype.startsWith and
     URL-constructor patterns but the simple indexOf === -1 against a
     literal scheme wasn't on the recognized list for this rule in this
     CodeQL version — 9 sinks still flagged.

This PR uses the URL constructor pattern CodeQL's xss-through-dom
library explicitly recognizes:

  var safeUrl;
  try { safeUrl = new URL(href, baseURL); } catch (e) { safeUrl = null; }
  if (safeUrl
      && safeUrl.protocol !== "javascript:"
      && safeUrl.protocol !== "vbscript:"
      && safeUrl.protocol !== "data:") {
      sink(safeUrl.href);
  } else {
      sink("#");
  }

After applying this to a single sink (PercTagListView.js) the CodeQL
check dropped from 9 alerts to 8, confirming the pattern is recognized.
The remaining 8 sinks have been migrated to the same pattern in this
push. The fallback is "/" for the two window.location sinks in
PercRegistrationView.js to match the original helper's behavior;
"#" for the href sinks.

Changes:
- delivery/common/js/views/PercBlogPostView.js: drop percSafeUrl
  helper; URL constructor on tagHref (#716) and categoryHref (#717).
- delivery/common/js/views/PercArchiveListView.js: drop percSafeUrl
  helper; URL constructor on the hierarchical year href (#709),
  hierarchical month href (#710), and flat-list month href (#711).
- delivery/common/js/views/PercCategoryListView.js: drop percSafeUrl
  helper; URL constructor on the parseNode href (#712).
- delivery/common/js/views/PercRegistrationView.js: drop percSafeUrl
  helper; URL constructor on both window.location sinks
  (confirmation-page redirect #714, rvkey redirect #713); fallback
  to "/" since the original helper returned that for invalid schemes.
- delivery/common/js/views/PercTagListView.js: drop percSafeUrl
  helper; URL constructor on the tag href (#718).
- docs/ai-generated/tasks/8.1.x-codeql-baseline/suppressions.md:
  drop the 9 js/xss-through-dom rows for the alerts being fixed
  (#709-#714, #716-#718) plus 3 stale rows for the alerts PR #62
  already closed (#707, #708, #715). The corresponding
  // codeql[js/xss-through-dom] sink-line markers are gone from the
  source, so the verify-suppressions.py greps would otherwise fail.
- docs/ai-generated/tasks/8.1.x-codeql-baseline/triage.md: keep the
  9 rows (alerts are still open in alerts.md until the next GHAS
  scan dismisses them) but update notes to describe the actual code
  fix and the post-fix line numbers; row count still matches
  alerts.md so verify-triage-inventory.py stays green.
- docs/ai-generated/tasks/8.1.x-codeql-baseline/clusters.md:
  cluster row updated to point at this PR and note the inlined
  sanitizer pattern; per-alert line numbers refreshed.

Verification:
  - node --check on each modified file                syntax OK
  - python3 scripts/verify-suppressions.py            PASS
  - python3 scripts/verify-triage-inventory.py        PASS (9 == 9)
  - python3 scripts/verify-valid-fixes.py             PASS
  - CodeQL check (single-sink test)                   9 -> 8 alerts

JDK 1.8.0 compatible (JS only). No CHANGELOG.md entry per AGENTS.md.

> Co-Authored by MiniMax Code (MiniMax-M3) with agent mavis.
@natechadwick-intsof
natechadwick-intsof force-pushed the bugfix/xss-through-dom-inline-sanitizers branch from 7656994 to 9876fcf Compare August 25, 2026 23:09
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.

3 participants