@sysid/sandbox-runtime-improved 0.0.56-sysid.1 → 0.0.61-sysid.1
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 +43 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sandbox/credential-mask-files.d.ts +72 -0
- package/dist/sandbox/credential-mask-files.d.ts.map +1 -0
- package/dist/sandbox/credential-mask-files.js +146 -0
- package/dist/sandbox/credential-mask-files.js.map +1 -0
- package/dist/sandbox/credential-sentinel.d.ts +72 -0
- package/dist/sandbox/credential-sentinel.d.ts.map +1 -0
- package/dist/sandbox/credential-sentinel.js +130 -0
- package/dist/sandbox/credential-sentinel.js.map +1 -0
- package/dist/sandbox/domain-pattern.d.ts +36 -0
- package/dist/sandbox/domain-pattern.d.ts.map +1 -0
- package/dist/sandbox/domain-pattern.js +60 -0
- package/dist/sandbox/domain-pattern.js.map +1 -0
- package/dist/sandbox/http-proxy.d.ts +32 -1
- package/dist/sandbox/http-proxy.d.ts.map +1 -1
- package/dist/sandbox/http-proxy.js +10 -2
- package/dist/sandbox/http-proxy.js.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.d.ts +15 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.js +106 -47
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -1
- package/dist/sandbox/listen-in-range.d.ts +12 -0
- package/dist/sandbox/listen-in-range.d.ts.map +1 -0
- package/dist/sandbox/listen-in-range.js +43 -0
- package/dist/sandbox/listen-in-range.js.map +1 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts +11 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.js +69 -14
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -1
- package/dist/sandbox/mitm-ca.d.ts +18 -2
- package/dist/sandbox/mitm-ca.d.ts.map +1 -1
- package/dist/sandbox/mitm-ca.js +49 -9
- package/dist/sandbox/mitm-ca.js.map +1 -1
- package/dist/sandbox/mitm-leaf.d.ts.map +1 -1
- package/dist/sandbox/mitm-leaf.js +24 -6
- package/dist/sandbox/mitm-leaf.js.map +1 -1
- package/dist/sandbox/mux-proxy.d.ts +59 -0
- package/dist/sandbox/mux-proxy.d.ts.map +1 -0
- package/dist/sandbox/mux-proxy.js +159 -0
- package/dist/sandbox/mux-proxy.js.map +1 -0
- package/dist/sandbox/request-filter.d.ts +11 -1
- package/dist/sandbox/request-filter.d.ts.map +1 -1
- package/dist/sandbox/request-filter.js.map +1 -1
- package/dist/sandbox/sandbox-config.d.ts +254 -25
- package/dist/sandbox/sandbox-config.d.ts.map +1 -1
- package/dist/sandbox/sandbox-config.js +200 -13
- package/dist/sandbox/sandbox-config.js.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts +4 -0
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.js +601 -194
- package/dist/sandbox/sandbox-manager.js.map +1 -1
- package/dist/sandbox/sandbox-schemas.d.ts +15 -0
- package/dist/sandbox/sandbox-schemas.d.ts.map +1 -1
- package/dist/sandbox/sandbox-utils.d.ts +42 -6
- package/dist/sandbox/sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/sandbox-utils.js +115 -27
- package/dist/sandbox/sandbox-utils.js.map +1 -1
- package/dist/sandbox/socks-proxy.d.ts +11 -5
- package/dist/sandbox/socks-proxy.d.ts.map +1 -1
- package/dist/sandbox/socks-proxy.js +13 -81
- package/dist/sandbox/socks-proxy.js.map +1 -1
- package/dist/sandbox/tls-terminate-proxy.d.ts +2 -2
- package/dist/sandbox/tls-terminate-proxy.d.ts.map +1 -1
- package/dist/sandbox/tls-terminate-proxy.js +31 -5
- package/dist/sandbox/tls-terminate-proxy.js.map +1 -1
- package/dist/sandbox/windows-sandbox-utils.d.ts +347 -11
- package/dist/sandbox/windows-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/windows-sandbox-utils.js +437 -45
- package/dist/sandbox/windows-sandbox-utils.js.map +1 -1
- package/package.json +7 -4
- package/vendor/seccomp/build.ts +8 -30
- package/vendor/srt-win/build.ts +21 -0
- package/dist/vendor/seccomp/Dockerfile.build +0 -6
- package/dist/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/build.ts +0 -91
- package/dist/vendor/seccomp/x64/apply-seccomp +0 -0
- package/dist/vendor/seccomp-src/apply-seccomp.c +0 -280
- package/dist/vendor/seccomp-src/seccomp-unix-block.c +0 -148
- package/dist/vendor/srt-win/Cargo.lock +0 -361
- package/dist/vendor/srt-win/Cargo.toml +0 -46
- package/dist/vendor/srt-win/ci/cleanup.ps1 +0 -49
- package/dist/vendor/srt-win/ci/smoke-exec.ps1 +0 -446
- package/dist/vendor/srt-win/ci/smoke.ps1 +0 -307
- package/dist/vendor/srt-win/src/job.rs +0 -102
- package/dist/vendor/srt-win/src/launch.rs +0 -732
- package/dist/vendor/srt-win/src/lib.rs +0 -20
- package/dist/vendor/srt-win/src/main.rs +0 -669
- package/dist/vendor/srt-win/src/self_protect.rs +0 -169
- package/dist/vendor/srt-win/src/sid.rs +0 -296
- package/dist/vendor/srt-win/src/token.rs +0 -341
- package/dist/vendor/srt-win/src/util.rs +0 -42
- package/dist/vendor/srt-win/src/wfp.rs +0 -992
- package/dist/vendor/srt-win/src/winsta.rs +0 -209
- package/dist/vendor/srt-win/tests/sd_access_check_matrix.rs +0 -259
- package/vendor/seccomp-src/apply-seccomp.c +0 -280
- package/vendor/seccomp-src/seccomp-unix-block.c +0 -148
- package/vendor/srt-win/Cargo.lock +0 -361
- package/vendor/srt-win/Cargo.toml +0 -46
- package/vendor/srt-win/ci/cleanup.ps1 +0 -49
- package/vendor/srt-win/ci/smoke-exec.ps1 +0 -446
- package/vendor/srt-win/ci/smoke.ps1 +0 -307
- package/vendor/srt-win/src/job.rs +0 -102
- package/vendor/srt-win/src/launch.rs +0 -732
- package/vendor/srt-win/src/lib.rs +0 -20
- package/vendor/srt-win/src/main.rs +0 -669
- package/vendor/srt-win/src/self_protect.rs +0 -169
- package/vendor/srt-win/src/sid.rs +0 -296
- package/vendor/srt-win/src/token.rs +0 -341
- package/vendor/srt-win/src/util.rs +0 -42
- package/vendor/srt-win/src/wfp.rs +0 -992
- package/vendor/srt-win/src/winsta.rs +0 -209
- package/vendor/srt-win/tests/sd_access_check_matrix.rs +0 -259
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
Based on [Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime).
|
|
7
7
|
|
|
8
|
-
This fork will be rebased on [SRT](https://github.com/anthropic-experimental/sandbox-runtime) continuously (currently based on upstream **v0.0.
|
|
8
|
+
This fork will be rebased on [SRT](https://github.com/anthropic-experimental/sandbox-runtime) continuously (currently based on upstream **v0.0.61**).
|
|
9
9
|
As new features become available, equivalent local ones will be removed.
|
|
10
10
|
|
|
11
11
|
> Objective: keep as close to original as possible, while allowing selected additional features.
|
|
@@ -320,6 +320,24 @@ Uses an **allow-only pattern** - all network access is denied by default.
|
|
|
320
320
|
- `https` - Proxy URL for HTTPS/CONNECT traffic (falls back to `http` if unset)
|
|
321
321
|
- `noProxy` - Comma-separated bypass list: hostname suffixes and CIDR ranges that connect directly
|
|
322
322
|
|
|
323
|
+
**TLS termination** (`network.tlsTerminate`, experimental): when set, HTTPS CONNECTs are terminated in-process so SRT can see (and filter, via `network.filterRequest`) the decrypted requests. The sandboxed process is pointed at a trust bundle containing the MITM CA (`caCertPath`/`caKeyPath`, or an ephemeral CA if omitted) plus the host's regular roots, so proxy-minted certificates and real upstream certificates both verify.
|
|
324
|
+
|
|
325
|
+
- `network.tlsTerminate.excludeDomains` - Domain patterns (same syntax as `allowedDomains`) that are **not** terminated. Matching CONNECTs are tunnelled opaquely instead: they are still subject to the domain allowlist, but the client inside the sandbox completes its own TLS handshake with the real upstream, and `filterRequest` / credential injection do not apply to their HTTPS traffic. Use this for the two cases TLS termination fundamentally breaks:
|
|
326
|
+
- **mTLS upstreams** - only the in-sandbox client holds the client certificate, so the proxy cannot re-originate the connection on its behalf.
|
|
327
|
+
- **Certificate-pinning clients** - clients that verify the upstream's identity themselves (custom CAs, SAN pinning) and reject the MITM certificate.
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"network": {
|
|
332
|
+
"allowedDomains": ["*.example.com", "internal-mtls.example.net"],
|
|
333
|
+
"deniedDomains": [],
|
|
334
|
+
"tlsTerminate": {
|
|
335
|
+
"excludeDomains": ["internal-mtls.example.net"]
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
323
341
|
**Unix Socket Settings** (platform-specific behavior):
|
|
324
342
|
|
|
325
343
|
| Setting | macOS | Linux |
|
|
@@ -455,7 +473,7 @@ Watchman accesses files outside the sandbox boundaries, which will trigger permi
|
|
|
455
473
|
|
|
456
474
|
- **macOS**: Uses `sandbox-exec` with custom profiles (no additional dependencies)
|
|
457
475
|
- **Linux**: Uses `bubblewrap` (bwrap) for containerization
|
|
458
|
-
- **Windows**:
|
|
476
|
+
- **Windows**: Alpha — see [Windows (alpha)](#windows-alpha) below for the threat model and known limitations
|
|
459
477
|
|
|
460
478
|
### Platform-Specific Dependencies
|
|
461
479
|
|
|
@@ -498,6 +516,29 @@ The package includes pre-generated seccomp BPF filters for x86-64 and arm archit
|
|
|
498
516
|
- Install via Homebrew: `brew install ripgrep`
|
|
499
517
|
- Or download from: https://github.com/BurntSushi/ripgrep/releases
|
|
500
518
|
|
|
519
|
+
## Windows (alpha)
|
|
520
|
+
|
|
521
|
+
Windows support is **alpha and the design is in flux**. The current implementation provides network egress filtering (via the Windows Filtering Platform) and file read/write denial (via ACL stamping), but it is **not a security boundary against a deliberately adversarial sandboxed process** — see [Threat model](#threat-model) below. The mechanism will be substantially revised (the sandboxed process will run under a separate sandbox user account) in a future version.
|
|
522
|
+
|
|
523
|
+
### Setup
|
|
524
|
+
|
|
525
|
+
Run `npx @sysid/sandbox-runtime-improved windows-install` once per machine (requires admin; creates the `sandbox-runtime-net` local group and installs the WFP egress filters). **Log out and log back in** for group membership to take effect on your token. After that, `SandboxManager.initialize()` and the `srti` CLI work as on other platforms.
|
|
526
|
+
|
|
527
|
+
### Threat model
|
|
528
|
+
|
|
529
|
+
**Important:** the Windows sandbox runs the child as the **same user** as the host, with a restricted token (the discriminator group is flipped to deny-only). This protects against **accidental** access by well-behaved tools, and against **deliberate in-token** access by the sandboxed process itself — code running under the restricted token cannot open denied files or make direct outbound connections.
|
|
530
|
+
|
|
531
|
+
It does **not** protect against a sandboxed process that deliberately escapes via system-service brokering. Task Scheduler, `PROC_THREAD_ATTRIBUTE_PARENT_PROCESS` (re-parenting onto an unrestricted process such as `explorer.exe`), BITS, and certain COM out-of-process servers will spawn a process under the user's full, unrestricted interactive token. That process can then read or write any "denied" file and make unrestricted network connections. There is no same-user fix for this class of escape; the structural answer is running the sandboxed process under a **separate sandbox user account**, which is the planned next revision.
|
|
532
|
+
|
|
533
|
+
Until that revision lands, treat the Windows sandbox as **best-effort defense against accidental access and well-behaved tooling**, not as containment for an adversarial process.
|
|
534
|
+
|
|
535
|
+
### Known limitations
|
|
536
|
+
|
|
537
|
+
- **Not a security boundary against a deliberate child** — see [Threat model](#threat-model) above.
|
|
538
|
+
- `filesystem.allowWrite` (write allow-list) and `filesystem.allowRead` (re-allow within denyRead) are **not supported** on Windows — `initialize()` rejects a config that sets either. Only `denyRead` / `denyWrite` are applied.
|
|
539
|
+
- `network.tlsTerminate` is not yet supported on Windows.
|
|
540
|
+
- Directory targets in `filesystem.denyRead` / `filesystem.denyWrite` are not yet supported (individual files only).
|
|
541
|
+
|
|
501
542
|
## Development
|
|
502
543
|
|
|
503
544
|
```bash
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ export { SandboxViolationStore } from './sandbox/sandbox-violation-store.js';
|
|
|
3
3
|
export type { SandboxRuntimeConfig, NetworkConfig, FilesystemConfig, CredentialsConfig, CredentialFileConfig, CredentialEnvVarConfig, CredentialMode, IgnoreViolationsConfig, } from './sandbox/sandbox-config.js';
|
|
4
4
|
export { SandboxRuntimeConfigSchema, NetworkConfigSchema, FilesystemConfigSchema, CredentialsConfigSchema, IgnoreViolationsConfigSchema, RipgrepConfigSchema, } from './sandbox/sandbox-config.js';
|
|
5
5
|
export type { SandboxAskCallback, FsReadRestrictionConfig, FsWriteRestrictionConfig, CredentialRestrictionConfig, NetworkRestrictionConfig, NetworkHostPattern, } from './sandbox/sandbox-schemas.js';
|
|
6
|
-
export type { FilterRequestCallback, RequestDecision, } from './sandbox/request-filter.js';
|
|
6
|
+
export type { FilterRequestCallback, RequestDecision, MutateForwardedHeaders, } from './sandbox/request-filter.js';
|
|
7
7
|
export type { SandboxViolationEvent } from './sandbox/macos-sandbox-utils.js';
|
|
8
8
|
export { type SandboxDependencyCheck } from './sandbox/linux-sandbox-utils.js';
|
|
9
|
-
export { getSrtWinPath, getWindowsGroupStatus, getWindowsWfpStatus, installWindowsSandbox, uninstallWindowsSandbox, createWindowsGroup, deleteWindowsGroup, createWindowsWfp, windowsInstallInstructions, DEFAULT_WINDOWS_GROUP_NAME, DEFAULT_WINDOWS_PROXY_PORT_RANGE, } from './sandbox/windows-sandbox-utils.js';
|
|
10
|
-
export type { WindowsGroupRef, WindowsInstallOptions, WindowsInstallResult, WindowsGroupStatus, WindowsGroupStatusResult, WindowsWfpStatus, WindowsWfpStatusResult, } from './sandbox/windows-sandbox-utils.js';
|
|
9
|
+
export { getSrtWinPath, getWindowsGroupStatus, getWindowsWfpStatus, getWindowsSandboxUserStatus, getWindowsSandboxCaCert, windowsTrustCa, installWindowsSandbox, uninstallWindowsSandbox, createWindowsGroup, deleteWindowsGroup, createWindowsWfp, windowsInstallInstructions, stampWindowsAcl, restoreWindowsAcl, expandWindowsFsDenyPaths, WINDOWS_ACL_PATH_OK, WINDOWS_ACL_PARENT_OK, DEFAULT_WINDOWS_GROUP_NAME, DEFAULT_WINDOWS_PROXY_PORT_RANGE, } from './sandbox/windows-sandbox-utils.js';
|
|
10
|
+
export type { WindowsGroupRef, WindowsInstallOptions, WindowsInstallResult, WindowsGroupStatus, WindowsGroupStatusResult, WindowsWfpStatus, WindowsAclStampOptions, WindowsAclRestoreOptions, WindowsAclRestoreResult, WindowsAclPathOutcome, WindowsAclParentOutcome, WindowsWfpStatusResult, WindowsSandboxUserStatus, } from './sandbox/windows-sandbox-utils.js';
|
|
11
11
|
export type { WindowsConfig } from './sandbox/sandbox-config.js';
|
|
12
12
|
export { WindowsConfigSchema } from './sandbox/sandbox-config.js';
|
|
13
13
|
export { getDefaultWritePaths } from './sandbox/sandbox-utils.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAG5E,YAAY,EACV,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,EACd,sBAAsB,GACvB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,6BAA6B,CAAA;AAGpC,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,8BAA8B,CAAA;AAGrC,YAAY,EACV,qBAAqB,EACrB,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAG5E,YAAY,EACV,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,EACd,sBAAsB,GACvB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,6BAA6B,CAAA;AAGpC,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,8BAA8B,CAAA;AAGrC,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,sBAAsB,GACvB,MAAM,6BAA6B,CAAA;AAGpC,YAAY,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAC7E,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AAG9E,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,oCAAoC,CAAA;AAC3C,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oCAAoC,CAAA;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAGjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAGjE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { SandboxManager } from './sandbox/sandbox-manager.js';
|
|
|
3
3
|
export { SandboxViolationStore } from './sandbox/sandbox-violation-store.js';
|
|
4
4
|
export { SandboxRuntimeConfigSchema, NetworkConfigSchema, FilesystemConfigSchema, CredentialsConfigSchema, IgnoreViolationsConfigSchema, RipgrepConfigSchema, } from './sandbox/sandbox-config.js';
|
|
5
5
|
// Windows install/status API
|
|
6
|
-
export { getSrtWinPath, getWindowsGroupStatus, getWindowsWfpStatus, installWindowsSandbox, uninstallWindowsSandbox, createWindowsGroup, deleteWindowsGroup, createWindowsWfp, windowsInstallInstructions, DEFAULT_WINDOWS_GROUP_NAME, DEFAULT_WINDOWS_PROXY_PORT_RANGE, } from './sandbox/windows-sandbox-utils.js';
|
|
6
|
+
export { getSrtWinPath, getWindowsGroupStatus, getWindowsWfpStatus, getWindowsSandboxUserStatus, getWindowsSandboxCaCert, windowsTrustCa, installWindowsSandbox, uninstallWindowsSandbox, createWindowsGroup, deleteWindowsGroup, createWindowsWfp, windowsInstallInstructions, stampWindowsAcl, restoreWindowsAcl, expandWindowsFsDenyPaths, WINDOWS_ACL_PATH_OK, WINDOWS_ACL_PARENT_OK, DEFAULT_WINDOWS_GROUP_NAME, DEFAULT_WINDOWS_PROXY_PORT_RANGE, } from './sandbox/windows-sandbox-utils.js';
|
|
7
7
|
export { WindowsConfigSchema } from './sandbox/sandbox-config.js';
|
|
8
8
|
// Utility functions
|
|
9
9
|
export { getDefaultWritePaths } from './sandbox/sandbox-utils.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAc5E,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAc5E,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,6BAA6B,CAAA;AAuBpC,6BAA6B;AAC7B,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,oCAAoC,CAAA;AAiB3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,oBAAoB;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAEjE,qBAAqB;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-file credential masking (Linux).
|
|
3
|
+
*
|
|
4
|
+
* For a `credentials.files` entry with `mode: "mask"`, srt reads the real
|
|
5
|
+
* file content on the host, registers a sentinel for it in the
|
|
6
|
+
* {@link SentinelRegistry}, and writes the sentinel to a fake file in a
|
|
7
|
+
* manager-owned temp directory. The Linux sandbox then `--ro-bind`s the
|
|
8
|
+
* fake over the real path, so the sandboxed process reads the sentinel.
|
|
9
|
+
* The proxy substitution from env-var masking already scans every header
|
|
10
|
+
* for any registered sentinel, so a tool that does
|
|
11
|
+
* `Authorization: Bearer $(cat <maskedFile>)` reaches the upstream with
|
|
12
|
+
* the real bytes — no proxy changes required.
|
|
13
|
+
*
|
|
14
|
+
* On macOS, SBPL cannot redirect reads, so masked files degrade to
|
|
15
|
+
* `mode: "deny"` (see macos-sandbox-utils.ts).
|
|
16
|
+
*
|
|
17
|
+
* LIMITATION: this is whole-file masking. It works when the file content
|
|
18
|
+
* *is* the credential (a token file). It does not work for structured
|
|
19
|
+
* files a tool parses (JSON/YAML/.netrc) — the tool will fail to parse
|
|
20
|
+
* the sentinel. See {@link CredentialFileConfigSchema}.
|
|
21
|
+
*/
|
|
22
|
+
import type { CredentialFileConfig } from './sandbox-config.js';
|
|
23
|
+
import type { SentinelRegistry } from './credential-sentinel.js';
|
|
24
|
+
/** One masked file's bind mapping for the platform builder. */
|
|
25
|
+
export interface MaskedFileBind {
|
|
26
|
+
/** Resolved (tilde-expanded, realpath'd) host path of the real file. */
|
|
27
|
+
realPath: string;
|
|
28
|
+
/** Path to the fake file containing the sentinel. */
|
|
29
|
+
fakePath: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Manager-owned temp dir holding the fake files.
|
|
33
|
+
*
|
|
34
|
+
* INVARIANT: this directory must never be writable from inside the sandbox.
|
|
35
|
+
* The Linux layer enforces this by emitting `--ro-bind <dirPath> <dirPath>`
|
|
36
|
+
* after every other filesystem mount (see generateFilesystemArgs), so the
|
|
37
|
+
* store stays read-only even if a caller's allowWrite covers os.tmpdir() or
|
|
38
|
+
* the host's $TMPDIR points under a default-writable path. If the sandbox
|
|
39
|
+
* could write here it could replace a fake's content (the bind exposes the
|
|
40
|
+
* source file) or plant a symlink for a later host-side write() to follow.
|
|
41
|
+
*/
|
|
42
|
+
export declare class MaskedFileStore {
|
|
43
|
+
private dir;
|
|
44
|
+
private readonly byKey;
|
|
45
|
+
/**
|
|
46
|
+
* Write `sentinel` to a fake file for `key` and return its path.
|
|
47
|
+
* Idempotent on `key`: a repeat call rewrites the same fake (so a
|
|
48
|
+
* changed sentinel after re-register propagates) instead of leaking a
|
|
49
|
+
* new file per wrapWithSandbox() call.
|
|
50
|
+
*/
|
|
51
|
+
write(key: string, sentinel: string): string;
|
|
52
|
+
/** Remove the temp dir and every fake file in it. Idempotent. */
|
|
53
|
+
dispose(): void;
|
|
54
|
+
/** Temp dir path, or undefined if no fake has been written yet. */
|
|
55
|
+
get dirPath(): string | undefined;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* For each `mode: "mask"` file entry: resolve the path, read the real
|
|
59
|
+
* content, register `(file:<path>, content, injectHosts)` in `registry`,
|
|
60
|
+
* write the sentinel to a fake file via `store`, and return the bind list.
|
|
61
|
+
*
|
|
62
|
+
* Entries whose path does not exist, is unreadable, or resolves to a
|
|
63
|
+
* directory are skipped with a debug log — same posture as a masked env
|
|
64
|
+
* var that's unset on the host: nothing to protect, and surfacing a hard
|
|
65
|
+
* error would make a portable config brittle across machines.
|
|
66
|
+
*
|
|
67
|
+
* The directory check is the authoritative one (the schema only catches a
|
|
68
|
+
* trailing slash); whole-file masking has no meaning for a directory.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildMaskedFileBinds(files: readonly CredentialFileConfig[], allowedDomains: readonly string[], registry: SentinelRegistry, store: MaskedFileStore): MaskedFileBind[];
|
|
71
|
+
export declare const MASKED_FILE_STORE_PREFIX = "srt-credmask-";
|
|
72
|
+
//# sourceMappingURL=credential-mask-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-mask-files.d.ts","sourceRoot":"","sources":["../../src/sandbox/credential-mask-files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAShE,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAA;IAChB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAElD;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAoB5C,iEAAiE;IACjE,OAAO,IAAI,IAAI;IAcf,mEAAmE;IACnE,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;CACF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,oBAAoB,EAAE,EACtC,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,eAAe,GACrB,cAAc,EAAE,CA8ClB;AAED,eAAO,MAAM,wBAAwB,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-file credential masking (Linux).
|
|
3
|
+
*
|
|
4
|
+
* For a `credentials.files` entry with `mode: "mask"`, srt reads the real
|
|
5
|
+
* file content on the host, registers a sentinel for it in the
|
|
6
|
+
* {@link SentinelRegistry}, and writes the sentinel to a fake file in a
|
|
7
|
+
* manager-owned temp directory. The Linux sandbox then `--ro-bind`s the
|
|
8
|
+
* fake over the real path, so the sandboxed process reads the sentinel.
|
|
9
|
+
* The proxy substitution from env-var masking already scans every header
|
|
10
|
+
* for any registered sentinel, so a tool that does
|
|
11
|
+
* `Authorization: Bearer $(cat <maskedFile>)` reaches the upstream with
|
|
12
|
+
* the real bytes — no proxy changes required.
|
|
13
|
+
*
|
|
14
|
+
* On macOS, SBPL cannot redirect reads, so masked files degrade to
|
|
15
|
+
* `mode: "deny"` (see macos-sandbox-utils.ts).
|
|
16
|
+
*
|
|
17
|
+
* LIMITATION: this is whole-file masking. It works when the file content
|
|
18
|
+
* *is* the credential (a token file). It does not work for structured
|
|
19
|
+
* files a tool parses (JSON/YAML/.netrc) — the tool will fail to parse
|
|
20
|
+
* the sentinel. See {@link CredentialFileConfigSchema}.
|
|
21
|
+
*/
|
|
22
|
+
import * as fs from 'node:fs';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { logForDebugging } from '../utils/debug.js';
|
|
26
|
+
import { normalizePathForSandbox } from './sandbox-utils.js';
|
|
27
|
+
/**
|
|
28
|
+
* Sentinel-registry key prefix for masked files. Keeps file keys disjoint
|
|
29
|
+
* from env-var names so a credential file at path `GH_TOKEN` cannot collide
|
|
30
|
+
* with the env var `GH_TOKEN`.
|
|
31
|
+
*/
|
|
32
|
+
const FILE_KEY_PREFIX = 'file:';
|
|
33
|
+
/**
|
|
34
|
+
* Manager-owned temp dir holding the fake files.
|
|
35
|
+
*
|
|
36
|
+
* INVARIANT: this directory must never be writable from inside the sandbox.
|
|
37
|
+
* The Linux layer enforces this by emitting `--ro-bind <dirPath> <dirPath>`
|
|
38
|
+
* after every other filesystem mount (see generateFilesystemArgs), so the
|
|
39
|
+
* store stays read-only even if a caller's allowWrite covers os.tmpdir() or
|
|
40
|
+
* the host's $TMPDIR points under a default-writable path. If the sandbox
|
|
41
|
+
* could write here it could replace a fake's content (the bind exposes the
|
|
42
|
+
* source file) or plant a symlink for a later host-side write() to follow.
|
|
43
|
+
*/
|
|
44
|
+
export class MaskedFileStore {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.byKey = new Map();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Write `sentinel` to a fake file for `key` and return its path.
|
|
50
|
+
* Idempotent on `key`: a repeat call rewrites the same fake (so a
|
|
51
|
+
* changed sentinel after re-register propagates) instead of leaking a
|
|
52
|
+
* new file per wrapWithSandbox() call.
|
|
53
|
+
*/
|
|
54
|
+
write(key, sentinel) {
|
|
55
|
+
if (this.dir === undefined) {
|
|
56
|
+
this.dir = fs.mkdtempSync(join(tmpdir(), 'srt-credmask-'));
|
|
57
|
+
}
|
|
58
|
+
let fakePath = this.byKey.get(key);
|
|
59
|
+
if (fakePath === undefined) {
|
|
60
|
+
fakePath = join(this.dir, `${this.byKey.size}.fake`);
|
|
61
|
+
this.byKey.set(key, fakePath);
|
|
62
|
+
}
|
|
63
|
+
// Never follow a symlink at fakePath: a prior sandbox invocation may
|
|
64
|
+
// have planted one (the store dir is ro-bound now, but defence in
|
|
65
|
+
// depth). Unlink first so writeFileSync creates a fresh regular file.
|
|
66
|
+
fs.rmSync(fakePath, { force: true });
|
|
67
|
+
// 0600: owner rw so the idempotent rewrite above succeeds; the bind
|
|
68
|
+
// into the sandbox is --ro-bind so the sandboxed process sees it
|
|
69
|
+
// read-only regardless of the host mode. No group/other.
|
|
70
|
+
fs.writeFileSync(fakePath, sentinel, { mode: 0o600 });
|
|
71
|
+
return fakePath;
|
|
72
|
+
}
|
|
73
|
+
/** Remove the temp dir and every fake file in it. Idempotent. */
|
|
74
|
+
dispose() {
|
|
75
|
+
if (this.dir !== undefined) {
|
|
76
|
+
try {
|
|
77
|
+
fs.rmSync(this.dir, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
logForDebugging(`MaskedFileStore cleanup failed: ${err}`, {
|
|
81
|
+
level: 'error',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.dir = undefined;
|
|
86
|
+
this.byKey.clear();
|
|
87
|
+
}
|
|
88
|
+
/** Temp dir path, or undefined if no fake has been written yet. */
|
|
89
|
+
get dirPath() {
|
|
90
|
+
return this.dir;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* For each `mode: "mask"` file entry: resolve the path, read the real
|
|
95
|
+
* content, register `(file:<path>, content, injectHosts)` in `registry`,
|
|
96
|
+
* write the sentinel to a fake file via `store`, and return the bind list.
|
|
97
|
+
*
|
|
98
|
+
* Entries whose path does not exist, is unreadable, or resolves to a
|
|
99
|
+
* directory are skipped with a debug log — same posture as a masked env
|
|
100
|
+
* var that's unset on the host: nothing to protect, and surfacing a hard
|
|
101
|
+
* error would make a portable config brittle across machines.
|
|
102
|
+
*
|
|
103
|
+
* The directory check is the authoritative one (the schema only catches a
|
|
104
|
+
* trailing slash); whole-file masking has no meaning for a directory.
|
|
105
|
+
*/
|
|
106
|
+
export function buildMaskedFileBinds(files, allowedDomains, registry, store) {
|
|
107
|
+
const binds = [];
|
|
108
|
+
for (const f of files) {
|
|
109
|
+
if (f.mode !== 'mask')
|
|
110
|
+
continue;
|
|
111
|
+
const realPath = normalizePathForSandbox(f.path);
|
|
112
|
+
let content;
|
|
113
|
+
try {
|
|
114
|
+
const stat = fs.statSync(realPath);
|
|
115
|
+
if (stat.isDirectory()) {
|
|
116
|
+
logForDebugging(`[credential-mask] Skipping masked file entry that resolves to ` +
|
|
117
|
+
`a directory: ${f.path} — use mode "deny" for directories.`, { level: 'warn' });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
// Read as bytes first: a utf8 read silently maps invalid sequences
|
|
121
|
+
// to U+FFFD, so the sentinel would round-trip to corrupted bytes at
|
|
122
|
+
// the proxy. Whole-file masking is for token files; reject binary.
|
|
123
|
+
const raw = fs.readFileSync(realPath);
|
|
124
|
+
content = raw.toString('utf8');
|
|
125
|
+
if (Buffer.byteLength(content, 'utf8') !== raw.length) {
|
|
126
|
+
logForDebugging(`[credential-mask] Skipping masked file with non-UTF-8 content ` +
|
|
127
|
+
`(binary credential files are not supported in whole-file ` +
|
|
128
|
+
`mask mode): ${f.path}`, { level: 'warn' });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
logForDebugging(`[credential-mask] Skipping masked file (unreadable on host): ` +
|
|
134
|
+
`${f.path} — ${err.message}`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const injectHosts = f.injectHosts ?? allowedDomains;
|
|
138
|
+
const key = FILE_KEY_PREFIX + realPath;
|
|
139
|
+
const sentinel = registry.register(key, content, injectHosts);
|
|
140
|
+
const fakePath = store.write(key, sentinel);
|
|
141
|
+
binds.push({ realPath, fakePath });
|
|
142
|
+
}
|
|
143
|
+
return binds;
|
|
144
|
+
}
|
|
145
|
+
export const MASKED_FILE_STORE_PREFIX = 'srt-credmask-';
|
|
146
|
+
//# sourceMappingURL=credential-mask-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-mask-files.js","sourceRoot":"","sources":["../../src/sandbox/credential-mask-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session sentinel registry for credential masking.
|
|
3
|
+
*
|
|
4
|
+
* A masked credential's real value is replaced inside the sandbox with a
|
|
5
|
+
* sentinel of the form `fake_value_<uuid4>`. The sandboxed process sees only
|
|
6
|
+
* the sentinel; the host-side proxy substitutes sentinel→real on egress to
|
|
7
|
+
* allowlisted destinations. The map lives only in process memory — it is
|
|
8
|
+
* never written to disk and never logged.
|
|
9
|
+
*/
|
|
10
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
11
|
+
export declare const SENTINEL_PREFIX = "fake_value_";
|
|
12
|
+
/** Predicate matching a destination host against one injectHosts pattern. */
|
|
13
|
+
export type HostMatcher = (host: string, pattern: string) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Sentinel↔real-value map for one sandbox session, keyed by credential name.
|
|
16
|
+
*
|
|
17
|
+
* Each credential carries its own `injectHosts` list, and substitution is
|
|
18
|
+
* gated per sentinel: a sentinel is swapped to its real value only when the
|
|
19
|
+
* destination matches THAT credential's hosts. This prevents laundering
|
|
20
|
+
* credential A through credential B's allowlisted host by sending A's
|
|
21
|
+
* sentinel there — the proxy leaves A's sentinel intact on B's host.
|
|
22
|
+
*
|
|
23
|
+
* Keying on name (not value) means two env vars holding the same secret get
|
|
24
|
+
* distinct sentinels, so each can have an independent host list.
|
|
25
|
+
*/
|
|
26
|
+
export declare class SentinelRegistry {
|
|
27
|
+
private readonly byName;
|
|
28
|
+
private readonly bySentinel;
|
|
29
|
+
/**
|
|
30
|
+
* Return the sentinel for the credential named `name`, minting a fresh one
|
|
31
|
+
* on first use. The sentinel is `fake_value_<uuid4>`: long enough that an
|
|
32
|
+
* accidental collision with legitimate header content is negligible, and
|
|
33
|
+
* free of shell/URL metacharacters so it survives `--setenv` and
|
|
34
|
+
* `env NAME=value` unquoted.
|
|
35
|
+
*
|
|
36
|
+
* Idempotent on `name`: a repeat call returns the same sentinel and updates
|
|
37
|
+
* `realValue`/`injectHosts` in place so `updateConfig()` can change either
|
|
38
|
+
* without invalidating sentinels the sandboxed process has already read.
|
|
39
|
+
*/
|
|
40
|
+
register(name: string, realValue: string, injectHosts: readonly string[]): string;
|
|
41
|
+
/** Real value for `sentinel`, or undefined if not registered. */
|
|
42
|
+
lookupReal(sentinel: string): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Names of the registered credentials whose `injectHosts` cover
|
|
45
|
+
* `destHost`. Diagnostic helper: the proxy uses it to warn when a host is
|
|
46
|
+
* exempted from TLS termination (so substitution can never run there) but
|
|
47
|
+
* a masked credential is configured for injection at it.
|
|
48
|
+
*/
|
|
49
|
+
namesInjectableAt(destHost: string, matches: HostMatcher): string[];
|
|
50
|
+
/** Iterate registered `[sentinel, realValue]` pairs. */
|
|
51
|
+
entries(): IterableIterator<[string, string]>;
|
|
52
|
+
/** Number of registered sentinels. */
|
|
53
|
+
get size(): number;
|
|
54
|
+
/** Drop every mapping. Called on session teardown. */
|
|
55
|
+
clear(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Replace registered sentinels found in `headers` with their real values,
|
|
58
|
+
* in place. Each sentinel substitutes only when `destHost` matches one of
|
|
59
|
+
* THAT credential's `injectHosts` patterns (via `matches`); a sentinel
|
|
60
|
+
* whose host list does not cover `destHost` is left as the useless fake.
|
|
61
|
+
*
|
|
62
|
+
* Scans all header values rather than a fixed set — a sentinel showing up
|
|
63
|
+
* anywhere is the substitution trigger, regardless of header name
|
|
64
|
+
* (Authorization, X-Api-Key, Private-Token, ...).
|
|
65
|
+
*
|
|
66
|
+
* The caller remains responsible for transport gating (TLS-terminated path
|
|
67
|
+
* unless `allowPlaintextInject`).
|
|
68
|
+
*/
|
|
69
|
+
substituteInHeaders(headers: IncomingHttpHeaders, destHost: string, matches: HostMatcher): void;
|
|
70
|
+
private substituteInString;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=credential-sentinel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-sentinel.d.ts","sourceRoot":"","sources":["../../src/sandbox/credential-sentinel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAE5C,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;AASpE;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAE9D;;;;;;;;;;OAUG;IACH,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,MAAM;IAcT,iEAAiE;IACjE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;OAKG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE;IAQnE,wDAAwD;IACvD,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAI9C,sCAAsC;IACtC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,sDAAsD;IACtD,KAAK,IAAI,IAAI;IAKb;;;;;;;;;;;;OAYG;IACH,mBAAmB,CACjB,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,GACnB,IAAI;IAcP,OAAO,CAAC,kBAAkB;CAgB3B"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session sentinel registry for credential masking.
|
|
3
|
+
*
|
|
4
|
+
* A masked credential's real value is replaced inside the sandbox with a
|
|
5
|
+
* sentinel of the form `fake_value_<uuid4>`. The sandboxed process sees only
|
|
6
|
+
* the sentinel; the host-side proxy substitutes sentinel→real on egress to
|
|
7
|
+
* allowlisted destinations. The map lives only in process memory — it is
|
|
8
|
+
* never written to disk and never logged.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
export const SENTINEL_PREFIX = 'fake_value_';
|
|
12
|
+
/**
|
|
13
|
+
* Sentinel↔real-value map for one sandbox session, keyed by credential name.
|
|
14
|
+
*
|
|
15
|
+
* Each credential carries its own `injectHosts` list, and substitution is
|
|
16
|
+
* gated per sentinel: a sentinel is swapped to its real value only when the
|
|
17
|
+
* destination matches THAT credential's hosts. This prevents laundering
|
|
18
|
+
* credential A through credential B's allowlisted host by sending A's
|
|
19
|
+
* sentinel there — the proxy leaves A's sentinel intact on B's host.
|
|
20
|
+
*
|
|
21
|
+
* Keying on name (not value) means two env vars holding the same secret get
|
|
22
|
+
* distinct sentinels, so each can have an independent host list.
|
|
23
|
+
*/
|
|
24
|
+
export class SentinelRegistry {
|
|
25
|
+
constructor() {
|
|
26
|
+
this.byName = new Map();
|
|
27
|
+
this.bySentinel = new Map();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Return the sentinel for the credential named `name`, minting a fresh one
|
|
31
|
+
* on first use. The sentinel is `fake_value_<uuid4>`: long enough that an
|
|
32
|
+
* accidental collision with legitimate header content is negligible, and
|
|
33
|
+
* free of shell/URL metacharacters so it survives `--setenv` and
|
|
34
|
+
* `env NAME=value` unquoted.
|
|
35
|
+
*
|
|
36
|
+
* Idempotent on `name`: a repeat call returns the same sentinel and updates
|
|
37
|
+
* `realValue`/`injectHosts` in place so `updateConfig()` can change either
|
|
38
|
+
* without invalidating sentinels the sandboxed process has already read.
|
|
39
|
+
*/
|
|
40
|
+
register(name, realValue, injectHosts) {
|
|
41
|
+
const existing = this.byName.get(name);
|
|
42
|
+
if (existing !== undefined) {
|
|
43
|
+
existing.realValue = realValue;
|
|
44
|
+
existing.injectHosts = injectHosts;
|
|
45
|
+
return existing.sentinel;
|
|
46
|
+
}
|
|
47
|
+
const sentinel = SENTINEL_PREFIX + randomUUID();
|
|
48
|
+
const entry = { name, sentinel, realValue, injectHosts };
|
|
49
|
+
this.byName.set(name, entry);
|
|
50
|
+
this.bySentinel.set(sentinel, entry);
|
|
51
|
+
return sentinel;
|
|
52
|
+
}
|
|
53
|
+
/** Real value for `sentinel`, or undefined if not registered. */
|
|
54
|
+
lookupReal(sentinel) {
|
|
55
|
+
return this.bySentinel.get(sentinel)?.realValue;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Names of the registered credentials whose `injectHosts` cover
|
|
59
|
+
* `destHost`. Diagnostic helper: the proxy uses it to warn when a host is
|
|
60
|
+
* exempted from TLS termination (so substitution can never run there) but
|
|
61
|
+
* a masked credential is configured for injection at it.
|
|
62
|
+
*/
|
|
63
|
+
namesInjectableAt(destHost, matches) {
|
|
64
|
+
const names = [];
|
|
65
|
+
for (const e of this.byName.values()) {
|
|
66
|
+
if (e.injectHosts.some(p => matches(destHost, p)))
|
|
67
|
+
names.push(e.name);
|
|
68
|
+
}
|
|
69
|
+
return names;
|
|
70
|
+
}
|
|
71
|
+
/** Iterate registered `[sentinel, realValue]` pairs. */
|
|
72
|
+
*entries() {
|
|
73
|
+
for (const e of this.bySentinel.values())
|
|
74
|
+
yield [e.sentinel, e.realValue];
|
|
75
|
+
}
|
|
76
|
+
/** Number of registered sentinels. */
|
|
77
|
+
get size() {
|
|
78
|
+
return this.bySentinel.size;
|
|
79
|
+
}
|
|
80
|
+
/** Drop every mapping. Called on session teardown. */
|
|
81
|
+
clear() {
|
|
82
|
+
this.byName.clear();
|
|
83
|
+
this.bySentinel.clear();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Replace registered sentinels found in `headers` with their real values,
|
|
87
|
+
* in place. Each sentinel substitutes only when `destHost` matches one of
|
|
88
|
+
* THAT credential's `injectHosts` patterns (via `matches`); a sentinel
|
|
89
|
+
* whose host list does not cover `destHost` is left as the useless fake.
|
|
90
|
+
*
|
|
91
|
+
* Scans all header values rather than a fixed set — a sentinel showing up
|
|
92
|
+
* anywhere is the substitution trigger, regardless of header name
|
|
93
|
+
* (Authorization, X-Api-Key, Private-Token, ...).
|
|
94
|
+
*
|
|
95
|
+
* The caller remains responsible for transport gating (TLS-terminated path
|
|
96
|
+
* unless `allowPlaintextInject`).
|
|
97
|
+
*/
|
|
98
|
+
substituteInHeaders(headers, destHost, matches) {
|
|
99
|
+
if (this.bySentinel.size === 0)
|
|
100
|
+
return;
|
|
101
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
102
|
+
if (value === undefined)
|
|
103
|
+
continue;
|
|
104
|
+
if (Array.isArray(value)) {
|
|
105
|
+
for (let i = 0; i < value.length; i++) {
|
|
106
|
+
value[i] = this.substituteInString(value[i], destHost, matches);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
headers[name] = this.substituteInString(value, destHost, matches);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
substituteInString(s, destHost, matches) {
|
|
115
|
+
// Fast path: the sentinel prefix is fixed, so a header value that
|
|
116
|
+
// doesn't contain it cannot contain any sentinel.
|
|
117
|
+
if (!s.includes(SENTINEL_PREFIX))
|
|
118
|
+
return s;
|
|
119
|
+
let out = s;
|
|
120
|
+
for (const e of this.bySentinel.values()) {
|
|
121
|
+
if (!out.includes(e.sentinel))
|
|
122
|
+
continue;
|
|
123
|
+
if (!e.injectHosts.some(p => matches(destHost, p)))
|
|
124
|
+
continue;
|
|
125
|
+
out = out.split(e.sentinel).join(e.realValue);
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=credential-sentinel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-sentinel.js","sourceRoot":"","sources":["../../src/sandbox/credential-sentinel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-pattern matching shared between runtime host filtering
|
|
3
|
+
* (sandbox-manager) and config-time validation (sandbox-config).
|
|
4
|
+
* Lives in its own module so the schema can import it without pulling
|
|
5
|
+
* in sandbox-manager (which imports the schema — circular).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Match a hostname against a domain pattern.
|
|
9
|
+
*
|
|
10
|
+
* Patterns:
|
|
11
|
+
* - `*` matches everything (deny-all; the schema only accepts this in
|
|
12
|
+
* deniedDomains).
|
|
13
|
+
* - `*.example.com` matches any strict subdomain of example.com.
|
|
14
|
+
* - anything else matches exactly (case-insensitive).
|
|
15
|
+
*
|
|
16
|
+
* Wildcard suffix matching is refused for IP literals so an IPv6 zone-ID
|
|
17
|
+
* payload like `::ffff:1.2.3.4%x.allowed.com` cannot pass `.endsWith()`
|
|
18
|
+
* while the OS connects to the bare IP. isValidHost already rejects `%`,
|
|
19
|
+
* but we refuse here too for defence in depth.
|
|
20
|
+
*/
|
|
21
|
+
export declare function matchesDomainPattern(hostname: string, pattern: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Decide whether a per-credential `injectHosts` entry is reachable via
|
|
24
|
+
* `network.allowedDomains` — i.e. every concrete host that could match
|
|
25
|
+
* `injectHost` is allowed by at least one entry in `allowedDomains`.
|
|
26
|
+
*
|
|
27
|
+
* For an exact `injectHost` (`api.github.com`) this is just
|
|
28
|
+
* `matchesDomainPattern` against each allowed pattern.
|
|
29
|
+
*
|
|
30
|
+
* For a wildcard `injectHost` (`*.X`), an exact allowedDomain can never
|
|
31
|
+
* cover it (it admits only one host), so coverage requires an allowed
|
|
32
|
+
* wildcard `*.Y` whose base is `X` or an ancestor of `X` — e.g.
|
|
33
|
+
* `*.api.github.com` is covered by `*.github.com`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isInjectHostCoveredByAllowedDomains(injectHost: string, allowedDomains: readonly string[]): boolean;
|
|
36
|
+
//# sourceMappingURL=domain-pattern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-pattern.d.ts","sourceRoot":"","sources":["../../src/sandbox/domain-pattern.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAST;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mCAAmC,CACjD,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,SAAS,MAAM,EAAE,GAChC,OAAO,CAUT"}
|