Back to skill

Security audit

Dataify Lead Intelligence

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly performs disclosed Dataify company research, but it needs review because it bundles an unrelated Amazon review scraper and has credential and local-file handling weaknesses.

Review before installing. Use this only with a Dataify token you are willing to use for public company research, avoid running it on untrusted resume state files, and be aware that the package includes an unrelated Amazon review scraper with implicit invocation enabled. Rotate the Dataify token if it may have been exposed in logs.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wait_for_task.py:35
Finding
API Token Transmitted in URL Query Parameters<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wait_for_task.py:35-39, 103-107, 129-134` **Duplicate Location**: `_dependencies/skills/dataify-task-operations/scripts/wait_for_task.py` **Vulnerability Type**: Credential exposure through URL query parameters **Risk Level**: Medium ### Vulnerable Code ```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() charset = response.headers.get_content_charset() or "utf-8" text = content.decode(charset, errors="replace") ``` The function is called with the API token included in `params`: ```python payload = request_json( STATUS_ENDPOINT, {"api_key": api_key, "task_id": task_id}, api_key, request_timeout, ) ``` The same behavior occurs 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 task status and result-download requests place `DATAIFY_API_TOKEN` in the URL query string. Although the destination is a fixed Dataify HTTPS endpoint, HTTPS only protects the URL while it is in transit. The complete URL can still be recorded by: - Reverse proxies and API gateways - Web server access logs - Network monitoring and observability systems - Error-reporting or tracing platforms - Debug logs generated by HTTP infrastructure - URL inspection middleware The implementation redacts the token from the response body after receiving it, but this does not protect the request URL from infrastructure logging. This also conflicts with the Skill documentation stating that tokens must not be exposed in logs. The network communication itself is necessary for the declared task-moni ...[truncated 1315 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `api_key` from all query parameters. 2. Send the credential in 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", ) ``` 3. Update both status and download requests so their parameter objects contain only non-secret values such as `task_id` and `type`. 4. If the remote API does not currently support authorization headers, update the API contract before deploying the Skill. As a temporary defense, configure every relevant proxy and server to redact the `api_key` parameter. 5. Ensure exceptions, telemetry, debug output, and HTTP tracing never record authorization headers or complete sensitive URLs. 6. Rotate any tokens that may already have appeared in access logs. 7. Apply the same change to the duplicate implementation under `_dependencies/skills/dataify-task-operations/scripts/wait_for_task.py`. 8. Add an automated test asserting that the token never appears in a generated request URL. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/business_workflow.py:585
Finding
Unvalidated Resume State Allows Arbitrary Local File Reads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/business_workflow.py:585-590, 547-550, 636-638` **Duplicate Location**: `_dependencies/skills/dataify-task-operations/scripts/business_workflow.py` **Vulnerability Type**: Path traversal and unsafe deserialization of workflow state **Risk Level**: Medium ### Vulnerable Code The resume file is loaded without schema or integrity 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")) ``` Later, an attacker-controlled `output` path is joined to the workflow root and read: ```python 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() decoded = raw.decode("utf-8", errors="replace") ``` The same unvalidated path is also read while restoring discovery payloads: ```python payloads[action["id"]] = decode_json_stream( (root / action["output"]).read_text(encoding="utf-8") ) ``` ### Technical Analysis The `--resume` option treats a caller-supplied JSON file as trusted workflow state. No validation restricts `action["output"]` to the run directory. Python `pathlib` permits both traversal and absolute paths in this construction: - `root / "../../sensitive-file"` - `root / "/absolute/path/to/file"` When the right operand is absolute, it replaces the preceding root. A crafted resume state can therefore mark an action as successful and direct the workflow to read any file accessible to the current process. The state also lacks validation for fields such as `kind`, `status`, `capability`, `action ID`, `max_actions`, and `output`. The confirmed security consequence is the arbitrary file read through `output`. The loaded content is decoded and processed as collection evide ...[truncated 2107 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define and enforce a strict schema for resumed workflow state. 2. Reject unknown or invalid values for: - Workflow kind - Action type - Capability - Status - Action ID - Stage - Maximum action count - Output path 3. Resolve every restored output path and verify that it remains under the resolved workflow root: ```python def safe_output_path(root: Path, value: str) -> Path: root_resolved = root.resolve() candidate = (root_resolved / value).resolve() try: candidate.relative_to(root_resolved) except ValueError: raise ValueError("Action output must remain inside the workflow directory") if not candidate.is_file(): raise ValueError("Action output is not a regular file") return candidate ``` 4. Restrict resumed output paths to the expected generated format, such as `raw/a01-subject.json`. 5. Reject absolute paths, `..` components, symbolic-link escapes, and non-regular files. 6. Replace every direct use of `root / action["output"]` with the validated path helper. 7. Consider authenticating state files with an HMAC stored separately from the workflow directory if resume files can cross trust boundaries. 8. Avoid automatically placing arbitrary non-JSON file contents into generated reports. 9. Apply identical hardening to the duplicate implementation under `_dependencies/skills/dataify-task-operations/scripts/business_workflow.py`. 10. Add regression tests for absolute paths, traversal paths, symbolic links, malformed states, unknown capabilities, and forged successful actions. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill instructs the agent to run a Python script, inspect environment state, use networked public sources, and potentially read/write local files, but it does not declare any permissions. This creates a transparency and sandboxing gap: the platform or reviewer cannot clearly enforce least privilege, and an agent may perform broader actions than users expect.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill’s declared purpose is Amazon review scraping, but it appears inside a lead-intelligence skill dependency path. This mismatch can cause an agent or reviewer to invoke unrelated scraping behavior under misleading business context, undermining least-privilege expectations and increasing the chance of unauthorized or policy-violating data collection.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The embedded skill advertises Amazon comment collection even though the parent skill is scoped to lead intelligence and company ICP matching. This capability mismatch can cause an agent to invoke an unrelated scraping function, expanding data collection beyond the declared purpose and creating a pathway for unauthorized or policy-inconsistent use.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
Amazon comment collection is not justified by the stated purpose of discovering and ranking companies using public company, hiring, and market evidence. Unnecessary collection capabilities increase attack surface, may lead to off-purpose scraping, and make it easier for downstream agents to gather data outside intended governance boundaries.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script is for submitting Amazon comment/review scraping tasks, which is unrelated to the declared lead-intelligence purpose of discovering and ranking companies. This capability mismatch is dangerous because it indicates undeclared data-collection behavior and expands the skill's effective permissions and external data flows beyond what a user would reasonably expect.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The code submits scraping jobs for Amazon product comments/reviews, which does not support the stated company lead-intelligence use cases. Even if not overtly malicious, unjustified collection capability increases the risk of policy violations, misuse of credentials, and hidden exfiltration through third-party scraping endpoints.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Implicit invocation is enabled without any trigger constraints, exclusions, or user-confirmation safeguards. In the context of an unrelated scraping capability, this makes accidental or hidden invocation more likely, allowing the agent to launch asynchronous collection tasks without clear operator intent.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The skill is configured for implicit invocation and its default prompt broadly encourages discovery and ranking of companies matching an ICP without clear user-intent or scope constraints. This can cause the agent to invoke the skill in situations where the user did not explicitly request prospecting behavior, leading to over-collection of public business intelligence, unintended outreach preparation, or use outside approved qualification workflows.

