Back to skill

Security audit

Git Hooks Toolkit

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Git hook manager, but it needs review because a removal command can delete files outside Git hooks and some templates automatically run package tools.

Review before installing. Use only in repositories where persistent Git hooks are desired, avoid the install-deps template unless you accept automatic package-manager execution after merges, and do not run the remove command with any value other than a normal Git hook name until the path validation issue is fixed.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/git_hooks.py:496
Finding
Arbitrary File Deletion Through Unvalidated Hook Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/git_hooks.py`, lines 496–505 **Vulnerability Type**: Path traversal and unrestricted file deletion **Risk Level**: High ### Complete Code Snippet ```python def remove_hook(repo_dir, hook_type): """Remove an installed hook.""" hook_path = os.path.join(repo_dir, ".git", "hooks", hook_type) if os.path.exists(hook_path): os.remove(hook_path) print(f"✓ Removed {hook_type} hook") return True else: print(f"No {hook_type} hook found") return False ``` ### Technical Analysis The `remove_hook` function uses the command-line-controlled `hook_type` value directly when constructing the deletion target. It does not restrict the value to supported Git hook names, reject absolute paths, reject path separators, or verify the resolved path remains inside `.git/hooks`. Python's `os.path.join()` does not provide containment enforcement. If `hook_type` is an absolute path, the preceding repository and hooks path components are discarded. A relative value containing `../` components can similarly traverse outside the hooks directory after filesystem path resolution. Installation validates hook types against `HOOK_TEMPLATES`, but the removal path has no equivalent validation. ### Attack Path 1. An attacker influences the arguments supplied to the `remove` command, or convinces a user or automated agent to invoke it with a crafted hook type. 2. The attacker supplies an absolute path or traversal path, for example: ```text remove /home/user/important-file ``` or: ```text remove ../../../../target-file ``` 3. `os.path.join()` produces a path outside the intended `.git/hooks` directory. 4. If the path exists, `os.remove()` deletes it without confirmation or containment validation. ### Impact Assessment Exploitation permits deletion of any file writable by the operating-sys ...[truncated 389 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict removal to an explicit allowlist of supported Git hook names. - Reject absolute paths, path separators, `.` components, and `..` components. - Resolve both the hooks directory and target with `os.path.realpath()`. - Verify with `os.path.commonpath()` that the target remains inside the hooks directory. - Require the resolved target's parent to be exactly the hooks directory if nested paths are unnecessary. - Consider refusing to follow symbolic links or using directory-relative file operations with appropriate platform safeguards. - Add tests covering absolute paths, traversal sequences, symbolic links, and valid hook names. Example hardening pattern: ```python allowed_hooks = set(HOOK_TEMPLATES) if hook_type not in allowed_hooks: raise ValueError("Unsupported hook type") hooks_dir = os.path.realpath(os.path.join(repo_dir, ".git", "hooks")) hook_path = os.path.realpath(os.path.join(hooks_dir, hook_type)) if os.path.dirname(hook_path) != hooks_dir: raise ValueError("Hook path escapes the hooks directory") ``` ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/git_hooks.py:42
Finding
Unpinned Package Download and Execution Through npx<![CDATA[ ## Vulnerability Details **File Location**: `scripts/git_hooks.py`, lines 42 and 169 **Vulnerability Type**: Unsafe third-party package resolution and execution **Risk Level**: Medium ### Complete Code Snippet ```sh if [ -n "$JS_FILES" ]; then echo "Linting JS/TS files..." if command -v npx >/dev/null 2>&1; then echo "$JS_FILES" | xargs npx eslint --fix 2>/dev/null || { echo "ESLint found errors. Fix them before committing." exit 1 } echo "$JS_FILES" | xargs git add fi fi ``` ```sh if [ -n "$WEB_FILES" ] && command -v npx >/dev/null 2>&1; then echo "Running Prettier..." echo "$WEB_FILES" | xargs npx prettier --write 2>/dev/null || true echo "$WEB_FILES" | xargs git add fi ``` ### Technical Analysis The generated hooks verify that `npx` exists but do not verify that `eslint` or `prettier` is installed locally from an audited, locked dependency set. Depending on the installed npm/npx version and configuration, `npx` can resolve an absent command through a package registry, download the package, and execute it. Neither invocation pins a package version, requires offline operation, nor disables remote installation. Consequently, the code executed during a commit may depend on mutable external registry state rather than only on reviewed repository dependencies. ### Attack Path 1. A user installs the `lint-staged` or `format-check` generated hook. 2. The repository does not contain a locally installed `eslint` or `prettier` executable. 3. The user stages a file matching the relevant extension and initiates a commit. 4. The Git hook invokes `npx eslint` or `npx prettier`. 5. `npx` resolves and potentially downloads the package from its configured registry. 6. Downloaded package code executes with the developer account's privileges. Successful malicious exploitation additionally requires compromise or manipulati ...[truncated 547 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require ESLint and Prettier to be declared in the repository's development dependencies and locked by the package-manager lockfile. - Prevent implicit downloads by using `npx --no-install`, `npm exec --offline`, or direct executables under `node_modules/.bin`. - Fail safely when a required local executable is absent rather than retrieving it automatically. - Pin package versions and review lockfile changes. - Use a trusted registry with integrity verification and appropriate organizational controls. - Avoid suppressing all diagnostic output, because doing so can obscure unexpected package resolution behavior. Safer examples include: ```sh npx --no-install eslint --fix npx --no-install prettier --write ``` or direct invocation of audited local binaries: ```sh ./node_modules/.bin/eslint --fix ./node_modules/.bin/prettier --write ``` ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/git_hooks.py:355
Finding
Automatic Dependency Installation After Merge Executes Repository-Controlled Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/git_hooks.py`, lines 355–379 **Vulnerability Type**: Automatic dependency and lifecycle-script execution **Risk Level**: Medium ### Complete Code Snippet ```sh # Node.js if echo "$CHANGED_FILES" | grep -q "package-lock.json\\|yarn.lock\\|pnpm-lock.yaml"; then echo "Dependencies changed. Installing..." if [ -f "pnpm-lock.yaml" ] && command -v pnpm >/dev/null 2>&1; then pnpm install elif [ -f "yarn.lock" ] && command -v yarn >/dev/null 2>&1; then yarn install elif [ -f "package-lock.json" ]; then npm install fi fi # Python if echo "$CHANGED_FILES" | grep -q "requirements.txt\\|Pipfile.lock\\|poetry.lock"; then echo "Python dependencies changed." if [ -f "Pipfile.lock" ] && command -v pipenv >/dev/null 2>&1; then pipenv install elif [ -f "poetry.lock" ] && command -v poetry >/dev/null 2>&1; then poetry install elif [ -f "requirements.txt" ]; then echo "Run: pip install -r requirements.txt" fi fi ``` ### Technical Analysis The generated `post-merge` hook automatically invokes package managers whenever selected dependency lockfiles change. Package installation can execute lifecycle scripts, build backends, package setup logic, plugins, or other dependency-controlled code. The hook does not request confirmation, inspect the merged dependency changes, disable installation scripts, or enforce immutable/frozen installation behavior. Although the automatic installation behavior is disclosed in `SKILL.md`, it creates a supply-chain execution boundary in which merged repository content causes local code execution as a side effect of a merge or pull. ### Attack Path 1. An attacker submits changes that introduce or modify a dependency and its lockfile. 2. Those changes are accepted into a branch that a developer later merges or pulls. 3. Git invokes ...[truncated 961 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not install dependencies automatically after a merge by default. - Report detected dependency changes and require explicit user confirmation before installation. - Prefer lockfile validation or integrity checks as the automatic post-merge action. - Use immutable or frozen lockfile modes, such as the applicable package manager's `--frozen-lockfile` or `--immutable` option. - Disable lifecycle scripts where operationally possible, for example with npm's `--ignore-scripts`, and run necessary reviewed build steps separately. - Pin dependencies, review all manifest and lockfile changes, and enforce trusted package registries. - Run dependency installation in a restricted container or sandbox with minimal credentials and filesystem access. - Clearly document that the hook executes dependency-controlled code and provide a non-executing default template. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [ $FOUND -eq 1 ]; then
    echo ""
    echo "Remove debug statements before committing."
    echo "Use 'git commit --no-verify' to bypass this check."
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [ $FOUND -eq 1 ]; then
    echo ""
    echo "Remove debug statements before committing."
    echo "Use 'git commit --no-verify' to bypass this check."
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [ $FOUND -eq 1 ]; then
    echo ""
    echo "Remove debug statements before committing."
    echo "Use 'git commit --no-verify' to bypass this check."
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [ "$BRANCH" = "$b" ]; then
        echo "⚠ Direct push to '$BRANCH' is not allowed!"
        echo "Create a feature branch and open a pull request."
        echo "Use 'git push --no-verify' to bypass (not recommended)."
        exit 1
    fi
done
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises actions that install and remove Git hooks via shell commands and write files into a repository, but it does not declare an explicit tool scope such as permissions or allowed-tools. That mismatch can cause the agent or user to authorize broader execution than intended, increasing the chance of unintended file modification or command execution in a local repo.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The listed templates include behaviors with side effects, such as auto-formatting code and auto-installing dependencies after merge, but the description does not clearly warn users that some templates will modify files or invoke package managers. In an agent setting, that omission can lead to surprising code changes or dependency execution in a repository the user did not expect to be altered.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The post-merge template automatically runs package manager install commands after a merge when lockfiles change. This is risky because merging or pulling untrusted changes can immediately trigger execution paths in package managers, including lifecycle scripts, causing arbitrary code execution from repository-controlled dependency metadata without explicit user confirmation.

Static analysis

No suspicious patterns detected.