@ai-sdk/provider-utils 4.0.18 → 4.0.19
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 +6 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +100 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/download-blob.ts +2 -0
- package/src/index.ts +1 -0
- package/src/validate-download-url.ts +143 -0
package/dist/index.mjs
CHANGED
|
@@ -288,9 +288,106 @@ 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
|
|
@@ -479,7 +576,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
|
|
|
479
576
|
}
|
|
480
577
|
|
|
481
578
|
// src/version.ts
|
|
482
|
-
var VERSION = true ? "4.0.
|
|
579
|
+
var VERSION = true ? "4.0.19" : "0.0.0-test";
|
|
483
580
|
|
|
484
581
|
// src/get-from-api.ts
|
|
485
582
|
var getOriginalFetch = () => globalThis.fetch;
|
|
@@ -2650,6 +2747,7 @@ export {
|
|
|
2650
2747
|
safeValidateTypes,
|
|
2651
2748
|
stripFileExtension,
|
|
2652
2749
|
tool,
|
|
2750
|
+
validateDownloadUrl,
|
|
2653
2751
|
validateTypes,
|
|
2654
2752
|
withUserAgentSuffix,
|
|
2655
2753
|
withoutTrailingSlash,
|