diff --git a/rules/python/django/insecure_smtp.yml b/rules/python/django/insecure_smtp.yml new file mode 100644 index 00000000..c7e5fa99 --- /dev/null +++ b/rules/python/django/insecure_smtp.yml @@ -0,0 +1,35 @@ +patterns: + - pattern: EMAIL_USE_TLS = $ + filters: + - variable: "FALSE" + detection: python_django_insecure_smtp_false + scope: cursor +auxiliary: + - id: python_django_insecure_smtp_false + patterns: + - "False" +languages: + - python +metadata: + description: "Usage of insecure SMTP connection" + remediation_message: |- + ## Description + + An insecure SMTP connection can expose transmitted data to unauthorized access. This rule identifies if SMTP settings are configured to enforce secure connections. + + ## Remediations + + - **Do** enable SSL encryption in your SMTP configuration to secure the connection. This prevents unauthorized access to the data being transmitted. + ```python + EMAIL_USE_TLS = True + ``` + + ## References + + - [OWASP insecure transport](https://owasp.org/www-community/vulnerabilities/Insecure_Transport) + cwe_id: + - 319 + id: python_django_insecure_smtp + documentation_url: https://docs.bearer.com/reference/rules/python_django_insecure_smtp + cloud_code_suggestions: true +severity: critical diff --git a/rules/python/lang/http_url_using_user_input.yml b/rules/python/lang/http_url_using_user_input.yml index 41765ee6..2baa25ea 100644 --- a/rules/python/lang/http_url_using_user_input.yml +++ b/rules/python/lang/http_url_using_user_input.yml @@ -2,604 +2,17 @@ languages: - python imports: - python_shared_common_user_input - - python_shared_lang_instance - - python_shared_lang_import1 - - python_shared_lang_import2 + - python_shared_lang_http_location patterns: - - pattern: $($<...>$$<...>) + - pattern: $ filters: - - variable: CONNECTION_CLASS - detection: python_shared_lang_import2 - scope: cursor + - variable: USER_INPUT_LOCATION + detection: python_shared_lang_http_location + scope: cursor_strict filters: - - variable: MODULE1 - values: [http] - - variable: MODULE2 - values: [client] - - variable: NAME - values: - - HTTPConnection - - HTTPSConnection - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($<...>$$<...>) - filters: - - variable: CONNECTION - detection: python_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [http] - - variable: MODULE2 - values: [client] - - variable: NAME - values: - - HTTPConnection - - HTTPSConnection - - variable: METHOD - values: - - request - - set_tunnel - - putrequest - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: URLOPEN - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib] - - variable: MODULE2 - values: [request] - - variable: NAME - values: [urlopen] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: REQUEST_CLASS - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib] - - variable: MODULE2 - values: [request] - - variable: NAME - values: [Request] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$ = $ - filters: - - variable: REQUEST - detection: python_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib] - - variable: MODULE2 - values: [request] - - variable: NAME - values: [Request] - - variable: ATTRIBUTE - values: - - full_url - - type - - host - - selector - - method - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: FUNCTION - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [requests] - - variable: NAME - values: - - get - - post - - put - - delete - - head - - options - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<_>, $$<...>) - filters: - - variable: REQUEST - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [requests] - - variable: NAME - values: [request, Request] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($$<...>) - filters: - - variable: SESSION - detection: python_lang_http_url_using_user_input_requests_session - scope: cursor - - variable: METHOD - values: - - get - - post - - put - - delete - - head - - options - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.request($<_>, $$<...>) - filters: - - variable: SESSION - detection: python_lang_http_url_using_user_input_requests_session - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: FUNCTION - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [httpx] - - variable: NAME - values: - - get - - options - - head - - post - - put - - delete - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<_>, $$<...>) - filters: - - variable: FUNCTION - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [httpx] - - variable: NAME - values: - - request - - stream - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<...>base_url=$$<...>) - filters: - - variable: CLIENT_CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [httpx] - - variable: NAME - values: [Client] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($$<...>) - filters: - - variable: CLIENT - detection: python_lang_http_url_using_user_input_httpx_client - scope: cursor - - variable: METHOD - values: - - get - - options - - head - - post - - put - - delete - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($<_>, $$<...>) - filters: - - variable: CLIENT - detection: python_lang_http_url_using_user_input_httpx_client - scope: cursor - - variable: METHOD - values: - - request - - stream - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<_>, $$<...>) - filters: - - variable: FUNCTION - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [aiohttp] - - variable: NAME - values: [request] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: SESSION_CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [aiohttp] - - variable: NAME - values: [ClientSession] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($$<...>) - filters: - - variable: SESSION - detection: python_lang_http_url_using_user_input_aiohttp_session - scope: cursor - - variable: METHOD - values: - - get - - put - - post - - delete - - head - - options - - patch - - ws_connect - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.request($<_>, $$<...>) - filters: - - variable: SESSION - detection: python_lang_http_url_using_user_input_aiohttp_session - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.request($$<...>) - filters: - - variable: HTTP - detection: python_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [httplib2] - - variable: NAME - values: [Http] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<_>, $$<...>) - filters: - - variable: REQUEST - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: NAME - values: [request] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: PROXY_CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: NAME - values: [ProxyManager] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($$<...>) - filters: - - variable: MANAGER - detection: python_lang_http_url_using_user_input_urllib3_manager - scope: cursor - - variable: METHOD - values: - - connection_from_host - - connection_from_url - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($<_>, $$<...>) - filters: - - variable: MANAGER - detection: python_lang_http_url_using_user_input_urllib3_manager - scope: cursor - - variable: METHOD - values: - - connection_from_host - - request - - request_encode_body - - request_encode_url - - urlopen - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.proxy = $ - filters: - - variable: MANAGER - detection: python_lang_http_url_using_user_input_urllib3_manager - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: POOL_CLASS - detection: python_lang_http_url_using_user_input_urllib3_pool_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<_>, $$<...>) - filters: - - variable: POOL_CLASS - detection: python_lang_http_url_using_user_input_urllib3_pool_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: FUNCTION - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: MODULE2 - values: [connectionpool] - - variable: NAME - values: [connection_from_url] - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.urlopen($<_>, $$<...>) - filters: - - variable: POOL - detection: python_lang_http_url_using_user_input_urllib3_pool - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($$<...>) - filters: - - variable: CONNECTION_CLASS - detection: python_lang_http_url_using_user_input_urllib3_connection_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<_>, $$<...>) - filters: - - variable: CONNECTION_CLASS - detection: python_lang_http_url_using_user_input_urllib3_connection_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $($<...>proxy=$$<...>) - filters: - - variable: CONNECTION_CLASS - detection: python_lang_http_url_using_user_input_urllib3_connection_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.$($<_>, $$<...>) - filters: - - variable: CONNECTION - detection: python_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - detection: python_lang_http_url_using_user_input_urllib3_connection_class - scope: cursor - - variable: METHOD - values: - - request - - request_chunked - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.set_tunnel($$<...>) - filters: - - variable: CONNECTION - detection: python_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - detection: python_lang_http_url_using_user_input_urllib3_connection_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result - - pattern: $.set_tunnel($<_>, $$<...>) - filters: - - variable: CONNECTION - detection: python_shared_lang_instance - scope: cursor - filters: - - variable: CLASS - detection: python_lang_http_url_using_user_input_urllib3_connection_class - scope: cursor - - variable: USER_INPUT - detection: python_shared_common_user_input - scope: result -auxiliary: - - id: python_lang_http_url_using_user_input_requests_session - patterns: - - pattern: $ - filters: - - variable: SESSION - detection: python_shared_lang_instance - scope: cursor_strict - filters: - - variable: CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [requests] - - variable: NAME - values: [Session] - - id: python_lang_http_url_using_user_input_httpx_client - patterns: - - pattern: $ - filters: - - variable: CLIENT - detection: python_shared_lang_instance - scope: cursor_strict - filters: - - variable: CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [httpx] - - variable: NAME - values: [Client] - - id: python_lang_http_url_using_user_input_aiohttp_session - patterns: - - pattern: $ - filters: - - variable: SESSION - detection: python_shared_lang_instance - scope: cursor_strict - filters: - - variable: CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [aiohttp] - - variable: NAME - values: [ClientSession] - - id: python_lang_http_url_using_user_input_urllib3_manager - patterns: - - pattern: $ - filters: - - variable: MANAGER - detection: python_shared_lang_instance - scope: cursor_strict - filters: - - variable: CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: NAME - values: - - PoolManager - - ProxyManager - - id: python_lang_http_url_using_user_input_urllib3_pool - patterns: - - pattern: $.$($<...>) - filters: - - variable: MANAGER - detection: python_lang_http_url_using_user_input_urllib3_manager - scope: cursor - - variable: METHOD - values: - - connection_from_context - - connection_from_host - - connection_from_pool_key - - connection_from_url - - pattern: $ - filters: - - variable: POOL - detection: python_shared_lang_instance - scope: cursor_strict - filters: - - variable: CLASS - detection: python_lang_http_url_using_user_input_urllib3_pool_class - scope: cursor - - pattern: $($<...>) - filters: - - variable: FUNCTION - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: MODULE2 - values: [connectionpool] - - variable: NAME - values: [connection_from_url] - - id: python_lang_http_url_using_user_input_urllib3_pool_class - patterns: - - pattern: $ - filters: - - variable: CLASS - detection: python_shared_lang_import1 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: NAME - values: - - HTTPConnectionPool - - HTTPSConnectionPool - - id: python_lang_http_url_using_user_input_urllib3_connection_class - patterns: - - pattern: $ - filters: - - variable: CLASS - detection: python_shared_lang_import2 - scope: cursor - filters: - - variable: MODULE1 - values: [urllib3] - - variable: MODULE2 - values: [connection] - - variable: NAME - values: - - HTTPConnection - - HTTPSConnection + - variable: LOCATION + detection: python_shared_common_user_input + scope: result severity: high metadata: description: "Unsanitized user input in HTTP request (SSRF)" diff --git a/rules/python/lang/insecure_ftp.yml b/rules/python/lang/insecure_ftp.yml new file mode 100644 index 00000000..cb1601b6 --- /dev/null +++ b/rules/python/lang/insecure_ftp.yml @@ -0,0 +1,96 @@ +imports: + - python_shared_lang_instance + - python_shared_lang_import1 +patterns: + - pattern: $($<...>) + filters: + - variable: FTP_CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [ftplib] + - variable: NAME + values: [FTP] + - pattern: $ + filters: + - variable: INSECURE_AIOFTP + detection: python_lang_insecure_ftp_aioftp_init + scope: cursor + - not: + variable: INSECURE_AIOFTP + detection: python_lang_insecure_ftp_aioftp_secure + scope: cursor +auxiliary: + - id: python_lang_insecure_ftp_aioftp_init + patterns: + - pattern: $($<...>) + filters: + - variable: CLASS + detection: python_lang_insecure_ftp_aioftp_client_class + scope: cursor + - pattern: $.context($<...>) + filters: + - variable: CLASS + detection: python_lang_insecure_ftp_aioftp_client_class + scope: cursor + - id: python_lang_insecure_ftp_aioftp_client_class + patterns: + - pattern: $ + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [aioftp] + - variable: NAME + values: [Client] + - id: python_lang_insecure_ftp_aioftp_secure + patterns: + - pattern: $<_>($<...>ssl=$) + filters: + - variable: "TRUE" + detection: python_lang_insecure_ftp_true + scope: cursor + - pattern: $<_>($<...>ssl=$) + filters: + - variable: CONTEXT + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [ssl] + - variable: NAME + values: [SSLContext] + - id: python_lang_insecure_ftp_true + patterns: + - "True" +languages: + - python +severity: critical +metadata: + description: "Usage of insecure FTP connection" + remediation_message: |- + ## Description + + Using insecure FTP connections can compromise the security of sensitive data. This vulnerability arises when applications that handle sensitive information communicate with FTP servers without secure protocols. Always verify that FTP connections in your application utilize SFTP for enhanced security. + + ## Remediations + + - **Do** use the `FTP_TLS` class to establish secure FTP connections. This function ensures that your connection to the FTP server is encrypted, protecting the data transmitted from potential interception or eavesdropping. + ```python + ftp = FTP_TLS("ftp.example.com") + ``` + + ## References + + - [OWASP insecure transport](https://owasp.org/www-community/vulnerabilities/Insecure_Transport) + cwe_id: + - 319 + id: python_lang_insecure_ftp + documentation_url: https://docs.bearer.com/reference/rules/python_lang_insecure_ftp diff --git a/rules/python/lang/insecure_http.yml b/rules/python/lang/insecure_http.yml new file mode 100644 index 00000000..ba19bbec --- /dev/null +++ b/rules/python/lang/insecure_http.yml @@ -0,0 +1,41 @@ +languages: + - python +imports: + - python_shared_lang_http_location + - python_shared_lang_insecure_url +patterns: + - pattern: $ + filters: + - variable: INSECURE_LOCATION + detection: python_shared_lang_http_location + scope: cursor_strict + filters: + - variable: LOCATION + detection: python_shared_lang_insecure_url + scope: result +severity: critical +metadata: + description: "Usage of insecure HTTP connection" + remediation_message: |- + ## Description + + Your application is at risk when it connects to APIs using insecure HTTP connections. This vulnerability occurs because HTTP lacks encryption, making data susceptible to interception and alteration. Always verify that your application exclusively uses HTTPS connections for enhanced security. + + ## Remediations + + - **Do not** initiate connections using unsecured HTTP. This exposes your data to potential interception and manipulation. + ```python + urllib.request.urlopen("http://insecure-api.com") # unsafe + ``` + - **Do** ensure all connections are made through HTTPS to encrypt data and protect against eavesdropping and tampering. + ```python + urllib.request.urlopen("https://secure-api.com") + ``` + + ## References + + - [OWASP insecure transport](https://owasp.org/www-community/vulnerabilities/Insecure_Transport) + cwe_id: + - 319 + id: python_lang_insecure_http + documentation_url: https://docs.bearer.com/reference/rules/python_lang_insecure_http diff --git a/rules/python/lang/insecure_smtp.yml b/rules/python/lang/insecure_smtp.yml new file mode 100644 index 00000000..6ca1fd42 --- /dev/null +++ b/rules/python/lang/insecure_smtp.yml @@ -0,0 +1,40 @@ +imports: + - python_shared_lang_import1 +patterns: + - pattern: $($<...>) + filters: + - variable: SMTP_CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [smtplib] + - variable: NAME + values: + - SMTP + - LMTP +languages: + - python +metadata: + description: "Usage of insecure SMTP connection" + remediation_message: |- + ## Description + + An insecure SMTP connection can expose transmitted data to unauthorized access. This rule identifies if SMTP settings are configured to enforce secure connections. + + ## Remediations + + - **Do** use the `SMTP_SSL` class to establish secure SMTP connections. This prevents unauthorized access to the data being transmitted. + ```python + smtp = smtplib.SMTP_SSL("smtp.example.com") + ``` + + ## References + + - [OWASP insecure transport](https://owasp.org/www-community/vulnerabilities/Insecure_Transport) + cwe_id: + - 319 + id: python_lang_insecure_smtp + documentation_url: https://docs.bearer.com/reference/rules/python_lang_insecure_smtp + cloud_code_suggestions: true +severity: critical diff --git a/rules/python/lang/insecure_websocket.yml b/rules/python/lang/insecure_websocket.yml new file mode 100644 index 00000000..8b5ed42e --- /dev/null +++ b/rules/python/lang/insecure_websocket.yml @@ -0,0 +1,92 @@ +languages: + - python +imports: + - python_shared_lang_insecure_url + - python_shared_lang_instance + - python_shared_lang_import1 + - python_shared_lang_import2 + - python_shared_lang_import3 +patterns: + - pattern: $($$<...>) + filters: + - either: + - variable: CONNECT + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [websockets] + - variable: NAME + values: [connect] + - variable: CONNECT + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [websockets] + - variable: MODULE2 + values: [client] + - variable: NAME + values: [connect] + - variable: CONNECT + detection: python_shared_lang_import3 + scope: cursor + filters: + - variable: MODULE1 + values: [websockets] + - variable: MODULE2 + values: [sync] + - variable: MODULE3 + values: [client] + - variable: NAME + values: [connect] + - variable: INSECURE_URL + detection: python_shared_lang_insecure_url + scope: cursor + - pattern: $.connect($$<...>) + filters: + - variable: CLIENT + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [socketio] + - variable: NAME + values: + - Client + - AsyncClient + - SimpleClient + - AsyncSimpleClient + - variable: INSECURE_URL + detection: python_shared_lang_insecure_url + scope: cursor +severity: critical +metadata: + description: "Usage of insecure websocket connection" + remediation_message: |- + ## Description + + Using insecure websocket connections can compromise data security. This vulnerability arises when applications connect to APIs without SSL, making the data susceptible to interception and manipulation. + + ## Remediations + + - **Do not** use unsecured websocket connections. These connections lack encryption, putting data at risk. + ```python + websockets.connect("ws://insecure-api.com") # unsafe + ``` + - **Do** ensure all websocket connections are secured with SSL. This encrypts the data, protecting it from unauthorized access. + ```python + websockets.connect("wss://secure-api.com") + ``` + + ## References + + - [OWASP insecure transport](https://owasp.org/www-community/vulnerabilities/Insecure_Transport) + cwe_id: + - 319 + id: python_lang_insecure_websocket + documentation_url: https://docs.bearer.com/reference/rules/python_lang_insecure_websocket diff --git a/rules/python/shared/lang/http_location.yml b/rules/python/shared/lang/http_location.yml new file mode 100644 index 00000000..66f0fc1f --- /dev/null +++ b/rules/python/shared/lang/http_location.yml @@ -0,0 +1,503 @@ +type: shared +imports: + - python_shared_lang_instance + - python_shared_lang_import1 + - python_shared_lang_import2 +patterns: + - pattern: $($<...>$$<...>) + filters: + - variable: CONNECTION_CLASS + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [http] + - variable: MODULE2 + values: [client] + - variable: NAME + values: + - HTTPConnection + - HTTPSConnection + - pattern: $.$($<...>$$<...>) + filters: + - variable: CONNECTION + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [http] + - variable: MODULE2 + values: [client] + - variable: NAME + values: + - HTTPConnection + - HTTPSConnection + - variable: METHOD + values: + - request + - set_tunnel + - putrequest + - pattern: $($$<...>) + filters: + - variable: URLOPEN + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib] + - variable: MODULE2 + values: [request] + - variable: NAME + values: [urlopen] + - pattern: $($$<...>) + filters: + - variable: REQUEST_CLASS + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib] + - variable: MODULE2 + values: [request] + - variable: NAME + values: [Request] + - pattern: $.$ = $ + filters: + - variable: REQUEST + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib] + - variable: MODULE2 + values: [request] + - variable: NAME + values: [Request] + - variable: ATTRIBUTE + values: + - full_url + - type + - host + - selector + - method + - pattern: $($$<...>) + filters: + - variable: FUNCTION + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [requests] + - variable: NAME + values: + - get + - post + - put + - delete + - head + - options + - pattern: $($<_>, $$<...>) + filters: + - variable: REQUEST + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [requests] + - variable: NAME + values: [request, Request] + - pattern: $.$($$<...>) + filters: + - variable: SESSION + detection: python_shared_lang_http_location_requests_session + scope: cursor + - variable: METHOD + values: + - get + - post + - put + - delete + - head + - options + - pattern: $.request($<_>, $$<...>) + filters: + - variable: SESSION + detection: python_shared_lang_http_location_requests_session + scope: cursor + - pattern: $($$<...>) + filters: + - variable: FUNCTION + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [httpx] + - variable: NAME + values: + - get + - options + - head + - post + - put + - delete + - pattern: $($<_>, $$<...>) + filters: + - variable: FUNCTION + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [httpx] + - variable: NAME + values: + - request + - stream + - pattern: $($<...>base_url=$$<...>) + filters: + - variable: CLIENT_CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [httpx] + - variable: NAME + values: [Client] + - pattern: $.$($$<...>) + filters: + - variable: CLIENT + detection: python_shared_lang_http_location_httpx_client + scope: cursor + - variable: METHOD + values: + - get + - options + - head + - post + - put + - delete + - pattern: $.$($<_>, $$<...>) + filters: + - variable: CLIENT + detection: python_shared_lang_http_location_httpx_client + scope: cursor + - variable: METHOD + values: + - request + - stream + - pattern: $($<_>, $$<...>) + filters: + - variable: FUNCTION + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [aiohttp] + - variable: NAME + values: [request] + - pattern: $($$<...>) + filters: + - variable: SESSION_CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [aiohttp] + - variable: NAME + values: [ClientSession] + - pattern: $.$($$<...>) + filters: + - variable: SESSION + detection: python_shared_lang_http_location_aiohttp_session + scope: cursor + - variable: METHOD + values: + - get + - put + - post + - delete + - head + - options + - patch + - ws_connect + - pattern: $.request($<_>, $$<...>) + filters: + - variable: SESSION + detection: python_shared_lang_http_location_aiohttp_session + scope: cursor + - pattern: $.request($$<...>) + filters: + - variable: HTTP + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [httplib2] + - variable: NAME + values: [Http] + - pattern: $($<_>, $$<...>) + filters: + - variable: REQUEST + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: NAME + values: [request] + - pattern: $($$<...>) + filters: + - variable: PROXY_CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: NAME + values: [ProxyManager] + - pattern: $.$($$<...>) + filters: + - variable: MANAGER + detection: python_shared_lang_http_location_urllib3_manager + scope: cursor + - variable: METHOD + values: + - connection_from_host + - connection_from_url + - pattern: $.$($<_>, $$<...>) + filters: + - variable: MANAGER + detection: python_shared_lang_http_location_urllib3_manager + scope: cursor + - variable: METHOD + values: + - connection_from_host + - request + - request_encode_body + - request_encode_url + - urlopen + - pattern: $.proxy = $ + filters: + - variable: MANAGER + detection: python_shared_lang_http_location_urllib3_manager + scope: cursor + - pattern: $($$<...>) + filters: + - variable: POOL_CLASS + detection: python_shared_lang_http_location_urllib3_pool_class + scope: cursor + - pattern: $($<_>, $$<...>) + filters: + - variable: POOL_CLASS + detection: python_shared_lang_http_location_urllib3_pool_class + scope: cursor + - pattern: $($$<...>) + filters: + - variable: FUNCTION + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: MODULE2 + values: [connectionpool] + - variable: NAME + values: [connection_from_url] + - pattern: $.urlopen($<_>, $$<...>) + filters: + - variable: POOL + detection: python_shared_lang_http_location_urllib3_pool + scope: cursor + - pattern: $($$<...>) + filters: + - variable: CONNECTION_CLASS + detection: python_shared_lang_http_location_urllib3_connection_class + scope: cursor + - pattern: $($<_>, $$<...>) + filters: + - variable: CONNECTION_CLASS + detection: python_shared_lang_http_location_urllib3_connection_class + scope: cursor + - pattern: $($<...>proxy=$$<...>) + filters: + - variable: CONNECTION_CLASS + detection: python_shared_lang_http_location_urllib3_connection_class + scope: cursor + - pattern: $.$($<_>, $$<...>) + filters: + - variable: CONNECTION + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_http_location_urllib3_connection_class + scope: cursor + - variable: METHOD + values: + - request + - request_chunked + - pattern: $.set_tunnel($$<...>) + filters: + - variable: CONNECTION + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_http_location_urllib3_connection_class + scope: cursor + - pattern: $.set_tunnel($<_>, $$<...>) + filters: + - variable: CONNECTION + detection: python_shared_lang_instance + scope: cursor + filters: + - variable: CLASS + detection: python_shared_lang_http_location_urllib3_connection_class + scope: cursor +auxiliary: + - id: python_shared_lang_http_location_requests_session + patterns: + - pattern: $ + filters: + - variable: SESSION + detection: python_shared_lang_instance + scope: cursor_strict + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [requests] + - variable: NAME + values: [Session] + - id: python_shared_lang_http_location_httpx_client + patterns: + - pattern: $ + filters: + - variable: CLIENT + detection: python_shared_lang_instance + scope: cursor_strict + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [httpx] + - variable: NAME + values: [Client] + - id: python_shared_lang_http_location_aiohttp_session + patterns: + - pattern: $ + filters: + - variable: SESSION + detection: python_shared_lang_instance + scope: cursor_strict + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [aiohttp] + - variable: NAME + values: [ClientSession] + - id: python_shared_lang_http_location_urllib3_manager + patterns: + - pattern: $ + filters: + - variable: MANAGER + detection: python_shared_lang_instance + scope: cursor_strict + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: NAME + values: + - PoolManager + - ProxyManager + - id: python_shared_lang_http_location_urllib3_pool + patterns: + - pattern: $.$($<...>) + filters: + - variable: MANAGER + detection: python_shared_lang_http_location_urllib3_manager + scope: cursor + - variable: METHOD + values: + - connection_from_context + - connection_from_host + - connection_from_pool_key + - connection_from_url + - pattern: $ + filters: + - variable: POOL + detection: python_shared_lang_instance + scope: cursor_strict + filters: + - variable: CLASS + detection: python_shared_lang_http_location_urllib3_pool_class + scope: cursor + - pattern: $($<...>) + filters: + - variable: FUNCTION + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: MODULE2 + values: [connectionpool] + - variable: NAME + values: [connection_from_url] + - id: python_shared_lang_http_location_urllib3_pool_class + patterns: + - pattern: $ + filters: + - variable: CLASS + detection: python_shared_lang_import1 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: NAME + values: + - HTTPConnectionPool + - HTTPSConnectionPool + - id: python_shared_lang_http_location_urllib3_connection_class + patterns: + - pattern: $ + filters: + - variable: CLASS + detection: python_shared_lang_import2 + scope: cursor + filters: + - variable: MODULE1 + values: [urllib3] + - variable: MODULE2 + values: [connection] + - variable: NAME + values: + - HTTPConnection + - HTTPSConnection +languages: + - python +metadata: + description: "Python HTTP request URL, host, port, path, etc." + id: python_shared_lang_http_location diff --git a/rules/python/shared/lang/insecure_url.yml b/rules/python/shared/lang/insecure_url.yml new file mode 100644 index 00000000..f0b07525 --- /dev/null +++ b/rules/python/shared/lang/insecure_url.yml @@ -0,0 +1,14 @@ +type: shared +languages: + - python +patterns: + - pattern: $ + filters: + - variable: INSECURE_URL + string_regex: '\A(ws|http):' + - not: + variable: INSECURE_URL + string_regex: '\A(ws|http)://(localhost|127\.0\.0\.1)' +metadata: + description: "Python insecure URL." + id: python_shared_lang_insecure_url diff --git a/tests/python/django/insecure_smtp/test.js b/tests/python/django/insecure_smtp/test.js new file mode 100644 index 00000000..2927ea11 --- /dev/null +++ b/tests/python/django/insecure_smtp/test.js @@ -0,0 +1,20 @@ +const { + createNewInvoker, + getEnvironment, +} = require("../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createNewInvoker(ruleId, ruleFile, testBase) + + test("insecure_smtp", () => { + const testCase = "settings.py" + + const results = invoke(testCase) + + expect(results).toEqual({ + Missing: [], + Extra: [] + }) + }) +}) \ No newline at end of file diff --git a/tests/python/django/insecure_smtp/testdata/settings.py b/tests/python/django/insecure_smtp/testdata/settings.py new file mode 100644 index 00000000..a09cbe1f --- /dev/null +++ b/tests/python/django/insecure_smtp/testdata/settings.py @@ -0,0 +1,3 @@ +EMAIL_USE_TLS = True +# bearer:expected python_django_insecure_smtp +EMAIL_USE_TLS = False diff --git a/tests/python/lang/http_url_using_user_input/testdata/main.py b/tests/python/lang/http_url_using_user_input/testdata/main.py index d3108d82..9934a667 100644 --- a/tests/python/lang/http_url_using_user_input/testdata/main.py +++ b/tests/python/lang/http_url_using_user_input/testdata/main.py @@ -1,3 +1,5 @@ +# this file is also testing the http location shared rule + user_input = input() def http_client(): diff --git a/tests/python/lang/insecure_ftp/test.js b/tests/python/lang/insecure_ftp/test.js new file mode 100644 index 00000000..636b970d --- /dev/null +++ b/tests/python/lang/insecure_ftp/test.js @@ -0,0 +1,20 @@ +const { + createNewInvoker, + getEnvironment, +} = require("../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createNewInvoker(ruleId, ruleFile, testBase) + + test("insecure_ftp", () => { + const testCase = "main.py" + + const results = invoke(testCase) + + expect(results).toEqual({ + Missing: [], + Extra: [] + }) + }) +}) \ No newline at end of file diff --git a/tests/python/lang/insecure_ftp/testdata/main.py b/tests/python/lang/insecure_ftp/testdata/main.py new file mode 100644 index 00000000..c6f00bbb --- /dev/null +++ b/tests/python/lang/insecure_ftp/testdata/main.py @@ -0,0 +1,30 @@ +def ftplib(): + from ftplib import FTP, FTP_TLS + + ftp = FTP_TLS('ftp.example.com') + # bearer:expected python_lang_insecure_ftp + ftp = FTP('ftp.example.com') + + +def aioftp(): + import ssl + import aioftp + + client = aioftp.Client("ftp.example.com", ssl=True) + client = aioftp.Client("ftp.example.com", ssl=ssl.SSLContext()) + # bearer:expected python_lang_insecure_ftp + client = aioftp.Client("ftp.example.com") + # bearer:expected python_lang_insecure_ftp + client = aioftp.Client("ftp.example.com", ssl=False) + # bearer:expected python_lang_insecure_ftp + client = aioftp.Client("ftp.example.com", ssl=unknown) + + aioftp.Client.context("ftp.example.com", ssl=True) + aioftp.Client.context("ftp.example.com", ssl=ssl.SSLContext()) + # bearer:expected python_lang_insecure_ftp + aioftp.Client.context("ftp.example.com") + # bearer:expected python_lang_insecure_ftp + aioftp.Client.context("ftp.example.com", ssl=False) + # bearer:expected python_lang_insecure_ftp + aioftp.Client.context("ftp.example.com", ssl=unknown) + \ No newline at end of file diff --git a/tests/python/lang/insecure_http/test.js b/tests/python/lang/insecure_http/test.js new file mode 100644 index 00000000..bebdc7f4 --- /dev/null +++ b/tests/python/lang/insecure_http/test.js @@ -0,0 +1,20 @@ +const { + createNewInvoker, + getEnvironment, +} = require("../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createNewInvoker(ruleId, ruleFile, testBase) + + test("http_insecure", () => { + const testCase = "main.py" + + const results = invoke(testCase) + + expect(results).toEqual({ + Missing: [], + Extra: [] + }) + }) +}) \ No newline at end of file diff --git a/tests/python/lang/insecure_http/testdata/main.py b/tests/python/lang/insecure_http/testdata/main.py new file mode 100644 index 00000000..6e39be8a --- /dev/null +++ b/tests/python/lang/insecure_http/testdata/main.py @@ -0,0 +1,11 @@ +import http.client + +user_input = input() + +conn = http.client.HTTPSConnection("https://api.example.com") +# bearer:expected python_lang_insecure_http +conn = http.client.HTTPSConnection("http://api.example.com") + +conn = http.client.HTTPSConnection("wss://api.example.com") +# bearer:expected python_lang_insecure_http +conn = http.client.HTTPSConnection("ws://api.example.com") diff --git a/tests/python/lang/insecure_smtp/test.js b/tests/python/lang/insecure_smtp/test.js new file mode 100644 index 00000000..c2d19a1b --- /dev/null +++ b/tests/python/lang/insecure_smtp/test.js @@ -0,0 +1,20 @@ +const { + createNewInvoker, + getEnvironment, +} = require("../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createNewInvoker(ruleId, ruleFile, testBase) + + test("insecure_smtp", () => { + const testCase = "main.py" + + const results = invoke(testCase) + + expect(results).toEqual({ + Missing: [], + Extra: [] + }) + }) +}) \ No newline at end of file diff --git a/tests/python/lang/insecure_smtp/testdata/main.py b/tests/python/lang/insecure_smtp/testdata/main.py new file mode 100644 index 00000000..39da317e --- /dev/null +++ b/tests/python/lang/insecure_smtp/testdata/main.py @@ -0,0 +1,7 @@ +import smtplib + +smtp = smtplib.SMTP_SSL('smtp.example.com') +# bearer:expected python_lang_insecure_smtp +smtp = smtplib.SMTP('smtp.example.com') +# bearer:expected python_lang_insecure_smtp +smtp = smtplib.LMTP('smtp.example.com') \ No newline at end of file diff --git a/tests/python/lang/insecure_websocket/test.js b/tests/python/lang/insecure_websocket/test.js new file mode 100644 index 00000000..d608a7e2 --- /dev/null +++ b/tests/python/lang/insecure_websocket/test.js @@ -0,0 +1,20 @@ +const { + createNewInvoker, + getEnvironment, +} = require("../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createNewInvoker(ruleId, ruleFile, testBase) + + test("insecure_websocket", () => { + const testCase = "main.py" + + const results = invoke(testCase) + + expect(results).toEqual({ + Missing: [], + Extra: [] + }) + }) +}) \ No newline at end of file diff --git a/tests/python/lang/insecure_websocket/testdata/main.py b/tests/python/lang/insecure_websocket/testdata/main.py new file mode 100644 index 00000000..cd71ad4d --- /dev/null +++ b/tests/python/lang/insecure_websocket/testdata/main.py @@ -0,0 +1,40 @@ +async def websockets(): + import websockets + + await websockets.connect("wss://example.com") + # bearer:expected python_lang_insecure_websocket + await websockets.connect("ws://example.com") + + from websockets.client import connect as connect_async + from websockets.sync.client import connect as connect_sync + + connect_sync("wss://example.com") + await connect_async("wss://example.com") + # bearer:expected python_lang_insecure_websocket + connect_sync("ws://example.com") + # bearer:expected python_lang_insecure_websocket + await connect_async("ws://example.com") + + +def socketio(): + import socketio + + sio = socketio.Client() + sio.connect("wss://example.com") + # bearer:expected python_lang_insecure_websocket + sio.connect("ws://example.com") + + sio2 = socketio.AsyncClient() + await sio2.connect("wss://example.com") + # bearer:expected python_lang_insecure_websocket + await sio2.connect("ws://example.com") + + sio3 = socketio.SimpleClient() + sio3.connect("wss://example.com") + # bearer:expected python_lang_insecure_websocket + sio3.connect("ws://example.com") + + sio4 = socketio.AsyncSimpleClient() + await sio4.connect("wss://example.com") + # bearer:expected python_lang_insecure_websocket + await sio4.connect("ws://example.com")