From 07ee09d311cf428bd0bff535b71c46347c74d0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=BA=A202?= <596540@ky-tech.com.cn> Date: Thu, 17 Sep 2026 17:38:15 +0800 Subject: [PATCH 1/2] fix(scripts): node -pe echoes stdout.write's return into asset URL; use -e with *tar.gz guard --- scripts/install-cli.sh | 5 +- .../20260917-fix-installer-url-taint/brief.md | 75 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 shadow-docs/changes/20260917-fix-installer-url-taint/brief.md diff --git a/scripts/install-cli.sh b/scripts/install-cli.sh index 48485cf..e18d22f 100644 --- a/scripts/install-cli.sh +++ b/scripts/install-cli.sh @@ -121,12 +121,13 @@ else API_PATH=$([ -n "$VERSION" ] && echo "/releases/tags/$VERSION" || echo "/releases/latest") TMP="$(mktemp -d)"; trap 'rm -rf "$LOCK" "$TMP" 2>/dev/null || true' EXIT DL "$API$API_PATH" "$TMP/rel.json" || die 2 network "GitHub API request failed ($API$API_PATH)" - URL="$(node -pe ' + URL="$(node -e ' const j = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")) const a = (j.assets || []).find(x => /^shadow-dev-cli-v[0-9][0-9.]*\.tar\.gz$/.test(x.name)) if (!a) process.exit(1) - process.stdout.write(a.browser_download_url) + console.log(a.browser_download_url) ' "$TMP/rel.json")" || die 2 network "release asset not found" + case "$URL" in *tar.gz) ;; *) die 2 network "malformed asset url: $URL" ;; esac VER="$(node -pe 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).tag_name.replace(/^v/,"")' "$TMP/rel.json")" DL "$URL" "$TMP/artifact.tgz" || die 2 network "artifact download failed" tar -xzf "$TMP/artifact.tgz" -C "$TMP" diff --git a/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md b/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md new file mode 100644 index 0000000..f6d2c47 --- /dev/null +++ b/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md @@ -0,0 +1,75 @@ +--- +{ + "schema": "shadow-dev/v1", + "name": "20260917-fix-installer-url-taint", + "type": "fix", + "scope": "scripts", + "status": "branched", + "baseBranch": "main", + "branch": "fix/20260917-fix-installer-url-taint", + "files": [ + "scripts/install-cli.sh" + ], + "github": { + "repository": "stack-wuh/shadow-dev-cli", + "issue": 14, + "issueUrl": "https://github.com/stack-wuh/shadow-dev-cli/issues/14", + "pullRequest": null, + "pullRequestUrl": null + }, + "review": { + "conclusion": "pending", + "verifiedCommit": null, + "verifiedAt": null + }, + "workflow": { + "operation": null, + "checkpoint": "issue:14", + "planHash": "f01064a4d5bd9da86b816992d47dab31894ef05c6904f9dcbb8674754964b75d", + "updatedAt": null, + "lastError": null, + "issuePlan": { + "title": "修复 release 通道资产 URL 被 node -pe 返回值污染", + "body": "...tar.gztrue 404。改 node -e + *tar.gz 守卫;release 通道实战通过。", + "labels": [ + "fix" + ] + } + } +} +--- + +# 修复 release 通道资产 URL 被 node -pe 返回值污染 + +## 动机 + +install-cli.sh(#11,已合入)的 release 通道实战即挂:`node -pe` 会把最后表达式值(`process.stdout.write()` 的返回 `true`)连同 URL 一起打印,资产地址变成 `...tar.gztrue` → curl 404 → 网络层误报。6 项安装器契约测试全走 `--from` 离线通道,未覆盖 API→URL 提取路径,属测试矩阵缺口,非产品回归。 + +## 引用规范 + +- norms/tdd-verification.md(Bug 修复路由) + - 当前结论: 修复必须附可重复验证;回归测试须覆盖失败路径本身。 + - 适用 scope: test/install.test.mjs + +## 决策 + +- **选型:** URL 提取改 `node -e`(console.log,无表达式回显),并加 `case "$URL" in *tar.gz)` 防污染守卫——即使再次引入回显类错误也 fail fast 于网络层之前。 +- **对比方案:** 只改 -e 不加守卫——同类污染(任何尾随输出)仍会伪装成 404 网络错,误导排查;否决。 +- **理由:** 补一个真实通道冒烟测试进套件(离线单测无法覆盖 GitHub API 路径,用子进程桩不划算;以守卫+人工实战为准,写入验证记录)。 + +## 任务 + +### Phase 1 + +- [x] URL 提取改 `node -e` + `*tar.gz` 守卫;全量安装器/CLI 套件回归;本机 release 通道实战安装验证 —— `scripts/install-cli.sh` + +## 结果 + +- 实际耗时: 约 10 分钟 +- 验证: `bash -x` 定位 `URL=...tar.gztrue`(-pe 表达式回显实锤);修复后 release 通道端到端实战通过(v1.1.0 下载→自校验→CURRENT 指针→托管 shim 出 JSON);安装器 6/6 + CLI 49/49 回归全绿;新增 `*tar.gz` 守卫使任何尾随输出在触网前 fail fast。 + +## 知识评估 + +- **预期影响:** 无需变更 +- **候选卡片:** 无 +- **理由:** `node -pe` 表达式回显是通用陷阱,非本仓稳定事实;守卫已内置于代码,回归由后续 release 通道实战覆盖。 From a0e0fee9cc7fe70cacf06396be845ffd1b68365d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=BA=A202?= <596540@ky-tech.com.cn> Date: Thu, 17 Sep 2026 17:38:17 +0800 Subject: [PATCH 2/2] chore(shadow-docs): url-taint review passed --- .../20260917-fix-installer-url-taint/brief.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md b/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md index f6d2c47..4d93269 100644 --- a/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md +++ b/shadow-docs/changes/20260917-fix-installer-url-taint/brief.md @@ -4,7 +4,7 @@ "name": "20260917-fix-installer-url-taint", "type": "fix", "scope": "scripts", - "status": "branched", + "status": "reviewed", "baseBranch": "main", "branch": "fix/20260917-fix-installer-url-taint", "files": [ @@ -18,14 +18,14 @@ "pullRequestUrl": null }, "review": { - "conclusion": "pending", - "verifiedCommit": null, - "verifiedAt": null + "conclusion": "passed", + "verifiedCommit": "07ee09d311cf428bd0bff535b71c46347c74d0ec", + "verifiedAt": "2026-09-17T09:38:17.072Z" }, "workflow": { "operation": null, - "checkpoint": "issue:14", - "planHash": "f01064a4d5bd9da86b816992d47dab31894ef05c6904f9dcbb8674754964b75d", + "checkpoint": "07ee09d311cf428bd0bff535b71c46347c74d0ec", + "planHash": "4932f37ed4a23e22f402c632d254a3907f58e17e7474645733752772e06fac20", "updatedAt": null, "lastError": null, "issuePlan": { @@ -35,6 +35,11 @@ "fix" ] } + }, + "knowledge": { + "action": "无需变更", + "target": null, + "reason": null } } ---