Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
202 commits
Select commit Hold shift + click to select a range
0e78754
fix(profile): fail loudly when a policy allocation fails
nmicic Sep 6, 2026
e5bf686
fix(profile): strip inline comments and trailing whitespace
nmicic Sep 6, 2026
a1fc9dd
feat(compartment-bpf): close no-chmod ACL/ioctl/utimes gaps, deny mou…
nmicic Sep 6, 2026
c0ac8f6
test(harness): move fixtures to an unpredictable mktemp root
nmicic Sep 6, 2026
81e4377
fix(profile): fail closed when a policy array overflows
nmicic Sep 6, 2026
e52867f
fix(compartment-root): mount /proc before detaching the old root
nmicic Sep 6, 2026
3184076
fix(compartment-root): bind real device nodes into the container /dev
nmicic Sep 6, 2026
5e35b10
fix(profile): make the security switches one-way
nmicic Sep 6, 2026
fcdfd8c
fix(compartment-root): complete the /proc mask set and handle /sys
nmicic Sep 6, 2026
19cc17a
test(harness): fix six false-green assertions in the matrix suite
nmicic Sep 6, 2026
477bed5
fix(compartment-root): mount the container root nosuid,nodev; force n…
nmicic Sep 6, 2026
f4577a9
fix(profile): commit a profile only after the whole file parses
nmicic Sep 6, 2026
04ce206
fix(compartment-root): install a built-in seccomp deny-list by default
nmicic Sep 6, 2026
a8314d9
test(harness): discover rootless.d/ and root.d/ suites
nmicic Sep 6, 2026
24b5d01
fix(compartment-bpf): mirror path_mount() flag order, gate compat ioc…
nmicic Sep 6, 2026
9678a5b
test(compartment-bpf): atime-only and cp -p over-deny guards for the …
nmicic Sep 6, 2026
29319b5
fix(compartment-root): deny setgroups before gid_map; add uid-map/gid…
nmicic Sep 6, 2026
7af710a
fix(profile): trust the profile source before trusting the profile
nmicic Sep 6, 2026
6b440d2
fix(compartment-root): parse --profile with getopt_long, not a pre-scan
nmicic Sep 6, 2026
eaf0f3e
test(harness): add core-matrix-extra rootless suite (52 assertions)
nmicic Sep 6, 2026
4a379ca
fix(compartment-user): validate $HOME before using it as a sandbox root
nmicic Sep 6, 2026
af4604d
fix(compartment-root): fd cleanup before seccomp; PID 1 reaper for th…
nmicic Sep 6, 2026
b166bd1
fix(compartment-root): confine --cgroup to /sys/fs/cgroup
nmicic Sep 6, 2026
8a6bd6d
fix(compartment-root): make --verbose and --dry-run report the real p…
nmicic Sep 6, 2026
dbd49f4
build: state the hardening flags instead of inheriting distro defaults
nmicic Sep 6, 2026
4bba363
docs(compartment-bpf): correct the marker-move rationale and three mi…
nmicic Sep 6, 2026
4836012
fix(profile): accept a private group as no wider than owner-write
nmicic Sep 6, 2026
0626dd7
test(harness): skip external-dependency suites explicitly
nmicic Sep 6, 2026
65fcf89
fix(compartment-user): validate COMPARTMENT_SHELL_DIR and harden shel…
nmicic Sep 6, 2026
e7ce34d
fix(scripts): timestamp.sh strands the user on the proof branch
nmicic Sep 6, 2026
6330a2b
test(harness): fix shellcheck -S warning findings
nmicic Sep 6, 2026
2a8f387
fix(extra): make the shipped helper scripts executable
nmicic Sep 6, 2026
aea6836
fix(audit): private log directory, openat, sanitised fields
nmicic Sep 6, 2026
4a9530c
chore(gitignore): stop '*.d' from swallowing tests/scripts/rootless.d
nmicic Sep 6, 2026
3adb3de
test(compartment-root): root-only suite under tests/scripts/root.d/
nmicic Sep 6, 2026
8aa6ef6
test(compartment-bpf): let BX-1/BX-2 report the v0.8 mount deny inste…
nmicic Sep 6, 2026
8f53f85
ci: build matrix, root job, sanitizers, shellcheck and mode gates
nmicic Sep 6, 2026
6cd3fc0
test(harness): report assertion totals from the suites themselves
nmicic Sep 6, 2026
2965e5b
docs(compartment-root): describe what the tool actually does
nmicic Sep 6, 2026
888d125
fix(compartment-root): tighten container_init and state its trust bou…
nmicic Sep 6, 2026
5b0376f
docs: make the test/CI/build documentation match the harness
nmicic Sep 6, 2026
b874f8a
test(compartment-root): pid-namespace and allow-list profile assertions
nmicic Sep 6, 2026
fe35120
fix(compartment.h): validate uid-map/gid-map ranges in 64-bit arithmetic
nmicic Sep 6, 2026
5d21701
test(compartment-bpf): resolve a real ELF exec fixture instead of /us…
nmicic Sep 6, 2026
03c24cc
test(compartment-bpf): make run-mesh.sh self-sufficient and PIN_ROOT-…
nmicic Sep 6, 2026
9a917da
docs: name the external-CLI smoke suite by what it needs
nmicic Sep 6, 2026
05e7e25
test(compartment-bpf): drive --pin as the loader behaves; assert the …
nmicic Sep 6, 2026
d81925d
fix(compartment-root): declare the mount_setattr shim after compartme…
nmicic Sep 6, 2026
6e30ace
docs(compartment-root): record the --netns user-namespace limitation
nmicic Sep 6, 2026
c6d4f70
test(compartment-bpf): revive the two permanently-dead bypass witnesses
nmicic Sep 6, 2026
733d00c
test(compartment-bpf): document and preflight the bypass host-side dr…
nmicic Sep 6, 2026
781c322
test(compartment-bpf): witness a hook by its name, not by a bare op s…
nmicic Sep 6, 2026
e65f4fd
test(harness): move the SC2034 justification to the file header
nmicic Sep 6, 2026
136fb0d
test(compartment-bpf): fail check-release on any unrecognised SKIP
nmicic Sep 6, 2026
cdbc471
test(compartment-root): assert cap-allow survives the init fork
nmicic Sep 6, 2026
0f30505
fix(compartment-user): close the env deny-list gaps, add prefix matching
nmicic Sep 6, 2026
10df540
feat(compartment-user): add --dump-profile, generate the shipped example
nmicic Sep 6, 2026
7984d00
docs: correct the claims about what survives after exec
nmicic Sep 6, 2026
4238b06
test: regression suite for profile trust, parsing and audit hardening
nmicic Sep 6, 2026
4f5a6e9
docs(DESIGN): record the profile-trust fixes
nmicic Sep 6, 2026
08b05bd
docs: describe the credential exception without naming providers
nmicic Sep 6, 2026
cf5f987
fix(compartment-root): report a failed loopback bring-up
nmicic Sep 6, 2026
23c3be1
test(compartment-root): widen the cleanup kill pattern
nmicic Sep 6, 2026
7ab2934
fix(compartment-user): do not spin on a malformed environ entry
nmicic Sep 6, 2026
8cac155
fix(kvm): scope the NAT rule, log the first boot, and preflight properly
nmicic Sep 6, 2026
043d180
docs(tests): point tests/README.md at the root-only suite
nmicic Sep 6, 2026
705f350
test: track the rootless.d and root.d suites
nmicic Sep 6, 2026
a2d67de
fix(syscall.py): emit loadable profiles, stop leaking credentials
nmicic Sep 6, 2026
24336d6
fix(extra): close the open-proxy config and the destructive helper sc…
nmicic Sep 6, 2026
59979e5
fix(examples): restore host-key verification in paranoid-ssh, correct…
nmicic Sep 6, 2026
301d752
docs: --rw does not grant execute
nmicic Sep 6, 2026
a6e896c
fix(sandbox.sh): make the HARD-mode shell intercept work, and fail lo…
nmicic Sep 6, 2026
75ab261
docs: use a neutral program name in the profiling examples
nmicic Sep 6, 2026
645b101
docs(compartment-bpf): write down how each suite is actually run
nmicic Sep 6, 2026
d46d200
docs(compartment-root): record the kernel 7.0 confirmation run
nmicic Sep 6, 2026
d2c4377
test(compartment-bpf): refuse a realbin fixture cache we do not own
nmicic Sep 6, 2026
eec2bfc
fix(audit): put the default log where the sandbox cannot reach it
nmicic Sep 6, 2026
a19618d
merge: fix/test-harness-ci — false-green fixes, suite discovery, hard…
nmicic Sep 6, 2026
120870e
merge: fix/profile-trust — profile search order and trust, transactio…
nmicic Sep 6, 2026
2dd06ed
merge: fix/compartment-root — mount ordering, device nodes, seccomp d…
nmicic Sep 6, 2026
d6dd2ce
merge: fix/aux-tools — syscall.py, extra/ helpers, examples, paranoid…
nmicic Sep 6, 2026
f93d5c8
feat(compartment-bpf): deny detaching a filesystem that hosts sealed …
nmicic Sep 6, 2026
1e294de
fix(compartment-root): stop --help advertising a profile path that is…
nmicic Sep 6, 2026
92dcea2
docs(compartment-user): list --allow, --env-allow and --version in --…
nmicic Sep 6, 2026
3994448
docs: record uid-map/gid-map in the directive reference; qualify the …
nmicic Sep 6, 2026
32ef634
docs(tests): list every discovered suite in tests/README.md
nmicic Sep 6, 2026
710565e
docs(SECURITY): record the --netns limitation, the audit-log confiden…
nmicic Sep 6, 2026
c82c315
docs: drop two stale descriptions of behaviour that no longer exists
nmicic Sep 6, 2026
e61db74
docs(HOWTO): point the root-test instructions at make test-root and t…
nmicic Sep 6, 2026
b72c7a4
test(root): reconcile the no-new-privs assertion and report assertion…
nmicic Sep 6, 2026
f127d16
merge: bpf-v0.8 — v0.8 port (ABI 0x0008), F1-F5 fixes, T3-T5 witnesse…
nmicic Sep 6, 2026
37766dc
merge: fix/bpf-tests-kvm — real-ELF exec fixture, run-mesh self-suffi…
nmicic Sep 6, 2026
d064583
feat(landlock): per-file rules, TCP port rules, one shared ruleset bu…
nmicic Sep 6, 2026
a435737
feat(audit): warn when the log directory is inside a writable rule
nmicic Sep 6, 2026
6beadf3
feat(seccomp): socket and event syscall families, one deny-list, secc…
nmicic Sep 6, 2026
ab797a0
fix(landlock): per-node /dev rules in the ai-agent and ssh profiles
nmicic Sep 6, 2026
b3f290c
refactor(compartment-root): use the shared built-in deny-list
nmicic Sep 6, 2026
1fde367
fix(compartment-root): join --netns in the parent, before clone()
nmicic Sep 6, 2026
b3f38eb
feat(compartment-root): private devpts and a tmpfs /dev/shm
nmicic Sep 6, 2026
1fa013e
feat(compartment-root): Landlock, mount hardening and rootdir ownership
nmicic Sep 6, 2026
0d466da
feat(compartment-root): filter the PID 1 reaper when the policy allow…
nmicic Sep 6, 2026
fc8737a
feat(examples): enable the network block in container.conf
nmicic Sep 6, 2026
9ddbe37
build: stop installing the example profiles from `make install`
nmicic Sep 6, 2026
a34a7dd
test: give the external-CLI smoke profile an execute grant for the CLI
nmicic Sep 6, 2026
dfbc6f3
feat(examples): restricted-root.conf, a compartment-root binary allow…
nmicic Sep 6, 2026
263daf4
test: rootless suite for per-file rules, port rules and the refused s…
nmicic Sep 6, 2026
eec2512
test: root suite for compartment-root Landlock, mounts and the netns …
nmicic Sep 6, 2026
cca575b
docs: per-file rules, TCP port policy, compartment-root Landlock and …
nmicic Sep 6, 2026
9b8f94e
docs(compartment-bpf): correct the suite tallies the v0.8 merge moved
nmicic Sep 6, 2026
d0be739
test(root): make the container listeners in the Landlock suite hang-p…
nmicic Sep 6, 2026
70b10a9
merge: feat/landlock-net — per-file Landlock rules, TCP port rules, c…
nmicic Sep 6, 2026
2deeb03
merge: bpf-integrated — compartment-bpf v0.8, ABI 0x0008, C-1 umount …
nmicic Sep 6, 2026
c945592
docs: reconcile the file lists, tallies and option references with th…
nmicic Sep 6, 2026
45bf98a
docs: correct the counts, orderings and reserved options that had dri…
nmicic Sep 6, 2026
0a4270f
docs: correct the cap-allow diagnostic and the quick-stability wall time
nmicic Sep 6, 2026
6876894
build(compartment-bpf): make check-release able to fail on a failing …
nmicic Sep 6, 2026
257cb57
test(root): prove the /proc mask mechanism instead of a file's size
nmicic Sep 6, 2026
35327d8
test(root): witness setgroups deny, the id maps and PR_SET_DUMPABLE
nmicic Sep 6, 2026
f2b531f
test(profile-trust): assert profile search precedence and the group-w…
nmicic Sep 6, 2026
d29f6ac
test(harness): count the assertions a skip stands in for, and require…
nmicic Sep 6, 2026
5dae06d
test(harness): make the assertions that could not fail capable of fai…
nmicic Sep 6, 2026
ac9d4d9
test(sandbox): run sandbox.sh HARD mode against a real namespace
nmicic Sep 6, 2026
87a75a2
test(compartment-bpf): give the release gate an integrity check of it…
nmicic Sep 6, 2026
fe2ca04
test(compartment-bpf): assert the pinned link set by name
nmicic Sep 6, 2026
6d424eb
test: witness the deny-list behaviourally and record the documented r…
nmicic Sep 6, 2026
d95d007
test: wire up the orphans and gate against new ones
nmicic Sep 6, 2026
fa4a668
test(harness): stop the suites modifying the host they measure
nmicic Sep 6, 2026
15dc10b
fix(profile): free the one-shot allocations and record their ownership
nmicic Sep 6, 2026
5559579
build: lint compartment-bpf against a baseline and give it CI
nmicic Sep 6, 2026
2b04e39
fix(profile): refuse an unknown directive and an invalid mode value
nmicic Sep 6, 2026
f7cb18a
test(compartment-bpf): make the coverage corpus deterministic and tra…
nmicic Sep 6, 2026
7e4bdda
build(compartment-bpf): stop re-dumping vmlinux.h on every make
nmicic Sep 6, 2026
adf0fb5
build: probe the _FORTIFY_SOURCE level that is actually in effect
nmicic Sep 6, 2026
3d538aa
test(compartment-bpf): assert a floor, not an exact global delta, on …
nmicic Sep 6, 2026
e0a4891
test(root): track the required /proc mask set as well as the tool's o…
nmicic Sep 6, 2026
f495f35
test(compartment-bpf): correct the fallocate residual and restore the…
nmicic Sep 6, 2026
b2db690
docs(compartment-bpf): correct the fallocate rows in LIMITATIONS and …
nmicic Sep 6, 2026
7246a02
test(harness): take the seccomp baseline before asserting filter mode
nmicic Sep 6, 2026
790c284
test(compartment-bpf): close the four allow-listed skips with three p…
nmicic Sep 6, 2026
c24cc4e
build(compartment-bpf): declare the new targets on their own lines
nmicic Sep 6, 2026
4b08cb9
build: run the compartment-bpf parser witnesses under sudo in CI
nmicic Sep 6, 2026
2ad2dc4
test(compartment-bpf): correct two count extractors in the release to…
nmicic Sep 6, 2026
80772f4
test(compartment-bpf): bound the AIDE scan and put the machine's conf…
nmicic Sep 6, 2026
809e0be
build(compartment-bpf): keep check-release's live output while fixing…
nmicic Sep 6, 2026
52042d7
build(compartment-bpf): carry the gate self-test through the real sca…
nmicic Sep 6, 2026
533d462
test(compartment-bpf): allow-list the chrony fixture skip instead of …
nmicic Sep 6, 2026
39e33bf
docs(tests): describe the new suites and the two gates that keep the …
nmicic Sep 6, 2026
ed5deac
test(compartment-bpf): record the full 1024-cycle stability soak
nmicic Sep 6, 2026
f93cfb5
release: compartment 1.4.0 (compartment-bpf v0.8.0)
nmicic Sep 6, 2026
77abdeb
merge: fix/test-hardening — mutation-review test hardening, release g…
nmicic Sep 6, 2026
679a87d
fix(kvm): install the packages the host-tool probe names
nmicic Sep 6, 2026
d14143a
fix(kvm): add acl and gcc-multilib so a fresh guest can pass check-re…
nmicic Sep 6, 2026
98e0838
docs(kvm): tell the operator to regenerate vmlinux.h, not to no-op it
nmicic Sep 6, 2026
c02940d
feat(profile): mknod and mknodat in the syscall table
nmicic Sep 6, 2026
1edc5b6
feat(compartment-user): cap-drop and mask directives
nmicic Sep 6, 2026
8d0d838
fix(compartment-user): shell-replacement mode for a real login shell
nmicic Sep 6, 2026
b95fee5
feat(profile): limited-root.conf and limited-root-authpath.conf
nmicic Sep 6, 2026
8bcc351
fix(compartment-user): a mask neutralises a write, it does not refuse…
nmicic Sep 6, 2026
4510586
test: end-to-end limited-root suite over a real sshd login
nmicic Sep 6, 2026
8f28d3f
docs: limited root over SSH
nmicic Sep 6, 2026
4bb8c37
test: update the declared assertion totals for the new example profile
nmicic Sep 6, 2026
2fe0e02
docs: name the --dry-run step and the PR_SET_MM gate in the seal profile
nmicic Sep 6, 2026
a8ec09a
test: retry --unpin, which refuses to race the pin lifecycle
nmicic Sep 6, 2026
08a5128
style(compartment.h): cfg_add_mask belongs above the boolean-parsing …
nmicic Sep 6, 2026
357731a
test: --pin holds the foreground, so start it, wait for the pins, sto…
nmicic Sep 6, 2026
a4ac36a
docs(compartment-bpf): bpf_map_freeze() is not map integrity
nmicic Sep 6, 2026
db1898f
abi(compartment-bpf): self-protection action codes and shared types
nmicic Sep 6, 2026
e047b57
bpf(compartment-bpf): lsm/bpf_map gate and pin-tamper branches
nmicic Sep 6, 2026
bf595a2
loader(compartment-bpf): --self-protect and --authorize-loader
nmicic Sep 6, 2026
c955db3
test(compartment-bpf): five self-protection witnesses
nmicic Sep 6, 2026
d864bda
bpf(compartment-bpf): answer each self-protection deny in its syscall…
nmicic Sep 6, 2026
128bf9a
build+tools(compartment-bpf): gate the self-protection surface
nmicic Sep 6, 2026
a544d17
docs(compartment-bpf): self-protection, the upgrade rule, and what it…
nmicic Sep 6, 2026
c6f6677
bench(compartment-bpf): measure what --self-protect costs bpf(2)
nmicic Sep 6, 2026
7c40126
loader(compartment-bpf): say why --stats was refused, once
nmicic Sep 6, 2026
b5bb8ba
test(compartment-bpf): raise the telemetry counter floor to the whole…
nmicic Sep 6, 2026
c88a689
docs(compartment-bpf): name the right action code for the bpffs umoun…
nmicic Sep 6, 2026
7617bc3
docs(compartment-bpf): the errno table now quotes what was measured
nmicic Sep 6, 2026
c466fe2
docs(compartment-bpf): drop a counter count that the same release cha…
nmicic Sep 6, 2026
c6e8398
test(compartment-bpf): assert the --stats refusal on tokens, not on p…
nmicic Sep 6, 2026
f482a78
docs(tests): list the bpf(2) overhead bench in RUNNING.md
nmicic Sep 6, 2026
09b665d
build(compartment-bpf): declare bench-bpf-syscall on its own .PHONY line
nmicic Sep 6, 2026
dc1edb1
docs(compartment-bpf): put the runtime cost in front of the operator
nmicic Sep 6, 2026
0621423
test(compartment-bpf): two self-protection assertions were not testin…
nmicic Sep 6, 2026
bf1a295
test(compartment-bpf): a self-protection witness must not be able to …
nmicic Sep 6, 2026
65e4f16
loader(compartment-bpf): do not blame --self-protect for a mode-700 r…
nmicic Sep 6, 2026
a784159
build(compartment-bpf): the counter gate checked one table and claime…
nmicic Sep 6, 2026
e3bcd9d
docs(compartment-bpf): three claims that the code does not make
nmicic Sep 6, 2026
aa2cdd4
docs(compartment-bpf): report the cost as a range, because it is one
nmicic Sep 6, 2026
ebc5dc5
merge: fix/kvm-rerun — install the packages the host-tool probe names…
nmicic Sep 6, 2026
ac10c0a
merge: feat/limited-root — cap-drop and mask directives, the mknod pa…
nmicic Sep 6, 2026
817d456
merge: feat/bpf-self-protect — --self-protect and --authorize-loader,…
nmicic Sep 6, 2026
eccca8f
docs: reconcile what the three merged branches say about each other
nmicic Sep 6, 2026
e6be5a3
docs(compartment-bpf): quote the numbers the release-candidate run me…
nmicic Sep 6, 2026
c38c9d1
docs: one complete list of what limited root does not protect against
nmicic Sep 6, 2026
4f16510
docs(compartment-bpf): consolidate what --self-protect closes, costs …
nmicic Sep 6, 2026
7c12c44
docs: name the trust boundaries the two 1.4.0 controls sit on, and cr…
nmicic Sep 6, 2026
2568a9f
docs: name the blast radius of the forgeable actor= identity
nmicic Sep 6, 2026
72dfd15
fix: close release 1.4 security review gaps
nmicic Sep 7, 2026
d938f42
fix(ci): unblock release checks on Ubuntu runners
nmicic Sep 7, 2026
b449e68
fix(ci): follow packaged bpftool symlinks
nmicic Sep 7, 2026
1a4d7eb
fix: address release review and CI environment findings
nmicic Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 275 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,298 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# ── Build + the whole rootless suite, on both supported releases ──
#
# 22.04 and 24.04 differ in kernel (Landlock ABI), glibc (_FORTIFY_SOURCE=3
# needs >= 2.35) and gcc, which is exactly what the hardening probes and the
# ABI negotiation have to cope with.
build-and-test:
runs-on: ubuntu-latest
name: build + rootless suite (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
steps:
- uses: actions/checkout@v4

- name: Toolchain and kernel
run: |
cc --version | head -1
ldd --version | head -1
uname -a

- name: Build
run: make

- name: Hardening flags selected on this toolchain
run: make show-hardening

- name: Build deny_probe
run: make tests/probes/deny_probe

- name: Run tests
run: make test-quick

- name: Verify --version
run: |
./compartment-user --version
./compartment-root --version

- name: Verify --verify
run: ./compartment-user --verify
run: |
set +e
./compartment-user --verify
rc=$?
set -e
echo "compartment-user --verify exit=${rc}"
if [ "${rc}" -ne 0 ]; then
echo "::warning::--verify reports a degraded host (exit ${rc}); see the output above"
fi

- name: Dry-run smoke test
run: ./compartment-user --dry-run -- /bin/true

- name: Hardening properties of the built binaries
run: |
rc=0
for b in compartment-user compartment-root tests/probes/deny_probe; do
echo "== ${b}"
readelf -hd "${b}" | grep -E 'Type:|BIND_NOW' || true
readelf -lW "${b}" | grep -E 'GNU_STACK|GNU_RELRO' || true
readelf -h "${b}" | grep -q 'Type:[[:space:]]*DYN' \
|| { echo "::error::${b} is not a PIE"; rc=1; }
readelf -d "${b}" | grep -q 'BIND_NOW' \
|| { echo "::error::${b} lacks BIND_NOW (full RELRO)"; rc=1; }
readelf -lW "${b}" | grep -E '^ *GNU_STACK' | grep -q 'RW ' \
|| { echo "::error::${b} has an executable stack"; rc=1; }
done
exit "${rc}"

# The full suite, minus the one suite that needs a third-party CLI and
# an authenticated session. sandbox.sh still runs: it needs only
# unprivileged user namespaces, which the runners allow, and its single
# proxy case skips itself when no proxy answers.
- name: Integration suite (no external services)
env:
COMPARTMENT_SKIP_EXTERNAL: "1"
run: make test-integration

# ── Root-only suites ─────────────────────────────────────────────
#
# GitHub runners give passwordless sudo and unprivileged user namespaces,
# so the compartment-root paths that no CI has ever executed can run here.
root-tests:
name: root suite (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
steps:
- uses: actions/checkout@v4

# Build as the normal user first so sudo does not leave root-owned
# objects in the checkout.
- name: Build
run: |
make
make tests/probes/deny_probe

- name: Refuses to run unprivileged
run: |
set +e
./tests/scripts/run_root_tests.sh
rc=$?
set -e
if [ "${rc}" -eq 0 ]; then
echo "::error::run_root_tests.sh must refuse to run as a non-root user"
exit 1
fi
echo "run_root_tests.sh correctly refused (exit ${rc})"

- name: Root suite
run: sudo make test-root

# ── Sanitizers ───────────────────────────────────────────────────
sanitizers:
name: asan+ubsan (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
# LeakSanitizer cannot run over the enforcing paths: it reads
# /proc/<pid>/task from an at-exit handler, and by then Landlock and
# seccomp are applied, so it aborts with "LeakSanitizer has
# encountered a fatal error" on every run. detect_leaks=0 therefore
# stays for the suite — but it used to be the only setting, which
# meant nothing anywhere looked for a leak. The step below runs LSan
# over every path that returns instead of exec'ing (--version,
# --verify, --dry-run, --dump-profile, and compartment-root's own
# dry run), which is where the parser allocates and where
# config_free_oneshot() now frees.
# UBSan is made fatal so a report fails the job instead of being printed
# and ignored.
env:
ASAN_OPTIONS: detect_leaks=0
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
COMPARTMENT_SKIP_EXTERNAL: "1"
steps:
- uses: actions/checkout@v4

- name: Build with -fsanitize=address,undefined
run: |
make clean
make \
EXTRA_CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer -U_FORTIFY_SOURCE -g -O1' \
EXTRA_LDFLAGS='-fsanitize=address,undefined'
make tests/probes/deny_probe \
EXTRA_CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer -U_FORTIFY_SOURCE -g -O1' \
EXTRA_LDFLAGS='-fsanitize=address,undefined'

- name: Sanitized dry-run
run: ./compartment-user --dry-run -- /bin/true

- name: Rootless suite under the sanitizers
run: ./tests/scripts/run_all.sh --quick

# Every non-enforcing path, with LeakSanitizer on. These are the
# paths that parse a profile and then return, so they exercise the
# whole allocation surface without reaching restrict_self() — which
# is the only reason detect_leaks had to be off at all.
- name: LeakSanitizer over the non-enforcing paths
env:
ASAN_OPTIONS: detect_leaks=1
run: |
set -e
conf="$(mktemp --suffix=.conf)"
printf 'ro /usr\nro /lib32?\nrw /tmp\nenv-deny FOO\nworkdir /tmp\nlandlock on\nseccomp on\n' > "$conf"
chmod go-w "$conf"
./compartment-user --version
./compartment-user --verify || true
./compartment-user --dry-run -- /bin/true
./compartment-user --dump-profile ai-agent > /dev/null
./compartment-user --dump-profile strict > /dev/null
./compartment-user --dry-run --profile "$conf" -- /bin/true
./compartment-user --dry-run --profile "$conf" --workdir /tmp \
--audit-log /tmp -- /bin/true
./compartment-root --dry-run --verbose -c /tmp -U root \
-M /proc/foo -A net_bind_service -L /tmp -- /bin/true
rm -f "$conf"

# ── compartment-bpf: everything that needs neither root nor a
# BPF-capable kernel ─────────────────────────────────────────────
#
# The whole of compartment-bpf/ was outside CI: 39 bypass witnesses,
# 3284 mesh trials, 17 strict-launch witnesses, the coverage gate and
# check-release ran only by hand on two VMs. The enforcement suites
# genuinely need root and a bpf-LSM kernel, but the build and the
# static gates do not, and those are what break first.
bpf-static:
name: compartment-bpf build + static gates
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Toolchain
run: |
sudo apt-get update
sudo apt-get install -y clang libbpf-dev linux-tools-common \
linux-tools-generic libelf-dev zlib1g-dev \
libsodium-dev
clang --version | head -1
# The runner may use an Azure kernel while linux-tools-generic
# installs tools for a generic kernel. Bypass Ubuntu's uname-based
# wrapper with the packaged binary for these BTF/static gates.
if ! bpftool version; then
installed_bpftool="$(find -L /usr/lib/linux-tools -type f -name bpftool -executable | sort -V | tail -1)"
test -n "$installed_bpftool"
sudo ln -s "$installed_bpftool" /usr/local/sbin/bpftool
export PATH="/usr/local/sbin:$PATH"
echo /usr/local/sbin >> "$GITHUB_PATH"
fi
bpftool version

- name: Kernel BTF is readable
run: test -r /sys/kernel/btf/vmlinux

- name: Build
working-directory: compartment-bpf
run: make

- name: Coverage gate
working-directory: compartment-bpf
run: make check-coverage-static

- name: Coverage-map selftest
working-directory: compartment-bpf
run: tools/coverage-map.py --selftest

- name: ABI / actor-hook gate
working-directory: compartment-bpf
run: make check-actor-hook

- name: Every shipped profile parses
working-directory: compartment-bpf
run: make check-profiles

# sudo, but still no BPF: check-parser-actor stages its fixtures
# under $TMPDIR and the loader refuses an actor resolved through a
# world-writable directory, so an unprivileged run fails on the
# fixtures rather than on the parser. 46 assertions of parser and
# actor-resolution coverage that CI has never had.
- name: Parser and actor witnesses
working-directory: compartment-bpf
run: sudo make check-parser-actor

- name: HOWTO examples still load
working-directory: compartment-bpf
run: make check-howto-examples

# ── Repository hygiene ───────────────────────────────────────────
lint:
name: shellcheck + file modes
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install shellcheck
run: |
if ! command -v shellcheck > /dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y shellcheck
fi
shellcheck --version

# Every tracked *.sh outside compartment-bpf/ must be clean at
# -S warning. Suppress a specific code only next to the line it
# applies to, with a comment saying why.
- name: shellcheck
run: make check-shell

# compartment-bpf/ is excluded above and had never been linted by
# any gate. Baselined rather than fixed at a release gate; a NEW
# finding fails.
- name: shellcheck (compartment-bpf, against the baseline)
run: make check-shell-bpf

- name: No orphaned test scripts
run: make check-orphans

- name: Documented symbols exist
run: make check-docs-symbols

# Every *.sh with a shebang must be mode 755 (all 12 scripts under
# extra/ shipped non-executable, so the documented ./start.sh workflow
# failed), and every *.sh without one must not be.
- name: Executable bits
run: make check-modes

- name: Workflow YAML is parseable
run: python3 -c 'import yaml; yaml.safe_load(open(".github/workflows/ci.yml"))'
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
compartment-user
compartment-root
tests/probes/deny_probe
tests/probes/fd_reader-static

# Build artifacts
*.o
*.d
*.a
*.so
# ...but the test discovery directories are not build artifacts: "*.d" above
# matches the directory names tests/scripts/root.d and tests/scripts/rootless.d,
# and git will not descend into an excluded directory, so every suite dropped
# into them would be silently untracked. Re-include the directories themselves.
!tests/scripts/*.d/

# Generated headers (config.py output)
config.h
Expand Down
Loading
Loading