Please skim the About this project
section of the README first. bit-crafts is a personal, AI-pair-
programmed R&D codebase maintained by a single person. Treat the
guidance below as best-effort, not a contract — there is no on-call
rotation, no SLA, and no security team behind this project.
Only the main branch is supported; tagged releases are point-in-time
snapshots, not LTS branches. Security fixes land on main and ship in
the next tag.
Do not open a public GitHub issue for security bugs.
Use GitHub's private vulnerability reporting
on this repository (Security tab → Report a vulnerability).
Include:
- A description of the issue (what / where / impact).
- A minimal reproduction (command line, input file, dataset, expected vs. observed behaviour).
- The version (
bchash --version, git commit, distribution).
I read security advisories when time allows; I cannot promise a reply, a triage, or a fix. If a report sits unanswered for 90 days, please consider yourself free to publish.
In scope:
- Memory safety bugs in any
bc-*library orbc-*tool (UAF, OOB, heap corruption, double-free, use-of-uninitialized). - Path traversal or symlink attacks in the walker / manifest / duplicate-prune paths.
- Subprocess argv injection from the Vigil GTK4 frontend
(
applications/vigil/) into thebchashCLI it spawns. - Sandbox-escape or XDG-state misuse in the Vigil Snap (strict
confinement) or Flatpak (
--filesystem=home:ro) packagings. - Hash collisions exploitable in the
verify/diffpaths (cross-tool digest mismatch, length-extension, ...). - Privilege escalation via
bcintegrity verify/bcduplicate pruneacting on attacker-controlled trees.
Out of scope:
- Performance regressions (file an issue).
- DoS via legitimate workloads (very large trees, very deep nesting).
- Findings that require root access on the runner host.
- Issues in third-party dependencies (
liburing,libxxhash,libssl,libblake3) — report those upstream.
The walker descends into pseudo-filesystems (/proc, /sys, /dev,
/run, /tmp) only when explicitly named as the root. Otherwise these
mounts are filtered. See subprojects/bc-io/src/path/bc_io_path.c for
the implementation.
bcintegrity and bchash open files with O_NOFOLLOW by default;
--follow-symlinks opts in to following links and runs cycle detection.
CLI parsers (bc_runtime_cli_parse, the per-tool dispatchers, the
manifest reader, the glob filter) are continuously fuzzed under
-Dfuzzing=true (see tools/*/fuzzing/, subprojects/bc-runtime/fuzzing/).
Vigil never links against the C libraries. It spawns bchash via
Gio.SubprocessLauncher.spawnv(argv) — list-form argv, no shell, no
string concatenation; the -- argv separator is always emitted before
positional path arguments. There is no subprocess.run, no
shell=True, no os.system anywhere under applications/vigil/src/.
Persistent state lives in $XDG_DATA_HOME/bitcrafts-vigil/ (default
~/.local/share/bitcrafts-vigil/) — directory mode 0o700, SQLite
file mode 0o600 (and on the -wal / -shm sidecars). In Snap the
prefix is ~/snap/bitcrafts-vigil/current/.local/share/bitcrafts-vigil/,
in Flatpak it's ~/.var/app/com.unmanagedbytes.BitCraftsVigil/data/bitcrafts-vigil/.
Both are isolated from other applications by their respective sandbox.
Snap plug list: home, desktop, desktop-legacy, wayland, x11,
opengl — no network, no removable-media, no system-observe.
Flatpak finish-args: --filesystem=home:ro (read-only home),
--filesystem=xdg-download:create (export-bundle write target),
--socket=wayland, --socket=fallback-x11, --device=dri. No
network access in either packaging.
bchash check (and bchash diff, reserved) accept a --root=DIR
option. When set, the root directory is opened as a dirfd and relative
entry paths from the manifest are resolved under that directory.
On Linux 5.6+ the underlying openat2(2) syscall enforces
RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS to prevent path traversal
and symlink escapes. When the kernel does not support openat2
(ENOSYS), a single warn-once message is emitted to stderr and
verification falls back to the plain open() path:
bchash: kernel lacks openat2; running without RESOLVE_BENEATH confinement
Default root when --root is not provided: dirname(manifest-path).
Manifests that contain absolute paths are rejected outright with
BC_RUNTIME_EXIT_DATAERR (65) — re-generate with bchash hash to
obtain a relative-path manifest.
bcintegrity verify <root> <manifest> now requires Linux 5.6+
and uses the same openat2(RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS)
confinement at the kernel level. Unlike bchash's graceful fallback,
bcintegrity fails closed on legacy kernels:
bcintegrity: kernel lacks openat2 (Linux >= 5.6 required); refusing to verify without confinement
Exit code is BC_RUNTIME_EXIT_NOPERM (77). There is no
--allow-legacy-kernel opt-out: <root> is always explicit, so the
attack surface is always intentional, so the confinement is always
required. Re-mount the workload onto a 5.6+ host if you need verify
on an older kernel.