Skip to content
Merged
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
28 changes: 19 additions & 9 deletions plugins/deploy-on-aws/scripts/lib/post_process_drawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,18 +302,28 @@ def main() -> None:
# Not a drawio file, exit silently (hook compatibility)
sys.exit(0)

path = Path(file_path)
# Reject symlinks BEFORE resolve() — resolve follows symlinks, so
# is_symlink() would always be False on the resolved path.
# nosemgrep: ai.ai-best-practices.hooks-path-traversal.hooks-path-traversal-python.hooks-path-traversal-python
if Path(file_path).is_symlink():
print(f"Refusing to process symlink: {file_path}", file=sys.stderr)
sys.exit(1)

# Resolve path to canonical absolute form to prevent path traversal
# (e.g., "../../etc/passwd.drawio") from hook input — see CWE-22.
# All file operations below use only the resolved `path` object.
# nosemgrep: ai.ai-best-practices.hooks-path-traversal.hooks-path-traversal-python.hooks-path-traversal-python
path = Path(file_path).resolve()

# Reject symlinks to prevent symlink-follow write attacks
if path.is_symlink():
print(f"Skipping symlink: {file_path}", file=sys.stderr)
# Re-validate extension after resolution (traversal could change it)
if not path.suffix == ".drawio" and not str(path).endswith(".drawio.xml"):
sys.exit(0)

# Reject files exceeding size limit before parsing
try:
file_size = path.stat().st_size
except OSError as e:
print(f"Cannot stat {file_path}: {e}", file=sys.stderr)
print(f"Cannot stat file: {e}", file=sys.stderr)
sys.exit(1)
if file_size > MAX_FILE_SIZE:
print(
Expand All @@ -324,9 +334,9 @@ def main() -> None:
sys.exit(0)

try:
tree = ET.parse(file_path)
tree = ET.parse(path)
except (ET.ParseError, FileNotFoundError) as e:
print(f"Error parsing {file_path}: {e}", file=sys.stderr)
print(f"Error parsing file: {e}", file=sys.stderr)
sys.exit(1)

# Top-level try/except prevents unhandled exception tracebacks from
Expand Down Expand Up @@ -368,8 +378,8 @@ def main() -> None:
# and importing stdlib xml.etree.ElementTree triggers security scanners.
# Output is valid but not pretty-printed. If human-readable XML is needed,
# add a custom indent helper that walks the element tree without stdlib import.
tree.write(file_path, encoding="unicode", xml_declaration=False)
print(f"Written: {file_path}")
tree.write(path, encoding="unicode", xml_declaration=False)
print(f"Written: {path}")
else:
print("(dry run, no changes written)")
else:
Expand Down
5 changes: 3 additions & 2 deletions plugins/deploy-on-aws/scripts/lib/validate_drawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ def validate(file_path):
errors = []
warnings = []

# 1. Read file
path = Path(file_path)
# 1. Read file — resolve to canonical absolute path to prevent path
# traversal (e.g., "../../etc/passwd.drawio") from hook input (CWE-22)
path = Path(file_path).resolve()
try:
file_size = path.stat().st_size
except OSError as e:
Expand Down
Loading