Skip to content

feat(docker): enforce pinned Linux egress allowlists - #132

Merged
senamakel merged 184 commits into
tinybox-27-pinned-egress-planfrom
tinybox-27-linux-egress
Oct 23, 2026
Merged

senamakel merged 184 commits into
tinybox-27-pinned-egress-planfrom
tinybox-27-linux-egress

Conversation

@senamakel

@senamakel senamakel commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Refs #27
Part of tinyhumansai/openhuman#7328

Stacked on PR #131 (tinybox-27-metadata-ip-alias), which carries the earlier TinyBox #27 phases.

Scope

Adds opt-in native Linux Docker Engine allowlist enforcement through NativeLinuxDockerEgressController. The controller resolves and pins every hostname once, creates a per-box bridge, verifies the bridge in the local namespace and both IPv4/IPv6 FORWARD-to-DOCKER-USER hooks, inspects the assigned IPAM subnets, then installs default-drop firewall rules before docker run. It refuses and rolls back if any pinned destination falls inside the new Docker bridge subnet, preventing on-link ARP routing around the intended gateway path.

The filter permits only pinned TCP destination IP/port pairs. It allows established/related return traffic for host-initiated connections, including published-port requests. New traffic from the managed bridge to the Docker host is dropped, except IPv6 Neighbor Solicitation needed for address resolution. Metadata and loopback destinations are refused by the planner and explicit firewall rules; other link-local destinations can be allowed by the pin plan. IPv6 link-local destinations (fe80::/10) are refused as Unsupported because the current pinning model cannot preserve an interface scope ID; IPv4 link-local destinations remain eligible for explicit allowlisting.

Each prepare attempt gets a fresh token stored as a Docker network label and incorporated into its firewall chain names. Cleanup verifies the network owner, scope, and attempt labels and preflights all chain markers before changing firewall state. If a process exits after exclusive chain creation but before writing its marker, a later cleanup can reclaim that chain only when the matching network labels prove its attempt, the chain is empty, and no firewall hook references it. Nonempty or unverified orphan chains are left untouched. Setup rollback tracks only chains this attempt created, so marker-install failure cannot flush another chain. Failed rollback keeps a retryable record; ordinary network-create conflicts do not trigger cleanup of another box's resources. Ambiguous create transport failures are reconciled only when inspected owner, scope, and attempt labels match the current attempt; nonzero Docker command results preserve the original error without reconciliation.

The chain marker uses a length-delimited SHA-256 token over namespace, box ID, and attempt (sha2 is a direct dependency for this purpose). The sandbox applies each controller Docker-argv rewrite once through ControllerHost; run_docker no longer applies it a second time. A non-idempotent test controller pins this contract.

Pinned hostname/IP pairs are added to the container's static /etc/hosts; container DNS points only to its own loopback resolver, with no Docker embedded DNS forwarding fallback. This uses Docker's per-container DNS setting as documented in Docker networking, and addresses the container-network DNS forwarding issue described in Moby GHSA-mq39-4gv4-mvpx. Unlisted names do not resolve through an upstream service.

Docker Desktop, Windows, remote/unknown hosts, rootless daemons, and unavailable or unverifiable firewall support remain Unsupported. Native filter behavior tests are Linux-only; non-Linux targets test refusal before host commands. The default Docker sandbox continues to refuse allowlists without an explicitly installed controller.

Verification

Fake-Host tests cover pinned rule construction and ordering, cleanup ownership and retry, empty orphan recovery and nonempty orphan refusal after restart, bridge-link and firewall-hook verification, marker-install rollback, TCP-only rules, DNS arguments, persisted allowlist destroy refusal, IPv4 and IPv6 subnet-overlap rejection with network rollback, and create-response reconciliation for current-attempt, old-attempt, absent, foreign, malformed, and unavailable inspection results. A non-idempotent controller test confirms exactly one argv rewrite.

