fix(pr-validation): run under pull_request_target + drop DoS-prone comment loop

Security audit fixes:
- The jobs gated on github.event_name == 'pull_request' but the trigger is
  pull_request_target, so they never ran (validation was silently disabled).
  Gate on 'pull_request_target'.
- Replace the loop-until-stable HTML-comment strip with a single linear pass
  (+ trailing-unterminated strip): still leaves no <!-- (CodeQL-clean) but
  removes the quadratic re-scan a crafted nested-comment body could abuse.
This commit is contained in:
Gauvino
2026-06-01 20:14:24 +02:00
parent 3de9b65b7d
commit 5f59dce0c7
2 changed files with 11 additions and 14 deletions

View File

@@ -17,7 +17,7 @@ concurrency:
jobs:
validate_pr_title:
name: "📝 Validate PR Title"
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-24.04
permissions:
pull-requests: write
@@ -51,7 +51,7 @@ jobs:
name: "📋 Validate PR Template"
# Skip pushes to an existing PR (the body rarely changes) and bot-authored PRs.
if: >-
github.event_name == 'pull_request' &&
github.event_name == 'pull_request_target' &&
github.event.action != 'synchronize' &&
github.actor != 'renovate[bot]' &&
github.actor != 'github-actions[bot]'

View File

@@ -23,18 +23,15 @@ const body = readFileSync(bodyFile, "utf8").replace(/\r\n/g, "\n");
const association = (process.env.AUTHOR_ASSOCIATION || "").toUpperCase();
const isMaintainer = ["OWNER", "MEMBER", "COLLABORATOR"].includes(association);
// Strip HTML comments robustly: loop until stable so nested/overlapping `<!--`
// markers can't survive a single pass, then drop any unterminated trailing
// comment. (Also satisfies CodeQL's "incomplete multi-character sanitization".)
const stripComments = (s) => {
let out = s;
let prev;
do {
prev = out;
out = out.replace(/<!--[\s\S]*?-->/g, "");
} while (out !== prev);
return out.replace(/<!--[\s\S]*$/, "").trim();
};
// Strip HTML comments in a single linear pass: remove complete `<!-- … -->`
// blocks, then drop any leftover unterminated `<!-- …` to end-of-string. This
// leaves no `<!--` behind (satisfies CodeQL) without the quadratic re-scan loop
// a malicious deeply-nested body could abuse for CPU-DoS.
const stripComments = (s) =>
s
.replace(/<!--[\s\S]*?-->/g, "")
.replace(/<!--[\s\S]*$/, "")
.trim();
// Grab the text under a heading whose title contains `keyword`, up to the next heading
// or the end of the body.