Back to skill

Security audit

Environment Promoter

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a read-only environment promotion helper, but it can expose secrets from .env files and includes unsafe command patterns that deserve manual review before installation.

Review before installing. Avoid running its compare command on production .env files until redaction is fixed, do not pass untrusted file paths, and require explicit approval before any validation command contacts endpoints found in config files.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:22
Finding

Arbitrary Python Code Execution Through Unsafely Interpolated File Paths

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 22-47
Vulnerability Type: Python code injection through dynamic source construction
Risk Level: High

Vulnerable Code

bash
ENV_SOURCE="${1:-.env.development}"
ENV_TARGET="${2:-.env.staging}"

if [ ! -f "$ENV_SOURCE" ] || [ ! -f "$ENV_TARGET" ]; then
  echo "Looking for environment files..."
  find . -maxdepth 3 -name ".env*" -not -path '*/node_modules/*' 2>/dev/null | sort
fi

python3 -c "
import sys

def parse_env(path):
    vars = {}
    try:
        for line in open(path):
            line = line.strip()
            if not line or line.startswith('#'): continue
            if '=' in line:
                key, val = line.split('=', 1)
                vars[key.strip()] = val.strip().strip('\"').strip(\"'\")
    except FileNotFoundError:
        print(f'File not found: {path}')
    return vars

source = parse_env('$ENV_SOURCE')
target = parse_env('$ENV_TARGET')

Technical Analysis

The source and target paths originate from positional arguments and are expanded directly inside the source text supplied to python3 -c. Shell quoting around the command does not make the expanded values safe Python string literals.

An attacker-controlled path containing a single quote, Python statements, and a comment marker can terminate the parse_env argument, inject additional statements, and comment out the remaining characters on that line. The Python interpreter then executes the injected statements with the privileges of the process invoking the Skill.

The preceding -f check does not prevent exploitation. It only prints a list when a path does not exist and does not terminate execution or sanitize the path before it reaches python3 -c.

Attack Path

  1. An attacker influences either environment-file argument passed to the comparison command.

  2. The attacker supplies a value shaped like:

    text
    x'); 
    

...[truncated 971 chars]

Remediation
View remediation

Remediation Suggestions

Never interpolate file paths or other untrusted values into executable source code. Pass them as ordinary arguments:

bash
python3 - "$ENV_SOURCE" "$ENV_TARGET" <<'PY'
import sys

env_source = sys.argv[1]
env_target = sys.argv[2]

source = parse_env(env_source)
target = parse_env(env_target)
PY

In addition:

  1. Exit with a nonzero status if either requested file does not exist.
  2. Validate that each input resolves to an expected regular file.
  3. If access should be limited to the project, resolve canonical paths and ensure they remain under the permitted project directory.
  4. Avoid eval, generated scripts, or shell re-parsing as alternative implementations.
  5. Add regression tests using paths containing quotes, semicolons, newlines, comment markers, and command-like text.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:57
Finding

Environment Secrets Can Be Disclosed Because Masking Examines Values Instead of Variable Names

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 57-84
Vulnerability Type: Insufficient redaction of sensitive configuration values
Risk Level: Medium

Vulnerable Code

python
# Mask sensitive values
def mask(val):
    sensitive = ['KEY', 'SECRET', 'TOKEN', 'PASSWORD', 'PASS', 'AUTH', 'CREDENTIAL']
    if any(s in val.upper() for s in sensitive) and len(val) > 4:
        return val[:2] + '***' + val[-2:]
    return val

print(f'Comparing: $ENV_SOURCE → $ENV_TARGET')
print(f'Total keys: {len(all_keys)} | Same: {len(same)} | Changed: {len(changed)} | Added: {len(added)} | Missing in target: {len(removed)}')
print()

if added:
    print('🟢 In source, missing in target (need to add):')
    for k in added:
        print(f'  + {k}={mask(source[k])}')
    print()

if removed:
    print('🔴 In target, missing in source (may need removal):')
    for k in removed:
        print(f'  - {k}={mask(target[k])}')
    print()

if changed:
    print('🟡 Different values:')
    for k in changed:
        print(f'  ~ {k}:')
        print(f'    source: {mask(source[k])}')
        print(f'    target: {mask(target[k])}')

Technical Analysis

The masking function receives only a value and searches that value for words such as PASSWORD, TOKEN, or SECRET. Sensitive environment-variable values ordinarily do not contain their credential type. For example, the value of DATABASE_PASSWORD=hunter2 does not contain the word PASSWORD, so it is returned and printed unchanged.

The variable name is available as k, but it is not supplied to the masking function. Consequently, common fields such as API_TOKEN, DATABASE_PASSWORD, AWS_SECRET_ACCESS_KEY, and CLIENT_SECRET can be disclosed whenever they are added, removed, or changed between environments.

Even when a value happens to contain a marker word, the implementation retains its first and last two characters. Those fragme ...[truncated 1284 chars]

Remediation
View remediation

Remediation Suggestions

Redaction decisions must primarily use the environment-variable name, not the value:

python
import re

SENSITIVE_KEY = re.compile(
    r'(^|_)(KEY|SECRET|TOKEN|PASSWORD|PASS|AUTH|CREDENTIAL|PRIVATE|CERT)($|_)',
    re.IGNORECASE,
)

def display_value(key, value):
    if SENSITIVE_KEY.search(key):
        return '[REDACTED]'
    return value

Update every output call to include the key:

python
print(f'  + {k}={display_value(k, source[k])}')
print(f'  - {k}={display_value(k, target[k])}')
print(f'    source: {display_value(k, source[k])}')
print(f'    target: {display_value(k, target[k])}')

Further hardening should include:

  1. Fully redact sensitive values rather than retaining prefixes or suffixes.
  2. Support a configurable allowlist or denylist for project-specific secret names.
  3. Treat uncertain high-entropy values as sensitive when reports may leave the local terminal.
  4. Offer a metadata-only mode that reports whether a value changed without printing either value.
  5. Make metadata-only output the default for CI, JSON, Markdown, and Agent-generated reports.
  6. Add tests covering common credential names whose values contain none of the marker words.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (5)

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The skill reads and compares .env files and then prints variable values with only weak masking. The masking logic checks the value for sensitive keywords instead of the variable name, so secrets like DATABASE_URL, API_KEY, TOKEN, or passwords may be exposed in output almost in full if the secret string itself lacks those words. Given the skill's purpose, it is likely to process production credentials, making disclosure particularly dangerous.

Content

Scanner excerpt · SKILL.md (reported line 21)May include surrounding context.

Environment Variable Comparison

bash
# Compare .env files across environments
ENV_SOURCE="${1:-.env.development}"
ENV_TARGET="${2:-.env.staging}"

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description says the skill can 'execute safe promotions with rollback support', but the file's concrete command implementations are limited to comparison, drift inspection, validation checks, history lookup, and a sample markdown plan. No code performs deployment changes, environment updates, rollback execution, or similar promotion actions; instead, a note later states it 'Does not execute deployments'. This is a substantive mismatch between claimed capability and actual behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This markdown file defines when the skill should be used, so vague-trigger analysis applies. The trigger list includes a generic phrase without clear scope constraints or exclusion conditions, which could cause the skill to activate for unrelated comparison tasks rather than specifically environment promotion work.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The validate command extracts URLs from environment files and issues curl requests to health/status/ready endpoints automatically. That creates unannounced network side effects against live systems and can leak access patterns, trigger monitoring, or contact internal endpoints discovered in sensitive config files; in agent contexts, this is effectively a low-grade SSRF/recon behavior.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Inline documentation at L286 explicitly says 'Does not execute deployments', while the manifest at L003 says the skill can 'execute safe promotions with rollback support'. These statements cannot both be true and create a direct intent-level contradiction about whether the skill is advisory-only or operational.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.