Back to skill

Security audit

Clawcap Avatar Equip

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Gemini-based avatar accessory tool, but its bundled web/API service has review-worthy security gaps around unrestricted URL fetching, unauthenticated public access, and under-disclosed image handling.

Install or run this only in a controlled environment. Avoid exposing the web API publicly without authentication, trusted CORS origins, request-size limits, spending/quota controls, and strict URL allowlisting or removal of URL fetch support. Users should understand that avatar images and prompts are processed by Google Gemini and should not submit sensitive personal images unless they accept that third-party data flow.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
utils/image_utils.py:13
Finding
Server-Side Request Forgery Through Unrestricted Image URLs<![CDATA[ ## Vulnerability Details **File Location**: `api/routes.py:81-85`; `utils/image_utils.py:13-19` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python # api/routes.py:81-85 if req.image_base64: img = load_image_from_base64(req.image_base64) else: img = await load_image_from_url(req.image_url) ``` ```python # utils/image_utils.py:13-19 async def load_image_from_url(url: str) -> Image.Image: """Download and load an image from a URL.""" async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get(url) resp.raise_for_status() return Image.open(io.BytesIO(resp.content)).convert("RGBA") ``` ### Technical Analysis The API accepts a caller-controlled URL and passes it directly to `httpx.AsyncClient.get`. It does not restrict URL schemes or destination hosts, resolve and validate destination addresses, block private and reserved networks, or validate redirect targets. An attacker can therefore make the application issue requests from the server's network context. Potential destinations include loopback services, private network hosts, link-local addresses, and cloud instance metadata endpoints. The response must ultimately be parseable as an image for the entire image-processing workflow to succeed, but the initial network request occurs regardless, allowing network probing and interaction with image-returning internal services. Redirects are not explicitly enabled in this client construction, which limits redirect-based bypasses under the relevant `httpx` defaults, but it does not mitigate direct requests to prohibited destinations. ### Attack Path 1. An attacker sends a request to `/api/skill/alpha-equip`. 2. The `image_url` field is set to an internal or otherwise inaccessible destination, such as a loopback, private-network, or link-local URL. 3. The application passes the value directly to `httpx`. 4. The server connects to the destinati ...[truncated 835 chars]
Remediation
<![CDATA[ ## Remediation Suggestions The safest option is to remove server-side URL ingestion and accept only directly uploaded image data. If URL ingestion is required: 1. Accept only `https` URLs. 2. Parse URLs with a strict URL parser and reject embedded credentials, malformed hosts, and unexpected ports. 3. Resolve the hostname before connecting. 4. Reject every resolved loopback, private, link-local, multicast, reserved, unspecified, and documentation address for both IPv4 and IPv6. 5. Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 6. Disable redirects or validate the destination of every redirect using the same policy. 7. Prefer an explicit allowlist of trusted image-hosting domains. 8. Route downloads through a network-isolated proxy with no access to internal services or metadata endpoints. 9. Return generic client errors rather than raw network exceptions that can reveal internal connectivity details. 10. Add tests for loopback, RFC1918, IPv6 local, integer-encoded IP, alternate address notation, and DNS-rebinding cases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
utils/image_utils.py:13
Finding
Unbounded Image Downloads and Base64 Decoding Permit Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `api/schemas.py:10-18`; `utils/image_utils.py:13-29`; `mcp_server.py:56-86` **Vulnerability Type**: Uncontrolled Resource Consumption **Risk Level**: Medium ### Vulnerable Code ```python # api/schemas.py:10-18 image_url: Optional[str] = Field( None, description="Original avatar public URL, mutually exclusive with image_base64" ) image_base64: Optional[str] = Field( None, description="Base64-encoded original avatar, mutually exclusive with image_url" ) ``` ```python # utils/image_utils.py:13-29 async def load_image_from_url(url: str) -> Image.Image: """Download and load an image from a URL.""" async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get(url) resp.raise_for_status() return Image.open(io.BytesIO(resp.content)).convert("RGBA") def load_image_from_base64(b64_string: str) -> Image.Image: """Load an image from a Base64 string.""" if "," in b64_string: b64_string = b64_string.split(",", 1)[1] raw = base64.b64decode(b64_string) return Image.open(io.BytesIO(raw)).convert("RGBA") ``` ```python # mcp_server.py:84-86 img = load_image_from_base64(image_base64) validate_image(img) original_size = img.size ``` ### Technical Analysis Neither `image_url` nor `image_base64` has an encoded-byte limit. URL responses are buffered completely in `resp.content`, while Base64 input is decoded completely into memory. PIL then opens and converts the image before `validate_image` checks its dimensions. The dimension validation therefore occurs too late to prevent: - Very large HTTP response bodies. - Very large Base64 request bodies. - Images with extreme decompressed pixel counts. - Compressed image bombs. - Expensive mode conversion before rejection. The HTTP endpoint has a per-IP rate limit, but a small number of sufficiently large requests may still exhaust a worker. The MCP entry point does not implement equivalent ra ...[truncated 980 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Configure a strict maximum HTTP request-body size at the reverse proxy and ASGI layer. 2. Add `max_length` constraints to `image_base64` and `image_url`. 3. Reject encoded payloads before decoding if their estimated decoded size exceeds the allowed maximum. 4. Stream remote downloads rather than reading `resp.content`. 5. Stop reading once a strict byte limit is reached. 6. Validate `Content-Length` when present, while still enforcing the streaming limit because the header cannot be trusted. 7. Permit only approved image media types. 8. Set a conservative `PIL.Image.MAX_IMAGE_PIXELS`. 9. Treat `DecompressionBombWarning` as an error. 10. Use `Image.verify()` before full decoding, then reopen the verified image for processing. 11. Check dimensions before calling `.convert("RGBA")`. 12. Add process-level memory, CPU, execution-time, and concurrency limits. 13. Add MCP payload, concurrency, and invocation-rate controls. 14. Use a real cancellation timeout around the full processing pipeline; the existing `API_TIMEOUT_MS` value only logs after completion. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
main.py:47
Finding
Unauthenticated Cross-Origin API Access Can Consume Operator Resources<![CDATA[ ## Vulnerability Details **File Location**: `main.py:47-54` **Vulnerability Type**: Overly Permissive CORS and Missing API Authentication **Risk Level**: Medium ### Vulnerable Code ```python # main.py:47-54 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) ``` ### Technical Analysis The application permits requests from all browser origins, allows all HTTP methods and headers, and enables credential support. The expensive image-generation endpoint has no authentication and uses the server operator's `GEMINI_API_KEY`. Consequently, an arbitrary website can attempt to invoke an exposed ClawCap instance from a visitor's browser. This is particularly relevant for publicly deployed instances and development instances that are reachable from the browser. Browser private-network access controls may limit some localhost attack scenarios, but they do not protect publicly accessible deployments and should not be treated as an application authorization mechanism. The per-IP rate limit reduces request frequency but does not establish authorization and can be distributed across many clients. ### Attack Path 1. An attacker hosts a webpage containing JavaScript that submits images and prompts to the ClawCap API. 2. A victim visits the attacker's webpage. 3. Because the API permits the attacker's origin, the browser is allowed to submit cross-origin requests where network policy permits. 4. The server processes the request without authenticating the caller. 5. Gemini operations are charged against or deducted from the server operator's configured account. 6. The attack can be distributed across multiple visitors to bypass the practical effect of per-IP rate limiting. ### Impact Assessment An attacker may: - Consume the operator's Gemini API quota or paid usage. - Cause unwanted third-party images to be transmitted to Gemini. - Consume server CPU, memory, and netwo ...[truncated 274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `allow_origins=["*"]` with an explicit list of trusted origins. 2. Disable `allow_credentials` unless cookie- or browser-credential-based authentication is genuinely required. 3. Restrict allowed methods to those needed by the UI, such as `GET` and `POST`. 4. Restrict allowed headers to the minimum required set. 5. Require authentication for the generation endpoint. 6. Apply per-user API quotas in addition to IP-based limits. 7. Add global concurrency and spending limits for Gemini operations. 8. Bind development servers to `127.0.0.1` by default rather than `0.0.0.0`. 9. Put public deployments behind a reverse proxy with authentication, request-size limits, rate controls, and TLS. 10. Consider CSRF protections if browser credentials are introduced. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
static/index.html:625
Finding
Untrusted Gemini Metadata Is Rendered Through innerHTML<![CDATA[ ## Vulnerability Details **File Location**: `core/vision_fingerprint.py:77-101`; `static/index.html:625-631` **Vulnerability Type**: DOM-Based Cross-Site Scripting **Risk Level**: Medium ### Vulnerable Code ```python # core/vision_fingerprint.py:77-101 raw_text = response.text.strip() if raw_text.startswith("```"): lines = raw_text.split("\n") lines = [l for l in lines if not l.strip().startswith("```")] raw_text = "\n".join(lines) try: result = json.loads(raw_text) except json.JSONDecodeError as e: logger.error(f"VLM returned invalid JSON: {raw_text}") raise RuntimeError(f"Unable to parse VLM response: {e}") from e required_keys = [ "art_style", "face_angle", "lighting_environment", "head_top_x", "head_top_y", "head_width" ] for key in required_keys: if key not in result: raise RuntimeError(f"VLM response is missing field: {key}") ``` ```javascript // static/index.html:625-631 const fp = data.metadata.detected_fingerprint; metadataEl.innerHTML = ` > ART_STYLE: <span>${fp.art_style}</span><br> > FACE_ANGLE: <span>${fp.face_angle}</span><br> > LIGHTING: <span>${fp.lighting_environment}</span><br> > LATENCY: <span>${data.metadata.processing_time_ms}ms</span> `; ``` ### Technical Analysis The server accepts Gemini-generated strings after checking only that the expected keys exist. It does not enforce the documented enums, string-length limits, safe character sets, numeric types, or coordinate ranges. The frontend then interpolates those strings into `innerHTML`. Any HTML contained in `art_style`, `face_angle`, or `lighting_environment` will be parsed as markup rather than displayed as text. The metadata originates from a vision model processing attacker-supplied images. Images containing adversarial instructions or text can potentially influence model output. Exploitation requires the attacker to cause Gemini to emit syntactically valid JSON containing a browser-executable HTML payload, making reliabil ...[truncated 1160 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use `innerHTML` for model-generated or otherwise untrusted values. 2. Construct the metadata display with DOM elements and assign every dynamic value through `textContent`. 3. Validate Gemini output against a strict server-side schema. 4. Enforce exact enums for `art_style` and `face_angle`. 5. Apply conservative length and character constraints to `lighting_environment`. 6. Require numeric coordinate values and enforce the `0.0` to `1.0` range. 7. Reject unknown fields and unexpected data types. 8. Use the Gemini SDK's structured-output or response-schema support where available. 9. Add a restrictive Content Security Policy that disallows inline script execution and dangerous object sources. 10. Add tests containing HTML, SVG event handlers, malformed JSON types, and adversarial image text. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Mutable Unhashed Dependency Ranges Prevent Reproducible Trusted Installation<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-10`; `SKILL.md:64-65` **Vulnerability Type**: Unpinned Third-Party Dependencies **Risk Level**: Low ### Vulnerable Code ```text # requirements.txt:1-10 fastapi>=0.115.0 uvicorn[standard]>=0.32.0 google-genai>=1.51.0 Pillow>=10.0.0 numpy>=1.26.0 python-dotenv>=1.0.0 httpx>=0.27.0 python-multipart>=0.0.9 mcp[cli]>=1.0.0 slowapi>=0.1.9 ``` ```text # SKILL.md:64-65 - `GEMINI_API_KEY` environment variable - Install deps: `pip install -r requirements.txt` ``` ### Technical Analysis Every dependency uses a lower-bound constraint without an upper bound, exact version, lock file, or package hash. The same installation command can consequently resolve different packages over time. No typosquatted package, nonstandard package index, direct remote archive, or currently known malicious dependency was identified in the reviewed files. The weakness is that future package resolution is mutable and lacks integrity verification. Python packages can execute code during installation, import, or application startup. A compromised upstream release, an incompatible future major release, or an unexpected transitive dependency can therefore affect the Skill without any change to the audited repository. ### Attack Path 1. A dependency or transitive dependency publishes a compromised or unexpectedly incompatible future version. 2. A user later runs `pip install -r requirements.txt`. 3. The lower-bound constraint permits pip to select that new release. 4. The package is installed without comparison to a reviewed hash or lock file. 5. Package code executes during installation, import, or normal application operation with the user's privileges. ### Impact Assessment The possible impact depends on the behavior of a future compromised dependency and may include: - Execution of arbitrary code under the account installing or running the Skill. - Access to the `GEMINI_API_KEY` available in the process environmen ...[truncated 274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate and commit a dependency lock file containing exact reviewed versions. 2. Use hashes for all downloaded distributions, such as a hash-locked requirements file installed with `pip --require-hashes`. 3. Lock transitive dependencies, not only direct dependencies. 4. Install the project in an isolated virtual environment or container. 5. Review changelogs and security advisories before updating locked versions. 6. Automate dependency vulnerability scanning and controlled update pull requests. 7. Use a trusted package mirror or repository policy where appropriate. 8. Avoid automatically widening version constraints without compatibility and security testing. ]]>
Vulnerability Patterns
  • 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
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (49)

Credential Access

High
Category
Privilege Escalation
Content
### 2. 配置 API Key

```bash
cp .env.example .env
# 编辑 .env,填入你的 Gemini API Key
# 申请地址:https://aistudio.google.com/apikey
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill description materially overstates and misrepresents behavior: it claims local image analysis, mask generation, and seamless inpainting, while the implementation reportedly relies on provided fingerprints, does not actually send the mask as implied, and transmits image data to an external Google GenAI service. This is dangerous because users may consent based on false assumptions about capability, privacy, and data flow, leading to unintended exposure of uploaded images and misplaced trust in output quality or isolation.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
f"Preserve the original face, expression, body, and background exactly as-is."
    )

    return prompt


async def inpaint_accessory(
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Credential Access

High
Category
Privilege Escalation
Content
if not GEMINI_API_KEY:
    logger.error(
        "⚠️  GEMINI_API_KEY 未设置!"
        "请在 .env 文件中配置,或设置环境变量。"
    )

app = FastAPI(
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
if not GEMINI_API_KEY:
    logger.error(
        "⚠️  GEMINI_API_KEY 未设置!"
        "请在 .env 文件中配置,或设置环境变量。"
    )

app = FastAPI(
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly advertises external image processing via Gemini and a public demo endpoint but does not warn users that uploaded avatar images may be transmitted to third-party services or a remote server. This creates a real privacy and data-handling risk because avatars can contain biometric or identifying information, and users are not informed about where data goes, how long it is retained, or who can access it.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises no explicit tool scope or permissions, yet the surrounding behavior indicates access to environment variables and outbound network use. That mismatch is dangerous because users and hosting agents cannot accurately assess what the skill can access, and image data plus API credentials may be used in ways not transparently declared.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The skill omits a clear warning that user-uploaded images are sent to external Gemini services for analysis and generation. In an image-processing skill, that context makes the issue more serious because avatars may contain personal, identifying, or proprietary content, and users are likely to assume local processing unless told otherwise.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The file’s docstrings and user-visible error/detail messages are written exclusively in Chinese, indicating a fixed language choice for API responses. There is no visible opt-in, negotiation, or documentation that the API is intentionally restricted to a Chinese-speaking context, which can violate language/locale policy requirements.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The handler fetches an image from a user-provided URL via `load_image_from_url`, which causes a network operation and may transmit request metadata or interact with external systems. In this code, there is no confirmation prompt, user-facing log/message, or inline warning/comment disclosing that remote retrieval occurs.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file's natural-language documentation is written entirely in Chinese, while the user-facing prompt text is in English, with no indication that language is configurable or limited to a justified region-specific use case. Under the policy, hard-coding a language/locale choice without documenting user opt-in can be a language policy violation.

Description-Behavior Mismatch

Medium
Confidence
83% confidence
Finding
The skill description claims accessory synthesis that automatically analyzes art style, lighting, and angle to seamlessly add headwear. In this file, the actual behavior is a generic call to a remote generative model with only a text prompt and the original image; it uses `art_style` from `fingerprint` but does not use `mask_img`, and there is no visible use of lighting or angle attributes to constrain the edit here. That creates a semantic gap between the advertised targeted synthesis behavior and the implementation shown.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The documentation states that editing is guided by the head-top region and explicitly discusses the mask image, but the implementation only sends `[prompt, original_img]` to the model and never includes `mask_img`. This is not merely incomplete documentation: it directly acknowledges the mask parameter while describing behavior that implies spatially guided inpainting, which the current code does not perform.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The file’s natural-language content is entirely in Chinese in the module docstring, function docstring, comments, and log message text, which imposes a specific language/locale on maintainers or users of logs without any opt-in or documented regional justification. The policy explicitly calls out forced language or locale as a violation when no choice or justification is provided.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code sends user-supplied avatar image bytes to Google's Gemini API for remote processing, but nothing in this file indicates consent, disclosure, or privacy controls. Because avatars may contain personal or biometric information, undisclosed third-party transfer creates a real privacy and compliance risk, especially if callers assume all processing is local.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The module docstring is entirely in Chinese and presents the skill identity and description only in that language. This imposes a language choice on users or maintainers without offering an alternative or documenting that the skill is intentionally region-specific.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The server instructions are written entirely in Chinese and direct behavior in a single language, with no indication that users may opt into another language or locale. The file does not document a region-specific or compliance-based justification for this constraint, so it appears to violate the language/locale policy requirement.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The documentation states an absolute guarantee that the operation will only add accessories and will not alter the face, background, or composition. In the implementation, the result is produced by `generate_mask(...)` plus `inpaint_accessory(...)`, with no explicit code-level checks that constrain edits away from facial regions or preserve background/composition exactly. That is an intent/documentation contradiction, not merely an omitted detail.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The tool processes user avatar images through AI pipeline components that likely rely on external model services, as suggested by the Gemini API key dependency, but provides no explicit notice, consent flow, or data-handling disclosure. This creates a privacy/security risk because sensitive biometric-like profile images may be transmitted off-box or to third-party providers without user awareness.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The document declares `lang="zh-CN"`, and the visible UI is predominantly Chinese-language without any apparent language selection or opt-in. The policy requires avoiding forced language or locale constraints unless the skill offers a choice or clearly documents a justified region-specific purpose.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The frontend implements generic prompt-based image editing by accepting arbitrary text and posting image_base64 plus accessory_prompt to the backend, rather than enforcing automatic headwear-only augmentation. In the context of an image-manipulation skill, this increases misuse potential and can materially expand capability beyond the declared skill purpose, weakening trust and safety controls.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The page is described as a narrowly scoped hat/headwear tool, but the UI exposes a freeform prompt and sends it directly as accessory_prompt, enabling broader image editing behavior than users or reviewers may expect. This scope mismatch is dangerous because it can bypass product-policy assumptions, safety review boundaries, or downstream moderation rules that were designed only for constrained avatar headwear synthesis.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The application uploads user-provided avatar images and receives generated image content without presenting a user-facing privacy notice about transmission, processing, retention, or third-party handling. Because avatar images may contain faces or other personal data, users may unknowingly submit sensitive biometric-like content to remote processing services.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The function downloads content from a user-supplied URL via httpx, which is a network operation that can transmit user-provided data to external systems. In this file, there is no confirmation prompt, user-facing log/print, or warning comment/docstring explaining the privacy or network implications of fetching remote content.

Static analysis

No suspicious patterns detected.