T09 · Insecure Skill Coding Practices
- Location
scripts/upload-report.js:128- Finding
Shell Command Injection Through R2 Bucket and Report Slug
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is a legitimate research-report generator, but it automatically publishes report content through under-disclosed hosting paths and includes unsafe upload/report-rendering code that needs review before installation.
Install only if you are comfortable with research summaries and metadata being turned into durable hosted HTML links. Avoid using it for confidential, regulated, private, or internal investigations unless upload is disabled or reviewed first; do not use --no-encrypt for sensitive reports, and treat the upload implementation as needing fixes before trusted use.
scripts/upload-report.js:128Shell Command Injection Through R2 Bucket and Report Slug
scripts/upload-report.js:134Runtime Execution of an Unpinned Package Through npx
scripts/upload-report.js:184Automatic Upload to an Undocumented Third-Party Endpoint
scripts/generate-report.js:254Stored HTML Injection in Interactive Map Tooltips
scripts/generate-report.js:66Dangerous URL Schemes Allowed in Generated Report Links
If the skill functions as a generic public HTML hosting mechanism with user-controlled slugs, it exceeds the declared purpose and can be abused to publish arbitrary content. This makes the skill materially more dangerous than advertised because it creates an unreviewed outbound publication path, not just a visualization helper.
If the skill functions as a generic public HTML hosting mechanism with user-controlled slugs, it exceeds the declared purpose and can be abused to publish arbitrary content. This makes the skill materially more dangerous than advertised because it creates an unreviewed outbound publication path, not just a visualization helper.
If the skill functions as a generic public HTML hosting mechanism with user-controlled slugs, it exceeds the declared purpose and can be abused to publish arbitrary content. This makes the skill materially more dangerous than advertised because it creates an unreviewed outbound publication path, not just a visualization helper.
If the skill functions as a generic public HTML hosting mechanism with user-controlled slugs, it exceeds the declared purpose and can be abused to publish arbitrary content. This makes the skill materially more dangerous than advertised because it creates an unreviewed outbound publication path, not just a visualization helper.
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.
<body>
<div class="container">
<!-- Header -->
<div class="header fade-in">
<div class="header-badge"><span class="dot"></span> Research Complete</div>
<h1>Polymarket Top Opportunities Analysis</h1>
This page ships a large encrypted blob, derives a decryption key from the URL fragment, decrypts it in the browser, and then injects the resulting plaintext directly with document.write(). The obfuscation itself is not the core issue, but it prevents review of the delivered HTML/JS and creates a hidden content execution path; if the encrypted payload or key-generation flow is tampered with, arbitrary active content can run in the user's browser.
var ck=await crypto.subtle.importKey('raw',kb,{name:'AES-GCM'},false,['decrypt']);
var iv=Uint8Array.from(atob('dz4m27C9fCr9xAI3'),function(c){return c.charCodeAt(0)});
var at=Uint8Array.from(atob('qp9EgYmBkXQ3swtchjEPTQ=='),function(c){return c.charCodeAt(0)});
var ct=Uint8Array.from(atob('qLjTAbYlHaq0AaQMCvAIhMtJoIz2wpwyPBeqaItCQGMl8QNI6koeMrwJVXaF+AkD99BpmNYBC6ybOhH4YetMMg11r3K7KUHFNGdrv4iH00T7qjZtpXMs+vMn9EfFZuuqmtRIVffXlNUobri64dMMootLg0mQ7MyWnIjyjXjEgFeSXFgg+IKO5J9T8EjqrUctBL56IQ1C9pxpMG6ofolEV3nPhqlt21YnCC20Eo3csy2OrcV4YlhAImaQhfGZZeEeJMs/5/cja+3ArTjWhNwbybHmjvb7FdMb9idVmRDaoM+jYWWiOIxf4DcU0an6lz9K88jnXUuM83bt5Z7v+CsoIogH6gMXF7PEOErnxpEevOx6iKZE05Gr9iQg0GHQCAS5n4qvEWNMBIs8TjgAeHJqzksT33bpg+8vd7RNHfmfOQJyJX4ADhBSX6B0kjGUzWCfvAh6b58g1KEM9esYKc7kWJsgz+uXqKO/vhNjSfl1VvkHWvkhdzc6quWAZLwXUuht7lNzz/b4WKOe+aOA4B0gSn9a+4v0RDF7jZpC84n0xVQAyhD2R9ShMv4M/QkPLHHYqX0RWus7BRuUeN984+n4ES6tkL8CXAo8kQiuPVLA7F6aQ0zk3nEpgtUvCkeyNAOSEiFCseGo3QbEBvZHfZdOJNc8SEgfpDvTPGTrM/a++CuIeGrzMnnSakr+axpTA3aQMneoPNLjQhrHLKoD9lYbER2ulbXTNtSUKsi0o0NvE2Jw+R1UP8Fx6+Tbvkv6gBuIZyraIBR7DLFZ6YR7qI4XPfip/rpgidEjMTKoRIjDaADRM6hjw0aEW7Y04ZDUi252vPv2phm96ui7L9RbzpWKFJ5FTboUOeKBC5arF4qklu7fqinC5WG+8vQfYeWdkq6uv+UKlyvDktR5Qi5YniOSaBYDI69N/jmHatbtwuSaO2+HDqMLw4Mb6Z+aM9dsSuTnqQvlryggn1BZIN6gUL6cy5LpsuXKGydgGla+p9rvcNZ8gmP/EDX4aZiE9GFLqsjPt4ARpocaCDkA9itGOkoKdlRg1GdyONjiX0ixrzxZ/maPspBFPwAmPFfhXGsQJ5Hk7ENdOqnr8l0oWTVKIUIheOhkSp4YFiSP5BQf1tZ0k9NVbckeGn0B63w3Ci/gQCFlyXCrQUUDzRg7LDezYduBeCu8CQOMSLLwIVmYlxi8JC4+/g4fv5rzp+m6dejzjozlROXV1SJDFFdYQwH5vwP4fVCv19l/k2SbRH44eWTJ7VBS7D1hsij59uCvTF6rNo0URXhWGnm6qg3om3COLTTmsOrFRbxPpP/VR9KZsJWUs9/usz5hEd2t5BAyZYxC2g9DeK9z8SY11EqZPlulCRlbGACi2fRkQN21SeKFofhuk6r0OXsfHi5UjYxjrifJ/vp5hJthTK/tspx1Tr086xbcG4EhU/59+HAKDkwUb4xNoCHnrCxdBsYDgWQAy3MDzGLQpjuB7PDVvENKPAhQay46PnSgn3g9N3zJWpKD1nKbD7aPlV4wEioRoAsHbwqdNblsVk8jeul6H93UKZ0oEfSWZJ49XNNG/+cbap/xSH1/URx605xFrf/z5miz015v7SbpMwxiiShVqTHzzV44vQ+nIil0uY+xFqlncsQU+dfGC4vhkyrkJjamSs68Zuc1CJIUh5qjnWxBtTmaGCXwkNvuqElLUJ9nebUMTS9cuHyIiaDW
...[truncated 27 chars]
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
status.innerHTML = '<div class="lock-icon">🔒</div><h2 class="error">' + msg + '</h2><p>The decryption key is missing or invalid.</p>';
}
// Extract key from URL fragment
const hash = window.location.hash.slice(1);
const params = new URLSearchParams(hash);
const keyB64 = params.get('key');
The skill invokes Node scripts that can read environment variables and perform network operations, but the manifest does not declare any tool scope or allowed-tools restrictions. This weakens reviewability and permission boundaries, making it easier for a skill to exfiltrate data or access credentials without clear operator awareness.
The skill says reports are uploaded to an external service but does not clearly warn that research content and conversation-derived summaries may leave the local/system boundary. Even with client-side encryption claims, users need explicit notice and consent because metadata, report existence, and mistakes in included content can still create privacy and compliance issues.
The manifest's vague 'Use when' phrases create trigger ambiguity and may cause the skill to be selected for routine analysis tasks where no report sharing is desired. Because this skill can publish or save detailed research artifacts, imprecise routing raises the risk of unintentional disclosure.
The activation rules are broad enough to trigger on many ordinary research interactions, which increases the chance the skill runs without a clear user request for external publishing. In this skill's context, accidental activation is more dangerous because the workflow culminates in generating shareable artifacts from conversation-derived content.
Directing the skill to extract structured JSON from the conversation and include all research steps can capture sensitive prompts, internal reasoning traces, API outputs, or user-provided confidential data. In a shareable report context, this creates a strong risk of semantic data leakage even if the final answer alone would have been safe to disclose.
The instruction to always include all research steps encourages disclosure of the full intermediate workflow, which can expose sensitive inputs, internal analysis artifacts, and proprietary source material. This is especially dangerous here because the result is designed to be saved locally or shared via a link, turning transient context into a durable disclosure artifact.
The article promotes automatic generation of a shareable report link after research and emphasizes convenience, but it does not clearly warn users that the report content is uploaded to a third-party service and may contain sensitive prompts, retrieved data, internal reasoning traces, or proprietary research context. Even with client-side encryption, disclosure risk remains because anyone with the full URL can access the report, users may share links in chats unintentionally, and the article frames the feature as something that should happen automatically on every deep research task.
The report makes an investment-style recommendation ('safest play') in close proximity to specific market probabilities and confidence scores, while the only disclaimer appears later in the footer rather than near the recommendation itself. In a research-visualization skill, this can mislead users into treating generated content as financial advice or a trading signal, especially because the presentation is polished and data-driven.
This is a true DOM XSS issue. The generated HTML embeds attacker-controlled report data into data-region/data-info attributes with escaping, but the runtime tooltip handler later reads those strings and inserts them with innerHTML ('<strong>'+r+'</strong><br/>'+i), so crafted input such as HTML entities can be decoded by the browser and executed as markup/script in the final report. In this skill context, the report is built from multi-step research context that may include untrusted web/API content, making stored client-side injection more plausible and more dangerous.
The header comment states uploads go to a2ui.me/R2 and emphasizes privacy, but the implementation can send report content to a separate workers.dev endpoint. This discrepancy is dangerous because users and calling agents may trust the documented privacy model while their research output is actually transmitted to a different third-party service, increasing the risk of unintended disclosure of sensitive data.
The script invokes npx wrangler without pinning an exact version, so execution depends on whatever package version npx resolves at runtime. In an automated agent context, this creates a supply-chain risk: a compromised, malicious, or behaviorally changed CLI could execute arbitrary code during upload and potentially access report contents, environment variables, or local files.
The skill promises uploaded clickable reports on a2ui.me, but the code may instead send content to a third-party public workers.dev service or fall back to a local file:// URL. In the context of a research-reporting skill that may process confidential investigation outputs, this mismatch can mislead downstream users and systems about where sensitive data is stored and how links behave.
The --no-encrypt path allows raw HTML reports to be uploaded with no user-facing warning, even though the skill is designed for research outputs that may contain sensitive collected data, analysis, or conclusions. Because the script's surrounding documentation strongly emphasizes privacy-preserving encrypted sharing, this silent opt-out can cause accidental public disclosure when users or agents disable encryption.
The code implements a public unauthenticated HTML upload-and-hosting service: any client can POST arbitrary HTML to /api/upload and later retrieve it from /r/.html. This materially exceeds the stated 'research visualizer' purpose and creates an attacker-controlled hosting surface suitable for phishing, malware delivery, and brand-abuse under the service's domain.
The endpoint accepts any string in body.html and persists it as text/html without sanitization, policy checks, or tying content to a completed research task. In the context of a skill that should visualize research results, this unjustified arbitrary publishing capability is dangerous because it can be repurposed as a general-purpose attacker-controlled web host.
This code performs a file write to R2 by persisting the provided HTML content, which is a safety-relevant operation affecting user data handling. There is no confirmation prompt, user-visible disclosure, or explanatory comment/docstring near the write indicating that submitted content will be stored and later served.
The document declares lang="en", which sets an English-only language expectation for the content. Under the policy rule, forcing a specific language without opt-in or clear justification can be a natural-language locale policy violation.
The HTML root sets lang="en", which establishes English as the page language, and the visible content is also entirely English. Under the policy criteria, forcing a specific language without user opt-in or documented justification is a natural-language locale policy issue.
Detected: suspicious.dangerous_exec