warmor-sbom-policy generates kernel-level allowlist policies from SPDX and CycloneDX SBOMs. Only binaries declared in the SBOM are allowed to execute — everything else is denied at the kernel level.
1. Generate: syft <image> -o cyclonedx-json > sbom.json
2. Extract: docker export $(docker create <image>) | tar -C ./rootfs -xf -
3. Policy: warmor-sbom-policy --rootfs ./rootfs sbom.json -o policy.yaml
4. Compile: warmor-compile policy.yaml -o policy.wasm
5. Enforce: warmor-daemon --policy policy.wasm
go install github.com/yasindce1998/warmor/cmd/warmor-sbom-policy@latestOr build from source:
cd cmd/warmor-sbom-policy && go build -o warmor-sbom-policy .warmor-sbom-policy [flags] <sbom.json>| Flag | Default | Description |
|---|---|---|
-o |
stdout | Output YAML file path |
--format |
auto |
SBOM format: spdx, cyclonedx, or auto (detect from JSON) |
--level |
binary |
Enforcement level: binary, library, or all |
--rootfs |
/ |
Path to container rootfs for package DB resolution |
--name |
auto from SBOM | Policy name |
--description |
auto-generated | Policy description |
--include-interpreters |
true |
Include known interpreters (python, node, sh) as allowed |
--version |
— | Print version and exit |
# Generate from a CycloneDX SBOM (auto-detected)
warmor-sbom-policy --rootfs ./rootfs sbom.json -o policy.yaml
# Explicit SPDX format, library-level enforcement
warmor-sbom-policy --format spdx --level library --rootfs ./rootfs sbom.json
# All files (strictest), no interpreters
warmor-sbom-policy --level all --include-interpreters=false --rootfs ./rootfs sbom.json
# Pipe to stdout for inspection
warmor-sbom-policy --rootfs ./rootfs sbom.jsonThe tool reads SPDX 2.3 or CycloneDX 1.4+ JSON files. Format is auto-detected by looking for spdxVersion or bomFormat keys. Both formats are normalized into a unified package list with name, version, type, and hashes.
Package names alone don't tell the kernel what to allow — file paths do. The tool reads the container's package database to map each SBOM package to its installed files:
| Distro | Database Location | Format |
|---|---|---|
| Alpine (APK) | /lib/apk/db/installed |
Plain text with P:/F:/R: records |
| Debian/Ubuntu (DEB) | /var/lib/dpkg/info/<pkg>.list |
One absolute path per line |
| RHEL/Fedora (RPM) | /var/lib/rpm/ |
Binary DB or manifest |
This requires access to the container's filesystem via --rootfs.
| Level | Included Files | Use Case |
|---|---|---|
binary |
Executables in PATH dirs (/usr/bin, /sbin, etc.) |
Default — blocks unauthorized process execution |
library |
Binaries + shared libraries (.so files) |
Prevents library injection attacks |
all |
Every file from every package | Strictest — blocks any undeclared file access |
The resolved file paths become a warmor allowlist policy:
- Binaries →
processevent rules withany_ofvariable - Libraries →
fileevent rules withany_ofvariable - Default action →
deny
Given a CycloneDX SBOM from an Alpine nginx image:
name: nginx-1-25-alpine-sbom-policy
version: 1
description: "SBOM-derived allowlist (47 packages, 23 binaries) from nginx:1.25-alpine"
variables:
sbom-binaries:
- /bin/bash
- /bin/sh
- /usr/bin/envsubst
- /usr/bin/getconf
- /usr/bin/getent
- /usr/bin/njs
- /usr/sbin/nginx
rules:
- name: allow-sbom-binaries
event: process
conditions:
all:
- path:
any_of: $sbom-binaries
action: allow
reason: "Binary declared in SBOM (CycloneDX nginx:1.25-alpine)"
default_action: denyThe --rootfs flag points to an extracted container filesystem. Several methods to obtain it:
# Method 1: docker export
docker export $(docker create nginx:1.25-alpine) | tar -C ./rootfs -xf -
# Method 2: crane (no Docker daemon needed)
crane export nginx:1.25-alpine - | tar -C ./rootfs -xf -
# Method 3: skopeo + umoci
skopeo copy docker://nginx:1.25-alpine oci:nginx-oci
umoci unpack --image nginx-oci:latest bundle
# rootfs is at ./bundle/rootfs/Any tool producing SPDX 2.3 or CycloneDX 1.4+ JSON works:
| Tool | Command |
|---|---|
| Syft | syft nginx:alpine -o cyclonedx-json |
| Trivy | trivy image --format cyclonedx nginx:alpine |
| Docker Scout | docker scout sbom nginx:alpine --format spdx |
Only restricts process execution. An attacker who gains code execution in an existing process can still read/write files, but cannot spawn new processes outside the SBOM.
Best for: general-purpose containers where file access patterns are too broad to enumerate.
Restricts process execution AND shared library loading. Prevents LD_PRELOAD attacks and unauthorized library injection.
Best for: containers with a well-defined set of libraries (static-linked binaries don't need this).
Restricts access to every file. Only files installed by SBOM-declared packages are accessible.
Best for: minimal containers (distroless, scratch-based) where the full file set is known and small.
- Generate SBOMs at build time — embed the SBOM in your CI pipeline so it matches the exact image being deployed.
- Use
--include-interpreters=falsefor containers that don't need shells or scripting runtimes — this blocks shell escape attacks. - Start with
binarylevel — it's the safest default. Move tolibraryorallonly after testing in audit mode. - Combine with audit-mode — deploy the generated policy in audit mode first to catch any legitimate binaries missing from the SBOM.
- Re-generate on image update — when the base image changes, re-run the SBOM generator and
warmor-sbom-policyto update the allowlist.