Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static Frames Validation Security Fix #736

Merged
merged 30 commits into from
Dec 2, 2024
Merged

Static Frames Validation Security Fix #736

merged 30 commits into from
Dec 2, 2024

Conversation

codabrink
Copy link
Contributor

@codabrink codabrink commented Nov 26, 2024

Summary by CodeRabbit

  • New Features

    • Enhanced validation logic for frame submissions, focusing on authorization checks.
    • Reintroduced methods for checking address and installation authorization in the client.
  • Bug Fixes

    • Improved error handling for unauthorized installations and wallet addresses.
  • Chores

    • Updated package dependencies for @xmtp/frames-validator and @xmtp/frames-client to the latest version.
  • Tests

    • Updated test cases to reflect new endpoint identifiers for improved accuracy.

Copy link

changeset-bot bot commented Nov 26, 2024

🦋 Changeset detected

Latest commit: 1d2dab8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@xmtp/frames-validator Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

coderabbitai bot commented Nov 26, 2024

Warning

Rate limit exceeded

@rygine has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 24 minutes and 15 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7968cea and 1d2dab8.

Walkthrough

The changes in this pull request involve updates to the @xmtp/frames-validator and @xmtp/frames-client packages, including version bumps for the @xmtp/node-sdk dependency. The validateFramesPost function in validation.ts has been enhanced to improve authorization checks, replacing direct inbox ID comparisons with new methods. Additionally, the Client class in the node-sdk has reintroduced authorization methods. The test cases in client.test.ts have been updated to reflect new endpoint URLs.

Changes

File Path Change Summary
packages/frames-validator/package.json Version updated from 1.0.0 to 1.0.1; @xmtp/node-sdk dependency updated from ^0.0.27 to ^0.0.29.
packages/frames-validator/src/validation.ts Modified validateFramesPost function to enhance validation logic with new authorization checks.
sdks/node-sdk/src/Client.ts Reintroduced isAddressAuthorized and isInstallationAuthorized methods; added static methods for authorization checks.
packages/frames-client/package.json @xmtp/node-sdk dependency updated from ^0.0.27 to ^0.0.29.
packages/frames-client/src/client.test.ts Updated URLs in worksE2E function to reflect new identifiers for testing.

Possibly related PRs

Suggested reviewers

  • nplasterer

🐇 Hop along, let's celebrate,
Version bumps and checks so great!
New methods here, old ones gone,
In our code, we carry on!
With URLs updated, tests align,
Hopping forward, all is fine! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

cloudflare-workers-and-pages bot commented Nov 26, 2024

Deploying xmtp-js-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1d2dab8
Status: ✅  Deploy successful!
Preview URL: https://1ee6ca8e.xmtp-js.pages.dev
Branch Preview URL: https://coda-frames-check.xmtp-js.pages.dev

View logs

sdks/node-sdk/package.json Outdated Show resolved Hide resolved
@codabrink codabrink marked this pull request as ready for review November 26, 2024 17:57
@codabrink codabrink requested a review from a team as a code owner November 26, 2024 17:57
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (6)
sdks/node-sdk/src/Client.ts (3)

175-180: Add JSDoc documentation and input validation

The method implementation looks correct, but could benefit from:

  1. JSDoc documentation explaining the purpose, parameters, and return value
  2. Input validation for the Ethereum address format
+  /**
+   * Checks if an address is authorized for a given inbox
+   * @param inboxId - The ID of the inbox to check
+   * @param address - The Ethereum address to verify
+   * @returns Promise resolving to true if the address is authorized
+   */
   async isAddressAuthorized(
     inboxId: string,
     address: string,
   ): Promise<boolean> {
+    if (!address.match(/^0x[0-9a-fA-F]{40}$/)) {
+      throw new Error('Invalid Ethereum address format');
+    }
     return this.#innerClient.isAddressAuthorized(inboxId, address);
   }

182-187: Add JSDoc documentation and input validation

The method implementation looks correct, but could benefit from:

  1. JSDoc documentation explaining the purpose, parameters, and return value
  2. Basic validation for the installationId parameter
