Back to skill

Security audit

Multi-Environment Isolator

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly an environment scaffolder, but it generates executable scripts and production config with unsafe defaults that should be reviewed before use.

Review before installing. Use only on a trusted local project, avoid untrusted command-line values, do not run generated scripts until values are inspected, replace all JWT secrets with strong random secrets before any production use, and review npm/package lockfiles before running the generated frontend or Playwright scripts.

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/setup_envs.py:105
Finding
Shell Command Injection in Generated Startup Scripts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_envs.py:105-157`; `scripts/setup_envs_v2.py:107-198` **Vulnerability Type**: Unsanitized CLI values embedded into executable shell scripts **Risk Level**: Critical ### Vulnerable Code From `scripts/setup_envs.py`: ```python def generate_start_script(env: str, config: dict) -> str: """Generate startup shell script for a specific environment.""" env_label = {"dev": "Development", "test": "Testing", "prod": "Production"}[env] port = config[f"{env}_port"] app_module = config["app_module"] name = config["name"] user_line = "" if env == "dev" and config.get("dev_user"): user_line = f'\necho "👩‍💻 Happy coding, {config["dev_user"]}!"' elif env == "test" and config.get("test_user"): user_line = f'\necho "🧪 Happy testing, {config["test_user"]}!"' reload_flag = "\n --reload \\" if env == "dev" else "" workers = "$(nproc)" if env == "prod" else "1" workers_line = "" if env == "dev" else f"\n --workers {workers} \\" log_level = {"dev": "debug", "test": "info", "prod": "warning"}[env] return f"""#!/bin/bash # {'=' * 50} # {name} - {env_label} Environment # {'=' * 50} set -e SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" export ENV_FILE="$PROJECT_DIR/.env.{env}" echo "🚀 Starting {name} {env_label} Environment..." echo "📍 Port: {port}" echo "📍 Environment: $ENV_FILE" echo "📍 Database: ./data/{env}/"{user_line} echo "" cd "$PROJECT_DIR" [ -d "venv" ] && source venv/bin/activate uvicorn {app_module} \\ --host 127.0.0.1 \\ --port {port} \\{reload_flag}{workers_line} --log-level {log_level} """ ``` From `scripts/setup_envs_v2.py`: ```python def generate_frontend_script(env: str, config: dict) -> str: """Generate frontend startup shell script for a specific environment.""" env_label = {"dev": "Development", "test": "Testing", "prod": "Production"}[e ...[truncated 3202 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not generate executable shell source containing raw user-controlled values. 2. Apply strict allowlist validation: - Require ports to be within `1` through `65535`. - Restrict application modules to an expected Python import format such as `package.module:attribute`. - Resolve and validate `frontend_dir` as a path below the selected project directory. - Reject control characters and shell metacharacters in display-only fields. 3. Use `shlex.quote()` for every value that must be represented as a shell argument: ```python import shlex safe_app_module = shlex.quote(config["app_module"]) safe_name = shlex.quote(config["name"]) ``` 4. Prefer passing values through positional parameters or a safely parsed configuration file rather than embedding them into generated Bash. 5. For informational messages, pass the value as data: ```bash printf '%s\n' "$APP_DISPLAY_NAME" ``` 6. Add security tests using payloads containing command substitutions, quotes, semicolons, newlines, backticks, and redirection operators. 7. Treat previously generated scripts as potentially unsafe and regenerate them after implementing validation and escaping. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup_envs.py:77
Finding
Predictable Hard-Coded JWT Secrets in Generated Environments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_envs.py:77-84`; `scripts/setup_envs_v2.py:79-86` **Vulnerability Type**: Predictable cryptographic secret **Risk Level**: High ### Vulnerable Code The same pattern is present in both generators: ```python lines = [ f"# {'=' * 50}", f"# {name} - {env_label} Environment", f"# {'=' * 50}", "", "# Application", f"APP_NAME={name} ({env_label})", f"APP_ENV={s['APP_ENV']}", f"DEBUG={s['DEBUG']}", f"LOG_LEVEL={s['LOG_LEVEL']}", "", "# Server", f"HOST=127.0.0.1", f"PORT={port}", "", "# Database", f"DATABASE_URL={db_url}", "", "# Storage", f"STORAGE_TYPE=local", f"MEDIA_STORAGE_PATH=./data/{env}/media", "", "# Security", f"JWT_SECRET={env}-jwt-secret-change-me", f"JWT_ALGORITHM=HS256", f"JWT_EXPIRE_MINUTES=1440", ] ``` ### Technical Analysis Each generated environment receives a deterministic JWT signing secret: ```text dev-jwt-secret-change-me test-jwt-secret-change-me prod-jwt-secret-change-me ``` HS256 uses the same secret to create and verify a JWT signature. Anyone who knows the secret can generate tokens containing arbitrary identity, role, or authorization claims, subject to how the target application interprets those claims. The documentation warns that the production secret must be changed, but the generator creates a runnable production configuration containing the known placeholder. A warning does not prevent accidental deployment. ### Attack Path 1. An operator runs the environment generator. 2. The generated `.env.prod` contains: ```dotenv JWT_SECRET=prod-jwt-secret-change-me JWT_ALGORITHM=HS256 ``` 3. The operator starts production without replacing the placeholder. 4. An attacker obtains any valid JWT format accepted by the application or infers its expected claims. 5. The attacker creates a new token with privileged claims and signs it using the publicly known production secret. ...[truncated 637 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a unique cryptographically secure secret for every environment: ```python import secrets jwt_secret = secrets.token_urlsafe(48) ``` 2. For production, prefer obtaining the secret from a dedicated secret manager or deployment environment instead of writing it into a project-local file. 3. Make production startup fail closed if the value is absent, too short, or matches a known placeholder. 4. Never reuse secrets between development, testing, and production. 5. Restrict generated environment-file permissions to the owning user: ```python path.write_text(generate_env_file(env, config)) path.chmod(0o600) ``` 6. Add automated checks that reject values such as `change-me`, default secrets, and secrets below the required entropy or length. 7. Rotate any placeholder secret that may already have been deployed and invalidate tokens signed with it. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup_envs_v2.py:180
Finding
Automatic Execution of Unverified npm Dependencies and Lifecycle Scripts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_envs_v2.py:180-186`; `scripts/setup_envs_v2.py:210-226` **Vulnerability Type**: Unsafe dependency installation and tool resolution **Risk Level**: Medium ### Vulnerable Code Generated frontend startup script: ```python return f"""#!/bin/bash # {'=' * 50} # {name} - Frontend {env_label} Environment # {'=' * 50} set -e SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" FRONTEND_DIR="$SCRIPT_DIR/../{frontend_dir}" echo "🎨 Starting {name} Frontend {env_label} Environment..." echo "📍 Port: {port} {port_comment}" echo "📍 Directory: $FRONTEND_DIR" echo "" cd "$FRONTEND_DIR" [ -d "node_modules" ] || {{ echo "⚠️ node_modules not found. Running npm install..." npm install }} export PORT={port} {command} """ ``` Generated Playwright runner: ```python return f"""#!/bin/bash # {'=' * 50} # {name} - Playwright E2E Tests # {'=' * 50} set -e SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" FRONTEND_DIR="$SCRIPT_DIR/../{frontend_dir}" echo "🧪 Running {name} Playwright E2E Tests..." echo "📍 Test Environment: Backend 8001, Frontend 3001" echo "📍 Test Directory: $FRONTEND_DIR/tests" echo "" cd "$FRONTEND_DIR" [ -d "node_modules" ] || {{ echo "⚠️ node_modules not found. Running npm install..." npm install }} [ -f "playwright.config.ts" ] || {{ echo "❌ playwright.config.ts not found!" echo " Make sure Playwright is installed: npm install -D @playwright/test" exit 1 }} npx playwright test "$@" """ ``` ### Technical Analysis The generated scripts automatically execute `npm install` whenever `node_modules` is absent. This operation resolves packages from configured registries and may execute dependency lifecycle hooks such as `preinstall`, `install`, and `postinstall`. The scripts do not require a reviewed lockfile, do not use deterministic `npm ci`, do not suppress lifecycle scripts, and do not validate registry configuration or package i ...[truncated 1791 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not install dependencies implicitly during application startup. Exit with clear instructions and require a separate, deliberate installation step. 2. Require a reviewed lockfile and use deterministic installation: ```bash npm ci --ignore-scripts ``` 3. If specific lifecycle scripts are operationally required, document and review them before enabling scripts rather than permitting all lifecycle execution by default. 4. Pin dependency versions and enforce expected registry configuration in CI and deployment workflows. 5. Use npm audit, provenance checks, lockfile review, and dependency allowlisting as appropriate to the deployment environment. 6. Invoke an explicitly installed local Playwright executable rather than permitting automatic package retrieval: ```bash test -x ./node_modules/.bin/playwright || { echo "Playwright is not installed locally." exit 1 } ./node_modules/.bin/playwright test "$@" ``` 7. Configure npm or invoke `npx` with options that prevent installation when a local executable is unavailable. 8. Run dependency installation in a sandboxed build environment with minimal credentials and no access to production secrets. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code largely matches the core stated purpose of setting up isolated dev/test/prod environments for a FastAPI/uvicorn project: it creates environment-specific config files, backend startup scripts, data directories, documentation, and updates .gitignore. However, the declared description materially overstates functionality by claiming frontend (Vue/React) support and Playwright test integration. The supplied code only generates backend uvicorn scripts and contains no frontend-specific scaffolding, no npm/Vue/React handling, and no Playwright files, commands, or configuration. Because these are explicit, user-facing capabilities in the description rather than minor implementation details, this is a description-behavior mismatch.

Credential Access

High
Category
Privilege Escalation
Content
def generate_env_file(env: str, config: dict) -> str:
    """Generate .env file content for a specific environment."""
    settings = {
        "dev": {
            "APP_ENV": "development",
Confidence
97% confidence
Finding
The generated .env content includes hardcoded JWT secrets derived only from the environment name, such as 'prod-jwt-secret-change-me'. These are predictable default credentials, and if a user forgets to replace them, attackers could forge authentication tokens or impersonate users across environments.

Credential Access

High
Category
Privilege Escalation
Content
def generate_env_file(env: str, config: dict) -> str:
    """Generate .env file content for a specific environment."""
    settings = {
        "dev": {
            "APP_ENV": "development",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def generate_env_file(env: str, config: dict) -> str:
    """Generate .env file content for a specific environment."""
    settings = {
        "dev": {
            "APP_ENV": "development",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def generate_env_file(env: str, config: dict) -> str:
    """Generate .env file content for a specific environment."""
    settings = {
        "dev": {
            "APP_ENV": "development",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill describes capabilities that would read, write, and execute shell actions against a target project, but it does not declare any explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, that increases the chance of over-broad execution, accidental file modification, or command execution in unintended locations because reviewers and runtime policy cannot easily constrain what the skill is allowed to do.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger conditions include broad natural-language phrases like general requests about separating development and production, which can cause the skill to activate in situations broader than intended. For a skill that can write files and run shell-oriented setup actions, over-triggering increases the risk of unintended project modifications or execution in the wrong repository/context.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation includes a forceful process termination command (`kill -9 <PID>`) without warning about data loss, skipped cleanup, or the risk of killing the wrong process. In the context of environment-management instructions, users may copy-paste this during troubleshooting and inadvertently terminate unrelated services or corrupt in-flight work.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The example `pkill -f "uvicorn"` is overly broad and may terminate any process whose command line matches `uvicorn`, including unrelated development or production services on the same host. Because this skill is specifically about managing multiple environments in parallel, the chance of collateral shutdown is higher, making the guidance more dangerous in context.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script recursively creates directories and writes configuration, startup, documentation, and .gitignore files inside an arbitrary user-supplied project path. Although it skips some existing files, it still overwrites docs and may modify repository state without any confirmation, backup, dry-run mode, or explicit warning, creating a real integrity risk if pointed at the wrong directory or run in automation.

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
91% confidence
Finding
The script automatically creates multiple `.env` files containing security-sensitive settings, including JWT secrets, without clearly warning the user that credentials and environment-specific security configuration are being written to disk. In the context of a setup generator for dev/test/prod isolation, this is more dangerous because it encourages immediate operational use of generated defaults across environments, increasing the chance that placeholder secrets remain deployed.

Intent-Code Divergence

Low
Confidence
94% confidence
Finding
The generated structure lists environment-specific scripts such as `start-backend-dev.sh`, `start-frontend-dev.sh`, and `run-playwright-tests.sh` (L037-L047), but the usage sections instruct users to run `./scripts/start-dev.sh`, `./scripts/start-test.sh`, and `./scripts/start-prod.sh` (L087-L094, L105-L111), which are not listed as generated artifacts. This is an intent/documentation contradiction rather than a mere omission because the file inventory explicitly specifies what the setup creates.

Intent-Code Divergence

Low
Confidence
96% confidence
Finding
Earlier usage examples document separate backend and frontend port flags such as `--dev-backend-port`, `--dev-frontend-port`, `--test-backend-port`, and `--prod-frontend-port` (L067-L078). The later command reference instead describes different flags `--dev-port`, `--test-port`, and `--prod-port` plus database options not mentioned elsewhere (L126-L135), creating conflicting documentation about what the script actually accepts.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This code modifies the project's .gitignore file, which is a persistent file write affecting repository behavior. It reports completion after the fact, but there is no prior disclosure in the script description or usage text that running the tool will change version-control ignore rules.

Static analysis

No suspicious patterns detected.