├── .github └── workflows │ └── analyze.yml ├── .gitignore ├── .phpstan.neon ├── .sonarcloud.properties ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── inc └── Constants.php ├── misc ├── examples │ ├── .htaccess │ └── connectors.ini └── tests │ ├── connectors_invalid.ini │ ├── connectors_valid.ini │ ├── hashes │ ├── ignore1.json │ ├── ignore2.json │ └── ignore3.json ├── phpunit.xml ├── shadowd.php ├── src ├── Config.php ├── Connection.php ├── Connector.php ├── Exceptions │ ├── BadJsonException.php │ ├── BadRequestException.php │ ├── BadSignatureException.php │ ├── CorruptedFileException.php │ ├── FailedConnectionException.php │ ├── InvalidProfileException.php │ ├── MissingConfigEntryException.php │ ├── MissingFileException.php │ ├── ProcessingException.php │ └── UnknownPathException.php ├── Input.php ├── Output.php └── Template.php ├── tests ├── ConfigTest.php └── InputTest.php └── tpl ├── bad_json.html.php ├── bad_request.html.php ├── bad_signature.html.php ├── base.html.php ├── blocked.html.php ├── corrupted_file.html.php ├── failed_connection.html.php ├── invalid_profile.html.php ├── missing_config_entry.html.php ├── missing_file.html.php ├── processing.html.php └── unknown_path.html.php /.github/workflows/analyze.yml: -------------------------------------------------------------------------------- 1 | name: Analyze 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - dev 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | jobs: 10 | run: 11 | runs-on: ${{ matrix.operating-system }} 12 | strategy: 13 | matrix: 14 | operating-system: [ubuntu-latest, windows-latest, macOS-latest] 15 | php-versions: ['7.2', '7.3', '8.0'] 16 | name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} 17 | steps: 18 | - name: Enforce LF 19 | run: | 20 | git config --global core.autocrlf false 21 | git config --global core.eol lf 22 | - name: Checkout 23 | uses: actions/checkout@v2 24 | - name: Install PHP 25 | uses: shivammathur/setup-php@v2 26 | with: 27 | php-version: ${{ matrix.php-versions }} 28 | - name: Install dependencies 29 | run: composer install 30 | - name: Run PHPCS 31 | run: php vendor/bin/phpcs --standard=PSR2 --ignore="autoload.php,Connector.php,libs/*" src 32 | - name: Run PHPStan 33 | run: php vendor/bin/phpstan analyse -c .phpstan.neon -l 6 src 34 | - name: Run PHPUnit 35 | run: php vendor/bin/phpunit tests 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | .phpunit.result.cache -------------------------------------------------------------------------------- /.phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | reportUnmatchedIgnoredErrors: false 3 | 4 | bootstrapFiles: 5 | - %rootDir%/../../../vendor/autoload.php 6 | 7 | excludes_analyse: 8 | - %rootDir%/../../../src/autoload.php 9 | - %rootDir%/../../../src/ConnectorHelper.php 10 | 11 | ignoreErrors: 12 | - '#Function get_magic_quotes_gpc not found#' -------------------------------------------------------------------------------- /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | sonar.sources=src 2 | sonar.exclusions=misc/*,vendor/**/* 3 | sonar.tests=tests 4 | sonar.sourceEncoding=UTF-8 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://shadowd.zecure.org/img/logo_small.png) 2 | 3 | [![Build Status](https://github.com/zecure/shadowd_php/actions/workflows/analyze.yml/badge.svg)](https://github.com/zecure/shadowd_php/actions/workflows/analyze.yml) 4 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=zecure_shadowd_php&metric=alert_status)](https://sonarcloud.io/dashboard?id=zecure_shadowd_php) 5 | 6 | **Shadow Daemon** is a *web application firewall* that intercepts requests at the application level. 7 | This repository contains a component of Shadow Daemon to connect PHP applications with the [shadowd](https://github.com/zecure/shadowd) server. 8 | 9 | # Documentation 10 | For the full documentation please refer to [shadowd.zecure.org](https://shadowd.zecure.org/). 11 | 12 | # Installation 13 | 1. If you are not using a prepackaged release you first have to run `composer install`. 14 | 2. You have to create a configuration file at `/etc/shadowd/connectors.ini`. You can find an example configuration at `misc/examples/connectors.ini`. It is annotated and should be self-explanatory. 15 | 3. The PHP setting [auto_prepend_file](http://php.net/manual/en/ini.core.php#ini.auto-prepend-file) should be used to load `shadowd.php` automatically. 16 | It is highly recommended to set `auto_prepend_file` using the capabilities of your web server instead of relying on `php.ini`. 17 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zecure/shadowd_php", 3 | "license": "GPLv2", 4 | "prefer-stable": true, 5 | "require-dev": { 6 | "phpstan/phpstan": "^0.12", 7 | "phpunit/phpunit": "^8.5", 8 | "squizlabs/php_codesniffer": "~3.5" 9 | }, 10 | "require": { 11 | "ext-json": "*", 12 | "symfony/polyfill-mbstring": "^1.22" 13 | }, 14 | "autoload": { 15 | "files": [ 16 | "inc/Constants.php" 17 | ], 18 | "psr-4": { 19 | "shadowd\\": "src/" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "654f903efae6a28d5a165c1893ca092a", 8 | "packages": [ 9 | { 10 | "name": "symfony/polyfill-mbstring", 11 | "version": "v1.23.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/symfony/polyfill-mbstring.git", 15 | "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", 20 | "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=7.1" 25 | }, 26 | "suggest": { 27 | "ext-mbstring": "For best performance" 28 | }, 29 | "type": "library", 30 | "extra": { 31 | "branch-alias": { 32 | "dev-main": "1.23-dev" 33 | }, 34 | "thanks": { 35 | "name": "symfony/polyfill", 36 | "url": "https://github.com/symfony/polyfill" 37 | } 38 | }, 39 | "autoload": { 40 | "psr-4": { 41 | "Symfony\\Polyfill\\Mbstring\\": "" 42 | }, 43 | "files": [ 44 | "bootstrap.php" 45 | ] 46 | }, 47 | "notification-url": "https://packagist.org/downloads/", 48 | "license": [ 49 | "MIT" 50 | ], 51 | "authors": [ 52 | { 53 | "name": "Nicolas Grekas", 54 | "email": "p@tchwork.com" 55 | }, 56 | { 57 | "name": "Symfony Community", 58 | "homepage": "https://symfony.com/contributors" 59 | } 60 | ], 61 | "description": "Symfony polyfill for the Mbstring extension", 62 | "homepage": "https://symfony.com", 63 | "keywords": [ 64 | "compatibility", 65 | "mbstring", 66 | "polyfill", 67 | "portable", 68 | "shim" 69 | ], 70 | "support": { 71 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" 72 | }, 73 | "funding": [ 74 | { 75 | "url": "https://symfony.com/sponsor", 76 | "type": "custom" 77 | }, 78 | { 79 | "url": "https://github.com/fabpot", 80 | "type": "github" 81 | }, 82 | { 83 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 84 | "type": "tidelift" 85 | } 86 | ], 87 | "time": "2021-05-27T12:26:48+00:00" 88 | } 89 | ], 90 | "packages-dev": [ 91 | { 92 | "name": "doctrine/instantiator", 93 | "version": "1.4.0", 94 | "source": { 95 | "type": "git", 96 | "url": "https://github.com/doctrine/instantiator.git", 97 | "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" 98 | }, 99 | "dist": { 100 | "type": "zip", 101 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", 102 | "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", 103 | "shasum": "" 104 | }, 105 | "require": { 106 | "php": "^7.1 || ^8.0" 107 | }, 108 | "require-dev": { 109 | "doctrine/coding-standard": "^8.0", 110 | "ext-pdo": "*", 111 | "ext-phar": "*", 112 | "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", 113 | "phpstan/phpstan": "^0.12", 114 | "phpstan/phpstan-phpunit": "^0.12", 115 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" 116 | }, 117 | "type": "library", 118 | "autoload": { 119 | "psr-4": { 120 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 121 | } 122 | }, 123 | "notification-url": "https://packagist.org/downloads/", 124 | "license": [ 125 | "MIT" 126 | ], 127 | "authors": [ 128 | { 129 | "name": "Marco Pivetta", 130 | "email": "ocramius@gmail.com", 131 | "homepage": "https://ocramius.github.io/" 132 | } 133 | ], 134 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 135 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 136 | "keywords": [ 137 | "constructor", 138 | "instantiate" 139 | ], 140 | "support": { 141 | "issues": "https://github.com/doctrine/instantiator/issues", 142 | "source": "https://github.com/doctrine/instantiator/tree/1.4.0" 143 | }, 144 | "funding": [ 145 | { 146 | "url": "https://www.doctrine-project.org/sponsorship.html", 147 | "type": "custom" 148 | }, 149 | { 150 | "url": "https://www.patreon.com/phpdoctrine", 151 | "type": "patreon" 152 | }, 153 | { 154 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 155 | "type": "tidelift" 156 | } 157 | ], 158 | "time": "2020-11-10T18:47:58+00:00" 159 | }, 160 | { 161 | "name": "myclabs/deep-copy", 162 | "version": "1.10.2", 163 | "source": { 164 | "type": "git", 165 | "url": "https://github.com/myclabs/DeepCopy.git", 166 | "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" 167 | }, 168 | "dist": { 169 | "type": "zip", 170 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", 171 | "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", 172 | "shasum": "" 173 | }, 174 | "require": { 175 | "php": "^7.1 || ^8.0" 176 | }, 177 | "replace": { 178 | "myclabs/deep-copy": "self.version" 179 | }, 180 | "require-dev": { 181 | "doctrine/collections": "^1.0", 182 | "doctrine/common": "^2.6", 183 | "phpunit/phpunit": "^7.1" 184 | }, 185 | "type": "library", 186 | "autoload": { 187 | "psr-4": { 188 | "DeepCopy\\": "src/DeepCopy/" 189 | }, 190 | "files": [ 191 | "src/DeepCopy/deep_copy.php" 192 | ] 193 | }, 194 | "notification-url": "https://packagist.org/downloads/", 195 | "license": [ 196 | "MIT" 197 | ], 198 | "description": "Create deep copies (clones) of your objects", 199 | "keywords": [ 200 | "clone", 201 | "copy", 202 | "duplicate", 203 | "object", 204 | "object graph" 205 | ], 206 | "support": { 207 | "issues": "https://github.com/myclabs/DeepCopy/issues", 208 | "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" 209 | }, 210 | "funding": [ 211 | { 212 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 213 | "type": "tidelift" 214 | } 215 | ], 216 | "time": "2020-11-13T09:40:50+00:00" 217 | }, 218 | { 219 | "name": "phar-io/manifest", 220 | "version": "2.0.3", 221 | "source": { 222 | "type": "git", 223 | "url": "https://github.com/phar-io/manifest.git", 224 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53" 225 | }, 226 | "dist": { 227 | "type": "zip", 228 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", 229 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53", 230 | "shasum": "" 231 | }, 232 | "require": { 233 | "ext-dom": "*", 234 | "ext-phar": "*", 235 | "ext-xmlwriter": "*", 236 | "phar-io/version": "^3.0.1", 237 | "php": "^7.2 || ^8.0" 238 | }, 239 | "type": "library", 240 | "extra": { 241 | "branch-alias": { 242 | "dev-master": "2.0.x-dev" 243 | } 244 | }, 245 | "autoload": { 246 | "classmap": [ 247 | "src/" 248 | ] 249 | }, 250 | "notification-url": "https://packagist.org/downloads/", 251 | "license": [ 252 | "BSD-3-Clause" 253 | ], 254 | "authors": [ 255 | { 256 | "name": "Arne Blankerts", 257 | "email": "arne@blankerts.de", 258 | "role": "Developer" 259 | }, 260 | { 261 | "name": "Sebastian Heuer", 262 | "email": "sebastian@phpeople.de", 263 | "role": "Developer" 264 | }, 265 | { 266 | "name": "Sebastian Bergmann", 267 | "email": "sebastian@phpunit.de", 268 | "role": "Developer" 269 | } 270 | ], 271 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 272 | "support": { 273 | "issues": "https://github.com/phar-io/manifest/issues", 274 | "source": "https://github.com/phar-io/manifest/tree/2.0.3" 275 | }, 276 | "time": "2021-07-20T11:28:43+00:00" 277 | }, 278 | { 279 | "name": "phar-io/version", 280 | "version": "3.1.0", 281 | "source": { 282 | "type": "git", 283 | "url": "https://github.com/phar-io/version.git", 284 | "reference": "bae7c545bef187884426f042434e561ab1ddb182" 285 | }, 286 | "dist": { 287 | "type": "zip", 288 | "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", 289 | "reference": "bae7c545bef187884426f042434e561ab1ddb182", 290 | "shasum": "" 291 | }, 292 | "require": { 293 | "php": "^7.2 || ^8.0" 294 | }, 295 | "type": "library", 296 | "autoload": { 297 | "classmap": [ 298 | "src/" 299 | ] 300 | }, 301 | "notification-url": "https://packagist.org/downloads/", 302 | "license": [ 303 | "BSD-3-Clause" 304 | ], 305 | "authors": [ 306 | { 307 | "name": "Arne Blankerts", 308 | "email": "arne@blankerts.de", 309 | "role": "Developer" 310 | }, 311 | { 312 | "name": "Sebastian Heuer", 313 | "email": "sebastian@phpeople.de", 314 | "role": "Developer" 315 | }, 316 | { 317 | "name": "Sebastian Bergmann", 318 | "email": "sebastian@phpunit.de", 319 | "role": "Developer" 320 | } 321 | ], 322 | "description": "Library for handling version information and constraints", 323 | "support": { 324 | "issues": "https://github.com/phar-io/version/issues", 325 | "source": "https://github.com/phar-io/version/tree/3.1.0" 326 | }, 327 | "time": "2021-02-23T14:00:09+00:00" 328 | }, 329 | { 330 | "name": "phpdocumentor/reflection-common", 331 | "version": "2.2.0", 332 | "source": { 333 | "type": "git", 334 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 335 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" 336 | }, 337 | "dist": { 338 | "type": "zip", 339 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", 340 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", 341 | "shasum": "" 342 | }, 343 | "require": { 344 | "php": "^7.2 || ^8.0" 345 | }, 346 | "type": "library", 347 | "extra": { 348 | "branch-alias": { 349 | "dev-2.x": "2.x-dev" 350 | } 351 | }, 352 | "autoload": { 353 | "psr-4": { 354 | "phpDocumentor\\Reflection\\": "src/" 355 | } 356 | }, 357 | "notification-url": "https://packagist.org/downloads/", 358 | "license": [ 359 | "MIT" 360 | ], 361 | "authors": [ 362 | { 363 | "name": "Jaap van Otterdijk", 364 | "email": "opensource@ijaap.nl" 365 | } 366 | ], 367 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 368 | "homepage": "http://www.phpdoc.org", 369 | "keywords": [ 370 | "FQSEN", 371 | "phpDocumentor", 372 | "phpdoc", 373 | "reflection", 374 | "static analysis" 375 | ], 376 | "support": { 377 | "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", 378 | "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" 379 | }, 380 | "time": "2020-06-27T09:03:43+00:00" 381 | }, 382 | { 383 | "name": "phpdocumentor/reflection-docblock", 384 | "version": "5.2.2", 385 | "source": { 386 | "type": "git", 387 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 388 | "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" 389 | }, 390 | "dist": { 391 | "type": "zip", 392 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", 393 | "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", 394 | "shasum": "" 395 | }, 396 | "require": { 397 | "ext-filter": "*", 398 | "php": "^7.2 || ^8.0", 399 | "phpdocumentor/reflection-common": "^2.2", 400 | "phpdocumentor/type-resolver": "^1.3", 401 | "webmozart/assert": "^1.9.1" 402 | }, 403 | "require-dev": { 404 | "mockery/mockery": "~1.3.2" 405 | }, 406 | "type": "library", 407 | "extra": { 408 | "branch-alias": { 409 | "dev-master": "5.x-dev" 410 | } 411 | }, 412 | "autoload": { 413 | "psr-4": { 414 | "phpDocumentor\\Reflection\\": "src" 415 | } 416 | }, 417 | "notification-url": "https://packagist.org/downloads/", 418 | "license": [ 419 | "MIT" 420 | ], 421 | "authors": [ 422 | { 423 | "name": "Mike van Riel", 424 | "email": "me@mikevanriel.com" 425 | }, 426 | { 427 | "name": "Jaap van Otterdijk", 428 | "email": "account@ijaap.nl" 429 | } 430 | ], 431 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 432 | "support": { 433 | "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", 434 | "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" 435 | }, 436 | "time": "2020-09-03T19:13:55+00:00" 437 | }, 438 | { 439 | "name": "phpdocumentor/type-resolver", 440 | "version": "1.5.1", 441 | "source": { 442 | "type": "git", 443 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 444 | "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" 445 | }, 446 | "dist": { 447 | "type": "zip", 448 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", 449 | "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", 450 | "shasum": "" 451 | }, 452 | "require": { 453 | "php": "^7.2 || ^8.0", 454 | "phpdocumentor/reflection-common": "^2.0" 455 | }, 456 | "require-dev": { 457 | "ext-tokenizer": "*", 458 | "psalm/phar": "^4.8" 459 | }, 460 | "type": "library", 461 | "extra": { 462 | "branch-alias": { 463 | "dev-1.x": "1.x-dev" 464 | } 465 | }, 466 | "autoload": { 467 | "psr-4": { 468 | "phpDocumentor\\Reflection\\": "src" 469 | } 470 | }, 471 | "notification-url": "https://packagist.org/downloads/", 472 | "license": [ 473 | "MIT" 474 | ], 475 | "authors": [ 476 | { 477 | "name": "Mike van Riel", 478 | "email": "me@mikevanriel.com" 479 | } 480 | ], 481 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", 482 | "support": { 483 | "issues": "https://github.com/phpDocumentor/TypeResolver/issues", 484 | "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" 485 | }, 486 | "time": "2021-10-02T14:08:47+00:00" 487 | }, 488 | { 489 | "name": "phpspec/prophecy", 490 | "version": "1.14.0", 491 | "source": { 492 | "type": "git", 493 | "url": "https://github.com/phpspec/prophecy.git", 494 | "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" 495 | }, 496 | "dist": { 497 | "type": "zip", 498 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", 499 | "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", 500 | "shasum": "" 501 | }, 502 | "require": { 503 | "doctrine/instantiator": "^1.2", 504 | "php": "^7.2 || ~8.0, <8.2", 505 | "phpdocumentor/reflection-docblock": "^5.2", 506 | "sebastian/comparator": "^3.0 || ^4.0", 507 | "sebastian/recursion-context": "^3.0 || ^4.0" 508 | }, 509 | "require-dev": { 510 | "phpspec/phpspec": "^6.0 || ^7.0", 511 | "phpunit/phpunit": "^8.0 || ^9.0" 512 | }, 513 | "type": "library", 514 | "extra": { 515 | "branch-alias": { 516 | "dev-master": "1.x-dev" 517 | } 518 | }, 519 | "autoload": { 520 | "psr-4": { 521 | "Prophecy\\": "src/Prophecy" 522 | } 523 | }, 524 | "notification-url": "https://packagist.org/downloads/", 525 | "license": [ 526 | "MIT" 527 | ], 528 | "authors": [ 529 | { 530 | "name": "Konstantin Kudryashov", 531 | "email": "ever.zet@gmail.com", 532 | "homepage": "http://everzet.com" 533 | }, 534 | { 535 | "name": "Marcello Duarte", 536 | "email": "marcello.duarte@gmail.com" 537 | } 538 | ], 539 | "description": "Highly opinionated mocking framework for PHP 5.3+", 540 | "homepage": "https://github.com/phpspec/prophecy", 541 | "keywords": [ 542 | "Double", 543 | "Dummy", 544 | "fake", 545 | "mock", 546 | "spy", 547 | "stub" 548 | ], 549 | "support": { 550 | "issues": "https://github.com/phpspec/prophecy/issues", 551 | "source": "https://github.com/phpspec/prophecy/tree/1.14.0" 552 | }, 553 | "time": "2021-09-10T09:02:12+00:00" 554 | }, 555 | { 556 | "name": "phpstan/phpstan", 557 | "version": "0.12.99", 558 | "source": { 559 | "type": "git", 560 | "url": "https://github.com/phpstan/phpstan.git", 561 | "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" 562 | }, 563 | "dist": { 564 | "type": "zip", 565 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", 566 | "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", 567 | "shasum": "" 568 | }, 569 | "require": { 570 | "php": "^7.1|^8.0" 571 | }, 572 | "conflict": { 573 | "phpstan/phpstan-shim": "*" 574 | }, 575 | "bin": [ 576 | "phpstan", 577 | "phpstan.phar" 578 | ], 579 | "type": "library", 580 | "extra": { 581 | "branch-alias": { 582 | "dev-master": "0.12-dev" 583 | } 584 | }, 585 | "autoload": { 586 | "files": [ 587 | "bootstrap.php" 588 | ] 589 | }, 590 | "notification-url": "https://packagist.org/downloads/", 591 | "license": [ 592 | "MIT" 593 | ], 594 | "description": "PHPStan - PHP Static Analysis Tool", 595 | "support": { 596 | "issues": "https://github.com/phpstan/phpstan/issues", 597 | "source": "https://github.com/phpstan/phpstan/tree/0.12.99" 598 | }, 599 | "funding": [ 600 | { 601 | "url": "https://github.com/ondrejmirtes", 602 | "type": "github" 603 | }, 604 | { 605 | "url": "https://github.com/phpstan", 606 | "type": "github" 607 | }, 608 | { 609 | "url": "https://www.patreon.com/phpstan", 610 | "type": "patreon" 611 | }, 612 | { 613 | "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", 614 | "type": "tidelift" 615 | } 616 | ], 617 | "time": "2021-09-12T20:09:55+00:00" 618 | }, 619 | { 620 | "name": "phpunit/php-code-coverage", 621 | "version": "7.0.15", 622 | "source": { 623 | "type": "git", 624 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 625 | "reference": "819f92bba8b001d4363065928088de22f25a3a48" 626 | }, 627 | "dist": { 628 | "type": "zip", 629 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", 630 | "reference": "819f92bba8b001d4363065928088de22f25a3a48", 631 | "shasum": "" 632 | }, 633 | "require": { 634 | "ext-dom": "*", 635 | "ext-xmlwriter": "*", 636 | "php": ">=7.2", 637 | "phpunit/php-file-iterator": "^2.0.2", 638 | "phpunit/php-text-template": "^1.2.1", 639 | "phpunit/php-token-stream": "^3.1.3 || ^4.0", 640 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 641 | "sebastian/environment": "^4.2.2", 642 | "sebastian/version": "^2.0.1", 643 | "theseer/tokenizer": "^1.1.3" 644 | }, 645 | "require-dev": { 646 | "phpunit/phpunit": "^8.2.2" 647 | }, 648 | "suggest": { 649 | "ext-xdebug": "^2.7.2" 650 | }, 651 | "type": "library", 652 | "extra": { 653 | "branch-alias": { 654 | "dev-master": "7.0-dev" 655 | } 656 | }, 657 | "autoload": { 658 | "classmap": [ 659 | "src/" 660 | ] 661 | }, 662 | "notification-url": "https://packagist.org/downloads/", 663 | "license": [ 664 | "BSD-3-Clause" 665 | ], 666 | "authors": [ 667 | { 668 | "name": "Sebastian Bergmann", 669 | "email": "sebastian@phpunit.de", 670 | "role": "lead" 671 | } 672 | ], 673 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 674 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 675 | "keywords": [ 676 | "coverage", 677 | "testing", 678 | "xunit" 679 | ], 680 | "support": { 681 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 682 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" 683 | }, 684 | "funding": [ 685 | { 686 | "url": "https://github.com/sebastianbergmann", 687 | "type": "github" 688 | } 689 | ], 690 | "time": "2021-07-26T12:20:09+00:00" 691 | }, 692 | { 693 | "name": "phpunit/php-file-iterator", 694 | "version": "2.0.4", 695 | "source": { 696 | "type": "git", 697 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 698 | "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05" 699 | }, 700 | "dist": { 701 | "type": "zip", 702 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/28af674ff175d0768a5a978e6de83f697d4a7f05", 703 | "reference": "28af674ff175d0768a5a978e6de83f697d4a7f05", 704 | "shasum": "" 705 | }, 706 | "require": { 707 | "php": ">=7.1" 708 | }, 709 | "require-dev": { 710 | "phpunit/phpunit": "^8.5" 711 | }, 712 | "type": "library", 713 | "extra": { 714 | "branch-alias": { 715 | "dev-master": "2.0.x-dev" 716 | } 717 | }, 718 | "autoload": { 719 | "classmap": [ 720 | "src/" 721 | ] 722 | }, 723 | "notification-url": "https://packagist.org/downloads/", 724 | "license": [ 725 | "BSD-3-Clause" 726 | ], 727 | "authors": [ 728 | { 729 | "name": "Sebastian Bergmann", 730 | "email": "sebastian@phpunit.de", 731 | "role": "lead" 732 | } 733 | ], 734 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 735 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 736 | "keywords": [ 737 | "filesystem", 738 | "iterator" 739 | ], 740 | "support": { 741 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 742 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4" 743 | }, 744 | "funding": [ 745 | { 746 | "url": "https://github.com/sebastianbergmann", 747 | "type": "github" 748 | } 749 | ], 750 | "time": "2021-07-19T06:46:01+00:00" 751 | }, 752 | { 753 | "name": "phpunit/php-text-template", 754 | "version": "1.2.1", 755 | "source": { 756 | "type": "git", 757 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 758 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 759 | }, 760 | "dist": { 761 | "type": "zip", 762 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 763 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 764 | "shasum": "" 765 | }, 766 | "require": { 767 | "php": ">=5.3.3" 768 | }, 769 | "type": "library", 770 | "autoload": { 771 | "classmap": [ 772 | "src/" 773 | ] 774 | }, 775 | "notification-url": "https://packagist.org/downloads/", 776 | "license": [ 777 | "BSD-3-Clause" 778 | ], 779 | "authors": [ 780 | { 781 | "name": "Sebastian Bergmann", 782 | "email": "sebastian@phpunit.de", 783 | "role": "lead" 784 | } 785 | ], 786 | "description": "Simple template engine.", 787 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 788 | "keywords": [ 789 | "template" 790 | ], 791 | "support": { 792 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 793 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" 794 | }, 795 | "time": "2015-06-21T13:50:34+00:00" 796 | }, 797 | { 798 | "name": "phpunit/php-timer", 799 | "version": "2.1.3", 800 | "source": { 801 | "type": "git", 802 | "url": "https://github.com/sebastianbergmann/php-timer.git", 803 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" 804 | }, 805 | "dist": { 806 | "type": "zip", 807 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", 808 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", 809 | "shasum": "" 810 | }, 811 | "require": { 812 | "php": ">=7.1" 813 | }, 814 | "require-dev": { 815 | "phpunit/phpunit": "^8.5" 816 | }, 817 | "type": "library", 818 | "extra": { 819 | "branch-alias": { 820 | "dev-master": "2.1-dev" 821 | } 822 | }, 823 | "autoload": { 824 | "classmap": [ 825 | "src/" 826 | ] 827 | }, 828 | "notification-url": "https://packagist.org/downloads/", 829 | "license": [ 830 | "BSD-3-Clause" 831 | ], 832 | "authors": [ 833 | { 834 | "name": "Sebastian Bergmann", 835 | "email": "sebastian@phpunit.de", 836 | "role": "lead" 837 | } 838 | ], 839 | "description": "Utility class for timing", 840 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 841 | "keywords": [ 842 | "timer" 843 | ], 844 | "support": { 845 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 846 | "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" 847 | }, 848 | "funding": [ 849 | { 850 | "url": "https://github.com/sebastianbergmann", 851 | "type": "github" 852 | } 853 | ], 854 | "time": "2020-11-30T08:20:02+00:00" 855 | }, 856 | { 857 | "name": "phpunit/php-token-stream", 858 | "version": "3.1.3", 859 | "source": { 860 | "type": "git", 861 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 862 | "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" 863 | }, 864 | "dist": { 865 | "type": "zip", 866 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", 867 | "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", 868 | "shasum": "" 869 | }, 870 | "require": { 871 | "ext-tokenizer": "*", 872 | "php": ">=7.1" 873 | }, 874 | "require-dev": { 875 | "phpunit/phpunit": "^7.0" 876 | }, 877 | "type": "library", 878 | "extra": { 879 | "branch-alias": { 880 | "dev-master": "3.1-dev" 881 | } 882 | }, 883 | "autoload": { 884 | "classmap": [ 885 | "src/" 886 | ] 887 | }, 888 | "notification-url": "https://packagist.org/downloads/", 889 | "license": [ 890 | "BSD-3-Clause" 891 | ], 892 | "authors": [ 893 | { 894 | "name": "Sebastian Bergmann", 895 | "email": "sebastian@phpunit.de" 896 | } 897 | ], 898 | "description": "Wrapper around PHP's tokenizer extension.", 899 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 900 | "keywords": [ 901 | "tokenizer" 902 | ], 903 | "support": { 904 | "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", 905 | "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" 906 | }, 907 | "funding": [ 908 | { 909 | "url": "https://github.com/sebastianbergmann", 910 | "type": "github" 911 | } 912 | ], 913 | "abandoned": true, 914 | "time": "2021-07-26T12:15:06+00:00" 915 | }, 916 | { 917 | "name": "phpunit/phpunit", 918 | "version": "8.5.21", 919 | "source": { 920 | "type": "git", 921 | "url": "https://github.com/sebastianbergmann/phpunit.git", 922 | "reference": "50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984" 923 | }, 924 | "dist": { 925 | "type": "zip", 926 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984", 927 | "reference": "50a58a60b85947b0bee4c8ecfe0f4bbdcf20e984", 928 | "shasum": "" 929 | }, 930 | "require": { 931 | "doctrine/instantiator": "^1.3.1", 932 | "ext-dom": "*", 933 | "ext-json": "*", 934 | "ext-libxml": "*", 935 | "ext-mbstring": "*", 936 | "ext-xml": "*", 937 | "ext-xmlwriter": "*", 938 | "myclabs/deep-copy": "^1.10.0", 939 | "phar-io/manifest": "^2.0.3", 940 | "phar-io/version": "^3.0.2", 941 | "php": ">=7.2", 942 | "phpspec/prophecy": "^1.10.3", 943 | "phpunit/php-code-coverage": "^7.0.12", 944 | "phpunit/php-file-iterator": "^2.0.4", 945 | "phpunit/php-text-template": "^1.2.1", 946 | "phpunit/php-timer": "^2.1.2", 947 | "sebastian/comparator": "^3.0.2", 948 | "sebastian/diff": "^3.0.2", 949 | "sebastian/environment": "^4.2.3", 950 | "sebastian/exporter": "^3.1.2", 951 | "sebastian/global-state": "^3.0.0", 952 | "sebastian/object-enumerator": "^3.0.3", 953 | "sebastian/resource-operations": "^2.0.1", 954 | "sebastian/type": "^1.1.3", 955 | "sebastian/version": "^2.0.1" 956 | }, 957 | "require-dev": { 958 | "ext-pdo": "*" 959 | }, 960 | "suggest": { 961 | "ext-soap": "*", 962 | "ext-xdebug": "*", 963 | "phpunit/php-invoker": "^2.0.0" 964 | }, 965 | "bin": [ 966 | "phpunit" 967 | ], 968 | "type": "library", 969 | "extra": { 970 | "branch-alias": { 971 | "dev-master": "8.5-dev" 972 | } 973 | }, 974 | "autoload": { 975 | "classmap": [ 976 | "src/" 977 | ] 978 | }, 979 | "notification-url": "https://packagist.org/downloads/", 980 | "license": [ 981 | "BSD-3-Clause" 982 | ], 983 | "authors": [ 984 | { 985 | "name": "Sebastian Bergmann", 986 | "email": "sebastian@phpunit.de", 987 | "role": "lead" 988 | } 989 | ], 990 | "description": "The PHP Unit Testing framework.", 991 | "homepage": "https://phpunit.de/", 992 | "keywords": [ 993 | "phpunit", 994 | "testing", 995 | "xunit" 996 | ], 997 | "support": { 998 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 999 | "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.21" 1000 | }, 1001 | "funding": [ 1002 | { 1003 | "url": "https://phpunit.de/donate.html", 1004 | "type": "custom" 1005 | }, 1006 | { 1007 | "url": "https://github.com/sebastianbergmann", 1008 | "type": "github" 1009 | } 1010 | ], 1011 | "time": "2021-09-25T07:37:20+00:00" 1012 | }, 1013 | { 1014 | "name": "sebastian/code-unit-reverse-lookup", 1015 | "version": "1.0.2", 1016 | "source": { 1017 | "type": "git", 1018 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1019 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" 1020 | }, 1021 | "dist": { 1022 | "type": "zip", 1023 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", 1024 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", 1025 | "shasum": "" 1026 | }, 1027 | "require": { 1028 | "php": ">=5.6" 1029 | }, 1030 | "require-dev": { 1031 | "phpunit/phpunit": "^8.5" 1032 | }, 1033 | "type": "library", 1034 | "extra": { 1035 | "branch-alias": { 1036 | "dev-master": "1.0.x-dev" 1037 | } 1038 | }, 1039 | "autoload": { 1040 | "classmap": [ 1041 | "src/" 1042 | ] 1043 | }, 1044 | "notification-url": "https://packagist.org/downloads/", 1045 | "license": [ 1046 | "BSD-3-Clause" 1047 | ], 1048 | "authors": [ 1049 | { 1050 | "name": "Sebastian Bergmann", 1051 | "email": "sebastian@phpunit.de" 1052 | } 1053 | ], 1054 | "description": "Looks up which function or method a line of code belongs to", 1055 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1056 | "support": { 1057 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 1058 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" 1059 | }, 1060 | "funding": [ 1061 | { 1062 | "url": "https://github.com/sebastianbergmann", 1063 | "type": "github" 1064 | } 1065 | ], 1066 | "time": "2020-11-30T08:15:22+00:00" 1067 | }, 1068 | { 1069 | "name": "sebastian/comparator", 1070 | "version": "3.0.3", 1071 | "source": { 1072 | "type": "git", 1073 | "url": "https://github.com/sebastianbergmann/comparator.git", 1074 | "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" 1075 | }, 1076 | "dist": { 1077 | "type": "zip", 1078 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", 1079 | "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", 1080 | "shasum": "" 1081 | }, 1082 | "require": { 1083 | "php": ">=7.1", 1084 | "sebastian/diff": "^3.0", 1085 | "sebastian/exporter": "^3.1" 1086 | }, 1087 | "require-dev": { 1088 | "phpunit/phpunit": "^8.5" 1089 | }, 1090 | "type": "library", 1091 | "extra": { 1092 | "branch-alias": { 1093 | "dev-master": "3.0-dev" 1094 | } 1095 | }, 1096 | "autoload": { 1097 | "classmap": [ 1098 | "src/" 1099 | ] 1100 | }, 1101 | "notification-url": "https://packagist.org/downloads/", 1102 | "license": [ 1103 | "BSD-3-Clause" 1104 | ], 1105 | "authors": [ 1106 | { 1107 | "name": "Sebastian Bergmann", 1108 | "email": "sebastian@phpunit.de" 1109 | }, 1110 | { 1111 | "name": "Jeff Welch", 1112 | "email": "whatthejeff@gmail.com" 1113 | }, 1114 | { 1115 | "name": "Volker Dusch", 1116 | "email": "github@wallbash.com" 1117 | }, 1118 | { 1119 | "name": "Bernhard Schussek", 1120 | "email": "bschussek@2bepublished.at" 1121 | } 1122 | ], 1123 | "description": "Provides the functionality to compare PHP values for equality", 1124 | "homepage": "https://github.com/sebastianbergmann/comparator", 1125 | "keywords": [ 1126 | "comparator", 1127 | "compare", 1128 | "equality" 1129 | ], 1130 | "support": { 1131 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 1132 | "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" 1133 | }, 1134 | "funding": [ 1135 | { 1136 | "url": "https://github.com/sebastianbergmann", 1137 | "type": "github" 1138 | } 1139 | ], 1140 | "time": "2020-11-30T08:04:30+00:00" 1141 | }, 1142 | { 1143 | "name": "sebastian/diff", 1144 | "version": "3.0.3", 1145 | "source": { 1146 | "type": "git", 1147 | "url": "https://github.com/sebastianbergmann/diff.git", 1148 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" 1149 | }, 1150 | "dist": { 1151 | "type": "zip", 1152 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", 1153 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", 1154 | "shasum": "" 1155 | }, 1156 | "require": { 1157 | "php": ">=7.1" 1158 | }, 1159 | "require-dev": { 1160 | "phpunit/phpunit": "^7.5 || ^8.0", 1161 | "symfony/process": "^2 || ^3.3 || ^4" 1162 | }, 1163 | "type": "library", 1164 | "extra": { 1165 | "branch-alias": { 1166 | "dev-master": "3.0-dev" 1167 | } 1168 | }, 1169 | "autoload": { 1170 | "classmap": [ 1171 | "src/" 1172 | ] 1173 | }, 1174 | "notification-url": "https://packagist.org/downloads/", 1175 | "license": [ 1176 | "BSD-3-Clause" 1177 | ], 1178 | "authors": [ 1179 | { 1180 | "name": "Sebastian Bergmann", 1181 | "email": "sebastian@phpunit.de" 1182 | }, 1183 | { 1184 | "name": "Kore Nordmann", 1185 | "email": "mail@kore-nordmann.de" 1186 | } 1187 | ], 1188 | "description": "Diff implementation", 1189 | "homepage": "https://github.com/sebastianbergmann/diff", 1190 | "keywords": [ 1191 | "diff", 1192 | "udiff", 1193 | "unidiff", 1194 | "unified diff" 1195 | ], 1196 | "support": { 1197 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1198 | "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" 1199 | }, 1200 | "funding": [ 1201 | { 1202 | "url": "https://github.com/sebastianbergmann", 1203 | "type": "github" 1204 | } 1205 | ], 1206 | "time": "2020-11-30T07:59:04+00:00" 1207 | }, 1208 | { 1209 | "name": "sebastian/environment", 1210 | "version": "4.2.4", 1211 | "source": { 1212 | "type": "git", 1213 | "url": "https://github.com/sebastianbergmann/environment.git", 1214 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" 1215 | }, 1216 | "dist": { 1217 | "type": "zip", 1218 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", 1219 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", 1220 | "shasum": "" 1221 | }, 1222 | "require": { 1223 | "php": ">=7.1" 1224 | }, 1225 | "require-dev": { 1226 | "phpunit/phpunit": "^7.5" 1227 | }, 1228 | "suggest": { 1229 | "ext-posix": "*" 1230 | }, 1231 | "type": "library", 1232 | "extra": { 1233 | "branch-alias": { 1234 | "dev-master": "4.2-dev" 1235 | } 1236 | }, 1237 | "autoload": { 1238 | "classmap": [ 1239 | "src/" 1240 | ] 1241 | }, 1242 | "notification-url": "https://packagist.org/downloads/", 1243 | "license": [ 1244 | "BSD-3-Clause" 1245 | ], 1246 | "authors": [ 1247 | { 1248 | "name": "Sebastian Bergmann", 1249 | "email": "sebastian@phpunit.de" 1250 | } 1251 | ], 1252 | "description": "Provides functionality to handle HHVM/PHP environments", 1253 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1254 | "keywords": [ 1255 | "Xdebug", 1256 | "environment", 1257 | "hhvm" 1258 | ], 1259 | "support": { 1260 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1261 | "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" 1262 | }, 1263 | "funding": [ 1264 | { 1265 | "url": "https://github.com/sebastianbergmann", 1266 | "type": "github" 1267 | } 1268 | ], 1269 | "time": "2020-11-30T07:53:42+00:00" 1270 | }, 1271 | { 1272 | "name": "sebastian/exporter", 1273 | "version": "3.1.3", 1274 | "source": { 1275 | "type": "git", 1276 | "url": "https://github.com/sebastianbergmann/exporter.git", 1277 | "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" 1278 | }, 1279 | "dist": { 1280 | "type": "zip", 1281 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", 1282 | "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", 1283 | "shasum": "" 1284 | }, 1285 | "require": { 1286 | "php": ">=7.0", 1287 | "sebastian/recursion-context": "^3.0" 1288 | }, 1289 | "require-dev": { 1290 | "ext-mbstring": "*", 1291 | "phpunit/phpunit": "^6.0" 1292 | }, 1293 | "type": "library", 1294 | "extra": { 1295 | "branch-alias": { 1296 | "dev-master": "3.1.x-dev" 1297 | } 1298 | }, 1299 | "autoload": { 1300 | "classmap": [ 1301 | "src/" 1302 | ] 1303 | }, 1304 | "notification-url": "https://packagist.org/downloads/", 1305 | "license": [ 1306 | "BSD-3-Clause" 1307 | ], 1308 | "authors": [ 1309 | { 1310 | "name": "Sebastian Bergmann", 1311 | "email": "sebastian@phpunit.de" 1312 | }, 1313 | { 1314 | "name": "Jeff Welch", 1315 | "email": "whatthejeff@gmail.com" 1316 | }, 1317 | { 1318 | "name": "Volker Dusch", 1319 | "email": "github@wallbash.com" 1320 | }, 1321 | { 1322 | "name": "Adam Harvey", 1323 | "email": "aharvey@php.net" 1324 | }, 1325 | { 1326 | "name": "Bernhard Schussek", 1327 | "email": "bschussek@gmail.com" 1328 | } 1329 | ], 1330 | "description": "Provides the functionality to export PHP variables for visualization", 1331 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1332 | "keywords": [ 1333 | "export", 1334 | "exporter" 1335 | ], 1336 | "support": { 1337 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1338 | "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" 1339 | }, 1340 | "funding": [ 1341 | { 1342 | "url": "https://github.com/sebastianbergmann", 1343 | "type": "github" 1344 | } 1345 | ], 1346 | "time": "2020-11-30T07:47:53+00:00" 1347 | }, 1348 | { 1349 | "name": "sebastian/global-state", 1350 | "version": "3.0.1", 1351 | "source": { 1352 | "type": "git", 1353 | "url": "https://github.com/sebastianbergmann/global-state.git", 1354 | "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" 1355 | }, 1356 | "dist": { 1357 | "type": "zip", 1358 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", 1359 | "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", 1360 | "shasum": "" 1361 | }, 1362 | "require": { 1363 | "php": ">=7.2", 1364 | "sebastian/object-reflector": "^1.1.1", 1365 | "sebastian/recursion-context": "^3.0" 1366 | }, 1367 | "require-dev": { 1368 | "ext-dom": "*", 1369 | "phpunit/phpunit": "^8.0" 1370 | }, 1371 | "suggest": { 1372 | "ext-uopz": "*" 1373 | }, 1374 | "type": "library", 1375 | "extra": { 1376 | "branch-alias": { 1377 | "dev-master": "3.0-dev" 1378 | } 1379 | }, 1380 | "autoload": { 1381 | "classmap": [ 1382 | "src/" 1383 | ] 1384 | }, 1385 | "notification-url": "https://packagist.org/downloads/", 1386 | "license": [ 1387 | "BSD-3-Clause" 1388 | ], 1389 | "authors": [ 1390 | { 1391 | "name": "Sebastian Bergmann", 1392 | "email": "sebastian@phpunit.de" 1393 | } 1394 | ], 1395 | "description": "Snapshotting of global state", 1396 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1397 | "keywords": [ 1398 | "global state" 1399 | ], 1400 | "support": { 1401 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1402 | "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" 1403 | }, 1404 | "funding": [ 1405 | { 1406 | "url": "https://github.com/sebastianbergmann", 1407 | "type": "github" 1408 | } 1409 | ], 1410 | "time": "2020-11-30T07:43:24+00:00" 1411 | }, 1412 | { 1413 | "name": "sebastian/object-enumerator", 1414 | "version": "3.0.4", 1415 | "source": { 1416 | "type": "git", 1417 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1418 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" 1419 | }, 1420 | "dist": { 1421 | "type": "zip", 1422 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", 1423 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", 1424 | "shasum": "" 1425 | }, 1426 | "require": { 1427 | "php": ">=7.0", 1428 | "sebastian/object-reflector": "^1.1.1", 1429 | "sebastian/recursion-context": "^3.0" 1430 | }, 1431 | "require-dev": { 1432 | "phpunit/phpunit": "^6.0" 1433 | }, 1434 | "type": "library", 1435 | "extra": { 1436 | "branch-alias": { 1437 | "dev-master": "3.0.x-dev" 1438 | } 1439 | }, 1440 | "autoload": { 1441 | "classmap": [ 1442 | "src/" 1443 | ] 1444 | }, 1445 | "notification-url": "https://packagist.org/downloads/", 1446 | "license": [ 1447 | "BSD-3-Clause" 1448 | ], 1449 | "authors": [ 1450 | { 1451 | "name": "Sebastian Bergmann", 1452 | "email": "sebastian@phpunit.de" 1453 | } 1454 | ], 1455 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1456 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1457 | "support": { 1458 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1459 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" 1460 | }, 1461 | "funding": [ 1462 | { 1463 | "url": "https://github.com/sebastianbergmann", 1464 | "type": "github" 1465 | } 1466 | ], 1467 | "time": "2020-11-30T07:40:27+00:00" 1468 | }, 1469 | { 1470 | "name": "sebastian/object-reflector", 1471 | "version": "1.1.2", 1472 | "source": { 1473 | "type": "git", 1474 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1475 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" 1476 | }, 1477 | "dist": { 1478 | "type": "zip", 1479 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", 1480 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", 1481 | "shasum": "" 1482 | }, 1483 | "require": { 1484 | "php": ">=7.0" 1485 | }, 1486 | "require-dev": { 1487 | "phpunit/phpunit": "^6.0" 1488 | }, 1489 | "type": "library", 1490 | "extra": { 1491 | "branch-alias": { 1492 | "dev-master": "1.1-dev" 1493 | } 1494 | }, 1495 | "autoload": { 1496 | "classmap": [ 1497 | "src/" 1498 | ] 1499 | }, 1500 | "notification-url": "https://packagist.org/downloads/", 1501 | "license": [ 1502 | "BSD-3-Clause" 1503 | ], 1504 | "authors": [ 1505 | { 1506 | "name": "Sebastian Bergmann", 1507 | "email": "sebastian@phpunit.de" 1508 | } 1509 | ], 1510 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1511 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1512 | "support": { 1513 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1514 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" 1515 | }, 1516 | "funding": [ 1517 | { 1518 | "url": "https://github.com/sebastianbergmann", 1519 | "type": "github" 1520 | } 1521 | ], 1522 | "time": "2020-11-30T07:37:18+00:00" 1523 | }, 1524 | { 1525 | "name": "sebastian/recursion-context", 1526 | "version": "3.0.1", 1527 | "source": { 1528 | "type": "git", 1529 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1530 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" 1531 | }, 1532 | "dist": { 1533 | "type": "zip", 1534 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", 1535 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", 1536 | "shasum": "" 1537 | }, 1538 | "require": { 1539 | "php": ">=7.0" 1540 | }, 1541 | "require-dev": { 1542 | "phpunit/phpunit": "^6.0" 1543 | }, 1544 | "type": "library", 1545 | "extra": { 1546 | "branch-alias": { 1547 | "dev-master": "3.0.x-dev" 1548 | } 1549 | }, 1550 | "autoload": { 1551 | "classmap": [ 1552 | "src/" 1553 | ] 1554 | }, 1555 | "notification-url": "https://packagist.org/downloads/", 1556 | "license": [ 1557 | "BSD-3-Clause" 1558 | ], 1559 | "authors": [ 1560 | { 1561 | "name": "Sebastian Bergmann", 1562 | "email": "sebastian@phpunit.de" 1563 | }, 1564 | { 1565 | "name": "Jeff Welch", 1566 | "email": "whatthejeff@gmail.com" 1567 | }, 1568 | { 1569 | "name": "Adam Harvey", 1570 | "email": "aharvey@php.net" 1571 | } 1572 | ], 1573 | "description": "Provides functionality to recursively process PHP variables", 1574 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1575 | "support": { 1576 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1577 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" 1578 | }, 1579 | "funding": [ 1580 | { 1581 | "url": "https://github.com/sebastianbergmann", 1582 | "type": "github" 1583 | } 1584 | ], 1585 | "time": "2020-11-30T07:34:24+00:00" 1586 | }, 1587 | { 1588 | "name": "sebastian/resource-operations", 1589 | "version": "2.0.2", 1590 | "source": { 1591 | "type": "git", 1592 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1593 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" 1594 | }, 1595 | "dist": { 1596 | "type": "zip", 1597 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", 1598 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", 1599 | "shasum": "" 1600 | }, 1601 | "require": { 1602 | "php": ">=7.1" 1603 | }, 1604 | "type": "library", 1605 | "extra": { 1606 | "branch-alias": { 1607 | "dev-master": "2.0-dev" 1608 | } 1609 | }, 1610 | "autoload": { 1611 | "classmap": [ 1612 | "src/" 1613 | ] 1614 | }, 1615 | "notification-url": "https://packagist.org/downloads/", 1616 | "license": [ 1617 | "BSD-3-Clause" 1618 | ], 1619 | "authors": [ 1620 | { 1621 | "name": "Sebastian Bergmann", 1622 | "email": "sebastian@phpunit.de" 1623 | } 1624 | ], 1625 | "description": "Provides a list of PHP built-in functions that operate on resources", 1626 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1627 | "support": { 1628 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues", 1629 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" 1630 | }, 1631 | "funding": [ 1632 | { 1633 | "url": "https://github.com/sebastianbergmann", 1634 | "type": "github" 1635 | } 1636 | ], 1637 | "abandoned": true, 1638 | "time": "2020-11-30T07:30:19+00:00" 1639 | }, 1640 | { 1641 | "name": "sebastian/type", 1642 | "version": "1.1.4", 1643 | "source": { 1644 | "type": "git", 1645 | "url": "https://github.com/sebastianbergmann/type.git", 1646 | "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" 1647 | }, 1648 | "dist": { 1649 | "type": "zip", 1650 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", 1651 | "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", 1652 | "shasum": "" 1653 | }, 1654 | "require": { 1655 | "php": ">=7.2" 1656 | }, 1657 | "require-dev": { 1658 | "phpunit/phpunit": "^8.2" 1659 | }, 1660 | "type": "library", 1661 | "extra": { 1662 | "branch-alias": { 1663 | "dev-master": "1.1-dev" 1664 | } 1665 | }, 1666 | "autoload": { 1667 | "classmap": [ 1668 | "src/" 1669 | ] 1670 | }, 1671 | "notification-url": "https://packagist.org/downloads/", 1672 | "license": [ 1673 | "BSD-3-Clause" 1674 | ], 1675 | "authors": [ 1676 | { 1677 | "name": "Sebastian Bergmann", 1678 | "email": "sebastian@phpunit.de", 1679 | "role": "lead" 1680 | } 1681 | ], 1682 | "description": "Collection of value objects that represent the types of the PHP type system", 1683 | "homepage": "https://github.com/sebastianbergmann/type", 1684 | "support": { 1685 | "issues": "https://github.com/sebastianbergmann/type/issues", 1686 | "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" 1687 | }, 1688 | "funding": [ 1689 | { 1690 | "url": "https://github.com/sebastianbergmann", 1691 | "type": "github" 1692 | } 1693 | ], 1694 | "time": "2020-11-30T07:25:11+00:00" 1695 | }, 1696 | { 1697 | "name": "sebastian/version", 1698 | "version": "2.0.1", 1699 | "source": { 1700 | "type": "git", 1701 | "url": "https://github.com/sebastianbergmann/version.git", 1702 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1703 | }, 1704 | "dist": { 1705 | "type": "zip", 1706 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1707 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1708 | "shasum": "" 1709 | }, 1710 | "require": { 1711 | "php": ">=5.6" 1712 | }, 1713 | "type": "library", 1714 | "extra": { 1715 | "branch-alias": { 1716 | "dev-master": "2.0.x-dev" 1717 | } 1718 | }, 1719 | "autoload": { 1720 | "classmap": [ 1721 | "src/" 1722 | ] 1723 | }, 1724 | "notification-url": "https://packagist.org/downloads/", 1725 | "license": [ 1726 | "BSD-3-Clause" 1727 | ], 1728 | "authors": [ 1729 | { 1730 | "name": "Sebastian Bergmann", 1731 | "email": "sebastian@phpunit.de", 1732 | "role": "lead" 1733 | } 1734 | ], 1735 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1736 | "homepage": "https://github.com/sebastianbergmann/version", 1737 | "support": { 1738 | "issues": "https://github.com/sebastianbergmann/version/issues", 1739 | "source": "https://github.com/sebastianbergmann/version/tree/master" 1740 | }, 1741 | "time": "2016-10-03T07:35:21+00:00" 1742 | }, 1743 | { 1744 | "name": "squizlabs/php_codesniffer", 1745 | "version": "3.6.1", 1746 | "source": { 1747 | "type": "git", 1748 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 1749 | "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e" 1750 | }, 1751 | "dist": { 1752 | "type": "zip", 1753 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f268ca40d54617c6e06757f83f699775c9b3ff2e", 1754 | "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e", 1755 | "shasum": "" 1756 | }, 1757 | "require": { 1758 | "ext-simplexml": "*", 1759 | "ext-tokenizer": "*", 1760 | "ext-xmlwriter": "*", 1761 | "php": ">=5.4.0" 1762 | }, 1763 | "require-dev": { 1764 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1765 | }, 1766 | "bin": [ 1767 | "bin/phpcs", 1768 | "bin/phpcbf" 1769 | ], 1770 | "type": "library", 1771 | "extra": { 1772 | "branch-alias": { 1773 | "dev-master": "3.x-dev" 1774 | } 1775 | }, 1776 | "notification-url": "https://packagist.org/downloads/", 1777 | "license": [ 1778 | "BSD-3-Clause" 1779 | ], 1780 | "authors": [ 1781 | { 1782 | "name": "Greg Sherwood", 1783 | "role": "lead" 1784 | } 1785 | ], 1786 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1787 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", 1788 | "keywords": [ 1789 | "phpcs", 1790 | "standards" 1791 | ], 1792 | "support": { 1793 | "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", 1794 | "source": "https://github.com/squizlabs/PHP_CodeSniffer", 1795 | "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" 1796 | }, 1797 | "time": "2021-10-11T04:00:11+00:00" 1798 | }, 1799 | { 1800 | "name": "symfony/polyfill-ctype", 1801 | "version": "v1.23.0", 1802 | "source": { 1803 | "type": "git", 1804 | "url": "https://github.com/symfony/polyfill-ctype.git", 1805 | "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" 1806 | }, 1807 | "dist": { 1808 | "type": "zip", 1809 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", 1810 | "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", 1811 | "shasum": "" 1812 | }, 1813 | "require": { 1814 | "php": ">=7.1" 1815 | }, 1816 | "suggest": { 1817 | "ext-ctype": "For best performance" 1818 | }, 1819 | "type": "library", 1820 | "extra": { 1821 | "branch-alias": { 1822 | "dev-main": "1.23-dev" 1823 | }, 1824 | "thanks": { 1825 | "name": "symfony/polyfill", 1826 | "url": "https://github.com/symfony/polyfill" 1827 | } 1828 | }, 1829 | "autoload": { 1830 | "psr-4": { 1831 | "Symfony\\Polyfill\\Ctype\\": "" 1832 | }, 1833 | "files": [ 1834 | "bootstrap.php" 1835 | ] 1836 | }, 1837 | "notification-url": "https://packagist.org/downloads/", 1838 | "license": [ 1839 | "MIT" 1840 | ], 1841 | "authors": [ 1842 | { 1843 | "name": "Gert de Pagter", 1844 | "email": "BackEndTea@gmail.com" 1845 | }, 1846 | { 1847 | "name": "Symfony Community", 1848 | "homepage": "https://symfony.com/contributors" 1849 | } 1850 | ], 1851 | "description": "Symfony polyfill for ctype functions", 1852 | "homepage": "https://symfony.com", 1853 | "keywords": [ 1854 | "compatibility", 1855 | "ctype", 1856 | "polyfill", 1857 | "portable" 1858 | ], 1859 | "support": { 1860 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" 1861 | }, 1862 | "funding": [ 1863 | { 1864 | "url": "https://symfony.com/sponsor", 1865 | "type": "custom" 1866 | }, 1867 | { 1868 | "url": "https://github.com/fabpot", 1869 | "type": "github" 1870 | }, 1871 | { 1872 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1873 | "type": "tidelift" 1874 | } 1875 | ], 1876 | "time": "2021-02-19T12:13:01+00:00" 1877 | }, 1878 | { 1879 | "name": "theseer/tokenizer", 1880 | "version": "1.2.1", 1881 | "source": { 1882 | "type": "git", 1883 | "url": "https://github.com/theseer/tokenizer.git", 1884 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" 1885 | }, 1886 | "dist": { 1887 | "type": "zip", 1888 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", 1889 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", 1890 | "shasum": "" 1891 | }, 1892 | "require": { 1893 | "ext-dom": "*", 1894 | "ext-tokenizer": "*", 1895 | "ext-xmlwriter": "*", 1896 | "php": "^7.2 || ^8.0" 1897 | }, 1898 | "type": "library", 1899 | "autoload": { 1900 | "classmap": [ 1901 | "src/" 1902 | ] 1903 | }, 1904 | "notification-url": "https://packagist.org/downloads/", 1905 | "license": [ 1906 | "BSD-3-Clause" 1907 | ], 1908 | "authors": [ 1909 | { 1910 | "name": "Arne Blankerts", 1911 | "email": "arne@blankerts.de", 1912 | "role": "Developer" 1913 | } 1914 | ], 1915 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1916 | "support": { 1917 | "issues": "https://github.com/theseer/tokenizer/issues", 1918 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1" 1919 | }, 1920 | "funding": [ 1921 | { 1922 | "url": "https://github.com/theseer", 1923 | "type": "github" 1924 | } 1925 | ], 1926 | "time": "2021-07-28T10:34:58+00:00" 1927 | }, 1928 | { 1929 | "name": "webmozart/assert", 1930 | "version": "1.10.0", 1931 | "source": { 1932 | "type": "git", 1933 | "url": "https://github.com/webmozarts/assert.git", 1934 | "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" 1935 | }, 1936 | "dist": { 1937 | "type": "zip", 1938 | "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", 1939 | "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", 1940 | "shasum": "" 1941 | }, 1942 | "require": { 1943 | "php": "^7.2 || ^8.0", 1944 | "symfony/polyfill-ctype": "^1.8" 1945 | }, 1946 | "conflict": { 1947 | "phpstan/phpstan": "<0.12.20", 1948 | "vimeo/psalm": "<4.6.1 || 4.6.2" 1949 | }, 1950 | "require-dev": { 1951 | "phpunit/phpunit": "^8.5.13" 1952 | }, 1953 | "type": "library", 1954 | "extra": { 1955 | "branch-alias": { 1956 | "dev-master": "1.10-dev" 1957 | } 1958 | }, 1959 | "autoload": { 1960 | "psr-4": { 1961 | "Webmozart\\Assert\\": "src/" 1962 | } 1963 | }, 1964 | "notification-url": "https://packagist.org/downloads/", 1965 | "license": [ 1966 | "MIT" 1967 | ], 1968 | "authors": [ 1969 | { 1970 | "name": "Bernhard Schussek", 1971 | "email": "bschussek@gmail.com" 1972 | } 1973 | ], 1974 | "description": "Assertions to validate method input/output with nice error messages.", 1975 | "keywords": [ 1976 | "assert", 1977 | "check", 1978 | "validate" 1979 | ], 1980 | "support": { 1981 | "issues": "https://github.com/webmozarts/assert/issues", 1982 | "source": "https://github.com/webmozarts/assert/tree/1.10.0" 1983 | }, 1984 | "time": "2021-03-09T10:59:23+00:00" 1985 | } 1986 | ], 1987 | "aliases": [], 1988 | "minimum-stability": "stable", 1989 | "stability-flags": [], 1990 | "prefer-stable": true, 1991 | "prefer-lowest": false, 1992 | "platform": { 1993 | "ext-json": "*" 1994 | }, 1995 | "platform-dev": [], 1996 | "plugin-api-version": "2.0.0" 1997 | } 1998 | -------------------------------------------------------------------------------- /inc/Constants.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | define('SHADOWD_CONNECTOR_VERSION', '2.2.0-php'); 22 | define('SHADOWD_LOG_PREFIX', 'shadowd: '); 23 | define('SHADOWD_ROOT_DIR', realpath(__DIR__ . '/..')); 24 | define('SHADOWD_MISC_TESTS', SHADOWD_ROOT_DIR . '/misc/tests/'); 25 | define('SHADOWD_DEFAULT_CONFIG_FILE', '/etc/shadowd/connectors.ini'); 26 | define('SHADOWD_DEFAULT_CONFIG_SECTION', 'shadowd_php'); 27 | define('SHADOWD_CONFIG_ENV_PREFIX', 'SHADOWD_CONNECTOR_CONFIG_'); 28 | define('SHADOWD_STATUS_OK', '1'); 29 | define('SHADOWD_STATUS_BAD_REQUEST', '2'); 30 | define('SHADOWD_STATUS_BAD_SIGNATURE', '3'); 31 | define('SHADOWD_STATUS_BAD_JSON', '4'); 32 | define('SHADOWD_STATUS_ATTACK', '5'); 33 | define('SHADOWD_STATUS_CRITICAL_ATTACK', '6'); 34 | -------------------------------------------------------------------------------- /misc/examples/.htaccess: -------------------------------------------------------------------------------- 1 | php_value auto_prepend_file "/usr/share/shadowd/php/Connector.php" 2 | -------------------------------------------------------------------------------- /misc/examples/connectors.ini: -------------------------------------------------------------------------------- 1 | [shadowd_php] 2 | ; Sets the profile id. Required. 3 | ;profile= 4 | 5 | ; Sets the key for the profile. Required. 6 | ;key= 7 | 8 | ; Sets the address of the shadowd server. 9 | ; Default Value: 127.0.0.1 10 | ;host= 11 | 12 | ; Sets the port of the shadowd server. 13 | ; Default Value: 9115 14 | ;port= 15 | 16 | ; Sets the connection timeout (in seconds). 17 | ; Default Value: 5 18 | ;timeout= 19 | 20 | ; Sets the path to the public key and enables SSL. It requires enabled SSL in 21 | ; shadowd, otherwise the connection will fail. 22 | ;ssl= 23 | 24 | ; If activated threats are not removed. This can be used to test new rules without 25 | ; making the web application unusable. It can be also used to turn Shadow Daemon 26 | ; into a high-interaction web honeypot. 27 | ; Possible Values: 28 | ; 0 29 | ; 1 30 | ; Default Value: 0 31 | ;observe= 32 | 33 | ; If activated the templating system will print proper error messages instead of a 34 | ; generic error. 35 | ; Possible Values: 36 | ; 0 37 | ; 1 38 | ; Default Value: 1 39 | ;templates= 40 | 41 | ; If activated log messages are send to the web server and saved in the log file. 42 | ; Additionally, stack traces for exceptions are shown in the templates. 43 | ; Possible Values: 44 | ; 0 45 | ; 1 46 | ; Default Value: 0 47 | ;debug= 48 | 49 | ; If set the ignore list is used to ignore certain parameters and not send them to 50 | ; the shadowd server. It is good practise to not send passwords or other very 51 | ; sensitive information to the server. 52 | ;ignore= 53 | 54 | ; If activated raw post data is saved as user input. The raw data is hard to analyze, 55 | ; but can be a source for malicious input as well. 56 | ; Possible Values: 57 | ; 0 58 | ; 1 59 | ; Default Value: 0 60 | ;raw_data= 61 | 62 | ; Sets the source for the client ip. It is used as key of $_SERVER. If you are using a 63 | ; reverse proxy or something similar forward the real client ip and reference it 64 | ; here, e.g., HTTP_X_FORWARDED_FOR. 65 | ; Default Value: REMOTE_ADDR 66 | ;client_ip= 67 | 68 | ; Sets the source for the caller. It is used as key of $_SERVER. The caller should 69 | ; represent the resource. Usually, that is the case when using the executed script. It is 70 | ; not the case if you are routing all requests through one script though. In that case use 71 | ; PHP_SELF, REDIRECT_URL, or similar sources to differentiate the different routes. 72 | ; Default Value: SCRIPT_FILENAME 73 | ;caller= 74 | 75 | ; Sets the source for the resource identifier. It is a key of $_SERVER. 76 | ; Default Value: REQUEST_URI 77 | ;resource= 78 | -------------------------------------------------------------------------------- /misc/tests/connectors_invalid.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zecure/shadowd_php/b88e17a07e307fd1e8732d35192f092c5b61870e/misc/tests/connectors_invalid.ini -------------------------------------------------------------------------------- /misc/tests/connectors_valid.ini: -------------------------------------------------------------------------------- 1 | [shadowd_php] 2 | profile=1 3 | key=foo 4 | host=bar 5 | port=1234 6 | timeout=10 7 | ssl=/foo/bar 8 | observe=1 9 | debug=1 10 | ignore=/foo/bar 11 | raw_data=1 12 | client_ip=FOOBAR 13 | caller=FOOBAR 14 | resource=FOOBAR 15 | 16 | [custom_section] 17 | profile=2 18 | -------------------------------------------------------------------------------- /misc/tests/hashes: -------------------------------------------------------------------------------- 1 | foobar 2 | -------------------------------------------------------------------------------- /misc/tests/ignore1.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"caller": "foo"} 3 | ] 4 | -------------------------------------------------------------------------------- /misc/tests/ignore2.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"path": "GET|bar"} 3 | ] 4 | -------------------------------------------------------------------------------- /misc/tests/ignore3.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"caller": "foo", "path": "GET|bar"} 3 | ] 4 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /shadowd.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | require_once(__DIR__ . '/vendor/autoload.php'); 22 | 23 | use shadowd\Connector; 24 | 25 | $connector = new Connector(); 26 | $connector->start(); 27 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd; 22 | 23 | use shadowd\Exceptions\CorruptedFileException; 24 | use shadowd\Exceptions\MissingConfigEntryException; 25 | use shadowd\Exceptions\MissingFileException; 26 | 27 | class Config 28 | { 29 | /** @var string */ 30 | private $section; 31 | 32 | /** @var array> */ 33 | private $data; 34 | 35 | /** 36 | * Construct a new object and parse ini file. 37 | * 38 | * @param string $file 39 | * @param string $section 40 | * @throws CorruptedFileException if config file is invalid 41 | * @throws MissingFileException if config file does not exist 42 | */ 43 | public function __construct($file, $section) 44 | { 45 | if (!file_exists($file)) { 46 | throw new MissingFileException($file); 47 | } 48 | 49 | $this->data = parse_ini_file($file, true); 50 | if (!$this->data) { 51 | throw new CorruptedFileException($file); 52 | } 53 | 54 | $this->section = $section; 55 | } 56 | 57 | /** 58 | * Get the value or stop if a required value is missing. 59 | * 60 | * @param string $key 61 | * @param bool $required 62 | * @param mixed $default 63 | * @return string|bool 64 | * @throws MissingConfigEntryException if value required but missing 65 | */ 66 | public function get($key, $required = false, $default = false) 67 | { 68 | $envKey = SHADOWD_CONFIG_ENV_PREFIX . strtoupper($key); 69 | if (getenv($envKey) !== false) { 70 | return getenv($envKey); 71 | } 72 | 73 | if (isset($this->data[$this->section][$key])) { 74 | return $this->data[$this->section][$key]; 75 | } 76 | 77 | if ($required) { 78 | throw new MissingConfigEntryException($key); 79 | } else { 80 | return $default; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Connection.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd; 22 | 23 | use shadowd\Exceptions\BadJsonException; 24 | use shadowd\Exceptions\BadRequestException; 25 | use shadowd\Exceptions\BadSignatureException; 26 | use shadowd\Exceptions\FailedConnectionException; 27 | use shadowd\Exceptions\InvalidProfileException; 28 | use shadowd\Exceptions\ProcessingException; 29 | 30 | class Connection 31 | { 32 | /** @var array */ 33 | private $options; 34 | 35 | /** 36 | * Construct a new object. 37 | * 38 | * @param array $options 39 | * @throws InvalidProfileException if profile id has incorrect format 40 | */ 41 | public function __construct($options) 42 | { 43 | if (empty($options['profile'])) { 44 | throw new InvalidProfileException('Profile is empty or null'); 45 | } elseif (!preg_match('/^[\d]*?$/', $options['profile'])) { 46 | throw new InvalidProfileException('Profile is not an integer'); 47 | } 48 | 49 | $this->options = $options; 50 | } 51 | 52 | /** 53 | * Send user input to background server. 54 | * 55 | * @param Input $input 56 | * @return array 57 | * @throws FailedConnectionException 58 | * @throws BadRequestException 59 | * @throws BadSignatureException 60 | * @throws BadJsonException 61 | * @throws ProcessingException 62 | */ 63 | public function send(Input $input) 64 | { 65 | $fp = $this->establishConnection(); 66 | fwrite($fp, $this->getInputData($input)); 67 | 68 | $outputData = ''; 69 | while (!feof($fp)) { 70 | $outputData .= fgets($fp, 1024); 71 | } 72 | 73 | fclose($fp); 74 | return $this->parseOutputData($outputData); 75 | } 76 | 77 | /** 78 | * Establish a connection to the background server. 79 | * 80 | * @return resource 81 | * @throws FailedConnectionException if connection can not be established 82 | */ 83 | private function establishConnection() 84 | { 85 | $context = stream_context_create(); 86 | 87 | if ($this->options['ssl']) { 88 | stream_context_set_option($context, 'ssl', 'verify_host', true); 89 | stream_context_set_option($context, 'ssl', 'cafile', $this->options['ssl']); 90 | stream_context_set_option($context, 'ssl', 'verify_peer', true); 91 | $prefix = 'ssl'; 92 | } else { 93 | $prefix = 'tcp'; 94 | } 95 | 96 | $fp = @stream_socket_client( 97 | $prefix . '://' . $this->options['host'] . ':' . $this->options['port'], 98 | $errorCode, 99 | $errorMessage, 100 | (int)$this->options['timeout'], 101 | STREAM_CLIENT_CONNECT, 102 | $context 103 | ); 104 | 105 | if ($fp) { 106 | return $fp; 107 | } 108 | 109 | if ($errorCode) { 110 | throw new FailedConnectionException($errorMessage); 111 | } 112 | throw new FailedConnectionException(); 113 | } 114 | 115 | /** 116 | * Prepare the message to the background server. 117 | * 118 | * @param Input $input 119 | * @return string 120 | */ 121 | private function getInputData(Input $input) 122 | { 123 | $data = [ 124 | 'version' => SHADOWD_CONNECTOR_VERSION, 125 | 'client_ip' => $input->getClientIp(), 126 | 'caller' => $input->getCaller(), 127 | 'resource' => $input->getResource(), 128 | 'input' => $input->getInput(), 129 | 'hashes' => $input->getHashes() 130 | ]; 131 | 132 | $json = json_encode($data); 133 | $hmac = $this->sign($this->options['key'], $json); 134 | 135 | return $this->options['profile'] . "\n" . $hmac . "\n" . $json . "\n"; 136 | } 137 | 138 | /** 139 | * Parse output from the background server. 140 | * 141 | * @param string $outputData 142 | * @return array 143 | * @throws BadRequestException 144 | * @throws BadSignatureException 145 | * @throws BadJsonException 146 | * @throws ProcessingException 147 | */ 148 | private function parseOutputData($outputData) 149 | { 150 | $json = json_decode($outputData, true); 151 | 152 | if (empty($json)) { 153 | throw new ProcessingException(); 154 | } 155 | 156 | switch ($json['status']) { 157 | case SHADOWD_STATUS_OK: 158 | return [ 159 | 'attack' => false 160 | ]; 161 | case SHADOWD_STATUS_BAD_REQUEST: 162 | throw new BadRequestException(isset($json['message']) ? $json['message'] : null); 163 | case SHADOWD_STATUS_BAD_SIGNATURE: 164 | throw new BadSignatureException(); 165 | case SHADOWD_STATUS_BAD_JSON: 166 | throw new BadJsonException(); 167 | case SHADOWD_STATUS_ATTACK: 168 | return [ 169 | 'attack' => true, 170 | 'critical' => false, 171 | 'threats' => $json['threats'] 172 | ]; 173 | case SHADOWD_STATUS_CRITICAL_ATTACK: 174 | return [ 175 | 'attack' => true, 176 | 'critical' => true 177 | ]; 178 | default: 179 | throw new ProcessingException(); 180 | } 181 | } 182 | 183 | /** 184 | * Sign the json encoded message as password verification. 185 | * 186 | * @param string $key 187 | * @param string $json 188 | * @return string 189 | */ 190 | private function sign($key, $json) 191 | { 192 | return hash_hmac('sha256', $json, $key); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/Connector.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd; 22 | 23 | class Connector 24 | { 25 | /** 26 | * Tie all other classes together. 27 | * 28 | * @return void 29 | */ 30 | public function start() 31 | { 32 | try { 33 | $output = new Output(); 34 | 35 | [$configFile, $configSection] = $this->getConfigOptions(); 36 | $config = new Config($configFile, $configSection); 37 | $output->setShowDebug((bool)$config->get('debug')); 38 | $output->setShowTemplates((bool)$config->get('templates', false, true)); 39 | 40 | $input = new Input([ 41 | 'clientIpKey' => $config->get('client_ip', false, 'REMOTE_ADDR'), 42 | 'callerKey' => $config->get('caller', false, 'SCRIPT_FILENAME'), 43 | 'ignoreFile' => $config->get('ignore', false, false), 44 | 'rawData' => $config->get('raw_data', false, false) 45 | ]); 46 | 47 | $connection = new Connection([ 48 | 'host' => $config->get('host', false, '127.0.0.1'), 49 | 'port' => $config->get('port', false, '9115'), 50 | 'profile' => $config->get('profile', true), 51 | 'key' => $config->get('key', true), 52 | 'ssl' => $config->get('ssl', false, false), 53 | 'timeout' => $config->get('timeout', false, 5) 54 | ]); 55 | $status = $connection->send($input); 56 | 57 | if ($status['attack'] === false || $config->get('observe')) { 58 | return; 59 | } 60 | 61 | if ($status['critical'] === true) { 62 | $output->log( 63 | 'stopped critical attack from client: ' . $input->getClientIp(), 64 | Output::LEVEL_DEBUG 65 | ); 66 | $output->error(); 67 | } 68 | 69 | $output->log( 70 | 'removed threat from client: ' . $input->getClientIp(), 71 | Output::LEVEL_DEBUG 72 | ); 73 | 74 | if (!$input->defuseInput($status['threats'])) { 75 | $output->error(); 76 | } 77 | } catch (\Exception $exception) { 78 | $output->log( 79 | get_class($exception) . ': ' . $exception->getTraceAsString(), 80 | Output::LEVEL_DEBUG 81 | ); 82 | 83 | // If there is no config or if protection mode is enabled we can't let this request pass. 84 | if (!isset($config) || !$config->get('observe')) { 85 | $output->error($exception); 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * @return array 92 | */ 93 | private function getConfigOptions() 94 | { 95 | if (getenv('SHADOWD_CONNECTOR_CONFIG')) { 96 | $file = getenv('SHADOWD_CONNECTOR_CONFIG'); 97 | } else { 98 | $file = SHADOWD_DEFAULT_CONFIG_FILE; 99 | } 100 | 101 | if (getenv('SHADOWD_CONNECTOR_CONFIG_SECTION')) { 102 | $section = getenv('SHADOWD_CONNECTOR_CONFIG_SECTION'); 103 | } else { 104 | $section = SHADOWD_DEFAULT_CONFIG_SECTION; 105 | } 106 | 107 | return [$file, $section]; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Exceptions/BadJsonException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class BadJsonException extends \Exception 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Exceptions/BadRequestException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class BadRequestException extends \Exception 24 | { 25 | /** 26 | * BadRequestException constructor. 27 | * @param string|null $message 28 | */ 29 | public function __construct($message = null) 30 | { 31 | parent::__construct($message); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Exceptions/BadSignatureException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class BadSignatureException extends \Exception 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Exceptions/CorruptedFileException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class CorruptedFileException extends \Exception 24 | { 25 | /** @var string */ 26 | private $corruptedFile; 27 | 28 | /** 29 | * CorruptedFileException constructor. 30 | * @param string $corruptedFile 31 | */ 32 | public function __construct($corruptedFile) 33 | { 34 | $this->corruptedFile = $corruptedFile; 35 | parent::__construct('File ' . $corruptedFile . ' is corrupted'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Exceptions/FailedConnectionException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class FailedConnectionException extends \Exception 24 | { 25 | /** 26 | * FailedConnectionException constructor. 27 | * @param string $message 28 | */ 29 | public function __construct($message = '') 30 | { 31 | parent::__construct($message); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidProfileException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class InvalidProfileException extends \Exception 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Exceptions/MissingConfigEntryException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class MissingConfigEntryException extends \Exception 24 | { 25 | /** @var string */ 26 | private $key; 27 | 28 | /** 29 | * MissingConfigEntryException constructor. 30 | * @param string $key 31 | */ 32 | public function __construct($key) 33 | { 34 | $this->key = $key; 35 | parent::__construct('Entry ' . $key . ' is missing'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Exceptions/MissingFileException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class MissingFileException extends \Exception 24 | { 25 | /** @var string */ 26 | private $missingFile; 27 | 28 | /** 29 | * MissingFileException constructor. 30 | * @param string $missingFile 31 | */ 32 | public function __construct($missingFile) 33 | { 34 | $this->missingFile = $missingFile; 35 | parent::__construct('File ' . $missingFile . ' is missing'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Exceptions/ProcessingException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class ProcessingException extends \Exception 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /src/Exceptions/UnknownPathException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd\Exceptions; 22 | 23 | class UnknownPathException extends \Exception 24 | { 25 | /** @var string */ 26 | private $unknownPath; 27 | 28 | /** 29 | * UnknownPathException constructor. 30 | * @param string $unknownPath 31 | */ 32 | public function __construct($unknownPath) 33 | { 34 | $this->unknownPath = $unknownPath; 35 | parent::__construct(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Input.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd; 22 | 23 | use shadowd\Exceptions\CorruptedFileException; 24 | use shadowd\Exceptions\MissingFileException; 25 | use shadowd\Exceptions\UnknownPathException; 26 | 27 | class Input 28 | { 29 | /** @var array */ 30 | private $options; 31 | 32 | /** 33 | * Construct a new object. 34 | * 35 | * @param array $options 36 | */ 37 | public function __construct($options) 38 | { 39 | $this->options = $options; 40 | } 41 | 42 | /** 43 | * Getter for the client ip. 44 | * 45 | * @return string|null 46 | */ 47 | public function getClientIp() 48 | { 49 | /* Allow for comma-separated client IP keys. */ 50 | $keys = explode(',', $this->options['clientIpKey']); 51 | foreach ($keys as $key) { 52 | $key = trim($key); 53 | /* Skip empty server keys. */ 54 | if (!empty($_SERVER[$key])) { 55 | /* X_FORWARD_FOR allows for comma-separated address listing 56 | * and the first IP is assumed the actual client IP. */ 57 | $addrs = explode(',', $_SERVER[$key]); 58 | return array_shift($addrs); 59 | } 60 | } 61 | /* Use the default or return empty string. */ 62 | return empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']; 63 | } 64 | 65 | /** 66 | * Getter for the caller. 67 | * 68 | * @return string|null 69 | */ 70 | public function getCaller() 71 | { 72 | return $_SERVER[$this->options['callerKey']]; 73 | } 74 | 75 | /** 76 | * Getter for the resource. 77 | * 78 | * @return string 79 | */ 80 | public function getResource() 81 | { 82 | return $_SERVER['REQUEST_URI']; 83 | } 84 | 85 | /** 86 | * Aggregate and get the user input. 87 | * 88 | * @return array 89 | */ 90 | public function getInput() 91 | { 92 | // Create copies of input sources. Only GET/POST/COOKIE here! 93 | $input = [ 94 | 'GET' => $_GET, 95 | 'POST' => $_POST, 96 | 'COOKIE' => $_COOKIE 97 | ]; 98 | 99 | // Strip slashes of GPC input if magic_quotes_gpc is activated to get the real values. 100 | if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { 101 | $this->stripslashes($input); 102 | } 103 | 104 | // Add names of uploaded files. 105 | foreach ($_FILES as $key => $value) { 106 | $input['FILES'][$key] = $value['name']; 107 | } 108 | 109 | // Add headers that contain user input. 110 | foreach ($_SERVER as $key => $value) { 111 | if (strpos($key, 'HTTP_') === 0) { 112 | $input['SERVER'][$key] = $value; 113 | } 114 | } 115 | 116 | $input['SERVER']['PHP_SELF'] = $_SERVER['PHP_SELF']; 117 | 118 | // Add raw post data if not empty. 119 | if (!empty($this->options['rawData'])) { 120 | $rawData = file_get_contents('php://input'); 121 | 122 | if ($rawData) { 123 | $input['DATA']['raw'] = $rawData; 124 | } 125 | } 126 | 127 | // Convert the complete input structure to a flat unique array. 128 | $flattenedInput = $this->flatten($input); 129 | 130 | // Remove user input that should be ignored. 131 | if (!empty($this->options['ignoreFile'])) { 132 | $flattenedInput = $this->removeIgnored($flattenedInput); 133 | } 134 | 135 | return $flattenedInput; 136 | } 137 | 138 | /** 139 | * Convert nested arrays to a flat array. 140 | * 141 | * @param array|string $input 142 | * @param string|bool $key 143 | * @param string|bool $path 144 | * @return array 145 | */ 146 | public function flatten($input, $key = false, $path = false) 147 | { 148 | $output = []; 149 | 150 | // The next part generates an unique identifier for every input element. 151 | $newPath = false; 152 | 153 | if (is_string($key)) { 154 | $key = $this->escapeKey($key); 155 | 156 | // If there is already a path just append the key, otherwise the key is the complete new path. 157 | if ($path !== false) { 158 | $newPath = $path . '|' . $key; 159 | } else { 160 | $newPath = $key; 161 | } 162 | } 163 | 164 | // Now we have to process the input. It can either be an array or a string, but we check both to be sure. 165 | if (is_array($input)) { 166 | // The current input is an array, so we have to call the convert function again. 167 | foreach ($input as $inputKey => $inputValue) { 168 | $output = array_replace($output, $this->flatten($inputValue, $inputKey, $newPath)); 169 | } 170 | } elseif ($newPath !== false) { 171 | // FIXME: the encoding does not work properly all the time yet. 172 | if (!mb_check_encoding($input, 'UTF-8')) { 173 | $input = mb_convert_encoding($input, 'UTF-8'); 174 | } 175 | 176 | return [$newPath => $input]; 177 | } 178 | 179 | return $output; 180 | } 181 | 182 | /** 183 | * Read in entries that should be ignored and remove them from the input. 184 | * 185 | * @param array $input 186 | * @return array 187 | * @throws CorruptedFileException if ignore file is invalid 188 | * @throws MissingFileException if ignore file is missing 189 | */ 190 | public function removeIgnored($input) 191 | { 192 | if (!file_exists($this->options['ignoreFile'])) { 193 | throw new MissingFileException($this->options['ignoreFile']); 194 | } 195 | 196 | $content = file_get_contents($this->options['ignoreFile']); 197 | if ($content === false) { 198 | throw new CorruptedFileException($this->options['ignoreFile']); 199 | } 200 | 201 | $json = json_decode($content, true); 202 | if ($json === null) { 203 | throw new CorruptedFileException($this->options['ignoreFile']); 204 | } 205 | 206 | foreach ($json as $entry) { 207 | // If there is only a caller and the caller matches delete all input. 208 | if (!isset($entry['path']) && isset($entry['caller'])) { 209 | if ($this->getCaller() === $entry['caller']) { 210 | return []; 211 | } 212 | } else { 213 | // Skip entry if caller is set, but does not match. 214 | if (isset($entry['caller'])) { 215 | if ($this->getCaller() !== $entry['caller']) { 216 | continue; 217 | } 218 | } 219 | 220 | // Delete the input based on its path. 221 | if (isset($entry['path'])) { 222 | unset($input[$entry['path']]); 223 | } 224 | } 225 | } 226 | 227 | return $input; 228 | } 229 | 230 | /** 231 | * Calculate and return cryptographically secure checksums. 232 | * 233 | * @return array 234 | */ 235 | public function getHashes() 236 | { 237 | $hashes = []; 238 | 239 | foreach (['sha256'] as $algorithm) { 240 | $hashes[$algorithm] = hash_file($algorithm, $_SERVER['SCRIPT_FILENAME']); 241 | } 242 | 243 | return $hashes; 244 | } 245 | 246 | /** 247 | * Iterate over all threats and try to remove them. 248 | * 249 | * Returns false if the complete request has to be blocked. 250 | * 251 | * @param string[] $threats 252 | * @return bool 253 | * @throws UnknownPathException if root path is invalid 254 | */ 255 | public function defuseInput($threats) 256 | { 257 | foreach ($threats as $path) { 258 | $pathSplitted = $this->splitPath($path); 259 | 260 | // A valid path needs at least two pieces. 261 | if (count($pathSplitted) < 2) { 262 | return false; 263 | } 264 | 265 | // The first element is the root path. 266 | $rootPath = array_shift($pathSplitted); 267 | 268 | // Arrays are ignored and completely removed if they contain a threat. 269 | // This is new in version 2.0 and was a hard decision, but security-wise 270 | // it is better than just emptying the variables, because it makes 271 | // injections via array keys impossible. 272 | $keyPath = $this->unescapeKey(array_shift($pathSplitted)); 273 | 274 | switch ($rootPath) { 275 | case 'GET': 276 | if (isset($_GET[$keyPath])) { 277 | unset($_GET[$keyPath]); 278 | } 279 | 280 | if (isset($_REQUEST[$keyPath])) { 281 | unset($_REQUEST[$keyPath]); 282 | } 283 | 284 | break; 285 | case 'POST': 286 | if (isset($_POST[$keyPath])) { 287 | unset($_POST[$keyPath]); 288 | } 289 | 290 | if (isset($_REQUEST[$keyPath])) { 291 | unset($_REQUEST[$keyPath]); 292 | } 293 | 294 | break; 295 | case 'COOKIE': 296 | if (isset($_COOKIE[$keyPath])) { 297 | unset($_COOKIE[$keyPath]); 298 | } 299 | 300 | if (isset($_REQUEST[$keyPath])) { 301 | unset($_REQUEST[$keyPath]); 302 | } 303 | 304 | break; 305 | case 'SERVER': 306 | if (isset($_SERVER[$keyPath])) { 307 | unset($_SERVER[$keyPath]); 308 | } 309 | 310 | break; 311 | case 'FILES': 312 | if (isset($_FILES[$keyPath])) { 313 | unset($_FILES[$keyPath]); 314 | } 315 | 316 | break; 317 | case 'DATA': 318 | return false; 319 | default: 320 | throw new UnknownPathException($path); 321 | } 322 | } 323 | 324 | // Don't stop the complete request. 325 | return true; 326 | } 327 | 328 | /** 329 | * Escape special characters in keys. 330 | * 331 | * To avoid a small security problem we have to escape some key chars. The reason for this is that 332 | * otherwise test[foo][bar] would be the same as test[foo|bar] in the internal representation, so 333 | * test.php?test[foo|bar]=evil&test[foo][bar]=23 could be used to bypass the filter if the target 334 | * script uses pipes in a key name. 335 | * 336 | * @param string $key 337 | * @return string 338 | */ 339 | public function escapeKey($key) 340 | { 341 | return str_replace(['\\', '|'], ['\\\\', '\\|'], $key); 342 | } 343 | 344 | /** 345 | * Escaped keys have to be unescaped before they can be defused. 346 | * 347 | * @param string $key 348 | * @return string 349 | */ 350 | public function unescapeKey($key) 351 | { 352 | return str_replace(['\\\\', '\\|'], ['\\', '|'], $key); 353 | } 354 | 355 | /** 356 | * Split path at dash, except if it is escaped. 357 | * 358 | * @param string $path 359 | * @return string[] 360 | */ 361 | public function splitPath($path) 362 | { 363 | return preg_split('/\\\\.(*SKIP)(*FAIL)|\|/s', $path); 364 | } 365 | 366 | /** 367 | * Strip slashes recursively if magic_quotes_gpc is enabled. 368 | * 369 | * Warning, this function uses value by reference! 370 | * 371 | * @param array|string $input 372 | * @return void 373 | */ 374 | private function stripslashes(&$input) 375 | { 376 | if (is_array($input)) { 377 | array_walk($input, [$this, 'stripslashes']); 378 | return; 379 | } 380 | 381 | $input = stripslashes($input); 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/Output.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd; 22 | 23 | class Output 24 | { 25 | /** @var int */ 26 | const LEVEL_DEBUG = 1; 27 | 28 | /** @var int */ 29 | const LEVEL_CRITICAL = 2; 30 | 31 | /** @var bool */ 32 | private $showDebug; 33 | 34 | /** @var bool */ 35 | private $showTemplates; 36 | 37 | /** 38 | * Output constructor. 39 | * 40 | * Settings come from the config but the output object has to exist in case parsing the config fails. 41 | * Thus the settings have to be set after initialization through setters. 42 | * 43 | * @param bool $showDebug 44 | * @param bool $showTemplates 45 | */ 46 | public function __construct($showDebug = false, $showTemplates = true) 47 | { 48 | $this->showDebug = $showDebug; 49 | $this->showTemplates = $showTemplates; 50 | } 51 | 52 | /** 53 | * Set debug flag to show additional information and store logs. 54 | * 55 | * @param bool $showDebug 56 | * @return void 57 | */ 58 | public function setShowDebug($showDebug) 59 | { 60 | $this->showDebug = $showDebug; 61 | } 62 | 63 | /** 64 | * Set template flag to show a template instead of generic error in case of a problem. 65 | * 66 | * @param bool $showTemplates 67 | * @return void 68 | */ 69 | public function setShowTemplates($showTemplates) 70 | { 71 | $this->showTemplates = $showTemplates; 72 | } 73 | 74 | /** 75 | * Show a fatal error and stop. 76 | * 77 | * @param \Exception|null $exception 78 | * @return void 79 | */ 80 | public function error($exception = null) 81 | { 82 | header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500); 83 | 84 | if ($this->showTemplates) { 85 | $template = new Template($exception, $this->showDebug); 86 | $template->show(); 87 | } else { 88 | echo '

