Back to skill

Security audit

WordPress Publisher Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real WordPress publisher, but it handles powerful site credentials and can publish or modify live content with insufficient safety boundaries.

Review carefully before installing on a production WordPress site. Use a dedicated least-privileged WordPress account, avoid passing application passwords on the command line, publish drafts first, confirm every live publish/update/delete action, and do not publish untrusted HTML or links without sanitization.

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

Warning
Location
scripts/wp_publisher.py:835
Finding

WordPress application password exposed through command-line arguments

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/content_to_gutenberg.py:29
Finding

Unsanitized HTML and URL attributes can produce active content in published WordPress posts

Content
View full analysis
str: """Escape HTML special characters while preserving existing HTML tags.""" # Don't escape if it looks like it already has HTML if re.search(r'<(strong|em|a|code|br|span)[^>]*>', text): return text return html.escape(text, quote=False) ``` ```python # Line 60: URL and link text are inserted without attribute-safe validation text = re.sub( r'\[([^\]]+)\]\(([^)]+)\)', r'\1', text ) ``` ```python # Lines 339-343: image URL is inserted directly into a quoted attribute img_match = re.match(r'!\[([^\]]*)\]\(([^)]+)\)', line.strip()) if img_match: alt = escape_html(img_match.group(1)) src = img_match.group(2) blocks.append( f'\n' f'{alt}\n' f'' ) ``` ```python # HTML conversion beginning at line 405 preserves input markup def html_to_gutenberg(content: str) -> str: blocks = [] pattern = r'(<(?:p|h[1-6]|ul|ol|blockquote|pre|table|figure|div)[^>]*>.*?)' parts = re.split(pattern, content, flags=re.DOTALL | re.IGNORECASE) for part in parts: part = part.strip() if not part: continue p_match = re.match( r']*>(.*?)

', part, re.DOTALL | re.IGNORECASE ) if p_match: blocks.append( f'\n' f'

{p_match.group(1)}

\n' f'
Remediation
View remediation
str: value = value.strip() parsed = urlparse(value) if parsed.scheme.lower() not in {'https', 'http'}: raise ValueError('Unsupported URL scheme') return html.escape(value, quote=True) ``` 4. Escape every attribute value with `html.escape(value, quote=True)` after semantic validation: ```python safe_src = safe_url(src) safe_alt = html.escape(alt, quote=True) image = f'{safe_alt}' ``` 5. Replace regex-based HTML reconstruction with an HTML parser. Regular expressions cannot safely distinguish nested tags, malformed attributes, comments, encoded payloads, or parser differentials. 6. Remove the early return in `escape_html()`. If limited inline markup must be supported, parse and rebuild only allowlisted tags and attributes instead of treating the whole string as safe. 7. Sanitize link text and all paragraph content consistently before applying controlled formatting transformations. 8. Add regression tests for: - Quotes embedded in Markdown link and image destinations. - `javascript:` ...[truncated 478 chars]
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (35)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The code accurately supports only one subset of the description: generating Gutenberg-compatible block markup, including tables, images, lists, headings, quotes, code blocks, and some helper block creators. However, the declared purpose centers on direct WordPress publishing and related CMS features. There is no REST API usage, no HTTP/network access, no authentication, no category retrieval, no SEO tag generation, and no preview/publish workflow. Therefore the description materially overstates the implemented behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

There is a clear mismatch between the declared description and the provided code. The description claims substantial WordPress publishing and content-conversion functionality, but the actual code chunk is only a placeholder test package file (tests/__init__.py) with no implementation. No declared capabilities are evidenced in the supplied code, so the description does not accurately represent the behavior of this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code chunk focuses narrowly on converting content into Gutenberg-compatible block markup and validating that markup. That partially aligns with the declared feature of converting markdown/HTML to Gutenberg blocks, including tables, images, lists, and formatting. However, the declared primary purpose is broader and centered on direct WordPress publishing through the REST API, plus category management, SEO tag generation, and preview functionality. None of those capabilities appear in this code. Because the actual code only implements/testing conversion helpers rather than the advertised publishing workflow, the description materially overstates what this code chunk does.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `references/gutenberg-blocks.md` - Block format reference

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · references/gutenberg-blocks.md (reported line 10)May include surrounding context.

Every Gutenberg block follows this pattern:

html
<!-- wp:block-name {"attribute":"value"} -->
<html-content>Content here</html-content>
<!-- /wp:block-name -->

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This markdown file documents WordPress REST API operations including authentication and creating, updating, deleting, and publishing content, which can affect live site data. There is no accompanying warning in the file about modifying remote content, using credentials, or the risk of unintended publication/deletion.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 17)May include surrounding context.

md
- **Gutenberg Block Support**: Full conversion of Markdown/HTML to WordPress Gutenberg blocks
- **Smart Category Selection**: Auto-load categories from your site and intelligently match content
- **SEO Tag Generation**: Automatically generate relevant tags for better discoverability
- **Preview Workflow**: Create drafts, preview, then publish with confidence
- **Media Management**: Upload and attach featured images
- **Scheduled Publishing**: Schedule posts for future publication
- **CLI Support**: Use from command line for automation

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README documents direct publishing, including status="publish", but does not prominently warn that these actions can immediately create or modify live content on a production WordPress site. In a skill designed to operate against remote CMS instances, missing safety guidance increases the chance of accidental publication, unauthorized production changes by confused users, or misuse in the wrong environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The CLI example shows passing site URL, username, and application password directly to a command that can publish content remotely, but it omits a clear warning about sending credentials to WordPress and potentially creating or modifying posts. This is dangerous because users may run the command against production without realizing its side effects or may expose secrets through shell history and process listings.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill demonstrates network access to a live WordPress site and references local script modules, but it does not declare any explicit tool scope such as allowed network domains, file access boundaries, or required permissions. That makes the operational boundary ambiguous and can enable overbroad execution, especially where credentials are handled and content can be published to external systems.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The invocation text includes broad triggers such as 'publish to blog' and 'create WordPress article,' which can cause the skill to activate on common user requests without sufficiently narrow consent or destination scoping. In a skill that can authenticate and publish to external infrastructure, overbroad invocation increases the risk of unintended posting, credential requests in the wrong context, or misuse of a connected site.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/wp_publisher.py (reported line 84)May include surrounding context.

python
upload_headers = {"Authorization": self.headers["Authorization"]}
                        response = requests.post(url, headers=upload_headers, files=files, data=data, timeout=60)
                    else:
                        response = requests.post(url, headers=self.headers, json=data, timeout=30)
                elif method == 'PUT':
                    response = requests.put(url, headers=self.headers, json=data, timeout=30)
                elif method == 'DELETE':

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/wp_publisher.py (reported line 86)May include surrounding context.

python
else:
                        response = requests.post(url, headers=self.headers, json=data, timeout=30)
                elif method == 'PUT':
                    response = requests.put(url, headers=self.headers, json=data, timeout=30)
                elif method == 'DELETE':
                    response = requests.delete(url, headers=self.headers, params=params, timeout=30)
                else:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code file supports a safety-critical operation: setting --status publish causes content to be posted live to a WordPress site. While success output is shown after the fact, there is no confirmation prompt or explicit warning near the publish-status option that choosing publish will make content publicly visible and modify the remote site immediately.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The changelog states that the skill can upload images and files and set featured images, which involves sending user-provided content to a remote service. The markdown does not warn users about external transmission of files or the potential privacy implications.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This markdown file documents use of the raw HTML block and provides an iframe example, which can affect privacy, load third-party content, and bypass safer structured blocks. The section does not include any warning about only embedding trusted sources or the implications of rendering arbitrary external content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The markdown describes YouTube, Twitter/X, and generic embeds, all of which can load third-party resources and expose visitor metadata to external services. There is no warning here about privacy implications, consent considerations, or restricting embeds to trusted providers.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The development dependency uses a lower-bound version specifier (pytest>=7.0.0) instead of pinning to an exact version or constrained range. This weakens build reproducibility and can allow installation of unexpected or later vulnerable releases, especially relevant because the file also references advisory-bearing packages and pulls from another requirements file.

Content

Scanner excerpt · requirements-dev.txt (reported line 8)May include surrounding context.

text
-r requirements.txt

# Testing
pytest>=7.0.0
pytest-cov>=4.0.0
pytest-mock>=3.10.0
responses>=0.22.0

Unverifiable Dependency: pytest has 2 known advisory(ies) (CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The manifest does not pin pytest, so it is impossible to verify whether the installed version includes or avoids the cited advisory. This uncertainty is itself a security weakness because CI or local installs may resolve to affected versions depending on when and where dependencies are installed.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

pytest-cov is unpinned and may resolve to different versions over time, making development environments non-reproducible. While this is a dev dependency, compromised or breaking upstream releases can still affect CI, test execution, and supply-chain trust.

Content

Scanner excerpt · requirements-dev.txt (reported line 9)May include surrounding context.

text
# Testing
pytest>=7.0.0
pytest-cov>=4.0.0
pytest-mock>=3.10.0
responses>=0.22.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

pytest-mock is specified with only a minimum version, so future installs may pull in unreviewed releases. This creates a supply-chain and reproducibility risk even if the package is only used in testing.

Content

Scanner excerpt · requirements-dev.txt (reported line 10)May include surrounding context.

text
# Testing
pytest>=7.0.0
pytest-cov>=4.0.0
pytest-mock>=3.10.0
responses>=0.22.0

# Code quality

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

responses is unpinned, allowing dependency resolution to vary across environments and over time. In CI and development tooling, this can introduce unanticipated vulnerable or malicious package versions if the upstream supply chain is compromised.

Content

Scanner excerpt · requirements-dev.txt (reported line 11)May include surrounding context.

text
pytest>=7.0.0
pytest-cov>=4.0.0
pytest-mock>=3.10.0
responses>=0.22.0

# Code quality
black>=23.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

black is unpinned, and the file separately notes that black has multiple known advisories across versions. Without an exact version, installs may land on a vulnerable release or change behavior unexpectedly in developer and CI environments.

Content

Scanner excerpt · requirements-dev.txt (reported line 14)May include surrounding context.

text
responses>=0.22.0

# Code quality
black>=23.0.0
flake8>=6.0.0
isort>=5.12.0
mypy>=1.0.0

Unverifiable Dependency: black has 5 known advisory(ies) (CVE-2026-32274 (Black: Arbitrary file writes from unsanitized user input in cache file name); CVE-2024-21503 (Black vulnerable to Regular Expression Denial of Service (ReDoS)); CVE-2024-21503 (Versions of the package black before 24.3.0 are vulnerable to Regular Expression) +2 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
94% confidence
Finding

black has known advisories across some versions, but the open-ended requirement prevents verification that only safe versions will be installed. Because formatting tools run automatically in developer and CI workflows, a vulnerable version could be executed in trusted environments.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

flake8 is not pinned, which reduces reproducibility and increases supply-chain uncertainty. Although it is a code-quality tool rather than runtime code, dev-tool compromise can still affect source integrity, CI outcomes, or developer workstations.

Content

Scanner excerpt · requirements-dev.txt (reported line 15)May include surrounding context.

text
# Code quality
black>=23.0.0
flake8>=6.0.0
isort>=5.12.0
mypy>=1.0.0

Static analysis

No suspicious patterns detected.