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

Email spam security #641

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Conversation

chiragchhatrala
Copy link
Collaborator

@chiragchhatrala chiragchhatrala commented Dec 10, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced registration process with hCaptcha integration for improved security.
    • Introduced loading state in the integration modal for better user experience during save operations.
  • Bug Fixes

    • Improved validation feedback for registration attempts, including handling of hCaptcha responses.
  • Documentation

    • Added tests for email integration functionalities, covering various user tiers and their limitations.
  • Chores

    • Updated user model to manage user metadata effectively.
    • Added migration for new meta field in the users table.

- Updated the `getValidationRules` method in various integration handlers (Discord, Email, Google Sheets, Slack, Webhook, Zapier) to accept an optional `Form` parameter, allowing for context-aware validation.
- Enhanced the `EmailIntegration` handler to enforce restrictions based on user plans, ensuring free users can only create one email integration per form and can only send to a single email address.
- Added a new test suite for `EmailIntegration` to validate the new restrictions and ensure proper functionality for both free and pro users.
- Introduced loading state management in the `IntegrationModal` component to improve user experience during save operations.

These changes improve the flexibility and user experience of form integrations, particularly for email handling.
Copy link
Contributor

coderabbitai bot commented Dec 10, 2024

Walkthrough

The changes in this pull request involve enhancements to the user registration process and integration handling within the application. Key updates include the addition of rate limiting middleware in the RegisterController, improved validation logic with the introduction of hCaptcha, and the inclusion of a meta field for user tracking. Additionally, several integration handlers have been modified to accept a Form parameter for context-specific validation. The User model has been updated to manage user metadata, and a new migration has been added to accommodate these changes in the database schema.

Changes

File Change Summary
api/app/Http/Controllers/Auth/RegisterController.php Added rate limiting middleware, refactored validation rules, introduced h-captcha validation, and updated user creation to include a meta field.
api/app/Http/Requests/Integration/FormIntegrationsRequest.php Added a private property for Form, updated validation rules loading logic, and refined error handling.
api/app/Integrations/Handlers/AbstractIntegrationHandler.php Updated getValidationRules method to accept an optional Form parameter.
api/app/Integrations/Handlers/DiscordIntegration.php Modified getValidationRules to accept a Form parameter and updated shouldRun logic.
api/app/Integrations/Handlers/EmailIntegration.php Updated getValidationRules to accept a Form parameter, enhanced validation for free plan users.
api/app/Integrations/Handlers/GoogleSheetsIntegration.php Changed getValidationRules to accept a Form parameter and improved logging in handle method.
api/app/Integrations/Handlers/SlackIntegration.php Updated getValidationRules to accept a Form parameter and modified shouldRun logic.
api/app/Integrations/Handlers/WebhookIntegration.php Modified getValidationRules to accept a Form parameter and added a new import for Form.
api/app/Integrations/Handlers/ZapierIntegration.php Updated getValidationRules to accept a Form parameter.
api/app/Models/User.php Added meta field to $fillable, $hidden, and casts arrays in the User model.
api/database/migrations/2024_12_10_094605_add_meta_to_users_table.php Created a migration to add a meta column to the users table.
api/tests/Feature/Integrations/Email/EmailIntegrationTest.php Introduced tests for email integration functionalities, validating user capabilities based on their plan.
api/tests/Feature/RegisterTest.php Enhanced registration tests to include hCaptcha validation and updated assertions for user metadata.
api/tests/Feature/UserManagementTest.php Updated user registration tests to integrate hCaptcha validation and maintain existing assertions.
client/components/open/integrations/components/IntegrationModal.vue Added loading state to save functionality to enhance user experience during asynchronous operations.
client/components/pages/auth/components/RegisterForm.vue Integrated hCaptcha component into the registration form, including logic for handling responses.

Possibly related PRs

  • Google Sheet - OAuth "client" powered integrations #415: Modifications in FormIntegrationsRequest.php include additional validation rules, potentially related to the enhancements in RegisterController.
  • confirmation email integration fixes #434: The confirmation email integration fixes may relate to the registration process by ensuring that users receive appropriate notifications upon registration.
  • Custom SMTP Settings #561: The Custom SMTP Settings may impact how registration emails are sent, thus relating to the registration process.
  • apply first submission modal changes #584: The changes in the PublicFormController and the introduction of the FirstSubmissionModal may enhance user experience during registration by providing feedback on submissions.
  • Apply Mentions everywhere #595: The implementation of mentions could enhance user interactions during registration, especially if users are tagged or notified in the process.
  • Form Editor v2.5 #599: The updates in the Form Editor may improve the overall user experience, including registration forms, by refining how forms are created and managed.

Suggested reviewers

  • JhumanJ

🐇 In the land of code, where changes bloom,
A new hCaptcha guards registration's room.
With limits set, and rules refined,
We track the users, their data aligned.
So hop along, dear devs, take flight,
For with these updates, our future is bright! 🌟


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 generate docstrings to generate docstrings for this PR. (Experiment)
  • @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
Contributor

@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: 10

🧹 Outside diff range and nitpick comments (16)
api/app/Integrations/Handlers/WebhookIntegration.php (2)

9-9: Consider utilizing the Form parameter for context-specific validation

The Form parameter is currently unused. Consider leveraging it to implement form-specific validation rules, such as:

  • Different URL patterns for different form types
  • Specific domain restrictions based on form context
  • Custom validation rules based on form settings

Example implementation:

     public static function getValidationRules(?Form $form): array
     {
+        $baseRules = [
+            'webhook_url' => [
+                'required',
+                'url',
+            ]
+        ];
+
+        if ($form && $form->requires_strict_validation) {
+            $baseRules['webhook_url'][] = 'regex:/^https:\/\/api\.(trusted-domain)\.(com|org)\/webhook/';
+        }
+
+        return $baseRules;
     }

Consider implementing rate limiting for webhook endpoints

Based on the codebase analysis, while there's basic URL validation, the webhook endpoints could benefit from additional security measures. I notice that:

  1. The application already implements rate limiting in various places (e.g., API, authentication) but not specifically for webhook endpoints
  2. There's no existing domain allowlist/blocklist configuration for webhooks
  3. The webhook integration is part of a larger forms system with existing security considerations

Recommended implementation:

public static function getValidationRules(?Form $form): array
{
    return [
        'webhook_url' => [
            'required',
            'url',
            'not_regex:/^https?:\/\/(\d{1,3}\.){3}\d{1,3}/', // Prevent IP-based URLs
            function ($attribute, $value, $fail) {
                $domain = parse_url($value, PHP_URL_HOST);
                if (filter_var($domain, FILTER_VALIDATE_IP)) {
                    $fail('IP-based webhook URLs are not allowed.');
                }
            }
        ]
    ];
}

Additionally:

  • Add the throttle:10,1 middleware to webhook routes to prevent abuse
  • Consider implementing a webhook signing mechanism for secure payload delivery
🔗 Analysis chain

Line range hint 9-14: Consider enhancing webhook URL validation for better security

While the basic URL validation is present, given this PR's focus on spam security, consider implementing additional security measures:

  1. Domain allowlist/blocklist validation
  2. Rate limiting for webhook endpoints
  3. Webhook URL pattern restrictions

Here's a suggested implementation with enhanced security:

     public static function getValidationRules(?Form $form): array
     {
         return [
-            'webhook_url' => 'required|url'
+            'webhook_url' => [
+                'required',
+                'url',
+                'not_regex:/^https?:\/\/(\d{1,3}\.){3}\d{1,3}/', // Prevent IP-based URLs
+                function ($attribute, $value, $fail) {
+                    $blockedDomains = config('security.blocked_domains', []);
+                    $domain = parse_url($value, PHP_URL_HOST);
+                    if (in_array($domain, $blockedDomains)) {
+                        $fail('This webhook domain is not allowed.');
+                    }
+                }
+            ]
         ];
     }

Let's verify if there are any existing security measures or configurations for webhook URLs:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for existing webhook security configurations
rg -l "webhook.*security|blocked.*domains|allowed.*domains" config/
# Check for any rate limiting on webhook endpoints
rg -l "throttle.*webhook|rate.*limit.*webhook" app/Http/Middleware/

Length of output: 248


Script:

#!/bin/bash
# Let's first locate the config and middleware directories
fd -t d "config|middleware" -i

# Check for any existing webhook-related configurations
rg -i "webhook" --type php

# Check for any rate limiting or throttling implementations
rg -i "throttle|rate.?limit" --type php

# Look for any security-related configurations or middleware
rg -i "security|blocked.?domains|allowed.?domains" --type php

Length of output: 16425

api/tests/Feature/UserManagementTest.php (1)

68-68: Optimize HCaptcha token usage in error scenarios

The tests should verify that HCaptcha validation is not performed when the registration fails due to other validations (expired/invalid invite token). This prevents unnecessary API calls and potential costs in production.

Consider validating the invite token before performing HCaptcha verification:

+it('skips hCaptcha validation for expired invite token', function () {
+    // Setup expired token...
+    $response = $this->postJson('/register', [...]);
+    $response->assertStatus(400);
+    Http::assertNothingSent();
+});

Also applies to: 98-98, 115-115

api/app/Http/Requests/Integration/FormIntegrationsRequest.php (1)

22-22: Use $request->route('id') instead of global request() helper

For consistency and better testability, use the $request object provided in the constructor rather than the global request() helper.

Apply this diff to make the change:

- $this->form = Form::findOrFail(request()->route('id'));
+ $this->form = Form::findOrFail($request->route('id'));
api/app/Integrations/Handlers/EmailIntegration.php (2)

Line range hint 18-54: Potential null reference on $form in getValidationRules

The $form parameter is nullable, but the method uses $form without checking if it is null. This could lead to a fatal error if $form is null. Please ensure $form is not null before accessing its properties.

Consider enforcing a non-null $form parameter:

- public static function getValidationRules(?Form $form): array
+ public static function getValidationRules(Form $form): array

Alternatively, add a null check at the beginning of the method:

if (is_null($form)) {
    throw new \InvalidArgumentException('Form cannot be null.');
}

36-40: Enhance email address parsing in closure validation

The current logic may not accurately parse email addresses containing commas or newlines. Consider using a more reliable method to split and validate multiple email addresses.

Example using regular expressions:

$emails = preg_split('/[\s,]+/', trim($value));
if (count($emails) > 1) {
    $fail('You can only send to a single email address on the free plan. Please upgrade to the Pro plan to create a new integration.');
}
api/database/migrations/2024_12_10_094605_add_meta_to_users_table.php (1)

14-14: Consider indexing if querying meta field

If you plan to query the meta JSON column frequently, consider adding appropriate indexes to optimize performance.

Example:

$table->json('meta')->default('{}')->index();

Be aware that indexing JSON fields may have limitations depending on the database.

api/app/Integrations/Handlers/ZapierIntegration.php (1)

20-20: Consider removing nullable type if $form is required

If all integration handlers require a Form instance for validation, consider removing the nullable type to enforce this requirement.

- public static function getValidationRules(?Form $form): array
+ public static function getValidationRules(Form $form): array
api/tests/Feature/RegisterTest.php (1)

9-11: Consider using dynamic mock tokens for hCaptcha tests

While mocking hCaptcha responses is correct, using a hardcoded 'test-token' might make tests brittle. Consider generating dynamic tokens or extracting to a constant.

+ const MOCK_HCAPTCHA_TOKEN = 'test-token';
  Http::fake([
      ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => true])
  ]);
  // ...
-     'h-captcha-response' => 'test-token', // Mock token for testing
+     'h-captcha-response' => MOCK_HCAPTCHA_TOKEN,

Also applies to: 20-20

client/components/open/integrations/components/IntegrationModal.vue (1)

Line range hint 115-119: Enhance error handling specificity

The catch block could be more specific about different types of errors to help identify potential security issues.

- try {
-   alert.error(error.data.message)
- } catch (e) {
-   alert.error("An error occurred while saving the integration")
- }
+ if (error.response?.status === 429) {
+   alert.error("Too many attempts. Please try again later.")
+ } else if (error.response?.data?.message) {
+   alert.error(error.response.data.message)
+ } else {
+   alert.error("An error occurred while saving the integration")
+ }
api/app/Integrations/Handlers/AbstractIntegrationHandler.php (1)

97-97: Consider adding base spam prevention validation rules

Since this PR focuses on email spam security, consider adding some base validation rules in the abstract class that all integrations must include. For example:

  • Rate limiting validation
  • Spam keyword detection
  • Input length restrictions
  • Character set validation

Would you like me to provide an example implementation of these security measures?

api/app/Integrations/Handlers/DiscordIntegration.php (2)

Line range hint 13-22: Add spam prevention validation rules

While restricting to pro users helps, additional validation rules should be added to prevent spam:

  1. Rate limiting validation
  2. Input sanitization for webhook messages
  3. Maximum length restrictions for submission data

Add these validation rules:

 public static function getValidationRules(?Form $form): array
 {
     return [
         'discord_webhook_url' => 'required|url|starts_with:https://discord.com/api/webhooks',
+        'message' => ['required', 'string', 'max:2000', 'not_regex:/(?:http|https|www\\.)[^\\s]+/i'],
         'include_submission_data' => 'boolean',
         'link_open_form' => 'boolean',
         'link_edit_form' => 'boolean',
         'views_submissions_count' => 'boolean',
         'link_edit_submission' => 'boolean'
     ];
 }

Line range hint 31-33: Add rate limiting to shouldRun method

The shouldRun method should include rate limiting checks to prevent spam attacks.

Consider implementing rate limiting:

 protected function shouldRun(): bool
 {
-    return !is_null($this->getWebhookUrl()) && $this->form->is_pro && parent::shouldRun();
+    return !is_null($this->getWebhookUrl()) 
+        && $this->form->is_pro 
+        && !$this->isRateLimited()
+        && parent::shouldRun();
 }
+
+private function isRateLimited(): bool
+{
+    $key = "discord_integration:{$this->form->id}";
+    return !RateLimiter::attempt($key, 60, function() {}, 60);
+}
api/app/Integrations/Handlers/SlackIntegration.php (1)

Line range hint 13-22: Add spam prevention validation rules and consider code reuse

The validation rules are similar to DiscordIntegration. Consider:

  1. Creating a shared trait for webhook security
  2. Adding spam prevention rules
  3. Implementing rate limiting

Create a new trait for shared security features:

trait WebhookSecurityTrait
{
    protected function getBaseValidationRules(): array
    {
        return [
            'message' => ['required', 'string', 'max:2000', 'not_regex:/(?:http|https|www\\.)[^\\s]+/i'],
            'include_submission_data' => 'boolean',
            'link_open_form' => 'boolean',
            'link_edit_form' => 'boolean',
            'views_submissions_count' => 'boolean',
            'link_edit_submission' => 'boolean'
        ];
    }

    protected function isRateLimited(): bool
    {
        $key = "webhook_integration:{$this->form->id}";
        return !RateLimiter::attempt($key, 60, function() {}, 60);
    }
}

Then update the validation rules:

 public static function getValidationRules(?Form $form): array
 {
-    return [
+    return array_merge(self::getBaseValidationRules(), [
         'slack_webhook_url' => 'required|url|starts_with:https://hooks.slack.com/',
-        'include_submission_data' => 'boolean',
-        'link_open_form' => 'boolean',
-        'link_edit_form' => 'boolean',
-        'views_submissions_count' => 'boolean',
-        'link_edit_submission' => 'boolean'
-    ];
+    ]);
 }
client/components/pages/auth/components/RegisterForm.vue (2)

182-184: Consider using camelCase for form field names.

The form field h-captcha-response uses kebab-case, which is unusual in JavaScript. Consider using camelCase (hCaptchaResponse) for consistency, unless this specific naming is required by the hCaptcha API.


Line range hint 1-324: Consider implementing a dedicated hCaptcha wrapper component.

The current implementation directly integrates hCaptcha into the registration form. Consider creating a reusable wrapper component that:

  1. Encapsulates the hCaptcha logic
  2. Provides a cleaner interface for getting/setting the response
  3. Handles self-hosted conditions internally
  4. Can be reused in other forms that need spam protection

This would improve maintainability and make it easier to add spam protection to other forms in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ea4cd85 and 01f7fa2.

📒 Files selected for processing (16)
  • api/app/Http/Controllers/Auth/RegisterController.php (4 hunks)
  • api/app/Http/Requests/Integration/FormIntegrationsRequest.php (3 hunks)
  • api/app/Integrations/Handlers/AbstractIntegrationHandler.php (1 hunks)
  • api/app/Integrations/Handlers/DiscordIntegration.php (1 hunks)
  • api/app/Integrations/Handlers/EmailIntegration.php (2 hunks)
  • api/app/Integrations/Handlers/GoogleSheetsIntegration.php (2 hunks)
  • api/app/Integrations/Handlers/SlackIntegration.php (1 hunks)
  • api/app/Integrations/Handlers/WebhookIntegration.php (1 hunks)
  • api/app/Integrations/Handlers/ZapierIntegration.php (2 hunks)
  • api/app/Models/User.php (3 hunks)
  • api/database/migrations/2024_12_10_094605_add_meta_to_users_table.php (1 hunks)
  • api/tests/Feature/Integrations/Email/EmailIntegrationTest.php (1 hunks)
  • api/tests/Feature/RegisterTest.php (3 hunks)
  • api/tests/Feature/UserManagementTest.php (6 hunks)
  • client/components/open/integrations/components/IntegrationModal.vue (4 hunks)
  • client/components/pages/auth/components/RegisterForm.vue (6 hunks)
🔇 Additional comments (23)
api/app/Integrations/Handlers/WebhookIntegration.php (1)

5-5: LGTM!

The Form import is correctly added to support the new parameter type.

api/tests/Feature/UserManagementTest.php (1)

Line range hint 1-156: Security Review: Additional Test Coverage Needed

While the addition of HCaptcha is a good step towards preventing spam registrations, the test coverage should be expanded to include:

  1. Edge cases in HCaptcha validation
  2. Integration with other security measures (rate limiting, email verification)
  3. Proper error handling and token optimization
  4. Protection against automated registration attempts

These improvements will ensure the spam protection measures are robust and effective.

Let's verify the current security measures:

api/app/Http/Requests/Integration/FormIntegrationsRequest.php (2)

5-5: Importing Form model

The Form model is correctly imported to access form data within the request.


83-85: Passing $this->form enhances context-aware validation

By passing the form instance to getValidationRules, validation can be tailored based on the specific form, improving flexibility and accuracy.

api/app/Integrations/Handlers/EmailIntegration.php (4)

5-6: Importing Form and FormIntegration models

The inclusion of the Form and FormIntegration models is necessary for accessing form data and integration capabilities.


12-12: Importing ValidationException for proper exception handling

The addition of ValidationException allows for more precise exception handling and user feedback during validation.


43-43: Verify the correctness of the route parameter 'integrationid'

Ensure that 'integrationid' is the correct route parameter. If it should be 'integration_id', this condition may not work as intended.

Check your route definitions to confirm the parameter name.


47-51: Appropriate use of ValidationException

Throwing ValidationException provides clear feedback to the user when validation constraints are not met.

api/database/migrations/2024_12_10_094605_add_meta_to_users_table.php (1)

14-14: Verify JSON column default value compatibility

Setting a default value for a JSON column may not be supported in some database versions (e.g., older MySQL versions). Ensure your database supports default JSON values.

Consider checking the minimum database version requirements and testing the migration on the target databases.

api/app/Integrations/Handlers/ZapierIntegration.php (1)

20-20: Ensure consistency in method signatures across integration handlers

The getValidationRules method now accepts a ?Form $form parameter. Even if it's not used in this class, this change maintains consistency with other integration handlers.

api/tests/Feature/RegisterTest.php (2)

25-28: LGTM: Good security practice tracking registration IPs

Properly testing the storage of registration IP addresses is crucial for security auditing and abuse prevention.


75-87: LGTM: Comprehensive hCaptcha validation for production

Good security practice ensuring hCaptcha is required in production environment.

client/components/open/integrations/components/IntegrationModal.vue (1)

103-104: LGTM: Good protection against duplicate submissions

The loading state check helps prevent duplicate form submissions, which is good for preventing spam and ensuring data integrity.

api/app/Integrations/Handlers/GoogleSheetsIntegration.php (2)

Line range hint 44-49: Review logging of sensitive information

While logging form identifiers is useful for debugging, ensure these logs are properly secured and not exposed to unauthorized users.

✅ Verification successful

Let me gather more context about the logging patterns to better understand how form IDs and slugs are handled in logs.


Let me try a different approach to check the logging patterns.


Form identifiers in logs are appropriately used and secured

The logging of form IDs and slugs appears to be implemented securely and appropriately:

  • Form IDs are only logged in debug/error contexts for operational tracking
  • Logs are used in controlled environments (integration handlers)
  • Sensitive data like email recipients and workspace details are logged separately
  • Error logging includes necessary context for troubleshooting integration issues
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other instances of form ID/slug logging
rg "form_(id|slug)" --type php

Length of output: 5991


Script:

#!/bin/bash
# Check for logging patterns around form data
rg "Log::(debug|info|warning|error).+form" --type php -A 2

Length of output: 60


Script:

#!/bin/bash
# Check for logging patterns in the integration handlers
rg "Log::" api/app/Integrations/Handlers/ -A 2

Length of output: 1365


26-28: Consider adding validation rules for spam prevention

The empty validation rules might need to be enhanced with rate limiting or other spam prevention measures, especially since this PR focuses on email security.

api/app/Integrations/Handlers/SlackIntegration.php (1)

Line range hint 31-33: Add rate limiting to shouldRun method

Similar to DiscordIntegration, implement rate limiting to prevent spam.

The same rate limiting implementation suggested for DiscordIntegration should be applied here.

api/app/Http/Controllers/Auth/RegisterController.php (1)

73-75: Verify security implications of skipping hCaptcha in self-hosted mode

While the hCaptcha integration helps prevent automated registrations, skipping this validation in self-hosted mode could create a security vulnerability if the instance is exposed to the internet.

Consider:

  1. Making hCaptcha configurable in self-hosted mode
  2. Implementing alternative spam prevention for self-hosted instances
✅ Verification successful

Rate limiting provides adequate security for self-hosted registrations

The registration endpoint is already protected by multiple layers of rate limiting:

  • 5 attempts per minute
  • 30 attempts per hour
  • Global API rate limit of 100 requests per minute

These rate limiting measures provide robust protection against automated registrations and brute force attacks, even without hCaptcha in self-hosted mode. The security concern is adequately addressed through these existing controls.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any existing security measures for self-hosted mode
rg -A 5 "self_hosted.*registration" 

Length of output: 37


Script:

#!/bin/bash
# Let's check for any registration-related security measures in the codebase
rg -A 5 "registration|register" config/

Length of output: 89


Script:

#!/bin/bash
# Check for other validation rules in the RegisterController
ast-grep --pattern 'function validator($$$) { $$$ }'  api/app/Http/Controllers/Auth/RegisterController.php

Length of output: 107


Script:

#!/bin/bash
# Look for any rate limiting or throttling mechanisms
rg -A 5 "throttle|RateLimit" api/app/Http/

Length of output: 2642


Script:

#!/bin/bash
# Check if there are any other security middleware applied to registration
rg -A 5 "middleware.*register" api/routes/

Length of output: 44

client/components/pages/auth/components/RegisterForm.vue (6)

148-148: LGTM!

The VueHcaptcha component is properly registered.


213-216: LGTM!

The hCaptcha reference is properly initialized based on the self-hosted condition.


55-69: LGTM! Verify hCaptcha integration in self-hosted environments.

The hCaptcha integration is well-implemented with proper error handling and conditional rendering.

Let's verify the self-hosted configuration handling:

✅ Verification successful

hCaptcha integration correctly handles self-hosted environments

The verification confirms that the self_hosted feature flag is properly integrated across the application:

  • The RegisterForm correctly uses isSelfHosted computed property derived from useFeatureFlag('self_hosted')
  • The conditional rendering v-if="!isSelfHosted" ensures hCaptcha is only shown in cloud environments
  • This aligns with the broader application's self-hosted vs cloud deployment strategy, as evidenced by similar patterns in other components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for self-hosted feature flag configuration
rg -A 3 "self_hosted" --type js

Length of output: 3011


179-180: Verify the necessity of utm_data addition.

The addition of utm_data seems unrelated to the hCaptcha integration. Please verify if this change belongs in this PR.

✅ Verification successful

The utm_data addition is a valid part of user registration tracking

The codebase analysis shows that utm_data is a well-integrated feature for tracking user registration sources:

  • There's a dedicated migration 2024_09_18_091846_add_utm_data_to_users_table.php adding this field
  • The User model properly declares it in fillable and casts arrays
  • It's consistently used in both regular registration (RegisterController) and OAuth registration flows
  • The implementation in RegisterForm.vue matches the backend expectations

The change belongs in this PR as it's part of the registration form data structure, even though it's separate from the hCaptcha integration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check usage of utm_data in the codebase
rg -A 3 "utm_data"

Length of output: 3734


188-190: LGTM! Verify secure handling of the site key.

The computed property correctly retrieves the hCaptcha site key. Ensure the key is properly secured in the runtime configuration.

✅ Verification successful

LGTM! The hCaptcha site key is properly handled.

The implementation is secure as:

  • The site key is properly sourced from environment variables via NUXT_PUBLIC_H_CAPTCHA_SITE_KEY
  • It's safely exposed through the runtime config system in client/runtimeConfig.js
  • The key is only used with the official @hcaptcha/vue3-hcaptcha component
  • There are no hardcoded keys in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential hardcoded site keys
rg -A 3 "hCaptchaSiteKey.*=.*['\"]" --type js

Length of output: 49


Script:

#!/bin/bash
# Search for hCaptcha related configuration in nuxt config files
fd -e js -e ts "nuxt.config" --exec rg -l "hCaptcha"

# Search for runtime config setup
rg -i "runtimeconfig.*hcaptcha" --type=vue --type=js --type=ts -A 3

Length of output: 148


Script:

#!/bin/bash
# Search for hCaptcha configuration in config files
rg -i "hcaptcha" --type-add 'vue:*.vue' --type=vue --type=js --type=ts -A 3

# Look for environment variable usage related to hCaptcha
rg -i "HCAPTCHA" --type=env --type=js --type=ts -A 2

Length of output: 5148


143-144: Verify the necessity of the workspaces import.

The fetchAllWorkspaces import appears unrelated to the hCaptcha integration. Please verify if this import is necessary for this component.

✅ Verification successful

The fetchAllWorkspaces import is necessary and correctly used

The import is used in the register method after successful user registration to fetch and set workspace data in the store. This is part of the post-registration flow where the application initializes the user's workspace data.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check usage of fetchAllWorkspaces in the component
ast-grep --pattern 'fetchAllWorkspaces($$$)'

Length of output: 119


Script:

#!/bin/bash
# Check if fetchAllWorkspaces is used in RegisterForm.vue
rg "fetchAllWorkspaces" "client/components/pages/auth/components/RegisterForm.vue"

# Get the content of RegisterForm.vue to understand the component's logic
cat "client/components/pages/auth/components/RegisterForm.vue"

Length of output: 8075

@@ -138,6 +147,7 @@
'password_confirmation' => 'secret',
'agree_terms' => true,
'invite_token' => $token,
'h-captcha-response' => 'test-token',
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add comprehensive spam protection test cases

The current test suite focuses on invite token validation but lacks coverage for important spam prevention scenarios:

  • Rate limiting of registration attempts
  • Multiple failed HCaptcha attempts
  • Email verification workflow
  • IP-based restrictions

Would you like me to help create additional test cases for these scenarios? This would help ensure the email spam security measures are working as intended.

@@ -31,6 +36,7 @@
'password_confirmation' => 'secret',
'agree_terms' => true,
'invite_token' => $token,
'h-captcha-response' => 'test-token',
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance HCaptcha validation test coverage

The test uses a static 'test-token' value without verifying if the HCaptcha validation was actually performed. Consider:

  • Asserting that the HCaptcha verification endpoint was called
  • Verifying the token value was passed correctly

Example enhancement:

+    Http::fake([
+        ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => true])
+    ]);
     $response = $this->postJson('/register', [
         // ... other fields ...
         'h-captcha-response' => 'test-token',
     ]);
     $response->assertSuccessful();
+    Http::assertSent(function ($request) {
+        return $request->url() === ValidHCaptcha::H_CAPTCHA_VERIFY_URL
+            && $request->data()['response'] === 'test-token';
+    });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +11 to +13
Http::fake([
ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => true])
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add test cases for HCaptcha verification failures

The current setup always mocks HCaptcha to return success. To ensure robust spam protection, we should also test:

  • Invalid/expired tokens
  • Rate-limited responses
  • Network failures

Example implementation:

 beforeEach(function () {
     $this->user = $this->actingAsProUser();
     $this->workspace = $this->createUserWorkspace($this->user);
-    Http::fake([
-        ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => true])
-    ]);
 });

+it('prevents registration with invalid hCaptcha token', function () {
+    Http::fake([
+        ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => false])
+    ]);
+    // Add test implementation
+});

Committable suggestion skipped: line range outside the PR's diff.

@@ -14,9 +15,11 @@ class FormIntegrationsRequest extends FormRequest
public array $integrationRules = [];

private ?string $integrationClassName = null;
private ?Form $form = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure $integrationClassName is initialized to prevent null reference errors

Since methods like attributes() and isOAuthRequired() rely on $this->integrationClassName, ensure it is initialized even when $request->integration_id is not provided to prevent potential null reference errors.

Consider initializing $integrationClassName or adding checks before its usage.

Comment on lines 31 to 32
$this->middleware('throttle:5,1')->only('register'); // 5 attempts per minute
$this->middleware('throttle:30,60')->only('register'); // 30 attempts per hour
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider implementing stricter rate limits for registration

While multiple rate limits are good, the current limits (5/min, 30/hour) might be too permissive for a registration endpoint. Consider reducing these limits to prevent potential abuse:

  • 3 attempts per hour is typically sufficient for legitimate users
  • Current limits could allow automated spam account creation
-        $this->middleware('throttle:5,1')->only('register'); // 5 attempts per minute
-        $this->middleware('throttle:30,60')->only('register'); // 30 attempts per hour
+        $this->middleware('throttle:3,60')->only('register'); // 3 attempts per hour
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$this->middleware('throttle:5,1')->only('register'); // 5 attempts per minute
$this->middleware('throttle:30,60')->only('register'); // 30 attempts per hour
$this->middleware('throttle:3,60')->only('register'); // 3 attempts per hour

@@ -84,6 +93,7 @@
'password' => bcrypt($data['password']),
'hear_about_us' => $data['hear_about_us'],
'utm_data' => array_key_exists('utm_data', $data) ? $data['utm_data'] : null,
'meta' => ['registration_ip' => request()->ip()],
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Review IP address storage for privacy compliance

Storing IP addresses has privacy implications and might be subject to data protection regulations (GDPR, CCPA):

  1. Consider hashing the IP address
  2. Implement a retention policy
  3. Document the purpose of collection
-            'meta' => ['registration_ip' => request()->ip()],
+            'meta' => ['registration_ip' => hash('sha256', request()->ip())],

Committable suggestion skipped: line range outside the PR's diff.

@@ -33,6 +33,7 @@ class User extends Authenticatable implements JWTSubject
'password',
'hear_about_us',
'utm_data',
'meta'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance security for the meta attribute

While it's good that the meta field is hidden, being fillable without validation poses risks:

  1. Mass assignment could allow injection of unexpected data
  2. No schema validation for meta contents

Consider:

  1. Remove from fillable and use explicit setters
  2. Add validation for meta contents
  3. Implement strict typing or schema validation
-    protected $fillable = [
-        'name',
-        'email',
-        'password',
-        'hear_about_us',
-        'utm_data',
-        'meta'
-    ];
+    protected $fillable = [
+        'name',
+        'email',
+        'password',
+        'hear_about_us',
+        'utm_data',
+    ];

+    public function setMeta(array $meta): void
+    {
+        $allowedKeys = ['registration_ip'];
+        $filteredMeta = array_intersect_key($meta, array_flip($allowedKeys));
+        $this->attributes['meta'] = json_encode($filteredMeta);
+    }

Also applies to: 48-48, 61-61

Comment on lines +136 to +175
test('free user can update their single email integration', function () {
$user = $this->actingAsUser();
$workspace = $this->createUserWorkspace($user);
$form = $this->createForm($user, $workspace);

// Create initial integration
$response = $this->postJson(route('open.forms.integration.create', $form), [
'integration_id' => 'email',
'status' => true,
'settings' => [
'send_to' => '[email protected]',
'sender_name' => 'Test Sender',
'subject' => 'Test Subject',
'email_content' => 'Test Content',
'include_submission_data' => true
]
]);

$response->assertSuccessful();
$integrationId = $response->json('form_integration.id');

// Update the integration
$response = $this->putJson(route('open.forms.integration.update', [$form, $integrationId]), [
'integration_id' => 'email',
'status' => true,
'settings' => [
'send_to' => '[email protected]',
'sender_name' => 'Updated Sender',
'subject' => 'Updated Subject',
'email_content' => 'Updated Content',
'include_submission_data' => true
]
]);

$response->assertSuccessful();

$integration = FormIntegration::find($integrationId);
expect($integration->data->send_to)->toBe('[email protected]');
expect($integration->data->sender_name)->toBe('Updated Sender');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add content validation test cases

The current tests should be extended to cover content security scenarios:

  1. HTML/Script injection in email content
  2. Maximum content size limits
  3. Content sanitization

Add these test cases:

+test('it sanitizes html in email content', function () {
+    $user = $this->actingAsUser();
+    $workspace = $this->createUserWorkspace($user);
+    $form = $this->createForm($user, $workspace);
+
+    $response = $this->postJson(route('open.forms.integration.create', $form), [
+        'integration_id' => 'email',
+        'status' => true,
+        'settings' => [
+            'send_to' => '[email protected]',
+            'sender_name' => 'Test Sender',
+            'subject' => 'Test Subject',
+            'email_content' => '<script>alert("xss")</script>Test Content',
+            'include_submission_data' => true
+        ]
+    ]);
+
+    $response->assertStatus(422);
+});

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +84 to +108
test('free user cannot add multiple emails', function () {
$user = $this->actingAsUser();
$workspace = $this->createUserWorkspace($user);
$form = $this->createForm($user, $workspace);

$response = $this->postJson(route('open.forms.integration.create', $form), [
'integration_id' => 'email',
'status' => true,
'settings' => [
'send_to' => "[email protected]\[email protected]",
'sender_name' => 'Test Sender',
'subject' => 'Test Subject',
'email_content' => 'Test Content',
'include_submission_data' => true
]
]);

$response->assertStatus(422)
->assertJsonValidationErrors(['settings.send_to'])
->assertJson([
'errors' => [
'settings.send_to' => ['You can only send to a single email address on the free plan. Please upgrade to the Pro plan to create a new integration.']
]
]);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add security-focused test cases for email validation

While the current tests cover business rules well, consider adding security-focused test cases:

  1. Email header injection prevention
  2. Email address format validation
  3. Large payload handling

Add these test cases:

+test('it prevents email header injection', function () {
+    $user = $this->actingAsUser();
+    $workspace = $this->createUserWorkspace($user);
+    $form = $this->createForm($user, $workspace);
+
+    $response = $this->postJson(route('open.forms.integration.create', $form), [
+        'integration_id' => 'email',
+        'status' => true,
+        'settings' => [
+            'send_to' => "[email protected]\nBcc: [email protected]",
+            'sender_name' => "Innocent Name\nBcc: [email protected]",
+            'subject' => "Test\nBcc: [email protected]",
+            'email_content' => 'Test Content',
+            'include_submission_data' => true
+        ]
+    ]);
+
+    $response->assertStatus(422);
+});

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 238 to 241
if (!this.isSelfHosted) {
this.form['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value
this.hcaptcha.reset()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid direct DOM access for hCaptcha response.

Instead of accessing the DOM directly with document.getElementsByName, consider using the hCaptcha component's methods or events to get the response value. The Vue ref is already available as this.hcaptcha.

-this.form['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value
+// Use the hCaptcha component's execute method to get the response
+const response = await this.hcaptcha.execute()
+this.form['h-captcha-response'] = response

Committable suggestion skipped: line range outside the PR's diff.

@@ -27,6 +28,10 @@ class RegisterController extends Controller
public function __construct()
{
$this->middleware('guest');
if (app()->environment() !== 'testing') {
Copy link
Owner

Choose a reason for hiding this comment

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

Any reason why this is in this if?

Copy link
Owner

@JhumanJ JhumanJ left a comment

Choose a reason for hiding this comment

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

Great work, small comments added thanks

api/app/Http/Controllers/Auth/RegisterController.php Outdated Show resolved Hide resolved
Copy link
Contributor

@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 (3)
api/app/Integrations/Handlers/EmailIntegration.php (2)

35-40: Strengthen email validation for spam prevention.

The validation for multiple email addresses is good, but consider these enhancements:

  1. Add input sanitization for the email addresses
  2. Consider adding rate limiting for email sending
 $rules['send_to'][] = function ($attribute, $value, $fail) use ($form) {
+    $value = trim(strip_tags($value));
     if (count(explode("\n", trim($value))) > 1 || count(explode(',', $value)) > 1) {
         $fail('You can only send to a single email address on the free plan. Please upgrade to the Pro plan to create a new integration.');
     }
+    if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
+        $fail('Please enter a valid email address.');
+    }
 };

Line range hint 1-140: Good security implementation with layered defenses.

The implementation provides multiple layers of security:

  1. Input validation for free users
  2. Integration limits
  3. Risk-based restrictions
  4. Pro feature gating

Consider implementing these architectural improvements:

  1. Add rate limiting at the API level
  2. Implement email domain validation/blacklisting
  3. Add monitoring for abuse patterns
api/tests/Feature/RegisterTest.php (1)

Line range hint 32-42: Consider adding negative test cases for hCaptcha validation.

While the test covers duplicate email validation, consider adding test cases for:

  • Invalid hCaptcha token
  • Missing hCaptcha token
  • Failed hCaptcha verification
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01f7fa2 and 9260b4b.

📒 Files selected for processing (5)
  • api/app/Http/Controllers/Auth/RegisterController.php (4 hunks)
  • api/app/Integrations/Handlers/EmailIntegration.php (2 hunks)
  • api/database/migrations/2024_12_10_094605_add_meta_to_users_table.php (1 hunks)
  • api/tests/Feature/RegisterTest.php (3 hunks)
  • client/components/pages/auth/components/RegisterForm.vue (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/database/migrations/2024_12_10_094605_add_meta_to_users_table.php
🔇 Additional comments (12)
api/app/Integrations/Handlers/EmailIntegration.php (3)

5-12: LGTM! Dependencies are properly imported.

The new imports for Form model and ValidationException are necessary for the added validation logic.


18-18: LGTM! Pro plan check is properly implemented.

The validation rules correctly bypass restrictions for pro users and self-hosted installations.

Also applies to: 31-33


16-16: Verify risk limit effectiveness.

The risk limit check provides an additional layer of security, but let's verify its effectiveness:

Also applies to: 77-89

✅ Verification successful

Risk limit implementation is effective and properly enforced

The verification shows a robust security implementation:

  • Risk assessment is cached for 15 minutes via remember() method
  • Workspace-level risk check evaluates all owners' risk status
  • Email sending is blocked when risky workspace reaches the 120 submissions limit
  • Suspicious activity is properly logged with workspace and form identifiers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any bypass attempts or related security issues
rg -A 5 "RISKY_USERS_LIMIT|is_risky" --type php

# Check for any email-related security incidents in logs
rg -A 5 "!!!DANGER!!!" --type php

Length of output: 3650

api/tests/Feature/RegisterTest.php (3)

8-28: LGTM! Comprehensive test coverage for successful registration.

The test properly mocks hCaptcha verification and validates the registration flow including IP address storage.


Line range hint 44-73: LGTM! Comprehensive validation for disposable emails.

The test properly validates the rejection of disposable emails and includes proper error message verification.


75-87: LGTM! Good coverage of environment-specific validation.

The test properly validates that hCaptcha is required in production environment.

api/app/Http/Controllers/Auth/RegisterController.php (3)

64-81: LGTM! Comprehensive validation rules with proper hCaptcha integration.

The validation rules are well-structured and the conditional hCaptcha validation is properly implemented.


31-34: ⚠️ Potential issue

Consider implementing stricter rate limits for registration.

While multiple rate limits are good, the current limits (5/min, 30/hour) might be too permissive for a registration endpoint.

Apply this diff to implement stricter limits:

-        if (app()->environment() !== 'testing') {
-            $this->middleware('throttle:5,1')->only('register'); // 5 attempts per minute
-            $this->middleware('throttle:30,60')->only('register'); // 30 attempts per hour
+        if (app()->environment() !== 'testing') {
+            $this->middleware('throttle:3,60')->only('register'); // 3 attempts per hour
+        }

98-98: ⚠️ Potential issue

Review IP address storage for privacy compliance.

Storing IP addresses has privacy implications and might be subject to data protection regulations (GDPR, CCPA).

Apply this diff to implement IP hashing:

-            'meta' => ['registration_ip' => request()->ip()],
+            'meta' => ['registration_ip' => hash('sha256', request()->ip())],
client/components/pages/auth/components/RegisterForm.vue (3)

55-68: LGTM! Clean hCaptcha component integration.

The hCaptcha component is properly integrated with conditional rendering and error handling.


179-189: LGTM! Well-structured form data with proper computed properties.

The form data is well-organized and the computed property for the hCaptcha site key is properly implemented.


237-240: 🛠️ Refactor suggestion

Avoid direct DOM access for hCaptcha response.

Instead of accessing the DOM directly with document.getElementsByName, use the hCaptcha component's methods or events.

Apply this diff to use the component's methods:

      if (this.hCaptchaSiteKey) {
-        this.form['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value
+        const response = await this.hcaptcha.execute()
+        this.form['h-captcha-response'] = response
        this.hcaptcha.reset()
      }

Comment on lines +42 to +52
// Free plan users can only have a single email integration per form (avoid spam)
if (!request()->route('integrationid')) {
$existingEmailIntegrations = FormIntegration::where('form_id', $form->id)
->where('integration_id', 'email')
->count();
if ($existingEmailIntegrations > 0) {
throw ValidationException::withMessages([
'settings.send_to' => ['Free users are limited to 1 email integration per form.']
]);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider edge cases in integration validation.

The validation for existing integrations should:

  1. Consider soft-deleted integrations
  2. Use consistent error messages
 if (!request()->route('integrationid')) {
     $existingEmailIntegrations = FormIntegration::where('form_id', $form->id)
         ->where('integration_id', 'email')
+        ->whereNull('deleted_at')  // Exclude soft-deleted integrations
         ->count();
     if ($existingEmailIntegrations > 0) {
         throw ValidationException::withMessages([
-            'settings.send_to' => ['Free users are limited to 1 email integration per form.']
+            'settings.send_to' => ['You can only have a single email integration on the free plan. Please upgrade to the Pro plan to create a new integration.']
         ]);
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Free plan users can only have a single email integration per form (avoid spam)
if (!request()->route('integrationid')) {
$existingEmailIntegrations = FormIntegration::where('form_id', $form->id)
->where('integration_id', 'email')
->count();
if ($existingEmailIntegrations > 0) {
throw ValidationException::withMessages([
'settings.send_to' => ['Free users are limited to 1 email integration per form.']
]);
}
}
// Free plan users can only have a single email integration per form (avoid spam)
if (!request()->route('integrationid')) {
$existingEmailIntegrations = FormIntegration::where('form_id', $form->id)
->where('integration_id', 'email')
->whereNull('deleted_at') // Exclude soft-deleted integrations
->count();
if ($existingEmailIntegrations > 0) {
throw ValidationException::withMessages([
'settings.send_to' => ['You can only have a single email integration on the free plan. Please upgrade to the Pro plan to create a new integration.']
]);
}
}

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.

2 participants