Back to skill

Security audit

AI Brand Analyzer

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent, but it saves AI-generated web research for reuse in other workflows without enough validation or overwrite safeguards.

Install only if you are comfortable sending brand names and prompts to Gemini and Google Search, and review generated profiles before using them in other AI workflows. Prefer GEMINI_API_KEY over --api-key, avoid confidential unreleased brands, pin dependencies before production use, and back up or review catalog files before using --auto-save.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (4)

T01 · Skill Instruction Hijacking

Error
Location
scripts/analyze.py:292
Finding
Indirect prompt injection through untrusted search content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:292-359` **Vulnerability Type**: Indirect prompt injection and unvalidated AI-generated data persistence **Risk Level**: High ### Vulnerable Code ```python user_prompt = f"""Analyze the brand: {brand_name} Use Google Search to research this brand thoroughly: 1. Find official brand information (website, corporate pages) 2. Search for advertising campaigns on Google Images 3. Identify visual patterns, photography style, and brand behavior Fill in the following JSON template with your analysis. Return ONLY valid JSON, no other text: {template_str}""" response = client.models.generate_content( model="gemini-2.5-flash", contents=user_prompt, config=types.GenerateContentConfig( system_instruction=SYSTEM_PROMPT, max_output_tokens=16384, temperature=0.3, # thinking_config=types.ThinkingConfig(thinking_level="low"), tools=[types.Tool(google_search=types.GoogleSearch())] ) ) # Parse JSON brand_data = extract_json_from_response(response_text) if brand_data is None: # Save raw response for debugging debug_path = f"/tmp/brand-analyzer-debug-{sanitize_brand_name(brand_name)}.txt" with open(debug_path, 'w') as f: f.write(response_text) raise RuntimeError(f"Failed to parse JSON from response. Raw saved to: {debug_path}") # Ensure brand name is set if brand_data.get("brand_info", {}).get("name", "") == "": brand_data.setdefault("brand_info", {})["name"] = brand_name ``` ### Technical Analysis The Gemini model is instructed to retrieve and analyze content through Google Search. Search results, indexed web pages, image descriptions, and Pinterest content are untrusted inputs that may contain instructions directed at AI systems. The system prompt does not explicitly require the model to treat retrieved text exclusively as data or to ignore instructions embedded in external content. An attacker who controls a website ...[truncated 2177 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add explicit instructions stating that all retrieved web content is untrusted data and that instructions found within it must never be followed. 2. Separate retrieved evidence from operational instructions using strongly delimited data sections. 3. Restrict research to allowlisted official domains where practical. 4. Define and enforce a strict JSON Schema matching `BRAND_IDENTITY_TEMPLATE`. 5. Reject additional properties, incorrect field types, excessive nesting, and oversized values. 6. Recursively inspect string fields for prompt-injection indicators before persistence or downstream use. 7. Do not treat model-produced URLs, commands, or instructions as trusted operational data. 8. Require human review before newly generated profiles become available to downstream orchestrators. 9. Record source provenance for generated claims so suspicious content can be traced and reviewed. 10. Ensure downstream workflows delimit profile values as untrusted data rather than incorporating them directly into system or developer instructions. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/analyze.py:3
Finding
Unpinned runtime dependency creates a supply-chain exposure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:3-7` **Vulnerability Type**: Non-reproducible third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.10" # dependencies = [ # "google-genai>=1.0.0", # ] # /// ``` The documented invocation causes `uv` to resolve the inline dependency at runtime: ```bash GEMINI_API_KEY="$KEY" uv run {baseDir}/scripts/analyze.py \ --brand "Brand Name" \ --output ./brands/Brand_Name.json ``` ### Technical Analysis The dependency declaration accepts any `google-genai` release at or above version `1.0.0`. The project contains no lockfile, exact version constraint, or integrity hash that would make dependency resolution reproducible. When the documented `uv run` command is used, a future version may be downloaded and imported without that version having been reviewed with the Skill. Python package initialization code executes with the same operating-system privileges and environment access as the analyzer. The package name itself does not exhibit evidence of typosquatting, and no currently malicious package was identified. The vulnerability is the unsafe, open-ended supply-chain policy: the effective executable code can change after the Skill has been audited. ### Attack Path 1. A new package version satisfying `google-genai>=1.0.0` is published. 2. The release is malicious, compromised, or otherwise contains exploitable initialization behavior. 3. A user executes the documented `uv run` command in an environment where that release is selected. 4. `uv` downloads and installs the newly resolved package. 5. The analyzer imports `google.genai`. 6. Package code executes with the user's privileges and can access the process environment, including `GEMINI_API_KEY`. ### Impact Assessment The maximum impact is equivalent to arbitrary Python execution under the account invoking the Skill. Depending on that account's permissions, a c ...[truncated 442 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `google-genai` to a reviewed exact version instead of using an open-ended lower bound. 2. Commit a lockfile generated by the chosen package manager. 3. Verify package integrity using hashes where the deployment process supports them. 4. Configure an approved package index rather than implicitly trusting arbitrary index configuration. 5. Review dependency updates before changing the pinned version. 6. Run dependency vulnerability and provenance checks in CI. 7. Execute the analyzer in a restricted environment with only the filesystem and network permissions required for its task. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyze.py:381
Finding
Gemini API key can be exposed through command-line arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:381` **Vulnerability Type**: Sensitive credential accepted through process arguments **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--api-key", help="Gemini API key (or set GEMINI_API_KEY)") ``` The argument is subsequently selected as a credential source: ```python def get_api_key(provided_key: str | None) -> str | None: """Get API key from argument, env var, or fail.""" if provided_key: return provided_key return os.environ.get("GEMINI_API_KEY") ``` ### Technical Analysis Command-line arguments are not an appropriate secret-transport mechanism on many systems. A key supplied as `--api-key VALUE` may be exposed through: - Shell history. - Process-listing utilities. - `/proc` process metadata on applicable operating systems. - Job-runner and orchestration logs. - Terminal-session recording. - Diagnostic or monitoring telemetry. Although the environment-variable alternative is available and the documentation primarily demonstrates it, the explicit command-line option encourages a credential-handling path that can disclose the key outside the process. ### Attack Path 1. A user invokes the analyzer with `--api-key` followed by a valid Gemini API key. 2. The shell records the complete command in history, or another local process reads the process command line while the analyzer is running. 3. An unauthorized user or logging system obtains the key. 4. The exposed key is used to make Gemini API requests under the victim's account and quota. ### Impact Assessment An attacker obtaining the key may gain access to the Gemini API permissions associated with that credential. Potential consequences include: - Unauthorized API use and quota consumption. - Financial charges where billing is enabled. - Access to API capabilities enabled for the key. - Service disruption through quota exhaustion. This issue does not itself expose unrelated system ...[truncated 118 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--api-key` command-line option. 2. Read the key from a protected environment variable, operating-system secret manager, or file with restrictive permissions. 3. If interactive entry is required, use a non-echoing prompt such as `getpass.getpass()`. 4. Ensure automation systems inject secrets through dedicated secret-management features rather than command strings. 5. Document that credentials must not be placed in shell history, configuration committed to source control, or logs. 6. Apply API-key restrictions, quota limits, monitoring, and regular rotation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyze.py:350
Finding
Predictable debug file permits symlink-based file overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:350-355` **Vulnerability Type**: Unsafe predictable temporary-file creation **Risk Level**: Medium ### Vulnerable Code ```python if brand_data is None: # Save raw response for debugging debug_path = f"/tmp/brand-analyzer-debug-{sanitize_brand_name(brand_name)}.txt" with open(debug_path, 'w') as f: f.write(response_text) raise RuntimeError(f"Failed to parse JSON from response. Raw saved to: {debug_path}") ``` ### Technical Analysis The debug filename is deterministically derived from the user-supplied brand name and is created in the shared `/tmp` directory. The ordinary `open(..., 'w')` call does not request exclusive creation and follows symbolic links. On a multi-user system, a local attacker can predict the path and create a symbolic link at that location before the analyzer writes the debug response. If JSON parsing fails, the process follows the link and truncates or overwrites its target with attacker-influenced model output. The file is also created using permissions determined by the process umask. Raw model output may therefore be readable by other local users under permissive configurations, and the file is not automatically removed. ### Attack Path 1. The attacker learns or predicts the brand name that will be analyzed. 2. The attacker applies the same sanitization rule to derive the debug path. 3. The attacker creates a symbolic link such as `/tmp/brand-analyzer-debug-TargetBrand.txt` pointing to a file writable by the victim. 4. The attacker causes or waits for Gemini to return output that cannot be parsed as JSON. 5. The analyzer enters the error-handling branch and opens the predictable path in write mode. 6. The operating system follows the symbolic link. 7. The target file is truncated and replaced with the raw model response. ### Impact Assessment The process can overwrite any file writable by the user running the analyzer. This may resul ...[truncated 546 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use `tempfile.NamedTemporaryFile()` or `tempfile.mkstemp()` to obtain an unpredictable, atomically created file. 2. Create the file with permissions restricted to the current user, preferably mode `0600`. 3. Avoid shared temporary directories when a private application-state directory is available. 4. Do not retain raw model responses unless debugging is explicitly enabled. 5. Automatically delete debug files after use or provide a documented retention policy. 6. If a stable filename is unavoidable, use exclusive creation and reject symbolic links with platform-appropriate flags such as `O_CREAT | O_EXCL | O_NOFOLLOW`. 7. Avoid returning the full temporary path in broadly collected logs unless necessary. ]]>
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 (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior does not fully match the actual behavior: it omits explicit disclosure of external network access to Gemini and Google Search, while also claiming list/update capabilities not represented here. Undisclosed network egress is security-relevant because brand names, prompts, and potentially sensitive workflow context may be sent to third-party services without clear user awareness or policy gating.

Credential Access

High
Category
Privilege Escalation
Content
def get_api_key(provided_key: str | None) -> str | None:
    """Get API key from argument, env var, or fail."""
    if provided_key:
        return provided_key
    return os.environ.get("GEMINI_API_KEY")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill documents capabilities that rely on environment-variable access and writing files, but it declares no explicit tool scope or permissions boundary. That creates a least-privilege failure: an agent or reviewer cannot clearly constrain or audit what the skill is allowed to access, increasing the risk of unintended secret exposure or filesystem modification.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script sends the user-provided brand name and related research prompts to Google Gemini with Google Search enabled, but it provides no explicit notice or consent flow warning that input data will be transmitted to external Google services. In a creative workflow this can expose confidential client, stealth-brand, or embargoed campaign information to third-party processing, creating a real privacy and data-governance issue even though the behavior appears intended for normal operation.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The skill promotes automatic saving into a fixed catalog path but provides no warning about overwriting existing files or modifying shared brand-profile data. In a multi-workflow environment, this can cause accidental data loss, integrity issues, or poisoning of downstream creative workflows that trust the stored profile.

Static analysis

No suspicious patterns detected.