Back to skill

Security audit

qrcode-remote

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent QR-code tool, but it can automatically install dependencies, send user QR data or local images to a third-party service, and overwrite spreadsheet files in ways users should review first.

Install only if you are comfortable with QR contents and some local images being sent to api.2dcode.biz during generation or decode fallback. Avoid using it on confidential QR codes or sensitive screenshots unless you disable or manually control remote fallback. Back up spreadsheets before batch decoding, prefer TXT output where possible, and install dependencies yourself in an isolated environment with pinned versions.

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

T08 · Insecure Dependencies

Warning
Location
SKILL.md:28
Finding
Automatic Installation of Unpinned Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:28-42`, `requirements.txt:1-4`, `package.json:5-10`, `README.md:28-69` **Vulnerability Type**: Supply-chain exposure through automatic installation of unpinned dependencies **Risk Level**: Medium ### Vulnerable Code `SKILL.md:28-42`: ```markdown **Dependency check and automatic installation:** After selecting the runtime, check whether dependencies are installed. If they are missing, install them automatically without asking the user: - **Python**: ```bash pip install -r requirements.txt ``` - **Node.js**: ```bash npm install ``` ``` `requirements.txt:1-4`: ```text zxingcpp Pillow openpyxl qrcode ``` `package.json:5-10`: ```json "dependencies": { "qrcode": "^1.5.0", "qr-scanner-wechat": "^0.1.0", "sharp": "^0.33.0", "xlsx": "^0.18.0", "archiver": "^7.0.0" } ``` ### Technical Analysis The Skill explicitly directs the Agent to install missing dependencies without obtaining user confirmation. All Python dependencies are unpinned, while the npm manifest uses caret ranges and has no accompanying lockfile. No integrity hashes are supplied for Python packages. Consequently, the code installed during first use may differ from the code originally reviewed. Package installation can execute package lifecycle or build logic with the privileges of the Agent process. This creates exposure to compromised package releases, dependency takeover, malicious transitive dependencies, and unexpected incompatible updates. The audit did not establish that any currently named package is malicious. The vulnerability is the unsafe dependency acquisition process and absence of reproducible dependency constraints. ### Attack Path 1. An attacker compromises a direct or transitive dependency, its maintainer account, or its distribution channel. 2. The attacker publishes a malicious version that satisfies the unpinned Python requirement or npm caret range. 3. A user invokes the Skill on a system ...[truncated 707 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit user confirmation before installing or updating dependencies. 2. Pin every direct Python dependency to a reviewed exact version. 3. Generate a hash-locked Python requirements file and install with `pip install --require-hashes`. 4. Commit an npm lockfile and use `npm ci` instead of `npm install`. 5. Pin npm dependencies to reviewed exact versions rather than caret ranges. 6. Review and lock transitive dependencies. 7. Where operationally possible, disable npm lifecycle scripts with `--ignore-scripts`. 8. Install dependencies in an isolated virtual environment or container with minimal filesystem and network permissions. 9. Add automated dependency vulnerability and provenance scanning to the release process. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/batch_decode.py:192
Finding
Spreadsheet Formula Injection Through Decoded QR-Code Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_decode.py:192-209`, `scripts/batch_decode.py:284-295`, `scripts/batch_decode.js:215-224`, `scripts/batch_decode.js:249-257` **Vulnerability Type**: CSV and Excel formula injection **Risk Level**: High ### Vulnerable Code `scripts/batch_decode.py:192-209`: ```python if output_txt: with open(output_txt, "w", encoding="utf-8") as f: f.write("\n".join(decoded_results)) return { "total": len(data_rows), "success": success, "failed": len(data_rows) - success, "output_file": os.path.abspath(input_path), "output_txt": os.path.abspath(output_txt), } result_col = "解码结果" headers.append(result_col) for i, row in enumerate(data_rows): row.append(decoded_results[i]) with open(input_path, "w", encoding="utf-8-sig", newline="") as f: writer = csv.writer(f) writer.writerow(headers) writer.writerows(data_rows) ``` `scripts/batch_decode.py:284-295`: ```python result_col_idx = ws.max_column + 1 ws.cell(row=1, column=result_col_idx, value="解码结果") for i, decoded in enumerate(decoded_results): ws.cell(row=i + 2, column=result_col_idx, value=decoded) wb.save(input_path) wb.close() ``` `scripts/batch_decode.js:215-224`: ```javascript headers.push("解码结果"); dataRows.forEach((r, i) => r.push(decoded[i])); const csvContent = [headers, ...dataRows] .map((r) => r.map((c) => `"${String(c).replace(/"/g, '""')}"`).join(",")) .join("\n"); fs.writeFileSync(inputPath, "\uFEFF" + csvContent, "utf-8"); ``` ### Technical Analysis Decoded QR-code text is attacker-controlled. Both implementations write that content directly into spreadsheet cells without neutralizing formula metacharacters. A value beginning with `=`, `+`, `-`, or `@` may be interpreted as a formula by spreadsheet applications. Quoting a CSV field does not reliably prevent formula evaluation. For XLSX output, assigning a string beginning with `=` through OpenPyXL may create ...[truncated 1597 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every decoded value as untrusted spreadsheet content. 2. Before writing CSV data, detect values whose first non-whitespace character is `=`, `+`, `-`, or `@`. 3. Neutralize dangerous values by prefixing them with an apostrophe or another application-compatible safe marker. 4. For XLSX output, explicitly store decoded values as literal strings rather than formulas. 5. Apply the same protection to all future exported fields, not only the decode-result column. 6. Document that decoded QR content is untrusted and must not be evaluated. 7. Add tests for values such as `=1+1`, `+SUM(1,1)`, `-1+2`, `@SUM(1,1)`, and formulas containing leading whitespace or control characters. 8. Consider exporting results to a non-executable format such as plain text or JSON by default. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/batch_decode.js:249
Finding
Destructive Replacement of Existing Excel Workbooks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_decode.js:249-257` **Vulnerability Type**: Unsafe and destructive workbook overwrite **Risk Level**: High ### Vulnerable Code ```javascript headers.push("解码结果"); dataRows.forEach((r, i) => r.push(decoded[i])); const allRows = [headers, ...dataRows]; const wb = XLSX.utils.book_new(); const ws = XLSX.utils.aoa_to_sheet(allRows); XLSX.utils.book_append_sheet(wb, ws); XLSX.writeFile(wb, inputPath); return { total: dataRows.length, success, failed: dataRows.length - success, output_file: path.resolve(inputPath), output_txt: null }; ``` The input-reading path only extracts the first sheet: ```javascript const wb = XLSX.readFile(fp); return XLSX.utils .sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1, defval: "" }) .map((r) => r.map((c) => String(c ?? ""))); ``` ### Technical Analysis The Node.js implementation does not modify the existing workbook. It converts the first worksheet to a plain two-dimensional array, constructs a completely new workbook containing one newly generated worksheet, and then writes that workbook over the original input path. This conversion discards information not represented by the value array, including: - All worksheets other than the first one - Formulas and formula metadata - Cell formatting and number formats - Charts, drawings, images, comments, and hyperlinks - Named ranges, validations, filters, and workbook metadata - Macros or other unsupported workbook features - Original sheet name and workbook structure The behavior contradicts the documented promise to add a decode-result column to the original workbook. ### Attack Path 1. A user supplies an existing `.xlsx` or `.xls` workbook containing multiple sheets, formulas, formatting, or other important workbook features. 2. The user invokes the Node.js batch decoder without `--output-txt`. 3. The script reads values from only the first worksheet. 4. It creates a new single-sheet workbook ...[truncated 615 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Preserve the loaded workbook object and update only the selected worksheet and result column. 2. Do not reconstruct a workbook from a plain array when modifying an existing file. 3. Write results to a distinct output path by default, such as `<name>_decoded.xlsx`. 4. Require an explicit `--overwrite` option before modifying the original file. 5. Create a backup before any in-place update. 6. Write to a temporary file first, verify that it can be reopened, and then perform an atomic replacement. 7. Preserve sheet names, all worksheets, cell types, formulas, formatting, and workbook metadata. 8. Reject formats or workbook features that the selected library cannot safely preserve. 9. Add regression tests using multi-sheet workbooks with formulas, formatting, charts, comments, hyperlinks, and named ranges. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/decode.js:37
Finding
Unbounded Remote Downloads and Image Processing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/decode.js:37-68`, `scripts/batch_decode.js:35-65`, `scripts/decode.py:25-42`, `scripts/batch_decode.py:58-69` **Vulnerability Type**: Resource exhaustion through unbounded network and image input **Risk Level**: High ### Vulnerable Code `scripts/decode.js:37-68`: ```javascript function httpGet(url) { return new Promise((resolve, reject) => { const mod = url.startsWith("https") ? https : http; mod.get(url, (res) => { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { return httpGet(res.headers.location).then(resolve, reject); } const chunks = []; res.on("data", (c) => chunks.push(c)); res.on("end", () => resolve(Buffer.concat(chunks))); }).on("error", reject); }); } function downloadToTemp(url) { return new Promise((resolve, reject) => { const ext = path.extname(url.split("?")[0]) || ".png"; const tmp = path.join(os.tmpdir(), `qr_${Date.now()}${ext}`); const mod = url.startsWith("https") ? https : http; const file = fs.createWriteStream(tmp); mod.get(url, (res) => { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { file.close(); fs.unlinkSync(tmp); return downloadToTemp(res.headers.location).then(resolve, reject); } res.pipe(file); file.on("finish", () => { file.close(); resolve(tmp); }); }).on("error", (e) => { file.close(); if (fs.existsSync(tmp)) fs.unlinkSync(tmp); reject(e); }); }); } ``` `scripts/decode.py:25-42`: ```python def download_image(url: str) -> str: """Download an image to a temporary file and return its path.""" import urllib.request suffix = Path(url.split("?")[0]).suffix or ".png" tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix) try: urllib.request.urlretrieve(url, tmp.name) except Exception as e: tmp.close() os. ...[truncated 2153 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Accept HTTPS URLs only unless plaintext HTTP is explicitly required and approved. 2. Set strict connection, read, and total-operation timeouts. 3. Limit redirects to a small fixed number and resolve redirect targets with a standards-compliant URL parser. 4. Reject HTTPS-to-HTTP redirect downgrades. 5. Enforce a maximum response size while streaming, regardless of `Content-Length`. 6. Reject missing, invalid, or excessive `Content-Length` values where appropriate. 7. Validate allowed MIME types and inspect file signatures before image processing. 8. Configure maximum image width, height, total pixels, frame count, and decompressed memory. 9. Abort and delete partial temporary files whenever any limit is exceeded. 10. Use randomized temporary filenames with exclusive creation. 11. Add batch-level limits for row count, aggregate downloaded bytes, concurrency, and total runtime. 12. If private-network access is unnecessary, resolve and reject loopback, private, link-local, and metadata-service addresses to reduce server-side request-forgery exposure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/decode.py:145
Finding
Automatic Disclosure of Local Images to a Third-Party API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/decode.py:145-158`, `scripts/decode.js:152-163`, `scripts/batch_decode.py:35-45`, `scripts/batch_decode.js:137-151`, `SKILL.md:247-256` **Vulnerability Type**: Unconfirmed transmission of local files to an external service **Risk Level**: Medium ### Vulnerable Code `scripts/decode.py:145-158`: ```python if mode == "--file": if not os.path.isfile(target): error(f"File does not exist: {target}") if not force_api: results = decode_with_zxing(target) if results: output("zxing", results) results = decode_with_api_file(target) if results: output("api", results) error("Unable to decode: neither local zxing nor the remote API recognized a QR code") ``` The upload implementation reads and transmits the complete file: ```python with open(file_path, "rb") as f: file_data = f.read() body = ( f"--{boundary}\r\n" f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n' f"Content-Type: {mime_type}\r\n\r\n" ).encode() + file_data + f"\r\n--{boundary}--\r\n".encode() req = urllib.request.Request( API_ENDPOINT, data=body, headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, ) with urllib.request.urlopen(req) as resp: data = json.loads(resp.read().decode()) ``` `scripts/batch_decode.py:35-45`: ```python result = _try_zxing(image_source) if result: return result if _is_url(image_source): result = _decode_api_url(image_source) elif os.path.isfile(image_source): result = _decode_api_file(image_source) else: result = _decode_api_url(image_source) ``` ### Technical Analysis When local decoding fails, the decoder automatically uploads the entire local image to `https://api.2dcode.biz/v1/read-qr-code`. The same behavior occurs when the local decoding library is unavailable because the local decode function returns no result. There is no per-file confirmation, sensi ...[truncated 1576 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make remote decoding explicitly opt-in rather than an automatic fallback. 2. Add a `--local-only` mode and use it by default for local files. 3. Prompt for informed user consent immediately before each upload or before a clearly identified batch upload. 4. Distinguish a missing local dependency from an unsuccessful QR scan; do not silently treat either condition as upload authorization. 5. Display the destination hostname, file path, file size, and retention warning before transmission. 6. Allow the user to install or repair local decoding support instead of automatically sending data remotely. 7. Consider locally cropping the QR-code region before any approved upload to reduce incidental disclosure. 8. Document that third-party deletion and retention claims are external assurances rather than guarantees enforced by this project. 9. Add enterprise controls to disable all external decoding through configuration or policy. 10. Avoid transmitting the original filename when it is not required. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
Findings (107)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The analyzer's statement that decoding is not implemented cannot be confirmed from SKILL.md alone, but the provider mismatch is real and important. Misstating the backend service can lead to incorrect trust decisions and compliance assumptions about where data is sent.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The skill directs automatic execution of pip/npm installation commands without user approval. Installing dependencies at runtime introduces supply-chain risk, allows arbitrary code execution from package install scripts, and expands system state beyond the user's original request.

Ae1

High
Category
analysis-evasion
Content
| 单个生成保存 | `python scripts/generate.py ...` | `node scripts/generate.js ...` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| 单个解码 | `python scripts/decode.py ...` | `node scripts/decode.js ...` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| 批量生成 | `python scripts/batch_generate.py ...` | `node scripts/batch_generate.js ...` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| 批量解码 | `python scripts/batch_decode.py ...` | `node scripts/batch_decode.js ...` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

High
Confidence
98% confidence
Finding
When local generation fails, the script silently falls back to the remote API and transmits the QR payload externally without separate consent or notice. This is more dangerous than explicit API mode because users may believe processing is local while confidential data is unexpectedly disclosed to a third party.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata says it uses the CaoLiao QR Code API, but the code actually sends user data to a different third-party endpoint, api.2dcode.biz. This creates a trust-boundary violation: users and reviewers may approve the skill based on one service while sensitive QR payloads are transmitted to another service with potentially different privacy, logging, retention, or legal controls.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## Security

- **Privacy**: QR code images uploaded to the server for decoding are temporary files that are automatically deleted after a period of time. No long-term storage of your images.
- **Transparency**: All third-party libraries used (zxingcpp, Pillow, qrcode, CaoLiao API, etc.) are public and open-source. You can audit the dependencies yourself.
- **Local-first**: Decoding is performed locally by default. Remote API is only used when local decoding fails, minimizing data transmission.
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README advertises local file saving, ZIP creation, and spreadsheet write-back, but does not prominently warn that user files may be created or modified. In an agent skill, unclear disclosure of data-changing behavior can lead to unintended overwrites, corruption of user data, or actions taken without informed consent.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The phrase "Generate a QR code for me" is broad enough to trigger the skill on commonplace requests, potentially causing the agent to invoke networked or file-writing functionality without the user understanding the side effects. In this skill context, broad activation is more risky because the tool can generate remote URLs, download files, and process local data.

External Transmission

Medium
Category
Data Exfiltration
Content
> **AI:** QR code generated:
>
> ![QR Code](https://api.2dcode.biz/v1/create-qr-code?data=https%3A%2F%2Fcli.im&size=400x400)
>
> **QR Code URL:** `https://api.2dcode.biz/v1/create-qr-code?data=https%3A%2F%2Fcli.im&size=400x400`
Confidence
93% confidence
Finding
The example shows QR generation via a third-party API URL containing user-supplied data in the query string. If users encode sensitive text, URLs, tokens, or internal links, that data is transmitted to an external service and may be logged by intermediaries, browsers, or the provider.

External Transmission

Medium
Category
Data Exfiltration
Content
>
> ![QR Code](https://api.2dcode.biz/v1/create-qr-code?data=https%3A%2F%2Fcli.im&size=400x400)
>
> **QR Code URL:** `https://api.2dcode.biz/v1/create-qr-code?data=https%3A%2F%2Fcli.im&size=400x400`

---
Confidence
93% confidence
Finding
This line repeats the externally hosted QR code URL, again embedding the encoded content directly in a third-party request. In this skill context, the danger is heightened because users may assume QR generation is local while the example normalizes remote disclosure of the payload.

External Transmission

Medium
Category
Data Exfiltration
Content
>
> QR code generated and saved locally:
>
> **QR Code URL:** `https://api.2dcode.biz/v1/create-qr-code?data=Hello%20World&size=400x400&format=svg`
> **Local file:** `C:\Users\xxx\Desktop\qrcode.svg`

---
Confidence
93% confidence
Finding
The saved-local example still relies on a remote API URL that includes the encoded text before downloading the image locally. This can mislead users into thinking the operation is private because the final artifact is local, even though the payload was first transmitted externally.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Static analysis

No suspicious patterns detected.