Commit Graph
20 Commits
Author SHA1 Message Date
Abhisek DattaandGitHub 5e0670266e test(sandbox): darwin end-to-end lockdown enforcement test (#374) 2026-07-10 23:01:36 +05:30
Abhisek DattaandGitHub ed614812f5 feat(sandbox): actionable violation message for lockdown network denials (#373) 2026-07-10 22:01:10 +05:30
3ac83a436d feat(sandbox): Seatbelt lockdown translation — deny-all outbound, allow loopback proxy port (#372)
* feat(sandbox): Seatbelt lockdown translation confines outbound to the PMG proxy

Under network_via_proxy_only the Seatbelt profile now denies all
network-outbound (with a target=direct violation marker) and allows only
the loopback proxy port. SBPL is last-match-wins, so the broad deny is
emitted first, specific allows after, and the allow_network_bind rules
last — keeping loopback-to-loopback dev traffic working under lockdown.
allow_direct_dns re-opens the /var/run/mDNSResponder unix socket that
the deny otherwise covers.

Replaces the temporary fail-closed rejection in the Seatbelt driver with
the real translation; non-lockdown profiles translate byte-identically
to before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* test(sandbox): assert deny marker presence before ordering comparison

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* feat(sandbox): render lockdown profiles without a proxy as deny-only with runtime note

pmg sandbox profile show renders profiles for debugging and must not
fail on lockdown profiles. Without a running proxy the translator keeps
the broad deny (rendered profile stays fail-closed, never looser than
runtime) and documents the runtime-only proxy-port allow in an SBPL
comment instead of fabricating a port. Execution is unaffected: the
driver validates the proxy address before translating.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 21:11:56 +05:30
5131c3f641 feat(sandbox): ExecutionContext plumbing and fail-closed lockdown contract (#371)
* feat(sandbox): ExecutionContext plumbing and fail-closed lockdown contract

Network lockdown needs the PMG proxy's address, which is only known at
spawn time. Thread an ExecutionContext from the proxy flow through the
runner and executor into every sandbox driver, and enforce the
network_via_proxy_only fail-closed contract: lockdown without a running
loopback proxy, or on a driver that cannot enforce it, is a hard error —
never a silent fallback to unrestricted network.

- sandbox.ExecutionContext{ProxyAddr} + 4-arg Sandbox.Execute
- sandbox.ValidateLockdown validates the proxy address (loopback only)
  with usefulerror code SandboxRequiresProxy
- Seatbelt validates lockdown before translation (translation itself
  lands next); bubblewrap and landlock reject lockdown as unsupported
  until Linux enforcement is implemented
- executor.WithExecutionContext, runner.ExecuteOptions.SandboxProxyAddr,
  proxy flow passes the live proxy address
- ApplySandbox also validates centrally before invoking the driver

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* fix(sandbox): require numeric in-range proxy port in ValidateLockdown

The validated port string is embedded into generated sandbox profiles,
so service names, zero, and out-of-range ports are refused.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* fix(sandbox): fail closed on Seatbelt lockdown until translation lands

A lockdown policy that passed proxy validation would silently receive
the pre-lockdown network rules from the translator. Reject it until the
lockdown profile translation is implemented, keeping the window between
plumbing and enforcement fail-closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* chore: review feedback — drop redundant comment, simplify stub help text

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 20:12:33 +05:30
e6b5157a2a ci: add macOS test job (#366)
* ci: add macOS test job

Darwin-tagged tests (Seatbelt translator, diagnostics, upcoming network
lockdown tests) never ran in CI: the test matrix covered ubuntu and
windows only. The M0 sandbox lockdown milestone is macOS-first, so macOS
coverage must exist before enforcement work lands.

Also establishes the convention that darwin integration tests requiring
sandbox-exec must fail (not skip) when running in CI, so missing tooling
cannot silently hide security tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* test(sandbox): make Seatbelt render golden test machine-independent

The golden file baked the authoring machine's HOME, CWD and TMPDIR parent
into the expected profile, so TestRenderSeatbelt_Golden could only pass on
that machine. It never ran in CI before the macOS job exposed it.

Normalize CWD, home and TMPDIR parents (raw and query-escaped forms) to
stable placeholders before comparison, longest path first so prefix
overlaps survive, and rewrite the golden accordingly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

* ci: make setup-go cache explicit in macOS job

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqMU5GNBbQvQct9nxek1VS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 19:07:15 +05:30
Abhisek DattaandGitHub c601e17cdc fix: Sandbox profile for Go ecosystem (#361)
* fix: Sandbox profile for Go ecosystem

* fix: Sandbox violations for network bind
2026-07-06 23:38:38 +05:30
788a031003 Fix glob parent directory allowance for patterns with glob characters (#331)
* fix(sandbox): support pnpm workspaces and macOS cache dir in pnpm profile

pnpm in a workspace (monorepo) creates a node_modules directory inside
every workspace package to symlink direct dependencies. The profile only
allowed writes to the root node_modules, so installs failed with EPERM
on mkdir of e.g. apps/mobile/node_modules.

pnpm on macOS also writes its cache (lockfile verification, metadata)
under ~/Library/Caches/pnpm, while the base profile only covers the XDG
path ~/.cache/pnpm.

Fixes are scoped to the pnpm leaf profile, not the shared
npm-restrictive base.

Ref: https://github.com/safedep/pmg/issues/329

* fix(sandbox): emit regex parent rule for nested-glob allow patterns on Seatbelt

For allow patterns ending in /**, the translator auto-allows the parent
directory so mkdir/stat of the directory itself succeeds. The rule was
always emitted as a literal, which can never match when the parent still
contains glob characters (e.g. ${CWD}/**/node_modules from a workspace
allowance) — silently leaving the directory's own creation denied.

Emit a regex rule for glob-bearing parents instead. This stays strictly
narrower than the Linux drivers (Bubblewrap binds the prefix before the
first /** read-write; Landlock grants the glob expansion or its parent),
and deny rules are emitted after allows, so mandatory credential denies
still override.

Ref: https://github.com/safedep/pmg/issues/329

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-12 12:11:12 +05:30
Sahil BansalandGitHub 872c5d663c fix(sandbox): bind parent dir for globstar allow_write on bwrap (#321)
* fix(sandbox): bind parent dir for globstar allow_write on bwrap

Fine-grained per-path mounts under read-only project binds broke pip
install into in-project .venv directories. Always mount the parent tree
for ** write rules instead.

Fixes #315

* test(sandbox): tighten globstar bind assertions and ensure ~/.npm exists for e2e

Strengthen TestBubblewrapAllowWriteGlobstarBindsParentOnly to verify the
parent dir is writably bound and the child path is read-only bound, not
just substring presence. Pre-create ~/.npm in the e2e harness so
bubblewrap --bind-try does not skip the npm cache dir on fresh runners.

* switch pnpm to /tmp in sandbox e2e

* test(sandbox): update glob ** test for parent-bind semantics

Globstar allow_write now binds the parent dir only (e2e740d), so the
test should assert the parent is writably bound and child subdirs are
not individually bound, instead of substring-matching subdir names.

* fix(sandbox): bind correct base dir for in-pattern globstar allow_write

Globstar allow_write previously used extractGlobParentDir, which walks past
the first ** and yields the wrong root for patterns like /a/b/**/d/**/e.
Introduce extractGlobstarWriteBaseDir, which takes the prefix before the
first /**, and use it in processWriteRule. Also dedup the coarse-fallback
parent-bind loop to mirror the read-rule fallback.
2026-06-07 10:03:23 +05:30
20e01d5cae refactor : Refactor error handling to use dry/usefulerror (#283)
* update the go.sum

Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com>

* migrate most of the files to dry errors

Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com>

* update the rest of the files

Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com>

* fixs the review comments

Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com>

* chores

Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com>

---------

Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com>
Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com>
2026-05-24 12:22:19 +05:30
Abhisek DattaandGitHub b8588e3df4 feat: Add Sandbox Inspection and Debugging Commands (#261)
* feat: add sandbox DX commands

* fix: Linter errors

* fix: Sandbox deny log parsing

* fix: Sandbox docs

* refactor: Maintain SSOT across pkg dependencies

* fix: Linter errors
2026-05-19 14:40:54 +05:30
Abhisek DattaandGitHub d993d57e3d feat: Add support for sandbox diagnostic log (#245)
* feat: Add support for sandbox diagnostic log

* fix: Normalize and prioritise sandbox violations

* fix: Code review fixes
2026-05-12 18:11:18 +05:30
Abhisek DattaandGitHub 00fd6d2a9c fix: Handle bubblewrap deny write with read overlay (#251) 2026-05-11 16:12:06 +05:30
Abhisek DattaandGitHub a31c201117 chore: Add a landlock test for path deny (#247)
* chore: Add a landlock test for path deny

* fix: Make sure sandbox is available:
2026-05-08 14:18:57 +00:00
Abhisek DattaandGitHub b671192598 fix: Allow macos fsevents by default in sandbox (#244)
* fix: Allow macos fsevents by default in sandbox

* fix: Code review fixes
2026-05-07 12:32:38 +00:00
Abhisek DattaandGitHub 4c42ceca0e feat: Add support for Landlock based Sandbox for Linux (#238)
* feat: Initial implementation of landlock based sandbox driver

* fix: Handle seccom probe failure

* fix: Remove unnecessary seccomp probe

* fix: Use file based policy load

* fix: Keep bpf filter in memory

* fix: Use TSYNC for seccom filter

* fix: Use TSYNC for seccom filter

* fix: Update landlock translator

* fix: Landlock sandbox implementation

* fix: Landlock + seccomp based sandboxing on Linux

* fix: Misc fixes

* fix: Cleanup sandbox files

* fix: Handle mandatory deny API change post merge

* fix: Landlock write access translation

* chore: Fix linter issues

* ci: Use /tmp for npm cache for landlock
2026-05-07 12:42:28 +05:30
d6755d3f44 feat/sandbox allow explicit dangerous pattern override (#239)
* feat(sandbox): allow opt-out of mandatory deny via explicit allow rules

Mandatory deny patterns (.env, .aws, .ssh, .gcloud, .kube, .gnupg,
.docker/config.json, .git/config) can now be opted out by listing the
exact literal post-expansion path in policy filesystem.allow_read /
allow_write, OR via --sandbox-allow read=... / write=... at runtime.
Both channels are treated at par.

Suppression is exact-match. Listing the CWD-absolute or HOME-absolute
form of a dangerous file additionally suppresses its **/<file> glob
sibling on the same direction so a single opt-out is sufficient.
Broad globs (${CWD}/**) and relative paths in user allow lists do not
suppress. The unnamed absolute form remains denied. .git/hooks is
unconditional and never suppressible (arbitrary code execution risk).

GetMandatoryDenyPatterns now returns split DenyRead / DenyWrite
slices and reports SuppressedRead / SuppressedWrite for audit. Both
translators emit per-direction deny rules and log.Warnf each
suppression. On Linux/bubblewrap, the tmpfs hide is restricted to the
intersection of DenyRead and DenyWrite; one-sided suppression falls
back to /dev/null (write) or the user's allow_read --ro-bind (read).
bwrap has no primitive that allows writes while denying reads, so
write-only opt-outs warn that the read-side mandatory deny is
unenforceable.

Updates docs/sandbox.md to document the opt-out, exact-match
semantics, and the Linux platform limitation. Updates pmg-e2e.yml to
create ./.env so the sandbox e2e test exercises the BLOCK case.

Closes #232

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: Code review fixes

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 12:45:36 +05:30
Abhisek DattaandGitHub 3778d4d1f3 fix: Sandbox Allow network bind for npx (#151)
* feat: Add support for network bind

* chore: Add comments for bwrap sandbox
2026-02-04 15:32:47 +05:30
Abhisek DattaandGitHub 80a1747e3e feat: Add support for Linux Sandbox using Bubblewrap (#120)
* feat: Add support for bubblewrap sandbox

* fix: Glob pattern expansion limit for linux

* fix: Bug in glob pattern expansion for bwrap

* fix: README on trust

* fix: Multiple bubblewrap translator fix

* test: Add E2E for linux sandbox

* fix: Refactor bwrap sandbox to use common dangerous files

* fix: Path test case

* fix: Non-existent path handling bug

* refactor: Misc cleanup

* fix: Avoid bind mount for non-existentent deny protection

* fix: Off by one bug in path depth handling

* ci: Disable AppArmor on GHA runner

* fix: Disable apparmor userns restrictions
2026-01-15 20:12:12 +05:30
2e1f5b1a36 feat: Add support for policy inheritence (#113)
* feat: Add support for policy inheritence

* fix: Linter fixes

* Update docs/sandbox.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>

* fix: Handle boolean inheritence

* ci: Add linter

* Update sandbox/policy_test.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>

* fix: Linter fixes

* fix: Linter fixes

* fix: Sandbox rule regex format

---------

Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-14 10:50:39 +05:30
9693428171 feat: Experimental Sandbox Support (#101)
* feat: Sandbox implementation with seatbelt

* refactor: Remove concept of PM_CACHE

* fix: Misc fixes

* refactor: Sandbox for separation of boundaries

* fix: Apply API

* fix: Add support for sandbox cleanup

* test: Add variable interpolation test

* fix: Misc cleanup fixes

* chore: Cleanup sandbox registry

* chore: Cleanup sandbox policy

* chore: Cleanup sandbox

* fix: Misc cleanup fixes

* fix: Remove violation mode

* fix: Update config template

* chore: Go mod cleanup

* fix: Handle the case when package manager policy is explicitly disabled

* fix: Sandbox executor

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>

* test: Remove unused var

* test: Add test for seatbelt sandbox driver

* fix: Sandbox profile loader from file should use path for caching

* test: Add policy test

* feat: Add support for config templates

* fix: Seatbelt translator handle glob

* fix: Merge conflicts

* fix: Fix sandbox policy generator for MacOS min permissions

* fix: Sandbox path handling bugs

* fix: Deny read to dangerous directories

* fix: Deny read to dangerous directories

* add sandbox e2e (#112)

* fix: Sandbox E2E test

* fix: Code review fixes

* fix: Code review fixes

* doc: Add sandbox debugging guide

* doc: Update sandbox doc

* docs: Add sandbox usage doc

* fix: Use better error for sandbox without policy

* fix: Add sandbox for npx

* fix: Enable PTY for npm

---------

Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sahil Bansal <bansalsahil315@gmail.com>
2026-01-13 14:52:02 +05:30