Back to skill

Security audit

Ops Code Review

Security checks for vulnerabilities and agentic risk

Overview

This is a real code-review automation skill, but it needs Review because it handles SVN passwords unsafely and can run broad, unpinned system installers.

Install only after review in a disposable container or dedicated scanning host. Use a read-only SVN account scoped to the needed repositories, fix the password-in-process-arguments bug before use, store configuration in a protected user directory instead of /tmp, pin dependency versions, and send reports only to an approved Feishu group because they may contain internal repository details.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/svn_manager.py:72
Finding
SVN Password Exposed Through Process Arguments## Vulnerability Details **File Location**: `scripts/svn_manager.py`, lines 72-100 **Vulnerability Type**: Credential exposure through command-line arguments **Risk Level**: High ### Vulnerable Code ```python def svn_auth_cmd(cmd: List[str]) -> List[str]: """Give the SVN command authentication parameters.""" if SVN_USER: cmd = cmd + ["--username", SVN_USER] if SVN_PASS: cmd = cmd + ["--password", SVN_PASS, "--no-auth-cache"] else: cmd = cmd + ["--no-auth-cache"] return cmd def run_cmd(cmd: List[str], cwd: Optional[str] = None) -> tuple: """Execute a command and return (returncode, stdout, stderr). If SVN_PASS is provided, use stdin to avoid exposing it in cmdline. """ env = None stdin_data = None if SVN_USER and SVN_PASS: cmd = cmd + ["--password-from-stdin"] stdin_data = SVN_PASS.encode() env = {**os.environ} result = subprocess.run( cmd, cwd=cwd, capture_output=True, text=True, input=stdin_data, env=env, ) ``` ### Technical Analysis `svn_auth_cmd()` appends the plaintext password to the argument vector using `--password`. Although `run_cmd()` subsequently adds `--password-from-stdin`, it does not remove the existing `--password` argument. Consequently, the password is transmitted through both stdin and the process argument vector. The comment claiming that the stdin mechanism avoids command-line exposure is inaccurate. On systems where process arguments are visible through `/proc`, process-monitoring tools, audit logs, or orchestration telemetry, another local user or monitoring service may capture the SVN password while the command is running. ### Attack Path 1. An operator configures `CODE_REVIEW_SVN_USER` and `CODE_REVIEW_SVN_PASS`. 2. A scan, synchronization, or repository information operation invokes an SV ...[truncated 808 chars]
Remediation
## Remediation Suggestions - Remove `--password` and its value from `svn_auth_cmd()`. - Implement exactly one authentication path in `run_cmd()`, using `--password-from-stdin` for supported SVN versions. - Detect the installed SVN version and fail securely rather than falling back to plaintext process arguments. - Use a dedicated read-only SVN account with access only to repositories that must be scanned. - Prevent credentials from being included in debug output, exceptions, process telemetry, and audit logs. - Add an automated test that inspects the final argument list and verifies that the password value never appears in it.

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/check_dependencies.py:131
Finding
Unsigned Remote Composer Installer Executed from a Predictable Temporary Path## Vulnerability Details **File Location**: `scripts/check_dependencies.py`, lines 131-154 **Vulnerability Type**: Remote payload execution and unsafe temporary-file handling **Risk Level**: High ### Vulnerable Code ```python def _install_composer() -> bool: """Download the Composer installer to /tmp, execute it, and clean it up.""" import urllib.request installer_url = "https://getcomposer.org/installer" installer_path = "/tmp/composer-setup.php" print(f"[INFO] Downloading composer installer to {installer_path}...") try: urllib.request.urlretrieve(installer_url, installer_path) except Exception as e: print(f"[ERROR] Failed to download composer: {e}") return False print("[INFO] Running composer installer...") result = subprocess.run( ["php", installer_path, "--", "--install-dir=/usr/local/bin", "--filename=composer"], capture_output=True, text=True, ) try: os.remove(installer_path) except OSError: pass ``` ### Technical Analysis The installation routine downloads executable PHP code from a mutable external URL and runs it without validating a published signature or expected cryptographic hash. HTTPS provides transport protection but does not establish that the retrieved installer is the exact artifact reviewed or expected by the application. The fixed path `/tmp/composer-setup.php` also creates a local race and symlink risk. Shared temporary directories are commonly writable by unprivileged users. An attacker may attempt to interfere with the predictable path between download and execution, or redirect writes where filesystem protections permit it. The installer targets `/usr/local/bin`, so this operation is likely to be run with elevated permissions. Any compromise of the retrieved script or temporary-file workflow would therefore execute with the privileges of the insta ...[truncated 1293 chars]
Remediation
## Remediation Suggestions - Prefer a Composer package supplied and authenticated by the operating system's package manager. - If the upstream installer is required, retrieve its published signature or expected SHA-384 hash through an authenticated process and verify it before execution. - Abort installation on any integrity-verification failure. - Create the temporary file with Python's `tempfile` facilities using exclusive creation and restrictive permissions. - Avoid predictable shared paths and reject symlinks. - Run installation inside an isolated container or build environment rather than on the host. - Drop privileges before executing downloaded content and avoid writing to `/usr/local/bin` unless explicitly authorized. - Pin the Composer version rather than accepting an unspecified current release.

T08 · Insecure Dependencies

Warning
Location
scripts/check_dependencies.py:12
Finding
Unpinned Dependencies Installed Globally and into the System Python Environment## Vulnerability Details **File Location**: `scripts/check_dependencies.py`, lines 12-44 and 95-128 **Vulnerability Type**: Insecure dependency installation and excessive host modification **Risk Level**: Medium ### Vulnerable Code ```python REQUIRED_TOOLS = { "svn": { "help": "SVN client", "install": "apt-get install subversion", }, "bandit": { "help": "Python security scanner", "install": "pip install --break-system-packages bandit", }, "pylint": { "help": "Python code checker", "install": "pip install --break-system-packages pylint", }, "npx": { "help": "Node.js package executor", "install": "npm install -g npx", }, "phpcs": { "help": "PHP code standards checker", "install": "composer global require squizlabs/php_codesniffer", "deps": ["composer"], }, "typescript-eslint": { "help": "ESLint TypeScript parser", "install": "npm install -g @typescript-eslint/parser " "@typescript-eslint/eslint-plugin typescript-eslint", }, } def install_tool(name: str) -> bool: if name not in REQUIRED_TOOLS: print(f"[ERROR] Unknown tool: {name}") return False info = REQUIRED_TOOLS[name] install_cmd = info["install"] if name == "composer": return _install_composer() result = subprocess.run( shlex.split(install_cmd), capture_output=True, text=True, ) ``` ### Technical Analysis The installation specifications do not pin exact package versions or integrity hashes. Python packages are installed using `--break-system-packages`, while npm and Composer packages are installed globally. These choices modify shared host environments rather than creating a dependency boundary dedicated to the scanner. Package installation can execute setup logic, ...[truncated 1499 chars]
Remediation
## Remediation Suggestions - Install Python tools in a dedicated virtual environment and remove `--break-system-packages`. - Install Node.js tools as project-local development dependencies rather than with `npm -g`. - Use a project-local Composer directory instead of the global Composer environment. - Pin exact versions for every direct dependency and commit appropriate lockfiles. - Use package-manager integrity controls, such as Python hashes and npm lockfile integrity values. - Review and constrain transitive dependencies. - Disable package lifecycle scripts unless a reviewed dependency explicitly requires them. - Perform installation and scanning in a non-root container with read-only source mounts and restricted network access. - Separate dependency installation from routine scan execution and require explicit operator approval.

T03 · Remote Payload Retrieval and Execution

