@ai-sdk/provider-utils 4.0.18 → 4.0.20
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/CHANGELOG.md +14 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +103 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +102 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/download-blob.ts +7 -0
- package/src/index.ts +1 -0
- package/src/validate-download-url.ts +143 -0
package/dist/index.mjs
CHANGED
|
@@ -288,13 +288,113 @@ async function readResponseWithSizeLimit({
|
|
|
288
288
|
return result;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
// src/validate-download-url.ts
|
|
292
|
+
function validateDownloadUrl(url) {
|
|
293
|
+
let parsed;
|
|
294
|
+
try {
|
|
295
|
+
parsed = new URL(url);
|
|
296
|
+
} catch (e) {
|
|
297
|
+
throw new DownloadError({
|
|
298
|
+
url,
|
|
299
|
+
message: `Invalid URL: ${url}`
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
303
|
+
throw new DownloadError({
|
|
304
|
+
url,
|
|
305
|
+
message: `URL scheme must be http or https, got ${parsed.protocol}`
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
const hostname = parsed.hostname;
|
|
309
|
+
if (!hostname) {
|
|
310
|
+
throw new DownloadError({
|
|
311
|
+
url,
|
|
312
|
+
message: `URL must have a hostname`
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost")) {
|
|
316
|
+
throw new DownloadError({
|
|
317
|
+
url,
|
|
318
|
+
message: `URL with hostname ${hostname} is not allowed`
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) {
|
|
322
|
+
const ipv6 = hostname.slice(1, -1);
|
|
323
|
+
if (isPrivateIPv6(ipv6)) {
|
|
324
|
+
throw new DownloadError({
|
|
325
|
+
url,
|
|
326
|
+
message: `URL with IPv6 address ${hostname} is not allowed`
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (isIPv4(hostname)) {
|
|
332
|
+
if (isPrivateIPv4(hostname)) {
|
|
333
|
+
throw new DownloadError({
|
|
334
|
+
url,
|
|
335
|
+
message: `URL with IP address ${hostname} is not allowed`
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function isIPv4(hostname) {
|
|
342
|
+
const parts = hostname.split(".");
|
|
343
|
+
if (parts.length !== 4) return false;
|
|
344
|
+
return parts.every((part) => {
|
|
345
|
+
const num = Number(part);
|
|
346
|
+
return Number.isInteger(num) && num >= 0 && num <= 255 && String(num) === part;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
function isPrivateIPv4(ip) {
|
|
350
|
+
const parts = ip.split(".").map(Number);
|
|
351
|
+
const [a, b] = parts;
|
|
352
|
+
if (a === 0) return true;
|
|
353
|
+
if (a === 10) return true;
|
|
354
|
+
if (a === 127) return true;
|
|
355
|
+
if (a === 169 && b === 254) return true;
|
|
356
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
357
|
+
if (a === 192 && b === 168) return true;
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
function isPrivateIPv6(ip) {
|
|
361
|
+
const normalized = ip.toLowerCase();
|
|
362
|
+
if (normalized === "::1") return true;
|
|
363
|
+
if (normalized === "::") return true;
|
|
364
|
+
if (normalized.startsWith("::ffff:")) {
|
|
365
|
+
const mappedPart = normalized.slice(7);
|
|
366
|
+
if (isIPv4(mappedPart)) {
|
|
367
|
+
return isPrivateIPv4(mappedPart);
|
|
368
|
+
}
|
|
369
|
+
const hexParts = mappedPart.split(":");
|
|
370
|
+
if (hexParts.length === 2) {
|
|
371
|
+
const high = parseInt(hexParts[0], 16);
|
|
372
|
+
const low = parseInt(hexParts[1], 16);
|
|
373
|
+
if (!isNaN(high) && !isNaN(low)) {
|
|
374
|
+
const a = high >> 8 & 255;
|
|
375
|
+
const b = high & 255;
|
|
376
|
+
const c = low >> 8 & 255;
|
|
377
|
+
const d = low & 255;
|
|
378
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
|
|
383
|
+
if (normalized.startsWith("fe80")) return true;
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
291
387
|
// src/download-blob.ts
|
|
292
388
|
async function downloadBlob(url, options) {
|
|
293
389
|
var _a2, _b2;
|
|
390
|
+
validateDownloadUrl(url);
|
|
294
391
|
try {
|
|
295
392
|
const response = await fetch(url, {
|
|
296
393
|
signal: options == null ? void 0 : options.abortSignal
|
|
297
394
|
});
|
|
395
|
+
if (response.redirected) {
|
|
396
|
+
validateDownloadUrl(response.url);
|
|
397
|
+
}
|
|
298
398
|
if (!response.ok) {
|
|
299
399
|
throw new DownloadError({
|
|
300
400
|
url,
|
|
@@ -479,7 +579,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
|
|
|
479
579
|
}
|
|
480
580
|
|
|
481
581
|
// src/version.ts
|
|
482
|
-
var VERSION = true ? "4.0.
|
|
582
|
+
var VERSION = true ? "4.0.20" : "0.0.0-test";
|
|
483
583
|
|
|
484
584
|
// src/get-from-api.ts
|
|
485
585
|
var getOriginalFetch = () => globalThis.fetch;
|
|
@@ -2650,6 +2750,7 @@ export {
|
|
|
2650
2750
|
safeValidateTypes,
|
|
2651
2751
|
stripFileExtension,
|
|
2652
2752
|
tool,
|
|
2753
|
+
validateDownloadUrl,
|
|
2653
2754
|
validateTypes,
|
|
2654
2755
|
withUserAgentSuffix,
|
|
2655
2756
|
withoutTrailingSlash,
|