Back to skill

Security audit

Nginx Proxy Manager

Security checks for vulnerabilities and agentic risk

Overview

This skill is not evidently malicious, but it uses live Nginx Proxy Manager admin credentials and can change or delete proxy hosts without strong safeguards.

Review before installing. Use only with a trusted NPM instance over HTTPS, prefer a least-privilege account if available, avoid shared environments, and be aware that running delete will immediately remove proxy-host configuration with no built-in confirmation or dry run. Clear or protect /root/.npm-token.json after use if token caching is not acceptable.

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
scripts/npm_client.py:10
Finding

Administrative credentials may be transmitted over plaintext HTTP

Content
View full analysis
time.time() + 3600: return data["token"] # Login to get new token url = f"{NPM_URL}/api/tokens" payload = {"identity": NPM_EMAIL, "secret": NPM_PASSWORD} req = urllib.request.Request(url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"}) try: with urllib.request.urlopen(req) as res: ``` The same unrestricted base URL is subsequently used to transmit bearer tokens: ```python def api_call(endpoint, method="GET", payload=None): token = get_token() url = f"{NPM_URL}/api/{endpoint}" headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } ``` ### Technical Analysis The client accepts `NPM_URL` directly from the environment without validating its scheme or requiring HTTPS. It sends the Nginx Proxy Manager administrator email and password to `${NPM_URL}/api/tokens`. It also sends the resulting bearer token in the `Authorization` header on later API requests. The documentation provides an HTTPS example, but the implementation still permits an operator to configure an `http://` URL. When HTTP is used, neither the credentials nor bearer token receive transport confidentiality or integrity protection. A network-positioned attacker can observe or modi ...[truncated 1611 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/npm_client.py:13
Finding

Administrative bearer token is cached without enforced restrictive permissions

Content
View full analysis
time.time() + 3600: return data["token"] # Login to get new token url = f"{NPM_URL}/api/tokens" payload = {"identity": NPM_EMAIL, "secret": NPM_PASSWORD} req = urllib.request.Request(url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"}) try: with urllib.request.urlopen(req) as res: result = json.loads(res.read().decode()) token = result["token"] # Expiration is usually ISO string "2026-01-24T18:15:57.122Z" from datetime import datetime expires_str = result["expires"].replace("Z", "+00:00") expires_at = datetime.fromisoformat(expires_str).timestamp() with open(TOKEN_FILE, "w") as f: json.dump({"token": token, "expires_at": expires_at}, f) ``` ### Technical Analysis The client persists an administrative bearer token in plaintext at the hardcoded path `/root/.npm-token.json`. The file is opened with the normal `open(..., "w")` operation, so its initial permissions depend on the process umask. The code does not explicitly create the file with mode `0600`, verify its owner, reject symbolic links, or ensure that an existing file has safe permissions. Although `/root` is normally accessible only to root, this is an environmental assumption rather than a protection enforced by the code. Container volume mounts, backups, unusual directory permissions, pe ...[truncated 1875 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Tainted flow: 'req' from os.environ.get (line 55, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/npm_client.py (reported line 31)May include surrounding context.

python
req = urllib.request.Request(url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
    
    try:
        with urllib.request.urlopen(req) as res:
            result = json.loads(res.read().decode())
            token = result["token"]
            # Expiration is usually ISO string "2026-01-24T18:15:57.122Z"

Tainted flow: 'req' from os.environ.get (line 55, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/npm_client.py (reported line 58)May include surrounding context.

python
req = urllib.request.Request(url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
    
    try:
        with urllib.request.urlopen(req) as res:
            result = json.loads(res.read().decode())
            token = result["token"]
            # Expiration is usually ISO string "2026-01-24T18:15:57.122Z"

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented behavior does not accurately match the implemented or instructed capabilities: it claims broad NPM management but includes destructive deletion while omitting key safety-critical details and implementation limits. This mismatch can mislead users or agents into invoking actions they did not intend, especially in infrastructure administration where deleting or modifying proxy hosts can disrupt exposed services.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises operational capabilities that require network access and sensitive environment variables, but it declares no explicit tool scope or permissions boundary. In an agent setting, this increases the chance of over-broad execution, making it harder to constrain or audit access to secrets, file operations, and outbound API calls.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill instructs users to provide NPM admin credentials via environment variables but gives no guidance on secure secret handling, storage, masking, or least-privilege use. Because these are administrative credentials for a reverse proxy management plane, exposure could allow an attacker to reconfigure routes, certificates, and access controls across hosted services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill documents a delete command for proxy hosts without any warning, confirmation step, or indication that the action is irreversible or service-impacting. In this context, deleting a proxy host can immediately break public routing for applications and create avoidable outages if triggered accidentally by a user or agent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The tool authenticates with stored credentials and sends them over the network without any user-facing disclosure or consent checkpoint. In an agent context, this matters because users may trigger actions without realizing their credentials are being used for live authentication against infrastructure services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The API token is cached on disk at /root/.npm-token.json without any disclosure and without visible permission hardening. If the host is shared or compromised, a reusable bearer token stored under root's home directory could be stolen and used to manage proxy hosts and certificates.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script exposes a destructive delete capability even though the skill description only mentions adding domains, pointing domains, enabling SSL, and checking status. Hidden destructive functionality increases the chance of accidental or unauthorized deletion of proxy hosts, especially when an agent or user relies on the manifest to understand the tool's scope.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The delete operation permanently removes proxy hosts with no interactive confirmation, dry-run, or explicit warning. In an automation or agent-driven environment, this makes unintended destructive actions much more likely and can cause service disruption or loss of reverse-proxy configuration.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.