Merge PR #66: fix(evidence) preserve immutable Evidence records
This commit is contained in:
@@ -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" \
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user