-
Notifications
You must be signed in to change notification settings - Fork 0
feat: W4-R3 签名证据包绑定面+回溯引擎+演练工作流(IR-0006) #456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,108 @@ | ||||||||||||||||||||||||||||||||
| name: attest-drill | ||||||||||||||||||||||||||||||||
| # 签名证据包演练面(IR-0006 W4-R3 / 卡 #420 / SC-4 / ADR-0103) | ||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||
| # 全链(AC-8e 部署可回溯): | ||||||||||||||||||||||||||||||||
| # .github@SHA 产物(git archive tar.gz) | ||||||||||||||||||||||||||||||||
| # → CIW attest_pack(SBOM+attestation,RS256——私钥 ATTEST_SIGNING_KEY secret) | ||||||||||||||||||||||||||||||||
| # → attest_verify 机械验证(AC-8f:digest 复算+验签,INV-01 零 LLM) | ||||||||||||||||||||||||||||||||
| # → 绑定 archive evidence/(write_evidence 判定记录+证据包文件,PR 面) | ||||||||||||||||||||||||||||||||
| # → attest-trace 回溯实测(账本反查→包→commit→git archive 重建→内容级验证) | ||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||
| # 产物本体不入 git(INV-06 摘要纪律):git 侧只存 SBOM/attestation/事件摘要; | ||||||||||||||||||||||||||||||||
| # 产物可由 materials.git_commit 经 git archive 复得。 | ||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||
| workflow_dispatch: {} | ||||||||||||||||||||||||||||||||
| # 骨架期手动触发;真实部署产物管线接入后转周期(release/tag 钩子) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| permissions: {} | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||
| drill: | ||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||
| timeout-minutes: 15 | ||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||
| persist-credentials: false | ||||||||||||||||||||||||||||||||
| - name: 拉取 CIW 证据包工具(sparse:pipeline/attestation) | ||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||
| GH_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }} | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||
| git clone --depth 1 --filter=blob:none --sparse \ | ||||||||||||||||||||||||||||||||
| "https://x-access-token:${GH_TOKEN}@github.com/Cloudbird-Software/CI-Workflows.git" ciw | ||||||||||||||||||||||||||||||||
| git -C ciw sparse-checkout set pipeline/attestation | ||||||||||||||||||||||||||||||||
| - name: 产物 + 证据包(pack)+ 机械验证(AC-8f) | ||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||
| ATTEST_SIGNING_KEY: ${{ secrets.ATTEST_SIGNING_KEY }} | ||||||||||||||||||||||||||||||||
| CARD: Cloudbird-Software/.github#420 | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||
| SHA=$(git rev-parse HEAD) | ||||||||||||||||||||||||||||||||
| git archive --format=tar.gz -o artifact.tar.gz "$SHA" | ||||||||||||||||||||||||||||||||
| python3 ciw/pipeline/attestation/attest_pack.py \ | ||||||||||||||||||||||||||||||||
| --artifact artifact.tar.gz --repo Cloudbird-Software/.github \ | ||||||||||||||||||||||||||||||||
| --commit "$SHA" --card "$CARD" --tenant cloudbird-internal --out bundle | ||||||||||||||||||||||||||||||||
| # 首发验证:字节级 digest + SBOM 重算 + RS256 验签 + commit/card 锚 | ||||||||||||||||||||||||||||||||
| python3 ciw/pipeline/attestation/attest_verify.py \ | ||||||||||||||||||||||||||||||||
| --bundle bundle --artifact artifact.tar.gz \ | ||||||||||||||||||||||||||||||||
| --pubkey ciw/pipeline/attestation/keys/attest-pub.pub \ | ||||||||||||||||||||||||||||||||
| --expect-commit "$SHA" --expect-card "$CARD" | ||||||||||||||||||||||||||||||||
| echo "SHA=$SHA" >> "$GITHUB_ENV" | ||||||||||||||||||||||||||||||||
| - name: 绑定 archive evidence/ 判定记录(write_evidence 唯一写入器,PR 面) | ||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||
| GH_TOKEN: ${{ secrets.GOVERNANCE_TOKEN }} | ||||||||||||||||||||||||||||||||
| CARD: Cloudbird-Software/.github#420 | ||||||||||||||||||||||||||||||||
| RUN_ID: ${{ github.run_id }} | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||
| ART_D=$(python3 -c "import json;print(json.load(open('bundle/attestation.json'))['subject']['digest']['sha256'])") | ||||||||||||||||||||||||||||||||
| SBOM_D=$(python3 -c "import json;print(json.load(open('bundle/attestation.json'))['materials']['sbom_sha256'])") | ||||||||||||||||||||||||||||||||
| BID="evidence/attestations/$SHA-${ART_D:0:12}" | ||||||||||||||||||||||||||||||||
| git clone --depth 5 "https://x-access-token:${GH_TOKEN}@github.com/Cloudbird-Software/archive.git" arch | ||||||||||||||||||||||||||||||||
| mkdir -p "arch/$BID" | ||||||||||||||||||||||||||||||||
| cp bundle/sbom.json bundle/attestation.json "arch/$BID/" | ||||||||||||||||||||||||||||||||
| cat > ev.json <<EV | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| "ts": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", | ||||||||||||||||||||||||||||||||
| "kind": "decision", | ||||||||||||||||||||||||||||||||
| "action": "attestation-pack", | ||||||||||||||||||||||||||||||||
| "verdict": "signed", | ||||||||||||||||||||||||||||||||
| "subject": {"wave": "W4-R3", "card": "$CARD", "tenant": "cloudbird-internal", "commit": "$SHA"}, | ||||||||||||||||||||||||||||||||
| "actor": {"identity": "attest-drill-bot", "role": "bot", "model": null}, | ||||||||||||||||||||||||||||||||
| "inputs_digest": "sha256:$(sha256sum "arch/$BID/attestation.json" | cut -d' ' -f1)", | ||||||||||||||||||||||||||||||||
| "payload": "{\"bundle_ref\": \"$BID\", \"artifact_digest\": \"$ART_D\", \"sbom_sha256\": \"$SBOM_D\", \"verify\": \"signed+verified\"}" | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| EV | ||||||||||||||||||||||||||||||||
| python3 arch/scripts/write_evidence.py --event ev.json --ledger arch/evidence/ledger.jsonl | ||||||||||||||||||||||||||||||||
| python3 arch/scripts/write_evidence.py --checkpoint --ledger arch/evidence/ledger.jsonl --checkpoints arch/evidence/checkpoints | ||||||||||||||||||||||||||||||||
| python3 arch/scripts/verify_evidence.py --ledger arch/evidence/ledger.jsonl --checkpoints arch/evidence/checkpoints | ||||||||||||||||||||||||||||||||
| git -C arch config user.name attest-drill-bot && git -C arch config user.email attest-bot@users.noreply.github.com | ||||||||||||||||||||||||||||||||
| BR="attest-$RUN_ID" | ||||||||||||||||||||||||||||||||
| git -C arch checkout -b "$BR" | ||||||||||||||||||||||||||||||||
| git -C arch add -A | ||||||||||||||||||||||||||||||||
| git -C arch commit -m "evidence: 签名证据包绑定(IR-0006 W4-R3,run #$RUN_ID,链验通过)" | ||||||||||||||||||||||||||||||||
| git -C arch push "https://x-access-token:${GH_TOKEN}@github.com/Cloudbird-Software/archive.git" "HEAD:refs/heads/$BR" | ||||||||||||||||||||||||||||||||
| export GH_TOKEN | ||||||||||||||||||||||||||||||||
| gh pr create --repo Cloudbird-Software/archive --head "$BR" --title "evidence: 签名证据包绑定(IR-0006 W4-R3 演练,run #$RUN_ID)" \ | ||||||||||||||||||||||||||||||||
| --body "Card: Cloudbird-Software/.github#420 | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| 自动生成(attest-drill run #$RUN_ID):SBOM+attestation 入 evidence/attestations/,判定记录经 write_evidence 唯一写入器追加(append-only+checkpoint 前进)。verify.yml 链验绿后可合。" \ | ||||||||||||||||||||||||||||||||
| | tee pr-url.txt | ||||||||||||||||||||||||||||||||
| echo "ARCH_PR=$(grep -o '[0-9]*' pr-url.txt | tail -1)" >> "$GITHUB_ENV" | ||||||||||||||||||||||||||||||||
| - name: 回溯实测(AC-8e:从判定记录反查产物全链) | ||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||
| CARD: Cloudbird-Software/.github#420 | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||
| # 从 PR 分支账本反查(合并前即可回溯——绑定即生效) | ||||||||||||||||||||||||||||||||
| BR="attest-$RUN_ID" | ||||||||||||||||||||||||||||||||
|
Comment on lines
+95
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🐛 建议修复 - name: 回溯实测(AC-8e:从判定记录反查产物全链)
env:
CARD: Cloudbird-Software/.github#420
+ RUN_ID: ${{ github.run_id }}
run: |📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| git -C arch fetch origin "$BR" 2>/dev/null || true | ||||||||||||||||||||||||||||||||
| bash governance/attest-trace.sh \ | ||||||||||||||||||||||||||||||||
| --ledger arch/evidence/ledger.jsonl \ | ||||||||||||||||||||||||||||||||
| --bundle-root arch \ | ||||||||||||||||||||||||||||||||
| --git-repo . \ | ||||||||||||||||||||||||||||||||
| --verify-cmd "python3 ciw/pipeline/attestation/attest_verify.py --bundle {BUNDLE} --artifact {ARTIFACT} --pubkey ciw/pipeline/attestation/keys/attest-pub.pub --content-only --expect-commit $SHA --expect-card $CARD" | ||||||||||||||||||||||||||||||||
| echo "OK 回溯全链绿(账本→证据包→commit→git archive 重建→内容级验证)——archive PR #$ARCH_PR 待 verify.yml 绿后合并" | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,67 @@ | ||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||
| # attest-trace.sh —— 部署可回溯:从 evidence/ 判定记录反查产物全链(IR-0006 W4-R3 / AC-8e) | ||||||||||||||||||||||
| # | ||||||||||||||||||||||
| # 回溯链(机械,零 LLM): | ||||||||||||||||||||||
| # 产物 digest ──→ archive evidence/ledger.jsonl(action=attestation-pack 事件: | ||||||||||||||||||||||
| # payload.bundle_ref + artifact_digest)──→ 证据包(attestation.json+sbom.json) | ||||||||||||||||||||||
| # ──→ materials.git_commit ──→ git archive 重建产物 ──→ 内容级验证(verify-cmd) | ||||||||||||||||||||||
| # | ||||||||||||||||||||||
| # 事件 payload 契约(write_evidence 写入,4KB 内联 INV-06): | ||||||||||||||||||||||
| # {"bundle_ref": "evidence/attestations/<id>", "artifact_digest": "<sha256>", | ||||||||||||||||||||||
| # "sbom_sha256": "<sha256>", "verify": "signed+verified"} | ||||||||||||||||||||||
| # | ||||||||||||||||||||||
| # 用法: | ||||||||||||||||||||||
| # attest-trace.sh --ledger <ledger.jsonl> --bundle-root <archive 仓根> \ | ||||||||||||||||||||||
| # --git-repo <产物源仓> --verify-cmd "<模板:{BUNDLE} {ARTIFACT} 代入>" | ||||||||||||||||||||||
| # 退出码:0=回溯全链绿 | 1=链断(事件/包/digest/验证任一不符)| 2=infra | ||||||||||||||||||||||
| set -uo pipefail | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| die2() { echo "FATAL attest-trace: $*" >&2; exit 2; } | ||||||||||||||||||||||
| die1() { echo "REJECT attest-trace: $*" >&2; exit 1; } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| while [[ $# -gt 0 ]]; do | ||||||||||||||||||||||
| case "$1" in | ||||||||||||||||||||||
| --ledger) LEDGER="$2"; shift 2 ;; | ||||||||||||||||||||||
| --bundle-root) ROOT="$2"; shift 2 ;; | ||||||||||||||||||||||
| --git-repo) REPO="$2"; shift 2 ;; | ||||||||||||||||||||||
| --verify-cmd) VCMD="$2"; shift 2 ;; | ||||||||||||||||||||||
| *) die2 "未知参数: $1" ;; | ||||||||||||||||||||||
| esac | ||||||||||||||||||||||
| done | ||||||||||||||||||||||
| [[ -n "${LEDGER:-}" && -n "${ROOT:-}" && -n "${REPO:-}" && -n "${VCMD:-}" ]] \ | ||||||||||||||||||||||
| || { echo "用法: attest-trace.sh --ledger <l> --bundle-root <r> --git-repo <g> --verify-cmd <cmd>" >&2; exit 2; } | ||||||||||||||||||||||
| [[ -f "$LEDGER" ]] || die2 "账本不存在: $LEDGER" | ||||||||||||||||||||||
| [[ -d "$REPO/.git" || -d "$REPO" ]] || die2 "源仓不存在: $REPO" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # ---- 1. 账本反查:最新 attestation-pack 事件 ---- | ||||||||||||||||||||||
| EV=$(grep '"action":"attestation-pack"' "$LEDGER" | tail -1) | ||||||||||||||||||||||
| [[ -n "$EV" ]] || die1 "账本无 attestation-pack 事件(绑定缺失——AC-8e 链断)" | ||||||||||||||||||||||
| PAYLOAD=$(jq -r '.payload' <<<"$EV") | ||||||||||||||||||||||
| [[ -n "$PAYLOAD" && "$PAYLOAD" != "null" ]] || die1 "事件 payload 缺失" | ||||||||||||||||||||||
| BUNDLE_REF=$(jq -r '.bundle_ref' <<<"$PAYLOAD") | ||||||||||||||||||||||
| ART_DIGEST=$(jq -r '.artifact_digest' <<<"$PAYLOAD") | ||||||||||||||||||||||
| COMMIT=$(jq -r '.subject.commit // empty' <<<"$EV") | ||||||||||||||||||||||
| CARD=$(jq -r '.subject.card' <<<"$EV") | ||||||||||||||||||||||
| [[ -n "$BUNDLE_REF" && -n "$ART_DIGEST" ]] || die1 "payload 契约字段缺失(bundle_ref/artifact_digest)" | ||||||||||||||||||||||
| [[ -n "$COMMIT" ]] || die1 "subject.commit 缺失(回溯链断)" | ||||||||||||||||||||||
| echo "OK 账本反查命中:$CARD @ $COMMIT → $BUNDLE_REF" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # ---- 2. 证据包在位 + 记录一致性(payload ↔ attestation 互证) ---- | ||||||||||||||||||||||
| BUNDLE="$ROOT/$BUNDLE_REF" | ||||||||||||||||||||||
| [[ -f "$BUNDLE/attestation.json" && -f "$BUNDLE/sbom.json" ]] \ | ||||||||||||||||||||||
| || die1 "证据包文件缺失: $BUNDLE" | ||||||||||||||||||||||
| SUBJ_DIGEST=$(jq -r '.subject.digest.sha256' "$BUNDLE/attestation.json") | ||||||||||||||||||||||
| SBOM_SHA=$(jq -r '.materials.sbom_sha256' "$BUNDLE/attestation.json") | ||||||||||||||||||||||
| ATTEST_COMMIT=$(jq -r '.materials.git_commit' "$BUNDLE/attestation.json") | ||||||||||||||||||||||
| [[ "$SUBJ_DIGEST" == "$ART_DIGEST" ]] || die1 "payload.artifact_digest ≠ attestation.subject.digest(两套记录漂移)" | ||||||||||||||||||||||
|
Comment on lines
+53
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4. Sbom anchor remains unchecked The trace reads attestation.materials.sbom_sha256 but never reads or compares the event payload's sbom_sha256. Consequently an event can advertise an SBOM digest different from its bound attestation while the trace still reports the payload-to-attestation records as consistent. Agent Prompt
|
||||||||||||||||||||||
| [[ "$ATTEST_COMMIT" == "$COMMIT" ]] || die1 "事件 commit ≠ attestation.materials.git_commit(锚点漂移)" | ||||||||||||||||||||||
| echo "OK 记录一致性绿(payload↔attestation 互证,commit 锚一致)" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # ---- 3. 产物重建(git archive)+ 内容级验证 ---- | ||||||||||||||||||||||
| TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT | ||||||||||||||||||||||
| git -C "$REPO" archive --format=tar.gz -o "$TMP/artifact.tar.gz" "$COMMIT" 2>/dev/null \ | ||||||||||||||||||||||
| || die1 "git archive 失败(commit=$COMMIT 不在源仓?)" | ||||||||||||||||||||||
| CMD=${VCMD//\{BUNDLE\}/$BUNDLE} | ||||||||||||||||||||||
| CMD=${CMD//\{ARTIFACT\}/$TMP/artifact.tar.gz} | ||||||||||||||||||||||
| bash -c "$CMD" || die1 "内容级验证红(产物与 SBOM 漂移或验签失败)" | ||||||||||||||||||||||
|
Comment on lines
+64
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3. Bundle reference enables injection BUNDLE_REF comes from the ledger and is substituted unquoted into a string executed by bash -c. A crafted evidence bundle directory whose name contains shell metacharacters can pass the file checks and execute arbitrary commands under the identity running the trace. Agent Prompt
Comment on lines
+64
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🔒 建议修复+[[ "$BUNDLE_REF" =~ ^evidence/attestations/[A-Za-z0-9._-]+$ ]] \
+ || die1 "bundle_ref 格式非法(拒绝注入字符): $BUNDLE_REF"
+
CMD=${VCMD//\{BUNDLE\}/\"\$1\"}
CMD=${CMD//\{ARTIFACT\}/\"\$2\"}
-bash -c "$CMD" || die1 "内容级验证红(产物与 SBOM 漂移或验签失败)"
+bash -c "$CMD" _ "$BUNDLE" "$TMP/artifact.tar.gz" \
+ || die1 "内容级验证红(产物与 SBOM 漂移或验签失败)"📝 Committable suggestion
Suggested change
🧰 Tools🪛 ast-grep (0.45.2)[error] 65-65: A variable, parameter expansion, or command-substitution result is passed as the command string to (bash-c-variable-injection-bash) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||
| echo "OK 回溯全链绿:$CARD ← 事件 ← 证据包 ← commit $COMMIT ← 产物重建内容一致" | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| #!/usr/bin/env bash | ||
| # test-attest-bind.sh —— W4-R3(#420)AC-8e 部署可回溯入 gate | ||
| # | ||
| # 离线自足(CIW/archive 仓均不依赖): | ||
| # fixture=throwaway RSA 密钥 + 临时 git 仓(git archive 产物)+ 内联生成 | ||
| # 证据包(sbom+attestation,与 CIW attest_pack.py 同契约)+ 模拟账本事件 | ||
| # (write_evidence 链字段由本测试按同款算法计算——trace 只消费契约字段)。 | ||
| # 断言:attest-trace.sh 全链绿;三负向(payload/attestation 漂移、commit 伪造、 | ||
| # 包缺失)必红。 | ||
| set -uo pipefail | ||
| DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | ||
| PASS=0; FAIL=0 | ||
| ok() { PASS=$((PASS+1)); echo "PASS $1"; } | ||
| bad() { FAIL=$((FAIL+1)); echo "FAIL $1"; } | ||
|
|
||
| TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT | ||
|
|
||
| # ---- fixture:源仓 + 产物 + 密钥 ---- | ||
| git init -q "$TMP/repo" && git -C "$TMP/repo" config user.name t && git -C "$TMP/repo" config user.email t@t | ||
| mkdir -p "$TMP/repo/gov" && echo "engine-v1" > "$TMP/repo/gov/engine.sh" && echo "policy-v1" > "$TMP/repo/policy.yaml" | ||
| git -C "$TMP/repo" add -A && git -C "$TMP/repo" commit -qm v1 | ||
| COMMIT=$(git -C "$TMP/repo" rev-parse HEAD) | ||
| echo "$COMMIT" > "$TMP/commit" | ||
| git -C "$TMP/repo" archive --format=tar.gz -o "$TMP/artifact.tar.gz" HEAD | ||
| openssl genrsa -out "$TMP/sk.pem" 2048 2>/dev/null | ||
| openssl rsa -in "$TMP/sk.pem" -pubout -out "$TMP/pub.pem" 2>/dev/null | ||
|
|
||
| # ---- 证据包(内联生成,与 CIW attest_pack.py 同契约) ---- | ||
| python3 - "$TMP" <<'PY' | ||
| import base64, hashlib, json, os, subprocess, sys, tarfile, tempfile, datetime | ||
| tmp = sys.argv[1] | ||
| def sha(b): return hashlib.sha256(b).hexdigest() | ||
| art = f"{tmp}/artifact.tar.gz" | ||
| blob = open(art, "rb").read() | ||
| files = [] | ||
| with tarfile.open(art) as tf: | ||
| for m in tf.getmembers(): | ||
| if m.isfile(): | ||
| files.append({"path": m.name, "sha256": sha(tf.extractfile(m).read()), "size": m.size}) | ||
| files.sort(key=lambda x: x["path"]) | ||
| sbom = {"sbom_version": "0", "format": "file-manifest", | ||
| "artifact": {"name": "artifact.tar.gz", "sha256": sha(blob), "size": len(blob)}, | ||
| "source": {"repo": "Cloudbird-Software/.github", "commit": open(f"{tmp}/commit").read().strip()}, | ||
| "files": files, | ||
| "generated_at": "2026-08-29T00:00:00Z"} | ||
| canon = json.dumps(sbom, ensure_ascii=False, sort_keys=True, separators=(",", ":")) | ||
| statement = {"_type": "attest-pack/v0", | ||
| "subject": {"name": "artifact.tar.gz", "digest": {"sha256": sha(blob)}}, | ||
| "materials": {"sbom_sha256": sha(canon.encode()), "repo": "Cloudbird-Software/.github", | ||
| "git_commit": sbom["source"]["commit"]}, | ||
| "predicate": {"card": "Cloudbird-Software/.github#420", "tenant": "cl", | ||
| "tool": "fixture", "generated_at": "2026-08-29T00:00:00Z"}} | ||
| msg = json.dumps(statement, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode() | ||
| with tempfile.NamedTemporaryFile(delete=False) as mf: | ||
| mf.write(msg) | ||
| sig = subprocess.run(["openssl", "dgst", "-sha256", "-sign", f"{tmp}/sk.pem", mf.name], | ||
| capture_output=True).stdout | ||
| os.unlink(mf.name) | ||
| att = dict(statement); att["signature"] = {"alg": "RS256", "sig": base64.b64encode(sig).decode()} | ||
| bdir = f"{tmp}/root/evidence/attestations/fixture-001" | ||
| os.makedirs(bdir, exist_ok=True) | ||
| json.dump(sbom, open(f"{bdir}/sbom.json", "w"), ensure_ascii=False, indent=1) | ||
| json.dump(att, open(f"{bdir}/attestation.json", "w"), ensure_ascii=False, indent=1) | ||
| PY | ||
|
|
||
| # ---- 账本事件(模拟 write_evidence 输出——链字段同款算法) ---- | ||
| make_event() { python3 - "$TMP" <<PY | ||
| import hashlib, json, sys | ||
| tmp = sys.argv[1] | ||
| att = json.load(open(f"{tmp}/root/evidence/attestations/fixture-001/attestation.json")) | ||
| sbom_sha = att["materials"]["sbom_sha256"] | ||
| payload = json.dumps({"bundle_ref": "evidence/attestations/fixture-001", | ||
| "artifact_digest": att["subject"]["digest"]["sha256"], | ||
| "sbom_sha256": sbom_sha, "verify": "signed+verified"}, | ||
| ensure_ascii=False, separators=(",", ":")) | ||
| ev = {"ts": "2026-08-29T08:00:00Z", "kind": "decision", "action": "attestation-pack", | ||
| "verdict": "signed", | ||
| "subject": {"wave": "W4-R3", "card": "Cloudbird-Software/.github#420", | ||
| "tenant": "cl", "commit": open(f"{tmp}/commit").read().strip()}, | ||
| "actor": {"identity": "attest-drill-bot", "role": "bot", "model": None}, | ||
| "inputs_digest": "sha256:" + hashlib.sha256(open(f"{tmp}/root/evidence/attestations/fixture-001/attestation.json", "rb").read()).hexdigest(), | ||
| "payload": payload} | ||
| rec = dict(ev); rec["seq"] = 2; rec["prev_hash"] = "deadbeef" * 8 | ||
| rec["hash"] = hashlib.sha256(json.dumps({k: v for k, v in rec.items() if k != "hash"}, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()).hexdigest() | ||
| open(f"{tmp}/ledger.jsonl", "w").write(json.dumps(rec, ensure_ascii=False, separators=(",", ":")) + "\n") | ||
| PY | ||
| } | ||
| make_event | ||
|
|
||
| # ---- fixture 验证器(trace 的 --verify-cmd 注入面;演练工作流代入 CIW 真 | ||
| # attest_verify.py --content-only——本测试用同语义最小实现) ---- | ||
| cat > "$TMP/verify_fixture.py" <<'PY' | ||
| import base64, hashlib, json, subprocess, sys, tarfile, tempfile | ||
| bundle, artifact, pub = sys.argv[1], sys.argv[2], sys.argv[3] | ||
| att = json.load(open(f"{bundle}/attestation.json")) | ||
| sb = json.load(open(f"{bundle}/sbom.json")) | ||
| files = [] | ||
| with tarfile.open(artifact) as tf: | ||
| for m in tf.getmembers(): | ||
| if m.isfile(): | ||
| files.append({"path": m.name, "sha256": hashlib.sha256(tf.extractfile(m).read()).hexdigest(), | ||
| "size": m.size}) | ||
| files.sort(key=lambda x: x["path"]) | ||
| assert files == sb["files"], "SBOM 漂移" | ||
| msg = json.dumps({k: v for k, v in att.items() if k != "signature"}, | ||
| ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode() | ||
| with tempfile.NamedTemporaryFile(delete=False) as mf, tempfile.NamedTemporaryFile(delete=False) as sf: | ||
| mf.write(msg) | ||
| sf.write(base64.b64decode(att["signature"]["sig"])) | ||
| # 先 close 再验签(NamedTemporaryFile 缓冲未 flush 时 openssl 读到空文件——实测教训) | ||
| r = subprocess.run(["openssl", "dgst", "-sha256", "-verify", pub, "-signature", sf.name, mf.name], | ||
| capture_output=True) | ||
| assert r.returncode == 0, "RS256 验签失败" | ||
| PY | ||
| VCMD="python3 '$TMP/verify_fixture.py' {BUNDLE} {ARTIFACT} '$TMP/pub.pem'" | ||
|
|
||
| # ---- 正向:回溯全链 ---- | ||
| bash "$DIR/governance/attest-trace.sh" --ledger "$TMP/ledger.jsonl" --bundle-root "$TMP/root" \ | ||
| --git-repo "$TMP/repo" --verify-cmd "$VCMD" >/dev/null 2>&1 | ||
| [[ $? -eq 0 ]] && ok "AC-8e 回溯全链绿(账本→包→commit→重建→内容+验签)" || bad "回溯全链红" | ||
|
|
||
| # ---- 负向 1:payload artifact_digest 漂移 ---- | ||
| python3 - "$TMP" <<'PY' | ||
| import json, sys | ||
| tmp = sys.argv[1] | ||
| lines = open(f"{tmp}/ledger.jsonl").read().strip().split("\n") | ||
| ev = json.loads(lines[0]) | ||
| p = json.loads(ev["payload"]) | ||
| d = list(p["artifact_digest"]); d[0] = "0" if d[0] != "0" else "1" | ||
| p["artifact_digest"] = "".join(d) | ||
| ev["payload"] = json.dumps(p, ensure_ascii=False, separators=(",", ":")) | ||
| open(f"{tmp}/ledger-bad1.jsonl", "w").write(json.dumps(ev, ensure_ascii=False, separators=(",", ":")) + "\n") | ||
| PY | ||
| bash "$DIR/governance/attest-trace.sh" --ledger "$TMP/ledger-bad1.jsonl" --bundle-root "$TMP/root" \ | ||
| --git-repo "$TMP/repo" --verify-cmd "$VCMD" >/dev/null 2>&1 | ||
| [[ $? -eq 1 ]] && ok "payload↔attestation digest 漂移 → 红(互证锚执法)" || bad "负向 1 漏检" | ||
|
|
||
| # ---- 负向 2:commit 伪造(不在源仓) ---- | ||
| python3 - "$TMP" <<'PY' | ||
| import json, sys | ||
| tmp = sys.argv[1] | ||
| ev = json.loads(open(f"{tmp}/ledger.jsonl").read().strip()) | ||
| ev["subject"]["commit"] = "1234567890" * 4 | ||
| open(f"{tmp}/ledger-bad2.jsonl", "w").write(json.dumps(ev, ensure_ascii=False, separators=(",", ":")) + "\n") | ||
| PY | ||
| bash "$DIR/governance/attest-trace.sh" --ledger "$TMP/ledger-bad2.jsonl" --bundle-root "$TMP/root" \ | ||
| --git-repo "$TMP/repo" --verify-cmd "$VCMD" >/dev/null 2>&1 | ||
| [[ $? -eq 1 ]] && ok "commit 伪造 → 红(git archive 锚执法)" || bad "负向 2 漏检" | ||
|
|
||
| # ---- 负向 3:包缺失 ---- | ||
| mv "$TMP/root/evidence/attestations/fixture-001" "$TMP/hidden" | ||
| bash "$DIR/governance/attest-trace.sh" --ledger "$TMP/ledger.jsonl" --bundle-root "$TMP/root" \ | ||
| --git-repo "$TMP/repo" --verify-cmd "$VCMD" >/dev/null 2>&1 | ||
| [[ $? -eq 1 ]] && ok "证据包缺失 → 红(bundle_ref 锚执法)" || bad "负向 3 漏检" | ||
| mv "$TMP/hidden" "$TMP/root/evidence/attestations/fixture-001" | ||
|
|
||
| # ---- 事件 schema 契约(write_evidence 执法面:card/tenant/inputs_digest) ---- | ||
| jq -e '.subject.card == "Cloudbird-Software/.github#420" and .subject.tenant == "cl" and (.inputs_digest | startswith("sha256:"))' \ | ||
| "$TMP/ledger.jsonl" >/dev/null && ok "事件契约字段齐(join key+tenant+inputs_digest provenance 锚)" || bad "事件契约字段缺" | ||
|
|
||
| echo "----------------------------------------" | ||
| echo "test-attest-bind: $([[ $FAIL -eq 0 ]] && echo PASS || echo "FAIL($FAIL)")" | ||
| exit $([[ $FAIL -eq 0 ]] && echo 0 || echo 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2. Trace step lacks run_id
🐞 Bug≡ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools