ai 7.0.35 → 7.0.36

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,24 @@
1
1
  # ai
2
2
 
3
+ ## 7.0.36
4
+
5
+ ### Patch Changes
6
+
7
+ - 7fa85b2: fix(ai): use injective serialization for tool approval HMAC payload
8
+
9
+ The tool approval signature (`experimental_toolApprovalSecret`) built its HMAC
10
+ payload by joining fields with `\n`. Because fields such as `toolName` and
11
+ `toolCallId` can themselves contain a newline, distinct field tuples could
12
+ serialize to identical bytes, allowing a signed approval to verify against a
13
+ different tuple. The payload is now serialized with `JSON.stringify` (with a
14
+ versioned domain-separation prefix), which escapes delimiter/control characters
15
+ and makes the encoding injective.
16
+
17
+ Verification remains backwards compatible: a signature in the old format still
18
+ verifies, but only when no field contains the `\n` delimiter (the condition
19
+ that made the old format ambiguous), so a pending approval that straddles an
20
+ upgrade is not rejected while the collision stays closed.
21
+
3
22
  ## 7.0.35
4
23
 
5
24
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1089,7 +1089,7 @@ import {
1089
1089
  } from "@ai-sdk/provider-utils";
1090
1090
 
1091
1091
  // src/version.ts
1092
- var VERSION = true ? "7.0.35" : "0.0.0-test";
1092
+ var VERSION = true ? "7.0.36" : "0.0.0-test";
1093
1093
 
1094
1094
  // src/util/download/download.ts
1095
1095
  var download = async ({
@@ -4853,6 +4853,17 @@ async function importKey(secret) {
4853
4853
  );
4854
4854
  }
4855
4855
  function buildPayload(approvalId, toolCallId, toolName, inputDigest) {
4856
+ return encoder2.encode(
4857
+ JSON.stringify([
4858
+ "ai-sdk-tool-approval-v1",
4859
+ approvalId,
4860
+ toolCallId,
4861
+ toolName,
4862
+ inputDigest
4863
+ ])
4864
+ );
4865
+ }
4866
+ function buildLegacyPayload(approvalId, toolCallId, toolName, inputDigest) {
4856
4867
  return encoder2.encode(
4857
4868
  `${approvalId}
4858
4869
  ${toolCallId}
@@ -4883,9 +4894,21 @@ async function verifyToolApprovalSignature({
4883
4894
  }) {
4884
4895
  const key = await importKey(secret);
4885
4896
  const inputDigest = await hashCanonical(input);
4886
- const payload = buildPayload(approvalId, toolCallId, toolName, inputDigest);
4887
4897
  const sigBytes = fromBase64url(signature);
4888
- return crypto.subtle.verify("HMAC", key, sigBytes, payload);
4898
+ const payload = buildPayload(approvalId, toolCallId, toolName, inputDigest);
4899
+ if (await crypto.subtle.verify("HMAC", key, sigBytes, payload)) {
4900
+ return true;
4901
+ }
4902
+ if (!approvalId.includes("\n") && !toolCallId.includes("\n") && !toolName.includes("\n")) {
4903
+ const legacyPayload = buildLegacyPayload(
4904
+ approvalId,
4905
+ toolCallId,
4906
+ toolName,
4907
+ inputDigest
4908
+ );
4909
+ return crypto.subtle.verify("HMAC", key, sigBytes, legacyPayload);
4910
+ }
4911
+ return false;
4889
4912
  }
4890
4913
  async function maybeSignApproval({
4891
4914
  secret,