From f2552dd9ab034af3903055c067e1939c5e53be5c Mon Sep 17 00:00:00 2001 From: Denis Ryabov Date: Mon, 10 May 2021 20:06:58 +0300 Subject: [PATCH 1/6] Add JEDCheckerHelper class with some common methods --- .../com_jedchecker/libraries/helper.php | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 administrator/components/com_jedchecker/libraries/helper.php diff --git a/administrator/components/com_jedchecker/libraries/helper.php b/administrator/components/com_jedchecker/libraries/helper.php new file mode 100644 index 0000000..6251e04 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/helper.php @@ -0,0 +1,197 @@ +element)) + { + $extension = (string) $xml->element; + } + else + { + $extension = (string) $xml->name; + + if (isset($xml->files)) + { + foreach ($xml->files->children() as $child) + { + if (isset($child[$type])) + { + $extension = (string) $child[$type]; + } + } + } + } + + $extension = strtolower(JFilterInput::getInstance()->clean($extension, 'cmd')); + + if ($type === 'component' && strpos($extension, 'com_') !== 0) + { + $extension = 'com_' . $extension; + } + + return $extension; + } + + /** + * Removes HTML, comments, and/or strings content keeping EOL characters to preserve line numbers + * + * @param string $content PHP sources + * @param int $options Bitwise set of options + * + * @return string + * @since 3.0 + */ + public static function cleanPhpCode($content, $options = self::CLEAN_HTML | self::CLEAN_COMMENTS) + { + $isCleanHtml = $options & self::CLEAN_HTML; + $isCleanComments = $options & self::CLEAN_COMMENTS; + $isCleanStrings = $options & self::CLEAN_STRINGS; + + if (!preg_match('/<\?php\s/i', $content, $match, PREG_OFFSET_CAPTURE)) + { + // No PHP code found + return $isCleanHtml ? '' : $content; + } + + $pos = $match[0][1]; + $code = substr($content, 0, $pos); + $cleanContent = $isCleanHtml ? self::removeContent($code) : $code; + + while (preg_match('/(?:[\'"]|\/\*|\/\/|\?>)/', $content, $match, PREG_OFFSET_CAPTURE, $pos)) + { + $foundPos = $match[0][1]; + $cleanContent .= substr($content, $pos, $foundPos - $pos); + $pos = $foundPos; + + switch ($match[0][0]) + { + case '"': + case "'": + $q = $match[0][0]; + + if (!preg_match("/$q(?>[^$q\\\\]+|\\\\.)*$q/As", $content, $match, 0, $pos)) + { + return $cleanContent . ($isCleanStrings ? $q : substr($content, $pos)); + } + + $code = $match[0]; + $cleanContent .= $isCleanStrings ? $q . self::removeContent($code) . $q : $code; + $pos += strlen($code); + break; + + case '/*': + $cleanContent .= '/*'; + $pos += 2; + + $endPos = strpos($content, '*/', $pos); + + if ($endPos === false) + { + return $isCleanComments ? $cleanContent : $cleanContent . substr($content, $pos); + } + + $code = substr($content, $pos, $endPos - $pos); + $cleanContent .= $isCleanComments ? self::removeContent($code) : $code; + $cleanContent .= '*/'; + $pos = $endPos + 2; + + break; + + case '//': + $commentLen = strcspn($content, "\r\n", $pos); + + if (!$isCleanComments) + { + $cleanContent .= substr($content, $pos, $commentLen); + } + + $pos += $commentLen; + break; + + case '?>': + $cleanContent .= '?>'; + $pos += 2; + + if (!preg_match('/<\?php\s/i', $content, $match, PREG_OFFSET_CAPTURE, $pos)) + { + // No PHP code found (up to the end of the file) + return $cleanContent . ($isCleanHtml ? '' : substr($content, $pos)); + } + + $foundPos = $match[0][1]; + $code = substr($content, $pos, $foundPos - $pos); + $cleanContent .= $isCleanHtml ? self::removeContent($code) : $code; + + $phpPreamble = $match[0][0]; + $cleanContent .= $phpPreamble; + $pos = $foundPos + strlen($phpPreamble); + + break; + } + } + + $cleanContent .= substr($content, $pos); + + return $cleanContent; + } + + /** + * Remove all text content by keeping newline characters only (to preserve line numbers) + * + * @param string $content Partial content + * + * @return string + * @since 3.0 + */ + protected static function removeContent($content) + { + return str_repeat("\n", substr_count($content, "\n")); + } +} From 6cee91b678006e538b79f58f433a74189533e4cf Mon Sep 17 00:00:00 2001 From: Denis Ryabov Date: Tue, 11 May 2021 14:40:19 +0300 Subject: [PATCH 2/6] introduce JEDCheckerHelper::findManifests to get "real" manifest files, sorted by depth --- .../com_jedchecker/libraries/helper.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/administrator/components/com_jedchecker/libraries/helper.php b/administrator/components/com_jedchecker/libraries/helper.php index 6251e04..26d71c3 100644 --- a/administrator/components/com_jedchecker/libraries/helper.php +++ b/administrator/components/com_jedchecker/libraries/helper.php @@ -23,6 +23,71 @@ abstract class JEDCheckerHelper const CLEAN_COMMENTS = 2; const CLEAN_STRINGS = 4; + /** + * Returns XML manifest files in the package (sorted by depth) + * + * @param string $basedir Extension's directory + * + * @return string[] + * @since 3.0 + */ + public static function findManifests($basedir) + { + // Find all XML files of the extension + $files = JFolder::files($basedir, '\.xml$', true, true); + + $excludeList = array(); + + foreach ($files as $file) + { + $xml = simplexml_load_file($file); + + if (!$xml || ($xml->getName() !== 'extension' && $xml->getName() !== 'install')) + { + // Exclude non-install-manifest XML files + $excludeList[] = $file; + } + elseif ((string) $xml['type'] === 'component' && isset($xml->administration->files['folder'])) + { + // Exclude possible duplicates of manifest in components + $excludeList[] = dirname($file) . '/' . trim($xml->administration->files['folder'], ' /') . '/' . basename($file); + } + elseif ((string) $xml['type'] === 'file' && isset($xml->fileset->files)) + { + // Exclude possible duplicates of file-type extension manifest + foreach ($xml->fileset->files as $child) + { + if (isset($child['folder'])) + { + $excludeList[] = dirname($file) . '/' . trim($child['folder'], ' /') . '/' . basename($file); + } + } + } + } + + $files = array_diff($files, $excludeList); + usort($files, array(__CLASS__, 'sortPathsCmp')); + + return $files; + } + + /** + * Sort directories by depth + * + * @param string $path1 1st path to compare + * @param string $path2 2nd path to compare + * + * @return integer + * @since 3.0 + */ + public static function sortPathsCmp($path1, $path2) + { + $depth1 = substr_count($path1, '/'); + $depth2 = substr_count($path2, '/'); + + return ($depth1 === $depth2) ? strcmp($path1, $path2) : ($depth1 - $depth2); + } + /** * Split text into lines * From 00076827b186810cf324d38c0994ff81e6247ef6 Mon Sep 17 00:00:00 2001 From: Denis Ryabov Date: Thu, 13 May 2021 21:56:05 +0300 Subject: [PATCH 3/6] fix processing of short php tags --- administrator/components/com_jedchecker/libraries/helper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/administrator/components/com_jedchecker/libraries/helper.php b/administrator/components/com_jedchecker/libraries/helper.php index 26d71c3..28b708b 100644 --- a/administrator/components/com_jedchecker/libraries/helper.php +++ b/administrator/components/com_jedchecker/libraries/helper.php @@ -159,7 +159,7 @@ abstract class JEDCheckerHelper $isCleanComments = $options & self::CLEAN_COMMENTS; $isCleanStrings = $options & self::CLEAN_STRINGS; - if (!preg_match('/<\?php\s/i', $content, $match, PREG_OFFSET_CAPTURE)) + if (!preg_match('/<\?(?:php\s|\s|=)/i', $content, $match, PREG_OFFSET_CAPTURE)) { // No PHP code found return $isCleanHtml ? '' : $content; @@ -224,7 +224,7 @@ abstract class JEDCheckerHelper $cleanContent .= '?>'; $pos += 2; - if (!preg_match('/<\?php\s/i', $content, $match, PREG_OFFSET_CAPTURE, $pos)) + if (!preg_match('/<\?(?:php\s|\s|=)/i', $content, $match, PREG_OFFSET_CAPTURE, $pos)) { // No PHP code found (up to the end of the file) return $cleanContent . ($isCleanHtml ? '' : substr($content, $pos)); From 09b49ec5af3e4312132b59acf5b206ae17101082 Mon Sep 17 00:00:00 2001 From: Denis Ryabov Date: Sat, 15 May 2021 16:11:57 +0300 Subject: [PATCH 4/6] fix @since version to 2.4 --- administrator/components/com_jedchecker/libraries/helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_jedchecker/libraries/helper.php b/administrator/components/com_jedchecker/libraries/helper.php index 28b708b..b58b3e0 100644 --- a/administrator/components/com_jedchecker/libraries/helper.php +++ b/administrator/components/com_jedchecker/libraries/helper.php @@ -15,7 +15,7 @@ defined('_JEXEC') or die('Restricted access'); * * This is a helper class with a set of static methods used by other JEDChecker classes * - * @since 3.0 + * @since 2.4 */ abstract class JEDCheckerHelper { From d1a1bfd10bd683338a18e28f4db53e8efc2aeae4 Mon Sep 17 00:00:00 2001 From: Denis Ryabov Date: Sat, 15 May 2021 22:15:06 +0300 Subject: [PATCH 5/6] 2.4, 2.4 is everywhere --- .../components/com_jedchecker/libraries/helper.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/administrator/components/com_jedchecker/libraries/helper.php b/administrator/components/com_jedchecker/libraries/helper.php index b58b3e0..f022aad 100644 --- a/administrator/components/com_jedchecker/libraries/helper.php +++ b/administrator/components/com_jedchecker/libraries/helper.php @@ -29,7 +29,7 @@ abstract class JEDCheckerHelper * @param string $basedir Extension's directory * * @return string[] - * @since 3.0 + * @since 2.4 */ public static function findManifests($basedir) { @@ -78,7 +78,7 @@ abstract class JEDCheckerHelper * @param string $path2 2nd path to compare * * @return integer - * @since 3.0 + * @since 2.4 */ public static function sortPathsCmp($path1, $path2) { @@ -94,7 +94,7 @@ abstract class JEDCheckerHelper * @param string $content Text to split * * @return string[] - * @since 3.0 + * @since 2.4 */ public static function splitLines($content) { @@ -108,7 +108,7 @@ abstract class JEDCheckerHelper * @param SimpleXMLElement $xml XML Manifest * * @return string - * @since 3.0 + * @since 2.4 */ public static function getElementName($xml) { @@ -151,7 +151,7 @@ abstract class JEDCheckerHelper * @param int $options Bitwise set of options * * @return string - * @since 3.0 + * @since 2.4 */ public static function cleanPhpCode($content, $options = self::CLEAN_HTML | self::CLEAN_COMMENTS) { @@ -253,7 +253,7 @@ abstract class JEDCheckerHelper * @param string $content Partial content * * @return string - * @since 3.0 + * @since 2.4 */ protected static function removeContent($content) { From cbd3bd8f9eaa57bfa4be3bcf563b65bf5297f2f6 Mon Sep 17 00:00:00 2001 From: Denis Ryabov Date: Fri, 21 May 2021 21:26:02 +0300 Subject: [PATCH 6/6] fix processing of one-line comments --- .../components/com_jedchecker/libraries/helper.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/administrator/components/com_jedchecker/libraries/helper.php b/administrator/components/com_jedchecker/libraries/helper.php index f022aad..5e28a95 100644 --- a/administrator/components/com_jedchecker/libraries/helper.php +++ b/administrator/components/com_jedchecker/libraries/helper.php @@ -169,7 +169,7 @@ abstract class JEDCheckerHelper $code = substr($content, 0, $pos); $cleanContent = $isCleanHtml ? self::removeContent($code) : $code; - while (preg_match('/(?:[\'"]|\/\*|\/\/|\?>)/', $content, $match, PREG_OFFSET_CAPTURE, $pos)) + while (preg_match('/(?:[\'"]|\/\*|\/\/|#|\?>)/', $content, $match, PREG_OFFSET_CAPTURE, $pos)) { $foundPos = $match[0][1]; $cleanContent .= substr($content, $pos, $foundPos - $pos); @@ -210,7 +210,14 @@ abstract class JEDCheckerHelper break; case '//': + case '#': $commentLen = strcspn($content, "\r\n", $pos); + $endPhpPos = strpos($content, '?>', $pos); + + if ($endPhpPos !== false && $endPhpPos < $pos + $commentLen) + { + $commentLen = $endPhpPos - $pos; + } if (!$isCleanComments) { @@ -218,6 +225,7 @@ abstract class JEDCheckerHelper } $pos += $commentLen; + break; case '?>':