Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions scripts/install-cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
80 changes: 80 additions & 0 deletions shadow-docs/changes/20260917-fix-installer-url-taint/brief.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
{
"schema": "shadow-dev/v1",
"name": "20260917-fix-installer-url-taint",
"type": "fix",
"scope": "scripts",
"status": "reviewed",
"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": "passed",
"verifiedCommit": "07ee09d311cf428bd0bff535b71c46347c74d0ec",
"verifiedAt": "2026-09-17T09:38:17.072Z"
},
"workflow": {
"operation": null,
"checkpoint": "07ee09d311cf428bd0bff535b71c46347c74d0ec",
"planHash": "4932f37ed4a23e22f402c632d254a3907f58e17e7474645733752772e06fac20",
"updatedAt": null,
"lastError": null,
"issuePlan": {
"title": "修复 release 通道资产 URL 被 node -pe 返回值污染",
"body": "...tar.gztrue 404。改 node -e + *tar.gz 守卫;release 通道实战通过。",
"labels": [
"fix"
]
}
},
"knowledge": {
"action": "无需变更",
"target": null,
"reason": null
}
}
---

# 修复 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 通道实战覆盖。
Loading