Skip to content

test(sandbox): phase 5 plan matrix, fail-closed untrusted tiers - #15

Merged
senamakel merged 3 commits into
mainfrom
security-bus-7328-p5
Oct 14, 2026
Merged

senamakel merged 3 commits into
mainfrom
security-bus-7328-p5

Conversation

@senamakel

@senamakel senamakel commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Part of tinyhumansai/openhuman#7328

Pins the sandbox planner's fail-closed behaviour with an exhaustive matrix (4 trust tiers x SaaS/local x env/agent/config switches x 5 backends x 4 isolation levels): untrusted tiers and SaaS never get a Noop, Process or None plan, OPENHUMAN_SANDBOX=off cannot disable isolation under SaaS, host network always requires approval, and the credential-store floor (.ssh/.aws/.gnupg) is excluded from every grant even when a ceiling or trusted root names it. Documents that Firejail/Bubblewrap map to the namespace backend. No behaviour change; the planner already enforced these.

Summary by CodeRabbit

  • Documentation

    • Clarified how legacy sandbox settings map to current backend options and when enforced kernel isolation is planned.
  • Tests

    • Added coverage for sandbox plans, trust and deployment combinations, network approvals, credential-store access, and isolation enforcement.

…lation, host-network approval and credential floor

Part of tinyhumansai/openhuman#7328 (phase 5). Documents that Firejail and
Bubblewrap map to the namespace backend.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

  • Run on-demand review

This review includes 1 billable file and costs up to $0.25.

Or wait 11 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0b9c7d00-220e-40aa-aea3-66441a2c51d9
📥 Commits

Reviewing files that changed from the base of the PR and between b81dc1a and a2986ed.

⛔ Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing: 1
  • crates/tinysecurity-sandbox/src/engine.rs

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f9f39fb0-68f3-4232-b5df-5968417dc992
📥 Commits

Reviewing files that changed from the base of the PR and between 9fcb6e7 and b81dc1a.

📒 Files selected for processing: 3
  • crates/tinysecurity-bus/src/future.rs
  • crates/tinysecurity-sandbox/src/engine.rs
  • crates/tinysecurity-sandbox/src/plan_matrix_tests.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The pull request updates SandboxBackend documentation and adds sandbox plan-matrix tests. The tests cover isolation and backend combinations, SaaS behavior, host-network requests, and credential-store grants.

Changes: Sandbox planning

Layer / File(s) Summary
Plan contract and matrix tests
crates/tinysecurity-bus/src/future.rs, crates/tinysecurity-sandbox/src/engine.rs, crates/tinysecurity-sandbox/src/plan_matrix_tests.rs
The comments map legacy firejail and bubblewrap settings to Namespace and describe when Jail reports enforced kernel isolation. New tests check plan outcomes across trust, deployment, sandbox settings, backends, and isolation levels. Additional tests check SaaS behavior, host-network approval denials, and credential-store grant denials.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Other

Merge Risk: ⚪ Minimal · up to b81dc

This change adds sandbox planner test coverage and clarifies documentation, with no production behavior change. No actionable merge-blocking risk was identified.

Pre-merge checks: Passed 5
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the main change: adding the sandbox phase 5 plan matrix tests and validating fail-closed behavior for untrusted tiers. It is concise and specific.
Docstring Coverage Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 3 files.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

A rabbit checks each sandbox plan,
Through trust and settings, line by line.
Kernel levels pass the test,
Denials keep the limits set.
Then hops away, content with the design.

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

@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: b81dc1a7a1

ℹ️ 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 on lines +541 to +543
#[cfg(test)]
#[path = "plan_matrix_tests.rs"]
mod plan_matrix_tests;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run rustfmt after adding the matrix module

The required cargo fmt --all -- --check command exits with status 1 here because rustfmt expects plan_matrix_tests before tests. Since CI runs this exact formatting check, the commit cannot pass validation until the declarations are formatted.

AGENTS.md reference: AGENTS.md:L25-L35

Useful? React with 👍 / 👎.

Comment on lines +1 to +2
//! Exhaustive plan matrix: trust x deployment x switches x backend x isolation.
use super::*;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Start the sibling test file with the parent import

This new sibling test file begins with a module doc comment rather than use super::*;, contrary to the repository's explicit layout requirement for every *_tests.rs file. Move the import to the first line so the test module follows the mandated structure.

AGENTS.md reference: AGENTS.md:L117-L125

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 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-14T14:30:34.835189Z a2986ed 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 commented Oct 14, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 5 lane(s) and found 0 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below.

State: Ready for maintainer review
Priority: none
Reviewed head: a2986edf8dd6
Updated: 2026-10-14T15:05:04Z

Review snapshot

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

Completeness: Complete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

Features

None identified with supported citations.

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

No active actionable findings.

Before merge

None.

How this fits together

