Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

163 lines
6.2 KiB
Go

package astquery
// PHP SAST starter pack.
func init() {
registerPHPEval()
registerPHPShellExec()
registerPHPUnserialize()
registerPHPSQLConcat()
registerPHPWeakHash()
registerPHPFileInclude()
registerPHPEchoUserInput()
registerPHPPregReplaceE()
registerPHPMySQLDeprecated()
registerPHPCURLNoVerify()
}
func registerPHPEval() {
mustRegisterSAST(sastRule{
Name: "php-eval-use",
Description: "`eval($str)` / `assert($str)` — executes arbitrary PHP. Trivial RCE with user input.",
Severity: "error",
CWE: "CWE-95",
OWASP: "A03:2021-Injection",
Tags: []string{"injection", "code-injection"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn) @match (#match? @fn "^(eval|assert)$"))`,
},
})
}
func registerPHPShellExec() {
mustRegisterSAST(sastRule{
Name: "php-shell-exec",
Description: "`system()` / `exec()` / `passthru()` / `shell_exec()` / `popen()` / `` `cmd` `` — shell execution functions. Any user-controlled argument is command injection. Use `escapeshellarg` + an argv-style alternative.",
Severity: "error",
CWE: "CWE-78",
OWASP: "A03:2021-Injection",
Tags: []string{"command-injection"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn) @match
(#match? @fn "^(system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec)$"))`,
},
})
}
func registerPHPUnserialize() {
mustRegisterSAST(sastRule{
Name: "php-unserialize",
Description: "`unserialize($x)` — magic methods on attacker-controlled classes are invoked. RCE primitive in any PHP framework. Use `json_decode` instead.",
Severity: "error",
CWE: "CWE-502",
OWASP: "A08:2021-Software and Data Integrity Failures",
Tags: []string{"deserialization"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn) @match (#eq? @fn "unserialize"))`,
},
})
}
func registerPHPSQLConcat() {
mustRegisterSAST(sastRule{
Name: "php-mysql-query-concat",
Description: "`mysql_query`/`mysqli_query`/`pg_query`/`PDO::query` with a string literal containing `.` concatenation — SQL injection. Use parameterised queries.",
Severity: "error",
CWE: "CWE-89",
OWASP: "A03:2021-Injection",
Tags: []string{"sqli"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn
arguments: (arguments (argument (binary_expression operator: ".")))) @match
(#match? @fn "^(mysql_query|mysqli_query|pg_query|pg_send_query)$"))`,
},
})
}
func registerPHPWeakHash() {
mustRegisterSAST(sastRule{
Name: "php-md5-sha1",
Description: "`md5($x)` / `sha1($x)` — broken hashes. Use `password_hash($x, PASSWORD_DEFAULT)` for passwords and `hash('sha256', $x)` for fingerprinting.",
Severity: "error",
CWE: "CWE-327",
Tags: []string{"crypto", "weak-hash"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn) @match (#match? @fn "^(md5|sha1)$"))`,
},
})
}
func registerPHPFileInclude() {
mustRegisterSAST(sastRule{
Name: "php-file-include-with-variable",
Description: "`include $f` / `require $f` / `include_once $f` / `require_once $f` with a variable — file-inclusion vulnerability. With user input + `allow_url_include=On` this is LFI/RFI / RCE.",
Severity: "warning",
CWE: "CWE-98",
Tags: []string{"file-inclusion"},
Pat: map[string]string{
"php": `(include_expression (variable_name) @arg) @match
(require_expression (variable_name) @arg) @match
(include_once_expression (variable_name) @arg) @match
(require_once_expression (variable_name) @arg) @match`,
},
})
}
func registerPHPEchoUserInput() {
mustRegisterSAST(sastRule{
Name: "php-echo-user-input",
Description: "`echo $_GET[...]` / `print $_POST[...]` / `echo $_REQUEST[...]` — reflected XSS unless `htmlspecialchars`-escaped. Use a templating engine that auto-escapes (Twig, Blade).",
Severity: "warning",
CWE: "CWE-79",
Tags: []string{"xss"},
Pat: map[string]string{
"php": `((echo_statement (subscript_expression
(variable_name) @src)) @match
(#match? @src "^_(GET|POST|REQUEST|COOKIE)$"))`,
},
})
}
func registerPHPPregReplaceE() {
mustRegisterSAST(sastRule{
Name: "php-preg-replace-e-modifier",
Description: "`preg_replace('/pattern/e', ...)` — the `e` modifier `eval`s the replacement. Deprecated in 5.5, removed in 7.0, but legacy projects still hit it. RCE primitive.",
Severity: "error",
CWE: "CWE-95",
Tags: []string{"injection", "deprecated"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn
arguments: (arguments (argument (string) @pat))) @match
(#eq? @fn "preg_replace") (#match? @pat "/[^\"']*/[a-zA-Z]*e[a-zA-Z]*[\"']"))`,
},
})
}
func registerPHPMySQLDeprecated() {
mustRegisterSAST(sastRule{
Name: "php-mysql-extension-removed",
Description: "`mysql_*` family — removed in PHP 7.0. Use `mysqli_*` or PDO with prepared statements.",
Severity: "info",
CWE: "CWE-1104",
Tags: []string{"deprecated"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn) @match (#match? @fn "^mysql_(connect|query|fetch_array|fetch_row|fetch_assoc|num_rows|result|select_db|close|escape_string|real_escape_string)$"))`,
},
})
}
func registerPHPCURLNoVerify() {
mustRegisterSAST(sastRule{
Name: "php-curl-no-verify",
Description: "`curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false)` — disables TLS certificate validation. Any MITM intercepts the request.",
Severity: "error",
CWE: "CWE-295",
Tags: []string{"tls", "no-verify"},
Pat: map[string]string{
"php": `((function_call_expression function: (name) @fn
arguments: (arguments (argument) (argument (name) @opt) (argument))) @match
(#eq? @fn "curl_setopt") (#match? @opt "^(CURLOPT_SSL_VERIFYPEER|CURLOPT_SSL_VERIFYHOST)$"))`,
},
})
}