500 Internal Server Error

'; 89 | } 90 | 91 | exit(1); 92 | } 93 | 94 | /** 95 | * Write message to error log. 96 | * 97 | * @param string $message 98 | * @param int $level 99 | * @return void 100 | */ 101 | public function log($message, $level = self::LEVEL_CRITICAL) 102 | { 103 | if ($this->showDebug !== true && $level === self::LEVEL_DEBUG) { 104 | return; 105 | } 106 | error_log(SHADOWD_LOG_PREFIX . $message); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Template.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | namespace shadowd; 22 | 23 | use shadowd\Exceptions\BadJsonException; 24 | use shadowd\Exceptions\BadRequestException; 25 | use shadowd\Exceptions\BadSignatureException; 26 | use shadowd\Exceptions\CorruptedFileException; 27 | use shadowd\Exceptions\FailedConnectionException; 28 | use shadowd\Exceptions\InvalidProfileException; 29 | use shadowd\Exceptions\MissingConfigEntryException; 30 | use shadowd\Exceptions\MissingFileException; 31 | use shadowd\Exceptions\ProcessingException; 32 | use shadowd\Exceptions\UnknownPathException; 33 | 34 | class Template 35 | { 36 | /** @var string */ 37 | const DEFAULT_KEY = 'default'; 38 | 39 | /** @var array */ 40 | const TITLES = [ 41 | self::DEFAULT_KEY => 'Request Blocked', 42 | BadJsonException::class => 'Bad JSON', 43 | BadRequestException::class => 'Bad Request', 44 | BadSignatureException::class => 'Bad Signature', 45 | CorruptedFileException::class => 'Corrupted File', 46 | FailedConnectionException::class => 'Failed Connection', 47 | InvalidProfileException::class => 'Invalid Profile', 48 | MissingConfigEntryException::class => 'Missing Config Entry', 49 | MissingFileException::class => 'Missing File', 50 | ProcessingException::class => 'Processing Error', 51 | UnknownPathException::class => 'Unknown Path' 52 | ]; 53 | 54 | /** @var array */ 55 | const FILES = [ 56 | self::DEFAULT_KEY => 'blocked.html.php', 57 | BadJsonException::class => 'bad_json.html.php', 58 | BadRequestException::class => 'bad_request.html.php', 59 | BadSignatureException::class => 'bad_signature.html.php', 60 | CorruptedFileException::class => 'corrupted_file.html.php', 61 | FailedConnectionException::class => 'failed_connection.html.php', 62 | InvalidProfileException::class => 'invalid_profile.html.php', 63 | MissingConfigEntryException::class => 'missing_config_entry.html.php', 64 | MissingFileException::class => 'missing_file.html.php', 65 | ProcessingException::class => 'processing.html.php', 66 | UnknownPathException::class => 'unknown_path.html.php' 67 | ]; 68 | 69 | /** @var \Exception|null */ 70 | private $exception; 71 | 72 | /** @var bool */ 73 | private $debug; 74 | 75 | /** 76 | * Template constructor. 77 | * 78 | * @param \Exception|null $exception 79 | * @param bool $debug 80 | */ 81 | public function __construct($exception, $debug) 82 | { 83 | $this->exception = $exception; 84 | $this->debug = $debug; 85 | } 86 | 87 | /** 88 | * Evaluate and print the base template. 89 | * 90 | * @return void 91 | */ 92 | public function show() 93 | { 94 | require(SHADOWD_ROOT_DIR . '/tpl/base.html.php'); 95 | } 96 | 97 | /** 98 | * Return the title that matches the exception. 99 | * 100 | * @return string 101 | */ 102 | public function getTitle() 103 | { 104 | if ($this->exception) { 105 | $class = get_class($this->exception); 106 | if (isset(self::TITLES[$class])) { 107 | return self::TITLES[$class]; 108 | } 109 | } 110 | 111 | return self::TITLES[self::DEFAULT_KEY]; 112 | } 113 | 114 | /** 115 | * Return the file that matches the exception. 116 | * 117 | * @return string 118 | */ 119 | public function getFile() 120 | { 121 | if ($this->exception) { 122 | $class = get_class($this->exception); 123 | if (isset(self::FILES[$class])) { 124 | return self::FILES[$class]; 125 | } 126 | } 127 | 128 | return self::FILES[self::DEFAULT_KEY]; 129 | } 130 | 131 | /** 132 | * Evaluate and print the description template that matches the exception. 133 | * 134 | * @return void 135 | */ 136 | public function printDescription() 137 | { 138 | require(SHADOWD_ROOT_DIR . '/tpl/' . $this->getFile()); 139 | } 140 | 141 | /** 142 | * Is debug mode enabled? 143 | * 144 | * @return bool 145 | */ 146 | public function isDebug() 147 | { 148 | return $this->debug; 149 | } 150 | 151 | /** 152 | * Is the error message triggered by an exception? 153 | * 154 | * @return bool 155 | */ 156 | public function isException() 157 | { 158 | return !is_null($this->exception); 159 | } 160 | 161 | /** 162 | * Return the exception class. 163 | * 164 | * @return false|string 165 | */ 166 | public function getExceptionClass() 167 | { 168 | return get_class($this->exception); 169 | } 170 | 171 | /** 172 | * Return the exception message. 173 | * 174 | * @return string 175 | */ 176 | public function getExceptionMessage() 177 | { 178 | return $this->exception->getMessage(); 179 | } 180 | 181 | /** 182 | * Return the exception stack trace. 183 | * 184 | * @return string 185 | */ 186 | public function getStackTrace() 187 | { 188 | return $this->exception->getTraceAsString(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /tests/ConfigTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | use shadowd\Config; 22 | use shadowd\Exceptions\CorruptedFileException; 23 | use shadowd\Exceptions\MissingConfigEntryException; 24 | use shadowd\Exceptions\MissingFileException; 25 | use PHPUnit\Framework\TestCase; 26 | 27 | class ConfigTest extends TestCase { 28 | public function testConstructorCorruptedFile() { 29 | $this->expectException(CorruptedFileException::class); 30 | new Config(SHADOWD_MISC_TESTS . '/connectors_invalid.ini', ''); 31 | } 32 | 33 | public function testConstructorMissingFile() { 34 | $this->expectException(MissingFileException::class); 35 | new Config(SHADOWD_MISC_TESTS . '/notfound', ''); 36 | } 37 | 38 | public function testGet() { 39 | $config = new Config(SHADOWD_MISC_TESTS . '/connectors_valid.ini', 'shadowd_php'); 40 | 41 | $optionalValue1 = $config->get('notfound'); 42 | $this->assertFalse($optionalValue1); 43 | 44 | $optionalValue2 = $config->get('profile'); 45 | $this->assertEquals('1', $optionalValue2); 46 | 47 | $requiredValue = $config->get('profile', true); 48 | $this->assertEquals('1', $requiredValue); 49 | } 50 | 51 | public function testEnvGet() { 52 | $config = new Config(SHADOWD_MISC_TESTS . '/connectors_valid.ini', 'shadowd_php'); 53 | 54 | $optionalValue1 = $config->get('foo'); 55 | $this->assertFalse($optionalValue1); 56 | 57 | putenv(SHADOWD_CONFIG_ENV_PREFIX . 'FOO=1'); 58 | $optionalValue2 = $config->get('foo'); 59 | $this->assertEquals('1', $optionalValue2); 60 | } 61 | 62 | public function testGetMissingEntry() { 63 | $config = new Config(SHADOWD_MISC_TESTS . '/connectors_valid.ini', ''); 64 | 65 | $this->expectException(MissingConfigEntryException::class); 66 | $config->get('notfound', true); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/InputTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of Shadow Daemon. Shadow Daemon is free software: you can 9 | * redistribute it and/or modify it under the terms of the GNU General Public 10 | * License as published by the Free Software Foundation, version 2. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | use shadowd\Input; 22 | use PHPUnit\Framework\TestCase; 23 | 24 | class InputTest extends TestCase { 25 | public function testGetInput() { 26 | $_GET['foo'] = 'bar'; 27 | $_POST['foo'] = 'bar'; 28 | $_COOKIE['foo'] = 'bar'; 29 | $_SERVER['HTTP_FOO'] = 'bar'; 30 | $_SERVER['foo'] = 'bar'; 31 | $_FILES['foo']['name'] = 'bar'; 32 | 33 | $i = new Input([]); 34 | $input = $i->getInput(); 35 | 36 | $this->assertTrue(array_key_exists('GET|foo', $input)); 37 | $this->assertEquals('bar', $input['GET|foo']); 38 | $this->assertTrue(array_key_exists('POST|foo', $input)); 39 | $this->assertEquals('bar', $input['POST|foo']); 40 | $this->assertTrue(array_key_exists('COOKIE|foo', $input)); 41 | $this->assertEquals('bar', $input['COOKIE|foo']); 42 | $this->assertTrue(array_key_exists('SERVER|HTTP_FOO', $input)); 43 | $this->assertEquals('bar', $input['SERVER|HTTP_FOO']); 44 | $this->assertFalse(array_key_exists('SERVER|foo', $input)); 45 | $this->assertTrue(array_key_exists('FILES|foo', $input)); 46 | $this->assertEquals('bar', $input['FILES|foo']); 47 | } 48 | 49 | public function testFlatten() { 50 | $input = [ 51 | 'foo' => 'bar', 52 | 'boo' => [ 53 | 'quz' => 'qoz' 54 | ] 55 | ]; 56 | 57 | $i = new Input([]); 58 | $flattened = $i->flatten($input); 59 | 60 | $this->assertTrue(array_key_exists('foo', $flattened)); 61 | $this->assertEquals('bar', $flattened['foo']); 62 | $this->assertTrue(array_key_exists('boo|quz', $flattened)); 63 | $this->assertEquals('qoz', $flattened['boo|quz']); 64 | } 65 | 66 | public function testDefuseInput() { 67 | $_GET['foo'] = 'bar'; 68 | $_POST['foo'] = 'bar'; 69 | $_COOKIE['foo'] = 'bar'; 70 | $_SERVER['HTTP_FOO'] = 'bar'; 71 | $_FILES['foo']['name'] = 'bar'; 72 | 73 | $i = new Input([]); 74 | $this->assertTrue($i->defuseInput([ 75 | 'GET|foo', 76 | 'POST|foo', 77 | 'COOKIE|foo', 78 | 'SERVER|HTTP_FOO', 79 | 'FILES|foo' 80 | ])); 81 | 82 | $this->assertArrayNotHasKey('foo', $_GET); 83 | $this->assertArrayNotHasKey('foo', $_POST); 84 | $this->assertArrayNotHasKey('foo', $_REQUEST); 85 | $this->assertArrayNotHasKey('foo', $_COOKIE); 86 | $this->assertArrayNotHasKey('HTTP_FOO', $_SERVER); 87 | $this->assertArrayNotHasKey('foo', $_FILES); 88 | } 89 | 90 | public function testEscapeKey() { 91 | $i = new Input([]); 92 | 93 | $this->assertEquals('foo', $i->escapeKey('foo')); 94 | $this->assertEquals('foo\\|bar', $i->escapeKey('foo|bar')); 95 | $this->assertEquals('foo\\\\\\|bar', $i->escapeKey('foo\\|bar')); 96 | $this->assertEquals('foo\\|\\|bar', $i->escapeKey('foo||bar')); 97 | $this->assertEquals('foo\\\\\\\\bar', $i->escapeKey('foo\\\\bar')); 98 | } 99 | 100 | public function testUnescapeKey() { 101 | $i = new Input([]); 102 | 103 | $this->assertEquals('foo', $i->unescapeKey('foo')); 104 | $this->assertEquals('foo|bar', $i->unescapeKey('foo\\|bar')); 105 | $this->assertEquals('foo\\bar', $i->unescapeKey('foo\\\\bar')); 106 | $this->assertEquals('foo\\|bar', $i->unescapeKey('foo\\\\\\|bar')); 107 | } 108 | 109 | public function testSplitSpath() { 110 | $i = new Input([]); 111 | 112 | $test1 = $i->splitPath('foo'); 113 | $this->assertEquals(1, count($test1)); 114 | $this->assertEquals('foo', $test1[0]); 115 | 116 | $test2 = $i->splitPath('foo|bar'); 117 | $this->assertEquals(2, count($test2)); 118 | $this->assertEquals('foo', $test2[0]); 119 | $this->assertEquals('bar', $test2[1]); 120 | 121 | $test3 = $i->splitPath('foo\\|bar'); 122 | $this->assertEquals(1, count($test3)); 123 | $this->assertEquals('foo\\|bar', $test3[0]); 124 | 125 | $test4 = $i->splitPath('foo\\\\|bar'); 126 | $this->assertEquals(2, count($test4)); 127 | $this->assertEquals('foo\\\\', $test4[0]); 128 | $this->assertEquals('bar', $test4[1]); 129 | 130 | $test5 = $i->splitPath('foo\\\\\\|bar'); 131 | $this->assertEquals(1, count($test5)); 132 | $this->assertEquals('foo\\\\\\|bar', $test5[0]); 133 | 134 | $test6 = $i->splitPath('foo\\'); 135 | $this->assertEquals(1, count($test6)); 136 | $this->assertEquals('foo\\', $test6[0]); 137 | } 138 | 139 | public function testRemoveIgnoredCaller() { 140 | $i = new Input([ 141 | 'callerKey' => 'shadowd_caller', 142 | 'ignoreFile' => SHADOWD_MISC_TESTS . 'ignore1.json' 143 | ]); 144 | $input = [ 145 | 'GET|bar' => 'foobar' 146 | ]; 147 | 148 | $_SERVER['shadowd_caller'] = 'foo'; 149 | $output = $i->removeIgnored($input); 150 | $this->assertArrayNotHasKey('GET|bar', $output); 151 | 152 | $_SERVER['shadowd_caller'] = 'boo'; 153 | $output = $i->removeIgnored($input); 154 | $this->assertArrayHasKey('GET|bar', $output); 155 | } 156 | 157 | public function testRemoveIgnoredPath() { 158 | $i = new Input([ 159 | 'ignoreFile' => SHADOWD_MISC_TESTS . 'ignore2.json' 160 | ]); 161 | 162 | $input = [ 163 | 'GET|bar' => 'foobar' 164 | ]; 165 | $output = $i->removeIgnored($input); 166 | $this->assertArrayNotHasKey('GET|bar', $output); 167 | 168 | $input = [ 169 | 'GET|boo' => 'foobar' 170 | ]; 171 | $output = $i->removeIgnored($input); 172 | $this->assertArrayHasKey('GET|boo', $output); 173 | } 174 | 175 | public function testRemoveIgnoredCallerPath() { 176 | $i = new Input([ 177 | 'callerKey' => 'shadowd_caller', 178 | 'ignoreFile' => SHADOWD_MISC_TESTS . 'ignore3.json' 179 | ]); 180 | 181 | $_SERVER['shadowd_caller'] = 'foo'; 182 | $input = [ 183 | 'GET|bar' => 'foobar' 184 | ]; 185 | $output = $i->removeIgnored($input); 186 | $this->assertArrayNotHasKey('GET|bar', $output); 187 | 188 | $_SERVER['shadowd_caller'] = 'foo'; 189 | $input = [ 190 | 'GET|boo' => 'foobar' 191 | ]; 192 | $output = $i->removeIgnored($input); 193 | $this->assertArrayHasKey('GET|boo', $output); 194 | 195 | $_SERVER['shadowd_caller'] = 'boo'; 196 | $input = [ 197 | 'GET|bar' => 'foobar' 198 | ]; 199 | $output = $i->removeIgnored($input); 200 | $this->assertArrayHasKey('GET|bar', $output); 201 | } 202 | 203 | public function testGetHashes() { 204 | $_SERVER['SCRIPT_FILENAME'] = SHADOWD_MISC_TESTS . 'hashes'; 205 | 206 | $i = new Input([]); 207 | $hashes = $i->getHashes(); 208 | 209 | $this->assertTrue(array_key_exists('sha256', $hashes)); 210 | $this->assertEquals('aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f', $hashes['sha256']); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /tpl/bad_json.html.php: -------------------------------------------------------------------------------- 1 |

The Shadow Daemon server received invalid JSON data from the connector.

2 |

This should not happen. Please create an issue in the Shadow Daemon issue tracker. Do not forget to include the debug output.

-------------------------------------------------------------------------------- /tpl/bad_request.html.php: -------------------------------------------------------------------------------- 1 |

Your request was rejected by the Shadow Daemon web application firewall.

2 |

To prevent harm it was blocked. If you think this is an error please contact your administrator.

-------------------------------------------------------------------------------- /tpl/bad_signature.html.php: -------------------------------------------------------------------------------- 1 |

The Shadow Daemon server received an invalid message authentication code from the connector.

2 |

The key of the profile and the key of the connector do not match. One or both of them have to be corrected.

3 | -------------------------------------------------------------------------------- /tpl/base.html.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Shadow Daemon - <?php echo htmlspecialchars($this->getTitle()); ?> 5 | 49 | 50 | 51 |
52 |
53 | Shadow Daemon 54 |
55 |

getTitle()); ?>

56 | printDescription(); ?> 57 |
58 | 59 | isDebug() && $this->isException()): ?> 60 |
61 |

getExceptionClass()); ?>

62 |

getExceptionMessage()); ?>

63 |
getStackTrace()); ?>
64 | You are seeing this stack trace because the debug mode is enabled. Debug output might leak information and should be disabled when not needed. 65 |
66 | 67 |
68 |
69 | 70 | -------------------------------------------------------------------------------- /tpl/blocked.html.php: -------------------------------------------------------------------------------- 1 |

Your request was classified as dangerous by the Shadow Daemon web application firewall.

2 |

To prevent harm it was blocked. If you think this is an error please contact your administrator.

-------------------------------------------------------------------------------- /tpl/corrupted_file.html.php: -------------------------------------------------------------------------------- 1 |

A required file to initialize the Shadow Daemon web application firewall is corrupted and can not be parsed.

2 | 3 | isDebug()): ?> 4 |

Enable the debug setting to get additional information about the corrupted file.

5 | -------------------------------------------------------------------------------- /tpl/failed_connection.html.php: -------------------------------------------------------------------------------- 1 |

No connection could be established with the Shadow Daemon server.

2 | getExceptionMessage())): ?> 3 |

The server is either unreachable or the wrong address is specified in the configuration file of the connector.

4 | isDebug()): ?> 5 |

Enable the debug setting to get additional information.

6 | 7 | 8 |

The exact cause is not known. It might be related to an invalid SSL public key.

9 | -------------------------------------------------------------------------------- /tpl/invalid_profile.html.php: -------------------------------------------------------------------------------- 1 |

An invalid profile id is specified in the configuration file of the Shadow Daemon connector.

2 |

The profile id has to be a positive integer. It can be found in the user interface (Management → Profiles).

3 | 4 | isDebug()): ?> 5 |

