Back to skill

Security audit

Local QR Code Generation And Recognition Not Require An API_KEY - 本地二维码生成与识别

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local QR-code tool, but it needs review because its file path handling and input limits can let it read or overwrite more than users would expect.

Install only in a contained environment and use a dedicated output directory. Avoid running it on untrusted paths or workspaces containing symlinks, and do not pass very large QR rendering parameters until path handling, overwrite behavior, duplicate code, and input limits are fixed.

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
qrcode.py:10
Finding
Symlink-Based Escape from the Intended Filesystem Boundary<![CDATA[ ## Vulnerability Details **File Location**: `qrcode.py:10-40`; affected callers at `qrcode.py:91-99` and `qrcode.py:120-130` **Vulnerability Type**: Insufficient path validation and symlink traversal **Risk Level**: High ### Vulnerable Code ```python def _normalize_local_path(user_path: str, field: str) -> Dict[str, Any]: """ 规范化并限制本地文件路径,只允许在当前工作目录及其子目录内读写。 禁止绝对路径和目录穿越(包含 ..)。 """ if not user_path: return { "error": "invalid_param", "message": f"field '{field}' is empty", } # 禁止绝对路径 if os.path.isabs(user_path): return { "error": "invalid_path", "message": f"Absolute path is not allowed for '{field}'", } # 规范化并检查目录穿越 norm = os.path.normpath(user_path) if norm.startswith("..") or norm == "..": return { "error": "invalid_path", "message": f"Path traversal is not allowed for '{field}'", } base = os.getcwd() full = os.path.join(base, norm) return {"error": None, "path": full, "relative": norm} ``` The resulting path is used for output as follows: ```python safe = _normalize_local_path(out_raw, "out") if safe["error"]: return safe out = safe["path"] out_dir = os.path.dirname(out) if out_dir: os.makedirs(out_dir, exist_ok=True) try: img.save(out) except Exception as e: return {"error": "save_failed", "message": str(e), "path": out} ``` It is also used for input as follows: ```python safe = _normalize_local_path(path_raw, "path") if safe["error"]: return safe path = safe["path"] if not os.path.isfile(path): return {"error": "file_not_found", "message": f"File not found: {safe['relative']}"} img = cv2.imread(path) ``` ### Technical Analysis The path validation is lexical. It rejects absolute paths and normalized strings beginning with `..`, but it does not resolve symbolic links before deciding whether a path remains inside the current working directory. ...[truncated 2133 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve both the trusted base directory and candidate path: ```python base = os.path.realpath(os.getcwd()) candidate = os.path.realpath(os.path.join(base, norm)) if os.path.commonpath([base, candidate]) != base: return { "error": "invalid_path", "message": f"Path escapes the working directory for '{field}'", } ``` 2. Reject symbolic links in every existing component of the candidate path when symlinks are not required by the feature. 3. For output files that do not yet exist, validate the resolved parent directory separately. 4. Reduce time-of-check/time-of-use exposure by using descriptor-relative filesystem operations and no-follow semantics, such as `openat`-style APIs and `O_NOFOLLOW`, where supported. 5. For output, reject an existing destination if it is a symbolic link and consider exclusive creation when overwriting is not required. 6. Add tests covering: - A direct absolute path. - `../` traversal. - A symlinked input file. - A symlinked parent directory. - A symlinked output destination. - A symlink changed between validation and access. 7. Remove the duplicated implementation so that the corrected validation logic has only one authoritative definition. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
qrcode.py:58
Finding
Unbounded QR Rendering Parameters Permit Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `qrcode.py:58-88`; duplicated at `qrcode.py:193-223` **Vulnerability Type**: Missing input limits and incomplete exception handling **Risk Level**: Medium ### Vulnerable Code ```python out_raw = req.get("out") or "qrcode.png" version = req.get("version") box_size = int(req.get("box_size", 10)) border = int(req.get("border", 4)) ec = str(req.get("error_correction", "M")).upper() ec_map = { "L": ERROR_CORRECT_L, "M": ERROR_CORRECT_M, "Q": ERROR_CORRECT_Q, "H": ERROR_CORRECT_H, } ec_const = ec_map.get(ec, ERROR_CORRECT_M) qr = qrcode.QRCode( version=None if version in (None, "", 0) else int(version), error_correction=ec_const, box_size=box_size, border=border, ) qr.add_data(text) qr.make(fit=True) fill_color = req.get("fill_color", "black") back_color = req.get("back_color", "white") img = qr.make_image(fill_color=fill_color, back_color=back_color) ``` ### Technical Analysis Caller-controlled `text`, `box_size`, and `border` values are accepted without application-level size limits. A very large rendering scale or border can cause QR image generation to request excessive memory and CPU resources. Oversized input data can also consume processing resources before the QR library rejects it. Numeric conversion and QR generation occur outside an exception handler. Inputs such as a nonnumeric `box_size` or an unsupported `version` can therefore raise uncaught exceptions and terminate the process rather than returning a structured error. The entire application is duplicated in the same file. Both `if __name__ == "__main__": main()` blocks execute during a normal invocation, so a successful operation is performed twice. This duplication can amplify CPU, memory, and filesystem costs. ### Attack Path 1. An attacker who can invoke the Skill supplies a request containing an extreme rendering parameter: ```bash python3 qrcode.py encode \ '{"text":"A","box_size":1000000," ...[truncated 1158 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define strict application-level limits before constructing the QR object: - Restrict `version` to `1` through `40`, or allow a documented automatic mode. - Require `box_size` to be a positive integer below a conservative maximum. - Require `border` to be a nonnegative integer below a conservative maximum. - Limit encoded text by UTF-8 byte length. - Calculate and cap the final image width, height, and pixel count. 2. Reject invalid error-correction values rather than silently substituting `M`. 3. Catch `TypeError`, `ValueError`, QR overflow exceptions, Pillow errors, and memory-related failures where safe to do so, then return structured error responses. 4. Apply operating-system resource limits or execute image processing in an isolated worker with CPU, memory, file-size, and execution-time quotas. 5. Validate input image dimensions and file sizes for decode operations as an additional defense against image-processing resource exhaustion. 6. Remove lines 173-343 containing the duplicated application. Retain one set of functions and one `if __name__ == "__main__"` entry point. 7. Add tests for boundary values, malformed numeric types, oversized text, excessive dimensions, and duplicate side effects. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:25
Finding
Unpinned Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:25-27` **Vulnerability Type**: Unpinned and non-reproducible dependency installation **Risk Level**: Low ### Vulnerable Code ```bash pip install "qrcode[pil]" opencv-python ``` ### Technical Analysis The documented installation command downloads the latest versions of `qrcode`, Pillow-related extras, and `opencv-python` available from the configured package index. The project provides no lock file, exact versions, or package hashes. This does not establish that the named packages are currently malicious. However, it makes installation non-reproducible and means the code reviewed during the audit may run against different dependency versions later. If an upstream account, release process, package index, or dependency is compromised, users following the instructions could install altered code without any integrity pin detecting the change. Python package installation can execute build-related code and installs executable library code that will later be imported by `qrcode.py`. ### Attack Path 1. A user follows the dependency installation instructions in `SKILL.md`. 2. `pip` resolves the unconstrained packages and transitive dependencies from the user's configured index. 3. The selected releases may differ from versions previously tested or reviewed. 4. If an upstream package or index response has been compromised, malicious package code can execute during installation or when imported. 5. That code runs with the privileges of the user or environment executing `pip` or the Skill. This path depends on an external supply-chain compromise or unsafe package-index configuration; no malicious dependency was identified in the audited files themselves. ### Impact Assessment The immediate confirmed impact is loss of build reproducibility and dependency integrity assurance. Under a successful external supply-chain compromise, package code could obtain the same filesystem, network, and process privileg ...[truncated 223 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed dependency lock file containing exact package and transitive-dependency versions. 2. Generate and verify cryptographic hashes for all distributions. 3. Install with hash enforcement: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` 4. Use a trusted, explicitly configured package index and prohibit unexpected extra indexes. 5. Prefer prebuilt, reviewed wheels where appropriate and isolate installation in a virtual environment or container. 6. Use automated dependency scanning and controlled update reviews. 7. Document the supported Python and dependency versions so deployed environments match the audited configuration. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrase includes broad wording like 'or similar local QR code problems', which can cause the agent to invoke this skill outside a narrowly intended scope. Over-broad routing increases the chance of inappropriate file processing or local image handling when another tool or a clarification step would be safer.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The skill documents writing generated QR images to local paths but does not prominently warn that it performs filesystem writes. In agent environments, insufficient disclosure about local file creation can lead to unexpected persistence, accidental overwrites, or user confusion about where data is stored.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The file is `qrcode.py`, but the first `main()` usage string tells users to invoke `qrcode2.py`. This inline documentation contradicts the actual file identity and could mislead operators about how to use the skill.

Static analysis

No suspicious patterns detected.