Back to skill

Security audit

Recipe to List

Security checks for vulnerabilities and agentic risk

Overview

The skill’s recipe-to-shopping-list purpose is coherent, but it uses broad local credential loading and live Todoist/workspace changes that need review before installation.

Review and harden this skill before installing. Use --dry-run and --no-save first, avoid the shell wrapper or replace its dotenv sourcing with an allowlisted parser for only GEMINI_API_KEY/GOOGLE_API_KEY and TODOIST_API_TOKEN, and require a confirmation step before Todoist updates. Install only if you are comfortable sending recipe photos or extracted text to Gemini and writing recipe files into the workspace.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/recipe-to-list.sh:10
Finding

Executable dotenv loading permits arbitrary shell execution and overexposes secrets

Content
View full analysis
Remediation
View remediation

T02 · Agent Memory Poisoning

Warning
Location
scripts/recipe_to_list.py:798
Finding

Untrusted recipe and model content is persisted into an agent-facing knowledge base

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/recipe_to_list.py:940
Finding

Undefined section-handling symbols cause crashes after successful Todoist mutations

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (20)

Tainted flow: 'req' from os.environ.get (line 528, 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/recipe_to_list.py (reported line 119)May include surrounding context.

python
method="POST",
        )
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                raw = resp.read().decode("utf-8")
            break
        except urllib.error.HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 528, 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/recipe_to_list.py (reported line 535)May include surrounding context.

python
method="POST",
        )
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                raw = resp.read().decode("utf-8")
            break
        except urllib.error.HTTPError as e:

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented behavior does not accurately match the implemented or required capabilities: it claims recipe web search/fetch flow and benign recipe processing, but the skill also depends on undeclared external network calls, Todoist CLI execution, and workspace writes. Description-behavior mismatch is dangerous because users and security controls may approve the skill under incomplete assumptions, leading to unanticipated data exfiltration to Gemini or unintended modification of Todoist and local files.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/recipe-to-list.sh (reported line 12)May include surrounding context.

sh
# Load keys/tokens
set -a
[[ -f ~/.clawdbot/.env ]] && source ~/.clawdbot/.env
set +a

python3 "$(dirname "$0")/recipe_to_list.py" --image "$IMG" --project "Shopping" --source "photo:$IMG"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill advertises operational behavior that requires sensitive capabilities (environment access, file write, network access, shell/CLI invocation) but does not declare any tool scope or permissions. This weakens reviewability and consent, making it easier for a user or orchestrator to invoke a skill that can modify Todoist data, write workspace files, and send content to external services without explicit capability boundaries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill sends recipe photos or fetched page text to Gemini for ingredient extraction, but the description does not clearly disclose this external transmission. This is a privacy and compliance risk because images or recipe pages may contain personal notes, metadata, subscription content, or other sensitive information that users may not expect to leave their environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill text describes updating the Shopping list but does not prominently warn that it will create or modify Todoist tasks, which is a state-changing action in a third-party account. In agent settings, insufficient disclosure around write operations can cause accidental data modification, duplicate tasks, or unwanted automation if a user expects analysis only.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The wrapper unconditionally sources ~/.clawdbot/.env and exports every variable into the child process environment. Even if intended to provide API keys, this grants the skill access to all secrets stored in that file, which exceeds the minimal privileges implied by a recipe-to-shopping-list tool and increases the blast radius if the downstream Python script, its dependencies, or any network call mishandles environment data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script silently loads local secrets from ~/.clawdbot/.env without informing the user at runtime. In this skill's context, which processes user-supplied recipe images and may call external services, undisclosed secret loading makes the data flow less transparent and can expose unrelated credentials to components the user would not expect.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/recipe_to_list.py (reported line 647)May include surrounding context.

python
def _get_existing_project_tasks(project: str) -> list[dict]:
    cp = subprocess.run(
        ["todoist", "tasks", "--all", "-p", project, "--json"],
        capture_output=True,
        text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/recipe_to_list.py (reported line 929)May include surrounding context.

python
# If existing has no qty but new does, set existing to new qty.
                if (ex_qty is None or not ex_unit) and (new_qty is not None and new_unit):
                    new_content = _rewrite_with_total(ex_content, new_qty, new_unit)
                    cp = subprocess.run(
                        ["todoist", "update", str(ex_id), "--content", new_content, "--json"],
                        capture_output=True,
                        text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/recipe_to_list.py (reported line 963)May include surrounding context.

python
# If existing has no qty but new does, set existing to new qty.
                if (ex_qty is None or not ex_unit) and (new_qty is not None and new_unit):
                    new_content = _rewrite_with_total(ex_content, new_qty, new_unit)
                    cp = subprocess.run(
                        ["todoist", "update", str(ex_id), "--content", new_content, "--json"],
                        capture_output=True,
                        text=True,

Tainted flow: 'new_content' from os.environ.get (line 928, credential/environment) → subprocess.run (code execution)

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/recipe_to_list.py (reported line 929)May include surrounding context.

python
# If existing has no qty but new does, set existing to new qty.
                if (ex_qty is None or not ex_unit) and (new_qty is not None and new_unit):
                    new_content = _rewrite_with_total(ex_content, new_qty, new_unit)
                    cp = subprocess.run(
                        ["todoist", "update", str(ex_id), "--content", new_content, "--json"],
                        capture_output=True,
                        text=True,

Tainted flow: 'new_content' from os.environ.get (line 958, credential/environment) → subprocess.run (code execution)

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/recipe_to_list.py (reported line 963)May include surrounding context.

python
total = None

                if 'total' in locals() and total is not None:
                    cp = subprocess.run(
                        ["todoist", "update", str(ex_id), "--content", new_content, "--json"],
                        capture_output=True,
                        text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/recipe_to_list.py (reported line 984)May include surrounding context.

python
if existing_task and skip_overlap:
            continue

        cp = subprocess.run(
            ["todoist", "add", title, "--project", project, "--json"],
            capture_output=True,
            text=True,

Tainted flow: 'title' from os.environ.get (line 1044, credential/environment) → subprocess.run (code execution)

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/recipe_to_list.py (reported line 984)May include surrounding context.

python
if existing_task and skip_overlap:
            continue

        cp = subprocess.run(
            ["todoist", "add", title, "--project", project, "--json"],
            capture_output=True,
            text=True,

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest says the skill can extract ingredients from either recipe photos or recipe web pages via search and fetch. In this file, the CLI requires an --image argument, verifies that image path exists, and then calls only the image-based Gemini extraction flow; there is no webpage search, fetch, or URL parsing behavior implemented here.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script automatically uploads the provided recipe photo to Gemini without an explicit runtime warning or confirmation. Because photos may contain personal notes, kitchen surroundings, or other incidental sensitive information, this creates a privacy risk through third-party data disclosure in a consumer productivity skill.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code comments say the list stays flat, but the function contains live references to target_section and _move_task even though neither is defined in this file. In non-dry-run operation this can raise a NameError after creating or updating tasks, causing partial side effects and unreliable behavior that can corrupt workflow integrity.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill mutates the user's Todoist shopping list and can update existing tasks based on model-derived parsing without an explicit confirmation step. In this context, integrity matters: a mistaken extraction or hallucinated quantity can silently alter shopping data and create unintended purchases or loss of existing list accuracy.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.