|
6 | 6 |
|
7 | 7 | set -euo pipefail
|
8 | 8 |
|
| 9 | +# A variable to memoize the command for canonicalizing paths. |
| 10 | +_CANONICALIZE_CMD="" |
| 11 | + |
| 12 | +# canonicalize_path resolves a path to its absolute, canonical form. |
| 13 | +# It tries 'realpath', 'readlink -f', 'python3', 'python', or 'perl' in order. |
| 14 | +# The chosen command is memoized to avoid repeated checks. |
| 15 | +# If none of these are available, it returns an empty string. |
| 16 | +canonicalize_path() { |
| 17 | +local path_to_resolve="$1" |
| 18 | + |
| 19 | +# If we haven't determined a command yet, find one. |
| 20 | +if [[-z"$_CANONICALIZE_CMD" ]];then |
| 21 | +ifcommand -v realpath>/dev/null2>&1;then |
| 22 | +_CANONICALIZE_CMD="realpath" |
| 23 | +elifcommand -v readlink>/dev/null2>&1&& readlink -f.>/dev/null2>&1;then |
| 24 | +_CANONICALIZE_CMD="readlink" |
| 25 | +elifcommand -v python3>/dev/null2>&1;then |
| 26 | +_CANONICALIZE_CMD="python3" |
| 27 | +elifcommand -v python>/dev/null2>&1;then |
| 28 | +_CANONICALIZE_CMD="python" |
| 29 | +elifcommand -v perl>/dev/null2>&1;then |
| 30 | +_CANONICALIZE_CMD="perl" |
| 31 | +else |
| 32 | +# No command found, so we can't resolve. |
| 33 | +# We set a "none" value to prevent re-checking. |
| 34 | +_CANONICALIZE_CMD="none" |
| 35 | +fi |
| 36 | +fi |
| 37 | + |
| 38 | +# Now, execute the command. |
| 39 | +case"$_CANONICALIZE_CMD"in |
| 40 | +realpath) |
| 41 | +realpath"$path_to_resolve"2>/dev/null |
| 42 | +;; |
| 43 | +readlink) |
| 44 | +readlink -f"$path_to_resolve"2>/dev/null |
| 45 | +;; |
| 46 | +python3) |
| 47 | +python3 -c"import os, sys; print(os.path.realpath(sys.argv[1]))""$path_to_resolve"2>/dev/null |
| 48 | +;; |
| 49 | +python) |
| 50 | +python -c"import os, sys; print(os.path.realpath(sys.argv[1]))""$path_to_resolve"2>/dev/null |
| 51 | +;; |
| 52 | +perl) |
| 53 | +perl -e'use Cwd "abs_path"; print abs_path(shift)'"$path_to_resolve"2>/dev/null |
| 54 | +;; |
| 55 | +*) |
| 56 | +# This handles the "none" case or any unexpected error. |
| 57 | +echo"" |
| 58 | +;; |
| 59 | +esac |
| 60 | +} |
| 61 | + |
9 | 62 | # Read JSON input from stdin
|
10 | 63 | input=$(cat)
|
11 | 64 |
|
12 | 65 | # Extract the file path from the JSON input
|
13 | 66 | # Expected format: {"tool_input": {"file_path": "/absolute/path/to/file"}} or {"tool_response": {"filePath": "/absolute/path/to/file"}}
|
14 | 67 | file_path=$(echo"$input"| jq -r'.tool_input.file_path // .tool_response.filePath // empty')
|
15 | 68 |
|
| 69 | +# Secure path canonicalization to prevent path traversal attacks |
| 70 | +# Resolve repo root to an absolute, canonical path. |
| 71 | +repo_root_raw="$(cd"$(dirname"$0")/../.."&& pwd)" |
| 72 | +repo_root="$(canonicalize_path"$repo_root_raw")" |
| 73 | +if [[-z"$repo_root" ]];then |
| 74 | +# Fallback if canonicalization fails |
| 75 | +repo_root="$repo_root_raw" |
| 76 | +fi |
| 77 | + |
| 78 | +# Resolve the input path to an absolute path |
| 79 | +if [["$file_path"= /* ]];then |
| 80 | +# Already absolute |
| 81 | +abs_file_path="$file_path" |
| 82 | +else |
| 83 | +# Make relative paths absolute from repo root |
| 84 | +abs_file_path="$repo_root/$file_path" |
| 85 | +fi |
| 86 | + |
| 87 | +# Canonicalize the path (resolve symlinks and ".." segments) |
| 88 | +canonical_file_path="$(canonicalize_path"$abs_file_path")" |
| 89 | + |
| 90 | +# Check if canonicalization failed or if the resolved path is outside the repo |
| 91 | +if [[-z"$canonical_file_path" ]]|| { [["$canonical_file_path"!="$repo_root" ]]&& [["$canonical_file_path"!="$repo_root"/* ]]; };then |
| 92 | +echo"Error: File path is outside repository or invalid:$file_path">&2 |
| 93 | +exit 1 |
| 94 | +fi |
| 95 | + |
| 96 | +# Handle the case where the file path is the repository root itself. |
| 97 | +if [["$canonical_file_path"=="$repo_root" ]];then |
| 98 | +echo"Warning: Formatting the repository root is not a supported operation. Skipping.">&2 |
| 99 | +exit 0 |
| 100 | +fi |
| 101 | + |
| 102 | +# Convert back to relative path from repo root for consistency |
| 103 | +file_path="${canonical_file_path#"$repo_root"/}" |
| 104 | + |
16 | 105 | if [[-z"$file_path" ]];then
|
17 | 106 | echo"Error: No file path provided in input">&2
|
18 | 107 | exit 1
|
|