Semgrep rules to replace this script

master
Swissky 2022-04-30 15:00:48 +02:00
parent 561726732e
commit e6fcd0d3ca
107 changed files with 2572 additions and 30 deletions

View File

@ -1,7 +1,12 @@
# VulnyCode - PHP Code Static Analysis [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=VulnyCode%20-%20PHP%20Code%20Static%20Analysis&url=https://github.com/swisskyrepo/Vulny-Code-Static-Analysis) # VulnyCode - PHP Code Static Analysis [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=VulnyCode%20-%20PHP%20Code%20Static%20Analysis&url=https://github.com/swisskyrepo/Vulny-Code-Static-Analysis) - Deprecated
![1.0.0](https://img.shields.io/badge/Version-1.0.0%20Beta-RED) ![Python](https://img.shields.io/badge/Python-3.4+-GREEN) ![Platform](https://img.shields.io/badge/Platforms-Linux%20x64-yellowgreen) ![1.0.0](https://img.shields.io/badge/Version-1.0.0%20Beta-RED) ![Python](https://img.shields.io/badge/Python-3.4+-GREEN) ![Platform](https://img.shields.io/badge/Platforms-Linux%20x64-yellowgreen)
:warning: **Deprecated**, you should use semgrep rules instead of this script: `semgrep --config=./semgrep/ vulns/*.php`
Most of the semgrep rules provided in this repository are from https://github.com/returntocorp/semgrep-rules
Basic script to detect vulnerabilities into a PHP source code, it is using Regular Expression to find sinkholes. Basic script to detect vulnerabilities into a PHP source code, it is using Regular Expression to find sinkholes.
```bash ```bash
@ -17,16 +22,16 @@ optional arguments:
# Example # Example
╭─ 👻 swissky@crashlab: ~/Github/PHP_Code_Static_Analysis master* ╭─ 👻 swissky@crashlab: ~/Github/PHP_Code_Static_Analysis master*
╰─$ python3 index.py --dir test ╰─$ python3 index.py --dir vulns
------------------------------------------------------------ ------------------------------------------------------------
Analyzing 'test' source code Analyzing 'vulns' source code
------------------------------------------------------------ ------------------------------------------------------------
Potential vulnerability found : File Inclusion Potential vulnerability found : File Inclusion
Line 19 in test/include.php Line 19 in vulns/include.php
Code : include($_GET['patisserie']) Code : include($_GET['patisserie'])
------------------------------------------------------------ ------------------------------------------------------------
Potential vulnerability found : Insecure E-mail Potential vulnerability found : Insecure E-mail
Line 2 in test/mail.php Line 2 in vulns/mail.php
Code : mail($dest, "subject", "message", "", "-f" . $_GET['from']) Code : mail($dest, "subject", "message", "", "-f" . $_GET['from'])
Declared at line 1 : $dest = $_GET['who']; Declared at line 1 : $dest = $_GET['who'];
``` ```

17
semgrep/assert-use.yaml Normal file
View File

@ -0,0 +1,17 @@
rules:
- id: assert-use
patterns:
- pattern: assert($ASSERT, ...);
# - pattern-not: assert(<... $ASSERT ...>, ...); - https://github.com/returntocorp/semgrep/issues/2035
- pattern-not: assert("...", ...);
message: >-
Calling assert with user input is equivalent to eval'ing.
metadata:
references:
- https://www.php.net/manual/en/function.assert
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/AssertsSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,14 @@
rules:
- id: backticks-use
pattern: "`...`;"
message: >-
Backticks use may lead to command injection vulnerabilities.
metadata:
references:
- https://www.php.net/manual/en/language.operators.execution.php
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/BackticksSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,23 @@
rules:
- id: curl-ssl-verifypeer-off
patterns:
- pattern-either:
- pattern: |
$ARG = $IS_VERIFIED;
...
curl_setopt(..., CURLOPT_SSL_VERIFYPEER, $ARG);
- pattern: curl_setopt(..., CURLOPT_SSL_VERIFYPEER, $IS_VERIFIED)
- metavariable-regex:
metavariable: $IS_VERIFIED
regex: 0|false|null
message: >-
SSL verification is disabled but should not be (currently CURLOPT_SSL_VERIFYPEER=
$IS_VERIFIED)
metadata:
references:
- https://www.saotn.org/dont-turn-off-curlopt_ssl_verifypeer-fix-php-configuration/
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,28 @@
rules:
- id: extract-user-data
mode: taint
pattern-sources:
- pattern-either:
- pattern: $_GET[...]
- pattern: $_FILES[...]
- pattern: $_POST[...]
pattern-sinks:
- pattern: extract(...)
pattern-sanitizers:
- pattern: extract($VAR, EXTR_SKIP,...)
message: Do not call 'extract()' on user-controllable data. If you must, then you
must also provide the EXTR_SKIP flag to prevent overwriting existing variables.
languages:
- php
metadata:
license: MIT
category: security
cwe: 'CWE-502: Deserialization of Untrusted Data'
owasp:
- A08:2021 - Software and Data Integrity Failures
- A08:2017 - Insecure Deserialization
technology:
- php
references:
- https://www.php.net/manual/en/function.extract.php#refsect1-function.extract-notes
severity: ERROR

View File

@ -0,0 +1,13 @@
rules:
- id: detected-generic-api-key
pattern-regex: |-
[aA][pP][iI]_?[kK][eE][yY].*['|"]?[0-9a-zA-Z]{32,45}['|"]?
languages: [regex]
message: Generic API Key detected
severity: ERROR
metadata:
source-rule-url: https://github.com/dxa4481/truffleHogRegexes/blob/master/truffleHogRegexes/regexes.json
category: security
technology:
- secrets
confidence: MEDIUM

View File

@ -0,0 +1,13 @@
rules:
- id: detected-generic-secret
pattern-regex: |-
[sS][eE][cC][rR][eE][tT][:= \t]*['|\"]?[0-9a-zA-Z]{32,45}['|\"]?
languages: [regex]
message: Generic Secret detected
severity: ERROR
metadata:
source-rule-url: https://github.com/dxa4481/truffleHogRegexes/blob/master/truffleHogRegexes/regexes.json
category: security
technology:
- secrets
confidence: MEDIUM

View File

@ -0,0 +1,27 @@
rules:
- id: detected-private-key
patterns:
- pattern-either:
- patterns:
- pattern:
-----BEGIN $TYPE PRIVATE KEY-----
$KEY
- metavariable-regex:
metavariable: $TYPE
regex: (?i)([dr]sa|ec|openssh|encrypted)?
- patterns:
- pattern: |
-----BEGIN PRIVATE KEY-----
$KEY
- metavariable-analysis:
metavariable: $KEY
analyzer: entropy
languages: [generic]
message: Private Key detected. This is a sensitive credential and should not be hardcoded here. Instead, store this in a separate, private file.
severity: ERROR
metadata:
source-rule-url: https://github.com/grab/secret-scanner/blob/master/scanner/signatures/pattern.go
category: security
technology:
- secrets
confidence: MEDIUM

View File

@ -0,0 +1,15 @@
rules:
- id: detected-username-and-password-in-uri
patterns:
- pattern-regex: ([\w+]{1,24})(://)([^$<]{1})([^\s";]{1,}):(?![\x{2022}*]+?@)([^$<\{]{1})([^\s";]{1,})@[-a-zA-Z0-9@:%._\+~#=]{1,256}[a-zA-Z0-9()]{1,24}([^\s]+)
languages:
- regex
message: Username and password in URI detected
severity: ERROR
metadata:
source-rule-url: https://github.com/grab/secret-scanner/blob/master/scanner/signatures/pattern.go
category: security
technology:
- secrets
confidence: MEDIUM
license: Commons Clause License Condition v1.0[LGPL-2.1-only]

View File

@ -0,0 +1,34 @@
rules:
- id: doctrine-dbal-dangerous-query
languages:
- php
message:
Detected string concatenation with a non-literal variable in a Doctrine DBAL query method. This could lead to SQL
injection if the variable is user-controlled and not properly sanitized. In order to prevent SQL injection, used parameterized
queries or prepared statements instead.
metadata:
category: security
cwe: "CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
owasp: "A1: Injection"
references:
- https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html
- https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
technology:
- doctrine
patterns:
- pattern-either:
- pattern: $CONNECTION->prepare($QUERY,...)
- pattern: $CONNECTION->createQuery($QUERY,...)
- pattern: $CONNECTION->executeQuery($QUERY,...)
- pattern-either:
- pattern-inside: |
use Doctrine\DBAL\Connection;
...
- pattern-inside: |
$CONNECTION = $SMTH->getConnection(...);
...
- pattern-not: $CONNECTION->prepare("...",...)
- pattern-not: $CONNECTION->createQuery("...",...)
- pattern-not: $CONNECTION->executeQuery("...",...)
severity: WARNING

View File

@ -0,0 +1,61 @@
rules:
- id: doctrine-orm-dangerous-query
languages:
- php
message: >-
`$QUERY` Detected string concatenation with a non-literal variable in a Doctrine
QueryBuilder method. This could lead to SQL injection if the variable is
user-controlled and not properly sanitized. In order to prevent SQL
injection, used parameterized queries or prepared statements instead.
metadata:
category: security
cwe: "CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
owasp: "A1: Injection"
references:
- https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/query-builder.html#security-safely-preventing-sql-injection
- https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
technology:
- doctrine
mode: taint
pattern-sinks:
- patterns:
- pattern: $SINK
- pattern-either:
- pattern-inside: $QUERY->add(...,$SINK,...)
- pattern-inside: $QUERY->select(...,$SINK,...)
- pattern-inside: $QUERY->addSelect(...,$SINK,...)
- pattern-inside: $QUERY->delete(...,$SINK,...)
- pattern-inside: $QUERY->update(...,$SINK,...)
- pattern-inside: $QUERY->insert(...,$SINK,...)
- pattern-inside: $QUERY->from(...,$SINK,...)
- pattern-inside: $QUERY->join(...,$SINK,...)
- pattern-inside: $QUERY->innerJoin(...,$SINK,...)
- pattern-inside: $QUERY->leftJoin(...,$SINK,...)
- pattern-inside: $QUERY->rightJoin(...,$SINK,...)
- pattern-inside: $QUERY->where(...,$SINK,...)
- pattern-inside: $QUERY->andWhere(...,$SINK,...)
- pattern-inside: $QUERY->orWhere(...,$SINK,...)
- pattern-inside: $QUERY->groupBy(...,$SINK,...)
- pattern-inside: $QUERY->addGroupBy(...,$SINK,...)
- pattern-inside: $QUERY->having(...,$SINK,...)
- pattern-inside: $QUERY->andHaving(...,$SINK,...)
- pattern-inside: $QUERY->orHaving(...,$SINK,...)
- pattern-inside: $QUERY->orderBy(...,$SINK,...)
- pattern-inside: $QUERY->addOrderBy(...,$SINK,...)
- pattern-inside: $QUERY->set($SINK,...)
- pattern-inside: $QUERY->setValue($SINK,...)
- pattern-either:
- pattern-inside: |
$Q = $X->createQueryBuilder();
...
- pattern-inside: |
$Q = new QueryBuilder(...);
...
pattern-sources:
- patterns:
- pattern-either:
- pattern: sprintf(...)
- pattern: |
"...".$SMTH
severity: WARNING

16
semgrep/eval-use.yaml Normal file
View File

@ -0,0 +1,16 @@
rules:
- id: eval-use
patterns:
- pattern: eval(...);
- pattern-not: eval('...');
message: >-
Evaluating non-constant commands. This can lead to command injection.
metadata:
references:
- https://www.php.net/manual/en/function.eval
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/NoEvalsSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

18
semgrep/exec-use.yaml Normal file
View File

@ -0,0 +1,18 @@
rules:
- id: exec-use
patterns:
- pattern: $FUNC(...);
- pattern-not: $FUNC('...', ...);
- metavariable-regex:
metavariable: $FUNC
regex: exec|passthru|proc_open|popen|shell_exec|system|pcntl_exec|expect_popen
message: >-
Executing non-constant commands. This can lead to command injection.
metadata:
references:
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/SystemExecFunctionsSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,21 @@
rules:
- id: file-inclusion
patterns:
- pattern: $FUNC(...);
- pattern-not: $FUNC("...");
- pattern-not: $FUNC(__DIR__ . "...");
- metavariable-regex:
metavariable: $FUNC
regex: \b(include|include_once|require|require_once|virtual)\b
message: >-
Detected non-constant file inclusion. This can lead to local file inclusion (LFI) or remote file inclusion (RFI) if user input reaches this statement. LFI and RFI could lead to sensitive files being obtained by attackers. Instead, explicitly specify what to include. If that is not a viable solution, validate user input thoroughly.
metadata:
references:
- https://www.php.net/manual/en/function.include.php
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/EasyRFISniff.php
- https://en.wikipedia.org/wiki/File_inclusion_vulnerability#Types_of_Inclusion
category: security
technology:
- php
languages: [php]
severity: ERROR

14
semgrep/file-upload.yaml Normal file
View File

@ -0,0 +1,14 @@
rules:
- id: file-upload-use
patterns:
- pattern: move_uploaded_file(...,...)
message: >-
Moves an uploaded file to a new location. This can lead to command injection.
metadata:
references:
- https://www.php.net/manual/en/function.move-uploaded-file
category: security
technology:
- php
languages: [php]
severity: ERROR

18
semgrep/ftp-use.yaml Normal file
View File

@ -0,0 +1,18 @@
rules:
- id: ftp-use
patterns:
- pattern: $FUNC(...);
- metavariable-regex:
metavariable: $FUNC
regex: ftp_.+
message: >-
FTP allows for unencrypted file transfers. Consider using an encrypted alternative.
metadata:
references:
- https://www.php.net/manual/en/intro.ftp.php
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/FringeFunctionsSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,33 @@
rules:
- id: laravel-api-route-sql-injection
mode: taint
pattern-sources:
- patterns:
- pattern: $ARG
- pattern-inside: |
Route::$METHOD($ROUTE_NAME, function(...,$ARG,...){...})
pattern-sanitizers:
- patterns:
- pattern: |
DB::raw("...",[...])
pattern-sinks:
- patterns:
- pattern: |
DB::raw(...)
message: HTTP method [$METHOD] to Laravel route $ROUTE_NAME is vulnerable to SQL
injection via string concatentation or unsafe interpolation.
languages:
- php
severity: WARNING
metadata:
category: security
cwe: 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command
(''SQL Injection'')'
owasp:
- A01:2017 - Injection
- A03:2021 - Injection
references:
- https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Laravel_Cheat_Sheet.md
technology:
- php
- laravel

View File

@ -0,0 +1,27 @@
rules:
- id: laravel-blade-form-missing-csrf
patterns:
- pattern: |
<form ... method="POST" ...>...</form>
- pattern-not: |
<form ... method="POST" ...>
@csrf
...
</form>
message: Found a Blade form POST definition without the `@csrf` decorator. State-changing
operations using simple HTTP content types should include an antiforgery token.
languages:
- generic
severity: ERROR
paths:
include:
- '*.blade.php'
metadata:
technology:
- php
- laravel
- blade
category: security
cwe: 'CWE-352: Cross-Site Request Forgery (CSRF)'
references:
- https://laravel.com/docs/9.x/blade#csrf-field

View File

@ -0,0 +1,26 @@
rules:
- id: laravel-dangerous-model-construction
patterns:
- pattern: |
$guarded = [];
- pattern-inside: |
class $CLASS extends Model {
...
}
message: Setting `$guarded` to an empty array allows mass assignment to every property
in a Laravel model. This explicitly overrides Eloquent's safe-by-default mass
assignment protections.
languages:
- php
metadata:
category: security
technology:
- php
- laravel
- eloquent
references:
- https://laravel.com/docs/9.x/eloquent#allowing-mass-assignment
- https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Laravel_Cheat_Sheet.md
cwe: 'CWE-915: Improperly Controlled Modification of Dynamically-Determined Object
Attributes'
severity: ERROR

View File

@ -0,0 +1,122 @@
rules:
- id: laravel-sql-injection
metadata:
owasp: 'A3: Injection'
cwe: "CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
category: security
technology:
- laravel
references:
- https://laravel.com/docs/8.x/queries
severity: WARNING
message: >-
Detected a SQL query based on user input.
This could lead to SQL injection, which could potentially result in sensitive data being exfiltrated by attackers.
Instead, use parameterized queries and prepared statements.
languages: [php]
mode: taint
pattern-sources:
- patterns:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_COOKIE
- pattern: $_REQUEST
- pattern: $_SERVER
pattern-sinks:
- patterns:
- pattern-either:
- patterns:
- pattern: $SQL
- pattern-either:
- pattern-inside: DB::table(...)->whereRaw($SQL, ...)
- pattern-inside: DB::table(...)->orWhereRaw($SQL, ...)
- pattern-inside: DB::table(...)->groupByRaw($SQL, ...)
- pattern-inside: DB::table(...)->havingRaw($SQL, ...)
- pattern-inside: DB::table(...)->orHavingRaw($SQL, ...)
- pattern-inside: DB::table(...)->orderByRaw($SQL, ...)
- patterns:
- pattern: $EXPRESSION
- pattern-either:
- pattern-inside: DB::table(...)->selectRaw($EXPRESSION, ...)
- pattern-inside: DB::table(...)->fromRaw($EXPRESSION, ...)
- patterns:
- pattern: $COLUMNS
- pattern-either:
- pattern-inside: DB::table(...)->whereNull($COLUMNS, ...)
- pattern-inside: DB::table(...)->orWhereNull($COLUMN)
- pattern-inside: DB::table(...)->whereNotNull($COLUMNS, ...)
- pattern-inside: DB::table(...)->whereRowValues($COLUMNS, ...)
- pattern-inside: DB::table(...)->orWhereRowValues($COLUMNS, ...)
- pattern-inside: DB::table(...)->find($ID, $COLUMNS)
- pattern-inside: DB::table(...)->paginate($PERPAGE, $COLUMNS, ...)
- pattern-inside: DB::table(...)->simplePaginate($PERPAGE, $COLUMNS, ...)
- pattern-inside: DB::table(...)->cursorPaginate($PERPAGE, $COLUMNS, ...)
- pattern-inside: DB::table(...)->getCountForPagination($COLUMNS)
- pattern-inside: DB::table(...)->aggregate($FUNCTION, $COLUMNS)
- pattern-inside: DB::table(...)->numericAggregate($FUNCTION, $COLUMNS)
- pattern-inside: DB::table(...)->insertUsing($COLUMNS, ...)
- pattern-inside: DB::table(...)->select($COLUMNS)
- pattern-inside: DB::table(...)->get($COLUMNS)
- pattern-inside: DB::table(...)->count($COLUMNS)
- patterns:
- pattern: $COLUMN
- pattern-either:
- pattern-inside: DB::table(...)->whereIn($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereIn($COLUMN, ...)
- pattern-inside: DB::table(...)->whereNotIn($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereNotIn($COLUMN, ...)
- pattern-inside: DB::table(...)->whereIntegerInRaw($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereIntegerInRaw($COLUMN, ...)
- pattern-inside: DB::table(...)->whereIntegerNotInRaw($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereIntegerNotInRaw($COLUMN, ...)
- pattern-inside: DB::table(...)->whereBetweenColumns($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereBetween($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereBetweenColumns($COLUMN, ...)
- pattern-inside: DB::table(...)->whereNotBetween($COLUMN, ...)
- pattern-inside: DB::table(...)->whereNotBetweenColumns($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereNotBetween($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereNotBetweenColumns($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereNotNull($COLUMN)
- pattern-inside: DB::table(...)->whereDate($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereDate($COLUMN, ...)
- pattern-inside: DB::table(...)->whereTime($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereTime($COLUMN, ...)
- pattern-inside: DB::table(...)->whereDay($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereDay($COLUMN, ...)
- pattern-inside: DB::table(...)->whereMonth($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereMonth($COLUMN, ...)
- pattern-inside: DB::table(...)->whereYear($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereYear($COLUMN, ...)
- pattern-inside: DB::table(...)->whereJsonContains($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereJsonContains($COLUMN, ...)
- pattern-inside: DB::table(...)->whereJsonDoesntContain($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereJsonDoesntContain($COLUMN, ...)
- pattern-inside: DB::table(...)->whereJsonLength($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhereJsonLength($COLUMN, ...)
- pattern-inside: DB::table(...)->having($COLUMN, ...)
- pattern-inside: DB::table(...)->orHaving($COLUMN, ...)
- pattern-inside: DB::table(...)->havingBetween($COLUMN, ...)
- pattern-inside: DB::table(...)->orderBy($COLUMN, ...)
- pattern-inside: DB::table(...)->orderByDesc($COLUMN)
- pattern-inside: DB::table(...)->latest($COLUMN)
- pattern-inside: DB::table(...)->oldest($COLUMN)
- pattern-inside: DB::table(...)->forPageBeforeId($PERPAGE, $LASTID, $COLUMN)
- pattern-inside: DB::table(...)->forPageAfterId($PERPAGE, $LASTID, $COLUMN)
- pattern-inside: DB::table(...)->value($COLUMN)
- pattern-inside: DB::table(...)->pluck($COLUMN, ...)
- pattern-inside: DB::table(...)->implode($COLUMN, ...)
- pattern-inside: DB::table(...)->min($COLUMN)
- pattern-inside: DB::table(...)->max($COLUMN)
- pattern-inside: DB::table(...)->sum($COLUMN)
- pattern-inside: DB::table(...)->avg($COLUMN)
- pattern-inside: DB::table(...)->average($COLUMN)
- pattern-inside: DB::table(...)->increment($COLUMN, ...)
- pattern-inside: DB::table(...)->decrement($COLUMN, ...)
- pattern-inside: DB::table(...)->where($COLUMN, ...)
- pattern-inside: DB::table(...)->orWhere($COLUMN, ...)
- pattern-inside: DB::table(...)->addSelect($COLUMN)
- patterns:
- pattern: $QUERY
- pattern-inside: DB::unprepared($QUERY)

View File

@ -0,0 +1,29 @@
rules:
- id: laravel-unsafe-validator
mode: taint
pattern-sources:
- patterns:
- pattern: $R
- pattern-inside: |
public function $F(...,Request $R,...){...}
pattern-sinks:
- patterns:
- pattern: |
Rule::unique($...X)->ignore(...)
message: Found a request argument passed to an `ignore()` definition in a Rule constraint. This
can lead to SQL injection.
languages:
- php
severity: ERROR
metadata:
category: security
cwe: 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command
(''SQL Injection'')'
owasp:
- A03:2021 - Injection
- A01:2017 - Injection
technology:
- php
- laravel
references:
- https://laravel.com/docs/9.x/validation#rule-unique

View File

@ -0,0 +1,23 @@
rules:
- id: ldap-bind-without-password
patterns:
- pattern-either:
- pattern: ldap_bind($LDAP, $DN, NULL)
- pattern: ldap_bind($LDAP, $DN, '')
- patterns:
- pattern: ldap_bind(...)
- pattern-not: ldap_bind($LDAP, $DN, $PASSWORD)
message: >-
Detected anonymous LDAP bind.
This permits anonymous users to execute LDAP statements.
Consider enforcing authentication for LDAP.
metadata:
references:
- https://www.php.net/manual/ru/function.ldap-bind.php
cwe: "CWE-287: Improper Authentication"
owasp: "A2: Broken Authentication"
category: security
technology:
- php
languages: [php]
severity: WARNING

View File

@ -0,0 +1,18 @@
rules:
- id: mb-ereg-replace-eval
patterns:
- pattern: mb_ereg_replace($PATTERN, $REPL, $STR, $OPTIONS);
- pattern-not: mb_ereg_replace($PATTERN, $REPL, $STR, "...");
message: >-
Calling mb_ereg_replace with user input in the options can lead to arbitrary
code execution. The eval modifier (`e`) evaluates the replacement argument
as code.
metadata:
references:
- https://www.php.net/manual/en/function.mb-ereg-replace.php
- https://www.php.net/manual/en/function.mb-regex-set-options.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,18 @@
rules:
- id: mb-eregi-replace-eval
patterns:
- pattern: mb_eregi_replace($PATTERN, $REPL, $STR, $OPTIONS);
- pattern-not: mb_eregi_replace($PATTERN, $REPL, $STR, "...");
message: >-
Calling mb_eregi_replace with user input in the options can lead to arbitrary
code execution. The eval modifier (`e`) evaluates the replacement argument
as code.
metadata:
references:
- https://www.php.net/manual/en/function.mb-ereg-replace.php
- https://www.php.net/manual/en/function.mb-regex-set-options.php
category: security
technology:
- php
languages: [php]
severity: ERROR

19
semgrep/mcrypt-use.yaml Normal file
View File

@ -0,0 +1,19 @@
rules:
- id: mcrypt-use
patterns:
- pattern: $FUNC(...);
- metavariable-regex:
metavariable: $FUNC
regex: (mcrypt_|mdecrypt_).+
message: >-
Mcrypt functionality has been deprecated and/or removed in recent PHP
versions. Consider using Sodium or OpenSSL.
metadata:
references:
- https://www.php.net/manual/en/intro.mcrypt.php
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/CryptoFunctionsSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,22 @@
rules:
- id: md5-loose-equality
patterns:
- pattern-either:
- pattern: $X == $FUNC(...)
- pattern: $FUNC(...) == $X
- pattern: $FUNC(...) == $FUNC(...)
- metavariable-regex:
metavariable: $FUNC
regex: md5|md5_file
message: >-
Make sure comparisons involving md5 values are strict (use `===` not `==`) to
avoid type juggling issues
metadata:
references:
- https://www.php.net/manual/en/types.comparisons.php
- https://www.whitehatsec.com/blog/magic-hashes/
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,35 @@
rules:
- id: md5-used-as-password
severity: WARNING
message: >-
It looks like MD5 is used as a password hash. MD5 is not considered a
secure password hash because it can be cracked by an attacker in a short
amount of time. Use a suitable password hashing function such as bcrypt.
You can use `password_hash($PASSWORD, PASSWORD_BCRYPT, $OPTIONS);`.
languages: [php]
metadata:
cwe: "CWE-327: Use of a Broken or Risky Cryptographic Algorithm"
owasp:
- A02:2017 - Broken Authentication
- A02:2021 - Cryptographic Failures
references:
- https://tools.ietf.org/html/rfc6151
- https://crypto.stackexchange.com/questions/44151/how-does-the-flame-malware-take-advantage-of-md5-collision
- https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords
- https://github.com/returntocorp/semgrep-rules/issues/1609
- https://www.php.net/password_hash
category: security
technology:
- md5
mode: taint
pattern-sources:
- patterns:
- pattern-either:
- pattern: md5(...)
- pattern: hash('md5', ...)
pattern-sinks:
- patterns:
- pattern: $FUNCTION(...)
- metavariable-regex:
metavariable: $FUNCTION
regex: (?i)(.*password.*)

View File

@ -0,0 +1,21 @@
rules:
- id: non-literal-header
patterns:
- pattern: header(...)
- pattern-not: header("...",...)
message: >-
Using user input when setting headers with `header()` is potentially dangerous.
This could allow an attacker to inject a new line and add a new header into the
response.
This is called HTTP response splitting.
To fix, do not allow whitespace inside `header()`: '[^\s]+'.
metadata:
references:
- https://www.php.net/manual/ru/function.header.php
- https://owasp.org/www-community/attacks/HTTP_Response_Splitting
category: security
technology:
- php
cwe: "CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')"
languages: [php]
severity: WARNING

View File

@ -0,0 +1,26 @@
rules:
- id: openssl-cbc-static-iv
patterns:
- pattern-either:
- pattern: openssl_encrypt($D, $M, $K, $FLAGS, "...",...);
- pattern: openssl_decrypt($D, $M, $K, $FLAGS, "...",...);
- metavariable-comparison:
metavariable: $M
comparison: re.match(".*-CBC",$M)
message: Static IV used with AES in CBC mode. Static IVs enable chosen-plaintext
attacks against encrypted data.
languages:
- php
severity: ERROR
metadata:
cwe: 'CWE-329: Generation of Predictable IV with CBC Mode'
references:
- https://csrc.nist.gov/publications/detail/sp/800-38a/final
owasp:
- A02:2021 - Cryptographic Failures
- A03:2017 - Sensitive Data Exposure
technology:
- php
- openssl
category: security
license: MIT

View File

@ -0,0 +1,69 @@
rules:
- id: openssl-decrypt-validate
patterns:
- pattern: openssl_decrypt(...);
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
if($DECRYPTED_STRING === false){
...
}
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
if($DECRYPTED_STRING == false){
...
}
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
if(false === $DECRYPTED_STRING){
...
}
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
if(false == $DECRYPTED_STRING){
...
}
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
assertTrue(false !== $DECRYPTED_STRING,...);
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
assertTrue($DECRYPTED_STRING !== false,...);
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
$REFERENCE::assertTrue(false !== $DECRYPTED_STRING,...);
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
$REFERENCE::assertTrue($DECRYPTED_STRING !== false,...);
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
assert(false !== $DECRYPTED_STRING,...);
- pattern-not-inside: |
$DECRYPTED_STRING = openssl_decrypt(...);
...
assert($DECRYPTED_STRING !== false,...);
message: The function `openssl_decrypt` returns either a string of the decrypted
data on success or `false` on failure. If the failure case is not handled, this
could lead to undefined behavior in your application. Please handle the case where
`openssl_decrypt` returns `false`.
languages:
- php
severity: WARNING
metadata:
references:
- https://www.php.net/manual/en/function.openssl-decrypt.php
cwe: 'CWE-252: Unchecked Return Value'
owasp:
- A02:2021 - Cryptographic Failures
technology:
- php
- openssl
category: security

View File

@ -0,0 +1,25 @@
rules:
- id: php-permissive-cors
patterns:
- pattern: header($VALUE,...)
- pattern-either:
- pattern: header("...",...)
- pattern-inside: |
$VALUE = "...";
...
- metavariable-regex:
metavariable: $VALUE
regex: (\'|\")\s*(Access-Control-Allow-Origin|access-control-allow-origin)\s*:\s*(\*)\s*(\'|\")
message: >-
Access-Control-Allow-Origin response header is set to "*".
This will disable CORS Same Origin Policy restrictions.
metadata:
references:
- https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
owasp: "A6: Security Misconfiguration"
cwe: "CWE-346: Origin Validation Error"
category: security
technology:
- php
languages: [php]
severity: WARNING

14
semgrep/phpinfo-use.yaml Normal file
View File

@ -0,0 +1,14 @@
rules:
- id: phpinfo-use
pattern: phpinfo(...);
message: >-
The 'phpinfo' function may reveal sensitive information about your environment.
metadata:
references:
- https://www.php.net/manual/en/function.phpinfo
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/PhpinfosSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,19 @@
rules:
- id: preg-replace-eval
patterns:
- pattern: preg_replace(...);
- pattern-not: preg_replace("...", ...);
message: >-
Calling preg_replace with user input in the pattern can lead to arbitrary
code execution. The eval modifier (`/e`) evaluates the replacement argument
as code.
metadata:
references:
- https://www.php.net/manual/en/function.preg-replace.php
- https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/PregReplaceSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

20
semgrep/source-leak.yaml Normal file
View File

@ -0,0 +1,20 @@
rules:
- id: source-leak
patterns:
- pattern: $FUNC(...);
- pattern-not: $FUNC("...");
- pattern-not: $FUNC(__DIR__ . "...");
- metavariable-regex:
metavariable: $FUNC
regex: \b(show_source|highlight_file)\b
message: >-
Detected non-constant source code display. This can lead to local file inclusion (LFI). LFI could lead to sensitive files being obtained by attackers. Instead, explicitly specify what to include. If that is not a viable solution, validate user input thoroughly.
metadata:
references:
- https://www.php.net/manual/en/function.highlight-file
- https://en.wikipedia.org/wiki/File_inclusion_vulnerability#Types_of_Inclusion
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -0,0 +1,30 @@
rules:
- id: symfony-csrf-protection-disabled
patterns:
- pattern-either:
- pattern: $X->createForm($TYPE, $TASK, [..., 'csrf_protection' => false, ...], ...)
- pattern: $X->prependExtensionConfig('framework', [..., 'csrf_protection' => false, ...], ...)
- pattern: $X->loadFromExtension('framework', [..., 'csrf_protection' => false, ...], ...)
- pattern: $X->setDefaults([..., 'csrf_protection' => false, ...], ...)
- patterns:
- pattern-either:
- pattern: $X->createForm($TYPE, $TASK, [..., 'csrf_protection' => $VAL, ...], ...)
- pattern: $X->prependExtensionConfig('framework', [..., 'csrf_protection' => $VAL, ...], ...)
- pattern: $X->loadFromExtension('framework', [..., 'csrf_protection' => $VAL, ...], ...)
- pattern: $X->setDefaults([..., 'csrf_protection' => $VAL, ...], ...)
- pattern-inside: |
$VAL = false;
...
message: >-
CSRF is disabled for this configuration. This is a security risk.
Make sure that it is safe or consider setting `csrf_protection` property to `true`.
metadata:
references:
- https://symfony.com/doc/current/security/csrf.html
cwe: "CWE-352: Cross-Site Request Forgery (CSRF)"
owasp: "A6: Security Misconfiguration"
category: security
technology:
- symfony
languages: [php]
severity: WARNING

View File

@ -0,0 +1,21 @@
rules:
- id: symfony-non-literal-redirect
patterns:
- pattern: $this->redirect(...)
- pattern-not: $this->redirect("...")
- pattern-not: $this->redirect()
message: >-
The `redirect()` method does not check its destination in any way. If you redirect to a URL provided by end-users, your
application may be open to the unvalidated redirects security vulnerability.
Consider using literal values or an allowlist to validate URLs.
languages: [php]
metadata:
references:
- https://symfony.com/doc/current/controller.html#redirecting
- https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html
owasp: "A1: Injection"
cwe: "CWE-601: URL Redirection to Untrusted Site ('Open Redirect')"
category: security
technology:
- symfony
severity: WARNING

View File

@ -0,0 +1,38 @@
rules:
- id: symfony-permissive-cors
patterns:
- pattern-inside: |
use Symfony\Component\HttpFoundation\Response;
...
- pattern-either:
- patterns:
- pattern-either:
- pattern: |
new Symfony\Component\HttpFoundation\Response($X, $Y, $HEADERS, ...)
- pattern: new Response($X, $Y, $HEADERS, ...)
- pattern-either:
- pattern: new $R($X, $Y, [$KEY => $VALUE], ...)
- pattern-inside: |
$HEADERS = [$KEY => $VALUE];
...
- patterns:
- pattern: $RES->headers->set($KEY, $VALUE)
- metavariable-regex:
metavariable: $KEY
regex: (\'|\")\s*(Access-Control-Allow-Origin|access-control-allow-origin)\s*(\'|\")
- metavariable-regex:
metavariable: $VALUE
regex: (\'|\")\s*(\*)\s*(\'|\")
message: >-
Access-Control-Allow-Origin response header is set to "*".
This will disable CORS Same Origin Policy restrictions.
metadata:
references:
- https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
owasp: "A6: Security Misconfiguration"
cwe: "CWE-346: Origin Validation Error"
category: security
technology:
- symfony
languages: [php]
severity: WARNING

View File

@ -0,0 +1,166 @@
rules:
- id: tainted-filename
severity: WARNING
message: >-
File name based on user input risks server-side request forgery.
metadata:
technology:
- php
category: security
cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
owasp:
- A01:2017 - Injection
- A10:2021 - Server-Side Request Forgery
languages: [php]
mode: taint
pattern-sources:
- patterns:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_COOKIE
- pattern: $_REQUEST
- pattern: $_SERVER
pattern-sanitizers:
- patterns:
- pattern-either:
- pattern-inside: basename($PATH, ...)
- pattern-inside: linkinfo($PATH, ...)
- pattern-inside: readlink($PATH, ...)
- pattern-inside: realpath($PATH, ...)
pattern-sinks:
- patterns:
- pattern-either:
- pattern-inside: opcache_compile_file($FILENAME, ...)
- pattern-inside: opcache_invalidate($FILENAME, ...)
- pattern-inside: opcache_is_script_cached($FILENAME, ...)
- pattern-inside: runkit7_import($FILENAME, ...)
- pattern-inside: readline_read_history($FILENAME, ...)
- pattern-inside: readline_write_history($FILENAME, ...)
- pattern-inside: rar_open($FILENAME, ...)
- pattern-inside: zip_open($FILENAME, ...)
- pattern-inside: gzfile($FILENAME, ...)
- pattern-inside: gzopen($FILENAME, ...)
- pattern-inside: readgzfile($FILENAME, ...)
- pattern-inside: hash_file($ALGO, $FILENAME, ...)
- pattern-inside: hash_update_file($CONTEXT, $FILENAME, ...)
- pattern-inside: pg_trace($FILENAME, ...)
- pattern-inside: dio_open($FILENAME, ...)
- pattern-inside: finfo_file($FINFO, $FILENAME, ...)
- pattern-inside: mime_content_type($FILENAME, ...)
- pattern-inside: chgrp($FILENAME, ...)
- pattern-inside: chmod($FILENAME, ...)
- pattern-inside: chown($FILENAME, ...)
- pattern-inside: clearstatcache($CLEAR_REALPATH_CACHE, $FILENAME, ...)
- pattern-inside: file_exists($FILENAME, ...)
- pattern-inside: file_get_contents($FILENAME, ...)
- pattern-inside: file_put_contents($FILENAME, ...)
- pattern-inside: file($FILENAME, ...)
- pattern-inside: fileatime($FILENAME, ...)
- pattern-inside: filectime($FILENAME, ...)
- pattern-inside: filegroup($FILENAME, ...)
- pattern-inside: fileinode($FILENAME, ...)
- pattern-inside: filemtime($FILENAME, ...)
- pattern-inside: fileowner($FILENAME, ...)
- pattern-inside: fileperms($FILENAME, ...)
- pattern-inside: filesize($FILENAME, ...)
- pattern-inside: filetype($FILENAME, ...)
- pattern-inside: fnmatch($PATTERN, $FILENAME, ...)
- pattern-inside: fopen($FILENAME, ...)
- pattern-inside: is_dir($FILENAME, ...)
- pattern-inside: is_executable($FILENAME, ...)
- pattern-inside: is_file($FILENAME, ...)
- pattern-inside: is_link($FILENAME, ...)
- pattern-inside: is_readable($FILENAME, ...)
- pattern-inside: is_uploaded_file($FILENAME, ...)
- pattern-inside: is_writable($FILENAME, ...)
- pattern-inside: lchgrp($FILENAME, ...)
- pattern-inside: lchown($FILENAME, ...)
- pattern-inside: lstat($FILENAME, ...)
- pattern-inside: parse_ini_file($FILENAME, ...)
- pattern-inside: readfile($FILENAME, ...)
- pattern-inside: stat($FILENAME, ...)
- pattern-inside: touch($FILENAME, ...)
- pattern-inside: unlink($FILENAME, ...)
- pattern-inside: xattr_get($FILENAME, ...)
- pattern-inside: xattr_list($FILENAME, ...)
- pattern-inside: xattr_remove($FILENAME, ...)
- pattern-inside: xattr_set($FILENAME, ...)
- pattern-inside: xattr_supported($FILENAME, ...)
- pattern-inside: enchant_broker_request_pwl_dict($BROKER, $FILENAME, ...)
- pattern-inside: pspell_config_personal($CONFIG, $FILENAME, ...)
- pattern-inside: pspell_config_repl($CONFIG, $FILENAME, ...)
- pattern-inside: pspell_new_personal($FILENAME, ...)
- pattern-inside: exif_imagetype($FILENAME, ...)
- pattern-inside: getimagesize($FILENAME, ...)
- pattern-inside: image2wbmp($IMAGE, $FILENAME, ...)
- pattern-inside: imagecreatefromavif($FILENAME, ...)
- pattern-inside: imagecreatefrombmp($FILENAME, ...)
- pattern-inside: imagecreatefromgd2($FILENAME, ...)
- pattern-inside: imagecreatefromgd2part($FILENAME, ...)
- pattern-inside: imagecreatefromgd($FILENAME, ...)
- pattern-inside: imagecreatefromgif($FILENAME, ...)
- pattern-inside: imagecreatefromjpeg($FILENAME, ...)
- pattern-inside: imagecreatefrompng($FILENAME, ...)
- pattern-inside: imagecreatefromtga($FILENAME, ...)
- pattern-inside: imagecreatefromwbmp($FILENAME, ...)
- pattern-inside: imagecreatefromwebp($FILENAME, ...)
- pattern-inside: imagecreatefromxbm($FILENAME, ...)
- pattern-inside: imagecreatefromxpm($FILENAME, ...)
- pattern-inside: imageloadfont($FILENAME, ...)
- pattern-inside: imagexbm($IMAGE, $FILENAME, ...)
- pattern-inside: iptcembed($IPTC_DATA, $FILENAME, ...)
- pattern-inside: mailparse_msg_extract_part_file($MIMEMAIL, $FILENAME, ...)
- pattern-inside: mailparse_msg_extract_whole_part_file($MIMEMAIL, $FILENAME, ...)
- pattern-inside: mailparse_msg_parse_file($FILENAME, ...)
- pattern-inside: fdf_add_template($FDF_DOCUMENT, $NEWPAGE, $FILENAME, ...)
- pattern-inside: fdf_get_ap($FDF_DOCUMENT, $FIELD, $FACE, $FILENAME, ...)
- pattern-inside: fdf_open($FILENAME, ...)
- pattern-inside: fdf_save($FDF_DOCUMENT, $FILENAME, ...)
- pattern-inside: fdf_set_ap($FDF_DOCUMENT, $FIELD_NAME, $FACE, $FILENAME, ...)
- pattern-inside: ps_add_launchlink($PSDOC, $LLX, $LLY, $URX, $URY, $FILENAME, ...)
- pattern-inside: ps_add_pdflink($PSDOC, $LLX, $LLY, $URX, $URY, $FILENAME, ...)
- pattern-inside: ps_open_file($PSDOC, $FILENAME, ...)
- pattern-inside: ps_open_image_file($PSDOC, $TYPE, $FILENAME, ...)
- pattern-inside: posix_access($FILENAME, ...)
- pattern-inside: posix_mkfifo($FILENAME, ...)
- pattern-inside: posix_mknod($FILENAME, ...)
- pattern-inside: ftok($FILENAME, ...)
- pattern-inside: fann_cascadetrain_on_file($ANN, $FILENAME, ...)
- pattern-inside: fann_read_train_from_file($FILENAME, ...)
- pattern-inside: fann_train_on_file($ANN, $FILENAME, ...)
- pattern-inside: highlight_file($FILENAME, ...)
- pattern-inside: php_strip_whitespace($FILENAME, ...)
- pattern-inside: stream_resolve_include_path($FILENAME, ...)
- pattern-inside: swoole_async_read($FILENAME, ...)
- pattern-inside: swoole_async_readfile($FILENAME, ...)
- pattern-inside: swoole_async_write($FILENAME, ...)
- pattern-inside: swoole_async_writefile($FILENAME, ...)
- pattern-inside: swoole_load_module($FILENAME, ...)
- pattern-inside: tidy_parse_file($FILENAME, ...)
- pattern-inside: tidy_repair_file($FILENAME, ...)
- pattern-inside: get_meta_tags($FILENAME, ...)
- pattern-inside: yaml_emit_file($FILENAME, ...)
- pattern-inside: yaml_parse_file($FILENAME, ...)
- pattern-inside: curl_file_create($FILENAME, ...)
- pattern-inside: ftp_chmod($FTP, $PERMISSIONS, $FILENAME, ...)
- pattern-inside: ftp_delete($FTP, $FILENAME, ...)
- pattern-inside: ftp_mdtm($FTP, $FILENAME, ...)
- pattern-inside: ftp_size($FTP, $FILENAME, ...)
- pattern-inside: rrd_create($FILENAME, ...)
- pattern-inside: rrd_fetch($FILENAME, ...)
- pattern-inside: rrd_graph($FILENAME, ...)
- pattern-inside: rrd_info($FILENAME, ...)
- pattern-inside: rrd_last($FILENAME, ...)
- pattern-inside: rrd_lastupdate($FILENAME, ...)
- pattern-inside: rrd_tune($FILENAME, ...)
- pattern-inside: rrd_update($FILENAME, ...)
- pattern-inside: snmp_read_mib($FILENAME, ...)
- pattern-inside: ssh2_sftp_chmod($SFTP, $FILENAME, ...)
- pattern-inside: ssh2_sftp_realpath($SFTP, $FILENAME, ...)
- pattern-inside: ssh2_sftp_unlink($SFTP, $FILENAME, ...)
- pattern-inside: apache_lookup_uri($FILENAME, ...)
- pattern-inside: md5_file($FILENAME, ...)
- pattern-inside: sha1_file($FILENAME, ...)
- pattern-inside: simplexml_load_file($FILENAME, ...)
- pattern: $FILENAME

View File

@ -0,0 +1,28 @@
rules:
- id: tainted-object-instantiation
languages:
- php
severity: WARNING
message: <-
A new object is created where the class name is based on user input. This
could lead to remote code execution, as it allows to instantiate any class in
the application.
metadata:
cwe: "CWE-470: Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')"
category: security
technology:
- php
mode: taint
pattern-sources:
- patterns:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_COOKIE
- pattern: $_REQUEST
- pattern: $_SERVER
pattern-sinks:
- patterns:
- pattern-either:
- pattern-inside: new $SINK(...)
- pattern: $SINK

View File

@ -0,0 +1,58 @@
rules:
- id: tainted-sql-string
languages:
- php
severity: ERROR
message: User data flows into this manually-constructed SQL string. User data can
be safely inserted into SQL strings using prepared statements or an object-relational
mapper (ORM). Manually-constructed SQL strings is a possible indicator of SQL
injection, which could let an attacker steal or manipulate data from the database.
Instead, use prepared statements (`$mysqli->prepare("INSERT INTO test(id, label)
VALUES (?, ?)");`) or a safe library.
metadata:
cwe: 'CWE-89: Improper Neutralization of Special Elements used in an SQL Command
(''SQL Injection'')'
owasp:
- A10:2021
- A01:2017
references:
- https://owasp.org/www-community/attacks/SQL_Injection
category: security
technology:
- php
mode: taint
pattern-sanitizers:
- pattern-either:
- pattern: mysqli_real_escape_string(...)
- pattern: real_escape_string(...)
- pattern: $MYSQLI->real_escape_string(...)
pattern-sources:
- patterns:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_COOKIE
- pattern: $_REQUEST
pattern-sinks:
- pattern-either:
- patterns:
- pattern: |
sprintf($SQLSTR, ...)
- metavariable-regex:
metavariable: $SQLSTR
regex: .*\b(?i)(select|delete|insert|create|update|alter|drop)\b.*
- patterns:
- pattern: |
"...{$EXPR}..."
- pattern-regex: |
.*\b(?i)(select|delete|insert|create|update|alter|drop)\b.*
- patterns:
- pattern: |
"...$EXPR..."
- pattern-regex: |
.*\b(?i)(select|delete|insert|create|update|alter|drop)\b.*
- patterns:
- pattern: |
"...".$EXPR
- pattern-regex: |
.*\b(?i)(select|delete|insert|create|update|alter|drop)\b.*

View File

@ -0,0 +1,54 @@
rules:
- id: tainted-url-host
languages:
- php
severity: WARNING
message: >-
User data flows into the host portion of this manually-constructed URL. This could allow an attacker to send data
to their own server, potentially exposing sensitive data such as cookies or authorization information sent with this request.
They could also probe internal servers or other resources that the server runnig this code can access. (This is called
server-side request forgery, or SSRF.) Do not allow arbitrary hosts. Instead, create an allowlist for approved hosts hardcode
the correct host.
metadata:
cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
owasp:
- A10:2021
- A01:2017
references:
- https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
category: security
technology:
- php
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
mode: taint
pattern-sources:
- patterns:
- pattern-either:
- pattern: $_GET
- pattern: $_POST
- pattern: $_COOKIE
- pattern: $_REQUEST
pattern-sinks:
- pattern-either:
- patterns:
- pattern: |
sprintf($URLSTR, ...)
- metavariable-pattern:
metavariable: $URLSTR
language: generic
pattern: $SCHEME://%s
- patterns:
- pattern: |
"...{$EXPR}..."
- pattern-regex: |
.*://\{.*
- patterns:
- pattern: |
"...$EXPR..."
- pattern-regex: |
.*://\$.*
- patterns:
- pattern: |
"...".$EXPR
- pattern-regex: |
.*://["'].*

20
semgrep/unlink-use.yaml Normal file
View File

@ -0,0 +1,20 @@
rules:
- id: unlink-use
patterns:
- pattern: unlink(...)
- pattern-not: unlink("...",...)
message: >-
Using user input when deleting files with `unlink()` is potentially dangerous.
A malicious actor could use this to modify
or access files they have no right to.
metadata:
references:
- https://www.php.net/manual/en/function.unlink
- https://owasp.org/www-project-top-ten/2017/A5_2017-Broken_Access_Control.html
category: security
technology:
- php
owasp: "A5: Broken Access Control"
cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')"
languages: [php]
severity: WARNING

View File

@ -0,0 +1,20 @@
rules:
- id: unserialize-use
patterns:
- pattern: unserialize(...)
- pattern-not: unserialize("...",...)
message: >-
Calling `unserialize()` with user input in the pattern can lead to arbitrary code
execution.
Consider using JSON or structured data approaches (e.g. Google Protocol Buffers).
metadata:
references:
- https://www.php.net/manual/ru/function.unserialize.php
- https://owasp.org/www-project-top-ten/2017/A8_2017-Insecure_Deserialization.html
category: security
technology:
- php
owasp: "A8: Insecure Deserialization"
cwe: "CWE-502: Deserialization of Untrusted Data"
languages: [php]
severity: WARNING

18
semgrep/weak-crypto.yaml Normal file
View File

@ -0,0 +1,18 @@
rules:
- id: weak-crypto
patterns:
- pattern: $FUNC(...);
- metavariable-regex:
metavariable: $FUNC
regex: crypt|md5|md5_file|sha1|sha1_file|str_rot13
message: >-
Detected usage of weak crypto function. Consider using stronger alternatives.
metadata:
references:
- https://www.php.net/manual/en/book.sodium.php
- https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/Security/Sniffs/BadFunctions/CryptoFunctionsSniff.php
category: security
technology:
- php
languages: [php]
severity: ERROR

View File

@ -1,24 +0,0 @@
<html>
<?php
if(isset($_FILES['nom'])){
$name = htmlentities($_FILES['nom']['name']);
if(stristr($name, ".jpg")==true || stristr($name, ".png")==true){
echo "<h3>The file ".$name." has been uploaded</h3>";
echo "<a href='./index.php' id='button'>UPLOAD AGAIN</a><br>";
}
else{
echo "<h3>Only JPG/PNG Files are allowed !</h3>";
echo "<a href='./index.php' id='button'>RETRY</a>";
}
else{
?>
<form method="post" action="index.php" enctype="multipart/form-data">
<div id='iconDl'>
<input type="file" name="nom" id='upload' onchange='this.form.submit()' />
</div>
</form>
<p>Click to upload</p>
<?php
}
?>
</html>

10
vulns/assert-use.php Normal file
View File

@ -0,0 +1,10 @@
<?php
// ruleid: assert-use
assert($user_input);
// ok: assert-use
assert('2 > 1');
// todook: assert-use
assert($user_input > 1);

5
vulns/backtick.php Normal file
View File

@ -0,0 +1,5 @@
<?php
echo `ping -n 3 {$user_input}`;
?>

4
vulns/backticks-use.php Normal file
View File

@ -0,0 +1,4 @@
<?php
// ruleid: backticks-use
echo `ping -n 3 {$user_input}`;

View File

@ -0,0 +1,12 @@
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.example.com/");
curl_setopt($ch, CURLOPT_HEADER, 0);
// ruleid: curl-ssl-verifypeer-off
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// ok: curl-ssl-verifypeer-off
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

26
vulns/deserialization.php Normal file
View File

@ -0,0 +1,26 @@
<?php
/* Suppose that $var_array is an array returned from
wddx_deserialize */
$size = "large";
$var_array = array("color" => "blue",
"size" => "medium",
"shape" => "sphere");
// ok: extract-user-data
extract($var_array, EXTR_PREFIX_SAME, "wddx");
$bad = $_GET['some_param'];
// ruleid:extract-user-data
extract($bad, EXTR_PREFIX_SAME, "wddx");
echo "$color, $size, $shape, $wddx_size\n";
$bad2 = $_FILES["/some/bad/path"];
// ruleid:extract-user-data
extract($bad2, EXTR_PREFIX_SAME, "wddx");
// ok: extract-user-data
$ok = $_FILES["/some/bad/path"];
extract($ok, EXTR_SKIP, "wddx");
?>

View File

@ -0,0 +1,49 @@
<?php
class ProductRepository extends ServiceEntityRepository
{
public function test1(int $price): array
{
$conn = $this->getEntityManager()->getConnection();
$sql = "SELECT * FROM product p WHERE p.price > " . $_GET['cur_price']. " ORDER BY p.price ASC";
// ruleid: doctrine-dbal-dangerous-query
$stmt = $conn->prepare($sql);
$stmt->execute(['price' => $price]);
return $stmt->fetchAllAssociative();
}
public function test2(): array
{
$conn = $this->getEntityManager()->getConnection();
// ruleid: doctrine-dbal-dangerous-query
$query = $conn->createQuery("SELECT u FROM User u WHERE u.username = '" . $_GET['username'] . "'");
$data = $query->getResult();
return $data;
}
public function okTest1(int $price): array
{
$conn = $this->getEntityManager()->getConnection();
$sql = "SELECT * FROM users WHERE username = ?";
// ok: doctrine-dbal-dangerous-query
$stmt = $conn->prepare($sql);
$stmt->bindValue(1, $_GET['username']);
$resultSet = $stmt->executeQuery();
return $resultSet;
}
public function okTest2(int $price): array
{
$conn = $this->foobar();
$sql = "SELECT * FROM users WHERE username = ?";
// ok: doctrine-dbal-dangerous-query
$stmt = $conn->prepare($sql);
$stmt->bindValue(1, $_GET['username']);
$resultSet = $stmt->executeQuery();
return $resultSet;
}
}

View File

@ -0,0 +1,40 @@
<?php
function test1($input)
{
$queryBuilder = $conn->createQueryBuilder();
$queryBuilder
->select('id', 'name')
->from('users')
// ruleid: doctrine-orm-dangerous-query
->where('email = '.$input)
;
}
function test2($email, $input)
{
$queryBuilder = new QueryBuilder($this->connection);
$queryBuilder
->select('id', 'name')
->from('users')
->where('email = ?')
->setParameter(0, $email)
// ruleid: doctrine-orm-dangerous-query
->andWhere(sprintf('user = %s', $input))
;
}
function okTest1($input)
{
$queryBuilder = $conn->createQueryBuilder();
$queryBuilder
->select('id', 'name')
->from('users')
// ok: doctrine-orm-dangerous-query
->where('email = ?')
->setParameter(0, $input)
;
}

7
vulns/eval-use.php Normal file
View File

@ -0,0 +1,7 @@
<?php
// ruleid: eval-use
eval($user_input);
// ok: eval-use
eval('echo "OK"');

25
vulns/exec-use.php Normal file
View File

@ -0,0 +1,25 @@
<?php
// ruleid: exec-use
exec($user_input);
// ok: exec-use
exec('whoami');
// ruleid: exec-use
passthru($user_input);
// ruleid: exec-use
$proc = proc_open($cmd, $descriptorspec, $pipes);
// ruleid: exec-use
$handle = popen($user_input, "r");
// ruleid: exec-use
$output = shell_exec($user_input);
// ruleid: exec-use
$output = system($user_input, $retval);
// ruleid: exec-use
pcntl_exec($path);

View File

@ -15,4 +15,6 @@ $target = $_REQUEST['target'];
if($target){ if($target){
if (stristr(php_uname('s'), 'Windows NT')) { if (stristr(php_uname('s'), 'Windows NT')) {
$cmd = shell_exec( 'ping ' . $target ); $cmd = shell_exec( 'ping ' . $target );
}
}
?> ?>

34
vulns/file-inclusion.php Normal file
View File

@ -0,0 +1,34 @@
<?php
// ruleid: file-inclusion
include($user_input);
// ok: file-inclusion
include('constant.php');
// ruleid: file-inclusion
include_once($user_input);
// ok: file-inclusion
include_once('constant.php');
// ruleid: file-inclusion
require($user_input);
// ok: file-inclusion
require('constant.php');
// ruleid: file-inclusion
require_once($user_input);
// ok: file-inclusion
require_once('constant.php');
// ruleid: file-inclusion
include(__DIR__ . $user_input);
// ok: file-inclusion
include(__DIR__ . 'constant.php');
// ok: file-inclusion
include_safe(__DIR__ . $user_input);

10
vulns/ftp-use.php Normal file
View File

@ -0,0 +1,10 @@
<?php
// ruleid: ftp-use
$conn_id = ftp_connect($ftp_server);
// ruleid: ftp-use
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
// ok: ftp-use
ssh2_scp_send($connection, '/local/filename', '/remote/filename', 0644);

4
vulns/info.php Normal file
View File

@ -0,0 +1,4 @@
<?php
// Comment
phpinfo();
?>

View File

@ -0,0 +1,26 @@
<?php
// https://www.cloudways.com/blog/laravel-security/
Route::get('this-is-prone-to-sql-injection', function($name) {
return DB::select(
// ruleid: laravel-api-route-sql-injection
DB::raw("SELECT * FROM users WHERE name = $name"));
});
Route::get('this-is-also-prone-to-sql-injection', function($name) {
return DB::select(
// ruleid: laravel-api-route-sql-injection
DB::raw("SELECT * FROM users WHERE name = " . $name));
});
Route::get('this-is-prone-to-sql-injection-too', function($name) {
return DB::select(
// ruleid: laravel-api-route-sql-injection
DB::raw("SELECT * FROM users WHERE name = $name AND someproperty = foo"));
});
Route::get('safe-from-sql-injection', function($name) {
return DB::select(
// ok: laravel-api-route-sql-injection
DB::raw("SELECT * FROM users WHERE name = ?", [$name]));
});
?>

View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav mr-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@if(!Auth::check())
<li><a class="nav-link" href="{{ url('/login') }}">Login</a></li>
<li><a class="nav-link" href="{{ url('/register') }}">Register</a></li>
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ url('/logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Logout
</a>
<!-- ok: laravel-blade-form-missing-csrf -->
<form id="logout-form" action="{{ url('/logout') }}" method="POST" style="display: none;">
@csrf
</form>
<!-- ruleid: laravel-blade-form-missing-csrf -->
<form id="logout-form-bad" action="{{ url('/logout') }}" method="POST" style="display: none;">
</form>
</div>
</li>
@endif
</ul>
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</div>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'flight_id';
/**
* The attributes that aren't mass assignable.
*
* @var array
*/
// ruleid: laravel-dangerous-model-construction
protected $guarded = [];
}

View File

@ -0,0 +1,37 @@
<?php
$tainted = $_GET['userinput'];
// https://laravel.com/docs/8.x/database
// Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement.
// ruleid: laravel-sql-injection
DB::unprepared("update users set votes = 100 where name = '$tainted'");
// https://laravel.com/docs/8.x/queries
// PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns.
// ruleid: laravel-sql-injection
$user = DB::table('users')->where($tainted, 'John')->first();
// ruleid: laravel-sql-injection
$titles = DB::table('users')->pluck($tainted);
// ruleid: laravel-sql-injection
DB::table('users')->orderBy($tainted);
// ruleid: laravel-sql-injection
$price = DB::table('orders')->max($tainted);
// ruleid: laravel-sql-injection
$query = DB::table('users')->select($tainted);
// ok: laravel-sql-injection
$user = DB::table('users')->where('name', $tainted)->first();
// https://laravel.com/docs/8.x/queries
// Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities.
// ruleid: laravel-sql-injection
$users = DB::table('users')->select(DB::raw($tainted));
// ruleid: laravel-sql-injection
$orders = DB::table('orders')->selectRaw($tainted);
// ruleid: laravel-sql-injection
$orders = DB::table('orders')->whereRaw($tainted);
// ok: laravel-sql-injection
$orders = DB::table('orders')->selectRaw('price * ? as price_with_tax', [$tainted]);

View File

@ -0,0 +1,118 @@
<?php
namespace App\Http\Controllers\Accounting;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Auth;
use Illuminate\Support\Facades\Validator;
use PDF;
use Illuminate\Validation\Rule;
class ChartofAccount extends Controller
{
public function __construct() {
$this->middleware("auth");
}
function index(){
if(Auth::user()->accesslevel == env("ACCTNG_HEAD")){
$accounts = \App\ChartOfAccount::orderBy("accounting_name")->get();
return view("accounting.chart_of_accounts.index", compact("accounts"));
}
}
function new_account(Request $request){
if(Auth::user()->accesslevel == env("ACCTNG_HEAD")){
$validate = Validator::make($request->all(),[
// ok: laravel-unsafe-validator
"accounting_code" => "required|unique:chart_of_accounts",
// ok: laravel-unsafe-validator
"accounting_name" => "required|unique:chart_of_accounts",
// ok: laravel-unsafe-validator
"some_property" => [ Rule::unique("some_column_name")->ignore(Auth::user()->id, "pk_column_name"), "required" ]
]);
if($validate->fails()){
return redirect()->back()->withErrors($validate);
}
$newaccount = new \App\ChartOfAccount;
$newaccount->accounting_code = $request->accounting_code;
$newaccount->accounting_name = $request->accounting_name;
$newaccount->category = $request->category;
$newaccount->save();
return redirect()->back()->withSuccess("Successfully created!");
}
}
function print_lists(){
if(Auth::user()->accesslevel == env("ACCTNG_HEAD")){
$accounts = \App\ChartOfAccount::orderBy("accounting_name")->get();
$pdf = PDF::loadView('accounting.chart_of_accounts.print_lists',compact('accounts'));
$pdf->setPaper('letter','portrait');
return $pdf->stream("chart_of_accounts.pdf");
}
}
function delete_account($id){
if(Auth::user()->accesslevel == env("ACCTNG_HEAD")){
$account = \App\ChartOfAccount::find($id);
$exists = \App\Accounting::where(function($query) use($account){
$query->where("accounting_code", $account->accounting_code)
->orWhere("accounting_name", $account->accounting_name);
})->get();
if($exists->isEmpty()){
$account->delete();
return redirect()->back()->withSuccess("Account already deleted!");
}else{
return redirect()->back()->withErrors("The account you are trying to delete already have a record.");
}
}
}
function update_account($id){
if(Auth::user()->accesslevel == env("ACCTNG_HEAD")){
$account = \App\ChartOfAccount::find($id);
$exists = \App\Accounting::where(function($query) use($account){
$query->where("accounting_code", $account->accounting_code)
->orWhere("accounting_name", $account->accounting_name);
})->get();
if($exists->isEmpty()){
return view("accounting.chart_of_accounts.update_form", compact("id","account"));
}else{
return redirect()->back()->withErrors("The account you are trying to update already have a record.");
}
}
}
function update_account_post(Request $request){
$validate = Validator::make($request->all(),[
// ruleid: laravel-unsafe-validator
"accounting_code" => [ Rule::unique("chart_of_accounts")->ignore($request->chart_id,"id"), "required" ],
// ruleid: laravel-unsafe-validator
"accounting_name" => [ Rule::unique("chart_of_accounts")->ignore($request->chart_id,"id"), "required" ],
]);
if($validate->fails()){
return redirect(url("/accounting/chart_of_accounts"))->withErrors($validate);
}
$newaccount = \App\ChartOfAccount::find($request->chart_id);
$newaccount->accounting_code = $request->accounting_code;
$newaccount->accounting_name = $request->accounting_name;
$newaccount->category = $request->category;
$newaccount->save();
return redirect(url("/accounting/chart_of_accounts"))->withSuccess("Successfully updated!");
}
}

View File

@ -0,0 +1,36 @@
<?php
$ldapconn = ldap_connect("foo.com");
// ruleid: ldap-bind-without-password
$ldapbind = ldap_bind($ldapconn);
// todoruleid: ldap-bind-without-password
LDAP_BIND($ldapconn, "username");
// ruleid: ldap-bind-without-password
ldap_bind($ldapconn, NULL, NULL);
// ruleid: ldap-bind-without-password
ldap_bind($ldapconn, "username", "");
$a = "";
$b = "";
// ruleid: ldap-bind-without-password
ldap_bind($ldapconn, $a, $b);
$c = "username";
$d = "";
// ruleid: ldap-bind-without-password
ldap_bind($ldapconn, $c, $d);
$e = "user";
$f = "pass";
// ok: ldap-bind-without-password
ldap_bind($ldapconn, $e, $f);
// ok: ldap-bind-without-password
ldap_bind($ldapconn, "username", "password");
// ok: ldap-bind-without-password
ldap_bind($ldapconn, $username, $password);

View File

@ -0,0 +1,10 @@
<?php
// ruleid: mb-ereg-replace-eval
mb_ereg_replace($pattern, $replacement, $string, $user_input_options);
// ok: mb-ereg-replace-eval
mb_ereg_replace($pattern, $replacement, $string, "msr");
// ok: mb-ereg-replace-eval
mb_ereg_replace($pattern, $replacement, $string);

16
vulns/mcrypt-use.php Normal file
View File

@ -0,0 +1,16 @@
<?php
// ruleid: mcrypt-use
mcrypt_ecb(MCRYPT_BLOWFISH, $key, base64_decode($input), MCRYPT_DECRYPT);
// ruleid: mcrypt-use
mcrypt_create_iv($iv_size, MCRYPT_RAND);
// ruleid: mcrypt-use
mdecrypt_generic($td, $c_t);
// ok: mcrypt-use
sodium_crypto_secretbox("Hello World!", $nonce, $key);
// ok: mcrypt-use
openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);

View File

@ -0,0 +1,16 @@
<?php
// ruleid: md5-loose-equality
md5("240610708") == "0";
// ruleid: md5-loose-equality
0 == md5("240610708");
// ruleid: md5-loose-equality
0 == md5_file("file.txt");
// ruleid: md5-loose-equality
md5("240610708") == md5_file("file.txt");
// ok: md5-loose-equality
md5("240610708") === "0";

View File

@ -0,0 +1,19 @@
<?php
function test1($value) {
$pass = md5($value);
// ruleid: md5-used-as-password
$user->setPassword($pass);
}
function test2($value) {
$pass = hash('md5', $value);
// ruleid: md5-used-as-password
$user->setPassword($pass);
}
function okTest1($value) {
// ok: md5-used-as-password
$pass = hash('sha256', $value);
$user->setPassword($pass);
}

View File

@ -0,0 +1,12 @@
<?php
$data = $_GET["data"];
// ruleid: non-literal-header
header("Some-Header: $data");
$data = $_GET["data"];
// ruleid: non-literal-header
header("Some-Header: ".$data);
// ok: non-literal-header
header("Some-Header: value");

View File

@ -0,0 +1,62 @@
<?php
function encrypt($plaintext, $password) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(16);
// ok
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
function encryptBad($plaintext, $password) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$iv = "4c25ecc95c8816db753cba44a3b56aca";
// ruleid: openssl-cbc-static-iv
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
function encryptBad2($plaintext, $password) {
$key = hash('sha256', $password, true);
$iv = "4c25ecc95c8816db753cba44a3b56aca";
// ruleid: openssl-cbc-static-iv
$ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
function decrypt($ivHashCiphertext, $password) {
$method = "AES-256-CBC";
$iv = substr($ivHashCiphertext, 0, 16);
$hash = substr($ivHashCiphertext, 16, 32);
$ciphertext = substr($ivHashCiphertext, 48);
$key = hash('sha256', $password, true);
if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;
// ok
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}
function decryptBad($ivHashCiphertext, $password) {
$method = "AES-256-CBC";
$iv = "4c25ecc95c8816db753cba44a3b56aca";
$hash = substr($ivHashCiphertext, 16, 32);
$ciphertext = substr($ivHashCiphertext, 48);
$key = hash('sha256', $password, true);
if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;
// ruleid: openssl-cbc-static-iv
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}

View File

@ -0,0 +1,70 @@
<?php
class OpenSslTest{
public static function decrypt_test_1($crypt, $ky) {
$key = html_entity_decode($ky);
$iv = "@@@@&&&&####$$$$";
// ruleid: openssl-decrypt-validate
$data = openssl_decrypt ( $crypt , "AES-128-CBC" , $key, 0, $iv );
return $data;
}
public static function decrypt_test_2($crypt, $ky) {
$key = html_entity_decode($ky);
$iv = "@@@@&&&&####$$$$";
// ruleid: openssl-decrypt-validate
$data = openssl_decrypt ( $crypt , "AES-128-CBC" , $key, 0, $iv );
if($data == true){
return "";
}
return $data;
}
public static function decrypt_test_3($crypt, $ky) {
$key = html_entity_decode($ky);
$iv = "@@@@&&&&####$$$$";
// ruleid: openssl-decrypt-validate
return openssl_decrypt ( $crypt , "AES-128-CBC" , $key, 0, $iv );
}
public static function decrypt_test_ok($crypt, $ky) {
$key = html_entity_decode($ky);
$iv = "@@@@&&&&####$$$$";
// ok: openssl-decrypt-validate
$data = openssl_decrypt ( $crypt , "AES-128-CBC" , $key, 0, $iv );
if($data == false){
return "";
}
return $data;
}
public static function decrypt_test_ok_2($crypt, $ky) {
$key = html_entity_decode($ky);
$iv = "@@@@&&&&####$$$$";
// ok: openssl-decrypt-validate
$data = openssl_decrypt ( $crypt , "AES-128-CBC" , $key, 0, $iv );
if(false === $data){
return "";
}
return $data;
}
public static function decrypt_test_ok_3($crypt, $ky) {
$key = html_entity_decode($ky);
$iv = "@@@@&&&&####$$$$";
// ok: openssl-decrypt-validate
$data = openssl_decrypt ( $crypt , "AES-128-CBC" , $key, 0, $iv );
assertTrue(false !== $data);
return $data;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace testing;
// ruleid: php-permissive-cors
header("Access-Control-Allow-Origin: *");
// ruleid: php-permissive-cors
header("Access-Control-Allow-Origin:* ");
// todoruleid: php-permissive-cors
Header("access-control-allow-origin: *");
// ok: php-permissive-cors
header("Access-Control-Allow-Origin: *something*");
// ok: php-permissive-cors
header("Other-Property: *");

4
vulns/phpinfo-use.php Normal file
View File

@ -0,0 +1,4 @@
<?php
// ruleid: phpinfo-use
echo phpinfo();

View File

@ -0,0 +1,7 @@
<?php
// ruleid: preg-replace-eval
preg_replace($user_input_pattern, $replacement, $string);
// ok: preg-replace-eval
preg_replace("/some_regexp/", "replacement", $string_before);

View File

@ -4,7 +4,7 @@ if (isset($_GET['which']))
{ {
$which = $_GET['which']; $which = $_GET['which'];
require_once $which.'noparenthesis.php'; require_once $which.'noparenthesis.php';
require_once($which.'parenthesis.php';) require_once($which.'parenthesis.php');
} }
?> ?>

View File

@ -0,0 +1,110 @@
<?php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class Type extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
// ruleid: symfony-csrf-protection-disabled
$resolver->setDefaults([
'data_class' => Type::class,
'csrf_protection' => false
]);
// ruleid: symfony-csrf-protection-disabled
$resolver->setDefaults(array(
'csrf_protection' => false
));
$csrf = false;
// ruleid: symfony-csrf-protection-disabled
$resolver->setDefaults([
'csrf_protection' => $csrf
]);
// ok: symfony-csrf-protection-disabled
$resolver->setDefaults([
'csrf_protection' => true
]);
// ok: symfony-csrf-protection-disabled
$resolver->setDefaults([
'data_class' => Type::class,
]);
// ok: symfony-csrf-protection-disabled
$resolver->setDefaults($options);
}
}
class TestExtension extends Extension implements PrependExtensionInterface
{
public function prepend(ContainerBuilder $container)
{
// ruleid: symfony-csrf-protection-disabled
$container->prependExtensionConfig('framework', ['csrf_protection' => false,]);
// ruleid: symfony-csrf-protection-disabled
$container->prependExtensionConfig('framework', ['something_else' => true, 'csrf_protection' => false,]);
$csrfOption = false;
// ruleid: symfony-csrf-protection-disabled
$container->prependExtensionConfig('framework', ['csrf_protection' => $csrfOption,]);
// ruleid: symfony-csrf-protection-disabled
$container->loadFromExtension('framework', ['csrf_protection' => false,]);
// ok: symfony-csrf-protection-disabled
$container->loadFromExtension('framework', ['csrf_protection' => null,]);
// ok: symfony-csrf-protection-disabled
$container->prependExtensionConfig('framework', ['csrf_protection' => true,]);
// ok: symfony-csrf-protection-disabled
$container->prependExtensionConfig('framework', ['csrf_protection' => null,]);
// ok: symfony-csrf-protection-disabled
$container->prependExtensionConfig('something_else', ['csrf_protection' => false,]);
}
}
class MyController1 extends AbstractController
{
public function action()
{
// ruleid: symfony-csrf-protection-disabled
$this->createForm(TaskType::class, $task, [
'other_option' => false,
'csrf_protection' => false,
]);
// ruleid: symfony-csrf-protection-disabled
$this->createForm(TaskType::class, $task, array(
'csrf_protection' => false,
));
$csrf = false;
// ruleid: symfony-csrf-protection-disabled
$this->createForm(TaskType::class, $task, array(
'csrf_protection' => $csrf,
));
// ok: symfony-csrf-protection-disabled
$this->createForm(TaskType::class, $task, ['csrf_protection' => true]);
// ok: symfony-csrf-protection-disabled
$this->createForm(TaskType::class, $task, ['other_option' => false]);
$this->redirectToRoute('/');
}
}

View File

@ -0,0 +1,39 @@
<?php
use Symfony\Component\HttpFoundation\RedirectResponse;
class WebAppController
{
public function test1(): RedirectResponse
{
$foobar = $session->get('foobar');
// ruleid: symfony-non-literal-redirect
return $this->redirect($foobar);
}
public function test2(): RedirectResponse
{
$addr = $request->query->get('page', 1);
// ruleid: symfony-non-literal-redirect
return $this->redirect('https://'. $addr);
}
public function okTest1(): RedirectResponse
{
$foobar = $session->get('foobar');
// ok: symfony-non-literal-redirect
return $this->redirectToRoute($foobar);
}
public function okTest2(): RedirectResponse
{
// ok: symfony-non-literal-redirect
return $this->redirect('http://symfony.com/doc');
}
public function okTest3(): RedirectResponse
{
// ok: symfony-non-literal-redirect
return $this->redirect();
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace symfony;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Response as FooResponse;
// ruleid: symfony-permissive-cors
$response = new Response('content', Response::HTTP_OK, ['Access-Control-Allow-Origin' => '*']);
// ruleid: symfony-permissive-cors
$response = new Response('content', Response::HTTP_OK, Array('Access-Control-Allow-Origin' => '*'));
// todoruleid: symfony-permissive-cors
$response = new response('content', Response::HTTP_OK, Array('Access-Control-Allow-Origin' => '*'));
// ruleid: symfony-permissive-cors
$response = new FooResponse('content', Response::HTTP_OK, ['Access-Control-Allow-Origin' => '*']);
$headers = ['Access-Control-Allow-Origin' => '*'];
// ruleid: symfony-permissive-cors
$response = new Response('content', Response::HTTP_OK, $headers);
// ruleid: symfony-permissive-cors
$response->headers->set(' access-control-allow-origin ', ' * ');
$safe = ['foo' => 'bar'];
// ok: symfony-permissive-cors
$response = new Response('content', Response::HTTP_OK, $safe);
// ok: symfony-permissive-cors
$response = new Response('content', Response::HTTP_OK, ['Access-Control-Allow-Origin' => 'https://www.example.com']);
// ok: symfony-permissive-cors
$response = new Response('content', Response::HTTP_OK, ['Other-Property' => '*']);
// ok: symfony-permissive-cors
$response = new Foo('content', Response::HTTP_OK, ['Access-Control-Allow-Origin' => '*']);
// ok: symfony-permissive-cors
$response->headers->set('Access-Control-Allow-Origin', 'foo');
// ok: symfony-permissive-cors
$response->headers->set('Other-Property', '*');

View File

@ -0,0 +1,20 @@
<?php
$tainted = $_GET["tainted"];
// ruleid: tainted-filename
hash_file('sha1', $tainted);
// ruleid: tainted-filename
file($tainted);
// ok: tainted-filename
hash_file($tainted, 'file.txt');
// ruleid: tainted-filename
file(dirname($tainted));
// Sanitized
// ok: tainted-filename
file(basename($tainted));

View File

@ -0,0 +1,14 @@
<?php
$parts = explode("/", $_SERVER['PATH_INFO']);
$controllerName = $parts[0];
// ruleid: tainted-object-instantiation
$controller = new $controllerName($parts[1]);
// ok: tainted-object-instantiation
$controller = new MyController($controllerName);
// ok: tainted-object-instantiation
$a = "MyController";
$controller = new $a();

View File

@ -0,0 +1,73 @@
<?php
// True Positives
function test1() {
// ruleid: tainted-sql-string
$query = "SELECT * FROM table WHERE Id = '".$_GET['url']."'";
$info = mysql_query($query);
return $info;
}
function test2() {
$part = $_POST['url'];
// ruleid: tainted-sql-string
$query = "SELECT * FROM table WHERE Id = '$part'";
$info = mysql_query($query);
return $info;
}
function test3() {
// ruleid: tainted-sql-string
$query = "SELECT * FROM table WHERE Id = '{$_REQUEST['url']}'";
$info = mysql_query($query);
return $info;
}
function test4() {
// ruleid: tainted-sql-string
$query = sprintf("SELECT * FROM table WHERE Id = '%s'", $_COOKIE['foo']);
$info = mysql_query($query);
return $info;
}
// True Negatives
function test1() {
// ok: tainted-sql-string
$query = 'SELECT * FROM table WHERE Id = 1';
$info = mysql_query($query);
return $info;
}
function test2() {
$value = 1;
// ok: tainted-sql-string
$query = "SELECT * FROM table WHERE Id = '".$value."'";
$info = mysql_query($query);
return $info;
}
function test3() {
// ok: tainted-sql-string
$query = "SELECT * FROM table WHERE Id = '{$foobar() ? 1 : 2}'";
$info = mysql_query($query);
return $info;
}
function test4() {
$value = 1;
// ok: tainted-sql-string
$query = sprintf("SELECT * FROM table WHERE Id = '%s'", $value);
$info = mysql_query($query);
return $info;
}
function test5() {
$part = $_POST['url'];
$part = mysqli_real_escape_string($part);
// ok: tainted-sql-string
$query = sprintf("SELECT * FROM table WHERE Id = '" . $part . "'");
$info = mysql_query($query);
return $info;
}

View File

@ -0,0 +1,73 @@
<?php
function make_request($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
// True Positives
function test1() {
// ruleid: tainted-url-host
$url = 'https://'.$_GET['url'].'/foobar';
$info = make_request($url);
return $info;
}
function test2() {
$part = $_POST['url'];
// ruleid: tainted-url-host
$url = "https://$part/foobar";
$info = make_request($url);
return $info;
}
function test3() {
// ruleid: tainted-url-host
$url = "https://{$_REQUEST['url']}/foobar";
$info = make_request($url);
return $info;
}
function test4() {
// ruleid: tainted-url-host
$url = sprintf('https://%s/%s/', $_COOKIE['foo'], $bar);
$info = make_request($url);
return $info;
}
// True Negatives
function test1() {
// ok: tainted-url-host
$url = 'https://www.google.com/'.$_GET['url'].'/foobar';
$info = make_request($url);
return $info;
}
function test2() {
$part = $_POST['url'];
// ok: tainted-url-host
$url = "some random text /$part/ foobar";
$info = make_request($url);
return $info;
}
function test3() {
// ok: tainted-url-host
$url = "https://www.google.com/{$_REQUEST['url']}/foobar";
$info = make_request($url);
return $info;
}
function test4() {
// ok: tainted-url-host
$url = sprintf('some random format string %s %s', $_COOKIE['foo'], $bar);
$info = make_request($url);
return $info;
}

10
vulns/unlink-use.php Normal file
View File

@ -0,0 +1,10 @@
<?php
$data = $_GET["data"];
// ruleid: unlink-use
unlink("/storage/" . $data . "/test");
// ok: unlink-use
unlink('/storage/foobar/test');
?>

View File

@ -0,0 +1,8 @@
<?php
$data = $_GET["data"];
// ruleid: unserialize-use
$object = unserialize($data);
// ok: unserialize-use
$object2 = unserialize('O:1:"a":1:{s:5:"value";s:3:"100";}');

Some files were not shown because too many files have changed in this diff Show More