Back to skill

Security audit

Image Manager

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local image organizer that saves user-selected images, thumbnails, and a searchable index in the OpenClaw workspace.

Install only if you are comfortable with images and metadata being copied into a persistent local media folder. Avoid storing highly sensitive receipts or people photos unless you manage deletion yourself, and treat displayed metadata as untrusted if other users or automation can add records.

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

T09 · Insecure Skill Coding Practices

Note
Location
scripts/save_image.py:110
Finding

Stored Terminal-Control Sequence Injection Through Image Metadata

Content
View full analysis

Vulnerability Details

File Locations:

  • scripts/save_image.py:110-112
  • scripts/search_image.py:68-71
  • scripts/list_images.py:88-91

Vulnerability Type: Stored terminal-control sequence injection
Risk Level: Low

Vulnerable Code

scripts/save_image.py:110-112 stores user-controlled metadata without validation:

python
"tags": tags or [],
"description": description,
"saved_at": datetime.now().isoformat(),

scripts/search_image.py:68-71 subsequently prints that metadata directly to the terminal:

python
tags_str = ', '.join(img.get('tags', []))
print(f"  📷 {img['id']}")
print(f"     类别: {img['category']} | 标签: {tags_str}")
print(f"     描述: {img.get('description', '无')}")

scripts/list_images.py:88-91 also prints stored metadata without terminal-safe encoding:

python
tags_str = ', '.join(img.get('tags', []))
desc = img.get('description', '')[:40]
print(f"  [{img['category']}] {img['id']}")
print(f"     {tags_str} | {desc}")

Technical Analysis

The --tags and --description command-line parameters are accepted as arbitrary strings and persisted in media/index.json. When the search or listing commands are later invoked, these stored values are written directly to an interactive terminal.

No filtering or escaping is applied to control characters such as ESC (\x1b) or to ANSI and OSC terminal sequences. Consequently, metadata can contain sequences interpreted by the terminal rather than displayed as ordinary text. Truncating a description to 40 characters does not neutralize such sequences because a complete control sequence may fit within that limit.

Depending on the terminal emulator and its configuration, crafted metadata could manipulate colors and cursor placement, erase or replace visible output, create deceptive hyperlinks, modify a terminal title, or invoke terminal-specific operations such as OSC clipboard handling. The affected code does not itself pass the data to a shell, so this is not dir ...[truncated 1448 chars]

Remediation
View remediation

Remediation Suggestions

  1. Introduce a shared terminal-safe rendering function and use it for every value read from index.json before printing it in human-readable mode.
  2. Remove or visibly escape C0 and C1 control characters, particularly ESC (\x1b), BEL (\x07), carriage return, and other non-printable characters.
  3. Preserve newline and tab characters only when explicitly required; otherwise encode them as visible representations such as \n and \t.
  4. Validate metadata when it is saved, imposing reasonable length limits and rejecting control characters. Output sanitization must still be retained as defense in depth because index.json can be modified independently.
  5. Keep machine-readable JSON output separate from terminal presentation. If raw metadata must remain available in JSON, document that downstream consumers must treat it as untrusted data.
  6. Add regression tests using ANSI CSI sequences, OSC hyperlinks, OSC clipboard sequences, carriage returns, and embedded newlines to verify that they are rendered harmlessly.
  7. Consider a sanitizer similar to:
python
import re

CONTROL_CHARS = re.compile(
    r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]'
)

def terminal_safe(value):
    value = str(value)
    value = value.replace('\x1b', r'\x1b')
    value = value.replace('\r', r'\r')
    value = value.replace('\n', r'\n')
    return CONTROL_CHARS.sub(
        lambda match: f'\\x{ord(match.group()):02x}',
        value
    )

Apply this function to identifiers, categories, tags, descriptions, paths, and timestamps before human-readable terminal output.

Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill clearly describes saving images, thumbnails, and a persistent JSON index containing metadata such as tags, descriptions, timestamps, source, and dimensions, but it does not warn users that this information is stored locally and may persist after use. This can create privacy and data-retention risks, especially because images may include sensitive content such as people or receipts and the index makes later discovery and correlation easier.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code prints user-facing CLI text in Chinese, including headings and labels, and later continues with Chinese-only status messages. That imposes a specific language on all users without opt-in or justification, which matches the natural-language locale policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The empty-state and listing output are also hard-coded in Chinese, confirming that the skill consistently forces one locale for user interaction. There is no visible opt-in, locale selection, or documented region-specific justification in this file.

Content

No source excerpt is available for this finding.

Tainted flow: 'INDEX_FILE' from os.environ.get (line 15, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/save_image.py (reported line 31)May include surrounding context.

python
def save_index(index):
    """Save the image index."""
    INDEX_FILE.parent.mkdir(parents=True, exist_ok=True)
    with open(INDEX_FILE, 'w', encoding='utf-8') as f:
        json.dump(index, f, ensure_ascii=False, indent=2)

Tainted flow: 'thumb_path' from os.environ.get (line 95, credential/environment) → shutil.copy2 (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/save_image.py (reported line 54)May include surrounding context.

python
except ImportError:
        # PIL not available, just copy as thumbnail
        thumb_path.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(str(source_path), str(thumb_path))
        return False
    except Exception as e:
        print(f"Warning: Thumbnail creation failed: {e}", file=sys.stderr)

Tainted flow: 'dest_path' from os.environ.get (line 94, credential/environment) → shutil.copy2 (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/save_image.py (reported line 98)May include surrounding context.

python
thumb_path = thumb_cat_dir / f"{img_id}.jpg"

    # Copy original (no compression)
    shutil.copy2(str(source_path), str(dest_path))

    # Create thumbnail
    create_thumbnail(source_path, thumb_path)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code presents usage examples and search results in Chinese-only strings, including the example query and result messages. The file does not offer an alternative language option or explain that the skill is intentionally limited to a Chinese-speaking or region-specific context, which creates a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The natural-language instructions and examples are presented only in Chinese, which may impose a language requirement on users without opt-in or justification. The policy requires flagging language or locale constraints when the skill does not offer a user language choice or clearly document why the restriction exists.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.