Skip to content

Commit

Permalink
feat(python): insecure allow origin (CWE-346) (#425)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapet authored May 30, 2024
1 parent 049e52d commit 867150a
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 0 deletions.
66 changes: 66 additions & 0 deletions rules/python/django/insecure_allow_origin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
imports:
- python_shared_common_user_input
- python_shared_django_http_response
- python_shared_lang_import2
patterns:
- pattern: $<RESPONSE>.headers[$<ALLOW_ORIGIN>] = $<...>$<USER_INPUT>$<...>
filters:
- variable: RESPONSE
detection: python_shared_django_http_response
scope: cursor
- variable: ALLOW_ORIGIN
string_regex: (?i)\Aaccess-control-allow-origin\z
- variable: USER_INPUT
detection: python_shared_common_user_input
scope: result
- pattern: |
$<RESPONSE>($<...> headers={$<...>$<ALLOW_ORIGIN>: $<...>$<USER_INPUT>$<...>} $<...>)
filters:
- variable: RESPONSE
detection: python_shared_lang_import2
scope: cursor
filters:
- variable: MODULE1
values: [django]
- variable: MODULE2
values: [http]
- variable: NAME
values: [HttpResponse]
- variable: ALLOW_ORIGIN
string_regex: (?i)\Aaccess-control-allow-origin\z
- variable: USER_INPUT
detection: python_shared_common_user_input
scope: result
languages:
- python
severity: medium
metadata:
description: Unsanitized user input in Access-Control-Allow-Origin
remediation_message: |-
## Description
Using unverified user-defined input to set the Access-Control-Allow-Origin header can result in unauthorized access to sensitive data. This vulnerability exposes your application to potential security risks by allowing attackers to specify origins that can access resources.
## Remediations
- **Do not** use user input to define the `Access-Control-Allow-Origin` header without validation. This practice can inadvertently grant access to sensitive information.
```python
response.headers['Access-Control-Allow-Origin'] = request.GET["my_origin"] # unsafe
```
- **Do** validate user input if it must be used to set the `Access-Control-Allow-Origin` header. Ensure that only trusted origins are allowed by implementing a safelist of approved origins.
```python
allowed_origins = ['http://www.example.com', 'https://www.secure.example.com']
user_origin = request.GET["my_origin"]
if user_origin in allowed_origins:
response.headers['Access-Control-Allow-Origin'] = user_origin
```
## References
- [OWASP Origin & Access-Control-Allow-Origin](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/07-Testing_Cross_Origin_Resource_Sharing)
cwe_id:
- 346
id: python_django_insecure_allow_origin
documentation_url: https://docs.bearer.com/reference/rules/python_django_insecure_allow_origin
58 changes: 58 additions & 0 deletions rules/python/lang/insecure_allow_origin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
imports:
- python_shared_common_user_input
- python_shared_lang_instance
patterns:
- pattern: $<HANDLER>.send_header($<ALLOW_ORIGIN>, $<...>$<USER_INPUT>$<...>)
filters:
- variable: ALLOW_ORIGIN
string_regex: (?i)\Aaccess-control-allow-origin\z
- variable: USER_INPUT
detection: python_shared_common_user_input
scope: result
- variable: HANDLER
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: [server]
- variable: NAME
values: [BaseHTTPRequestHandler]
languages:
- python
severity: medium
metadata:
description: Unsanitized user input in Access-Control-Allow-Origin
remediation_message: |-
## Description
Using unverified user-defined input to set the Access-Control-Allow-Origin header can result in unauthorized access to sensitive data. This vulnerability exposes your application to potential security risks by allowing attackers to specify origins that can access resources.
## Remediations
- **Do not** use user input to define the `Access-Control-Allow-Origin` header without validation. This practice can inadvertently grant access to sensitive information.
```python
self.send_header('Access-Control-Allow-Origin', external_input) # unsafe
```
- **Do** validate user input if it must be used to set the `Access-Control-Allow-Origin` header. Ensure that only trusted origins are allowed by implementing a safelist of approved origins.
```python
allowed_origins = ['http://www.example.com', 'https://www.secure.example.com']
user_origin = input("Enter origin: ")
if user_origin in allowed_origins:
self.send_header('Access-Control-Allow-Origin', user_origin)
```
## References
- [OWASP Origin & Access-Control-Allow-Origin](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/07-Testing_Cross_Origin_Resource_Sharing)
cwe_id:
- 346
id: python_lang_insecure_allow_origin
documentation_url: https://docs.bearer.com/reference/rules/python_lang_insecure_allow_origin
20 changes: 20 additions & 0 deletions tests/python/django/insecure_allow_origin/test.js
Original file line number Diff line number Diff line change
@@ -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_allow_origin", () => {
const testCase = "main.py"

const results = invoke(testCase)

expect(results).toEqual({
Missing: [],
Extra: []
})
})
})
8 changes: 8 additions & 0 deletions tests/python/django/insecure_allow_origin/testdata/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.http import HttpResponse

response = HttpResponse()
# bearer:expected python_django_insecure_allow_origin
response.headers['access-control-allow-origin'] = request.GET["my_origin"]

# bearer:expected python_django_insecure_allow_origin
HttpResponse(headers={"Access-Control-Allow-Origin": request.GET["my_origin"]})
20 changes: 20 additions & 0 deletions tests/python/lang/insecure_allow_origin/test.js
Original file line number Diff line number Diff line change
@@ -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_allow_origin", () => {
const testCase = "main.py"

const results = invoke(testCase)

expect(results).toEqual({
Missing: [],
Extra: []
})
})
})
9 changes: 9 additions & 0 deletions tests/python/lang/insecure_allow_origin/testdata/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from http.server import BaseHTTPRequestHandler

class SomeClass(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
# bearer:expected python_lang_insecure_allow_origin
self.send_header('Access-Control-Allow-Origin', input())
self.end_headers()

0 comments on commit 867150a

Please sign in to comment.