Back to skill

Security audit

Zeelin Patent Retriever

Security checks for vulnerabilities and agentic risk

Overview

This patent-search skill is mostly coherent, but it needs review because a mutable query plan can redirect BigQuery access beyond the stated Google Patents dataset and queries have no default billing cap.

Install only in an isolated environment with a narrowly scoped Google Cloud identity. Before running, verify query_plan.json keeps table set to patents-public-data.patents.publications, set a nonzero --max-bytes-billed limit, and pin or lock dependencies if you need reproducible installs.

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

Warning
Location
scripts/patent_search_plan.py:198
Finding
Mutable Query Plan Allows Queries Against Unapproved BigQuery Tables<![CDATA[ ## Vulnerability Details **File Location**: `scripts/patent_search_plan.py:198-210`, `scripts/patent_search_plan.py:369-374`; `schemas/query_plan.schema.json:9` **Vulnerability Type**: Unrestricted SQL identifier controlled through a query-plan file **Risk Level**: Medium ### Vulnerable Code ```python where_sql = "\n AND ".join(where_parts) match_score_sql = " + ".join(score_terms) if score_terms else "0" sql = f""" SELECT publication_number, country_code, (SELECT text FROM UNNEST(title_localized) WHERE text IS NOT NULL LIMIT 1) AS title, (SELECT text FROM UNNEST(abstract_localized) WHERE text IS NOT NULL LIMIT 1) AS abstract, SUBSTR((SELECT text FROM UNNEST(claims_localized) WHERE text IS NOT NULL LIMIT 1), 1, 1200) AS claims, ARRAY(SELECT name FROM UNNEST(inventor_harmonized)) AS inventors, ARRAY(SELECT name FROM UNNEST(assignee_harmonized)) AS assignees, ARRAY(SELECT code FROM UNNEST(ipc)) AS ipc_codes, ARRAY(SELECT code FROM UNNEST(cpc)) AS cpc_codes, filing_date, publication_date, ({match_score_sql}) AS match_score FROM `{table}` WHERE {where_sql} ORDER BY match_score DESC, publication_date DESC LIMIT @limit """ job_config = bigquery.QueryJobConfig(query_parameters=params) ``` The value is read directly from the mutable plan: ```python table = str(plan.get("table") or "").strip() if not table: raise SystemExit("query_plan.table 为空") policy = _effective_policy(plan, args) client = bigquery.Client() ``` It is subsequently passed into the query builder: ```python sql, job_config = _build_round_query(table=table, round_cfg=round_cfg) ``` The corresponding schema imposes no allowlist or constant restriction: ```json "table": {"type": "string"} ``` ### Technical Analysis Search terms, dates, countries, and other filter values are correctly passed through BigQuery query parameters. However, the table identifier cannot be represented by a normal query parameter and is instead interpolated directly into the SQL stat ...[truncated 2095 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the table name from the externally mutable plan and use a code-level constant: ```python PATENTS_TABLE = "patents-public-data.patents.publications" ``` 2. If the field must remain for compatibility, require exact equality before creating the client or running any query: ```python ALLOWED_TABLE = "patents-public-data.patents.publications" table = str(plan.get("table") or "").strip() if table != ALLOWED_TABLE: raise SystemExit(f"Unsupported BigQuery table: {table!r}") ``` 3. Change the schema to a constant or single-value enumeration: ```json "table": { "type": "string", "enum": ["patents-public-data.patents.publications"] } ``` 4. Validate the query plan against `schemas/query_plan.schema.json` inside the executor before using any plan values. Do not rely on a separately documented validation command. 5. Configure the Google identity with least-privilege BigQuery permissions. Avoid granting broad access to unrelated datasets. 6. Add tests proving that alternate table names, malformed identifiers, missing fields, and additional unexpected plan properties are rejected before a network request occurs. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/patent_search_plan.py:406
Finding
BigQuery Queries Have No Default Billing Limit<![CDATA[ ## Vulnerability Details **File Location**: `scripts/patent_search_plan.py:406`, `scripts/patent_search_plan.py:435-437`, `scripts/patent_search_plan.py:497-499` **Vulnerability Type**: Unbounded cloud-query resource consumption **Risk Level**: Low ### Vulnerable Code The command-line default explicitly represents unlimited billed bytes: ```python ap.add_argument("--max-bytes-billed", type=int, default=0, help="Per-query max bytes billed (0=unlimited)") ``` The billing limit is applied only when the caller supplies a positive value: ```python sql, job_config = _build_round_query(table=table, round_cfg=round_cfg) if args.max_bytes_billed > 0: job_config.maximum_bytes_billed = int(args.max_bytes_billed) result = client.query(sql, job_config=job_config).result() ``` The same behavior applies to automatic expansion queries: ```python sql, job_config = _build_round_query(table=table, round_cfg=extra_round) if args.max_bytes_billed > 0: job_config.maximum_bytes_billed = int(args.max_bytes_billed) result = client.query(sql, job_config=job_config).result() ``` ### Technical Analysis BigQuery can scan substantial volumes even when the final result uses a small `LIMIT`; a result limit does not necessarily limit bytes processed. The script leaves `maximum_bytes_billed` unset by default and can execute multiple planned rounds followed by automatic expansion rounds. Broad filters, numerous search terms, or a modified plan can therefore initiate several queries without an application-level cost ceiling. The user must know about and explicitly provide `--max-bytes-billed` to obtain protection. The issue is a cloud-resource and cost-control weakness rather than a credential leak or privilege-escalation vulnerability. ### Attack Path 1. Supply a plan containing broad filters, large round limits, or multiple query rounds. 2. Run the documented command without `--max-bytes-billed`, as shown in the Skill’s standard workflow. 3. The script submits eac ...[truncated 823 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set a conservative, nonzero default for `--max-bytes-billed` based on expected patent-query size: ```python ap.add_argument( "--max-bytes-billed", type=int, default=1_000_000_000, help="Maximum bytes billed per query", ) ``` 2. Reject zero and negative values in production rather than interpreting them as unlimited. 3. Perform a dry run before execution, inspect the estimated bytes processed, and reject or request confirmation when the estimate exceeds the configured budget. 4. Cap all relevant workload dimensions: - Maximum query rounds. - Maximum automatic expansion rounds. - Per-round result limit. - Number and length of filter terms. 5. Require explicit user confirmation before intentionally allowing an unlimited or unusually expensive query. 6. Configure project-level BigQuery quotas, billing budgets, and billing alerts as defense in depth. 7. Include estimated and actual bytes processed in the execution report so cost-relevant behavior is auditable. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Dependency Versions Are Not Reproducibly Pinned<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-3` **Vulnerability Type**: Unbounded third-party dependency version selection **Risk Level**: Low ### Vulnerable Code ```text python-dotenv>=1.0.0 google-cloud-bigquery>=3.20.0 jsonschema>=4.20.0 ``` The documented installation command resolves these ranges at installation time: ```bash python3 -m pip install -r requirements.txt ``` ### Technical Analysis The listed packages are recognizable dependencies appropriate to the Skill’s functionality. No typosquatted package, untrusted package index, remote archive, or known malicious dependency was identified during this static audit. However, each requirement specifies only a minimum version. A future installation may therefore select any newer compatible release available from the configured package index, including releases that were never tested or reviewed with this project. Transitive dependencies are also not locked. This makes installations non-reproducible and expands supply-chain exposure. The finding does not establish that the current packages are malicious; it identifies the absence of controls that bind installation to reviewed artifacts. ### Attack Path 1. A user follows the documented `pip install -r requirements.txt` command. 2. Pip resolves the broad version constraints against the package index configured in the user’s environment. 3. Pip selects current package releases and transitive dependencies rather than a fixed reviewed set. 4. Installation-time or runtime code from those resolved artifacts executes in the user environment. 5. A compromised, malicious, or unexpectedly incompatible future release could affect the Skill with the process user’s privileges. Successful malicious exploitation requires compromise or substitution of an allowed dependency artifact, a malicious configured index, or another package-resolution failure. No such compromise was observed in the audited repository. ### Impact Assessment C ...[truncated 630 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate and commit a lock file containing exact direct and transitive dependency versions. 2. Install with hash verification, for example by maintaining a requirements file containing exact versions and `--hash` entries and using: ```bash python3 -m pip install --require-hashes -r requirements.lock ``` 3. Keep human-maintained minimum constraints separate from deployment locks if library compatibility ranges are still needed. 4. Review dependency updates through a controlled process that includes: - Changelog and provenance review. - Vulnerability scanning. - Automated tests. - Manual approval for major or security-sensitive updates. 5. Use only explicitly approved package indexes and disable unexpected extra indexes in production installation environments. 6. Run the Skill in an isolated virtual environment or container under a nonprivileged account. 7. Add automated dependency auditing to CI and regenerate hashes only after the selected artifacts have been reviewed. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code does use Google BigQuery against the Google Patents public dataset, so that portion aligns with the description. However, the declared purpose significantly overstates the functionality. The implementation is a straightforward patent searcher: it splits a keyword string, computes a match score over title/abstract, optionally filters by country, sorts results, and exports them to JSON. It does not accept general natural-language research intent and transform it into an auditable multi-round retrieval plan; it does not implement explicit filter handling for date, assignee, inventor, IPC, or CPC; and it does not validate or emit structured planning artifacts beyond a simple JSON dump of results. Thus the description does not accurately represent the actual behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description presents a patent research and evidence retrieval skill with domain-specific planning and Google Patents BigQuery functionality. The supplied code does none of that. It is a generic schema validation script that operates on local files, with no network/database access, no patent-domain logic, no search triggers, and no conversion of user intent into retrieval plans. While JSON validation could be a supporting component in a larger system, this code chunk by itself has a materially different primary purpose from the declared skill behavior.

Credential Access

High
Category
Privilege Escalation
Content
"""配置加载:支持从 .env 读取 GOOGLE_APPLICATION_CREDENTIALS 等。"""
import os
from pathlib import Path
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""配置加载:支持从 .env 读取 GOOGLE_APPLICATION_CREDENTIALS 等。"""
import os
from pathlib import Path
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import os
from pathlib import Path

ENV_PATH = Path(__file__).resolve().parent.parent / ".env"
if ENV_PATH.exists():
    try:
        from dotenv import load_dotenv  # type: ignore
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises executable behavior that uses environment variables and reads/writes local files, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization and review gap: an agent may grant broader capabilities than users expect, increasing the chance of unintended data access, credential exposure, or filesystem modification during execution.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This markdown file presents all methodology instructions exclusively in Chinese, and there is no indication that users may choose another language or that the skill is intentionally limited to a Chinese-language audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code generates multiple natural-language output fields entirely in Chinese, including titles, claim angles, synergy descriptions, and problem/solution text. Because the file does not offer user opt-in for language selection or document that the skill is intentionally Chinese-only, it appears to force a specific language/locale in generated outputs.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file’s natural-language strings and comments are written in Chinese, including the module docstring and runtime error message. This indicates a locale/language constraint without any indication that the user can choose another language or that the restriction is explicitly justified.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The module docstring says the script will 'Enforce quality quota (min results + recency ratio + country diversity)', but the implementation explicitly ignores the country-diversity threshold when determining whether quality is met. In `_quality_met`, `min_country_count` is discarded and only result count and recency ratio are enforced, so the documented execution intent contradicts actual behavior.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code includes a user-facing error message in Chinese, and additional user-visible messages later in the script also use Chinese only. For a general-purpose utility, forcing one language without user opt-in is a natural-language policy violation under the locale/language rule.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The script sends query content derived from the input plan to Google BigQuery, which is a network operation that may transmit user-supplied research terms and related metadata. While the module docstring states that it executes BigQuery retrieval, there is no explicit user-facing warning, prompt, or privacy disclosure near execution about data being sent to an external service.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This user-facing failure message is only in Chinese and does not provide any language selection or indicate that the tool is intentionally Chinese-only. That forces a specific language on users and violates the language/locale policy for general-use skills.

Unpinned Dependencies

Low
Category
Supply Chain
Content
python-dotenv>=1.0.0
google-cloud-bigquery>=3.20.0
jsonschema>=4.20.0
Confidence
98% confidence
Finding
The dependency is specified with only a lower bound, which makes builds non-reproducible and allows future installs to pull in unexpected or vulnerable releases. In a production-grade retrieval skill that may run in automated environments, this increases supply-chain risk and complicates incident response and auditing.

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
The manifest references python-dotenv without pinning a version, while advisories exist for some releases, so there is no assurance that deployed environments avoid affected versions. Even if exploitation depends on how the library is used elsewhere, the dependency specification leaves open the possibility of installing a vulnerable build.

Unpinned Dependencies

Low
Category
Supply Chain
Content
python-dotenv>=1.0.0
google-cloud-bigquery>=3.20.0
jsonschema>=4.20.0
Confidence
98% confidence
Finding
Using an unpinned version of google-cloud-bigquery permits installation of any newer release, which can introduce vulnerable transitive dependencies, breaking changes, or malicious compromise of the package supply chain. Because this skill is intended for production BigQuery access, dependency drift is especially undesirable for security and reliability.

Unpinned Dependencies

Low
Category
Supply Chain
Content
python-dotenv>=1.0.0
google-cloud-bigquery>=3.20.0
jsonschema>=4.20.0
Confidence
97% confidence
Finding
An unpinned jsonschema dependency allows uncontrolled upgrades that may introduce security fixes too late, or new regressions and incompatible behavior without notice. For a skill that validates JSON artifacts for downstream analysis, predictable validator behavior is important for both security and correctness.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The raised ValueError uses a Chinese-only message, which imposes a specific language on users and downstream tooling. Under the language/locale policy, user-facing text should not force a language unless the skill explicitly supports locale selection or documents a justified regional constraint.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The code checks for GOOGLE_APPLICATION_CREDENTIALS and GOOGLE_CLOUD_PROJECT, which involves accessing sensitive configuration related to cloud credentials. While comments describe the purpose for developers, there is no user-facing warning, confirmation, or explicit disclosure that the skill reads credential-related environment variables.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This code file contains user-facing natural-language documentation that forces a specific language/locale. The policy allows locale constraints only when users are given a choice or when the restriction is clearly justified, which is not present here.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The dependency error messages are user-facing strings and are only available in Chinese. Because the file does not offer an opt-in language selection or document a justified locale restriction, this is a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The CLI's description, argument help text, and save confirmation message are all fixed to Chinese. This enforces a specific language in user interaction without presenting a choice or justification.

Description-Behavior Mismatch

Low
Confidence
79% confidence
Finding
The manifest describes a production-grade patent retrieval skill that converts research intent into retrieval plans and outputs validated JSON artifacts for downstream analysis. In this file, the interactive CLI additionally performs local filesystem persistence by choosing or generating an output filename and writing the results to disk, which is a concrete side effect not mentioned in the manifest description of BigQuery retrieval.

Missing User Warnings

Low
Confidence
73% confidence
Finding
The script creates directories and writes retrieved patent data and execution details to JSON files on disk. Although output paths are CLI arguments, the code does not provide a visible confirmation or runtime notice before persisting potentially sensitive research artifacts and metadata.

Static analysis

No suspicious patterns detected.