diff --git a/.github/workflows/homebrew.yml b/.github/workflows/homebrew.yml index 9636d59..41601f8 100644 --- a/.github/workflows/homebrew.yml +++ b/.github/workflows/homebrew.yml @@ -4,13 +4,15 @@ on: release: types: [published] +permissions: read-all + jobs: homebrew: name: Bump Homebrew formula runs-on: ubuntu-latest if: ${{ !github.event.release.prerelease }} steps: - - uses: mislav/bump-homebrew-formula-action@v3 + - uses: mislav/bump-homebrew-formula-action@56a283fa15557e9abaa4bdb63b8212abc68e655c # v3 with: formula-name: git-gtr formula-path: Formula/git-gtr.rb diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8507681..7ea0886 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,12 +6,14 @@ on: pull_request: branches: [main] +permissions: read-all + jobs: shellcheck: name: ShellCheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install ShellCheck run: sudo apt-get update && sudo apt-get install -y shellcheck @@ -24,7 +26,7 @@ jobs: name: Completions runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Verify completion files are up to date run: ./scripts/generate-completions.sh --check @@ -33,7 +35,7 @@ jobs: name: Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install BATS run: sudo apt-get update && sudo apt-get install -y bats diff --git a/README.md b/README.md index d1a7ff4..dd750d5 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,16 @@ git gtr clean --merged --force --yes # Force-clean and auto-confirm **Note:** The `--merged` mode auto-detects your hosting provider (GitHub or GitLab) from the `origin` remote URL and requires the corresponding CLI tool (`gh` or `glab`) to be installed and authenticated. For self-hosted instances, set the provider explicitly: `git gtr config set gtr.provider gitlab`. +### `git gtr trust` + +Review and approve hook commands defined in the repository's `.gtrconfig` file. Hooks from `.gtrconfig` are **not executed** until explicitly trusted — this prevents malicious contributors from injecting arbitrary shell commands via shared config files. + +```bash +git gtr trust # Review and approve .gtrconfig hooks +``` + +Trust is stored per content hash and must be re-approved if hooks change. Hooks from your local git config (`.git/config`, `~/.gitconfig`) are always trusted. + ### Other Commands - `git gtr doctor` - Health check (verify git, editors, AI tools) @@ -390,10 +400,12 @@ git gtr config set gtr.ui.color never ai = claude ``` +**Hook trust:** Hooks defined in `.gtrconfig` require explicit approval before they execute. Run `git gtr trust` after cloning a repository or when `.gtrconfig` hooks change. This protects against malicious hook injection in shared repositories. + **Configuration precedence** (highest to lowest): 1. `git config --local` (`.git/config`) - personal overrides -2. `.gtrconfig` (repo root) - team defaults +2. `.gtrconfig` (repo root) - team defaults (hooks require `git gtr trust`) 3. `git config --global` (`~/.gitconfig`) - user defaults > For complete configuration reference including all settings, hooks, file copying patterns, and environment variables, see [docs/configuration.md](docs/configuration.md) diff --git a/bin/git-gtr b/bin/git-gtr index ef19932..9ec06f1 100755 --- a/bin/git-gtr +++ b/bin/git-gtr @@ -100,6 +100,9 @@ main() { init) cmd_init "$@" ;; + trust) + cmd_trust "$@" + ;; version|--version|-v) echo "git gtr version $GTR_VERSION" ;; diff --git a/completions/_git-gtr b/completions/_git-gtr index e0a5ba3..ddb340c 100644 --- a/completions/_git-gtr +++ b/completions/_git-gtr @@ -45,6 +45,7 @@ _git-gtr() { 'config:Manage configuration' 'completion:Generate shell completions' 'init:Generate shell integration for cd support' + 'trust:Trust .gtrconfig hooks' 'version:Show version' 'help:Show help' ) diff --git a/completions/git-gtr.fish b/completions/git-gtr.fish index 7fdc786..f3b436c 100644 --- a/completions/git-gtr.fish +++ b/completions/git-gtr.fish @@ -54,6 +54,7 @@ complete -f -c git -n '__fish_git_gtr_using_command completion' -a 'bash zsh fis complete -f -c git -n '__fish_git_gtr_needs_command' -a init -d 'Generate shell integration for cd support' complete -f -c git -n '__fish_git_gtr_using_command init' -a 'bash zsh fish' -d 'Shell type' complete -c git -n '__fish_git_gtr_using_command init' -l as -d 'Custom function name' -r +complete -f -c git -n '__fish_git_gtr_needs_command' -a trust -d 'Trust .gtrconfig hooks' complete -f -c git -n '__fish_git_gtr_needs_command' -a version -d 'Show version' complete -f -c git -n '__fish_git_gtr_needs_command' -a help -d 'Show help' diff --git a/completions/gtr.bash b/completions/gtr.bash index 13314cf..f9a80d6 100644 --- a/completions/gtr.bash +++ b/completions/gtr.bash @@ -25,7 +25,7 @@ _git_gtr() { # If we're completing the first argument after 'git gtr' if [ "$cword" -eq 2 ]; then - COMPREPLY=($(compgen -W "new go run copy editor ai rm mv rename ls list clean doctor adapter config completion init help version" -- "$cur")) + COMPREPLY=($(compgen -W "new go run copy editor ai rm mv rename ls list clean doctor adapter config completion init trust help version" -- "$cur")) return 0 fi diff --git a/lib/adapters.sh b/lib/adapters.sh index 91c3c08..254be2b 100644 --- a/lib/adapters.sh +++ b/lib/adapters.sh @@ -166,9 +166,10 @@ editor_open() { target="$workspace" fi - # $GTR_EDITOR_CMD may contain arguments (e.g., "code --wait") - # Using eval here is necessary to handle multi-word commands properly - eval "$GTR_EDITOR_CMD \"\$target\"" + # Split multi-word commands (e.g., "code --wait") into an array for safe execution + local _cmd_arr + read -ra _cmd_arr <<< "$GTR_EDITOR_CMD" + "${_cmd_arr[@]}" "$target" } # Globals set by load_ai_adapter: GTR_AI_CMD, GTR_AI_CMD_NAME @@ -179,9 +180,10 @@ ai_can_start() { ai_start() { local path="$1" shift - # $GTR_AI_CMD may contain arguments (e.g., "bunx @github/copilot@latest") - # Using eval here is necessary to handle multi-word commands properly - (cd "$path" && eval "$GTR_AI_CMD \"\$@\"") + # Split multi-word commands (e.g., "bunx @github/copilot@latest") into an array for safe execution + local _cmd_arr + read -ra _cmd_arr <<< "$GTR_AI_CMD" + (cd "$path" && "${_cmd_arr[@]}" "$@") } # Standard AI adapter builder — used by adapter files that follow the common pattern @@ -295,6 +297,15 @@ resolve_workspace_file() { # Usage: _load_adapter