@nangohq/runner-sdk 0.70.6 → 0.70.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,192 @@
1
+ import { getBaseUrlOverrideDenylistFromEnv, isBaseUrlOverrideDenied } from './baseUrlOverrideDenylist.js';
2
+ import { ActionError } from './errors.js';
3
+ const UNCONTROLLED_FETCH_MAX_REDIRECTS = 5;
4
+ const REDIRECT_STATUS_CODES = new Set([301, 302, 303, 307, 308]);
5
+ const ALLOWED_REDIRECT_PROTOCOLS = new Set(['http:', 'https:']);
6
+ const makeActionError = (code, message) => {
7
+ return new ActionError({ code, message });
8
+ };
9
+ export async function executeUncontrolledFetch(options, onBytes) {
10
+ const recordTransfer = (params) => {
11
+ if (params.bytesSent > 0 || params.bytesReceived > 0)
12
+ onBytes(params);
13
+ };
14
+ const baseUrlOverrideDenylist = getBaseUrlOverrideDenylistFromEnv();
15
+ const throwIfDenied = (absoluteUrl) => {
16
+ if (baseUrlOverrideDenylist.size > 0 && isBaseUrlOverrideDenied(absoluteUrl, baseUrlOverrideDenylist)) {
17
+ throw makeActionError('url_not_allowed', 'This URL is not allowed by server configuration.');
18
+ }
19
+ };
20
+ throwIfDenied(options.url.toString());
21
+ let currentUrl = new URL(options.url.href);
22
+ let method = options.method || 'GET';
23
+ let body = options.body ?? undefined;
24
+ const headerBag = new Headers(options.headers);
25
+ for (let redirectsFollowed = 0;; redirectsFollowed++) {
26
+ const props = {
27
+ headers: new Headers(headerBag),
28
+ method,
29
+ redirect: 'manual'
30
+ // TODO: use agent
31
+ };
32
+ if (body) {
33
+ props.body = body;
34
+ }
35
+ const response = await fetch(currentUrl, props);
36
+ const bytesSent = countRequestBytes(props.headers, body);
37
+ const bytesReceived = countHeaderBytes(response.headers);
38
+ if (!REDIRECT_STATUS_CODES.has(response.status)) {
39
+ if (response.body === null) {
40
+ recordTransfer({ bytesSent, bytesReceived });
41
+ return response;
42
+ }
43
+ const contentLength = parseContentLength(response.headers);
44
+ if (contentLength !== null) {
45
+ recordTransfer({ bytesSent: bytesSent, bytesReceived: bytesReceived + contentLength });
46
+ return response;
47
+ }
48
+ // CL not present: emit known bytes now; body bytes follow when stream settles.
49
+ recordTransfer({ bytesSent, bytesReceived });
50
+ return tapResponseStreamAndCount(response, ({ bytes }) => {
51
+ recordTransfer({ bytesSent: 0, bytesReceived: bytes });
52
+ });
53
+ }
54
+ recordTransfer({ bytesSent, bytesReceived });
55
+ const location = response.headers.get('Location');
56
+ if (!location) {
57
+ // Return as-is when there's a redirect status but no Location header.
58
+ return response;
59
+ }
60
+ // We're about to follow the redirect; we won't return this response, so cancel its body.
61
+ void response.body?.cancel();
62
+ if (redirectsFollowed >= UNCONTROLLED_FETCH_MAX_REDIRECTS) {
63
+ throw makeActionError('too_many_redirects', `Exceeded maximum of ${UNCONTROLLED_FETCH_MAX_REDIRECTS} redirects.`);
64
+ }
65
+ let nextUrl;
66
+ try {
67
+ nextUrl = new URL(location, currentUrl);
68
+ }
69
+ catch {
70
+ throw makeActionError('invalid_redirect', 'Redirect Location could not be parsed as a URL.');
71
+ }
72
+ // Native fetch rejects redirects to non-HTTP(S) schemes.
73
+ if (!ALLOWED_REDIRECT_PROTOCOLS.has(nextUrl.protocol)) {
74
+ throw makeActionError('invalid_redirect', 'Redirect Location must use http: or https:.');
75
+ }
76
+ throwIfDenied(nextUrl.toString());
77
+ // Native fetch strips sensitive headers when redirecting to a different origin.
78
+ // Because we follow redirects manually, we must replicate that to avoid credential leaks.
79
+ if (currentUrl.origin !== nextUrl.origin) {
80
+ headerBag.delete('authorization');
81
+ headerBag.delete('proxy-authorization');
82
+ headerBag.delete('cookie');
83
+ }
84
+ // Match common fetch redirect semantics:
85
+ // - 303: switch to GET only if method is neither GET nor HEAD (drop body)
86
+ // - 301/302: switch to GET only for POST (preserve PUT/PATCH/DELETE, etc.)
87
+ // - 307/308: preserve method and body
88
+ const upperMethod = method.toUpperCase();
89
+ const shouldSwitchToGet = (response.status === 303 && upperMethod !== 'GET' && upperMethod !== 'HEAD') ||
90
+ ((response.status === 301 || response.status === 302) && upperMethod === 'POST');
91
+ if (shouldSwitchToGet) {
92
+ method = 'GET';
93
+ body = undefined;
94
+ // When we rewrite to GET, drop request-body specific headers (native fetch does not forward them).
95
+ for (const h of [
96
+ 'content-length',
97
+ 'content-type',
98
+ 'content-encoding',
99
+ 'content-language',
100
+ 'content-location',
101
+ 'content-md5',
102
+ 'content-range',
103
+ 'transfer-encoding'
104
+ ]) {
105
+ headerBag.delete(h);
106
+ }
107
+ }
108
+ currentUrl = nextUrl;
109
+ }
110
+ }
111
+ export function parseContentLength(headers) {
112
+ const cl = headers.get('content-length');
113
+ if (cl === null)
114
+ return null;
115
+ const n = parseInt(cl, 10);
116
+ return Number.isFinite(n) && n >= 0 ? n : null;
117
+ }
118
+ export function countHeaderBytes(headers) {
119
+ let bytes = 0;
120
+ headers.forEach((value, name) => {
121
+ bytes += Buffer.byteLength(name, 'utf8') + Buffer.byteLength(value, 'utf8');
122
+ });
123
+ return bytes;
124
+ }
125
+ export function countRequestBytes(headers, body) {
126
+ let bytes = body !== undefined ? Buffer.byteLength(body, 'utf8') : 0;
127
+ bytes += countHeaderBytes(headers);
128
+ return bytes;
129
+ }
130
+ export function tapResponseStreamAndCount(response, onStreamedBytes) {
131
+ // defensive check: shouldn't happen as we validate before calling this function
132
+ if (response.body === null) {
133
+ return response;
134
+ }
135
+ let streamedBytes = 0;
136
+ const { readable, writable } = new TransformStream({
137
+ transform(chunk, controller) {
138
+ streamedBytes += chunk.byteLength;
139
+ controller.enqueue(chunk);
140
+ },
141
+ flush() {
142
+ onStreamedBytes({ bytes: streamedBytes });
143
+ }
144
+ });
145
+ void response.body.pipeTo(writable).catch(() => {
146
+ // Swallow errors: if the caller cancels the stream the flush may not fire,
147
+ // which is acceptable — we avoid forcing a download just for metering.
148
+ });
149
+ return proxyResponseBody(readable, response);
150
+ }
151
+ /**
152
+ * Returns a Proxy over `response` that routes property accesses in two ways:
153
+ * - Body-related properties (`body`, `bodyUsed`, `text`, `json`, `arrayBuffer`,
154
+ * `blob`, `formData`, `clone`) are routed to a body façade built on top of
155
+ * `readable`, so body consumption goes through the tapped stream.
156
+ * - Everything else (including `url`, `type`, and other runtime-populated
157
+ * read-only fields) is routed to the original response object, which
158
+ * constructing `new Response(…)` would silently drop.
159
+ *
160
+ * Note: `clone()` resolves through the body façade, so clones lose `url`/`type` —
161
+ * acceptable since `clone()` is used for body re-reading, not metadata access.
162
+ */
163
+ function proxyResponseBody(readable, response) {
164
+ const bodyFacade = new Response(readable, {
165
+ status: response.status,
166
+ statusText: response.statusText,
167
+ headers: response.headers
168
+ });
169
+ return new Proxy(response, {
170
+ get(target, prop) {
171
+ switch (prop) {
172
+ case 'body':
173
+ return readable;
174
+ case 'bodyUsed':
175
+ return bodyFacade.bodyUsed;
176
+ case 'text':
177
+ case 'json':
178
+ case 'arrayBuffer':
179
+ case 'blob':
180
+ case 'formData':
181
+ case 'bytes':
182
+ case 'clone':
183
+ return prop in bodyFacade ? bodyFacade[prop].bind(bodyFacade) : undefined;
184
+ default: {
185
+ const val = Reflect.get(target, prop, target);
186
+ return typeof val === 'function' ? val.bind(target) : val;
187
+ }
188
+ }
189
+ }
190
+ });
191
+ }
192
+ //# sourceMappingURL=uncontrolledFetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uncontrolledFetch.js","sourceRoot":"","sources":["../lib/uncontrolledFetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};