bad_path
A Python package to identify potentially dangerous file paths.
Overview
bad_path provides functions to test whether a supplied file path points to a system-sensitive location, taking
into account different OS platforms (Windows, macOS, Linux).
Installation
From PyPI
pip install bad_path
From Conda
conda install -c phygbu bad_path
# or
mamba install -c phygbu bad_path
From Source
git clone https://github.com/stonerlab/bad_path.git
cd bad_path
pip install -e .
Quick Start
from bad_path import is_dangerous_path, DangerousPathError
# Check if a path is dangerous
if is_dangerous_path("/etc/passwd"):
print("Warning: This path points to a sensitive location!")
# Raise an exception for dangerous paths
try:
is_dangerous_path("/etc/passwd", raise_error=True)
except DangerousPathError as e:
print(f"Error: {e}")
# Use the PathChecker class for more details
from bad_path import PathChecker
checker = PathChecker("/etc/passwd")
if not checker:
print(f"Dangerous path detected!")
print(f"Platform system path: {checker.is_system_path}")
print(f"User-defined sensitive path: {checker.is_sensitive_path}")
# Check path accessibility
checker = PathChecker("/tmp/myfile.txt")
if checker:
print("Safe path!")
print(f"Readable: {checker.is_readable}")
print(f"Writable: {checker.is_writable}")
print(f"Creatable: {checker.is_creatable}")
Features
- ✅ Cross-platform support (Windows, macOS, Linux)
- ✅ Simple API for checking dangerous paths
- ✅ Object-oriented
PathCheckerclass with detailed information - ✅ Path accessibility checks (read, write, create permissions)
- ✅ Invalid character detection (platform-specific)
- ✅ Path traversal protection (optional
cwd_onlyflag) - ✅ Customizable error handling
- ✅ Lightweight with no external dependencies
- ✅ Works with both strings and
pathlib.Pathobjects - ✅ User-defined dangerous paths support
Usage Examples
Basic Path Checking
from bad_path import is_dangerous_path
# Simple boolean check
if is_dangerous_path("/etc/passwd"):
print("This is a dangerous system path!")
if not is_dangerous_path("/tmp/myfile.txt"):
print("Safe to use!")
Read vs Write Validation
The mode parameter makes it easy to validate paths for different purposes:
from bad_path import PathChecker
# Validate for reading - allows system configuration files
checker = PathChecker("/etc/passwd", mode="read")
if checker:
print("Safe to read from this path!")
# Read the file...
# Validate for writing - strict validation
checker = PathChecker("/tmp/output.txt", mode="write")
if checker:
print("Safe to write to this path!")
# Write to the file...
# Attempting to write to system paths is blocked
checker = PathChecker("/etc/myconfig.txt", mode="write")
if not checker:
print("Blocked: Cannot write to system paths!")
Checking Path Accessibility
from bad_path import PathChecker
# Check if a file is readable
checker = PathChecker("/etc/passwd")
if checker.is_readable:
print("File can be read")
# Check if a file is writable
checker = PathChecker("/tmp/test.txt")
if checker.is_writable:
print("File can be written to")
# Check if a new file can be created
checker = PathChecker("/tmp/newfile.txt")
if checker.is_creatable:
print("File can be created in this location")
Combining Safety and Accessibility Checks
from bad_path import PathChecker
def safe_to_write(filepath):
"""Check if a path is both safe and writable."""
checker = PathChecker(filepath)
# PathChecker evaluates to True for safe paths
if not checker:
return False # Dangerous location
# Must be writable or creatable
return checker.is_writable or checker.is_creatable
# Usage
safe_to_write("/tmp/myfile.txt") # True - safe and creatable
safe_to_write("/etc/passwd") # False - dangerous location
Checking for Invalid Characters
from bad_path import PathChecker
# Check if a path contains invalid characters for the platform
checker = PathChecker("/tmp/test\x00file.txt") # Null byte is invalid on all platforms
print(f"Has invalid characters: {checker.has_invalid_chars}") # True
print(f"Is safe: {bool(checker)}") # False - dangerous due to invalid char
# Platform-specific invalid characters:
# - POSIX (Linux): null byte (\0)
# - macOS (Darwin): null byte (\0) and colon (:)
# - Windows: < > : " | ? * and control characters (0-31)
# Also checks for reserved names: CON, PRN, AUX, NUL, COM1-9, LPT1-9
# Windows example - reserved name check
checker = PathChecker("C:\\tmp\\CON.txt") # CON is a reserved name
print(f"Has invalid characters: {checker.has_invalid_chars}") # True on Windows
# Paths ending with space or period are invalid on Windows
checker = PathChecker("C:\\tmp\\file. ")
print(f"Has invalid characters: {checker.has_invalid_chars}") # True on Windows
Path Traversal Protection
The cwd_only flag provides protection against path traversal attacks by restricting paths to the current working
directory and its subdirectories. This is disabled by default to maintain backward compatibility.
from bad_path import PathChecker
# Enable path traversal protection
checker = PathChecker("../../../etc/passwd", cwd_only=True)
if not checker:
print("Blocked: Path traversal attempt detected!")
# Paths outside CWD are blocked
checker = PathChecker("/tmp/file.txt", cwd_only=True)
if not checker:
print("Blocked: Path is outside current working directory!")
# Paths within CWD and its subdirectories are allowed
checker = PathChecker("./data/file.txt", cwd_only=True)
if checker:
print("Safe: Path is within current working directory")
# Works with raise_error for automatic exception handling
from bad_path import DangerousPathError
try:
checker = PathChecker("../../sensitive.txt", cwd_only=True, raise_error=True)
except DangerousPathError as e:
print(f"Error: {e}")
Use cases for cwd_only:
- Web applications handling user-provided file paths
- CLI tools that should only operate on files in the current project
- Sandboxed environments where file access should be restricted
- Any scenario where you need to prevent directory traversal attacks
# Example: Secure file handler for a web application
def handle_user_file_request(user_path):
"""Safely handle user-provided file paths."""
# Validate the path is within CWD to prevent traversal attacks
checker = PathChecker(user_path, cwd_only=True, raise_error=True)
# Additional checks
if not checker.is_readable:
raise PermissionError("File is not readable")
# Safe to proceed with file operations
with open(checker.path, 'r') as f:
return f.read()
Documentation
Full documentation is available at https://stonerlab.github.io/bad_path/
Development
For development, install with the optional development dependencies:
pip install -e ".[dev]"
Run tests:
pytest
Build documentation:
cd docs
make html
License
MIT License - see LICENSE file for details.
Metadata
Release files for bad-path 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bad_path-0.2.1.tar.gz | 30.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bad_path-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.6 kB
Release files / bad_path-0.2.1.tar.gz
| Download URL | bad_path-0.2.1.tar.gz |
|---|---|
| Size | 30.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
073ccd8f7e6f929dc66d15a277437dfd3be804cef14208da13fa3abf6a2dfdc3
|
|
BLAKE2b-256 checksum How to use checksums |
9ce12354d5ea1150217e62bd5235fa4f3b3d237deb7bf42ab12407572bda1480
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|
Release files / bad_path-0.2.1-py3-none-any.whl
| Download URL | bad_path-0.2.1-py3-none-any.whl |
|---|---|
| Size | 19.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9baaf2eab82c0bc1a2891f3461c9eed9275ca1dca43d8258bdcc6daf9eb4d54e
|
|
BLAKE2b-256 checksum How to use checksums |
da31280db75db820cdc7427898df576589b0d85e81d08190589ae544b5089f0d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|