You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(ci): neutralize legacy ##[ commands in autofix stdout echoes (#9871)
* fix(ci): neutralize legacy ##[ commands in autofix stdout echoes
A review finding about workflow-command injection carried its payload
strings (::error::forged, ##[add-matcher]) verbatim as example text.
Prepare echoed feedback.md raw, the runner parsed the mid-line
##[add-matcher] and failed the step before the agent ran, and the
consecutive-failure breaker burned the takeover window of #9761 in
about 70 minutes while reporting it as a 100/100 round cap.
The existing neutralization covered only the modern :: syntax; the
runner also parses the legacy ##[name] form, even mid-line. Extend
every untrusted-content echo in the autofix family (prepare feedback
echo, both artifact dump loops, the gate's failure/handoff/no-action
echoes, the deferred-findings dumps and upsert re-emit loops) to
neutralize both prefixes, and pin the census plus a behavioral ##[
case in the contract tests.
* fix(ci): unify the autofix neutralizer on one canonical spelling
Review feedback on #9871: the two upsert re-emit loops neutralized with
a hybrid spelling (bash expansion for :: plus a single-syntax sed for
##[) while the other fourteen sites used the canonical two-expression
sed, establishing two implementations of one normalization. A future
edit fixing one spelling could miss the other and recreate the
single-syntax half-guard that let #9761 through. Unify both loops on
the canonical sed and consolidate the pinned test shapes.
The census test's comment also promised more than its assertions
deliver: it pins the known enumeration, it does not auto-detect new
un-neutralized echoes. Reword it to state the actual guarantee, ban
the ##[-only sed and the bare bash expansion alongside the existing
::-only ban, and fold the script-side re-emit loop into the census.
* test(ci): assert the neutralizer pairing property instead of banning shapes
The three shape bans only matched the no--e spellings, so the canonical
line's natural half-copies (sed -e 's/::/;;/g' alone, or the ##[-only -e
mirror) passed every ban while guarding only one syntax. Assert the
pairing property instead: every occurrence of either substitution
expression must belong to the canonical two-expression pair, so a
half-guard in ANY spelling unbalances the count and fails the census —
verified by probe against all four half-guard shapes.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Copy file name to clipboardExpand all lines: .github/workflows/qwen-autofix.yml
+36-14Lines changed: 36 additions & 14 deletions
Original file line number
Diff line number
Diff line change
@@ -1318,13 +1318,15 @@ jobs:
1318
1318
if [[ -f "${WORKDIR}/failure.md" && -n "$(git status --porcelain)" ]]; then
1319
1319
echo "❌ Agent wrote failure.md after leaving a dirty workspace:"
1320
1320
git status --short
1321
-
cat "${WORKDIR}/failure.md"
1321
+
# Agent-written content on step stdout: neutralize both command
1322
+
# syntaxes (`##[` parses mid-line too — #9761).
1323
+
sed -e 's/::/;;/g' -e 's/##\[/##[/g' "${WORKDIR}/failure.md"
1322
1324
exit 1
1323
1325
fi
1324
1326
1325
1327
if [[ -f "${WORKDIR}/failure.md" ]]; then
1326
1328
echo "🛑 Agent aborted intentionally:"
1327
-
cat "${WORKDIR}/failure.md"
1329
+
sed -e 's/::/;;/g' -e 's/##\[/##[/g' "${WORKDIR}/failure.md"
1328
1330
exit 1
1329
1331
fi
1330
1332
@@ -1395,12 +1397,14 @@ jobs:
1395
1397
for f in decision.json pr-title.txt pr-body.md e2e-report.md failure.md failure.zh.md fix.diff; do
1396
1398
if [[ -f "${WORKDIR}/${f}" ]]; then
1397
1399
echo "=============== ${f} ==============="
1398
-
# Agent-written content on step STDOUT: a line-start `::` would
1399
-
# be parsed as a workflow command (::error::, ::add-mask::), the
1400
-
# same reason the PR-lane dump loop neutralizes it. (The two
1401
-
# step-SUMMARY loops write to a file, where `::` is not
1400
+
# Agent-written content on step STDOUT: both workflow-command
1401
+
# syntaxes parse here — a line-start `::` (::error::,
1402
+
# ::add-mask::) AND `##[` even mid-line (a quoted
1403
+
# `##[add-matcher]` fails THIS step; measured on #9761). The
1404
+
# same reason the PR-lane dump loop neutralizes both. (The two
1405
+
# step-SUMMARY loops write to a file, where commands are not
1402
1406
# parsed.)
1403
-
sed 's/::/;;/g' "${WORKDIR}/${f}"
1407
+
sed -e 's/::/;;/g' -e 's/##\[/##[/g' "${WORKDIR}/${f}"
1404
1408
echo
1405
1409
fi
1406
1410
done
@@ -4699,7 +4703,17 @@ jobs:
4699
4703
fi
4700
4704
} > "${WORKDIR}/feedback.md"
4701
4705
echo '--- feedback.md ---'
4702
-
cat "${WORKDIR}/feedback.md"
4706
+
# Reviewer/bot comment bodies ride this file VERBATIM and this echo
4707
+
# puts them on step stdout, which the runner scans for workflow
4708
+
# commands in BOTH syntaxes: `::name::` AND the legacy `##[name]`,
4709
+
# the latter parsed MID-line too — a quoted `##[add-matcher]` makes
4710
+
# the runner load the rest of the line as a matcher file and fail
4711
+
# THIS step before the agent runs. Measured on #9761: a review
4712
+
# finding about that injection channel carried the payload strings
4713
+
# as its example text, and five consecutive pre-agent crashes
4714
+
# burned the takeover window in ~70 minutes. Neutralize both
4715
+
# prefixes like every other untrusted echo.
4716
+
cat "${WORKDIR}/feedback.md" | sed -e 's/::/;;/g' -e 's/##\[/##[/g'
4703
4717
4704
4718
# The agent below runs for up to 130 minutes and the verification gate adds
4705
4719
# more, but nothing reaches the PR thread until "Push and report" at the
@@ -5072,7 +5086,9 @@ jobs:
5072
5086
else
5073
5087
echo "::warning::could not merge carried deferrals across the repair (one of the two sets is unparseable); keeping the carried set and preserving this round's as deferred-findings.unmerged.json. Raw content follows:"
5074
5088
fi
5075
-
head -c 4000 "${WORKDIR}/deferred-findings.json" | sed 's/::/;;/g'
5089
+
# Both command syntaxes, like every other untrusted echo
5090
+
# (`##[` parses mid-line too — #9761).
5091
+
head -c 4000 "${WORKDIR}/deferred-findings.json" | sed -e 's/::/;;/g' -e 's/##\[/##[/g'
5076
5092
echo
5077
5093
rm -f "${WORKDIR}/deferred-findings.carry.next"
5078
5094
# Keep the discarded set ON DISK so the warning's pointer at
@@ -5300,10 +5316,12 @@ jobs:
5300
5316
for f in feedback.md address-summary.md no-action.md failure.md failure.zh.md handoff.md gate-rejection.md gate-advisories.md growth-audit.json agent-api-error agent-api-error-kind agent-timeout resolved-comments.txt comment-replies.json deferred-findings.json deferred-findings.carry.json deferred-findings.unmerged.json pr.diff; do
5301
5317
if [[ -f "${WORKDIR}/${f}" ]]; then
5302
5318
echo "=============== ${f} ==============="
5303
-
# Agent-written content: a line-start `::` would be parsed as a
5304
-
# workflow command (::error::, ::add-mask::), the same reason
5305
-
# every other echo of these files neutralizes it.
5306
-
sed 's/::/;;/g' "${WORKDIR}/${f}"
5319
+
# Agent/reviewer-written content: both workflow-command syntaxes
5320
+
# parse here — a line-start `::` (::error::, ::add-mask::) AND
5321
+
# `##[` even mid-line (a quoted `##[add-matcher]` fails THIS
5322
+
# step; measured on #9761 via the prepare echo of this same
5323
+
# file). Neutralize both, like every other echo of these files.
5324
+
sed -e 's/::/;;/g' -e 's/##\[/##[/g' "${WORKDIR}/${f}"
5307
5325
echo
5308
5326
fi
5309
5327
done
@@ -5995,7 +6013,11 @@ jobs:
5995
6013
elif [[ "${_upsert_line}" == __upsert_trusted__* ]]; then
0 commit comments