Back to skill

Security audit

Figma

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly a real Figma analysis/export tool, but its generated HTML reports can include unescaped Figma content that may run active browser content when opened.

Review before installing. Use a least-privileged Figma token from a secret store or environment variable, avoid the --token argument, run exports into a dedicated directory, and treat generated HTML reports as unsafe unless the Figma file and its collaborators are trusted. Prefer pinned dependencies in an isolated environment.

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

Error
Location
scripts/accessibility_checker.py:347
Finding
Stored HTML Injection in Accessibility Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/accessibility_checker.py`, lines 35–36, 100–101, 140–141, 174–175, 215–216, and 347–458 **Vulnerability Type**: Stored HTML injection through unescaped Figma metadata **Risk Level**: High ### Vulnerable Code Untrusted file and node metadata is copied from the Figma API into report data: ```python results = { 'file_key': file_key, 'file_name': file_data.get('name', 'Unknown'), 'wcag_level': level, 'timestamp': time.time(), 'compliance_score': 0, 'issues': [], 'summary': {} } ``` Issue records also retain attacker-controlled node names and text content: ```python results['issues'].append({ 'type': 'text_size', 'severity': 'error', 'message': f'Text too small: {font_size}px (minimum: {min_size}px)', 'node_id': node.get('id'), 'node_name': node.get('name', ''), 'wcag_criterion': '1.4.4', 'details': { 'font_size': font_size, 'min_size': min_size, 'characters': node.get('characters', '')[:50] } }) ``` These values are subsequently interpolated directly into an HTML document: ```python html = f"""<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Accessibility Report - {results['file_name']}</title> ``` ```python for issue in results['issues']: severity_class = issue['severity'] html += f""" <div class="issue {severity_class}"> <h3>{issue['type'].replace('_', ' ').title()}: {issue['message']}</h3> <span class="wcag-criterion">WCAG {issue['wcag_criterion']}</span> <div class="node-info"> <strong>Element:</strong> {issue.get('node_name', 'N/A')} (ID: {issue.get('node_id', 'N/A')}) </div> """ if 'details' in issue and issue['details']: html += "<div style='margin-top: 10px;'><strong>Details:</strong><ul>" for key, value in issu ...[truncated 2407 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every dynamic value before inserting it into HTML: ```python from html import escape safe_file_name = escape(str(results.get('file_name', 'Unknown')), quote=True) safe_node_name = escape(str(issue.get('node_name', 'N/A')), quote=True) safe_node_id = escape(str(issue.get('node_id', 'N/A')), quote=True) safe_message = escape(str(issue.get('message', '')), quote=True) safe_value = escape(str(value), quote=True) ``` 2. Prefer a template engine such as Jinja2 with automatic escaping enabled instead of building HTML through string concatenation. 3. Validate internal enumerated fields such as severity, issue type, and WCAG level against explicit allowlists before using them in markup or CSS class attributes. 4. Add a restrictive Content Security Policy to generated reports, for example: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:;"> ``` 5. Avoid inline event-capable markup and do not enable scripts in static reports. 6. Add regression tests using malicious file names, node names, IDs, and text values to verify that payloads are rendered as text rather than interpreted as HTML. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/style_auditor.py:655
Finding
Stored HTML Injection in Style Audit Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/style_auditor.py`, lines 82, 182, 194, 237, 262, 290, 320, 344, and 655–688 **Vulnerability Type**: Stored HTML injection through unescaped Figma audit fields **Risk Level**: High ### Vulnerable Code The auditor records the Figma file name and node names without sanitization: ```python 'file_name': file_data.get('name', 'Unknown'), ``` ```python return { 'severity': issue.severity, 'category': issue.category, 'message': issue.message, 'node_id': issue.node_id, 'node_name': issue.node_name, 'suggestions': issue.suggestions, 'details': issue.details } ``` The values are inserted directly into the generated report: ```python <div class="header"> <h1>Figma Design Audit Report</h1> <p>File: {audit_results.get('file_name', 'Unknown')}</p> <p>Audit Date: {time.strftime('%Y-%m-%d %H:%M:%S')}</p> </div> ``` ```python for issue in audit_results['issues']: severity_class = issue['severity'] html += f""" <div class="issue {severity_class}"> <h3>{issue['category'].title()}: {issue['message']}</h3> <p><strong>Node:</strong> {issue.get('node_name', 'N/A')} ({issue.get('node_id', 'N/A')})</p> <p><strong>Suggestions:</strong></p> <ul> """ for suggestion in issue.get('suggestions', []): html += f"<li>{suggestion}</li>\n" ``` ### Technical Analysis The style auditor treats Figma API metadata as trusted HTML. File names and node names originate in a remotely managed collaborative design and can therefore contain attacker-controlled markup. They are interpolated directly into the report body. The same unsafe construction is used for issue categories, messages, identifiers, suggestions, and recommendations. Although several of these fields are normally produced internally, the absence of contextual escaping means that any field incorporating remote design metadata can become an injection sink. The vulnerability becom ...[truncated 1450 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape all report values with `html.escape(value, quote=True)` before interpolation, including file names, node names, node IDs, messages, suggestions, recommendations, and categories. 2. Replace manual string concatenation with an auto-escaped HTML template: ```python from jinja2 import Environment, select_autoescape env = Environment(autoescape=select_autoescape(['html', 'xml'])) template = env.from_string(REPORT_TEMPLATE) html_report = template.render(audit_results=audit_results) ``` 3. Restrict `severity` and other values used in HTML attributes to predefined constants: ```python allowed_severities = {'error', 'warning', 'info'} severity_class = issue.get('severity') if severity_class not in allowed_severities: severity_class = 'info' ``` 4. Add a Content Security Policy that prevents script execution and external resource loading. 5. Create automated tests covering malicious Figma metadata, including closing tags, script tags, event-handler attributes, entity-encoded payloads, and malformed markup. 6. Clearly treat all API-returned names and text as untrusted at the point where audit records are created. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unnecessary and Non-Reproducible Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, lines 1–3 **Vulnerability Type**: Unsafe dependency constraints and unnecessary package installation **Risk Level**: Low ### Vulnerable Code ```text requests>=2.31.0 aiohttp>=3.9.0 pathlib ``` ### Technical Analysis The requirements use lower-bound-only version constraints. Consequently, a future installation can resolve to dependency versions that were not reviewed or tested as part of this audit. This weakens reproducibility and expands exposure to future compromised, malicious, or incompatible releases. The project also declares `pathlib`, although `pathlib` is included in the Python standard library for supported modern Python versions. Installing a separately published package with this name is unnecessary and increases the dependency-confusion and supply-chain attack surface. Additionally, `aiohttp` is imported in `scripts/export_manager.py` but is not used by the observed export implementation. The actual download path uses `requests` through `FigmaClient.download_image`. Retaining an unused network dependency creates avoidable installation and maintenance risk. No evidence was found that the currently declared packages are intentionally malicious. The finding concerns avoidable supply-chain exposure and non-reproducible resolution rather than a confirmed compromise of a specific package release. ### Attack Path 1. A user installs the project dependencies: ```bash pip install -r requirements.txt ``` 2. The package resolver selects versions newer than those reviewed because only minimum versions are specified. 3. It also installs unnecessary packages, including the external `pathlib` distribution and unused `aiohttp`. 4. If one of these unnecessary or future-resolved distributions is compromised, malicious installation or runtime code may execute with the privileges of the user performing the installation. ### Impact Assessment Dependency installation code gener ...[truncated 611 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the external `pathlib` requirement because the project already targets Python versions where it is part of the standard library. 2. Remove `aiohttp` unless asynchronous HTTP functionality is actually implemented and required. 3. Pin reviewed dependency versions instead of using unrestricted lower bounds: ```text requests==<reviewed-version> ``` 4. Generate and commit a lock file containing hashes, or use a hash-checked requirements file: ```bash pip install --require-hashes -r requirements.txt ``` 5. Separate direct dependencies from transitive dependencies and document the supported Python version. 6. Use automated dependency scanning and update tooling to review new versions before changing the lock file. 7. Install dependencies in an isolated virtual environment or restricted CI container rather than with administrator privileges. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (39)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill overstates major capabilities such as multi-format asset export and comprehensive documentation/reporting when those features are only partial or absent. Overclaiming functionality is dangerous in a security context because it obscures what code actually does, frustrates auditability, and can cause users to trust outputs or workflows that were never implemented or validated.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The skill overstates major capabilities such as multi-format asset export and comprehensive documentation/reporting when those features are only partial or absent. Overclaiming functionality is dangerous in a security context because it obscures what code actually does, frustrates auditability, and can cause users to trust outputs or workflows that were never implemented or validated.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill overstates major capabilities such as multi-format asset export and comprehensive documentation/reporting when those features are only partial or absent. Overclaiming functionality is dangerous in a security context because it obscures what code actually does, frustrates auditability, and can cause users to trust outputs or workflows that were never implemented or validated.

Credential Access

High
Category
Privilege Escalation
Content
# Set your Figma access token
export FIGMA_ACCESS_TOKEN="your-token-here"

# Or store in .env file
echo "FIGMA_ACCESS_TOKEN=your-token" >> .env
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# Set your Figma access token
export FIGMA_ACCESS_TOKEN="your-token-here"

# Or store in .env file
echo "FIGMA_ACCESS_TOKEN=your-token" >> .env
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Authentication

### Access Token Setup
1. Generate personal access token: Figma → Settings → Account → Personal Access Tokens
2. For team/organization usage: Create OAuth app for broader access
3. Set environment variable: `FIGMA_ACCESS_TOKEN=your_token_here`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Authentication

### Access Token Setup
1. Generate personal access token: Figma → Settings → Account → Personal Access Tokens
2. For team/organization usage: Create OAuth app for broader access
3. Set environment variable: `FIGMA_ACCESS_TOKEN=your_token_here`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Authentication

### Access Token Setup
1. Generate personal access token: Figma → Settings → Account → Personal Access Tokens
2. For team/organization usage: Create OAuth app for broader access
3. Set environment variable: `FIGMA_ACCESS_TOKEN=your_token_here`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Authentication

### Access Token Setup
1. Generate personal access token: Figma → Settings → Account → Personal Access Tokens
2. For team/organization usage: Create OAuth app for broader access
3. Set environment variable: `FIGMA_ACCESS_TOKEN=your_token_here`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Authentication

### Access Token Setup
1. Generate personal access token: Figma → Settings → Account → Personal Access Tokens
2. For team/organization usage: Create OAuth app for broader access
3. Set environment variable: `FIGMA_ACCESS_TOKEN=your_token_here`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
5. **User consent**: Request permissions appropriately

### Security
1. **Token protection**: Never expose access tokens in client-side code
2. **Scope principle**: Use minimal required permissions
3. **Input validation**: Validate all user inputs and API responses
4. **Audit logs**: Track API usage for compliance
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
)
        
        if not self.config.access_token:
            raise ValueError("Figma access token required. Set FIGMA_ACCESS_TOKEN env var or pass token.")
            
        self.session = requests.Session()
        self.session.headers.update({
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
)
        
        if not self.config.access_token:
            raise ValueError("Figma access token required. Set FIGMA_ACCESS_TOKEN env var or pass token.")
            
        self.session = requests.Session()
        self.session.headers.update({
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser.add_argument('--format', default='png', choices=['png', 'svg', 'pdf'])
    parser.add_argument('--scale', type=float, default=1.0)
    parser.add_argument('--output', help='Output file path')
    parser.add_argument('--token', help='Figma access token (overrides env var)')
    
    args = parser.parse_args()
Confidence
80% confidence
Finding
Allowing the access token to be passed as a CLI argument exposes it to shell history, process listings, and potentially audit logs on multi-user systems. While convenient, this is a real credential-handling weakness compared with environment variables or secret managers.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises and demonstrates capabilities that require environment access, network access, and local file writing, but it does not declare any explicit tool scope or permissions boundary. This increases the chance that an agent or reviewer will underestimate what the skill can do, leading to over-broad execution in environments where credential access, downloads, and file creation should be tightly controlled.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest explicitly frames the skill as 'Read-only analysis of Figma files', but this module generates and saves HTML reports to disk. Local file creation is a side effect beyond purely read-only analysis, even though the Figma file itself is not modified.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
When --output is provided, the script writes JSON audit results to a file. That behavior conflicts with a manifest claiming read-only analysis if interpreted as no filesystem modification, because the skill performs local writes in addition to analysis.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The manifest explicitly describes the skill as providing read-only analysis, yet this file creates directories and later writes multiple output files as part of export workflows. While exporting is mentioned in the manifest, local filesystem writes are still behaviorally inconsistent with a strict 'read-only' claim.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest advertises extracting design data and analyzing design systems, but the token extraction functions fabricate values such as style-id-derived colors and fixed typography/effect/spacing placeholders instead of reading real token properties from the Figma file. This creates a mismatch between the promised analysis/export fidelity and the actual implementation.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The function documentation states it extracts color design tokens, which implies returning actual color values from the design. Instead, the code assigns placeholder values based on the first six characters of the style ID, directly contradicting the stated behavior.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The documentation promises extraction of typography tokens from the file, but the function ignores real typography properties and always returns fixed values like 16px, 400, 1.5, and Inter. That is an active contradiction, not merely an omission.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The function claims to extract effect tokens such as shadows, yet it does not inspect actual effect definitions and instead assigns the same hard-coded shadow value to all effect styles. This contradicts the documented intent of extraction.

External Transmission

Medium
Category
Data Exfiltration
Content
class FigmaConfig:
    """Configuration for Figma API client"""
    access_token: str
    base_url: str = "https://api.figma.com/v1"
    rate_limit_delay: float = 0.5
    max_retries: int = 3
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill metadata describes the capability as read-only analysis, but this function writes downloaded content to a caller-supplied local path. That mismatch matters because an agent or user may grant it more trust than warranted, and arbitrary local writes can overwrite files or create unexpected artifacts on the host.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
download_image fetches remote content and writes it directly to a caller-controlled path without path restrictions, overwrite checks, size limits, or content validation. In an agent setting, this can be abused to place arbitrary files in sensitive locations or consume disk space unexpectedly.

Static analysis

No suspicious patterns detected.