Files
skills/skills/app-dev/scripts/selftest-check-app.sh
T
sunjunzhao 3010fca54c fix(app-dev): align embedCsp contract with platform boolean flags
- embedCsp is connectSrc + wasmUnsafeEval + blob (all default off), not per-directive lists
- cross-origin script/worker loading is unsupported; fetch + Blob is the supported path
- check-app.sh/selftest validate flags and platform capability boundaries
2026-10-26 01:04:35 +08:00

138 lines
5.5 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# check-app.sh 自测:用临时 fixture 覆盖大产物 / embedCsp 规则,防止技能与平台校验漂移。
# 用法: bash scripts/selftest-check-app.sh 退出码 0=全部断言通过。
set -u
HERE=$(cd "$(dirname "$0")" && pwd)
CHECK="$HERE/check-app.sh"
[ -f "$CHECK" ] || { echo "找不到 $CHECK" >&2; exit 2; }
ROOT=$(mktemp -d)
trap 'rm -rf "$ROOT"' EXIT
N=0; BAD=0
# fixture <name>:建一个最小 micro 仓库骨架(manifest/Dockerfile/入口/前端)。
fixture() {
local dir="$ROOT/$1"
mkdir -p "$dir/web/src/skin" "$dir/dist"
cat >"$dir/app.manifest.json" <<'JSON'
{ "listingKey": "selftest-app", "name": "自测应用", "version": "1.0.0", "type": "micro" }
JSON
cat >"$dir/Dockerfile" <<'DOCKER'
FROM tickshield-node-runtime:base
RUN ln -s /opt/xgent/runtime/node_modules /app/node_modules
DOCKER
cat >"$dir/server.js" <<'JS'
import { createGuard } from '@aigrego/guard';
const guard = createGuard({ portalUrl: 'http://x', appKey: 'selftest-app', appSecret: 'x' });
export function handler(req, res) {}
export const routes = guard.routes([]);
JS
cat >"$dir/web/index.html" <<'HTML'
<!doctype html><html><body>
<script src="/apps/selftest-app/vendor/portal-sdk.js"></script>
<script src="/apps/selftest-app/app.js"></script>
</body></html>
HTML
cat >"$dir/web/src/main.tsx" <<'TS'
import { initSkin } from './skin/apply';
initSkin();
TS
printf "export const SKINS = [{ key: 'light' }, { key: 'dark' }, { key: 'jade' }];\n// SKIN_SNAPSHOT_DATE = '2026-10-22'\n" >"$dir/web/src/skin/tokens.ts"
printf ":root { font-family: system-ui, -apple-system, 'PingFang SC'; font-size: 13px; }\n" >"$dir/web/app.css"
echo '<!doctype html><html><body>app</body></html>' >"$dir/dist/index.html"
echo "$dir"
}
# set_embed <manifest> <json-array>:写入 embedCsp.connectSrc(node 优先,退化 python3)。
set_embed() {
set_embed_json "$1" "{\"connectSrc\":$2}"
}
# set_embed_json <manifest> <embedCsp-json>:写入任意 embedCsp 对象。
set_embed_json() {
local f="$1" body="$2"
if command -v node >/dev/null 2>&1; then
node -e 'const fs=require("fs");const [p,src]=process.argv.slice(1);const o=JSON.parse(fs.readFileSync(p));o.embedCsp=JSON.parse(src);fs.writeFileSync(p,JSON.stringify(o,null,2));' "$f" "$body"
else
python3 - "$f" "$body" <<'PY'
import json, sys
p, body = sys.argv[1], json.loads(sys.argv[2])
o = json.load(open(p))
o['embedCsp'] = body
json.dump(o, open(p, 'w'), ensure_ascii=False, indent=2)
PY
fi
}
# expect <name> <dir> <contains-regex> <want:1|0>
expect() {
local name="$1" dir="$2" pat="$3" want="$4" out got
out=$(bash "$CHECK" "$dir" 2>&1)
N=$((N + 1))
if printf '%s\n' "$out" | grep -qE "$pat"; then got=1; else got=0; fi
if [ "$got" = "$want" ]; then
echo "ok $name"
else
BAD=$((BAD + 1))
echo "FAIL ${name}(期望命中=$want 模式=${pat})"
printf '%s\n' "$out" | sed 's/^/ /'
fi
}
# 1) dist tgz 超限且未声明 embedCsp → 两条 die + RESULT FAIL。
D=$(fixture over-limit)
dd if=/dev/urandom of="$D/dist/core.bin" bs=1048576 count=70 2>/dev/null
expect "tgz 超限诊断" "$D" 'dist 打 tgz 后 [0-9]+MiB > 平台 64MiB' 1
expect "超限未声明外置" "$D" 'dist 超限但 manifest 未声明 embedCsp' 1
expect "超限 RESULT FAIL" "$D" '^RESULT: FAIL$' 1
# 2) embedCsp 带路径 → 结构非法。
D=$(fixture embed-bad)
set_embed "$D/app.manifest.json" '["https://cdn.example.com/assets"]'
expect "embedCsp 路径非法" "$D" 'embedCsp 非法' 1
# 3) 平台能力边界:跨域直载不支持;blob / wasm 需显式开关。
D=$(fixture worker-ext)
echo "new Worker('https://cdn.example.com/w.js');" >>"$D/web/src/main.tsx"
expect "跨域 Worker 直载拦截" "$D" '不支持跨域直载 Worker' 1
D=$(fixture ext-script)
echo '<script src="https://cdn.example.com/lib.js"></script>' >>"$D/web/index.html"
expect "跨域脚本直载拦截" "$D" '不支持跨域直载脚本' 1
D=$(fixture blob-worker)
echo "const u = URL.createObjectURL(new Blob([])); new Worker(u);" >>"$D/web/src/main.tsx"
expect "blob 未开启拦截" "$D" 'embedCsp.blob 未开启' 1
set_embed_json "$D/app.manifest.json" '{"blob":true}'
expect "blob 开启放行" "$D" 'embedCsp.blob 未开启' 0
D=$(fixture wasm-use)
echo "WebAssembly.instantiate(bytes);" >>"$D/web/src/main.tsx"
expect "wasm 未开启拦截" "$D" 'wasmUnsafeEval 未开启' 1
set_embed_json "$D/app.manifest.json" '{"wasmUnsafeEval":true}'
expect "wasm 开启放行" "$D" 'wasmUnsafeEval 未开启' 0
# 2b) 开关必须是布尔。
D=$(fixture embed-bad-flag)
set_embed_json "$D/app.manifest.json" '{"blob":"yes"}'
expect "开关非布尔拦截" "$D" 'embedCsp 非法' 1
# 4) 合规声明 + 使用点 + 小 dist → 不误报。
D=$(fixture embed-ok)
set_embed "$D/app.manifest.json" '["https://cdn.example.com"]'
echo "fetch('https://cdn.example.com/apps/selftest-app/1.0.0/core.data');" >>"$D/web/src/main.tsx"
expect "合规声明不报非法" "$D" 'embedCsp 非法' 0
expect "合规声明不报超限" "$D" 'dist 打 tgz 后' 0
expect "合规声明不报死配置" "$D" '找不到使用点' 0
expect "输出始终带结论行" "$D" '^RESULT: (PASS|FAIL)$' 1
# 5) 免提示自检入口:无 AGENTS.md → warn;有且含 check-app.sh → 不再 warn。
expect "缺 AGENTS.md 提示" "$D" '缺 AGENTS.md 自检硬规则' 1
printf '# AGENTS.md\n开工前先跑 `bash .agents/skills/app-dev/scripts/check-app.sh .`。\n' >"$D/AGENTS.md"
expect "有 AGENTS.md 不提示" "$D" '缺 AGENTS.md 自检硬规则' 0
echo "---"
echo "selftest: $N 项,失败 $BAD"
[ "$BAD" -gt 0 ] && exit 1
exit 0