Merge PR #66: fix(evidence) preserve immutable Evidence records

This commit is contained in:
yhc
2026-08-11 19:26:41 +08:00
4 changed files with 81 additions and 4 deletions
+9
View File
@@ -114,6 +114,15 @@ jobs:
grep -Eq '^- ready_for_act: true$' "$scratch/project/work/network-default/scope.md"
bash skills/scripts/case-guard.sh --case-root "$scratch/project/work/network-default"
bash skills/scripts/case-init.sh \
--hint "authorized web review" \
--case-name "uppercase-network" \
--package-root "$scratch/project" \
--auth-granted \
--network-profile "AUTHORIZED_TARGET_ONLY" \
--target-url "https://example.test/"
grep -Eq '^- mode: authorized_target_only$' "$scratch/project/work/uppercase-network/scope.md"
bash skills/scripts/case-init.sh \
--hint "pending review" \
--case-name "guard-section" \
+25 -1
View File
@@ -155,7 +155,31 @@ $notesBlock
"@
$utf8 = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($path, $body, $utf8)
$stream = $null
$writer = $null
try {
try {
$stream = [System.IO.File]::Open(
$path,
[System.IO.FileMode]::CreateNew,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::None
)
} catch [System.IO.IOException] {
if (Test-Path -LiteralPath $path) {
throw ("Evidence record already exists and is immutable: {0}. Use a new -Id." -f $fileName)
}
throw
}
$writer = [System.IO.StreamWriter]::new($stream, $utf8)
$writer.Write($body)
} finally {
if ($null -ne $writer) {
$writer.Dispose()
} elseif ($null -ne $stream) {
$stream.Dispose()
}
}
$index = Join-Path $evDir 'INDEX.md'
$line = "- $idSafe | $sev | $st | $titleLine | $fileName"
+5 -3
View File
@@ -94,7 +94,8 @@ if [[ -z "$CASE_NAME" || "$case_name_length" -gt 80 ||
exit 2
fi
if [[ -n "$NETWORK_PROFILE" ]]; then
case "${NETWORK_PROFILE,,}" in
network_profile_normalized="$(printf '%s' "$NETWORK_PROFILE" | tr '[:upper:]' '[:lower:]')"
case "$network_profile_normalized" in
offline|lab_only|authorized_target_only|unrestricted_lab|lab|authorized|auth|offline_only) ;;
*)
echo "Invalid --network-profile '$NETWORK_PROFILE'. Allowed: offline, lab_only, authorized_target_only, unrestricted_lab (aliases: lab, authorized, auth, offline_only)." >&2
@@ -146,11 +147,12 @@ elif [[ "$auth_status_resolved" == "granted" && ${#ASSETS[@]} -gt 0 && -z "$SAMP
else
network_mode="offline"
fi
case "${network_mode,,}" in
network_mode="$(printf '%s' "$network_mode" | tr '[:upper:]' '[:lower:]')"
case "$network_mode" in
lab) network_mode="lab_only" ;;
authorized|auth) network_mode="authorized_target_only" ;;
offline_only) network_mode="offline" ;;
offline|lab_only|authorized_target_only|unrestricted_lab) network_mode="${network_mode,,}" ;;
offline|lab_only|authorized_target_only|unrestricted_lab) ;;
*)
echo "Invalid --network-profile '$network_mode'. Allowed: offline, lab_only, authorized_target_only, unrestricted_lab (aliases: lab, authorized, auth, offline_only)." >&2
exit 2
+42
View File
@@ -121,6 +121,48 @@ if (-not (Test-Path -LiteralPath $mr)) {
}
$routeSummary -join [Environment]::NewLine | Set-Content (Join-Path $LogDir '03-route-summary.txt') -Encoding UTF8
# --- 4) Evidence ID immutability ---
$appendEvidence = Join-Path $scriptDir 'append-evidence.ps1'
$evidenceCase = Join-Path $LogDir 'evidence-immutability'
if (-not (Test-Path -LiteralPath $appendEvidence)) {
Bad 'append-evidence.ps1 missing for immutability check'
} else {
New-Item -ItemType Directory -Path $evidenceCase -Force | Out-Null
& powershell -NoProfile -ExecutionPolicy Bypass -File $appendEvidence `
-CaseRoot $evidenceCase `
-Id 'E-IMMUTABLE' `
-Title 'first write' `
-ReproCommand 'echo first' 2>&1 | Out-Null
$firstEvidenceExit = $LASTEXITCODE
if ($firstEvidenceExit -ne 0) {
Bad ("initial Evidence append exit {0}" -f $firstEvidenceExit)
} else {
$evidencePath = Join-Path $evidenceCase 'evidence\E-IMMUTABLE.md'
$indexPath = Join-Path $evidenceCase 'evidence\INDEX.md'
$beforeEvidence = (Get-FileHash -LiteralPath $evidencePath -Algorithm SHA256).Hash
$beforeIndex = (Get-FileHash -LiteralPath $indexPath -Algorithm SHA256).Hash
& powershell -NoProfile -ExecutionPolicy Bypass -File $appendEvidence `
-CaseRoot $evidenceCase `
-Id 'E-IMMUTABLE' `
-Title 'second write' `
-ReproCommand 'echo second' 2>&1 | Out-Null
$duplicateEvidenceExit = $LASTEXITCODE
$afterEvidence = (Get-FileHash -LiteralPath $evidencePath -Algorithm SHA256).Hash
$afterIndex = (Get-FileHash -LiteralPath $indexPath -Algorithm SHA256).Hash
if ($duplicateEvidenceExit -eq 0) {
Bad 'duplicate Evidence ID was accepted'
} elseif ($beforeEvidence -ne $afterEvidence) {
Bad 'existing Evidence changed after duplicate append'
} elseif ($beforeIndex -ne $afterIndex) {
Bad 'Evidence index changed after duplicate append'
} else {
Ok 'duplicate Evidence ID rejected without mutation'
}
}
}
# --- summary ---
$summary = @(
"VERIFY_EXIT=$verifyExit",