Back to skill

Security audit

Dataify Competitive Intelligence

Security checks for vulnerabilities and agentic risk

Overview

The skill’s research purpose is coherent, but its resume/report scripts trust saved run state in ways that could run unintended local code or ingest local files.

Review before installing. Use only run directories you created and trust, avoid resuming third-party or edited state.json files, keep DATAIFY_API_TOKEN scoped to the minimum needed account, and run the skill in a restricted workspace if possible. Prefer dry-run/checkpoint modes for unfamiliar research plans and inspect generated evidence before sharing reports.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run_research.py:243
Finding

Unvalidated Resume State Enables Execution of Unapproved Local Python Scripts

Content
View full analysis
list[str]: action_type = action.get("type", "discover") if action_type == "discover": return [sys.executable, str(SEARCH_SCRIPT), "--q", action["query"], "--json", "1"] if action_type == "fetch": return [sys.executable, str(UNLOCKER_SCRIPT), "--url", action["url"], "--clean-content", "true"] folder = action["capability"] scripts = sorted((REPO_ROOT / "skills" / folder / "scripts").glob("*.py")) scripts = [path for path in scripts if "preview" not in path.name] if not scripts: raise FileNotFoundError(f"No executable script for {folder}") script = scripts[0] if action.get("tool_sign"): parameter = TOOL_URL_PARAMETERS.get(action["tool_sign"], "url") return [sys.executable, str(script), "--tool-sign", action["tool_sign"], "--params-json", json.dumps({parameter: action["url"]})] argument = "--job-url" if folder == "scraper-indeed-job-listings" else "--url" return [sys.executable, str(script), argument, action["url"]] ``` The attacker-controlled state is loaded without schema validation or a capability allowlist: ```python def load_or_plan(args: argparse.Namespace) -> tuple[Path, dict[str, Any]]: if args.resume: state_path = args.resume if args.resume.name == "state.json" else args.resume / "state.json" state = json.loads(state_path.read_text(encoding="utf-8")) ``` The selected script is subsequently executed: ```python command = command_for(action) return subprocess.run( command, capture_output=True, text=True, encoding="utf-8", errors="replace", check=False ) ``` ### Technical Analysis The `--resume` feature treats the contents of `state.json` as ...[truncated 2441 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/research_outputs.py:27
Finding

Resumed Action Output Path Allows Arbitrary Local File Ingestion

Content
View full analysis
list[dict[str, Any]]: state = json.loads(state_path.read_text(encoding="utf-8")) evidence = [] for action in state["actions"]: output = action.get("output") if action.get("status") != "success" or not output: continue raw_path = state_path.parent / output if not raw_path.exists(): continue raw = raw_path.read_text(encoding="utf-8", errors="replace") ``` The read content is subsequently copied into generated evidence as an excerpt and hash: ```python "content": { "raw_path": output, "sha256": hashlib.sha256(raw.encode("utf-8")).hexdigest(), "excerpt": re.sub(r"\s+", " ", raw)[:500], }, ``` ### Technical Analysis The normalizer trusts the `output` field stored in `state.json`. That field is joined to the run directory without rejecting absolute paths or traversal components. In Python, joining a `Path` with an absolute child discards the preceding parent path. Relative values containing `..` can likewise resolve outside the run directory. Consequently, a crafted successful action can direct `normalize()` to any readable text file accessible to the current process. The file is read and its first 500 normalized characters are copied into `evidence.json`. The path and content hash are also recorded. Generated reports or subsequent agent analysis can therefore expose or process data that was never collected as legitimate research evidence. ### Attack Path 1. An attacker creates or modifies a resumable `state.json`. 2. The attacker marks an action as `success`. 3. The action's `output` is set to an absolute path or a traversal path, for example: ```json { "id": "a01", "entity": "Example", ...[truncated 1231 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding

The skill directs execution of scripts, environment-variable checks, file reads/writes, network access, and shell commands, but it declares no explicit tool scope or permission boundaries. That leaves the runtime free to over-grant capabilities, increasing the blast radius of prompt injection, accidental misuse, or unintended side effects such as writing files, making external requests, or reading sensitive environment state.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 108)May include surrounding context.

md
## Account CTA policy

- Show a prominent Dataify account CTA only when the API token is missing, rejected/invalid, or the account has insufficient credits.
- For a missing token, offer https://dashboard.dataify.com/login?utm_source=skill and state: New accounts get 50 free credits, enough for about 6,000 trial results, valid for 7 days, and only successful requests are billed. Never ask the user to paste the token into chat.
- Detect the current operating system and shell. Show only the matching session-scoped setup command first (`export` for macOS/Linux shells, `$env:` for Windows PowerShell, or `set` for Windows Command Prompt). Show other platforms or persistent setup only when detection is ambiguous or the user asks.
- After the user says the token is configured, verify only whether `DATAIFY_API_TOKEN` is present; never print its value. If verification succeeds, continue the original task without asking the user to repeat it.
- Explain that persistent shell changes may require a new terminal or restarting the agent application. Do not recommend a project `.env` unless the execution path explicitly loads it, and ensure `.env` is ignored by version control.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 110)May include surrounding context.

md
- Show a prominent Dataify account CTA only when the API token is missing, rejected/invalid, or the account has insufficient credits.
- For a missing token, offer https://dashboard.dataify.com/login?utm_source=skill and state: New accounts get 50 free credits, enough for about 6,000 trial results, valid for 7 days, and only successful requests are billed. Never ask the user to paste the token into chat.
- Detect the current operating system and shell. Show only the matching session-scoped setup command first (`export` for macOS/Linux shells, `$env:` for Windows PowerShell, or `set` for Windows Command Prompt). Show other platforms or persistent setup only when detection is ambiguous or the user asks.
- After the user says the token is configured, verify only whether `DATAIFY_API_TOKEN` is present; never print its value. If verification succeeds, continue the original task without asking the user to repeat it.
- Explain that persistent shell changes may require a new terminal or restarting the agent application. Do not recommend a project `.env` unless the execution path explicitly loads it, and ensure `.env` is ignored by version control.
- For an invalid token, direct the user to API-key management without implying that a new registration is required. For insufficient credits, direct the user to balance or recharge management.
- During normal submission, processing, and successful completion, do not promote registration or the Dashboard. Never expose the token or include it in CTA attribution parameters.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/monitor_research.py (reported line 39)May include surrounding context.

python
command = [sys.executable, str(runner), "--resume", str(run_dir), "--autopilot"]
    if args.dry_run:
        command.append("--dry-run")
    result = subprocess.run(command, check=False)
    if result.returncode != 0 or args.dry_run:
        return result.returncode
    old_evidence = baseline_state.parent / "evidence.json"

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/run_research.py (reported line 303)May include surrounding context.

python
if required is not None and not required.exists():
        return direct_request(action, token)
    command = command_for(action)
    return subprocess.run(
        command, capture_output=True, text=True, encoding="utf-8", errors="replace", check=False
    )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script performs outbound research requests, fetches arbitrary discovered URLs, and writes raw evidence and reports to local storage during normal execution without an explicit interactive confirmation step or equivalent user-facing notice in the execution path. In a skill context, this can surprise operators, collect/store sensitive or regulated content from third-party sources, and create privacy/compliance or operational risk if run on behalf of a user who did not realize it would persist collected data locally.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.