From b5d8fab7be79226424034906ce0aac901240ccea Mon Sep 17 00:00:00 2001 From: David Roe Date: Mon, 2 Oct 2023 16:05:30 +0100 Subject: [PATCH] feat: add url with sensitive data rule --- rules/php/lang/http_insecure.yml | 301 +-- .../lang/http_url_using_sensitive_data.yml | 398 +++ rules/php/lang/http_url_using_user_input.yml | 301 +-- rules/php/shared/lang/http_url.yml | 252 ++ scripts/rule_schema.json | 2 +- .../__snapshots__/test.js.snap | 2233 +++++++++++++++++ .../http_url_using_sensitive_data/test.js | 106 + .../testdata/bad.php | 3 + .../testdata/bad_guzzle.php | 28 + .../testdata/bad_httplug.php | 7 + .../testdata/bad_laminas.php | 18 + .../testdata/bad_laravel.php | 20 + .../testdata/bad_pecl_http.php | 7 + .../testdata/bad_requests.php | 26 + .../testdata/bad_sendgrid.php | 9 + .../testdata/bad_symfony.php | 18 + .../testdata/bad_unirest.php | 5 + .../testdata/ok.php | 3 + .../testdata/ok_guzzle.php | 14 + .../testdata/ok_httplug.php | 7 + .../testdata/ok_laminas.php | 18 + .../testdata/ok_laravel.php | 20 + .../testdata/ok_pecl_http.php | 7 + .../testdata/ok_requests.php | 28 + .../testdata/ok_sendgrid.php | 9 + .../testdata/ok_symfony.php | 18 + .../testdata/ok_unirest.php | 5 + .../__snapshots__/test.js.snap | 388 ++- .../testdata/bad_guzzle.php | 1 + .../testdata/bad_unirest.php | 4 + 30 files changed, 3558 insertions(+), 698 deletions(-) create mode 100644 rules/php/lang/http_url_using_sensitive_data.yml create mode 100644 rules/php/shared/lang/http_url.yml create mode 100644 tests/php/lang/http_url_using_sensitive_data/__snapshots__/test.js.snap create mode 100644 tests/php/lang/http_url_using_sensitive_data/test.js create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_guzzle.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_httplug.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_laminas.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_laravel.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_pecl_http.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_requests.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_sendgrid.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_symfony.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/bad_unirest.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_guzzle.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_httplug.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_laminas.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_laravel.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_pecl_http.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_requests.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_sendgrid.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_symfony.php create mode 100644 tests/php/lang/http_url_using_sensitive_data/testdata/ok_unirest.php diff --git a/rules/php/lang/http_insecure.yml b/rules/php/lang/http_insecure.yml index 960a99453..878d7994f 100644 --- a/rules/php/lang/http_insecure.yml +++ b/rules/php/lang/http_insecure.yml @@ -1,307 +1,18 @@ languages: - php imports: + - php_shared_lang_http_url - php_shared_lang_insecure_url - - php_shared_lang_instance patterns: - - pattern: $($$<...>) + - pattern: $; filters: - - variable: FUNCTION - values: - - curl_init - - file_get_contents - - fopen - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: curl_setopt($<_>, CURLOPT_URL, $) - filters: - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: new SoapClient($<_>, $) - filters: - - variable: OPTIONS - detection: php_lang_http_insecure_soap_options - scope: cursor - # PSR-17 - - pattern: $<_>->createRequest($<_>, $) - filters: - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - # pecl_http - - pattern: new $($<_>, $$<...>) - filters: - - variable: CLASS - regex: \A(http\\Client\\)?Request\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: new $($$<...>) - filters: - - variable: CLASS - regex: \A((Laminas|Zend)\\Http\\)?Client\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $->setUri($) - filters: - - variable: LAMINAS - detection: php_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - regex: \A((Laminas|Zend)\\Http\\)?(Client|Request)\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: new $($) - filters: - - variable: CLASS - regex: \A(GuzzleHttp\\)?Client\z - - variable: OPTIONS - detection: php_lang_http_insecure_guzzle_options - scope: result - - pattern: $->request($<_>, $$<...>) - filters: - - variable: GUZZLE - detection: php_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - regex: \A(GuzzleHttp\\)?Client\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $->$($$<...>) - filters: - - variable: GUZZLE - detection: php_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - regex: \A(GuzzleHttp\\)?Client\z - - variable: METHOD - values: - - get - - delete - - head - - options - - patch - - post - - put - - getAsync - - deleteAsync - - headAsync - - optionsAsync - - patchAsync - - postAsync - - putAsync - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: new $($<_>, $$<...>) - filters: - - variable: CLASS - regex: \A(GuzzleHttp\\Psr7\\)?Request\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $::$($$<...>) - filters: - - variable: CLASS - regex: \A(WpOrg\\Requests\\)?Requests\z - - variable: METHOD - values: - - get - - head - - delete - - trace - - post - - put - - options - - patch - - request - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $::request_multiple($$<...>) - filters: - - variable: CLASS - regex: \A(WpOrg\\Requests\\)?Requests\z - - variable: OPTIONS - detection: php_lang_http_insecure_requests_options - scope: result - - pattern: new $($$<...>) - filters: - - variable: CLASS - regex: \A(WpOrg\\Requests\\)?Session\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $->$($$<...>) - filters: - - variable: SESSION - detection: php_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - regex: \A(WpOrg\\Requests\\)?Session\z - - variable: METHOD - values: - - get - - head - - delete - - post - - put - - patch - - request - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $->request_multiple($$<...>) - filters: - - variable: SESSION - detection: php_shared_lang_instance - scope: cursor + - variable: HTTP_URL + detection: php_shared_lang_http_url + scope: cursor_strict filters: - - variable: CLASS - regex: \A(WpOrg\\Requests\\)?Session\z - - variable: OPTIONS - detection: php_lang_http_insecure_requests_options - scope: result - - pattern: $->$($$<...>) - filters: - - variable: BROWSER - detection: php_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - regex: \A(Buzz\\)?Browser\z - - variable: METHOD - values: - - get - - post - - head - - patch - - put - - delete - - submitForm - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $->request($<_>, $$<...>) - filters: - - variable: BROWSER - detection: php_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - regex: \A(Buzz\\)?Browser\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: new $($$<...>) - filters: - - variable: CLASS - regex: \A(SendGrid\\)?Client\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $::$($$<...>) - filters: - - variable: CLASS - regex: \A(Unirest\\)?Request\z - - variable: METHOD - values: - - get - - post - - put - - patch - - delete - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $::send($<_>, $$<...>) - filters: - - variable: CLASS - regex: \A(Unirest\\)?Request\z - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $::$($$<...>) - filters: - - variable: CLASS - regex: \A(Illuminate\\Support\\Facades\\)?Http\z - - variable: METHOD - values: - - head - - get - - post - - put - - patch - - delete - - withUrlParameters - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - pattern: $->$($$<...>) - filters: - - variable: HTTP - detection: php_lang_http_insecure_laravel_http - scope: cursor - - variable: METHOD - values: - - head - - get - - post - - put - - patch - - delete - - withUrlParameters - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - # symfony - - pattern: $<_>->request($<_>, $$<...>); - filters: - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result -auxiliary: - - id: php_lang_http_insecure_soap_options - patterns: - - pattern: array('location' => $) - filters: - - variable: INSECURE_URL + - variable: URL detection: php_shared_lang_insecure_url scope: result - - id: php_lang_http_insecure_guzzle_options - patterns: - - pattern: array('base_uri' => $) - filters: - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - id: php_lang_http_insecure_requests_options - patterns: - - pattern: array('url' => $) - filters: - - variable: INSECURE_URL - detection: php_shared_lang_insecure_url - scope: result - - id: php_lang_http_insecure_laravel_http - patterns: - - pattern: $::$<_>() - filters: - - variable: CLASS - regex: \A(Illuminate\\Support\\Facades\\)?Http\z - - pattern: $->$<_>() - filters: - - variable: HTTP - detection: php_lang_http_insecure_laravel_http - scope: cursor severity: low metadata: description: "Communication through an insecure HTTP connection detected." diff --git a/rules/php/lang/http_url_using_sensitive_data.yml b/rules/php/lang/http_url_using_sensitive_data.yml new file mode 100644 index 000000000..3e6d5f78f --- /dev/null +++ b/rules/php/lang/http_url_using_sensitive_data.yml @@ -0,0 +1,398 @@ +languages: + - php +imports: + - php_shared_lang_instance + - php_shared_lang_http_url +patterns: + - pattern: $; + filters: + - variable: HTTP_URL + detection: php_shared_lang_http_url + scope: cursor_strict + filters: + - variable: URL + detection: datatype + scope: result + - pattern: $->$($<_>, $<_>, $) + filters: + - variable: GUZZLE + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(GuzzleHttp\\)?Client\z + - variable: METHOD + values: + - request + - requestAsync + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_guzzle_options + scope: cursor + - pattern: $->$($<_>, $) + filters: + - variable: GUZZLE + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(GuzzleHttp\\)?Client\z + - variable: METHOD + values: + - get + - delete + - head + - options + - patch + - post + - put + - getAsync + - deleteAsync + - headAsync + - optionsAsync + - patchAsync + - postAsync + - putAsync + - send + - sendAsync + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_guzzle_options + scope: cursor + - pattern: new $($) + filters: + - variable: CLASS + regex: \A(Http\\Message\\Authentication\\)?QueryParam\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->setParameterGet($) + filters: + - variable: CLIENT + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A((Laminas|Zend)\\Http\\)?Client\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->$($<...>$$<...>) + filters: + - variable: QUERY + detection: php_lang_http_url_using_sensitive_data_laminas_query + scope: cursor + - variable: METHOD + values: + - set + - offsetSet + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->$<_> = $ + filters: + - variable: QUERY + detection: php_lang_http_url_using_sensitive_data_laminas_query + scope: cursor + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::$($<_>, $) + filters: + - variable: CLASS + regex: \A(Illuminate\\Support\\Facades\\)?Http\z + - variable: METHOD + values: + - head + - get + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->$($<_>, $) + filters: + - variable: HTTP + detection: php_lang_http_url_using_sensitive_data_laravel_http + scope: cursor + - variable: METHOD + values: + - head + - get + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::withQueryParameters($) + filters: + - variable: CLASS + regex: \A(Illuminate\\Support\\Facades\\)?Http\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->withQueryParameters($) + filters: + - variable: HTTP + detection: php_lang_http_url_using_sensitive_data_laravel_http + scope: cursor + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::send($<_>, $<_>, $) + filters: + - variable: CLASS + regex: \A(Illuminate\\Support\\Facades\\)?Http\z + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_guzzle_options + scope: cursor + - pattern: $->send($<_>, $<_>, $) + filters: + - variable: HTTP + detection: php_lang_http_url_using_sensitive_data_laravel_http + scope: cursor + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_guzzle_options + scope: cursor + # pecl_http + - pattern: $->$($) + filters: + - variable: REQUEST + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(http\\Client\\)?Request\z + - variable: METHOD + values: + - addQuery + - setQuery + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::$($<_>, $<_>, $$<...>) + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Requests\z + - variable: METHOD + values: + - get + - head + - delete + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::request($<_>, $<_>, $) + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Requests\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::request($<_>, $<_>, $, $$<...>) + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Requests\z + - variable: DATA_TYPE + detection: datatype + scope: result + - variable: TYPE + detection: php_lang_http_url_using_sensitive_data_requests_type + scope: cursor + - pattern: $::request_multiple($$<...>) + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Requests\z + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_requests_options + scope: result + - pattern: new $($<_>, $<_>, $$<...>) + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Session\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->request($<_>, $<_>, $) + filters: + - variable: SESSION + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Session\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $->request($<_>, $<_>, $, $$<...>) + filters: + - variable: SESSION + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Session\z + - variable: DATA_TYPE + detection: datatype + scope: result + - variable: TYPE + detection: php_lang_http_url_using_sensitive_data_requests_type + scope: cursor + - pattern: $->request_multiple($$<...>) + filters: + - variable: SESSION + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Session\z + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_requests_options + scope: result + - pattern: $->$($<_>, $$<...>) + filters: + - variable: CLIENT + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A(SendGrid\\)?Client\z + - variable: METHOD + values: + - get + - post + - patch + - put + - delete + - variable: DATA_TYPE + detection: datatype + scope: result + # symfony + - pattern: $<_>->request($<_>, $<_>, $$<...>) + filters: + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_guzzle_options + scope: cursor + - pattern: $::get($<_>, $<_>, $$<...>) + filters: + - variable: CLASS + regex: \A(Unirest\\)?Request\z + - variable: DATA_TYPE + detection: datatype + scope: result + - pattern: $::send($<_>, $<_>, $<_>, $$<...>) + filters: + - variable: CLASS + regex: \A(Unirest\\)?Request\z + - variable: DATA_TYPE + detection: datatype + scope: result +auxiliary: + - id: php_lang_http_url_using_sensitive_data_guzzle_options + patterns: + - pattern: array('query' => $) + filters: + - variable: DATA_TYPE + detection: datatype + scope: result + - id: php_lang_http_url_using_sensitive_data_laminas_query + patterns: + - pattern: $->getQuery() + filters: + - variable: REQUEST + detection: php_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + regex: \A((Laminas|Zend)\\Http\\)?Request\z + - id: php_lang_http_url_using_sensitive_data_laravel_http + patterns: + - pattern: $::$<_>() + filters: + - variable: CLASS + regex: \A(Illuminate\\Support\\Facades\\)?Http\z + - pattern: $->$<_>() + filters: + - variable: HTTP + detection: php_lang_http_url_using_sensitive_data_laravel_http + scope: cursor + - id: php_lang_http_url_using_sensitive_data_requests_type + patterns: + - pattern: $::$ + filters: + - variable: CLASS + regex: \A(WpOrg\\Requests\\)?Requests\z + - variable: CONSTANT + values: + - GET + - HEAD + - DELETE + - id: php_lang_http_url_using_sensitive_data_requests_options + patterns: + - pattern: $; + filters: + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_requests_options_data + scope: cursor_strict + - either: + - variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_requests_options_type + scope: cursor_strict + - not: + variable: OPTIONS + detection: php_lang_http_url_using_sensitive_data_requests_options_any_type + scope: cursor_strict + - id: php_lang_http_url_using_sensitive_data_requests_options_data + patterns: + - pattern: array('data' => $) + filters: + - variable: DATA_TYPE + detection: datatype + scope: result + - id: php_lang_http_url_using_sensitive_data_requests_options_type + patterns: + - pattern: array('type' => $) + filters: + - variable: TYPE + detection: php_lang_http_url_using_sensitive_data_requests_type + scope: cursor + - id: php_lang_http_url_using_sensitive_data_requests_options_any_type + patterns: + - array('type' => $<_>) +metadata: + description: "Sensitive data detected in HTTP URL." + remediation_message: | + ## Description + Sensitive data should never be sent as part of the URL in HTTP requests. + + ## Remediations + Avoid sending sensitive data in a URL as they can be seen by intermediaries, + or could be logged by applications: + + ❌ Avoid adding sensitive data in paths: + ```php + $curl = curl_init("https://example.com/users/{$user->email}"); + ``` + + ❌ Avoid adding sensitive data in query parameters: + ```php + $query = http_build_query(['email' => $user->email]); + $curl = curl_init("https://example.com/users?$query"); + ``` + + ✅ Use an HTTP POST body if you need to send sensitive data: + + ```php + $query = http_build_query(['email' => $user->email]); + $curl = curl_init("https://example.com/users/list"); + curl_setopt($curl, CURLOPT_POSTFIELDS, $query); + ``` + + ✅ Or avoid sending sending sensitive data altogether: + + ```php + $query = http_build_query(['uuid' => $user->uuid]); + $curl = curl_init("https://example.com/users?$query"); + ``` + +