From 1a66409a8637b2303088acf3c71cd41f8d53fdfa Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Fri, 10 Jul 2026 14:35:27 +1000 Subject: [PATCH 1/2] Harden external plugin gate comment rendering Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../external-plugin-pr-quality-gates.yml | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/external-plugin-pr-quality-gates.yml b/.github/workflows/external-plugin-pr-quality-gates.yml index cb05e1390..4104d333f 100644 --- a/.github/workflows/external-plugin-pr-quality-gates.yml +++ b/.github/workflows/external-plugin-pr-quality-gates.yml @@ -200,15 +200,36 @@ jobs: : qualityResult.overall_status === 'pass' || !shouldRun ? '## ✅ External plugin PR checks passed' : '## ⚠️ External plugin PR checks need maintainer follow-up'; + const MAX_GATE_OUTPUT_CHARS = 2000; + const escapeHtml = (value) => + String(value || '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + const truncateGateOutput = (rawOutput) => { + const normalized = String(rawOutput || '').trim(); + if (!normalized) { + return '_No output captured._'; + } + if (normalized.length <= MAX_GATE_OUTPUT_CHARS) { + return normalized; + } + return `${normalized.slice(0, MAX_GATE_OUTPUT_CHARS)}\n...output truncated...`; + }; const formatGateOutput = (pluginName, gateName, gateStatus, rawOutput) => { - const output = String(rawOutput || '').trim() || '_No output captured._'; + const summaryPluginName = escapeHtml(String(pluginName || 'unknown')); + const summaryGateName = escapeHtml(String(gateName || 'gate')); + const summaryGateStatus = escapeHtml(String(gateStatus || 'not_run')); + const output = escapeHtml(truncateGateOutput(rawOutput)); return [ '
', - `${pluginName} — ${gateName} (${gateStatus || 'not_run'})`, + `${summaryPluginName} - ${summaryGateName} (${summaryGateStatus})`, '', - '```text', + '
',
                 output,
-                '```',
+                '
', '
', ].join('\n'); }; From 54a130fe93a46c8c920c499210b9a8a7101124ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:58:16 +0000 Subject: [PATCH 2/2] Fix gate output truncation after HTML escaping Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> --- .github/workflows/external-plugin-pr-quality-gates.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/external-plugin-pr-quality-gates.yml b/.github/workflows/external-plugin-pr-quality-gates.yml index 4104d333f..068886a9a 100644 --- a/.github/workflows/external-plugin-pr-quality-gates.yml +++ b/.github/workflows/external-plugin-pr-quality-gates.yml @@ -208,21 +208,22 @@ jobs: .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); + const TRUNCATED_OUTPUT_MARKER = '\n...output truncated...'; const truncateGateOutput = (rawOutput) => { - const normalized = String(rawOutput || '').trim(); + const normalized = escapeHtml(String(rawOutput || '').trim()); if (!normalized) { return '_No output captured._'; } if (normalized.length <= MAX_GATE_OUTPUT_CHARS) { return normalized; } - return `${normalized.slice(0, MAX_GATE_OUTPUT_CHARS)}\n...output truncated...`; + return `${normalized.slice(0, Math.max(0, MAX_GATE_OUTPUT_CHARS - TRUNCATED_OUTPUT_MARKER.length))}${TRUNCATED_OUTPUT_MARKER}`; }; const formatGateOutput = (pluginName, gateName, gateStatus, rawOutput) => { const summaryPluginName = escapeHtml(String(pluginName || 'unknown')); const summaryGateName = escapeHtml(String(gateName || 'gate')); const summaryGateStatus = escapeHtml(String(gateStatus || 'not_run')); - const output = escapeHtml(truncateGateOutput(rawOutput)); + const output = truncateGateOutput(rawOutput); return [ '
', `${summaryPluginName} - ${summaryGateName} (${summaryGateStatus})`,