ai 6.0.202 → 6.0.203
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 +27 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +20 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -23
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +10 -12
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +11 -13
- package/dist/internal/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/generate-text/stream-text.ts +6 -5
- package/src/middleware/extract-json-middleware.ts +2 -1
- package/src/middleware/extract-reasoning-middleware.ts +2 -1
- package/src/ui-message-stream/create-ui-message-stream.ts +2 -3
- package/src/util/download/download.ts +11 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# ai
|
|
2
2
|
|
|
3
|
+
## 6.0.203
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f42aa79: fix: harden download URL SSRF guard against hostname and redirect bypasses
|
|
8
|
+
|
|
9
|
+
`validateDownloadUrl` and the file download helpers (`downloadBlob`, `download`) could be bypassed in several ways when handling untrusted URLs:
|
|
10
|
+
|
|
11
|
+
- A fully-qualified hostname with a trailing dot (e.g. `localhost.`, `myhost.local.`) skipped the localhost/`.local` blocklist.
|
|
12
|
+
- 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.
|
|
13
|
+
- 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.
|
|
14
|
+
- 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`).
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
- 5291f7e: Harden stream text processing and middleware against prototype pollution from stream part IDs.
|
|
19
|
+
- b4b575a: fix: redact server error details from UI message streams by default
|
|
20
|
+
|
|
21
|
+
`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.
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [bfa5864]
|
|
26
|
+
- Updated dependencies [f42aa79]
|
|
27
|
+
- @ai-sdk/provider-utils@4.0.29
|
|
28
|
+
- @ai-sdk/gateway@3.0.129
|
|
29
|
+
|
|
3
30
|
## 6.0.202
|
|
4
31
|
|
|
5
32
|
### 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 `
|
|
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,
|
|
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 `
|
|
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,
|
|
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.
|
|
1284
|
+
var VERSION = true ? "6.0.203" : "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
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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 =
|
|
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 =
|
|
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(
|