Back to skill

Security audit

Dataify Glassdoor Builder

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real Dataify/Glassdoor integration, but it reaches beyond its advertised company-URL scope and has risky token and local-code handling that should be reviewed before installation.

Install only if you trust the publisher and are comfortable with Dataify receiving your submitted targets and using your API token. Before use, restrict the skill to glassdoor_company_by-url, remove or isolate job-listing and generic workflow scripts, avoid persistent token storage unless necessary, and rotate the token if it may have appeared in logs.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wait_for_task.py:35
Finding
API Token Exposed in Task Status and Download Query Strings<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wait_for_task.py:35-37`, `scripts/wait_for_task.py:103-107`, and `scripts/wait_for_task.py:129-134` **Vulnerability Type**: API credential exposure through URL query parameters **Risk Level**: High ### Vulnerable Code ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request(url, method="GET") ``` The normal task-status request passes the API token as a parameter: ```python payload = request_json( STATUS_ENDPOINT, {"api_key": api_key, "task_id": task_id}, api_key, request_timeout, ) ``` The result-download request repeats the same behavior: ```python return request_json( DOWNLOAD_ENDPOINT, {"api_key": api_key, "task_id": task_id, "type": "json"}, api_key, request_timeout, ) ``` ### Technical Analysis The value of `DATAIFY_API_TOKEN` is embedded directly in the URLs used to poll task status and download results. Although HTTPS encrypts the URL while it is in transit, query strings are routinely recorded by web-server access logs, reverse proxies, API gateways, observability systems, exception telemetry, and other infrastructure components. The response-body replacement performed elsewhere in `request_json()` does not protect the request URL: ```python if api_key: text = text.replace(api_key, "<redacted>") ``` This only redacts a token if it appears in the response body. It does not remove the credential from network request logs or upstream telemetry. The behavior occurs during the default completion workflow because `scripts/task_runtime.py` invokes `wait_for_task()`, which repeatedly performs the affected status request and eventually performs the affected download request. ### Attack Path 1. A user configures `DATAIFY_API_TOKEN` and invokes the documented builder workflow. 2. The task is submitted successfully. 3. The completion runtime calls `wait_f ...[truncated 912 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `api_key` from all query parameters. 2. Authenticate status and download requests through 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": f"Bearer {api_key}"}, method="GET", ) ``` Call it without adding the token to `params`: ```python request_json( STATUS_ENDPOINT, {"task_id": task_id}, api_key, request_timeout, ) ``` 3. Prefer POST requests if the service supports them and task parameters are considered sensitive. 4. If the remote API currently requires query-string authentication, request a header-authentication endpoint from the provider. 5. Until the API is changed, configure all client, proxy, gateway, server, and observability layers to redact `api_key` query parameters. 6. Add automated tests asserting that serialized request URLs never contain the configured token. 7. Rotate tokens that may already have appeared in logs and apply restricted retention and access controls to historical logs. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/build-dataify-request.py:5
Finding
External Import-Path Precedence Enables Local Module Hijacking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/build-dataify-request.py:5-9` **Vulnerability Type**: Python module search-path hijacking **Risk Level**: High ### Vulnerable Code ```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 script prepends a directory outside the audited Skill package to `sys.path`. The subsequent unqualified import of `catalog_builder` therefore resolves from that external directory before resolving the packaged `scripts/catalog_builder.py`. If `../../dataify-task-operations/scripts/catalog_builder.py` exists, Python can import it in preference to the local audited implementation. Importing a Python module executes its top-level code immediately. This behavior crosses the package's trust boundary and makes the effective implementation dependent on mutable files outside the reviewed artifact. It is especially dangerous because users are instructed to configure `DATAIFY_API_TOKEN` before invoking the script. A substituted module consequently executes in a process that can access that credential and all files available to the current user. ### Attack Path 1. An attacker obtains write access to the sibling `dataify-task-operations/scripts` directory, or supplies a compromised package that creates it. 2. The attacker places a malicious `catalog_builder.py` in that directory. 3. The victim configures `DATAIFY_API_TOKEN` and runs `scripts/build-dataify-request.py`. 4. The script inserts the attacker-controlled directory at index zero of `sys.path`. 5. Python imports the attacker's `catalog_builder.py` instead of the local reviewed module. 6. Top-level attacker code executes before the legitimate builder workflow. 7. The malicious module can read environment var ...[truncated 691 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the external `sys.path` modification. 2. Package the scripts as a Python package and use an explicit package-relative import: ```python from .catalog_builder import build_curl, run_catalog_builder ``` 3. If direct script execution must remain supported, load the local implementation from a path anchored to `__file__`, and verify that the resolved module path remains inside the Skill package. 4. If shared task-operation code is required, distribute it as a version-pinned and integrity-verified dependency rather than a mutable sibling directory. 5. After importing, assert the expected module location: ```python expected = os.path.realpath(os.path.dirname(__file__)) actual = os.path.realpath(os.path.dirname(catalog_builder.__file__)) if actual != expected: raise RuntimeError("Unexpected catalog_builder module location") ``` 6. Do not allow project or workspace directories writable by untrusted users to precede trusted code on `sys.path`. 7. Add a regression test that creates a same-named module in the sibling directory and confirms that it cannot override the packaged implementation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/business_workflow.py:535
Finding
Unvalidated Resume State Allows Arbitrary Local File Reads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/business_workflow.py:535-542` and `scripts/business_workflow.py:586-589` **Vulnerability Type**: Path traversal and absolute-path injection through untrusted state **Risk Level**: High ### Vulnerable Code The resume file is loaded without schema or trust validation: ```python if args.resume: root = args.resume if args.resume.is_dir() else args.resume.parent state_path = root / "state.json" if args.resume.is_dir() else args.resume state = json.loads(state_path.read_text(encoding="utf-8")) ``` Values from that state subsequently control file reads: ```python def build_outputs(root: Path, state: dict[str, Any]) -> dict[str, Any]: evidence: list[dict[str, Any]] = [] records: list[dict[str, Any]] = [] for action in state["actions"]: if action["status"] != "success" or not action.get("output"): continue raw_path = root / action["output"] raw = raw_path.read_bytes() ``` ### Technical Analysis The workflow trusts every field loaded from an externally supplied resume file. In particular, `action["output"]` is used as a filesystem path without rejecting: - Absolute paths. - Parent-directory traversal components such as `../`. - Symlinks that resolve outside the workflow directory. - Paths that do not correspond to files created by the workflow. With `pathlib`, joining a root with an absolute path discards the root. Relative traversal paths can likewise escape the intended output directory after resolution. The read bytes are decoded and parsed, then used to build records and evidence in `report.json` and `report.md`. Therefore, the issue is not merely an availability problem: content from an unintended local file can be incorporated into generated output and subsequently disclosed. This vulnerable code belongs to a generic business workflow that is not required for the narrowly declared Glassdoor builder functionality, increasing the Skill' ...[truncated 1584 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate resume files against a strict schema before using any field. 2. Reject absolute paths and paths containing parent traversal. 3. Resolve both the workflow root and candidate path, then enforce containment: ```python root_resolved = root.resolve(strict=True) raw_path = (root_resolved / action["output"]).resolve(strict=True) try: raw_path.relative_to(root_resolved) except ValueError: raise RuntimeError("Action output escapes the workflow directory") ``` 4. Reject symbolic links, or verify that the final resolved target remains under an expected `raw` directory. 5. Restrict output paths to a generated filename pattern, such as: ```python raw/[A-Za-z0-9_-]+-[A-Za-z0-9._-]+\.json ``` 6. Reconstruct expected output paths from trusted action identifiers instead of accepting arbitrary paths from state. 7. Validate `version`, `kind`, action IDs, statuses, capabilities, and all required fields. 8. Treat resume files received from other users or downloaded from external sources as untrusted. 9. Remove `business_workflow.py` from this Skill if it is not necessary for the declared Glassdoor collection purpose. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/tool-params.json:1
Finding
Empty Parameter Catalog Disables Target and Request Validation<![CDATA[ ## Vulnerability Details **File Location**: `references/tool-params.json:1` and `scripts/catalog_builder.py:56-85`, `scripts/catalog_builder.py:101-112` **Vulnerability Type**: Missing input schema and allowlist enforcement for credit-consuming requests **Risk Level**: Medium ### Vulnerable Code All catalog entries declare empty parameter lists, including the URL-oriented tools: ```json [ { "tool_name_cn": "公司URL", "tool_sign": "glassdoor_company_by-url", "spider_name": "glassdoor.com", "params": [] }, { "tool_name_cn": "公司过滤器", "tool_sign": "glassdoor_company_by-inputfilter", "spider_name": "glassdoor.com", "params": [] }, { "tool_name_cn": "公司关键词", "tool_sign": "glassdoor_company_by-keywords", "spider_name": "glassdoor.com", "params": [] }, { "tool_name_cn": "公司列表URL", "tool_sign": "glassdoor_company_by-listurl", "spider_name": "glassdoor.com", "params": [] }, { "tool_name_cn": "职位URL", "tool_sign": "glassdoor_joblistings_by-url", "spider_name": "glassdoor.com", "params": [] }, { "tool_name_cn": "职位关键词", "tool_sign": "glassdoor_joblistings_by-keywords", "spider_name": "glassdoor.com", "params": [] }, { "tool_name_cn": "职位列表URL", "tool_sign": "glassdoor_joblistings_by-listurl", "spider_name": "glassdoor.com", "params": [] } ] ``` Validation depends entirely on those missing definitions: ```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.g ...[truncated 3959 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restore complete parameter definitions for every supported tool. 2. Define, at minimum: - Parameter names. - Required status. - Data types. - Input modes. - Allowed select values. - URL formats. - Length and numeric bounds. 3. Reject any input key that is not explicitly declared for the selected tool. 4. For URL-oriented tools, enforce HTTPS and a tool-specific hostname allowlist such as `glassdoor.com` and explicitly approved subdomains. 5. Validate expected Glassdoor paths for company and job-listing tools rather than accepting any syntactically valid URL. 6. Set limits on: - Number of parameter rows. - Number of URLs or keywords. - String length. - Serialized request size. - Page count and other cost-affecting fields. 7. Require explicit confirmation for high-volume, multi-page, or otherwise materially expensive requests. 8. Add tests proving that missing required fields, unknown fields, non-Glassdoor URLs, oversized batches, and unsupported select values are rejected before network submission. 9. Retain server-side validation as defense in depth rather than relying exclusively on it. ]]>
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 (19)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill instructs the agent to read environment variables, read local reference files, invoke helper scripts, and send network requests, yet it declares no permissions. This weakens user oversight and platform enforcement because the actual capability surface is much broader than the metadata suggests.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The metadata says this skill is only for known Glassdoor company URLs, but the body expands behavior to a catalog-driven submission flow that can route to many unrelated tools, including job listings and other scraper families via external references. This mismatch can mislead users and reviewers about scope, causing unintended data collection, cost exposure, and broader outbound transmission than authorized.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The workflow explicitly broadens the skill from company-by-URL collection to multiple other Glassdoor modes, including filters, keywords, search URLs, and job listings. That scope expansion defeats the narrow trust boundary implied by the skill name and description and increases the chance of collecting unintended targets or higher-volume data.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The quick-start section presents the skill as rooted at `glassdoor_company_by-url`, but later operational steps direct the agent to offer many other tools, including job listings. This inconsistency makes the skill harder to reason about and can enable use beyond the approved or expected purpose.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata says it is for collecting Glassdoor company information from known company URLs, but the body expands scope to unrelated scraping modes including filters, keywords, search URLs, and job listings. This scope drift can cause the agent to perform broader collection than the user or platform expects, increasing data access, cost, and policy risk.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The instructions direct the agent to read a shared tool-parameter file for all tools under the skill family, not just the declared company-by-URL tool. That broadens the effective authority of the skill and can enable unintended tool selection or parameter exposure outside the advertised scope.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill is described as collecting structured Glassdoor company information from known company URLs, but the tool parameter file exposes broader company-search and job-listing tools that exceed that scope. This creates a scope-expansion risk: an agent or integrator could invoke unintended collection capabilities, leading to overcollection of data and behavior that does not match the declared purpose of the skill.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
Including job-listing collection capability in a company-by-URL skill is unjustified by the stated purpose and materially expands what data can be gathered. This mismatch can enable unauthorized scraping workflows, policy bypass, or accidental collection of unrelated recruitment data, making the skill more dangerous because its actual capabilities are broader than users and reviewers would expect.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The file is a shared multi-mode workflow engine supporting price, review, lead, and brand intelligence, which materially exceeds the declared purpose of a Glassdoor company-by-URL skill. In a security review, this mismatch increases the risk that the skill can be repurposed to collect unrelated data, invoke unrelated capabilities, and bypass user expectations about scope.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The action builder performs search-engine discovery and generates new collection tasks from user subject terms, competitors, and keywords instead of restricting itself to known company URLs. That broadens collection from user-supplied targets to arbitrary third-party pages, creating unauthorized scope expansion and increasing privacy/compliance risk.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The workflow automatically extracts links from discovery results and appends them as new detail actions until capacity is reached. This autonomous expansion means the skill can crawl additional pages not explicitly authorized by the user, making it more dangerous in the context of a supposedly bounded company-by-URL collector.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The lead mode actively targets LinkedIn and Crunchbase company pages, which is unrelated to collecting structured information from known Glassdoor company URLs. This creates a clear capability mismatch that could be used to gather external company intelligence beyond the skill's declared purpose.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The record parsing, filtering, and analytics support unrelated price intelligence, review mining, lead scoring, and brand monitoring workflows. In this skill context, those extra behaviors are unjustified and expand both the accessible data types and the opportunities for misuse far beyond the stated Glassdoor company URL collection task.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The command dispatcher can invoke multiple other skills including shopping search, news search, general search, Amazon comments, Google Maps reviews, and a web unlocker. That broad execution surface is unnecessary for a single Glassdoor URL collector and increases the chance of unintended data collection or lateral capability abuse.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The client exposes broad web-search and arbitrary page-fetching primitives that are not constrained to known Glassdoor company URLs, despite the skill’s declared scope. This creates a scope-expansion risk: downstream prompts or callers can repurpose the skill to discover and retrieve unrelated websites, increasing the chance of unauthorized collection, policy bypass, or misuse as a general web-scraping tool.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
A generic Google search function is inconsistent with a skill intended to operate on already-known Glassdoor company URLs. In practice, this enables discovery of arbitrary targets and can be chained with the fetch capability to turn the skill into a general reconnaissance and collection tool outside its stated purpose.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The unlock function accepts any normalized HTTP(S) URL and forwards it to a remote page-unlocking service with JS rendering and redirect following enabled. Because it is not restricted to Glassdoor domains or company-page paths, it can fetch arbitrary third-party content and meaningfully exceeds the skill’s declared scope, making misuse more dangerous in this context.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The code sends user-supplied queries and URLs to external Dataify endpoints, which is expected for collection but not clearly surfaced or constrained in this file. In the context of a narrowly described skill, undisclosed transmission of user-provided targets to third-party services increases privacy, consent, and data-governance risk.

External Transmission

Medium
Category
Data Exfiltration
Content
13. Set `spider_name` to `glassdoor.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
94% confidence
Finding
The skill directs the agent to construct and submit requests to an external service, transmitting user-supplied URLs and scraping parameters off-platform. External transmission is expected for this type of integration, but it is still security-relevant because the skill's actual scope is broader than advertised and users may not realize what data is being sent.

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