Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
PhpCode
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
6 / 6
14
100.00% covered (success)
100.00%
1 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
1
 normalizeSource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasUnicode
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 isDisabled
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 hasDeprecatedFunction
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 validateFileIntegrity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateStringIntegrity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Security
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Security;
16
17/**
18 * Php code security class.
19 *
20 * This can be used to ensure php code doesn't contain malicious functions and or characters.
21 * Additionally this can also be used in order verify that the source code is not altered compared to some expected source code.
22 *
23 * @package phpOMS\Security
24 * @license OMS License 2.0
25 * @link    https://jingga.app
26 * @since   1.0.0
27 */
28final class PhpCode
29{
30    /**
31     * Disabled functions
32     *
33     * @var string[]
34     * @since 1.0.0
35     */
36    public static array $disabledFunctions = [
37        'apache_child_terminate', 'apache_setenv', 'define_syslog_variables', 'eval',
38        'exec', 'fp', 'fput', 'ftp_connect', 'ftp_exec', 'ftp_get', 'ftp_login', 'ftp_nb_fput', 'ftp_put', 'ftp_raw',
39        'ftp_rawlist', 'highlight_file', 'ini_alter', 'ini_get_all', 'ini_restore', 'inject_code', 'mysql_pconnect',
40        'openlog', 'php_uname', 'phpAds_remoteInfo', 'phpAds_XmlRpc', 'phpAds_xmlrpcDecode',
41        'phpAds_xmlrpcEncode', 'popen', 'posix_getpwuid', 'posix_kill', 'posix_mkfifo', 'posix_setpgid', 'posix_setsid',
42        'posix_setuid', 'posix_uname', 'proc_close', 'proc_get_status', 'shell_exec', 'serialize', 'unserialize', '__serialize', '__unserialize',
43    ];
44
45    /**
46     * Deprecated functions
47     *
48     * @var string[]
49     * @since 1.0.0
50     */
51    public static array $deprecatedFunctions = [
52        'apache_child_terminate', 'apache_setenv', 'define_syslog_variables', 'eval',
53        'exec', 'fp', 'fput', 'ftp_connect', 'ftp_exec', 'ftp_get', 'ftp_login', 'ftp_nb_fput', 'ftp_put', 'ftp_raw',
54        'ftp_rawlist', 'highlight_file', 'ini_alter', 'ini_get_all', 'ini_restore', 'inject_code', 'mysql_pconnect',
55        'openlog', 'php_uname', 'phpAds_remoteInfo', 'phpAds_XmlRpc', 'phpAds_xmlrpcDecode',
56        'phpAds_xmlrpcEncode', 'popen', 'posix_getpwuid', 'posix_kill', 'posix_mkfifo', 'posix_setpgid', 'posix_setsid',
57        'posix_setuid', 'posix_uname', 'proc_close', 'proc_get_status', 'shell_exec',
58    ];
59
60    /**
61     * Constructor.
62     *
63     * @since 1.0.0
64     * @codeCoverageIgnore
65     */
66    private function __construct()
67    {
68    }
69
70    /**
71     * Normalize source code for inspection
72     *
73     * @param string $source Source code
74     *
75     * @return string Normalized source code
76     *
77     * @since 1.0.0
78     */
79    public static function normalizeSource(string $source) : string
80    {
81        return \str_replace(["\n", "\r\n", "\r", "\t"], ['', '', '', ' '], $source);
82    }
83
84    /**
85     * Check if has source unicode
86     *
87     * @param string $source Source code
88     *
89     * @return bool Returns true if the code has unicode characters otherwise false is returned
90     *
91     * @since 1.0.0
92     */
93    public static function hasUnicode(string $source) : bool
94    {
95        $length = \mb_strlen($source, 'UTF-8');
96
97        for ($i = 0; $i < $length; ++$i) {
98            $char      = \mb_substr($source, $i, 1, 'UTF-8');
99            $codePoint = \ord($char);
100
101            if ($codePoint > 127) {
102                return true;
103            }
104        }
105
106        return false;
107    }
108
109    /**
110     * Check if function is disabled
111     *
112     * @param string[] $functions Functions to check
113     *
114     * @return bool Returns true if code has disabled function calls otherwise false is returned
115     *
116     * @since 1.0.0
117     */
118    public static function isDisabled(array $functions) : bool
119    {
120        $disabled = \ini_get('disable_functions');
121
122        if ($disabled === false) {
123            return true; // @codeCoverageIgnore
124        }
125
126        $disabled = \str_replace(' ', '', $disabled);
127        $disabled = \explode(',', $disabled);
128
129        foreach ($functions as $function) {
130            if (!\in_array($function, $disabled)) {
131                return false;
132            }
133        }
134
135        return true; // @codeCoverageIgnore
136    }
137
138    /**
139     * Check if has deprecated functions
140     *
141     * @param string $source Source code
142     *
143     * @return bool Returns true if code contains deprecated functions otherwise false is returned
144     *
145     * @since 1.0.0
146     */
147    public static function hasDeprecatedFunction(string $source) : bool
148    {
149        foreach (self::$deprecatedFunctions as $function) {
150            if (\preg_match('/' . $function . '\s*\(/', $source) === 1) {
151                return true;
152            }
153        }
154
155        return false;
156    }
157
158    /**
159     * Validate file integrety
160     *
161     * @param string $source Source code path
162     * @param string $hash   Source hash (md5)
163     *
164     * @return bool Returns true if filee matches expected signature otherwise false is returned
165     *
166     * @since 1.0.0
167     */
168    public static function validateFileIntegrity(string $source, string $hash) : bool
169    {
170        return \md5_file($source) === $hash;
171    }
172
173    /**
174     * Validate code integrety
175     *
176     * @param string $source Source code
177     * @param string $remote Remote code
178     *
179     * @return bool Returns true if source code is the same as the expected code otherwise false is returned
180     *
181     * @since 1.0.0
182     */
183    public static function validateStringIntegrity(string $source, string $remote) : bool
184    {
185        return $source === $remote;
186    }
187}