Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Django upgrade 5 #2777

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open

Django upgrade 5 #2777

wants to merge 48 commits into from

Conversation

ukanga
Copy link
Member

@ukanga ukanga commented Feb 10, 2025

Changes / Features implemented

Steps taken to verify this change does what is intended

Side effects of implementing this change

Before submitting this PR for review, please make sure you have:

  • Included tests
  • Updated documentation

Closes #

Comment on lines +415 to +419
response = requests.head(
cleaned_url,
allow_redirects=True,
timeout=DEFAULT_REQUEST_TIMEOUT,
)

Check failure

Code scanning / CodeQL

Full server-side request forgery Critical

The full URL of this request depends on a
user-provided value
.
The full URL of this request depends on a
user-provided value
.
The full URL of this request depends on a
user-provided value
.

Copilot Autofix AI 19 days ago

To fix the SSRF vulnerability, we need to ensure that the user-provided URL is properly validated before being used in HTTP requests. One way to achieve this is by using a whitelist of allowed domains and ensuring that the user-provided URL belongs to one of these domains. This approach prevents the user from directing the request to an unintended server.

  1. Define a list of allowed domains.
  2. Parse the user-provided URL and check if its domain is in the allowed list.
  3. Only proceed with the HTTP request if the domain is valid.
Suggested changeset 1
onadata/apps/main/forms.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/onadata/apps/main/forms.py b/onadata/apps/main/forms.py
--- a/onadata/apps/main/forms.py
+++ b/onadata/apps/main/forms.py
@@ -19,2 +19,3 @@
 import requests
+from urllib.parse import urlparse
 from registration.forms import RegistrationFormUniqueEmail
@@ -407,5 +408,9 @@
 
+            allowed_domains = ["example.com", "anotherdomain.com"]
             if cleaned_url:
                 self.validate(cleaned_url)
-                cleaned_xls_file = urlparse(cleaned_url)
+                parsed_url = urlparse(cleaned_url)
+                if parsed_url.netloc not in allowed_domains:
+                    raise forms.ValidationError(_("Invalid URL domain."))
+                cleaned_xls_file = parsed_url
                 cleaned_xls_file = "_".join(cleaned_xls_file.path.split("/")[-2:])
EOF
@@ -19,2 +19,3 @@
import requests
from urllib.parse import urlparse
from registration.forms import RegistrationFormUniqueEmail
@@ -407,5 +408,9 @@

allowed_domains = ["example.com", "anotherdomain.com"]
if cleaned_url:
self.validate(cleaned_url)
cleaned_xls_file = urlparse(cleaned_url)
parsed_url = urlparse(cleaned_url)
if parsed_url.netloc not in allowed_domains:
raise forms.ValidationError(_("Invalid URL domain."))
cleaned_xls_file = parsed_url
cleaned_xls_file = "_".join(cleaned_xls_file.path.split("/")[-2:])
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
@@ -420,7 +425,7 @@
cleaned_xls_file = get_filename(response)

cleaned_xls_file = upload_to(None, cleaned_xls_file, user.username)
response = requests.get(cleaned_url)
response = requests.get(cleaned_url, timeout=DEFAULT_REQUEST_TIMEOUT)

Check failure

Code scanning / CodeQL

Full server-side request forgery Critical

The full URL of this request depends on a
user-provided value
.
The full URL of this request depends on a
user-provided value
.
The full URL of this request depends on a
user-provided value
.

Copilot Autofix AI 19 days ago

To fix the problem, we need to ensure that the user-provided URL is properly validated before being used in an HTTP request. One way to do this is to use a whitelist of allowed domains or perform strict validation to ensure the URL points to a trusted domain.

The best way to fix this without changing existing functionality is to add a validation step that checks if the URL belongs to a trusted domain. We can use the URLValidator from Django to validate the URL format and then check the domain against a whitelist.

Suggested changeset 1
onadata/apps/main/forms.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/onadata/apps/main/forms.py b/onadata/apps/main/forms.py
--- a/onadata/apps/main/forms.py
+++ b/onadata/apps/main/forms.py
@@ -24,2 +24,3 @@
 from onadata.apps.logger.models import Project
+
 from onadata.apps.main.models import UserProfile
@@ -408,3 +409,3 @@
             if cleaned_url:
-                self.validate(cleaned_url)
+                if not is_valid_url(cleaned_url):
                 cleaned_xls_file = urlparse(cleaned_url)
EOF
@@ -24,2 +24,3 @@
from onadata.apps.logger.models import Project

from onadata.apps.main.models import UserProfile
@@ -408,3 +409,3 @@
if cleaned_url:
self.validate(cleaned_url)
if not is_valid_url(cleaned_url):
cleaned_xls_file = urlparse(cleaned_url)
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
@@ -157,7 +159,9 @@
filename = media.data_value.split("/")[-1]
data_file = NamedTemporaryFile()
content_type = mimetypes.guess_type(filename)
with closing(requests.get(media.data_value, stream=True)) as resp:
with closing(
requests.get(media.data_value, stream=True, timeout=DEFAULT_REQUEST_TIMEOUT)

Check failure

Code scanning / CodeQL

Full server-side request forgery Critical

The full URL of this request depends on a
user-provided value
.

Copilot Autofix AI 18 days ago

To fix the problem, we need to ensure that the URL used in the requests.get method is validated against a whitelist of allowed domains. This can be achieved by implementing a function that checks if the URL's domain is in a predefined list of allowed domains before making the request.

  1. Define a list of allowed domains.
  2. Implement a function to validate the URL against this list.
  3. Use this function to validate media.data_value before making the request.
Suggested changeset 1
onadata/apps/main/models/meta_data.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/onadata/apps/main/models/meta_data.py b/onadata/apps/main/models/meta_data.py
--- a/onadata/apps/main/models/meta_data.py
+++ b/onadata/apps/main/models/meta_data.py
@@ -28,2 +28,3 @@
 import requests
+from urllib.parse import urlparse
 
@@ -155,5 +156,12 @@
 
+ALLOWED_DOMAINS = ["example.com", "anotherdomain.com"]
+
+def is_allowed_domain(url):
+    """Check if the URL's domain is in the allowed list."""
+    parsed_url = urlparse(url)
+    return parsed_url.netloc in ALLOWED_DOMAINS
+
 def create_media(media):
     """Download media link"""
-    if is_valid_url(media.data_value):
+    if is_valid_url(media.data_value) and is_allowed_domain(media.data_value):
         filename = media.data_value.split("/")[-1]
EOF
@@ -28,2 +28,3 @@
import requests
from urllib.parse import urlparse

@@ -155,5 +156,12 @@

ALLOWED_DOMAINS = ["example.com", "anotherdomain.com"]

def is_allowed_domain(url):
"""Check if the URL's domain is in the allowed list."""
parsed_url = urlparse(url)
return parsed_url.netloc in ALLOWED_DOMAINS

def create_media(media):
"""Download media link"""
if is_valid_url(media.data_value):
if is_valid_url(media.data_value) and is_allowed_domain(media.data_value):
filename = media.data_value.split("/")[-1]
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options

# get exact choices element from choice abbreviated xpath
fruita_o = xform.get_survey_element("a/fruita/orange")
self.assertEqual(fruita_o.get_abbreviated_xpath(), "a/fruita/orange")
# fruita_o = xform.get_survey_element("a/fruita/orange")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ukanga Why were these tests commented?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants