Add Secmon SBOM release evidence
ober
90e73bd3095852f2f884308c711486ccde2827fb
--- a/.jerboa/security.json +++ b/.jerboa/security.json @@ -72,6 +72,11 @@ "requireTempCleanup": true }, "releaseEvidence": { + "sbom": { + "status": "required", + "marker": "sbom_status=present", + "scope": "source-typed-rust-binaries-target-proof-posture" + }, "targetLoadProof": { "proofFileEnv": "JSECMON_TARGET_LOAD_PROOF_FILE", "requireEnv": "JSECMON_REQUIRE_TARGET_LOAD_PROOF", --- a/Makefile +++ b/Makefile @@ -15,13 +15,14 @@ BUILD ?= build/rust TYPED := $(wildcard typed/*.ss) DIST_DIR ?= dist/release-evidence REPRO_DIR ?= dist/reproducibility +SBOM_DIR ?= dist/sbom SOAK_DIR ?= dist/soak SOAK_TIMEOUT_SECONDS ?= 300 SOAK_ITERATIONS ?= 8 BINARY_SMOKE_TIMEOUT_SECONDS ?= 10 .PHONY: vendor-deps ensure-jerboa ensure-jsqlite rust test ffi-demo kernels-check triage-check triage-store-check analytics-check detect-check storage-check entity-check threats-check geoip-check sigma-check yaml-rules-check buffer-check dns-sniffer-check suspicious-check netconn-check kernmod-check selinux-check container-check dns-servers-check sensitive-path-check dtrace-parse-check dtrace-runtime-check stealth-check ebpf-events-check ebpf-runtime-check proc-linux-check freebsd-parse-check event-meta-check config-check privdrop-check event-danger-check persistence-check file-change-check webshell-check platform-mounts-check analyze-cli-check collector-cli-check event-summary-check ioc-check frame-check correlate-check revshell-check cron-check logtamper-check detection-rules-check daemon-telemetry-check mux-telemetry-check ipaddr-check auth-check lolbin-check dga-check calendar-check monitor-process-check monitor-network-check monitor-files-check monitor-auth-check monitor-kernel-check monitor-cron-check monitor-container-check monitor-rootkit-check monitor-podman-check monitor-selinux-check monitor-lateral-check monitor-webshell-check monitor-revshell-check monitor-persistence-check monitor-logtamper-check monitor-dns-check monitor-manager-check event-json-check collector-check protocol-check event-codec-check local-store-check collector-pull-check agent-server-check checks native-runtime keygen analyze collector agent telemetry binaries clean -.PHONY: security audit verify reproducibility-report release-evidence soak-check soak-evidence binary-smoke +.PHONY: security audit verify sbom reproducibility-report release-evidence soak-check soak-evidence binary-smoke # Combined libdir path so sibling libraries `(jsecmon ...)` resolve to ./jsecmon # (a second --libdirs would replace, not append, the jerboa one). LIBDIRS := "$(CURDIR):$(JSQLITE_SRC):$(JERBOA)/lib" @@ -660,13 +661,16 @@ soak-evidence: verify: security test checks audit +sbom: rust + REPO_ROOT="$(CURDIR)" SBOM_DIR="$(SBOM_DIR)" BUILD="$(BUILD)" SCHEME="$(SCHEME)" sh scripts/sbom.sh + reproducibility-report: REPO_ROOT="$(CURDIR)" REPRO_DIR="$(REPRO_DIR)" MAKE="$(MAKE)" JERBOA="$(JERBOA)" JSQLITE_REPO="$(JSQLITE_REPO)" JSQLITE_SRC="$(JSQLITE_SRC)" SCHEME="$(SCHEME)" sh scripts/reproducibility-report.sh binary-smoke: REPO_ROOT="$(CURDIR)" BINARY_SMOKE_TIMEOUT_SECONDS="$(BINARY_SMOKE_TIMEOUT_SECONDS)" sh scripts/binary-smoke.sh -release-evidence: verify reproducibility-report +release-evidence: verify reproducibility-report sbom rm -rf "$(DIST_DIR)" mkdir -p "$(DIST_DIR)" $(MAKE) --no-print-directory soak-evidence @@ -676,7 +680,7 @@ release-evidence: verify reproducibility-report cp "$(SOAK_DIR)/soak-check.txt" "$(DIST_DIR)/soak-check.txt" git rev-parse HEAD > "$(DIST_DIR)/git-commit.txt" git status --short > "$(DIST_DIR)/git-status.txt" - uname -a > "$(DIST_DIR)/build-environment.txt" + { echo "repo=jerboa-secmon"; echo "platform=$$(uname -srm)"; } > "$(DIST_DIR)/build-environment.txt" "$(SCHEME)" --version >> "$(DIST_DIR)/build-environment.txt" cargo metadata --manifest-path "$(BUILD)/Cargo.toml" --locked --format-version 1 > "$(DIST_DIR)/cargo-metadata-typed.json" cargo audit --file "$(BUILD)/Cargo.lock" -D warnings > "$(DIST_DIR)/rustsec-typed.txt" @@ -685,7 +689,10 @@ release-evidence: verify reproducibility-report { for bin in jsecmon-keygen jsecmon-analyze jsecmon-collector jsecmon-agent jsecmon-telemetry; do echo "== $$bin =="; if command -v otool >/dev/null 2>&1; then otool -L "$$bin"; elif command -v ldd >/dev/null 2>&1; then ldd "$$bin"; else echo "no dynamic-linkage inspector found"; fi; done; } > "$(DIST_DIR)/native-linkage.txt" rm -rf "$(DIST_DIR)/reproducibility" cp -R "$(REPRO_DIR)" "$(DIST_DIR)/reproducibility" + rm -rf "$(DIST_DIR)/sbom" + cp -R "$(SBOM_DIR)" "$(DIST_DIR)/sbom" test "$$(grep '^status=' "$(DIST_DIR)/reproducibility/report.txt" | cut -d= -f2)" = "match" + sh scripts/sanitize-evidence.sh "$(DIST_DIR)" # ── Native binaries ─────────────────────────────────────────────────────────── # The shippable artifacts are compiled, self-contained executables — never --- a/docs/release-evidence.md +++ b/docs/release-evidence.md @@ -36,6 +36,9 @@ The evidence directory contains: - `build-environment.txt` with OS and Scheme toolchain identity. - Cargo dependency metadata and RustSec audit output for the generated typed Rust crate. +- `sbom/`, a host-neutral SBOM-style manifest covering source, examples, docs, + release controls, typed Rust metadata, generated binary hashes when present, + and target proof posture. The SBOM status marker is `sbom_status=present`. - SHA-256 hashes for `jsecmon-keygen`, `jsecmon-analyze`, `jsecmon-collector`, `jsecmon-agent`, and `jsecmon-telemetry`. - Release input hashes and dynamic-linkage output from `otool` or `ldd`. @@ -57,6 +60,7 @@ Required release markers: - `local_loopback_iterations_default: 8` - `production_load_status=blocked-not-run` +- `sbom_status=present` - `target_load_proof_status: documented` - `target_telemetry_proof_status: documented` new file mode 100755 --- /dev/null +++ b/scripts/sanitize-evidence.sh @@ -0,0 +1,29 @@ +#!/bin/sh +set -eu + +if [ "$#" -eq 0 ]; then + set -- dist/release-evidence +fi + +for evidence_dir in "$@"; do + if [ ! -d "$evidence_dir" ]; then + continue + fi + + find "$evidence_dir" -type f -print | while IFS= read -r file; do + if ! LC_ALL=C grep -Iq . "$file" 2>/dev/null; then + continue + fi + perl -0pi -e ' + s#/Users/[^[:space:]\x22\x27()]+#<local-path>#g; + if (defined $ENV{HOME} && length $ENV{HOME}) { + my $home = quotemeta($ENV{HOME}); + s#$home#<home>#g; + } + s#~/mine#<local-path>#g; + s#\$\((HOME)\)/mine#<local-path>#g; + s#users-MacBook-Pro#<host>#g; + s#git\@#git-at-#g; + ' "$file" + done +done new file mode 100755 --- /dev/null +++ b/scripts/sbom.sh @@ -0,0 +1,118 @@ +#!/bin/sh +set -eu + +repo_root=${REPO_ROOT:-$(pwd)} +out_dir=${SBOM_DIR:-"$repo_root/dist/sbom"} +build_dir=${BUILD:-build/rust} +scheme=${SCHEME:-scheme} +sanitizer=${JSECMON_EVIDENCE_SANITIZER:-scripts/sanitize-evidence.sh} + +case "$out_dir" in + /*) ;; + *) out_dir="$repo_root/$out_dir" ;; +esac + +rm -rf "$out_dir" +mkdir -p "$out_dir" +cd "$repo_root" + +if [ -x "$sanitizer" ]; then + trap '"$sanitizer" "$out_dir"' EXIT +fi + +sha256_file() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" + else + shasum -a 256 "$1" + fi +} + +hash_manifest() { + dir=$1 + out=$2 + if [ -d "$dir" ]; then + ( + cd "$dir" + find . -type f \ + ! -path './.git/*' \ + ! -path './dist/*' \ + ! -path './build/*' \ + ! -path './vendor/*' \ + -print | LC_ALL=C sort | while IFS= read -r file; do + sha256_file "$file" + done + ) > "$out" + else + : > "$out" + fi +} + +{ + echo "repo=jerboa-secmon" + echo "generated_at_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo "git_head=$(git rev-parse HEAD 2>/dev/null || true)" + echo "platform=$(uname -srm)" + echo "scheme_status=selected" + "$scheme" --version 2>/dev/null || true + echo "cargo=$(cargo --version 2>/dev/null || true)" + echo "rustc=$(rustc --version 2>/dev/null || true)" +} > "$out_dir/build-environment.txt" + +if [ -f "$build_dir/Cargo.toml" ]; then + cp "$build_dir/Cargo.toml" "$out_dir/typed-Cargo.toml" + [ -f "$build_dir/Cargo.lock" ] && cp "$build_dir/Cargo.lock" "$out_dir/typed-Cargo.lock" + cargo metadata --manifest-path "$build_dir/Cargo.toml" --locked --format-version 1 > "$out_dir/cargo-metadata-typed.json" 2>&1 || true + if command -v cargo >/dev/null 2>&1 && cargo audit --version >/dev/null 2>&1 && [ -f "$build_dir/Cargo.lock" ]; then + cargo audit --file "$build_dir/Cargo.lock" -D warnings > "$out_dir/rustsec-typed.txt" 2>&1 \ + && echo "cargo_audit_status=pass" > "$out_dir/cargo-audit.status" \ + || echo "cargo_audit_status=fail" > "$out_dir/cargo-audit.status" + else + echo "cargo_audit_status=blocked" > "$out_dir/cargo-audit.status" + echo "cargo audit is not installed or Cargo.lock is missing." > "$out_dir/rustsec-typed.txt" + fi +else + echo "cargo_metadata_status=blocked-build-rust-missing" > "$out_dir/cargo-audit.status" +fi + +hash_manifest bin "$out_dir/bin.sha256" +hash_manifest jsecmon "$out_dir/jsecmon.sha256" +hash_manifest typed "$out_dir/typed.sha256" +hash_manifest tests "$out_dir/tests.sha256" +hash_manifest examples "$out_dir/examples.sha256" +hash_manifest docs "$out_dir/docs.sha256" +hash_manifest scripts "$out_dir/scripts.sha256" +hash_manifest .github "$out_dir/github-workflows.sha256" + +if [ -f .jerboa/security.json ]; then + sha256_file .jerboa/security.json > "$out_dir/jerboa-security.sha256" +else + : > "$out_dir/jerboa-security.sha256" +fi + +find . -maxdepth 1 -type f \ + \( -name 'Makefile' -o -name 'README.md' -o -name 'SECURITY.md' -o -name 'LICENSE' -o -name '*.ss' -o -name '*.c' \) \ + -print | LC_ALL=C sort | while IFS= read -r file; do + sha256_file "$file" + done > "$out_dir/top-level.sha256" + +bins="jsecmon-keygen jsecmon-analyze jsecmon-collector jsecmon-agent jsecmon-telemetry" +: > "$out_dir/binaries.sha256" +for bin in $bins; do + if [ -f "$bin" ]; then + sha256_file "$bin" >> "$out_dir/binaries.sha256" + fi +done + +{ + echo "repo=jerboa-secmon" + echo "sbom_status=present" + echo "typed_rust_crate_status=$([ -f "$build_dir/Cargo.toml" ] && echo present || echo blocked-build-rust-missing)" + echo "binary_hash_status=$([ -s "$out_dir/binaries.sha256" ] && echo present || echo not-built)" + echo "crypto_provider=generated-typed-rust-and-jerboa-native" + echo "sqlite_provider=jsqlite" + echo "target_load_proof=fail-closed" + echo "target_telemetry_proof=fail-closed" +} > "$out_dir/dependency-posture.txt" + +echo "status=generated" --- a/scripts/security-check.sh +++ b/scripts/security-check.sh @@ -41,6 +41,8 @@ require_file ".jerboa/security.json" require_file "scripts/build-binaries.sh" require_file "scripts/binary-smoke.sh" require_file "scripts/security-check.sh" +require_file "scripts/sanitize-evidence.sh" +require_file "scripts/sbom.sh" require_file "scripts/reproducibility-report.sh" require_file "scripts/soak-evidence.sh" require_file "jsecmon/tcp-runtime.ss" @@ -52,6 +54,7 @@ find . -maxdepth 1 -iname "README*" -type f | grep -q . || { for release_marker in \ 'local_loopback_iterations_default: 8' \ 'production_load_status=blocked-not-run' \ + 'sbom_status=present' \ 'target_load_proof_status: documented' \ 'target_telemetry_proof_status: documented' do @@ -75,6 +78,8 @@ require_text 'JSECMON_REQUIRE_TARGET_LOAD_PROOF' scripts/soak-evidence.sh require_text 'JSECMON_TARGET_TELEMETRY_PROOF_FILE' scripts/soak-evidence.sh require_text 'JSECMON_REQUIRE_TARGET_TELEMETRY_PROOF' scripts/soak-evidence.sh require_text 'blocked-target-proof' scripts/soak-evidence.sh +require_text 'sbom_status=present' scripts/sbom.sh +require_text 'scripts/sanitize-evidence.sh' Makefile if command -v rg >/dev/null 2>&1; then say "running high-confidence secret scan"