Autofix: Code scanning alert #2#3
Conversation
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
| output_path = os.path.abspath(args.write_md) | ||
| if os.path.commonpath([base_dir, output_path]) != base_dir: | ||
| raise ValueError(f"Refusing to write report outside base directory: {output_path}") | ||
| os.makedirs(os.path.dirname(output_path), exist_ok=True) |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 months ago
In general, to fix uncontrolled-path issues with CLI arguments, we should (1) choose a fixed, trusted root directory; (2) normalize the user-supplied path against that root; and (3) verify after normalization that the resulting path is still within the allowed root before performing any filesystem operations. This ensures that even if the user passes absolute paths or directory traversal sequences (../..), writes remain confined to the trusted directory.
For this specific script, the simplest robust fix is to base output_path on a fixed, trusted root rather than the current working directory. A good choice is the directory containing triage_all_repos.py itself (i.e., the project/scripts directory), which is under the control of whoever deploys the script. We can then join that safe_root with the user-supplied args.write_md, normalize the result, and enforce that the normalized path starts with safe_root using os.path.commonpath. This preserves existing behavior for relative paths (they will still resolve under the script directory, just as they effectively do now under the working directory in normal usage), while safely rejecting absolute or traversing paths that would escape safe_root. Concretely:
- Define
safe_rootusingos.path.dirname(os.path.abspath(__file__)). - Replace
base_dir = os.path.abspath(os.getcwd())withsafe_root = ...as above. - Build
output_pathwithos.path.abspath(os.path.join(safe_root, args.write_md))instead of usingargs.write_mddirectly. - Update the path check to compare against
safe_rootinstead ofbase_dir.
All of these changes are localized to the main() function in scripts/triage_all_repos.py, and they do not require any new imports beyond the already-imported os.
| @@ -161,10 +161,10 @@ | ||
| md = render_markdown_report(args.owner, scanned, skipped_no_analysis, per_repo_counts, top_alerts) | ||
|
|
||
| # write report | ||
| base_dir = os.path.abspath(os.getcwd()) | ||
| output_path = os.path.abspath(args.write_md) | ||
| if os.path.commonpath([base_dir, output_path]) != base_dir: | ||
| raise ValueError(f"Refusing to write report outside base directory: {output_path}") | ||
| safe_root = os.path.dirname(os.path.abspath(__file__)) | ||
| output_path = os.path.abspath(os.path.join(safe_root, args.write_md)) | ||
| if os.path.commonpath([safe_root, output_path]) != safe_root: | ||
| raise ValueError(f"Refusing to write report outside safe directory: {output_path}") | ||
| os.makedirs(os.path.dirname(output_path), exist_ok=True) | ||
| with open(output_path, "w", encoding="utf-8") as f: | ||
| f.write(md) |
| if os.path.commonpath([base_dir, output_path]) != base_dir: | ||
| raise ValueError(f"Refusing to write report outside base directory: {output_path}") | ||
| os.makedirs(os.path.dirname(output_path), exist_ok=True) | ||
| with open(output_path, "w", encoding="utf-8") as f: |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 months ago
In general, the fix is to ensure that any path derived from untrusted input is normalized and then checked to be within an allowed base directory before it is used in file operations. Normalization should handle .. segments and symlinks (os.path.realpath), and the containment check should be robust across platforms, avoiding exceptions from os.path.commonpath on different drives.
For this code, the best minimal fix is to:
- Normalize both the base directory and the output path with
os.path.realpath(or at leastnormpath) after converting them to absolute paths. - Use a defensive wrapper around
os.path.commonpathso that if it raisesValueError(e.g., on Windows with different drives), we treat that as “outside the base directory” and raise a controlledValueErrorwith a clear message instead of proceeding to write. - Keep the existing behavior that refuses to write outside the current working directory, preserving functionality and intent.
Concretely, in scripts/triage_all_repos.py within main():
- Replace the current computation and check:
base_dir = os.path.abspath(os.getcwd())
output_path = os.path.abspath(args.write_md)
if os.path.commonpath([base_dir, output_path]) != base_dir:
raise ValueError(f"Refusing to write report outside base directory: {output_path}")with code that:
- Normalizes with
os.path.realpath. - Handles
ValueErrorfromos.path.commonpathby rejecting the path.
No new imports are required because os is already imported. All changes remain confined to the shown snippet.
| @@ -161,10 +161,15 @@ | ||
| md = render_markdown_report(args.owner, scanned, skipped_no_analysis, per_repo_counts, top_alerts) | ||
|
|
||
| # write report | ||
| base_dir = os.path.abspath(os.getcwd()) | ||
| output_path = os.path.abspath(args.write_md) | ||
| if os.path.commonpath([base_dir, output_path]) != base_dir: | ||
| base_dir = os.path.realpath(os.path.abspath(os.getcwd())) | ||
| output_path = os.path.realpath(os.path.abspath(args.write_md)) | ||
| try: | ||
| common = os.path.commonpath([base_dir, output_path]) | ||
| except ValueError: | ||
| # Paths on different drives or otherwise incomparable; treat as outside base_dir | ||
| raise ValueError(f"Refusing to write report outside base directory: {output_path}") | ||
| if common != base_dir: | ||
| raise ValueError(f"Refusing to write report outside base directory: {output_path}") | ||
| os.makedirs(os.path.dirname(output_path), exist_ok=True) | ||
| with open(output_path, "w", encoding="utf-8") as f: | ||
| f.write(md) |
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Automated autofix campaign run at 2026-01-18T23:38:52Z.
Alert: https://github.com/Notoriousjayy/ghas-code-scanning-toolkit/security/code-scanning/2