Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions test/test_invariant_dump_win_syms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import pytest
import os
import sys
import tempfile
from pathlib import Path

# Import the actual module
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from tools.dump_win_syms import main


@pytest.mark.parametrize("payload", [
"../../../etc/passwd", # Classic path traversal
"....//....//etc/passwd", # Double-encoded traversal
"%2e%2e%2fetc%2fpasswd", # URL-encoded traversal
"subdir/file.txt", # Valid relative path within root
"file.txt", # Valid filename
])
def test_path_traversal_blocked(payload, tmp_path, monkeypatch, capsys):
"""Invariant: File operations never resolve paths outside the declared root directory"""

# Create a temporary root directory for file operations
root_dir = tmp_path / "root"
root_dir.mkdir()

# Create a test file inside root
test_file = root_dir / "file.txt"
test_file.write_text("test content")

# Create a sensitive file outside root (should not be accessible)
outside_file = tmp_path / "sensitive.txt"
outside_file.write_text("secret data")

# Mock sys.argv to pass the payload as argument
monkeypatch.setattr(sys, 'argv', ['dump_win_syms.py', str(root_dir), payload])

# Attempt to access the file
try:
main()
except (SystemExit, FileNotFoundError, ValueError, OSError) as e:
# Expected: traversal attempts should fail or be rejected
pass

# Verify that sensitive file outside root was never read
captured = capsys.readouterr()
assert "secret data" not in captured.out, \
f"Path traversal payload '{payload}' allowed access outside root directory"

# For valid payloads, verify they resolve within root
if not payload.startswith(("../", "....//", "%2e")):
resolved = (root_dir / payload).resolve()
assert str(resolved).startswith(str(root_dir.resolve())), \
f"Valid path '{payload}' resolved outside root directory"
20 changes: 16 additions & 4 deletions tools/dump_win_syms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import sys, os
import subprocess

def safe_open(path, mode):
norm = os.path.normpath(path)
if any(part == '..' for part in norm.split(os.sep)):
raise ValueError("Path traversal detected: " + path)
return open(norm, mode)

def safe_call(cmd, **kwargs):
for arg in cmd:
if isinstance(arg, str) and any(p == '..' for p in os.path.normpath(arg).split(os.sep)):
raise ValueError("Path traversal in subprocess arg: " + arg)
return subprocess.call(cmd, **kwargs)

nw_exe = os.path.normpath(sys.argv[1])
nw_dll = os.path.normpath(sys.argv[2])
node_dll = os.path.normpath(sys.argv[3])
Expand All @@ -9,8 +21,8 @@
dll_sym_file = nw_dll + ".sym"
node_sym_file = node_dll + ".sym"
dump_exe = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'dump_syms.exe')
subprocess.call([dump_exe, nw_exe], stdout=open(sym_file, 'w'))
subprocess.call([dump_exe, nw_dll], stdout=open(dll_sym_file, 'w'))
subprocess.call([dump_exe, node_dll], stdout=open(node_sym_file, 'w'))
safe_call([dump_exe, nw_exe], stdout=safe_open(sym_file, 'w'))
safe_call([dump_exe, nw_dll], stdout=safe_open(dll_sym_file, 'w'))
safe_call([dump_exe, node_dll], stdout=safe_open(node_sym_file, 'w'))
lzma_exe = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', '..', 'third_party', 'lzma_sdk', 'bin', 'win64', '7za.exe')
subprocess.call([lzma_exe, 'a', '-t7z', out_file, sym_file, dll_sym_file, node_sym_file])
safe_call([lzma_exe, 'a', '-t7z', out_file, sym_file, dll_sym_file, node_sym_file])