Back to skill

Security audit

Mermaid Image Uploader

Security checks for vulnerabilities and agentic risk

Overview

This skill does the advertised Mermaid conversion/upload work, but it can send diagram contents to third-party services by default and has under-disclosed privacy and generated-HTML safety risks.

Review this skill carefully before installing if your diagrams or Markdown may contain internal architecture, hostnames, customer data, credentials, or unreleased business details. Prefer a local mermaid-cli-only workflow for sensitive content, avoid the Markdown batch mode unless you want hosted image links, and treat generated HTML as active content that should not be opened from untrusted Mermaid input.

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

other

Warning
Location
mermaid_converter.py:93
Finding
Mermaid Source Is Disclosed to Kroki During Default Conversion<![CDATA[ ## Vulnerability Details **File Location**: `mermaid_converter.py:93-105` and `mermaid_converter.py:255-258` **Vulnerability Type**: Unintended data disclosure to a third-party conversion service **Risk Level**: Medium ### Vulnerable Code ```python # Compress and encode Mermaid code compressed = zlib.compress(mermaid_code.encode('utf-8'), 9) encoded = base64.urlsafe_b64encode(compressed).decode('utf-8') # Build URL url = f"https://kroki.io/mermaid/{output_format}/{encoded}" # Request response = requests.get(url, timeout=30) ``` The automatic method selection invokes Kroki before attempting local conversion: ```python # Try Kroki first print("Trying Kroki online conversion...") if self.convert_with_kroki(mermaid_code, output_path, output_format): return output_path ``` ### Technical Analysis The entire Mermaid source is compressed and Base64-encoded into a URL sent to `kroki.io`. Compression and Base64 encoding do not provide confidentiality; the original source can be recovered by anyone with access to the URL. This network disclosure occurs during the default conversion path, including when the user does not specify `--upload`. Consequently, a user requesting what appears to be image conversion may unknowingly transmit diagram content to a third party. Placing the encoded source in a GET URL also increases exposure because URLs may be retained in proxy, gateway, browser, CDN, or server access logs. The online conversion feature is relevant to the declared functionality, but automatically preferring it exceeds the minimum network privilege needed when a local converter is available. ### Attack Path 1. A user places confidential architecture, internal hostnames, workflow details, customer names, or other sensitive information in a Mermaid diagram. 2. The user invokes ordinary PNG, SVG, or JPG conversion without explicitly selecting Kroki or requesting an upload. 3. The default method-selection logic invokes `convert_with_kroki`. 4. The c ...[truncated 603 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make local `mermaid-cli` conversion the default when it is available. 2. Require explicit user consent for online conversion, such as `--method kroki` or `--allow-network-conversion`. 3. Display a clear warning that the complete Mermaid source will be sent to a third party before making the request. 4. Use Kroki's POST interface, if supported, rather than embedding source data in a GET URL. 5. Provide a strict offline mode that prevents all network requests. 6. Document the third-party service's data handling and advise users not to submit confidential diagrams. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
mermaid_converter.py:134
Finding
Untrusted Mermaid Source Is Injected into Executable HTML Without Escaping<![CDATA[ ## Vulnerability Details **File Location**: `mermaid_converter.py:134-183` **Vulnerability Type**: HTML and JavaScript injection in generated output **Risk Level**: High ### Vulnerable Code ```python html_content = f""" <!DOCTYPE html> <html> <head> <title>Mermaid Diagram</title> <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script> <style> body {{ font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }} .container {{ max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }} h1 {{ color: #333; }} .mermaid {{ text-align: center; margin: 20px 0; }} .instructions {{ background: #e7f3ff; padding: 15px; border-radius: 4px; margin-bottom: 20px; }} </style> </head> <body> <div class="container"> <h1>Diagram Preview</h1> <div class="instructions"> <strong>Instructions:</strong> <ol> <li>Wait for diagram to render</li> <li>Right-click diagram -> "Save image as..."</li> <li>Or use screenshot tool</li> </ol> </div> <div class="mermaid"> {mermaid_code} </div> </div> <script> mermaid.initialize({{ startOnLoad: true }}); </script> </body> </html> """ with open(output_path, 'w', encoding='utf-8') as f: f.write(html_content) ``` ### Technical Analysis The `mermaid_code` value is inserted directly into an HTML document without HTML escaping or structural validation. An attacker-controlled input can terminate the `.mermaid` element and introduce arbitrary HTML, including script elements, event handlers, forms, iframes, and external res ...[truncated 1405 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. HTML-escape all Mermaid source before embedding it in a document. 2. Prefer placing the source in a non-executable text container and passing its text content to Mermaid through a safe API. 3. Do not use `innerHTML` or equivalent parsing for untrusted source. 4. Add a restrictive Content Security Policy that blocks inline scripts, event handlers, frames, form submission, and unexpected network destinations. 5. Pin the Mermaid JavaScript asset to a reviewed version and apply Subresource Integrity if it must be loaded from a CDN. 6. Validate generated output against malicious test cases containing closing tags, script tags, SVG event handlers, and encoded markup. 7. Clearly identify HTML output as active content and avoid automatically opening it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
mermaid_converter.py:220
Finding
Predictable Shared Temporary Output Permits Symlink Overwrite and Conversion Races<![CDATA[ ## Vulnerability Details **File Location**: `mermaid_converter.py:220-223`, with writes at `mermaid_converter.py:107-109` and `mermaid_converter.py:180-181` **Vulnerability Type**: Unsafe temporary-file handling **Risk Level**: Medium ### Vulnerable Code The default output path is fixed and predictable: ```python if output_path is None: output_path = os.path.join( self.temp_dir, f"mermaid_diagram.{output_format}" ) ``` The resulting path is opened normally and follows existing symbolic links: ```python if response.status_code == 200: with open(output_path, 'wb') as f: f.write(response.content) ``` The HTML conversion path has the same behavior: ```python with open(output_path, 'w', encoding='utf-8') as f: f.write(html_content) ``` ### Technical Analysis Every process using the same temporary directory and output format receives the same path, such as `/tmp/mermaid_diagram.png`. The code neither creates the output atomically nor verifies that it is a regular file owned by the current process. On systems where the temporary directory is shared, another local user can pre-create the predictable path as a symbolic link. Python's normal `open` operation follows that link and truncates the target if the victim has permission to write it. Concurrent legitimate conversions can also overwrite one another or consume partially replaced output. ### Attack Path 1. A local attacker determines the predictable output filename for the desired format. 2. The attacker creates that path in the shared temporary directory as a symbolic link to a file writable by the victim. 3. The victim runs a conversion without supplying an explicit output path. 4. The converter opens the predictable path for writing and follows the symbolic link. 5. The linked target is truncated and replaced with image or HTML output under the victim's permissions. ### Impact Assessment The attacker cannot write files that the victim is not authorize ...[truncated 331 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a private randomized directory with `tempfile.TemporaryDirectory`. 2. Generate output names using cryptographically unpredictable temporary-file APIs. 3. Use exclusive creation and no-follow semantics where supported, such as `O_CREAT | O_EXCL | O_NOFOLLOW`. 4. Verify that any caller-supplied output path is not a symbolic link before replacement. 5. Write to a securely created temporary file and atomically move it to the final destination. 6. Give each invocation and each diagram a unique output path. 7. Apply restrictive file and directory permissions so other local users cannot replace generated artifacts. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:16
Finding
Dependency Instructions Recommend an Unpinned and Potentially Incorrect Package<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:16-25` **Vulnerability Type**: Unsafe dependency installation guidance **Risk Level**: Medium ### Vulnerable Code ```bash pip install mermaid-cli requests ``` The documentation separately identifies the Node.js package used to provide `mmdc`: ```bash npm install -g @mermaid-js/mermaid-cli ``` ### Technical Analysis The implementation invokes the `mmdc` executable associated with the Node.js package `@mermaid-js/mermaid-cli`, but the primary installation command also tells users to install a similarly named Python package, `mermaid-cli`. This creates package-identity ambiguity and unnecessary supply-chain exposure. Neither dependency command pins a reviewed version or verifies package integrity. The npm command also installs globally, increasing the package's reach and making future behavior dependent on mutable registry content. The audit did not establish that either named package is malicious; the finding concerns unsafe and inconsistent installation guidance. ### Attack Path 1. A user follows the documented `pip install mermaid-cli requests` command. 2. The package installer resolves mutable third-party packages by name without version or hash verification. 3. The user may install an unintended package that is not required by the implementation. 4. If the package name is compromised, transferred, or maliciously updated, installation-time or runtime package behavior executes in the user's environment. 5. Alternatively, the global npm command installs a mutable package version with broad availability on the user's system. ### Impact Assessment The exact impact depends on the behavior of the retrieved dependency. Package installation commonly permits package-controlled code to run with the installing user's privileges, potentially exposing that user's files, credentials, and development environment. There is no evidence in the audited project that the current dependency is malicious, so t ...[truncated 96 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the Python `mermaid-cli` installation instruction unless that exact package is reviewed and demonstrably required. 2. Document `@mermaid-js/mermaid-cli` as the single source of the `mmdc` executable. 3. Pin dependencies to reviewed versions. 4. Use a lockfile and integrity checks or hashes for reproducible installation. 5. Prefer a project-local npm dependency over a global installation. 6. Invoke the local binary through a controlled path rather than trusting any executable named `mmdc` found on `PATH`. 7. Regularly review dependency advisories and update pinned versions through a controlled process. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
image_host_uploader.py:27
Finding
Shared FreeImage API Credential Is Hard-Coded in Source<![CDATA[ ## Vulnerability Details **File Location**: `image_host_uploader.py:27-34` **Vulnerability Type**: Hard-coded reusable service credential **Risk Level**: Low ### Vulnerable Code ```python url = "https://freeimage.host/api/1/upload" with open(image_path, 'rb') as f: files = {'source': f} data = {'key': '6d207e02198a847aa98d0a2a901485a5'} # public key for demo response = requests.post(url, files=files, data=data, timeout=30) ``` ### Technical Analysis A reusable FreeImage API key is embedded directly in the repository. Calling it a public demonstration key does not prevent third parties from copying and abusing it. Since all users share the same credential, its lifecycle cannot be managed independently, and rotation requires a code release. The actual image upload is consistent with the Skill's declared functionality and is not covert exfiltration: only the selected image file is sent, and upload occurs through an upload workflow. The security issue is the credential-management design rather than the existence of the network request. ### Attack Path 1. A third party reads the API key from the public source code. 2. The third party uses the key outside the application to perform uploads or consume service resources. 3. The shared account or quota is exhausted, rate-limited, suspended, or revoked by the provider. 4. All legitimate users relying on the embedded credential experience upload failure. ### Impact Assessment The exposed key may allow unauthorized use of the associated upload quota or service identity. The likely scope is abuse, quota exhaustion, content attribution problems, and denial of service for users sharing the key. No evidence shows that this key grants local system access or broader provider-account administration. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke or rotate the exposed key if it remains active. 2. Require users to provide their own credential through an environment variable, protected configuration file, or secret manager. 3. Do not place credentials in command-line arguments where they may appear in process listings or shell history. 4. If the provider supports anonymous upload, use the documented anonymous mechanism instead of a shared credential. 5. Validate that credentials are present only when required and avoid logging them. 6. Document upload visibility, retention, and third-party data handling so users understand that uploaded images leave the local system. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (15)

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The file recommends Kroki online conversion as the preferred method but does not disclose that Mermaid source is sent to an external network service for rendering. This is dangerous because users may assume conversion is local and unknowingly expose proprietary or sensitive diagram content to a remote service.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The documentation promotes uploading generated Mermaid images to third-party image hosts without warning users that diagram content and resulting images will leave the local environment and be stored by external services. If users process internal architecture, credentials, URLs, or other sensitive content, this can cause unintended data disclosure to untrusted providers.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README encourages use of Kroki and multiple image-hosting services for converting and uploading Mermaid diagrams, but it does not warn users that diagram and possibly Markdown content will be sent to third-party services. This can expose sensitive architecture details, internal identifiers, or embedded secrets if users process private documentation, making the omission a real privacy and data-handling risk in this skill's context.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly promotes uploading generated images to third-party hosting services but does not disclose that Mermaid content, rendered diagrams, filenames, or embedded sensitive information may be transmitted to external providers. In a documentation/content workflow, users may unknowingly publish internal architecture, credentials accidentally embedded in diagrams, or other sensitive material to public services.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This function uploads a local file to a third-party image host and returns a public URL, but it provides no explicit user consent prompt, warning, or policy control around external disclosure. In an agent/skill context, that creates a real data-exfiltration risk if the caller passes sensitive local files or if users do not realize the image becomes available to an external service.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This Postimages path transmits local image data to an external service without any user-facing disclosure or consent mechanism. Even though the behavior is functionally intended, it is dangerous because a skill can be invoked on sensitive screenshots or documents and silently publish them outside the local environment.

External Transmission

Medium
Category
Data Exfiltration
Content
Image URL or None
        """
        try:
            url = "https://api.imgur.com/3/image"

            headers = {'Authorization': f'Client-ID {client_id}'}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The Imgur upload sends both local file contents and an authorization header to a third-party service, again without explicit disclosure or confirmation. This increases risk because it not only exports local data but also uses credentials/tokens in the outbound request, which can have privacy and account-governance implications if used unexpectedly.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def check_mermaid_cli(self) -> bool:
        """Check if mermaid-cli is installed"""
        try:
            result = subprocess.run(
                ['mmdc', '--version'],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]

                # Execute conversion
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)

                if result.returncode == 0:
                    print(f"Converted successfully: {output_path}")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
This code sends the full Mermaid source to the third-party Kroki service over the network, which can expose sensitive diagram content, embedded credentials, internal architecture, or proprietary information. The risk is increased because the transmission occurs as part of normal conversion behavior without an explicit privacy warning, consent gate, or local-only default.

Missing User Warnings

Medium
Confidence
99% confidence
Finding
The auto-selection logic tries Kroki first, meaning users who do not explicitly choose a method may unknowingly transmit diagram contents to an external service. In a skill context, diagrams often contain internal system topology, infrastructure details, or business logic, so making remote exfiltration the default materially increases confidentiality risk.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The Markdown processing example states that Mermaid blocks will be replaced with image links, but it does not warn that this modifies file contents and may overwrite or materially transform source documentation. Users could unintentionally lose original Mermaid source in derived files or introduce externally hosted links into documents without realizing the persistence and integrity implications.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The module docstring states it is 'Designed for WeChat Official Account articles,' which ties the skill to a specific platform/locale context in natural language. Under the policy rule, locale- or platform-specific constraints should be clearly justified or offer user opt-in; this file does not present such a choice.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The description is written entirely in Chinese and explicitly targets WeChat public-account article workflows ("专为公众号文章设计"). This imposes a specific language and locale context in the skill metadata without indicating any user choice or broader language support.