Back to skill

Security audit

PixelLab AI

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent PixelLab asset-generation helper, but its automatic asset downloader can fetch unvalidated URLs returned by the service, which exceeds the declared network scope.

Review before installing. Use only with a PixelLab API key you control, avoid elevated shells, prefer a pinned ClawHub CLI version, and do not enable automatic downloads from untrusted or custom PixelLab endpoints until asset URL validation is added.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pixellab_client.py:302
Finding

Unrestricted Server-Provided Asset URLs Enable Server-Side Request Forgery

Content
View full analysis
Iterable[str]: if isinstance(obj, str): if looks_like_url(obj) and is_asset_url_path(path): yield obj return if isinstance(obj, dict): for key, value in obj.items(): yield from iter_asset_urls(value, path + (normalize_key(key),)) elif isinstance(obj, list): for item in obj: yield from iter_asset_urls(item, path) ``` ```python def download_one(url: str, destination: pathlib.Path, timeout: int, max_bytes: int) -> None: total = 0 request = urllib.request.Request( url, headers={ "User-Agent": "pixellab-ai-skill/1.5 (+https://www.pixellab.ai/)", "Accept": "image/png,image/*,*/*", }, ) with urllib.request.urlopen(request, timeout=timeout) as resp, open(destination, "wb") as f: while True: chunk = resp.read(1024 * 256) if not chunk: break total += len(chunk) if total > max_bytes: raise RuntimeError(f"download exceeded {max_bytes} bytes") f.write(chunk) ``` ```python request_urls = set(iter_all_urls(payload)) discovered_urls = [url for url in iter_asset_urls(result) if url not in request_urls] output: Any = result decoded_files: list[str] = [] downloaded_files: list[str] = [] download_errors: list[str] = [] if args.download_dir: output_dir = pathlib.Path(args.download_dir) if discovered_urls: downloaded_files, download_errors = download_assets( discovered_urls, output_dir, timeout=args.timeout, max_byte ...[truncated 2672 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:47
Finding

Installation Instructions Execute an Unpinned Registry Package

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (22)

Tainted flow: 'req' from os.environ.get (line 178, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/pixellab_client.py (reported line 180)May include surrounding context.

python
for attempt in range(retry_limit + 1):
        req = urllib.request.Request(url=url, data=body, method=method_upper, headers=headers)
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                raw = resp.read()
                if not raw:
                    return {}

Tainted flow: 'request' from os.environ.get (line 380, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The client downloads asset URLs returned by API responses using urllib.request.urlopen without enforcing that the URL belongs to an approved PixelLab domain or even to HTTPS. If the remote service, a custom base endpoint, or a compromised response supplies attacker-controlled URLs, the client can be turned into an SSRF primitive that reaches internal services or downloads unexpected content to disk.

Content

Scanner excerpt · scripts/pixellab_client.py (reported line 387)May include surrounding context.

python
"Accept": "image/png,image/*,*/*",
        },
    )
    with urllib.request.urlopen(request, timeout=timeout) as resp, open(destination, "wb") as f:
        while True:
            chunk = resp.read(1024 * 256)
            if not chunk:

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 90)May include surrounding context.

md
5. For multi-asset requests, create a manifest with `scripts/pixellab_workflow.py plan`; do not hand-run dozens of ad hoc calls.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding

The install command uses npx --yes clawhub without pinning an exact package version, which means future installs may fetch whatever version is current on the registry at execution time. This creates a supply-chain risk: a compromised or maliciously updated package could run arbitrary code during installation in the user's environment.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 66)May include surrounding context.

md
- Requires API key: yes. Live PixelLab generation needs the user's own PixelLab account token. Users can start at `https://www.pixellab.ai/` and get a PixelLab MCP/API token at `https://api.pixellab.ai/mcp`.
- It is safe and required to name the local env var `PIXELLAB_API_KEY`; never reveal, print, commit, package, or upload the key value stored in it.
- Do not ask the user to paste the key into chat. Prefer a local env var or OpenClaw `skills.entries.pixellab-ai.apiKey` secret config.
- The helper reads `PIXELLAB_API_KEY` from the process environment. It does not auto-discover local secret files; pass `--env-file PATH` only when the user provides an explicit local file.
- `PIXELLAB_API_BASE` defaults to `https://api.pixellab.ai`; custom HTTPS bases require the explicit `--allow-custom-base` flag and should be trusted test endpoints only.
- If the key is missing, stop after preparing payloads and setup instructions. Do not fake generated assets.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

The documentation instructs users to run npx --yes clawhub install pixellab-ai --force without pinning a specific clawhub package version. This creates a supply-chain risk because execution depends on whatever version is current in the registry at install time, allowing unexpected or compromised upstream changes to affect users.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The instruction to 'ask naturally for a PixelLab pixel-art asset workflow' is broad enough that normal user prompts may trigger the skill implicitly. In agent environments, overly broad invocation language can cause unintended execution of external-networked functionality or confusion about when the skill is active.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

The publish command again invokes npx --yes clawhub without a pinned version, so the publishing workflow depends on mutable registry state. A compromised or breaking release of the CLI could alter behavior, leak package contents, or tamper with publication steps.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding

The scan command also uses an unpinned npx --yes clawhub, exposing users to the same supply-chain risk during post-publish analysis. Because security scan output may influence trust decisions, compromised tooling here could hide or falsify results.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The invocation guidance says to use this reference when the user asks for items like 'animation sprites' or 'frame-grid cleanup,' which are broad phrases that could match many general art or game-development requests. The file does not provide explicit trigger boundaries, exclusions, or negative examples clarifying when this skill should not be invoked.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger for visual review includes extremely common deictic words like 'this', 'these', 'that', and 'here' without requiring stronger contextual signals. In an agent workflow, this can cause unnecessary opening of linked sources or local visual queues for ordinary user phrasing, expanding untrusted content exposure and creating prompt-injection or data-access opportunities through excessive external navigation.

Content

No source excerpt is available for this finding.

Tainted flow: 'destination' from os.environ.get (line 424, 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/pixellab_client.py (reported line 387)May include surrounding context.

python
"Accept": "image/png,image/*,*/*",
        },
    )
    with urllib.request.urlopen(request, timeout=timeout) as resp, open(destination, "wb") as f:
        while True:
            chunk = resp.read(1024 * 256)
            if not chunk:

Tainted flow: 'data' from os.environ.get (line 284, credential/environment) → pathlib.Path.write_text (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/pixellab_client.py (reported line 600)May include surrounding context.

python
return
    result_path = pathlib.Path(path)
    result_path.parent.mkdir(parents=True, exist_ok=True)
    result_path.write_text(json.dumps(data, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")


def parse_size(raw: str | None) -> tuple[int, int] | None:

Tainted flow: 'data' from pathlib.Path.read_bytes (line 1215, file read) → pathlib.Path.write_text (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/pixellab_workflow.py (reported line 161)May include surrounding context.

python
def write_json(path: pathlib.Path, data: Any) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")


def read_optional_json(path: pathlib.Path) -> Any | None:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 27)May include surrounding context.

md
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 54)May include surrounding context.

md
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 64)May include surrounding context.

md
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 155)May include surrounding context.

md
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/install-and-secrets.md (reported line 24)May include surrounding context.

md
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/install-and-secrets.md (reported line 79)May include surrounding context.

md
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/pixellab_workflow.py (reported line 1621)May include surrounding context.

python
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/pixellab_workflow.py (reported line 1631)May include surrounding context.

python
report: dict[str, Any] = {}

    for version in ("v1", "v2"):
        url = f"https://api.pixellab.ai/{version}/openapi.json"
        spec = fetch(url)
        write_json(api_dir / f"{version}-openapi.json", spec)
        paths = spec.get("paths", {}) if isinstance(spec, dict) else {}

Static analysis

No suspicious patterns detected.