ai 6.0.202 → 6.0.204

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,39 @@
1
1
  # ai
2
2
 
3
+ ## 6.0.204
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [c5d4716]
8
+ - @ai-sdk/gateway@3.0.130
9
+
10
+ ## 6.0.203
11
+
12
+ ### Patch Changes
13
+
14
+ - f42aa79: fix: harden download URL SSRF guard against hostname and redirect bypasses
15
+
16
+ `validateDownloadUrl` and the file download helpers (`downloadBlob`, `download`) could be bypassed in several ways when handling untrusted URLs:
17
+
18
+ - A fully-qualified hostname with a trailing dot (e.g. `localhost.`, `myhost.local.`) skipped the localhost/`.local` blocklist.
19
+ - IPv6 addresses that embed an IPv4 address in their last 32 bits — IPv4-compatible (`::127.0.0.1`), IPv4-translated (`::ffff:0:127.0.0.1`), and NAT64 (`64:ff9b::127.0.0.1`, including the `64:ff9b:1::/48` local-use prefix) — were not decoded and checked against the private IPv4 ranges.
20
+ - Redirects were validated only _after_ `fetch` had already followed them, so the request to a redirect target (e.g. an internal/metadata address) had already been issued before the check ran.
21
+ - Several reserved/internal address ranges were not blocked: CGNAT (`100.64.0.0/10`, used by some cloud providers for internal traffic), benchmarking (`198.18.0.0/15`), IETF protocol assignments (`192.0.0.0/24`), the reserved `240.0.0.0/4` block (including the `255.255.255.255` broadcast address), and IPv6 site-local (`fec0::/10`) and multicast (`ff00::/8`).
22
+
23
+ The validator now strips trailing dots before the hostname checks and fully expands IPv6 addresses to detect embedded private IPv4 targets. The download helpers now follow redirects manually (`redirect: 'manual'`), re-validating each hop before requesting it, so an unsafe redirect target is never fetched. When a redirect cannot be inspected because the runtime returns an opaque response, the helpers fail closed (reject the redirect) on the server; only in a real browser — where SSRF is not reachable (fetch is constrained by CORS and cannot reach a server's internal network or cloud-metadata endpoints) — is the redirect followed natively so legitimate redirected downloads keep working.
24
+
25
+ - 5291f7e: Harden stream text processing and middleware against prototype pollution from stream part IDs.
26
+ - b4b575a: fix: redact server error details from UI message streams by default
27
+
28
+ `streamText(...).toUIMessageStream()` and `createUIMessageStream` defaulted their `onError` callback to `getErrorMessage`, which serializes the raw error (`error.toString()` / `JSON.stringify(error)`) into the client-facing `{ type: 'error', errorText }` chunk — and also into `tool-output-error` parts. The documented default was `() => 'An error occurred.'`, so applications relying on the documented behavior were unknowingly streaming server exception details (internal hostnames, paths, provider request data, validation inputs) to end users.
29
+
30
+ The default `onError` now returns the documented generic `'An error occurred.'`. Raw error details are only emitted when the developer explicitly supplies an `onError` handler. This also redacts `tool-output-error` and invalid-tool-input error text by default; pass an `onError` to surface richer messages.
31
+
32
+ - Updated dependencies [bfa5864]
33
+ - Updated dependencies [f42aa79]
34
+ - @ai-sdk/provider-utils@4.0.29
35
+ - @ai-sdk/gateway@3.0.129
36
+
3
37
  ## 6.0.202
4
38
 
5
39
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -4296,7 +4296,7 @@ interface UIMessageStreamWriter<UI_MESSAGE extends UIMessage = UIMessage> {
4296
4296
  * Creates a UI message stream that can be used to send messages to the client.
4297
4297
  *
4298
4298
  * @param options.execute - A function that is called with a writer to write UI message chunks to the stream.
4299
- * @param options.onError - A function that extracts an error message from an error. Defaults to `getErrorMessage`.
4299
+ * @param options.onError - A function that extracts an error message from an error. Defaults to `() => 'An error occurred.'` so server-side error details are not leaked to the client; supply your own to surface richer messages.
4300
4300
  * @param options.originalMessages - The original messages. If provided, persistence mode is assumed
4301
4301
  * and a message ID is provided for the response message.
4302
4302
  * @param options.onStepFinish - A callback that is called when each step finishes. Useful for persisting intermediate messages.
@@ -4305,7 +4305,8 @@ interface UIMessageStreamWriter<UI_MESSAGE extends UIMessage = UIMessage> {
4305
4305
  *
4306
4306
  * @returns A `ReadableStream` of UI message chunks.
4307
4307
  */
4308
- declare function createUIMessageStream<UI_MESSAGE extends UIMessage>({ execute, onError, originalMessages, onStepFinish, onFinish, generateId, }: {
4308
+ declare function createUIMessageStream<UI_MESSAGE extends UIMessage>({ execute, onError, // prevent leaking server error details to the client by default
4309
+ originalMessages, onStepFinish, onFinish, generateId, }: {
4309
4310
  execute: (options: {
4310
4311
  writer: UIMessageStreamWriter<UI_MESSAGE>;
4311
4312
  }) => Promise<void> | void;
package/dist/index.d.ts CHANGED
@@ -4296,7 +4296,7 @@ interface UIMessageStreamWriter<UI_MESSAGE extends UIMessage = UIMessage> {
4296
4296
  * Creates a UI message stream that can be used to send messages to the client.
4297
4297
  *
4298
4298
  * @param options.execute - A function that is called with a writer to write UI message chunks to the stream.
4299
- * @param options.onError - A function that extracts an error message from an error. Defaults to `getErrorMessage`.
4299
+ * @param options.onError - A function that extracts an error message from an error. Defaults to `() => 'An error occurred.'` so server-side error details are not leaked to the client; supply your own to surface richer messages.
4300
4300
  * @param options.originalMessages - The original messages. If provided, persistence mode is assumed
4301
4301
  * and a message ID is provided for the response message.
4302
4302
  * @param options.onStepFinish - A callback that is called when each step finishes. Useful for persisting intermediate messages.
@@ -4305,7 +4305,8 @@ interface UIMessageStreamWriter<UI_MESSAGE extends UIMessage = UIMessage> {
4305
4305
  *
4306
4306
  * @returns A `ReadableStream` of UI message chunks.
4307
4307
  */
4308
- declare function createUIMessageStream<UI_MESSAGE extends UIMessage>({ execute, onError, originalMessages, onStepFinish, onFinish, generateId, }: {
4308
+ declare function createUIMessageStream<UI_MESSAGE extends UIMessage>({ execute, onError, // prevent leaking server error details to the client by default
4309
+ originalMessages, onStepFinish, onFinish, generateId, }: {
4309
4310
  execute: (options: {
4310
4311
  writer: UIMessageStreamWriter<UI_MESSAGE>;
4311
4312
  }) => Promise<void> | void;
package/dist/index.js CHANGED
@@ -1281,7 +1281,7 @@ function detectMediaType({
1281
1281
  var import_provider_utils3 = require("@ai-sdk/provider-utils");
1282
1282
 
1283
1283
  // src/version.ts
1284
- var VERSION = true ? "6.0.202" : "0.0.0-test";
1284
+ var VERSION = true ? "6.0.204" : "0.0.0-test";
1285
1285
 
1286
1286
  // src/util/download/download.ts
1287
1287
  var download = async ({
@@ -1291,19 +1291,17 @@ var download = async ({
1291
1291
  }) => {
1292
1292
  var _a22;
1293
1293
  const urlText = url.toString();
1294
- (0, import_provider_utils3.validateDownloadUrl)(urlText);
1295
1294
  try {
1296
- const response = await fetch(urlText, {
1297
- headers: (0, import_provider_utils3.withUserAgentSuffix)(
1298
- {},
1299
- `ai-sdk/${VERSION}`,
1300
- (0, import_provider_utils3.getRuntimeEnvironmentUserAgent)()
1301
- ),
1302
- signal: abortSignal
1295
+ const headers = (0, import_provider_utils3.withUserAgentSuffix)(
1296
+ {},
1297
+ `ai-sdk/${VERSION}`,
1298
+ (0, import_provider_utils3.getRuntimeEnvironmentUserAgent)()
1299
+ );
1300
+ const response = await (0, import_provider_utils3.fetchWithValidatedRedirects)({
1301
+ url: urlText,
1302
+ headers,
1303
+ abortSignal
1303
1304
  });
1304
- if (response.redirected) {
1305
- (0, import_provider_utils3.validateDownloadUrl)(response.url);
1306
- }
1307
1305
  if (!response.ok) {
1308
1306
  throw new import_provider_utils3.DownloadError({
1309
1307
  url: urlText,
@@ -7047,8 +7045,8 @@ var DefaultStreamTextResult = class {
7047
7045
  let recordedNoOutputError;
7048
7046
  const pendingDeferredToolCalls = /* @__PURE__ */ new Map();
7049
7047
  let rootSpan;
7050
- let activeTextContent = {};
7051
- let activeReasoningContent = {};
7048
+ let activeTextContent = createIdMap();
7049
+ let activeReasoningContent = createIdMap();
7052
7050
  const eventProcessor = new TransformStream({
7053
7051
  async transform(chunk, controller) {
7054
7052
  var _a22, _b, _c, _d;
@@ -7164,8 +7162,8 @@ var DefaultStreamTextResult = class {
7164
7162
  }
7165
7163
  if (part.type === "start-step") {
7166
7164
  recordedContent = [];
7167
- activeReasoningContent = {};
7168
- activeTextContent = {};
7165
+ activeReasoningContent = createIdMap();
7166
+ activeTextContent = createIdMap();
7169
7167
  recordedRequest = part.request;
7170
7168
  recordedWarnings = part.warnings;
7171
7169
  }
@@ -8272,7 +8270,8 @@ var DefaultStreamTextResult = class {
8272
8270
  sendSources = false,
8273
8271
  sendStart = true,
8274
8272
  sendFinish = true,
8275
- onError = import_provider29.getErrorMessage
8273
+ onError = () => "An error occurred."
8274
+ // prevent leaking server error details to the client by default
8276
8275
  } = {}) {
8277
8276
  const responseMessageId = generateMessageId != null ? getResponseUIMessageId({
8278
8277
  originalMessages,
@@ -8710,7 +8709,8 @@ var ToolLoopAgent = class {
8710
8709
  var import_provider_utils22 = require("@ai-sdk/provider-utils");
8711
8710
  function createUIMessageStream({
8712
8711
  execute,
8713
- onError = import_provider_utils22.getErrorMessage,
8712
+ onError = () => "An error occurred.",
8713
+ // prevent leaking server error details to the client by default
8714
8714
  originalMessages,
8715
8715
  onStepFinish,
8716
8716
  onFinish,
@@ -12074,7 +12074,7 @@ function extractJsonMiddleware(options) {
12074
12074
  },
12075
12075
  wrapStream: async ({ doStream }) => {
12076
12076
  const { stream, ...rest } = await doStream();
12077
- const textBlocks = {};
12077
+ const textBlocks = createIdMap();
12078
12078
  const SUFFIX_BUFFER_SIZE = 12;
12079
12079
  return {
12080
12080
  stream: stream.pipeThrough(
@@ -12237,7 +12237,7 @@ function extractReasoningMiddleware({
12237
12237
  },
12238
12238
  wrapStream: async ({ doStream }) => {
12239
12239
  const { stream, ...rest } = await doStream();
12240
- const reasoningExtractions = {};
12240
+ const reasoningExtractions = createIdMap();
12241
12241
  let delayedTextStart;
12242
12242
  return {
12243
12243
  stream: stream.pipeThrough(