Enable the debug setting to get additional information about the invalid profile id.

6 | -------------------------------------------------------------------------------- /tpl/missing_config_entry.html.php: -------------------------------------------------------------------------------- 1 |

A required configuration entry to initialize the Shadow Daemon web application firewall is missing.

2 | 3 | isDebug()): ?> 4 |

Enable the debug setting to get additional information about the missing entry.

5 | -------------------------------------------------------------------------------- /tpl/missing_file.html.php: -------------------------------------------------------------------------------- 1 |

A required file to initialize the Shadow Daemon web application firewall is missing.

2 |

This indicates that the installation is incomplete or corrupted. The error can also be caused by insufficient permissions to access a required file.

3 | 4 | isDebug()): ?> 5 |

Enable the debug setting to get additional information about the missing file.

6 | -------------------------------------------------------------------------------- /tpl/processing.html.php: -------------------------------------------------------------------------------- 1 |

The connector received a response from the Shadow Daemon server that could not be parsed.

2 |

A wrong address (host/port) might be specified in the configuration file of the connector.

3 | 4 | isDebug()): ?> 5 |

Enable the debug setting to get additional information about the processing error.

6 | -------------------------------------------------------------------------------- /tpl/unknown_path.html.php: -------------------------------------------------------------------------------- 1 |

The Shadow Daemon server sent an invalid path to the connector.

2 |

This should not happen. Please create an issue in the Shadow Daemon issue tracker. Do not forget to include the debug output.

3 | --------------------------------------------------------------------------------