From e36f55a169eebcabd98a43f127df584bbb5e36a2 Mon Sep 17 00:00:00 2001 From: Joseph Katsioloudes Date: Thu, 16 Jan 2025 16:47:57 +0000 Subject: [PATCH] Improves season 1 level 3 solution (#113) * Reversing Season 2 level-3 with level-4 due to another initiative we are working on. * adds mocha * fixes template reference * replaces executable * improves the solution in Season-1/Level-3 * Potential fix for code scanning alert no. 46: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- Season-1/Level-3/solution.py | 87 +++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/Season-1/Level-3/solution.py b/Season-1/Level-3/solution.py index 2a2de8c..b472604 100644 --- a/Season-1/Level-3/solution.py +++ b/Season-1/Level-3/solution.py @@ -1,34 +1,61 @@ -import os +# Model solution follows -# Example of a secure function that doesn't suffer from path traversal -def safe_path(path): - base_dir = os.path.dirname(os.path.abspath(__file__)) - filepath = os.path.normpath(os.path.join(base_dir, path)) - if base_dir != os.path.commonpath([base_dir, filepath]): - return None - return filepath - -# Following the above, this is the secure version of the respective function on code.py -def get_prof_picture(self, path=None): - # setting a profile picture is optional - if not path: - pass - - # defends against path traversal attacks - if path.startswith('/') or path.startswith('..'): - return None - - # builds path - base_dir = os.path.dirname(os.path.abspath(__file__)) - prof_picture_path = os.path.normpath(os.path.join(base_dir, path)) - if base_dir != os.path.commonpath([base_dir, prof_picture_path]): - return None - - with open(prof_picture_path, 'rb') as pic: - picture = bytearray(pic.read()) - - # assume that image is returned on screen after this - return prof_picture_path +import os +from flask import Flask, request + +### Unrelated to the exercise -- Starts here -- Please ignore +app = Flask(__name__) +@app.route("/") +def source(): + TaxPayer('foo', 'bar').get_tax_form_attachment(request.args["input"]) + TaxPayer('foo', 'bar').get_prof_picture(request.args["input"]) +### Unrelated to the exercise -- Ends here -- Please ignore + +class TaxPayer: + + def __init__(self, username, password): + self.username = username + self.password = password + self.prof_picture = None + self.tax_form_attachment = None + + # returns the path of an optional profile picture that users can set + def get_prof_picture(self, path=None): + # setting a profile picture is optional + if not path: + pass + + # builds path + base_dir = os.path.dirname(os.path.abspath(__file__)) + prof_picture_path = os.path.normpath(os.path.join(base_dir, path)) + if not prof_picture_path.startswith(base_dir): + return None + + with open(prof_picture_path, 'rb') as pic: + picture = bytearray(pic.read()) + + # assume that image is returned on screen after this + return prof_picture_path + + # returns the path of an attached tax form that every user should submit + def get_tax_form_attachment(self, path=None): + tax_data = None + + # A tax form is required + if not path: + raise Exception("Error: Tax form is required for all users") + + # Validate the path to prevent path traversal attacks + base_dir = os.path.dirname(os.path.abspath(__file__)) + tax_form_path = os.path.normpath(os.path.join(base_dir, path)) + if not tax_form_path.startswith(base_dir): + return None + + with open(tax_form_path, 'rb') as form: + tax_data = bytearray(form.read()) + + # assume that tax data is returned on screen after this + return tax_form_path # Solution explanation