@sysid/sandbox-runtime-improved 0.0.50-sysid.1 → 0.0.51-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 +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sandbox/http-proxy.d.ts +21 -1
- package/dist/sandbox/http-proxy.d.ts.map +1 -1
- package/dist/sandbox/http-proxy.js +27 -2
- package/dist/sandbox/http-proxy.js.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.d.ts +2 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.js +3 -2
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.d.ts +2 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.js +2 -2
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -1
- package/dist/sandbox/mitm-ca.d.ts +33 -16
- package/dist/sandbox/mitm-ca.d.ts.map +1 -1
- package/dist/sandbox/mitm-ca.js +124 -24
- package/dist/sandbox/mitm-ca.js.map +1 -1
- package/dist/sandbox/mitm-leaf.d.ts +28 -0
- package/dist/sandbox/mitm-leaf.d.ts.map +1 -0
- package/dist/sandbox/mitm-leaf.js +94 -0
- package/dist/sandbox/mitm-leaf.js.map +1 -0
- package/dist/sandbox/request-filter.d.ts +46 -0
- package/dist/sandbox/request-filter.d.ts.map +1 -0
- package/dist/sandbox/request-filter.js +106 -0
- package/dist/sandbox/request-filter.js.map +1 -0
- package/dist/sandbox/sandbox-config.d.ts +47 -26
- package/dist/sandbox/sandbox-config.d.ts.map +1 -1
- package/dist/sandbox/sandbox-config.js +21 -3
- package/dist/sandbox/sandbox-config.js.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts +2 -0
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.js +143 -98
- package/dist/sandbox/sandbox-manager.js.map +1 -1
- package/dist/sandbox/sandbox-utils.d.ts +6 -1
- package/dist/sandbox/sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/sandbox-utils.js +24 -1
- package/dist/sandbox/sandbox-utils.js.map +1 -1
- package/dist/sandbox/socks-proxy.d.ts.map +1 -1
- package/dist/sandbox/socks-proxy.js +26 -9
- package/dist/sandbox/socks-proxy.js.map +1 -1
- package/dist/sandbox/tls-terminate-proxy.d.ts +41 -0
- package/dist/sandbox/tls-terminate-proxy.d.ts.map +1 -0
- package/dist/sandbox/tls-terminate-proxy.js +167 -0
- package/dist/sandbox/tls-terminate-proxy.js.map +1 -0
- package/package.json +7 -5
package/dist/sandbox/mitm-ca.js
CHANGED
|
@@ -1,38 +1,126 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MITM CA loader for the in-process TLS-terminating proxy.
|
|
2
|
+
* MITM CA loader/generator for the in-process TLS-terminating proxy.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The CA is supplied via `network.tlsTerminate.{caCertPath,caKeyPath}` (see
|
|
5
|
+
* sandbox-config.ts). If both paths are omitted, SRT generates an ephemeral
|
|
6
|
+
* RSA-2048 self-signed CA into a temp directory; the cert path is what the
|
|
7
|
+
* trust env vars point at. The caller is responsible for cleaning up via
|
|
8
|
+
* `disposeMitmCA()` (SandboxManager.reset() does this).
|
|
8
9
|
*/
|
|
9
|
-
import
|
|
10
|
+
import forge from 'node-forge';
|
|
11
|
+
import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { rm } from 'node:fs/promises';
|
|
13
|
+
import { tmpdir } from 'node:os';
|
|
14
|
+
import { join, dirname } from 'node:path';
|
|
10
15
|
import { logForDebugging } from '../utils/debug.js';
|
|
11
|
-
|
|
16
|
+
const { pki, md, random, util } = forge;
|
|
12
17
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
18
|
+
* Create a MitmCA. If `caCertPath`/`caKeyPath` are provided, load from disk
|
|
19
|
+
* (throws if either file is missing, unreadable, not PEM, fails to parse, or
|
|
20
|
+
* the key is not RSA). If both are omitted, generate an ephemeral CA into a
|
|
21
|
+
* fresh temp directory.
|
|
16
22
|
*
|
|
17
|
-
*
|
|
23
|
+
* Pure factory: no module-level state. The caller (SandboxManager) owns the
|
|
24
|
+
* returned object and its lifetime.
|
|
18
25
|
*/
|
|
19
|
-
export function
|
|
20
|
-
if (
|
|
21
|
-
return
|
|
22
|
-
|
|
26
|
+
export function createMitmCA(opts) {
|
|
27
|
+
if (opts.caCertPath && opts.caKeyPath) {
|
|
28
|
+
return loadCA(opts.caCertPath, opts.caKeyPath);
|
|
29
|
+
}
|
|
30
|
+
if (opts.caCertPath || opts.caKeyPath) {
|
|
31
|
+
throw new Error('tlsTerminate: caCertPath and caKeyPath must be provided together');
|
|
32
|
+
}
|
|
33
|
+
return generateEphemeralCA();
|
|
34
|
+
}
|
|
35
|
+
/** Remove the temp directory for an SRT-generated CA. No-op for user CAs. */
|
|
36
|
+
export async function disposeMitmCA(ca) {
|
|
37
|
+
if (!ca.ephemeral)
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
await rm(dirname(ca.certPath), { recursive: true, force: true });
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
logForDebugging(`[mitm-ca] cleanup failed: ${err.message}`, {
|
|
44
|
+
level: 'warn',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function loadCA(certPath, keyPath) {
|
|
23
49
|
const certPem = readPem(certPath, 'CERTIFICATE', 'tlsTerminate.caCertPath');
|
|
24
50
|
const keyPem = readPem(keyPath, 'PRIVATE KEY', 'tlsTerminate.caKeyPath');
|
|
25
|
-
|
|
51
|
+
let cert;
|
|
52
|
+
let key;
|
|
53
|
+
try {
|
|
54
|
+
cert = pki.certificateFromPem(certPem);
|
|
55
|
+
key = pki.privateKeyFromPem(keyPem);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new Error(`tlsTerminate: failed to parse CA from ${certPath}: ` +
|
|
59
|
+
err.message);
|
|
60
|
+
}
|
|
61
|
+
if (!('n' in key) || !('d' in key)) {
|
|
62
|
+
// node-forge can only sign with RSA private keys.
|
|
63
|
+
throw new Error(`tlsTerminate.caKeyPath: CA key at ${keyPath} must be RSA`);
|
|
64
|
+
}
|
|
26
65
|
logForDebugging(`[mitm-ca] loaded CA from ${certPath}`);
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
66
|
+
return {
|
|
67
|
+
certPath,
|
|
68
|
+
keyPath,
|
|
69
|
+
certPem,
|
|
70
|
+
keyPem,
|
|
71
|
+
cert,
|
|
72
|
+
key: key,
|
|
73
|
+
leafCerts: new Map(),
|
|
74
|
+
secureContexts: new Map(),
|
|
75
|
+
ephemeral: false,
|
|
76
|
+
};
|
|
32
77
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
78
|
+
function generateEphemeralCA() {
|
|
79
|
+
const keys = pki.rsa.generateKeyPair(2048);
|
|
80
|
+
const cert = pki.createCertificate();
|
|
81
|
+
cert.publicKey = keys.publicKey;
|
|
82
|
+
cert.serialNumber = randomSerial();
|
|
83
|
+
cert.validity.notBefore = daysFromNow(-1);
|
|
84
|
+
cert.validity.notAfter = daysFromNow(825);
|
|
85
|
+
const subject = [
|
|
86
|
+
{ name: 'commonName', value: 'sandbox-runtime ephemeral CA' },
|
|
87
|
+
{ name: 'organizationName', value: 'sandbox-runtime' },
|
|
88
|
+
];
|
|
89
|
+
cert.setSubject(subject);
|
|
90
|
+
cert.setIssuer(subject);
|
|
91
|
+
cert.setExtensions([
|
|
92
|
+
{ name: 'basicConstraints', cA: true, critical: true },
|
|
93
|
+
{
|
|
94
|
+
name: 'keyUsage',
|
|
95
|
+
critical: true,
|
|
96
|
+
keyCertSign: true,
|
|
97
|
+
cRLSign: true,
|
|
98
|
+
digitalSignature: true,
|
|
99
|
+
},
|
|
100
|
+
{ name: 'subjectKeyIdentifier' },
|
|
101
|
+
]);
|
|
102
|
+
cert.sign(keys.privateKey, md.sha256.create());
|
|
103
|
+
const certPem = pki.certificateToPem(cert);
|
|
104
|
+
const keyPem = pki.privateKeyToPem(keys.privateKey);
|
|
105
|
+
// Write to disk so trust env vars (NODE_EXTRA_CA_CERTS etc.) can point at
|
|
106
|
+
// a real path. mkdtemp gives us an unguessable per-process directory.
|
|
107
|
+
const dir = mkdtempSync(join(tmpdir(), 'srt-ca-'));
|
|
108
|
+
const certPath = join(dir, 'ca.crt');
|
|
109
|
+
const keyPath = join(dir, 'ca.key');
|
|
110
|
+
writeFileSync(certPath, certPem, { mode: 0o644 });
|
|
111
|
+
writeFileSync(keyPath, keyPem, { mode: 0o600 });
|
|
112
|
+
logForDebugging(`[mitm-ca] generated ephemeral CA at ${certPath}`);
|
|
113
|
+
return {
|
|
114
|
+
certPath,
|
|
115
|
+
keyPath,
|
|
116
|
+
certPem,
|
|
117
|
+
keyPem,
|
|
118
|
+
cert,
|
|
119
|
+
key: keys.privateKey,
|
|
120
|
+
leafCerts: new Map(),
|
|
121
|
+
secureContexts: new Map(),
|
|
122
|
+
ephemeral: true,
|
|
123
|
+
};
|
|
36
124
|
}
|
|
37
125
|
function readPem(path, label, field) {
|
|
38
126
|
let pem;
|
|
@@ -50,4 +138,16 @@ function readPem(path, label, field) {
|
|
|
50
138
|
}
|
|
51
139
|
return pem;
|
|
52
140
|
}
|
|
141
|
+
function randomSerial() {
|
|
142
|
+
// 16 random bytes, high bit cleared so the DER INTEGER stays positive.
|
|
143
|
+
const bytes = random.getBytesSync(16);
|
|
144
|
+
const hex = util.bytesToHex(bytes);
|
|
145
|
+
const firstNibble = parseInt(hex[0], 16) & 0x7;
|
|
146
|
+
return firstNibble.toString(16) + hex.slice(1);
|
|
147
|
+
}
|
|
148
|
+
function daysFromNow(days) {
|
|
149
|
+
const d = new Date();
|
|
150
|
+
d.setDate(d.getDate() + days);
|
|
151
|
+
return d;
|
|
152
|
+
}
|
|
53
153
|
//# sourceMappingURL=mitm-ca.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mitm-ca.js","sourceRoot":"","sources":["../../src/sandbox/mitm-ca.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mitm-ca.js","sourceRoot":"","sources":["../../src/sandbox/mitm-ca.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-host leaf certificate minter for the in-process TLS-terminating proxy.
|
|
3
|
+
*
|
|
4
|
+
* Given a MitmCA (see mitm-ca.ts), mints a leaf certificate for a specific
|
|
5
|
+
* hostname on first use and caches it for the lifetime of that CA instance.
|
|
6
|
+
* The leaf is signed by the CA and carries SAN=DNS:<host> (or IP:<addr>),
|
|
7
|
+
* so a client that trusts the CA will accept it for that host.
|
|
8
|
+
*/
|
|
9
|
+
import { type SecureContext } from 'node:tls';
|
|
10
|
+
import type { MitmCA } from './mitm-ca.js';
|
|
11
|
+
export type LeafCert = {
|
|
12
|
+
/** Leaf cert PEM followed by the CA cert PEM (full chain). */
|
|
13
|
+
certPem: string;
|
|
14
|
+
/** Leaf private key PEM. */
|
|
15
|
+
keyPem: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Mint (or return cached) an RSA-2048 leaf cert for `hostname`, signed by `ca`.
|
|
19
|
+
* The cache lives on `ca.leafCerts`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mintLeafCert(ca: MitmCA, hostname: string): LeafCert;
|
|
22
|
+
/**
|
|
23
|
+
* Mint-or-cache a Node TLS SecureContext for `hostname`. Intended as the
|
|
24
|
+
* SNICallback target in the terminating proxy. The cache lives on
|
|
25
|
+
* `ca.secureContexts`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function secureContextFor(ca: MitmCA, hostname: string): SecureContext;
|
|
28
|
+
//# sourceMappingURL=mitm-leaf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mitm-leaf.d.ts","sourceRoot":"","sources":["../../src/sandbox/mitm-leaf.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAA;AAElE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAI1C,MAAM,MAAM,QAAQ,GAAG;IACrB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;IACf,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAsCnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAO5E"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-host leaf certificate minter for the in-process TLS-terminating proxy.
|
|
3
|
+
*
|
|
4
|
+
* Given a MitmCA (see mitm-ca.ts), mints a leaf certificate for a specific
|
|
5
|
+
* hostname on first use and caches it for the lifetime of that CA instance.
|
|
6
|
+
* The leaf is signed by the CA and carries SAN=DNS:<host> (or IP:<addr>),
|
|
7
|
+
* so a client that trusts the CA will accept it for that host.
|
|
8
|
+
*/
|
|
9
|
+
import forge from 'node-forge';
|
|
10
|
+
import { isIP } from 'node:net';
|
|
11
|
+
import { createSecureContext } from 'node:tls';
|
|
12
|
+
import { logForDebugging } from '../utils/debug.js';
|
|
13
|
+
const { pki, md, random, util } = forge;
|
|
14
|
+
/**
|
|
15
|
+
* Mint (or return cached) an RSA-2048 leaf cert for `hostname`, signed by `ca`.
|
|
16
|
+
* The cache lives on `ca.leafCerts`.
|
|
17
|
+
*/
|
|
18
|
+
export function mintLeafCert(ca, hostname) {
|
|
19
|
+
const cached = ca.leafCerts.get(hostname);
|
|
20
|
+
if (cached)
|
|
21
|
+
return cached;
|
|
22
|
+
const keys = pki.rsa.generateKeyPair(2048);
|
|
23
|
+
const cert = pki.createCertificate();
|
|
24
|
+
cert.publicKey = keys.publicKey;
|
|
25
|
+
cert.serialNumber = randomSerial();
|
|
26
|
+
cert.validity.notBefore = daysFromNow(-1);
|
|
27
|
+
cert.validity.notAfter = clampValidity(ca.cert);
|
|
28
|
+
cert.setSubject([{ name: 'commonName', value: hostname }]);
|
|
29
|
+
cert.setIssuer(ca.cert.subject.attributes);
|
|
30
|
+
cert.setExtensions([
|
|
31
|
+
{ name: 'basicConstraints', cA: false, critical: true },
|
|
32
|
+
{
|
|
33
|
+
name: 'keyUsage',
|
|
34
|
+
critical: true,
|
|
35
|
+
digitalSignature: true,
|
|
36
|
+
keyEncipherment: true,
|
|
37
|
+
},
|
|
38
|
+
{ name: 'extKeyUsage', serverAuth: true },
|
|
39
|
+
{ name: 'subjectAltName', altNames: [sanFor(hostname)] },
|
|
40
|
+
// No authorityKeyIdentifier: node-forge stores SKI as a hex string for
|
|
41
|
+
// in-memory certs (e.g. the ephemeral CA) but as raw bytes for certs
|
|
42
|
+
// parsed from PEM (e.g. a user-supplied CA). Passing the hex string as
|
|
43
|
+
// keyIdentifier produces an AKI that doesn't match the CA's SKI and
|
|
44
|
+
// chain verification fails. AKI is optional — issuer/subject DN match
|
|
45
|
+
// is sufficient — so omit it.
|
|
46
|
+
]);
|
|
47
|
+
cert.sign(ca.key, md.sha256.create());
|
|
48
|
+
const leaf = {
|
|
49
|
+
certPem: pki.certificateToPem(cert) + ca.certPem,
|
|
50
|
+
keyPem: pki.privateKeyToPem(keys.privateKey),
|
|
51
|
+
};
|
|
52
|
+
ca.leafCerts.set(hostname, leaf);
|
|
53
|
+
logForDebugging(`[mitm-leaf] minted RSA leaf for ${hostname}`);
|
|
54
|
+
return leaf;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Mint-or-cache a Node TLS SecureContext for `hostname`. Intended as the
|
|
58
|
+
* SNICallback target in the terminating proxy. The cache lives on
|
|
59
|
+
* `ca.secureContexts`.
|
|
60
|
+
*/
|
|
61
|
+
export function secureContextFor(ca, hostname) {
|
|
62
|
+
const cached = ca.secureContexts.get(hostname);
|
|
63
|
+
if (cached)
|
|
64
|
+
return cached;
|
|
65
|
+
const { certPem, keyPem } = mintLeafCert(ca, hostname);
|
|
66
|
+
const ctx = createSecureContext({ cert: certPem, key: keyPem });
|
|
67
|
+
ca.secureContexts.set(hostname, ctx);
|
|
68
|
+
return ctx;
|
|
69
|
+
}
|
|
70
|
+
function sanFor(hostname) {
|
|
71
|
+
// RFC 5280 GeneralName tags: 2 = dNSName, 7 = iPAddress.
|
|
72
|
+
return isIP(hostname) !== 0
|
|
73
|
+
? { type: 7, ip: hostname }
|
|
74
|
+
: { type: 2, value: hostname };
|
|
75
|
+
}
|
|
76
|
+
/** Leaf validity capped at min(CA notAfter, now+825d) per CA/B baseline. */
|
|
77
|
+
function clampValidity(caCert) {
|
|
78
|
+
const caEnd = caCert.validity.notAfter;
|
|
79
|
+
const max = daysFromNow(825);
|
|
80
|
+
return caEnd < max ? new Date(caEnd) : max;
|
|
81
|
+
}
|
|
82
|
+
function randomSerial() {
|
|
83
|
+
// 16 random bytes, high bit cleared so the DER INTEGER stays positive.
|
|
84
|
+
const bytes = random.getBytesSync(16);
|
|
85
|
+
const hex = util.bytesToHex(bytes);
|
|
86
|
+
const firstNibble = parseInt(hex[0], 16) & 0x7;
|
|
87
|
+
return firstNibble.toString(16) + hex.slice(1);
|
|
88
|
+
}
|
|
89
|
+
function daysFromNow(days) {
|
|
90
|
+
const d = new Date();
|
|
91
|
+
d.setDate(d.getDate() + days);
|
|
92
|
+
return d;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=mitm-leaf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mitm-leaf.js","sourceRoot":"","sources":["../../src/sandbox/mitm-leaf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-level filter hook for the forward proxy.
|
|
3
|
+
*
|
|
4
|
+
* Library consumers supply a `filterRequest` callback via
|
|
5
|
+
* `network.filterRequest`. It receives the parsed HTTP request (web-standard
|
|
6
|
+
* `Request`) and returns a decision. Applies to plain HTTP through the proxy
|
|
7
|
+
* and, when `tlsTerminate` is configured, to terminated HTTPS. The proxy
|
|
8
|
+
* enforces the decision; the library does not bless any matching DSL.
|
|
9
|
+
*/
|
|
10
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
11
|
+
import { Readable } from 'node:stream';
|
|
12
|
+
export type RequestDecision = {
|
|
13
|
+
action: 'allow' | 'deny';
|
|
14
|
+
/**
|
|
15
|
+
* Human-readable reason. For denials this is surfaced to the sandboxed
|
|
16
|
+
* client in the response body so the agent can tell a policy block from a
|
|
17
|
+
* network failure.
|
|
18
|
+
*/
|
|
19
|
+
reason?: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Called once per HTTP request that the proxy parses.
|
|
23
|
+
*
|
|
24
|
+
* - `request` is a web-standard `Request`: method, URL, headers, and a lazy
|
|
25
|
+
* `request.body` stream (one branch of a tee — reading it does not consume
|
|
26
|
+
* the bytes that get forwarded upstream). `request.signal` aborts when the
|
|
27
|
+
* client disconnects.
|
|
28
|
+
* - **Throwing or rejecting denies the request.** This is the failure
|
|
29
|
+
* contract for a security boundary: a buggy policy fails closed.
|
|
30
|
+
*/
|
|
31
|
+
export type FilterRequestCallback = (request: Request) => Promise<RequestDecision>;
|
|
32
|
+
/**
|
|
33
|
+
* Build a `Request`, run the callback, and if denied write the 403 response
|
|
34
|
+
* and return `null`. On allow, returns the body stream the caller must pipe
|
|
35
|
+
* upstream — this is the original `IncomingMessage` when no tee was needed
|
|
36
|
+
* (GET/HEAD/OPTIONS), or the upstream-side branch of the tee otherwise.
|
|
37
|
+
* Callers must pipe the returned stream (not `req`) to the outbound request.
|
|
38
|
+
*
|
|
39
|
+
* For methods that carry a body, `req` is converted to a web stream and
|
|
40
|
+
* `tee()`'d: one branch goes to the callback's `Request.body`, the other is
|
|
41
|
+
* returned for the caller to forward. If the callback never reads its
|
|
42
|
+
* branch, we cancel it after the decision so the tee does not buffer the
|
|
43
|
+
* entire upload.
|
|
44
|
+
*/
|
|
45
|
+
export declare function decideAndRespond(filterRequest: FilterRequestCallback, req: IncomingMessage, res: ServerResponse, url: string, signal: AbortSignal): Promise<Readable | null>;
|
|
46
|
+
//# sourceMappingURL=request-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-filter.d.ts","sourceRoot":"","sources":["../../src/sandbox/request-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAGtC,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAA;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,eAAe,CAAC,CAAA;AAI7B;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,qBAAqB,EACpC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAsD1B"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-level filter hook for the forward proxy.
|
|
3
|
+
*
|
|
4
|
+
* Library consumers supply a `filterRequest` callback via
|
|
5
|
+
* `network.filterRequest`. It receives the parsed HTTP request (web-standard
|
|
6
|
+
* `Request`) and returns a decision. Applies to plain HTTP through the proxy
|
|
7
|
+
* and, when `tlsTerminate` is configured, to terminated HTTPS. The proxy
|
|
8
|
+
* enforces the decision; the library does not bless any matching DSL.
|
|
9
|
+
*/
|
|
10
|
+
import { Readable } from 'node:stream';
|
|
11
|
+
import { logForDebugging } from '../utils/debug.js';
|
|
12
|
+
const BODYLESS_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
13
|
+
/**
|
|
14
|
+
* Build a `Request`, run the callback, and if denied write the 403 response
|
|
15
|
+
* and return `null`. On allow, returns the body stream the caller must pipe
|
|
16
|
+
* upstream — this is the original `IncomingMessage` when no tee was needed
|
|
17
|
+
* (GET/HEAD/OPTIONS), or the upstream-side branch of the tee otherwise.
|
|
18
|
+
* Callers must pipe the returned stream (not `req`) to the outbound request.
|
|
19
|
+
*
|
|
20
|
+
* For methods that carry a body, `req` is converted to a web stream and
|
|
21
|
+
* `tee()`'d: one branch goes to the callback's `Request.body`, the other is
|
|
22
|
+
* returned for the caller to forward. If the callback never reads its
|
|
23
|
+
* branch, we cancel it after the decision so the tee does not buffer the
|
|
24
|
+
* entire upload.
|
|
25
|
+
*/
|
|
26
|
+
export async function decideAndRespond(filterRequest, req, res, url, signal) {
|
|
27
|
+
let forCallback;
|
|
28
|
+
let forUpstream = req;
|
|
29
|
+
if (!BODYLESS_METHODS.has(req.method ?? 'GET')) {
|
|
30
|
+
const web = Readable.toWeb(req);
|
|
31
|
+
const [a, b] = web.tee();
|
|
32
|
+
forCallback = a;
|
|
33
|
+
forUpstream = Readable.fromWeb(b);
|
|
34
|
+
}
|
|
35
|
+
let webReq;
|
|
36
|
+
try {
|
|
37
|
+
webReq = new Request(url, {
|
|
38
|
+
method: req.method,
|
|
39
|
+
headers: incomingHeaders(req),
|
|
40
|
+
signal,
|
|
41
|
+
...(forCallback ? { body: forCallback, duplex: 'half' } : {}),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
// Malformed URL/headers from the client — deny rather than crash.
|
|
46
|
+
deny(res, {
|
|
47
|
+
action: 'deny',
|
|
48
|
+
reason: `malformed request: ${err.message}`,
|
|
49
|
+
});
|
|
50
|
+
void forCallback?.cancel();
|
|
51
|
+
forUpstream.destroy();
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
let decision;
|
|
55
|
+
try {
|
|
56
|
+
decision = await filterRequest(webReq);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
decision = {
|
|
60
|
+
action: 'deny',
|
|
61
|
+
reason: `filterRequest threw: ${err.message}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// If the callback didn't read its branch, cancel it so tee() stops
|
|
65
|
+
// buffering bytes nobody will consume. If it did, the tee already buffered
|
|
66
|
+
// whatever was read; the upstream branch sees the same bytes.
|
|
67
|
+
if (forCallback && !webReq.bodyUsed) {
|
|
68
|
+
void forCallback.cancel();
|
|
69
|
+
}
|
|
70
|
+
if (decision.action === 'allow') {
|
|
71
|
+
logForDebugging(`[request-filter] allow ${req.method} ${url}`);
|
|
72
|
+
return forUpstream;
|
|
73
|
+
}
|
|
74
|
+
deny(res, decision);
|
|
75
|
+
forUpstream.destroy();
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function deny(res, decision) {
|
|
79
|
+
const reason = decision.reason ?? 'denied by filterRequest';
|
|
80
|
+
logForDebugging(`[request-filter] deny: ${reason}`);
|
|
81
|
+
if (res.headersSent) {
|
|
82
|
+
res.destroy();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
res.writeHead(403, {
|
|
86
|
+
'Content-Type': 'text/plain',
|
|
87
|
+
'X-Proxy-Error': 'blocked-by-sandbox-runtime',
|
|
88
|
+
});
|
|
89
|
+
res.end(reason + '\n');
|
|
90
|
+
}
|
|
91
|
+
function incomingHeaders(req) {
|
|
92
|
+
const h = new Headers();
|
|
93
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
94
|
+
if (v === undefined)
|
|
95
|
+
continue;
|
|
96
|
+
if (Array.isArray(v)) {
|
|
97
|
+
for (const vv of v)
|
|
98
|
+
h.append(k, vv);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
h.append(k, v);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return h;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=request-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-filter.js","sourceRoot":"","sources":["../../src/sandbox/request-filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Configuration for Sandbox Runtime
|
|
3
3
|
* This is the main configuration interface that consumers pass to SandboxManager.initialize()
|
|
4
4
|
*/
|
|
5
|
+
import type { FilterRequestCallback } from './request-filter.js';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
/**
|
|
7
8
|
* Schema for MITM proxy configuration
|
|
@@ -57,15 +58,22 @@ export declare const NetworkConfigSchema: z.ZodObject<{
|
|
|
57
58
|
socketPath: string;
|
|
58
59
|
domains: string[];
|
|
59
60
|
}>>;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
filterRequest: z.ZodOptional<z.ZodType<FilterRequestCallback, z.ZodTypeDef, FilterRequestCallback>>;
|
|
62
|
+
tlsTerminate: z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
63
|
+
caCertPath: z.ZodOptional<z.ZodString>;
|
|
64
|
+
caKeyPath: z.ZodOptional<z.ZodString>;
|
|
63
65
|
}, "strip", z.ZodTypeAny, {
|
|
64
|
-
caCertPath
|
|
65
|
-
caKeyPath
|
|
66
|
+
caCertPath?: string | undefined;
|
|
67
|
+
caKeyPath?: string | undefined;
|
|
66
68
|
}, {
|
|
67
|
-
caCertPath
|
|
68
|
-
caKeyPath
|
|
69
|
+
caCertPath?: string | undefined;
|
|
70
|
+
caKeyPath?: string | undefined;
|
|
71
|
+
}>, {
|
|
72
|
+
caCertPath?: string | undefined;
|
|
73
|
+
caKeyPath?: string | undefined;
|
|
74
|
+
}, {
|
|
75
|
+
caCertPath?: string | undefined;
|
|
76
|
+
caKeyPath?: string | undefined;
|
|
69
77
|
}>>;
|
|
70
78
|
parentProxy: z.ZodOptional<z.ZodObject<{
|
|
71
79
|
http: z.ZodOptional<z.ZodString>;
|
|
@@ -93,9 +101,10 @@ export declare const NetworkConfigSchema: z.ZodObject<{
|
|
|
93
101
|
socketPath: string;
|
|
94
102
|
domains: string[];
|
|
95
103
|
} | undefined;
|
|
104
|
+
filterRequest?: FilterRequestCallback | undefined;
|
|
96
105
|
tlsTerminate?: {
|
|
97
|
-
caCertPath
|
|
98
|
-
caKeyPath
|
|
106
|
+
caCertPath?: string | undefined;
|
|
107
|
+
caKeyPath?: string | undefined;
|
|
99
108
|
} | undefined;
|
|
100
109
|
parentProxy?: {
|
|
101
110
|
http?: string | undefined;
|
|
@@ -115,9 +124,10 @@ export declare const NetworkConfigSchema: z.ZodObject<{
|
|
|
115
124
|
socketPath: string;
|
|
116
125
|
domains: string[];
|
|
117
126
|
} | undefined;
|
|
127
|
+
filterRequest?: FilterRequestCallback | undefined;
|
|
118
128
|
tlsTerminate?: {
|
|
119
|
-
caCertPath
|
|
120
|
-
caKeyPath
|
|
129
|
+
caCertPath?: string | undefined;
|
|
130
|
+
caKeyPath?: string | undefined;
|
|
121
131
|
} | undefined;
|
|
122
132
|
parentProxy?: {
|
|
123
133
|
http?: string | undefined;
|
|
@@ -204,15 +214,22 @@ export declare const SandboxRuntimeConfigSchema: z.ZodObject<{
|
|
|
204
214
|
socketPath: string;
|
|
205
215
|
domains: string[];
|
|
206
216
|
}>>;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
filterRequest: z.ZodOptional<z.ZodType<FilterRequestCallback, z.ZodTypeDef, FilterRequestCallback>>;
|
|
218
|
+
tlsTerminate: z.ZodOptional<z.ZodEffects<z.ZodObject<{
|
|
219
|
+
caCertPath: z.ZodOptional<z.ZodString>;
|
|
220
|
+
caKeyPath: z.ZodOptional<z.ZodString>;
|
|
210
221
|
}, "strip", z.ZodTypeAny, {
|
|
211
|
-
caCertPath
|
|
212
|
-
caKeyPath
|
|
222
|
+
caCertPath?: string | undefined;
|
|
223
|
+
caKeyPath?: string | undefined;
|
|
224
|
+
}, {
|
|
225
|
+
caCertPath?: string | undefined;
|
|
226
|
+
caKeyPath?: string | undefined;
|
|
227
|
+
}>, {
|
|
228
|
+
caCertPath?: string | undefined;
|
|
229
|
+
caKeyPath?: string | undefined;
|
|
213
230
|
}, {
|
|
214
|
-
caCertPath
|
|
215
|
-
caKeyPath
|
|
231
|
+
caCertPath?: string | undefined;
|
|
232
|
+
caKeyPath?: string | undefined;
|
|
216
233
|
}>>;
|
|
217
234
|
parentProxy: z.ZodOptional<z.ZodObject<{
|
|
218
235
|
http: z.ZodOptional<z.ZodString>;
|
|
@@ -240,9 +257,10 @@ export declare const SandboxRuntimeConfigSchema: z.ZodObject<{
|
|
|
240
257
|
socketPath: string;
|
|
241
258
|
domains: string[];
|
|
242
259
|
} | undefined;
|
|
260
|
+
filterRequest?: FilterRequestCallback | undefined;
|
|
243
261
|
tlsTerminate?: {
|
|
244
|
-
caCertPath
|
|
245
|
-
caKeyPath
|
|
262
|
+
caCertPath?: string | undefined;
|
|
263
|
+
caKeyPath?: string | undefined;
|
|
246
264
|
} | undefined;
|
|
247
265
|
parentProxy?: {
|
|
248
266
|
http?: string | undefined;
|
|
@@ -262,9 +280,10 @@ export declare const SandboxRuntimeConfigSchema: z.ZodObject<{
|
|
|
262
280
|
socketPath: string;
|
|
263
281
|
domains: string[];
|
|
264
282
|
} | undefined;
|
|
283
|
+
filterRequest?: FilterRequestCallback | undefined;
|
|
265
284
|
tlsTerminate?: {
|
|
266
|
-
caCertPath
|
|
267
|
-
caKeyPath
|
|
285
|
+
caCertPath?: string | undefined;
|
|
286
|
+
caKeyPath?: string | undefined;
|
|
268
287
|
} | undefined;
|
|
269
288
|
parentProxy?: {
|
|
270
289
|
http?: string | undefined;
|
|
@@ -336,9 +355,10 @@ export declare const SandboxRuntimeConfigSchema: z.ZodObject<{
|
|
|
336
355
|
socketPath: string;
|
|
337
356
|
domains: string[];
|
|
338
357
|
} | undefined;
|
|
358
|
+
filterRequest?: FilterRequestCallback | undefined;
|
|
339
359
|
tlsTerminate?: {
|
|
340
|
-
caCertPath
|
|
341
|
-
caKeyPath
|
|
360
|
+
caCertPath?: string | undefined;
|
|
361
|
+
caKeyPath?: string | undefined;
|
|
342
362
|
} | undefined;
|
|
343
363
|
parentProxy?: {
|
|
344
364
|
http?: string | undefined;
|
|
@@ -384,9 +404,10 @@ export declare const SandboxRuntimeConfigSchema: z.ZodObject<{
|
|
|
384
404
|
socketPath: string;
|
|
385
405
|
domains: string[];
|
|
386
406
|
} | undefined;
|
|
407
|
+
filterRequest?: FilterRequestCallback | undefined;
|
|
387
408
|
tlsTerminate?: {
|
|
388
|
-
caCertPath
|
|
389
|
-
caKeyPath
|
|
409
|
+
caCertPath?: string | undefined;
|
|
410
|
+
caKeyPath?: string | undefined;
|
|
390
411
|
} | undefined;
|
|
391
412
|
parentProxy?: {
|
|
392
413
|
http?: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-config.d.ts","sourceRoot":"","sources":["../../src/sandbox/sandbox-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"sandbox-config.d.ts","sourceRoot":"","sources":["../../src/sandbox/sandbox-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAGhE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAgEvB;;;GAGG;AACH,QAAA,MAAM,qBAAqB;;;;;;;;;EAQzB,CAAA;AAEF;;;;GAIG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;EAoB3B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4G9B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;EAqBjC,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,2DAItC,CAAA;AAEH;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAY9B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;EAa9B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+DrC,CAAA;AAGF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACnE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACvE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAC1C,OAAO,4BAA4B,CACpC,CAAA;AACD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA"}
|