@manifest-network/manifest-agent-core 0.9.0 → 0.11.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 +1 -1
- package/dist/close-lease.d.ts +3 -2
- package/dist/close-lease.d.ts.map +1 -1
- package/dist/close-lease.js +4 -3
- package/dist/close-lease.js.map +1 -1
- package/dist/deploy-app.d.ts +3 -2
- package/dist/deploy-app.d.ts.map +1 -1
- package/dist/deploy-app.js +245 -77
- package/dist/deploy-app.js.map +1 -1
- package/dist/internals/build-fred-input.d.ts +38 -0
- package/dist/internals/build-fred-input.d.ts.map +1 -0
- package/dist/internals/build-fred-input.js +147 -0
- package/dist/internals/build-fred-input.js.map +1 -0
- package/dist/internals/evaluate-readiness-from-fred.d.ts +28 -0
- package/dist/internals/evaluate-readiness-from-fred.d.ts.map +1 -0
- package/dist/internals/evaluate-readiness-from-fred.js +94 -0
- package/dist/internals/evaluate-readiness-from-fred.js.map +1 -0
- package/dist/internals/format-success.js.map +1 -1
- package/dist/internals/guarded-fetch.d.ts +2 -138
- package/dist/internals/guarded-fetch.js +1 -241
- package/dist/internals/humanize-denom.js.map +1 -1
- package/dist/internals/inspect-image.js.map +1 -1
- package/dist/internals/lease-items.js +1 -4
- package/dist/internals/lease-items.js.map +1 -1
- package/dist/internals/render-deployment-plan.js.map +1 -1
- package/dist/internals/verify-recover.js.map +1 -1
- package/dist/manage-domain.d.ts +3 -2
- package/dist/manage-domain.d.ts.map +1 -1
- package/dist/manage-domain.js +4 -3
- package/dist/manage-domain.js.map +1 -1
- package/dist/troubleshoot.js.map +1 -1
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -5
- package/dist/internals/guarded-fetch.d.ts.map +0 -1
- package/dist/internals/guarded-fetch.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"guarded-fetch.d.ts","names":[],"sources":["../../src/internals/guarded-fetch.ts"],"mappings":";;AAyDA;;;;;AAUA;;;;;;;;;AA+BA;;;;;;;;;AA2CA;;;;;;;;;AAqEA;;;;;;;;;;;;;;;;;;;;;KAzJY,YAAA,UAAsB,KAAA;;;;;;;;;cAUrB,mBAAA,EAAqB,aAAA;EAAA,SACvB,KAAA;EAAA,SACA,GAAA;AAAA;;;;;;;;;cA6BE,mBAAA,EAAqB,aAAA;EAAA,SACvB,KAAA;EAAA,SACA,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCK,SAAA,CAAU,QAAA;EACxB,KAAA;EACA,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;iBAmEc,kBAAA,CAAA,GAAsB,YAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"guarded-fetch.js","names":[],"sources":["../../src/internals/guarded-fetch.ts"],"sourcesContent":["import ipaddr from 'ipaddr.js';\n\n/**\n * SSRF-guarded `fetch` factory. A Node-native undici Dispatcher that\n * DNS-resolves once at connect time and rejects any address whose\n * `ipaddr.js` range is not `'unicast'`.\n *\n * Why DIY rather than `request-filtering-agent`: the library only works with\n * `http`/`https`.Agent (legacy http API) and explicitly does NOT plug into\n * undici / native `fetch` per its v3.2.0 README. Re-routing the same\n * blocking semantics through undici's Dispatcher hook lets agent-core's\n * `inspectImage` (and future consumers) use native `fetch` while preserving\n * the same SSRF posture.\n *\n * Design (architect-blessed):\n * - **`ipaddr.js`'s `range()` is the source of truth.** Same approach as\n * `request-filtering-agent`: block any IP whose range is not `'unicast'`.\n * This covers loopback / private / link-local / multicast / broadcast /\n * reserved / carrier-grade-NAT / unspecified / ipv4Mapped / etc. via the\n * library's well-maintained RFC-classification table.\n * - **IPv4-mapped IPv6 normalization** (security-critical). An attacker\n * writing `::ffff:127.0.0.1` would otherwise sit in `ipaddr.js`'s\n * `'ipv4Mapped'` IPv6 range — coincidentally blocked, but for the\n * structural reason (\"v4-mapped form\") rather than the security reason\n * (\"loopback target\"). We normalize first so the block is justified.\n * Without this step, a v4-mapped form of a PUBLIC IPv4 (`::ffff:8.8.8.8`)\n * would also be blocked (wrong outcome — public IP is fine via\n * v4-mapped). Normalization gets both cases right.\n * - **DNS-resolve INSIDE the connect hook** to close the TOCTOU window\n * between resolve and TCP connect. The resolved IP gets substituted as\n * the connect hostname so the kernel doesn't re-resolve.\n * - **Module-level singleton Dispatcher**, lazy-instantiated on first\n * `createGuardedFetch()` invocation. Mirrors the CJS singleton-agent\n * pattern; avoids the aggressive `setGlobalDispatcher()` side-effect.\n * - **Construction-time runtime check** (`typeof process === 'undefined'`)\n * throws a clear error on browser/Deno so the failure is actionable, not\n * a confusing mid-fetch module-resolution error.\n * - **Redirect safety:** undici re-fires the connect hook on every cross-\n * host redirect; same-host redirects reuse the checked socket. The fetch\n * closure does NOT need `redirect: 'manual'` — default `follow` is safe\n * by construction.\n *\n * Blocked-range exports (`BLOCKED_RANGES_IPV4`, `BLOCKED_RANGES_IPV6`) are\n * provided for audit + test purposes: they enumerate the `ipaddr.js`\n * `range()` classifications we treat as non-`unicast` with their RFC\n * citations, so a reviewer can grep the code without consulting the\n * `ipaddr.js` source.\n *\n * Cross-platform note: agent-core's `tsdown.config.ts` targets\n * `platform: 'neutral'`. `ipaddr.js` is isomorphic (pure JS, no node:*\n * imports), so the static import is fine. `undici` and `node:dns/promises`\n * + `node:net` are Node-only — dynamic-imported INSIDE the lazy singleton\n * creation so the package stays importable from browsers / Deno (calling\n * `createGuardedFetch()` from those throws the construction-time error\n * with actionable guidance).\n */\n\nexport type GuardedFetch = typeof fetch;\n\n/**\n * `ipaddr.js`-classified IPv4 range labels we block (i.e., everything\n * except `'unicast'`). Exposed as a module-level constant so the audit\n * trail is greppable and a future range-list update is a focused edit.\n *\n * RFC citations included for each label for audit visibility — `ipaddr.js`\n * owns the actual CIDR tables that map IPs to these labels.\n */\nexport const BLOCKED_RANGES_IPV4: ReadonlyArray<{\n readonly range: string;\n readonly rfc: string;\n}> = [\n {\n range: 'unspecified',\n rfc: 'RFC 1122 §3.2.1.3 — 0.0.0.0/8 (this network / meta)',\n },\n { range: 'private', rfc: 'RFC 1918 — 10/8, 172.16/12, 192.168/16 (private)' },\n { range: 'loopback', rfc: 'RFC 5735 — 127/8 (loopback)' },\n {\n range: 'linkLocal',\n rfc: 'RFC 3927 — 169.254/16 (link-local, incl. AWS/GCP/Azure metadata at 169.254.169.254)',\n },\n { range: 'carrierGradeNat', rfc: 'RFC 6598 — 100.64/10 (carrier-grade NAT)' },\n { range: 'broadcast', rfc: 'RFC 919 — 255.255.255.255 (limited broadcast)' },\n { range: 'multicast', rfc: 'RFC 5771 — 224/4 (multicast)' },\n {\n range: 'reserved',\n rfc: 'RFC 1112 / 6890 — 240/4, 192.0.0/24, 198.18/15 etc. (reserved)',\n },\n];\n\n/**\n * `ipaddr.js`-classified IPv6 range labels we block. Note: `'ipv4Mapped'`\n * is NOT included here because we normalize IPv4-mapped IPv6 addresses to\n * their underlying IPv4 form BEFORE the range check — otherwise a v4-\n * mapped form of a public IP (`::ffff:8.8.8.8`) would be wrongly blocked,\n * and a v4-mapped form of a private IP (`::ffff:127.0.0.1`) would be\n * blocked only structurally (not for the security reason).\n */\nexport const BLOCKED_RANGES_IPV6: ReadonlyArray<{\n readonly range: string;\n readonly rfc: string;\n}> = [\n { range: 'unspecified', rfc: 'RFC 4291 — :: (unspecified)' },\n { range: 'loopback', rfc: 'RFC 4291 — ::1/128 (loopback)' },\n { range: 'linkLocal', rfc: 'RFC 4291 — fe80::/10 (link-local)' },\n { range: 'uniqueLocal', rfc: 'RFC 4193 — fc00::/7 (unique local / private)' },\n { range: 'multicast', rfc: 'RFC 4291 — ff00::/8 (multicast)' },\n { range: 'reserved', rfc: 'RFC 4291 / 5156 — various reserved blocks' },\n];\n\n/**\n * SSRF block-check for a single IP string. **Allow-list policy:** only\n * ipaddr.js's `'unicast'` classification is permitted; every other range\n * label is blocked.\n *\n * The prior deny-list implementation iterated BLOCKED_RANGES_* and let\n * anything not explicitly enumerated fall through as \"allowed\" — a\n * security-critical bias error. IPv6 categories like `6to4` (which can\n * wrap loopback or RFC 1918 IPs as `2002:7f00::/24` etc.), `teredo`,\n * `rfc6052` (NAT64), and `discard` were ALL un-named and therefore\n * allowed-by-omission. Under the allow-list policy, these all\n * default-deny along with any future ipaddr.js classification we\n * haven't audited.\n *\n * Returned `{range, rfc}` descriptor sources:\n * - **Named in BLOCKED_RANGES_IPV4 / BLOCKED_RANGES_IPV6** → returns\n * that entry verbatim (carries the audited RFC citation).\n * - **Unknown non-unicast label** → synthesizes\n * `{range: <label>, rfc: 'ipaddr.js classification (default-deny non-unicast)'}`.\n * The audit string is generic but the block decision is correct;\n * a future PR can promote frequently-seen labels into\n * BLOCKED_RANGES_* with proper RFC citations.\n *\n * IPv4-mapped IPv6 addresses (`::ffff:1.2.3.4`) are normalized to their\n * IPv4 form before the range check so the security verdict tracks the\n * underlying IP, not the structural wrapping.\n *\n * Throws `Error` on unparseable input — callers should catch and treat\n * \"unparseable\" as \"block\" (defense-in-depth — better to refuse than to\n * pass through to network on garbage input).\n */\nexport function isBlocked(ipString: string): {\n range: string;\n rfc: string;\n} | null {\n let parsed: ipaddr.IPv4 | ipaddr.IPv6 = ipaddr.parse(ipString);\n\n // IPv4-mapped IPv6 normalization — security-critical. See doc-comment.\n if (parsed.kind() === 'ipv6') {\n const v6 = parsed as ipaddr.IPv6;\n if (v6.isIPv4MappedAddress()) {\n parsed = v6.toIPv4Address();\n }\n }\n\n const rangeLabel = parsed.range();\n\n // Allow-list gate: `'unicast'` is the only category permitted.\n // Everything else defaults to block; the lookup below is for audit info.\n if (rangeLabel === 'unicast') {\n return null;\n }\n\n const list =\n parsed.kind() === 'ipv4' ? BLOCKED_RANGES_IPV4 : BLOCKED_RANGES_IPV6;\n // Both lists are short; linear scan is fine.\n const named = list.find((r) => r.range === rangeLabel);\n if (named) return named;\n\n // Default-deny fallback for unknown non-unicast labels (6to4, teredo,\n // rfc6052, discard, future categories ipaddr.js may add). The block\n // decision is correct; the audit string is generic.\n return {\n range: rangeLabel,\n rfc: 'ipaddr.js classification (default-deny non-unicast)',\n };\n}\n\ninterface DispatcherCache {\n dispatcher: unknown;\n fetch: typeof fetch;\n}\n\n/**\n * Cache the in-flight Promise (not the resolved value) so concurrent first-\n * call racers share the same construction and don't double-build the\n * undici Agent. Resolves to the singleton DispatcherCache. After\n * resolution, subsequent calls await the already-settled Promise (cheap).\n */\nlet cachedP: Promise<DispatcherCache> | undefined;\n\n/**\n * Build the SSRF-guarded fetch closure. Construction-time runtime check\n * gates Node-only — browser / Deno consumers either pass their own\n * `opts.fetch` to consumers like `inspectImage` or accept this error.\n *\n * The returned function matches `typeof fetch` and lazy-instantiates the\n * undici Dispatcher on first invocation. Subsequent calls share the\n * cached singleton.\n *\n * **Important: uses undici's own `fetch`**, not Node's built-in. Node's\n * built-in fetch is backed by its bundled undici, which is pinned to\n * Node's release-cycle version (Node 22 → undici 6.x). The npm-installed\n * `undici` package may be newer, and the Dispatcher protocol between\n * versions isn't guaranteed compatible (we observed \"invalid\n * onRequestStart method\" when mixing Node 22's fetch with undici@8 Agent).\n * Routing through undici's own fetch (same package version as the Agent)\n * sidesteps the mismatch. The function signature stays identical to\n * Node's `fetch` so consumers can't tell the difference.\n */\nexport function createGuardedFetch(): GuardedFetch {\n if (typeof process === 'undefined' || !process.versions?.node) {\n throw new Error(\n 'createGuardedFetch requires a Node.js runtime. On browser/Deno consumers, pass `opts.fetch` directly with your own SSRF-guarded implementation. See agent-core README.',\n );\n }\n\n return async (input, init) => {\n // Cache the Promise rather than the resolved value to dedup concurrent\n // first-call construction. Two callers racing through the lazy path\n // would otherwise both call `buildSsrfDispatcher()` and end up with\n // two undici Agents (no correctness bug, just double resource).\n //\n // Catch-and-reset on rejection: if `buildSsrfDispatcher()` ever fails\n // (e.g., dynamic `import('undici')` throws on a runtime that masquerades\n // as Node but lacks the module), the rejected Promise must NOT stay\n // cached — otherwise every subsequent `createGuardedFetch()` call would\n // re-throw the same error permanently. Clearing `cachedP` in the catch\n // arm lets a future caller retry construction.\n if (!cachedP) {\n cachedP = buildSsrfDispatcher().catch((err: unknown) => {\n cachedP = undefined;\n throw err;\n });\n }\n const c = await cachedP;\n // undici's fetch accepts a `dispatcher` option natively. Cast the init\n // to undici's expected shape — undici's fetch signature is structurally\n // compatible with global fetch but TS can't see through the dispatcher\n // field without a cast.\n const initWithDispatcher = {\n ...(init ?? {}),\n dispatcher: c.dispatcher,\n } as RequestInit;\n return c.fetch(input, initWithDispatcher);\n };\n}\n\n/**\n * Lazy dynamic-import of Node-only modules so the package stays importable\n * from non-Node consumers. The runtime check in `createGuardedFetch`\n * already gates Node-only — this function is only reached on Node.\n *\n * Returns BOTH the dispatcher and undici's own `fetch` so the\n * `createGuardedFetch` closure can route through undici directly (avoiding\n * Node-bundled-undici vs npm-undici Dispatcher protocol mismatches).\n */\nasync function buildSsrfDispatcher(): Promise<DispatcherCache> {\n const [undici, dnsModule, netModule] = await Promise.all([\n import('undici'),\n import('node:dns/promises'),\n import('node:net'),\n ]);\n\n const baseConnect = undici.buildConnector({});\n\n const dispatcher = new undici.Agent({\n connect: (options, callback) => {\n // Defensive: undici's Connect signature carries hostname + port + others.\n // We snapshot the original hostname for error messages; the resolved\n // IP gets substituted into `options` before the underlying TCP connect\n // so the kernel doesn't re-resolve (DNS-rebinding mitigation).\n const hostname = (options as { hostname?: string }).hostname ?? '';\n\n resolveAndCheck(hostname, netModule, dnsModule)\n .then((resolved) => {\n if (resolved.blocked) {\n callback(\n new Error(\n `SSRF blocked: ${hostname} resolves to ${resolved.ip} which is in blocked range '${resolved.blocked.range}' (${resolved.blocked.rfc})`,\n ),\n null,\n );\n return;\n }\n // Substitute the resolved IP so the kernel doesn't re-resolve.\n baseConnect(\n { ...options, hostname: resolved.ip } as Parameters<\n typeof baseConnect\n >[0],\n callback,\n );\n })\n .catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n // Fail closed — unparseable / unresolvable hostnames get rejected\n // rather than passed through. This keeps the SSRF posture intact\n // against DNS errors that might otherwise leak through.\n callback(\n new Error(\n `SSRF blocked: refused to connect to ${hostname}: ${msg}`,\n ),\n null,\n );\n });\n },\n });\n\n // undici's fetch has a structurally compatible signature with global fetch.\n // Cast at the boundary; downstream consumers see `typeof fetch`.\n return {\n dispatcher,\n fetch: undici.fetch as unknown as typeof fetch,\n };\n}\n\n/**\n * Resolve the connection target's IP and check against the blocked-range\n * sets. Handles three input cases:\n * 1. Hostname is already an IP literal → check directly (no DNS lookup).\n * 2. Hostname is an FQDN → resolve via `dns.lookup` (returns first\n * address; matches Node's default connection behavior).\n * 3. Resolution failure → throws, which the caller's `.catch` translates\n * into a fail-closed SSRF-block error.\n *\n * **DELIBERATE DIVERGENCE FROM SPEC** — architect's spec said\n * `dns.resolve4` / `dns.resolve6`; this implementation uses `dns.lookup`.\n * Rationale: `dns.lookup` matches the kernel's actual connection-time\n * resolution path (hosts file + nsswitch.conf + DNS in order), so the IP\n * we check IS the IP the kernel would connect to. Using `resolve4/6`\n * would consult DNS only and miss the hosts-file path — if an attacker\n * could write to `/etc/hosts` (root only) the check would be incomplete\n * because the kernel's actual connect would use a different address than\n * the one we checked. Per threat model: hosts-file writes require root,\n * so an attacker capable of writing there already owns the machine; this\n * is \"fixing the right problem\" — the check should track what the kernel\n * does, not its own model of resolution.\n *\n * Documented in PR 2 description for reviewer awareness. If the threat\n * model expands to include shared-host scenarios where attacker-controlled\n * hosts entries are realistic, switch to `resolve4/6` and accept the\n * connect-time-mismatch risk.\n */\nasync function resolveAndCheck(\n hostname: string,\n netModule: typeof import('node:net'),\n dnsModule: typeof import('node:dns/promises'),\n): Promise<{\n ip: string;\n blocked: { range: string; rfc: string } | null;\n}> {\n let ip: string;\n if (netModule.isIP(hostname) !== 0) {\n ip = hostname;\n } else {\n // `dns.lookup` calls `getaddrinfo` and follows the kernel's resolution\n // order (hosts → nsswitch → DNS, OS-defined), so the IP we check IS\n // the IP the kernel uses at connect(2) time. `dns.resolve4`/`resolve6`\n // would query system DNS only and miss `/etc/hosts` entries — a\n // `/etc/hosts evil.example.com 127.0.0.1` line would slip past the\n // SSRF check (DNS returns a clean IP) while the kernel connects to\n // loopback. Architect-endorsed correction to the original spec.\n //\n // `verbatim: true` preserves OS-defined IPv4/IPv6 ordering to avoid\n // reorder-induced check-vs-connect drift in mixed-family DNS responses.\n const result = await dnsModule.lookup(hostname, { verbatim: true });\n ip = result.address;\n }\n // Defense-in-depth: treat parse failures as blocks. ipaddr.parse throws\n // for malformed input — catch in the caller via the .catch wiring.\n const blocked = isBlocked(ip);\n return { ip, blocked };\n}\n"],"mappings":"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"}
|