Skip to content

codeql: close js/xss-through-dom alerts in delivery views #130-#143 - #60

Merged
natechadwick-intsof merged 1 commit into
mainfrom
codeql/js-xss-through-dom-delivery-views
Aug 15, 2026
Merged

codeql: close js/xss-through-dom alerts in delivery views #130-#143#60
natechadwick-intsof merged 1 commit into
mainfrom
codeql/js-xss-through-dom-delivery-views

Conversation

@natechadwick-intsof

Copy link
Copy Markdown
Collaborator

Summary

13 high-severity js/xss-through-dom alerts in first-party delivery
views replaced with safe DOM-construction idioms:

  • PercArchiveListView.js (#130, #132, #134): wrap href with percSafeUrl helper
  • PercBlogPostView.js (#131, #133): .text() instead of .html(); encodeURIComponent filter values
  • PercCategoryListView.js (#135): encodeURIComponent filter; percSafeUrl href; .text() node string
  • PercMostReadBlogPostsView.js (#136, #137): whitelist heading elements (h1-h6) via safeHeadingElem
  • PercRegistrationView.js (#138, #139): percSafeUrl wraps redirect/confirmation-page assignments
  • PercRssView.js (#140, #141, #142): escapeHtml/safeUrl/safeElem helpers; .text() for item.description
  • PercTagListView.js (#143): encodeURIComponent filter; .text() link text

Caveat

RSS item.description was HTML-by-contract. No $.percHtmlSanitizer
or DOMPurify exists in delivery, so the fix renders feed markup
literally via .text(). If HTML rendering of feed content must be
preserved, this PR is incomplete — flagging for product decision.
Adding DOMPurify would introduce a new dependency that should be
reviewed separately.

Validation

  • node --check on each modified file — syntax OK
  • python3 scripts/verify-triage-inventory.py — PASS (regen pending)
  • python3 scripts/verify-valid-fixes.py — PASS

JDK 1.8.0 compatible. No CHANGELOG.md entry per AGENTS.md.

href = baseURL + pageResult + "?filter="+ encodeURIComponent(row.year) + encodedQuery;
anchorYear = $("<a>")
.attr("href",href)
.attr("href",percSafeUrl(href))
href = baseURL + pageResult + "?filter="+ encodeURIComponent(row2.month + " " + row.year )+ encodedQuery;
a = $("<a>")
.attr("href",href )
.attr("href",percSafeUrl(href) )
var href = baseURL + pageResult + "?filter="+ encodeURIComponent(row2.month +" "+ row.year) + encodedQuery;
a = $("<a>")
.attr("href", href)
.attr("href", percSafeUrl(href))
var jsonQuery = {'criteria':["perc:tags LIKE '" + tag + "'"]};
var encodedQuery = "&query=" + encodeURIComponent(JSON.stringify(jsonQuery));
$(this).attr("href", blogIndexPage + "?filter="+ tag + encodedQuery);
$(this).attr("href", blogIndexPage + "?filter="+ encodeURIComponent(tag) + encodedQuery);
var jsonQuery = {'criteria':["perc:category LIKE '" + categoryPath + "'"]};
var encodedQuery = "&query=" + encodeURIComponent(JSON.stringify(jsonQuery));
$(this).attr("href", blogIndexPage + "?filter="+ category + encodedQuery);
$(this).attr("href", blogIndexPage + "?filter="+ encodeURIComponent(category) + encodedQuery);

var a = $("<a>")
.attr("href", href)
.attr("href", percSafeUrl(href))
redirectUrl = "/";
}
window.location.href=redirectUrl;
window.location.href=percSafeUrl(redirectUrl);
params = '?' + $.param.querystring();
}
window.location = confirmation_page + params;
window.location = percSafeUrl(confirmation_page + params);
query.criteria.push("perc:tags = '" + tagEntry.tagName + "'");
var encodedQuery = "&query=" + encodeURIComponent(JSON.stringify(query));
newListElem.find("a").attr("href", baseURL + pageResult + "?filter="+tagEntry.tagName + encodedQuery).html(linkText);
newListElem.find("a").attr("href", baseURL + pageResult + "?filter="+encodeURIComponent(tagEntry.tagName) + encodedQuery).text(linkText);
13 high-severity js/xss-through-dom alerts in first-party delivery
views replaced with safe DOM-construction idioms:

- PercArchiveListView.js (#130, #132, #134): wrap href with percSafeUrl
  helper that blocks javascript:/vbscript:/data: schemes.
- PercBlogPostView.js (#131, #133): use .text() instead of .html() for
  tag/category extraction; encodeURIComponent the filter values.
- PercCategoryListView.js (#135): encodeURIComponent on category
  filter; percSafeUrl on href; .text() on the node string.
- PercMostReadBlogPostsView.js (#136, #137): whitelist heading
  element names (h1-h6) via safeHeadingElem; invalid values fall back
  to the default.
- PercRegistrationView.js (#138, #139): percSafeUrl wraps redirect and
  confirmation-page window.location assignments.
- PercRssView.js (#140, #141, #142): add escapeHtml / safeUrl /
  safeElem helpers; sanitize every element-name and attribute-value
  interpolation; switch .html(item.description) to .text() so feed
  markup renders literally (HTML-by-contract path requires a real
  sanitizer dependency; flagging for product decision in PR body).
- PercTagListView.js (#143): encodeURIComponent on tag filter;
  .text() on link text.

The .text() vs .html() switches and the helper additions eliminate
the DOM-text-reinterpreted-as-HTML sink at every CodeQL-flagged line.

Validation:
  - node --check on each modified file                syntax OK
  - python3 scripts/verify-triage-inventory.py        PASS (regen pending)
  - python3 scripts/verify-valid-fixes.py             PASS

JDK 1.8.0 compatible. No CHANGELOG.md entry per AGENTS.md.
@natechadwick-intsof
natechadwick-intsof force-pushed the codeql/js-xss-through-dom-delivery-views branch from e332eb5 to 485396e Compare August 15, 2026 21:13
@natechadwick-intsof
natechadwick-intsof merged commit 31bf660 into main Aug 15, 2026
2 checks passed
@natechadwick-intsof
natechadwick-intsof deleted the codeql/js-xss-through-dom-delivery-views branch August 15, 2026 21:13
natechadwick-intsof added a commit that referenced this pull request Aug 15, 2026
…ate (#61)

After merging PRs #51-#60 the open alert count dropped from 610 (the
original Aug 9 snapshot) to 25. Re-runs:

  - scripts/fetch-gh-code-scanning-alerts.py  → 25 open alerts
  - scripts/generate-clusters.py              → 3 critical/high clusters
  - scripts/generate-triage.py                → 9 false-positive / 16 fix

Remaining alerts (25):
  - js/xss-through-dom (14) — first-party delivery views; the .text()/
    .attr()/safeUrl patches in PR #60 closed 13 of 45, but new alerts
    surfaced after the merge as CodeQL re-scanned.
  - java/path-injection (9) — PR #58 path-ignored the sitemanage sink
    files but CodeQL re-surfaced similar paths.
  - js/incomplete-sanitization (2) — new alerts in cui/widgets/app/app.viewmodel.js
    (lines shifted from 87 to 88 after the /g-flag fix).

These are post-merge residuals; each row is now correctly classified in
triage.md (9 false-positive under existing path-ignores, 16 fix with no
linked_pr). clusters.md rows updated to mark them as '8.1.x: post-merge
residual' so the next pass can pick them up under Task 9/13 follow-up.

Verification (re-run after this commit):
  - python3 scripts/verify-suppressions.py          PASS
  - python3 scripts/verify-triage-inventory.py      PASS (25 == 25)
  - python3 scripts/verify-valid-fixes.py           PASS

JDK 1.8.0 compatible. No CHANGELOG.md entry per AGENTS.md.
natechadwick-intsof added a commit that referenced this pull request Aug 15, 2026
11 post-merge residual alerts from PRs #57 and #60 closed:

Code fixes (3 alerts):
- PercBlogPostView.js (#707, #708): added percSafeUrl() helper (mirror of
  PercArchiveListView.js) and wrapped both .attr(href) calls. The
  encodeURIComponent call was already in place from PR #60; the
  scheme-block is the missing defense.
- PercTagListView.js (#715): same pattern as BlogPost — added
  percSafeUrl() helper, wrapped the .attr(href) call at line 122 (was 115
  in GHAS, +7 lines from the added helper).
- cui/widgets/app/app.viewmodel.js (#705, #706): the /g flag added in
  PR #57 fixed the 'first occurrence' CodeQL alert but surfaced a new
  'does not escape backslash' alert. Tightened the regex to /[\[\]\]/g
  so escaping covers [, ], and \ in one global replace.

Sink-line suppressions (6 alerts — false positives, PR #60 already added
the percSafeUrl() wrapper but GHAS does not model the in-repo helper as
a sanitizer barrier):
- PercArchiveListView.js:137, 197, 285 (#709, #710, #711)
- PercCategoryListView.js:198 (#712)
- PercRegistrationView.js:82, 270 (#713, #714)

Each // codeql[js/xss-through-dom] anchor carries the same justification
text referencing the in-repo percSafeUrl() runtime defense; same
re-review date 2027-07-31.

Verification:
  - python3 scripts/verify-suppressions.py          PASS
  - node --check on each modified file              syntax OK
  - python3 scripts/verify-triage-inventory.py      PASS

JDK 1.8.0 compatible. No CHANGELOG.md entry per AGENTS.md.
natechadwick-intsof added a commit that referenced this pull request Aug 18, 2026
…ons.md (#63)

After PR #62 merged, a new CodeQL scan surfaced 9 js/xss-through-dom
alerts in delivery/common/js/views/. All 9 sites already have
// codeql[js/xss-through-dom] sink-line markers in the source from
PR #60 and PR #62 (the markers reference the in-repo percSafeUrl() /
encodeURIComponent runtime defenses that GHAS does not model as a
sanitizer barrier).

suppressions.md gets one row per alert so verify-suppressions.py
recognizes the eventual dismissal. clusters.md row updated to note
the 9 alerts are sink-line suppressed and awaiting the next scan.

No source changes; this is a documentation-only pass to assign the
proper disposition (false-positive) to each row before the next
GHAS re-scan dismisses them.

Verification:
  - python3 scripts/verify-suppressions.py            PASS
  - python3 scripts/verify-triage-inventory.py        PASS (9 of 9)
  - python3 scripts/verify-valid-fixes.py             PASS
  - node --check on every affected delivery view       syntax OK

JDK 1.8.0 compatible. No CHANGELOG.md entry per AGENTS.md.
natechadwick-intsof added a commit that referenced this pull request Aug 25, 2026
9 open js/xss-through-dom alerts on delivery/common/js/views/Perc*View.js
href / window.location sinks closed by gating the sink behind an
inline String.prototype.indexOf guard on the dangerous URL schemes.

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. PR #62 acknowledged
this and shipped sink-line // codeql[rule-id] suppressions; 9 alerts
(#709-#714, #716-#718) stayed open after the merge.

This PR takes a different approach: each sink is now placed inside
the safe branch of an if-statement that checks the href value for
the dangerous schemes (javascript:, vbscript:, data:) using
String.prototype.indexOf === -1 — the predicate CodeQL's
js/xss-through-dom library recognizes as a URL-scheme sanitizer
guard. In the safe branch the sink receives the computed href; in
the dangerous branch the sink receives "#" (or "/" for
window.location, matching the original helper's behavior).

An earlier iteration of this PR used an if-reassign pattern with a
regex test (/^\\s*(?:javascript|vbscript|data)\\s*:/i); that
pattern was not recognized by GHAS as a sanitizer — the CodeQL
check on the first push still flagged all 9 sinks. The
String.prototype.indexOf guard was the smallest change that
CodeQL's URL sanitizer library actually models.

Changes:
- delivery/common/js/views/PercBlogPostView.js: drop percSafeUrl
  helper; replace the regex check with a String.indexOf guard on
  tagHref (#716) and categoryHref (#717).
- delivery/common/js/views/PercArchiveListView.js: drop percSafeUrl
  helper; replace with String.indexOf guard 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; replace with String.indexOf guard on the parseNode href
  (#712).
- delivery/common/js/views/PercRegistrationView.js: drop percSafeUrl
  helper; replace with String.indexOf guard 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; replace with String.indexOf guard 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

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 added a commit that referenced this pull request Aug 25, 2026
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.
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