diff --git a/Makefile b/Makefile index 60d789d..5d23576 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,8 @@ check-compiler: "$$gate_tools/fs-sandbox" \ --dir / \ --ro /usr \ - --ro /proc \ + --ro /proc/1 \ + --ro /proc/sysvipc \ --ro "$$gate_tools" \ --ro "$$gate_tools/toolchain" \ --rw /dev/null \ @@ -167,7 +168,7 @@ check-compiler: /usr/bin/env -i LANG=C LC_ALL=C PATH=/usr/bin:/bin /usr/bin/cc -std=c17 -O2 -D_FORTIFY_SOURCE=3 -fPIE -pie -Wall -Wextra -Werror -pedantic -Wl,-z,relro,-z,now "$$cargo_home/check-src/tools/fs_sandbox.c" -o "$$gate_tools/fs-sandbox"; \ [[ -f "$$gate_tools/fs-sandbox" && ! -L "$$gate_tools/fs-sandbox" && -x "$$gate_tools/fs-sandbox" ]] || { printf '%s\n' 'filesystem sandbox build is invalid' >&2; exit 1; }; \ manifest="$$cargo_home/check-src/compiler/Cargo.toml"; \ - run_cargo /bin/bash --norc -p -c 'for capability_set in CapInh CapPrm CapEff CapBnd CapAmb; do [[ "$$(/usr/bin/sed -n "s/^$${capability_set}:[[:space:]]*//p" /proc/self/status)" == 0000000000000000 ]] || exit 1; done; for descriptor in /proc/self/fd/*; do [[ ! -e "$$descriptor" || "$${descriptor##*/}" =~ ^[012]$$ ]] || exit 1; done; standard_output="$$(/usr/bin/readlink -- /proc/$$$$/fd/1)"; [[ "$$(/usr/bin/readlink -- /proc/$$$$/fd/0)" == /dev/null && "$$standard_output" =~ ^pipe:\[[0-9]+\]$$ && "$$(/usr/bin/readlink -- /proc/$$$$/fd/2)" == "$$standard_output" ]] || exit 1; for descriptor in 0 1 2; do descriptor_flags="$$(/usr/bin/sed -n "s/^flags:[[:space:]]*//p" "/proc/$$$$/fdinfo/$$descriptor")"; expected_access="$$((descriptor == 0 ? 0 : 1))"; [[ "$$((8#$$descriptor_flags & 3))" == "$$expected_access" ]] || exit 1; done; for ipc_table in msg sem shm; do [[ -z "$$(/usr/bin/sed -n "2p" "/proc/sysvipc/$$ipc_table")" ]] || exit 1; done; ! /usr/bin/head -c 1 -- "$$3/Makefile" >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /etc/passwd >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /sys/devices/system/cpu/online >/dev/null 2>&1 || exit 1; [[ $$$$ == 1 && $$PPID == 0 && "$$(/usr/bin/id -u)" == "$$1" && "$$(/usr/bin/id -g)" == "$$2" && "$$(/usr/bin/hostname)" == orange-gate && "$$(/usr/bin/readlink -- /proc/self/ns/ipc)" != "$$6" && "$$(/usr/bin/readlink -- /proc/self/ns/uts)" != "$$7" && "$$HOME" == "$$4" && "$$PATH" == "$$5/toolchain/bin:/usr/bin:/bin" && "$$(/usr/bin/sed -n "s/^NoNewPrivs:[[:space:]]*//p" /proc/self/status)" == 1 && -z "$$(/usr/bin/sed -n "2p" /proc/net/route)" ]]' gate-isolation "$$gate_uid" "$$gate_gid" "$$repository_root" "$$cargo_home/home" "$$gate_tools" "$$gate_ipc_namespace" "$$gate_uts_namespace"; \ + run_cargo /bin/bash --norc -p -c 'gate_process_root=/proc/1; for capability_set in CapInh CapPrm CapEff CapBnd CapAmb; do [[ "$$(/usr/bin/sed -n "s/^$${capability_set}:[[:space:]]*//p" "$$gate_process_root/status")" == 0000000000000000 ]] || exit 1; done; for descriptor in "$$gate_process_root"/fd/*; do [[ ! -e "$$descriptor" || "$${descriptor##*/}" =~ ^[012]$$ ]] || exit 1; done; standard_output="$$(/usr/bin/readlink -- "$$gate_process_root/fd/1")"; [[ "$$(/usr/bin/readlink -- "$$gate_process_root/fd/0")" == /dev/null && "$$standard_output" =~ ^pipe:\[[0-9]+\]$$ && "$$(/usr/bin/readlink -- "$$gate_process_root/fd/2")" == "$$standard_output" ]] || exit 1; for descriptor in 0 1 2; do descriptor_flags="$$(/usr/bin/sed -n "s/^flags:[[:space:]]*//p" "$$gate_process_root/fdinfo/$$descriptor")"; expected_access="$$((descriptor == 0 ? 0 : 1))"; [[ "$$((8#$$descriptor_flags & 3))" == "$$expected_access" ]] || exit 1; done; for ipc_table in msg sem shm; do [[ -z "$$(/usr/bin/sed -n "2p" "/proc/sysvipc/$$ipc_table")" ]] || exit 1; done; for hidden_proc_path in /proc/cpuinfo /proc/meminfo /proc/self/status /proc/version; do ! /usr/bin/head -c 1 -- "$$hidden_proc_path" >/dev/null 2>&1 || exit 1; done; ! /usr/bin/head -c 1 -- "$$3/Makefile" >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /etc/passwd >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /sys/devices/system/cpu/online >/dev/null 2>&1 || exit 1; [[ $$$$ == 1 && $$PPID == 0 && "$$(/usr/bin/id -u)" == "$$1" && "$$(/usr/bin/id -g)" == "$$2" && "$$(/usr/bin/hostname)" == orange-gate && "$$(/usr/bin/readlink -- "$$gate_process_root/ns/ipc")" != "$$6" && "$$(/usr/bin/readlink -- "$$gate_process_root/ns/uts")" != "$$7" && "$$HOME" == "$$4" && "$$PATH" == "$$5/toolchain/bin:/usr/bin:/bin" && "$$(/usr/bin/sed -n "s/^NoNewPrivs:[[:space:]]*//p" "$$gate_process_root/status")" == 1 && -z "$$(/usr/bin/sed -n "2p" "$$gate_process_root/net/route")" ]]' gate-isolation "$$gate_uid" "$$gate_gid" "$$repository_root" "$$cargo_home/home" "$$gate_tools" "$$gate_ipc_namespace" "$$gate_uts_namespace"; \ run_cargo /usr/bin/env PYTHONHASHSEED=0 /usr/bin/python3 -S -P -B -X utf8 -W error::ResourceWarning "$$cargo_home/check-src/tools/validate_foundation.py"; \ run_cargo /usr/bin/env PYTHONHASHSEED=0 PYTHONPYCACHEPREFIX="$$cargo_home/snapshot-python-cache" /usr/bin/python3 -S -P -B -X utf8 -W error::ResourceWarning -c 'import sys, unittest; sys.path.insert(0, sys.argv.pop(1)); unittest.main(module=None)' "$$cargo_home/check-src" discover -s "$$cargo_home/check-src/tools/tests" -p 'test_*.py'; \ run_cargo /usr/bin/env PYTHONHASHSEED=0 /usr/bin/python3 -S -P -B -X utf8 -W error::ResourceWarning "$$cargo_home/check-src/tools/validate_foundation.py"; \ diff --git a/compiler/README.md b/compiler/README.md index e2c118b..00611f9 100644 --- a/compiler/README.md +++ b/compiler/README.md @@ -74,7 +74,8 @@ Before the drop, the namespace supervisor bind-mounts the selected toolchain read-only and covers `/home` with a private non-executable `tmpfs`. A protected C launcher built from the captured tree requires Landlock ABI 3 or newer, permits directory-name traversal but grants file reads and execution only to `/usr`, the -private `/proc`, the gate tool roots, and the selected toolchain, and grants +gate launcher's private `/proc/1`, the private `/proc/sysvipc` tables, the gate +tool roots, and the selected toolchain, and grants writes only to the two gate roots and four admitted character devices. Host account/configuration files under `/etc` and kernel/device state under `/sys` remain unreadable, with representative runtime assertions. The launcher also @@ -115,8 +116,9 @@ trusted outer `/usr/bin/cat` relays that merged stream to the caller's final output sink. The caller can still close or truncate that final sink, and the two copied output channels are intentionally indistinguishable. The system C compiler, launcher source, relay, kernel, mount implementation, and allowlisted -system roots remain trusted boundaries. Private `/proc` still exposes kernel, -CPU, and process metadata. Resource ceilings are not aggregate +system roots remain trusted boundaries. The gate launcher's PID-1 metadata and +private System V IPC tables remain readable; representative global kernel/CPU +and dynamic `/proc/self` content is asserted unreadable. Resource ceilings are not aggregate cgroup budgets: virtual address space and CPU time are limited per process, file size is limited per file, aggregate resident memory is not capped, and the process ceiling includes other processes with the same real user ID. diff --git a/docs/operations/CI_DEPENDENCIES.md b/docs/operations/CI_DEPENDENCIES.md index 66bced7..897de6d 100644 --- a/docs/operations/CI_DEPENDENCIES.md +++ b/docs/operations/CI_DEPENDENCIES.md @@ -279,7 +279,7 @@ recorded identities and digests. | Result | What is currently fixed | What the result actually establishes | Why it is not reproducible evidence yet | | --- | --- | --- | --- | -| `scripts/ci/check-repository` | Repository bytes, Cargo lock, selected Rust version, allowlisted Python and Cargo environments, fresh bytecode/build cache roots, disabled system Git configuration, global Git configuration fixed to `/dev/null`, private mount, PID, `/proc`, network, IPC, and UTS namespaces with asserted distinct identities, a fixed gate hostname, a read-only selected-toolchain bind, hidden `/home`, a protected Landlock allowlist that omits `/etc` and `/sys`, default ordinary catchable signal dispositions, an empty ordinary signal mask, read-only null standard input, a merged write-only copied-command output pipe with a trusted outer relay, and hard copied-command ceilings of 4 GiB of virtual address space and 600 CPU seconds per process, 512 MiB per file, 1,024 open files, 256 real-user processes, and zero core bytes; the gate uses an unprivileged user namespace where available or a privilege-dropping namespace supervisor otherwise, removes all five capability sets, closes nonstandard descriptors, and runs Cargo locked/offline | Rust formatting, lint, docs, debug and optimized unit/CLI tests, repository invariants, and synthetic/adversarial fixture expectations pass under the executing toolchain and OS without copied-code file contents outside admitted roots, host account/configuration file contents, sysfs kernel/device contents, caller-selected system/global Git configuration, trusted-parent process visibility, inherited host System V IPC objects, the host hostname, inherited blocked `SIGTERM` or ignored `SIGPIPE`, inherited nonstandard descriptors, caller-supplied standard-input bytes, direct access to caller output descriptors, unbounded single-process address-space reservation, unbounded single-process CPU time, unbounded single-file growth, Linux capabilities, privilege-reacquisition permission, or a new external network path | Rust archives, C compiler, Python, shell, Make, kernel, Landlock, namespace and privilege-drop implementations, filesystem, output relay, and host tools are not content-fixed; directory names, private-`/proc` kernel/CPU/process metadata, and unmediated metadata operations remain visible, libc-reserved signal state remains platform-dependent, copied commands can alter their own signal state after execution, the caller controls and can close or truncate the relay's final output sink, resource ceilings are not aggregate cgroup budgets and do not cap aggregate resident memory, and no signed execution manifest or independently replayable environment is emitted | +| `scripts/ci/check-repository` | Repository bytes, Cargo lock, selected Rust version, allowlisted Python and Cargo environments, fresh bytecode/build cache roots, disabled system Git configuration, global Git configuration fixed to `/dev/null`, private mount, PID, `/proc`, network, IPC, and UTS namespaces with asserted distinct identities, a fixed gate hostname, a read-only selected-toolchain bind, hidden `/home`, a protected Landlock allowlist that admits only `/proc/1` and `/proc/sysvipc` from private procfs and omits `/etc` and `/sys`, default ordinary catchable signal dispositions, an empty ordinary signal mask, read-only null standard input, a merged write-only copied-command output pipe with a trusted outer relay, and hard copied-command ceilings of 4 GiB of virtual address space and 600 CPU seconds per process, 512 MiB per file, 1,024 open files, 256 real-user processes, and zero core bytes; the gate uses an unprivileged user namespace where available or a privilege-dropping namespace supervisor otherwise, removes all five capability sets, closes nonstandard descriptors, and runs Cargo locked/offline | Rust formatting, lint, docs, debug and optimized unit/CLI tests, repository invariants, and synthetic/adversarial fixture expectations pass under the executing toolchain and OS without copied-code file contents outside admitted roots, host account/configuration file contents, sysfs kernel/device contents, global procfs kernel/CPU or dynamic self-process content, caller-selected system/global Git configuration, trusted-parent process visibility, inherited host System V IPC objects, the host hostname, inherited blocked `SIGTERM` or ignored `SIGPIPE`, inherited nonstandard descriptors, caller-supplied standard-input bytes, direct access to caller output descriptors, unbounded single-process address-space reservation, unbounded single-process CPU time, unbounded single-file growth, Linux capabilities, privilege-reacquisition permission, or a new external network path | Rust archives, C compiler, Python, shell, Make, kernel, Landlock, namespace and privilege-drop implementations, filesystem, output relay, and host tools are not content-fixed; directory names, gate-launcher PID-1 metadata, private System V IPC tables, and unmediated metadata operations remain visible, libc-reserved signal state remains platform-dependent, copied commands can alter their own signal state after execution, the caller controls and can close or truncate the relay's final output sink, resource ceilings are not aggregate cgroup budgets and do not cap aggregate resident memory, and no signed execution manifest or independently replayable environment is emitted | | Required hosted CI | Repository revision, direct Action SHAs, actionlint archive digest, and zizmor image digest | The configured repository, Markdown, workflow, and workflow-security methods passed in one hosted run | Runner image, Node, Git, Docker host, Action bundles, and service behavior are not fully archived or fixed | | Dependency Review | Event base/head identities and the selected Action revision | GitHub reported no configured dependency-policy violation for its then-current dependency data | Dependency graph/API state and service implementation are external, time-indexed inputs | | External Links | lychee archive digest, link-check flags, and repository locators | The non-excluded endpoints produced accepted responses during the run | Remote content, DNS, TLS, routing, rate limits, and server policy change independently of repository bytes | diff --git a/docs/security/THREAT_MODEL.md b/docs/security/THREAT_MODEL.md index 8ce995d..bf24e3f 100644 --- a/docs/security/THREAT_MODEL.md +++ b/docs/security/THREAT_MODEL.md @@ -409,7 +409,7 @@ required control. Reviews must replace conditional ranks with deployment facts. | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | TM-001 | ADV-002/004/008; TB-002/003; AS-001/002/003/011 | Compromise or unilateral authority changes or erases authoritative decisions, settings, or history. | CTL-001, CTL-007, CTL-008; ruleset `18810248` constrains ordinary Git updates, and public history aids detection. | Preserve the active no-bypass ruleset and exact required checks; test safe direct-update rejection; maintain recovery material and periodic owner access review; never claim independent governance. | Medium-high: one administrator and zero required approvals, despite protected Git updates | High | High | Owner or platform compromise can weaken settings or abuse admin recovery; zero approvals do not create independent review. | Project owner; each rules/access change and quarterly; `open-current` | | TM-002 | ADV-001/002; TB-001/004; AS-003/004 | Attacker-controlled workflow, script, or metadata reaches a privileged runner and steals credentials or changes state. | CTL-005/006/019; current PR jobs declare read-only source access, no configured repository or environment secrets, only a job-scoped read-only `GITHUB_TOKEN`, timeouts, and no `pull_request_target`. Ruleset `18810248` requires the exact app-bound Required CI and Dependency Review contexts. PR #11 Required CI `29292600483`, Dependency Review `29292600471`, and CodeQL `29292598799` succeeded; post-merge Required CI `29292740885`, Workflow Online Audit `29292740874`, External Links `29292740884`, Scorecard `29292740941`, and dynamic CodeQL `29292740478` succeeded at latest recorded subject revision `23352bcde976b86890db28ea4d375a31e6354bca`. | Policy-check every workflow diff; keep privileged jobs off PR events; validate metadata before shell use; require CI and security review on workflow paths. | Low now: demonstrated PR jobs have read-only permissions; reassess every permission or event change | High | Medium | Actions/runner isolation, sole-owner review, query coverage, open Scorecard posture alerts, and future permission edits remain trusted or residual risks. Green runs do not prove vulnerability absence. | CI/Release authority, currently Bootstrap Steward; every workflow change; `open-current` | -| TM-003 | ADV-003; TB-005/012/013; AS-004/009/013 | Compromised action, tool, image, Rust toolchain, registry, archive, or dependency falsifies results or executes malicious code. | CTL-004/010/020; current full-SHA settings, pinned tool identities, no third-party Rust crates, exact required PR contexts, green exact-revision hosted evidence, and a local hardened gate that discards the caller environment, restricts tool resolution, disables system Git configuration, binds global Git configuration to `/dev/null`, validates the captured tree before copied test modules run, revalidates it after them before Rust executes and after Rust checks, and compares NUL-safe non-directory membership plus every tracked file's type, mode, and bytes in all three compiler input roots with a fresh extraction of the identity-checked capture. After initial consistency checks, the authoritative archive and inventory are unlinked behind read-only descriptors that copied Python and Rust processes do not inherit. Every copied command runs with private mount, PID, `/proc`, network, IPC, and UTS namespaces; the latter two remove inherited System V IPC objects and replace the host name with a fixed gate identity. The namespace supervisor bind-mounts the selected toolchain read-only, hides `/home`, drops to the invoking identity with all five capability sets empty and `no_new_privs`, then invokes a protected Landlock launcher that limits file contents to admitted roots, excludes `/etc` host account/configuration files and `/sys` kernel/device state, resets ordinary catchable signal dispositions and the ordinary signal mask, closes all nonstandard descriptors, and fixes hard ceilings for core files, per-process address space and CPU time, per-file growth, open files, and real-user processes. Copied standard input is read-only `/dev/null`, while copied standard output and error share one write-only anonymous pipe relayed by trusted outer `/usr/bin/cat`. | Preserve the minimum allowlist and exact admission records; archive dependencies; verify signatures where available; use locked offline checks and separately provisioned owner rebuilds. | Medium: admitted third-party CI and the Rust toolchain execute code | High | High | Environment, namespace, Landlock, signal-state reset, descriptor-confined capture, standard-stream confinement, resource ceilings, and final exact comparison prevent caller-selected wrappers and system/global Git configuration, inherited blocked `SIGTERM` or ignored `SIGPIPE`, copied-code parent-descriptor traversal, inherited host System V IPC access, host-name observation, `/etc` and `/sys` file-content reads, caller-supplied standard-input bytes, direct access to caller output descriptors, new copied-code network connections, unbounded single-process address-space reservation, unbounded single-process CPU time, unbounded single-file growth, ordinary named-file capture replacement, live-tree drift, added source entries, and unrestored Python-, Rust-test-, or reproducibility-build source drift from silently replacing a passing check. Directory names, private-`/proc` kernel/CPU/process metadata, unmediated metadata operations, other host identity and IPC mechanisms, libc-reserved signal state, copied-code changes to its own signal state, the caller-controlled final relay sink, aggregate process-tree CPU/storage/resident-memory use, and same-user process accounting remain visible or residual; empty-directory additions are immaterial; the caller can close or truncate the final sink; and an admitted child can restore modified extracted state before comparison. The C compiler, protected launcher, output relay, kernel enforcement, passwordless namespace policy, selected account-owned proxy, `cat`, `hostname`, `mount`, `sudo`, `unshare`, `setpriv`, other system tools, copied validator/tests, archive implementation, publisher, registry, runner, host, and solo-admission judgment remain trusted or residual risks. | Project owner; each admission/update and surveillance run; `open-current` | +| TM-003 | ADV-003; TB-005/012/013; AS-004/009/013 | Compromised action, tool, image, Rust toolchain, registry, archive, or dependency falsifies results or executes malicious code. | CTL-004/010/020; current full-SHA settings, pinned tool identities, no third-party Rust crates, exact required PR contexts, green exact-revision hosted evidence, and a local hardened gate that discards the caller environment, restricts tool resolution, disables system Git configuration, binds global Git configuration to `/dev/null`, validates the captured tree before copied test modules run, revalidates it after them before Rust executes and after Rust checks, and compares NUL-safe non-directory membership plus every tracked file's type, mode, and bytes in all three compiler input roots with a fresh extraction of the identity-checked capture. After initial consistency checks, the authoritative archive and inventory are unlinked behind read-only descriptors that copied Python and Rust processes do not inherit. Every copied command runs with private mount, PID, `/proc`, network, IPC, and UTS namespaces; the latter two remove inherited System V IPC objects and replace the host name with a fixed gate identity. The namespace supervisor bind-mounts the selected toolchain read-only, hides `/home`, drops to the invoking identity with all five capability sets empty and `no_new_privs`, then invokes a protected Landlock launcher that limits file contents to admitted roots, admits only the gate launcher's `/proc/1` metadata and private `/proc/sysvipc` tables from procfs, excludes `/etc` host account/configuration files and `/sys` kernel/device state, resets ordinary catchable signal dispositions and the ordinary signal mask, closes all nonstandard descriptors, and fixes hard ceilings for core files, per-process address space and CPU time, per-file growth, open files, and real-user processes. Copied standard input is read-only `/dev/null`, while copied standard output and error share one write-only anonymous pipe relayed by trusted outer `/usr/bin/cat`. | Preserve the minimum allowlist and exact admission records; archive dependencies; verify signatures where available; use locked offline checks and separately provisioned owner rebuilds. | Medium: admitted third-party CI and the Rust toolchain execute code | High | High | Environment, namespace, Landlock, signal-state reset, descriptor-confined capture, standard-stream confinement, resource ceilings, and final exact comparison prevent caller-selected wrappers and system/global Git configuration, inherited blocked `SIGTERM` or ignored `SIGPIPE`, copied-code parent-descriptor traversal, inherited host System V IPC access, host-name observation, `/etc` and `/sys` file-content reads, global procfs kernel/CPU and dynamic self-process reads, caller-supplied standard-input bytes, direct access to caller output descriptors, new copied-code network connections, unbounded single-process address-space reservation, unbounded single-process CPU time, unbounded single-file growth, ordinary named-file capture replacement, live-tree drift, added source entries, and unrestored Python-, Rust-test-, or reproducibility-build source drift from silently replacing a passing check. Directory names, gate-launcher PID-1 metadata, private System V IPC tables, unmediated metadata operations, other host identity and IPC mechanisms, libc-reserved signal state, copied-code changes to its own signal state, the caller-controlled final relay sink, aggregate process-tree CPU/storage/resident-memory use, and same-user process accounting remain visible or residual; empty-directory additions are immaterial; the caller can close or truncate the final sink; and an admitted child can restore modified extracted state before comparison. The C compiler, protected launcher, output relay, kernel enforcement, passwordless namespace policy, selected account-owned proxy, `cat`, `hostname`, `mount`, `sudo`, `unshare`, `setpriv`, other system tools, copied validator/tests, archive implementation, publisher, registry, runner, host, and solo-admission judgment remain trusted or residual risks. | Project owner; each admission/update and surveillance run; `open-current` | | TM-004 | ADV-001/002/008; TB-001/002/004/005; AS-003/004/005/010 | Secret enters source, artifact, log, cache, or untrusted job and is used before revocation. | CTL-003/005/006/017; secret scanning and push protection enabled, minimal workflow permissions, no product keys, and a fail-closed lifecycle/playbook is documented. | Exercise synthetic leak/revocation paths; enable broader scanning if available; keep release/root keys out of GitHub; rotate immediately and treat history deletion as insufficient. | Medium: humans and tooling can leak unsupported patterns | High | High | Detection is not prevention for every secret; account custody and incident execution are not independently verified. | Security authority, currently Bootstrap Steward; every alert/credential event and quarterly; `open-current` | | TM-005 | ADV-004/005; TB-008/009/010/012; AS-006/008/009/011 | Valid evidence is rebound, omitted, downgraded, or confused across source, target, artifact, or claim context. | CTL-009/012 specify explicit subjects, digests, contexts, assumptions, and fail-closed outcomes. | Ratify canonical schemas; bind complete claim closure to exact bytes and versions; check bundle traversal through separate owner-executable paths; add substitution, omission, downgrade, and cross-target negative tests. | Future | High | High | Schema validity cannot prove truthful binding; human standards intent remains an assumption. | Project owner; every schema/claim change and release; `future-blocking` | | TM-006 | ADV-005/007; TB-008/009; AS-006 | Malformed or adversarial proofs exploit unsoundness, parser differential, resource exhaustion, or hidden axioms. | CTL-009/013 target a small deterministic checker, axiom ledger, resource bounds, implementation diversity, and adversarial corpus. | Prove the checked relation sound; fuzz and mutate accepted objects; test malformed/cyclic/oversized inputs; enforce canonical decoding and budgets; record external audit as unavailable. | Future | High | Critical | A shared solo-authored semantic error can survive multiple implementations; the missing independent audit remains explicit. | Project owner; every checker/format/axiom change and release; `future-stop-ship` | diff --git a/policy/README.md b/policy/README.md index 2558a7f..f829d78 100644 --- a/policy/README.md +++ b/policy/README.md @@ -105,8 +105,10 @@ channels are intentionally merged. The fixed host C compiler, relay, `mount`, `unshare`, `sudo`, and `setpriv` implementations, protected launcher, passwordless namespace-setup policy, user-namespace availability, kernel namespace/Landlock enforcement, and unrelated same-account processes outside -the private namespace remain trusted or residual boundaries. The admitted -private `/proc` still exposes kernel, CPU, and process metadata. Resource ceilings +the private namespace remain trusted or residual boundaries. Copied commands +can read only the gate launcher's PID-1 procfs metadata and the private System V +IPC tables; representative global kernel/CPU and dynamic `/proc/self` content is +asserted unreadable. Resource ceilings are not aggregate cgroup budgets: virtual address space and CPU time are limited per process, file size is limited per file, aggregate resident memory is not capped, and the process ceiling includes other processes with the same real user diff --git a/policy/gate0-repository-policy.json b/policy/gate0-repository-policy.json index d11d0df..daf285c 100644 --- a/policy/gate0-repository-policy.json +++ b/policy/gate0-repository-policy.json @@ -1,5 +1,5 @@ { - "policy_version": "0.2.94", + "policy_version": "0.2.95", "repository": "chasebryan/orange", "stage": "solo-bootstrap", "status": "enforced", @@ -252,7 +252,7 @@ "CONTRIBUTING.md": "ee6a23e1c2bca6f86f6a40e2511c4de4c253a77ac2b24d3ae3d975416055b86f", "DEPENDENCY_POLICY.md": "ae5e10534b9081c401d943a55fc85fb2aa4a284cc366129f6139eefdb8389438", "GOVERNANCE.md": "8cbf5da50c63908948d181b1525c86e0f8a554eaa71fc98cf2f0ec47f6776103", - "Makefile": "72178308af81fe3261a6991ecf448cee32ac5e1de13d4493b0324c069243deb8", + "Makefile": "117cd3065e6ee63987da4124101b48a5a5d133d69c8ec0d432e279c70f90d320", "README.md": "82767e5ee4eebabcdaab249a95171d0feae55664d4868c00ca12f103f9382182", "RELEASE_POLICY.md": "f8a3f0fa3494eb28bdd9fc3e6d18ddc8df2fdf63a4c628a5f6c9d72762586e45", "SECURITY.md": "1a801158996153650a2d94a4dbf5043d0a08ce9b96e4aefa9abdcd66344a0ede", @@ -291,13 +291,13 @@ "docs/DECISIONS.md": "5ba13656b29a404aa7ffc047fe1a02df9a60bf43d440912557889bffb5493047", "docs/LANGUAGE_2026.md": "28bcb8741e67adad12c92fa3e0ad8d4b759cf6625333eea5af6dd5a663c014bd", "docs/SEMANTICS_2026.md": "a465aca6c98344fb1271944ce9cdd5af3575afcf295442dfbbd6e05a95182b33", - "docs/operations/CI_DEPENDENCIES.md": "a02be043a15ad8890307e84bc1e4e26edaff8cfb3a83208c7d76368784928139", + "docs/operations/CI_DEPENDENCIES.md": "33e71909283a702dfc89712c0d564f600630a06cc7107097c8c86b6eada6b059", "docs/operations/GITHUB_CONTROLS.md": "f86bdf0234e9db17256f4be07e20e65a9913de45e96e1fdd55e2c57d056ae94b", "docs/security/OSPS_BASELINE.md": "38efd43d1e4e15f335c9189c7cf921b58eb9b15ff8305acb75c7a47ff9fd2d72", "docs/security/SECRETS_AND_INCIDENTS.md": "93332edb737f84c7a3f74f256b5fb603537bf6f524388f62013140cb9906f6a6", - "docs/security/THREAT_MODEL.md": "640c0e56a8e7c44300059655df1702c4897ecf84d64ee25d1e384119b7d0967c", - "policy/README.md": "d68f85d2acb72939188574f2c602e1444f3760aca74c2cb095c42684a2e594bb", - "policy/makefile-entrypoint-contract-v0.1.json": "2295c5b7c5fd59f0080d600961c580344a8bd2bef751df9ff9c51e3eee5d009e", + "docs/security/THREAT_MODEL.md": "0336a69bdfd1e282b3d2ab165de8795415044ca8d7a151a0d682c555ce67c994", + "policy/README.md": "56447c68e2c5fda663b5c5ca5eb7dc5d9c2e492fd16f0cfd2ffedc4945478dd4", + "policy/makefile-entrypoint-contract-v0.1.json": "b19f6a5a262b7bac8491c2091d8e29ecf4ba8b91c021b01161695bd8fb6fb7e3", "schemas/README.md": "39a7b91e15a316c1221cfce5082608eb453f20ea58b5e1c5a0cf32a07a81d774", "schemas/gate0/claim-record-v0.1.schema.json": "a287dde9ddf114da30af61d050aa96406f23e480d62e0f796d66943489579131", "schemas/gate0/evidence-manifest-v0.1.schema.json": "987ba1cddb23aaaf67a1234456fbffde8f80d45678b9671b8df97ad256742efd", @@ -310,7 +310,7 @@ "scripts/ci/install-lychee": "21dd0050173bdd6b461ce7893dc4f77f74dde67b051d76697d6e8ec466b6c41f", "tools/fs_sandbox.c": "80d94ad673b9d3edf628d98e8e7659da93030a56918cc9c2193ede1fdfb76b66", "tools/tests/test_validate_foundation.py": "7a5d34d64733d76e24dc2f188413e5b24af0fd94ff1a1afeb0e2581656da5e92", - "tools/tests/test_validate_foundation_hardening.py": "ec4d15925bc0c50bf45dca007e20192bc0c00b18c81486a79d0d3c32dbe94353" + "tools/tests/test_validate_foundation_hardening.py": "2d1d471fa02bd580b6e605bcc740da8132a2ad66d79770e794560cdced7b1d90" }, "executable_paths": [ "scripts/ci/check-external-links", diff --git a/policy/makefile-entrypoint-contract-v0.1.json b/policy/makefile-entrypoint-contract-v0.1.json index b8ae11d..e307d45 100644 --- a/policy/makefile-entrypoint-contract-v0.1.json +++ b/policy/makefile-entrypoint-contract-v0.1.json @@ -33,7 +33,11 @@ "/usr/bin/rm -- \"$$repro_source_paths_after\"; \\\n\tverify_capture_identity", "/usr/bin/env -i LANG=C LC_ALL=C PATH=/usr/bin:/bin /usr/bin/cc -std=c17 -O2 -D_FORTIFY_SOURCE=3 -fPIE -pie -Wall -Wextra -Werror -pedantic -Wl,-z,relro,-z,now \"$$cargo_home/check-src/tools/fs_sandbox.c\" -o \"$$gate_tools/fs-sandbox\"", "manifest=\"$$cargo_home/check-src/compiler/Cargo.toml\"", - "run_cargo /bin/bash --norc -p -c 'for capability_set in CapInh CapPrm CapEff CapBnd CapAmb; do [[ \"$$(/usr/bin/sed -n \"s/^$${capability_set}:[[:space:]]*//p\" /proc/self/status)\" == 0000000000000000 ]] || exit 1; done; for descriptor in /proc/self/fd/*; do [[ ! -e \"$$descriptor\" || \"$${descriptor##*/}\" =~ ^[012]$$ ]] || exit 1; done; standard_output=\"$$(/usr/bin/readlink -- /proc/$$$$/fd/1)\"; [[ \"$$(/usr/bin/readlink -- /proc/$$$$/fd/0)\" == /dev/null && \"$$standard_output\" =~ ^pipe:\\[[0-9]+\\]$$ && \"$$(/usr/bin/readlink -- /proc/$$$$/fd/2)\" == \"$$standard_output\" ]] || exit 1; for descriptor in 0 1 2; do descriptor_flags=\"$$(/usr/bin/sed -n \"s/^flags:[[:space:]]*//p\" \"/proc/$$$$/fdinfo/$$descriptor\")\"; expected_access=\"$$((descriptor == 0 ? 0 : 1))\"; [[ \"$$((8#$$descriptor_flags & 3))\" == \"$$expected_access\" ]] || exit 1; done; for ipc_table in msg sem shm; do [[ -z \"$$(/usr/bin/sed -n \"2p\" \"/proc/sysvipc/$$ipc_table\")\" ]] || exit 1; done; ! /usr/bin/head -c 1 -- \"$$3/Makefile\" >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /etc/passwd >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /sys/devices/system/cpu/online >/dev/null 2>&1 || exit 1; [[ $$$$ == 1 && $$PPID == 0 && \"$$(/usr/bin/id -u)\" == \"$$1\" && \"$$(/usr/bin/id -g)\" == \"$$2\" && \"$$(/usr/bin/hostname)\" == orange-gate && \"$$(/usr/bin/readlink -- /proc/self/ns/ipc)\" != \"$$6\" && \"$$(/usr/bin/readlink -- /proc/self/ns/uts)\" != \"$$7\" && \"$$HOME\" == \"$$4\" && \"$$PATH\" == \"$$5/toolchain/bin:/usr/bin:/bin\" && \"$$(/usr/bin/sed -n \"s/^NoNewPrivs:[[:space:]]*//p\" /proc/self/status)\" == 1 && -z \"$$(/usr/bin/sed -n \"2p\" /proc/net/route)\" ]]' gate-isolation \"$$gate_uid\" \"$$gate_gid\" \"$$repository_root\" \"$$cargo_home/home\" \"$$gate_tools\" \"$$gate_ipc_namespace\" \"$$gate_uts_namespace\"", + "run_cargo /bin/bash --norc -p -c 'gate_process_root=/proc/1;", + "for descriptor in \"$$gate_process_root\"/fd/*;", + "for hidden_proc_path in /proc/cpuinfo /proc/meminfo /proc/self/status /proc/version;", + "\"$$(/usr/bin/readlink -- \"$$gate_process_root/ns/ipc\")\" != \"$$6\"", + "\"$$(/usr/bin/sed -n \"2p\" \"$$gate_process_root/net/route\")\"", "PYTHONPYCACHEPREFIX=\"$$cargo_home/snapshot-python-cache\"", "run_cargo cargo fmt --manifest-path \"$$manifest\" --all -- --check", "run_cargo cargo clippy --manifest-path \"$$manifest\" --workspace --lib --bins --locked --offline", @@ -79,7 +83,7 @@ "\"$${namespace_runner[@]}\" /bin/true; \\\n\tfi;", "exec 8<&- 9<&-;", "cd -- /;", - "\"$${namespace_runner[@]}\" \\\n\t\t\t\t\"$$gate_tools/fs-sandbox\" \\\n\t\t\t\t--dir / \\\n\t\t\t\t--ro /usr \\\n\t\t\t\t--ro /proc \\\n\t\t\t\t--ro \"$$gate_tools\" \\\n\t\t\t\t--ro \"$$gate_tools/toolchain\" \\\n\t\t\t\t--rw /dev/null \\\n\t\t\t\t--rw /dev/zero \\\n\t\t\t\t--rw /dev/random \\\n\t\t\t\t--rw /dev/urandom \\\n\t\t\t\t--rw \"$$cargo_home\" \\\n\t\t\t\t--rw \"$$repro_home_b\" \\\n\t\t\t\t-- \\\n\t\t\t\t/usr/bin/env -i", + "\"$${namespace_runner[@]}\" \\\n\t\t\t\t\"$$gate_tools/fs-sandbox\" \\\n\t\t\t\t--dir / \\\n\t\t\t\t--ro /usr \\\n\t\t\t\t--ro /proc/1 \\\n\t\t\t\t--ro /proc/sysvipc \\\n\t\t\t\t--ro \"$$gate_tools\" \\\n\t\t\t\t--ro \"$$gate_tools/toolchain\" \\\n\t\t\t\t--rw /dev/null \\\n\t\t\t\t--rw /dev/zero \\\n\t\t\t\t--rw /dev/random \\\n\t\t\t\t--rw /dev/urandom \\\n\t\t\t\t--rw \"$$cargo_home\" \\\n\t\t\t\t--rw \"$$repro_home_b\" \\\n\t\t\t\t-- \\\n\t\t\t\t/usr/bin/env -i", "CARGO_HOME=\"$$cargo_home\"", "CARGO_NET_OFFLINE=true", "CARGO_TARGET_DIR=\"$$cargo_home/target\"", @@ -105,7 +109,11 @@ "repro_source_paths=/proc/self/fd/8", "/usr/bin/env -i LANG=C LC_ALL=C PATH=/usr/bin:/bin /usr/bin/cc -std=c17 -O2 -D_FORTIFY_SOURCE=3 -fPIE -pie -Wall -Wextra -Werror -pedantic -Wl,-z,relro,-z,now \"$$cargo_home/check-src/tools/fs_sandbox.c\" -o \"$$gate_tools/fs-sandbox\"", "[[ -f \"$$gate_tools/fs-sandbox\" && ! -L \"$$gate_tools/fs-sandbox\" && -x \"$$gate_tools/fs-sandbox\" ]]", - "run_cargo /bin/bash --norc -p -c 'for capability_set in CapInh CapPrm CapEff CapBnd CapAmb; do [[ \"$$(/usr/bin/sed -n \"s/^$${capability_set}:[[:space:]]*//p\" /proc/self/status)\" == 0000000000000000 ]] || exit 1; done; for descriptor in /proc/self/fd/*; do [[ ! -e \"$$descriptor\" || \"$${descriptor##*/}\" =~ ^[012]$$ ]] || exit 1; done; standard_output=\"$$(/usr/bin/readlink -- /proc/$$$$/fd/1)\"; [[ \"$$(/usr/bin/readlink -- /proc/$$$$/fd/0)\" == /dev/null && \"$$standard_output\" =~ ^pipe:\\[[0-9]+\\]$$ && \"$$(/usr/bin/readlink -- /proc/$$$$/fd/2)\" == \"$$standard_output\" ]] || exit 1; for descriptor in 0 1 2; do descriptor_flags=\"$$(/usr/bin/sed -n \"s/^flags:[[:space:]]*//p\" \"/proc/$$$$/fdinfo/$$descriptor\")\"; expected_access=\"$$((descriptor == 0 ? 0 : 1))\"; [[ \"$$((8#$$descriptor_flags & 3))\" == \"$$expected_access\" ]] || exit 1; done; for ipc_table in msg sem shm; do [[ -z \"$$(/usr/bin/sed -n \"2p\" \"/proc/sysvipc/$$ipc_table\")\" ]] || exit 1; done; ! /usr/bin/head -c 1 -- \"$$3/Makefile\" >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /etc/passwd >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /sys/devices/system/cpu/online >/dev/null 2>&1 || exit 1; [[ $$$$ == 1 && $$PPID == 0 && \"$$(/usr/bin/id -u)\" == \"$$1\" && \"$$(/usr/bin/id -g)\" == \"$$2\" && \"$$(/usr/bin/hostname)\" == orange-gate && \"$$(/usr/bin/readlink -- /proc/self/ns/ipc)\" != \"$$6\" && \"$$(/usr/bin/readlink -- /proc/self/ns/uts)\" != \"$$7\" && \"$$HOME\" == \"$$4\" && \"$$PATH\" == \"$$5/toolchain/bin:/usr/bin:/bin\" && \"$$(/usr/bin/sed -n \"s/^NoNewPrivs:[[:space:]]*//p\" /proc/self/status)\" == 1 && -z \"$$(/usr/bin/sed -n \"2p\" /proc/net/route)\" ]]' gate-isolation \"$$gate_uid\" \"$$gate_gid\" \"$$repository_root\" \"$$cargo_home/home\" \"$$gate_tools\" \"$$gate_ipc_namespace\" \"$$gate_uts_namespace\"", + "run_cargo /bin/bash --norc -p -c 'gate_process_root=/proc/1;", + "for descriptor in \"$$gate_process_root\"/fd/*;", + "for hidden_proc_path in /proc/cpuinfo /proc/meminfo /proc/self/status /proc/version;", + "\"$$(/usr/bin/readlink -- \"$$gate_process_root/ns/ipc\")\" != \"$$6\"", + "\"$$(/usr/bin/sed -n \"2p\" \"$$gate_process_root/net/route\")\"", "--create --file=\"$$capture_archive_path\"", "--format=gnu --sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner --mode='u+rwX,go+rX,go-w,u-s,g-s,o-t'", "/usr/bin/env -i PATH=/usr/bin:/bin GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 /usr/bin/git -C \"$$repository_root\" ls-files --cached -z > \"$$capture_paths_path\"", diff --git a/tools/tests/test_validate_foundation_hardening.py b/tools/tests/test_validate_foundation_hardening.py index f23df2d..3922c4e 100644 --- a/tools/tests/test_validate_foundation_hardening.py +++ b/tools/tests/test_validate_foundation_hardening.py @@ -3219,6 +3219,16 @@ def test_make_check_must_be_policy_first_serialized_and_environment_isolated(sel "", "make.compiler_environment_contract", ), + ( + "\t\t\t\t--ro /proc/1 \\\n", + "", + "make.compiler_environment_contract", + ), + ( + "\t\t\t\t--ro /proc/sysvipc \\\n", + "", + "make.compiler_environment_contract", + ), ( "\t\t\t\t--rw \"$$cargo_home\" \\\n", "", @@ -3271,8 +3281,8 @@ def test_make_check_must_be_policy_first_serialized_and_environment_isolated(sel "make.compiler_environment_contract", ), ( - "run_cargo /bin/bash --norc -p -c 'for capability_set in CapInh CapPrm CapEff CapBnd CapAmb; do [[ \"$$(/usr/bin/sed -n \"s/^$${capability_set}:[[:space:]]*//p\" /proc/self/status)\" == 0000000000000000 ]] || exit 1; done; for descriptor in /proc/self/fd/*; do [[ ! -e \"$$descriptor\" || \"$${descriptor##*/}\" =~ ^[012]$$ ]] || exit 1; done; standard_output=\"$$(/usr/bin/readlink -- /proc/$$$$/fd/1)\"; [[ \"$$(/usr/bin/readlink -- /proc/$$$$/fd/0)\" == /dev/null && \"$$standard_output\" =~ ^pipe:\\[[0-9]+\\]$$ && \"$$(/usr/bin/readlink -- /proc/$$$$/fd/2)\" == \"$$standard_output\" ]] || exit 1; for descriptor in 0 1 2; do descriptor_flags=\"$$(/usr/bin/sed -n \"s/^flags:[[:space:]]*//p\" \"/proc/$$$$/fdinfo/$$descriptor\")\"; expected_access=\"$$((descriptor == 0 ? 0 : 1))\"; [[ \"$$((8#$$descriptor_flags & 3))\" == \"$$expected_access\" ]] || exit 1; done; for ipc_table in msg sem shm; do [[ -z \"$$(/usr/bin/sed -n \"2p\" \"/proc/sysvipc/$$ipc_table\")\" ]] || exit 1; done; ! /usr/bin/head -c 1 -- \"$$3/Makefile\" >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /etc/passwd >/dev/null 2>&1 || exit 1; ! /usr/bin/head -c 1 -- /sys/devices/system/cpu/online >/dev/null 2>&1 || exit 1; [[ $$$$ == 1 && $$PPID == 0 && \"$$(/usr/bin/id -u)\" == \"$$1\" && \"$$(/usr/bin/id -g)\" == \"$$2\" && \"$$(/usr/bin/hostname)\" == orange-gate && \"$$(/usr/bin/readlink -- /proc/self/ns/ipc)\" != \"$$6\" && \"$$(/usr/bin/readlink -- /proc/self/ns/uts)\" != \"$$7\" && \"$$HOME\" == \"$$4\" && \"$$PATH\" == \"$$5/toolchain/bin:/usr/bin:/bin\" && \"$$(/usr/bin/sed -n \"s/^NoNewPrivs:[[:space:]]*//p\" /proc/self/status)\" == 1 && -z \"$$(/usr/bin/sed -n \"2p\" /proc/net/route)\" ]]' gate-isolation \"$$gate_uid\" \"$$gate_gid\" \"$$repository_root\" \"$$cargo_home/home\" \"$$gate_tools\" \"$$gate_ipc_namespace\" \"$$gate_uts_namespace\"", - "/usr/bin/true", + "for hidden_proc_path in /proc/cpuinfo /proc/meminfo /proc/self/status /proc/version;", + "for hidden_proc_path in /proc/cpuinfo;", "make.compiler_environment_contract", ), ( diff --git a/tools/validate_foundation.py b/tools/validate_foundation.py index 0f3f69f..3081dfd 100755 --- a/tools/validate_foundation.py +++ b/tools/validate_foundation.py @@ -441,7 +441,7 @@ comment_summary_in_pr: never warn_only: false """ -_PHD = "2505d267f3bec16db52c716ccafc364c461733081136a496e5e2ae45d8120c23" +_PHD = "9511c50cc26b96e1eb4e05ca6f11b6c015e8ba3fe40af1a0b50777b14ced981b" _CR = ( "run: /usr/bin/env -u BASH_ENV -u ENV -u GNUMAKEFLAGS -u MAKEFLAGS -u MAKEFILES " "-u MAKEOVERRIDES -u MFLAGS /usr/bin/make --no-builtin-rules --no-builtin-variables check-compiler"