Path Traversal Prevention¶
Overview¶
Implementations MUST prevent include directives from accessing files outside the intended directory tree.
The Threat¶
; Malicious include attempts
include "../../../etc/passwd"
include "/etc/shadow"
include "C:\Windows\System32\config\SAM"
Requirements¶
Path Resolution¶
Implementations MUST:
- Resolve paths to absolute form before access
- Verify containment within allowed directory
- Reject escaping paths with clear error message
Allowed Directories¶
By default, includes are allowed within: - Directory containing the main ledger file - Subdirectories thereof
Blocked Patterns¶
| Pattern | Example | Action |
|---|---|---|
| Parent traversal | ../secret.beancount |
REJECT |
| Absolute path | /etc/passwd |
REJECT (unless in allowed) |
| Null bytes | file\x00.txt |
REJECT |
| URL schemes | file:///etc/passwd |
REJECT |
Implementation¶
Algorithm¶
def is_safe_include(base_dir: Path, include_path: str) -> bool:
# 1. Reject null bytes
if '\x00' in include_path:
return False
# 2. Resolve to absolute path
if os.path.isabs(include_path):
resolved = Path(include_path).resolve()
else:
resolved = (base_dir / include_path).resolve()
# 3. Check containment
try:
resolved.relative_to(base_dir.resolve())
return True
except ValueError:
return False
Platform Considerations¶
| Platform | Path Separator | Case Sensitive |
|---|---|---|
| Linux | / |
Yes |
| macOS | / |
No (usually) |
| Windows | \ or / |
No |
Implementations MUST normalize paths for the platform.
Configuration¶
Optional: Allow additional include directories:
option "include_paths" "/shared/ledgers:/home/user/includes"
Or command-line:
beancount --include-path=/shared/ledgers ledger.beancount
Error Messages¶
error: Path traversal blocked
--> main.beancount:5:1
|
5 | include "../../../etc/passwd"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ path escapes allowed directory
|
= resolved: /etc/passwd
= allowed: /home/user/ledgers/**
Test Vectors¶
Must Reject¶
include "../secret.beancount"
include "../../etc/passwd"
include "/etc/passwd"
include "subdir/../../secret.beancount"
include "subdir/../../../etc/passwd"
Must Accept¶
include "accounts.beancount"
include "subdir/file.beancount"
include "./accounts.beancount"
include "subdir/../accounts.beancount" ; Resolves within allowed
Security Modes¶
| Mode | Behavior |
|---|---|
| Strict (default) | Only subdirectories of main file |
| Permissive | Allow configured paths |
| Disabled | No path checking (DANGEROUS) |
Implementations SHOULD default to strict mode.