projection: begin the exact-path public epoch
The public tree and its history contain only the listed paths. Earlier projection history remains preserved internally. Source-Sha: 913fde029b935671833254797f0f20f1eb9fabba Policy-Sha: 913fde029b935671833254797f0f20f1eb9fabba Tree-Digest: 180ae530c1058a2a5c89837bdce2d323ae83e669e38590ca72e75b8d92b7262f
This commit is contained in:
commit
67e26be2d9
838 changed files with 157302 additions and 0 deletions
179
installer/windows/verify-msi.sh
Executable file
179
installer/windows/verify-msi.sh
Executable file
|
|
@ -0,0 +1,179 @@
|
|||
#!/usr/bin/env bash
|
||||
# Prove that a RedFlag Server MSI contains the exact bytes staged for it.
|
||||
# MSI compressed size is diagnostic only; it is never an acceptance gate.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
MSI="${1:-}"
|
||||
STAGED_EXE="${2:-}"
|
||||
STAGED_ENV="${3:-}"
|
||||
shift "$(( $# >= 3 ? 3 : $# ))"
|
||||
|
||||
REDFLAG_VERSION=""
|
||||
MSI_VERSION=""
|
||||
PROOF_RECORD=""
|
||||
SOURCE_COMMIT="${GITHUB_SHA:-}"
|
||||
SOURCE_REF="${GITHUB_REF:-}"
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
[ "$#" -ge 2 ] || { echo "missing value for $1" >&2; exit 2; }
|
||||
case "$1" in
|
||||
--redflag-version) REDFLAG_VERSION="${2:-}"; shift 2 ;;
|
||||
--msi-version) MSI_VERSION="${2:-}"; shift 2 ;;
|
||||
--proof-record) PROOF_RECORD="${2:-}"; shift 2 ;;
|
||||
--source-commit) SOURCE_COMMIT="${2:-}"; shift 2 ;;
|
||||
--source-ref) SOURCE_REF="${2:-}"; shift 2 ;;
|
||||
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$MSI" ] && [ -n "$STAGED_EXE" ] && [ -n "$STAGED_ENV" ] || {
|
||||
echo "usage: $0 <msi> <staged-server-exe> <staged-env-example> [options]" >&2
|
||||
exit 2
|
||||
}
|
||||
for f in "$MSI" "$STAGED_EXE" "$STAGED_ENV"; do
|
||||
[ -f "$f" ] || { echo "no such file: $f" >&2; exit 2; }
|
||||
done
|
||||
for tool in msiinfo msiextract sha256sum; do
|
||||
command -v "$tool" >/dev/null || { echo "$tool not found" >&2; exit 2; }
|
||||
done
|
||||
if [ -n "$PROOF_RECORD" ]; then
|
||||
[ -n "$REDFLAG_VERSION" ] && [ -n "$MSI_VERSION" ] || {
|
||||
echo "--proof-record requires --redflag-version and --msi-version" >&2; exit 2; }
|
||||
command -v jq >/dev/null || { echo "jq not found" >&2; exit 2; }
|
||||
fi
|
||||
|
||||
FAIL=0
|
||||
EMBEDDED=false
|
||||
ok() { echo "[ok] $*"; }
|
||||
bad() { echo "[FAIL] $*"; FAIL=1; }
|
||||
|
||||
MSI_SIZE=$(stat -c%s "$MSI")
|
||||
MSI_SHA=$(sha256sum "$MSI" | cut -d' ' -f1)
|
||||
STAGED_SERVER_SIZE=$(stat -c%s "$STAGED_EXE")
|
||||
STAGED_SERVER_SHA=$(sha256sum "$STAGED_EXE" | cut -d' ' -f1)
|
||||
STAGED_CONFIG_SIZE=$(stat -c%s "$STAGED_ENV")
|
||||
STAGED_CONFIG_SHA=$(sha256sum "$STAGED_ENV" | cut -d' ' -f1)
|
||||
|
||||
echo "=== package under test ==="
|
||||
echo " path: $MSI"
|
||||
echo " size: $MSI_SIZE bytes <- diagnostic only, never a gate"
|
||||
echo " sha256: $MSI_SHA"
|
||||
|
||||
echo "=== tables ==="
|
||||
for t in Property Directory Component File Feature FeatureComponents \
|
||||
ServiceInstall ServiceControl Upgrade Media; do
|
||||
if msiinfo export "$MSI" "$t" >/dev/null 2>&1; then ok "table $t present"
|
||||
else bad "table $t missing"; fi
|
||||
done
|
||||
carries() {
|
||||
if msiinfo export "$MSI" "$1" 2>/dev/null | grep -F "$2" >/dev/null; then ok "$1 carries $2"
|
||||
else bad "$1 does not carry $2"; fi
|
||||
}
|
||||
carries ServiceInstall RedFlagServer
|
||||
carries ServiceControl RedFlagServer
|
||||
carries Component ServerBinary
|
||||
carries Component ConfigTemplate
|
||||
carries File redflag-server.exe
|
||||
carries File redflag.env.example
|
||||
|
||||
echo "=== version identity ==="
|
||||
PRODUCT_VERSION=$(msiinfo export "$MSI" Property 2>/dev/null |
|
||||
awk -F'\t' '$1 == "ProductVersion" {print $2; exit}' | tr -d '\r')
|
||||
if [ -n "$MSI_VERSION" ]; then
|
||||
[ "$PRODUCT_VERSION" = "$MSI_VERSION" ] && ok "MSI ProductVersion is $MSI_VERSION" || \
|
||||
bad "MSI ProductVersion '${PRODUCT_VERSION:-missing}' != expected $MSI_VERSION"
|
||||
fi
|
||||
if [ -n "$REDFLAG_VERSION" ]; then
|
||||
if ! command -v go >/dev/null; then
|
||||
bad "go is required to inspect the staged Server build version"
|
||||
else
|
||||
SERVER_VERSION=$(go version -m "$STAGED_EXE" 2>/dev/null |
|
||||
sed -n 's/.*versions\.AgentVersion=\([^[:space:]]*\).*/\1/p' | head -1)
|
||||
[ "$SERVER_VERSION" = "$REDFLAG_VERSION" ] && \
|
||||
ok "staged Server build version is $REDFLAG_VERSION" || \
|
||||
bad "staged Server build version '${SERVER_VERSION:-missing}' != release $REDFLAG_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== cabinet custody ==="
|
||||
msiinfo export "$MSI" Media 2>/dev/null | sed 's/^/ media| /'
|
||||
CAB=$(msiinfo export "$MSI" Media 2>/dev/null |
|
||||
awk -F'\t' 'NR > 3 && $4 != "" {print $4; exit}')
|
||||
if [ -z "$CAB" ]; then
|
||||
bad "Media table names no cabinet at all"
|
||||
elif [ "${CAB#\#}" = "$CAB" ]; then
|
||||
bad "Media names external cabinet '$CAB'"
|
||||
else
|
||||
STREAM="${CAB#\#}"
|
||||
if msiinfo streams "$MSI" 2>/dev/null | grep -Fx "$STREAM" >/dev/null; then
|
||||
EMBEDDED=true
|
||||
ok "Media names embedded cabinet '$CAB' and storage carries '$STREAM'"
|
||||
else
|
||||
bad "embedded cabinet stream '$STREAM' is absent"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== extraction (MSI copied alone to an empty directory) ==="
|
||||
ISO=$(mktemp -d)
|
||||
OUT=$(mktemp -d)
|
||||
trap 'rm -rf "$ISO" "$OUT"' EXIT
|
||||
cp "$MSI" "$ISO/package.msi"
|
||||
if (cd "$ISO" && msiextract -C "$OUT" package.msi >/dev/null 2>&1); then
|
||||
ok "msiextract succeeded without a sibling cabinet"
|
||||
else
|
||||
bad "msiextract failed without a sibling cabinet"
|
||||
fi
|
||||
find "$OUT" -type f -printf ' %s bytes %P\n' 2>/dev/null | sort || true
|
||||
|
||||
EXTRACTED_SERVER=""
|
||||
EXTRACTED_CONFIG=""
|
||||
compare() {
|
||||
local name="$1" staged="$2" destination="$3" got gs ss gh sh
|
||||
got=$(find "$OUT" -type f -name "$name" -print -quit 2>/dev/null)
|
||||
if [ -z "$got" ]; then bad "$name is not in the extracted payload"; return; fi
|
||||
printf -v "$destination" '%s' "$got"
|
||||
gs=$(stat -c%s "$got"); ss=$(stat -c%s "$staged")
|
||||
gh=$(sha256sum "$got" | cut -d' ' -f1); sh=$(sha256sum "$staged" | cut -d' ' -f1)
|
||||
[ "$gs" = "$ss" ] && ok "$name size matches staged ($gs bytes)" || bad "$name size $gs != staged $ss"
|
||||
[ "$gh" = "$sh" ] && ok "$name sha256 matches staged ($gh)" || bad "$name sha256 $gh != staged $sh"
|
||||
}
|
||||
echo "=== payload identity ==="
|
||||
compare redflag-server.exe "$STAGED_EXE" EXTRACTED_SERVER
|
||||
compare redflag.env.example "$STAGED_ENV" EXTRACTED_CONFIG
|
||||
|
||||
if [ "$FAIL" -ne 0 ]; then
|
||||
echo "RESULT: payload custody NOT proven"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXTRACTED_SERVER_SHA=$(sha256sum "$EXTRACTED_SERVER" | cut -d' ' -f1)
|
||||
EXTRACTED_CONFIG_SHA=$(sha256sum "$EXTRACTED_CONFIG" | cut -d' ' -f1)
|
||||
if [ -n "$PROOF_RECORD" ]; then
|
||||
mkdir -p "$(dirname "$PROOF_RECORD")"
|
||||
WIXL_VERSION=$(wixl --version 2>&1 | head -1 || true)
|
||||
MSIINFO_VERSION=$(msiinfo --version 2>&1 | head -1 || true)
|
||||
MSIEXTRACT_VERSION=$(msiextract --version 2>&1 | head -1 || true)
|
||||
GO_VERSION=$(go version 2>&1 || true)
|
||||
jq -n \
|
||||
--arg source_commit "$SOURCE_COMMIT" --arg source_ref "$SOURCE_REF" \
|
||||
--arg redflag_version "$REDFLAG_VERSION" --arg msi_product_version "$PRODUCT_VERSION" \
|
||||
--arg msi_file "$(basename "$MSI")" --arg msi_sha256 "$MSI_SHA" --argjson msi_size "$MSI_SIZE" \
|
||||
--arg staged_server_sha256 "$STAGED_SERVER_SHA" --argjson staged_server_size "$STAGED_SERVER_SIZE" \
|
||||
--arg extracted_server_sha256 "$EXTRACTED_SERVER_SHA" \
|
||||
--arg staged_config_sha256 "$STAGED_CONFIG_SHA" --argjson staged_config_size "$STAGED_CONFIG_SIZE" \
|
||||
--arg extracted_config_sha256 "$EXTRACTED_CONFIG_SHA" \
|
||||
--arg wixl "$WIXL_VERSION" --arg msiinfo "$MSIINFO_VERSION" \
|
||||
--arg msiextract "$MSIEXTRACT_VERSION" --arg go "$GO_VERSION" \
|
||||
'{record_type:"redflag-installer-custody-proof",signed_release_manifest:false,
|
||||
source:{commit:$source_commit,ref:$source_ref},redflag_version:$redflag_version,
|
||||
msi:{filename:$msi_file,product_version:$msi_product_version,size:$msi_size,sha256:$msi_sha256},
|
||||
staged:{server:{size:$staged_server_size,sha256:$staged_server_sha256},config:{size:$staged_config_size,sha256:$staged_config_sha256}},
|
||||
extracted:{server:{sha256:$extracted_server_sha256},config:{sha256:$extracted_config_sha256}},
|
||||
cabinet_embedded:true,verifier_result:"passed",
|
||||
tools:{wixl:$wixl,msiinfo:$msiinfo,msiextract:$msiextract,go:$go}}' > "$PROOF_RECORD"
|
||||
ok "wrote installer proof record $PROOF_RECORD"
|
||||
fi
|
||||
|
||||
echo "RESULT: payload custody proven"
|
||||
echo "BOUNDARY: this is not a Windows installation or publisher-authentication proof"
|
||||
Loading…
Reference in a new issue