@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 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 SSRF download guard on every hop.
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
- * SSRF guard.
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 SSRF is not reachable (fetch is constrained by CORS and
265
- * cannot reach a server's internal network or cloud-metadata). On any other
266
- * runtime we cannot validate the hop, so we fail closed rather than follow it
267
- * blindly and bypass the SSRF guard.
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. Other runtimes should
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 whose hops skip target validation. Must
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 SSRF download guard on every hop.
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
- * SSRF guard.
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 SSRF is not reachable (fetch is constrained by CORS and
265
- * cannot reach a server's internal network or cloud-metadata). On any other
266
- * runtime we cannot validate the hop, so we fail closed rather than follow it
267
- * blindly and bypass the SSRF guard.
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. Other runtimes should
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 whose hops skip target validation. Must
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
- const baseInit = { signal: abortSignal };
641
- if (headers !== void 0) {
642
- baseInit.headers = headers;
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, { ...baseInit, redirect: "follow" });
701
+ return await fetch(currentUrl, perHopInit("follow"));
663
702
  }
664
703
  const location = (_a2 = response.headers) == null ? void 0 : _a2.get("location");
665
- if (response.status >= 300 && response.status < 400 && location) {
666
- await cancelResponseBody(response);
667
- currentUrl = new URL(location, currentUrl).toString();
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.60" : "0.0.0-test";
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 = getOriginalFetch()
1019
+ fetch,
1020
+ validateUrl,
1021
+ credentialedOrigin,
1022
+ trustedOrigin
968
1023
  }) => {
969
1024
  try {
970
- const response = await fetch(url, {
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: withUserAgentSuffix(
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,