124 lines
3.1 KiB
Bash
Executable File
124 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
source "$(dirname "$0")/lib/common.sh"
|
|
|
|
ROOT="$(vmcc_root)"
|
|
CMD="${1:-}"
|
|
RUN_ID="${VMCC_RUN_ID:-$(run_id)}"
|
|
DAY="$(date -u "+%Y-%m-%d")"
|
|
|
|
EVID_DIR="$ROOT/30-evidence/$DAY/$RUN_ID"
|
|
RULE_DIR="$ROOT/50-reports/$DAY/$RUN_ID/rules"
|
|
REP_DIR="$ROOT/50-reports/$DAY/$RUN_ID"
|
|
|
|
mkdir -p "$EVID_DIR" "$RULE_DIR" "$REP_DIR"
|
|
|
|
hash_file() {
|
|
local file="$1"
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "$file" | awk '{print $1}'
|
|
else
|
|
shasum -a 256 "$file" | awk '{print $1}'
|
|
fi
|
|
}
|
|
|
|
write_manifest() {
|
|
local manifest="$EVID_DIR/manifest.json"
|
|
local ts
|
|
ts="$(iso_utc_now)"
|
|
{
|
|
echo "{"
|
|
echo " \"version\": \"1.0.0\","
|
|
echo " \"collected_at\": \"${ts}\","
|
|
echo " \"run_id\": \"${RUN_ID}\","
|
|
echo " \"files\": ["
|
|
local first=1
|
|
while IFS= read -r file; do
|
|
local rel
|
|
rel="${file#$ROOT/}"
|
|
local sha
|
|
sha="$(hash_file "$file")"
|
|
if [[ $first -eq 0 ]]; then
|
|
echo " ,"
|
|
fi
|
|
first=0
|
|
echo " {\"path\": \"${rel}\", \"sha256\": \"${sha}\"}"
|
|
done < <(find "$EVID_DIR" -type f ! -name "manifest.json" | sort)
|
|
echo " ]"
|
|
echo "}"
|
|
} > "$manifest"
|
|
}
|
|
|
|
run_collect() {
|
|
echo "[vmcc] run_id=$RUN_ID"
|
|
echo "[vmcc] collecting evidence -> $EVID_DIR"
|
|
"$ROOT/20-collectors/collect_ledger_verify.sh" "$EVID_DIR"
|
|
"$ROOT/20-collectors/collect_constitution_hash.sh" "$EVID_DIR"
|
|
"$ROOT/20-collectors/collect_backup_restore_drill.sh" "$EVID_DIR"
|
|
write_manifest
|
|
}
|
|
|
|
run_evaluate() {
|
|
echo "[vmcc] evaluating rules -> $RULE_DIR"
|
|
"$ROOT/40-rules/ledger_hash_chain_intact.sh" "$EVID_DIR" > "$RULE_DIR/ledger.hash_chain_intact.json"
|
|
"$ROOT/40-rules/governance_constitution_pinned.sh" "$EVID_DIR" > "$RULE_DIR/governance.constitution_pinned.json"
|
|
"$ROOT/40-rules/backup_restore_drill_recent.sh" "$EVID_DIR" > "$RULE_DIR/backup.restore_drill_recent.json"
|
|
}
|
|
|
|
run_report() {
|
|
require_cmd jq
|
|
echo "[vmcc] assembling report -> $REP_DIR/report.json"
|
|
TS="$(iso_utc_now)"
|
|
PASSED_COUNT="$(jq -s '[.[] | select(.passed==true)] | length' "$RULE_DIR"/*.json)"
|
|
FAILED_COUNT="$(jq -s '[.[] | select(.passed==false)] | length' "$RULE_DIR"/*.json)"
|
|
|
|
jq -n \
|
|
--arg version "1.0.0" \
|
|
--arg timestamp "$TS" \
|
|
--arg run_id "$RUN_ID" \
|
|
--arg day "$DAY" \
|
|
--slurpfile rules <(cat "$RULE_DIR"/*.json) \
|
|
--argjson passed "$PASSED_COUNT" \
|
|
--argjson failed "$FAILED_COUNT" \
|
|
'{
|
|
version: $version,
|
|
timestamp: $timestamp,
|
|
period: "run",
|
|
run_id: $run_id,
|
|
day: $day,
|
|
summary: {
|
|
rules_passed: $passed,
|
|
rules_failed: $failed,
|
|
status: (if $failed == 0 then "COMPLIANT" else "NONCOMPLIANT" end)
|
|
},
|
|
rules: $rules
|
|
}' > "$REP_DIR/report.json"
|
|
}
|
|
|
|
case "$CMD" in
|
|
collect)
|
|
run_collect
|
|
;;
|
|
evaluate)
|
|
run_evaluate
|
|
;;
|
|
report)
|
|
run_report
|
|
;;
|
|
all)
|
|
run_collect
|
|
run_evaluate
|
|
run_report
|
|
require_cmd jq
|
|
FAILED_COUNT="$(jq -s '[.[] | select(.passed==false)] | length' "$RULE_DIR"/*.json)"
|
|
if [[ "$FAILED_COUNT" -ne 0 ]]; then
|
|
exit 3
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {collect|evaluate|report|all}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|