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

Fix code scanning alert - Uncontrolled data used in path expression #18

Open
1 task
fabriziosalmi opened this issue Jun 11, 2024 · 0 comments
Open
1 task

Comments

@fabriziosalmi
Copy link
Owner

fabriziosalmi commented Jun 11, 2024

Tracking issue for:

We need to ensure that the file paths are securely validated and sanitized. The key is to make sure the paths are controlled and not influenced by user input in a way that could lead to accessing unintended files.

Steps to Mitigate Path Traversal

  1. Validate File Paths: Use normalization and validation techniques to ensure paths are within the expected directory.
  2. Restrict Access: Implement an allowlist of valid file names or patterns.
  3. Sanitize Input: Use functions to remove or handle potentially dangerous characters or sequences.

Given your requirement, we'll focus on securely handling file paths for serving the uglyfeed.xml file.

Applying Security Best Practices

We will modify the file serving part to ensure it only serves files from within the static_dir and specifically validate against allowed files.

Revised Secure Code for Handling XML Requests

from urllib.parse import unquote
import os

# Custom HTTP handler to serve XML with correct content type
class XMLHTTPRequestHandler(SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path.endswith(".xml"):
            try:
                # Define the only allowed file to serve
                allowed_files = {"uglyfeed.xml"}

                # Decode the URL and get the basename
                requested_file = unquote(os.path.basename(self.path))

                # Check if the requested file is in the allowlist
                if requested_file not in allowed_files:
                    self.send_error(403, "Forbidden: Access is denied.")
                    return
                
                # Construct the file path safely
                file_path = static_dir / requested_file

                # Validate the file path to ensure it is within the expected directory
                file_path = file_path.resolve()
                
                # Check if the resolved path is under the static_dir
                if not str(file_path).startswith(str(static_dir)):
                    self.send_error(403, "Forbidden: Access is denied.")
                    return
                
                # Check if the file exists and is a file
                if file_path.exists() and file_path.is_file():
                    self.send_response(200)
                    self.send_header("Content-Type", "application/xml")
                    self.end_headers()
                    with open(file_path, 'rb') as file:
                        self.wfile.write(file.read())
                else:
                    self.send_error(404, "File not found")
            except Exception as e:
                self.send_error(500, f"Internal Server Error: {e}")
        else:
            super().do_GET()

Explanation:

  1. Path Decoding and Normalization: We decode the URL path and use os.path.basename to safely get the file name, preventing traversal attacks like ../file.

  2. Allowlist Validation: We check if the requested file is in the predefined list of allowed files.

  3. Path Resolution and Validation: We resolve the full path and verify that it starts with static_dir. This ensures that any attempts to access files outside of static_dir are blocked.

  4. Response Handling: Appropriate HTTP responses (200, 403, 404, 500) are returned based on the conditions checked.

Updated Server Start Code

Ensure the server starts in a safe context with the correct working directory:

def start_custom_server(port):
    os.chdir(static_dir)  # Change working directory to the static directory
    server_address = ('', port)
    httpd = HTTPServer(server_address, XMLHTTPRequestHandler)
    httpd.serve_forever()

# Start the custom server in a new thread on an available port
custom_server_port = find_available_port(8001)
server_thread = threading.Thread(target=start_custom_server, args=(custom_server_port,), daemon=True)
server_thread.start()
fabriziosalmi added a commit that referenced this issue Jun 12, 2024
still working on that: #18
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

No branches or pull requests

1 participant