Back to skill

Security audit

calorie-detective-v1

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent food-photo calorie purpose, but its implementation can send arbitrary readable files and wrongly selected API keys to external providers if misused or misconfigured.

Review before installing. Use only with non-sensitive food images, run it with a narrowly scoped upload directory if possible, and fix image validation plus provider-specific API key selection before production use. Prefer pinned dependencies and add a clear consent notice for third-party image analysis.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/calorie_calculator.py:458
Finding
Arbitrary Local File Contents Can Be Transmitted to External Vision APIs<![CDATA[ ## Vulnerability Details **File Location**: `src/calorie_calculator.py`, lines 56-58, 64-65, 94, 103-108, 137-138, 167, 176-181, and 458-469 **Vulnerability Type**: Insufficient file-type and path validation before external transmission **Risk Level**: High ### Vulnerable Code ```python def _encode_image(self, image_path: str) -> str: """Encode the image as Base64.""" with open(image_path, 'rb') as f: return base64.b64encode(f.read()).decode('utf-8') ``` The Kimi request reads the entire supplied file and represents it as a JPEG without validating its contents: ```python with open(image_path, 'rb') as f: base64_image = base64.b64encode(f.read()).decode('utf-8') payload = { "model": self.model, "messages": [ { "role": "user", "content": [ { "type": "text", "text": """Please identify the food in this image and return JSON.""" }, { "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{base64_image}" } } ] } ], "max_tokens": 500 } response = requests.post( "https://api.moonshot.cn/v1/chat/completions", headers=headers, json=payload, timeout=30 ) ``` The OpenAI path repeats the same behavior: ```python with open(image_path, 'rb') as f: base64_image = base64.b64encode(f.read()).decode('utf-8') # ... "image_url": { "url": f"data:image/jpeg;base64,{base64_image}" } # ... response = requests.post( "https://api.openai.com/v1/chat/completions", headers=headers, json=payload, timeout=30 ) ``` The command-line entry point checks only whether the path exists: ```python image_path = sys.argv[1] if not os.path.exists(image_path): print(f"Error: image file does not exist: {image_path}") sys.exit(1) calculator = FoodCalo ...[truncated 2650 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require a regular file and reject directories, devices, FIFOs, and special files: ```python path = Path(image_path).resolve(strict=True) if not path.is_file(): raise ValueError("Input must be a regular image file") ``` 2. Restrict access to a dedicated upload directory or to a trusted file handle supplied by the hosting platform. Resolve symbolic links before enforcing the boundary: ```python upload_root = Path("/srv/app/uploads").resolve() path = Path(image_path).resolve(strict=True) if upload_root not in path.parents: raise ValueError("Image is outside the approved upload directory") ``` 3. Set a conservative maximum encoded input size before reading the complete file. 4. Decode the file with Pillow and verify that it is a supported image: ```python with Image.open(path) as image: image.verify() with Image.open(path) as image: if image.format not in {"JPEG", "PNG", "WEBP"}: raise ValueError("Unsupported image format") ``` 5. Re-encode the decoded image into a canonical format before sending it. This ensures that arbitrary trailing or embedded file data is not forwarded. 6. Derive the media type from the verified image format rather than always labeling the payload as JPEG. 7. Display an explicit notice that the image will be transferred to the selected cloud provider and obtain user confirmation where appropriate. 8. Add tests proving that text files, symbolic links escaping the upload directory, oversized inputs, malformed images, and special files are rejected before any network request occurs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/calorie_calculator.py:370
Finding
Cross-Provider API Key Confusion Can Disclose Credentials to the Wrong Service<![CDATA[ ## Vulnerability Details **File Location**: `src/calorie_calculator.py`, lines 28-31, 69, 103-108, and 370-375 **Vulnerability Type**: Incorrect credential selection and cross-provider secret fallback **Risk Level**: High ### Vulnerable Code The recognizer accepts either provider's environment variable without binding the credential to the selected provider: ```python def __init__(self, provider: str = 'kimi', api_key: str = None, model: str = None): self.provider = provider self.api_key = api_key or os.environ.get('KIMI_API_KEY') or os.environ.get('OPENAI_API_KEY') self.model = model or os.environ.get('KIMI_MODEL', 'moonshot-v1-auto') ``` The main calculator always passes the OpenAI configuration field, even if `vision.provider` is `kimi`: ```python vision_config = self.config.get('vision', {}) api_keys = self.config.get('api_keys', {}) self.recognizer = VisionRecognizer( provider=vision_config.get('provider', 'openai'), api_key=api_keys.get('openai') ) ``` The resulting value is sent as a bearer credential to the endpoint selected by the provider: ```python headers = { "Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}" } response = requests.post( "https://api.moonshot.cn/v1/chat/completions", headers=headers, json=payload, timeout=30 ) ``` ### Technical Analysis Credentials should be selected according to the authenticated service and must not fall back across unrelated trust domains. Here, a non-empty `api_keys.openai` value takes precedence because it is passed explicitly to `VisionRecognizer`, including when the configured provider is Kimi. The credential is then placed into the `Authorization` header of a request to `api.moonshot.cn`. If the explicit value is empty or absent, the constructor still falls back first to `KIMI_API_KEY` and then to `OPENAI_API_KEY` without checking the selected provider. This means: - The Kimi provider can send an OpenAI credential t ...[truncated 1702 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind each provider to its own configuration field and environment variable: ```python provider = vision_config.get("provider", "kimi") provider_keys = { "kimi": api_keys.get("kimi") or os.environ.get("KIMI_API_KEY"), "openai": api_keys.get("openai") or os.environ.get("OPENAI_API_KEY"), } api_key = provider_keys.get(provider) if not api_key: raise ValueError(f"Missing API key for provider: {provider}") self.recognizer = VisionRecognizer( provider=provider, api_key=api_key, model=vision_config.get("model"), ) ``` 2. Remove all cross-provider fallback behavior from `VisionRecognizer`. 3. Reject unsupported providers and missing credentials before opening the image or making a network request. 4. Keep secrets in environment variables or a dedicated secret manager rather than ordinary YAML files. 5. Add unit tests that mock network requests and assert: - Kimi requests use only `KIMI_API_KEY`. - OpenAI requests use only `OPENAI_API_KEY`. - A missing provider-specific key produces an error. - A key belonging only to another provider is never transmitted. 6. Avoid logging authorization headers, request objects, or exception structures that might contain credentials. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:2
Finding
Unpinned Third-Party Dependencies Produce Non-Reproducible and Unsafe Installations<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, lines 2-4; `DEPLOY.md`, line 35 **Vulnerability Type**: Unpinned executable dependencies and missing integrity verification **Risk Level**: Medium ### Vulnerable Code Runtime dependencies use open-ended minimum versions: ```text requests>=2.28.0 pyyaml>=6.0 Pillow>=9.0.0 ``` The deployment documentation also installs a command-line package without a reviewed version or hash: ```bash pip install kimi-claw ``` ### Technical Analysis Python package installation may execute package build or installation logic with the privileges of the invoking user. Open-ended version constraints permit future package releases to be selected without further review. The unversioned `kimi-claw` installation has the same problem and introduces an executable deployment tool into the environment. The identified package names are not demonstrated to be malicious, and no dependency-confusion or typosquatting package was confirmed. The vulnerability is the absence of version locking and integrity verification, which creates a supply-chain exposure and prevents reproducible builds. The deployment command is documentation rather than an automatic action in `run.sh`; exploitation therefore requires an operator to follow the installation instructions. ### Attack Path 1. An operator follows the deployment guide or installs `requirements.txt`. 2. `pip` resolves the newest versions that satisfy the open-ended constraints, or the newest available `kimi-claw` release. 3. A future compromised release, malicious maintainer update, compromised package account, or manipulated package source supplies altered code. 4. `pip` downloads the package without checking a project-maintained cryptographic hash. 5. Package build or installation code executes with the operator's privileges. 6. The installed code subsequently runs as part of deployment or normal Skill execution. ### Impact Assessment The maximum impact depends on ...[truncated 635 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct and transitive dependency to a reviewed version through a lock file. 2. Generate and enforce cryptographic hashes: ```bash pip-compile --generate-hashes requirements.in pip install --require-hashes -r requirements.txt ``` 3. Pin the deployment CLI to a reviewed version: ```bash pip install --require-hashes -r deployment-requirements.txt ``` 4. Install dependencies inside an isolated virtual environment or container as a non-root user. 5. Use an approved package index and explicitly configure trusted sources. Do not add unknown extra indexes. 6. Scan locked dependencies for known vulnerabilities in continuous integration and review updates before merging them. 7. Build immutable deployment artifacts so production startup does not resolve or download packages dynamically. 8. Where feasible, verify package provenance, signatures, and software bills of materials in addition to hashes. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (30)

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The usage instructions tell users to upload food photos for analysis but do not warn that those images will be sent to a remote third-party AI service. This creates a privacy and consent risk because food photos may contain sensitive contextual information such as location, other people, documents, or health-related dietary data.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The deployment guide explicitly encourages custom natural-language triggers such as "自定义触发词" without any constraints, namespace guidance, or negative examples. Broad triggers can cause accidental activation on unrelated user messages, increasing unintended image processing and data transfer to the remote model service, especially in a consumer chat environment.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documentation describes capabilities that require environment variable access, local file access, and outbound network access, but it does not declare any explicit tool scope or permissions. This creates an under-specified trust boundary where deployers may enable broader access than intended, increasing the chance of accidental over-privilege and misuse of secrets or local files.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The feature description promotes photo upload and AI vision analysis but does not warn that user food images may be sent to external providers. Because photos can contain sensitive incidental data such as location clues, people, or private surroundings, failing to disclose this external transmission creates a privacy and consent risk.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The skill claims support for natural-language triggering without defining exact invocation phrases, allowed contexts, or boundaries. Broad activation semantics can cause unintended invocation or prompt-capture by unrelated user text, especially in assistant environments where tool routing is automatic.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# 测试 API Key
curl https://api.moonshot.cn/v1/chat/completions \
  -H "Authorization: Bearer $KIMI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "moonshot-v1-auto", "messages": [{"role": "user", "content": "Hello"}]}'
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The manifest sets `OUTPUT_LANGUAGE: "zh"`, which imposes a specific language choice in the skill configuration. Under the policy, locale or language constraints should either be user-selectable or clearly justified as region-specific; neither is indicated here.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The prompt literal explicitly instructs the model to answer in Chinese, and the file-level description/reporting is likewise Chinese-only. This forces a specific language/locale without offering the user a choice or documenting a justified region-specific restriction.

External Transmission

Medium
Category
Data Exfiltration
Content
"max_tokens": 500
        }
        
        response = requests.post(
            "https://api.moonshot.cn/v1/chat/completions",
            headers=headers,
            json=payload,
Confidence
92% confidence
Finding
This request sends user-supplied image data to an external network endpoint, which is a genuine data exposure boundary. In this skill's context, that matters because uploaded photos may include more than food alone, and the transfer occurs automatically as part of recognition without any apparent minimization or consent enforcement in code.

External Transmission

Medium
Category
Data Exfiltration
Content
"max_tokens": 500
        }
        
        response = requests.post(
            "https://api.moonshot.cn/v1/chat/completions",
            headers=headers,
            json=payload,
Confidence
92% confidence
Finding
This request sends user-supplied image data to an external network endpoint, which is a genuine data exposure boundary. In this skill's context, that matters because uploaded photos may include more than food alone, and the transfer occurs automatically as part of recognition without any apparent minimization or consent enforcement in code.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code base64-encodes the local image and sends it to Moonshot/Kimi's external API for processing, but there is no explicit user-facing consent, privacy notice, or data-handling disclosure in the code path. This is a real privacy/security weakness because food photos may contain sensitive incidental data such as faces, location clues, medical/dietary information, or metadata that users may not expect to leave the local environment.

External Transmission

Medium
Category
Data Exfiltration
Content
}
        
        response = requests.post(
            "https://api.moonshot.cn/v1/chat/completions",
            headers=headers,
            json=payload,
            timeout=30
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}
        
        response = requests.post(
            "https://api.moonshot.cn/v1/chat/completions",
            headers=headers,
            json=payload,
            timeout=30
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This second provider path repeats the instruction to answer in Chinese, creating the same language-policy issue across providers. Users are not offered a locale selection or opt-in despite the skill enforcing a specific language.

External Transmission

Medium
Category
Data Exfiltration
Content
"max_tokens": 500
        }
        
        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers=headers,
            json=payload,
Confidence
93% confidence
Finding
The OpenAI request is an external transmission of potentially sensitive user image data and therefore represents a real privacy/security concern. While external API use is expected for vision features, the absence of safeguards around notice, consent, and minimization makes this a valid issue rather than a false positive.

External Transmission

Medium
Category
Data Exfiltration
Content
"max_tokens": 500
        }
        
        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers=headers,
            json=payload,
Confidence
93% confidence
Finding
The OpenAI request is an external transmission of potentially sensitive user image data and therefore represents a real privacy/security concern. While external API use is expected for vision features, the absence of safeguards around notice, consent, and minimization makes this a valid issue rather than a false positive.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The OpenAI path transmits the full image contents to api.openai.com without any visible consent or warning mechanism. This is dangerous in context because users of a calorie calculator may assume simple local analysis, while uploaded meal images can expose private surroundings, health-related habits, or other personal data to a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
}
        
        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers=headers,
            json=payload,
            timeout=30
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The file consistently uses Chinese for the skill name, description, commands, and instructions, but does not indicate that the Chinese-only experience is intentional or provide an opt-in language choice. This can be a language-policy issue when a skill effectively forces a specific language without user selection.

Context-Inappropriate Capability

Low
Confidence
85% confidence
Finding
The documentation introduces cron-based scheduled task management even though the stated purpose is on-demand food-photo calorie analysis. Unnecessary scheduling expands the operational surface area and could enable unattended execution, repeated network calls, or background processing that users did not expect.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The display name and slogan are entirely in Chinese, which signals a fixed language/locale presentation in the skill metadata. There is no indication in this file that users can opt into that language or that the skill is intentionally limited to a Chinese-speaking region, so this may violate a language/locale choice policy.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The config sets `output.language: zh`, which imposes a specific language/locale in the skill's behavior. Under the policy, forcing a language without user opt-in can be a natural-language policy violation, and this file does not indicate that users can choose their preferred language at runtime.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Food Calorie Calculator Dependencies
requests>=2.28.0
pyyaml>=6.0
Pillow>=9.0.0
Confidence
95% confidence
Finding
The dependency is specified with a lower bound only, which allows future installs to resolve to different versions over time. This undermines reproducible builds and can unintentionally introduce vulnerable or incompatible releases, especially for a package like requests with a history of security advisories.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
Because requests is not pinned, it is impossible to verify from this manifest whether deployments will use a version affected by known CVEs. In a skill that likely makes outbound API calls for vision recognition, an affected HTTP client could expose credentials or weaken transport security depending on runtime behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Food Calorie Calculator Dependencies
requests>=2.28.0
pyyaml>=6.0
Pillow>=9.0.0
Confidence
96% confidence
Finding
Using pyyaml>=6.0 leaves the actual installed version unconstrained above the minimum, so environments may pull different releases. Because PyYAML has had multiple security issues, lack of pinning increases supply-chain and patch-verification risk.

Static analysis

No suspicious patterns detected.