Skip to content

Commit 30472ec

Browse files
aaronpowellCopilotCopilot
authored
Harden external plugin gate comment output safety (#2265)
* Harden external plugin gate comment rendering Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix gate output truncation after HTML escaping Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>
1 parent 1af733e commit 30472ec

1 file changed

Lines changed: 26 additions & 4 deletions

File tree

.github/workflows/external-plugin-pr-quality-gates.yml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,37 @@ jobs:
200200
: qualityResult.overall_status === 'pass' || !shouldRun
201201
? '## ✅ External plugin PR checks passed'
202202
: '## ⚠️ External plugin PR checks need maintainer follow-up';
203+
const MAX_GATE_OUTPUT_CHARS = 2000;
204+
const escapeHtml = (value) =>
205+
String(value || '')
206+
.replace(/&/g, '&amp;')
207+
.replace(/</g, '&lt;')
208+
.replace(/>/g, '&gt;')
209+
.replace(/"/g, '&quot;')
210+
.replace(/'/g, '&#39;');
211+
const TRUNCATED_OUTPUT_MARKER = '\n...output truncated...';
212+
const truncateGateOutput = (rawOutput) => {
213+
const normalized = escapeHtml(String(rawOutput || '').trim());
214+
if (!normalized) {
215+
return '_No output captured._';
216+
}
217+
if (normalized.length <= MAX_GATE_OUTPUT_CHARS) {
218+
return normalized;
219+
}
220+
return `${normalized.slice(0, Math.max(0, MAX_GATE_OUTPUT_CHARS - TRUNCATED_OUTPUT_MARKER.length))}${TRUNCATED_OUTPUT_MARKER}`;
221+
};
203222
const formatGateOutput = (pluginName, gateName, gateStatus, rawOutput) => {
204-
const output = String(rawOutput || '').trim() || '_No output captured._';
223+
const summaryPluginName = escapeHtml(String(pluginName || 'unknown'));
224+
const summaryGateName = escapeHtml(String(gateName || 'gate'));
225+
const summaryGateStatus = escapeHtml(String(gateStatus || 'not_run'));
226+
const output = truncateGateOutput(rawOutput);
205227
return [
206228
'<details>',
207-
`<summary>${pluginName} — ${gateName} (${gateStatus || 'not_run'})</summary>`,
229+
`<summary>${summaryPluginName} - ${summaryGateName} (${summaryGateStatus})</summary>`,
208230
'',
209-
'```text',
231+
'<pre><code>',
210232
output,
211-
'```',
233+
'</code></pre>',
212234
'</details>',
213235
].join('\n');
214236
};

0 commit comments

Comments
 (0)