Back to skill

Security audit

Wip License Hook

Security checks for vulnerabilities and agentic risk

Overview

This is a real license-compliance tool, but its scanner and Git-hook installer create review-worthy local code-execution and persistence risks.

Review carefully before installing. Use this only on trusted repositories, prefer offline mode for untrusted code, and avoid running the hook installer until existing-hook overwrite behavior and the unpinned npx fallback are fixed. Back up .git/hooks first, regenerate and review the lockfile, and treat scans of attacker-controlled package manifests as unsafe until the shell command and path handling issues are corrected.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
src/core/scanner.ts:97
Finding

Manifest-Controlled Dependency Names Permit Shell Command Injection

Content
View full analysis
/dev/null`, { encoding: "utf-8", timeout: 10000 }).trim(); if (out) detectedLicense = normalizeSpdx(out); } catch { /* offline or not found */ } } ``` ```ts // pip dependencies if (!offline) { try { const out = execSync(`pip show ${name} 2>/dev/null`, { encoding: "utf-8", timeout: 10000 }); const match = out.match(/^License:\s*(.+)$/m); if (match) detectedLicense = normalizeSpdx(match[1]); } catch { /* skip */ } } ``` ```ts // Cargo dependencies if (!offline) { try { const out = execSync(`cargo info ${name} 2>/dev/null`, { encoding: "utf-8", timeout: 10000 }); const lMatch = out.match(/license:\s*(.+)/i); if (lMatch) detectedLicense = normalizeSpdx(lMatch[1]); } catch { /* skip */ } } ``` The same vulnerable behavior is present in the shipped runtime artifact at `dist/core/scanner.js:73`, `dist/core/scanner.js:115`, and `dist/core/scanner.js:152`. ### Technical Analysis `execSync()` executes string commands through a shell. Dependency names are obtained from repository-controlled files, including `package.json`, `requirements.txt`, and `Cargo.toml`, and are interpolated directly into these command strings. No ecosystem-specific validation, shell escaping, or argument separation is performed. A dependency name containing shell metacharacters such as command separators, substitutions, or redirections can therefore change the command executed by the shell. Suppressing standard error with `2>/dev/null` does not mitigate command injection and may make exploitation less visible. C ...[truncated 1351 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/core/ledger.ts:82
Finding

Dependency Names Can Traverse Outside Intended Snapshot and Module Directories

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Error
Location
hooks/pre-push.sh:10
Finding

Persistent Git Hooks Download and Execute an Unpinned npm Package

Content
View full analysis
/dev/null; then HOOK_CMD="wip-license-hook" elif [ -f "$REPO_ROOT/node_modules/.bin/wip-license-hook" ]; then HOOK_CMD="$REPO_ROOT/node_modules/.bin/wip-license-hook" elif command -v npx &>/dev/null; then HOOK_CMD="npx @wipcomputer/wip-license-hook" else # No tool available — push proceeds silently exit 0 fi ``` The selected command is subsequently executed by the hooks: ```bash OUTPUT=$($HOOK_CMD gate 2>&1) || true ``` ```bash $HOOK_CMD gate ``` ### Technical Analysis When no global or repository-local executable is available, both hooks fall back to `npx @wipcomputer/wip-license-hook`. No package version is specified and no integrity value is enforced. As a result, ordinary Git operations can cause `npx` to resolve, download, and execute a mutable package release from the npm registry. The effective code executed by the hook can therefore change after this Skill package has been reviewed or after the hooks have been installed. Git hooks persist in `.git/hooks` and execute in the security context of the developer or CI account. This creates a recurring supply-chain execution channel that is not required for local license checking. ### Attack Path 1. A user runs `wip-license-hook install`, placing the scripts in `.git/hooks`. 2. The expected local or global `wip-license-hook` executable is later missing or unavailable through `PATH`. 3. The user performs a Git push or merge. 4. The installed hook selects the `npx` fallback. 5. `npx` resolves the package from the npm registry and may download the current release. 6. Any compromised, malicious, or unexpectedly changed release executes with the user's privileges as part of the Git operation. ### I ...[truncated 572 chars]
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
src/cli/index.ts:145
Finding

Hook Installation Silently Replaces Existing Repository Security Hooks

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
hooks/pre-push.sh:33
Finding

Pre-Push Hook Discards the Gate Failure Status and Never Displays License-Drift Warnings

Content
View full analysis
&1) || true EXIT_CODE=$? if [ $EXIT_CODE -ne 0 ]; then echo "" echo "╔══════════════════════════════════════════════════╗" echo "║ ⚠️ LICENSE DRIFT DETECTED ║" echo "║ ║" echo "║ Upstream license may have changed. ║" echo "║ Your push will proceed (it's your code). ║" echo "║ ║" echo "║ Run: wip-license-hook scan --verbose ║" echo "║ to review the changes. ║" echo "╚══════════════════════════════════════════════════╝" echo "" echo "$OUTPUT" echo "" fi ``` ### Technical Analysis The command substitution is followed by `|| true`. When the gate command fails, the shell executes `true`, and `$?` consequently contains the successful exit status of `true`, not the nonzero status of the gate. `EXIT_CODE` is therefore always zero after this expression. The conditional warning block cannot execute when the gate reports a license change or another failure. The hook intentionally permits pushes, but its declared security function is to provide an advisory warning. This logic defect removes even that advisory protection. ### Attack Path 1. An upstream license changes or the gate otherwise returns a nonzero exit status. 2. The user performs a Git push. 3. The pre-push hook invokes the gate. 4. The gate failure causes the `true` command after `||` to run. 5. The hook records zero as `EXIT_CODE`. 6. The warning condition evaluates as false. 7. The push proceeds without the documented license-drift warning. ### Impact Assessment This creates a false sense of protection ...[truncated 452 chars]
Remediation
View remediation
&1) EXIT_CODE=$? set -e ``` Alternatively, use an explicit conditional: ```bash if OUTPUT=$("$HOOK_CMD" gate 2>&1); then EXIT_CODE=0 else EXIT_CODE=$? fi ``` Additional hardening should include: 1. Store the executable and its arguments in an array rather than a whitespace-containing string. 2. Distinguish license drift from scanner execution errors. 3. Display scanner failures even if pushes remain advisory. 4. Add automated shell tests confirming that exit codes zero and nonzero produce the intended output. 5. Run `shellcheck` as part of continuous integration. ]]>

T08 · Insecure Dependencies

Warning
Location
package-lock.json:1
Finding

Package Lockfile Omits the Declared Runtime Dependency and Does Not Match the Package Version

Content
View full analysis
=18.0.0" } } } } ``` ### Technical Analysis The committed lockfile does not represent the current `package.json`. In particular, it omits `@modelcontextprotocol/sdk`, which is imported by `mcp-server.mjs`, and records a different package version. A lockfile is intended to capture exact dependency resolution and integrity information. When it is stale, installation behavior becomes dependent on whether tooling honors the lockfile, regenerates it, or resolves the semver range afresh. The SDK declaration uses the broad range `^1.0.0`, so fresh resolution can select a later compatible release that was not represented in the audited lockfile. ### Attack Path 1. A user or build system installs the project dependencies. 2. The package manager detects that `package.json` and `package-lock.json` are inconsistent. 3. A strict `npm ci` installation may fail, or another workflow may update dependency resolution. 4. Th ...[truncated 640 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The supplied code is limited to helper functions for recognizing license types from text and normalizing SPDX labels. While this could support a broader license-compliance system, it does not itself perform the declared core behaviors such as scanning repositories/dependencies, tracking changes, gating merges, maintaining records, or generating dashboards. Therefore the declared description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

The code substantially matches the core declared purpose: it scans dependencies and an upstream fork for licenses, detects changes over time via a ledger, and provides a gate check that can block progress when a license changes. However, the description also claims generation of a public compliance dashboard, and there is no dashboard-generation logic in this code chunk. The code also performs archival of license text snapshots, which is adjacent but not explicitly declared. More importantly under the stated criteria, the declared permissions are empty, but the implementation clearly reads repository files and invokes git/npm/pip/cargo subprocesses, which is inconsistent resource access. Overall this is a partial description-behavior mismatch rather than a wholly different purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description promises a broader license-compliance system with scanning, merge gating, ledger maintenance, and dashboard generation. The supplied code chunk is much narrower: it is only a pre-push shell hook that invokes an external tool if present, shows a warning on detected license drift, and explicitly never blocks the push. This materially conflicts with the declared 'gates upstream merges' capability and omits the described ledger/dashboard behaviors. Additionally, the description lists no triggers, but the code is specifically a Git pre-push hook, which is a concrete trigger.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The supplied code is a narrow utility for classifying license text and normalizing SPDX identifiers. While this could support a larger license compliance system, it does not itself implement the declared primary behaviors: scanning dependency/fork ecosystems, tracking license changes, gating merges, maintaining a ledger, or generating a dashboard. There are no triggers, external integrations, persistence, or enforcement behaviors shown. This is therefore a material description-to-behavior mismatch rather than merely an implementation detail.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The code substantially matches the core declared purpose around license-rug-pull detection: it scans dependencies and an upstream fork for license information, records results in a ledger, archives license text, detects license changes, and returns a gate decision when changes are found. However, the description claims it generates a public compliance dashboard, and there is no dashboard generation, publishing, or reporting logic in this code chunk. Also, 'gates upstream merges' is only partially represented: the code returns a safe/blocked result and alerts, but does not integrate with VCS/CI merge controls itself. There are no obvious undeclared sensitive capabilities beyond local file reads/writes and subprocess calls needed for scanning.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code automatically inspects and fetches from an 'upstream' git remote during scanning, which can trigger unintended network access and interaction with attacker-controlled remotes in an untrusted repository. In a CI or agent context, silent fetches can leak environment/network metadata, alter local git state, and violate expectations about offline or read-only analysis, making the behavior materially more dangerous than a simple local subprocess.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · README.md (reported line 11)May include surrounding context.

md
## The Problem

You fork an MIT project. You build on it. Six months later, the upstream quietly changes to BSL or proprietary. You pull the update without checking. Now your project is poisoned.

This has happened. It will happen again.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest describes detecting license changes, gating merges, maintaining a ledger, and generating a dashboard. This code also writes executable hook scripts into .git/hooks, altering repository behavior and persistence in a way not described in the manifest’s stated capabilities.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code executes an external shell command using package metadata (npm view ${name} license) without any visible confirmation, log message, or explanatory comment warning the user that subprocesses will be run. Although dependency scanning may imply inspection, invoking package-manager commands is a safety-relevant operation and the file does not provide user-facing disclosure for it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The scanner runs pip show for each dependency but provides no confirmation prompt, user-facing log, or warning comment indicating that local subprocesses will be executed. This is a safety-relevant operation under the rule because it launches external commands and may inspect the local Python environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code executes cargo info to retrieve license information with no visible disclosure to the user that external commands will be launched. There is no prompt, print/log output, or explanatory inline documentation near the operation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Although the script is described as a hard gate that blocks pull/merge on license changes, it explicitly exits successfully when the checker is unavailable. That creates a fail-open control bypass: anyone in an environment missing the tool, or where PATH/local files are manipulated, can proceed without the promised license verification.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The hook falls back to executing npx @wipcomputer/wip-license-hook without pinning an exact version, so every hook invocation may fetch and run the latest published package code. In a Git hook context this is especially risky because the code executes automatically during developer workflow, enabling supply-chain compromise, unexpected behavior changes, or malicious package takeover to run on contributor machines.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding

The hook falls back to executing npx @wipcomputer/wip-license-hook without a pinned version, which allows the fetched package version to change over time and makes the execution path dependent on the current registry state. In a Git hook context this is especially risky because the code is run automatically during developer workflows, so a compromised publisher account, malicious new release, or dependency confusion-style event could lead to arbitrary code execution on contributor machines.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The construct OUTPUT=$($HOOK_CMD gate 2>&1) || true causes the command substitution pipeline to be masked, and $? then reflects the success of true rather than the gate command, so the script cannot reliably detect tool failures or license-drift results. In this skill's context, that undermines the stated security/compliance purpose by silently suppressing alerts, allowing license changes or scanner failures to go unnoticed while still presenting the hook as protective.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code invokes execSync with an interpolated package name in a shell command, which is a safety-relevant subprocess operation. While the file header mentions offline support, there is no confirmation prompt, log/print disclosure, or comment/docstring warning at the call site that package-manager commands will be executed against discovered dependencies.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The scanner executes pip show for each parsed dependency, which is a subprocess operation and may inspect the local Python environment. There is no prompt, visible log message, or nearby explanatory warning informing users that external package-manager commands will be run during scanning.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This call runs cargo info for dependency names, which is a safety-relevant subprocess action. The file does not provide any user-facing notice, confirmation, or logging that the scan will invoke Cargo commands when online.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code automatically inspects and fetches from the configured upstream git remote, which can trigger unanticipated network access to attacker-controlled infrastructure if the repository config is malicious. In a CI or agent setting, silent remote interaction can leak metadata such as IP, timing, and environment-specific behavior, and it expands the attack surface beyond purely local file analysis.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The scan updates persistent state by archiving license text snapshots and writing the ledger back to disk, which are file-write operations. Although the function name suggests updating, there is no explicit warning, comment, or user-facing output disclosing that running this path modifies repository files.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The surrounding documentation and variable name prePullSrc imply a pre-pull hook, but the destination path is .git/hooks/pre-merge-commit. This is an active contradiction between the code comments/identifiers and the actual hook being installed.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The generated HTML explicitly sets lang="en" and uses English-only UI strings such as the page title, which imposes a specific language/locale in natural-language output. There is no indication that users can opt into another language or that the English-only constraint is required for a region-specific or compliance-specific reason.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 27)May include surrounding context.

json
"author": "WIP Computer",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0"
  },
  "devDependencies": {
    "typescript": "^5.3.0",

Unverifiable Dependency: @modelcontextprotocol/sdk has 3 known advisory(ies) (CVE-2026-25536 (@modelcontextprotocol/sdk has cross-client data leak via shared server/transport); CVE-2026-0621 (Anthropic's MCP TypeScript SDK has a ReDoS vulnerability); CVE-2025-66414 (Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protec)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
77% confidence
Finding

The manifest includes @modelcontextprotocol/sdk with a broad version range while that package is reported to have multiple advisories, so consumers may resolve to a vulnerable release without realizing it. In a skill that scans dependencies and may interact with external repositories, metadata, or MCP transports, relying on an affected SDK could expose sensitive data, enable ReDoS, or weaken network-origin protections depending on the deployed version.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
dist/core/scanner.js:73

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/core/scanner.ts:97