Back to skill

Security audit

celebrate

Security checks for vulnerabilities and agentic risk

Overview

The skill’s purpose is coherent, but its renderer has under-disclosed unsafe config paths that can execute raw HTML/JavaScript and write files outside the intended output folder.

Review before installing. Use this only with configs and screenshots you control, avoid the undocumented headline_html and banner_headline_html fields, do not render celebration JSON from untrusted sources, and choose output/name/slug values that are simple filenames. Sensitive screenshots should be cropped locally and checked before rendering because the browser-based renderer can expose embedded proof data if hostile HTML is present.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/render_cards.py:341
Finding

Arbitrary HTML and JavaScript Injection Through Raw Headline Fields

Content
View full analysis
%s' % ( fs, self.c.get("headline_html") or esc(self.c.get("headline", "")) ) ``` ```python head = self.c.get("banner_headline_html") or ( esc(self.c.get("headline", "")) + ' · ' + esc(self.c.get("banner_suffix", "")) + '' if self.c.get("banner_suffix") else esc(self.c.get("headline", "")) ) ``` The generated page is subsequently opened in headless Chrome: ```python cmd = [ chrome, "--headless", "--disable-gpu", "--hide-scrollbars", "--force-device-scale-factor=%d" % args.scale, "--virtual-time-budget=3000", "--window-size=%d,%d" % (f["w"], f["h"]), "--screenshot=" + png_path, "file:///" + html_path.replace("\\", "/"), ] proc = subprocess.run(cmd, capture_output=True, text=True, timeout=180) ``` ### Technical Analysis Most configuration text is processed by `esc()`, but the `headline_html` and `banner_headline_html` configuration properties bypass escaping and are inserted verbatim into an HTML document. Because the generated document is loaded by a full headless browser, these properties can contain active markup such as `
Remediation
View remediation
` and rejects: - `

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render_cards.py:447
Finding

Arbitrary Output File Placement Through Unvalidated Configuration Slug

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/prepare_proof.py:40
Finding

Proof Derivative Path Traversal Through Unvalidated Name Argument

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description promises a broader asset-production workflow: turning a win into a set of designed, on-brand assets for many channels and sizes. The supplied code does something much narrower: it preserves the original screenshot, optionally crops it, and creates a brightened/contrast-enhanced derivative image. That is related to preparing proof imagery, but it does not implement multi-size export, branding, layout composition, channel-specific formatting, or verification logic. So the code represents only a small preprocessing step, not the declared end-to-end skill behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill clearly instructs use of shell commands, file reads/writes, environment-variable overrides, and local scripts, but declares no explicit tool scope or permissions boundary. That increases the chance an agent will execute higher-risk capabilities than a user expects, especially when handling screenshots and producing files on disk.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger list includes broad phrases like "show off," "celebrate this," and "announcement image," which can match ordinary conversation and cause unintended activation. In a skill that invokes shell, file, and possible network behavior, accidental triggering can lead to unnecessary processing of user files or external requests without clear intent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 57)May include surrounding context.

md
|---|---|
| GitHub stars, forks, age | `gh api repos/OWNER/REPO --jq '{stars:.stargazers_count,forks:.forks_count,created:.created_at}'` |
| GitHub releases, contributors | `gh api repos/OWNER/REPO/releases --jq 'length'`, `.../contributors?per_page=100` |
| npm downloads | `curl -s https://api.npmjs.org/downloads/point/last-month/PACKAGE` |
| PyPI downloads | pypistats, or the BigQuery public dataset |
| Crate downloads | `curl -s https://crates.io/api/v1/crates/NAME` |
| Docker pulls | `curl -s https://hub.docker.com/v2/repositories/OWNER/NAME/` |

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · references/verify.md (reported line 13)May include surrounding context.

md
|---|---|
| GitHub stars, forks, age | `gh api repos/OWNER/REPO --jq '{stars:.stargazers_count,forks:.forks_count,created:.created_at}'` |
| GitHub releases, contributors | `gh api repos/OWNER/REPO/releases --jq 'length'`, `.../contributors?per_page=100` |
| npm downloads | `curl -s https://api.npmjs.org/downloads/point/last-month/PACKAGE` |
| PyPI downloads | pypistats, or the BigQuery public dataset |
| Crate downloads | `curl -s https://crates.io/api/v1/crates/NAME` |
| Docker pulls | `curl -s https://hub.docker.com/v2/repositories/OWNER/NAME/` |

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/render_cards.py (reported line 481)May include surrounding context.

python
"file:///" + html_path.replace("\\", "/"),
            ]
            try:
                proc = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
            except subprocess.TimeoutExpired:
                failures.append(name + " (browser timed out)")
                print("FAIL  " + name)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README states that the skill verifies numbers against a live source and uses browser-based capture, which implies outbound network access and interaction with potentially sensitive screenshots or metadata, but it does not clearly warn users about that behavior. In an agent-skill context, undocumented network access can surprise users, cause unintended disclosure of private leaderboard/dashboard data, or violate expectations in restricted environments.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/render_cards.py (reported line 416)May include surrounding context.

python
return self.page(w, h, body)

    def render_html(self, layout, w, h, cta):
        return getattr(self, layout)(w, h, cta)


def main():