@arnilo/prism-tools 0.0.0-stage → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -2
  4. package/browser/README.md +98 -0
  5. package/browser/skills/browser-automation/SKILL.md +14 -0
  6. package/browser/src/actions/cdp.ts +131 -0
  7. package/browser/src/actions/dialog.ts +25 -0
  8. package/browser/src/actions/download_release.ts +22 -0
  9. package/browser/src/actions/index.ts +37 -0
  10. package/browser/src/actions/locator.ts +70 -0
  11. package/browser/src/actions/navigate.ts +23 -0
  12. package/browser/src/actions/screenshot.ts +27 -0
  13. package/browser/src/actions/scroll.ts +24 -0
  14. package/browser/src/actions/select_page.ts +21 -0
  15. package/browser/src/actions/types.ts +114 -0
  16. package/browser/src/actions/upload.ts +44 -0
  17. package/browser/src/actions/wait.ts +45 -0
  18. package/browser/src/browser.ts +197 -0
  19. package/browser/src/cdp.ts +178 -0
  20. package/browser/src/checkpoint.ts +189 -0
  21. package/browser/src/config.ts +72 -0
  22. package/browser/src/downloads.ts +253 -0
  23. package/browser/src/errors.ts +31 -0
  24. package/browser/src/evaluate.ts +101 -0
  25. package/browser/src/index.ts +129 -0
  26. package/browser/src/limits.ts +271 -0
  27. package/browser/src/manager.ts +730 -0
  28. package/browser/src/network.ts +263 -0
  29. package/browser/src/observe.ts +178 -0
  30. package/browser/src/policy.ts +133 -0
  31. package/browser/src/screenshot.ts +107 -0
  32. package/browser/src/shared-sandbox.ts +53 -0
  33. package/browser/src/snapshot.ts +99 -0
  34. package/browser/src/targets.ts +165 -0
  35. package/browser/src/tools.ts +480 -0
  36. package/browser/src/types.ts +316 -0
  37. package/browser/src/uploads.ts +174 -0
  38. package/coding/README.md +337 -0
  39. package/coding/src/agent/acp-operations.ts +146 -0
  40. package/coding/src/agent/artifacts.ts +38 -0
  41. package/coding/src/agent/ask-user-decision.ts +623 -0
  42. package/coding/src/agent/atomic-write.ts +23 -0
  43. package/coding/src/agent/bounded-file.ts +21 -0
  44. package/coding/src/agent/checks.ts +301 -0
  45. package/coding/src/agent/coding-checkpoint.ts +828 -0
  46. package/coding/src/agent/delete.ts +237 -0
  47. package/coding/src/agent/diagnostics-tool.ts +163 -0
  48. package/coding/src/agent/diagnostics.ts +262 -0
  49. package/coding/src/agent/edit-diff.ts +382 -0
  50. package/coding/src/agent/edit.ts +322 -0
  51. package/coding/src/agent/effects.ts +102 -0
  52. package/coding/src/agent/env.ts +28 -0
  53. package/coding/src/agent/execution-policy.ts +89 -0
  54. package/coding/src/agent/file-mutation-queue.ts +72 -0
  55. package/coding/src/agent/forge/github.ts +603 -0
  56. package/coding/src/agent/forge/index.ts +16 -0
  57. package/coding/src/agent/forge/types.ts +185 -0
  58. package/coding/src/agent/git-aware-repository.ts +305 -0
  59. package/coding/src/agent/git-exec.ts +326 -0
  60. package/coding/src/agent/git-status.ts +177 -0
  61. package/coding/src/agent/git-tools.ts +360 -0
  62. package/coding/src/agent/git.ts +1074 -0
  63. package/coding/src/agent/glob-match.ts +146 -0
  64. package/coding/src/agent/glob.ts +182 -0
  65. package/coding/src/agent/goal-verify.ts +385 -0
  66. package/coding/src/agent/grep-worker.ts +74 -0
  67. package/coding/src/agent/grep.ts +459 -0
  68. package/coding/src/agent/identity.ts +48 -0
  69. package/coding/src/agent/index.ts +676 -0
  70. package/coding/src/agent/language/client.ts +429 -0
  71. package/coding/src/agent/language/framing.ts +190 -0
  72. package/coding/src/agent/language/index.ts +23 -0
  73. package/coding/src/agent/language/intelligence.ts +682 -0
  74. package/coding/src/agent/language/types.ts +187 -0
  75. package/coding/src/agent/lifecycle-events.ts +121 -0
  76. package/coding/src/agent/lifecycle.ts +166 -0
  77. package/coding/src/agent/limits.ts +214 -0
  78. package/coding/src/agent/move.ts +178 -0
  79. package/coding/src/agent/mutation-path.ts +52 -0
  80. package/coding/src/agent/output-accumulator.ts +304 -0
  81. package/coding/src/agent/path-utils.ts +120 -0
  82. package/coding/src/agent/process/index.ts +58 -0
  83. package/coding/src/agent/process/recovery.ts +508 -0
  84. package/coding/src/agent/process/sessions-host.ts +222 -0
  85. package/coding/src/agent/process/sessions-monitor.ts +141 -0
  86. package/coding/src/agent/process/sessions-recovery.ts +241 -0
  87. package/coding/src/agent/process/sessions-spawn.ts +613 -0
  88. package/coding/src/agent/process/sessions-teardown.ts +281 -0
  89. package/coding/src/agent/process/sessions.ts +30 -0
  90. package/coding/src/agent/process/types.ts +358 -0
  91. package/coding/src/agent/read-path-set.ts +126 -0
  92. package/coding/src/agent/read.ts +713 -0
  93. package/coding/src/agent/repository/glob.ts +147 -0
  94. package/coding/src/agent/repository/indexed-search.ts +480 -0
  95. package/coding/src/agent/repository/list.ts +131 -0
  96. package/coding/src/agent/repository/operations.ts +22 -0
  97. package/coding/src/agent/repository/path.ts +98 -0
  98. package/coding/src/agent/repository/search.ts +277 -0
  99. package/coding/src/agent/repository/types.ts +243 -0
  100. package/coding/src/agent/repository/walk.ts +126 -0
  101. package/coding/src/agent/repository.ts +12 -0
  102. package/coding/src/agent/shell-background.ts +264 -0
  103. package/coding/src/agent/shell-output.ts +75 -0
  104. package/coding/src/agent/shell.ts +526 -0
  105. package/coding/src/agent/todo.ts +399 -0
  106. package/coding/src/agent/truncate.ts +251 -0
  107. package/coding/src/agent/write.ts +170 -0
  108. package/coding/src/ask-user-host.ts +48 -0
  109. package/coding/src/config.ts +278 -0
  110. package/coding/src/hard-deny.ts +97 -0
  111. package/coding/src/index.ts +318 -0
  112. package/coding/src/principal-guard.ts +84 -0
  113. package/coding/src/sandbox.ts +92 -0
  114. package/coding/src/security/approval.ts +144 -0
  115. package/coding/src/security/command-rules.ts +57 -0
  116. package/coding/src/security/docker-cli.ts +222 -0
  117. package/coding/src/security/docker-sandbox.ts +1205 -0
  118. package/coding/src/security/e2b-sandbox.ts +853 -0
  119. package/coding/src/security/egress/dns-pin.ts +88 -0
  120. package/coding/src/security/egress/index.ts +32 -0
  121. package/coding/src/security/egress/limits.ts +57 -0
  122. package/coding/src/security/egress/policy.ts +137 -0
  123. package/coding/src/security/egress/proxy.ts +599 -0
  124. package/coding/src/security/egress/types.ts +30 -0
  125. package/coding/src/security/index.ts +199 -0
  126. package/coding/src/security/native-sandbox.ts +639 -0
  127. package/coding/src/security/path-containment.ts +55 -0
  128. package/coding/src/security/sandbox-coding-operations.ts +392 -0
  129. package/coding/src/security/sandbox-fs-operations.ts +644 -0
  130. package/coding/src/security/sandbox-limits.ts +147 -0
  131. package/coding/src/security/sandbox-tar.ts +308 -0
  132. package/coding/src/security/sandbox.ts +179 -0
  133. package/coding/src/security/semaphore.ts +55 -0
  134. package/coding/src/targets.ts +110 -0
  135. package/coding/src/workspace/diff.ts +78 -0
  136. package/coding/src/workspace/errors.ts +29 -0
  137. package/coding/src/workspace/index.ts +5 -0
  138. package/coding/src/workspace/merge.ts +151 -0
  139. package/coding/src/workspace/routing.ts +76 -0
  140. package/coding/src/workspace/service.ts +584 -0
  141. package/computer-use/README.md +105 -0
  142. package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
  143. package/computer-use/src/classify.ts +53 -0
  144. package/computer-use/src/command.ts +29 -0
  145. package/computer-use/src/config.ts +69 -0
  146. package/computer-use/src/create.ts +170 -0
  147. package/computer-use/src/devices.ts +247 -0
  148. package/computer-use/src/env.ts +28 -0
  149. package/computer-use/src/index.ts +157 -0
  150. package/computer-use/src/skill.ts +21 -0
  151. package/openapi/README.md +97 -0
  152. package/openapi/src/compile.ts +386 -0
  153. package/openapi/src/config.ts +73 -0
  154. package/openapi/src/errors.ts +16 -0
  155. package/openapi/src/execute.ts +241 -0
  156. package/openapi/src/index.ts +124 -0
  157. package/openapi/src/limits.ts +63 -0
  158. package/openapi/src/specs.ts +86 -0
  159. package/openapi/src/tools.ts +60 -0
  160. package/openapi/src/types.ts +61 -0
  161. package/package.json +171 -4
  162. package/research/README.md +72 -0
  163. package/research/skills/research-report/SKILL.md +12 -0
  164. package/research/skills/research-scoping/SKILL.md +10 -0
  165. package/research/skills/research-synthesis/SKILL.md +10 -0
  166. package/research/skills/source-triage/SKILL.md +10 -0
  167. package/research/src/check.ts +71 -0
  168. package/research/src/config.ts +41 -0
  169. package/research/src/index.ts +147 -0
  170. package/research/src/ledger.ts +157 -0
  171. package/research/src/urls.ts +21 -0
  172. package/web/README.md +287 -0
  173. package/web/skills/web-research/SKILL.md +17 -0
  174. package/web/src/brave.ts +66 -0
  175. package/web/src/cache.ts +151 -0
  176. package/web/src/config.ts +210 -0
  177. package/web/src/controls.ts +128 -0
  178. package/web/src/domains.ts +114 -0
  179. package/web/src/exa.ts +60 -0
  180. package/web/src/firecrawl.ts +160 -0
  181. package/web/src/index.ts +387 -0
  182. package/web/src/kagi.ts +73 -0
  183. package/web/src/limits.ts +55 -0
  184. package/web/src/native/convert.ts +84 -0
  185. package/web/src/native/fetch.ts +226 -0
  186. package/web/src/normalize.ts +131 -0
  187. package/web/src/obscura/cdp.ts +201 -0
  188. package/web/src/obscura/classify.ts +35 -0
  189. package/web/src/obscura/cli.ts +254 -0
  190. package/web/src/obscura/errors.ts +8 -0
  191. package/web/src/obscura/index.ts +59 -0
  192. package/web/src/obscura/limits.ts +92 -0
  193. package/web/src/obscura/mcp.ts +104 -0
  194. package/web/src/obscura/playwright.ts +133 -0
  195. package/web/src/obscura/process.ts +235 -0
  196. package/web/src/obscura/search-profile.ts +73 -0
  197. package/web/src/obscura/types.ts +48 -0
  198. package/web/src/obscura/web.ts +419 -0
  199. package/web/src/prompt-fetch.ts +88 -0
  200. package/web/src/render.ts +75 -0
  201. package/web/src/searxng.ts +78 -0
  202. package/web/src/slice.ts +139 -0
  203. package/web/src/tavily.ts +77 -0
  204. package/web/src/tools.ts +182 -0
  205. package/web/src/transport.ts +310 -0
  206. package/web/src/types.ts +199 -0
  207. package/work/README.md +77 -0
  208. package/work/THIRD_PARTY_NOTICES +24 -0
  209. package/work/docling/ocr.py +150 -0
  210. package/work/sandbox/Dockerfile +36 -0
  211. package/work/sandbox/soffice.sh +12 -0
  212. package/work/src/command.ts +29 -0
  213. package/work/src/config.ts +138 -0
  214. package/work/src/connectors/cli.ts +343 -0
  215. package/work/src/connectors/drafts.ts +647 -0
  216. package/work/src/connectors/errors.ts +8 -0
  217. package/work/src/connectors/file-bytes.ts +149 -0
  218. package/work/src/connectors/google-workspace-http.ts +399 -0
  219. package/work/src/connectors/google-workspace.ts +465 -0
  220. package/work/src/connectors/gws-tools.ts +378 -0
  221. package/work/src/connectors/http.ts +123 -0
  222. package/work/src/connectors/idempotency.ts +235 -0
  223. package/work/src/connectors/identity.ts +34 -0
  224. package/work/src/connectors/index.ts +93 -0
  225. package/work/src/connectors/limits.ts +50 -0
  226. package/work/src/connectors/m365-tools.ts +308 -0
  227. package/work/src/connectors/microsoft365-http.ts +428 -0
  228. package/work/src/connectors/microsoft365.ts +402 -0
  229. package/work/src/connectors/normalize.ts +120 -0
  230. package/work/src/connectors/tool-helpers.ts +398 -0
  231. package/work/src/connectors/tools.ts +42 -0
  232. package/work/src/connectors/types.ts +399 -0
  233. package/work/src/diagrams/canonicalize.ts +166 -0
  234. package/work/src/diagrams/caps.ts +52 -0
  235. package/work/src/diagrams/embed.ts +340 -0
  236. package/work/src/diagrams/errors.ts +76 -0
  237. package/work/src/diagrams/index.ts +81 -0
  238. package/work/src/diagrams/messages.ts +150 -0
  239. package/work/src/diagrams/origin.ts +52 -0
  240. package/work/src/diagrams/telemetry.ts +34 -0
  241. package/work/src/diagrams/xml.ts +267 -0
  242. package/work/src/document-extraction/index.ts +563 -0
  243. package/work/src/document-reader/ambient.d.ts +2 -0
  244. package/work/src/document-reader/errors.ts +7 -0
  245. package/work/src/document-reader/index.ts +181 -0
  246. package/work/src/document-reader/mistral-ocr.ts +318 -0
  247. package/work/src/document-reader/parsers.ts +171 -0
  248. package/work/src/documents/caps.ts +135 -0
  249. package/work/src/documents/diff.ts +269 -0
  250. package/work/src/documents/errors.ts +44 -0
  251. package/work/src/documents/fidelity.ts +98 -0
  252. package/work/src/documents/generate.ts +96 -0
  253. package/work/src/documents/hash.ts +8 -0
  254. package/work/src/documents/index.ts +14 -0
  255. package/work/src/documents/model-schema.ts +639 -0
  256. package/work/src/documents/parse.ts +252 -0
  257. package/work/src/documents/patch-history.ts +81 -0
  258. package/work/src/documents/patch.ts +476 -0
  259. package/work/src/documents/preview-html.ts +245 -0
  260. package/work/src/documents/preview.ts +227 -0
  261. package/work/src/documents/telemetry.ts +36 -0
  262. package/work/src/documents/translate/docx.ts +292 -0
  263. package/work/src/documents/translate/pptx.ts +304 -0
  264. package/work/src/documents/translate/xlsx.ts +137 -0
  265. package/work/src/documents/types.ts +188 -0
  266. package/work/src/index.ts +311 -0
  267. package/work/src/runtime/worker-pool-entry.ts +102 -0
  268. package/work/src/runtime/worker-pool.ts +267 -0
  269. package/work/src/sandbox/composition.ts +212 -0
  270. package/work/src/sandbox/image.ts +6 -0
  271. package/work/src/sandbox/index.ts +14 -0
  272. package/work/src/sheets/caps.ts +68 -0
  273. package/work/src/sheets/csv.ts +351 -0
  274. package/work/src/sheets/decimal.ts +388 -0
  275. package/work/src/sheets/errors.ts +52 -0
  276. package/work/src/sheets/index.ts +62 -0
  277. package/work/src/sheets/inference.ts +378 -0
  278. package/work/src/sheets/telemetry.ts +34 -0
  279. package/work/src/sheets/types.ts +162 -0
  280. package/work/src/sheets/xlsx.ts +388 -0
  281. package/work/src/skills/index.ts +8 -0
  282. package/work/src/skills/load.ts +43 -0
  283. package/work/src/tools/filesystem.ts +21 -0
  284. package/work/src/tools/index.ts +1 -0
  285. package/work/src/tools/local-filesystem.ts +33 -0
  286. package/work/src/tools/office.ts +311 -0
  287. package/work/vendor/hermes-agent/LICENSE +21 -0
  288. package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
  289. package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
  290. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
  291. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
  292. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
  293. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
  294. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
  295. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
  296. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
  297. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
  298. package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
  299. package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
  300. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
  301. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
  302. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
  303. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
  304. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
  305. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
  306. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
  307. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
  308. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
  309. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
  310. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
  311. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
  312. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
  313. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
  314. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
  315. package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
  316. package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
  317. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
  318. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
  319. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
  320. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
  321. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
  322. package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
  323. package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
  324. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
  325. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
  326. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
  327. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
  328. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
  329. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
  330. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
  331. package/work/vendor-lock.json +11 -0
