@kafca/agentdock 0.1.61 → 0.1.63

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.
Files changed (127) hide show
  1. package/README.md +18 -0
  2. package/dist/renderer/assets/AutomationList-DR-micIT.js +16 -0
  3. package/dist/renderer/assets/{Badge-BLI4LHse.js → Badge-COE1Zm2i.js} +1 -1
  4. package/dist/renderer/assets/{Card-DJeLDdL3.js → Card-F6UNFxnQ.js} +1 -1
  5. package/dist/renderer/assets/{Config-D1awc81A.js → Config-BKqxfhZi.js} +2 -2
  6. package/dist/renderer/assets/Dashboard-Bvp7-_2z.js +36 -0
  7. package/dist/renderer/assets/{EmptyState-CkI1RwPX.js → EmptyState-BwV_J0Og.js} +1 -1
  8. package/dist/renderer/assets/{HighlightedMarkdown-BUhDlhgW.js → HighlightedMarkdown-CBIBaWyM.js} +1 -1
  9. package/dist/renderer/assets/{Input-DHYrn-Ne.js → Input-C8LZd4Bi.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeDetail-Bij-Xo90.js → KnowledgeDetail-DjXYNMIs.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-DmPZ2IlI.js → KnowledgeHome-Cqk5_nJh.js} +2 -2
  12. package/dist/renderer/assets/{Logs-ISY99heA.js → Logs-B6cXFvW4.js} +2 -2
  13. package/dist/renderer/assets/{Modal-DAQfz0ot.js → Modal-CAj7soll.js} +3 -3
  14. package/dist/renderer/assets/{Page-Dw43UYDT.js → Page-DlnMnu4F.js} +1 -1
  15. package/dist/renderer/assets/{Select-DzmN5OGQ.js → Select-CVay_LUX.js} +1 -1
  16. package/dist/renderer/assets/{ThreadChat-B5CWqqMK.js → ThreadChat-BxXRpYa1.js} +3 -3
  17. package/dist/renderer/assets/{Workspace-C5g9LBj9.js → Workspace-YC0pC7cM.js} +1 -1
  18. package/dist/renderer/assets/{arrow-left-BLXh56Ip.js → arrow-left-Cwm09dxx.js} +1 -1
  19. package/dist/renderer/assets/{book-open-DhPGbc-7.js → book-open-BwcGC_im.js} +1 -1
  20. package/dist/renderer/assets/{channels-DfQrmuYO.js → channels-BPytmVCd.js} +2 -2
  21. package/dist/renderer/assets/{chevron-down-CEFtpPDc.js → chevron-down-2LP9oGHG.js} +1 -1
  22. package/dist/renderer/assets/en-Bnr4nMzg.js +1 -0
  23. package/dist/renderer/assets/es-B1GIFh3i.js +1 -0
  24. package/dist/renderer/assets/{index-BIKL3fTP.css → index-B6WcpkJF.css} +1 -1
  25. package/dist/renderer/assets/index-eoVERL5K.js +162 -0
  26. package/dist/renderer/assets/{index-CYhDUAR_.js → index-llCPCiYk.js} +1 -1
  27. package/dist/renderer/assets/ja-CDWPWqEW.js +1 -0
  28. package/dist/renderer/assets/{knowledge-uHRQ80sR.js → knowledge-QujAv-Zh.js} +1 -1
  29. package/dist/renderer/assets/{pencil-BD2Y31YC.js → pencil-0P2INETc.js} +1 -1
  30. package/dist/renderer/assets/{plus-DDcldp_c.js → plus-tiGRgE2v.js} +1 -1
  31. package/dist/renderer/assets/{save-CMW_cZI9.js → save-D8U93DNa.js} +1 -1
  32. package/dist/renderer/assets/{search-BM-0CvJr.js → search-C-9J9J3f.js} +1 -1
  33. package/dist/renderer/assets/{shield-check-DaMB0FKG.js → shield-check-BCKkPPTJ.js} +1 -1
  34. package/dist/renderer/assets/{threads-DZgt9tWj.js → threads-Bsrwwnpm.js} +1 -1
  35. package/dist/renderer/assets/{trash-2-DTXVbC5y.js → trash-2-CP4sn15n.js} +1 -1
  36. package/dist/renderer/assets/zh-MSddxMDX.js +1 -0
  37. package/dist/renderer/assets/zh-TW-C_LNWX0o.js +1 -0
  38. package/dist/renderer/index.html +2 -2
  39. package/dist-electron/electron/managed-skills/agent-browser/SKILL.md +41 -0
  40. package/dist-electron/electron/managed-skills/agent-browser/references/authentication.md +83 -0
  41. package/dist-electron/electron/managed-skills/agent-browser/references/commands.md +50 -0
  42. package/dist-electron/electron/managed-skills/agent-browser/references/profiling.md +20 -0
  43. package/dist-electron/electron/managed-skills/agent-browser/references/proxy-support.md +18 -0
  44. package/dist-electron/electron/managed-skills/agent-browser/references/session-management.md +17 -0
  45. package/dist-electron/electron/managed-skills/agent-browser/references/snapshot-refs.md +17 -0
  46. package/dist-electron/electron/managed-skills/agent-browser/references/video-recording.md +21 -0
  47. package/dist-electron/electron/managed-skills/agent-browser/templates/authenticated-session.sh +11 -0
  48. package/dist-electron/electron/managed-skills/agent-browser/templates/capture-workflow.sh +12 -0
  49. package/dist-electron/electron/managed-skills/agent-browser/templates/form-automation.sh +10 -0
  50. package/dist-electron/electron/managed-skills/condition-trigger/SKILL.md +21 -0
  51. package/dist-electron/electron/managed-skills/condition-trigger/scripts/register-condition-trigger.sh +106 -0
  52. package/dist-electron/electron/managed-skills/knowledge-base/SKILL.md +13 -0
  53. package/dist-electron/electron/managed-skills/knowledge-base/scripts/search-knowledge.sh +80 -0
  54. package/dist-electron/packages/contracts/src/automations.js +211 -0
  55. package/dist-electron/packages/contracts/src/index.js +1 -0
  56. package/dist-electron/packages/core-sdk/src/automations.js +87 -0
  57. package/dist-electron/packages/core-sdk/src/client.js +101 -0
  58. package/dist-electron/packages/core-sdk/src/index.js +2 -1
  59. package/dist-electron/services/local-ai-core/src/acp/store/automation-script-store.js +415 -0
  60. package/dist-electron/services/local-ai-core/src/acp/store/automation-store.js +829 -0
  61. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +158 -0
  62. package/dist-electron/services/local-ai-core/src/acp/store/schema.js +81 -0
  63. package/dist-electron/services/local-ai-core/src/automation/automation-action-executor.js +144 -0
  64. package/dist-electron/services/local-ai-core/src/automation/automation-condition-engine.js +79 -0
  65. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +39 -73
  66. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +695 -260
  67. package/dist-electron/services/local-ai-core/src/automation/automation-script-service.js +507 -0
  68. package/dist-electron/services/local-ai-core/src/automation/automation-service.js +1019 -0
  69. package/dist-electron/services/local-ai-core/src/automation/automation-trigger-engine.js +141 -0
  70. package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +60 -17
  71. package/dist-electron/services/local-ai-core/src/automation/legacy-automation-mappers.js +286 -0
  72. package/dist-electron/services/local-ai-core/src/automation/scripts/anthropic-sandbox-runner.js +792 -0
  73. package/dist-electron/services/local-ai-core/src/automation/scripts/sandbox-runner.js +2 -0
  74. package/dist-electron/services/local-ai-core/src/automation/scripts/script-package.js +475 -0
  75. package/dist-electron/services/local-ai-core/src/automation/scripts/script-protocol-runner.js +266 -0
  76. package/dist-electron/services/local-ai-core/src/automation/scripts/secret-resolver.js +37 -0
  77. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +1 -2
  78. package/dist-electron/services/local-ai-core/src/channel/weixin/transport.js +9 -8
  79. package/dist-electron/services/local-ai-core/src/cli/lac.js +229 -0
  80. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +21 -0
  81. package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +76 -0
  82. package/dist-electron/services/local-ai-core/src/runtime/handlers/automations-handler.js +322 -0
  83. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +20 -0
  84. package/dist-electron/services/local-ai-core/src/runtime/managed-skill-catalog.js +37 -0
  85. package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +14 -4
  86. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +75 -0
  87. package/dist-electron/services/local-ai-core/src/runtime/server.js +21 -0
  88. package/dist-electron/services/local-ai-core/src/runtime/standalone.js +1 -0
  89. package/dist-electron/services/local-ai-core/src/scheduler/cron.js +151 -39
  90. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +2 -4
  91. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-application-service.js +64 -12
  92. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +71 -4
  93. package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +10 -0
  94. package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +16 -1
  95. package/dist-electron/src/pages/Automation/automation-page-model.js +76 -0
  96. package/dist-electron/tests/contracts/agent-message-policy.test.js +32 -0
  97. package/dist-electron/tests/contracts/architecture-docs.test.js +30 -0
  98. package/dist-electron/tests/contracts/automation-contracts.test.js +154 -0
  99. package/dist-electron/tests/contracts/automation-renderer-model.test.js +47 -0
  100. package/dist-electron/tests/electron/automation-action-executor.test.js +75 -0
  101. package/dist-electron/tests/electron/automation-compatibility.test.js +463 -0
  102. package/dist-electron/tests/electron/automation-deployment.test.js +66 -0
  103. package/dist-electron/tests/electron/automation-engine.test.js +264 -0
  104. package/dist-electron/tests/electron/automation-monitor.test.js +1333 -0
  105. package/dist-electron/tests/electron/automation-sandbox-runner.test.js +344 -0
  106. package/dist-electron/tests/electron/automation-script-approval.test.js +553 -0
  107. package/dist-electron/tests/electron/automation-script-package.test.js +499 -0
  108. package/dist-electron/tests/electron/automation-script-runner.test.js +120 -0
  109. package/dist-electron/tests/electron/automation-service.test.js +1078 -0
  110. package/dist-electron/tests/electron/automation-store.test.js +593 -0
  111. package/dist-electron/tests/electron/condition-trigger-skill.test.js +93 -0
  112. package/dist-electron/tests/electron/core-client.test.js +99 -0
  113. package/dist-electron/tests/electron/lac-cli.test.js +110 -0
  114. package/dist-electron/tests/electron/workspace-task-store.test.js +25 -0
  115. package/dist-electron/tests/integration/automation-routes.test.js +186 -0
  116. package/dist-electron/tests/integration/automation-sandbox-runtime.test.js +92 -0
  117. package/package.json +4 -3
  118. package/dist/renderer/assets/CronList-CupZoNwW.js +0 -1
  119. package/dist/renderer/assets/Dashboard-nLZnOtcs.js +0 -41
  120. package/dist/renderer/assets/MonitorList-BlX82rXF.js +0 -1
  121. package/dist/renderer/assets/en-CmAr_0e6.js +0 -1
  122. package/dist/renderer/assets/es-Cs9x52ca.js +0 -1
  123. package/dist/renderer/assets/index-Dd00oqgY.js +0 -167
  124. package/dist/renderer/assets/ja-DtvtBRTW.js +0 -1
  125. package/dist/renderer/assets/play-DBJ8wsSy.js +0 -6
  126. package/dist/renderer/assets/zh-CcJ9vBZj.js +0 -1
  127. package/dist/renderer/assets/zh-TW-elI72AC1.js +0 -1
@@ -0,0 +1,792 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isPrivateNetworkAddress = exports.AnthropicSandboxRunner = exports.buildSandboxConfig = exports.SandboxUnavailableError = exports.PRIVATE_NETWORK_DENY_HOSTS = void 0;
4
+ exports.buildSandboxRuntimeConfig = buildSandboxRuntimeConfig;
5
+ exports.createAnthropicSandboxRunner = createAnthropicSandboxRunner;
6
+ const node_child_process_1 = require("node:child_process");
7
+ const promises_1 = require("node:dns/promises");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_net_1 = require("node:net");
10
+ const node_path_1 = require("node:path");
11
+ const node_os_1 = require("node:os");
12
+ const node_crypto_1 = require("node:crypto");
13
+ /**
14
+ * Explicit hostnames are kept in the runtime deny list because Sandbox Runtime
15
+ * evaluates its allow/deny rules before its request hook. The request hook
16
+ * below additionally rejects all RFC1918/link-local/loopback IPs, including
17
+ * addresses not represented by these finite host rules.
18
+ */
19
+ exports.PRIVATE_NETWORK_DENY_HOSTS = [
20
+ 'localhost',
21
+ 'localhost.localdomain',
22
+ 'ip6-localhost',
23
+ 'ip6-loopback',
24
+ '127.0.0.1',
25
+ '127.0.0.2',
26
+ '127.255.255.255',
27
+ '0.0.0.0',
28
+ '::1',
29
+ '[::1]',
30
+ '169.254.169.254',
31
+ '169.254.170.2',
32
+ '10.0.0.1',
33
+ '10.255.255.254',
34
+ '172.16.0.1',
35
+ '172.31.255.254',
36
+ '192.168.0.1',
37
+ '192.168.255.254',
38
+ '100.64.0.1',
39
+ '100.127.255.254',
40
+ '192.0.0.1',
41
+ '192.0.2.1',
42
+ '198.18.0.1',
43
+ '198.51.100.1',
44
+ '203.0.113.1',
45
+ 'metadata.google.internal',
46
+ ];
47
+ const CAPABILITY_ORDER = ['sandbox_unavailable', 'bwrap', 'socat', 'rg', 'seccomp', 'network.namespace', 'sandbox_runtime', 'apparmor.userns'];
48
+ const DEFAULT_WRITE_DENY_PATHS = [
49
+ '/tmp/claude',
50
+ '/private/tmp/claude',
51
+ (0, node_path_1.join)((0, node_os_1.homedir)(), '.npm/_logs'),
52
+ (0, node_path_1.join)((0, node_os_1.homedir)(), '.claude/debug'),
53
+ ];
54
+ const CONTROLLED_TEMP_ROOT = (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-automation-runs');
55
+ const MAX_NETWORK_AUDIT_ENTRIES = 100;
56
+ let managerOperation = Promise.resolve();
57
+ async function withManagerLock(operation) {
58
+ const previous = managerOperation;
59
+ let release;
60
+ managerOperation = new Promise((resolveRelease) => { release = resolveRelease; });
61
+ await previous;
62
+ try {
63
+ return await operation();
64
+ }
65
+ finally {
66
+ release();
67
+ }
68
+ }
69
+ class SandboxUnavailableError extends Error {
70
+ missing;
71
+ code = 'sandbox_unavailable';
72
+ constructor(missing) {
73
+ super(`sandbox_unavailable${missing.length > 0 ? `: ${missing.join(', ')}` : ''}`);
74
+ this.missing = missing;
75
+ this.name = 'SandboxUnavailableError';
76
+ }
77
+ }
78
+ exports.SandboxUnavailableError = SandboxUnavailableError;
79
+ /**
80
+ * Build the native runtime policy from one script invocation. Keeping this
81
+ * pure makes the security policy testable without starting proxy processes.
82
+ */
83
+ function buildSandboxRuntimeConfig(input) {
84
+ const cwd = input.cwd || input.workspacePath || process.cwd();
85
+ const packagePath = absolutePath(input.packagePath || input.packageRoot || cwd);
86
+ const tempRoot = absolutePath(input.tempRoot || CONTROLLED_TEMP_ROOT);
87
+ assertSafeTempRoot(tempRoot);
88
+ const tempPath = absolutePath(input.tempDir || input.tempPath || input.temporaryDirectory || (0, node_path_1.join)(tempRoot, 'run'));
89
+ assertContainedPath(tempPath, tempRoot, 'tempDir');
90
+ const mode = resolveNetworkMode(input);
91
+ const manifestCapabilities = input.manifest?.capabilities;
92
+ const allowedReadDirs = uniqueAbsolutePaths([
93
+ ...(input.allowedReadDirs || []),
94
+ ...(manifestCapabilities?.allowedReadDirs || []),
95
+ ]);
96
+ const explicitDenyRead = uniqueAbsolutePaths(input.denyRead || []);
97
+ const interpreterPath = input.interpreterPath ? absolutePath(input.interpreterPath) : process.execPath;
98
+ const allowRead = uniqueAbsolutePaths([packagePath, tempPath, interpreterPath, (0, node_path_1.dirname)(interpreterPath), ...allowedReadDirs])
99
+ .filter((path) => !explicitDenyRead.some((deny) => path === deny || path.startsWith(`${deny}${node_path_1.sep}`) || deny.startsWith(`${path}${node_path_1.sep}`)));
100
+ const systemTemp = (0, node_path_1.resolve)((0, node_os_1.tmpdir)());
101
+ const broadReadDeny = [cwd, (0, node_os_1.homedir)(), (0, node_path_1.dirname)(packagePath), ...(input.userDataPath ? [absolutePath(input.userDataPath)] : [])]
102
+ .filter((path) => path !== node_path_1.sep && path !== systemTemp && !systemTemp.startsWith(`${path}${node_path_1.sep}`));
103
+ if ((0, node_path_1.dirname)(cwd) !== node_path_1.sep && (0, node_path_1.dirname)(cwd) !== systemTemp && !systemTemp.startsWith(`${(0, node_path_1.dirname)(cwd)}${node_path_1.sep}`))
104
+ broadReadDeny.push((0, node_path_1.dirname)(cwd));
105
+ const denyRead = uniqueAbsolutePaths([
106
+ // ASRT uses deny-then-allow semantics. Keep workspace/user data denied,
107
+ // then carve out only the immutable package and declared read roots.
108
+ ...broadReadDeny,
109
+ ...explicitDenyRead,
110
+ ]);
111
+ // Keep every request on the callback path. A non-empty ASRT allowlist would
112
+ // short-circuit the callback for matching hosts and reintroduce DNS/private
113
+ // destination bypasses.
114
+ const allowedDomains = [];
115
+ const deniedDomains = mode === 'none'
116
+ ? ['*']
117
+ : [...exports.PRIVATE_NETWORK_DENY_HOSTS];
118
+ return {
119
+ network: {
120
+ // Public mode intentionally leaves the allowlist empty and delegates
121
+ // each host to the ask callback. This keeps numeric private addresses
122
+ // and DNS aliases on the same callback path as HTTPS CONNECT/SOCKS.
123
+ allowedDomains,
124
+ deniedDomains: [...deniedDomains],
125
+ strictAllowlist: mode === 'none',
126
+ allowLocalBinding: false,
127
+ allowUnixSockets: [],
128
+ filterRequest: async (request) => {
129
+ const host = new URL(request.url).hostname;
130
+ if (await isPrivateOrLocalHost(host)) {
131
+ return { action: 'deny', reason: 'private or localhost egress is not permitted' };
132
+ }
133
+ return { action: 'allow' };
134
+ },
135
+ },
136
+ filesystem: {
137
+ // The staged package is readable but never writable. `allowWrite` is
138
+ // intentionally a singleton: no cwd/package/config writes are allowed.
139
+ denyRead,
140
+ allowRead,
141
+ allowWrite: [tempPath],
142
+ denyWrite: [packagePath, ...DEFAULT_WRITE_DENY_PATHS],
143
+ allowGitConfig: false,
144
+ },
145
+ allowPty: false,
146
+ };
147
+ }
148
+ /** Alias kept small and discoverable for callers that name the policy a sandbox config. */
149
+ exports.buildSandboxConfig = buildSandboxRuntimeConfig;
150
+ class AnthropicSandboxRunner {
151
+ platform;
152
+ commandExists;
153
+ managerOverride;
154
+ appArmorUsernsRestricted;
155
+ probeInitialization;
156
+ tempRoot;
157
+ managerPromise;
158
+ probePromise;
159
+ initialized = false;
160
+ constructor(options = {}) {
161
+ this.platform = normalizePlatform(options.platform || process.platform);
162
+ this.commandExists = options.commandExists || defaultCommandExists;
163
+ this.managerOverride = options.manager;
164
+ this.appArmorUsernsRestricted = options.appArmorUsernsRestricted;
165
+ this.probeInitialization = options.probeInitialization !== false;
166
+ this.tempRoot = absolutePath(options.tempRoot || CONTROLLED_TEMP_ROOT);
167
+ assertSafeTempRoot(this.tempRoot);
168
+ (0, node_fs_1.mkdirSync)(this.tempRoot, { recursive: true });
169
+ }
170
+ async probe() {
171
+ if (!this.probePromise) {
172
+ this.probePromise = withManagerLock(() => this.probeInternal()).then((result) => {
173
+ // Do not permanently cache a transient initialization/reset failure;
174
+ // a later diagnostics/run call must be able to recover.
175
+ if (!result.available)
176
+ this.probePromise = undefined;
177
+ return result;
178
+ });
179
+ }
180
+ return this.probePromise;
181
+ }
182
+ async run(input) {
183
+ const capability = await this.probe();
184
+ if (!capability.available)
185
+ throw new SandboxUnavailableError(capability.missing);
186
+ return withManagerLock(() => this.runLocked(input));
187
+ }
188
+ async runLocked(input) {
189
+ const suppliedTempPath = input.tempDir || input.tempPath || input.temporaryDirectory;
190
+ const tempPath = absolutePath(suppliedTempPath || (0, node_path_1.join)(this.tempRoot, `run-${(0, node_crypto_1.randomUUID)()}`));
191
+ const ownsTempPath = !suppliedTempPath;
192
+ assertContainedPath(tempPath, this.tempRoot, 'tempDir');
193
+ (0, node_fs_1.mkdirSync)(tempPath, { recursive: true });
194
+ const effectiveInput = { ...input, tempDir: tempPath, tempRoot: this.tempRoot };
195
+ const manager = await this.getManager();
196
+ const config = buildSandboxRuntimeConfig(effectiveInput);
197
+ const networkAudit = [];
198
+ // SandboxManager is a process-global singleton. Always tear down any
199
+ // previous owner before applying this invocation's filesystem policy.
200
+ try {
201
+ await manager.reset?.();
202
+ const mode = resolveNetworkMode(effectiveInput);
203
+ const allowedDomains = effectiveInput.allowedDomains ?? effectiveInput.manifest?.capabilities?.allowedDomains ?? [];
204
+ await manager.initialize(config, async ({ host, port }) => {
205
+ const allowed = mode !== 'none' && !(await isPrivateOrLocalHost(host))
206
+ && (mode !== 'restricted' || allowedDomains.some((pattern) => matchesDomain(host, pattern)));
207
+ if (networkAudit.length < MAX_NETWORK_AUDIT_ENTRIES) {
208
+ networkAudit.push({ host: sanitizeAuditHost(host), ...(port === undefined ? {} : { port }), allowed, timestamp: new Date().toISOString() });
209
+ }
210
+ return allowed;
211
+ }, false);
212
+ this.initialized = true;
213
+ }
214
+ catch (error) {
215
+ this.initialized = false;
216
+ if (ownsTempPath)
217
+ (0, node_fs_1.rmSync)(tempPath, { recursive: true, force: true });
218
+ throw new SandboxUnavailableError(['sandbox_runtime']);
219
+ }
220
+ try {
221
+ if (manager.waitForNetworkInitialization) {
222
+ const ready = await manager.waitForNetworkInitialization();
223
+ if (!ready)
224
+ throw new SandboxUnavailableError(['sandbox_runtime']);
225
+ }
226
+ // The wrapped command is deliberately scoped to this method. It is
227
+ // never returned, logged, or attached to SandboxRunResult.
228
+ if (!manager.wrapWithSandboxArgv)
229
+ throw new SandboxUnavailableError(['sandbox_runtime']);
230
+ const tempEnvKeys = ['TMPDIR', 'TMP', 'TEMP', 'CLAUDE_CODE_TMPDIR', 'CLAUDE_TMPDIR'];
231
+ const previousTempEnv = Object.fromEntries(tempEnvKeys.map((key) => [key, process.env[key]]));
232
+ for (const key of tempEnvKeys)
233
+ process.env[key] = tempPath;
234
+ let wrapped;
235
+ try {
236
+ wrapped = await manager.wrapWithSandboxArgv(effectiveInput.command);
237
+ }
238
+ finally {
239
+ for (const key of tempEnvKeys) {
240
+ if (previousTempEnv[key] === undefined)
241
+ delete process.env[key];
242
+ else
243
+ process.env[key] = previousTempEnv[key];
244
+ }
245
+ }
246
+ const result = await spawnWrappedCommand(wrapped, effectiveInput);
247
+ return { ...result, ...(networkAudit.length === 0 ? {} : { networkAudit }) };
248
+ }
249
+ finally {
250
+ manager.cleanupAfterCommand?.();
251
+ try {
252
+ await manager.reset?.();
253
+ }
254
+ finally {
255
+ this.initialized = false;
256
+ if (ownsTempPath)
257
+ (0, node_fs_1.rmSync)(tempPath, { recursive: true, force: true });
258
+ }
259
+ }
260
+ }
261
+ async probeInternal() {
262
+ const missing = new Set();
263
+ const unverified = new Set();
264
+ if (this.platform === 'windows') {
265
+ return { available: false, platform: this.platform, missing: ['sandbox_unavailable'] };
266
+ }
267
+ const manager = await this.getManager().catch(() => undefined);
268
+ if (!manager)
269
+ missing.add('sandbox_runtime');
270
+ if (manager?.isSupportedPlatform && !manager.isSupportedPlatform())
271
+ missing.add('sandbox_unavailable');
272
+ if (this.platform === 'linux') {
273
+ try {
274
+ const deps = manager?.checkDependencies?.() || { errors: [], warnings: [] };
275
+ addDependencyNames(missing, [...deps.errors, ...deps.warnings]);
276
+ }
277
+ catch {
278
+ missing.add('sandbox_runtime');
279
+ }
280
+ }
281
+ else {
282
+ if (!this.commandExists('sandbox-exec'))
283
+ missing.add('sandbox-exec');
284
+ if (!this.commandExists('rg'))
285
+ missing.add('rg');
286
+ }
287
+ const behaviorBlockers = this.platform === 'linux'
288
+ ? ['sandbox_runtime', 'sandbox_unavailable', 'bwrap', 'socat', 'rg']
289
+ : ['sandbox_runtime', 'sandbox_unavailable', 'sandbox-exec'];
290
+ const behaviorBlocked = behaviorBlockers.some((capability) => missing.has(capability));
291
+ const markLinuxBehaviorUnverified = () => {
292
+ // An earlier failure prevents the only authoritative behavior proof.
293
+ // Keep every unproven isolation capability failed rather than reporting
294
+ // a misleading green row from absence of evidence.
295
+ missing.add('apparmor.userns');
296
+ missing.add('network.namespace');
297
+ missing.add('seccomp');
298
+ unverified.add('apparmor.userns');
299
+ unverified.add('network.namespace');
300
+ unverified.add('seccomp');
301
+ };
302
+ if (this.platform === 'linux' && behaviorBlocked)
303
+ markLinuxBehaviorUnverified();
304
+ if (!this.probeInitialization) {
305
+ missing.add('sandbox_runtime');
306
+ if (this.platform === 'linux')
307
+ markLinuxBehaviorUnverified();
308
+ }
309
+ if (manager && this.probeInitialization && !behaviorBlocked) {
310
+ let attempted = false;
311
+ const probeTempPath = (0, node_path_1.join)(this.tempRoot, `probe-${(0, node_crypto_1.randomUUID)()}`);
312
+ try {
313
+ (0, node_fs_1.mkdirSync)(probeTempPath, { recursive: true });
314
+ const probeConfig = buildSandboxRuntimeConfig({
315
+ command: 'true',
316
+ cwd: process.cwd(),
317
+ packagePath: process.cwd(),
318
+ tempRoot: this.tempRoot,
319
+ tempDir: probeTempPath,
320
+ network: 'none',
321
+ });
322
+ attempted = true;
323
+ await manager.initialize(probeConfig);
324
+ if (manager.waitForNetworkInitialization && !(await manager.waitForNetworkInitialization())) {
325
+ missing.add('sandbox_runtime');
326
+ if (this.platform === 'linux')
327
+ markLinuxBehaviorUnverified();
328
+ }
329
+ else if (!manager.wrapWithSandboxArgv) {
330
+ missing.add('sandbox_runtime');
331
+ if (this.platform === 'linux')
332
+ markLinuxBehaviorUnverified();
333
+ }
334
+ else {
335
+ const wrapped = await manager.wrapWithSandboxArgv('true');
336
+ let result;
337
+ try {
338
+ result = await spawnWrappedCommand(wrapped, {
339
+ command: 'true',
340
+ cwd: process.cwd(),
341
+ packagePath: process.cwd(),
342
+ tempRoot: this.tempRoot,
343
+ tempDir: probeTempPath,
344
+ network: 'none',
345
+ timeoutMs: 5_000,
346
+ stdoutBytes: 16_384,
347
+ stderrBytes: 16_384,
348
+ });
349
+ }
350
+ finally {
351
+ manager.cleanupAfterCommand?.();
352
+ }
353
+ if (result.exitCode !== 0 || result.signal) {
354
+ if (this.platform === 'linux') {
355
+ const failure = classifyLinuxBehaviorFailures(result.stderr, this.isAppArmorUsernsRestricted());
356
+ for (const capability of failure.missing) {
357
+ missing.add(capability);
358
+ }
359
+ for (const capability of failure.unverified)
360
+ unverified.add(capability);
361
+ }
362
+ else {
363
+ missing.add('sandbox_runtime');
364
+ }
365
+ }
366
+ }
367
+ }
368
+ catch (error) {
369
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
370
+ if (this.platform === 'linux') {
371
+ const failure = classifyLinuxBehaviorFailures(message, this.isAppArmorUsernsRestricted());
372
+ for (const capability of failure.missing) {
373
+ missing.add(capability);
374
+ }
375
+ for (const capability of failure.unverified)
376
+ unverified.add(capability);
377
+ }
378
+ else {
379
+ missing.add('sandbox_runtime');
380
+ }
381
+ }
382
+ finally {
383
+ if (attempted) {
384
+ try {
385
+ await manager.reset?.();
386
+ }
387
+ catch {
388
+ missing.add('sandbox_runtime');
389
+ }
390
+ }
391
+ (0, node_fs_1.rmSync)(probeTempPath, { recursive: true, force: true });
392
+ }
393
+ }
394
+ const orderedUnverified = orderCapabilities(unverified);
395
+ return {
396
+ available: missing.size === 0,
397
+ platform: this.platform,
398
+ missing: orderCapabilities(missing),
399
+ ...(orderedUnverified.length === 0 ? {} : { unverified: orderedUnverified }),
400
+ };
401
+ }
402
+ isAppArmorUsernsRestricted() {
403
+ if (typeof this.appArmorUsernsRestricted === 'function')
404
+ return this.appArmorUsernsRestricted();
405
+ if (typeof this.appArmorUsernsRestricted === 'boolean')
406
+ return this.appArmorUsernsRestricted;
407
+ try {
408
+ // This value is diagnostic context only. A dedicated AppArmor profile
409
+ // can grant userns while the global restriction remains enabled, so the
410
+ // wrapped behavioral probe above is always authoritative.
411
+ return (0, node_fs_1.readFileSync)('/proc/sys/kernel/apparmor_restrict_unprivileged_userns', 'utf8').trim() === '1';
412
+ }
413
+ catch {
414
+ return false;
415
+ }
416
+ }
417
+ async getManager() {
418
+ if (this.managerOverride)
419
+ return this.managerOverride;
420
+ if (!this.managerPromise) {
421
+ this.managerPromise = importSandboxRuntimeManager();
422
+ }
423
+ return this.managerPromise;
424
+ }
425
+ }
426
+ exports.AnthropicSandboxRunner = AnthropicSandboxRunner;
427
+ function createAnthropicSandboxRunner(options = {}) {
428
+ return new AnthropicSandboxRunner(options);
429
+ }
430
+ async function importSandboxRuntimeManager() {
431
+ // TypeScript emits CommonJS for Electron. A native dynamic import through
432
+ // Function avoids transpiling this call to require(), since ASRT is ESM.
433
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
434
+ const module = await dynamicImport('@anthropic-ai/sandbox-runtime');
435
+ return module.SandboxManager;
436
+ }
437
+ function resolveNetworkMode(input) {
438
+ return input.networkMode || input.network || input.manifest?.capabilities?.network || 'none';
439
+ }
440
+ function normalizePlatform(value) {
441
+ if (value === 'darwin' || value === 'macos')
442
+ return 'macos';
443
+ if (value === 'win32' || value === 'windows')
444
+ return 'windows';
445
+ if (value === 'linux')
446
+ return 'linux';
447
+ return 'windows';
448
+ }
449
+ function absolutePath(path) {
450
+ if (!(0, node_path_1.isAbsolute)(path))
451
+ throw new Error('Sandbox paths must be absolute.');
452
+ return (0, node_path_1.resolve)(path);
453
+ }
454
+ function assertContainedPath(candidate, root, label) {
455
+ const candidatePath = (0, node_path_1.resolve)(candidate);
456
+ const rootPath = (0, node_path_1.resolve)(root);
457
+ const relativePath = (0, node_path_1.relative)(rootPath, candidatePath);
458
+ if (relativePath === '..' || relativePath.startsWith(`..${node_path_1.sep}`) || (0, node_path_1.isAbsolute)(relativePath)) {
459
+ throw new Error(`${label} must be inside the controlled temporary root.`);
460
+ }
461
+ }
462
+ function assertSafeTempRoot(root) {
463
+ const normalized = (0, node_path_1.resolve)(root);
464
+ if (normalized === node_path_1.sep || normalized === (0, node_path_1.resolve)((0, node_os_1.tmpdir)()) || normalized === (0, node_path_1.resolve)((0, node_os_1.homedir)())) {
465
+ throw new Error('tempRoot must be a dedicated, non-broad temporary directory.');
466
+ }
467
+ }
468
+ function uniqueAbsolutePaths(paths) {
469
+ return [...new Set(paths.filter(Boolean).map(absolutePath))];
470
+ }
471
+ function addDependencyNames(missing, errors) {
472
+ for (const error of errors) {
473
+ const lower = error.toLowerCase();
474
+ if (lower.includes('bwrap') || lower.includes('bubblewrap'))
475
+ missing.add('bwrap');
476
+ if (lower.includes('socat'))
477
+ missing.add('socat');
478
+ if (lower.includes('ripgrep') || /\brg\b/.test(lower))
479
+ missing.add('rg');
480
+ if (lower.includes('seccomp'))
481
+ missing.add('seccomp');
482
+ if (lower.includes('network namespace') || lower.includes('unshare-net'))
483
+ missing.add('network.namespace');
484
+ if (lower.includes('unsupported platform'))
485
+ missing.add('sandbox_unavailable');
486
+ if (lower.includes('sandbox'))
487
+ missing.add('sandbox_runtime');
488
+ }
489
+ }
490
+ function classifyLinuxBehaviorFailures(message, appArmorUsernsRestricted) {
491
+ const lower = message.toLowerCase();
492
+ if (lower.includes('seccomp') || lower.includes('apply-seccomp')) {
493
+ return { missing: ['seccomp'], unverified: [] };
494
+ }
495
+ if (lower.includes('unshare-net') || lower.includes('network namespace')) {
496
+ return { missing: ['network.namespace', 'seccomp'], unverified: ['seccomp'] };
497
+ }
498
+ if (lower.includes('apparmor')) {
499
+ return {
500
+ missing: ['apparmor.userns', 'network.namespace', 'seccomp'],
501
+ unverified: ['network.namespace', 'seccomp'],
502
+ };
503
+ }
504
+ if (appArmorUsernsRestricted &&
505
+ (lower.includes('new namespace') || lower.includes('user namespace') || lower.includes('operation not permitted'))) {
506
+ return {
507
+ missing: ['apparmor.userns', 'network.namespace', 'seccomp'],
508
+ unverified: ['network.namespace', 'seccomp'],
509
+ };
510
+ }
511
+ if (lower.includes('new namespace') || lower.includes('user namespace') || lower.includes('operation not permitted')) {
512
+ return {
513
+ missing: ['apparmor.userns', 'network.namespace', 'seccomp'],
514
+ unverified: ['network.namespace', 'seccomp'],
515
+ };
516
+ }
517
+ return {
518
+ missing: ['sandbox_runtime', 'apparmor.userns', 'network.namespace', 'seccomp'],
519
+ unverified: ['apparmor.userns', 'network.namespace', 'seccomp'],
520
+ };
521
+ }
522
+ function orderCapabilities(values) {
523
+ return [...values].sort((left, right) => {
524
+ const leftIndex = CAPABILITY_ORDER.indexOf(left);
525
+ const rightIndex = CAPABILITY_ORDER.indexOf(right);
526
+ return (leftIndex < 0 ? CAPABILITY_ORDER.length : leftIndex) - (rightIndex < 0 ? CAPABILITY_ORDER.length : rightIndex) || left.localeCompare(right);
527
+ });
528
+ }
529
+ function defaultCommandExists(command) {
530
+ const pathValue = process.env.PATH || '';
531
+ return pathValue.split(node_path_1.delimiter).some((directory) => (0, node_fs_1.existsSync)((0, node_path_1.join)(directory, command)));
532
+ }
533
+ async function spawnWrappedCommand(wrapped, input) {
534
+ if (wrapped.argv.length === 0 || !(0, node_path_1.isAbsolute)(wrapped.argv[0])) {
535
+ throw new SandboxUnavailableError(['sandbox_runtime']);
536
+ }
537
+ const runtimePath = wrapped.env.PATH || '/usr/bin:/bin';
538
+ const allowedEnv = input.allowedEnv ?? input.manifest?.env ?? [];
539
+ const inputEnv = Object.fromEntries(Object.entries(input.env || {}).filter(([key]) => allowedEnv.includes(key) && !isProxyEnvKey(key)));
540
+ const tempPath = absolutePath(input.tempDir || input.tempPath || input.temporaryDirectory || CONTROLLED_TEMP_ROOT);
541
+ const child = (0, node_child_process_1.spawn)(wrapped.argv[0], wrapped.argv.slice(1), {
542
+ cwd: input.cwd || input.workspacePath || process.cwd(),
543
+ env: {
544
+ ...sanitizeRuntimeEnv(wrapped.env),
545
+ ...sanitizeRuntimeEnv(inputEnv, allowedEnv),
546
+ PATH: runtimePath,
547
+ TMPDIR: tempPath,
548
+ TMP: tempPath,
549
+ TEMP: tempPath,
550
+ CLAUDE_CODE_TMPDIR: tempPath,
551
+ CLAUDE_TMPDIR: tempPath,
552
+ },
553
+ shell: false,
554
+ // A new process group lets timeout/overflow termination include shell
555
+ // descendants rather than leaving a detached child running in the sandbox.
556
+ detached: process.platform !== 'win32',
557
+ stdio: ['pipe', 'pipe', 'pipe'],
558
+ });
559
+ if (input.stdin !== undefined)
560
+ child.stdin?.end(input.stdin, 'utf8');
561
+ else
562
+ child.stdin?.end();
563
+ return collectChildResult(child, input.timeoutMs, input.signal, input);
564
+ }
565
+ function sanitizeRuntimeEnv(runtimeEnv, allowedDataKeys = []) {
566
+ const result = {};
567
+ const blocked = new Set(['BASH_ENV', 'ENV', 'SHELLOPTS', 'NODE_OPTIONS', 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'DYLD_INSERT_LIBRARIES', 'DYLD_LIBRARY_PATH', 'PATH']);
568
+ for (const [key, value] of Object.entries(runtimeEnv)) {
569
+ if (value === undefined)
570
+ continue;
571
+ if (blocked.has(key))
572
+ continue;
573
+ if (allowedDataKeys.includes(key) && !blocked.has(key) && !isProxyEnvKey(key)) {
574
+ result[key] = value;
575
+ }
576
+ else if (key === 'PATH' || key === 'HOME' || key === 'USER' || key === 'SHELL' ||
577
+ key === 'LANG' || key.startsWith('LC_') || key.startsWith('HTTP_PROXY') ||
578
+ key.startsWith('HTTPS_PROXY') || key.startsWith('ALL_PROXY') ||
579
+ key.startsWith('http_proxy') || key.startsWith('https_proxy') || key.startsWith('all_proxy') ||
580
+ key === 'SSL_CERT_FILE' || key === 'GIT_SSL_CAINFO' || key === 'CURL_CA_BUNDLE' ||
581
+ key === 'REQUESTS_CA_BUNDLE' || key === 'PIP_CERT' || key === 'AWS_CA_BUNDLE' ||
582
+ key === 'CARGO_HTTP_CAINFO' || key === 'DENO_CERT' || key === 'CLOUDSDK_CORE_CUSTOM_CA_CERTS_FILE' ||
583
+ key === 'CLAUDE_CODE_TMPDIR' || key === 'CLAUDE_TMPDIR' ||
584
+ key === 'CLAUDE_CODE_HOST_HTTP_PROXY_PORT' || key === 'CLAUDE_CODE_HOST_SOCKS_PROXY_PORT') {
585
+ result[key] = value;
586
+ }
587
+ }
588
+ // NO_PROXY is intentionally omitted: it would let a script bypass the
589
+ // mediated proxy and defeat public/private host policy.
590
+ delete result.NO_PROXY;
591
+ delete result.no_proxy;
592
+ return result;
593
+ }
594
+ function isProxyEnvKey(key) {
595
+ return key === 'HTTP_PROXY' || key === 'HTTPS_PROXY' || key === 'ALL_PROXY' ||
596
+ key === 'http_proxy' || key === 'https_proxy' || key === 'all_proxy' ||
597
+ key === 'NO_PROXY' || key === 'no_proxy';
598
+ }
599
+ function collectChildResult(child, timeoutMs = 30_000, signal, input) {
600
+ return new Promise((resolveResult) => {
601
+ let stdout = '';
602
+ let stderr = '';
603
+ let settled = false;
604
+ let outputLimitExceeded;
605
+ const terminateTree = () => {
606
+ if (child.pid && process.platform !== 'win32') {
607
+ try {
608
+ process.kill(-child.pid, 'SIGKILL');
609
+ return;
610
+ }
611
+ catch { /* child may have already exited */ }
612
+ }
613
+ child.kill('SIGKILL');
614
+ };
615
+ const timer = setTimeout(terminateTree, timeoutMs);
616
+ const abort = () => terminateTree();
617
+ signal?.addEventListener('abort', abort, { once: true });
618
+ const stdoutParts = [];
619
+ const stderrParts = [];
620
+ let stdoutSize = 0;
621
+ let stderrSize = 0;
622
+ const append = (stream, chunk) => {
623
+ const limit = stream === 'stdout' ? input?.stdoutBytes : input?.stderrBytes;
624
+ const current = stream === 'stdout' ? stdoutSize : stderrSize;
625
+ const remaining = limit === undefined ? undefined : limit - current;
626
+ if (remaining === undefined || remaining >= chunk.byteLength) {
627
+ if (stream === 'stdout') {
628
+ stdoutParts.push(chunk);
629
+ stdoutSize += chunk.byteLength;
630
+ }
631
+ else {
632
+ stderrParts.push(chunk);
633
+ stderrSize += chunk.byteLength;
634
+ }
635
+ return;
636
+ }
637
+ const truncated = truncateUtf8(chunk, Math.max(0, remaining));
638
+ if (stream === 'stdout') {
639
+ stdoutParts.push(truncated);
640
+ stdoutSize += truncated.byteLength;
641
+ }
642
+ else {
643
+ stderrParts.push(truncated);
644
+ stderrSize += truncated.byteLength;
645
+ }
646
+ if (!outputLimitExceeded) {
647
+ outputLimitExceeded = stream;
648
+ terminateTree();
649
+ }
650
+ };
651
+ child.stdout?.on('data', (chunk) => append('stdout', chunk));
652
+ child.stderr?.on('data', (chunk) => append('stderr', chunk));
653
+ const finish = (exitCode, childSignal) => {
654
+ if (settled)
655
+ return;
656
+ settled = true;
657
+ clearTimeout(timer);
658
+ signal?.removeEventListener('abort', abort);
659
+ stdout = Buffer.concat(stdoutParts).toString('utf8');
660
+ stderr = Buffer.concat(stderrParts).toString('utf8');
661
+ resolveResult({ exitCode, signal: childSignal, stdout, stderr, ...(outputLimitExceeded ? { outputLimitExceeded } : {}) });
662
+ };
663
+ child.once('error', (error) => {
664
+ stderrParts.push(Buffer.from(error instanceof Error ? error.message : String(error), 'utf8'));
665
+ finish(null, null);
666
+ });
667
+ child.once('exit', (exitCode, childSignal) => {
668
+ // Retain the normal stdout tail while streams close. If a background
669
+ // descendant inherited them, tear down the process group shortly after
670
+ // wrapper exit instead of allowing an orphan to keep the run open.
671
+ setTimeout(() => {
672
+ if (settled)
673
+ return;
674
+ let groupAlive = false;
675
+ if (child.pid && process.platform !== 'win32') {
676
+ try {
677
+ process.kill(-child.pid, 0);
678
+ groupAlive = true;
679
+ }
680
+ catch { /* group exited; let pipes drain */ }
681
+ }
682
+ if (groupAlive) {
683
+ terminateTree();
684
+ child.stdout?.destroy();
685
+ child.stderr?.destroy();
686
+ finish(exitCode, childSignal);
687
+ return;
688
+ }
689
+ // A closed group should normally emit close immediately. Retain tail
690
+ // output, but still bound a pathological inherited pipe hang.
691
+ setTimeout(() => {
692
+ if (settled)
693
+ return;
694
+ child.stdout?.destroy();
695
+ child.stderr?.destroy();
696
+ finish(exitCode, childSignal);
697
+ }, 1_000);
698
+ }, 50);
699
+ });
700
+ child.once('close', (exitCode, childSignal) => finish(exitCode, childSignal));
701
+ });
702
+ }
703
+ function truncateUtf8(value, limit) {
704
+ if (value.byteLength <= limit)
705
+ return value;
706
+ // Keep the longest valid UTF-8 prefix. Only the final code point can be
707
+ // partial, so four attempts cover UTF-8's maximum sequence width while
708
+ // preserving legitimate U+FFFD characters already present in the stream.
709
+ const decoder = new TextDecoder('utf-8', { fatal: true });
710
+ for (let end = limit; end >= Math.max(0, limit - 3); end -= 1) {
711
+ try {
712
+ decoder.decode(value.subarray(0, end));
713
+ return value.subarray(0, end);
714
+ }
715
+ catch { /* try a shorter final boundary */ }
716
+ }
717
+ return Buffer.alloc(0);
718
+ }
719
+ async function isPrivateOrLocalHost(host) {
720
+ const normalized = host.toLowerCase().replace(/^\[|\]$/g, '').replace(/\.$/, '');
721
+ if (exports.PRIVATE_NETWORK_DENY_HOSTS.some((entry) => entry.replace(/^\[|\]$/g, '').toLowerCase() === normalized))
722
+ return true;
723
+ const ipVersion = (0, node_net_1.isIP)(normalized);
724
+ if (ipVersion === 4 && isPrivateIpv4(normalized))
725
+ return true;
726
+ if (ipVersion === 6 && isPrivateIpv6(normalized))
727
+ return true;
728
+ if (ipVersion === 0) {
729
+ try {
730
+ const addresses = await (0, promises_1.lookup)(normalized, { all: true, verbatim: true });
731
+ // 198.18/15 is a reserved benchmark range commonly used by local
732
+ // network shims for public DNS answers. Direct numeric destinations in
733
+ // that range remain denied; DNS aliases still deny RFC1918/link-local.
734
+ return addresses.length === 0 || addresses.some((address) => address.family === 4 ? isPrivateIpv4(address.address, false) : isPrivateIpv6(address.address));
735
+ }
736
+ catch {
737
+ // A transient DNS failure is reported by the proxy and does not turn
738
+ // into host-side execution: fail closed on resolver errors.
739
+ return true;
740
+ }
741
+ }
742
+ return false;
743
+ }
744
+ /** Deterministic policy seam used by capability/network tests. */
745
+ exports.isPrivateNetworkAddress = isPrivateOrLocalHost;
746
+ function matchesDomain(host, pattern) {
747
+ const normalizedHost = host.toLowerCase().replace(/\.$/, '');
748
+ const normalizedPattern = pattern.toLowerCase().replace(/\.$/, '');
749
+ return normalizedPattern === '*' || normalizedPattern === normalizedHost ||
750
+ (normalizedPattern.startsWith('*.') && normalizedHost.endsWith(`.${normalizedPattern.slice(2)}`));
751
+ }
752
+ function sanitizeAuditHost(value) {
753
+ const host = value.toLowerCase().replace(/^\[|\]$/g, '').replace(/\.$/, '');
754
+ return /^[a-z0-9.:-]+$/i.test(host) && !host.includes('..') ? host : 'invalid-host';
755
+ }
756
+ function isPrivateIpv4(address, includeBenchmarkRange = true) {
757
+ const parts = address.split('.').map(Number);
758
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255))
759
+ return true;
760
+ const value = (((parts[0] * 256 + parts[1]) * 256 + parts[2]) * 256 + parts[3]);
761
+ const first16 = value >>> 16;
762
+ return parts[0] === 10 ||
763
+ (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
764
+ (parts[0] === 192 && parts[1] === 168) ||
765
+ parts[0] === 127 ||
766
+ (parts[0] === 169 && parts[1] === 254) ||
767
+ (parts[0] === 0) ||
768
+ (first16 >= 0x6440 && first16 <= 0x647f) ||
769
+ (parts[0] === 192 && parts[1] === 0) ||
770
+ (parts[0] === 198 && parts[1] === 51 && parts[2] === 100) ||
771
+ (parts[0] === 203 && parts[1] === 0 && parts[2] === 113) ||
772
+ (includeBenchmarkRange && first16 >= 0xc612 && first16 <= 0xc613);
773
+ }
774
+ function isPrivateIpv6(address) {
775
+ const normalized = address.toLowerCase();
776
+ if (normalized.startsWith('::ffff:')) {
777
+ const mapped = normalized.slice('::ffff:'.length);
778
+ if ((0, node_net_1.isIP)(mapped) === 4)
779
+ return isPrivateIpv4(mapped);
780
+ const groups = mapped.split(':');
781
+ if (groups.length >= 2) {
782
+ const high = Number.parseInt(groups[groups.length - 2], 16);
783
+ const low = Number.parseInt(groups[groups.length - 1], 16);
784
+ if (Number.isInteger(high) && Number.isInteger(low)) {
785
+ const dotted = `${high >>> 8}.${high & 0xff}.${low >>> 8}.${low & 0xff}`;
786
+ return isPrivateIpv4(dotted);
787
+ }
788
+ }
789
+ return true;
790
+ }
791
+ return normalized === '::1' || normalized === '::' || normalized.startsWith('fe8') || normalized.startsWith('fe9') || normalized.startsWith('fea') || normalized.startsWith('feb') || normalized.startsWith('fc') || normalized.startsWith('fd');
792
+ }