+1
-1
| { | ||
| "name": "ai", | ||
| "version": "7.0.35", | ||
| "version": "7.0.36", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.", |
@@ -21,2 +21,5 @@ import { convertBase64ToUint8Array } from '@ai-sdk/provider-utils'; | ||
| // Serialize with JSON so the encoding is injective: fields may contain any | ||
| // character (including newlines), and escaping + array structure keeps field | ||
| // boundaries unambiguous. The version prefix provides domain separation. | ||
| function buildPayload( | ||
@@ -29,2 +32,25 @@ approvalId: string, | ||
| return encoder.encode( | ||
| JSON.stringify([ | ||
| 'ai-sdk-tool-approval-v1', | ||
| approvalId, | ||
| toolCallId, | ||
| toolName, | ||
| inputDigest, | ||
| ]), | ||
| ); | ||
| } | ||
| // Legacy newline-joined payload. Ambiguous when any field contains the `\n` | ||
| // delimiter, which is why it was replaced; only used as a verify-time fallback | ||
| // guarded on delimiter-free fields. | ||
| // TODO(#17494): remove in v8 when backwards compatibility with pre-JSON | ||
| // signatures (approvals signed before the injective format) is no longer | ||
| // needed. | ||
| function buildLegacyPayload( | ||
| approvalId: string, | ||
| toolCallId: string, | ||
| toolName: string, | ||
| inputDigest: string, | ||
| ): Uint8Array { | ||
| return encoder.encode( | ||
| `${approvalId}\n${toolCallId}\n${toolName}\n${inputDigest}`, | ||
@@ -71,5 +97,31 @@ ); | ||
| const inputDigest = await hashCanonical(input); | ||
| const sigBytes = fromBase64url(signature); | ||
| const payload = buildPayload(approvalId, toolCallId, toolName, inputDigest); | ||
| const sigBytes = fromBase64url(signature); | ||
| return crypto.subtle.verify('HMAC', key, sigBytes, payload); | ||
| if (await crypto.subtle.verify('HMAC', key, sigBytes, payload)) { | ||
| return true; | ||
| } | ||
| // Backwards compatibility: accept a signature produced by the legacy | ||
| // newline-joined format, but only when no field contains the `\n` delimiter | ||
| // — the exact condition that made that format ambiguous. This keeps the | ||
| // retupling collision closed (the attack requires a newline in a field) | ||
| // while still verifying benign approvals signed by an older version, e.g. a | ||
| // pending approval that straddles an upgrade. | ||
| // TODO(#17494): remove in v8 (drop buildLegacyPayload and this fallback). | ||
| if ( | ||
| !approvalId.includes('\n') && | ||
| !toolCallId.includes('\n') && | ||
| !toolName.includes('\n') | ||
| ) { | ||
| const legacyPayload = buildLegacyPayload( | ||
| approvalId, | ||
| toolCallId, | ||
| toolName, | ||
| inputDigest, | ||
| ); | ||
| return crypto.subtle.verify('HMAC', key, sigBytes, legacyPayload); | ||
| } | ||
| return false; | ||
| } | ||
@@ -76,0 +128,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
6431956
0.17%65982
0.14%