Skip to content

fix(sandbox): run the macOS desktop sandbox confined to the selected folder - #7824

Merged
M3gA-Mind merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/gh-7819-macos-sandbox-folder-confined
Oct 15, 2026
Merged

M3gA-Mind merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/gh-7819-macos-sandbox-folder-confined

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • With sandbox mode on, macOS runs shell commands inside the selected folder again. The folder is confined by Seatbelt: writes are allowed only there and in the toolchain grants.
  • The isolation decision is now one pure function, jail_isolation. It keeps the Kernel floor for an explicitly requested Landlock jail and for SaaS tenants.
  • Sandbox status reports which posture applies: isolation: "isolated" or "folder_confined".

Owner review needed (@senamakel, sandbox owner). This reverses the 10-11 isolation floor (99626d9, continued in a24da6f / f08eac8) for the local desktop sandbox on macOS only. Seatbelt gives filesystem write confinement and no process isolation, and the status says so. Untrusted-code paths keep the floor. Please confirm this is the intended product contract ("sandbox mode = the agent works inside the selected folder").

Problem

On macOS, sandbox mode refused every shell command with TinyBox native jail cannot meet the required isolation floor, even ls in the selected folder (#7819):

  • native_jail.rs gated status, require_backend and execute_owned on suitable_for_untrusted_code, which means Kernel isolation.
  • TinyBox rates Seatbelt IsolationLevel::None (tinybox-jail/src/macos.rs).

The gate conflated "confine to the selected folder" with "isolate untrusted code".

Solution

  • jail_isolation(backend, available, suitable_for_untrusted_code, untrusted) returns:
    • None when the jail is unavailable
    • Isolated when it meets the Kernel floor
    • FolderConfined for seatbelt when the caller is not untrusted
    • None in every other case (noop, Landlock below the floor, Seatbelt for untrusted code)
  • Callers of the gate:
    • status passes untrusted = is_saas().
    • require_backend (explicit Landlock only) passes untrusted = true.
    • execute_owned passes untrusted = explicitly_requested || is_saas().
  • What TinyBox is asked for:
    • Isolated is unchanged: Enforced, require_process_isolation: true.
    • FolderConfined asks for Declared and require_process_isolation: false. That is what Seatbelt can give: it declares Filesystem, Network and Subprocess as BestEffort, and TinyBox's admission refuses Enforced or a process-isolation demand for it. Declared still refuses an Unsupported constraint.
  • Writable grant: the selected folder (policy.workspace_root) is the jail root. The Seatbelt profile allows writes only under the root, the TinyBox-resolved read-write grants, the per-call scratch dir, /private/tmp and /dev/null. Reads are allow-default, so the toolchain works.
  • Status: SandboxBackendHandle.isolation: Option<SandboxIsolation>, skipped when None, so other handles serialize unchanged.

Network decision

  • Intended policy: resolve_sandbox_policy sets allow_network = !is_remote_session for Sandboxed mode. The local desktop therefore has network, and remote sessions do not.
  • What enforces it: the jail's allow_net follows that policy. It is what Seatbelt enforces: (deny network*) is added only when it is false.
  • The old setting: run.isolate_network = true only set the passthrough resource's declared NetworkPolicy, which the passthrough backend never reads. It is now !policy.allow_network, so the declaration matches what is enforced.
  • Net effect: npm install and git push work in the local sandbox, and remote sessions still deny network.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy: native_jail_tests.rs covers each backend/isolation combination. Seatbelt is accepted for folder confinement and refused for untrusted code (explicit Landlock or SaaS). Kernel isolation is accepted for every caller. A missing launcher, the noop backend and Landlock below the floor are refused. The status mapping and both jail_request postures are also tested.
  • Diff coverage ≥ 80%: the new decision function and request mapping are unit-tested. The status/execute_owned call sites need a live TinyBox module.
  • (N/A) Coverage matrix: no matrix rows change
  • (N/A) Affected feature IDs: no matrix rows change
  • No new external network dependencies introduced
  • Manual smoke checklist: verified live in the desktop app on macOS (see Impact)
  • Linked issue: Closes Sandbox mode on macOS refuses every shell command, even inside the selected folder #7819

Impact

  • Who it affects: macOS desktop users with sandbox mode on. Linux (Landlock, Kernel) and Docker are unchanged.
  • Live verification (desktop app, macOS, sandbox on, built from this commit on a dev branch):
    • In the selected folder the shell ran pwd, git, gh, curl and git init. curl exercises the network decision.
    • Sandbox status reports backend seatbelt with isolation: folder_confined.
    • Denial outside the folder: not yet verified live.
  • Known limitation (not fixed here): folder_confined does not confine reads. Seatbelt is allow-default for reads, so credential stores stay readable and, with network on, could be sent out. The pinned TinyBox contract cannot deny reads. That capability is feat(jail): deny reads of host-chosen paths in native jails (contract 1.20) tinybox#87 (deny_read, contract 1.20). After its release and repin, a follow-up fills deny_read from the is_always_forbidden set. The sandbox README states this limitation.
  • Risk: the released TinyBox module must accept Declared with require_process_isolation: false, as the vendored source at the gitlink does. The live run above shows that it does.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/gh-7819-macos-sandbox-folder-confined
  • Commit SHA: 1922647 (fix 703908d + README)

Validation Run

  • (N/A) pnpm --filter openhuman-app format:check: no frontend files
  • (N/A) pnpm typecheck: no frontend files
  • Focused tests: not run locally. CI runs the sandbox unit tests. The app build of the same commit compiled and ran.
  • Rust fmt/check (if changed): rustfmt --check on the changed files
  • (N/A) Tauri fmt/check: no Tauri changes

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: the macOS local sandbox runs commands confined to the selected folder instead of refusing them, and sandbox status reports the isolation posture.
  • User-visible effect: shell, git and npm work in sandbox mode on macOS.

Parity Contract

  • Legacy behavior preserved: the Kernel floor for explicit Landlock and SaaS. Linux and Docker are unchanged.
  • Guard/fallback/dispatch parity checks: an unavailable or noop jail is still refused, and there is no fallback to unconfined execution.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this one
  • Resolution (closed/superseded/updated): N/A

Summary by CodeRabbit

  • New Features
    • Sandbox status now reports the available isolation level, distinguishing process isolation from folder-confined access.
    • Trusted local runs can use folder confinement when stronger isolation is unavailable. Explicitly requested and SaaS runs continue to require process isolation.
  • Bug Fixes
    • Network isolation now follows the configured network policy.
    • Folder-confined runs no longer require process isolation; runs with process isolation retain enforced isolation requirements.
    • Local sandbox status is inactive when no supported isolation level is available.

@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: Incomplete
Priority: none
Reviewed head: 39c890a3de65
Updated: 2026-10-15T08:32:57Z

Review snapshot

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

Completeness: Incomplete
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.

Could not review: crates/openhuman-core/src/sandbox/ops.rs

Before merge

  • Complete the security review for crates/openhuman-core/src/sandbox/ops.rs.

How this fits together

flowchart LR
  n0["approved_environment<br/>changed"]:::changed
  n1["execute_owned<br/>changed"]:::changed
  n2["jail_request<br/>changed"]:::changed
  n3["...preserves_grants_and_requires_enforcement<br/>changed"]:::changed
  n4["Result"]:::impacted
  n5["SandboxPolicy"]:::impacted
  n6["execute_in_sandbox_metered"]:::impacted
  n7["execute_in_sandbox"]:::impacted
  n8["resolve_command_policy"]:::impacted
  n9["load_config"]:::impacted
  n0 -->|uses| n4
  n1 -->|calls| n0
  n1 -->|calls| n2
  n1 -->|uses| n4
  n1 -->|uses| n5
  n2 -->|uses| n4
  n2 -->|uses| n5
  n3 -->|uses| n4
  n6 -->|uses| n4
  n6 -->|uses| n5
  n6 -->|calls| n9
  n7 -->|uses| n4
  n7 -->|uses| n5
  n7 -->|calls| n6
  n8 -->|uses| n4
  n8 -->|uses| n5
  n8 -->|calls| n9
  n9 -->|uses| n4
  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: Reviewed 8 files; 0 findings. _The code index is behind this pull request (indexed at `3a4514512e94`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: incomplete; unanswered: crates/openhuman-core/src/sandbox/ops.rs
  • Lane summary: Reviewed 6 files; 0 findings. 1 file could not be reviewed: crates/openhuman-core/src/sandbox/ops.rs. 1 file was not security-reviewed: crates/openhuman-core/src/sandbox/README.md (prose or tabular data). _The code index is behind this pull request (indexed at `3a4514512e94`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: _The code index is behind this pull request (indexed at `3a4514512e94`), so retrieved context may be out of date._

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: I have the diff and description. Two claims I cannot verify from the diff alone, and which the verdict depends on: (1) the `contract::NativeJailConfig` shape and whether `JailEnforcement::Declared` exists, so that `jail_request` compiles as written; (2) the unchanged `execute_owned` prologue, to confirm `explicitly_requested` is the same explicit-Landlock request `require_backend` passes `untrusted = true` for, and to see what `status`'s old callers look like. I will also check the actual repository layout to know which crate owns these paths and whether the sandbox module could be compiled without the `modules` feature in a way that breaks the build of other crates that use `SandboxIsolation`. Then I will report on whether the description matches the code and any defects in the code itself. I expect to find that the description matches; the notable risk is a mismatch between the description's 'local desktop sandbox' framing and code that accepts Seatbelt for any non-SaaS caller, or a compile break from using `isolation` in `status` for other backends. I will settle that with the lookups if the evidence cooperates. If nothing concrete appears, I will say so and report no findings. I will not report the PR's stated unverified live checks as code defects unless the code shows a reachable failure. I also need to see `policy.allow_network`'s producer `resolve_sandbox_policy` to check the description's claim that it is `!is_remote_session`, since `run.isolate_network = !policy.allow_network` is a deliberate behavioural change the description stakes its network decision on; a mismatch there would be a description/behaviour gap the diff alone cannot show. One look each settles these, plus a listing for paths I may have guessed wrong. The `#[path]` declaration for the test file is also worth confirming against the repository rule that tests are sibling `#[cfg(test)]` and `#[path]` declared; the diff does not show `native_jail.rs`'s test declaration, so I cannot confirm it from here. I will include a lookup for that, and to read the unchanged part of `native_jail.rs` around the change to see whether `execute_owned`'s callers changed in a way the description does not mention. If the layout differs from my assumption I will adjust on the next turn. In the worst case I will report only what I can see and leave the rest unreported, which is the instruction's preference over weak findings. I have not yet decided whether to report anything; that decision waits on these reads. Nothing in the diff is visible that would fail to compile; the only compile risk is the `contract` module which I cannot see and which a lookup will settle. If the lookup fails to find the contract module, that is not a finding, just an unresolved doubt I will note in the summary as unchecked. The repository rules the author supplied are respected by the visible change: the test file is `native_jail_tests.rs`, not a forbidden name, and the module is a sibling. Whether it is declared with `#[cfg(test)]` and `#[path]` is not visible and worth one read. I will also read `tinybox-jail/src/macos.rs` if it is in the tree to see whether the Seatbelt backend indeed reports `suitable_for_untrusted_code: false` and available `true`, which underpins whether the new `FolderConfined` path can ever be reached in practice on macOS as the description claims. If the module is not vendored here, that is out of reach and I will note it. Finally, `ops.rs`'s `local_status_for_jail` used to be `pub(crate)` with two booleans; the new signature is `pub(crate)` with `Option<SandboxIsolation>` and is used by `native_jail_tests.rs` with `use super::super::ops::local_status_for_jail;` — that is visible and fine. I will not report speculative compile risks without evidence. My provisional reading is that the description matches the code closely, with the one open question of whether the description's 'local desktop sandbox' framing is enforced by code or by convention elsewhere; that is what the `execute_owned` prologue read is for. If it shows that `explicitly_requested` is a genuine Landlock-only request, the description holds. I am also mildly unsure whether the `#[cfg(any(feature = _The code index is behind this pull request (indexed at `3a4514512e94`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash, glm-5.3-flash,
  • Spend: $0.004695
  • Tokens: 774531 input · 47127 output · 152704 cached · 654 embedding
Head State Pass summary
08df2e24a0b1 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-14T13:38:37Z)
192264707eb9 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-15T07:17:57Z)
39c890a3de65 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-15T08:32:57Z)

tinysweeper 0.1.2

@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-14T13:32:27.399914Z 08df2e2 PR opened
ℹ️ 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.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: beff2a82-7f68-4602-af90-4a9cfa625a53
📥 Commits

Reviewing files that changed from the base of the PR and between 1922647 and 39c890a.

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

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: 4366bd6c-1088-47cb-9208-6bc2dfb6769d
📥 Commits

Reviewing files that changed from the base of the PR and between 08df2e2 and 1922647.

📒 Files selected for processing: 4
  • crates/openhuman-core/src/sandbox/README.md
  • crates/openhuman-core/src/sandbox/native_jail.rs
  • crates/openhuman-core/src/sandbox/ops.rs
  • crates/openhuman-core/src/sandbox/schemas.rs
🚧 Files skipped from review as they are similar to previous changes: 1
  • crates/openhuman-core/src/sandbox/schemas.rs

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

📝 Walkthrough

Sandbox handles now report whether a local jail provides process isolation or folder confinement. Native jail status and execution use this distinction to classify and configure jail requests.

Changes: Sandbox isolation handling

Layer / File(s) Summary
Isolation type and handle contract
crates/openhuman-core/src/sandbox/types.rs, crates/openhuman-core/src/sandbox/mod.rs, crates/openhuman-core/src/sandbox/schemas.rs, crates/openhuman-core/src/sandbox/docker.rs, crates/openhuman-core/src/sandbox/ops.rs
SandboxBackendHandle gains an optional isolation field. SandboxIsolation distinguishes Isolated from FolderConfined. Handles without a local jail isolation value set the field to None, and the status schema describes the field.
Jail classification and status
crates/openhuman-core/src/sandbox/native_jail.rs, crates/openhuman-core/src/sandbox/ops.rs, crates/openhuman-core/src/sandbox/native_jail_tests.rs, crates/openhuman-core/src/sandbox/README.md
Native jail status reports an isolation level. Local backend readiness depends on whether isolation is reported, and local handles carry that value. Tests and documentation cover isolated, folder-confined, and unavailable jail configurations.
Execution isolation and jail requests
crates/openhuman-core/src/sandbox/native_jail.rs, crates/openhuman-core/src/sandbox/native_jail_tests.rs
Execution selects an isolation level based on run requirements. Jail requests use enforced process isolation for Isolated runs and declared constraints without process isolation for FolderConfined runs. Network isolation follows the policy setting.

Priority: ➖ Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: senamakel

Merge Risk: ⚪ Minimal · up to 19226

The folder-confined path applies write restrictions rather than merely reporting them. No merge-blocking issue is established; normal validation can proceed.

Security architecture risk: 🟡 Moderate · up to 19226

Local commands regain execution with weaker protection: writes are restricted, but host-readable credentials and network access can remain exposed. The risk requires locally authorized execution of attacker-influenced code; SaaS and explicitly isolated execution retain stronger requirements.

Retained concerns

  • High · security · inferred: Newly admitted local Seatbelt execution lacks a credential-read boundary. If an authorized command executes attacker-controlled project code, that code can potentially read files accessible to the desktop user and transmit them over default-enabled networking. Filtering credential paths out of grants and blocking literal command arguments do not constrain accesses performed inside the launched program. The provider limitation predates this PR, but its exposure through sandboxed native execution changes from refusal to acceptance. SaaS and explicit isolation requirements remain protected.
Security review details

Security Blast Radius

  • inferred — The confidentiality exposure is bounded by the desktop user’s effective file permissions, not the selected folder. Readable credentials could extend impact to downstream services according to their privileges. This does not establish access to encrypted credential contents, elevated OS privileges, or other SaaS tenants.

Security Findings and Attack Paths

  • inferred — A conditional attack path is an authorized local command launching attacker-controlled project code, followed by credential-file reads and network transmission. Literal-path checks inspect analyzed command words, while grant filtering only removes additional permissions; neither supplies runtime read denial for that program. This is an inferred design exposure, not a verified exploit or resolution of the deferred Declared-enforcement candidate.

Trust Boundaries and Controls

  • observed — Execution derives its untrusted flag from explicit isolation selection or process-wide SaaS mode. Both require Isolated; explicit native selection additionally requires Landlock. The mode is not a per-user configuration setting. Classifier tests cover rejection of Seatbelt for untrusted execution and rejection of unavailable or unconfining providers.

Resilience and Maintainability Implications

  • observed — Host admission errors and disabled module support refuse native execution rather than falling back to an unjailed host command. Successful execution also requires resource-close acknowledgement. These controls contain failures but do not independently prove provider enforcement or recovery behavior.

Hardening Proposals

  • proposed — Make acceptance of write-only protection explicit when local commands may execute untrusted project code. When a compatible provider supports credential-read denial, apply the protected-path floor at runtime and validate denial, descendant-process confinement, and cancellation cleanup against the released macOS provider.
Pre-merge checks: Passed 3 · Failed 1 · Inconclusive 1

Failed Failed checks: 1 warning · 1 inconclusive

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 78.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 7 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check Inconclusive Issue [#7819] requires macOS commands to run in the selected folder and requires writes outside that folder to fail. The PR adds jail_isolation, keeps Isolated for explicit Landlock and SaaS paths… Provide reviewable runtime or integration-test evidence that a macOS Seatbelt command cannot write outside policy.workspace_root. Keep the existing evidence for the decision, status, and refused-path requirements.
Passed Passed checks: 3 passed
Check name Status Explanation
Out of Scope Changes check Passed The Docker isolation: None default, public SandboxIsolation type, status schema changes, request mapping, network-policy alignment, and README updates support [#7819] by reporting the confinement …
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the main change: enabling the macOS desktop sandbox to confine execution to the selected folder.
Full details: Linked Issues check

Explanation

Issue [#7819] requires macOS commands to run in the selected folder and requires writes outside that folder to fail. The PR adds jail_isolation, keeps Isolated for explicit Landlock and SaaS paths, accepts Seatbelt only for trusted local runs, maps status to isolated or folder_confined, and tests accepted and refused decision paths. The request mapping passes the workspace and grants to TinyBox and disables the process-isolation requirement only for folder confinement. However, the available evidence does not establish the outside-folder write denial. The PR reports that this behavior was not verified live, and the tests do not execute an outside-folder write.

Full details: Docstring Coverage

Explanation

Docstring coverage is 78.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 7 files. (1 skipped: 1 unsupported.)

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

A rabbit checks the sandbox gate,
Landlock guards the path we take.
Seatbelt keeps writes in their place,
Two postures show the jail’s embrace.
I thump: clear status, hop, and build!

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: 08df2e24a0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +36 to +37
} else if !untrusted && backend == SEATBELT {
Some(SandboxIsolation::FolderConfined)

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 Keep Seatbelt from bypassing protected read paths

On a macOS single-user run (untrusted == false), this accepts Seatbelt even though this posture leaves reads allow-by-default. security/policy/command_checks.rs:319-322 explicitly says the literal-path guard cannot protect arbitrary shell/interpreter code without a filesystem sandbox, so an agent can construct a path such as Path.home() / (chr(46) + "ssh") / "id_rsa" at runtime, evade the analyzed path words, and read it; the local policy also permits network access. Sandbox mode therefore newly exposes credential and system paths that are meant to remain unreachable; keep Seatbelt unavailable until the TinyBox profile enforces those read denials.

AGENTS.md reference: AGENTS.md:L262-L264

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agreed, this is a real gap, and it is not fixed in this PR. I am leaving this thread open.

Evidence that the host cannot close it with the TinyBox contract we pin:

  • tinybox-bus/src/jail.rs NativeJailConfig has only read_only, read_write, allow_net, allow_subprocess, enforcement and require_process_isolation. There is no deny list. That holds at this branch's pin, at main's pin (7ceaf5c3bd) and on tinybox main.
  • tinybox-jail/src/macos.rs render_profile starts with (allow default) and adds only (deny network*), process denies and (deny file-write*) with write re-allows. It never emits a file-read* deny, and read_only is "informational on macOS". No field we send can deny reads.

Fix plan, with the capability in TinyBox first per the submodule-ownership rule:

  1. feat(jail): deny reads of host-chosen paths in native jails (contract 1.20) tinybox#87 (contract 1.20) adds NativeJailConfig::deny_read.
    • Seatbelt ends its profile with (deny file-read* file-write* (subpath …)).
    • The module canonicalizes denials and refuses one that covers the root or a writable grant, or that lies inside a grant on a backend that cannot carve it out.
  2. After its release and repin, jail_request(.., FolderConfined) fills deny_read from the same floor as is_always_forbidden / SENSITIVE_COMPONENTS (security/policy/path_checks.rs, one list, not a fork), expanded against $HOME, plus Keychains and the single-file stores. A test will check that the request carries them.

Until then the sandbox README states this as a known limitation of folder_confined (1922647).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Follow-up for this P1 is #7917. tinybox v0.1.35 shipped NativeJailConfig.deny_read (tinybox#87, contract 1.20). #7917 repins to it, and jail_request now sends the credential stores that is_always_forbidden protects as deny_read for FolderConfined, so Seatbelt denies them by absolute path. A path built at runtime, like the chr(46) + "ssh" example, still lands on that denial. Only stores that exist are sent; a store holding the jail root or a writable grant is dropped with a warning, since TinyBox refuses such a denial. Landlock (Isolated) is unchanged. I am leaving this thread for the reviewer to resolve.

Comment thread crates/openhuman-core/src/sandbox/native_jail.rs

@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, but could not review everything, so this is not an approval: crates/openhuman-core/src/sandbox/native_jail.rs.

             $0.0069 · 913,843 in / 90,232 out · 170,752 cached (19%) · ladder/vectors-oai3, deepseek-v4.1-flash · 546 embedded
critique:    $0.0014 · 440,447 in / 16,916 out · 37,888 cached (9%)   · deepseek-v4.1-flash
security:    $0.0026 · 361,219 in / 33,844 out · 112,384 cached (31%) · deepseek-v4.1-flash
tests:       $0.0014 · 64,337 in  / 20,201 out · 16,384 cached (25%)  · deepseek-v4.1-flash
description: $0.0009 · 41,532 in  / 12,711 out · 4,096 cached (10%)   · deepseek-v4.1-flash

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Oct 14, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Oct 14, 2026

@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, but could not review everything, so this is not an approval: crates/openhuman-core/src/sandbox/README.md, crates/openhuman-core/src/sandbox/native_jail.rs, tinysweeper/tests.

             $0.0008 · 71,316 in / 1,398 out · 0 cached (0%) · ladder/vectors-oai3, glm-5.3-flash · 654 embedded
critique:    $0.0004 · 46,404 in / 484 out   · 0 cached (0%) · glm-5.3-flash
security:    $0.0001 · 13,482 in / 901 out   · 0 cached (0%) · glm-5.3-flash
description: $0.0001 · 11,430 in / 13 out    · 0 cached (0%) · glm-5.3-flash

…folder

The native jail gated every command on TinyBox's untrusted-code floor
(kernel isolation). Seatbelt reports no process isolation, so on macOS
sandbox mode refused every shell command, even in the selected folder.

Split the two requirements in a pure jail_isolation decision: kernel
isolation stays required for an explicit Landlock request and for SaaS
tenants; the local desktop sandbox accepts Seatbelt as folder
confinement (writes only in the selected folder and the toolchain
grants), asked of TinyBox as declared constraints without process
isolation. Sandbox status reports which one applies, and the resource's
network declaration now follows the policy the jail already enforces.

Refs tinyhumansai#7819
Document the two postures jail_isolation picks, who each is accepted
for, and the known limitation of folder confinement: Seatbelt reads are
allow-default, so credential stores stay readable until TinyBox's
deny_read (tinyhumansai/tinybox#87) is released and pinned.
@M3gA-Mind
M3gA-Mind force-pushed the fix/gh-7819-macos-sandbox-folder-confined branch from 1922647 to 39c890a Compare October 15, 2026 07:34

@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, but could not review everything, so this is not an approval: crates/openhuman-core/src/sandbox/ops.rs.

             $0.0047 · 774,531 in / 47,127 out · 152,704 cached (20%) · ladder/vectors-oai3, deepseek-v4.1-flash, glm-5.3-flash,  · 654 embedded
critique:    $0.0029 · 514,732 in / 38,110 out · 138,240 cached (27%) · deepseek-v4.1-flash
security:    $0.0013 · 221,836 in / 5,770 out  · 14,464 cached (7%)   · glm-5.3-flash, deepseek-v4.1-flash,
tests:       $0.0002 · 18,081 in  / 72 out     · 0 cached (0%)        · glm-5.3-flash
description: $0.0001 · 11,405 in  / 942 out    · 0 cached (0%)        · glm-5.3-flash

@M3gA-Mind
M3gA-Mind merged commit 574a505 into tinyhumansai:main Oct 15, 2026
17 of 19 checks passed
M3gA-Mind added a commit that referenced this pull request Oct 16, 2026
…ial-stores

fix(sandbox): deny reads of credential stores in the folder-confined jail
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.

Sandbox mode on macOS refuses every shell command, even inside the selected folder

1 participant