Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions apps/jobs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,37 @@ def get_remaining_submission_for_a_phase(

def is_url_valid(url):
"""
Checks that a given URL is reachable.
Checks that a given URL is reachable and not an internal/private address.
:param url: A URL
:return type: bool
"""
request = urllib.request.Request(url)
request.get_method = lambda: "HEAD"
try:
urllib.request.urlopen(request)
from urllib.parse import urlparse
import ipaddress
import socket

parsed = urlparse(url)

# Only allow http and https schemes
if parsed.scheme not in ("http", "https"):
return False

hostname = parsed.hostname
if not hostname:
return False

# Resolve hostname and check against private/reserved IP ranges
resolved_ip = socket.gethostbyname(hostname)
ip = ipaddress.ip_address(resolved_ip)
if ip.is_private or ip.is_reserved or ip.is_loopback or ip.is_link_local:
return False

request = urllib.request.Request(url)
request.get_method = lambda: "HEAD"
urllib.request.urlopen(request, timeout=5)
return True
except urllib.request.HTTPError:
except (urllib.request.HTTPError, urllib.request.URLError,
socket.gaierror, ValueError, OSError):
return False


Expand Down
30 changes: 30 additions & 0 deletions apps/jobs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,13 @@ def update_submission(request, challenge_pk):
metadata = request.data.get("metadata", "")
submission = get_submission_model(submission_pk)

# Verify submission belongs to the challenge specified in the URL
if submission.challenge_phase.challenge_id != int(challenge_pk):
response_data = {
"error": "Submission does not belong to this challenge"
}
return Response(response_data, status=status.HTTP_403_FORBIDDEN)

public_results = []
successful_submission = (
True if submission_status == Submission.FINISHED else False
Expand Down Expand Up @@ -1353,6 +1360,14 @@ def update_submission(request, challenge_pk):
if request.method == "PATCH":
submission_pk = request.data.get("submission")
submission = get_submission_model(submission_pk)

# Verify submission belongs to the challenge specified in the URL
if submission.challenge_phase.challenge_id != int(challenge_pk):
response_data = {
"error": "Submission does not belong to this challenge"
}
return Response(response_data, status=status.HTTP_403_FORBIDDEN)

# Update submission_input_file for is_static_dataset_code_upload
# submission evaluation
if (
Expand Down Expand Up @@ -1630,6 +1645,13 @@ def update_partially_evaluated_submission(request, challenge_pk):
metadata = request.data.get("metadata", "")
submission = get_submission_model(submission_pk)

# Verify submission belongs to the challenge specified in the URL
if submission.challenge_phase.challenge_id != int(challenge_pk):
response_data = {
"error": "Submission does not belong to this challenge"
}
return Response(response_data, status=status.HTTP_403_FORBIDDEN)

public_results = []
successful_submission = (
True
Expand Down Expand Up @@ -1798,6 +1820,14 @@ def update_partially_evaluated_submission(request, challenge_pk):
submission_status = request.data.get("submission_status", "").lower()
job_name = request.data.get("job_name", "").lower()
submission = get_submission_model(submission_pk)

# Verify submission belongs to the challenge specified in the URL
if submission.challenge_phase.challenge_id != int(challenge_pk):
response_data = {
"error": "Submission does not belong to this challenge"
}
return Response(response_data, status=status.HTTP_403_FORBIDDEN)

jobs = submission.job_name
if job_name:
jobs.append(job_name)
Expand Down
9 changes: 7 additions & 2 deletions scripts/workers/submission_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,12 +335,17 @@ def extract_challenge_data(challenge, phases):
challenge_data_directory, "requirements.txt"
)
if os.path.isfile(requirements_location):
logger.warning(
"Installing custom requirements for challenge {}. "
"This may pose a security risk.".format(challenge.id)
)
subprocess.check_output(
[
sys.executable,
"-m",
"pip",
"install",
"--no-deps",
"-r",
requirements_location,
]
Expand Down Expand Up @@ -892,7 +897,7 @@ def main():
q_params["pk"] = challenge_pk

if settings.DEBUG or settings.TEST:
if eval(LIMIT_CONCURRENT_SUBMISSION_PROCESSING):
if LIMIT_CONCURRENT_SUBMISSION_PROCESSING and LIMIT_CONCURRENT_SUBMISSION_PROCESSING.lower() in ("true", "1", "yes"):
if not challenge_pk:
logger.exception(
"{} Please add CHALLENGE_PK for the challenge to be loaded in the docker.env file.".format(
Expand Down Expand Up @@ -926,7 +931,7 @@ def main():
):
continue
if settings.DEBUG or settings.TEST:
if eval(LIMIT_CONCURRENT_SUBMISSION_PROCESSING):
if LIMIT_CONCURRENT_SUBMISSION_PROCESSING and LIMIT_CONCURRENT_SUBMISSION_PROCESSING.lower() in ("true", "1", "yes"):
current_running_submissions_count = (
Submission.objects.filter(
challenge_phase__challenge=challenge.id,
Expand Down