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

🔨 chore: Appsero integration via Composer with Mozart #92

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

devAsadNur
Copy link
Member

@devAsadNur devAsadNur commented Nov 29, 2024

Integrated Appsero via Composer with Mozart.

Summary by CodeRabbit

  • New Features

    • Introduced a composer.json file for WPML compatibility with the Dokan plugin.
    • Added classes for managing plugin data, insights, and licensing functionalities.
  • Bug Fixes

    • Updated client instantiation to use namespaced classes for better organization.
  • Chores

    • Added a new entry to the .gitignore to exclude the vendor directory from version control.

Copy link

coderabbitai bot commented Nov 29, 2024

Walkthrough

The changes in this pull request involve the addition of a .gitignore entry to exclude the vendor directory, the creation of a new composer.json file for the wedevs/dokan-wpml project, and the introduction of several new classes (Client, Insights, and License) within the WeDevs\DokanWpml\Dependencies\Appsero namespace. These classes provide functionalities for managing plugin data, tracking usage, and handling license operations. Additionally, modifications were made to the Dokan_WPML class to incorporate Composer's autoloading.

Changes

File Change Summary
.gitignore Added entry: /vendor to ignore the vendor directory.
composer.json New file created with project metadata, dependencies, autoloading, and scripts for WPML compatibility.
dependencies/Appsero/Client.php New class Client added with properties and methods for managing plugin data and updates.
dependencies/Appsero/Insights.php New class Insights added for tracking plugin usage and managing user consent.
dependencies/Appsero/License.php New class License added for managing license activation, deactivation, and validation.
dokan-wpml.php Updated Dokan_WPML class to include Composer autoload and modified client instantiation to use namespaced class.

Poem

In the garden where plugins play,
A new .gitignore leads the way.
With Composer's help, dependencies bloom,
Insights and Licenses make room!
A hop and a skip, the code's now bright,
For Dokan and WPML, all feels right! 🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bbb82b5 and a0aacb1.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • composer.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • composer.json

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

@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 (13)
dependencies/Appsero/Client.php (3)

17-17: Consider making the version configurable

The hardcoded version 2.0.4 could become outdated. Consider making it configurable through composer.json or environment variables.


24-93: Consider implementing property access control

The class exposes several public properties without validation. Consider:

  1. Making properties private/protected and providing getter/setter methods
  2. Adding validation in setters to ensure data integrity
  3. Using readonly properties where applicable (if PHP 8.1+ is supported)

250-254: Improve local server detection

The current implementation of is_local_server() could be enhanced:

  1. Add additional local development domains
  2. Consider checking server hostname
  3. Add development environment detection
     public function is_local_server() {
-        $is_local = isset( $_SERVER['REMOTE_ADDR'] ) && in_array( $_SERVER['REMOTE_ADDR'], [ '127.0.0.1', '::1' ], true );
+        $is_local = false;
+
+        // Check IP address
+        if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
+            $is_local = in_array( $_SERVER['REMOTE_ADDR'], [ '127.0.0.1', '::1' ], true );
+        }
+
+        // Check hostname
+        if ( ! $is_local && isset( $_SERVER['HTTP_HOST'] ) ) {
+            $domains = [ '.local', '.test', '.example', '.invalid', '.localhost' ];
+            foreach ( $domains as $domain ) {
+                if ( strpos( $_SERVER['HTTP_HOST'], $domain ) !== false ) {
+                    $is_local = true;
+                    break;
+                }
+            }
+        }
+
+        // Check development environment
+        if ( ! $is_local && defined( 'WP_ENVIRONMENT_TYPE' ) ) {
+            $is_local = WP_ENVIRONMENT_TYPE === 'local' || WP_ENVIRONMENT_TYPE === 'development';
+        }
 
         return apply_filters( 'appsero_is_local', $is_local );
     }
dokan-wpml.php (1)

Line range hint 184-188: LGTM! Consider extracting configuration values

The Appsero client initialization looks good with proper namespace usage. However, consider extracting the client ID and plugin name to a configuration file for better maintainability.

+// In a config file (e.g., config.php)
+define('DOKAN_WPML_APPSERO_CLIENT_ID', 'f7973783-e0d0-4d56-bbba-229e5581b0cd');
+define('DOKAN_WPML_PLUGIN_NAME', 'Dokan - WPML Integration');
+
 // In the current file
-$client = new WeDevs\DokanWpml\Dependencies\Appsero\Client( 'f7973783-e0d0-4d56-bbba-229e5581b0cd', 'Dokan - WPML Integration', __FILE__ );
+$client = new WeDevs\DokanWpml\Dependencies\Appsero\Client( DOKAN_WPML_APPSERO_CLIENT_ID, DOKAN_WPML_PLUGIN_NAME, __FILE__ );
dependencies/Appsero/License.php (8)

48-51: Typo in comment: Correct "Corn" to "Cron"

The comment and property description contain a typo. Replace "Corn" with "Cron" to correctly refer to scheduled tasks.


79-79: Typo in comment: Correct "corn" to "cron"

In the comment // Active/Deactive corn schedule, "corn" should be "cron".


753-755: Typo in comment: Correct "sicense" to "license"

The comment * Schedule daily sicense checker event contains a misspelling. Replace "sicense" with "license".


318-318: Typo in error message: Correct "vefification" to "verification"

The error message has a typo:

- $this->error = $this->client->__trans( 'Nonce vefification failed.' );
+ $this->error = $this->client->__trans( 'Nonce verification failed.' );

190-195: Ensure all user-facing strings are translatable

The success message in refresh_license_api() is not translatable. Wrap the string with the translation function to support localization.

Apply this diff to make the message translatable:

- wp_send_json_success(
-     [
-         'message' => 'License refreshed successfully.',
-     ],
-     200
- );
+ wp_send_json_success(
+     [
+         'message' => $this->client->__trans( 'License refreshed successfully.' ),
+     ],
+     200
+ );

792-808: Enhance security by masking the full license key

In the get_input_license_value() method, the license key is partially displayed when deactivating. For better security, consider masking the entire license key or displaying only the last few characters.

Here’s how you might adjust the code:

- if ( 'deactive' === $action ) {
-     $key_length = strlen( $license['key'] );
-
-     return str_pad(
-         substr( $license['key'], 0, $key_length / 2 ),
-         $key_length,
-         '*'
-     );
- }
+ if ( 'deactive' === $action ) {
+     // Mask all but the last 4 characters
+     $masked_key = str_repeat( '*', max( 0, strlen( $license['key'] ) - 4 ) );
+     $masked_key .= substr( $license['key'], -4 );
+     return $masked_key;
+ }

282-285: Optimize SVG inclusion for maintainability

Consider moving the inline SVG code to a separate file or using an <img> tag to reference an SVG file. This can improve maintainability and readability of the HTML.


709-718: Simplify function calls without call_user_func

Directly calling the function improves readability and avoids unnecessary overhead. Replace call_user_func with direct function calls.

Apply this diff:

- call_user_func(
-     'add_menu_page',
-     $this->menu_args['page_title'],
-     $this->menu_args['menu_title'],
-     $this->menu_args['capability'],
-     $this->menu_args['menu_slug'],
-     [ $this, 'menu_output' ],
-     $this->menu_args['icon_url'],
-     $this->menu_args['position']
- );
+ add_menu_page(
+     $this->menu_args['page_title'],
+     $this->menu_args['menu_title'],
+     $this->menu_args['capability'],
+     $this->menu_args['menu_slug'],
+     [ $this, 'menu_output' ],
+     $this->menu_args['icon_url'],
+     $this->menu_args['position']
+ );
dependencies/Appsero/Insights.php (1)

446-450: Enqueue scripts instead of using inline JavaScript

Using inline JavaScript can be less maintainable and may pose security risks. It's better to enqueue scripts properly using wp_enqueue_script.

Refactor the inline script to an external JS file and enqueue it:

  1. Move the script content to a separate JS file.
  2. Use wp_enqueue_script to load the script in the appropriate context.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9ecb21d and bbb82b5.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • .gitignore (1 hunks)
  • composer.json (1 hunks)
  • dependencies/Appsero/Client.php (1 hunks)
  • dependencies/Appsero/Insights.php (1 hunks)
  • dependencies/Appsero/License.php (1 hunks)
  • dokan-wpml.php (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • composer.json
🔇 Additional comments (5)
dependencies/Appsero/License.php (1)

775-784: Review hook registration for activation and deactivation

Ensure that the activation and deactivation hooks are registered correctly for both plugins and themes. Double-check the $this->client->type value to confirm it matches the expected types.

Run the following script to verify the type property:

✅ Verification successful

Let me gather more context about the Client class initialization to ensure the type is properly set.


Let me try a different approach to check the Client class initialization.


The hook registration is correctly implemented based on the type determination

The verification shows that the type property is properly set in the Client class's set_basename_and_slug() method. The logic determines the type as follows:

  • Sets type = 'plugin' if the file is not in the themes directory
  • Sets type = 'theme' if the file is in the themes directory

The switch statement in License.php correctly uses this type to register appropriate hooks:

  • For plugins: Uses register_activation_hook and register_deactivation_hook
  • For themes: Uses after_switch_theme and switch_theme actions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that $this->client->type is correctly set to 'plugin' or 'theme' in all instances.

# Search for the assignment of $this->client->type
rg "\$this->client->type\s*="

# Find all instances where 'type' is set or used
rg "\->type"

# Expected: $this->client->type should only be 'plugin' or 'theme'

Length of output: 532


Script:

#!/bin/bash
# Check the Client class constructor and initialization
ast-grep --pattern 'class Client {
  $$$
  public function __construct($hash, $name, $file) {
    $$$
  }
  $$$
}'

# Also check how the type is determined
ast-grep --pattern 'if ($$$) {
  $this->type = $_;
}'

Length of output: 172


Script:

#!/bin/bash
# Get the content of Client.php to see how type is determined
cat dependencies/Appsero/Client.php

# Also check for any switch statements using type
rg "switch.*type" -A 2

Length of output: 7168

dependencies/Appsero/Insights.php (4)

906-906: ⚠️ Potential issue

Escape text output to ensure safety

The reason text should be escaped using esc_html to prevent potential XSS issues.

Apply this diff:

-<div class="wd-de-reason-text"><?php echo $reason['text']; ?></div>
+<div class="wd-de-reason-text"><?php echo esc_html( $reason['text'] ); ?></div>

Likely invalid or redundant comment.


373-373: ⚠️ Potential issue

Correct the condition when checking for a dot in the host

Using ! strpos( $host, '.' ) is problematic because strpos returns 0 when the dot is at the beginning of the string, which is a falsy value. Use false === strpos( $host, '.' ) instead.

Apply this diff:

-            ! strpos( $host, '.' ) ||
+            false === strpos( $host, '.' ) ||

Likely invalid or redundant comment.


904-904: ⚠️ Potential issue

Escape attributes to prevent XSS vulnerabilities

The value attribute of the input element should be escaped using esc_attr to ensure safety against XSS attacks.

Apply this diff:

-<input type="radio" name="selected-reason" value="<?php echo $reason['id']; ?>">
+<input type="radio" name="selected-reason" value="<?php echo esc_attr( $reason['id'] ); ?>">

Likely invalid or redundant comment.


905-905: ⚠️ Potential issue

Sanitize SVG output to prevent potential XSS

When outputting SVG content, it's important to sanitize it appropriately. Use wp_kses with allowed HTML tags and attributes for SVG.

Apply this diff:

-<div class="wd-de-reason-icon"><?php echo $reason['icon']; ?></div>
+<div class="wd-de-reason-icon"><?php echo wp_kses( $reason['icon'], $this->allowed_svg_tags() ); ?></div>

Define the allowed_svg_tags() method to specify permissible SVG elements and attributes.

Likely invalid or redundant comment.

Comment on lines +102 to +108
public function __construct( $hash, $name, $file ) {
$this->hash = $hash;
$this->name = $name;
$this->file = $file;

$this->set_basename_and_slug();
}
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 parameter validation in constructor

The constructor accepts parameters without validation. Consider:

  1. Adding parameter validation to ensure non-empty values
  2. Adding type hints if minimum PHP version allows
  3. Validating file path existence
-    public function __construct( $hash, $name, $file ) {
+    public function __construct( $hash, $name, $file ) {
+        if ( empty( $hash ) || empty( $name ) || empty( $file ) ) {
+            throw new \InvalidArgumentException( 'Hash, name, and file parameters are required' );
+        }
+        
+        if ( ! file_exists( $file ) ) {
+            throw new \InvalidArgumentException( 'Invalid file path provided' );
+        }
+
         $this->hash = $hash;
         $this->name = $name;
         $this->file = $file;
📝 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 function __construct( $hash, $name, $file ) {
$this->hash = $hash;
$this->name = $name;
$this->file = $file;
$this->set_basename_and_slug();
}
public function __construct( $hash, $name, $file ) {
if ( empty( $hash ) || empty( $name ) || empty( $file ) ) {
throw new \InvalidArgumentException( 'Hash, name, and file parameters are required' );
}
if ( ! file_exists( $file ) ) {
throw new \InvalidArgumentException( 'Invalid file path provided' );
}
$this->hash = $hash;
$this->name = $name;
$this->file = $file;
$this->set_basename_and_slug();
}

Comment on lines +135 to +148
public function updater() {
// do not show update notice on ajax request and rest api request
if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
return;
}

// show deprecated notice
_deprecated_function( __CLASS__ . '::updater', '2.0', '\WeDevs\DokanWpml\Dependencies\Appsero\Updater::init($client);, for more details please visit: https://appsero.com/docs/appsero-developers-guide/appsero-client/appsero-sdk-updater-changes/' );

// initialize the new updater
if ( method_exists( '\WeDevs\DokanWpml\Dependencies\Appsero\Updater', 'init' ) ) {
\WeDevs\DokanWpml\Dependencies\Appsero\Updater::init( $this );
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address deprecated updater implementation

The updater method uses a deprecated implementation. Consider:

  1. Updating to use the new Updater::init approach directly
  2. Removing the deprecated method in future versions
  3. Adding migration guide for users
-    public function updater() {
-        if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
-            return;
-        }
-
-        _deprecated_function( __CLASS__ . '::updater', '2.0', '\WeDevs\DokanWpml\Dependencies\Appsero\Updater::init($client);, for more details please visit: https://appsero.com/docs/appsero-developers-guide/appsero-client/appsero-sdk-updater-changes/' );
-
-        if ( method_exists( '\WeDevs\DokanWpml\Dependencies\Appsero\Updater', 'init' ) ) {
-            \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init( $this );
-        }
-    }
+    /**
+     * @deprecated 2.0 Use \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init() instead
+     */
+    public function updater() {
+        trigger_error(
+            'Method ' . __METHOD__ . ' is deprecated. Use \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init() instead.',
+            E_USER_DEPRECATED
+        );
+        
+        if ( class_exists( '\WeDevs\DokanWpml\Dependencies\Appsero\Updater' ) ) {
+            return \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init( $this );
+        }
+    }
📝 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 function updater() {
// do not show update notice on ajax request and rest api request
if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
return;
}
// show deprecated notice
_deprecated_function( __CLASS__ . '::updater', '2.0', '\WeDevs\DokanWpml\Dependencies\Appsero\Updater::init($client);, for more details please visit: https://appsero.com/docs/appsero-developers-guide/appsero-client/appsero-sdk-updater-changes/' );
// initialize the new updater
if ( method_exists( '\WeDevs\DokanWpml\Dependencies\Appsero\Updater', 'init' ) ) {
\WeDevs\DokanWpml\Dependencies\Appsero\Updater::init( $this );
}
}
/**
* @deprecated 2.0 Use \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init() instead
*/
public function updater() {
trigger_error(
'Method ' . __METHOD__ . ' is deprecated. Use \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init() instead.',
E_USER_DEPRECATED
);
if ( class_exists( '\WeDevs\DokanWpml\Dependencies\Appsero\Updater' ) ) {
return \WeDevs\DokanWpml\Dependencies\Appsero\Updater::init( $this );
}
}

Comment on lines +220 to +243
public function send_request( $params, $route, $blocking = false ) {
$url = $this->endpoint() . $route;

$headers = [
'user-agent' => 'Appsero/' . md5( esc_url( home_url() ) ) . ';',
'Accept' => 'application/json',
];

$response = wp_remote_post(
$url,
[
'method' => 'POST',
'timeout' => 30,
'redirection' => 5,
'httpversion' => '1.0',
'blocking' => $blocking,
'headers' => $headers,
'body' => array_merge( $params, [ 'client' => $this->version ] ),
'cookies' => [],
]
);

return $response;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance security in send_request method

The API communication implementation needs security improvements:

  1. Add nonce verification for authenticated requests
  2. Implement rate limiting
  3. Add request/response logging for debugging
  4. Consider using wp_json_encode for body parameters
     public function send_request( $params, $route, $blocking = false ) {
+        if ( ! current_user_can( 'manage_options' ) ) {
+            return new \WP_Error( 'unauthorized', 'Unauthorized request' );
+        }
+
         $url = $this->endpoint() . $route;
 
         $headers = [
             'user-agent' => 'Appsero/' . md5( esc_url( home_url() ) ) . ';',
             'Accept'     => 'application/json',
         ];
 
+        $body = array_merge( $params, [ 'client' => $this->version ] );
+
         $response = wp_remote_post(
             $url,
             [
                 'method'      => 'POST',
                 'timeout'     => 30,
                 'redirection' => 5,
                 'httpversion' => '1.0',
                 'blocking'    => $blocking,
                 'headers'     => $headers,
-                'body'        => array_merge( $params, [ 'client' => $this->version ] ),
+                'body'        => wp_json_encode( $body ),
                 'cookies'     => [],
             ]
         );
 
+        if ( is_wp_error( $response ) ) {
+            error_log( sprintf( 'Appsero API request failed: %s', $response->get_error_message() ) );
+        }
+
         return $response;
     }

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

dokan-wpml.php Outdated
@@ -76,6 +76,8 @@ class Dokan_WPML {
* @uses add_action()
*/
public function __construct() {
require_once __DIR__ . '/vendor/autoload.php';
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Incorrect path for vendor autoload

The require_once __DIR__ . '/vendor/autoload.php' statement is incorrect as:

  1. The vendor directory doesn't exist at the expected location
  2. The project uses a custom autoloader configuration in composer.json that maps to the dependencies/ directory instead
  3. The dependencies/ directory exists and contains the Appsero directory, which aligns with the PSR-4 autoload configuration

The autoload path should be adjusted to match the project's structure or Composer's autoload configuration should be updated accordingly.

🔗 Analysis chain

Verify vendor directory location

The autoload path assumes the vendor directory is at the same level as this file. While the path is correctly constructed using __DIR__, we should verify this assumption.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if vendor directory exists at the expected location
if [ -d "vendor" ]; then
    echo "✓ Vendor directory exists at repository root"
    if [ -f "vendor/autoload.php" ]; then
        echo "✓ autoload.php exists in vendor directory"
    else
        echo "✗ autoload.php not found in vendor directory"
    fi
else
    echo "✗ Vendor directory not found at repository root"
fi

# Check composer.json for autoload configuration
if [ -f "composer.json" ]; then
    echo "Checking composer.json configuration:"
    cat composer.json | jq '.autoload'
fi

Length of output: 349


Script:

#!/bin/bash
# Check if dependencies directory exists and contains the expected structure
if [ -d "dependencies" ]; then
    echo "✓ Dependencies directory exists"
    ls -la dependencies/
else
    echo "✗ Dependencies directory not found"
fi

# Check the actual location of dokan-wpml.php
fd -t f "dokan-wpml.php" -x dirname {}

# Check if there's a vendor directory relative to dokan-wpml.php location
fd -t f "dokan-wpml.php" -x sh -c 'if [ -d "$(dirname {})/vendor" ]; then echo "✓ Vendor exists at $(dirname {})/vendor"; else echo "✗ No vendor at $(dirname {})/vendor"; fi'

Length of output: 528

Comment on lines +248 to +255
if ( isset( $_POST['_nonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_nonce'] ) ), $this->client->name ) ) {
$form_data = [
'_nonce' => sanitize_key( wp_unslash( $_POST['_nonce'] ) ),
'_action' => isset( $_POST['_action'] ) ? sanitize_text_field( wp_unslash( $_POST['_action'] ) ) : '',
'license_key' => isset( $_POST['license_key'] ) ? sanitize_text_field( wp_unslash( $_POST['license_key'] ) ) : '',
];
$this->license_form_submit( $form_data );
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Sanitize and validate form inputs thoroughly

While you are sanitizing inputs, consider adding additional validation to ensure that the form data meets expected formats, especially for the license_key.

You might include:

  • Checking the license_key format with a regular expression.
  • Ensuring that the _action value is one of the allowed actions.

Comment on lines +1048 to +1062
private function get_user_ip_address() {
$response = wp_remote_get( 'https://icanhazip.com/' );

if ( is_wp_error( $response ) ) {
return '';
}

$ip = trim( wp_remote_retrieve_body( $response ) );

if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return '';
}

return $ip;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Review the necessity and method of fetching the server's external IP

The get_user_ip_address function fetches the server's external IP by making an HTTP request to https://icanhazip.com/. This may have performance implications and could fail if the external service is unreachable. Additionally, collecting the server's IP may raise privacy concerns.

Consider whether retrieving the server's external IP is necessary. If so, cache the result to avoid repeated HTTP requests, and ensure compliance with privacy regulations.


$data = $this->get_tracking_data();
$data['reason_id'] = sanitize_text_field( wp_unslash( $_POST['reason_id'] ) );
$data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( sanitize_text_field( wp_unslash( $_REQUEST['reason_info'] ) ) ) : '';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use sanitize_textarea_field for multiline inputs

When sanitizing input from a textarea, sanitize_textarea_field is more appropriate than sanitize_text_field as it preserves new lines.

Apply this diff:

-$data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( sanitize_text_field( wp_unslash( $_REQUEST['reason_info'] ) ) ) : '';
+$data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( sanitize_textarea_field( wp_unslash( $_REQUEST['reason_info'] ) ) ) : '';
📝 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
$data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( sanitize_text_field( wp_unslash( $_REQUEST['reason_info'] ) ) ) : '';
$data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( sanitize_textarea_field( wp_unslash( $_REQUEST['reason_info'] ) ) ) : '';

Comment on lines +479 to +481
private function is_valid_request() {
return isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), '_wpnonce' );
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use specific action names when verifying nonces

Using a generic action like '_wpnonce' in wp_verify_nonce reduces the security effectiveness of the nonce. It's better to use a unique and specific action name.

Apply this diff to use a specific nonce action:

 private function is_valid_request() {
-    return isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), '_wpnonce' );
+    return isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), $this->client->slug . '_nonce' );
 }

Ensure you create the nonce with the same action elsewhere in your code.

📝 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 function is_valid_request() {
return isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), '_wpnonce' );
}
private function is_valid_request() {
return isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), $this->client->slug . '_nonce' );
}

Comment on lines +193 to +196
$response = $this->client->send_request( $tracking_data, 'track' );

update_option( $this->client->slug . '_tracking_last_send', time() );
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle potential errors from send_request

The response from $this->client->send_request is not checked for errors. If the request fails, it might cause issues or hide problems. It's recommended to check if $response is a WP_Error and handle it appropriately.

Apply this diff to handle errors:

 $response = $this->client->send_request( $tracking_data, 'track' );

+if ( is_wp_error( $response ) ) {
+    // Log the error or handle it as needed
+    error_log( 'Failed to send tracking data: ' . $response->get_error_message() );
+    return;
+}

 update_option( $this->client->slug . '_tracking_last_send', time() );
📝 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
$response = $this->client->send_request( $tracking_data, 'track' );
update_option( $this->client->slug . '_tracking_last_send', time() );
}
$response = $this->client->send_request( $tracking_data, 'track' );
if ( is_wp_error( $response ) ) {
// Log the error or handle it as needed
error_log( 'Failed to send tracking data: ' . $response->get_error_message() );
return;
}
update_option( $this->client->slug . '_tracking_last_send', time() );
}

Comment on lines +56 to +64
public function __construct( $client, $name = null, $file = null ) {
if ( is_string( $client ) && ! empty( $name ) && ! empty( $file ) ) {
$client = new Client( $client, $name, $file );
}

if ( is_object( $client ) && is_a( $client, 'WeDevs\DokanWpml\Dependencies\Appsero\Client' ) ) {
$this->client = $client;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure $this->client is properly initialized

The constructor may leave $this->client unset if the $client parameter doesn't meet the expected conditions. This could lead to errors when $this->client is used later in the class. Consider adding error handling to ensure $this->client is always properly initialized.

Apply this diff to handle the initialization:

 public function __construct( $client, $name = null, $file = null ) {
     if ( is_string( $client ) && ! empty( $name ) && ! empty( $file ) ) {
         $client = new Client( $client, $name, $file );
     }

     if ( is_object( $client ) && is_a( $client, 'WeDevs\DokanWpml\Dependencies\Appsero\Client' ) ) {
         $this->client = $client;
+    } else {
+        // Handle invalid $client parameter
+        throw new \InvalidArgumentException( 'Invalid client parameter provided to Insights constructor.' );
     }
 }
📝 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 function __construct( $client, $name = null, $file = null ) {
if ( is_string( $client ) && ! empty( $name ) && ! empty( $file ) ) {
$client = new Client( $client, $name, $file );
}
if ( is_object( $client ) && is_a( $client, 'WeDevs\DokanWpml\Dependencies\Appsero\Client' ) ) {
$this->client = $client;
}
}
public function __construct( $client, $name = null, $file = null ) {
if ( is_string( $client ) && ! empty( $name ) && ! empty( $file ) ) {
$client = new Client( $client, $name, $file );
}
if ( is_object( $client ) && is_a( $client, 'WeDevs\DokanWpml\Dependencies\Appsero\Client' ) ) {
$this->client = $client;
} else {
// Handle invalid $client parameter
throw new \InvalidArgumentException( 'Invalid client parameter provided to Insights constructor.' );
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant