Back to skill

Security audit

PublishGuard — Post Verification & Credential Manager

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent publishing-helper purpose, but it should be reviewed because it can persist real account credentials insecurely and verify arbitrary URLs from the agent host.

Review before installing. Do not store real platform tokens with this skill unless the credential path is fixed to use a proper secret store or audited encryption, and avoid verifying URLs that are not public posts on the intended platform. Also consider clearing audit logs because they may retain full URLs, titles, post IDs, and error details.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish_guard.py:173
Finding

Active Credential Store Persists Authentication Secrets in Plaintext

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/publish_guard.py:248
Finding

Unrestricted Post Verification URL Enables Server-Side Request Forgery

Content
View full analysis
VerifyResult: """ Verify a URL is accessible and optionally contains expected content. """ result = VerifyResult( verified=False, url=url, checked_at=time.time(), ) try: req = urllib.request.Request( url, headers={"User-Agent": "PublishGuard/1.0 (Aoineco)"} ) with urllib.request.urlopen(req, timeout=timeout) as resp: result.status_code = resp.status body = resp.read().decode('utf-8', errors='ignore') result.content_preview = body[:500] ``` The public entry point forwards the caller-controlled URL without validation: ```python def verify_post(self, url: str, platform: str = "", expected_content: str = "") -> VerifyResult: result = self.verifier.verify_url(url, expected_content) self._record_verification(platform or "unknown", url, result) return result ``` ### Technical Analysis `verify_post()` accepts an arbitrary URL and passes it directly to `urllib.request.urlopen()`. The implementation does not: - Restrict the scheme to HTTPS. - Match the destination against the selected platform's configured origin. - Block loopback, link-local, private, multicast, or reserved addresses. - Validate DNS resolution results. - Validate redirect destinations. - Limit the number of response bytes read. `urllib` normally follows HTTP redirects. Therefore, checking only an initial hostname would also be insufficient unless every redirect destination were validated. The request is necessary for the declared post-verification feature, but unrestricted network ac ...[truncated 1512 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/vault_crypto.py:36
Finding

Vault Master Key Is Derived from Predictable Machine Metadata

Content
View full analysis
bytes: """ Generate a machine-specific fingerprint. Combines hostname + username + a stable machine identifier. """ parts = [] try: import socket parts.append(socket.gethostname()) except Exception: parts.append("unknown-host") try: parts.append(os.getenv("USER", os.getenv("USERNAME", "unknown"))) except Exception: parts.append("unknown-user") parts.append(str(Path.home())) workspace = os.getenv( "OPENCLAW_WORKSPACE", os.path.expanduser("~/.openclaw/workspace") ) parts.append(workspace) combined = "|".join(parts).encode("utf-8") return hashlib.sha256(combined).digest() ``` The fingerprint is used as the default secret: ```python machine_fp = _get_machine_fingerprint() extra = extra_passphrase.encode("utf-8") if extra_passphrase else b"" self._master_key = derive_key( machine_fp + extra, b"aoineco-vault-salt-v1", iterations=200_000 ) ``` ### Technical Analysis Hostname, username, home directory, and workspace path are identifiers rather than secrets. They are commonly predictable, visible in logs, exposed through filesystem paths, or known to another local user. Hashing these values does not create entropy. The optional `extra_passphrase` defaults to an empty string. Consequently, normal use derives the vault key entirely from reproducible metadata and a fixed PBKDF2 salt. The high PBKDF2 iteration count increases attack cost but does not compensate for low-entropy, guessable inputs. The hostname access is related to the declared machine-binding feature, is local, and is not transmitted by this module. It therefore does not constitute independent e ...[truncated 1181 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish_guard.py:548
Finding

Audit Logs Persist Full URLs and Publishing Metadata Without Explicit Access Controls

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (12)

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The module documentation materially misrepresents the cryptographic design by claiming AES-256-CBC and Fernet compatibility when the code actually implements a custom HMAC-derived XOR stream cipher. This can cause operators and downstream code to rely on interoperability and security properties that do not exist, increasing the chance that sensitive credentials are stored under a homegrown scheme without proper review.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly promotes persistent credential storage and audit logging, but the user-facing description does not prominently warn that sensitive publishing data, tokens, URLs, and activity history may be retained across sessions. In an agent skill context, undisclosed persistence materially increases the risk of accidental secret retention, privacy leakage, and misuse of historical publishing metadata by later runs or other components.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The BotMadang platform configuration states that titles must contain Korean characters, and the validator enforces this by rejecting titles without Korean. This is a natural-language locale constraint presented as a hard requirement, but the skill does not offer an opt-in choice or explain a policy justification for forcing that language behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill automatically writes platform authentication data to persistent local storage without a clear user-facing warning or consent flow. This increases the chance that sensitive tokens are retained longer than intended, left behind in shared workspaces, or captured in snapshots and backups. In a multi-session agent environment, silent persistence of credentials is security-relevant because users may assume credentials are ephemeral.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The credential store persists authentication material to a JSON file in plaintext while the comments describe the location as a secure 'vault'. Any local user, compromised process, backup system, or accidental file disclosure can recover API tokens directly and use them to post or impersonate the agent on external platforms. In an agent skill whose purpose includes credential management, this materially increases the likelihood of secret exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The validator programmatically rejects content unless the title contains Korean characters, which constitutes a language/locale restriction. Because the skill does not provide a language choice or clearly frame this as a justified region-specific compliance requirement, it matches the policy-violation criterion.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The comments state that the code implements AES-256-CTR, but no AES primitive exists in the Python standard library and the actual code uses HMAC output as a keystream. Mislabeling a custom cipher as AES can mislead reviewers into approving or depending on a stronger and more standardized primitive than is actually present.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

Natural-language statements in comments and CLI output present strong security guarantees for a custom cryptographic construction, including a final message asserting the system is secure. Such blanket assurance can violate policy expectations around careful security claims because users may rely on these statements beyond what is justified or independently validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The migration routine irreversibly overwrites and deletes the plaintext credential file without confirmation, backup, or transactional safeguards. If migration fails partway, the wrong file path is supplied, or the encrypted vault is not actually usable on another system, users can lose access to credentials or destroy sensitive source data unexpectedly.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/vault_crypto.py (reported line 333)May include surrounding context.

python
# Verify permissions
        import stat
        mode = os.stat("/tmp/test_vault.vault").st_mode
        assert not (mode & stat.S_IROTH), "FAIL: File is world-readable!"
        assert not (mode & stat.S_IRGRP), "FAIL: File is group-readable!"
        print("  ✅ File permissions: owner-only (0600)")

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

Natural-language statements in comments and CLI output present strong security guarantees for a custom cryptographic construction, including a final message asserting the system is secure. Such blanket assurance can violate policy expectations around careful security claims because users may rely on these statements beyond what is justified or independently validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file states that BotMadang requires Korean in the title, which is a locale/language constraint presented as a platform-specific rule. In this skill description, that requirement is not framed as an optional user choice or clearly justified compliance limitation, so it may violate the language/locale policy criterion.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.