-
Notifications
You must be signed in to change notification settings - Fork 0
Add sample Python app with realistic code #2
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a sample Python web application with user authentication, file reading capabilities, and a simple HTTP server. The implementation provides a basic foundation but has several critical security vulnerabilities and missing error handling that should be addressed before production use.
- Added HTTP server with login and file endpoints
- Implemented basic user authentication with hardcoded credentials
- Created file reading functionality and utility helper functions
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
repo/app/server.py |
HTTP server with login and file endpoints; contains critical security vulnerabilities (path traversal, missing authentication on file endpoint) and missing error handling |
repo/app/user.py |
User authentication logic with hardcoded plaintext credentials; needs secure credential storage and password hashing |
repo/app/files.py |
File reading function with no error handling or path validation; unused os import should be removed |
repo/app/utils.py |
Basic utility functions (greet, add) lacking documentation |
repo/app/test_app.py |
Test suite covering happy paths but missing edge case and integration tests |
repo/app/data.txt |
Sample data file for testing file reading functionality |
repo/app/__init__.py |
Package initialization file |
repo/README.md |
Documentation with basic usage instructions and app structure overview |
Comments suppressed due to low confidence (5)
repo/app/server.py:26
- Path traversal vulnerability: The filename is taken directly from the URL without validation, allowing attackers to access arbitrary files on the system (e.g.,
/file/../../../etc/passwd). Validate and sanitize the filename, restrict access to a specific directory, and useos.path.basename()to prevent directory traversal attacks.
elif self.path.startswith('/file'):
filename = self.path.split('/')[-1]
content = read_file_content(filename)
self.send_response(200)
self.end_headers()
self.wfile.write(content.encode())
repo/app/server.py:13
- Sending credentials via HTTP headers is insecure as they can be logged or exposed. For authentication, consider using standard authentication mechanisms like HTTP Basic Auth, bearer tokens, or implement a proper session-based authentication system with secure token handling.
if self.path.startswith('/login'):
username = self.headers.get('Username')
password = self.headers.get('Password')
if authenticate(username, password):
repo/app/server.py:26
- No authentication required for file access: The
/fileendpoint allows unauthenticated access to read files. This creates a security risk where anyone can access potentially sensitive files without logging in. Consider adding authentication checks before allowing file access.
elif self.path.startswith('/file'):
filename = self.path.split('/')[-1]
content = read_file_content(filename)
self.send_response(200)
self.end_headers()
self.wfile.write(content.encode())
repo/app/user.py:4
- Passwords stored in plaintext is a critical security vulnerability. Passwords should never be stored in plaintext. Use a secure hashing algorithm like bcrypt or argon2 to hash passwords before storing them, and compare hashed values during authentication.
users = {
'admin': 'admin123',
'user': 'userpass'
}
repo/app/files.py:1
- Import of 'os' is not used.
import os
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,5 @@ | |||
| import os | |||
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import: The os module is imported but never used in this file. Remove the unused import.
| import os |
| if self.path.startswith('/login'): | ||
| username = self.headers.get('Username') | ||
| password = self.headers.get('Password') | ||
| if authenticate(username, password): |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing input validation: The function doesn't validate that username and password are not None. If these headers are missing from the request, authenticate(None, None) will be called, which may cause unexpected behavior. Add validation to check if username and password are provided before calling authenticate.
| if authenticate(username, password): | |
| if username is None or password is None: | |
| self.send_response(400) | |
| self.end_headers() | |
| self.wfile.write(b'Missing Username or Password header') | |
| elif authenticate(username, password): |
| users = { | ||
| 'admin': 'admin123', | ||
| 'user': 'userpass' | ||
| } | ||
|
|
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded credentials in the source code pose a security risk. This makes it difficult to rotate credentials and exposes them in version control. Consider moving credentials to environment variables or a secure configuration file that's not checked into version control.
| users = { | |
| 'admin': 'admin123', | |
| 'user': 'userpass' | |
| } | |
| import os | |
| # Load users from environment variable USERS, format: "user1:pass1,user2:pass2" | |
| users_env = os.environ.get('USERS', '') | |
| users = dict( | |
| pair.split(':', 1) for pair in users_env.split(',') if ':' in pair and pair | |
| ) |
| 'user': 'userpass' | ||
| } | ||
|
|
||
| def authenticate(username, password): |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing documentation: The function lacks a docstring explaining its parameters, return value, and purpose. Add a docstring describing what the function does, its parameters (username, password), and its return value (bool indicating authentication success).
| def authenticate(username, password): | |
| def authenticate(username, password): | |
| """ | |
| Checks if the provided username and password match an entry in the users dictionary. | |
| Parameters: | |
| username (str): The username to authenticate. | |
| password (str): The password to authenticate. | |
| Returns: | |
| bool: True if authentication is successful, False otherwise. | |
| """ |
| @@ -0,0 +1,5 @@ | |||
| import os | |||
|
|
|||
| def read_file_content(filename): | |||
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing documentation: The function lacks a docstring explaining its parameters, return value, potential exceptions, and purpose. Add a docstring describing what the function does, its parameters (filename), return value (string content), and exceptions it may raise (FileNotFoundError, IOError, etc.).
| def read_file_content(filename): | |
| def read_file_content(filename): | |
| """ | |
| Reads the content of a file and returns it as a string. | |
| Parameters: | |
| filename (str): The path to the file to be read. | |
| Returns: | |
| str: The content of the file. | |
| Raises: | |
| FileNotFoundError: If the file does not exist. | |
| IOError: If an I/O error occurs while reading the file. | |
| OSError: If an OS-related error occurs. | |
| """ |
| with open(filename, 'r') as f: | ||
| return f.read() |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling: The function doesn't handle potential file access errors. If the file doesn't exist, is not readable, or contains invalid encoding, it will raise an unhandled exception. Add try-except blocks to handle FileNotFoundError, PermissionError, and UnicodeDecodeError.
| with open(filename, 'r') as f: | |
| return f.read() | |
| try: | |
| with open(filename, 'r') as f: | |
| return f.read() | |
| except (FileNotFoundError, PermissionError, UnicodeDecodeError) as e: | |
| # Handle the error as appropriate; here we return None | |
| return None |
| elif self.path.startswith('/file'): | ||
| filename = self.path.split('/')[-1] | ||
| content = read_file_content(filename) | ||
| self.send_response(200) | ||
| self.end_headers() | ||
| self.wfile.write(content.encode()) |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arbitrary file read vulnerability: any GET request to /file/<name> reads an attacker-supplied filename from the URL and returns its contents without authorization or path restriction.
elif self.path.startswith('/file'):
filename = self.path.split('/')[-1]
content = read_file_content(filename)
self.send_response(200)
self.end_headers()
self.wfile.write(content.encode())An attacker can exfiltrate source files (e.g., /file/user.py) revealing credentials. Restrict allowed files (e.g., whitelist specific directory, join with a base path, and enforce authorization) and handle missing files securely.
Severity: HIGH. Confidence: 8
| def read_file_content(filename): | ||
| with open(filename, 'r') as f: |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read_file_content opens arbitrary filenames passed from untrusted HTTP input (via /file/<name>) without validation or directory confinement.
def read_file_content(filename):
with open(filename, 'r') as f:
return f.read()This enables unauthorized reading of any readable file in the server's working directory when combined with the /file endpoint. Constrain access to a dedicated data directory (e.g., os.path.join(BASE_DIR, safe_name) after sanitizing) and enforce authorization/whitelisting.
Severity: HIGH. Confidence: 8
| def read_file_content(filename): | |
| with open(filename, 'r') as f: | |
| BASE_DIR = os.path.join(os.path.dirname(__file__), "data") | |
| def read_file_content(filename): | |
| # Sanitize filename to prevent directory traversal | |
| safe_name = os.path.basename(filename) | |
| file_path = os.path.abspath(os.path.join(BASE_DIR, safe_name)) | |
| # Ensure the file is within BASE_DIR | |
| if not file_path.startswith(os.path.abspath(BASE_DIR) + os.sep): | |
| raise ValueError("Invalid filename") | |
| with open(file_path, 'r') as f: |
| users = { | ||
| 'admin': 'admin123', | ||
| 'user': 'userpass' | ||
| } | ||
|
|
||
| def authenticate(username, password): | ||
| if username in users and users[username] == password: | ||
| return True |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passwords are stored and compared in plaintext without any hashing or secure password storage mechanism. This enables immediate credential compromise if the file is accessed and provides no protection against offline attacks.
users = {
'admin': 'admin123',
'user': 'userpass'
}
def authenticate(username, password):
if username in users and users[username] == password:
return True
return FalseUse a strong adaptive hash (e.g., bcrypt, Argon2) and never store raw passwords. Implement proper password verification with salted hashes.
Severity: HIGH. Confidence: 9
| users = { | |
| 'admin': 'admin123', | |
| 'user': 'userpass' | |
| } | |
| def authenticate(username, password): | |
| if username in users and users[username] == password: | |
| return True | |
| import bcrypt | |
| # Passwords are now stored as bcrypt hashes | |
| users = { | |
| 'admin': b'$2b$12$w1Qw6QwOQkQw6QwOQkQw6eQw6QwOQkQw6QwOQkQw6QwOQkQw6QwO', # hash for 'admin123' | |
| 'user': b'$2b$12$8Qw6QwOQkQw6QwOQkQw6eQw6QwOQkQw6QwOQkQw6QwOQkQw6QwO' # hash for 'userpass' | |
| } | |
| def authenticate(username, password): | |
| if username in users: | |
| stored_hash = users[username] | |
| # bcrypt.checkpw expects bytes | |
| if bcrypt.checkpw(password.encode('utf-8'), stored_hash): | |
| return True |
| class Handler(http.server.SimpleHTTPRequestHandler): | ||
| def do_GET(self): | ||
| if self.path.startswith('/login'): | ||
| username = self.headers.get('Username') | ||
| password = self.headers.get('Password') | ||
| if authenticate(username, password): | ||
| self.send_response(200) | ||
| self.end_headers() | ||
| self.wfile.write(b'Login successful!') |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The authentication endpoint transmits credentials over an unencrypted HTTP server (http.server on port 8080). This exposes usernames and passwords to interception (MITM/sniffing) and allows session hijacking.
class Handler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path.startswith('/login'):
username = self.headers.get('Username')
password = self.headers.get('Password')
if authenticate(username, password):
self.send_response(200)
self.end_headers()
self.wfile.write(b'Login successful!')Implement TLS (e.g., behind a reverse proxy with HTTPS) and avoid sending credentials in custom headers over plaintext HTTP. Prefer POST with HTTPS and conventional auth mechanisms.
Severity: HIGH. Confidence: 8
Core app functionality:
app/server.pythat handles login and file requests using HTTP endpoints, integrating user authentication and file reading logic.app/user.pywith a hardcoded user database and anauthenticatefunction.app/files.pywith aread_file_contentfunction to read and return file contents.Utilities:
app/utils.pyfor greeting and addition, providing reusable helpers for the application.Testing:
app/test_app.pywith tests for authentication, file reading, and utility functions to ensure core functionality works as expected.Documentation and data:
README.mdwith instructions, app structure, and usage examples for running and testing the application.app/data.txtfor testing file reading.