qwen-code/scripts/tests
Shaojin Wen 5a6452a2a5
fix(ci): make autofix verification gates hermetic to runner git config (#8961)
* fix(ci): make autofix verification gates hermetic to runner git config

A leaked global exec knob on the persistent pool (run 31516789251:
diff.external=global-driver in the runner user's ~/.gitconfig) failed
four per-hunk probe tests in packages/cli on #8613. The rejection was
charged to the round (package tests are A/B-exempt), which burned the
18-minute repair on a failure no repair can reach and ended the round
as a timeout — attempt 4 of the failure window, with nothing pushed.

Three layers against that class:

- Both verification gates (the review script and the issue-fix inline
  twin) now export a per-run throwaway GIT_CONFIG_GLOBAL (seeded with
  the workspace safe.directory) and GIT_CONFIG_SYSTEM=/dev/null before
  their first git command, so every check they spawn — vitest fixture
  repos included — is hermetic to the host, and a branch-authored
  `git config --global` dies with the run instead of poisoning the
  next one.
- The sanitize step (all three byte-identical copies) now also scrubs
  the runner USER's global config — denylist of the command-execution
  families only, so infra-owned routing/credential keys survive. This
  self-heals the live pollution on the affected runner on its next job
  and removes (not merely bypasses) a planted global core.hooksPath.
- test-efficacy.integration.test.ts gets the same GIT_CONFIG_GLOBAL /
  HOME isolation as git.integration.test.ts: the code under test pins
  --no-ext-diff, but the test scaffolding's plain `git diff` did not,
  so a hostile user git config could fail the suite anywhere.

Contract tests pin the gate redirects (before the first git command,
truncated per run) and functionally run the extracted scrub pipeline
against a poisoned fixture config, asserting the kept/removed sets.

* fix(ci): widen the config scrub and re-sanitize before PAT-bearing steps

Address the #8961 review findings (2 Critical, 8 Suggestions across two
reviews), all probe-verified by the reviewers:

- Denylist regex: subsection slots are .+ (git subsection names may
  contain dots — diff."a.b".command flattened past [^.]+), and the
  missing exec families are added: gpg.(*.)program, pager.*,
  interactive.diffFilter, difftool./mergetool., remote.*.uploadpack/
  receivepack. The functional fixture now covers every alternation plus
  dotted subsections, non-exec/corrupt/missing-config arms pin the two
  load-bearing '|| true' guards.
- The global scrub moved above the .git early-exit: host hygiene owes
  nothing to the workspace existing.
- New resanitize-git-config.sh (staged from the trusted base) re-runs
  the local allowlist sweep and the global denylist scrub at the top of
  both PAT-bearing git steps — the gates run branch test code on the
  host after the job-start sanitize, and the env redirect is not a
  filesystem boundary. Contract tests pin script/step lists equal, the
  staging in both jobs, the call-before-credential ordering, and run
  the script functionally against planted local+global keys.
- All three one-shot credential helpers lead with -c credential.helper=
  (empty resets the helper list; the first helper to answer wins, so a
  planted one must never run first). Count-pinned in the contract test.
- comment-status.integration.test.ts gets the same git-config isolation
  as its siblings; test-efficacy gains an isolation tripwire test that
  goes red if the redirect is removed, instead of only on hostile hosts.
- Comment fixes: the copies are cross-referenced as contract-test-pinned,
  and the system-config bypass is documented in both gates.

* fix(ci): close the XDG/env/transport bypasses around the config scrub

Address the #8961 round-2 review findings (5 Critical + 8 Suggestions,
probe-verified by the reviewers; the XDG listing gap independently
reproduced on git 2.55):

- The global scrub sweeps BOTH files of the global scope: with
  ~/.gitconfig and $XDG_CONFIG_HOME/git/config both present,
  `git config --global` lists/unsets only the former, so keys planted
  in the XDG file survived every copy. The scrub is now a loop that
  redirects GIT_CONFIG_GLOBAL at each file in turn.
- Denylist adds url.*.insteadOf/pushInsteadOf (transport rewrite of the
  PAT push/fetch; rest of url.* stays) and http.*.sslVerify/sslCAInfo
  (turns a kept http.proxy into a TLS-terminating interceptor); the
  three PAT helper chains lead with -c http.sslVerify=true.
- The staged resanitize script's provenance holds at cp time only —
  RUNNER_TEMP is writable by the branch code that runs in between — so
  the staging steps record its sha256 in GITHUB_OUTPUT and the PAT
  steps verify before executing.
- Both gates and both PAT steps export GIT_CONFIG_COUNT=0:
  GITHUB_ENV-injected GIT_CONFIG_KEY/VALUE entries apply at
  command-line precedence and outrank every file-level guard.
- Gates emit a ::notice when /etc/gitconfig exists (bypassed by the
  redirect — replicate needed settings via per-job env).
- Tests: the scrub's functional harness drives HOME/XDG fixtures and
  covers the new families; the resanitize run plants worktree-scoped
  config (deleting the rm -f line previously stayed green); the gate
  redirect block is executed against a hostile HOME and an env-planted
  GIT_CONFIG_* key; the isolation tripwire pins the NOSYSTEM leg and
  probes system-scope leakage.
- The process-env git isolation pattern is extracted into
  isolateHostGitConfig() in review/lib/test-utils.ts and adopted by all
  five suites that duplicated it; comment-status gains the same
  tripwire.

* fix(ci): take PAT git steps off host scopes and close the env channels

Address the #8961 round-3 review (5 Critical + 6 Suggestions,
probe-verified by the reviewer):

- Both PAT-bearing steps now run fully hermetic, same shape as the
  gates: a per-run throwaway GIT_CONFIG_GLOBAL + GIT_CONFIG_SYSTEM=
  /dev/null, so a concurrent job rewriting the shared ~/.gitconfig in
  the sweep->push window (max-parallel, one HOME across ~27 runner
  registrations) can no longer steer the push, and a URL-scoped
  sslVerify=false there can no longer override the -c pin. Both steps
  and both gates also strip the git ENV channels that outrank file
  config: GIT_CONFIG_PARAMETERS, GIT_SSL_NO_VERIFY/CAINFO,
  GIT_PROXY_COMMAND, GIT_EXEC_PATH, GIT_DIR/WORK_TREE, GIT_ASKPASS,
  GIT_SSH/_COMMAND, plus GIT_CONFIG_COUNT=0.
- The push-race salvage merge runs -c commit.gpgsign=false: a global
  commit.gpgsign=true with no key would exit 128 and be misread as a
  content conflict, discarding a verified round (R2-10).
- The maintainer-fork fetch, the one PAT-bearing network site the
  round-2 rollout skipped, leads with -c http.sslVerify=true
  -c credential.helper= (anonymous; public fork heads need no auth, so
  it fails closed on a 401 instead of feeding a planted helper the PAT).
- Denylist widens protocol.ext.allow to protocol.(ext.)?allow (the
  top-level fallback policy arms ext:: too) in all four copies.
- Tests: the two PAT hermetic blocks and the two gate blocks are pinned
  equal; the sha256 verify line is pinned verbatim and asserted to carry
  no bypass; the resanitize fixture plants a live XDG exec key (drops
  of the loop's XDG leg now fail); the gate redirect functional exec adds
  the env-channel unsets; diff-plan adopts isolateHostGitConfig (sixth
  suite) keeping its GIT_TERMINAL_PROMPT delta; comment-status tripwire
  gains the GIT_CONFIG_GLOBAL assertion.

* fix(ci): pin PATH, seal repo-redirect and env channels, harden all PAT sites

Address the #8961 round-4 review (6 Critical + suggestions,
probe-verified by the reviewer):

- PATH is pinned to a value the stage step records before any branch
  code runs, and LD_PRELOAD/LD_AUDIT/LD_LIBRARY_PATH are dropped, at the
  top of every PAT step and both gate steps — a $GITHUB_ENV-planted PATH
  or preload would otherwise swap the git/sha256sum/bash the digest gate
  itself runs on.
- The Prepare step (PAT-bearing, previously unhardened) now takes the
  same hermetic preamble as the push steps; all three PAT preambles are
  pinned identical by the contract test.
- The throwaway global config is created with mktemp, not a fixed
  literal path a same-user watcher could re-plant into after the seed.
- The env-strip list gains GIT_ALLOW_PROTOCOL (env twin of
  protocol.allow), GIT_COMMON_DIR / GIT_OBJECT_DIRECTORY /
  GIT_ALTERNATE_OBJECT_DIRECTORIES / GIT_SHALLOW_FILE (repo-redirect
  twins), across all PAT and gate copies; the salvage/fork fetches carry
  -c fetch.recurseSubmodules=false -c protocol.ext.allow=never so a
  planted submodule cannot execute an ext:: URL with the PAT.
- resanitize removes .git/commondir and .git/shallow (the file twins of
  GIT_COMMON_DIR/GIT_SHALLOW_FILE), and Push-and-report refuses to push
  a HEAD that is not the gate's recorded verified_head — closing the
  repo-redirect path that pushed attacker content.
- The gate runner (run-autofix-review-verification.sh) is now digest-
  verified before both gate passes, like resanitize already was: the
  branch runs its own build/test between the passes, so an unverified
  copy would let it define its own verdict.
- Contract tests pin every new surface: the three identical PAT
  preambles, the full unset var set, the mktemp path, the trusted-PATH
  wiring, the two gate-runner digest checks, the recurse/protocol pins,
  the HEAD==verified_head guard, and the commondir/shallow removal.