The live proof runs only in the hosted Ubuntu Docker job. It checks an allowlisted hostname over IPv4 and probes the resolved IPv6 literal directly, blocks a different raw TCP port, blocks an explicitly allowed host-gateway listener, blocks unlisted DNS and raw UDP/53, verifies a published-port TCP response, and checks that forbidden listeners were not hit. Every successfully created test box is destroyed even if a probe or assertion fails. No firewall-mutating test was run on the shared development host; hosted Ubuntu is the only real firewall proof.

Local validation: formatting, strict workspace Clippy, cargo test --all-features, and cargo doc --no-deps --all-features with RUSTDOCFLAGS=-D warnings pass. Focused egress tests pass (27/27). cargo llvm-cov --package tinybox-docker --lib reports egress_enforcement.rs at 91.75% line coverage (845/921). The full scripts/check-coverage.sh local run hit a Rust 1.99.1 compiler ICE (no resolution for an import) during coverage compilation; the hosted exact-head coverage job will provide the full-workspace result. Windows-target compilation, cargo-deny, and all-target build passed on the immediately preceding implementation head.

The Tokio lockfile is updated to 1.53.3 for RUSTSEC-2026-0386 (fixed starting at 1.53.3).

senamakel and others added 24 commits October 23, 2026 15:01
Add assertions for the execution_location accessor on the local and SSH
hosts, and on the SSH host's inner host, so the new reporting behaviour
is exercised for local, remote, and unknown cases.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a HostExecutionLocation enum and an execution_location method on the Host trait so security-sensitive controllers can determine whether a host runs commands locally or remotely instead of inferring it from the host's registration name. Custom hosts default to Unknown until they explicitly attest to their locality.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Implement execution_location on LocalHost so callers can tell that
commands run on the local machine rather than a remote host.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
SshHost now implements execution_location and returns Remote, so callers can
tell that commands run on the far machine rather than locally.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add tests that verify the egress controller prepares the allowlist before the container starts and cleans up after destroy, and that a controller failure rolls back the box record before it is dropped.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Register the new egress_enforcement module and re-export
DockerEgressController and NativeLinuxDockerEgressController so callers can
enforce egress policy through the native Linux backend.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The Docker sandbox now accepts an optional egress controller that can rewrite
docker argv before execution, letting callers install per-box allowlist
filtering. Without a controller the sandbox keeps refusing
NetworkPolicy::Allowlist as before.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The remove_container helper now passes its argv straight to run_docker instead of wrapping it with docker_argv, since the caller already supplies the full command. This removes a redundant layer of argument construction with no change in behaviour.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a validate_host method to the DockerEgressController trait with a
default no-op implementation, and have the native Linux controller check
that the host is supported, the target is Linux, and the engine and
firewall are available. Also skip forcing the local rootful docker env
prefix when the command already starts with env.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Wrap the host in a ControllerHost that rewrites docker argv through the
egress controller before execution, so observed hosts and sandboxes with a
controller attached get the same egress handling as the primary host.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The docker sandbox now installs an egress filter through the egress
controller when a box requests an allowlist network policy, instead of
refusing the policy outright. The controller validates the host and
prepares the default-drop filter before the container starts, and any
failure during create tears the egress state back down so no filter is
left behind.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat the egress enforcement and sandbox modules to satisfy rustfmt, wrapping long argument lists and error strings and collapsing short ones. No behaviour changes.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The firewall output helper now converts the lossy stdout to an owned value before returning it, so the result no longer borrows from the command output.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Failed container creates now run a shared rollback path that tears down
egress rules and records the attempt for later cleanup when that teardown
itself fails, instead of silently discarding the failure record. Egress
enforcement also installs allowlist rules only on the forward chain and
drops all host-origin traffic on the input chain, so allowlisted
destinations no longer apply to traffic originating from the host.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…identity

