fix(security): resolve the CodeQL backlog, three fixes and three documented false positives (#1225)

Triage of all 9 CodeQL alerts surfaced on main by #1223. None were introduced by that PR.

Fixed: the compliance-review artifact now unpacks to runner.temp instead of over the trusted checkout (actions/artifact-poisoning, critical); MCP LIKE patterns escape backslash first, which was a real correctness bug returning wrong rows for any search containing a backslash (js/incomplete-sanitization, 2 sites); and the mcp-oauth consent form action is HTML-escaped (js/reflected-xss, not exploitable because WHATWG URL already percent-encodes " < >, but & is not in that encode set).

Dismissed as false positives with reasoning recorded at each site and in DECISIONS.md: sie-export escapeQuotes, where doubling backslashes would violate SIE 4B, corrupt files in conformant readers and skew #KSUMMA under BFL 7-year retention; hashApiKey, where SHA-256 is correct for a 256-bit CSPRNG token and changing it would invalidate every live gnubok_sk_ key; and the DuplicateBookingDialog href, which is a DB UUID behind a literal path prefix.

Regression tests cover both behavioural fixes, including the escape ordering.
This commit is contained in:
Jakob Wennberg
2026-07-27 14:02:25 +02:00
committed by GitHub
parent 4702a63cff
commit 7dde8cac82
8 changed files with 161 additions and 7 deletions
@@ -43,13 +43,25 @@ jobs:
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: compliance-input
# Unpacked OUTSIDE the checkout, not into the workspace root.
#
# This artifact is built from a fork's PR head, so its contents are
# attacker-influenced. Extracted over the workspace, an entry named
# `scripts/swedish-compliance-review.mjs` would overwrite the trusted
# script this job is about to run, with the AWS secrets and a write
# token already in scope. Stage 1 only ever writes three fixed
# filenames, and its workflow definition comes from the base repo even
# for fork PRs, so that is not reachable today: this keeps it
# unreachable if stage 1 ever grows a filename derived from PR
# content. Flagged by CodeQL as actions/artifact-poisoning.
path: ${{ runner.temp }}/compliance-input
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve PR number
id: pr
run: |
set -euo pipefail
NUM=$(cat pr-number.txt)
NUM=$(cat "$RUNNER_TEMP/compliance-input/pr-number.txt")
# Guard: pr-number.txt must be a plain integer (artifact is untrusted input).
if ! [[ "$NUM" =~ ^[0-9]+$ ]]; then
echo "Refusing to continue: pr-number.txt is not a number" >&2
@@ -81,8 +93,8 @@ jobs:
AWS_REGION: ${{ secrets.AWS_REGION || 'eu-north-1' }}
REVIEW_MODEL: eu.anthropic.claude-sonnet-5
# Two-stage mode: read the diff from the artifact instead of git-diffing.
DIFF_FILE: diff.patch
FILES_FILE: files.txt
DIFF_FILE: ${{ runner.temp }}/compliance-input/diff.patch
FILES_FILE: ${{ runner.temp }}/compliance-input/files.txt
run: node scripts/swedish-compliance-review.mjs
- name: Assert review produced output
# This job once produced no compliance signal for 10 consecutive PR