Unvalidated Output Injection

High
Category
Output Handling
Content
method="POST",
            )
        with urllib.request.urlopen(request, timeout=120) as response:
            return subprocess.CompletedProcess([], 0, stdout=response.read().decode("utf-8", errors="replace"), stderr="")
    except urllib.error.HTTPError as exc:
        detail = exc.read().decode("utf-8", errors="replace")
        return subprocess.CompletedProcess([], 1, stdout="", stderr=detail or "HTTP {}".format(exc.code))
Confidence
90% confidence
Finding
The code trusts remote HTTP response bodies and returns them as successful output, which are later persisted to raw files and parsed as JSON without content-type, size, or schema validation. Because the workflow follows links discovered from search results and fetches arbitrary pages through a web unlocker, a malicious or hostile endpoint can inject oversized, malformed, or adversarial content that contaminates downstream reports and may cause resource exhaustion or unsafe data propagation.

Unvalidated Output Injection

High
Category
Output Handling
Content
def execute_action(action: dict[str, Any], token: str) -> subprocess.CompletedProcess[str]:
    invocation = command(action)
    if len(invocation) > 1 and Path(invocation[1]).exists():
        return subprocess.run(invocation, capture_output=True, text=True, encoding="utf-8", errors="replace", check=False)
    if action.get('capability', '').startswith('scraper-'):
        return subprocess.CompletedProcess([], 1, '', 'Required platform scraper is not installed; install it before executing this action.')
    return direct_request(action, token)
Confidence
88% confidence
Finding
Output from invoked helper scripts is accepted wholesale and later decoded, parsed, written to disk, and incorporated into reports with minimal validation. If a helper script is compromised, buggy, or produces attacker-influenced output from hostile remote content, the parent workflow can ingest poisoned data, generate misleading intelligence, or consume excessive resources due to unbounded output handling.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
_dependencies/skills/dataify-task-operations/scripts/task_runtime.py:38

File appears to expose a hardcoded API secret or token.

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