Add a test asserting that the native Linux egress controller refuses to
create a sandbox when the host has no explicit local identity, returning
ConstraintNotEnforced for the network constraint without issuing any
host commands.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the test resolver to return std::io::Result so it matches the
EgressResolver trait signature.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the live Docker test imports with BoxId, EgressAllowlist, and the
egress controller types so the suite can exercise network policy
enforcement against a real daemon.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a live test that runs two listeners in a test-owned bridge container and
checks the native firewall controller permits the allowlisted port while
blocking other raw TCP, using a fixed resolver so no public service or Docker
host path is involved. The test tears down the sandbox and listener container
before asserting so failures do not leak resources.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a unit test asserting that a failed egress cleanup keeps the box record
so a later cleanup retry can find it, and extend the live allowlist test to
prove that direct raw TCP to the host gateway is refused while the pinned
destination still connects. The CI workflow now runs the new live test.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The contract and runtime specs now describe the NativeLinuxDockerEgressController, which lets a verified local rootful Linux Docker Engine enforce Allowlist by pinning resolved addresses into default-drop IPv4/IPv6 filters before the workload starts. Other backends and unsupported daemon configurations continue to refuse Allowlist, and the allowlist semantics were reworded to clarify that filtering pins firewall authorization addresses rather than DNS answers.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The native egress controller test now exercises both unknown and remote
execution locations instead of only the default, and the scripted host
gains a configurable execution location so the refusal path is verified
for each case.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat the tinybox-core import list and the host execution location array in the sandbox tests to satisfy rustfmt. No behaviour changes.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The live allowlist test now clears HTTP_PROXY and related environment
variables before running its nc probes, so an inherited proxy setting
cannot redirect the connections and mask the allowlist behaviour under
test.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper

tinysweeper Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Reviewed head: 1923fefc18cb5c2fbae9bca28a4eb9687a8f6c3d

Findings

No active actionable findings.

Resolved this pass

  • Match the drop rule against iptables -S output format
  • Verify the unconditional final DROP, not the owner marker
  • Match the drop rule against iptables -S output format
  • Verify the unconditional final DROP, not the owner marker
  • Verify the unconditional final DROP, not the owner marker
  • Match the drop rule against iptables -S output format
  • Verify the unconditional final DROP, not the owner marker

Verdict: Ready for maintainer review · Priority: none
Coverage: Reviewed 24 of 25 files; not reviewed: Cargo.lock.

Diagnostics and supporting details

Full summary

Pull request purpose (title): feat(docker): enforce pinned Linux egress allowlists. The whole-pull-request change adds an opt-in native Linux Docker Engine egress controller that resolves and pins allowlist destinations once, creates a per-box bridge, verifies the local bridge and IPv4/IPv6 DOCKER-USER/INPUT hooks plus Docker-assigned IPAM subnets, and installs default-drop filters before the workload starts; setup failure rolls back owned resources and retains a retryable record when cleanup also fails. The change also adds a HostExecutionLocation capability to the Host trait so the controller can refuse hosts that do not attest to local execution, normalizes IPv4-mapped IPv6 resolver answers and refuses IPv6 link-local targets, routes every controller Docker command through a single ControllerHost argv rewrite, and updates the contract and runtime specs. The default Docker sandbox still has no controller and refuses allowlists. The latest-push delta is unavailable, so the whole-pull-request diff is described rather than presented as a push delta.

Unreviewed paths

  • Cargo.lock

Review snapshot

Change surface Files Review signal Count
Production 12 Active findings 0
Tests 8 Noted findings 0
Documentation 3 Resolved findings 7
Configuration 1 Pending checks/questions 0

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

Latest-push delta: unavailable. The review state has the prior reviewed head (a7daa81) and rendered evidence but no trusted base-to-head diff comparison, so the whole-pull-request diff is described below rather than presented as a push delta.

