fix(agent99): bind check receipts to same-run identity
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 1m40s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 11s

This commit is contained in:
ogt
2026-07-15 18:03:42 +08:00
parent d91ecc0376
commit d6d0066bae
2 changed files with 171 additions and 11 deletions

View File

@@ -47,7 +47,7 @@ usage() {
"Usage: $0 [--check|--apply] [options]" \
"" \
"Default is a remote no-write check/plan against fixed target ${TARGET}." \
"Apply requires --apply plus --trace-id, --run-id, and --work-item-id." \
"Check and apply require --trace-id, --run-id, and --work-item-id." \
"" \
"Options:" \
" --check No-write transport and envelope check (default)." \
@@ -139,12 +139,13 @@ done
[[ "${CONNECT_TIMEOUT_SECONDS}" =~ ^[0-9]+$ ]] || fail "invalid_connect_timeout"
(( CONNECT_TIMEOUT_SECONDS >= 1 && CONNECT_TIMEOUT_SECONDS <= 30 )) || fail "invalid_connect_timeout"
readonly SAFE_ID_PATTERN='^[A-Za-z0-9][A-Za-z0-9._:@+-]{0,127}$'
[[ "${TRACE_ID}" =~ ${SAFE_ID_PATTERN} ]] || fail "${MODE}_trace_id_missing_or_invalid"
[[ "${RUN_ID}" =~ ${SAFE_ID_PATTERN} ]] || fail "${MODE}_run_id_missing_or_invalid"
[[ "${WORK_ITEM_ID}" =~ ${SAFE_ID_PATTERN} ]] || fail "${MODE}_work_item_id_missing_or_invalid"
if [[ "${MODE}" == "apply" ]]; then
REMOTE_EXECUTION_TIMEOUT_SECONDS="2400"
readonly SAFE_ID_PATTERN='^[A-Za-z0-9][A-Za-z0-9._:@+-]{0,127}$'
[[ "${TRACE_ID}" =~ ${SAFE_ID_PATTERN} ]] || fail "apply_trace_id_missing_or_invalid"
[[ "${RUN_ID}" =~ ${SAFE_ID_PATTERN} ]] || fail "apply_run_id_missing_or_invalid"
[[ "${WORK_ITEM_ID}" =~ ${SAFE_ID_PATTERN} ]] || fail "apply_work_item_id_missing_or_invalid"
fi
for dependency in git python3 ssh scp mktemp hostname timeout; do
@@ -208,6 +209,7 @@ trap cleanup EXIT HUP INT TERM
ENVELOPE_PATH="${WORK_DIR}/envelope.json"
BOOTSTRAP_PATH="${WORK_DIR}/remote-bootstrap.ps1"
BOOTSTRAP_PAYLOAD_PATH="${WORK_DIR}/remote-bootstrap.ps1.gz.b64"
CHECK_REMOTE_RECEIPT_PATH="${WORK_DIR}/check-remote-receipt.json"
python3 - \
"${SOURCE_ROOT}" \
@@ -561,12 +563,65 @@ cleanup_remote_payload() {
>/dev/null 2>&1
}
project_check_receipt_identity() {
python3 - \
"${CHECK_REMOTE_RECEIPT_PATH}" \
"${TRACE_ID}" \
"${RUN_ID}" \
"${WORK_ITEM_ID}" <<'PY'
from __future__ import annotations
import json
import sys
from pathlib import Path
receipt_path = Path(sys.argv[1])
trace_id, run_id, work_item_id = sys.argv[2:5]
try:
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
raise SystemExit(65)
if not isinstance(receipt, dict):
raise SystemExit(65)
if receipt.get("schemaVersion") != "agent99_remote_atomic_deploy_receipt_v1":
raise SystemExit(65)
if receipt.get("mode") != "check":
raise SystemExit(65)
if receipt.get("status") not in {"check_ready", "check_failed_no_apply"}:
raise SystemExit(65)
identity = {
"traceId": trace_id,
"runId": run_id,
"workItemId": work_item_id,
}
for field, expected in identity.items():
if field in receipt and receipt[field] != expected:
raise SystemExit(65)
receipt[field] = expected
sys.stdout.write(json.dumps(receipt, separators=(",", ":"), ensure_ascii=True) + "\n")
PY
}
if [[ "${MODE}" == "check" ]]; then
set +e
timeout --signal=TERM --kill-after=10 "${REMOTE_EXECUTION_TIMEOUT_SECONDS}s" \
ssh "${SSH_OPTIONS[@]}" "${TARGET}" "${REMOTE_CHECK_COMMAND}" </dev/null
ssh "${SSH_OPTIONS[@]}" "${TARGET}" "${REMOTE_CHECK_COMMAND}" </dev/null \
>"${CHECK_REMOTE_RECEIPT_PATH}"
status=$?
set -e
set +e
project_check_receipt_identity
projection_status=$?
set -e
if [[ "${projection_status}" -ne 0 ]]; then
printf 'agent99_transport_error=check_receipt_identity_projection_failed\n' >&2
if [[ "${status}" -eq 0 ]]; then
exit 65
fi
fi
exit "${status}"
fi

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
import base64
import json
import os
import re
import shlex
@@ -55,9 +56,9 @@ def test_sender_is_valid_shell_with_check_as_the_default() -> None:
assert 'MODE="check"' in source
assert "--apply" in source
assert 'MODE="apply"' in source
assert "apply_trace_id_missing_or_invalid" in source
assert "apply_run_id_missing_or_invalid" in source
assert "apply_work_item_id_missing_or_invalid" in source
assert 'fail "${MODE}_trace_id_missing_or_invalid"' in source
assert 'fail "${MODE}_run_id_missing_or_invalid"' in source
assert 'fail "${MODE}_work_item_id_missing_or_invalid"' in source
assert 'MODE_SELECTION="default"' in source
assert 'REMOTE_EXECUTION_TIMEOUT_SECONDS="180"' in source
assert 'REMOTE_EXECUTION_TIMEOUT_SECONDS="2400"' in source
@@ -291,6 +292,34 @@ def test_receiver_has_single_flight_idempotency_and_no_secret_receipt_boundary()
assert "[bool]$prior.relayReload.newGenerationVerified" in source
def test_check_and_apply_receipts_project_the_same_run_identity() -> None:
sender = SENDER.read_text(encoding="utf-8")
projector = sender[
sender.index("project_check_receipt_identity()") : sender.index(
'if [[ "${MODE}" == "check" ]]'
)
]
assert '"traceId": trace_id' in projector
assert '"runId": run_id' in projector
assert '"workItemId": work_item_id' in projector
assert "receipt[field] != expected" in projector
assert "raise SystemExit(65)" in projector
receiver = RECEIVER.read_text(encoding="utf-8")
success_receipt = receiver[
receiver.index("$successReceipt = [pscustomobject]@{") : receiver.index(
"Write-AgentRemoteDeployReceipt $receiptPath $successReceipt"
)
]
assert "traceId = $traceId" in success_receipt
assert "runId = $runId" in success_receipt
assert "workItemId = $workItemId" in success_receipt
assert "secretValueRead = $false" in success_receipt
assert "privateKeyValueRead = $false" in success_receipt
assert "tokenValueRead = $false" in success_receipt
assert "environmentSecretRead = $false" in success_receipt
def test_receiver_creates_canonical_success_receipt_once_with_atomic_no_replace() -> None:
source = RECEIVER.read_text(encoding="utf-8")
writer = source[
@@ -586,6 +615,39 @@ def test_apply_without_all_three_identities_fails_before_ssh() -> None:
assert "known_hosts_file_missing" not in result.stderr
def test_check_requires_complete_safe_same_run_identity_before_ssh() -> None:
cases = (
((), "check_trace_id_missing_or_invalid"),
(("--trace-id", "trace-fixture"), "check_run_id_missing_or_invalid"),
(
("--trace-id", "trace-fixture", "--run-id", "run-fixture"),
"check_work_item_id_missing_or_invalid",
),
(
(
"--trace-id",
"trace fixture",
"--run-id",
"run-fixture",
"--work-item-id",
"AIA-SRE-P0-20260715",
),
"check_trace_id_missing_or_invalid",
),
)
for identity_args, expected_error in cases:
result = subprocess.run(
["bash", str(SENDER), "--check", *identity_args],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 64
assert expected_error in result.stderr
assert "known_hosts_file_missing" not in result.stderr
def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
tmp_path: Path,
) -> None:
@@ -610,7 +672,13 @@ def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
"#!/usr/bin/env bash\n"
f"printf '%s\\n' \"$@\" >{shlex.quote(str(args_path))}\n"
f"wc -c | tr -d ' ' >{shlex.quote(str(bytes_path))}\n"
"printf '%s\\n' '{\"status\":\"check_ready\"}'\n",
'if [[ -n "${FAKE_CHECK_RECEIPT:-}" ]]; then\n'
" printf '%s\\n' \"$FAKE_CHECK_RECEIPT\"\n"
"else\n"
" printf '%s\\n' "
'\'{"schemaVersion":"agent99_remote_atomic_deploy_receipt_v1",'
'"status":"check_ready","mode":"check"}\'\n'
"fi\n",
encoding="utf-8",
)
fake_timeout = fake_bin / "timeout"
@@ -647,6 +715,12 @@ def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
"bash",
str(SENDER),
"--check",
"--trace-id",
"trace-fixture",
"--run-id",
"run-fixture",
"--work-item-id",
"AIA-SRE-P0-20260715",
"--known-hosts-file",
str(known_hosts),
"--identity-file",
@@ -659,7 +733,11 @@ def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
)
assert result.returncode == 0, result.stderr
assert '"status":"check_ready"' in result.stdout
receipt = json.loads(result.stdout)
assert receipt["status"] == "check_ready"
assert receipt["traceId"] == "trace-fixture"
assert receipt["runId"] == "run-fixture"
assert receipt["workItemId"] == "AIA-SRE-P0-20260715"
args = args_path.read_text(encoding="utf-8")
remote_command = args.splitlines()[-1]
assert "BatchMode=yes" in args
@@ -673,6 +751,9 @@ def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
assert "agent99-contract-check.ps1" in check_script
assert "Get-FileHash" in check_script
assert "runtimeContract" in check_script
assert "trace-fixture" not in check_script
assert "run-fixture" not in check_script
assert "AIA-SRE-P0-20260715" not in check_script
assert "$ready=[bool]($c.executed-and$c.ok-and$c.exitCode-eq 0)" in check_script
assert "'check_failed_no_apply'" in check_script
assert "if(-not$ready){exit 65}" in check_script
@@ -692,6 +773,30 @@ def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
stdin_bytes = int(bytes_path.read_text(encoding="utf-8"))
assert stdin_bytes == 0
conflicting_environment = environment.copy()
conflicting_environment["FAKE_CHECK_RECEIPT"] = json.dumps(
{
"schemaVersion": "agent99_remote_atomic_deploy_receipt_v1",
"status": "check_ready",
"mode": "check",
"traceId": "different-trace",
},
separators=(",", ":"),
)
conflicting_result = subprocess.run(
result.args,
check=False,
capture_output=True,
text=True,
env=conflicting_environment,
)
assert conflicting_result.returncode == 65
assert conflicting_result.stdout == ""
assert (
"agent99_transport_error=check_receipt_identity_projection_failed"
in conflicting_result.stderr
)
def test_apply_uses_ephemeral_file_transport_and_cleans_up_without_ssh_stdin(
tmp_path: Path,