Back to skill

Security audit

麦当劳点餐skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its McDonald's ordering purpose, but it handles real orders and payment QR codes with unsafe command and URL handling that users should review before installing.

Install only if you trust the McDonald's MCP endpoint and are comfortable giving the skill access to your McDonald's token, saved addresses, local MCP configuration, and real order/payment workflow. Before use, prefer revising the command examples to pass data without a shell, validate payment URLs against the expected McDonald's host, and require explicit confirmation before config changes, address creation, and final order placement.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:87
Finding
Shell Command Injection Through Untrusted JSON and Text Arguments<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 87-103 and 139-156 **Vulnerability Type**: Shell command injection caused by embedding untrusted data in single-quoted command arguments **Risk Level**: High ### Vulnerable Code ```bash python3 {SKILL_DIR}/scripts/order_helper.py load-default-meal --time-slot <slot> --menu '<data_json>' ``` ```bash python3 {SKILL_DIR}/scripts/order_helper.py calorie-pairing \ --menu '<data_json>' \ --nutrition-text '<raw_text>' \ --time-slot <slot> ``` ```bash python3 {SKILL_DIR}/scripts/order_helper.py format-order-summary \ --items '<cart_items_json>' \ --price '<calculate_price_result_json>' \ --address '<selected_address_json>' ``` ```bash python3 {SKILL_DIR}/scripts/order_helper.py gen-pay-qr --pay-url '<payUrl>' ``` ### Technical Analysis The skill instructs the agent to interpolate MCP responses, menu data, nutrition text, addresses, cart contents, pricing responses, and payment URLs directly into shell commands. Wrapping a value in single quotes does not make interpolation safe if the value itself can contain a single quote. For example, a menu item, address field, nutrition entry, or remote response containing the following value can terminate the quoted argument and append a new shell command: ```text '; id > /tmp/injected # ``` The resulting command would be interpreted approximately as: ```bash python3 order_helper.py ... --menu ''; id > /tmp/injected #' ``` The Python script safely parses its arguments with `argparse`, but this occurs only after the invoking shell has parsed and executed the injected command. Therefore, the JSON parser does not mitigate this issue. The affected values originate from remote MCP responses or user-controlled order and address data. A compromised service response, malicious upstream record, or crafted user field could consequently cross the data-to-command boundary. ### Attack Path 1. An attacker causes an MCP response or user-controlled f ...[truncated 1149 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not construct shell command strings containing MCP or user-controlled data. - Invoke the helper with an argument-array API that does not use a shell, such as Python's `subprocess.run([...], shell=False)`. - Prefer passing structured data over standard input: ```python subprocess.run( ["python3", helper_path, "load-default-meal", "--time-slot", slot, "--menu-file", menu_path], check=True, shell=False, ) ``` - Alternatively, write JSON to a securely created file and pass only its path. - If shell execution is unavoidable, apply platform-appropriate argument escaping to every dynamic value. This is less robust than avoiding the shell. - Validate the schema and maximum size of all MCP responses before processing them. - Avoid including secrets in the environment of processes that handle untrusted data where practical. - Add regression tests using quotes, command substitutions, newlines, and shell metacharacters in every dynamic field. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/order_helper.py:352
Finding
Unvalidated Payment URL Can Produce a Spoofed or Malicious QR Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/order_helper.py`, lines 352-375 **Vulnerability Type**: Untrusted URL encoded into a payment QR code without origin or scheme validation **Risk Level**: High ### Vulnerable Code ```python def cmd_gen_pay_qr(args): raw_url = args.pay_url.strip() pay_url = re.sub(r'/scanToPay\?', '/jumpToApp/?', raw_url) m = re.search(r'orderId=(\w+)', pay_url) order_id = m.group(1) if m else "unknown" try: import qrcode except ImportError: print(json.dumps({ "ok": False, "error": "Missing dependency: install qrcode with Pillow support", }, ensure_ascii=False)) sys.exit(1) qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=10, border=4, ) qr.add_data(pay_url) qr.make(fit=True) ``` ### Technical Analysis The command accepts an arbitrary string through `--pay-url`, performs only a path-text substitution, and then encodes the resulting string into a QR code. It does not parse the URL or verify: - That the scheme is HTTPS. - That the hostname is exactly an approved McDonald's domain. - That the port, user information, or host representation is safe. - That the original path is the expected payment path. - That `orderId` is present exactly once and has an approved format. - That the URL was obtained from the authenticated order-creation response. The substitution does not restrict the origin. For example, an attacker-controlled URL such as the following remains attacker-controlled after processing: ```text https://attacker.example/scanToPay?orderId=ABC123 ``` It becomes: ```text https://attacker.example/jumpToApp/?orderId=ABC123 ``` The generated QR code is then presented in the context of a successful McDonald's order, giving the malicious destination a trusted appearance. ### Attack Path 1. An attacker influences the `payUrl` value returned ...[truncated 938 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Parse the URL with `urllib.parse.urlsplit` instead of using regular-expression substitution. - Require `scheme == "https"`. - Require an exact allowlisted hostname, such as `m.mcd.cn`; do not use suffix or substring matching. - Reject unexpected ports, embedded credentials, fragments, malformed hostnames, and internationalized lookalike domains. - Require the exact expected input path and reconstruct the destination URL from trusted constants. - Validate `orderId` against the service's documented format and length. - Construct the output URL independently rather than retaining arbitrary components: ```python from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit parsed = urlsplit(raw_url) if parsed.scheme != "https" or parsed.hostname != "m.mcd.cn": raise ValueError("Unapproved payment URL origin") if parsed.port not in (None, 443) or parsed.path != "/mcp/scanToPay": raise ValueError("Unexpected payment URL") values = parse_qs(parsed.query, strict_parsing=True) order_ids = values.get("orderId", []) if len(order_ids) != 1 or not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", order_ids[0]): raise ValueError("Invalid order ID") pay_url = urlunsplit(( "https", "m.mcd.cn", "/mcp/jumpToApp/", urlencode({"orderId": order_ids[0]}), "", )) ``` - Bind the QR-generation request to the order ID returned by the authenticated order-creation call. - Display the validated hostname and order ID beside the QR code so the user can verify the destination. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/order_helper.py:377
Finding
Predictable QR Output Path Allows Temporary-File Symlink Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/order_helper.py`, lines 377-385 **Vulnerability Type**: Unsafe predictable temporary-file creation **Risk Level**: Medium ### Vulnerable Code ```python try: img = qr.make_image(fill_color="black", back_color="white") qr_path = f"/tmp/mcd_pay_{order_id}.png" img.save(qr_path) print(json.dumps({ "ok": True, "pay_url": pay_url, "qr_path": qr_path, "mode": "image", }, ensure_ascii=False)) ``` ### Technical Analysis The QR image is written to a predictable path in the shared `/tmp` directory. The filename is derived from an order ID disclosed in or inferable from the payment URL. The code does not: - Create the file atomically with exclusive-create semantics. - Reject symbolic links. - Place the output in a private directory. - Apply an explicitly restrictive permission mode. - Remove the payment artifact after use. On systems where another local account can create entries in `/tmp`, an attacker can pre-create the expected path as a symbolic link to a file writable by the agent account. When `img.save()` opens the path, it can follow that link and overwrite the target with PNG data. The deterministic `"unknown"` fallback makes the path `/tmp/mcd_pay_unknown.png`, which is especially easy to pre-position when the URL lacks a matching order ID. ### Attack Path 1. A local attacker predicts the order ID or triggers QR generation with a URL that produces the `"unknown"` fallback. 2. The attacker creates `/tmp/mcd_pay_<order_id>.png` as a symbolic link to a target file writable by the agent. 3. The user or agent invokes `gen-pay-qr`. 4. `img.save()` follows the pre-existing link and overwrites the target with QR image content. 5. Depending on the selected target, the overwrite can corrupt configuration, application data, or executable scripts owned by the invoking account. ### Impact Assessment The impact is limited to files writable by the process ...[truncated 434 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a private temporary directory with mode `0700` using `tempfile.TemporaryDirectory`. - Generate an unpredictable filename inside that directory. - Create the output atomically and ensure that existing paths and symbolic links are not followed. - Set file permissions to `0600`. - Delete the QR image immediately after it has been presented or after a short expiration period. - Do not derive filesystem paths directly from externally supplied order identifiers. - If a persistent output is required, use a dedicated application data directory with controlled ownership and permissions. - Add tests that pre-create files and symbolic links at candidate output paths and verify that they are never followed or overwritten. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/order_helper.py:364
Finding
Unpinned Runtime Installation Guidance Creates a Dependency Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/order_helper.py`, lines 364-371 **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```python try: import qrcode except ImportError: print(json.dumps({ "ok": False, "error": "Missing dependency; run: pip3 install 'qrcode[pil]'", }, ensure_ascii=False)) sys.exit(1) ``` ### Technical Analysis The project does not include a pinned dependency manifest or hashes for the QR library. When the import fails, it directs the operator to install the latest package and optional dependencies resolved by the active Python package index. This makes the effective code installed and executed by the skill change over time without corresponding review of the skill package. The installed artifacts depend on package-index state, configured mirrors, resolver behavior, and transitive dependencies. The code does not itself execute `pip`, so exploitation requires a user or agent to follow the displayed installation instruction. Nevertheless, the guidance is part of the normal payment workflow and may encourage an automated agent to install mutable third-party code at runtime. ### Attack Path 1. QR generation reaches a system where `qrcode` is not installed. 2. The helper instructs the operator or agent to run the unpinned `pip3 install` command. 3. Package resolution occurs against the configured index or mirror without project-provided hashes. 4. A compromised upstream release, compromised mirror, or maliciously altered package source supplies hostile installation or runtime code. 5. The package executes during installation or when imported by a later QR-generation request with the privileges of the invoking account. ### Impact Assessment A malicious dependency can execute arbitrary Python code as the account performing installation or running the skill. This may expose files, environment variables such as `MCD_MCP_TOKEN`, n ...[truncated 209 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Declare dependencies in a reviewed lock file or pinned requirements file. - Pin exact versions of both direct and transitive dependencies. - Require hashes during installation, for example with `pip install --require-hashes -r requirements.txt`. - Install dependencies during a controlled build or deployment stage rather than from within the runtime workflow. - Use a trusted package index and verify package provenance where supported. - Scan locked dependencies for known vulnerabilities and update them through a reviewed process. - Run the skill in a dedicated virtual environment with least privilege. - Avoid suggesting unrestricted package installation to an autonomous agent. ]]>
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 Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill reads environment variables and local files, but it does not declare any explicit tool scope or allowed-tools boundary. That creates a permission transparency problem: users and orchestrators may invoke a skill that can access sensitive configuration and local state without clear upfront restriction, increasing the chance of unintended data exposure or unsafe file access patterns.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases are very broad and cover ordinary discussion like wanting McDonald's, asking what is tasty, promotions, calories, or order status. This makes accidental invocation more likely, which is especially risky here because the skill can read config, modify local files, create delivery addresses, and place real orders through an external service.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs writing to config.json, registering an MCP server in the user's home directory, creating addresses, and placing orders, but it does not require a clear upfront warning that local files and external account/order state will be modified. In combination with broad triggers, this can lead to unexpected persistence and real-world transactions without sufficiently informed user consent.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This JSON configuration uses Chinese-only labels and item names throughout the user-facing meal definitions and calorie mappings. Because the file provides no indication that the skill is region-specific or that users can opt into a Chinese locale, it appears to enforce a specific language by default, which matches the language/locale policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstring and command help are entirely in Chinese, presenting the skill as Chinese-only without any opt-in, locale selection, or explanation that it is intended exclusively for a China-specific audience. This is a natural-language locale policy issue because the skill effectively imposes a language constraint on users through its user-facing interface.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The manifest describes menu browsing, price calculation, order creation/tracking, nutrition lookup, and promotions queries through the McDonald's MCP service. This helper additionally creates local PNG payment artifacts on disk, which is a separate file-output/payment-facilitation capability not mentioned in the stated purpose.

Static analysis

No suspicious patterns detected.