From 3f5120de8656a825580197e775bcdc1870428c3f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 00:13:53 +0000 Subject: [PATCH 1/3] ci(cflite): add the Dockerfile ClusterFuzzLite needs and fix the fuzz build ClusterFuzzLite fails in build_fuzzers on every PR, for three reasons: - It runs `docker build -f .clusterfuzzlite/Dockerfile`, but the directory only had a Containerfile. Add a Dockerfile that mirrors it (same pinned base-builder-rust digest). The Containerfile stays for podman-first local builds. - build.sh copied targets from ./target/..., but cargo-fuzz writes them to ./fuzz/target/x86_64-unknown-linux-gnu/release/. The copy failed with "cannot stat". - Once built, fuzz_parser reported a leak on its first input (a single newline). The leak comes from nickel-lang-parser, which allocates AST nodes in a bumpalo arena that never runs destructors, so strings in those nodes always leak. Add fuzz_parser.options turning off leak detection for libFuzzer and ASan, and have build.sh copy each target's .options file to $OUT. ASan still reports memory-safety errors. Verified locally with the same base image: - The compile finishes and $OUT contains fuzz_parser. - A 90-second fuzz run (615 executions) produced no crashes. - With both leak switches off, the run exits 0 with no artifacts. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0132Cdz3iwk2QGKv2sUhhsye --- .clusterfuzzlite/Dockerfile | 13 +++++++++++++ .clusterfuzzlite/build.sh | 5 ++++- .clusterfuzzlite/fuzz_parser.options | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .clusterfuzzlite/Dockerfile create mode 100644 .clusterfuzzlite/fuzz_parser.options diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 0000000..274187a --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: MPL-2.0 +# ClusterFuzzLite build image for bunsenite. +# +# ClusterFuzzLite invokes `docker build -f .clusterfuzzlite/Dockerfile`, +# so this file must be named `Dockerfile`. It mirrors the OCI-neutral +# Containerfile in this directory (kept for podman-first local builds per +# the authority stack); keep the two in sync. +FROM gcr.io/oss-fuzz-base/base-builder-rust@sha256:73c1d5648db54100639339d411a5d192cbc8bf413ee91e843a07cf6f0e319dc7 + +COPY . $SRC/bunsenite +WORKDIR $SRC/bunsenite + +COPY .clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index bfdc807..8a410da 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -9,5 +9,8 @@ cargo +nightly fuzz build # Copy fuzz targets to $OUT for target in $(cargo +nightly fuzz list); do - cp ./target/x86_64-unknown-linux-gnu/release/$target $OUT/ + cp ./fuzz/target/x86_64-unknown-linux-gnu/release/$target $OUT/ + if [ -f .clusterfuzzlite/$target.options ]; then + cp .clusterfuzzlite/$target.options $OUT/ + fi done diff --git a/.clusterfuzzlite/fuzz_parser.options b/.clusterfuzzlite/fuzz_parser.options new file mode 100644 index 0000000..46556d0 --- /dev/null +++ b/.clusterfuzzlite/fuzz_parser.options @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MPL-2.0 +# Runtime options for the fuzz_parser target. build.sh copies this file to +# $OUT next to the binary, where the ClusterFuzzLite runner picks it up. +# +# Leak detection is off because nickel-lang-parser allocates AST nodes in a +# bumpalo arena, which never runs destructors. Strings held by those nodes +# therefore leak by design, on every input, including a single newline. +# Without this, every run reports that upstream leak as a crash and no real +# finding can surface. Memory-safety errors (overflows, use-after-free) are +# still reported by AddressSanitizer. +[libfuzzer] +detect_leaks = 0 + +[asan] +detect_leaks = 0 From bb33a7e116d5c23ac2e939322541780e1db62568 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 00:21:43 +0000 Subject: [PATCH 2/3] ci(cflite): copy only what the fuzz build reads, and use [[ in build.sh SonarCloud failed this PR on a D security rating for new code: - Dockerfile:10 "Copying recursively might inadvertently add sensitive data to the container." `COPY . $SRC/bunsenite` sent the whole checkout, .git included, into the build image. - build.sh:13 "Use '[[' instead of '['". The image now copies only what the fuzz build reads: - Cargo.toml and Cargo.lock - src/ - benches/ (declared as [[bench]] targets) - crates/ (the patched nickel-lang-vector) - fuzz/ - .clusterfuzzlite/ (build.sh reads the per-target .options from there) The Containerfile gets the same change, to stay in sync. Nothing in these paths uses include_str!/include_bytes! on files outside them, and `cargo metadata` resolves both manifests from that subset. The "runs as root" warning stays as it is: the OSS-Fuzz base-builder image needs root. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0132Cdz3iwk2QGKv2sUhhsye --- .clusterfuzzlite/Containerfile | 11 ++++++++++- .clusterfuzzlite/Dockerfile | 11 ++++++++++- .clusterfuzzlite/build.sh | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.clusterfuzzlite/Containerfile b/.clusterfuzzlite/Containerfile index ff3d423..2aeae2f 100644 --- a/.clusterfuzzlite/Containerfile +++ b/.clusterfuzzlite/Containerfile @@ -2,7 +2,16 @@ # ClusterFuzzLite build environment for bunsenite FROM gcr.io/oss-fuzz-base/base-builder-rust@sha256:73c1d5648db54100639339d411a5d192cbc8bf413ee91e843a07cf6f0e319dc7 -COPY . $SRC/bunsenite +# Copy only what the fuzz build reads, not the whole checkout: the crate +# manifest and lockfile, its sources and declared benches, the patched +# nickel-lang-vector crate, the fuzz crate, and this directory (build.sh +# reads per-target .options files from it). +COPY Cargo.toml Cargo.lock $SRC/bunsenite/ +COPY src $SRC/bunsenite/src +COPY benches $SRC/bunsenite/benches +COPY crates $SRC/bunsenite/crates +COPY fuzz $SRC/bunsenite/fuzz +COPY .clusterfuzzlite $SRC/bunsenite/.clusterfuzzlite WORKDIR $SRC/bunsenite COPY .clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 274187a..4fafcd9 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -7,7 +7,16 @@ # the authority stack); keep the two in sync. FROM gcr.io/oss-fuzz-base/base-builder-rust@sha256:73c1d5648db54100639339d411a5d192cbc8bf413ee91e843a07cf6f0e319dc7 -COPY . $SRC/bunsenite +# Copy only what the fuzz build reads, not the whole checkout: the crate +# manifest and lockfile, its sources and declared benches, the patched +# nickel-lang-vector crate, the fuzz crate, and this directory (build.sh +# reads per-target .options files from it). +COPY Cargo.toml Cargo.lock $SRC/bunsenite/ +COPY src $SRC/bunsenite/src +COPY benches $SRC/bunsenite/benches +COPY crates $SRC/bunsenite/crates +COPY fuzz $SRC/bunsenite/fuzz +COPY .clusterfuzzlite $SRC/bunsenite/.clusterfuzzlite WORKDIR $SRC/bunsenite COPY .clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 8a410da..aeb61da 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -10,7 +10,7 @@ cargo +nightly fuzz build # Copy fuzz targets to $OUT for target in $(cargo +nightly fuzz list); do cp ./fuzz/target/x86_64-unknown-linux-gnu/release/$target $OUT/ - if [ -f .clusterfuzzlite/$target.options ]; then + if [[ -f .clusterfuzzlite/$target.options ]]; then cp .clusterfuzzlite/$target.options $OUT/ fi done From 91d8388bf55b587dc015317a9211c78c3897eb49 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 00:06:31 +0000 Subject: [PATCH 3/3] fix(k9): satisfy the K9 validator in three contracts The Dogfood Gate's "Validate K9 contracts" job (k9-ecosystem/validate-action) fails on main with four errors: - config/build.k9.ncl, config/rust-fmt.k9.ncl: "Missing K9 format marker". The K9! preamble sat on line 2, under the SPDX comment; the validator requires it as the first non-empty line, which is how the contractiles templates lay it out. K9! now comes first, SPDX second. - config/rust-fmt.k9.ncl: "Pedigree block missing 'name' field". Both config contracts now carry a pedigree name. - setup-dev-env.k9.ncl: "Hunt-level K9 file must include a 'signature' or 'signature_required' field". It is a Hunt component (it runs installers and shell commands), so its pedigree now declares signature_required = true, as contractiles/self-validating/template-hunt.k9.ncl does. The validator now reports 10 files scanned, 0 errors. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0132Cdz3iwk2QGKv2sUhhsye (cherry picked from commit a94f3eaa487b65ce9d810e5b4e0ea4df1eaa817c) --- config/build.k9.ncl | 3 ++- config/rust-fmt.k9.ncl | 3 ++- setup-dev-env.k9.ncl | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/config/build.k9.ncl b/config/build.k9.ncl index 2c396ce..ed65584 100644 --- a/config/build.k9.ncl +++ b/config/build.k9.ncl @@ -1,10 +1,11 @@ -# SPDX-License-Identifier: MPL-2.0 K9! +# SPDX-License-Identifier: MPL-2.0 leash = 'Yard # Validation only, no I/O pedigree = { schema_version = "1.0.0", component_type = "rust-build-config", + name = "bunsenite-build-config", tool = "bunsenite", validation_level = "strict", description = "Self-validating Cargo build configuration for Bunsenite" diff --git a/config/rust-fmt.k9.ncl b/config/rust-fmt.k9.ncl index e0035dc..8bd2033 100644 --- a/config/rust-fmt.k9.ncl +++ b/config/rust-fmt.k9.ncl @@ -1,10 +1,11 @@ -# SPDX-License-Identifier: MPL-2.0 K9! +# SPDX-License-Identifier: MPL-2.0 leash = 'Yard # Validation only, no I/O pedigree = { schema_version = "1.0.0", component_type = "rust-formatter-config", + name = "bunsenite-rust-fmt-config", tool = "bunsenite", validation_level = "strict", description = "Self-validating Rust formatter configuration for Bunsenite" diff --git a/setup-dev-env.k9.ncl b/setup-dev-env.k9.ncl index 3c9d087..730f0d1 100644 --- a/setup-dev-env.k9.ncl +++ b/setup-dev-env.k9.ncl @@ -11,6 +11,7 @@ leash = 'Hunt pedigree = { schema_version = "1.0.0", component_type = "dev-env-setup", + signature_required = true, author = "Jonathan D.A. Jewell ", description = "Automated development environment setup for Bunsenite", created = "2026-01-30",