feat: pin constitution hash and manifest evidence
This commit is contained in:
@@ -5,9 +5,17 @@ OUT_DIR="${1:?usage: collect_ledger_verify.sh <out_dir>}"
|
|||||||
mkdir -p "$OUT_DIR"
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
if command -v ledger >/dev/null 2>&1; then
|
if command -v ledger >/dev/null 2>&1; then
|
||||||
ledger verify --format json > "$OUT_DIR/ledger_verify.json"
|
if ! ledger verify --format json > "$OUT_DIR/ledger_verify.json"; then
|
||||||
|
cat > "$OUT_DIR/ledger_verify.json" <<'JSON'
|
||||||
|
{"collected": false, "reason": "ledger verify failed"}
|
||||||
|
JSON
|
||||||
|
fi
|
||||||
elif command -v ledger-cli >/dev/null 2>&1; then
|
elif command -v ledger-cli >/dev/null 2>&1; then
|
||||||
ledger-cli verify --format json > "$OUT_DIR/ledger_verify.json"
|
if ! ledger-cli verify --format json > "$OUT_DIR/ledger_verify.json"; then
|
||||||
|
cat > "$OUT_DIR/ledger_verify.json" <<'JSON'
|
||||||
|
{"collected": false, "reason": "ledger-cli verify failed"}
|
||||||
|
JSON
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
cat > "$OUT_DIR/ledger_verify.json" <<'JSON'
|
cat > "$OUT_DIR/ledger_verify.json" <<'JSON'
|
||||||
{"collected": false, "reason": "ledger CLI not found"}
|
{"collected": false, "reason": "ledger CLI not found"}
|
||||||
|
|||||||
@@ -7,8 +7,14 @@ EVID_DIR="${1:?usage: governance_constitution_pinned.sh <evidence_dir>}"
|
|||||||
TS="$(iso_utc_now)"
|
TS="$(iso_utc_now)"
|
||||||
FILE="$EVID_DIR/constitution_hash.json"
|
FILE="$EVID_DIR/constitution_hash.json"
|
||||||
|
|
||||||
|
ROOT="$(vmcc_root)"
|
||||||
|
PIN_FILE="${VMCC_PINS_FILE:-$ROOT/config/pins.yaml}"
|
||||||
PINNED_SHA256="${VMCC_PINNED_CONSTITUTION_SHA256:-}"
|
PINNED_SHA256="${VMCC_PINNED_CONSTITUTION_SHA256:-}"
|
||||||
|
|
||||||
|
if [[ -z "$PINNED_SHA256" && -f "$PIN_FILE" ]]; then
|
||||||
|
PINNED_SHA256="$(awk -F': *' '/^constitution_sha256:/ {print $2}' "$PIN_FILE" | tr -d '"' | tr -d "'" | head -n 1)"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -f "$FILE" ]]; then
|
if [[ ! -f "$FILE" ]]; then
|
||||||
json_emit "$(jq -n --arg ts "$TS" '{
|
json_emit "$(jq -n --arg ts "$TS" '{
|
||||||
version:"1.0.0",
|
version:"1.0.0",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ vm-cc is the continuous compliance and evidence orchestration layer. It ingests
|
|||||||
## Layout
|
## Layout
|
||||||
- config/: source/rule/redaction/schedule configs
|
- config/: source/rule/redaction/schedule configs
|
||||||
- schemas/: JSON/YAML schemas for evidence, rules, reports
|
- schemas/: JSON/YAML schemas for evidence, rules, reports
|
||||||
- 00-frameworks/: frameworks and mappings (e.g., CIS→rules)
|
- 00-frameworks/: frameworks and mappings (e.g., CIS->rules)
|
||||||
- 10-controls/: control definitions
|
- 10-controls/: control definitions
|
||||||
- 20-collectors/: collectors to pull evidence from vm-skills, ops, ledger, mcp
|
- 20-collectors/: collectors to pull evidence from vm-skills, ops, ledger, mcp
|
||||||
- 30-evidence/: raw evidence drops (per-run folders)
|
- 30-evidence/: raw evidence drops (per-run folders)
|
||||||
@@ -19,7 +19,7 @@ vm-cc is the continuous compliance and evidence orchestration layer. It ingests
|
|||||||
- 70-violations/: findings and escalations
|
- 70-violations/: findings and escalations
|
||||||
- 80-remediation/: playbooks/automation for fixes
|
- 80-remediation/: playbooks/automation for fixes
|
||||||
- 90-automation/: pipelines/glue for end-to-end runs
|
- 90-automation/: pipelines/glue for end-to-end runs
|
||||||
- scripts/: thin CLI wrappers to orchestrate collect → evaluate → report → sign
|
- scripts/: thin CLI wrappers to orchestrate collect -> evaluate -> report -> sign
|
||||||
|
|
||||||
## Run directories
|
## Run directories
|
||||||
Each execution writes to a per-run folder set:
|
Each execution writes to a per-run folder set:
|
||||||
@@ -45,6 +45,7 @@ RUN_ID format: `YYYYMMDDThhmmssZ_<shorthash>`.
|
|||||||
"sha256": "..."
|
"sha256": "..."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"details": { "entries_checked": 18231 }
|
"details": { "entries_checked": 18231 },
|
||||||
|
"remediation": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
2
config/pins.yaml
Normal file
2
config/pins.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
version: "1.0.0"
|
||||||
|
constitution_sha256: ""
|
||||||
@@ -20,14 +20,13 @@ vm_ledger:
|
|||||||
path: "../vm-ledger"
|
path: "../vm-ledger"
|
||||||
evidence:
|
evidence:
|
||||||
- "log/entries.cborseq"
|
- "log/entries.cborseq"
|
||||||
- "ledger-cli verify --format json" # command invocation placeholder
|
|
||||||
|
|
||||||
vm_mcp:
|
vm_mcp:
|
||||||
path: "../vm-mcp"
|
path: "../vm-mcp"
|
||||||
evidence:
|
evidence:
|
||||||
- "governance/constitution.lock"
|
- "governance/constitution.lock"
|
||||||
|
|
||||||
vm_contracts:
|
contracts:
|
||||||
path: "../vm-contracts"
|
path: "../vm-contracts"
|
||||||
evidence:
|
evidence:
|
||||||
- "receipt_v1.schema.json"
|
- "receipt_v1.schema.json"
|
||||||
|
|||||||
73
scripts/vmcc
73
scripts/vmcc
@@ -14,21 +14,59 @@ REP_DIR="$ROOT/50-reports/$DAY/$RUN_ID"
|
|||||||
|
|
||||||
mkdir -p "$EVID_DIR" "$RULE_DIR" "$REP_DIR"
|
mkdir -p "$EVID_DIR" "$RULE_DIR" "$REP_DIR"
|
||||||
|
|
||||||
case "$CMD" in
|
hash_file() {
|
||||||
collect)
|
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] run_id=$RUN_ID"
|
||||||
echo "[vmcc] collecting evidence -> $EVID_DIR"
|
echo "[vmcc] collecting evidence -> $EVID_DIR"
|
||||||
"$ROOT/20-collectors/collect_ledger_verify.sh" "$EVID_DIR"
|
"$ROOT/20-collectors/collect_ledger_verify.sh" "$EVID_DIR"
|
||||||
"$ROOT/20-collectors/collect_constitution_hash.sh" "$EVID_DIR"
|
"$ROOT/20-collectors/collect_constitution_hash.sh" "$EVID_DIR"
|
||||||
"$ROOT/20-collectors/collect_backup_restore_drill.sh" "$EVID_DIR"
|
"$ROOT/20-collectors/collect_backup_restore_drill.sh" "$EVID_DIR"
|
||||||
;;
|
write_manifest
|
||||||
evaluate)
|
}
|
||||||
|
|
||||||
|
run_evaluate() {
|
||||||
echo "[vmcc] evaluating rules -> $RULE_DIR"
|
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/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/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"
|
"$ROOT/40-rules/backup_restore_drill_recent.sh" "$EVID_DIR" > "$RULE_DIR/backup.restore_drill_recent.json"
|
||||||
;;
|
}
|
||||||
report)
|
|
||||||
|
run_report() {
|
||||||
require_cmd jq
|
require_cmd jq
|
||||||
echo "[vmcc] assembling report -> $REP_DIR/report.json"
|
echo "[vmcc] assembling report -> $REP_DIR/report.json"
|
||||||
TS="$(iso_utc_now)"
|
TS="$(iso_utc_now)"
|
||||||
@@ -56,9 +94,30 @@ case "$CMD" in
|
|||||||
},
|
},
|
||||||
rules: $rules
|
rules: $rules
|
||||||
}' > "$REP_DIR/report.json"
|
}' > "$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}" >&2
|
echo "Usage: $0 {collect|evaluate|report|all}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user