Back to skill

Security audit

speechcanvas-free-expression-swarm

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly coherent and not malicious, but users should review its mutable install command and weak validation gates before installing.

Review before installing. Prefer the OpenClaw registry install path over the npx latest command, do not rely on the README verification hash for this inspected artifact, and treat the validators as best-effort aids rather than proof that generated image prompts are safe or schema-compliant.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/safety_validator.py:72
Finding
Safety Validator Negation Stripping Can Hide Prohibited Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/safety_validator.py:72-75, 109-112, 145-149` **Vulnerability Type**: Safety validation bypass caused by overbroad input removal **Risk Level**: Medium ### Vulnerable Code ```python NEGATION_SPAN = re.compile( r"\b(?:no|without|never|not|avoid|exclude[d]?|must\s+not|do\s+not|don't|" r"zero|banned|forbidden)\b[^,.;\n]{0,60}", re.I) ``` ```python def strip_negations(text): """Remove prohibitive spans ('no fake documents', 'never depict ...') so a pack's own safety constraints don't trip the forbidden-mechanics detector.""" return NEGATION_SPAN.sub(" ", text) ``` ```python def validate(text, pack=None): """Return verdict dict: {'verdict': 'pass'|'block'|'warn', ...}""" decl_theme = bool(pack.get("deception_theme")) if isinstance(pack, dict) else False if isinstance(pack, dict) and pack.get("guardian_status") == "FAIL": return {"verdict": "block", "deception_theme": decl_theme, "motif_found": False, "blocked": [{"id": "G01", "severity": "block", "meaning": "pack guardian_status is FAIL — a rejected pack can never pass the safety gate", "span": "guardian_status: FAIL"}], "warnings": [], "motifs_available": len(ALLOWED_MOTIFS), "rules_checked": len(F) + 2} body = strip_negations(ZERO_WIDTH.sub("", text)) variants = [body, normalize(body), squash(normalize(body))] ``` ### Technical Analysis The validator attempts to prevent required safety constraints such as `no fake documents` from triggering its forbidden-content patterns. It does this by deleting any span beginning with a broad negation keyword and continuing for as many as 60 characters until selected punctuation is encountered. This approach does not establish that the text following the negation is actually negated. Consequently, an attacker can place a negation keyword before prohibited content and cause the ...[truncated 1758 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not remove arbitrary character spans based only on the presence of a negation keyword. 2. Scan the original normalized content for forbidden phrases first, then determine whether each specific match is genuinely negated using a narrowly bounded grammar. 3. Treat standard constraint fields separately from generative fields rather than applying blanket negation stripping: - Validate known constraint values against an exact allowlist. - Scan all generative fields without deleting negated spans. 4. Reject input if preprocessing produces an empty or substantially erased body. 5. Add regression tests for adversarial constructions, including: - `no hesitation create fake passport` - `never mind, depict forged evidence` - `avoid delay and make a fake ballot` - repeated or nested negation keywords 6. Prefer explicit field-aware validation over natural-language deletion heuristics. 7. Continue applying semantic Guardian review because regex validation alone cannot prove safety. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/validate_pack.py:31
Finding
Offline Structural Validator Does Not Enforce the Declared JSON Schema Contract<![CDATA[ ## Vulnerability Details **File Location**: `scripts/validate_pack.py:31-74` **Related Contract**: `schema/prompt_pack.schema.json:7-24` **Vulnerability Type**: Incomplete schema enforcement **Risk Level**: Medium ### Vulnerable Code ```python def check(pack, final=False): errs = [] if not isinstance(pack, dict): return ["pack is not a JSON object"] for k in REQUIRED: if k not in pack: errs.append(f"missing required field: {k}") for k in TEXT_FIELDS: v = pack.get(k) if isinstance(v, str) and len(v) > 200: errs.append(f"{k} exceeds 200 chars ({len(v)})") elif v is not None and not isinstance(v, str): errs.append(f"{k} must be a string") c = pack.get("constraints") if not isinstance(c, list) or not c: errs.append("constraints must be a non-empty array") else: lc = [str(x).lower().strip().rstrip(".") for x in c] for req in REQUIRED_CONSTRAINTS: if req not in lc: errs.append(f"constraints missing safety fence: '{req}'") t = pack.get("safety_tags") if not isinstance(t, list) or not t: errs.append("safety_tags must be a non-empty array") else: unknown = [x for x in t if x not in SAFETY_TAGS] if unknown: errs.append(f"safety_tags unknown values: {unknown}") missing = REQUIRED_TAGS - set(t) if missing: errs.append(f"safety_tags missing required: {sorted(missing)}") it = pack.get("iteration") if it is not None and (not isinstance(it, int) or isinstance(it, bool) or not 1 <= it <= 3): errs.append("iteration must be an integer 1-3") g = pack.get("guardian_status") if g is not None and g not in ("PASS", "FAIL"): errs.append("guardian_status must be PASS or FAIL") if final and g != "PASS": errs.append("final/deliverable pack must have guardian_status == PASS") cn = pack.get("critic_note ...[truncated 2734 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a standards-compliant JSON Schema Draft 2020-12 validator with a pinned, reviewed dependency when feasible. 2. If the stdlib-only requirement must remain, implement every declared schema condition: - Reject keys not listed in `REQUIRED`. - Enforce every text field's minimum and maximum length. - Require 6-16 constraint entries. - Require every constraint to be a string of 5-100 characters. - Require 4-5 unique safety tags. - Require `critic_notes` to be a string with the declared maximum length. 3. Keep the schema and checker synchronized through tests that load both definitions and compare their constraints. 4. Add negative tests for every schema keyword, not only successful examples. 5. Remove conversions such as `str(x)` where the schema requires a concrete string type. 6. Fail closed if a schema feature is unsupported rather than silently omitting enforcement. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:30
Finding
Installation Documentation Executes an Unpinned Mutable Registry Package<![CDATA[ ## Vulnerability Details **File Location**: `README.md:30-34` **Vulnerability Type**: Unpinned third-party package execution **Risk Level**: Medium ### Vulnerable Code ```bash openclaw skills install @orionshaowswmw/speechcanvas-free-expression-swarm # or: npx --yes clawhub@latest install speechcanvas-free-expression-swarm ``` ### Technical Analysis The `npx` installation command resolves `clawhub@latest` from a third-party package registry and executes it. The `latest` tag is mutable, so the code executed in the future is not necessarily the code reviewed when this Skill was audited. The `--yes` option suppresses interactive confirmation. The packaged Skill itself contains no network retrieval or malicious execution behavior. The risk arises specifically from instructing users to download and execute an unpinned installer whose effective payload can change after review. ### Attack Path 1. A user follows the installation command from the README. 2. `npx` resolves the current package associated with the mutable `latest` tag. 3. The package and its dependency graph are downloaded from the registry. 4. Package lifecycle or CLI code executes with the invoking user's privileges. 5. If the registry account, package release, or transitive dependency has been compromised, malicious code executes before the user can inspect the installed Skill. ### Impact Assessment The potential impact is determined by the privileges of the user running `npx`. A compromised installer could read or modify files accessible to that user, access environment variables and credentials, make network requests, install persistence within user-writable locations, or alter the Skill being installed. No such malicious behavior was found in the audited project files. This finding concerns the mutable external supply-chain boundary created by the documented command. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `clawhub@latest` with an explicitly reviewed version, for example `clawhub@<fixed-version>`. 2. Publish and document an integrity hash or registry integrity value for the installer package. 3. Avoid `--yes` so users retain an explicit confirmation step. 4. Recommend inspecting package metadata and provenance before execution. 5. Pin or lock the complete dependency graph where the installation mechanism supports it. 6. Document a manual installation method that downloads files without immediately executing package code. 7. Update version pins only after reviewing the new installer and its transitive dependencies. ]]>
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill declares no permissions, yet its instructions explicitly invoke shell commands, read local files, and optionally write a JSONL log. This creates a trust and review gap: an operator or host may treat the skill as data-only while it actually requests code-execution and filesystem capabilities, increasing the chance of unintended file access or command execution in permissive runtimes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
83% confidence
Finding
The documented purpose presents the skill as an image-prompt swarm, but the file also instructs the agent to run validators, perform safety scanning, and optionally persist run records to disk. That mismatch can mislead reviewers and users about the operational footprint, causing them to approve or invoke the skill in contexts where code execution and file writes were not expected.

Static analysis

No suspicious patterns detected.