Features

  • Added — DockerEgressController trait and NativeLinuxDockerEgressController: Provides an installable controller that validates a local rootful Linux Engine, pins resolver answers, verifies the created network, its IPAM subnets, and the bridge link, rejects pinned destinations inside Docker-assigned subnets, and installs default-drop IPv4/IPv6 filters, failing as ConstraintNotEnforced/Unsupported on unsupported or unverifiable configurations; setup failure triggers native cleanup and returns a distinct rollback operation when cleanup also fails. (crates/tinybox-docker/src/egress_enforcement.rs, crates/tinybox-docker/src/lib.rs pub use oneshot::{)
  • Modified — DockerSandbox allowlist enforcement, argv binding, and rollback: Allowlist boxes run only with an installed controller; the host is wrapped in ControllerHost so every docker command is rewritten by the controller exactly once via docker_argv, network preparation precedes docker run, and failed args, name conflicts, and run failures route through rollback that cleans egress state and retains the record when cleanup fails. Destroy refuses persisted allowlist boxes without a controller before touching Docker. (crates/tinybox-docker/src/sandbox/mod.rs impl Sandbox for DockerSandbox {, crates/tinybox-docker/src/sandbox/mod.rs impl DockerSandbox {, crates/tinybox-docker/src/sandbox/args.rs pub(super) fn run()
  • Added — HostExecutionLocation capability on the Host trait: Hosts state where commands run; LocalHost, LimitedLocalHost, and JailLocalHost report Local, SshHost reports Remote, and custom hosts default to Unknown, letting the native controller refuse hosts that do not attest to local execution. (crates/tinybox-core/src/runtime/mod.rs pub trait Host: std::fmt::Debug + Send + Sync + 'static {, crates/tinybox-host/src/local/mod.rs impl Host for LocalHost {, crates/tinybox-host/src/limited/mod.rs impl Host for LimitedLocalHost {, crates/tinybox-host/src/limited/jailed.rs impl Host for JailLocalHost {, crates/tinybox-ssh/src/host/mod.rs impl Host for SshHost {)
  • Modified — Egress planner answer normalization and IPv6 link-local refusal: Resolver answers given as IPv4-mapped IPv6 are normalized to IPv4 before forbidden-address checks and rule generation, so mapped metadata or loopback addresses are still evaluated and permitted destinations install IPv4 rules; IPv6 link-local destinations are refused as Unsupported because the pinning model cannot preserve an interface scope. (crates/tinybox-docker/src/egress.rs fn normalize_host(host: &str) -> String {, crates/tinybox-docker/src/egress.rs pub fn plan_egress_allowlist(, crates/tinybox-docker/src/egress_tests.rs fn allows_private_addresses_when_the_policy_does_not_forbid_them())
  • Modified — Jailed native pipe registration without unsafe code: The deprecated AsyncFd constructor is retained under an explicit allow because the workspace forbids unsafe code; the owned std::fs::File is moved directly into AsyncFd so its descriptor stays valid until AsyncFd is dropped. (crates/tinybox-host/src/limited/jailed.rs fn async_pipe(file: std::fs::File) -> Result<AsyncPipe> {)
  • Modified — Specification updates for allowlist status: The contract and runtime spec document that Docker enforces Allowlist only with the explicitly installed native controller on a verified local rootful Linux Engine, that pinned addresses rather than DNS answers are authorized, that only container-local loopback is the DNS upstream, and that other backends and daemon configurations continue to refuse. (docs/specs/contract-1-19.md least 19, which `tinybox_bus::is_compatible` already enforces., docs/specs/tinybox-runtime.md composition in M4. ADR 0004 records the tradeoff.)

Tests

  • unit — Uses scripted and fake hosts to cover rollback, record retention, and host-capability forwarding paths, including rollback and attempt-token retention when egress cleanup fails.: Exercises the new sandbox rollback and rewrite contracts; no lane reports execution or passing results. (crates/tinybox-docker/src/sandbox/mod_tests.rs impl Host for ScriptedHost {, crates/tinybox-docker/src/sandbox/mod_tests.rs struct ScriptedHost {)
  • unit — Asserts allowlist run args pin every resolved address with --dns 127.0.0.1 and IPv4/IPv6 --add-host formatting.: Backs the argument-construction claims; execution is not reported by the trusted evidence. (crates/tinybox-docker/src/sandbox/mod_tests.rs use std::sync::{, crates/tinybox-docker/src/sandbox/args.rs pub(super) fn run()
  • unit — Assertions that LocalHost reports Local execution location, that LimitedLocalHost reports Local, that JailLocalHost attests Local, and that SshHost reports Remote while its inner recording host defaults to Unknown.: Directly tests the new locality capability on each shipped host implementation; execution is not reported by the trusted evidence. (crates/tinybox-host/src/local/mod_tests.rs fn it_reports_its_name() {, crates/tinybox-host/src/limited/jailed_tests.rs impl tinybox_jail::JailBackend for FixtureBackend {, crates/tinybox-ssh/src/host/mod_tests.rs fn last_argv(inner: &RecordingHost) -> Vec<String> {)
  • unit — A fake native host models the Docker and firewall command surface to exercise default-drop verification, including the case where the owner-marker DROP exists but the expected final -A <chain> -j DROP is missing.: The answered tests lane reports this pins the default-drop verification and resolves the earlier disputed finding; execution is not reported by the trusted evidence. (crates/tinybox-docker/src/egress_enforcement_tests.rs, crates/tinybox-docker/src/egress_enforcement.rs)

Before merge

None.

How this fits together

flowchart LR
  n0["PinnedEgressTarget<br/>changed"]:::changed
  n1["normalize_host<br/>changed"]:::changed
  n2["plan_egress_allowlist<br/>changed"]:::changed
  n3["...sses_when_the_policy_does_not_forbid_them<br/>changed"]:::changed
  n4["io"]:::impacted
  n5["StaticResolver"]:::impacted
  n6["EgressResolver"]:::impacted
  n7["addresses"]:::impacted
  n8["clone"]:::impacted
  n2 -->|uses| n0
  n2 -->|calls| n1
  n2 -->|uses| n6
  n2 -->|calls| n8
  n3 -->|calls| n2
  n3 -->|tests| n2
  n3 -->|calls| n7
  n3 -->|tests| n7
  n5 -->|implements| n6
  n6 -->|uses| n4
  n7 -->|calls| n5
  n7 -->|uses| n5
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The answered critique lane reports the change is small and self-consistent, with the async_pipe descriptor-lifetime claim holding and the scoped #[allow(deprecated)], and that JailLocalHost now attests HostExecutionLocation::Local matching the trait default's documented contract.
  • Lane summary: The file is a test-only addition (a sibling `#[cfg(test)] #[path]` suite for `egress_enforcement`), and its fakes are only ever exercised on the paths those tests drive. The two earlier findings do not survive inspection of the current code: the fake's `-S` branch emits exactly the `-A FORWARD -j DOCKER-USER` / `-A FORWARD -j DOCKER-FORWARD` lines that `validate_host` looks for, and the `missing_final_drop` branch regenerates the `git ls-files`-equivalent chain dump without the unconditional trailing `-A <chain> -j DROP` that `install_family` verifies, so the final-DROP check is genuinely what fails that test. I found no defect in the new test file worth blocking on; the two prior concerns are dropped.

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The answered security lane reports no untrusted input, auth, or process-boundary change is introduced and finds no defects in its group; it notes the earlier DROP-rule and owner-marker verification concerns are now covered.
  • Lane summary: This file adds test-only fakes and assertions for the native Docker egress controller; it introduces no production code paths, no new network or subprocess behaviour, and no untrusted input reaching a sink. The two earlier findings are not raised again: the drop-rule matching concern was disputed and no current-head evidence establishes it, and the unconditional-final-DROP concern is now directly covered by the fake host's `missing_final_drop` path and the `owner_marker_drop_does_not_satisfy_default_drop_verification` test. Nothing reportable remains.

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The answered tests lane reports the final-DROP verification is anchored on the expected literal -A <chain> -j DROP as the last rule in each custom chain, resolving the earlier high finding about the owner marker satisfying default-drop verification and folding in the earlier iptables -S parsing finding.
  • Lane summary: The prior disputed finding is resolved by current-head evidence: `install_family` now checks the last rule of each custom chain against the exact string `-A {chain} -j DROP` rather than accepting the owner marker's `-j DROP`, and `PreparedNativeHost` plus the new test `owner_marker_drop_does_not_satisfy_default_drop_verification` exercise that distinction by emitting the marker line while omitting the final drop. The other prior finding is not re-raised, since I cannot establish from this diff that the cleanup-path ownership scan misparses `iptables -S` output. I found no new reportable defect in the added enforcement code or its tests. (1 earlier finding(s) still open)

commits

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The answered commits lane reports no sensitive content in committed files, and notes two earlier findings still open.
  • Lane summary: No sensitive content in committed files. (2 earlier finding(s) still open)

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The answered description lane reports the PR description covers the egress controller, the argv-rewrite fix, and the live proof, and that the drop-rule/hook verification logic quoted in the diff is consistent with iptables -S output.
  • Lane summary: This revision adds the previously missing test module for the native Docker egress controller and the new fake-host coverage for ownership reconciliation, subnet-overlap rollback, and default-drop verification. The two concerns I raised before are now addressed on the current head: the drop check parses `iptables -S` output as written (`-A <chain> -j DROP`) instead of the arptables-style form, and the new `owner_marker_drop_does_not_satisfy_default_drop_verification` test pins the behaviour that the comment-bearing owner marker cannot stand in for the unconditional final DROP. I found no new defect that this diff introduces.
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.019452
  • Tokens: 771228 input · 5543 output · 218752 cached · 799 embedding
  • Updated: 2026-10-23T18:36:42Z
  • Continuity: summary cache chain restarted at the storage ceiling.
Head State Pass summary
234e47bae758 ready for maintainer review 0 active finding(s), 5 resolved finding(s) (at 2026-10-23T17:58:41Z)
68ce7422a0a5 ready for maintainer review 0 active finding(s), 5 resolved finding(s) (at 2026-10-23T18:00:16Z)
fd6b8912156f ready for maintainer review 0 active finding(s), 8 resolved finding(s) (at 2026-10-23T18:25:46Z)
a7daa817d178 ready for maintainer review 0 active finding(s), 5 resolved finding(s) (at 2026-10-23T18:34:23Z)
1923fefc18cb ready for maintainer review 0 active finding(s), 7 resolved finding(s) (at 2026-10-23T18:36:42Z)

tinysweeper 0.1.2

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d28329fd-fe45-4df3-8d39-19e9039c0135

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T18:39:46.875194Z 1923fef New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

Diagnostics

Total spend: $0.0024

Comment thread crates/tinybox-docker/src/egress_enforcement.rs Outdated
@tinysweeper tinysweeper Bot added the priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. label Oct 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7be9921765

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tinybox-docker/src/egress_enforcement.rs Outdated
Comment thread crates/tinybox-docker/src/egress_enforcement.rs Outdated
Comment thread crates/tinybox-docker/src/sandbox/mod.rs
senamakel and others added 21 commits October 23, 2026 18:20
The cleanup path now always attempts to remove the Docker network instead of
guarding the removal behind a prior existence check, since the removal command
already tolerates a missing network. The Uuid import is also gated behind
cfg(not(test)) to avoid an unused import warning in test builds.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…cases

The single atomic-chain test was replaced by two focused tests: one
asserting that cleanup reclaims an empty attempt chain after a restart,
and one asserting that cleanup refuses a nonempty unmarked chain and
issues no delete, flush, or chain-removal commands.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…reates

The ownership check used by ambiguous network create reconciliation now also
compares the attempt label, so a network left behind by an earlier create
attempt for the same box is no longer mistaken for this attempt's network.
Inspection and verification both parse the extra label and treat a mismatch
as a foreign network to leave alone.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a Linux-only test asserting that when a network create fails after
its side effect, cleanup inspects the attempt label and leaves a network
belonging to an older attempt untouched instead of removing it.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The attempt-label assertion now spells out the full label string instead of
relying on the shared constant, and two prepared native hosts are bound as
mutable so the tests can adjust their state.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Two test bindings had the wrong mutability, so the host is now declared mutable where its fields are assigned and immutable where they are not.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Two test bindings had the wrong mutability, so the first now declares the
prepared host as mutable and the second no longer does. This keeps the tests
compiling without changing what they exercise.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The README now documents that each attempt gets a fresh random token stored on
the Docker network and folded into chain names, and that chains are created
exclusively before receiving their owner marker. It also explains the
reclaim rules for unmarked chains after a restart and that ambiguous orphans
fail closed.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The fake host now records which chains have rules added and reports
missing rules for -C checks, so the egress enforcement tests can
exercise the rule verification path.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test host now resolves the iptables or ip6tables binary once per
invocation and reuses it for the marked-chain and chains-with-rules
bookkeeping, instead of re-deriving it inline at each call site. A
chains_with_rules removal was also added to the -D path so deletions
stay consistent with insertions.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test host now clones the firewall identifier for the marked-chains
removal and reuses the original value for the chains-with-rules removal,
instead of re-deriving it from argv. This keeps both removals keyed on the
same firewall value.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the egress enforcement module and its tests to satisfy rustfmt, wrapping long call chains and assertions and collapsing short conditions onto single lines. No behaviour changed.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The firewall binary lookup was computed twice in a row, so the first
binding was dropped and the second reused. The remaining binding is now
cloned at the insertion site to keep the value available.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The cleanup test now also records the forward and input chains as having
rules and carrying the owner marker, so the refusal path is exercised
against chains that look genuinely owned by another instance.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Rename the test to make clear it covers rollback of both the created
chain and the network when the owner marker install fails.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a Linux-only test asserting that cleanup succeeds and issues only the
two expected chain-existence probes when the box network is already absent
and no matching hooks remain, without running any delete, flush, or remove
commands.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The cleanup test asserted exactly two `-S` invocations, which is brittle
against extra read-only listing calls. It now checks that at least two
occur, keeping the intent while tolerating additional chain inspections.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test that makes the owned network removal fail after an ambiguous
create, verifying the cleanup error is retained and the network is
inspected twice.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a fault injection hook to the prepared native host so a single
`docker network rm` invocation can be made to fail, letting tests
exercise the cleanup path when network removal errors out.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The ambiguous create rollback test now binds the prepare result and includes
it along with the recorded commands in the assertion message, so a failure
shows what the controller actually returned instead of only the mismatch.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The ambiguous create test now asserts on the simulated lost command
response message instead of the owned egress network rollback failure,
so the assertion matches the error the test actually injects.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. labels Oct 23, 2026
senamakel and others added 3 commits October 23, 2026 18:35
The test now compiles the network-removal assertion only on Linux, since the
behaviour it exercises is platform-specific and broke builds elsewhere.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The no_matching_hooks field and its initializer are now behind a
cfg(target_os = "linux") attribute, matching the other Linux-only fields in
PreparedNativeHost. This avoids an unused-field warning on other platforms.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper tinysweeper Bot added priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. and removed priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. labels Oct 23, 2026
@senamakel
senamakel merged commit b9a5a70 into tinybox-27-pinned-egress-plan Oct 23, 2026
34 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant