bx-mac 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/bx.js +14 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -243,6 +243,8 @@ ro:reference/docs
|
|
|
243
243
|
ro:shared/toolchain
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
+
`rw:` and `ro:` entries also **override** the built-in protected lists - e.g. `ro:.npmrc` makes the otherwise-blocked `~/.npmrc` readable, `rw:.aws` opens the AWS credentials directory. Files (not just directories) are accepted as targets. Use this with care - you are explicitly weakening the default protection.
|
|
247
|
+
|
|
246
248
|
Deny rules are applied **in addition** to the built-in protected lists:
|
|
247
249
|
|
|
248
250
|
> 🔒 **Dotdirs:** `.ssh` `.gnupg` `.docker` `.zsh_sessions` `.cargo` `.gradle` `.gem`
|
|
@@ -397,6 +399,7 @@ These are great when available, but they only protect within their own tool. bx
|
|
|
397
399
|
## 🔗 Alternatives
|
|
398
400
|
|
|
399
401
|
- [Agent Safehouse](https://agent-safehouse.dev/) — macOS kernel-level sandboxing for LLM coding agents via `sandbox-exec`. Uses a **deny-first model**: everything is blocked by default and only explicitly listed paths are opened up. This gives you theoretically stricter control (e.g. `~/Library` is fully blocked and only specific subdirs are allowed), but requires more configuration — tools and runtimes that need paths you haven't whitelisted will break silently. If you need that level of precision and are willing to tune profiles per tool, Agent Safehouse may be the better fit. bx uses the opposite **allow-first model** (only sensitive paths are blocked), which works out of the box for VSCode, shells, Claude Code, and other tools without any per-tool configuration.
|
|
402
|
+
- [Docker AI Sandboxes](https://docs.docker.com/ai/sandboxes/) — Docker's built-in sandbox environment for AI coding agents. Runs tools in isolated containers with controlled filesystem and network access. Stronger isolation than kernel-level sandboxing, but requires Docker Desktop and adds container overhead.
|
|
400
403
|
- **Docker / VMs** — for stronger isolation, run AI tools in a virtualized environment (containers, VMs). Full process and network isolation at the cost of setup overhead.
|
|
401
404
|
- **Web sandboxes** — browser-based approaches for running AI agents. See Simon Willison's [Living dangerously with Claude](https://simonwillison.net/2025/Oct/22/living-dangerously-with-claude/) for an overview.
|
|
402
405
|
|
package/dist/bx.js
CHANGED
|
@@ -1091,7 +1091,7 @@ function parseHomeConfig(home, workDirs) {
|
|
|
1091
1091
|
if (!match) continue;
|
|
1092
1092
|
const [, prefix, rawPath] = match;
|
|
1093
1093
|
const absolute = resolve(home, rawPath.trim());
|
|
1094
|
-
if (!existsSync(absolute)
|
|
1094
|
+
if (!existsSync(absolute)) continue;
|
|
1095
1095
|
if (prefix.toUpperCase() === "RW") allowed.add(absolute);
|
|
1096
1096
|
else readOnly.add(absolute);
|
|
1097
1097
|
}
|
|
@@ -1143,20 +1143,20 @@ function collectProtectedContainers(home) {
|
|
|
1143
1143
|
}
|
|
1144
1144
|
return matched;
|
|
1145
1145
|
}
|
|
1146
|
-
function collectReadOnlyDotfiles(home) {
|
|
1147
|
-
return PROTECTED_HOME_DOTFILES_RO.map((f) => join(home, f));
|
|
1146
|
+
function collectReadOnlyDotfiles(home, overrides = /* @__PURE__ */ new Set()) {
|
|
1147
|
+
return PROTECTED_HOME_DOTFILES_RO.map((f) => join(home, f)).filter((p) => !overrides.has(p));
|
|
1148
1148
|
}
|
|
1149
|
-
function collectIgnoredPaths(home, workDirs) {
|
|
1149
|
+
function collectIgnoredPaths(home, workDirs, overrides = /* @__PURE__ */ new Set()) {
|
|
1150
1150
|
const ignored = [
|
|
1151
1151
|
...PROTECTED_DOTDIRS.map((d) => join(home, d)),
|
|
1152
1152
|
...PROTECTED_HOME_DOTFILES.map((f) => join(home, f)),
|
|
1153
1153
|
...PROTECTED_LIBRARY_DIRS.map((d) => join(home, "Library", d)),
|
|
1154
1154
|
...new Set(collectProtectedContainers(home))
|
|
1155
|
-
];
|
|
1155
|
+
].filter((p) => !overrides.has(p));
|
|
1156
1156
|
const globalIgnore = join(home, ".bxignore");
|
|
1157
1157
|
if (existsSync(globalIgnore)) {
|
|
1158
1158
|
const denyLines = parseLines(globalIgnore).filter((l) => !ACCESS_PREFIX_RE.test(l));
|
|
1159
|
-
for (const line of denyLines)
|
|
1159
|
+
for (const line of denyLines) for (const m of resolveGlobMatches(line, home)) if (!overrides.has(m)) ignored.push(m);
|
|
1160
1160
|
}
|
|
1161
1161
|
for (const workDir of workDirs) collectIgnoreFilesRecursive(workDir, ignored);
|
|
1162
1162
|
return ignored;
|
|
@@ -1199,7 +1199,7 @@ function collectSystemDenyPaths(home) {
|
|
|
1199
1199
|
function generateProfile(workDirs, blockedDirs, ignoredPaths, readOnlyDirs = [], home = "", readOnlyFiles = []) {
|
|
1200
1200
|
const blockedRules = sbplDenyBlock("Blocked paths (auto-generated from $HOME contents)", "file*", blockedDirs.map(sbplPathRule));
|
|
1201
1201
|
const ignoredRules = sbplDenyBlock("Hidden paths from .bxignore", "file*", ignoredPaths.map(sbplPathRule));
|
|
1202
|
-
const readOnlyRules = sbplDenyBlock("Read-only
|
|
1202
|
+
const readOnlyRules = sbplDenyBlock("Read-only paths", "file-write*", readOnlyDirs.map(sbplPathRule));
|
|
1203
1203
|
const readOnlyFileRules = sbplDenyBlock("Read-only home dotfiles (shell init - write-protected against injection)", "file-write*", readOnlyFiles.map(sbplLiteral));
|
|
1204
1204
|
const systemRules = home ? sbplDenyBlock("System-level restrictions", "file*", collectSystemDenyPaths(home).map(sbplSubpath)) : "";
|
|
1205
1205
|
return `; Auto-generated sandbox profile
|
|
@@ -1623,8 +1623,8 @@ Configuration:
|
|
|
1623
1623
|
built-in apps (code, xcode) can be overridden
|
|
1624
1624
|
~/.bxignore sandbox rules (one per line):
|
|
1625
1625
|
path block access (deny)
|
|
1626
|
-
rw:path allow read-write access
|
|
1627
|
-
ro:path allow read-only access
|
|
1626
|
+
rw:path allow read-write access (overrides built-in protection)
|
|
1627
|
+
ro:path allow read-only access (overrides built-in protection)
|
|
1628
1628
|
<workdir>/.bxignore blocked paths in project (.gitignore-style matching)
|
|
1629
1629
|
/ or . self-protect: block entire directory
|
|
1630
1630
|
<dir>/.bxprotect marker file: block the containing directory
|
|
@@ -1699,7 +1699,7 @@ function printDryRunTree({ home, blockedDirs, ignoredPaths, readOnlyDirs, workDi
|
|
|
1699
1699
|
}
|
|
1700
1700
|
//#endregion
|
|
1701
1701
|
//#region src/index.ts
|
|
1702
|
-
const VERSION = "1.
|
|
1702
|
+
const VERSION = "1.4.0";
|
|
1703
1703
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1704
1704
|
if (process$1.argv.includes("--version") || process$1.argv.includes("-v")) {
|
|
1705
1705
|
console.log(`bx ${VERSION}`);
|
|
@@ -1748,9 +1748,10 @@ async function main() {
|
|
|
1748
1748
|
const profileSandbox = vscodeUserFlag !== false ? vscodeUserFlag : app?.profile ?? false;
|
|
1749
1749
|
if (profileSandbox) await setupVSCodeProfile(HOME, profileSandbox);
|
|
1750
1750
|
const { allowed, readOnly } = parseHomeConfig(HOME, workDirs);
|
|
1751
|
-
const
|
|
1752
|
-
const
|
|
1753
|
-
const
|
|
1751
|
+
const allAccessible = new Set([...allowed, ...readOnly]);
|
|
1752
|
+
const blockedDirs = collectBlockedDirs(HOME, HOME, __dirname, allAccessible);
|
|
1753
|
+
const ignoredPaths = collectIgnoredPaths(HOME, workDirs, allAccessible);
|
|
1754
|
+
const readOnlyDotfiles = collectReadOnlyDotfiles(HOME, allAccessible);
|
|
1754
1755
|
printPolicySummary(mode, workDirs, blockedDirs, ignoredPaths, readOnly);
|
|
1755
1756
|
const profile = generateProfile(workDirs, blockedDirs, ignoredPaths, [...readOnly], HOME, readOnlyDotfiles);
|
|
1756
1757
|
if (verbose) {
|