�PNG  IHDR22?�� pHYs  �� OiCCPPhotoshop ICC profilexڝSgTS�=���BK���KoR RB���&*! J�!��Q�EEȠ�����Q, � ��!���������{�kּ������>���� �H3Q5� �B�������.@� $p�d!s�#�~<<+"��x� �M��0���B�\���t�8K�@z�B�@F���&S�`�cb�P-`'������{[�!�� e�Dh;��V�EX0fK�9�-0IWfH���� � 0Q��){`�##x��F�W<�+��*x��<�$9E�[-qWW.(�I+6aa�@.�y�2�4���������x����6��_-��"bb���ϫp@�t~��,/��;�m��%�h^ �u��f�@����W�p�~<�5�j>{�-�]c�K'Xt����o��(�h���w��?�G�%�fI�q^D$.Tʳ?�D��*�A��,���� �`6�B$��BB d�r`)��B(�Ͱ*`/�@4�Qh��p.�U�=p�a��(�� A�a!ڈb�X#����!�H�$ ɈQ"K�5H1R�T UH�=r9�\F��;�2����G1���Q=� �C��7�F� �dt1�����r�=�6��Ыhڏ>C�0��3�l0.��B�8, �c˱"� ���V����cϱw�E� 6wB aAHXLXN�H� $4� 7 �Q�'"��K�&���b21�XH,#��/{�C�7$�C2'��I��T��F�nR#�,��4H#���dk�9�, +ȅ����3��!�[ �b@q��S�(R�jJ��4�e�2AU��Rݨ�T5�ZB���R�Q��4u�9̓IK�����hh�i��t�ݕN��W���G���w ��Ljg(�gw��L�Ӌ�T071���oUX*�*|�� �J�&�*/T����ު U�U�T��^S}�FU3S� Ԗ�U��P�SSg�;���g�oT?�~Y��Y�L�OC�Q��_�� c�x,!k ��u�5�&���|v*�����=���9C3J3W�R�f?�q��tN �(���~���)�)�4L�1e\k����X�H�Q�G�6������E�Y��A�J'\'Gg����S�Sݧ �M=:��.�k���Dw�n��^��Lo��y���}/�T�m���G X� $� �<�5qo</���QC]�@C�a�a�ᄑ��<��F�F�i�\�$�m�mƣ&&!&KM�M�RM��)�;L;L���͢�֙5�=1�2��כ߷`ZxZ,����eI��Z�Yn�Z9Y�XUZ]�F���%ֻ�����N�N���gð�ɶ�����ۮ�m�}agbg�Ů��}�}��= ���Z~s�r:V:ޚΜ�?}����/gX���3��)�i�S��Ggg�s�󈋉K��.�>.���Ƚ�Jt�q]�z���������ۯ�6�i�ܟ�4�)�Y3s���C�Q��? ��0k߬~OCO�g��#/c/�W�װ��w��a�>�>r��>�<7�2�Y_�7��ȷ�O�o�_��C#�d�z����%g��A�[��z|!��?:�e����A���AA�������!h�쐭!��Α�i�P~���a�a��~ '���W�?�p�X�1�5w��Cs�D�D�Dޛg1O9�-J5*>�.j<�7�4�?�.fY��X�XIlK9.*�6nl������� �{�/�]py�����.,:�@L�N8��A*��%�w%� y��g"/�6ш�C\*N�H*Mz�쑼5y$�3�,幄'���L Lݛ:��v m2=:�1����qB�!M��g�g�fvˬe����n��/��k���Y- �B��TZ(�*�geWf�͉�9���+��̳�ې7�����ᒶ��KW-X潬j9�������(�x��oʿ�ܔ���Ĺd�f�f���-�[����n �ڴ �V����E�/��(ۻ��C���<��e����;?T�T�T�T6��ݵa��n��{��4���[���>ɾ�UUM�f�e�I���?�������m]�Nmq����#�׹���=TR��+�G�����w- 6 U����#pDy��� �� :�v�{���vg/jB��F�S��[b[�O�>����z�G��499�?r����C�d�&����ˮ/~�����јѡ�򗓿m|������������x31^�V���w�w��O�| (�h���SЧ��������c3-� cHRMz%������u0�`:�o�_�F5IDATx��ytUս�?�;���fN$$��@B�!�k��� -*�gE���j�O�kQ�ʠ`U�B_ �AÐ0%�Ȕ�@r3ޛ;߳��$�@��v����^�s����������$��ߢ !�W��5�̱'-٘������4�>�Rƫ$�G��0U�H}�_ _� #��k�MJ/=�9Du *ڲ�k�PQ;0��j|�*�$�^컂脛y���>�z×� B��� ��(�:$�ʔ_�6��C!B����MVx��a��ζyY����j��ƋY2�9�F�{r��r�딟F��Q��hY6��� x��[l��RC��������nt@��;P��3��� F�w��7��:�P���ɋ'���mj���Bƪ�$�Q�L��y��߷g��\���z����v5�p�v$�v櫻�`6~K �Y�X����,�:��"�o�e0II"(��,������l_�C���/4�+f�E�!q��:����u����c��樼.UE�@� �]K�W���$-�h]�JG���Dz)�`��.�A/ ���mᏣc�3jK� �!�>���$�Q�!�_�? \�C2(E �M$Z I{�U���s�ǣ���=��@Բe ޻��W�����@ �w����}�9v,)��.}*|"�s��di���,���G�]W�AC�������|<����[��ߕ������K�*�G nߴ!��‹��J�!��6(L�a)��Am��������;adf��:�xm��nh�;֦I��(���o�C~ډa?�=��s����h��E������xp����)���_���t��W�����´;8���`dz=(��@@�+,�;�`�fhl��XȌ�3,uI�~z(J����� �ZX�A����H'�À�㜦�82|��a�n�z2��I���� ���EY�h�ε<�0�q�{��C��v�˅��?"@��χ�x�WS+��N�~�KQ�J�8ل�� _.W��� !TU{�����,��"�� �;�������HI S� ��))�o�1�H�D� ���� (� ;����"Ҕ��,���� �F=���O��&�Ph>�`���y��߲ &S��P�<��'��1s�1cĂ�N����2)����@�u��tv��<�+�-&�����h��[!FSG���' � z z0��<����8d&�����E0�,��<��"!P�b4��� �{S�����X����.�囤���T��hEF��X�q0"-<����h���]�Py*����F⩍��@v�˂�Y08�� .q��a��s����(!<̫1�3�L�H��7sYW�`��qtu�\7��?�@�Y�� %Ka�x�GZ�*W�P��y�����N0r9��Z�c�%���q,��k�����!!,��>���IB��� �x1�Xu�Y���v-���ֺ��N������JP��a�2`�e5��w��u;�6�i���̃��(K���ťa��(l51��JL��O'%���]���ٖ뿉!��{*�p+f ���i8��D�ȑ���ܩ�M��V��ӟz��x�7Z�1i}�^͜���6e����x��)u�d�ˏ�V�eG�\�X1�����=n�U'�~Y����L�\,�(d!x ,�:�TW8� M`r���@��r+v�Q0��>^;˴�VbF��('����� �*O��i��j�Ӆ����^�S3&� ���{������.ی�� N;��NU%d0�Ce\r"�C�b��&t7N�7��� �ȪD�[%l�۪�$��'m?�A9tI�9>��NVL ��� 6v~M�����ъ�B���0����9$&=ώm��W�0���L���&��E���f�f��'+�9��hv��7,���M��6�D�&�^� �����8 D֮�����o��k^z��O��hP�8q�5d*��YVq3�{��������#LX4�?��jP=3�~�؅c�ݰ�̱�ұ��8̲��2�G�����j��έ�9��w7���U�X�w�85C��?!I&���o&����L���I#�^�eV�,�^t5�k� �xs˛\��U�8��."���{���ǵ�l4��ݼ�;��r"���G�/~���gJV�N糦��ZZ^k+NN�D��I$�=OE�z����|*�*{��%k���G��>�`9��b;�s���8,�qs��c��.b��q�%�>r��n�R�Ƨ�%�,�A��le#�8���}; 21�0�b�&�QX�f���q�00����$>�h�N�'�a3��A���/�r ��L5�8�GԲ'�����w;��c#�q1���h$*��V^�k��QĽ�b:i������� ����H F#�_���,��]g����Ը����`RUU���� ��"�qD���>-��ϤI�~��3�̴�����NZ[[555�eeek7�m����YF#[��Đ��R��i��L�H�r ��@��J-�Pŝ��_|�-��6y��ks�V�U~��6���w.����(��0A� 4��f��� /���C�xŊ�ٳ����z=6�����rg���mKo��o�/����a�g���?'9%9��P��斓��~���L{�� �H��{AW��B��$����v�1b�&Mb�ر��ر#�0`�`xMBp! )Z�Nmo�mZ�g�}�eذaWTVV ���'99��C5��&�&+5�bT>|8��PUUETT����&���t��~TE%��'������b�tEGG;RSS�S�N�v��ѯ��t����u1@�ޚ"f��͈���r'L�Ph��m��ƌ�74o��`�x�q�P���NRSS�X,�����l&&&��MFFA!-��`��ҷ�n��Yuu�����x<I�LB%����KԒ50�!�3�bz�����ʸKL���;---�l6[��`��������Ȏ;�����egg4��!D�T��jWujyZ��������K����%����p�^���X,6��sV=�ܖ����6�3�X ���4ҿ��A2�&����_k�;d�� �VfOIEND�B`� 0x0x
PHP 8.1.34
Preview: ConditionMatcher.php Size: 22.10 KB
/home2/astrociv/public_html/avitours.in/wp-content/plugins/imunify-security/inc/App/Defender/ConditionMatcher.php
<?php
/**
 * Copyright (с) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2025 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * https://www.cloudlinux.com/legal/
 */

namespace CloudLinux\Imunify\App\Defender;

use CloudLinux\Imunify\App\Debug;

/**
 * Pure comparison functions for condition evaluation.
 *
 * Each match method handles both string and array values internally,
 * extracting leaf strings from arrays via Request::extractLeafValues().
 *
 * @since 3.0.0
 */
class ConditionMatcher {

	/** Bounds blocking sends per request; above the number of broken patterns one site's rule set can realistically hit. */
	const MAX_PCRE_REPORTS_PER_REQUEST = 5;

	/**
	 * Reports already sent; static, so the budget lasts one request on a per-request SAPI.
	 *
	 * @var int
	 */
	private static $pcreReportsThisRequest = 0;

	/**
	 * XSS detection patterns.
	 *
	 * Focused on genuine attack vectors — dangerous tags, event handlers,
	 * protocol injection, and JS execution. Form/layout elements and
	 * encoding-format patterns removed to prevent FPs (normalization
	 * layer handles encoding evasion instead).
	 *
	 * @var string[]
	 */
	const XSS_PATTERNS = array(
		// Dangerous tags: tags whose mere presence in user input is an attack
		// signal (script/object/embed family + obsolete code-execution tags).
		// Removed: svg/math/body/img/video/audio/details/template — these have
		// legitimate uses (data:image/svg+xml from WP-Rocket beacon, image
		// galleries, Vue/page-builder templates). Their XSS variants are
		// caught by the event-handler pattern below or javascript: protocol.
		'/<\s*(?:script|iframe|object|embed|applet|base|noscript|xmp|plaintext|listing)[\s\/>]/i',
		// JavaScript / VBScript / LiveScript protocol — tolerates whitespace within keyword
		// (browsers accept `java\tscript:` in href attributes; livescript: is the legacy Netscape scheme name).
		'/(?:java\s*script|vb\s*script|live\s*script)\s*:/i',
		// Data URI with executable content types.
		'/data\s*:\s*(?:text\/html|application\/(?:x-)?javascript|text\/javascript)/i',
		// JS execution functions.
		'/\b(?:eval|settimeout|setinterval)\s*\(/i',
		// CSS expression (IE legacy, still relevant).
		'/expression\s*\(/i',
		// DOM access for data exfiltration / redirect.
		'/\bdocument\s*\.\s*(?:cookie|write|writeln|location|domain)/i',
		'/\bwindow\s*\.\s*(?:location|open|navigate)/i',
		// DOM sink assignment (CRS 941180, PHPIDS #37, NinjaFirewall).
		'/\b(?:inner|outer)html\s*=/i',
		'/\binsertadjacenthtml\s*\(/i',
		// String.fromCharCode — payload construction without literal tags
		// (CRS 941390, PHPIDS #43, voku/anti-xss).
		'/string\s*\.\s*fromcharcode\s*\(/i',
		// Constructor chain execution.
		'/constructor\s*[\[\(]/i',
		// CSS url() with JS protocol.
		'/url\s*\(\s*(?:javascript|data\s*:\s*text\/html)/i',
		// ESI injection (observed in CH production fleet data).
		'/<\s*esi\s*:/i',
	);

	/**
	 * Event-handler shape gate (cheap pre-filter).
	 *
	 * Matches anything shaped like an HTML event handler attribute
	 * (`on<name>=`) independent of whether `name` is a real handler. Used
	 * as a cost-bound gate before the precise allow-list — with no `on…=`
	 * token in the body we skip the 100+ alternative allow-list entirely,
	 * which dominates per-request cost on ARGS scans.
	 *
	 * Name is bounded [a-z]{2,24} to match all real HTML handlers while
	 * keeping PCRE work linear in input length.
	 *
	 * @var string
	 */
	const XSS_EVENT_HANDLER_SHAPE_GATE = '/\bon[a-z]{2,24}\s*=/i';

	/**
	 * Event-handler precise allow-list.
	 *
	 * Evaluated only when XSS_EVENT_HANDLER_SHAPE_GATE fires. Covers the
	 * AB-validated production set, CRS 941160 HTML5 handlers (onmessage,
	 * onstorage, onpageshow), and the imunify-connect port's form /
	 * clipboard / media handlers (onabort, oninvalid, onreset, oncopy,
	 * oncut, onpaste, onloadstart, onsearch, oncancel, onclose,
	 * onauxclick). This pattern is the sole catch-all for `<svg>`/`<img>`/
	 * `<video>`/`<audio>`/`<body>` XSS now that tag-narrowing removed
	 * those tags from the dangerous-tag list.
	 *
	 * @var string
	 */
	const XSS_EVENT_HANDLER_ALLOWLIST = '/\bon(?:error|load|loadstart|loadend|loadeddata|loadedmetadata|click|auxclick|dblclick|mouseover|mouseout|mouseenter|mouseleave|mousemove|mousedown|mouseup|focus|focusin|focusout|blur|submit|reset|invalid|change|input|search|keydown|keyup|keypress|animationend|animationstart|animationiteration|transitionend|transitionstart|transitionrun|transitioncancel|toggle|begin|start|canplay|canplaythrough|ended|playing|pause|play|seeking|seeked|stalled|suspend|waiting|emptied|durationchange|ratechange|timeupdate|volumechange|progress|cuechange|beforeunload|hashchange|popstate|resize|scroll|scrollend|unload|dragstart|dragend|drag|dragenter|dragleave|dragover|drop|contextmenu|wheel|pointerdown|pointerup|pointermove|pointercancel|pointerenter|pointerleave|pointerover|pointerout|gotpointercapture|lostpointercapture|touchstart|touchend|touchmove|touchcancel|copy|cut|paste|cancel|close|message|storage|pageshow|pagehide|afterprint|beforeprint|afterscriptexecute|beforescriptexecute|formdata|securitypolicyviolation|abort|selectstart|select|show|sort|activate)\s*=/i';

	/**
	 * SQL injection detection patterns.
	 *
	 * Focused on structural SQL injection signatures — UNION SELECT,
	 * stacked queries, tautologies, time/error-based blind, schema
	 * access, and data exfiltration. Single English keyword patterns
	 * and bare comment/encoding patterns removed to prevent FPs
	 * (normalization layer handles encoding and inline-comment evasion).
	 *
	 * @var string[]
	 */
	const SQLI_PATTERNS = array(
		// UNION-based injection.
		'/\bunion\b\s+(?:all\s+)?select\b/i',
		// Stacked queries: semicolon followed by DML/DDL keyword.
		'/;\s*(?:select|insert|update|delete|drop|alter|create|exec|execute)\b/i',
		// Boolean-based blind: numeric tautology / contradiction, including
		// hex literals (0x31=0x31) and float-notation (1e0=1e0) — libinjection
		// and CRS 942140/942100 collapse these to the same class.
		'/\b(?:or|and)\s+[\'"]?(?:\d+(?:e\d+)?|0x[0-9a-f]+)[\'"]?\s*=\s*[\'"]?(?:\d+(?:e\d+)?|0x[0-9a-f]+)/i',
		// Boolean-based blind: string-literal tautology (' x '=' x ', " a "=" a ")
		// — the non-numeric counterpart. Both quote styles supported so MSSQL
		// double-quoted identifiers are covered.
		'/\b(?:or|and)\s+([\'"])[^\'"\s]{1,32}\1\s*=\s*([\'"])[^\'"\s]{1,32}\2/i',
		// Boolean-based blind: SQL keyword operands (true / false / null). Plain
		// `or true` / `and false` has no numeric operands so the patterns above
		// miss it; these are common libinjection class-1a tautologies.
		'/\b(?:or|and)\s+(?:true|false|null)\b(?:\s+(?:is\s+null|is\s+not\s+null))?/i',
		// Time-based blind: sleep / benchmark functions.
		'/\b(?:sleep|benchmark|pg_sleep|dbms_lock\.sleep)\s*\(/i',
		// Time-based blind: MSSQL WAITFOR DELAY.
		'/\bwaitfor\s+delay\b/i',
		// Error-based: XML functions.
		'/\b(?:extractvalue|updatexml)\s*\(/i',
		// Error-based: floor(rand()) GROUP BY collision.
		'/\bfloor\s*\(\s*rand\b/i',
		// Error-based: MySQL 5.6+ GTID, geometric, ELT.
		'/\b(?:gtid_subset|st_latfromgeohash|st_longfromgeohash|elt)\s*\(/i',
		// Data exfiltration functions.
		'/\b(?:group_concat|load_file|concat_ws)\s*\(/i',
		// CHAR() / CHR() string construction — evades string-literal matching
		// (CRS 942370, PHPIDS #62, libinjection). Accepts numeric, hex-literal,
		// or a nested numeric-producing function as the first arg — raw `char(`
		// alone is too broad and FPs on the English word "char" in prose.
		'/\b(?:char|chr|ascii)\s*\(\s*(?:\d+|0x[0-9a-f]+|cast\b|unhex\b|conv\b|hex\b|concat\b|ord\b|mid\b)/i',
		// MSSQL system procedures (CRS 942250-942260).
		// sp_executesql can be called with `n'sql'` instead of `(`, so
		// the parenthesis is optional.
		'/\b(?:xp_cmdshell|sp_executesql|openrowset)\b/i',
		// SQLite metadata access (CRS 942530); WordPress 6.1+ SQLite plugin.
		'/\b(?:sqlite_master|sqlite_schema)\b/i',
		// JSON function injection (MySQL 5.7+).
		'/\b(?:json_extract|json_value|json_set|json_keys)\s*\(/i',
		// MySQL/MariaDB JSON arrow operator — `col->'$.path'` (JSON_EXTRACT)
		// and `col->>'$.path'` (JSON_UNQUOTE + JSON_EXTRACT). The `$` root is
		// mandatory syntax so anchoring on quote + `$` avoids FP on JS arrow
		// functions or C++ member dereference (neither uses quoted `$`).
		'/->>?\s*[\'"]\s*\$/i',
		// File write.
		'/\binto\s+(?:out|dump)file\b/i',
		// PROCEDURE ANALYSE.
		'/\bprocedure\s+analyse\b/i',
		// Schema / metadata access.
		'/\binformation_schema\b/i',
		// Database-specific system object access.
		'/\b(?:mysql|sys|pg_catalog)\s*\.\s*\w+/i',
		// Subquery injection: SELECT...FROM inside parentheses.
		// Bounded .{0,200} keeps this inside the backtrack limit on real input.
		'/\(\s*select\b[\s(].{0,200}\bfrom\b/i',
		// INSERT INTO...VALUES.
		'/\binsert\s+into\b\s+.{1,200}\bvalues\b/i',
		// UPDATE table SET column=value.
		'/\bupdate\b\s+\S+\s+\bset\b\s+\S+\s*=/i',
		// DROP / ALTER DDL.
		'/\b(?:drop|alter)\s+(?:table|database|column|index)\b/i',
		// Classic string injection: quote break followed by SQL keyword.
		'/\'\s*(?:or|and|union|having|order|group)\s/i',
		// Comment termination after quote break (auth bypass).
		'/\'\s*(?:--|#|\/\*)/i',
		// XOR boolean blind (observed in CH production fleet data).
		'/\bxor\s*\(\s*(?:if|case)\b/i',
		// Prepared statement bypass.
		'/\bprepare\s+\w+\s+from\b/i',
		// Hex-encoded strings in SQL context.
		'/\b(?:concat|select|union|from|where)\s*\(?\s*0x[0-9a-f]{8,}/i',
		// Information disclosure functions.
		'/\b(?:user|database|version|schema|current_user|session_user)\s*\(\s*\)/i',
		// Privilege operations (structured — avoids FP on single words).
		'/\b(?:grant|revoke)\s+(?:all|select|insert|update|delete|execute|usage|alter|create|drop)\b/i',
		// Aggregate function followed by FROM (in query context).
		'/\b(?:count|sum|avg)\s*\([^)]*\)\s+(?:from|over)\b/i',
	);

	/**
	 * Test if a value strictly equals the expected string.
	 *
	 * For array values, extracts leaf strings and checks each.
	 *
	 * @param mixed  $value    The resolved value (string or array).
	 * @param string $expected The expected string.
	 *
	 * @return bool
	 */
	public function matchEquals( $value, $expected ) {
		if ( is_string( $value ) ) {
			return $value === $expected;
		}

		if ( is_array( $value ) ) {
			foreach ( Request::extractLeafValues( $value ) as $leaf ) {
				if ( $leaf === $expected ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Test if a value contains the given substring.
	 *
	 * For array values, extracts leaf strings and checks each.
	 *
	 * @param mixed  $value  The resolved value (string or array).
	 * @param string $needle The substring to search for.
	 *
	 * @return bool
	 */
	public function matchContains( $value, $needle ) {
		if ( is_string( $value ) ) {
			return false !== strpos( $value, $needle );
		}

		if ( is_array( $value ) ) {
			foreach ( Request::extractLeafValues( $value ) as $leaf ) {
				if ( false !== strpos( $leaf, $needle ) ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Test if a value matches a PCRE pattern (fails open on a PCRE error).
	 *
	 * For array values, extracts leaf strings and checks each.
	 *
	 * @param mixed  $value   The resolved value (string or array).
	 * @param string $pattern PCRE pattern.
	 *
	 * @return bool
	 */
	public function matchRegex( $value, $pattern ) {
		if ( is_string( $value ) ) {
			return self::pregMatchFailOpen( $pattern, $value );
		}

		if ( is_array( $value ) ) {
			foreach ( Request::extractLeafValues( $value ) as $leaf ) {
				if ( self::pregMatchFailOpen( $pattern, $leaf ) ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Detect XSS patterns in a value.
	 *
	 * For array values, extracts leaf strings and checks each.
	 *
	 * @param mixed $value The value to check.
	 *
	 * @return bool True if XSS is detected.
	 */
	public function matchXSS( $value ) {
		if ( is_array( $value ) ) {
			foreach ( Request::extractLeafValues( $value ) as $leaf ) {
				if ( $this->detectXSS( $leaf ) ) {
					return true;
				}
			}
			return false;
		}

		return $this->detectXSS( $value );
	}

	/**
	 * Detect SQL injection patterns in a value.
	 *
	 * For array values, extracts leaf strings and checks each.
	 *
	 * @param mixed $value The value to check.
	 *
	 * @return bool True if SQL injection is detected.
	 */
	public function matchSQLi( $value ) {
		if ( is_array( $value ) ) {
			foreach ( Request::extractLeafValues( $value ) as $leaf ) {
				if ( $this->detectSQLi( $leaf ) ) {
					return true;
				}
			}
			return false;
		}

		return $this->detectSQLi( $value );
	}

	/**
	 * Match a PCRE pattern, treating an engine error as no-match and reporting it.
	 *
	 * @since 4.1.3
	 *
	 * @param string $pattern PCRE pattern.
	 * @param string $subject String to test.
	 *
	 * @return bool True only when the pattern actually matched.
	 */
	public static function pregMatchFailOpen( $pattern, $subject ) {
		$result = @preg_match( $pattern, $subject ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- PCRE errors are handled via the return value.

		if ( false !== $result ) {
			return 1 === $result;
		}

		self::reportPcreFailure( $pattern );

		return false;
	}

	/**
	 * Report a PCRE failure to Sentry, throttled per reason and pattern.
	 *
	 * @since 4.1.3
	 *
	 * @param string $pattern The pattern whose evaluation failed.
	 *
	 * @return void
	 */
	private static function reportPcreFailure( $pattern ) {
		// Each report is a blocking HTTP send; skipped patterns keep their slots.
		if ( self::$pcreReportsThisRequest >= self::MAX_PCRE_REPORTS_PER_REQUEST ) {
			return;
		}

		$code = preg_last_error();
		$name = self::pcreErrorName( $code );
		// Per pattern, or the first broken one holds the hourly slot for all of them.
		// nosemgrep: php.lang.security.weak-crypto.weak-crypto -- bounds the transient key length, not cryptography.
		$hash = substr( md5( $pattern ), 0, 8 );

		if ( ! function_exists( 'do_action' ) || ! Debug::throttleGate( 'waf_pcre_error_' . $name . '_' . $hash ) ) {
			return;
		}

		++self::$pcreReportsThisRequest;

		do_action(
			'imunify_security_set_error',
			E_WARNING,
			'WAF: PCRE error (' . $name . ') evaluating a rule condition; condition treated as no-match',
			__FILE__,
			__LINE__,
			array(
				'fingerprint' => array( 'waf-pcre-error', $name, $hash ),
				'pcre_error'  => $code,
			)
		);
	}

	/**
	 * Map a preg_last_error() code to a short, stable name.
	 *
	 * @since 4.1.3
	 *
	 * @param int $code preg_last_error() value.
	 *
	 * @return string Short name; also part of the throttle key.
	 */
	private static function pcreErrorName( $code ) {
		// constant(): the bare token does not exist on PHP 5.6.
		if ( defined( 'PREG_JIT_STACKLIMIT_ERROR' ) && constant( 'PREG_JIT_STACKLIMIT_ERROR' ) === $code ) {
			return 'jit_stacklimit';
		}

		switch ( $code ) {
			case PREG_BACKTRACK_LIMIT_ERROR:
				return 'backtrack_limit';
			case PREG_RECURSION_LIMIT_ERROR:
				return 'recursion_limit';
			case PREG_BAD_UTF8_ERROR:
				return 'bad_utf8';
			case PREG_BAD_UTF8_OFFSET_ERROR:
				return 'bad_utf8_offset';
			case PREG_INTERNAL_ERROR:
				return 'internal';
			default:
				return 'unknown';
		}
	}

	/**
	 * Normalize input for detection: decode encodings and strip evasion bytes.
	 *
	 * Handles URL encoding (double-pass for double-encoding), HTML entities,
	 * hex escapes (\x3c), Unicode escapes (\u003c), and null bytes.
	 * Result is lowercased for case-insensitive matching.
	 *
	 * @param string $value Raw input value.
	 *
	 * @return string Normalized, lowercased value.
	 */
	private function normalizeInput( $value ) {
		// URL-decode iteratively until stable (handles single, double, and
		// triple encoding). Bounded to 4 passes to prevent pathological
		// inputs from consuming unbounded CPU.
		for ( $i = 0; $i < 4; $i++ ) {
			$decoded = rawurldecode( $value );
			if ( $decoded === $value ) {
				break;
			}
			$value = $decoded;
		}

		// Strip null bytes (evasion: <scr\x00ipt>).
		$value = str_replace( "\x00", '', $value );

		// Decode HTML entities + JS hex/unicode escapes. The three stages
		// feed each other: an entity can decode to `\x3c`, a `\`
		// decodes to `\` that forms a new `\x..` escape, and so on. We
		// iterate until the value stabilises (max 3 passes to bound
		// pathological inputs). ENT_HTML5 enables HTML5 named entities
		// (&Tab;, &NewLine;, &colon;, &lpar;, &rpar;, etc.) — without
		// it, attackers evade by wrapping delimiters in those entities.
		for ( $pass = 0; $pass < 3; $pass++ ) {
			$before = $value;

			// Browsers accept numeric entities without trailing `;`
			// (`&#60script` -> `<script`). PHP's html_entity_decode
			// REQUIRES the semicolon, so attackers drop it to evade.
			// Pre-pass: inject `;` whenever a numeric entity is followed
			// by a non-digit/non-semicolon so html_entity_decode can
			// process it correctly. Runs inside the loop so newly-exposed
			// entities produced by the previous pass (e.g. `&amp;#60…` ->
			// `&#60…` after one decode) are also normalised.
			$decoded = preg_replace(
				array(
					'/&#x([0-9a-fA-F]{1,6})(?=[^0-9a-fA-F;]|$)/',
					'/&#([0-9]{1,7})(?=[^0-9;]|$)/',
				),
				array( '&#x$1;', '&#$1;' ),
				$value
			);
			if ( null !== $decoded ) {
				$value = $decoded;
			}

			$value = html_entity_decode( $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' );

			$decoded = preg_replace_callback(
				'/\\\\x([0-9a-fA-F]{2})/',
				function ( $m ) {
					return chr( hexdec( $m[1] ) );
				},
				$value
			);
			if ( null !== $decoded ) {
				$value = $decoded;
			}

			$decoded = preg_replace_callback(
				'/\\\\u([0-9a-fA-F]{4})/',
				function ( $m ) {
					return html_entity_decode( '&#x' . $m[1] . ';', ENT_QUOTES, 'UTF-8' );
				},
				$value
			);
			if ( null !== $decoded ) {
				$value = $decoded;
			}

			if ( $value === $before ) {
				break;
			}
		}

		// Second null-byte strip: hex (\x00) and unicode (\u0000) decode
		// stages above can produce new null bytes that weren't present in
		// the original URL-decoded value.
		$value = str_replace( "\x00", '', $value );

		// Protocol keyword re-normalization: browsers strip ASCII tab/newline
		// (U+0009, U+000A, U+000D) from URL schemes before parsing, so
		// `jav\tascript:` (produced e.g. by `jav&Tab;ascript:` decoding via
		// ENT_HTML5, or by literal whitespace inserted at any keyword
		// position) is equivalent to `javascript:`. Collapse whitespace
		// sprinkled anywhere within javascript/vbscript/livescript schemes
		// to the canonical form so the existing protocol pattern matches.
		// Bounded \s{0,3} per inter-char gap limits backtracking; overall
		// match is anchored by the trailing `:`.
		$decoded = preg_replace(
			array(
				'/j\s{0,3}a\s{0,3}v\s{0,3}a\s{0,3}s\s{0,3}c\s{0,3}r\s{0,3}i\s{0,3}p\s{0,3}t\s*:/i',
				'/v\s{0,3}b\s{0,3}s\s{0,3}c\s{0,3}r\s{0,3}i\s{0,3}p\s{0,3}t\s*:/i',
				'/l\s{0,3}i\s{0,3}v\s{0,3}e\s{0,3}s\s{0,3}c\s{0,3}r\s{0,3}i\s{0,3}p\s{0,3}t\s*:/i',
			),
			array( 'javascript:', 'vbscript:', 'livescript:' ),
			$value
		);
		if ( null !== $decoded ) {
			$value = $decoded;
		}

		// Lowercase for case-insensitive matching.
		$value = strtolower( $value );

		return $value;
	}

	/**
	 * Detect XSS patterns in a string value.
	 *
	 * Applies input normalization (URL/entity/hex decoding, null-byte stripping)
	 * before matching patterns, so encoding evasion is handled by the
	 * normalization layer rather than individual patterns.
	 *
	 * @param string $value The value to check for XSS patterns.
	 *
	 * @return bool True if XSS is detected, false otherwise.
	 */
	private function detectXSS( $value ) {
		if ( ! is_string( $value ) || empty( $value ) ) {
			return false;
		}

		$value = $this->normalizeInput( $value );

		foreach ( self::XSS_PATTERNS as $pattern ) {
			if ( self::pregMatchFailOpen( $pattern, $value ) ) {
				return true;
			}
		}

		// Two-phase event-handler check: the cheap bounded shape gate
		// (`on[a-z]{2,24}\s*=`) filters the common case where no handler-
		// shaped token exists in the body, so we only pay the cost of the
		// 100+ alternative precise allow-list when the gate fires.
		// A PCRE error in either phase fails open and is reported.
		if ( self::pregMatchFailOpen( self::XSS_EVENT_HANDLER_SHAPE_GATE, $value )
			&& self::pregMatchFailOpen( self::XSS_EVENT_HANDLER_ALLOWLIST, $value ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Detect SQL injection patterns in a string value.
	 *
	 * Applies input normalization plus SQL-specific comment stripping.
	 * Inline comments are normalized in two modes to handle both
	 * between-keyword evasion (UNION/**\/SELECT) and within-keyword
	 * evasion (UN/**\/ION).
	 *
	 * @param string $value The value to check for SQL injection patterns.
	 *
	 * @return bool True if SQL injection is detected, false otherwise.
	 */
	private function detectSQLi( $value ) {
		if ( ! is_string( $value ) || empty( $value ) ) {
			return false;
		}

		$base = $this->normalizeInput( $value );

		// Mode 1: comments → space (handles UNION/**/SELECT → UNION SELECT).
		// Each step falls back to its predecessor (not $base) to preserve
		// intermediate results if a later preg_replace fails.
		$step   = preg_replace( '/\/\*!\d{0,5}(.*?)\*\//s', ' $1 ', $base );
		$spaced = null !== $step ? $step : $base;
		$step   = preg_replace( '/\/\*.*?\*\//s', ' ', $spaced );
		$spaced = null !== $step ? $step : $spaced;
		$step   = preg_replace( '/\s+/', ' ', $spaced );
		$spaced = null !== $step ? trim( $step ) : trim( $spaced );

		foreach ( self::SQLI_PATTERNS as $pattern ) {
			if ( self::pregMatchFailOpen( $pattern, $spaced ) ) {
				return true;
			}
		}

		// Mode 2: comments → empty (handles UN/**/ION → UNION).
		// Skip entirely when no comments present — both modes produce same result.
		if ( false !== strpos( $base, '/*' ) ) {
			$step   = preg_replace( '/\/\*!\d{0,5}(.*?)\*\//s', '$1', $base );
			$joined = null !== $step ? $step : $base;
			$step   = preg_replace( '/\/\*.*?\*\//s', '', $joined );
			$joined = null !== $step ? $step : $joined;
			$step   = preg_replace( '/\s+/', ' ', $joined );
			$joined = null !== $step ? trim( $step ) : trim( $joined );

			if ( $joined !== $spaced ) {
				foreach ( self::SQLI_PATTERNS as $pattern ) {
					if ( self::pregMatchFailOpen( $pattern, $joined ) ) {
						return true;
					}
				}
			}
		}

		return false;
	}
}

Directory Contents

Dirs: 2 × Files: 16
Name Size Perms Modified Actions
Handler DIR
- drwxr-xr-x 2026-11-03 01:06:25
Edit Download
Model DIR
- drwxr-xr-x 2026-11-03 01:06:25
Edit Download
1.97 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
3.44 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
22.33 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
22.10 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
4.28 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
945 B lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
6.88 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
17.87 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
3.08 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
62.62 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
4.92 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
18.52 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
4.44 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
14.27 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
9.23 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
28.98 KB lrwxr-xr-x 2026-11-03 01:06:25
Edit Download
If ZipArchive is unavailable, a .tar will be created (no compression).
© 2026 0xNothings — Secure File Manager. All rights reserved. Built with ❤️ & Tailwind x Dark UI