|
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' and 'readlink -f' 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 | +else |
| 26 | +# No command found, so we can't resolve. |
| 27 | +# We set a "none" value to prevent re-checking. |
| 28 | +_CANONICALIZE_CMD="none" |
| 29 | +fi |
| 30 | +fi |
| 31 | + |
| 32 | +# Now, execute the command. |
| 33 | +case"$_CANONICALIZE_CMD"in |
| 34 | +realpath) |
| 35 | +realpath"$path_to_resolve"2>/dev/null |
| 36 | +;; |
| 37 | +readlink) |
| 38 | +readlink -f"$path_to_resolve"2>/dev/null |
| 39 | +;; |
| 40 | +*) |
| 41 | +# This handles the "none" case or any unexpected error. |
| 42 | +echo"" |
| 43 | +;; |
| 44 | +esac |
| 45 | +} |
| 46 | + |
9 | 47 | # Read JSON input from stdin
|
10 | 48 | input=$(cat)
|
11 | 49 |
|
12 | 50 | # Extract the file path from the JSON input
|
13 | 51 | # Expected format: {"tool_input": {"file_path": "/absolute/path/to/file"}} or {"tool_response": {"filePath": "/absolute/path/to/file"}}
|
14 | 52 | file_path=$(echo"$input"| jq -r'.tool_input.file_path // .tool_response.filePath // empty')
|
15 | 53 |
|
| 54 | +# Secure path canonicalization to prevent path traversal attacks |
| 55 | +# Resolve repo root to an absolute, canonical path. |
| 56 | +repo_root_raw="$(cd"$(dirname"$0")/../.."&& pwd)" |
| 57 | +repo_root="$(canonicalize_path"$repo_root_raw")" |
| 58 | +if [[-z"$repo_root" ]];then |
| 59 | +# Fallback if canonicalization fails |
| 60 | +repo_root="$repo_root_raw" |
| 61 | +fi |
| 62 | + |
| 63 | +# Resolve the input path to an absolute path |
| 64 | +if [["$file_path"= /* ]];then |
| 65 | +# Already absolute |
| 66 | +abs_file_path="$file_path" |
| 67 | +else |
| 68 | +# Make relative paths absolute from repo root |
| 69 | +abs_file_path="$repo_root/$file_path" |
| 70 | +fi |
| 71 | + |
| 72 | +# Canonicalize the path (resolve symlinks and ".." segments) |
| 73 | +canonical_file_path="$(canonicalize_path"$abs_file_path")" |
| 74 | + |
| 75 | +# Check if canonicalization failed or if the resolved path is outside the repo |
| 76 | +if [[-z"$canonical_file_path" ]]|| { [["$canonical_file_path"!="$repo_root" ]]&& [["$canonical_file_path"!="$repo_root"/* ]]; };then |
| 77 | +echo"Error: File path is outside repository or invalid:$file_path">&2 |
| 78 | +exit 1 |
| 79 | +fi |
| 80 | + |
| 81 | +# Handle the case where the file path is the repository root itself. |
| 82 | +if [["$canonical_file_path"=="$repo_root" ]];then |
| 83 | +echo"Warning: Formatting the repository root is not a supported operation. Skipping.">&2 |
| 84 | +exit 0 |
| 85 | +fi |
| 86 | + |
| 87 | +# Convert back to relative path from repo root for consistency |
| 88 | +file_path="${canonical_file_path#"$repo_root"/}" |
| 89 | + |
16 | 90 | if [[-z"$file_path" ]];then
|
17 | 91 | echo"Error: No file path provided in input">&2
|
18 | 92 | exit 1
|
|