@@ -0,0 +1,88 @@
1
+ /** DNS pinning and rebinding defense: resolve once, connect to pinned answers, verify the socket. */
2
+
3
+ import { lookup } from "node:dns/promises";
4
+ import { EgressError } from "./types.js";
5
+
6
+ /** Normalize IPv6-mapped IPv4 (`::ffff:1.2.3.4` → `1.2.3.4`) for comparison. */
7
+ export function normalizeAddress(address: string): string {
8
+ const lower = address.toLowerCase();
9
+ if (lower.startsWith("::ffff:")) return lower.slice("::ffff:".length);
10
+ return lower;
11
+ }
12
+
13
+ function ipv4ToInt(address: string): number | undefined {
14
+ const parts = address.split(".");
15
+ if (parts.length !== 4) return undefined;
16
+ let value = 0;
17
+ for (const part of parts) {
18
+ const octet = Number(part);
19
+ if (!Number.isInteger(octet) || octet < 0 || octet > 255) return undefined;
20
+ value = value * 256 + octet;
21
+ }
22
+ return value;
23
+ }
24
+
25
+ /** Private, loopback, link-local, metadata, multicast, and reserved ranges. */
26
+ export function isPrivateAddress(address: string): boolean {
27
+ const normalized = normalizeAddress(address);
28
+ if (normalized.includes(":")) {
29
+ if (normalized === "::1" || normalized === "::") return true;
30
+ if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true; // fc00::/7 ULA
31
+ if (normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb"))
32
+ return true; // fe80::/10
33
+ if (normalized.startsWith("::ffff:")) return isPrivateAddress(normalized.slice("::ffff:".length));
34
+ if (normalized.startsWith("2001:db8:")) return true; // documentation
35
+ if (normalized.startsWith("ff")) return true; // multicast
36
+ return false;
37
+ }
38
+ const value = ipv4ToInt(normalized);
39
+ if (value === undefined) return true; // malformed → treat as non-routable
40
+ if (value === 0) return true; // 0.0.0.0/8
41
+ if (value >>> 24 === 10) return true; // 10/8
42
+ if (value >>> 24 === 127) return true; // 127/8 loopback
43
+ if (value >>> 16 === 0xa9fe) return true; // 169.254/16 link-local + metadata
44
+ if (value >>> 20 === 0xac1) return true; // 172.16/12
45
+ if (value >>> 16 === 0xc0a8) return true; // 192.168/16
46
+ if (value >>> 22 === 0x191) return true; // 100.64/10 CGNAT
47
+ if (value >>> 24 === 192 && (value & 0xff) === 0) return true; // 192.0.0.0/24
48
+ if (value >>> 24 === 192 && (value & 0xff) === 2) return true; // 192.0.2.0/24 TEST-NET
49
+ if (value >>> 16 === 0xc612) return true; // 198.18/15 benchmarking
50
+ if (value >>> 24 === 198 && (value & 0xff) === 51) return true; // 198.51.100/24
51
+ if (value >>> 24 === 203 && (value & 0xff) === 113) return true; // 203.0.113/24
52
+ if (value >>> 28 === 0xe) return true; // 224/4 multicast
53
+ if (value >>> 28 === 0xf) return true; // 240/4 reserved
54
+ return false;
55
+ }
56
+
57
+ /** 169.254.169.254 and IPv6 link-local metadata endpoints. */
58
+ export function isMetadataAddress(address: string): boolean {
59
+ const normalized = normalizeAddress(address);
60
+ if (normalized === "169.254.169.254") return true;
61
+ if (normalized.startsWith("fe80:")) return true;
62
+ return false;
63
+ }
64
+
65
+ export type AddressResolver = (host: string) => Promise<readonly string[]>;
66
+
67
+ /** Resolve a host to pinned addresses; IP literals resolve to themselves. */
68
+ export async function resolvePinned(host: string, resolve?: AddressResolver): Promise<readonly string[]> {
69
+ if (resolve) return resolve(host);
70
+ if (host.includes(":") || /^\d{1,3}(\.\d{1,3}){3}$/.test(host)) return [host];
71
+ try {
72
+ const records = await lookup(host, { all: true, verbatim: true });
73
+ return records.map((record) => record.address);
74
+ } catch {
75
+ throw new EgressError("ERR_PRISM_EGRESS_DNS", `DNS resolution failed for ${host}`);
76
+ }
77
+ }
78
+
79
+ /** Rebind defense: the connected socket must belong to the pinned answer set. */
80
+ export function assertPinned(host: string, remoteAddress: string | undefined, pinned: readonly string[]): void {
81
+ if (!remoteAddress) {
82
+ throw new EgressError("ERR_PRISM_EGRESS_DNS", `no remote address for ${host}`);
83
+ }
84
+ const normalized = normalizeAddress(remoteAddress);
85
+ if (!pinned.some((ip) => normalizeAddress(ip) === normalized)) {
86
+ throw new EgressError("ERR_PRISM_EGRESS_DNS", `connection address ${remoteAddress} is not a pinned answer for ${host}`);
87
+ }
88
+ }
@@ -0,0 +1,32 @@
1
+ /** Egress module entry point: policy, proxy, DNS pinning, limits, attestation. */
2
+
3
+ export type { AddressResolver } from "./dns-pin.js";
4
+ export { assertPinned, isMetadataAddress, isPrivateAddress, normalizeAddress, resolvePinned } from "./dns-pin.js";
5
+ export type { EgressLimitOptions, ResolvedEgressLimits } from "./limits.js";
6
+ export {
7
+ DEFAULT_MAX_EGRESS_CONCURRENT_CONNECTIONS,
8
+ DEFAULT_MAX_EGRESS_REDIRECT_HOPS,
9
+ DEFAULT_MAX_EGRESS_REQUEST_BYTES,
10
+ DEFAULT_MAX_EGRESS_RESPONSE_BYTES,
11
+ DEFAULT_MAX_EGRESS_RULES,
12
+ DEFAULT_MAX_EGRESS_TRANSFER_TIME_MS,
13
+ HARD_MAX_EGRESS_CONCURRENT_CONNECTIONS,
14
+ HARD_MAX_EGRESS_REDIRECT_HOPS,
15
+ HARD_MAX_EGRESS_REQUEST_BYTES,
16
+ HARD_MAX_EGRESS_RESPONSE_BYTES,
17
+ HARD_MAX_EGRESS_RULES,
18
+ HARD_MAX_EGRESS_TRANSFER_TIME_MS,
19
+ resolveEgressLimits,
20
+ } from "./limits.js";
21
+ export type { CreateEgressPolicyOptions, EgressPolicy, EgressPreset, EgressProtocol, EgressRule } from "./policy.js";
22
+ export { createEgressPolicy, EGRESS_PRESETS } from "./policy.js";
23
+ export type {
24
+ CreateAllowListEgressProxyOptions,
25
+ EgressAttestation,
26
+ EgressProxy,
27
+ EgressProxyEndpoint,
28
+ EgressProxyStats,
29
+ } from "./proxy.js";
30
+ export { createAllowListEgressProxy } from "./proxy.js";
31
+ export type { EgressAuditRecord, EgressErrorCode } from "./types.js";
32
+ export { EgressError } from "./types.js";
@@ -0,0 +1,57 @@
1
+ /** Frozen finite caps for the allow-list egress proxy. */
2
+
3
+ export const DEFAULT_MAX_EGRESS_CONCURRENT_CONNECTIONS = 32;
4
+ export const HARD_MAX_EGRESS_CONCURRENT_CONNECTIONS = 256;
5
+ export const DEFAULT_MAX_EGRESS_REQUEST_BYTES = 64 * 1024 ** 2;
6
+ export const HARD_MAX_EGRESS_REQUEST_BYTES = 1024 ** 3;
7
+ export const DEFAULT_MAX_EGRESS_RESPONSE_BYTES = 64 * 1024 ** 2;
8
+ export const HARD_MAX_EGRESS_RESPONSE_BYTES = 1024 ** 3;
9
+ export const DEFAULT_MAX_EGRESS_TRANSFER_TIME_MS = 600_000;
10
+ export const HARD_MAX_EGRESS_TRANSFER_TIME_MS = 3_600_000;
11
+ export const DEFAULT_MAX_EGRESS_RULES = 128;
12
+ export const HARD_MAX_EGRESS_RULES = 1_024;
13
+ export const DEFAULT_MAX_EGRESS_REDIRECT_HOPS = 5;
14
+ export const HARD_MAX_EGRESS_REDIRECT_HOPS = 10;
15
+
16
+ export interface EgressLimitOptions {
17
+ readonly concurrentConnections?: number;
18
+ readonly requestBytes?: number;
19
+ readonly responseBytes?: number;
20
+ readonly transferTimeMs?: number;
21
+ readonly rulesPerPolicy?: number;
22
+ readonly redirectHops?: number;
23
+ }
24
+
25
+ export interface ResolvedEgressLimits {
26
+ readonly concurrentConnections: number;
27
+ readonly requestBytes: number;
28
+ readonly responseBytes: number;
29
+ readonly transferTimeMs: number;
30
+ readonly rulesPerPolicy: number;
31
+ readonly redirectHops: number;
32
+ }
33
+
34
+ const SPECS: Record<keyof ResolvedEgressLimits, readonly [number, number]> = {
35
+ concurrentConnections: [DEFAULT_MAX_EGRESS_CONCURRENT_CONNECTIONS, HARD_MAX_EGRESS_CONCURRENT_CONNECTIONS],
36
+ requestBytes: [DEFAULT_MAX_EGRESS_REQUEST_BYTES, HARD_MAX_EGRESS_REQUEST_BYTES],
37
+ responseBytes: [DEFAULT_MAX_EGRESS_RESPONSE_BYTES, HARD_MAX_EGRESS_RESPONSE_BYTES],
38
+ transferTimeMs: [DEFAULT_MAX_EGRESS_TRANSFER_TIME_MS, HARD_MAX_EGRESS_TRANSFER_TIME_MS],
39
+ rulesPerPolicy: [DEFAULT_MAX_EGRESS_RULES, HARD_MAX_EGRESS_RULES],
40
+ redirectHops: [DEFAULT_MAX_EGRESS_REDIRECT_HOPS, HARD_MAX_EGRESS_REDIRECT_HOPS],
41
+ };
42
+
43
+ function validateEgressLimit(name: keyof ResolvedEgressLimits, value: number, hardCap: number): number {
44
+ if (!Number.isSafeInteger(value) || value < 1 || value > hardCap) {
45
+ throw new RangeError(`${name} must be a positive safe integer at most ${hardCap}`);
46
+ }
47
+ return value;
48
+ }
49
+
50
+ export function resolveEgressLimits(input: EgressLimitOptions = {}): ResolvedEgressLimits {
51
+ return Object.fromEntries(
52
+ Object.entries(SPECS).map(([name, [fallback, hardCap]]) => {
53
+ const value = input[name as keyof EgressLimitOptions] ?? fallback;
54
+ return [name, validateEgressLimit(name as keyof ResolvedEgressLimits, value as number, hardCap)];
55
+ }),
56
+ ) as unknown as ResolvedEgressLimits;
57
+ }
@@ -0,0 +1,137 @@
1
+ /** Allow-list egress policy: deny-all default, exact host/port/protocol rules, frozen presets. */
2
+
3
+ import { createHash } from "node:crypto";
4
+ import { DEFAULT_MAX_EGRESS_RULES, HARD_MAX_EGRESS_RULES } from "./limits.js";
5
+ import { EgressError } from "./types.js";
6
+
7
+ export type EgressProtocol = "http" | "https";
8
+
9
+ export interface EgressRule {
10
+ readonly host: string;
11
+ readonly port: number;
12
+ readonly protocol: EgressProtocol;
13
+ /** Allow private/link-local/metadata addresses for this rule (default false). */
14
+ readonly allowPrivate?: boolean;
15
+ }
16
+
17
+ export type EgressPreset = "npm-registry" | "github";
18
+
19
+ export interface EgressPolicy {
20
+ readonly rules: readonly EgressRule[];
21
+ readonly fingerprint: string;
22
+ allows(host: string, port: number, protocol: EgressProtocol): boolean;
23
+ /** Exact rule match (undefined when denied). */
24
+ match(host: string, port: number, protocol: EgressProtocol): EgressRule | undefined;
25
+ }
26
+
27
+ /** Frozen preset expansions — explicit rule lists, no wildcards. */
28
+ export const EGRESS_PRESETS: Record<EgressPreset, readonly EgressRule[]> = {
29
+ "npm-registry": [{ host: "registry.npmjs.org", port: 443, protocol: "https" }],
30
+ github: [
31
+ { host: "api.github.com", port: 443, protocol: "https" },
32
+ { host: "github.com", port: 443, protocol: "https" },
33
+ { host: "codeload.github.com", port: 443, protocol: "https" },
34
+ { host: "objects.githubusercontent.com", port: 443, protocol: "https" },
35
+ { host: "raw.githubusercontent.com", port: 443, protocol: "https" },
36
+ { host: "uploads.github.com", port: 443, protocol: "https" },
37
+ ],
38
+ };
39
+
40
+ const HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
41
+ const IPV4_RE = /^(\d{1,3}\.){3}\d{1,3}$/;
42
+ const IPV6_RE = /^[0-9a-f:]+$/;
43
+
44
+ function validateHost(host: string): string {
45
+ if (typeof host !== "string" || host.length === 0 || host.length > 253) {
46
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", "egress rule host must be a non-empty string at most 253 chars");
47
+ }
48
+ if (host.includes("*") || host.includes(" ") || host.includes("\t") || host.includes("/")) {
49
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `egress rule host must not contain wildcards or whitespace: ${host}`);
50
+ }
51
+ const lower = host.toLowerCase();
52
+ if (!HOSTNAME_RE.test(lower) && !IPV4_RE.test(lower) && !IPV6_RE.test(lower)) {
53
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `egress rule host is not a valid hostname or IP literal: ${host}`);
54
+ }
55
+ return lower;
56
+ }
57
+
58
+ function validatePort(port: number): number {
59
+ if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) {
60
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `egress rule port must be an integer in 1..65535: ${port}`);
61
+ }
62
+ return port;
63
+ }
64
+
65
+ function validateRule(rule: EgressRule): EgressRule {
66
+ if (!rule || typeof rule !== "object") {
67
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", "egress rule must be an object");
68
+ }
69
+ if (rule.protocol !== "http" && rule.protocol !== "https") {
70
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `egress rule protocol must be http or https: ${String(rule.protocol)}`);
71
+ }
72
+ if (rule.allowPrivate !== undefined && typeof rule.allowPrivate !== "boolean") {
73
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", "egress rule allowPrivate must be a boolean when present");
74
+ }
75
+ return {
76
+ host: validateHost(rule.host),
77
+ port: validatePort(rule.port),
78
+ protocol: rule.protocol,
79
+ ...(rule.allowPrivate ? { allowPrivate: true } : {}),
80
+ };
81
+ }
82
+
83
+ function fingerprint(rules: readonly EgressRule[]): string {
84
+ const canonical = rules
85
+ .map((r) => `${r.protocol}|${r.host}|${r.port}|${r.allowPrivate === true ? 1 : 0}`)
86
+ .sort()
87
+ .join("\n");
88
+ return createHash("sha256").update(canonical).digest("hex");
89
+ }
90
+
91
+ export interface CreateEgressPolicyOptions {
92
+ readonly allow?: readonly EgressRule[];
93
+ readonly presets?: readonly EgressPreset[];
94
+ /** Rule-count cap (default 128, hard 1024). */
95
+ readonly maxRules?: number;
96
+ }
97
+
98
+ export function createEgressPolicy(options: CreateEgressPolicyOptions = {}): EgressPolicy {
99
+ const maxRules = options.maxRules ?? DEFAULT_MAX_EGRESS_RULES;
100
+ if (!Number.isSafeInteger(maxRules) || maxRules < 1 || maxRules > HARD_MAX_EGRESS_RULES) {
101
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `maxRules must be a positive safe integer at most ${HARD_MAX_EGRESS_RULES}`);
102
+ }
103
+ const seen = new Set<string>();
104
+ const rules: EgressRule[] = [];
105
+ const push = (rule: EgressRule): void => {
106
+ const key = `${rule.protocol}|${rule.host}|${rule.port}|${rule.allowPrivate === true ? 1 : 0}`;
107
+ if (seen.has(key)) return;
108
+ seen.add(key);
109
+ rules.push(rule);
110
+ };
111
+ for (const preset of options.presets ?? []) {
112
+ if (preset !== "npm-registry" && preset !== "github") {
113
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `unknown egress preset: ${String(preset)}`);
114
+ }
115
+ for (const rule of EGRESS_PRESETS[preset]) push(rule);
116
+ }
117
+ for (const rule of options.allow ?? []) push(validateRule(rule));
118
+ if (rules.length > maxRules) {
119
+ throw new EgressError("ERR_PRISM_EGRESS_POLICY", `egress policy exceeds maxRules (${maxRules})`);
120
+ }
121
+ const frozen = Object.freeze(rules);
122
+ const policy: EgressPolicy = {
123
+ rules: frozen,
124
+ fingerprint: fingerprint(frozen),
125
+ allows(host, port, protocol) {
126
+ return policy.match(host, port, protocol) !== undefined;
127
+ },
128
+ match(host, port, protocol) {
129
+ const h = host.toLowerCase();
130
+ for (const rule of frozen) {
131
+ if (rule.host === h && rule.port === port && rule.protocol === protocol) return rule;
132
+ }
133
+ return undefined;
134
+ },
135
+ };
136
+ return policy;
137
+ }