+  /**
+   * Checks if an installation is authorized for a given inbox
+   * @param inboxId - The ID of the inbox to check
+   * @param installationId - The installation ID as a byte array
+   * @returns Promise resolving to true if the installation is authorized
+   */
   async isInstallationAuthorized(
     inboxId: string,
     installationId: Uint8Array,
   ): Promise<boolean> {
+    if (!installationId?.length) {
+      throw new Error('Installation ID cannot be empty');
+    }
     return this.#innerClient.isInstallationAuthorized(inboxId, installationId);
   }

175-187: Consider documenting the authorization flow

These methods are part of a larger authorization system and are used by the frames-validator. Consider adding documentation that explains:

  1. The overall authorization flow
  2. How these methods fit into the larger picture
  3. Common usage patterns and examples

This would help developers understand when and how to use these methods effectively.

Consider adding this documentation in:

  1. The class-level JSDoc
  2. A separate authorization flow document
  3. Code examples in the README
packages/frames-validator/src/validation.ts (3)

31-31: Include missing variable in destructuring assignment

You're destructuring deserializeProtoMessage(messageBytes) into several variables. Ensure that all necessary variables returned by deserializeProtoMessage are included in the assignment. If messageType or other relevant variables are returned by the function but not destructured here, consider adding them for completeness.


59-61: Consistent error messages for unauthorized access

The error message "Unable to associate wallet address with inbox" might not clearly convey that the wallet address is not authorized. Consider rephrasing it to make the issue more explicit.

Suggested change:

-throw new Error("Unable to associate wallet address with inbox");
+throw new Error("Wallet address is not authorized to access the inbox");

59-61: Handle potential errors from isAddressAuthorized method

Similar to the previous authorization check, wrap the call to Client.isAddressAuthorized in a try-catch block to handle any unexpected errors.

Apply this change:

+try {
  const isMember = await Client.isAddressAuthorized(inboxId, walletAddress);
  if (!isMember) {
    throw new Error("Wallet address is not authorized to access the inbox");
  }
+} catch (error) {
+  throw new Error(`Address authorization check failed: ${error.message}`);
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4e393d2 and e530d1b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • packages/frames-validator/package.json (1 hunks)
  • packages/frames-validator/src/validation.ts (2 hunks)
  • sdks/node-sdk/package.json (2 hunks)
  • sdks/node-sdk/src/Client.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • sdks/node-sdk/package.json
🔇 Additional comments (3)
packages/frames-validator/package.json (1)

43-43: Verify the impact of node-sdk upgrade

The version bump of @xmtp/node-sdk to ^0.0.28 aligns with the PR's authorization changes. However, since this is a pre-1.0 version, we should be extra cautious about potential breaking changes.

Let's verify the changelog and any potential breaking changes:

Consider adding a test plan in the PR description to verify that the new authorization functionality works as expected with this version of the SDK.

✅ Verification successful

Version bump is safe to proceed

The code analysis shows that @xmtp/node-sdk is primarily used for authorization-related functionality in the validation process. The upgrade aligns with the PR's focus on authorization changes, and the codebase has comprehensive tests covering both V2 and V3 authentication flows. The main usage is through Client.isInstallationAuthorized and Client.isAddressAuthorized methods, which are well-tested in the validation test suite.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the changelog and git history for breaking changes

# Check the latest release notes
gh release view v0.0.28 --repo xmtp/xmtp-js

# Look for any breaking change indicators in recent commits
git log --grep="BREAKING CHANGE" --grep="breaking change" -i HEAD~50..HEAD

Length of output: 63


Script:

#!/bin/bash
# Let's verify the actual usage of @xmtp/node-sdk in the codebase
# to understand the impact of the upgrade

# Find files that import or require @xmtp/node-sdk
rg "@xmtp/node-sdk" -l

# Look for specific usage patterns
ast-grep --pattern 'import $_ from "@xmtp/node-sdk"'

# Check package-lock.json for any dependency conflicts
fd package-lock.json --exec cat {} | jq '.dependencies."@xmtp/node-sdk"' 2>/dev/null

# Check if there are any tests that might be affected
rg "@xmtp/node-sdk" --type test

Length of output: 891


Script:

#!/bin/bash
# Let's check the specific usage in validation.ts and its tests
# to understand the impact on authorization-related code

# Check the validation.ts file where the SDK is used
cat packages/frames-validator/src/validation.ts

# Check the corresponding test file
cat packages/frames-validator/src/validation.test.ts

# Check if there are any recent changes to these files
git diff HEAD~10 HEAD -- packages/frames-validator/src/validation.ts packages/frames-validator/src/validation.test.ts

Length of output: 11273

sdks/node-sdk/src/Client.ts (1)

175-187: Verify error handling and usage patterns

The new authorization methods look well-integrated, but we should verify:

  1. Error handling propagation from the inner client
  2. Usage patterns in the frames-validator
✅ Verification successful

Based on the results, I can now generate the final response since I have sufficient information about both error handling patterns and usage in the frames-validator:

Error handling and usage patterns are properly implemented

The implementation is correct and well-integrated:

  • Error handling follows the same pattern as other methods in the Client class - direct propagation from inner client, which is consistent with simple getter methods like accountAddress(), inboxId(), etc.
  • The frames-validator properly uses these methods with explicit error handling:
    • Throws specific error messages when authorization checks fail
    • Used in critical validation flows for both installation and wallet address verification
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check error handling patterns in other similar methods
rg -A 2 "this\.#innerClient\." sdks/node-sdk/src/Client.ts

# Check usage patterns in frames-validator
rg -A 5 "isAddressAuthorized|isInstallationAuthorized" packages/frames-validator/src/

Length of output: 3146

packages/frames-validator/src/validation.ts (1)

51-57: Verify correctness of isInstallationAuthorized parameters

Ensure that the parameters inboxId and installationId are in the correct order as expected by the Client.isInstallationAuthorized method. Passing them in the wrong order could lead to incorrect authorization checks.

Run the following script to verify the parameter order in the method definition:

✅ Verification successful

Parameters are correctly ordered in isInstallationAuthorized method

The verification shows that the parameters in the method call match the method definition in sdks/node-sdk/src/Client.ts:

  • Method call: Client.isInstallationAuthorized(inboxId, installationId)
  • Method definition: async isInstallationAuthorized(inboxId: string, installationId: Uint8Array)

The parameters are being passed in the correct order as expected by the method signature.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the parameter order of `isInstallationAuthorized`

# Search for the method definition
rg -A 5 'static async isInstallationAuthorized\('

Length of output: 51


Script:

#!/bin/bash
# Let's try a broader search for the Client class and its methods
ast-grep --pattern 'class Client {
  $$$
  isInstallationAuthorized($$$) {
    $$$
  }
  $$$
}'

# Also search for any file containing Client class
fd -e ts -e js Client

Length of output: 830


Script:

#!/bin/bash
# Let's also try searching for the method name without the static keyword
rg "isInstallationAuthorized" -A 5

Length of output: 1107

packages/frames-validator/src/validation.ts Show resolved Hide resolved
packages/frames-validator/src/validation.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
packages/frames-validator/src/validation.ts (4)

52-58: Enhance installation authorization error handling

While the authorization check is good, consider adding more context to the error message to aid debugging.

-      throw new Error("Installation not a member of association state");
+      throw new Error(`Installation ${installationId} not authorized for inbox ${inboxId}`);

60-63: Add error context for wallet authorization

Similarly, enhance the error message for wallet authorization failures.

-      throw new Error("Unable to associate wallet address with inbox");
+      throw new Error(`Wallet address ${walletAddress} not authorized for inbox ${inboxId}`);

Line range hint 69-73: Enhance signature verification error handling

The signature verification is correct, but consider adding more context to aid debugging.

     if (!valid) {
-      throw new Error("Invalid signature");
+      throw new Error(`Invalid installation signature for installation ${installationId}`);
     }