flowchart LR
  n0["supports<br/>changed"]:::changed
  n1["resolve"]:::impacted
  n2["plan"]:::impacted
  n3["SandboxArtifact2"]:::impacted
  n4["validate"]:::impacted
  n5["SandboxPlanRequest2"]:::impacted
  n6["select"]:::impacted
  n1 -->|uses| n3
  n1 -->|uses| n5
  n1 -->|calls| n6
  n2 -->|uses| n5
  n4 -->|calls| n0
  n4 -->|calls| n1
  n4 -->|uses| n3
  n4 -->|uses| n5
  n6 -->|calls| n0
  n6 -->|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
  • Lane summary: The change factors the isolation check into `suitable_for_untrusted`, which is behavior-preserving: the new `backend != Noop` term is redundant with the existing `e.backend != SandboxBackend::Noop` guard in `select` and with `executor.backend == SandboxBackend::Noop` in `validate`, so no previously allowed plan becomes denied and none become allowed. The new matrix tests are pure additions and the two `#[cfg(test)]` attributes on the test module declarations are harmless (one is redundant). I found no defect in these three files.

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change factors the isolation check into `suitable_for_untrusted` and adds an exhaustive plan matrix test. Reviewing `select` and `validate`: `suitable_for_untrusted` adds `backend != Noop` on top of the previous `matches!(e.isolation, Kernel | MicroVm)`, so it is strictly more restrictive than the check it replaces in `select` (which already required `e.backend != SandboxBackend::Noop` on the following line) — no loosening. In `validate`, the substituted condition sits inside `executor.backend == SandboxBackend::Noop || ...`, so adding the Noop term there is redundant but not a behaviour change or a bypass. No untrusted input reaches a new sink, no CI or dependency changes, and I found no repository-rule violation.

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change is a behaviour-preserving refactor: `suitable_for_untrusted` folds together the `backend != Noop` check that both callers already made, so `select` and `validate` reject exactly the same executors as before. The new `plan_matrix_tests.rs` does exercise the isolation gate it touches (trust × deployment × switches × backend × isolation, with a local re-derivation of the expected verdict), so a loosened gate would fail it. One new rustdoc comment, however, states a restriction the planner does not apply; it is reported below.

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change extracts the isolation predicate into `suitable_for_untrusted` (behaviour-preserving: the original expression already required `isolation` to be Kernel or MicroVm, and the new helper adds a `backend != Noop` conjunct that both call sites already enforce on the very next/previous condition), adds an exhaustive 4-trust × 2-deployment × 8-switch × 5-backend × 4-isolation plan matrix plus network, credential-floor and SaaS-off tests, and documents the Namespace/Jail backend mapping. The tests exercise only the public planner API and the description accurately says there is no behaviour change. I found no defect worth reporting.
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.001830
  • Tokens: 314435 input · 23685 output · 27776 cached · 389 embedding
Head State Pass summary
a2986edf8dd6 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-14T15:05:04Z)

tinysweeper 0.1.2

@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: 50619267df

ℹ️ 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 on lines +91 to +95
assert_eq!(
response.denial,
Some(SandboxDenial2::Unsupported),
"{label}"
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Assert the deny verdict in the failure branch

For isolation-required combinations using Noop, None, or Process, this branch checks only the absent artifact and denial metadata, not response.decision.verdict. A regression in denied() that emits Verdict::Allow while retaining those fields would therefore leave the new fail-closed matrix green even though the public authorization decision permits the operation; assert the corresponding Verdict::Deny and reason here.

AGENTS.md reference: AGENTS.md:L18-L20

Useful? React with 👍 / 👎.

Comment on lines +68 to +69
/// Path-jail executor. It is planned only when it reports enforced kernel
/// isolation, so an untrusted tier never receives a best-effort jail.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Qualify the jail isolation guarantee

This public documentation promises that every Jail plan reports kernel isolation, but Engine::resolve requests kernel isolation only when isolation is mandatory or enabled, and the new matrix explicitly expects trusted-local/all-disabled Jail executors reporting None or Process to be allowed. Either qualify this guarantee as applying only to isolation-required plans or enforce it for every Jail selection so the contract matches planner behavior.

AGENTS.md reference: AGENTS.md:L162-L163

Useful? React with 👍 / 👎.

…d validate

Gives the planner the issue's is_suitable_for_untrusted_code check in one
place so Noop and non-kernel executors can never back an untrusted or SaaS plan.

Co-authored-by: Medulla <medulla@tinyhumans.ai>

@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: a2986edf8d

ℹ️ 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".

} else {
SandboxDeployment2::Local
};
scope.origin = if saas { Origin::Saas } else { Origin::Operator };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Vary SaaS origin independently of deployment

This assigns origin from the same saas flag as deployment, so the matrix never covers deployment == Local with origin == SaaS (or the reverse), even though Engine::resolve treats them as independent mandatory-isolation predicates. If the facts.origin == Origin::Saas check were removed, a trusted local-deployment scope with SaaS origin, disabled switches, and process-only isolation could be allowed while this purportedly exhaustive fail-closed suite still passed; add origin as an independent matrix dimension.

Useful? React with 👍 / 👎.

@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.

tinysweeper found nothing blocking. Approving.

             $0.0018 · 314,435 in / 23,685 out · 27,776 cached (9%) · ladder/vectors-oai3, deepseek-v4.1-flash · 389 embedded
critique:    $0.0002 · 150,836 in / 1,875 out  · 12,288 cached (8%) · deepseek-v4.1-flash
security:    $0.0005 · 71,450 in  / 6,151 out  · 5,376 cached (8%)  · deepseek-v4.1-flash
tests:       $0.0010 · 56,581 in  / 13,032 out · 4,352 cached (8%)  · deepseek-v4.1-flash
description: $0.0001 · 30,586 in  / 922 out    · 5,760 cached (19%) · deepseek-v4.1-flash

@senamakel
senamakel merged commit 624ab6f into main Oct 14, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant