Skip to content

Conversation

@eug-L
Copy link
Contributor

@eug-L eug-L commented Nov 28, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new input field for the JavaScript API key under "Privacy Options" for visitor recognition.
    • Added functionality to toggle visibility elements based on the visitor recognition checkbox.
  • Enhancements

    • Improved security by implementing encryption for the JavaScript API key.
    • Enhanced privacy settings interface with clearer organization and new functionality.
    • Updated tests to use dynamic waits for improved reliability.
    • Added a command to disable the "coming soon" feature in WooCommerce during setup.
  • Bug Fixes

    • Streamlined logic for displaying widget settings to improve readability.
    • Added test coverage for privacy options related to visitor recognition and API key functionality.

@coderabbitai
Copy link

coderabbitai bot commented Nov 28, 2024

Walkthrough

The changes in this pull request encompass updates across multiple files to enhance the functionality and security of the Tawk.to plugin. Key modifications include the addition of a root property in the ESLint configuration, new visitor recognition features in JavaScript, the introduction of a JavaScript API key in the configuration, and significant updates to the TawkTo_Settings class for improved data handling and encryption. Additionally, a new input field for the API key has been integrated into the settings template, along with various refinements to the user interface and validation processes.

Changes

File Change Summary
.eslintrc.js Added property 'root': true to indicate root configuration for ESLint.
tawkto/assets/js/tawk.admin.js Introduced visitor recognition functionality based on the #enable-visitor-recognition checkbox.
tawkto/includes/default_config.php Added new configuration options: 'js_api_key' => '' in the security array and 'enable_visitor_recognition' => 1 in the privacy array.
tawkto/tawkto.php Added constants for privacy and security options, updated validation methods, and introduced methods for data encryption/decryption.
tawkto/templates/settings.php Updated input field for the API key and refined the settings display and logic for visitor recognition.
tests/Coverages/PrivacyOptionsTest.php Added a new test method and modified existing tests to enhance coverage for visitor object data.
tawkto/upgrade.manager.php Added new upgrade version TawkToUpgradeVersion090 to handle updates for version 0.9.0.
tawkto/upgrades/version.090.php Introduced class TawkToUpgradeVersion090 with an upgrade() method for migrating visitor recognition settings.
tests/Coverages/VisibilityOptionsTest.php Replaced fixed wait times with dynamic waits based on specific elements in multiple test methods.
tests/Coverages/WoocommerceOptionsTest.php Replaced fixed wait times with dynamic waits based on specific elements in multiple test methods.
tests/TestFiles/Modules/Web.php Replaced fixed wait times with dynamic waits based on specific elements in multiple methods.
tests/docker/build/files/build-wordpress.sh Added command to set WooCommerce option for disabling "coming soon" feature.

Poem

In the burrow where changes bloom,
A key for JavaScript finds its room.
With toggles and checks, the visitors see,
A world of options, as bright as can be!
Encryption whispers, secrets kept tight,
Hopping along, all feels just right! 🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between aee2922 and 2cd1008.

📒 Files selected for processing (2)
  • tawkto/upgrades/version.090.php (1 hunks)
  • tests/docker/build/files/build-wordpress.sh (1 hunks)
🔇 Additional comments (5)
tests/docker/build/files/build-wordpress.sh (1)

75-75: LGTM! Verify WooCommerce option name and values.

The addition follows the established pattern of WooCommerce option settings and is properly placed with other related configurations.

Let's verify the WooCommerce option name and possible values:

tawkto/upgrades/version.090.php (4)

1-10: LGTM! Well-structured class definition.

The file follows good practices with proper class loading, path handling, and documentation.


11-11: LGTM! Clear version definition.

The version constant follows semantic versioning and matches the file name.


28-28: Document security implications of empty API key.

Please add documentation explaining:

  1. Why the API key is initialized as empty
  2. Security implications of an empty API key
  3. When and how this key should be set by administrators

Run this script to check the security key usage:


18-29: Verify data preservation during migration.

The migration process moves settings between options but doesn't verify if the data was preserved correctly.

Run this script to check the current state of these options in the database and their usage:

✅ Verification successful

Migration safety is verified and confirmed

The migration process is safe and preserves data integrity through:

  • Comprehensive validation methods for each option type
  • Proper merging with existing data using array_merge()
  • WordPress standard sanitization and validation hooks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the usage and current state of the migrated options

# Search for references to these options
echo "Checking option usage..."
rg -l "TAWK_VISIBILITY_OPTIONS|TAWK_PRIVACY_OPTIONS|TAWK_SECURITY_OPTIONS"

# Search for any direct database interactions with these options
echo "Checking direct database interactions..."
rg -l "get_option|update_option" | xargs rg "tawk_visibility|tawk_privacy|tawk_security"

# Check for any upgrade-related error handling
echo "Checking existing error handling patterns..."
rg -l "try|catch|error_log" | xargs rg "upgrade"

Length of output: 967


Script:

#!/bin/bash
# Check the content of main plugin file and settings class
echo "=== Main plugin file content ==="
rg -A 5 "class TawkTo_Settings|TAWK_VISIBILITY_OPTIONS|TAWK_PRIVACY_OPTIONS|TAWK_SECURITY_OPTIONS" tawkto/tawkto.php

echo -e "\n=== Option usage patterns ==="
ast-grep --pattern 'get_option($_, $_)' 
ast-grep --pattern 'update_option($_, $_)'

echo -e "\n=== Validation patterns ==="
rg "sanitize|validate" --type php

Length of output: 6030


🪧 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. (Beta)
  • @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

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

🧹 Outside diff range and nitpick comments (6)
tawkto/includes/default_config.php (1)

19-19: Consider separating security configurations from visibility settings

The js_api_key is placed within the visibility array, but it's a security credential rather than a visibility setting. Consider creating a separate security or authentication configuration section to better organize and maintain security-related settings.

 return array(
 	'visibility' => array(
 		'always_display'             => 1,
 		// ... other visibility settings ...
-		'js_api_key'                 => '',
 	),
+	'security' => array(
+		'js_api_key'                 => '',
+	),
 );
tawkto/assets/js/tawk.admin.js (3)

76-82: Consider clearing the API key field when disabled

When visitor recognition is disabled, consider clearing the API key field value in addition to disabling it. This prevents any stored API key from being visible in the DOM when the feature is disabled.

 if ( jQuery( '#enable-visitor-recognition' ).prop( 'checked' ) ) {
   jQuery( '.tawk-selected-visitor' ).show();
   jQuery( '#js-api-key' ).prop( 'disabled', false );
 } else {
   jQuery( '.tawk-selected-visitor' ).hide();
   jQuery( '#js-api-key' ).prop( 'disabled', true );
+  jQuery( '#js-api-key' ).val('');
 }

84-94: Enhance change handler implementation

Two suggestions for improvement:

  1. Clear the API key field when disabling visitor recognition
  2. Add a consistent animation duration for better UX alignment with other animations in the file
 jQuery( '#enable-visitor-recognition' ).change(
   function() {
     if ( this.checked ) {
-      jQuery( '.tawk-selected-visitor' ).fadeIn();
+      jQuery( '.tawk-selected-visitor' ).fadeIn(400);
       jQuery( '#js-api-key' ).prop( 'disabled', false );
     } else {
-      jQuery( '.tawk-selected-visitor' ).fadeOut();
+      jQuery( '.tawk-selected-visitor' ).fadeOut(400);
       jQuery( '#js-api-key' ).prop( 'disabled', true );
+      jQuery( '#js-api-key' ).val('');
     }
   }
 );

75-94: Consider additional security measures for API key handling

Since this code handles sensitive API key information, consider implementing these security measures:

  1. Add input validation for the API key format
  2. Consider implementing a secure way to test the API key validity
  3. Ensure the API key is properly sanitized before being saved

Would you like assistance in implementing any of these security measures?

tawkto/templates/settings.php (1)

416-423: Enhance the notice with additional context and documentation

Consider adding a link to the Tawk.to documentation about Secure Mode and its benefits to help users make informed decisions.

 <p class='tawk-notice'>
   <?php esc_html_e( 'Note: If ', 'tawk-to-live-chat' ); ?>
   <b><?php esc_html_e( 'Secure Mode', 'tawk-to-live-chat' ); ?></b>
   <?php esc_html_e( ' is enabled on your property, please enter your ', 'tawk-to-live-chat' ); ?>
   <b><?php esc_html_e( 'Javascript API Key', 'tawk-to-live-chat' ); ?></b>
   <?php esc_html_e( ' for visitor recognition to work correctly.', 'tawk-to-live-chat' ); ?>
+  <br/>
+  <a href="https://www.tawk.to/knowledgebase/secure-mode" target="_blank" rel="noopener noreferrer">
+    <?php esc_html_e( 'Learn more about Secure Mode', 'tawk-to-live-chat' ); ?>
+  </a>
 </p>
tawkto/tawkto.php (1)

30-31: Use openssl_cipher_iv_length() to determine IV length instead of hardcoding

To ensure compatibility with the specified cipher and improve maintainability, consider using openssl_cipher_iv_length(self::CIPHER) to dynamically determine the IV length instead of hardcoding CIPHER_IV_LENGTH as 16. This approach ensures that the IV length always matches the requirements of the cipher algorithm.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 740763d and c6dd2f1.

📒 Files selected for processing (5)
  • .eslintrc.js (1 hunks)
  • tawkto/assets/js/tawk.admin.js (1 hunks)
  • tawkto/includes/default_config.php (1 hunks)
  • tawkto/tawkto.php (5 hunks)
  • tawkto/templates/settings.php (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .eslintrc.js
🔇 Additional comments (2)
tawkto/includes/default_config.php (1)

19-19: Verify secure handling of the API key

Since this is a sensitive credential being introduced as part of secure mode support, let's verify the implementation of proper security measures in the related files.

✅ Verification successful

Let me verify the encryption functions to ensure complete security. I'll check the implementation of encryption/decryption methods.


Let me try a different approach to check the encryption methods and frontend usage.


Let me check one final aspect about the cipher configuration and usage.


API key implementation follows security best practices

The implementation shows proper security measures:

  • The API key is encrypted using AES-256-CBC with a random IV before storage
  • Encryption uses WordPress's SECURE_AUTH_KEY constant
  • The key is properly escaped when output in forms
  • No direct exposure of the key in JavaScript
  • Key is only used server-side for HMAC generation
  • Decryption is properly handled with base64 decode and IV separation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper encryption and secure handling of js_api_key

# Check if the key is properly encrypted when stored
rg -A 5 "js_api_key" --type php

# Look for encryption-related functions handling this key
ast-grep --pattern 'function $_($$js_api_key) {
  $$$
}'

# Check for any direct/unencrypted usage of the key
rg -i "js_api_key.*=.*['\"]\w+['\"]" --type php

Length of output: 2304


Script:

#!/bin/bash
# Check the implementation of encryption/decryption methods
ast-grep --pattern 'function get_encrypted_data($_) {
  $$$
}'

ast-grep --pattern 'function get_decrypted_data($_) {
  $$$
}'

# Check how the key is used in frontend/output
rg -A 3 "wp_localize_script.*js_api" --type php

Length of output: 179


Script:

#!/bin/bash
# Search for encryption-related functions in the class
rg -A 10 "function get_(en|de)crypted_data" --type php

# Check how the API key is passed to frontend
rg -A 5 "wp_localize_script|wp_add_inline_script" --type php

# Look for any direct output of the key
rg "echo.*js_api" --type php

Length of output: 1757


Script:

#!/bin/bash
# Check cipher configuration
rg -A 2 "CIPHER.*=" --type php

# Check if the key is used in any JavaScript
rg -g "*.js" "js_api|api_key" 

# Check for any WordPress hooks/filters related to the key
rg "add_filter|apply_filters.*js_api" --type php

Length of output: 394

tawkto/templates/settings.php (1)

415-438: Add API key validation and test functionality

Consider adding immediate validation feedback and a test connection feature to help users verify their configuration.

 <div class="tawk-selected-visitor">
   <!-- ... existing code ... -->
   <table class="form-table">
     <tr valign="top">
       <th class="tawk-setting" scope="row">
         <?php esc_html_e( 'Javascript API Key', 'tawk-to-live-chat' ); ?>
       </th>
       <td>
         <input type="password"
           id="js-api-key"
           name="tawkto-visibility-options[js_api_key]"
           value="<?php echo esc_attr( $visibility['js_api_key'] ); ?>" />
+        <button type="button" 
+          class="button" 
+          id="test-api-key"
+          onclick="testApiKey()"
+          <?php echo empty($visibility['js_api_key']) ? 'disabled' : ''; ?>>
+          <?php esc_html_e( 'Test Connection', 'tawk-to-live-chat' ); ?>
+        </button>
+        <p class="description" id="js-api-key-status"></p>
       </td>
     </tr>
   </table>
 </div>

Add this JavaScript to handle the test functionality:

function testApiKey() {
  const apiKey = document.getElementById('js-api-key').value;
  const statusEl = document.getElementById('js-api-key-status');
  
  // Show loading state
  statusEl.textContent = 'Testing connection...';
  
  // Make an AJAX call to test the API key
  // This requires implementing an endpoint in the plugin's PHP code
  jQuery.post(ajaxurl, {
    action: 'test_tawkto_api_key',
    api_key: apiKey,
    nonce: tawktoSettings.nonce
  })
  .done(function(response) {
    if (response.success) {
      statusEl.style.color = '#00a32a';
      statusEl.textContent = 'Connection successful!';
    } else {
      statusEl.style.color = '#d63638';
      statusEl.textContent = response.data.message || 'Connection failed!';
    }
  })
  .fail(function() {
    statusEl.style.color = '#d63638';
    statusEl.textContent = 'Connection test failed. Please try again.';
  });
}

// Enable/disable test button based on API key input
document.getElementById('js-api-key').addEventListener('input', function() {
  document.getElementById('test-api-key').disabled = !this.value;
});

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 (3)
tawkto/tawkto.php (1)

376-403: Improve error handling in encryption method.

While the method handles basic error cases, it could be enhanced:

  1. Add logging for encryption failures
  2. Consider throwing exceptions instead of returning empty strings
 private static function get_encrypted_data( $data ) {
     try {
         $iv = random_bytes( self::CIPHER_IV_LENGTH );
     } catch ( Exception $e ) {
+        error_log( 'Failed to generate IV: ' . $e->getMessage() );
         return '';
     }

     $encrypted_data = openssl_encrypt( $data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );

     if ( false === $encrypted_data ) {
+        error_log( 'Failed to encrypt data with OpenSSL' );
         return '';
     }

     // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
     $encrypted_data = base64_encode( $iv . $encrypted_data );

     if ( false === $encrypted_data ) {
+        error_log( 'Failed to base64 encode encrypted data' );
         return '';
     }

     return $encrypted_data;
 }
tawkto/templates/settings.php (2)

418-422: Typographical correction: 'Javascript' should be 'JavaScript'

In the notice text, "Javascript API Key" should be spelled "JavaScript API Key" to reflect the correct spelling of "JavaScript".


428-428: Typographical correction: 'Javascript' should be 'JavaScript'

In the label, "Javascript API Key" should be updated to "JavaScript API Key" for accurate spelling.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c6dd2f1 and a011f8f.

📒 Files selected for processing (2)
  • tawkto/tawkto.php (5 hunks)
  • tawkto/templates/settings.php (1 hunks)
🔇 Additional comments (8)
tawkto/tawkto.php (5)

30-31: LGTM! Encryption configuration looks secure.

The chosen cipher (AES-256-CBC) and IV length (16 bytes) follow cryptographic best practices.


254-254: Consider using a more appropriate sanitization method for API keys.

The current implementation uses sanitize_text_field() which might alter special characters in the API key.

Also applies to: 335-339


436-444: LGTM! API key getter implementation is secure.

The method properly handles missing or invalid API keys by returning an empty string.


543-548: LGTM! Secure implementation of visitor recognition.

The code correctly:

  1. Checks for empty email and API key before computing hash
  2. Uses HMAC-SHA256 for secure hash generation

411-429: 🛠️ Refactor suggestion

Add input validation in decryption method.

The method should validate the input length before attempting decryption.

 private static function get_decrypted_data( $data ) {
+    if ( empty( $data ) ) {
+        return '';
+    }
+
     // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
     $decoded_data = base64_decode( $data );

     if ( false === $decoded_data ) {
+        error_log( 'Failed to base64 decode encrypted data' );
         return '';
     }

+    if ( strlen( $decoded_data ) <= self::CIPHER_IV_LENGTH ) {
+        error_log( 'Decoded data is too short' );
+        return '';
+    }

     $iv             = substr( $decoded_data, 0, self::CIPHER_IV_LENGTH );
     $encrypted_data = substr( $decoded_data, self::CIPHER_IV_LENGTH );

     $decrypted_data = openssl_decrypt( $encrypted_data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );

     if ( false === $decrypted_data ) {
+        error_log( 'Failed to decrypt data with OpenSSL' );
         return '';
     }

     return $decrypted_data;
 }

Likely invalid or redundant comment.

tawkto/templates/settings.php (3)

435-435: Reconsider the input validation pattern for 'JavaScript API Key'

The current pattern ^[a-zA-Z0-9]+$ allows only letters and digits. If the API key may include other characters such as hyphens or underscores, consider updating the pattern to accommodate them.

You can adjust the pattern as follows:

-								pattern="^[a-zA-Z0-9]+$"
+								pattern="^[a-zA-Z0-9-_]+$"

434-436: Add a placeholder to the API key input field for better user guidance

Including a placeholder can improve user experience by indicating what the user should enter.

You can add the placeholder attribute:

								value="<?php echo esc_attr( $visibility['js_api_key'] ); ?>"
+								placeholder="<?php esc_attr_e( 'Enter your JavaScript API Key', 'tawk-to-live-chat' ); ?>"
								pattern="^[a-zA-Z0-9]+$"

431-436: Enhance the API key input field with a show/hide toggle

Adding a toggle button to show or hide the API key can improve usability while maintaining security.

If you'd like assistance implementing this feature, I can help provide the necessary code or open a GitHub issue.

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

♻️ Duplicate comments (3)
tawkto/tawkto.php (3)

405-430: 🛠️ Refactor suggestion

Use random_bytes() and improve error handling in encryption method.

The encryption implementation can be enhanced with better error handling and modern cryptographic functions.


438-456: 🛠️ Refactor suggestion

Add error handling and input validation in decryption method.

The decryption implementation needs better error handling for potential failures.


581-586: 🛠️ Refactor suggestion

Ensure email is not empty before computing hash.

Verify that the email is not empty before computing the hash with hash_hmac().

🧹 Nitpick comments (3)
tests/Coverages/PrivacyOptionsTest.php (1)

88-88: Use a realistic API key format for testing.

The current test uses a repeated character for the API key. Consider using a more realistic API key format that matches the expected pattern from the tawk.to service.

-self::$driver->find_element_and_input('#js-api-key', str_repeat('a', 40));
+self::$driver->find_element_and_input('#js-api-key', '1234567890abcdef1234567890abcdef12345678');
tawkto/templates/settings.php (1)

415-419: Add more context to the notice message.

The notice should provide more information about Secure Mode and why the API key is required.

-    <?php esc_html_e( 'Note: If Secure Mode is enabled on your property, please enter your Javascript API Key to ensure visitor recognition works correctly.', 'tawk-to-live-chat' ); ?>
+    <?php esc_html_e( 'Note: If Secure Mode is enabled on your property, please enter your Javascript API Key. This key is required to securely identify visitors and ensure visitor recognition works correctly. You can find your API key in the tawk.to dashboard under Property Settings > Security.', 'tawk-to-live-chat' ); ?>
tawkto/tawkto.php (1)

463-479: Cache duration might be too long for sensitive data.

The API key is cached for 1 hour (3600 seconds). Consider reducing the cache duration for sensitive data.

-    set_transient( self::TAWK_API_KEY, $key, 60 * 60 );
+    set_transient( self::TAWK_API_KEY, $key, 15 * 60 ); // Cache for 15 minutes instead of 1 hour
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a011f8f and e6036b8.

📒 Files selected for processing (3)
  • tawkto/tawkto.php (7 hunks)
  • tawkto/templates/settings.php (1 hunks)
  • tests/Coverages/PrivacyOptionsTest.php (3 hunks)
🔇 Additional comments (4)
tests/Coverages/PrivacyOptionsTest.php (1)

64-64: LGTM! The assertion verifies secure mode behavior.

The added assertion correctly verifies that the visitor object does not contain a hash when secure mode is not enabled (no API key provided).

tawkto/templates/settings.php (1)

427-433: Enhance the API key input field with security and UX improvements.

The current implementation can be improved with additional security features and better user experience.

tawkto/tawkto.php (2)

30-34: LGTM! Constants are well-defined and follow naming conventions.

The constants are appropriately named and provide clear separation of concerns for encryption-related functionality.


557-559: LGTM! Proper cleanup of sensitive data.

The deactivation method correctly cleans up both the API key and its encrypted version from transients.

Comment on lines 90 to 91
self::$driver->move_mouse_to( '#submit-header' )->click();
self::$driver->wait_for_seconds( 1 );
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace arbitrary wait with explicit wait condition.

Using a fixed wait time can make tests flaky. Instead, wait for a specific condition to be met.

-self::$driver->move_mouse_to('#submit-header')->click();
-self::$driver->wait_for_seconds(1);
+self::$driver->move_mouse_to('#submit-header')->click();
+self::$driver->wait_until_element_is_located('#setting-error-settings_updated');
📝 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
self::$driver->move_mouse_to( '#submit-header' )->click();
self::$driver->wait_for_seconds( 1 );
self::$driver->move_mouse_to('#submit-header')->click();
self::$driver->wait_until_element_is_located('#setting-error-settings_updated');

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

🧹 Nitpick comments (1)
tawkto/tawkto.php (1)

340-349: Add format validation for the API key.

While the length check is good, consider adding a format validation to ensure the API key contains only valid characters.

-            $fields['js_api_key'] = 40 === strlen( $fields['js_api_key'] ) ? self::get_encrypted_data( $fields['js_api_key'] ) : '';
+            $fields['js_api_key'] = (40 === strlen( $fields['js_api_key'] ) && preg_match('/^[a-zA-Z0-9]+$/', $fields['js_api_key'])) 
+                ? self::get_encrypted_data( $fields['js_api_key'] ) 
+                : '';
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e6036b8 and 0af8c10.

📒 Files selected for processing (1)
  • tawkto/tawkto.php (7 hunks)
🔇 Additional comments (4)
tawkto/tawkto.php (4)

30-33: LGTM! Well-defined encryption constants.

The encryption configuration uses industry-standard AES-256-CBC with appropriate IV length.


404-429: LGTM! Robust encryption implementation.

The encryption and decryption methods:

  • Use cryptographically secure random_bytes()
  • Include comprehensive error handling
  • Properly handle the IV
  • Use base64 encoding for safe storage

Also applies to: 437-455


579-584: LGTM! Secure implementation of visitor recognition.

The implementation follows security best practices:

  • Uses HMAC-SHA256 for secure hashing
  • Properly validates email and API key presence

557-557: LGTM! Proper cleanup of sensitive data.

The deactivation handler correctly removes the cached API key.

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

🧹 Nitpick comments (2)
tawkto/upgrades/version.090.php (1)

3-5: Consider using a namespace for better organization.

Since this is a new file, consider organizing the upgrade classes under a namespace to prevent potential class name conflicts.

 <?php
+namespace TawkTo\Upgrades;
 
-if ( ! class_exists( 'TawkToUpgradeBase' ) ) {
-    require_once dirname( __FILE__ ) . '/base.php';
-}
+use TawkTo\Upgrades\Base as TawkToUpgradeBase;
+require_once dirname( __FILE__ ) . '/base.php';
tawkto/upgrade.manager.php (1)

42-45: Add validation for upgrade class existence.

Consider validating that the upgrade classes exist before adding them to the upgrades array to prevent potential runtime errors.

 public function __construct( $version, $version_var_name ) {
+    $this->validateUpgradeClasses([
+        TawkToUpgradeVersion070::class,
+        TawkToUpgradeVersion090::class
+    ]);
+
     $this->upgrades = array(
         TawkToUpgradeVersion070::get_version() => TawkToUpgradeVersion070::class,
         TawkToUpgradeVersion090::get_version() => TawkToUpgradeVersion090::class,
     );

Add this helper method:

/**
 * Validates that all upgrade classes exist
 *
 * @param array $classes Array of class names to validate
 * @throws RuntimeException if any class doesn't exist
 */
private function validateUpgradeClasses(array $classes) {
    foreach ($classes as $class) {
        if (!class_exists($class)) {
            throw new RuntimeException(
                sprintf('Upgrade class %s not found', $class)
            );
        }
    }
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0af8c10 and 4d9bc2a.

📒 Files selected for processing (5)
  • tawkto/includes/default_config.php (1 hunks)
  • tawkto/tawkto.php (15 hunks)
  • tawkto/templates/settings.php (1 hunks)
  • tawkto/upgrade.manager.php (2 hunks)
  • tawkto/upgrades/version.090.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tawkto/includes/default_config.php
  • tawkto/tawkto.php
🔇 Additional comments (4)
tawkto/templates/settings.php (2)

415-437: Enhance the API key input field with security and UX improvements.

The current implementation includes basic security measures, but could benefit from the previously suggested improvements for better user experience while maintaining security.

The previous review comment suggested:

  1. Adding a placeholder text
  2. Adding a toggle button to show/hide the API key
  3. Using WordPress dashicons for consistency
  4. Adding ARIA labels for accessibility

407-409: Verify data migration for the visitor recognition setting.

The setting has been moved from the visibility options to privacy options. This change requires data migration for existing installations to preserve user preferences.

Run this script to check if any installations might be affected:

✅ Verification successful

Migration is properly implemented for the visitor recognition setting

The change is safely handled by the migration code in upgrades/version.090.php, which moves the setting from visibility options to privacy options while preserving user preferences.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential data migration issues
# Look for files that might reference the old setting path

# Search for old setting references
echo "Searching for old setting references..."
rg -l "tawkto-visibility-options\[enable_visitor_recognition\]"

# Search for potential upgrade/migration code
echo "Checking for migration handling..."
rg -l "enable_visitor_recognition" -g "*/update*.php"
rg -l "enable_visitor_recognition" -g "*/upgrade*.php"

Length of output: 491


Script:

#!/bin/bash
# Broader search for visitor recognition and migration code
echo "Searching for visitor recognition setting..."
rg -l "enable_visitor_recognition"

echo -e "\nSearching for update/migration code..."
rg -l "update.*options|upgrade.*options|register_activation_hook" --type php

echo -e "\nChecking file content around visitor recognition..."
rg -C 3 "enable_visitor_recognition"

Length of output: 3897

tawkto/upgrade.manager.php (2)

4-4: LGTM!

The new upgrade file is properly included.


44-44: Verify upgrade version ordering.

The upgrade versions in the array determine the execution order. Ensure that version 0.9.0 should indeed be executed after version 0.7.0.

Run this script to verify the version ordering:

✅ Verification successful

Version ordering is correct

The version ordering in the upgrade manager is proper, with version 0.9.0 correctly positioned to execute after version 0.7.0. This is confirmed by:

  • Semantic versioning order (0.7.0 < 0.9.0)
  • Independent migration purposes with no cross-version dependencies
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify upgrade version ordering by checking all upgrade files

# Find all version files and extract version numbers
echo "Checking upgrade version files ordering:"
fd "version\.\d+\.php$" tawkto/upgrades/ --exec grep -H "VERSION = '\d\.\d\.\d'" {} \;

Length of output: 188


Script:

#!/bin/bash
# First, let's find all PHP files with "Version" in their name
echo "=== Finding version-related PHP files ==="
fd -e php -g "*[Vv]ersion*"

echo -e "\n=== Searching for version constants ==="
rg -l "VERSION\s*=\s*['\"]" --type php

echo -e "\n=== Looking for version class definitions ==="
ast-grep --pattern 'class TawkToUpgradeVersion$_ {
  $$$
}'

Length of output: 557


Script:

#!/bin/bash
echo "=== Version 0.7.0 implementation ==="
rg "VERSION\s*=|class\s+TawkToUpgradeVersion070" -A 5 tawkto/upgrades/version.070.php

echo -e "\n=== Version 0.9.0 implementation ==="
rg "VERSION\s*=|class\s+TawkToUpgradeVersion090" -A 5 tawkto/upgrades/version.090.php

echo -e "\n=== Checking for version dependencies ==="
rg "require|depend|before|after" --type php tawkto/upgrades/

Length of output: 1176

Comment on lines 18 to 27
public static function upgrade() {
$visibility = get_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS );

if ( isset( $visibility['enable_visitor_recognition'] ) ) {
update_option( TawkTo_Settings::TAWK_PRIVACY_OPTIONS, array( 'enable_visitor_recognition' => $visibility['enable_visitor_recognition'] ) );

unset( $visibility['enable_visitor_recognition'] );
update_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS, $visibility );
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and validation to the upgrade process.

The upgrade method should include:

  1. Validation of retrieved options
  2. Error handling for failed updates
  3. Logging of migration status for debugging

Consider this implementation:

 public static function upgrade() {
+    $success = true;
     $visibility = get_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS );
+    
+    if (false === $visibility) {
+        error_log('TawkTo Upgrade 0.9.0: Failed to retrieve visibility options');
+        return false;
+    }

     if ( isset( $visibility['enable_visitor_recognition'] ) ) {
-        update_option( TawkTo_Settings::TAWK_PRIVACY_OPTIONS, array( 'enable_visitor_recognition' => $visibility['enable_visitor_recognition'] ) );
+        $privacy_update = update_option( 
+            TawkTo_Settings::TAWK_PRIVACY_OPTIONS, 
+            array( 'enable_visitor_recognition' => $visibility['enable_visitor_recognition'] )
+        );
+        
+        if (!$privacy_update) {
+            error_log('TawkTo Upgrade 0.9.0: Failed to update privacy options');
+            $success = false;
+        }

         unset( $visibility['enable_visitor_recognition'] );
-        update_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS, $visibility );
+        $visibility_update = update_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS, $visibility );
+        
+        if (!$visibility_update) {
+            error_log('TawkTo Upgrade 0.9.0: Failed to update visibility options');
+            $success = false;
+        }
     }
+    
+    return $success;
 }
📝 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
public static function upgrade() {
$visibility = get_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS );
if ( isset( $visibility['enable_visitor_recognition'] ) ) {
update_option( TawkTo_Settings::TAWK_PRIVACY_OPTIONS, array( 'enable_visitor_recognition' => $visibility['enable_visitor_recognition'] ) );
unset( $visibility['enable_visitor_recognition'] );
update_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS, $visibility );
}
}
public static function upgrade() {
$success = true;
$visibility = get_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS );
if (false === $visibility) {
error_log('TawkTo Upgrade 0.9.0: Failed to retrieve visibility options');
return false;
}
if ( isset( $visibility['enable_visitor_recognition'] ) ) {
$privacy_update = update_option(
TawkTo_Settings::TAWK_PRIVACY_OPTIONS,
array( 'enable_visitor_recognition' => $visibility['enable_visitor_recognition'] )
);
if (!$privacy_update) {
error_log('TawkTo Upgrade 0.9.0: Failed to update privacy options');
$success = false;
}
unset( $visibility['enable_visitor_recognition'] );
$visibility_update = update_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS, $visibility );
if (!$visibility_update) {
error_log('TawkTo Upgrade 0.9.0: Failed to update visibility options');
$success = false;
}
}
return $success;
}

@eug-L eug-L force-pushed the feature/secure-mode branch from 4d9bc2a to ce5e8a4 Compare January 20, 2025 08:09
@eug-L eug-L force-pushed the feature/secure-mode branch from ce5e8a4 to 128eb19 Compare January 20, 2025 08:12
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 1

🧹 Nitpick comments (3)
tawkto/tawkto.php (3)

81-95: Add error handling for database operations.

The init_options method should handle potential database failures when calling update_option(). Consider wrapping the operations in try-catch blocks and logging any failures.

 public static function init_options() {
     $options = self::get_default_options();
+    try {
         if ( ! get_option( self::TAWK_VISIBILITY_OPTIONS, false ) ) {
-            update_option( self::TAWK_VISIBILITY_OPTIONS, $options['visibility'] );
+            if ( ! update_option( self::TAWK_VISIBILITY_OPTIONS, $options['visibility'] ) ) {
+                error_log( 'Failed to initialize Tawk.to visibility options' );
+            }
         }
         // Similar changes for privacy and security options
+    } catch ( Exception $e ) {
+        error_log( 'Error initializing Tawk.to options: ' . $e->getMessage() );
+    }
 }

416-439: Enhance API key validation.

The current validation only checks the length of the API key. Consider adding:

  1. Character set validation (alphanumeric check)
  2. Format validation (if the API key follows a specific pattern)
  3. Rate limiting for failed validation attempts
 private static function validate_js_api_key( &$fields, $default ) {
     if ( self::NO_CHANGE === $fields['js_api_key'] ) {
         unset( $fields['js_api_key'] );
         return;
     }

     delete_transient( self::TAWK_API_KEY );

     if ( '' === $fields['js_api_key'] ) {
         return;
     }

     try {
         if ( 40 !== strlen( $fields['js_api_key'] ) ) {
             throw new Exception( 'Invalid key. Please provide value with 40 characters' );
         }
+        if ( ! preg_match('/^[a-zA-Z0-9]+$/', $fields['js_api_key']) ) {
+            throw new Exception( 'Invalid key. Only alphanumeric characters are allowed' );
+        }
+        // Add rate limiting check here if needed

         $fields['js_api_key'] = self::get_encrypted_data( $fields['js_api_key'] );
     } catch ( Exception $e ) {
         self::show_tawk_options_error( 'Javascript API Key: ' . $e->getMessage() );
         $fields['js_api_key'] = $default;
     }
 }

658-664: Enhance plugin cleanup on deactivation.

Consider adding cleanup for any remaining transients and user meta data that might have been created during the plugin's operation.

 public static function deactivate() {
     delete_option( TawkTo_Settings::TAWK_PAGE_ID_VARIABLE );
     delete_option( TawkTo_Settings::TAWK_WIDGET_ID_VARIABLE );
     delete_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS );
     delete_option( TawkTo_Settings::TAWK_PRIVACY_OPTIONS );
     delete_option( TawkTo_Settings::TAWK_SECURITY_OPTIONS );
     delete_option( self::PLUGIN_VERSION_VARIABLE );

     delete_transient( TawkTo_Settings::TAWK_API_KEY );
+    // Clean up any user meta data
+    delete_metadata( 'user', 0, 'tawkto_user_settings', '', true );
+    // Clean up any additional transients
+    delete_transient( 'tawkto_rate_limit' );
 }
🛑 Comments failed to post (1)
tawkto/tawkto.php (1)

553-568: 🛠️ Refactor suggestion

Use constant-time comparison for API key validation.

When comparing API keys in your application logic, ensure you use hash_equals() for constant-time comparison to prevent timing attacks.

 public static function get_js_api_key() {
-    if ( ! empty( get_transient( self::TAWK_API_KEY ) ) ) {
+    $cached_key = get_transient( self::TAWK_API_KEY );
+    if ( ! empty( $cached_key ) ) {
         return get_transient( self::TAWK_API_KEY );
     }

     $security = get_option( self::TAWK_SECURITY_OPTIONS );

     if ( ! isset( $security['js_api_key'] ) ) {
         return '';
     }

     $key = self::get_decrypted_data( $security['js_api_key'] );
+    if ( empty( $key ) ) {
+        error_log( 'Failed to decrypt API key' );
+        return '';
+    }

     set_transient( self::TAWK_API_KEY, $key, 60 * 60 );

     return $key;
 }
📝 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.

		public static function get_js_api_key() {
			$cached_key = get_transient( self::TAWK_API_KEY );
			if ( ! empty( $cached_key ) ) {
				return get_transient( self::TAWK_API_KEY );
			}

			$security = get_option( self::TAWK_SECURITY_OPTIONS );

			if ( ! isset( $security['js_api_key'] ) ) {
				return '';
			}

			$key = self::get_decrypted_data( $security['js_api_key'] );
			if ( empty( $key ) ) {
				error_log( 'Failed to decrypt API key' );
				return '';
			}

			set_transient( self::TAWK_API_KEY, $key, 60 * 60 );

			return $key;

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

🧹 Nitpick comments (3)
tawkto/tawkto.php (3)

416-439: Add input validation for the JS API key format.

The JS API key validation could be enhanced to ensure it matches the expected format before encryption.

 private static function validate_js_api_key( &$fields, $default ) {
     if ( self::NO_CHANGE === $fields['js_api_key'] ) {
         unset( $fields['js_api_key'] );
         return;
     }

     delete_transient( self::TAWK_API_KEY );

     if ( '' === $fields['js_api_key'] ) {
         return;
     }

     try {
         if ( 40 !== strlen( $fields['js_api_key'] ) ) {
             throw new Exception( 'Invalid key. Please provide value with 40 characters' );
         }
+        if ( ! preg_match('/^[a-f0-9]{40}$/i', $fields['js_api_key']) ) {
+            throw new Exception( 'Invalid key format. Key should contain only hexadecimal characters' );
+        }

         $fields['js_api_key'] = self::get_encrypted_data( $fields['js_api_key'] );
     } catch ( Exception $e ) {
         self::show_tawk_options_error( 'Javascript API Key: ' . $e->getMessage() );

         $fields['js_api_key'] = $default;
     }
 }

553-568: Consider increasing cache duration for API key.

The API key is cached for only 1 hour. Consider increasing this to reduce database calls.

-set_transient( self::TAWK_API_KEY, $key, 60 * 60 );
+// Cache for 24 hours since API keys rarely change
+set_transient( self::TAWK_API_KEY, $key, 24 * 60 * 60 );

701-709: Add type casting for privacy options.

The privacy options should be explicitly type cast to ensure consistent behavior.

-if ( isset( $privacy ) && isset( $privacy['enable_visitor_recognition'] ) ) {
-    $enable_visitor_recognition = 1 === $privacy['enable_visitor_recognition'];
+if ( isset( $privacy['enable_visitor_recognition'] ) ) {
+    $enable_visitor_recognition = 1 === (int) $privacy['enable_visitor_recognition'];
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ce5e8a4 and 128eb19.

📒 Files selected for processing (3)
  • tawkto/tawkto.php (15 hunks)
  • tawkto/upgrade.manager.php (2 hunks)
  • tawkto/upgrades/version.090.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tawkto/upgrade.manager.php
  • tawkto/upgrades/version.090.php
🔇 Additional comments (5)
tawkto/tawkto.php (5)

28-34: LGTM! Well-structured constants for new features.

The new constants are well-named and logically grouped for privacy and security features.


81-95: LGTM! Proper initialization of default options.

The init_options method correctly initializes visibility, privacy, and security options with default values if they don't exist.


686-691: LGTM! Secure implementation of visitor hash.

The implementation correctly uses HMAC-SHA256 for visitor recognition and only generates the hash when both email and API key are present.


495-520: 🛠️ Refactor suggestion

Enhance error handling in encryption method.

The encryption method should validate the encrypted data length and handle OpenSSL errors.

 private static function get_encrypted_data( $data ) {
     if ( ! defined( 'SECURE_AUTH_KEY' ) ) {
         throw new Exception( 'SECURE_AUTH_KEY is not defined' );
     }

     try {
         $iv = random_bytes( self::CIPHER_IV_LENGTH );
     } catch ( Exception $e ) {
         throw new Exception( 'Error generating IV' );
     }

+    if ( ! in_array( self::CIPHER, openssl_get_cipher_methods() ) ) {
+        throw new Exception( 'Cipher method not supported' );
+    }

     $encrypted_data = openssl_encrypt( $data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );

     if ( false === $encrypted_data ) {
+        $error = openssl_error_string();
+        throw new Exception( 'Error encrypting data: ' . ($error ? $error : 'Unknown error') );
     }

     // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
     $encrypted_data = base64_encode( $iv . $encrypted_data );

     if ( false === $encrypted_data ) {
         throw new Exception( 'Error encoding data' );
     }

+    if ( empty( $encrypted_data ) ) {
+        throw new Exception( 'Encrypted data is empty' );
+    }

     return $encrypted_data;
 }

Likely invalid or redundant comment.


528-546: 🛠️ Refactor suggestion

Enhance error handling in decryption method.

The decryption method should validate input length and handle OpenSSL errors.

 private static function get_decrypted_data( $data ) {
+    if ( empty( $data ) ) {
+        return '';
+    }

     // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
     $decoded_data = base64_decode( $data );

     if ( false === $decoded_data ) {
         return '';
     }

+    if ( strlen( $decoded_data ) <= self::CIPHER_IV_LENGTH ) {
+        return '';
+    }

     $iv             = substr( $decoded_data, 0, self::CIPHER_IV_LENGTH );
     $encrypted_data = substr( $decoded_data, self::CIPHER_IV_LENGTH );

     $decrypted_data = openssl_decrypt( $encrypted_data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );

     if ( false === $decrypted_data ) {
+        $error = openssl_error_string();
+        error_log( 'Tawk.to decryption error: ' . ($error ? $error : 'Unknown error') );
         return '';
     }

     return $decrypted_data;
 }

Likely invalid or redundant comment.

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

🧹 Nitpick comments (2)
tests/Coverages/PrivacyOptionsTest.php (1)

88-88: Use a meaningful API key constant.

Instead of using a hardcoded string of repeated characters, consider defining a constant or using a more meaningful test API key. This improves test maintainability and makes it clearer what constitutes a valid API key format.

-self::$driver->find_element_and_input('#js-api-key', str_repeat('a', 40));
+const VALID_TEST_API_KEY = '1234567890abcdef1234567890abcdef12345678';
+self::$driver->find_element_and_input('#js-api-key', self::VALID_TEST_API_KEY);
tests/TestFiles/Modules/Web.php (1)

Line range hint 242-303: Good alignment with test improvements!

The changes consistently use appropriate success indicators:

  • '#successMessage' for widget operations
  • '#setting-error-settings_updated' for WordPress settings

Consider adding code comments to document these element IDs and their significance.

 // ensures widget is added.
-self::$driver->wait_until_element_is_located( '#successMessage' );
+// Wait for tawk.to's success message element that appears after widget operations
+self::$driver->wait_until_element_is_located( '#successMessage' );

 // ensures widget is added.
-self::$driver->wait_until_element_is_located( '#setting-error-settings_updated' );
+// Wait for WordPress's standard settings updated success message
+self::$driver->wait_until_element_is_located( '#setting-error-settings_updated' );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 128eb19 and 82e9e79.

📒 Files selected for processing (4)
  • tests/Coverages/PrivacyOptionsTest.php (3 hunks)
  • tests/Coverages/VisibilityOptionsTest.php (15 hunks)
  • tests/Coverages/WoocommerceOptionsTest.php (12 hunks)
  • tests/TestFiles/Modules/Web.php (4 hunks)
🔇 Additional comments (5)
tests/Coverages/PrivacyOptionsTest.php (3)

38-38: LGTM! Good setup enhancement.

Adding navigation to privacy options in setup ensures a consistent starting state for all tests.


64-64: LGTM! Good defensive testing.

The added assertion verifies that the hash is not present by default, which is important for security and complements the new visitor recognition test case.


90-91: Replace arbitrary wait with explicit wait condition.

Using a fixed wait time can make tests flaky. Instead, wait for a specific condition to be met.

-self::$driver->move_mouse_to('#submit-header')->click();
-self::$driver->wait_until_element_is_located('#setting-error-settings_updated');
+self::$driver->move_mouse_to('#submit-header')->click();
+self::$driver->wait_until_element_is_located('#setting-error-settings_updated', 'Settings saved.');
tests/Coverages/WoocommerceOptionsTest.php (1)

Line range hint 85-240: Great improvement to test reliability!

Replacing fixed delays with dynamic waits for the WordPress settings updated message makes the tests more robust and less prone to timing issues. The change is consistently applied across all test methods.

tests/Coverages/VisibilityOptionsTest.php (1)

Line range hint 81-411: Consistent implementation across test files!

The changes mirror those in WoocommerceOptionsTest.php, maintaining consistency in the test suite's approach to handling asynchronous operations.

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

🧹 Nitpick comments (2)
tawkto/tawkto.php (2)

552-567: Consider increasing the transient expiration time.

The API key is cached for only 1 hour. Consider increasing this to reduce database calls, as API keys typically have longer lifetimes.

-    set_transient( self::TAWK_API_KEY, $key, 60 * 60 );
+    set_transient( self::TAWK_API_KEY, $key, 24 * 60 * 60 ); // Cache for 24 hours

Line range hint 792-811: Consider caching the current URL.

The get_current_url() is called multiple times in the URL pattern matching logic. Consider caching the result.

Apply this diff to improve performance:

 if ( isset( $visibility['include_url'] ) && 1 === $visibility['include_url'] ) {
-    $current_url = $this->get_current_url();
+    $current_url = $current_url ?? $this->get_current_url();

     $included_url_list = $visibility['included_url_list'];
     $included_url_list = array_map( 'trim', preg_split( '/,/', $included_url_list ) );

     if ( UrlPatternMatcher::match( $current_url, $included_url_list ) ) {
         $display = true;
     }
 }

 if ( isset( $visibility['exclude_url'] ) && ( 1 === $visibility['exclude_url'] ) ) {
-    $current_url = $this->get_current_url();
+    $current_url = $current_url ?? $this->get_current_url();

     $excluded_url_list = $visibility['excluded_url_list'];
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 82e9e79 and aee2922.

📒 Files selected for processing (1)
  • tawkto/tawkto.php (15 hunks)
🔇 Additional comments (4)
tawkto/tawkto.php (4)

28-34: LGTM! Well-structured constants for new features.

The new constants are well-named and follow WordPress coding standards.


76-95: LGTM! Proper initialization of default options.

The init_options() method correctly initializes visibility, privacy, and security options with default values if they don't exist.


685-690: LGTM! Secure implementation of visitor hash.

The HMAC hash implementation for visitor recognition is secure and follows best practices.


527-545: ⚠️ Potential issue

Add length validation in decryption method.

The decryption method should validate the length of the decoded data before substring operations.

Apply this diff to add validation:

 private static function get_decrypted_data( $data ) {
     // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
     $decoded_data = base64_decode( $data );

     if ( false === $decoded_data ) {
         return '';
     }

+    if ( strlen($decoded_data) <= self::CIPHER_IV_LENGTH ) {
+        return '';
+    }
+
     $iv             = substr( $decoded_data, 0, self::CIPHER_IV_LENGTH );
     $encrypted_data = substr( $decoded_data, self::CIPHER_IV_LENGTH );

Likely invalid or redundant comment.

Comment on lines +494 to +519
private static function get_encrypted_data( $data ) {
if ( ! defined( 'SECURE_AUTH_KEY' ) ) {
throw new Exception( 'SECURE_AUTH_KEY is not defined' );
}

try {
$iv = random_bytes( self::CIPHER_IV_LENGTH );
} catch ( Exception $e ) {
throw new Exception( 'Error generating IV' );
}

$encrypted_data = openssl_encrypt( $data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );

if ( false === $encrypted_data ) {
throw new Exception( 'Error encrypting data' );
}

// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$encrypted_data = base64_encode( $iv . $encrypted_data );

if ( false === $encrypted_data ) {
throw new Exception( 'Error encoding data' );
}

return $encrypted_data;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for openssl_encrypt failures.

The encryption method needs additional error handling:

  1. Check if the OpenSSL extension is loaded
  2. Verify the cipher method exists

Apply this diff to improve error handling:

 private static function get_encrypted_data( $data ) {
+    if (!extension_loaded('openssl')) {
+        throw new Exception('OpenSSL extension is not loaded');
+    }
+
+    if (!in_array(self::CIPHER, openssl_get_cipher_methods())) {
+        throw new Exception('Cipher method not supported');
+    }
+
     if ( ! defined( 'SECURE_AUTH_KEY' ) ) {
         throw new Exception( 'SECURE_AUTH_KEY is not defined' );
     }
📝 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
private static function get_encrypted_data( $data ) {
if ( ! defined( 'SECURE_AUTH_KEY' ) ) {
throw new Exception( 'SECURE_AUTH_KEY is not defined' );
}
try {
$iv = random_bytes( self::CIPHER_IV_LENGTH );
} catch ( Exception $e ) {
throw new Exception( 'Error generating IV' );
}
$encrypted_data = openssl_encrypt( $data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );
if ( false === $encrypted_data ) {
throw new Exception( 'Error encrypting data' );
}
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$encrypted_data = base64_encode( $iv . $encrypted_data );
if ( false === $encrypted_data ) {
throw new Exception( 'Error encoding data' );
}
return $encrypted_data;
}
private static function get_encrypted_data( $data ) {
if (!extension_loaded('openssl')) {
throw new Exception('OpenSSL extension is not loaded');
}
if (!in_array(self::CIPHER, openssl_get_cipher_methods())) {
throw new Exception('Cipher method not supported');
}
if ( ! defined( 'SECURE_AUTH_KEY' ) ) {
throw new Exception( 'SECURE_AUTH_KEY is not defined' );
}
try {
$iv = random_bytes( self::CIPHER_IV_LENGTH );
} catch ( Exception $e ) {
throw new Exception( 'Error generating IV' );
}
$encrypted_data = openssl_encrypt( $data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );
if ( false === $encrypted_data ) {
throw new Exception( 'Error encrypting data' );
}
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$encrypted_data = base64_encode( $iv . $encrypted_data );
if ( false === $encrypted_data ) {
throw new Exception( 'Error encoding data' );
}
return $encrypted_data;
}

@eug-L eug-L merged commit c1f5681 into master Jan 27, 2025
6 checks passed
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.

3 participants