52-63: Add debug logging for security operations

Consider adding debug logging for security-relevant operations to aid in troubleshooting and audit trails.

+    console.debug(`Verifying installation ${installationId} for inbox ${inboxId}`);
     const authorized = await client.isInstallationAuthorized(
       inboxId,
       installationId,
     );
     if (!authorized) {
+      console.warn(`Installation authorization failed for ${installationId}`);
       throw new Error("Installation not a member of association state");
     }

+    console.debug(`Verifying wallet ${walletAddress} for inbox ${inboxId}`);
     const isMember = await client.isAddressAuthorized(inboxId, walletAddress);

     if (!isMember) {
+      console.warn(`Wallet authorization failed for ${walletAddress}`);
       throw new Error("Unable to associate wallet address with inbox");
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e530d1b and c33fa9e.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • packages/frames-client/package.json (1 hunks)
  • packages/frames-validator/src/validation.ts (3 hunks)
  • sdks/node-sdk/src/Client.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdks/node-sdk/src/Client.ts
🔇 Additional comments (2)
packages/frames-client/package.json (2)

73-73: Verify the version update and document the changes

The update to @xmtp/node-sdk@^0.0.28 reintroduces authorization methods, but being a pre-1.0 version, we should ensure stability.

Let's verify the version and changelog:

Consider adding a comment in the PR description documenting why this specific version is required, particularly noting the reintroduction of isAddressAuthorized and isInstallationAuthorized methods.

✅ Verification successful

Version update to @xmtp/node-sdk@^0.0.28 is appropriate and recent

The version update is verified to be the latest available version (0.0.28), published just recently on November 26, 2024. This aligns perfectly with the PR's authorization-related changes, and the version bump from 0.0.27 (published on November 23, 2024) represents the most current stable release.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check latest version and changelog for @xmtp/node-sdk

# Check latest version on npm
echo "Latest version on npm:"
npm view @xmtp/node-sdk version

# Check for recent versions and their publish dates
echo -e "\nRecent versions and publish dates:"
npm view @xmtp/node-sdk time --json | jq 'with_entries(select(.key|test("^0.0.2[7-9]$")))'

# Check for any security advisories
echo -e "\nChecking for security advisories:"
npm audit @xmtp/node-sdk --json || true

Length of output: 1069


73-73: Verify peer dependency compatibility

The package has a peer dependency on @xmtp/xmtp-js (>9.3.1). Let's ensure the new node-sdk version is compatible.

✅ Verification successful

Let me try another approach to verify the dependencies.


Peer dependency compatibility verified

The package.json shows that @xmtp/node-sdk is in devDependencies, not in dependencies, and the project already has @xmtp/xmtp-js@^12.0.0 as a devDependency which satisfies the peer dependency requirement of >9.3.1. Since both packages are in devDependencies, there's no compatibility concern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check compatibility between node-sdk and xmtp-js

# Get node-sdk's dependencies
echo "Checking @xmtp/[email protected] dependencies:"
npm view @xmtp/[email protected] peerDependencies --json || true

# Check if current xmtp-js version (^12.0.0) satisfies any constraints
echo -e "\nCurrent xmtp-js version in package.json:"
jq '.dependencies["@xmtp/xmtp-js"]' package.json

Length of output: 308


Script:

#!/bin/bash
# Let's check the actual package.json content first
echo "Content of packages/frames-client/package.json:"
cat packages/frames-client/package.json

echo -e "\nChecking @xmtp/[email protected] all dependencies:"
npm view @xmtp/[email protected] dependencies peerDependencies --json || true

Length of output: 2901

packages/frames-validator/src/validation.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
packages/frames-validator/src/validation.ts (2)

52-61: Consider memoizing the client instance

Creating a new wallet and client instance for each validation call could impact performance. Consider memoizing these instances or moving them to a higher scope.

Example implementation:

+let cachedClient: Client | null = null;
+
+async function getOrCreateClient(): Promise<Client> {
+  if (cachedClient) return cachedClient;
+  const randomWallet = Wallet.createRandom();
+  const encryptionKey = getRandomValues(new Uint8Array(32));
+  cachedClient = await Client.create(
+    {
+      getAddress: () => randomWallet.address,
+      signMessage: async (message: string) =>
+        getBytes(await randomWallet.signMessage(message)),
+    },
+    encryptionKey,
+  );
+  return cachedClient;
+}

Line range hint 18-22: Document behavioral changes

While the function signature remains the same, the internal validation behavior has changed significantly. This needs to be documented to help users understand the new authorization requirements.

Consider:

  1. Adding JSDoc comments explaining the new validation process
  2. Creating a migration guide for existing users
  3. Documenting the new error scenarios

Example JSDoc:

/**
 * Validates a frames post request using XMTP authorization.
 * @param data - The frames request data
 * @param env - Optional XMTP environment
 * @throws {Error} When installation or wallet address authorization fails
 * @throws {Error} When required fields are missing or invalid
 * @returns {Promise<XmtpValidationResponse>}
 */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3d4fa7e and 2cf9051.

📒 Files selected for processing (1)
  • packages/frames-validator/src/validation.ts (3 hunks)
🔇 Additional comments (2)
packages/frames-validator/src/validation.ts (2)

33-35: ⚠️ Potential issue

Validate extracted fields before use

The inboxId and installationId are used in authorization checks without validation. These fields should be validated before use to prevent runtime errors.

Add validation checks:

    installationId,
    installationSignature,
    inboxId,
  } = deserializeProtoMessage(messageBytes);