* fix(ci): pin gh env channels, harmonize allowlist subsection slots

Round-4 follow-ups:
- Pin GH_HOST=github.com and unset GH_TOKEN/GH_ENTERPRISE_TOKEN/
  GH_CONFIG_DIR before the first gh call in all three PAT steps, so a
  $GITHUB_ENV-planted GH_HOST cannot spoof the identity check and a
  planted GH_TOKEN cannot outrank the inline one.
- Harmonize the local allowlist's remote/submodule subsection slots to
  .+ (matching the denylist comment and preventing a dotted-name remote
  from silently losing its url/fetch on every resanitize).

* fix(ci): pin gh config dir and push the exact verified object

Round-5 closable findings before landing:
- R5-7: pin GH_CONFIG_DIR to a fresh mktemp -d instead of unsetting it,
  so PAT-bearing gh calls no longer fall back to the attacker-writable
  ~/.config/gh (whose config.yml can carry http_unix_socket and other
  transport reroutes) on the shared HOME.
- R5-8: push the exact verified commit object (PUSH_SHA:refs/heads/...),
  not symbolic HEAD which the push would re-resolve — closing the
  check-then-use race the verified-HEAD guard was added to close. PUSH_SHA
  is pinned to VERIFIED_HEAD under the guard and re-pinned to the merge
  result after each salvage merge.

The remaining round-5 Criticals (BASH_ENV/BASH_FUNC_* and LD_PRELOAD
executing at step-shell startup before any unset runs; GITHUB_OUTPUT
writable by gate-run branch code) are not closable from inside a Actions
step — they require runner-level isolation and are tracked as a
follow-up.
2026-08-13 11:39:04 +00:00
..
ai-release-notes-workflow.test.js fix(release): keep notes anchored and cap the release body (#8199) 2026-07-31 09:55:38 +00:00
audit-runtime-critical.test.js ci: run Windows merge queue tests on ECS (#8386) 2026-08-05 12:14:42 +00:00
build-and-publish-image-workflow.test.js ci(autofix): restore sandbox image flow (#6261) 2026-07-03 15:30:58 +00:00
capture-tmux-ci.test.js ci: install tmux and zip tooling on the Linux test lane, and pin it (#8792) 2026-08-11 05:47:39 +00:00
check-build-status.test.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
check-i18n.test.ts fix(cli): localize approval mode UI labels (#6592) 2026-07-11 00:07:03 +00:00
check-voice-guard-sync.test.js feat(voice): support trusted private ASR base URLs (#8350) 2026-08-06 14:04:57 +00:00
chrome-extension-package.test.js fix(ci): cover release integration regressions (#5994) 2026-06-29 11:54:11 +00:00
ci-flaky-rerun-workflow.test.js fix(ci): stop a slow patrol classifier from killing every flaky rerun (#7358) 2026-07-21 02:33:51 +00:00
ci-flaky-rerun.test.js feat(ci): auto-open a deflake fix issue for confirmed flaky tests (#7231) 2026-07-19 16:49:29 +00:00
clean-package-build-artifacts.test.js test(core): stabilize file history eviction test (#6637) 2026-07-10 06:39:52 +00:00
cli-entry.test.js fix(cli): preserve Qwen Review startup version in footers (#8431) 2026-08-04 14:58:56 +00:00
comment-attachment-guard-workflow.test.js ci: route trusted-author fork PRs and no-checkout jobs to the ECS pool (#8502) 2026-08-04 03:48:24 +00:00
desktop-oss-workflow.test.js fix(desktop): harden release pipeline (#9009) 2026-08-12 16:38:12 +00:00
dev.test.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
e2e-workflow.test.js fix(ci): keep the post-merge E2E signal on main alive (#7795) 2026-07-28 11:54:52 +00:00
generate-changelog.test.js feat(release): generate AI-assisted release notes (#6756) 2026-07-12 13:00:22 +00:00
generate-release-notes.test.js ci: run Windows merge queue tests on ECS (#8386) 2026-08-05 12:14:42 +00:00
get-release-version-python-sdk.test.js feat(sdk-python): add network timeouts to release version helper (#3833) 2026-05-05 19:25:00 +08:00
get-release-version.test.js fix(release): bump preview base past published stable (#7978) 2026-07-29 23:21:00 +00:00
install-script.test.js ci: run Windows merge queue tests on ECS (#8386) 2026-08-05 12:14:42 +00:00
integration-vitest-config.test.ts fix(tests): apply integration worker limits to forks (#8689) 2026-08-08 00:53:33 +00:00
issue-triage-ownership-workflow.test.js ci: remove broken legacy scheduled PR triage workflow (#8434) 2026-08-03 10:20:42 +00:00
lint.test.js fix(ci): cache downloaded linters on ECS runners (#9001) 2026-08-13 05:13:23 +00:00
live-host-oss-workflow.test.js fix(ci): restore Live Host release mirroring (#8917) 2026-08-11 07:08:26 +00:00
main-ci-failure-issue-workflow.test.js fix(ci): keep the post-merge E2E signal on main alive (#7795) 2026-07-28 11:54:52 +00:00
no-ak-integration-ci.test.js fix(tests): avoid blocking integration test cleanup (#8688) 2026-08-08 02:17:17 +00:00
package-assets.test.js chore(deps): bump sharp to ^0.35.0 to resolve GHSA-f88m-g3jw-g9cj (#8952) 2026-08-13 06:56:10 +00:00
package-scripts.test.js fix(tests): apply integration worker limits to forks (#8689) 2026-08-08 00:53:33 +00:00
pr-force-push-reminder-workflow.test.js ci(autofix): run agents on dedicated ECS runners (#6207) 2026-07-03 07:40:07 +00:00
pr-self-report-label.test.js fix(ci): route workflow label mutations through REST (#8761) 2026-08-09 15:05:15 +00:00
qwen-autofix-fork-bridge-workflow.test.js feat(autofix): bridge fork-PR reviews into the credentialed review lane (#8676) 2026-08-07 16:11:48 +00:00
qwen-autofix-workflow.test.js fix(ci): make autofix verification gates hermetic to runner git config (#8961) 2026-08-13 11:39:04 +00:00
qwen-fleet-shepherd-workflow.test.js feat(autofix): bridge fork-PR reviews into the credentialed review lane (#8676) 2026-08-07 16:11:48 +00:00
qwen-pr-review-workflow.test.js perf(ci): tighten the automatic review kill switch for micro diffs (#8774) 2026-08-10 04:01:36 +00:00
qwen-repo-hygiene-workflow.test.js fix(ci): route workflow label mutations through REST (#8761) 2026-08-09 15:05:15 +00:00
qwen-resolve-workflow.test.js fix(ci): keep the review workflow under the expression-length limit (#8720) 2026-08-08 05:53:35 +00:00
qwen-triage-finalize-workflow.test.js fix(ci): rename triage status marker to avoid duplicate-guard collision (#7723) 2026-07-26 15:51:22 +00:00
qwen-triage-workflow.test.js perf(ci): make the triage budget operator-tunable and raise it (#8810) 2026-08-10 12:40:21 +00:00
release-helpers.test.js refactor: extract shared release helper utilities (#3834) 2026-05-05 10:15:17 +08:00
release-sdk-workflow.test.js fix(ci): skip empty SDK release PR (#6861) 2026-07-14 13:19:42 +00:00
release-workflow.test.js feat(review): say so when the bundle is older than the review it runs (#8390) 2026-08-07 03:21:26 +00:00
review-source-digest.test.ts feat(review): say so when the bundle is older than the review it runs (#8390) 2026-08-07 03:21:26 +00:00
review-worktree-cleanup-workflow.test.js fix(ci): clean review worktrees after cancellation (#8474) 2026-08-05 02:39:36 +00:00
sandbox-command.test.js fix(scripts): avoid shell injection in sandbox command detection (#6108) 2026-07-01 16:20:40 +08:00
sdk-java-workflow.test.js ci: route trusted-author fork PRs and no-checkout jobs to the ECS pool (#8502) 2026-08-04 03:48:24 +00:00
sdk-node-exporter-stub.test.js perf(telemetry): lazy-load the SDK and split OTLP exporter chains by protocol (#7276) 2026-07-21 07:35:30 +00:00
serve-fast-path-bundle-check.test.js feat(ci): fail the startup bundle check when the CLI entry is hoisted into a chunk (#8203) 2026-07-31 08:57:57 +00:00
start.test.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
test-setup.ts feat(installer): add standalone archive installation (#3776) 2026-05-11 13:25:48 +08:00
update-ecs-runner-qwen-workflow.test.js fix(ci): reconcile ECS runner updater on workflow changes (#8373) 2026-08-02 09:25:17 +00:00
upload-aliyun-oss-assets.test.js feat(installer): add standalone hosted install and uninstall flow (#3828) 2026-05-21 11:57:10 +08:00
verify-capture.test.js fix(ci): avoid verify capture color conflict (#8236) 2026-07-31 14:15:40 +00:00
vitest.config.ts ci: run Windows merge queue tests on ECS (#8386) 2026-08-05 12:14:42 +00:00
workflow-helpers.js ci: run Windows merge queue tests on ECS (#8386) 2026-08-05 12:14:42 +00:00
workspaces.test.js feat(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) 2026-06-11 21:57:20 +08:00