@clipboard-health/clearance 1.0.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 ADDED
@@ -0,0 +1,219 @@
1
+ # @clipboard-health/clearance
2
+
3
+ HTTP/HTTPS clearance for deny-by-default sandboxes.
4
+
5
+ The proxy ships with **zero compiled-in opinions** about which hosts to allow.
6
+ Bring your own list — either inline via env or by pointing at one or more
7
+ plain-text files. Teams typically check those files into their repo so
8
+ everyone shares the same baseline.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install -g @clipboard-health/clearance
14
+ ```
15
+
16
+ This installs two binaries: `clearance` (the proxy) and `clearance-ensure`
17
+ (idempotent daemon launcher).
18
+
19
+ ## Raw proxy usage
20
+
21
+ ```bash
22
+ CLEARANCE_ALLOW_HOSTS=api.example.com clearance
23
+ ```
24
+
25
+ Then point tools at `http://127.0.0.1:19999` with `HTTP_PROXY` and
26
+ `HTTPS_PROXY`.
27
+
28
+ By default the proxy listens on `127.0.0.1:19999`, allows destination port
29
+ `443`, and blocks private, loopback, link-local, multicast, documentation,
30
+ and other non-public IP ranges after DNS resolution.
31
+
32
+ If neither `CLEARANCE_ALLOW_HOSTS` nor `CLEARANCE_ALLOW_HOSTS_FILES` is
33
+ set, the proxy refuses to start.
34
+
35
+ ### Allow-host files
36
+
37
+ A file is a plain-text list of hosts, one per line. Blank lines are ignored,
38
+ `#` introduces a comment to end-of-line, and trailing dots / wildcard
39
+ prefixes (`*.example.com`) are normalized.
40
+
41
+ ```text
42
+ # AI agents
43
+ api.openai.com
44
+ api.anthropic.com
45
+
46
+ # Source code
47
+ github.com
48
+ api.github.com
49
+ *.githubusercontent.com
50
+ ```
51
+
52
+ Point at one or more files via `CLEARANCE_ALLOW_HOSTS_FILES` using your
53
+ platform's PATH delimiter — `:` on macOS/Linux, `;` on Windows. The values
54
+ from `CLEARANCE_ALLOW_HOSTS`, all referenced files, and any duplicates
55
+ are concatenated and deduped.
56
+
57
+ ```bash
58
+ CLEARANCE_ALLOW_HOSTS_FILES="$REPO/clearance-allow-hosts:$HOME/.config/clearance/personal-hosts" \
59
+ clearance
60
+ ```
61
+
62
+ This is how teams share a baseline: check a `team-allow-hosts` file into
63
+ your repo, and let individuals layer a personal file on top via the same
64
+ env var.
65
+
66
+ ### Configuration reference
67
+
68
+ <!-- markdownlint-disable MD060 -->
69
+
70
+ | Variable | Default | Notes |
71
+ | ----------------------------- | ----------- | --------------------------------------------------------------------------------------------------------- |
72
+ | `CLEARANCE_ALLOW_HOSTS` | _(unset)_ | Comma- or whitespace-separated exact hosts and wildcard suffixes. |
73
+ | `CLEARANCE_ALLOW_HOSTS_FILES` | _(unset)_ | PATH-delimited paths (`:` macOS/Linux, `;` Windows) to plain-text host files (`#` comments, blank lines). |
74
+ | `CLEARANCE_ALLOW_PORTS` | `443` | Comma- or whitespace-separated TCP ports. |
75
+ | `CLEARANCE_LISTEN_HOST` | `127.0.0.1` | Bind host. |
76
+ | `CLEARANCE_PORT` | `19999` | Bind port. |
77
+ | `CLEARANCE_DNS_TTL_MS` | `60000` | In-process DNS cache TTL. |
78
+ | `CLEARANCE_IDLE_TIMEOUT_MS` | `120000` | Socket idle timeout. |
79
+ | `CLEARANCE_MAX_SOCKETS` | `1024` | Inbound connection cap and outbound HTTP agent cap. |
80
+ | `CLEARANCE_ALLOW_PRIVATE_IPS` | _(unset)_ | Set to `1` to disable private/non-public IP blocking for local testing. |
81
+
82
+ <!-- markdownlint-enable MD060 -->
83
+
84
+ ## Managed proxy: `clearance-ensure`
85
+
86
+ `clearance-ensure` checks `127.0.0.1:19999`; if no proxy is listening it
87
+ spawns one detached using the same env vars and waits for it to bind. Logs
88
+ live at `${XDG_CACHE_HOME:-$HOME/.cache}/clearance/clearance.log`,
89
+ pid at `…/clearance.pid`.
90
+
91
+ ```bash
92
+ CLEARANCE_ALLOW_HOSTS_FILES="$REPO/clearance-allow-hosts" clearance-ensure
93
+ ```
94
+
95
+ To stop or restart the managed proxy after editing your allow-host files:
96
+
97
+ ```bash
98
+ kill "$(cat "${XDG_CACHE_HOME:-$HOME/.cache}/clearance/clearance.pid")"
99
+ ```
100
+
101
+ ## Safehouse integration (macOS)
102
+
103
+ Safehouse uses macOS sandbox profiles, so this section is for macOS hosts
104
+ only. Safehouse allows network access by default for agent compatibility.
105
+ To force a wrapped agent through this proxy, run the proxy outside
106
+ Safehouse, then append a Safehouse profile that denies direct remote
107
+ egress while leaving `localhost` open for `http://127.0.0.1:19999`.
108
+
109
+ The package ships a `safehouse-clearance` wrapper plus the matching
110
+ `clearance.env` and `clearance-only.sb` profile. After a global install,
111
+ the wrapper lives at `$(npm root -g)/@clipboard-health/clearance/safehouse/safehouse-clearance`.
112
+ It ensures the proxy is running, then `exec`s safehouse with the env file and
113
+ sandbox profile alongside it. It does **not** parse or rewrite your
114
+ arguments — anything you pass is forwarded verbatim, so put any safehouse
115
+ flags before the agent command:
116
+
117
+ ```bash
118
+ SAFEHOUSE_CLEARANCE="$(npm root -g)/@clipboard-health/clearance/safehouse/safehouse-clearance"
119
+
120
+ "$SAFEHOUSE_CLEARANCE" \
121
+ --enable=cloud-credentials --env-pass=AWS_PROFILE,AWS_REGION \
122
+ -- codex --dangerously-bypass-approvals-and-sandbox
123
+ ```
124
+
125
+ For day-to-day agent use, set `CLEARANCE_ALLOW_HOSTS_FILES` to point at
126
+ your team's checked-in file (and optionally a personal file), then add
127
+ shell aliases:
128
+
129
+ ```bash
130
+ SAFEHOUSE_CLEARANCE="$(npm root -g)/@clipboard-health/clearance/safehouse/safehouse-clearance"
131
+ export CLEARANCE_ALLOW_HOSTS_FILES="$HOME/code/<your-repo>/clearance-allow-hosts:$HOME/.config/clearance/personal-allow-hosts"
132
+
133
+ alias codex-proxy="$SAFEHOUSE_CLEARANCE codex --dangerously-bypass-approvals-and-sandbox"
134
+ alias claude-proxy="$SAFEHOUSE_CLEARANCE claude --enable-auto-mode"
135
+ ```
136
+
137
+ For AWS SSO work, log in on the host first, then layer the safehouse cloud
138
+ credentials flag through the wrapper:
139
+
140
+ ```bash
141
+ aws sso login --profile <profile>
142
+ AWS_PROFILE=<profile> "$SAFEHOUSE_CLEARANCE" \
143
+ --enable=cloud-credentials --env-pass=AWS_PROFILE,AWS_REGION,AWS_SDK_LOAD_CONFIG,AWS_CONFIG_FILE,AWS_SHARED_CREDENTIALS_FILE,AWS_CA_BUNDLE \
144
+ -- codex --dangerously-bypass-approvals-and-sandbox
145
+ ```
146
+
147
+ Make sure your allow-host file includes the AWS endpoints you need
148
+ (typically `*.amazonaws.com`, `*.api.aws`, `*.awsapps.com`, `*.ecr.aws`).
149
+
150
+ ### Manual setup
151
+
152
+ If you'd rather not depend on the bundled assets, you can build the same
153
+ setup by hand.
154
+
155
+ ```bash
156
+ mkdir -p ~/.config/agent-safehouse
157
+ ```
158
+
159
+ ```bash
160
+ cat > ~/.config/agent-safehouse/clearance-only.sb <<'SB'
161
+ ;; Force remote network egress through the local clearance while keeping
162
+ ;; localhost available for local-only agent workflows.
163
+ (deny network-outbound
164
+ (remote ip "*:*")
165
+ (remote tcp "*:*")
166
+ (remote udp "*:*"))
167
+
168
+ (allow network-outbound
169
+ (remote ip "localhost:*")
170
+ (remote tcp "localhost:*")
171
+ (remote udp "localhost:*"))
172
+ SB
173
+ ```
174
+
175
+ ```bash
176
+ cat > ~/.config/agent-safehouse/clearance.env <<'ENV'
177
+ HTTP_PROXY="http://127.0.0.1:19999"
178
+ HTTPS_PROXY="http://127.0.0.1:19999"
179
+ ALL_PROXY="http://127.0.0.1:19999"
180
+ NO_PROXY="localhost,127.0.0.1,::1"
181
+
182
+ http_proxy="${HTTP_PROXY}"
183
+ https_proxy="${HTTPS_PROXY}"
184
+ all_proxy="${ALL_PROXY}"
185
+ no_proxy="${NO_PROXY}"
186
+ ENV
187
+ ```
188
+
189
+ Then run the wrapped command with Safehouse:
190
+
191
+ ```bash
192
+ safehouse \
193
+ --env="$HOME/.config/agent-safehouse/clearance.env" \
194
+ --append-profile="$HOME/.config/agent-safehouse/clearance-only.sb" \
195
+ -- <agent-command>
196
+ ```
197
+
198
+ Do not pass API-key environment variables just for proxying. The env file
199
+ only supplies proxy settings; agents should continue to use their normal
200
+ auth/config stores.
201
+
202
+ Quick checks:
203
+
204
+ ```bash
205
+ # Should use the proxy and usually return 401 without auth.
206
+ safehouse --env="$HOME/.config/agent-safehouse/clearance.env" \
207
+ --append-profile="$HOME/.config/agent-safehouse/clearance-only.sb" \
208
+ -- curl -I https://api.openai.com/v1/models
209
+
210
+ # Should fail because this bypasses the proxy and tries direct egress.
211
+ safehouse --env="$HOME/.config/agent-safehouse/clearance.env" \
212
+ --append-profile="$HOME/.config/agent-safehouse/clearance-only.sb" \
213
+ -- curl --noproxy '*' -I https://api.openai.com/v1/models
214
+
215
+ # Should be denied by the proxy unless example.com is in your allow-host list.
216
+ safehouse --env="$HOME/.config/agent-safehouse/clearance.env" \
217
+ --append-profile="$HOME/.config/agent-safehouse/clearance-only.sb" \
218
+ -- curl -I https://example.com
219
+ ```
package/bin/ensure.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import "../src/ensureCli.js";
package/bin/run.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import "../src/cli.js";
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@clipboard-health/clearance",
3
+ "version": "1.0.0",
4
+ "description": "Localhost HTTP/HTTPS egress proxy with hostname allow-listing, plus a macOS Safehouse integration for sandboxed agent processes.",
5
+ "keywords": [
6
+ "agent",
7
+ "allowlist",
8
+ "egress",
9
+ "proxy",
10
+ "safehouse",
11
+ "sandbox"
12
+ ],
13
+ "bugs": "https://github.com/ClipboardHealth/core-utils/issues",
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/ClipboardHealth/core-utils.git",
18
+ "directory": "packages/clearance"
19
+ },
20
+ "bin": {
21
+ "clearance": "./bin/run.js",
22
+ "clearance-ensure": "./bin/ensure.js"
23
+ },
24
+ "type": "module",
25
+ "main": "./src/index.js",
26
+ "typings": "./src/index.d.ts",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "tslib": "2.8.1"
32
+ },
33
+ "types": "./src/index.d.ts",
34
+ "module": "./src/index.js"
35
+ }
@@ -0,0 +1,11 @@
1
+ ;; Force remote network egress through the local clearance while keeping
2
+ ;; localhost available for local-only agent workflows.
3
+ (deny network-outbound
4
+ (remote ip "*:*")
5
+ (remote tcp "*:*")
6
+ (remote udp "*:*"))
7
+
8
+ (allow network-outbound
9
+ (remote ip "localhost:*")
10
+ (remote tcp "localhost:*")
11
+ (remote udp "localhost:*"))
@@ -0,0 +1,9 @@
1
+ HTTP_PROXY="http://127.0.0.1:19999"
2
+ HTTPS_PROXY="http://127.0.0.1:19999"
3
+ ALL_PROXY="http://127.0.0.1:19999"
4
+ NO_PROXY="localhost,127.0.0.1,::1"
5
+
6
+ http_proxy="${HTTP_PROXY}"
7
+ https_proxy="${HTTPS_PROXY}"
8
+ all_proxy="${ALL_PROXY}"
9
+ no_proxy="${NO_PROXY}"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ source_path="${BASH_SOURCE[0]}"
5
+ while [ -h "$source_path" ]; do
6
+ source_dir="$(cd -P "$(dirname "$source_path")" >/dev/null 2>&1 && pwd)"
7
+ link_target="$(readlink "$source_path")"
8
+ case "$link_target" in
9
+ /*) source_path="$link_target" ;;
10
+ *) source_path="$source_dir/$link_target" ;;
11
+ esac
12
+ done
13
+
14
+ script_dir="$(cd -P "$(dirname "$source_path")" >/dev/null 2>&1 && pwd)"
15
+ package_dir="$(cd -P "$script_dir/.." >/dev/null 2>&1 && pwd)"
16
+
17
+ node "$package_dir/bin/ensure.js" >&2
18
+
19
+ exec safehouse \
20
+ --env="$script_dir/clearance.env" \
21
+ --append-profile="$script_dir/clearance-only.sb" \
22
+ "$@"
@@ -0,0 +1,5 @@
1
+ export interface ResolveAllowlistInput {
2
+ env: NodeJS.ProcessEnv;
3
+ readFile?: (path: string) => string;
4
+ }
5
+ export declare function resolveAllowlist(input: ResolveAllowlistInput): readonly string[];
@@ -0,0 +1,48 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { delimiter as PATH_DELIMITER } from "node:path";
3
+ import { normalizeRule, parseList } from "./hostRule.js";
4
+ const COMMENT_PREFIX = "#";
5
+ export function resolveAllowlist(input) {
6
+ const readFile = input.readFile ?? defaultReadFile;
7
+ const fromEnv = parseList(input.env["CLEARANCE_ALLOW_HOSTS"]);
8
+ const fromFiles = parseFilesEnv(input.env["CLEARANCE_ALLOW_HOSTS_FILES"]).flatMap((path) => readHostsFile({ path, readFile }));
9
+ const normalized = [...fromEnv, ...fromFiles]
10
+ .map(normalizeRule)
11
+ .filter((rule) => rule !== undefined);
12
+ if (normalized.length === 0) {
13
+ throw new Error("Set CLEARANCE_ALLOW_HOSTS or CLEARANCE_ALLOW_HOSTS_FILES, e.g. CLEARANCE_ALLOW_HOSTS=api.example.com");
14
+ }
15
+ return [...new Set(normalized)];
16
+ }
17
+ function parseFilesEnv(value) {
18
+ if (value === undefined) {
19
+ return [];
20
+ }
21
+ return value
22
+ .split(PATH_DELIMITER)
23
+ .map((path) => path.trim())
24
+ .filter((path) => path.length > 0);
25
+ }
26
+ function readHostsFile(input) {
27
+ let content;
28
+ try {
29
+ content = input.readFile(input.path);
30
+ }
31
+ catch (error) {
32
+ const message = error instanceof Error ? error.message : String(error);
33
+ throw new Error(`Failed to read CLEARANCE_ALLOW_HOSTS_FILES path ${JSON.stringify(input.path)}: ${message}`, { cause: error });
34
+ }
35
+ const hosts = [];
36
+ for (const rawLine of content.split("\n")) {
37
+ const [beforeComment = ""] = rawLine.split(COMMENT_PREFIX, 1);
38
+ const trimmed = beforeComment.trim();
39
+ if (trimmed.length > 0) {
40
+ hosts.push(trimmed);
41
+ }
42
+ }
43
+ return hosts;
44
+ }
45
+ function defaultReadFile(path) {
46
+ return readFileSync(path, "utf8");
47
+ }
48
+ //# sourceMappingURL=allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allowlist.js","sourceRoot":"","sources":["../../../../packages/clearance/src/allowlist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzD,MAAM,cAAc,GAAG,GAAG,CAAC;AAO3B,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,eAAe,CAAC;IACnD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACzF,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAClC,CAAC;IACF,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,SAAS,CAAC;SAC1C,GAAG,CAAC,aAAa,CAAC;SAClB,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IAExD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,aAAa,CAAC,KAAyB;IAC9C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,KAAK;SACT,KAAK,CAAC,cAAc,CAAC;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAOD,SAAS,aAAa,CAAC,KAAyB;IAC9C,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CACb,mDAAmD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE,EAC3F,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,aAAa,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC"}
package/src/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/src/cli.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { startClearanceFromEnv } from "./index.js";
3
+ startClearanceFromEnv({ env: process.env }).catch((error) => {
4
+ const message = error instanceof Error ? error.message : String(error);
5
+ process.stderr.write(`${message}\n`);
6
+ process.exitCode = 2;
7
+ });
8
+ //# sourceMappingURL=cli.js.map
package/src/cli.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../packages/clearance/src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAqB,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACnE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { ensureClearance } from "./launcher.js";
3
+ ensureClearance({
4
+ logger: (message) => {
5
+ process.stderr.write(`${message}\n`);
6
+ },
7
+ }).catch((error) => {
8
+ const message = error instanceof Error ? error.message : String(error);
9
+ process.stderr.write(`${message}\n`);
10
+ process.exit(2);
11
+ });
12
+ //# sourceMappingURL=ensureCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensureCli.js","sourceRoot":"","sources":["../../../../packages/clearance/src/ensureCli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,eAAe,CAAC;IACd,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACvC,CAAC;CACF,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC1B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function normalizeRule(rule: string): string | undefined;
2
+ export declare function normalizeRules(rules: readonly string[]): string[];
3
+ export declare function parseList(input: string | undefined): string[];
4
+ export declare function normalizeHost(host: string): string | undefined;
@@ -0,0 +1,62 @@
1
+ import * as net from "node:net";
2
+ import { domainToASCII } from "node:url";
3
+ export function normalizeRule(rule) {
4
+ const trimmed = rule.trim().toLowerCase().replace(/\.$/, "");
5
+ if (trimmed.length === 0) {
6
+ return undefined;
7
+ }
8
+ if (trimmed.startsWith("*.")) {
9
+ const suffix = normalizeHost(trimmed.slice(2));
10
+ if (suffix === undefined || net.isIP(suffix) !== 0) {
11
+ return undefined;
12
+ }
13
+ return `*.${suffix}`;
14
+ }
15
+ return normalizeHost(trimmed);
16
+ }
17
+ export function normalizeRules(rules) {
18
+ const normalizedRules = [];
19
+ for (const rule of rules) {
20
+ const normalizedRule = normalizeRule(rule);
21
+ if (normalizedRule !== undefined) {
22
+ normalizedRules.push(normalizedRule);
23
+ }
24
+ }
25
+ return [...new Set(normalizedRules)];
26
+ }
27
+ export function parseList(input) {
28
+ if (input === undefined) {
29
+ return [];
30
+ }
31
+ return input
32
+ .split(/[\s,]+/)
33
+ .map((item) => item.trim())
34
+ .filter((item) => item.length > 0);
35
+ }
36
+ export function normalizeHost(host) {
37
+ const trimmed = host.trim().toLowerCase().replace(/\.$/, "");
38
+ const isBracketed = trimmed.startsWith("[") && trimmed.endsWith("]");
39
+ const unbracketed = isBracketed ? trimmed.slice(1, -1) : trimmed;
40
+ if (unbracketed.length === 0 ||
41
+ unbracketed.includes("%") ||
42
+ unbracketed.includes("/") ||
43
+ /\s/.test(unbracketed)) {
44
+ return undefined;
45
+ }
46
+ if (net.isIP(unbracketed) !== 0) {
47
+ return unbracketed;
48
+ }
49
+ // Brackets are reserved for IP literals; bracketed non-IPs are malformed.
50
+ if (isBracketed) {
51
+ return undefined;
52
+ }
53
+ if (unbracketed.includes(":") || unbracketed.includes("@")) {
54
+ return undefined;
55
+ }
56
+ const ascii = domainToASCII(unbracketed);
57
+ if (ascii.length === 0 || ascii.includes("*")) {
58
+ return undefined;
59
+ }
60
+ return ascii;
61
+ }
62
+ //# sourceMappingURL=hostRule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostRule.js","sourceRoot":"","sources":["../../../../packages/clearance/src/hostRule.ts"],"names":[],"mappings":"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"}
package/src/index.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { LookupAddress } from "node:dns";
2
+ import * as http from "node:http";
3
+ export { resolveAllowlist, type ResolveAllowlistInput } from "./allowlist.js";
4
+ export { type ClearanceCheckInput, type ClearanceListenerCheck, type ClearanceSpawner, ensureClearance, type EnsureClearanceInput, type EnsureClearanceResult, isClearanceListening, spawnClearance, type SpawnClearanceInput, } from "./launcher.js";
5
+ export declare const CLEARANCE_PACKAGE_NAME = "@clipboard-health/clearance";
6
+ export interface ClearanceConfig {
7
+ allowedHosts: readonly string[];
8
+ allowedPorts: readonly number[];
9
+ dnsTtlMs: number;
10
+ idleTimeoutMs: number;
11
+ listenHost: string;
12
+ maxSockets: number;
13
+ port: number;
14
+ shouldBlockPrivateIps: boolean;
15
+ }
16
+ export interface ClearanceLogger {
17
+ info(message: string): void;
18
+ }
19
+ export type DnsLookup = (hostname: string) => Promise<readonly LookupAddress[]>;
20
+ export interface CreateClearanceServerOptions {
21
+ allowedHosts: readonly string[];
22
+ allowedPorts?: readonly number[];
23
+ dnsLookup?: DnsLookup;
24
+ dnsTtlMs?: number;
25
+ idleTimeoutMs?: number;
26
+ logger?: ClearanceLogger;
27
+ maxSockets?: number;
28
+ shouldBlockPrivateIps?: boolean;
29
+ }
30
+ export interface StartClearanceFromEnvInput {
31
+ env: NodeJS.ProcessEnv;
32
+ logger?: ClearanceLogger;
33
+ }
34
+ export declare function resolveClearanceConfig(env: NodeJS.ProcessEnv): ClearanceConfig;
35
+ export declare function createClearanceServer(options: CreateClearanceServerOptions): http.Server;
36
+ export declare function startClearanceFromEnv(input: StartClearanceFromEnvInput): Promise<http.Server>;
package/src/index.js ADDED
@@ -0,0 +1,509 @@
1
+ import { Buffer } from "node:buffer";
2
+ import * as dns from "node:dns/promises";
3
+ import * as http from "node:http";
4
+ import * as net from "node:net";
5
+ import { resolveAllowlist } from "./allowlist.js";
6
+ import { normalizeHost, normalizeRules, parseList } from "./hostRule.js";
7
+ export { resolveAllowlist } from "./allowlist.js";
8
+ export { ensureClearance, isClearanceListening, spawnClearance, } from "./launcher.js";
9
+ export const CLEARANCE_PACKAGE_NAME = "@clipboard-health/clearance";
10
+ const DEFAULT_LISTEN_HOST = "127.0.0.1";
11
+ const DEFAULT_PORT = 19_999;
12
+ const DEFAULT_ALLOWED_PORTS = [443];
13
+ const DEFAULT_DNS_TTL_MS = 60_000;
14
+ const DEFAULT_IDLE_TIMEOUT_MS = 120_000;
15
+ const DEFAULT_MAX_SOCKETS = 1024;
16
+ const SERVER_HEADERS_TIMEOUT_MS = 15_000;
17
+ const SERVER_KEEP_ALIVE_TIMEOUT_MS = 5000;
18
+ const NOOP_LOGGER = {
19
+ info: noop,
20
+ };
21
+ const HOP_BY_HOP_HEADERS = new Set([
22
+ "connection",
23
+ "keep-alive",
24
+ "proxy-authenticate",
25
+ "proxy-authorization",
26
+ "proxy-connection",
27
+ "te",
28
+ "trailer",
29
+ "transfer-encoding",
30
+ "upgrade",
31
+ ]);
32
+ const PRIVATE_IPV4_RANGES = [
33
+ ["0.0.0.0", 8],
34
+ ["10.0.0.0", 8],
35
+ ["100.64.0.0", 10],
36
+ ["127.0.0.0", 8],
37
+ ["169.254.0.0", 16],
38
+ ["172.16.0.0", 12],
39
+ ["192.0.0.0", 24],
40
+ ["192.0.2.0", 24],
41
+ ["192.168.0.0", 16],
42
+ ["198.18.0.0", 15],
43
+ ["198.51.100.0", 24],
44
+ ["203.0.113.0", 24],
45
+ ["224.0.0.0", 4],
46
+ ["240.0.0.0", 4],
47
+ ];
48
+ const PRIVATE_IPV6_RANGES = [
49
+ ["::", 128],
50
+ ["::1", 128],
51
+ ["::", 96],
52
+ ["::ffff:0:0", 96],
53
+ ["64:ff9b::", 96],
54
+ ["64:ff9b:1::", 48],
55
+ ["100::", 64],
56
+ ["2001::", 23],
57
+ ["2001:db8::", 32],
58
+ ["2002::", 16],
59
+ ["fc00::", 7],
60
+ ["fe80::", 10],
61
+ ["ff00::", 8],
62
+ ];
63
+ const PRIVATE_IPV4_BLOCK_LIST = createIpBlockList(PRIVATE_IPV4_RANGES, "ipv4");
64
+ const PRIVATE_IPV6_BLOCK_LIST = createIpBlockList(PRIVATE_IPV6_RANGES, "ipv6");
65
+ export function resolveClearanceConfig(env) {
66
+ const allowedHosts = resolveAllowlist({ env });
67
+ return {
68
+ allowedHosts,
69
+ allowedPorts: normalizeAllowedPorts(parseList(env["CLEARANCE_ALLOW_PORTS"] ?? DEFAULT_ALLOWED_PORTS.join(","))),
70
+ dnsTtlMs: parseIntegerEnv(env, "CLEARANCE_DNS_TTL_MS", DEFAULT_DNS_TTL_MS, 0, Number.MAX_SAFE_INTEGER),
71
+ idleTimeoutMs: parseIntegerEnv(env, "CLEARANCE_IDLE_TIMEOUT_MS", DEFAULT_IDLE_TIMEOUT_MS, 1, Number.MAX_SAFE_INTEGER),
72
+ listenHost: env["CLEARANCE_LISTEN_HOST"] ?? DEFAULT_LISTEN_HOST,
73
+ maxSockets: parseIntegerEnv(env, "CLEARANCE_MAX_SOCKETS", DEFAULT_MAX_SOCKETS, 1, Number.MAX_SAFE_INTEGER),
74
+ port: parseIntegerEnv(env, "CLEARANCE_PORT", DEFAULT_PORT, 1, 65_535),
75
+ shouldBlockPrivateIps: env["CLEARANCE_ALLOW_PRIVATE_IPS"] !== "1",
76
+ };
77
+ }
78
+ export function createClearanceServer(options) {
79
+ const state = createProxyState(options);
80
+ const server = http.createServer((req, res) => {
81
+ void handleHttpRequest({ req, res, state });
82
+ });
83
+ server.on("connect", (req, clientSocket, head) => {
84
+ /* v8 ignore next @preserve */
85
+ if (!isNetSocket(clientSocket)) {
86
+ socketReply(clientSocket, 400, "Bad Request", "Bad CONNECT socket\n");
87
+ return;
88
+ }
89
+ void handleConnect({ clientSocket, head, req, state });
90
+ });
91
+ server.on("clientError", (_error, socket) => {
92
+ socketReply(socket, 400, "Bad Request", "Bad Request\n");
93
+ });
94
+ server.headersTimeout = SERVER_HEADERS_TIMEOUT_MS;
95
+ server.keepAliveTimeout = SERVER_KEEP_ALIVE_TIMEOUT_MS;
96
+ server.maxConnections = state.maxSockets;
97
+ server.once("close", () => {
98
+ state.httpAgent.destroy();
99
+ });
100
+ return server;
101
+ }
102
+ export async function startClearanceFromEnv(input) {
103
+ const { env } = input;
104
+ const logger = input.logger ?? console;
105
+ const config = resolveClearanceConfig(env);
106
+ const server = createClearanceServer({
107
+ allowedHosts: config.allowedHosts,
108
+ allowedPorts: config.allowedPorts,
109
+ dnsTtlMs: config.dnsTtlMs,
110
+ idleTimeoutMs: config.idleTimeoutMs,
111
+ logger,
112
+ maxSockets: config.maxSockets,
113
+ shouldBlockPrivateIps: config.shouldBlockPrivateIps,
114
+ });
115
+ await listen(server, config);
116
+ logger.info(`clearance listening on http://${config.listenHost}:${config.port}`);
117
+ logger.info(`allowed hosts: ${config.allowedHosts.join(", ")}`);
118
+ logger.info(`allowed ports: ${config.allowedPorts.join(",")}`);
119
+ return server;
120
+ }
121
+ function createProxyState(options) {
122
+ const allowedHosts = normalizeRules(options.allowedHosts);
123
+ if (allowedHosts.length === 0) {
124
+ throw new Error("allowedHosts must include at least one valid host rule");
125
+ }
126
+ const maxSockets = options.maxSockets ?? DEFAULT_MAX_SOCKETS;
127
+ return {
128
+ allowedHosts,
129
+ allowedPorts: new Set(normalizeAllowedPorts(options.allowedPorts ?? DEFAULT_ALLOWED_PORTS)),
130
+ dnsCache: new Map(),
131
+ dnsLookup: options.dnsLookup ?? defaultDnsLookup,
132
+ dnsTtlMs: options.dnsTtlMs ?? DEFAULT_DNS_TTL_MS,
133
+ httpAgent: new http.Agent({ keepAlive: true, maxSockets }),
134
+ idleTimeoutMs: options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS,
135
+ logger: options.logger ?? NOOP_LOGGER,
136
+ maxSockets,
137
+ shouldBlockPrivateIps: options.shouldBlockPrivateIps ?? true,
138
+ };
139
+ }
140
+ async function listen(server, config) {
141
+ await new Promise((resolve, reject) => {
142
+ function onError(error) {
143
+ server.off("listening", onListening);
144
+ reject(error);
145
+ }
146
+ function onListening() {
147
+ server.off("error", onError);
148
+ resolve();
149
+ }
150
+ server.once("error", onError);
151
+ server.once("listening", onListening);
152
+ server.listen(config.port, config.listenHost);
153
+ });
154
+ }
155
+ async function handleConnect(input) {
156
+ const { clientSocket, head, req, state } = input;
157
+ /* v8 ignore next @preserve */
158
+ clientSocket.on("error", noop);
159
+ /* v8 ignore next @preserve */
160
+ clientSocket.setTimeout(state.idleTimeoutMs, () => {
161
+ clientSocket.destroy(new Error("idle timeout"));
162
+ });
163
+ const parsed = parseConnectTarget(String(req.url));
164
+ if (parsed === undefined) {
165
+ socketReply(clientSocket, 400, "Bad Request", "Bad CONNECT target\n");
166
+ return;
167
+ }
168
+ let target;
169
+ try {
170
+ target = await authorize({ host: parsed.host, port: parsed.port, state });
171
+ }
172
+ catch (error) {
173
+ const reason = errorMessage(error);
174
+ logDecision(state.logger, {
175
+ decision: "DENY",
176
+ hostname: parsed.host,
177
+ method: "CONNECT",
178
+ port: parsed.port,
179
+ reason,
180
+ });
181
+ socketReply(clientSocket, 403, "Forbidden", `${reason}\n`);
182
+ return;
183
+ }
184
+ let isEstablished = false;
185
+ const upstream = net.createConnection({
186
+ family: target.family,
187
+ host: target.address,
188
+ port: target.port,
189
+ });
190
+ upstream.setNoDelay(true);
191
+ /* v8 ignore next @preserve */
192
+ upstream.setTimeout(state.idleTimeoutMs, () => {
193
+ upstream.destroy(new Error("idle timeout"));
194
+ });
195
+ upstream.once("connect", () => {
196
+ isEstablished = true;
197
+ logDecision(state.logger, {
198
+ address: target.address,
199
+ decision: "ALLOW",
200
+ hostname: target.hostname,
201
+ method: "CONNECT",
202
+ port: target.port,
203
+ });
204
+ clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
205
+ if (head.length > 0) {
206
+ upstream.write(head);
207
+ }
208
+ upstream.pipe(clientSocket);
209
+ clientSocket.pipe(upstream);
210
+ });
211
+ upstream.once("error", (error) => {
212
+ /* v8 ignore else @preserve */
213
+ if (!isEstablished) {
214
+ socketReply(clientSocket, 502, "Bad Gateway", `${error.message}\n`);
215
+ return;
216
+ }
217
+ /* v8 ignore next @preserve */
218
+ clientSocket.destroy(error);
219
+ });
220
+ clientSocket.once("close", () => {
221
+ upstream.destroy();
222
+ });
223
+ }
224
+ async function handleHttpRequest(input) {
225
+ const { req, res, state } = input;
226
+ const rawUrl = String(req.url);
227
+ let url;
228
+ try {
229
+ url = new URL(rawUrl);
230
+ }
231
+ catch {
232
+ httpReply(res, 400, "HTTP proxy requests must use absolute URLs\n");
233
+ return;
234
+ }
235
+ if (url.protocol !== "http:") {
236
+ httpReply(res, 400, "Use CONNECT for HTTPS\n");
237
+ return;
238
+ }
239
+ const port = url.port.length > 0 ? Number(url.port) : 80;
240
+ const method = String(req.method);
241
+ let target;
242
+ try {
243
+ target = await authorize({ host: url.hostname, port, state });
244
+ }
245
+ catch (error) {
246
+ const reason = errorMessage(error);
247
+ logDecision(state.logger, {
248
+ decision: "DENY",
249
+ hostname: url.hostname,
250
+ method,
251
+ port,
252
+ reason,
253
+ });
254
+ httpReply(res, 403, `${reason}\n`);
255
+ return;
256
+ }
257
+ const headers = stripHopByHopHeaders(req.headers);
258
+ headers.host = url.host;
259
+ const upstreamReq = http.request({
260
+ agent: state.httpAgent,
261
+ family: target.family,
262
+ headers,
263
+ hostname: target.address,
264
+ method: req.method,
265
+ path: `${url.pathname}${url.search}`,
266
+ port: target.port,
267
+ }, (upstreamRes) => {
268
+ logDecision(state.logger, {
269
+ address: target.address,
270
+ decision: "ALLOW",
271
+ hostname: target.hostname,
272
+ method,
273
+ port: target.port,
274
+ });
275
+ /* v8 ignore next @preserve */
276
+ const statusCode = upstreamRes.statusCode ?? 502;
277
+ res.writeHead(statusCode, stripHopByHopHeaders(upstreamRes.headers));
278
+ upstreamRes.pipe(res);
279
+ });
280
+ upstreamReq.setTimeout(state.idleTimeoutMs, () => {
281
+ upstreamReq.destroy(new Error("upstream timeout"));
282
+ });
283
+ /* v8 ignore next @preserve */
284
+ req.socket.setTimeout(state.idleTimeoutMs, () => {
285
+ upstreamReq.destroy(new Error("client idle timeout"));
286
+ });
287
+ req.once("end", () => {
288
+ req.socket.setTimeout(0);
289
+ });
290
+ upstreamReq.on("error", (error) => {
291
+ /* v8 ignore next @preserve */
292
+ if (res.headersSent) {
293
+ res.destroy(error);
294
+ return;
295
+ }
296
+ httpReply(res, 502, `${error.message}\n`);
297
+ });
298
+ upstreamReq.once("close", () => {
299
+ req.socket.setTimeout(0);
300
+ });
301
+ /* v8 ignore next @preserve */
302
+ req.once("aborted", () => {
303
+ upstreamReq.destroy(new Error("client aborted"));
304
+ });
305
+ res.once("close", () => {
306
+ if (!res.writableEnded) {
307
+ upstreamReq.destroy(new Error("client disconnected"));
308
+ }
309
+ });
310
+ req.pipe(upstreamReq);
311
+ }
312
+ async function authorize(input) {
313
+ const { host, port, state } = input;
314
+ const hostname = normalizeHost(host);
315
+ if (hostname === undefined) {
316
+ throw new Error("empty or invalid host");
317
+ }
318
+ if (!state.allowedPorts.has(port)) {
319
+ throw new Error(`port not allowed: ${port}`);
320
+ }
321
+ if (!hostAllowed(hostname, state.allowedHosts)) {
322
+ throw new Error(`host not allowed: ${hostname}`);
323
+ }
324
+ const resolved = await resolveHost({ hostname, state });
325
+ return { address: resolved.address, family: resolved.family, hostname, port };
326
+ }
327
+ async function resolveHost(input) {
328
+ const { hostname, state } = input;
329
+ const directIpFamily = net.isIP(hostname);
330
+ if (isIpFamily(directIpFamily)) {
331
+ if (state.shouldBlockPrivateIps && isPrivateIpAddress(hostname, directIpFamily)) {
332
+ throw new Error(`private IP blocked: ${hostname}`);
333
+ }
334
+ return { address: hostname, family: directIpFamily };
335
+ }
336
+ const now = Date.now();
337
+ const cached = state.dnsCache.get(hostname);
338
+ const cachedRecord = cached?.records[0];
339
+ if (cached !== undefined && cached.until > now && cachedRecord !== undefined) {
340
+ return cachedRecord;
341
+ }
342
+ if (cached !== undefined && cached.until <= now) {
343
+ state.dnsCache.delete(hostname);
344
+ }
345
+ const lookupRecords = await state.dnsLookup(hostname);
346
+ const records = lookupRecords
347
+ .filter(hasIpFamily)
348
+ .filter((record) => !(state.shouldBlockPrivateIps && isPrivateIpAddress(record.address, record.family)))
349
+ .toSorted((a, b) => a.family - b.family);
350
+ const [firstRecord] = records;
351
+ if (firstRecord === undefined) {
352
+ throw new Error(`no public address for ${hostname}`);
353
+ }
354
+ if (state.dnsTtlMs > 0) {
355
+ state.dnsCache.set(hostname, { records, until: now + state.dnsTtlMs });
356
+ }
357
+ return firstRecord;
358
+ }
359
+ async function defaultDnsLookup(hostname) {
360
+ return await dns.lookup(hostname, { all: true, verbatim: false });
361
+ }
362
+ function hostAllowed(hostname, allowedHosts) {
363
+ return allowedHosts.some((rule) => {
364
+ if (rule.startsWith("*.")) {
365
+ const suffix = rule.slice(1);
366
+ return hostname.endsWith(suffix) && hostname.length > suffix.length;
367
+ }
368
+ return hostname === rule;
369
+ });
370
+ }
371
+ function parseConnectTarget(input) {
372
+ const ipv6 = /^\[([^\]]+)]:(\d+)$/.exec(input);
373
+ if (ipv6 !== null) {
374
+ const [, host, rawPort] = ipv6;
375
+ const port = parsePort(rawPort);
376
+ /* v8 ignore next @preserve */
377
+ if (host === undefined || port === undefined) {
378
+ return undefined;
379
+ }
380
+ return { host, port };
381
+ }
382
+ const separatorIndex = input.lastIndexOf(":");
383
+ if (separatorIndex <= 0) {
384
+ return undefined;
385
+ }
386
+ const port = parsePort(input.slice(separatorIndex + 1));
387
+ if (port === undefined) {
388
+ return undefined;
389
+ }
390
+ return { host: input.slice(0, separatorIndex), port };
391
+ }
392
+ function stripHopByHopHeaders(headers) {
393
+ const blockedHeaders = new Set(HOP_BY_HOP_HEADERS);
394
+ for (const token of parseConnectionHeaderTokens(headers.connection)) {
395
+ blockedHeaders.add(token);
396
+ }
397
+ const out = {};
398
+ for (const [key, value] of Object.entries(headers)) {
399
+ if (!blockedHeaders.has(key.toLowerCase()) && value !== undefined) {
400
+ out[key] = value;
401
+ }
402
+ }
403
+ return out;
404
+ }
405
+ function parseConnectionHeaderTokens(value) {
406
+ /* v8 ignore next @preserve */
407
+ if (value === undefined) {
408
+ return [];
409
+ }
410
+ /* v8 ignore next @preserve */
411
+ const values = Array.isArray(value) ? value : [String(value)];
412
+ return values
413
+ .flatMap((header) => header.split(","))
414
+ .map((token) => token.trim().toLowerCase())
415
+ .filter((token) => token.length > 0);
416
+ }
417
+ function normalizeAllowedPorts(rawPorts) {
418
+ const ports = rawPorts
419
+ .map((rawPort) => (typeof rawPort === "number" ? rawPort : Number(rawPort)))
420
+ .filter((port) => isValidPort(port));
421
+ if (ports.length === 0) {
422
+ throw new Error("CLEARANCE_ALLOW_PORTS must include at least one valid TCP port");
423
+ }
424
+ return [...new Set(ports)];
425
+ }
426
+ function parseIntegerEnv(env, name, fallback, minimum, maximum) {
427
+ const value = env[name];
428
+ if (value === undefined || value.trim().length === 0) {
429
+ return fallback;
430
+ }
431
+ const parsed = Number(value);
432
+ if (!Number.isInteger(parsed)) {
433
+ throw new TypeError(`${name} must be an integer`);
434
+ }
435
+ if (parsed < minimum || parsed > maximum) {
436
+ throw new Error(`${name} must be between ${minimum} and ${maximum}`);
437
+ }
438
+ return parsed;
439
+ }
440
+ function parsePort(rawPort) {
441
+ if (rawPort === undefined || rawPort.trim().length === 0) {
442
+ return undefined;
443
+ }
444
+ const port = Number(rawPort);
445
+ return isValidPort(port) ? port : undefined;
446
+ }
447
+ function isValidPort(port) {
448
+ return Number.isInteger(port) && port > 0 && port <= 65_535;
449
+ }
450
+ function isIpFamily(family) {
451
+ return family === 4 || family === 6;
452
+ }
453
+ function hasIpFamily(record) {
454
+ return isIpFamily(record.family);
455
+ }
456
+ function isPrivateIpAddress(ip, family) {
457
+ if (family === 4) {
458
+ return PRIVATE_IPV4_BLOCK_LIST.check(ip, "ipv4");
459
+ }
460
+ return PRIVATE_IPV6_BLOCK_LIST.check(ip, "ipv6");
461
+ }
462
+ function createIpBlockList(ranges, family) {
463
+ const blockList = new net.BlockList();
464
+ for (const [address, prefix] of ranges) {
465
+ blockList.addSubnet(address, prefix, family);
466
+ }
467
+ return blockList;
468
+ }
469
+ function httpReply(res, status, body) {
470
+ res.writeHead(status, {
471
+ connection: "close",
472
+ "content-length": Buffer.byteLength(body),
473
+ "content-type": "text/plain; charset=utf-8",
474
+ });
475
+ res.end(body);
476
+ }
477
+ function socketReply(socket, status, reason, body) {
478
+ /* v8 ignore next @preserve */
479
+ if (socket.destroyed) {
480
+ return;
481
+ }
482
+ socket.end(`HTTP/1.1 ${status} ${reason}\r\ncontent-type: text/plain; charset=utf-8\r\ncontent-length: ${Buffer.byteLength(body)}\r\nconnection: close\r\n\r\n${body}`);
483
+ }
484
+ function logDecision(logger, input) {
485
+ const parts = [
486
+ input.decision,
487
+ `method=${input.method}`,
488
+ `host=${input.hostname}`,
489
+ `port=${input.port}`,
490
+ ];
491
+ if (input.address !== undefined) {
492
+ parts.push(`address=${input.address}`);
493
+ }
494
+ if (input.reason !== undefined) {
495
+ parts.push(`reason=${input.reason}`);
496
+ }
497
+ logger.info(parts.join(" "));
498
+ }
499
+ function errorMessage(error) {
500
+ /* v8 ignore next @preserve */
501
+ return error instanceof Error ? error.message : String(error);
502
+ }
503
+ function isNetSocket(socket) {
504
+ return socket instanceof net.Socket;
505
+ }
506
+ function noop() {
507
+ // Intentionally ignore optional proxy log and socket error events.
508
+ }
509
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/clearance/src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export interface ClearanceCheckInput {
2
+ host: string;
3
+ port: number;
4
+ }
5
+ export type ClearanceListenerCheck = (input: ClearanceCheckInput) => Promise<boolean>;
6
+ export interface SpawnClearanceInput {
7
+ args: readonly string[];
8
+ command: string;
9
+ env: NodeJS.ProcessEnv;
10
+ logPath: string;
11
+ }
12
+ export type ClearanceSpawner = (input: SpawnClearanceInput) => number;
13
+ export interface EnsureClearanceInput {
14
+ cacheDir?: string;
15
+ env?: NodeJS.ProcessEnv;
16
+ isListening?: ClearanceListenerCheck;
17
+ logger?: (message: string) => void;
18
+ pollIntervalMs?: number;
19
+ sleep?: (ms: number) => Promise<void>;
20
+ spawnDetached?: ClearanceSpawner;
21
+ timeoutMs?: number;
22
+ }
23
+ export interface EnsureClearanceResult {
24
+ logPath?: string;
25
+ pid?: number;
26
+ pidPath?: string;
27
+ port: number;
28
+ status: "already-running" | "started";
29
+ }
30
+ export declare function isClearanceListening(input: ClearanceCheckInput): Promise<boolean>;
31
+ export declare function spawnClearance(input: SpawnClearanceInput): number;
32
+ export declare function ensureClearance(input?: EnsureClearanceInput): Promise<EnsureClearanceResult>;
@@ -0,0 +1,148 @@
1
+ import { spawn } from "node:child_process";
2
+ import { closeSync, mkdirSync, openSync, writeFileSync } from "node:fs";
3
+ import * as net from "node:net";
4
+ import { homedir } from "node:os";
5
+ import { join, resolve as resolvePath } from "node:path";
6
+ import { resolveAllowlist } from "./allowlist.js";
7
+ const CLEARANCE_HOST = "127.0.0.1";
8
+ const CLEARANCE_PORT = 19_999;
9
+ const CLEARANCE_START_TIMEOUT_MS = 3000;
10
+ const CLEARANCE_POLL_INTERVAL_MS = 100;
11
+ const FORWARDED_ENV_VARS = [
12
+ "CLEARANCE_ALLOW_HOSTS",
13
+ "CLEARANCE_ALLOW_HOSTS_FILES",
14
+ "CLEARANCE_ALLOW_PORTS",
15
+ "CLEARANCE_ALLOW_PRIVATE_IPS",
16
+ "CLEARANCE_DNS_TTL_MS",
17
+ "CLEARANCE_IDLE_TIMEOUT_MS",
18
+ "CLEARANCE_MAX_SOCKETS",
19
+ "HOME",
20
+ "PATH",
21
+ "XDG_CACHE_HOME",
22
+ ];
23
+ // import.meta.dirname is `<package>/{src,dist}`; the proxy server bin lives at `<package>/bin/run.js`.
24
+ const PACKAGE_ROOT = resolvePath(import.meta.dirname, "..");
25
+ const CLEARANCE_BIN_PATH = resolvePath(PACKAGE_ROOT, "bin", "run.js");
26
+ export async function isClearanceListening(input) {
27
+ return await new Promise((resolve) => {
28
+ const socket = net.createConnection({ host: input.host, port: input.port });
29
+ socket.setTimeout(500);
30
+ socket.once("connect", () => {
31
+ socket.destroy();
32
+ resolve(true);
33
+ });
34
+ socket.once("error", () => {
35
+ socket.destroy();
36
+ resolve(false);
37
+ });
38
+ /* v8 ignore next 4 @preserve -- timeout is a defensive slow-network path; connection refused is the normal closed-port path */
39
+ socket.once("timeout", () => {
40
+ socket.destroy();
41
+ resolve(false);
42
+ });
43
+ });
44
+ }
45
+ export function spawnClearance(input) {
46
+ const logFile = openSync(input.logPath, "a");
47
+ try {
48
+ const child = spawn(input.command, input.args, {
49
+ detached: true,
50
+ env: input.env,
51
+ stdio: ["ignore", logFile, logFile],
52
+ });
53
+ child.unref();
54
+ /* v8 ignore next 3 @preserve -- Node assigns pid for spawned processes; this is a defensive guard */
55
+ if (child.pid === undefined) {
56
+ throw new Error("clearance process started without a pid");
57
+ }
58
+ return child.pid;
59
+ }
60
+ finally {
61
+ closeSync(logFile);
62
+ }
63
+ }
64
+ export async function ensureClearance(input = {}) {
65
+ const env = input.env ?? defaultProxyEnv();
66
+ /* v8 ignore next @preserve -- default listener is covered directly by isClearanceListening tests */
67
+ const isListening = input.isListening ?? isClearanceListening;
68
+ const logger = input.logger ?? noop;
69
+ if (await isListening({ host: CLEARANCE_HOST, port: CLEARANCE_PORT })) {
70
+ logger(`Clearance already listening on http://${CLEARANCE_HOST}:${CLEARANCE_PORT}`);
71
+ return { port: CLEARANCE_PORT, status: "already-running" };
72
+ }
73
+ // Fail fast with a readable message instead of a "did not start listening" timeout.
74
+ resolveAllowlist({ env });
75
+ const cacheDir = input.cacheDir ?? cacheDirFor(env);
76
+ const logPath = join(cacheDir, "clearance.log");
77
+ const pidPath = join(cacheDir, "clearance.pid");
78
+ mkdirSync(cacheDir, { recursive: true });
79
+ /* v8 ignore next @preserve -- default spawner is covered directly by spawnClearance tests */
80
+ const spawnDetached = input.spawnDetached ?? spawnClearance;
81
+ const pid = spawnDetached({
82
+ args: [CLEARANCE_BIN_PATH],
83
+ command: process.execPath,
84
+ env: childEnv(env),
85
+ logPath,
86
+ });
87
+ writeFileSync(pidPath, `${pid}\n`);
88
+ const isReady = await waitForListening({
89
+ host: CLEARANCE_HOST,
90
+ isListening,
91
+ pollIntervalMs: input.pollIntervalMs ?? CLEARANCE_POLL_INTERVAL_MS,
92
+ port: CLEARANCE_PORT,
93
+ sleep: input.sleep ?? defaultSleep,
94
+ timeoutMs: input.timeoutMs ?? CLEARANCE_START_TIMEOUT_MS,
95
+ });
96
+ if (!isReady) {
97
+ throw new Error(`Clearance did not start listening on ${CLEARANCE_HOST}:${CLEARANCE_PORT}; check ${logPath}`);
98
+ }
99
+ logger(`Started clearance on http://${CLEARANCE_HOST}:${CLEARANCE_PORT} (pid ${pid}); logs: ${logPath}`);
100
+ return { logPath, pid, pidPath, port: CLEARANCE_PORT, status: "started" };
101
+ }
102
+ function noop() {
103
+ // No-op default for the optional logger hook.
104
+ }
105
+ function cacheDirFor(env) {
106
+ const xdgCacheHome = env["XDG_CACHE_HOME"];
107
+ if (xdgCacheHome !== undefined && xdgCacheHome.length > 0) {
108
+ return join(xdgCacheHome, "clearance");
109
+ }
110
+ const home = env["HOME"];
111
+ /* v8 ignore else @preserve -- tests use HOME/XDG_CACHE_HOME to avoid writing to the real home dir */
112
+ if (home !== undefined && home.length > 0) {
113
+ return join(home, ".cache", "clearance");
114
+ }
115
+ /* v8 ignore next @preserve -- tests pass HOME/XDG_CACHE_HOME to avoid writing to the real home dir */
116
+ return join(homedir(), ".cache", "clearance");
117
+ }
118
+ function childEnv(env) {
119
+ return {
120
+ ...env,
121
+ CLEARANCE_LISTEN_HOST: CLEARANCE_HOST,
122
+ CLEARANCE_PORT: String(CLEARANCE_PORT),
123
+ };
124
+ }
125
+ function defaultProxyEnv() {
126
+ return Object.fromEntries(FORWARDED_ENV_VARS
127
+ // oxlint-disable-next-line node/no-process-env -- centralized env accessor for the launcher CLI
128
+ .map((name) => [name, process.env[name]])
129
+ .filter(([, value]) => value !== undefined));
130
+ }
131
+ async function waitForListening(input) {
132
+ const attempts = Math.max(1, Math.ceil(input.timeoutMs / input.pollIntervalMs));
133
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
134
+ // eslint-disable-next-line no-await-in-loop -- readiness polling is intentionally sequential
135
+ if (await input.isListening({ host: input.host, port: input.port })) {
136
+ return true;
137
+ }
138
+ // eslint-disable-next-line no-await-in-loop -- readiness polling is intentionally sequential
139
+ await input.sleep(input.pollIntervalMs);
140
+ }
141
+ return false;
142
+ }
143
+ async function defaultSleep(ms) {
144
+ await new Promise((resolve) => {
145
+ setTimeout(resolve, ms);
146
+ });
147
+ }
148
+ //# sourceMappingURL=launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../../../packages/clearance/src/launcher.ts"],"names":[],"mappings":"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"}