Back to skill

Security audit

Wordpress REST API

Security checks for vulnerabilities and agentic risk

Overview

This WordPress automation skill is coherent, but it can send reusable credentials over non-HTTPS URLs and can modify or delete live site content without confirmation safeguards.

Install only if you are comfortable giving the skill a WordPress credential that can affect your site. Use HTTPS-only WordPress URLs, a dedicated low-privilege account, and avoid giving it Editor/Admin credentials unless needed. Treat delete and raw request commands as high-risk and require human review before running them against production.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wp-cli.js:61
Finding

WordPress Credentials May Be Transmitted over Plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: scripts/wp-cli.js:61-67, 91
Vulnerability Type: Missing HTTPS enforcement for authenticated requests
Risk Level: High

Vulnerable Code

js
function resolveBaseUrl() {
  const base = process.env.WP_BASE_URL;
  if (!base) {
    console.error('Missing WP_BASE_URL. Example: https://example.com');
    process.exit(1);
  }
  return base.replace(/\/$/, '');
}
js
const response = await fetch(buildApiUrl(path, query), options);

Authentication headers are constructed from reusable credentials before the request:

js
function buildAuthHeader() {
  const basicToken = process.env.WP_BASIC_TOKEN;
  if (basicToken) {
    return { Authorization: `Basic ${basicToken}` };
  }

  const user = process.env.WP_USER;
  const appPassword = process.env.WP_APP_PASSWORD;
  if (user && appPassword) {
    const token = Buffer.from(`${user}:${appPassword}`).toString('base64');
    return { Authorization: `Basic ${token}` };
  }

  const jwt = process.env.WP_JWT_TOKEN;
  if (jwt) {
    return { Authorization: `Bearer ${jwt}` };
  }

  return {};
}

Technical Analysis

WP_BASE_URL is accepted without validating its URL scheme. The request code attaches an Authorization header containing either a Basic authentication token, a Base64-encoded username and application password, or a JWT bearer token. It then sends the request to the configured URL using fetch().

Base64 encoding does not encrypt Basic authentication credentials. If WP_BASE_URL uses http:, credentials, JSON request bodies, and server responses may be visible to network-positioned attackers. Documentation recommends HTTPS, but a documentation-only recommendation does not prevent insecure runtime configuration. The Skill description in SKILL.md also refers to “plain HTTP,” increasing the risk of unsafe interpretation.

Sending credentials to the operator-con ...[truncated 1765 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse and validate WP_BASE_URL before constructing requests:

    js
    function resolveBaseUrl() {
      const configured = process.env.WP_BASE_URL;
      if (!configured) {
        throw new Error('Missing WP_BASE_URL. Example: https://example.com');
      }
    
      const url = new URL(configured);
      if (url.protocol !== 'https:') {
        throw new Error('WP_BASE_URL must use HTTPS.');
      }
    
      url.pathname = url.pathname.replace(/\/+$/, '');
      return url.toString().replace(/\/$/, '');
    }
    
  2. Reject all schemes other than https:. If plaintext HTTP is required for local development, require an explicit opt-in and restrict it to loopback hosts such as 127.0.0.1, ::1, or localhost.

  3. Do not silently follow cross-origin redirects while retaining authentication. Implement explicit redirect handling and ensure credentials are never forwarded to a different origin.

  4. Update SKILL.md to replace “using plain HTTP” with wording that clearly requires HTTPS for authenticated remote connections.

  5. Continue recommending dedicated, minimally privileged WordPress accounts and short-lived or readily revocable credentials.

  6. Add automated tests confirming that http:// remote URLs and non-HTTP schemes are rejected before authentication headers are transmitted.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (9)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Documenting a raw DELETE /posts/{id} operation without guardrails enables destructive parameter use, where an agent or user can delete arbitrary posts if given an ID. Because this skill is a plain HTTP CLI for the WordPress REST API, the context makes parameter abuse more dangerous: a single mistaken or manipulated identifier can permanently affect live content.

Content

Scanner excerpt · assets/wordpress-rest-api-guide.md (reported line 18)May include surrounding context.

md
- Get: `GET /posts/{id}`
- Create: `POST /posts`
- Update: `POST /posts/{id}`
- Delete: `DELETE /posts/{id}`

Common fields:
- `title`, `content`, `excerpt`

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

The documented DELETE /pages/{id} endpoint exposes the same destructive parameter-abuse risk for site pages, which are often more business-critical than posts. In this skill's context, an automation agent using plain HTTP against the live REST API could remove important navigational or landing pages through an incorrect, unverified, or attacker-influenced page ID.

Content

Scanner excerpt · assets/wordpress-rest-api-guide.md (reported line 31)May include surrounding context.

md
- Get: `GET /pages/{id}`
- Create: `POST /pages`
- Update: `POST /pages/{id}`
- Delete: `DELETE /pages/{id}`

### Categories
- List: `GET /categories`

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises network and environment-variable based operation but does not declare any explicit tool scope or permission boundaries. In an agent environment, this can lead to overbroad execution where the skill can read credentials from the environment and make outbound HTTP requests without clear user-visible constraints, increasing the chance of secret misuse or unintended remote actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The guide explicitly documents create, update, and delete operations against live WordPress content but provides no caution about destructive effects, confirmation requirements, or safe usage boundaries. In an agent skill context, this increases the chance that an automated system will perform unintended content modification or deletion on production sites.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script automatically attaches Basic or Bearer authentication headers from environment variables to outbound requests without any runtime disclosure or confirmation. In an agent setting, that can cause users to unknowingly send privileged credentials to a configured WordPress host, increasing the chance of silent privileged actions or credential misuse against an unintended target.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The CLI includes a generic request <method> <path> primitive that can access arbitrary WordPress REST endpoints, not just the post/page/category/tag/user operations described by the skill. In an agent skill context, this broadens capability beyond least privilege and can enable unintended reads, writes, or plugin-specific administrative actions whenever credentials are present.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

posts:delete performs immediate deletion through the authenticated REST API with no confirmation prompt, dry-run, or safety interlock. In an automated or agent-driven workflow, a mistaken invocation can irreversibly remove content, especially when force=true is supplied.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

pages:delete issues authenticated delete requests without any warning or confirmation, making destructive content removal easy to trigger accidentally. This is particularly risky for agent-operated tools because a malformed prompt or automation bug can delete published pages with little visibility before execution.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The request command accepts attacker-controlled HTTP methods and paths, allowing use of the skill as a general authenticated WordPress API client rather than a narrowly scoped content-management tool. That mismatch increases risk because an agent or downstream caller can reach sensitive endpoints, including custom plugin routes, with stored credentials.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/wp-cli.js:48