Security hardening and release readiness
ober
edab46ebba267d0ee1649890e857a3a8b1550a88
--- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ TYPED_LLVMIR_PARITY_FN ?= sample_typed_llvmir_smoke::main LLVM_BIN ?= $(shell if command -v llvm-as >/dev/null 2>&1; then dirname "$$(command -v llvm-as)"; elif [ -x /opt/homebrew/opt/llvm/bin/llvm-as ]; then echo /opt/homebrew/opt/llvm/bin; elif [ -x /usr/local/opt/llvm/bin/llvm-as ]; then echo /usr/local/opt/llvm/bin; fi) TYPED_WRAPPER_DIR ?= build/typed/jerboa -.PHONY: help chez chez-cross build binary binary-typed binary-typed-smoke binary-cross native-cross pure-audit typecheck typed-rust typed-llvmir typed-llvmir-check typed-llvmir-smoke typed-llvmir-parity typed-wrappers typed-build typed-wrapper-smoke typed-split-tree-smoke typed-test typed-clean test test-known-flaky test-reader test-core test-runtime test-try-debug test-stdlib test-ffi test-modules test-expanded test-contract test-ergo test-sqlite-robustness test-limits-primitives test-typed-core test-typed-parser test-typed-checker test-typed-rust test-typed-llvmir test-typed-wrappers test-pure-audit test-features test-wrappers test-phase4a test-phase4b test-phase4c test-phase4d test-phase4e test-phase4f test-phase5 test-phase5e test-phase6 test-phase7 test-phase8 test-functional test-repl test-security test-security-profile test-native test-gaps native clean-native audit audit-native security-audit clean security security-production security-profile security-hardware-smoke fuzz fuzz-smoke fuzz-deep fuzz-reader-fuzz fuzz-json-fuzz fuzz-http2-fuzz fuzz-dns-fuzz fuzz-pregexp-fuzz fuzz-csv-fuzz fuzz-base64-fuzz fuzz-hex-fuzz fuzz-uri-fuzz fuzz-format-fuzz fuzz-router-fuzz fuzz-sandbox-fuzz test-rawstring test-regex test-rx test-peg test-regex-all check-docs check-docs-strict data-check docker-build docker-push +.PHONY: help chez chez-cross build binary binary-typed binary-typed-smoke binary-cross native-cross pure-audit typecheck typed-rust typed-llvmir typed-llvmir-check typed-llvmir-smoke typed-llvmir-parity typed-wrappers typed-build typed-wrapper-smoke typed-split-tree-smoke typed-test typed-clean test test-known-flaky test-reader test-core test-runtime test-try-debug test-stdlib test-ffi test-modules test-expanded test-contract test-ergo test-sqlite-robustness test-limits-primitives test-typed-core test-typed-parser test-typed-checker test-typed-rust test-typed-llvmir test-typed-wrappers test-pure-audit test-features test-wrappers test-phase4a test-phase4b test-phase4c test-phase4d test-phase4e test-phase4f test-phase5 test-phase5e test-phase6 test-phase7 test-phase8 test-functional test-repl test-security test-security-profile test-native test-gaps native clean-native audit audit-native security-audit clean security security-production security-profile security-hardware-smoke sbom reproducibility-report release-evidence fuzz fuzz-smoke fuzz-deep fuzz-reader-fuzz fuzz-json-fuzz fuzz-http2-fuzz fuzz-dns-fuzz fuzz-pregexp-fuzz fuzz-csv-fuzz fuzz-base64-fuzz fuzz-hex-fuzz fuzz-uri-fuzz fuzz-format-fuzz fuzz-router-fuzz fuzz-sandbox-fuzz test-rawstring test-regex test-rx test-peg test-regex-all check-docs check-docs-strict data-check docker-build docker-push .PHONY: check-cross-tools docker fuzz-websocket-fuzz jlsp jlsp-freebsd-amd64 \ jlsp-install jlsp-linux-amd64 jlsp-portable jmcp-freebsd-amd64 \ jmcp-freebsd-arm64 jmcp-linux-amd64 jmcp-linux-arm64 \ @@ -116,6 +116,9 @@ help: @echo " audit Run production security/release gate checks" @echo " audit-native Run cargo audit on Rust native library" @echo " pure-audit Scan jerboa-* repos for non-pure runtime surfaces" + @echo " sbom Write local SBOM/toolchain evidence under dist/sbom" + @echo " reproducibility-report Compare two local multicall builds" + @echo " release-evidence Write release-readiness evidence under dist/release-evidence" @echo "" @echo "Cross-platform binaries (one static jerboa + jerbuild/jmcp/jlsp symlinks):" @echo " jerboa-macos-arm64 Build for macOS arm64 -> dist/macos-arm64/" @@ -229,7 +232,9 @@ $(SCHEME): vendor/ChezScheme/configure test -x $(SCHEME) build: chez - $(SCHEME) --libdirs $(LIBDIRS) --script support/build.ss + @mkdir -p build + JERBOA_BUILD_GENSYM_PREFIX_FILE=$(CURDIR)/build/jerboa-build-gensym-prefix.txt \ + $(SCHEME) --libdirs $(LIBDIRS) --script support/build.ss # Build a self-contained Jerboa binary that bundles petite.boot, scheme.boot, # and a WPO-compiled entry program. Output: ./jerboa-bin @@ -280,8 +285,10 @@ jerbuild-smoke: jerbuild # dist/jmcp # MCP server (mcp/server.ss) # dist/jlsp # LSP server (lsp/main-binary.ss) # dist/jerbuild <src> <lib> # transpile; dist/jerbuild binary E.ss OUT -.PHONY: jerboa jerboa-smoke -jerboa: chez build mcp-check lsp-gen +.PHONY: jerboa jerboa-multicall jerboa-smoke +jerboa: chez build mcp-check lsp-gen jerboa-multicall + +jerboa-multicall: chez $(SCHEME) --libdirs $(LIBDIRS) --script support/build-jerboa-multicall.ss .PHONY: system-check @@ -479,8 +486,12 @@ RELEASE_TARGET ?= $(shell host="$(HOST_UNAME_S)-$(HOST_UNAME_M)"; if [ "$$host" RELEASE_TARGETS ?= macos-arm64 linux-amd64 linux-arm64 freebsd-amd64 RELEASE_DIR ?= dist/release RELEASE_REPO ?= ~lisp/jerboa +EVIDENCE_DIR ?= dist/release-evidence +SIGNING_EVIDENCE_DIR ?= dist/signing-evidence +SBOM_DIR ?= dist/sbom +REPRO_DIR ?= dist/reproducibility -.PHONY: release-artifact release-artifacts release-upload +.PHONY: release-artifact release-artifacts sign-release-artifacts signing-evidence release-upload sbom reproducibility-report release-evidence release-artifact: @support/package-jerboa-release.sh "$(RELEASE_VERSION)" "$(RELEASE_TARGET)" "$(RELEASE_DIR)" @@ -489,10 +500,52 @@ release-artifacts: jerboa-portable support/package-jerboa-release.sh "$(RELEASE_VERSION)" "$$target" "$(RELEASE_DIR)"; \ done +sign-release-artifacts: + JERBOA_RELEASE_DIR="$(RELEASE_DIR)" support/sign-release-artifacts.sh sign + +signing-evidence: + JERBOA_RELEASE_DIR="$(RELEASE_DIR)" JERBOA_SIGNING_EVIDENCE_DIR="$(SIGNING_EVIDENCE_DIR)" support/sign-release-artifacts.sh evidence + release-upload: @support/sourcehut-release-upload.sh "$(RELEASE_VERSION)" "$(RELEASE_REPO)" \ "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz \ - "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.sha256 + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.sha256 \ + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.asc \ + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.sig \ + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.minisig \ + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.sha256.asc \ + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.sha256.sig \ + "$(RELEASE_DIR)"/jerboa-"$(RELEASE_VERSION)"-*.tar.gz.sha256.minisig \ + "$(RELEASE_DIR)"/release-manifest.sha256 \ + "$(RELEASE_DIR)"/release-manifest.sha256.asc \ + "$(RELEASE_DIR)"/release-manifest.sha256.sig \ + "$(RELEASE_DIR)"/release-manifest.sha256.minisig + +sbom: + JERBOA_SBOM_DIR="$(SBOM_DIR)" support/sbom.sh + +reproducibility-report: + JERBOA_REPRO_DIR="$(REPRO_DIR)" support/reproducibility-report.sh + +release-evidence: + mkdir -p "$(EVIDENCE_DIR)" + git rev-parse HEAD > "$(EVIDENCE_DIR)/git-commit.txt" + git status --short > "$(EVIDENCE_DIR)/git-status.txt" + uname -a > "$(EVIDENCE_DIR)/build-environment.txt" + $(MAKE) audit > "$(EVIDENCE_DIR)/audit.txt" 2>&1 + $(MAKE) jerboa-smoke > "$(EVIDENCE_DIR)/jerboa-smoke.txt" 2>&1 + $(MAKE) unification-release-check > "$(EVIDENCE_DIR)/unification-release-check.txt" 2>&1 + $(MAKE) security-hardware-smoke > "$(EVIDENCE_DIR)/security-hardware-smoke.txt" 2>&1 + $(MAKE) reproducibility-report > "$(EVIDENCE_DIR)/reproducibility-report.txt" 2>&1 + JERBOA_RELEASE_DIR="$(RELEASE_DIR)" JERBOA_SIGNING_EVIDENCE_DIR="$(EVIDENCE_DIR)/signing" support/sign-release-artifacts.sh evidence > "$(EVIDENCE_DIR)/signing-evidence.txt" 2>&1 + $(MAKE) sbom > "$(EVIDENCE_DIR)/sbom.txt" 2>&1 + shasum -a 256 Makefile SECURITY.md README.md docs/release-security.md docs/release-artifacts.md support/sign-release-artifacts.sh support/sbom.sh support/fasl-cache-equivalence.ss support/reproducibility-report.sh tools/security-audit.sh > "$(EVIDENCE_DIR)/release-inputs-sha256.txt" + rm -rf "$(EVIDENCE_DIR)/sbom" "$(EVIDENCE_DIR)/reproducibility" + cp -R "$(SBOM_DIR)" "$(EVIDENCE_DIR)/sbom" + cp -R "$(REPRO_DIR)" "$(EVIDENCE_DIR)/reproducibility" + grep -q '^signature_status=' "$(EVIDENCE_DIR)/signing/status.txt" + grep -q '^status=match$$' "$(EVIDENCE_DIR)/reproducibility/result.txt" + grep -q '^sbom_status=present$$' "$(EVIDENCE_DIR)/sbom/manifest.txt" # Cross-compiled jerbuild. Drives support/build-jerbuild.sh with TARGET_* env, # producing ./jerbuild-<machine> alongside the host ./jerbuild. --- a/README.md +++ b/README.md @@ -212,6 +212,7 @@ Common local targets: make build # build core Jerboa libraries make binary # native binary build for macOS/FreeBSD/other local hosts make audit # production security and release-readiness gate +make release-evidence # audit, SBOM, reproducibility, and release smoke evidence make test # core test suite make test-features # feature-phase tests make test-native # Rust native backend tests --- a/SECURITY.md +++ b/SECURITY.md @@ -17,6 +17,11 @@ cut from a clean checkout after: - Release artifacts additionally pass `make jerboa`, `make unification-release-check`, `make jerboa-smoke`, and `make security-hardware-smoke`. +- `make sbom`, `make reproducibility-report`, and `make release-evidence` + produce current SBOM, two-build multicall reproducibility, checksum, and + signing-posture evidence. +- Production release artifacts have detached publisher signatures recorded by + `make signing-evidence` with `JERBOA_REQUIRE_RELEASE_SIGNATURES=1`. - RustSec, secret scanning, `.jerboa-system` manifest checks, generated artifact policy, native boundary checks, and security profile tests are clean. - FFI, filesystem, network, shell, generated-artifact, and credential-handling --- a/data/anti-patterns.sexp +++ b/data/anti-patterns.sexp @@ -1315,4 +1315,1399 @@ ("title" . "Escape Tree-sitter Query Anchor Dots In Emacs Lisp") - ("tools" "rg" "emacs --batch" "make test" "make verify"))) + ("tools" "rg" "emacs --batch" "make test" "make verify")) + (("advice" + . + "Wrap rg/find patterns containing Markdown backticks in single quotes, or avoid shell parsing by using an argv-style tool invocation. When composing a shell string, quote the whole pattern before any backtick characters.") + ("avoid" + . + "Do not pass unquoted Markdown text containing backticks to shell commands. Bash treats backticks as command substitution, which can run unintended commands and make read-only searches fail noisily.") + ("id" . "quote-markdown-backticks-in-shell-searches") + ("kinds" "all" "docs" "security") + ("pattern" . "rg .*`[^`]+`") ("severity" . "low") + ("tags" "shell" "rg" "markdown" "backticks" "tracker" + "quoting") + ("title" + . + "Quote Markdown Backticks In Shell Search Patterns") + ("tools" + "rg" + "jerboa_anti_pattern_lookup" + "jerboa_security_scan")) + (("advice" + . + "Before copying release evidence subdirectories, remove the destination paths explicitly, for example `rm -rf \"$DIST_DIR/sbom\" \"$DIST_DIR/reproducibility\"`, then `cp -R` the freshly generated directories. Verify the copied top-level `result.txt` and check that no nested `reproducibility/reproducibility` directory exists.") + ("avoid" + . + "Do not copy fresh SBOM or reproducibility directories into an existing release-evidence destination. Re-running the target can nest a new directory inside stale evidence and leave the top-level result from an older failed run.") + ("id" . "release-evidence-copy-without-clean-destination") + ("kinds" "docs" "security") + ("pattern" + . + "cp -R .*dist/release-evidence/(sbom|reproducibility)|cp -R .*\\$\\(DIST_DIR\\)/(sbom|reproducibility)") + ("severity" . "medium") + ("tags" "release-evidence" "reproducibility" "sbom" "cp" + "stale-artifacts") + ("title" + . + "Release Evidence Copy Without Cleaning Destination") + ("tools" + "rg" + "find" + "make release-evidence" + "git diff --check")) + (("advice" + . + "Use a small checked helper around `open-file-output-port` with `(file-options no-fail)`, `(buffer-mode block)`, and `(native-transcoder)` for textual generated files. Route generated Scheme/C/helper output through that helper, then rerun `jerboa_security_scan` and the build/smoke target.") + ("avoid" + . + "Do not leave generated-file overwrite paths as scanner-visible `call-with-output-file` calls when the project security scanner flags overwrite semantics. Even a third `'replace` argument may be missed by line-oriented scanners and leaves the release evidence noisy.") + ("id" . "scanner-visible-call-with-output-file-overwrite") + ("kinds" "security" "module" "script") + ("pattern" . "call-with-output-file\\s+[^\\n]+") + ("severity" . "medium") + ("tags" "call-with-output-file" "open-file-output-port" + "overwrite" "scanner" "generated-files") + ("title" + . + "Scanner-Visible Generated Writes Need Explicit Replacement Helpers") + ("tools" + "jerboa_eval" + "jerboa_security_scan" + "jerboa_check_balance" + "git diff --check")) + (("advice" + . + "Order release evidence so reproducibility rebuilds run before final provenance/SBOM capture, or rerun provenance after rebuilding. Make native shim targets depend on Makefile/build-flag inputs such as `Makefile` and deterministic linker flags, then verify the final binary hash in provenance, SBOM, and the filesystem all match.") + ("avoid" + . + "Do not capture native shim hashes, linkage, or SBOM provenance before a later reproducibility step removes and rebuilds the same native artifact. The evidence can record a stale binary hash while the final workspace contains a different rebuilt artifact.") + ("id" . "native-provenance-before-repro-build") + ("kinds" "security" "ffi" "test") + ("pattern" + . + "release-evidence.*(provenance|sbom).*reproducibility|qt-shim-provenance.*reproducibility-report") + ("severity" . "medium") + ("tags" "release-evidence" "reproducibility" "native-shim" + "sbom" "ffi") + ("title" + . + "Do Not Capture Native Provenance Before Reproducibility Rebuilds") + ("tools" "make release-evidence" "shasum" "otool -l" + "git diff --check" "jerboa_security_scan")) + (("advice" + . + "Use `cargo audit --version` as the availability check and run `cargo audit --file <repo-native/Cargo.lock> -D warnings`. Treat missing cargo-audit or any advisory as a nonzero production gate and copy the audit output into release evidence.") + ("avoid" + . + "Do not check for a `cargo-audit` executable or audit only by manifest path in release gates. Modern installs usually expose `cargo audit` as a Cargo subcommand, and manifest-only invocation can miss the exact lockfile evidence path.") + ("id" . "cargo-audit-subcommand-not-cargo-audit-binary") + ("kinds" "security" "script") + ("pattern" + . + "command -v cargo-audit|cargo audit --manifest-path") + ("severity" . "medium") + ("tags" "cargo" "audit" "rustsec" "release-evidence" + "dependency-audit") + ("title" + . + "Use Cargo Audit Subcommand For Fail-Closed Audits") + ("tools" + "rg" + "make release-evidence" + "cargo audit --file" + "jerboa_security_scan")) + (("advice" + . + "Apply deterministic no-UUID linking only to the final Jerboa standalone binary through the Jerboa binary linker support. Leave Cargo native crates with normal macOS UUID load commands unless the crate build system explicitly supports a safe final-artifact-only flag. Verify with a full `make release-evidence`, not just `cargo build`.") + ("avoid" + . + "Do not set Cargo-wide `RUSTFLAGS=-C link-arg=-Wl,-no_uuid` to make release evidence reproducible on macOS. Cargo applies it to proc-macro and dependency dylibs too, and the dynamic loader can reject them for missing `LC_UUID`.") + ("id" . "cargo-rustflags-no-uuid-breaks-macos-proc-macros") + ("kinds" "script" "debug-error") + ("pattern" . "RUSTFLAGS=.*no_uuid|LC_UUID load command") + ("severity" . "high") + ("tags" "macos" "cargo" "rustflags" "no_uuid" + "reproducibility") + ("title" + . + "Do Not Apply macOS no_uuid Through Cargo RUSTFLAGS") + ("tools" + "make release-evidence" + "otool -l" + "jerboa_failure_advisor" + "git diff --check")) + (("advice" + . + "When running sibling release evidence from a parent Makefile, explicitly unset parent evidence path variables for the sub-make, while passing only the intended toolchain variables such as `JERBUILD`. Then verify each sibling's own `dist/release-evidence/result.txt` before copying or summarizing it.") + ("avoid" + . + "Do not invoke sibling repository `make release-evidence` targets while inheriting the parent repo's `DIST_DIR`, `RELEASE_EVIDENCE_DIR`, `SBOM_DIR`, or `REPRO_DIR`. The sibling can write into the parent evidence tree, causing false dependency evidence or clobbered artifacts.") + ("id" . "recursive-release-evidence-env-leak") + ("kinds" "script" "security") + ("pattern" . "\\$\\(MAKE\\) -C .*release-evidence") + ("severity" . "medium") + ("tags" "make" "release-evidence" "recursive-make" + "environment" "sbom") + ("title" + . + "Recursive Release Evidence Must Clear Parent Evidence Paths") + ("tools" + "rg" + "make dependency-evidence" + "find dist/release-evidence" + "git diff --check")) + (("advice" + . + "For reproducibility evidence, set `JERBUILD_BINARY_OBJ_DIR` to a fixed absolute directory whose basename starts with `jerbuild-binary-`, remove it before each build, and clean native artifacts between passes. If mismatch remains, inspect byte diffs and preserved generated inputs before weakening the gate.") + ("avoid" + . + "Do not compare two `jerbuild binary` outputs built with default temporary object directories and assume a byte mismatch means source nondeterminism. Absolute temporary object paths can be embedded in generated/link inputs and change between runs.") + ("id" . "jerbuild-repro-temp-object-paths") + ("kinds" "script" "test") + ("pattern" + . + "/tmp/jerbuild-binary-[0-9]+|binary_status=mismatch") + ("severity" . "medium") + ("tags" "jerbuild" "reproducibility" "binary" + "JERBUILD_BINARY_OBJ_DIR" "release-evidence") + ("title" + . + "Temporary Jerbuild Object Paths Break Binary Reproducibility") + ("tools" "make reproducibility-report" "cmp -l" "xxd" + "JERBUILD_BINARY_KEEP_OBJ_DIR" "jerboa_security_scan")) + (("advice" + . + "Before copying sibling evidence into an SBOM, verify the sibling currently defines a `release-evidence` target without running recipes, for example by parsing its Makefile target list or using a non-executing make database query. Do not use `make -n release-evidence` for this check because recursive `$(MAKE)` recipe lines may still execute during dry-run. Report `target-missing`, `missing`, `blocked`, or `present` distinctly, and remove any copied destination when the target is missing so stale evidence cannot survive.") + ("avoid" + . + "Do not mark sibling dependency evidence as present merely because `../sibling/dist/release-evidence` exists. That directory may be stale even if the sibling has no current `make release-evidence` target or the latest run failed before producing valid evidence.") + ("id" . "stale-sibling-evidence-without-target-check") + ("kinds" "script" "security") + ("pattern" + . + "copy.*dist/release-evidence|release_evidence=present") + ("severity" . "medium") + ("tags" "release-evidence" "sbom" "dependency-evidence" + "stale-artifact" "make") + ("title" + . + "Do Not Copy Stale Sibling Evidence Without Target Verification") + ("tools" + "awk Makefile target parser" + "rg" + "find dist/release-evidence" + "make release-evidence")) + (("advice" + . + "Use a non-executing target definition check instead: parse the sibling Makefile target header, or use a make database/query mode that does not execute recursive recipes. Reserve real `make release-evidence` runs for the actual evidence step and record their status separately.") + ("avoid" + . + "Do not test whether a sibling target exists by running `make -n <target>` when the recipe contains recursive `$(MAKE)` commands. GNU make may execute recursive make lines even in dry-run mode, so the probe can fail because a nested dry-run target failed, not because the target is missing.") + ("id" . "make-n-target-probe-recursive-make") + ("kinds" "script" "debug-error") + ("pattern" + . + "make -C .* -n release-evidence|\\$\\(MAKE\\).*reproducibility-report") + ("severity" . "medium") + ("tags" "make" "dry-run" "recursive-make" "release-evidence" + "target-detection") + ("title" . "Do Not Use make -n As A Recursive Target Probe") + ("tools" "awk" "make -qp" "rg" "make release-evidence")) + (("advice" + . + "Verify package-name compatibility before treating a fork as a RustSec fix. Run `cargo update -p <package>`, inspect `cargo tree -i <package>`, and run `cargo audit --file Cargo.lock -D warnings`. If the fork has a different package name, use an upstream same-name fork, vendor with an explicit reviewed patch, remove the dependency path, or keep the audit gate fail-closed with documented risk.") + ("avoid" + . + "Do not assume `[patch.crates-io] rsa = { package = \"sad-rsa\", ... }` will replace a transitive dependency on the `rsa` package. Cargo will either reject same-source crates.io replacement or ignore a differently named Git package, leaving the vulnerable package in the graph.") + ("id" . "cargo-patch-different-package-name") + ("kinds" "security" "docs") + ("pattern" + . + "\\[patch\\.crates-io\\].*package\\s*=\\s*\"sad-rsa\"") + ("severity" . "medium") + ("tags" "cargo" "rustsec" "patch" "transitive-dependency" + "rsa" "sad-rsa") + ("title" + . + "Cargo Patch Cannot Replace A Different Package Name") + ("tools" + "cargo tree -i" + "cargo audit --file" + "cargo update" + "rg Cargo.lock")) + (("advice" + . + "After removing a failed patch, run `cargo update` from the crate root and verify with `rg '<fork-name>|<new advisory package>' Cargo.lock Cargo.toml`, `cargo tree -i <original-package>`, and `cargo audit --file Cargo.lock -D warnings`. Keep only lockfile entries actually used by the resolved graph.") + ("avoid" + . + "Do not leave unused Git or fork package entries in `Cargo.lock` after removing a failed `[patch.crates-io]` experiment. They can make dependency provenance look safer than the compiled graph and confuse audits.") + ("id" . "cargo-lock-stale-unused-patch-package") + ("kinds" "security" "test") + ("pattern" . "name = \"sad-rsa\"[\\s\\S]*source = \"git\\+") + ("severity" . "low") + ("tags" "cargo" "Cargo.lock" "patch" "dependency-audit" + "rustsec") + ("title" + . + "Remove Stale Lockfile Entries After Failed Cargo Patch Experiments") + ("tools" + "cargo update" + "cargo tree -i" + "cargo audit --file" + "rg Cargo.lock")) + (("advice" + . + "After any age upgrade, run `cargo audit --file Cargo.lock -D warnings` and search the lockfile for both `proc-macro-error` and `proc-macro-error2`. Treat advisory swaps as unresolved unless the release tracker explicitly accepts the new risk or the dependency path is removed.") + ("avoid" + . + "Do not assume upgrading `age` from 0.10 to 0.11 clears the unmaintained proc-macro audit finding. Current 0.11 dependency resolution can replace `proc-macro-error` with `proc-macro-error2`, which has its own RustSec unmaintained advisory.") + ("id" . "age-upgrade-proc-macro-error2-audit-swap") + ("kinds" "security") + ("pattern" + . + "age\\s*=\\s*\\{\\s*version\\s*=\\s*\"0\\.11\"") + ("severity" . "medium") + ("tags" "age" "rustsec" "proc-macro-error" + "proc-macro-error2" "cargo-audit") + ("title" + . + "Age 0.11 May Swap proc-macro-error Advisory For proc-macro-error2") + ("tools" + "cargo audit --file" + "cargo tree -i proc-macro-error" + "cargo tree -i proc-macro-error2" + "rg Cargo.lock")) + (("advice" + . + "For release evidence, isolate the shared Jerboa library state: snapshot or use a fresh immutable/prewarmed libdir for each pass, avoid rebuilding support/native targets between individual WPO binaries, and verify each entry both alone and in the exact sequential target. If a later WPO build crashes after an earlier binary, suspect corrupted or stale shared FASL artifacts before changing application code.") + ("avoid" + . + "Do not treat a multi-binary WPO reproducibility report as clean if each binary build can mutate shared `jerboa/lib` `.so`/`.wpo` artifacts before the next binary or before the second pass. Do not fix this with ad hoc deletion lists or byte-level FASL edits without proving the shared artifact baseline is identical.") + ("id" . "sequential-wpo-shared-artifact-repro") + ("kinds" "security" "script" "debug-error") + ("pattern" + . + "make\\s+.*(binaries|release-evidence).*compile-whole-program|restore_jerboa_artifacts|jerboa/lib.*\\.so|\\.wpo") + ("severity" . "high") + ("tags" "wpo" "reproducibility" "jerboa-lib" "fasl" + "release-evidence") + ("title" + . + "Do Not Compare Sequential WPO Binaries With Mutable Shared Lib Artifacts") + ("tools" "jerboa_check_balance" "jerboa_compile_check" + "jerboa_security_scan" "make reproducibility-report" + "shasum" "otool -l")) + (("advice" + . + "For runtime crypto advisories, remove the dependency path, disable the affected feature behind a fail-closed default, or replace it with a strict-audit-clean backend. Keep ABI compatibility with unsupported-operation stubs only if callers fail closed, update docs/SBOM/release evidence, and add a regression test proving the disabled path rejects before processing payloads.") + ("avoid" + . + "Do not make a production Jerboa release gate pass by adding cargo-audit ignores for runtime crypto advisories in native FFI dependencies.") + ("id" . "ignore-runtime-crypto-rustsec") + ("kinds" "security" "ffi") + ("pattern" + . + "cargo audit.*--ignore|RUSTSEC-.*(rsa|crypto|pgp|openssl|tls|signature)") + ("severity" . "high") + ("tags" "cargo-audit" "rustsec" "crypto" "ffi" + "release-gate" "fail-closed") + ("title" + . + "Do Not Ignore Runtime Crypto RustSec Advisories") + ("tools" "cargo audit -D warnings" "cargo tree -i" + "jerboa_security_scan" "jerboa_compile_check" + "make release-evidence")) + (("advice" + . + "Treat daemon readiness markers as part of the release contract. Match the current structured event names, keep backwards-compatible alternatives only when needed, and include a failing startup log in evidence when readiness is not observed. After changing daemon logs, run the bench/soak harness against a short local smoke before trusting load evidence.") + ("avoid" + . + "Do not make benchmark or soak harnesses wait for an old literal startup log line such as \"listening on\" after daemon logging has moved to structured event names. The daemon may be ready while the harness times out, kills it, and records a false load-test failure.") + ("id" . "stale-daemon-readiness-log-marker") + ("kinds" "code" "security" "test") + ("pattern" + . + "listening on|tcp_listening|readiness marker|run-server") + ("severity" . "medium") + ("tags" "daemon" "benchmark" "soak" "release-evidence" + "logs") + ("title" + . + "Daemon Harness Waits For Stale Readiness Log Marker") + ("tools" + "rg" + "make soak-evidence" + "sh -n" + "jerboa_security_scan")) + (("advice" + . + "For two-pass binary targets, remove and recreate the fixed object directory between internal build phases whenever generated FFI symbols, native helper artifacts, or embedded WASM inputs change. Then rerun the full `make reproducibility-report` or `make release-evidence` path and inspect the recorded status instead of weakening the gate.") + ("avoid" + . + "Do not assume clearing a fixed `JERBUILD_BINARY_OBJ_DIR` before each outer reproducibility build is enough when the target itself runs two `jerbuild binary` phases and regenerates FFI symbols between them. The second phase can reuse stale WPO/object inputs from the placeholder first phase and produce mismatched release evidence.") + ("id" . "two-pass-binary-fixed-objdir-staleness") + ("kinds" "build" "security") + ("pattern" + . + "JERBUILD_BINARY_OBJ_DIR.*(jerbuild binary|make binary).*generated.*symbols|two[- ]pass.*binary.*fixed object") + ("severity" . "medium") + ("tags" "jerbuild" "reproducibility" "ffi-symbols" "daemon" + "release-evidence" "wpo") + ("title" + . + "Two-Pass Binary Targets Must Clear Fixed Object Dirs Between Generated Symbol Phases") + ("tools" "rg" "make reproducibility-report" "make release-evidence" + "JERBUILD_BINARY_KEEP_OBJ_DIR" "jerboa_security_scan")) + (("advice" + . + "Add a `make soak-evidence` or equivalent target that always writes a status file. Default local evidence should record explicit blocked statuses such as `status=blocked-not-run`, `load_status=blocked-not-run`, or `queue_exhaustion_status=blocked-not-run`. Copy that directory and log into `dist/release-evidence/`, document the opt-in production run, and mark short local smokes as non-production.") + ("avoid" + . + "Do not ship or track daemon release evidence that simply omits soak, load, or queue-exhaustion artifacts. Absence is ambiguous and can be mistaken for a completed production gate.") + ("id" . "daemon-release-evidence-omits-soak-status") + ("kinds" "security" "docs" "test") + ("pattern" + . + "release[- ]evidence.*(soak|load).*omitted|make release-evidence.*without.*soak") + ("severity" . "medium") + ("tags" "daemon" "release-evidence" "soak" "load-test" + "production-blocker" "audit") + ("title" + . + "Daemon Release Evidence Must Not Omit Missing Soak Status") + ("tools" "rg" "make soak-evidence" "make release-evidence" + "sh -n" "jerboa_security_scan")) + (("advice" + . + "Add a binary-smoke gate after the measured build and before release evidence is accepted. Smoke every shipped executable, record expected exit codes, and fail the release target if the binary cannot start. On macOS, include code-signature verification because unsigned Mach-O binaries can be killed before runtime startup.") + ("avoid" + . + "Do not accept release evidence that builds standalone binaries and records hashes without executing each shipped binary through a no-secret smoke path.") + ("id" . "binary-release-evidence-without-runtime-smoke") + ("kinds" "security" "test") + ("pattern" + . + "release-evidence.*(binary|binaries)(?![\\s\\S]*binary-smoke)") + ("severity" . "high") + ("tags" "release-evidence" "binary" "smoke" "fasl" + "runtime") + ("title" . "Binary Release Evidence Without Runtime Smoke") + ("tools" + "make binary-smoke" + "make release-evidence" + "codesign --verify" + "jerboa_security_scan")) + (("advice" + . + "Treat FASL bytes as executable data. If byte drift remains, first isolate object dirs, prewarm source-only dependencies, and compare inputs. Any byte rewrite must be followed by binary smoke and compile/load verification. If a rewrite causes malformed FASL headers or startup aborts, remove the rewrite and record exact-binary drift separately.") + ("avoid" + . + "Do not force byte-for-byte reproducibility by stripping or canonicalizing runtime-loaded Chez FASL/WPO payloads unless the transformed payload is proven loadable.") + ("id" . "postprocessing-runtime-fasl-for-reproducibility") + ("kinds" "security" "module" "debug-error") + ("pattern" + . + "canonicaliz|strip|rewrite.*(fasl|\\.so|\\.wpo)") + ("severity" . "high") + ("tags" "fasl" "reproducibility" "wpo" "binary" "chez") + ("title" . "Do Not Rewrite Runtime FASL Payloads Blindly") + ("tools" "make binary-smoke" "make reproducibility-report" + "jerboa_verify" "cmp -l" "xxd")) + (("advice" + . + "Wrap daemon TCP creation/accept in a project runtime that sets and verifies O_NONBLOCK and FD_CLOEXEC, exposes a shutdown path that wakes blocked accepts, and make security checks reject direct daemon imports of the generic TCP module.") + ("avoid" + . + "Do not put daemon listeners into background accept loops without verifying listener and accepted file descriptors are nonblocking and close-on-exec.") + ("id" + . + "daemon-listener-accept-loop-unverified-nonblocking") + ("kinds" "security" "ffi" "module") + ("pattern" . "tcp-listen|tcp-accept-binary") + ("severity" . "high") + ("tags" "daemon" "tcp" "nonblocking" "cloexec" "accept") + ("title" + . + "Daemon Listener Accept Loop Without FD Hardening") + ("tools" + "rg" + "jerboa_security_scan" + "make soak-check" + "make release-evidence")) + (("advice" + . + "Use explicit positive path lists, separate find invocations, or shell loops that check containment before deleting. After cleanup changes, run the full reproducibility or release-evidence target and inspect that vendored baselines still exist.") + ("avoid" + . + "Do not rely on `find ... -prune -o ... -delete` to preserve vendored or baseline paths without testing it on the target platform. `-delete` can imply depth-first traversal and make prune behavior surprising.") + ("id" . "find-delete-prune-vendor-cleanup") + ("kinds" "script" "security") + ("pattern" . "find .* -prune .* -delete") + ("severity" . "medium") + ("tags" "find" "delete" "prune" "vendor" "cleanup") + ("title" . "Find Delete Can Defeat Prune-Based Cleanup") + ("tools" "sh -n" "find" "rg" "make reproducibility-report" + "make release-evidence")) + (("advice" + . + "For binary builders, use direct argv-based process execution with a whitelisted environment, collect-safe waitpid, explicit allocation/freeing, and no shell string interpolation. Keep the verifier focused on the exact binary build target and rerun smoke after replacing process execution.") + ("avoid" + . + "Do not route long standalone binary builders through higher-level async/channel process helpers when they are unstable in the build host/runtime combination.") + ("id" . "jerboa-spawn-process-in-binary-build-hot-path") + ("kinds" "ffi" "script" "security") + ("pattern" . "spawn-process|open-process|system\\s*\\(") + ("severity" . "medium") + ("tags" "spawn-process" "posix_spawnp" "binary-build" + "macos" "ffi") + ("title" . "Async Spawn Layer In Binary Build Hot Path") + ("tools" + "jerboa_security_scan" + "make binaries" + "make binary-smoke" + "jerboa_verify")) + (("advice" + . + "Let the reproducibility-report target produce the measured binaries, then copy/hash/smoke those exact binaries into release evidence. If a prebuild is required, reset all shared object dirs and source-only compiled artifacts before the measured pass.") + ("avoid" + . + "Do not run an unmeasured `binaries` target before a measured reproducibility report if that build mutates shared compiled artifacts or toolchain caches.") + ("id" + . + "release-evidence-prebuild-perturbs-reproducibility") + ("kinds" "security" "script") + ("pattern" + . + "release-evidence:.*binaries.*reproducibility|binaries.*reproducibility-report") + ("severity" . "medium") + ("tags" "release-evidence" "reproducibility" "prebuild" + "binary" "make") + ("title" + . + "Prebuilding Before Measured Reproducibility Perturbs Evidence") + ("tools" + "make -n release-evidence" + "make reproducibility-report" + "make release-evidence" + "shasum")) + (("advice" + . + "Prewarm the exact source-only stdlib modules once, snapshot the baseline, and restore or isolate it between passes. Record the source manifest and release-input hashes separately from exact binary-byte status.") + ("avoid" + . + "Do not compare two release builds while source-only Jerboa stdlib modules are compiled opportunistically during each measured pass.") + ("id" . "reproducibility-baseline-source-only-stdlibs") + ("kinds" "security" "module") + ("pattern" . "compile.*std/.*\\.ss|source-only.*stdlib") + ("severity" . "medium") + ("tags" "stdlib" "reproducibility" "fasl" "prewarm" + "baseline") + ("title" + . + "Source-Only Stdlib Rebuilds Poison Reproducibility Baselines") + ("tools" + "make reproducibility-report" + "shasum" + "find" + "jerboa_verify")) + (("advice" + . + "Record exact binary-byte status separately from source, lockfile, static library, release-input, and source-manifest status. If exact bytes mismatch but higher-level inputs match, require passing runtime smoke, signed binary evidence where applicable, and a release-note risk entry instead of silently weakening or overstating the gate.") + ("avoid" + . + "Do not report an overall release reproducibility pass as if every binary was byte-identical when Chez FASL/runtime payload bytes still drift.") + ("id" + . + "exact-binary-reproducibility-with-chez-fasl-runtime-drift") + ("kinds" "security" "docs") + ("pattern" + . + "status=match(?![\\s\\S]*(daemon_binaries_status|binary-smoke))") + ("severity" . "medium") + ("tags" "binary" "reproducibility" "fasl" "release-evidence" + "smoke") + ("title" + . + "Hide Exact Binary Drift Behind Overall Reproducibility Status") + ("tools" "make reproducibility-report" "make binary-smoke" + "make release-evidence" "cmp -l" "jerboa_security_scan")) + (("advice" + . + "For release evidence, run a clean prewarm build with `JERBUILD_BINARY_KEEP_OBJ_DIR=1`, snapshot the generated `bundle/` and `u-lib/` `.so`/`.wpo` files from the fixed object directory, and restore that exact snapshot before each measured binary build. Record prepared-input hashes, generated-input hashes, and final binary byte status; state clearly when the evidence is prepared-input reproducibility rather than raw two-clean-build equivalence.") + ("avoid" + . + "Do not assume a fixed `JERBUILD_BINARY_OBJ_DIR` alone proves reproducibility when `jerbuild binary` populates generated `bundle/` or `u-lib/` `.so`/`.wpo` inputs inside that object directory. Consecutive builds can alternate between freshly compiled and already-prepared input shapes, especially around WPO and FFI-heavy binaries.") + ("id" . "jerbuild-fixed-objdir-without-input-snapshot") + ("kinds" "script" "security") + ("pattern" + . + "JERBUILD_BINARY_OBJ_DIR=.*make binary|jerbuild build") + ("severity" . "medium") + ("tags" "jerbuild" "binary" "reproducibility" "WPO" + "release-evidence" "generated-inputs") + ("title" + . + "Fixed Jerbuild Object Dir Without Generated Input Snapshot") + ("tools" "make reproducibility-report" "JERBUILD_BINARY_OBJ_DIR" + "JERBUILD_BINARY_KEEP_OBJ_DIR" "shasum" "cmp -l" + "jerboa_security_scan")) + (("advice" + . + "Compare shipped generated artifacts such as lib/*.sls separately. Record src/.jerbuild-hashes as release metadata, normalize its path/hash pairs before comparison, and require normalized metadata to match before reporting status=match. Preserve raw metadata in evidence archives for review.") + ("avoid" + . + "Do not include raw src/.jerbuild-hashes in the same generated-artifact manifest as shipped lib/*.sls outputs and fail release evidence on order-only hash metadata drift.") + ("id" . "jerbuild-hash-metadata-order-repro") + ("kinds" "test" "security") + ("pattern" + . + "src/\\.jerbuild-hashes|generated_lib_status=mismatch|first-generated-libs\\.sha256|second-generated-libs\\.sha256") + ("severity" . "medium") + ("tags" "jerbuild" "reproducibility" "release-evidence" + "generated-artifacts" "metadata") + ("title" + . + "Do Not Treat Jerbuild Hash Metadata Order Drift As Generated Output Drift") + ("tools" + "make reproducibility-report" + "make release-evidence" + "diff -u" + "jerboa_security_scan")) + (("advice" + . + "Prefer an argv-based process API. If the codebase only has a shell-string boundary, shell-quote each complete caller-supplied argv element before joining, keep shell syntax separate from data, and add a regression that passes semicolons or redirections as arguments and proves no marker command executes.") + ("avoid" + . + "Do not build a command string by joining caller-provided argv with spaces and passing it to checked-system-command, safe-system, or open-process-ports. Checked path wrappers do not make shell metacharacters inert.") + ("id" . "process-applet-argv-join-shell-injection") + ("kinds" "security" "module" "test") + ("pattern" + . + "string-join \\(cons .*args\\) \" \"|checked-system-command cmd-str|open-process-ports cmd-str") + ("severity" . "high") + ("tags" "process" "shell-injection" "argv" "coreutils" + "release-evidence") + ("title" + . + "Process Applets Must Not Join Raw Argv Into Shell Commands") + ("tools" + "rg" + "jerboa_security_scan" + "make focused-security-tests" + "make release-evidence")) + (("advice" + . + "Reject raw and encoded traversal shapes before path expansion, then resolve the candidate under the configured static root and require it to equal the root or be a true descendant path with a separator boundary. Add negative smoke tests for ../secret, encoded dot/slash/backslash variants, and oversized files.") + ("avoid" + . + "Do not rely on path normalization plus a string prefix check as the only static-file containment guard, especially before rejecting dot segments, backslash traversal, encoded separators, or NUL encodings.") + ("id" . "static-prefix-check-before-traversal-reject") + ("kinds" "security" "module" "test") + ("pattern" + . + "path-normalize|path-expand|static.*prefix|starts-with") + ("severity" . "high") + ("tags" "static-files" "path-traversal" "dot-segment" + "url-encoding" "containment" "web") + ("title" + . + "Static Path Prefix Check Before Traversal Rejection") + ("tools" + "jerboa_security_scan" + "jerboa_run_tests" + "jerboa_verify" + "git diff --check")) + (("advice" + . + "Store the exception object or a structured error marker in the worker result slot, always signal the completion condition, and let the assertion compare/report the captured value. For cleanup-only guards, close the resource and re-raise unless the cleanup itself is intentionally best-effort.") + ("avoid" + . + "Do not wrap concurrent test worker bodies in catch-all guards that return #f, #t, or void. That converts real connection, timeout, parser, or assertion failures into ordinary result values and can make the test hang or fail without useful diagnostics.") + ("id" . "concurrent-test-guard-masks-thread-errors") + ("kinds" "test" "security" "debug-error") + ("pattern" + . + "guard \\(e \\[#t #f\\]|guard \\(e \\[#t \\(void\\)\\]") + ("severity" . "medium") + ("tags" "concurrency" "tests" "guard" "threads" + "diagnostics" "scanner") + ("title" . "Concurrent Test Guard Masks Thread Errors") + ("tools" + "jerboa_security_scan" + "jerboa_run_tests" + "jerboa_failure_advisor" + "git diff --check")) + (("advice" + . + "Keep the release default on the deterministic build mode that has exact-byte evidence, expose WPO through an explicit diagnostic/output allowlist such as `*_BINARY_WPO_OUTPUTS`, and record exact binary-byte status separately in release evidence. Only promote WPO to the release default after repeated clean release-evidence runs prove byte-stable outputs and the docs match the actual build path.") + ("avoid" + . + "Do not switch shipped release binaries to WPO by default just because WPO is the advanced or optimized Jerboa path when exact-byte reproducibility is still drifting.") + ("id" . "wpo-diagnostic-binary-as-release-default") + ("kinds" "security" "script" "docs") + ("pattern" + . + "J[A-Z0-9_]*_BINARY_WPO=1|WPO binaries for .*release|with WPO and the other shipped binaries in direct mode") + ("severity" . "medium") + ("tags" "WPO" "reproducibility" "release-evidence" "binary" + "jerbuild" "diagnostic") + ("title" + . + "Do Not Make WPO Diagnostic Binaries The Release Default Before Exact-Byte Evidence Is Stable") + ("tools" "make reproducibility-report" + "make release-evidence" "cmp -l" "shasum" + "jerboa_security_scan")) + (("advice" + . + "Compare the binaries directly with `cmp -s first-bin second-bin`, or normalize the hash files to only the digest column with `awk '{print $1}'` before comparing. Keep the full `shasum` files as evidence, but make the gate use path-insensitive digest comparison.") + ("avoid" + . + "Do not decide binary reproducibility by comparing raw `shasum` output files when each line hashes a different pathname, such as `/tmp/first-bin` versus `/tmp/second-bin`. The digest can match while the filename column differs, creating a false mismatch.") + ("id" . "shasum-filename-column-false-repro-mismatch") + ("kinds" "script" "test") + ("pattern" + . + "cmp -s .*-binary\\.sha256 .*-binary\\.sha256|shasum -a 256 .*>.*binary.*\\.sha256") + ("severity" . "medium") + ("tags" "reproducibility" "shasum" "binary-hash" + "release-evidence" "false-negative") + ("title" + . + "Do Not Compare Raw Shasum Files With Different Paths") + ("tools" "make reproducibility-report" "cmp -s" "shasum" + "awk" "git diff --check")) + (("advice" + . + "For credentialed-client release smoke tests, run against non-production accounts and record redacted status fields only: pass/fail, count, version, endpoint class, and command exit status. Write raw stdout to a temporary file only long enough to compute a count or structural check, then delete it before preserving evidence. Keep live send/receive or mutation smokes opt-in and mark missing target-host evidence as blocked-not-run rather than omitting it.") + ("avoid" + . + "Do not store raw output from account-list, profile, inbox, contact, workspace, project, or device-list commands in release evidence. Those commands can expose phone numbers, usernames, tenant IDs, account IDs, message metadata, or production state even when no secret token is printed.") + ("id" . "credentialed-smoke-raw-account-output") + ("kinds" + "security" + "release-evidence" + "credentialed-client") + ("pattern" . "") ("severity" . "medium") + ("tags" "security" "release-evidence" "redaction" + "credentialed-clients" "privacy") + ("title" + . + "Do Not Preserve Raw Credentialed Account Smoke Output") + ("tools" + "rg" + "make soak-evidence" + "make release-evidence" + "jerboa_security_scan")) + (("advice" + . + "For reproducibility evidence, delete only the measured outputs and fixed Jerbuild object directory needed for the comparison. Record `destructive_clean_used=0` when avoiding the clean target, and include source manifests before and after measured builds to prove the narrower cleanup did not hide source changes. If a warmup build is required, record it explicitly and distinguish post-warm evidence from first-run clean-host evidence.") + ("avoid" + . + "Do not have a reproducibility or release-evidence script invoke a repository-wide `make clean` target without checking what it removes. Some repos include large tracked release artifacts, vendored baselines, generated tarballs, or externally expensive build outputs in the clean recipe.") + ("id" + . + "repro-script-destructive-clean-deletes-release-artifacts") + ("kinds" "security" "release-evidence" "reproducibility") + ("pattern" . "") ("severity" . "medium") + ("tags" "release-evidence" "reproducibility" "clean" + "generated-artifacts" "jerbuild") + ("title" + . + "Do Not Blindly Call Destructive Clean From Reproducibility Evidence") + ("tools" "rg" "make -n clean" "make reproducibility-report" + "git status --short" "git diff --check")) + (("advice" + . + "Run a throwaway clean binary build, clean again, then start the measured reproducibility pair. Record prewarm_status=present in the report, keep JERBUILD_BINARY_OBJ_DIR isolated for measured builds, and rerun make reproducibility-report or make release-evidence until the same verifier passes.") + ("avoid" + . + "Do not compare the first fixed-object-dir Jerbuild binary build against a second build when the first build may still be warming Chez/Jerbuild WPO state. This can produce binary_status=mismatch and generated_input_status=mismatch even when source and native inputs are stable.") + ("id" . "binary-repro-without-prewarm") + ("kinds" "release-evidence" "reproducibility" "binary") + ("pattern" . "") ("severity" . "medium") + ("tags" "jerbuild" "reproducibility" "WPO" + "fixed-object-dir" "release-evidence") + ("title" + . + "Binary Reproducibility Evidence Without Toolchain Prewarm") + ("tools" + "make reproducibility-report" + "make release-evidence" + "git diff --check" + "jerboa_security_scan")) + (("advice" + . + "For delimited parsers, make EOF before the closing delimiter raise a parser error, add an expected-reject regression such as an unterminated replacement, and include deterministic malformed corpus cases that prove the parser fails closed.") + ("avoid" + . + "Do not treat end-of-file as a successful terminator when reading delimited hostile-input constructs such as sed substitutions, regexes, strings, or protocol fields.") + ("id" . "delimited-parser-eof-success") + ("kinds" "security" "test") + ("pattern" + . + "\\[\\(not c\\)\\s+\\(get-output-string out\\)\\]") + ("severity" . "medium") + ("tags" "parser" "delimited-input" "eof" "fuzz" "security") + ("title" . "Do Not Accept EOF As A Delimiter") + ("tools" + "rg" + "make parser-corpus" + "jerboa_check_balance" + "jerboa_security_scan")) + (("advice" + . + "Prefer the audited sibling toolchain path when it exists, for example `../jerboa/dist/jerbuild`, and only fall back to PATH after checking the repo-local deterministic toolchain is unavailable. When a reproducibility mismatch appears, compare generated inputs, source manifests, Mach-O UUIDs, and code signatures before changing source code.") + ("avoid" + . + "Do not let release-evidence or reproducibility targets silently prefer whichever `jerbuild` appears first on PATH when a repo depends on deterministic binary output. A stale PATH tool can introduce Mach-O UUID or signature drift and make otherwise identical generated inputs look unreproducible.") + ("id" . "path-jerbuild-reproducibility-drift") + ("kinds" "security" "script" "docs") + ("pattern" + . + "(?i)(JERBUILD\\s*\\?=\\s*\\$\\(shell\\s*command -v jerbuild|command -v jerbuild.*reproducibility|binary_status=mismatch.*UUID)") + ("severity" . "medium") + ("tags" "release-evidence" "reproducibility" "jerbuild" + "mach-o" "toolchain") + ("title" + . + "PATH Jerbuild Can Drift From Deterministic Release Toolchain") + ("tools" "make reproducibility-report" + "make release-evidence" "xcrun dwarfdump --uuid" + "codesign -dv" "shasum")) + (("advice" + . + "Record transport-specific status fields. Keep UDP load fields separate from TCP query/session smoke or TCP load fields, and require `tcp_smoke_status=pass` or equivalent whenever an opt-in release soak claims UDP/TCP coverage. Preserve `production_load_status=blocked-not-run` for short local smokes.") + ("avoid" + . + "Do not describe a UDP-only load tool run as covering both UDP and TCP daemon behavior. For DNS, `dnsperf` evidence alone is UDP-focused unless the command or logs prove TCP mode.") + ("id" . "udp-only-soak-as-udp-tcp-coverage") + ("kinds" "security" "docs" "test") + ("pattern" + . + "(?i)(UDP/TCP.*dnsperf|dnsperf.*UDP/TCP|latest UDP/TCP soak.*dnsperf)") + ("severity" . "medium") + ("tags" "daemon" "soak" "dns" "tcp" "release-evidence") + ("title" . "Do Not Claim UDP-Only Soak As UDP/TCP Coverage") + ("tools" + "make soak-evidence" + "make release-evidence" + "rg tcp_smoke_status" + "jerboa_security_scan")) + (("advice" + . + "Pass `JERBUILD` into the soak target, derive `$(JERBUILD --jerboa-home)/jerboa-native-rs/target/release`, export it through `DYLD_FALLBACK_LIBRARY_PATH` and `LD_LIBRARY_PATH`, and record those paths in the evidence environment. Then rerun the short smoke with parser fallback disabled when feasible.") + ("avoid" + . + "Do not run daemon soak or smoke evidence for WASM-backed parser paths without exporting the native runtime directory for the selected `JERBUILD`. The daemon can reject inputs because the parser runtime is missing, which tests packaging accidentally instead of the intended protocol behavior.") + ("id" . "wasm-soak-without-native-runtime-path") + ("kinds" "security" "test" "script") + ("pattern" + . + "(?i)(wasm_.*fallback reason=\"libjerboa_native not loaded\"|DATA rejected.*soak|JERBUILD=.*make release-evidence.*soak-evidence)") + ("severity" . "medium") + ("tags" "daemon" "wasm" "soak" "native-runtime" + "release-evidence") + ("title" + . + "WASM-Backed Soak Smokes Need The Selected Native Runtime Path") + ("tools" + "make soak-evidence" + "make release-evidence" + "rg wasm_.*fallback" + "jerboa_security_scan")) + (("advice" + . + "For release evidence, create an ignored work corpus under dist or another generated evidence directory, copy the checked-in seed files into it, and pass that work corpus path explicitly to cargo-fuzz. After the run, verify fuzz/corpus still contains only intended source seeds and keep generated artifacts under ignored evidence paths.") + ("avoid" + . + "Do not run release-evidence cargo-fuzz jobs directly against the checked-in fuzz/corpus directories. libFuzzer may add minimized hash-named inputs, including binary inputs, back into the source corpus and break deterministic corpus smoke or pollute safe-for-publication seeds.") + ("id" . "cargo-fuzz-source-corpus-contamination") + ("kinds" "test" "security" "script") + ("pattern" + . + "cargo[- ]fuzz run .*fuzz/corpus|cargo[- ]fuzz evidence.*source corpus|fuzz/corpus/.*/[0-9a-f]{40}") + ("severity" . "medium") + ("tags" "cargo-fuzz" "fuzz-corpus" "release-evidence" + "daemon" "corpus-smoke" "determinism") + ("title" + . + "Do Not Run Cargo-Fuzz Evidence Against Source Corpus") + ("tools" "find fuzz/corpus" + "cargo run --manifest-path fuzz/Cargo.toml --bin corpus_smoke" + "make fuzz-evidence" "make release-evidence" + "git diff --check" "jerboa_security_scan")) + (("advice" + . + "For large response bodies or payloads, flush existing buffered data and copy the source bytevector into bounded chunks before each FFI write, or redesign the native boundary so the native side owns a stable copy before blocking. Add a large-response regression test that verifies status parsing and exact body length/content.") + ("avoid" + . + "Do not pass a large Scheme bytevector directly to a collect-safe FFI write and assume the bytevector memory remains stable throughout a blocking OS write.") + ("id" . "large-bytevector-collect-safe-ffi-write") + ("kinds" "ffi" "security" "module") + ("pattern" + . + "writer-write-bv!.*conn-write|foreign-procedure __collect_safe.*u8\\*.*int") + ("severity" . "high") + ("tags" "ffi" "collect-safe" "bytevector" "httpd" + "tcp-write" "memory-corruption") + ("title" + . + "Do Not Hand Large Scheme Bytevectors To Collect-Safe FFI Writes") + ("tools" + "jerboa_security_scan" + "jerboa_verify" + "jerboa_run_tests" + "make test-httpd")) + (("advice" + . + "Keep both ports reachable for the whole connection lifetime, or redesign fd ownership so the close handler cannot be garbage-collected while the output port is still active. Close both ports explicitly on disconnect. If only one direction is needed, still retain the owning port in the connection record.") + ("avoid" + . + "Do not bind only the output port from a TCP fd->ports/fd->binary-ports pair when the input port owns the close handler for the shared fd.") + ("id" . "discarded-input-port-shared-fd-output") + ("kinds" "ffi" "module" "security") + ("pattern" + . + "let-values.*\\(\\[\\(_+|_in|ignored|unused)\\s+out\\].*tcp-(connect|accept)-binary") + ("severity" . "high") + ("tags" "tcp" "fd->binary-ports" "GC" "close-handler" + "transport" "socket") + ("title"