Back to skill

Security audit

Dataify Play.google Builder

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly performs the advertised Dataify Google Play task, but it ships under-disclosed broader scraping workflows and weak scoping around inputs and imports.

Review before installing. Use only with a Dataify token you are comfortable exposing to this workflow, prefer session-scoped token setup, avoid broad or sensitive URLs, and treat the bundled business workflow and sibling-module import behavior as overbroad for a narrowly named Google Play review skill.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wait_for_task.py:34
Finding
API Token Exposed in HTTP Query Strings During Task Polling<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wait_for_task.py`, lines 34–39, 106–112, and 131–136 **Vulnerability Type**: Sensitive credential exposure through URL query parameters **Risk Level**: High ### Complete Code Snippet ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request(url, method="GET") try: with urllib.request.urlopen(request, timeout=timeout) as response: content = response.read() ``` The function is called with the API token inside `params`: ```python payload = request_json( STATUS_ENDPOINT, {"api_key": api_key, "task_id": task_id}, api_key, request_timeout, ) ``` The same behavior is used when downloading results: ```python return request_json( DOWNLOAD_ENDPOINT, {"api_key": api_key, "task_id": task_id, "type": "json"}, api_key, request_timeout, ) ``` ### Technical Analysis The Dataify API token is incorporated directly into the request URL as an `api_key` query parameter. Although the request uses HTTPS, HTTPS does not prevent the URL from being recorded at either endpoint of the connection. Query strings may be retained by: - Dataify web-server access logs - Reverse proxies and load balancers - Local or enterprise HTTP debugging tools - Monitoring and observability platforms - Exception and request telemetry - Browser or networking history if the URL is copied or reused - Security appliances that record complete request targets The response-body redaction performed later by the function does not protect the request URL: ```python if api_key: text = text.replace(api_key, "<redacted>") ``` This only modifies response text after it has been received. It does not remove the token from proxy, server, or client-side request logs. ### Attack Path 1. A user configures a valid `DATAIFY_API_TOKEN`. 2. The Skill submits a task and begins polling its status. 3. ...[truncated 1017 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Move the API token into an HTTP authorization header: ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request( url, headers={"Authorization": "Bearer {}".format(api_key)}, method="GET", ) ``` 2. Keep only non-secret values such as `task_id` and `type` in the query string. 3. Ensure exception messages never include the full request URL or request headers. 4. Configure server, proxy, and application logs to redact authorization headers. 5. If the Dataify endpoint currently requires `api_key` in the query string: - Request or implement header-based authentication. - Disable query-string logging on all participating infrastructure. - Restrict log access and retention. - Rotate affected tokens after deployment of the fix. 6. Add automated tests asserting that generated request URLs never contain the API token. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/build-dataify-request.py:5
Finding
External Sibling Directory Can Override the Audited Builder Module<![CDATA[ ## Vulnerability Details **File Location**: `scripts/build-dataify-request.py`, lines 5–9 **Vulnerability Type**: Python module search-path hijacking **Risk Level**: High ### Complete Code Snippet ```python TASK_RUNTIME_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "dataify-task-operations", "scripts")) if TASK_RUNTIME_DIR not in sys.path: sys.path.insert(0, TASK_RUNTIME_DIR) from catalog_builder import build_curl, run_catalog_builder ``` ### Technical Analysis The entry point computes a path outside the current Skill package and inserts it at index zero of `sys.path`. Python searches index zero before the bundled script directory when resolving imports. Consequently, if the external directory contains a file named `catalog_builder.py`, the following import can load that external file instead of the audited local file: ```python from catalog_builder import build_curl, run_catalog_builder ``` Imported Python modules execute their top-level code immediately. An attacker-controlled replacement therefore does not need to provide working implementations before executing malicious behavior. This trust boundary is especially dangerous because the documented execution flow expects `DATAIFY_API_TOKEN` to be available in the process environment. A substituted module can read and transmit that token before the legitimate Builder logic runs. ### Attack Path 1. An attacker gains write access to the expected sibling path: `../../dataify-task-operations/scripts`. 2. The attacker creates a malicious `catalog_builder.py`. 3. The user runs the documented command: ```bash python3 scripts/build-dataify-request.py ... ``` 4. The entry point prepends the attacker-controlled directory to `sys.path`. 5. Python imports the attacker's module instead of the bundled `scripts/catalog_builder.py`. 6. Top-level malicious code executes with the user's local privileges. 7. The malicious module can read `DATAIFY_API_TOKEN`, alter task par ...[truncated 846 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not prepend an external directory to `sys.path`. 2. Package the Skill scripts as a Python package and use an explicit relative import: ```python from .catalog_builder import build_curl, run_catalog_builder ``` 3. If direct script execution must remain supported, load the bundled module from an exact, resolved path rather than by ambiguous module name. 4. If shared external runtime code is genuinely required: - Install it as a version-pinned package. - Verify its source and cryptographic integrity. - Require a trusted, administrator-controlled installation directory. - Do not silently fall back between external and bundled implementations. 5. Add a runtime assertion that the imported module's resolved `__file__` is inside the expected Skill directory. 6. Add tests that create a same-named module in the sibling directory and confirm that it cannot override the bundled implementation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:43
Finding
Documentation Recommends Plaintext Persistent Storage of API Tokens<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 43–65; `SKILL.zh-CN.md`, lines 28–55 **Vulnerability Type**: Insecure persistent credential storage **Risk Level**: Medium ### Complete Code Snippet ```powershell [Environment]::SetEnvironmentVariable("DATAIFY_API_TOKEN", "your_token_here", "User") ``` ```bash echo 'export DATAIFY_API_TOKEN="your_token_here"' >> ~/.bashrc source ~/.bashrc ``` ```bash echo 'export DATAIFY_API_TOKEN="your_token_here"' >> ~/.zshrc source ~/.zshrc ``` ### Technical Analysis The documentation explicitly prefers permanent environment-variable setup and instructs users to write the API token into shell startup files or the Windows user environment. On macOS and Linux, the token is stored as plaintext in `.bashrc` or `.zshrc`. These files may be exposed through: - Dotfile synchronization systems - Home-directory backups - Diagnostic or support bundles - Accidental source-control commits - Other processes or users with permission to read the file - Shell configuration sharing On Windows, a user-level environment variable is persistent but is not equivalent to encrypted secret storage. Processes running under the same user context can commonly read it. The runtime code appropriately avoids printing the configured token, but that does not eliminate the risks introduced by persistent plaintext storage. ### Attack Path 1. A user follows the documented persistent setup command. 2. The token is stored in a shell initialization file or user environment. 3. The file or environment is included in a backup, synchronized repository, support archive, or accessible process context. 4. An attacker or unintended recipient obtains the stored token. 5. The token is reused to access Dataify APIs and consume account resources. ### Impact Assessment Exposure grants the permissions assigned to the Dataify token, potentially including: - Unauthorized scraper submissions - Consumption of paid credits - Retrieval of task data ...[truncated 231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer a session-scoped environment variable as the default documented setup: ```bash export DATAIFY_API_TOKEN='...' ``` 2. Recommend a platform secret manager for persistent storage, such as: - macOS Keychain - Windows Credential Manager - Secret Service-compatible keyrings on Linux - An approved enterprise secrets manager 3. If plaintext environment storage remains supported: - Clearly disclose that it is plaintext. - Recommend restrictive file permissions. - Warn against committing or synchronizing shell configuration files. - Recommend short-lived or narrowly scoped tokens where available. 4. Avoid placing literal tokens directly in reusable shell-history commands. Prefer an interactive, non-echoing prompt or secret-manager retrieval. 5. Document immediate token rotation procedures for suspected exposure. 6. Keep verification commands limited to checking whether the variable exists; never print its value. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/business_workflow.py:58
Finding
Bundled Business Workflow Exceeds the Declared Google Play Collection Scope<![CDATA[ ## Vulnerability Details **File Location**: `scripts/business_workflow.py`, lines 58–183 and 596–648 **Vulnerability Type**: Excessive capabilities and violation of least privilege **Risk Level**: Medium ### Complete Code Snippet The module accepts arbitrary source URLs and multiple unrelated intelligence modes: ```python def parser(kind: str) -> argparse.ArgumentParser: config = CONFIG[kind] result = argparse.ArgumentParser(description="Run a bounded Dataify {} workflow.".format(config["title"])) result.add_argument(config["input_flag"], dest="subject", help=config["input_help"]) result.add_argument("--competitor", action="append", default=[]) result.add_argument("--keyword", action="append", default=[]) result.add_argument("--source-url", action="append", default=[]) result.add_argument("--official-domain") result.add_argument("--geography", default="US") result.add_argument("--freshness", default="12 months") result.add_argument("--mode", choices=tuple(MODES), default="quick") result.add_argument("--max-actions", type=int) result.add_argument("--output-dir", type=Path) result.add_argument("--resume", type=Path) result.add_argument("--dry-run", action="store_true") return result ``` It can invoke scripts from other installed Skills: ```python def command(action: dict[str, Any]) -> list[str]: capability = action["capability"] geo = str(action.get("geography", "")).strip().lower() geo_args = ["--gl", geo] if re.fullmatch(r"[a-z]{2}", geo) else [] if capability == "dataify-google-shopping": return [sys.executable, str(ROOT / "skills/serp-google-shopping/scripts/google_shopping.py"), "--q", action["query"], "--json", "1", *geo_args] if capability == "dataify-google-news": return [sys.executable, str(ROOT / "skills/serp-google-news/scripts/google_news.py"), "--q", action["query"], "--json", "1", *geo_args] if capability == "dataify-google-search": ...[truncated 4072 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `business_workflow.py` from this narrowly scoped Skill package if it is not required for Google Play review collection. 2. If any workflow functionality is retained: - Permit only the declared Google Play scraper IDs. - Allowlist `play.google.com`. - Require the expected `/store/apps/details` path. - Reject arbitrary source URLs and unrelated modes. 3. Eliminate dynamic execution of sibling Skills from this package. 4. Separate broad business-intelligence functionality into a distinct Skill with an accurate manifest and explicit permissions. 5. Require confirmation before high-volume, multi-target, or credit-intensive operations. 6. Store raw responses only when explicitly requested, use restrictive permissions, and document retention behavior. 7. Add tests proving that non-Google-Play hosts, unrelated scraper capabilities, and sibling-script invocation are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/catalog_builder.py:18
Finding
Builder Accepts Undeclared Parameters and Does Not Enforce the Google Play Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/catalog_builder.py`, lines 18–29 and 49–85; `references/tool-params.json`, line 1 **Vulnerability Type**: Insufficient input validation and scope enforcement **Risk Level**: Medium ### Complete Code Snippet URL validation accepts any HTTP or HTTPS host: ```python def normalize_http_url(value, parameter="url", example=None): text = str(value or "").strip() parsed = urllib.parse.urlsplit(text) if parsed.scheme not in {"http", "https"} or not parsed.netloc: hint = " For example: {}".format(example) if example else "" raise ValueError( "Invalid URL for {}. Provide a complete URL starting with https://.{}".format( parameter, hint ) ) return text ``` Unknown input keys are preserved because missing definitions default to an empty dictionary: ```python def map_select_labels(tool, rows): definitions = {item["param"]: item for item in tool.get("params", [])} normalized = [] for row in rows: mapped = {} for key, value in row.items(): definition = definitions.get(key, {}) final = value if definition.get("input_mode") == "select": for option in definition.get("options", []): if value in {option.get("label"), option.get("submitted_value"), option.get("raw_value"), option.get("raw_type_value")}: final = option.get("submitted_value") break mapped[key] = final normalized.append(mapped) return normalized ``` Validation only checks parameters that are already declared in the catalog: ```python def validate_required(tool, rows): definitions = {item["param"]: item for item in tool.get("params", [])} required = [key for key, item in definitions.items() if item.get("required") is True] for index, row in enumerate(rows, 1): missing = [key for key in ...[truncated 3217 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define an explicit parameter schema for every catalog tool, including the required Google Play URL field. 2. Reject all keys not present in the selected tool's schema: ```python unknown = set(row) - set(definitions) if unknown: raise ValueError("Unknown parameters: {}".format(", ".join(sorted(unknown)))) ``` 3. Enforce the intended target: ```python parsed = urllib.parse.urlsplit(text) if parsed.scheme != "https": raise ValueError("HTTPS is required") if parsed.hostname != "play.google.com": raise ValueError("Only play.google.com URLs are allowed") if parsed.path != "/store/apps/details": raise ValueError("A Google Play app-details URL is required") ``` 4. Reject embedded usernames, passwords, unsupported ports, fragments, and malformed hostnames. 5. Validate the expected application identifier in the query string. 6. Require at least one defined and validated target URL before submission. 7. Add tests for unknown fields, non-Play hosts, HTTP URLs, URL credentials, malformed app IDs, and empty parameter objects. 8. Keep remote validation as defense in depth rather than relying on it as the primary control. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill exercises sensitive capabilities—environment access, file reads, shell usage, and network transmission—without any declared permissions or narrowing constraints. That mismatch weakens reviewability and can let a seemingly simple review-collection skill invoke broader behaviors than users or policy expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill is presented as a narrowly scoped Google Play review collector, but its workflow references generic catalog-driven tool selection and behavior consistent with broader scraping and discovery. Description/behavior mismatch is dangerous because it can mislead reviewers and users into authorizing a much more powerful data-exfiltration and web-scraping capability than advertised.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The OpenAI-facing metadata expands the tool’s apparent purpose from the declared scope of collecting reviews for a known Google Play app URL to generic "Play.google Builder data" collection. This creates a scope mismatch that can cause an agent to invoke the skill for unsupported or unintended data collection tasks, weakening policy boundaries and increasing the chance of misuse or overbroad access.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The shared workflow supports multiple intelligence modes—price, review, lead, and brand—despite the skill being described as collecting Google Play reviews from a known app URL only. This scope mismatch is dangerous because it grants operators a much broader collection surface, including search-driven discovery and unrelated intelligence gathering, which can violate least-privilege expectations and enable misuse beyond the declared skill purpose.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The action-building and capability-selection logic enables competitor discovery, keyword expansion, lead generation, brand monitoring, shopping/news search, and scraping of Amazon, Google Maps, and arbitrary web pages. In the context of a skill advertised as Google Play reviews-by-URL, these extra capabilities materially expand data access and collection behavior beyond user and platform expectations, increasing the risk of overcollection and policy bypass.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The workflow automatically extracts links from discovery results and appends new detail actions up to the configured action limit, effectively turning one request into a broader crawl. For a skill that should operate on a known Play Store URL, this autonomous expansion is risky because it can follow unrelated or external links and collect data outside the declared scope.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The file exposes a general Google search client (`search`) and an arbitrary URL fetcher (`unlock`) even though the skill is supposed to collect reviews from a known Google Play app URL only. That creates a broader external browsing and retrieval primitive than the declared scope, which can be repurposed for unrelated web discovery, data collection, or access to attacker-chosen destinations through a paid third-party scraping service.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The recursive URL extraction logic walks arbitrary JSON structures and collects any normalized links it finds, which expands the skill from targeted review collection into generic web discovery. In the context of a skill that should not perform app discovery or broad search, this enables harvesting and pivoting to unrelated URLs from search or scraper responses, increasing misuse potential and request spend.

External Transmission

Medium
Category
Data Exfiltration
Content
13. Set `spider_name` to `play.google.com`.
14. Set `spider_id` to the selected tool's `tool_sign`.
15. Always include `spider_errors=true` and `file_name={{TasksID}}`.
16. Return a curl command for `https://scraperapi.dataify.com/builder`.

## Set DATAIFY_API_TOKEN
Confidence
92% confidence
Finding
The skill instructs transmission of user-supplied parameters and task metadata to an external third-party endpoint. External transmission is expected for this integration, but it is still security-relevant because sensitive URLs, collected data, or metadata may leave the local trust boundary without explicit minimization or consent controls.

External Transmission

Medium
Category
Data Exfiltration
Content
---
name: "dataify-google-play-store-reviews-by-url"
description: "为 play.google.com 上以 google-play-store_reviews_by-url 为根的 scraper 系列准备 Dataify builder 请求。当需要处理成功的 Dataify scraper detail 条目 google-play-store_reviews_by-url、让用户选择可用工具、读取已保存的 getToolParams 选项,并使用 DATAIFY_API_TOKEN 生成 scraperapi.dataify.com/builder curl 请求时,使用此 skill。"
---

# Dataify Builder Skill 中文版
Confidence
83% confidence
Finding
The skill is explicitly designed to send user-supplied parameters and an authorization token to an external third-party endpoint at scraperapi.dataify.com. This creates a real data-exfiltration/privacy boundary crossing risk if users provide sensitive app URLs, parameters, or other data without clear consent and destination disclosure.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/task_runtime.py:38