Warning
Location
scripts/analyzer_runner.py:151
Finding
Runtime npx Invocation May Retrieve and Execute an Unpinned ESLint Package## Vulnerability Details **File Location**: `scripts/analyzer_runner.py`, lines 151-174 **Vulnerability Type**: Dynamic dependency retrieval during repository scanning **Risk Level**: Medium ### Vulnerable Code ```python def run_eslint(base_path: str) -> Dict[str, Any]: """Run ESLint and TypeScript checks with the bundled configuration.""" import os env = {**os.environ, "NODE_PATH": "/usr/lib/node_modules"} cmd = [ "npx", "eslint", "src", "--ext", ".ts,.tsx", "--no-config-lookup", "-c", ESLINT_CONFIG, "--format=json", ] result = subprocess.run( cmd, capture_output=True, text=True, cwd=base_path, timeout=120, env=env, ) ``` ### Technical Analysis The dependency checker verifies the presence of `npx` and the TypeScript parser, but it does not establish that a reviewed, pinned ESLint executable is locally installed. Depending on the installed npm/npx version and configuration, `npx eslint` may resolve and download ESLint from the npm registry when no suitable local or cached binary exists. This makes routine scanning a potential remote code retrieval event. The resolved package version is not pinned, and installation or execution behavior can change after the Skill itself has been reviewed. The subprocess inherits the surrounding environment and runs in the checked-out repository's directory, increasing exposure of repository data and environment variables to the resolved tool. ### Attack Path 1. The host has `npx` and the TypeScript parser but lacks a local ESLint binary. 2. A React or TypeScript repository scan invokes `npx eslint`. 3. `npx` resolves an ESLint package from the configured npm registry. 4. The package or one of its dependencies is downloaded without a project lockfile controlling the version. 5. Package lifecycle or runtime code executes as the scanning accoun ...[truncated 519 chars]
Remediation
## Remediation Suggestions - Declare ESLint as an exact-version, project-local dependency. - Commit and enforce a lockfile with integrity metadata. - Invoke the exact local executable from `node_modules/.bin/eslint`. - If `npx` must be retained, use a no-download option such as `npx --no-install eslint` and fail when the binary is unavailable. - Validate the installed ESLint version before every scan. - Run analyzers in a restricted container with no registry access during routine scanning. - Pass a minimal environment to subprocesses instead of inheriting all environment variables. - Use a read-only repository mount where analyzer operation permits it.
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (40)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This variant highlights that the described skill performs privileged host modifications and executes remotely downloaded installer code while presenting itself as a code review tool. That mismatch is security-relevant because users may authorize a benign-seeming scanner without realizing it alters the system globally and runs external installation commands.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This variant highlights that the described skill performs privileged host modifications and executes remotely downloaded installer code while presenting itself as a code review tool. That mismatch is security-relevant because users may authorize a benign-seeming scanner without realizing it alters the system globally and runs external installation commands.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This variant highlights that the described skill performs privileged host modifications and executes remotely downloaded installer code while presenting itself as a code review tool. That mismatch is security-relevant because users may authorize a benign-seeming scanner without realizing it alters the system globally and runs external installation commands.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This variant highlights that the described skill performs privileged host modifications and executes remotely downloaded installer code while presenting itself as a code review tool. That mismatch is security-relevant because users may authorize a benign-seeming scanner without realizing it alters the system globally and runs external installation commands.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- id: composer
        kind: system
        label: "Install Composer (PHP package manager) — ⚠️ download then execute, not pipe"
        command: "curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php && php /tmp/composer-setup.php -- --install-dir=/usr/local/bin --filename=composer && rm -f /tmp/composer-setup.php"
        note: "⚠️ Downloads installer to /tmp first before executing. Review the command before running; prefer isolated environment."
      - id: phpcs
        kind: composer
Confidence
97% confidence
Finding
The skill includes a command that downloads an installer from the network and executes it with php on the host. Even though it avoids piping directly to the interpreter, it still trusts remote code at execution time without integrity verification, which can lead to arbitrary code execution if the source, transport, or environment is compromised.

Chaining Abuse

High
Category
Tool Misuse
Content
- id: composer
        kind: system
        label: "Install Composer (PHP package manager) — ⚠️ download then execute, not pipe"
        command: "curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php && php /tmp/composer-setup.php -- --install-dir=/usr/local/bin --filename=composer && rm -f /tmp/composer-setup.php"
        note: "⚠️ Downloads installer to /tmp first before executing. Review the command before running; prefer isolated environment."
      - id: phpcs
        kind: composer