+ if (!inboxId || !installationId) {
+   throw new Error("Missing required fields: inboxId or installationId");
+ }

64-75: ⚠️ Potential issue

Enhance error handling for authorization checks

The authorization checks should handle potential errors from the client methods and provide more informative error messages.

Implement proper error handling:

-    const authorized = await client.isInstallationAuthorized(
-      inboxId,
-      installationId,
-    );
-    if (!authorized) {
-      throw new Error("Installation not a member of association state");
-    }
-
-    const isMember = await client.isAddressAuthorized(inboxId, walletAddress);
-
-    if (!isMember) {
-      throw new Error("Unable to associate wallet address with inbox");
-    }
+    try {
+      const authorized = await client.isInstallationAuthorized(
+        inboxId,
+        installationId,
+      );
+      if (!authorized) {
+        throw new Error("Installation not a member of association state");
+      }
+
+      const isMember = await client.isAddressAuthorized(inboxId, walletAddress);
+      if (!isMember) {
+        throw new Error("Unable to associate wallet address with inbox");
+      }
+    } catch (error) {
+      throw new Error(`Authorization check failed: ${error.message}`);
+    }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
packages/frames-validator/src/validation.ts (3)

16-16: Remove commented out code

This commented line appears to be unused development code. If it's not needed, it should be removed to maintain code cleanliness.

-// let seed = Mnemonic.fromEntropy(randomBytes(16), "", LangEn.wordlist()).computeSeed();

70-77: Consider implementing typed errors

To improve error handling for consumers, consider implementing specific error types:

// Add at the top of the file
export class ValidationError extends Error {
  constructor(message: string, public code: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

// Then update the error throws
if (!authorized) {
  throw new ValidationError(
    "Installation not a member of association state",
    "UNAUTHORIZED_INSTALLATION"
  );
}

if (!isMember) {
  throw new ValidationError(
    "Unable to associate wallet address with inbox",
    "UNAUTHORIZED_ADDRESS"
  );
}

Line range hint 20-24: Document behavior changes

While the function signature remains the same, the internal validation behavior has changed significantly. Consider:

  1. Adding JSDoc comments to document the new validation process
  2. Updating the README with migration notes
  3. Bumping the major version number due to the behavioral changes
/**
 * Validates a frames post request using XMTP authorization.
 * @param data - The frames request data to validate
 * @param env - Optional XMTP environment configuration
 * @throws {ValidationError} When installation or address authorization fails
 * @throws {Error} When signature or data validation fails
 * @since 2.0.0 Now performs XMTP authorization checks
 */
sdks/node-sdk/src/Client.ts (1)

458-474: Consider adding input validation for network options

While the host resolution logic is correct, consider adding validation for the network options to provide better error messages when invalid options are provided.

Example implementation:

private static resolveHost(networkOptions: NetworkOptions): string {
  if (networkOptions.apiUrl) {
    return networkOptions.apiUrl;
  }
  
  const env = networkOptions.env || "dev";
  if (!(env in ApiUrls)) {
    throw new Error(`Invalid environment: ${env}`);
  }
  
  return ApiUrls[env];
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2cf9051 and 0eae3f6.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • packages/frames-validator/src/validation.ts (4 hunks)
  • sdks/node-sdk/package.json (1 hunks)
  • sdks/node-sdk/src/Client.ts (3 hunks)
🔇 Additional comments (7)
sdks/node-sdk/package.json (1)

56-56: LGTM! Verify version compatibility.

The dependency update to @xmtp/node-bindings@^0.0.26 looks good and aligns with the broader changes mentioned in the PR summary.

Let's verify version compatibility across the ecosystem:

✅ Verification successful

Version compatibility check completed - All versions are aligned

The version check shows:

  • @xmtp/node-bindings is only used in node-sdk at version ^0.0.26
  • @xmtp/node-sdk is consistently used at version ^0.0.28 across dependent packages (frames-validator and frames-client)

This indicates proper version alignment across the ecosystem, with no conflicting or outdated versions found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for version consistency across related packages

# Find all package.json files
echo "Checking @xmtp/node-bindings versions across the repository:"
rg -A 1 '"@xmtp/node-bindings":\s*"[^"]*"' --type json

echo -e "\nChecking @xmtp/node-sdk versions across the repository:"
rg -A 1 '"@xmtp/node-sdk":\s*"[^"]*"' --type json

Length of output: 768

packages/frames-validator/src/validation.ts (3)

1-5: LGTM: Import changes align with new authorization requirements

The new imports support the enhanced authorization logic using the XMTP client and wallet functionality.


66-77: LGTM: Robust authorization checks implemented

The dual authorization checks provide comprehensive validation:

  1. Installation authorization ensures valid installation
  2. Address authorization verifies wallet association

35-38: Verify validity of inboxId

Ensure that inboxId extracted from the deserialized message is valid before using it in authorization checks.

+  if (!inboxId) {
+    throw new Error("Invalid inbox ID");
+  }
sdks/node-sdk/src/Client.ts (3)

18-19: LGTM: Import statements are correctly added

The new bindings are properly imported and appropriately aliased to avoid naming conflicts with the class methods.


274-286: LGTM: Instance methods are well-implemented

The new authorization methods are properly implemented:

  • Clear method signatures with appropriate types
  • Consistent with the class's pattern of delegating to the inner client
  • Follow async/await pattern correctly

274-286: Verify the integration with frames-validator

Let's verify how these authorization methods are being used in the frames-validator package.

Also applies to: 458-474

✅ Verification successful

Integration with frames-validator is properly implemented

The authorization methods are correctly used in the frames-validator package for validating frame posts:

  • isInstallationAuthorized is used to verify installation membership
  • isAddressAuthorized is used to verify wallet address association with the inbox

The integration is working as expected with proper error handling and validation flow.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how the new authorization methods are used in the frames-validator package

# Search for usage of authorization methods
echo "Searching for usage of authorization methods..."
rg -A 5 "isAddressAuthorized|isInstallationAuthorized" --type typescript

# Check if the frames-validator package imports the Client class
echo "Checking imports in frames-validator..."
rg -A 5 "from.*@xmtp/node-sdk.*Client" --type typescript

Length of output: 397


Script:

#!/bin/bash
# Let's try searching without file type restriction since typescript extension might not be recognized
echo "Searching for usage of authorization methods..."
rg "isAddressAuthorized|isInstallationAuthorized"

echo -e "\nChecking imports from node-sdk..."
rg "from.*@xmtp/node-sdk.*Client"

echo -e "\nLet's also check for any .ts files in the repository..."
fd "\.ts$"

echo -e "\nChecking package.json in frames-validator..."
fd "package.json" --exec grep -l "@xmtp/node-sdk" {}

Length of output: 12177


Script:

#!/bin/bash
# Let's check the specific implementation in frames-validator
echo "Checking the implementation in frames-validator/validation.ts..."
cat packages/frames-validator/src/validation.ts

echo -e "\nChecking the dependency version in frames-validator's package.json..."
cat packages/frames-validator/package.json

Length of output: 6661

packages/frames-validator/src/validation.ts Outdated Show resolved Hide resolved
sdks/node-sdk/src/Client.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
packages/frames-validator/src/validation.ts (1)

1-1: Remove unused imports

The imports getRandomValues and randomBytes from 'crypto' are not used in the current implementation.

-import { getRandomValues, randomBytes } from "crypto";
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0eae3f6 and 5714605.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • packages/frames-client/package.json (1 hunks)
  • packages/frames-validator/package.json (1 hunks)
  • packages/frames-validator/src/validation.ts (3 hunks)
  • sdks/node-sdk/package.json (2 hunks)
  • sdks/node-sdk/src/Client.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/frames-client/package.json
  • packages/frames-validator/package.json
  • sdks/node-sdk/package.json
  • sdks/node-sdk/src/Client.ts
🔇 Additional comments (3)
packages/frames-validator/src/validation.ts (3)

33-36: ⚠️ Potential issue

Validate inboxId before use

The inboxId extracted from the deserialized message should be validated before use in authorization checks.

    installationId,
    installationSignature,
    inboxId,
  } = deserializeProtoMessage(messageBytes);
+  if (!inboxId) {
+    throw new Error("Invalid inbox ID");
+  }

53-69: Verify authorization changes impact

Let's verify the usage of these new authorization methods across the codebase.

✅ Verification successful

Based on the search results, I can now generate the final response:

Authorization changes are properly encapsulated

The authorization methods isInstallationAuthorized and isAddressAuthorized are only used in two locations:

  • Core implementation in sdks/node-sdk/src/Client.ts
  • Current validation logic in packages/frames-validator/src/validation.ts

The validateFramesPost function is only called from:

  • Test files (validation.test.ts)
  • Validator implementation (validator.ts)

These changes are well-contained within the validation flow and don't require modifications elsewhere in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of authorization methods and potential impacts

# Check for direct usage of isInstallationAuthorized and isAddressAuthorized
echo "Checking for direct usage of authorization methods:"
rg "isInstallationAuthorized|isAddressAuthorized" --type ts -A 2

# Check for validateFramesPost callers that might be affected
echo "Checking for validateFramesPost callers:"
rg "validateFramesPost\(" --type ts -A 2

Length of output: 3640


53-60: ⚠️ Potential issue

Add error handling for isInstallationAuthorized

The async call to isInstallationAuthorized should be wrapped in a try-catch block to handle potential errors gracefully.

-    const authorized = await Client.isInstallationAuthorized(
-      { env },
-      inboxId,
-      installationId,
-    );
-    if (!authorized) {
-      throw new Error("Installation not a member of association state");
-    }
+    try {
+      const authorized = await Client.isInstallationAuthorized(
+        { env },
+        inboxId,
+        installationId,
+      );
+      if (!authorized) {
+        throw new Error("Installation not a member of association state");
+      }
+    } catch (error) {
+      throw new Error(`Installation authorization check failed: ${error.message}`);
+    }

Likely invalid or redundant comment.

packages/frames-validator/src/validation.ts Outdated Show resolved Hide resolved
@codabrink codabrink changed the title Authorize with lib Static Frames Validation Security Fix Nov 28, 2024
packages/frames-validator/package.json Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
packages/frames-validator/src/validation.ts (1)

51-67: Consider combining authorization checks for better performance

The current implementation makes two separate async calls for authorization. Consider combining these checks in the Client class for better performance.

Suggestion for the Client class:

// In Client class
static async validateFrameAuthorization(
  env: XmtpEnv,
  inboxId: string,
  installationId: string,
  walletAddress: string
): Promise<void> {
  const [isInstallationAuth, isAddressAuth] = await Promise.all([
    this.isInstallationAuthorized(env, inboxId, installationId),
    this.isAddressAuthorized(env, inboxId, walletAddress)
  ]);
  
  if (!isInstallationAuth || !isAddressAuth) {
    throw new Error("Authorization failed");
  }
}

Then update the validation code to use this combined check:

-    const authorized = await Client.isInstallationAuthorized(
-      { env },
-      inboxId,
-      installationId,
-    );
-    if (!authorized) {
-      throw new Error("Installation not a member of association state");
-    }
-
-    const isMember = await Client.isAddressAuthorized(
-      { env },
-      inboxId,
-      walletAddress,
-    );
-
-    if (!isMember) {
-      throw new Error("Unable to associate wallet address with inbox");
-    }
+    try {
+      await Client.validateFrameAuthorization(
+        { env },
+        inboxId,
+        installationId,
+        walletAddress
+      );
+    } catch (error) {
+      console.error("Frame authorization failed:", error);
+      throw new Error("Authorization failed");
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5714605 and 7fbb4d1.

📒 Files selected for processing (1)
  • packages/frames-validator/src/validation.ts (3 hunks)
🔇 Additional comments (3)
packages/frames-validator/src/validation.ts (3)

51-58: ⚠️ Potential issue

Add error handling for isInstallationAuthorized

The async call to isInstallationAuthorized should be wrapped in a try-catch block to prevent unhandled rejections from exposing internal details.

Apply this change:

-    const authorized = await Client.isInstallationAuthorized(
-      { env },
-      inboxId,
-      installationId,
-    );
-    if (!authorized) {
-      throw new Error("Installation not a member of association state");
-    }
+    try {
+      const authorized = await Client.isInstallationAuthorized(
+        { env },
+        inboxId,
+        installationId,
+      );
+      if (!authorized) {
+        throw new Error("Authorization failed");
+      }
+    } catch (error) {
+      // Log the actual error internally but throw a generic message
+      console.error("Installation authorization failed:", error);
+      throw new Error("Authorization failed");
+    }

31-34: ⚠️ Potential issue

Validate inboxId before authorization checks

The inboxId from the deserialized message is used in authorization checks without validation. A null or undefined inboxId could lead to security vulnerabilities.

Add validation before the authorization checks:

    inboxId,
  } = deserializeProtoMessage(messageBytes);

+ if (!inboxId) {
+   throw new Error("Authorization failed");  // Generic error for security
+ }

Likely invalid or redundant comment.


60-67: ⚠️ Potential issue

Use generic error messages for security

Current error messages like "Unable to associate wallet address with inbox" could leak internal implementation details. Use generic error messages for security failures.

Apply this change:

-    const isMember = await Client.isAddressAuthorized(
-      { env },
-      inboxId,
-      walletAddress,
-    );
-
-    if (!isMember) {
-      throw new Error("Unable to associate wallet address with inbox");
-    }
+    try {
+      const isMember = await Client.isAddressAuthorized(
+        { env },
+        inboxId,
+        walletAddress,
+      );
+      if (!isMember) {
+        throw new Error("Authorization failed");
+      }
+    } catch (error) {
+      // Log the actual error internally but throw a generic message
+      console.error("Wallet authorization failed:", error);
+      throw new Error("Authorization failed");
+    }

Likely invalid or redundant comment.

sdks/node-sdk/package.json Outdated Show resolved Hide resolved
@rygine rygine force-pushed the coda/frames-check branch from db3c086 to ae0a28c Compare December 2, 2024 23:16
@rygine rygine requested a review from a team as a code owner December 2, 2024 23:18
@rygine rygine merged commit 6edc8a5 into main Dec 2, 2024
24 checks passed
@rygine rygine deleted the coda/frames-check branch December 2, 2024 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants