diff --git a/website/templates/base.html b/website/templates/base.html
index f1c8bad8c2..d1619ba589 100644
--- a/website/templates/base.html
+++ b/website/templates/base.html
@@ -288,6 +288,7 @@
}
});
});
+
// Check if the modal exists in the DOM
const modal = document.getElementById('cardModalDailyStatus');
@@ -319,6 +320,7 @@
}
}
+
// Function to hide modal
function hideModal() {
if (!modal) return;
@@ -326,6 +328,7 @@
document.body.classList.remove('modal-open'); // Restore background scrolling
}
+\
// Event listener to close modal when clicking the close button
closeButton.addEventListener('click', hideModal);
@@ -346,6 +349,7 @@
openCheckInButton.addEventListener('click', showModal);
}
+
// Handle check-in form submission with AJAX
if (checkInForm) {
checkInForm.addEventListener('submit', function(e) {
diff --git a/website/views/core.py b/website/views/core.py
index b04bec3d05..f8b2dbfdb9 100644
--- a/website/views/core.py
+++ b/website/views/core.py
@@ -1,8 +1,10 @@
+import ipaddress
import argparse
import json
import logging
import os
import re
+import socket
import subprocess
import tracemalloc
import urllib
@@ -27,7 +29,7 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
-from django.core.exceptions import FieldError
+from django.core.exceptions import FieldError, ValidationError
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.core.management import call_command, get_commands, load_command_class
@@ -1536,23 +1538,50 @@ def sync_github_projects(request):
def check_owasp_compliance(request):
- """
- View to check OWASP project compliance with guidelines.
- Combines form and results in a single template.
- """
if request.method == "POST":
url = request.POST.get("url", "").strip()
if not url:
messages.error(request, "Please provide a valid URL")
return redirect("check_owasp_compliance")
+ # Validate URL format and accessibility
try:
+ if not url.startswith(("http://", "https://")):
+ url = "https://" + url
+
+ parsed = urlparse(url)
+ hostname = parsed.hostname
+ if not hostname:
+ raise ValidationError("Invalid hostname")
+
+ # Prevent SSRF by validating hostname
+ addr_info = socket.getaddrinfo(hostname, None)
+ for info in addr_info:
+ ip = info[4][0]
+ ip_obj = ipaddress.ip_address(ip)
+ if (
+ ip_obj.is_private
+ or ip_obj.is_loopback
+ or ip_obj.is_reserved
+ or ip_obj.is_multicast
+ or ip_obj.is_link_local
+ or ip_obj.is_unspecified
+ ):
+ raise ValidationError("Invalid IP address")
+
+ # Create safe URL with only scheme, netloc and path
+ safe_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
+
# Parse URL to determine if it's a GitHub repository
- is_github = "github.com" in url.lower()
- is_owasp_org = "github.com/owasp" in url.lower()
+ is_github = hostname and (hostname == "github.com" or hostname.endswith(".github.com"))
+ is_owasp_org = (
+ hostname
+ and (hostname == "github.com" or hostname.endswith(".github.com"))
+ and "/owasp" in parsed.path.lower()
+ )
- # Fetch and analyze website content
- response = requests.get(url, timeout=10, verify=False)
+ # Fetch and analyze website content with proper verification
+ response = requests.get(safe_url, timeout=10, verify=True)
soup = BeautifulSoup(response.text.lower(), "html.parser")
content = soup.get_text().lower()
@@ -1612,10 +1641,12 @@ def check_owasp_compliance(request):
return render(request, "check_owasp_compliance.html", context)
- except requests.RequestException as e:
- messages.error(request, f"Error accessing the URL: {str(e)}. Please check if the URL is accessible.")
- except Exception as e:
- messages.error(request, f"Error checking compliance: {str(e)}. Please try again.")
+ except (ValidationError, socket.gaierror, ValueError) as e:
+ messages.error(request, "Invalid or inaccessible URL provided")
+ return redirect("check_owasp_compliance")
+ except requests.exceptions.RequestException:
+ messages.error(request, "Could not access the provided URL")
+ return redirect("check_owasp_compliance")
return render(request, "check_owasp_compliance.html")