@peac/protocol 0.10.7 → 0.10.8

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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * SSRF-safe fetch utility for PEAC verifiers
3
+ *
4
+ * Implements SSRF protection per VERIFIER-SECURITY-MODEL.md:
5
+ * - HTTPS only
6
+ * - Block private IP ranges (RFC 1918)
7
+ * - Block link-local addresses
8
+ * - Block loopback addresses
9
+ * - Redirect limits and scheme downgrade protection
10
+ *
11
+ * ## Security Model: Best-Effort Protection
12
+ *
13
+ * **IMPORTANT**: SSRF protection is BEST-EFFORT, not a guarantee. The level of
14
+ * protection depends on the runtime environment's capabilities. In environments
15
+ * without DNS pre-resolution (browsers, edge workers), protection is limited to
16
+ * URL scheme validation and response limits. Defense-in-depth: combine with
17
+ * network-level controls (firewalls, egress filtering) in production.
18
+ *
19
+ * ## Hard Invariants (ALWAYS Enforced)
20
+ *
21
+ * These protections are enforced in ALL runtimes:
22
+ *
23
+ * | Invariant | Enforcement |
24
+ * |-----------|-------------|
25
+ * | HTTPS only | URL scheme validation before fetch |
26
+ * | No redirects (pointer fetch) | `redirect: 'manual'` + policy check |
27
+ * | Response size cap | Streaming with byte counter, abort on limit |
28
+ * | Timeout | AbortController with configurable timeout |
29
+ * | No scheme downgrade | Redirect target scheme validation |
30
+ *
31
+ * ## Runtime-Dependent Protections
32
+ *
33
+ * These protections require DNS pre-resolution capability:
34
+ *
35
+ * | Protection | Requires |
36
+ * |------------|----------|
37
+ * | Private IP blocking (RFC 1918) | DNS pre-resolution |
38
+ * | Loopback blocking (127.0.0.0/8) | DNS pre-resolution |
39
+ * | Link-local blocking (169.254.0.0/16) | DNS pre-resolution |
40
+ *
41
+ * ## Runtime Capability Model
42
+ *
43
+ * SSRF protection capabilities vary by runtime environment:
44
+ *
45
+ * | Runtime | DNS Pre-Resolution | IP Blocking | Notes |
46
+ * |-------------------|-------------------|-------------|-------|
47
+ * | Node.js | YES | YES | Full protection via dns module |
48
+ * | Browser | NO | NO | Relies on server-side validation |
49
+ * | Cloudflare Workers| NO | NO | No DNS access, relies on CF network |
50
+ * | Deno | NO | NO | dns module not available by default |
51
+ * | Bun | YES | YES | Compatible with Node.js dns module |
52
+ *
53
+ * Use `getSSRFCapabilities()` to detect runtime capabilities.
54
+ *
55
+ * @packageDocumentation
56
+ */
57
+ /**
58
+ * Runtime environment where SSRF protection is running
59
+ */
60
+ export type SSRFRuntime = 'node' | 'bun' | 'deno' | 'browser' | 'cloudflare-workers' | 'edge-generic' | 'unknown';
61
+ /**
62
+ * SSRF protection capabilities available in the current runtime
63
+ *
64
+ * These capabilities determine what security measures can be applied:
65
+ * - `dnsPreResolution`: Can resolve hostnames to IPs before connecting
66
+ * - `ipBlocking`: Can inspect and block connections based on resolved IPs
67
+ * - `networkIsolation`: Runtime provides network-level isolation (e.g., CF Workers)
68
+ *
69
+ * When `dnsPreResolution` is false, SSRF protection is limited to:
70
+ * - URL scheme validation (HTTPS only)
71
+ * - Hostname pattern matching (if configured)
72
+ * - Response size limits
73
+ * - Timeout enforcement
74
+ *
75
+ * This is a defense-in-depth model: even without DNS pre-resolution,
76
+ * multiple layers of protection remain active.
77
+ */
78
+ export interface SSRFCapabilities {
79
+ /** Detected runtime environment */
80
+ runtime: SSRFRuntime;
81
+ /** Can resolve DNS before making HTTP connection */
82
+ dnsPreResolution: boolean;
83
+ /** Can block connections based on resolved IP addresses */
84
+ ipBlocking: boolean;
85
+ /** Runtime provides network-level isolation */
86
+ networkIsolation: boolean;
87
+ /** Human-readable description of protection level */
88
+ protectionLevel: 'full' | 'partial' | 'minimal';
89
+ /** Advisory notes for operators */
90
+ notes: string[];
91
+ }
92
+ /**
93
+ * Detect SSRF protection capabilities for the current runtime
94
+ *
95
+ * This function performs runtime detection and returns a capability object
96
+ * that describes what SSRF protections are available.
97
+ *
98
+ * @returns SSRF capabilities for the current runtime
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const caps = getSSRFCapabilities();
103
+ * if (!caps.dnsPreResolution) {
104
+ * console.warn('Running without DNS pre-resolution; SSRF protection is limited');
105
+ * }
106
+ * ```
107
+ */
108
+ export declare function getSSRFCapabilities(): SSRFCapabilities;
109
+ /**
110
+ * Reset cached capabilities (for testing)
111
+ * @internal
112
+ */
113
+ export declare function resetSSRFCapabilitiesCache(): void;
114
+ /**
115
+ * SSRF fetch options
116
+ */
117
+ export interface SSRFFetchOptions {
118
+ /** Timeout in milliseconds (default: VERIFIER_LIMITS.fetchTimeoutMs) */
119
+ timeoutMs?: number;
120
+ /** Maximum response size in bytes (default: VERIFIER_LIMITS.maxResponseBytes) */
121
+ maxBytes?: number;
122
+ /** Maximum redirects to follow (default: 0 for SSRF safety) */
123
+ maxRedirects?: number;
124
+ /** Allow redirects (default: VERIFIER_NETWORK.allowRedirects) */
125
+ allowRedirects?: boolean;
126
+ /**
127
+ * Allow cross-origin redirects (default: true for CDN compatibility).
128
+ * When true, redirects to different origins are allowed if the target passes SSRF checks.
129
+ * When false, redirects must stay within the same origin.
130
+ */
131
+ allowCrossOriginRedirects?: boolean;
132
+ /**
133
+ * How to handle DNS resolution failures (default: 'block' for fail-closed security).
134
+ * - 'block': Treat DNS failure as blocked (fail-closed, recommended)
135
+ * - 'fail': Return network_error and allow caller to decide
136
+ */
137
+ dnsFailureBehavior?: 'block' | 'fail';
138
+ /** Custom headers to include */
139
+ headers?: Record<string, string>;
140
+ }
141
+ /**
142
+ * SSRF fetch result
143
+ */
144
+ export interface SSRFFetchResult {
145
+ /** Whether the fetch succeeded */
146
+ ok: true;
147
+ /** Response status code */
148
+ status: number;
149
+ /** Response body as string */
150
+ body: string;
151
+ /**
152
+ * Raw response bytes for digest computation.
153
+ * Use this for computing digests to avoid encoding round-trip issues.
154
+ */
155
+ rawBytes: Uint8Array;
156
+ /** Response content type */
157
+ contentType?: string;
158
+ }
159
+ /**
160
+ * SSRF fetch error
161
+ */
162
+ export interface SSRFFetchError {
163
+ /** Fetch failed */
164
+ ok: false;
165
+ /** Error reason code */
166
+ reason: 'invalid_url' | 'not_https' | 'private_ip' | 'loopback' | 'link_local' | 'dns_failure' | 'too_many_redirects' | 'scheme_downgrade' | 'cross_origin_redirect' | 'timeout' | 'response_too_large' | 'jwks_too_many_keys' | 'network_error';
167
+ /** Human-readable error message */
168
+ message: string;
169
+ /** Blocked URL (if applicable) */
170
+ blockedUrl?: string;
171
+ }
172
+ /**
173
+ * Check if an IP address is private/blocked
174
+ */
175
+ export declare function isBlockedIP(ip: string): {
176
+ blocked: true;
177
+ reason: 'private_ip' | 'loopback' | 'link_local';
178
+ } | {
179
+ blocked: false;
180
+ };
181
+ /**
182
+ * Perform an SSRF-safe fetch
183
+ *
184
+ * This function implements the SSRF protection algorithm from VERIFIER-SECURITY-MODEL.md:
185
+ * 1. Parse URL; reject if not https://
186
+ * 2. Resolve hostname to IP(s)
187
+ * 3. For each IP: reject if private, link-local, or loopback
188
+ * 4. Perform fetch with timeout
189
+ * 5. On redirect: increment counter, reject if > max, apply checks to redirect URL
190
+ * 6. Validate response size
191
+ *
192
+ * @param url - URL to fetch (must be https://)
193
+ * @param options - Fetch options
194
+ * @returns Fetch result or error
195
+ */
196
+ export declare function ssrfSafeFetch(url: string, options?: SSRFFetchOptions): Promise<SSRFFetchResult | SSRFFetchError>;
197
+ /**
198
+ * Convenience function to fetch JWKS with SSRF protection
199
+ */
200
+ export declare function fetchJWKSSafe(jwksUrl: string, options?: Omit<SSRFFetchOptions, 'maxBytes'>): Promise<SSRFFetchResult | SSRFFetchError>;
201
+ /**
202
+ * Convenience function to fetch pointer target with SSRF protection
203
+ */
204
+ export declare function fetchPointerSafe(pointerUrl: string, options?: Omit<SSRFFetchOptions, 'maxBytes'>): Promise<SSRFFetchResult | SSRFFetchError>;
205
+ //# sourceMappingURL=ssrf-safe-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf-safe-fetch.d.ts","sourceRoot":"","sources":["../src/ssrf-safe-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAQH;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,KAAK,GACL,MAAM,GACN,SAAS,GACT,oBAAoB,GACpB,cAAc,GACd,SAAS,CAAC;AAEd;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,OAAO,EAAE,WAAW,CAAC;IACrB,oDAAoD;IACpD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,2DAA2D;IAC3D,UAAU,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qDAAqD;IACrD,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAChD,mCAAmC;IACnC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAOtD;AA4GD;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACtC,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,EAAE,EAAE,IAAI,CAAC;IACT,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,EAAE,UAAU,CAAC;IACrB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mBAAmB;IACnB,EAAE,EAAE,KAAK,CAAC;IACV,wBAAwB;IACxB,MAAM,EACF,aAAa,GACb,WAAW,GACX,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,aAAa,GACb,oBAAoB,GACpB,kBAAkB,GAClB,uBAAuB,GACvB,SAAS,GACT,oBAAoB,GACpB,oBAAoB,GACpB,eAAe,CAAC;IACpB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAqED;;GAEG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,GACT;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,YAAY,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,CAuC1F;AAwFD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,GAAG,cAAc,CAAC,CA+R3C;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAC3C,OAAO,CAAC,eAAe,GAAG,cAAc,CAAC,CAS3C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAC3C,OAAO,CAAC,eAAe,GAAG,cAAc,CAAC,CAS3C"}