Back to skill

Security audit

design-inspiration-collector

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed design-inspiration collector that searches or constructs Dribbble/Pinterest links and writes local report files, with some review notes but no artifact-backed malicious behavior.

Install only if you are comfortable sending design search topics to Tavily and having Markdown/JSON reports written under ~/design_inspirations when the script is used. Treat generated links as search results, not guaranteed-safe official links, until the URL validation is hardened and the Tavily dependency is pinned.

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/design_collector.py:23
Finding
Hostname Substring Matching Allows URL Allowlist Bypass<![CDATA[ ## Vulnerability Details **File Location**: `scripts/design_collector.py`, lines 23-57 **Vulnerability Type**: Improper URL validation **Risk Level**: Medium ### Vulnerable Code ```python def is_valid_dribbble_url(url: str) -> bool: if "dribbble.com" not in url: return False allowed_patterns = [ r"dribbble\.com/search/", r"dribbble\.com/tags/", r"dribbble\.com/shots/popular", ] for p in allowed_patterns: if re.search(p, url): return True return False def is_valid_pinterest_url(url: str) -> bool: if "pinterest.com" not in url: return False allowed_patterns = [ r"pinterest\.com/search/pins", r"pinterest\.com/search/\?", r"pinterest\.com/ideas/", ] for p in allowed_patterns: if re.search(p, url): return True return False ``` ### Technical Analysis The URL validators search for trusted-domain strings and path fragments anywhere in the complete URL. They do not parse the URL or verify that the actual hostname is an approved Dribbble or Pinterest hostname. Consequently, an attacker-controlled URL can satisfy the checks merely by embedding a trusted-looking string in its hostname or path. Examples include: ```text https://dribbble.com.attacker.example/search/healthcare https://attacker.example/dribbble.com/search/healthcare https://pinterest.com.attacker.example/search/pins?q=design ``` These URLs are controlled by `attacker.example`, but the substring and regular-expression checks can accept them. Dribbble URLs received from Tavily are passed through this validation before being written to the generated Markdown and JSON reports. ### Attack Path 1. An attacker creates a page whose URL contains an allowed domain and path string but whose actual hostname is attacker-controlled. 2. The attacker causes the page to appear in search-engine results for one of the queries used by the Skill. 3. Tavily returns the m ...[truncated 814 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Parse each URL and validate its components independently: 1. Use `urllib.parse.urlparse`. 2. Require the `https` scheme. 3. Normalize the hostname to lowercase and remove any trailing dot. 4. Compare the hostname against an explicit set of permitted hostnames. 5. Validate the parsed path with anchored rules. 6. Reject URLs containing credentials, malformed ports, or unexpected hostnames. 7. Revalidate URLs immediately before report generation. Example: ```python from urllib.parse import urlparse def is_valid_dribbble_url(url: str) -> bool: try: parsed = urlparse(url) host = (parsed.hostname or "").lower().rstrip(".") if parsed.scheme != "https": return False if host not in {"dribbble.com", "www.dribbble.com"}: return False return ( parsed.path.startswith("/search/") or parsed.path.startswith("/tags/") or parsed.path == "/shots/popular" or parsed.path.startswith("/shots/popular/") ) except ValueError: return False def is_valid_pinterest_url(url: str) -> bool: try: parsed = urlparse(url) host = (parsed.hostname or "").lower().rstrip(".") if parsed.scheme != "https": return False if host not in {"pinterest.com", "www.pinterest.com"}: return False return ( parsed.path == "/search/pins/" or parsed.path.startswith("/ideas/") ) except ValueError: return False ``` Add negative tests for deceptive hostnames, user-information syntax, mixed-case hosts, trailing-dot hosts, alternate ports, and trusted-domain strings embedded in paths. ]]>

T08 · Insecure Dependencies

Note
Location
skill.yaml:34
Finding
Unpinned Tavily Dependency Creates Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `skill.yaml`, lines 34-37 **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Low ### Vulnerable Configuration ```yaml requirements: env: - TAVILY_API_KEY tools: - tavily-python dependencies: pip: - tavily-python ``` The same unversioned installation pattern is documented in: - `SKILL.md`, line 151: `pip install tavily-python` - `README.md`, line 65: `pip3 install tavily-python` ### Technical Analysis The dependency declaration does not specify a reviewed version, lockfile, or package hash. Every fresh installation may therefore resolve to a different release of `tavily-python`. This does not establish that the current package is malicious. However, it leaves installation behavior dependent on mutable upstream package state. A compromised maintainer account, malicious future release, or upstream distribution compromise could cause unreviewed code to be installed and subsequently imported by the Skill. The script imports the package at runtime: ```python from tavily import TavilyClient ``` Any malicious behavior present in the resolved package could therefore execute in the Python process when the import or client operations occur. ### Attack Path 1. An attacker compromises the upstream package publication channel or publishes a malicious future release under the legitimate package name. 2. A user installs the Skill dependencies without a version or hash constraint. 3. The package resolver downloads the attacker-controlled release. 4. The Skill imports `tavily`. 5. Malicious package code executes with the privileges of the user running the Skill. This path depends on compromise of the legitimate upstream dependency or its distribution channel; no dependency-confusion namespace or typosquatted package was identified in the audited project. ### Impact Assessment A compromised dependency could potentially access the Skill process environment, including `TAVILY_ ...[truncated 286 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `tavily-python` to a specifically reviewed version in `skill.yaml` and all installation documentation. 2. Generate and maintain a lockfile containing transitive dependency versions. 3. Use hash verification, such as a `requirements.txt` generated with `pip-compile --generate-hashes`. 4. Install only from the intended official package index. 5. Review dependency updates before changing the pinned version. 6. Run dependency vulnerability and provenance checks in CI. 7. Keep documentation and machine-readable dependency declarations synchronized. Example direct pin: ```yaml dependencies: pip: - tavily-python==<reviewed-version> ``` For stronger reproducibility, reference a locked requirements file containing exact versions and SHA-256 hashes for all resolved packages. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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 (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The documented behavior and the described implementation diverge in multiple ways, including undeclared filesystem effects and output artifacts not disclosed to the user. Behavior mismatches are dangerous because reviewers and users may approve the skill for one purpose while it performs additional actions, reducing transparency and creating room for data leakage or unexpected persistence.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The skill description is explicitly presented as a Chinese-only capability (“双平台设计灵感收集技能”), and the document does not offer any user opt-in or alternative language behavior. Under the stated policy, forcing a specific language without user choice is a natural-language locale policy issue.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger list includes generic phrases like '找灵感', '设计参考', and platform names that can appear in ordinary design discussions, increasing the chance the skill activates without a clear user request to use this specific capability. In an agent environment, over-broad activation can cause unintended external searches, file generation, and disclosure of user topics to third-party services such as Tavily, Dribbble, and Pinterest.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill appears to require environment access, network access, and file writing, but it does not declare an explicit tool scope or permission boundary. That creates an authorization and review gap: the agent may use sensitive capabilities without clear limitation, making unintended data access or side effects harder to detect and govern.

Vague Triggers

Medium
Confidence
94% confidence
Finding
L003 将“找灵感、收集灵感、设计参考、UI参考、视觉灵感、设计趋势”等作为触发词,其中多项是常见中文日常/工作表达,且未限定必须是针对 Dribbble/Pinterest 收集任务时才触发。虽然描述给出了一些示例主题,但没有提供排除条件或负例来界定何时不应激活该技能。

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
L003 的整体描述以中文明确规定技能行为与输出格式,但未说明是否支持其他语言,也未给用户提供语言选择或按用户语言响应的选项。根据规则,未经用户选择而隐含固定语言/locale 属于自然语言政策风险。

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill instructs the agent to write a Markdown file into the workspace without requiring explicit user confirmation, path disclosure, or overwrite safeguards. Unprompted file creation can overwrite existing work, create persistence the user did not expect, or be abused when the skill is triggered accidentally.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
SQP-3 applies to all file types and covers language or locale policy violations in natural-language content. The module description and later CLI/output strings are Chinese-only, with no opt-in, alternative language, or stated region-specific justification, which can impose an unintended language constraint on users.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest says the skill should use Tavily to search official results from both Dribbble and Pinterest, then organize the returned content. In code, Dribbble uses Tavily, but Pinterest bypasses Tavily entirely and returns a fixed list of constructed search URLs, so the actual behavior does not match the claimed dual-platform search workflow.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger list contains generic terms like 'Dribbble', 'Pinterest', '设计参考', and '设计趋势' that can appear in ordinary conversation, increasing the chance the skill activates when the user did not explicitly ask for it. Unintended activation can cause inappropriate tool use, unexpected external searches, and context hijacking by routing broad design discussions into this skill.

Intent-Code Divergence

Low
Confidence
75% confidence
Finding
The top-level docstring says the tool 'collects' inspiration only from official search/tag pages and excludes other content, which implies gathered results reflect actual collected sources. However, when Tavily results are insufficient, the code fabricates fallback entries in build_dribbble_urls/search_dribbble rather than collecting them from search output, creating a mismatch between the documentation's stated process and the implemented behavior.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The natural-language description appears to assume Chinese as the interaction language without offering an alternative or documenting that the skill is intentionally language-specific. This can violate language/locale policy when no user choice or explicit scope is provided.

Static analysis

No suspicious patterns detected.