Confidence
90% confidence
Finding
The chained shell command combines download, execution, and cleanup in a single line, reducing auditability and making it easier to overlook the high-risk execution step. Chaining itself is not the root issue, but in this context it compounds the danger of remote-code execution and makes safe review, error handling, and operator confirmation harder.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
["node", "-e", "require('@typescript-eslint/parser')"],
            capture_output=True,
            text=True,
            env={**os.environ, "NODE_PATH": "/usr/lib/node_modules"},
        )
        return result.returncode == 0
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
["node", "-e", "require('@typescript-eslint/parser')"],
            capture_output=True,
            text=True,
            env={**os.environ, "NODE_PATH": "/usr/lib/node_modules"},
        )
        return result.returncode == 0
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
# SVN 1.10+ 支持 --password-from-stdin
        cmd = cmd + ["--password-from-stdin"]
        stdin_data = SVN_PASS.encode()
        env = {**os.environ}

    result = subprocess.run(
        cmd,
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares capabilities that involve shell execution, file access, environment variables, and network activity, but it does not define an explicit tool scope such as permissions or allowed-tools. In a skill that installs packages, reads credentials, accesses SVN, and sends reports externally, missing scope boundaries increases the risk of unintended or excessive host actions if the skill is invoked broadly.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill advertises automatic delivery of scan reports to a Feishu group but does not warn that findings may include code snippets, file paths, vulnerability details, or other sensitive repository-derived information. Sending these results to an external chat destination can create an unintended data exfiltration path if the target chat, bot, or webhook is misconfigured or broadly accessible.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The instructions tell users to copy repository configuration containing internal SVN URLs and local checkout paths into /tmp, which is commonly world-readable or otherwise accessible by other local users and processes. This can expose internal infrastructure details and enable tampering or information disclosure, especially on shared systems.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The usage example instructs running `npx eslint` without pinning an exact package version, which can cause execution of whatever version npm resolves at runtime. In a security scanning skill, this is risky because tool behavior and dependency trees can change unexpectedly, and in some environments it may fetch and execute unreviewed code from the registry.

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.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The module docstring states it supports "Django/Python、React+TS、PHP、Java", and main file discovery includes .java files. However, scan_repo only dispatches python/django, react, php, or mixed modes, with no Java analyzer branch at all, so Java repositories or files are effectively collected but not reviewed.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstring is written entirely in Chinese and states the skill purpose only in Chinese, which imposes a language expectation without offering any user choice or opt-in. The policy for this audit flags language or locale constraints when they are forced and not explicitly documented as optional or region-specific.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_bandit(base_path: str) -> Dict[str, Any]:
    """运行 Bandit 安全扫描 - 只返回高置信度结果"""
    result = subprocess.run(
        ["bandit", "-r", base_path, "-f", "json"],
        capture_output=True,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_pylint_heavy(base_path: str) -> Dict[str, Any]:
    """Pylint 只报严重错误(语法/导入/逻辑错误),不报风格问题"""
    result = subprocess.run(
        ["pylint",
         "--output-format=json",
         "--disable=all",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
for f in files:
            if f.endswith(".py"):
                path = os.path.join(root, f)
                rc = subprocess.run(["python3", "-m", "py_compile", path],
                                    capture_output=True, text=True)
                if rc.returncode != 0:
                    # 提取行号
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"--format=json",
    ]

    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return {"issues": [], "summary": "No PHP files"}

    # 只报 ERROR 级别
    result = subprocess.run(
        ["phpcs", "--standard=PSR12", "--severity=5,4,3", "-q"] + php_files,
        capture_output=True,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""检查工具是否存在"""
    # 特殊处理 Node.js 模块(@scope/package-name 形式)
    if name == "typescript-eslint":
        result = subprocess.run(
            ["node", "-e", "require('@typescript-eslint/parser')"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
)
        return result.returncode == 0

    result = subprocess.run(
        ["which", name],
        capture_output=True,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
A dependency-check script is expected to inspect prerequisites, but this one also installs software globally and downloads then executes a remote installer. In the context of an agent skill, that broadens its authority from inspection to host mutation and code execution, which is dangerous because users may not expect or approve those side effects.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if name == "composer":
        return _install_composer()

    result = subprocess.run(
        shlex.split(install_cmd),
        capture_output=True,
        text=True,
Confidence
89% confidence
Finding
This code executes installation commands that modify the host environment, including global package installation via pip and npm. Although the command source is an internal mapping rather than direct user input, the capability is risky in an agent skill because it performs privileged system changes and may fetch and run unpinned code from external registries, expanding the attack surface and enabling supply-chain compromise.

Static analysis

No suspicious patterns detected.