@ai-sdk/provider-utils 4.0.60 → 4.0.62
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 +15 -0
- package/dist/index.d.mts +61 -12
- package/dist/index.d.ts +61 -12
- package/dist/index.js +86 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/encode-path-segment.ts +15 -0
- package/src/fetch-with-validated-redirects.ts +65 -28
- package/src/get-from-api.ts +65 -10
- package/src/index.ts +1 -0
- package/src/sanitize-request-headers.ts +53 -0
- package/src/validate-download-url.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @ai-sdk/provider-utils
|
|
2
2
|
|
|
3
|
+
## 4.0.62
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1183cc4: Block `.internal` hostnames in download URL validation.
|
|
8
|
+
- d726591: Encode provider-returned identifiers in follow-up request paths with a shared encodePathSegment helper.
|
|
9
|
+
|
|
10
|
+
## 4.0.61
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 5aa3086: feat(openai-compatible): support URL-based image responses
|
|
15
|
+
|
|
16
|
+
Add opt-in URL validation and credential origin handling to `getFromApi` for URL-based image responses. Validate redirect targets, strip unsafe request headers, and drop credentials on cross-origin redirects while preserving existing unvalidated requests.
|
|
17
|
+
|
|
3
18
|
## 4.0.60
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -250,24 +250,34 @@ declare function fetchWithValidatedEndpoint({ url, init, fetch: customFetch, tru
|
|
|
250
250
|
redirect?: 'error' | 'manual';
|
|
251
251
|
}): Promise<Response>;
|
|
252
252
|
/**
|
|
253
|
-
* Fetches a URL while enforcing the
|
|
253
|
+
* Fetches a URL while enforcing the download guard on every hop.
|
|
254
254
|
*
|
|
255
255
|
* Redirects are followed manually (`redirect: 'manual'`) so each hop is
|
|
256
256
|
* validated with {@link validateDownloadUrl} *before* it is requested. Relying
|
|
257
257
|
* on the default `redirect: 'follow'` would issue the request to a redirect
|
|
258
258
|
* target (e.g. an internal address) before we ever see its URL, defeating the
|
|
259
|
-
*
|
|
259
|
+
* guard.
|
|
260
|
+
*
|
|
261
|
+
* Request headers are also protected: {@link sanitizeRequestHeaders} strips
|
|
262
|
+
* proxy/metadata/cookie/hop-by-hop headers before the first request, and all
|
|
263
|
+
* caller headers except `User-Agent` are dropped on a cross-origin redirect.
|
|
264
|
+
* Credentials and custom headers are preserved on the first hop for backwards
|
|
265
|
+
* compatibility. The caller must ensure that the initial URL may receive them.
|
|
266
|
+
* The fetch spec only strips `Authorization` on cross-origin redirects because
|
|
267
|
+
* in a browser, CORS preflighting protects custom headers; there is no CORS on
|
|
268
|
+
* the server, so provider API keys carried in custom headers (e.g. `x-key`)
|
|
269
|
+
* must be dropped here as well.
|
|
260
270
|
*
|
|
261
271
|
* A `redirect: 'manual'` request yields an unreadable opaque response in the
|
|
262
272
|
* browser (and in other spec-compliant fetch implementations), so the redirect
|
|
263
273
|
* target cannot be validated here. In a real browser this is safe to follow
|
|
264
|
-
* natively because
|
|
265
|
-
* cannot reach a server's internal network or
|
|
266
|
-
* runtime we cannot validate the hop, so we fail
|
|
267
|
-
* blindly and bypass the
|
|
274
|
+
* natively because reaching an internal network is not possible (fetch is
|
|
275
|
+
* constrained by CORS and cannot reach a server's internal network or
|
|
276
|
+
* cloud-metadata). On any other runtime we cannot validate the hop, so we fail
|
|
277
|
+
* closed rather than follow it blindly and bypass the guard.
|
|
268
278
|
*
|
|
269
279
|
* A hop that is same-origin with `trustedOrigin` (the developer-configured
|
|
270
|
-
* endpoint) skips target validation: that origin is exactly what an
|
|
280
|
+
* provider endpoint) skips target validation: that origin is exactly what an
|
|
271
281
|
* unvalidated, config-derived request would fetch anyway, and validating it
|
|
272
282
|
* would break legitimate self-hosted / localhost deployments whose response
|
|
273
283
|
* URLs point back at the configured host. Hops on any other origin are always
|
|
@@ -278,7 +288,8 @@ declare function fetchWithValidatedEndpoint({ url, init, fetch: customFetch, tru
|
|
|
278
288
|
*
|
|
279
289
|
* On Node.js, the default fetch resolves every hostname through a validating
|
|
280
290
|
* lookup hook and passes those exact addresses to the connector, preventing
|
|
281
|
-
* hostname-to-private-IP and DNS-rebinding bypasses.
|
|
291
|
+
* hostname-to-private-IP and DNS-rebinding bypasses. An injected fetch is
|
|
292
|
+
* responsible for equivalent connect-time validation. Other runtimes should
|
|
282
293
|
* constrain egress at the network layer when handling untrusted URLs.
|
|
283
294
|
*
|
|
284
295
|
* @throws DownloadError if a hop is unsafe, the redirect limit is exceeded, or
|
|
@@ -291,8 +302,8 @@ declare function fetchWithValidatedRedirects({ url, headers, abortSignal, maxRed
|
|
|
291
302
|
maxRedirects?: number;
|
|
292
303
|
fetch?: FetchFunction;
|
|
293
304
|
/**
|
|
294
|
-
* A developer-configured origin
|
|
295
|
-
* never be derived from response data.
|
|
305
|
+
* A developer-configured origin (e.g. the provider's `baseURL`) whose hops
|
|
306
|
+
* skip target validation. Must never be derived from response data.
|
|
296
307
|
*/
|
|
297
308
|
trustedOrigin?: string;
|
|
298
309
|
}): Promise<Response>;
|
|
@@ -323,6 +334,13 @@ type IdGenerator = () => string;
|
|
|
323
334
|
*/
|
|
324
335
|
declare const generateId: IdGenerator;
|
|
325
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Encodes a raw identifier as one URL path segment, including standalone dot
|
|
339
|
+
* segments that URL parsers would otherwise normalize. Pass unencoded values;
|
|
340
|
+
* for resource paths with intentional slashes, encode each segment separately.
|
|
341
|
+
*/
|
|
342
|
+
declare function encodePathSegment(value: string): string;
|
|
343
|
+
|
|
326
344
|
declare function getErrorMessage(error: unknown | undefined): string;
|
|
327
345
|
|
|
328
346
|
/**
|
|
@@ -467,13 +485,44 @@ declare const createJsonResponseHandler: <T>(responseSchema: FlexibleSchema<T>)
|
|
|
467
485
|
declare const createBinaryResponseHandler: () => ResponseHandler<Uint8Array>;
|
|
468
486
|
declare const createStatusCodeErrorResponseHandler: () => ResponseHandler<APICallError>;
|
|
469
487
|
|
|
470
|
-
declare const getFromApi: <T>({ url, headers, successfulResponseHandler, failedResponseHandler, abortSignal, fetch, }: {
|
|
488
|
+
declare const getFromApi: <T>({ url, headers, successfulResponseHandler, failedResponseHandler, abortSignal, fetch, validateUrl, credentialedOrigin, trustedOrigin, }: {
|
|
471
489
|
url: string;
|
|
472
490
|
headers?: Record<string, string | undefined>;
|
|
473
491
|
failedResponseHandler: ResponseHandler<Error>;
|
|
474
492
|
successfulResponseHandler: ResponseHandler<T>;
|
|
475
493
|
abortSignal?: AbortSignal;
|
|
476
494
|
fetch?: FetchFunction;
|
|
495
|
+
/**
|
|
496
|
+
* Set `true` when `url` is untrusted (e.g. taken from a provider response
|
|
497
|
+
* body): it is routed through {@link fetchWithValidatedRedirects}, which
|
|
498
|
+
* rejects private/loopback/link-local targets and re-validates redirect
|
|
499
|
+
* hops; blocked URLs throw `DownloadError`. Set `false` only for URLs built
|
|
500
|
+
* from a developer-configured endpoint.
|
|
501
|
+
*
|
|
502
|
+
* Optional for backwards compatibility with existing callers; omitting it
|
|
503
|
+
* behaves like `false` (no validation).
|
|
504
|
+
*/
|
|
505
|
+
validateUrl?: boolean;
|
|
506
|
+
/**
|
|
507
|
+
* When set, `headers` are sent only if `url` is same-origin with this origin
|
|
508
|
+
* (the user-agent suffix is always kept). Pass the provider's configured
|
|
509
|
+
* base URL alongside `validateUrl: true` so credentials never ride a request
|
|
510
|
+
* to a response-supplied host on a different origin (e.g. a CDN). Redirects
|
|
511
|
+
* that later cross origin drop all caller headers regardless (see
|
|
512
|
+
* {@link fetchWithValidatedRedirects}).
|
|
513
|
+
*/
|
|
514
|
+
credentialedOrigin?: string;
|
|
515
|
+
/**
|
|
516
|
+
* A developer-configured origin (e.g. the provider's `baseURL`) that is
|
|
517
|
+
* exempt from URL validation when `validateUrl` is `true`. A response URL
|
|
518
|
+
* (or redirect hop) that is same-origin with it is fetched without target
|
|
519
|
+
* validation — it points at exactly the host a config-derived
|
|
520
|
+
* `validateUrl: false` request would fetch anyway, so blocking it would
|
|
521
|
+
* only break legitimate self-hosted / localhost deployments whose response
|
|
522
|
+
* URLs point back at the configured host. Hops on any other origin are
|
|
523
|
+
* still validated. Must never be derived from response data.
|
|
524
|
+
*/
|
|
525
|
+
trustedOrigin?: string;
|
|
477
526
|
}) => Promise<{
|
|
478
527
|
value: T;
|
|
479
528
|
rawValue?: unknown;
|
|
@@ -1695,4 +1744,4 @@ interface ToolResult<NAME extends string, INPUT, OUTPUT> {
|
|
|
1695
1744
|
*/
|
|
1696
1745
|
type ToolCallOptions = ToolExecutionOptions;
|
|
1697
1746
|
|
|
1698
|
-
export { type AssistantContent, type AssistantModelMessage, DEFAULT_MAX_DOWNLOAD_SIZE, type DataContent, DelayedPromise, DownloadError, EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL as EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL, EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER as EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER, type EmbeddingModelProviderOptionsTransformer, type FetchFunction, type FilePart, type FlexibleSchema, type IdGenerator, type ImagePart, type InferSchema, type InferToolInput, type InferToolOutput, type LazySchema, type MaybePromiseLike, type ModelMessage, type ParseResult, type ProviderOptions, type ProviderToolFactory, type ProviderToolFactoryWithOutputSchema, type ReasoningPart, type Resolvable, type ResponseHandler, type RetryDelayProvider, type RetryErrorFactory, type RetryErrorReason, type RetryFunction, type Schema, type ShouldRetryFunction, type StreamingToolCallDelta, StreamingToolCallTracker, type StreamingToolCallTrackerOptions, type SystemModelMessage, type TextPart, type Tool, type ToolApprovalRequest, type ToolApprovalResponse, type ToolCall, type ToolCallOptions, type ToolCallPart, type ToolContent, type ToolExecuteFunction, type ToolExecutionOptions, type ToolModelMessage, type ToolNameMapping, type ToolNeedsApprovalFunction, type ToolResult, type ToolResultOutput, type ToolResultPart, type UserContent, type UserModelMessage, VERSION, type ValidationResult, asSchema, cancelResponseBody, combineHeaders, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertImageModelFileToDataUri, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createEventSourceResponseHandler, createIdGenerator, createJsonErrorResponseHandler, createJsonResponseHandler, createProviderToolFactory, createProviderToolFactoryWithOutputSchema, createStatusCodeErrorResponseHandler, createToolNameMapping, delay, downloadBlob, dynamicTool, executeTool, extractResponseHeaders, fetchWithValidatedEndpoint, fetchWithValidatedRedirects, generateId, getErrorMessage, getFromApi, getRuntimeEnvironmentUserAgent, injectJsonInstructionIntoMessages, isAbortError, isBrowserRuntime, isNonNullable, isParsableJson, isSameOrigin, isUrlSupported, jsonSchema, lazySchema, loadApiKey, loadOptionalSetting, loadSetting, mediaTypeToExtension, normalizeHeaders, parseJSON, parseJsonEventStream, parseProviderOptions, postFormDataToApi, postJsonToApi, postToApi, readResponseWithSizeLimit, removeUndefinedEntries, resolve, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, secureJsonParse, stripFileExtension, tool, validateDownloadUrl, validateTypes, withUserAgentSuffix, withoutTrailingSlash, zodSchema };
|
|
1747
|
+
export { type AssistantContent, type AssistantModelMessage, DEFAULT_MAX_DOWNLOAD_SIZE, type DataContent, DelayedPromise, DownloadError, EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL as EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL, EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER as EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER, type EmbeddingModelProviderOptionsTransformer, type FetchFunction, type FilePart, type FlexibleSchema, type IdGenerator, type ImagePart, type InferSchema, type InferToolInput, type InferToolOutput, type LazySchema, type MaybePromiseLike, type ModelMessage, type ParseResult, type ProviderOptions, type ProviderToolFactory, type ProviderToolFactoryWithOutputSchema, type ReasoningPart, type Resolvable, type ResponseHandler, type RetryDelayProvider, type RetryErrorFactory, type RetryErrorReason, type RetryFunction, type Schema, type ShouldRetryFunction, type StreamingToolCallDelta, StreamingToolCallTracker, type StreamingToolCallTrackerOptions, type SystemModelMessage, type TextPart, type Tool, type ToolApprovalRequest, type ToolApprovalResponse, type ToolCall, type ToolCallOptions, type ToolCallPart, type ToolContent, type ToolExecuteFunction, type ToolExecutionOptions, type ToolModelMessage, type ToolNameMapping, type ToolNeedsApprovalFunction, type ToolResult, type ToolResultOutput, type ToolResultPart, type UserContent, type UserModelMessage, VERSION, type ValidationResult, asSchema, cancelResponseBody, combineHeaders, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertImageModelFileToDataUri, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createEventSourceResponseHandler, createIdGenerator, createJsonErrorResponseHandler, createJsonResponseHandler, createProviderToolFactory, createProviderToolFactoryWithOutputSchema, createStatusCodeErrorResponseHandler, createToolNameMapping, delay, downloadBlob, dynamicTool, encodePathSegment, executeTool, extractResponseHeaders, fetchWithValidatedEndpoint, fetchWithValidatedRedirects, generateId, getErrorMessage, getFromApi, getRuntimeEnvironmentUserAgent, injectJsonInstructionIntoMessages, isAbortError, isBrowserRuntime, isNonNullable, isParsableJson, isSameOrigin, isUrlSupported, jsonSchema, lazySchema, loadApiKey, loadOptionalSetting, loadSetting, mediaTypeToExtension, normalizeHeaders, parseJSON, parseJsonEventStream, parseProviderOptions, postFormDataToApi, postJsonToApi, postToApi, readResponseWithSizeLimit, removeUndefinedEntries, resolve, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, secureJsonParse, stripFileExtension, tool, validateDownloadUrl, validateTypes, withUserAgentSuffix, withoutTrailingSlash, zodSchema };
|
package/dist/index.d.ts
CHANGED
|
@@ -250,24 +250,34 @@ declare function fetchWithValidatedEndpoint({ url, init, fetch: customFetch, tru
|
|
|
250
250
|
redirect?: 'error' | 'manual';
|
|
251
251
|
}): Promise<Response>;
|
|
252
252
|
/**
|
|
253
|
-
* Fetches a URL while enforcing the
|
|
253
|
+
* Fetches a URL while enforcing the download guard on every hop.
|
|
254
254
|
*
|
|
255
255
|
* Redirects are followed manually (`redirect: 'manual'`) so each hop is
|
|
256
256
|
* validated with {@link validateDownloadUrl} *before* it is requested. Relying
|
|
257
257
|
* on the default `redirect: 'follow'` would issue the request to a redirect
|
|
258
258
|
* target (e.g. an internal address) before we ever see its URL, defeating the
|
|
259
|
-
*
|
|
259
|
+
* guard.
|
|
260
|
+
*
|
|
261
|
+
* Request headers are also protected: {@link sanitizeRequestHeaders} strips
|
|
262
|
+
* proxy/metadata/cookie/hop-by-hop headers before the first request, and all
|
|
263
|
+
* caller headers except `User-Agent` are dropped on a cross-origin redirect.
|
|
264
|
+
* Credentials and custom headers are preserved on the first hop for backwards
|
|
265
|
+
* compatibility. The caller must ensure that the initial URL may receive them.
|
|
266
|
+
* The fetch spec only strips `Authorization` on cross-origin redirects because
|
|
267
|
+
* in a browser, CORS preflighting protects custom headers; there is no CORS on
|
|
268
|
+
* the server, so provider API keys carried in custom headers (e.g. `x-key`)
|
|
269
|
+
* must be dropped here as well.
|
|
260
270
|
*
|
|
261
271
|
* A `redirect: 'manual'` request yields an unreadable opaque response in the
|
|
262
272
|
* browser (and in other spec-compliant fetch implementations), so the redirect
|
|
263
273
|
* target cannot be validated here. In a real browser this is safe to follow
|
|
264
|
-
* natively because
|
|
265
|
-
* cannot reach a server's internal network or
|
|
266
|
-
* runtime we cannot validate the hop, so we fail
|
|
267
|
-
* blindly and bypass the
|
|
274
|
+
* natively because reaching an internal network is not possible (fetch is
|
|
275
|
+
* constrained by CORS and cannot reach a server's internal network or
|
|
276
|
+
* cloud-metadata). On any other runtime we cannot validate the hop, so we fail
|
|
277
|
+
* closed rather than follow it blindly and bypass the guard.
|
|
268
278
|
*
|
|
269
279
|
* A hop that is same-origin with `trustedOrigin` (the developer-configured
|
|
270
|
-
* endpoint) skips target validation: that origin is exactly what an
|
|
280
|
+
* provider endpoint) skips target validation: that origin is exactly what an
|
|
271
281
|
* unvalidated, config-derived request would fetch anyway, and validating it
|
|
272
282
|
* would break legitimate self-hosted / localhost deployments whose response
|
|
273
283
|
* URLs point back at the configured host. Hops on any other origin are always
|
|
@@ -278,7 +288,8 @@ declare function fetchWithValidatedEndpoint({ url, init, fetch: customFetch, tru
|
|
|
278
288
|
*
|
|
279
289
|
* On Node.js, the default fetch resolves every hostname through a validating
|
|
280
290
|
* lookup hook and passes those exact addresses to the connector, preventing
|
|
281
|
-
* hostname-to-private-IP and DNS-rebinding bypasses.
|
|
291
|
+
* hostname-to-private-IP and DNS-rebinding bypasses. An injected fetch is
|
|
292
|
+
* responsible for equivalent connect-time validation. Other runtimes should
|
|
282
293
|
* constrain egress at the network layer when handling untrusted URLs.
|
|
283
294
|
*
|
|
284
295
|
* @throws DownloadError if a hop is unsafe, the redirect limit is exceeded, or
|
|
@@ -291,8 +302,8 @@ declare function fetchWithValidatedRedirects({ url, headers, abortSignal, maxRed
|
|
|
291
302
|
maxRedirects?: number;
|
|
292
303
|
fetch?: FetchFunction;
|
|
293
304
|
/**
|
|
294
|
-
* A developer-configured origin
|
|
295
|
-
* never be derived from response data.
|
|
305
|
+
* A developer-configured origin (e.g. the provider's `baseURL`) whose hops
|
|
306
|
+
* skip target validation. Must never be derived from response data.
|
|
296
307
|
*/
|
|
297
308
|
trustedOrigin?: string;
|
|
298
309
|
}): Promise<Response>;
|
|
@@ -323,6 +334,13 @@ type IdGenerator = () => string;
|
|
|
323
334
|
*/
|
|
324
335
|
declare const generateId: IdGenerator;
|
|
325
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Encodes a raw identifier as one URL path segment, including standalone dot
|
|
339
|
+
* segments that URL parsers would otherwise normalize. Pass unencoded values;
|
|
340
|
+
* for resource paths with intentional slashes, encode each segment separately.
|
|
341
|
+
*/
|
|
342
|
+
declare function encodePathSegment(value: string): string;
|
|
343
|
+
|
|
326
344
|
declare function getErrorMessage(error: unknown | undefined): string;
|
|
327
345
|
|
|
328
346
|
/**
|
|
@@ -467,13 +485,44 @@ declare const createJsonResponseHandler: <T>(responseSchema: FlexibleSchema<T>)
|
|
|
467
485
|
declare const createBinaryResponseHandler: () => ResponseHandler<Uint8Array>;
|
|
468
486
|
declare const createStatusCodeErrorResponseHandler: () => ResponseHandler<APICallError>;
|
|
469
487
|
|
|
470
|
-
declare const getFromApi: <T>({ url, headers, successfulResponseHandler, failedResponseHandler, abortSignal, fetch, }: {
|
|
488
|
+
declare const getFromApi: <T>({ url, headers, successfulResponseHandler, failedResponseHandler, abortSignal, fetch, validateUrl, credentialedOrigin, trustedOrigin, }: {
|
|
471
489
|
url: string;
|
|
472
490
|
headers?: Record<string, string | undefined>;
|
|
473
491
|
failedResponseHandler: ResponseHandler<Error>;
|
|
474
492
|
successfulResponseHandler: ResponseHandler<T>;
|
|
475
493
|
abortSignal?: AbortSignal;
|
|
476
494
|
fetch?: FetchFunction;
|
|
495
|
+
/**
|
|
496
|
+
* Set `true` when `url` is untrusted (e.g. taken from a provider response
|
|
497
|
+
* body): it is routed through {@link fetchWithValidatedRedirects}, which
|
|
498
|
+
* rejects private/loopback/link-local targets and re-validates redirect
|
|
499
|
+
* hops; blocked URLs throw `DownloadError`. Set `false` only for URLs built
|
|
500
|
+
* from a developer-configured endpoint.
|
|
501
|
+
*
|
|
502
|
+
* Optional for backwards compatibility with existing callers; omitting it
|
|
503
|
+
* behaves like `false` (no validation).
|
|
504
|
+
*/
|
|
505
|
+
validateUrl?: boolean;
|
|
506
|
+
/**
|
|
507
|
+
* When set, `headers` are sent only if `url` is same-origin with this origin
|
|
508
|
+
* (the user-agent suffix is always kept). Pass the provider's configured
|
|
509
|
+
* base URL alongside `validateUrl: true` so credentials never ride a request
|
|
510
|
+
* to a response-supplied host on a different origin (e.g. a CDN). Redirects
|
|
511
|
+
* that later cross origin drop all caller headers regardless (see
|
|
512
|
+
* {@link fetchWithValidatedRedirects}).
|
|
513
|
+
*/
|
|
514
|
+
credentialedOrigin?: string;
|
|
515
|
+
/**
|
|
516
|
+
* A developer-configured origin (e.g. the provider's `baseURL`) that is
|
|
517
|
+
* exempt from URL validation when `validateUrl` is `true`. A response URL
|
|
518
|
+
* (or redirect hop) that is same-origin with it is fetched without target
|
|
519
|
+
* validation — it points at exactly the host a config-derived
|
|
520
|
+
* `validateUrl: false` request would fetch anyway, so blocking it would
|
|
521
|
+
* only break legitimate self-hosted / localhost deployments whose response
|
|
522
|
+
* URLs point back at the configured host. Hops on any other origin are
|
|
523
|
+
* still validated. Must never be derived from response data.
|
|
524
|
+
*/
|
|
525
|
+
trustedOrigin?: string;
|
|
477
526
|
}) => Promise<{
|
|
478
527
|
value: T;
|
|
479
528
|
rawValue?: unknown;
|
|
@@ -1695,4 +1744,4 @@ interface ToolResult<NAME extends string, INPUT, OUTPUT> {
|
|
|
1695
1744
|
*/
|
|
1696
1745
|
type ToolCallOptions = ToolExecutionOptions;
|
|
1697
1746
|
|
|
1698
|
-
export { type AssistantContent, type AssistantModelMessage, DEFAULT_MAX_DOWNLOAD_SIZE, type DataContent, DelayedPromise, DownloadError, EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL as EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL, EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER as EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER, type EmbeddingModelProviderOptionsTransformer, type FetchFunction, type FilePart, type FlexibleSchema, type IdGenerator, type ImagePart, type InferSchema, type InferToolInput, type InferToolOutput, type LazySchema, type MaybePromiseLike, type ModelMessage, type ParseResult, type ProviderOptions, type ProviderToolFactory, type ProviderToolFactoryWithOutputSchema, type ReasoningPart, type Resolvable, type ResponseHandler, type RetryDelayProvider, type RetryErrorFactory, type RetryErrorReason, type RetryFunction, type Schema, type ShouldRetryFunction, type StreamingToolCallDelta, StreamingToolCallTracker, type StreamingToolCallTrackerOptions, type SystemModelMessage, type TextPart, type Tool, type ToolApprovalRequest, type ToolApprovalResponse, type ToolCall, type ToolCallOptions, type ToolCallPart, type ToolContent, type ToolExecuteFunction, type ToolExecutionOptions, type ToolModelMessage, type ToolNameMapping, type ToolNeedsApprovalFunction, type ToolResult, type ToolResultOutput, type ToolResultPart, type UserContent, type UserModelMessage, VERSION, type ValidationResult, asSchema, cancelResponseBody, combineHeaders, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertImageModelFileToDataUri, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createEventSourceResponseHandler, createIdGenerator, createJsonErrorResponseHandler, createJsonResponseHandler, createProviderToolFactory, createProviderToolFactoryWithOutputSchema, createStatusCodeErrorResponseHandler, createToolNameMapping, delay, downloadBlob, dynamicTool, executeTool, extractResponseHeaders, fetchWithValidatedEndpoint, fetchWithValidatedRedirects, generateId, getErrorMessage, getFromApi, getRuntimeEnvironmentUserAgent, injectJsonInstructionIntoMessages, isAbortError, isBrowserRuntime, isNonNullable, isParsableJson, isSameOrigin, isUrlSupported, jsonSchema, lazySchema, loadApiKey, loadOptionalSetting, loadSetting, mediaTypeToExtension, normalizeHeaders, parseJSON, parseJsonEventStream, parseProviderOptions, postFormDataToApi, postJsonToApi, postToApi, readResponseWithSizeLimit, removeUndefinedEntries, resolve, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, secureJsonParse, stripFileExtension, tool, validateDownloadUrl, validateTypes, withUserAgentSuffix, withoutTrailingSlash, zodSchema };
|
|
1747
|
+
export { type AssistantContent, type AssistantModelMessage, DEFAULT_MAX_DOWNLOAD_SIZE, type DataContent, DelayedPromise, DownloadError, EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL as EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL, EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER as EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER, type EmbeddingModelProviderOptionsTransformer, type FetchFunction, type FilePart, type FlexibleSchema, type IdGenerator, type ImagePart, type InferSchema, type InferToolInput, type InferToolOutput, type LazySchema, type MaybePromiseLike, type ModelMessage, type ParseResult, type ProviderOptions, type ProviderToolFactory, type ProviderToolFactoryWithOutputSchema, type ReasoningPart, type Resolvable, type ResponseHandler, type RetryDelayProvider, type RetryErrorFactory, type RetryErrorReason, type RetryFunction, type Schema, type ShouldRetryFunction, type StreamingToolCallDelta, StreamingToolCallTracker, type StreamingToolCallTrackerOptions, type SystemModelMessage, type TextPart, type Tool, type ToolApprovalRequest, type ToolApprovalResponse, type ToolCall, type ToolCallOptions, type ToolCallPart, type ToolContent, type ToolExecuteFunction, type ToolExecutionOptions, type ToolModelMessage, type ToolNameMapping, type ToolNeedsApprovalFunction, type ToolResult, type ToolResultOutput, type ToolResultPart, type UserContent, type UserModelMessage, VERSION, type ValidationResult, asSchema, cancelResponseBody, combineHeaders, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertImageModelFileToDataUri, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createEventSourceResponseHandler, createIdGenerator, createJsonErrorResponseHandler, createJsonResponseHandler, createProviderToolFactory, createProviderToolFactoryWithOutputSchema, createStatusCodeErrorResponseHandler, createToolNameMapping, delay, downloadBlob, dynamicTool, encodePathSegment, executeTool, extractResponseHeaders, fetchWithValidatedEndpoint, fetchWithValidatedRedirects, generateId, getErrorMessage, getFromApi, getRuntimeEnvironmentUserAgent, injectJsonInstructionIntoMessages, isAbortError, isBrowserRuntime, isNonNullable, isParsableJson, isSameOrigin, isUrlSupported, jsonSchema, lazySchema, loadApiKey, loadOptionalSetting, loadSetting, mediaTypeToExtension, normalizeHeaders, parseJSON, parseJsonEventStream, parseProviderOptions, postFormDataToApi, postJsonToApi, postToApi, readResponseWithSizeLimit, removeUndefinedEntries, resolve, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, secureJsonParse, stripFileExtension, tool, validateDownloadUrl, validateTypes, withUserAgentSuffix, withoutTrailingSlash, zodSchema };
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ __export(index_exports, {
|
|
|
59
59
|
delay: () => delay,
|
|
60
60
|
downloadBlob: () => downloadBlob,
|
|
61
61
|
dynamicTool: () => dynamicTool,
|
|
62
|
+
encodePathSegment: () => encodePathSegment,
|
|
62
63
|
executeTool: () => executeTool,
|
|
63
64
|
extractResponseHeaders: () => extractResponseHeaders,
|
|
64
65
|
fetchWithValidatedEndpoint: () => fetchWithValidatedEndpoint,
|
|
@@ -390,7 +391,7 @@ function validateDownloadUrl(url) {
|
|
|
390
391
|
message: `URL must have a hostname`
|
|
391
392
|
});
|
|
392
393
|
}
|
|
393
|
-
if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost")) {
|
|
394
|
+
if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost") || hostname.endsWith(".internal")) {
|
|
394
395
|
throw new DownloadError({
|
|
395
396
|
url,
|
|
396
397
|
message: `URL with hostname ${hostname} is not allowed`
|
|
@@ -605,8 +606,45 @@ function getCurrentModulePath() {
|
|
|
605
606
|
}
|
|
606
607
|
}
|
|
607
608
|
|
|
609
|
+
// src/sanitize-request-headers.ts
|
|
610
|
+
var BLOCKED_REQUEST_HEADERS = [
|
|
611
|
+
// Hop-by-hop / transport (RFC 7230 §6.1)
|
|
612
|
+
"connection",
|
|
613
|
+
"keep-alive",
|
|
614
|
+
"te",
|
|
615
|
+
"trailer",
|
|
616
|
+
"transfer-encoding",
|
|
617
|
+
"upgrade",
|
|
618
|
+
// Host / virtual-host routing
|
|
619
|
+
"host",
|
|
620
|
+
// Proxy / origin spoofing
|
|
621
|
+
"forwarded",
|
|
622
|
+
"proxy-authorization",
|
|
623
|
+
"via",
|
|
624
|
+
"x-forwarded-for",
|
|
625
|
+
"x-forwarded-host",
|
|
626
|
+
"x-forwarded-proto",
|
|
627
|
+
"x-real-ip",
|
|
628
|
+
// Cloud metadata (GCP, AWS IMDSv1/v2, Azure, Alibaba, DigitalOcean)
|
|
629
|
+
"metadata",
|
|
630
|
+
"metadata-flavor",
|
|
631
|
+
"x-aws-ec2-metadata-token",
|
|
632
|
+
"x-metadata-token",
|
|
633
|
+
// Session / cookie
|
|
634
|
+
"cookie",
|
|
635
|
+
"set-cookie"
|
|
636
|
+
];
|
|
637
|
+
function sanitizeRequestHeaders(input) {
|
|
638
|
+
const headers = new Headers(input);
|
|
639
|
+
for (const name2 of BLOCKED_REQUEST_HEADERS) {
|
|
640
|
+
headers.delete(name2);
|
|
641
|
+
}
|
|
642
|
+
return headers;
|
|
643
|
+
}
|
|
644
|
+
|
|
608
645
|
// src/fetch-with-validated-redirects.ts
|
|
609
646
|
var MAX_DOWNLOAD_REDIRECTS = 10;
|
|
647
|
+
var REDIRECT_STATUS_CODES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
610
648
|
async function getValidatedFetch(customFetch) {
|
|
611
649
|
return customFetch == null || customFetch === globalThis.fetch ? await getDefaultDownloadFetch() : customFetch;
|
|
612
650
|
}
|
|
@@ -637,10 +675,14 @@ async function fetchWithValidatedRedirects({
|
|
|
637
675
|
trustedOrigin
|
|
638
676
|
}) {
|
|
639
677
|
var _a2;
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
678
|
+
let currentHeaders = headers === void 0 ? void 0 : sanitizeRequestHeaders(headers);
|
|
679
|
+
const perHopInit = (redirect) => {
|
|
680
|
+
const init = { signal: abortSignal, redirect };
|
|
681
|
+
if (currentHeaders !== void 0) {
|
|
682
|
+
init.headers = new Headers(currentHeaders);
|
|
683
|
+
}
|
|
684
|
+
return init;
|
|
685
|
+
};
|
|
644
686
|
let currentUrl = url;
|
|
645
687
|
for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
|
|
646
688
|
const isTrustedHop = trustedOrigin !== void 0 && isSameOrigin(currentUrl, trustedOrigin);
|
|
@@ -648,10 +690,7 @@ async function fetchWithValidatedRedirects({
|
|
|
648
690
|
validateDownloadUrl(currentUrl);
|
|
649
691
|
}
|
|
650
692
|
const fetch = isTrustedHop && customFetch != null ? customFetch : isTrustedHop ? globalThis.fetch : await getValidatedFetch(customFetch);
|
|
651
|
-
const response = await fetch(currentUrl,
|
|
652
|
-
...baseInit,
|
|
653
|
-
redirect: "manual"
|
|
654
|
-
});
|
|
693
|
+
const response = await fetch(currentUrl, perHopInit("manual"));
|
|
655
694
|
if (response.type === "opaqueredirect") {
|
|
656
695
|
if (!isBrowserRuntime()) {
|
|
657
696
|
throw new DownloadError({
|
|
@@ -659,12 +698,19 @@ async function fetchWithValidatedRedirects({
|
|
|
659
698
|
message: `Redirect from ${currentUrl} could not be validated and was blocked`
|
|
660
699
|
});
|
|
661
700
|
}
|
|
662
|
-
return await fetch(currentUrl,
|
|
701
|
+
return await fetch(currentUrl, perHopInit("follow"));
|
|
663
702
|
}
|
|
664
703
|
const location = (_a2 = response.headers) == null ? void 0 : _a2.get("location");
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
|
|
704
|
+
if (REDIRECT_STATUS_CODES.has(response.status) && location) {
|
|
705
|
+
void cancelResponseBody(response);
|
|
706
|
+
const nextUrl = new URL(location, currentUrl).toString();
|
|
707
|
+
if (currentHeaders !== void 0 && !isSameOrigin(nextUrl, currentUrl)) {
|
|
708
|
+
const userAgent = currentHeaders.get("user-agent");
|
|
709
|
+
currentHeaders = new Headers(
|
|
710
|
+
userAgent == null ? void 0 : { "user-agent": userAgent }
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
currentUrl = nextUrl;
|
|
668
714
|
continue;
|
|
669
715
|
}
|
|
670
716
|
return response;
|
|
@@ -800,6 +846,12 @@ var createIdGenerator = ({
|
|
|
800
846
|
};
|
|
801
847
|
var generateId = createIdGenerator();
|
|
802
848
|
|
|
849
|
+
// src/encode-path-segment.ts
|
|
850
|
+
function encodePathSegment(value) {
|
|
851
|
+
const encodedValue = encodeURIComponent(value);
|
|
852
|
+
return encodedValue === "." ? "%252E" : encodedValue === ".." ? "%252E%252E" : encodedValue;
|
|
853
|
+
}
|
|
854
|
+
|
|
803
855
|
// src/get-error-message.ts
|
|
804
856
|
function getErrorMessage(error) {
|
|
805
857
|
if (error == null) {
|
|
@@ -954,7 +1006,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
|
|
|
954
1006
|
}
|
|
955
1007
|
|
|
956
1008
|
// src/version.ts
|
|
957
|
-
var VERSION = true ? "4.0.
|
|
1009
|
+
var VERSION = true ? "4.0.62" : "0.0.0-test";
|
|
958
1010
|
|
|
959
1011
|
// src/get-from-api.ts
|
|
960
1012
|
var getOriginalFetch = () => globalThis.fetch;
|
|
@@ -964,16 +1016,28 @@ var getFromApi = async ({
|
|
|
964
1016
|
successfulResponseHandler,
|
|
965
1017
|
failedResponseHandler,
|
|
966
1018
|
abortSignal,
|
|
967
|
-
fetch
|
|
1019
|
+
fetch,
|
|
1020
|
+
validateUrl,
|
|
1021
|
+
credentialedOrigin,
|
|
1022
|
+
trustedOrigin
|
|
968
1023
|
}) => {
|
|
969
1024
|
try {
|
|
970
|
-
const
|
|
1025
|
+
const requestFetch = fetch != null ? fetch : getOriginalFetch();
|
|
1026
|
+
const outgoingHeaders = credentialedOrigin !== void 0 && !isSameOrigin(url, credentialedOrigin) ? {} : headers;
|
|
1027
|
+
const requestHeaders = withUserAgentSuffix(
|
|
1028
|
+
outgoingHeaders,
|
|
1029
|
+
`ai-sdk/provider-utils/${VERSION}`,
|
|
1030
|
+
getRuntimeEnvironmentUserAgent()
|
|
1031
|
+
);
|
|
1032
|
+
const response = validateUrl ? await fetchWithValidatedRedirects({
|
|
1033
|
+
url,
|
|
1034
|
+
headers: requestHeaders,
|
|
1035
|
+
abortSignal,
|
|
1036
|
+
fetch,
|
|
1037
|
+
trustedOrigin
|
|
1038
|
+
}) : await requestFetch(url, {
|
|
971
1039
|
method: "GET",
|
|
972
|
-
headers:
|
|
973
|
-
headers,
|
|
974
|
-
`ai-sdk/provider-utils/${VERSION}`,
|
|
975
|
-
getRuntimeEnvironmentUserAgent()
|
|
976
|
-
),
|
|
1040
|
+
headers: requestHeaders,
|
|
977
1041
|
signal: abortSignal
|
|
978
1042
|
});
|
|
979
1043
|
const responseHeaders = extractResponseHeaders(response);
|
|
@@ -3618,6 +3682,7 @@ var import_stream2 = require("eventsource-parser/stream");
|
|
|
3618
3682
|
delay,
|
|
3619
3683
|
downloadBlob,
|
|
3620
3684
|
dynamicTool,
|
|
3685
|
+
encodePathSegment,
|
|
3621
3686
|
executeTool,
|
|
3622
3687
|
extractResponseHeaders,
|
|
3623
3688
|
fetchWithValidatedEndpoint,
|