Back to skill

Security audit

Apollo-like leads scraper (Apify)

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it handles lead/contact data and Apify credentials with insufficient guardrails and disclosure.

Install only if you are comfortable sending lead-search criteria to Apify and collecting business contact data under your own legal, privacy, platform-terms, and anti-spam obligations. Prefer a narrowly scoped Apify token supplied through APIFY_TOKEN or a secret manager, avoid the --apify-token command-line option, and review whether the actor-id override should be allowed in your environment.

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/apollo_like_leads_actor.py:128
Finding
Apify API Token Exposed in URL Query String## Vulnerability Details **File Location**: `scripts/apollo_like_leads_actor.py`, lines 128–143 **Vulnerability Type**: Credential exposure through URL query parameters **Risk Level**: Medium ```python def run_actor(token: str, actor_id: str, payload: Dict[str, Any], timeout_sec: int) -> Dict[str, Any]: base_url = f"https://api.apify.com/v2/acts/{actor_id}/run-sync-get-dataset-items" params = { "token": token, "timeout": timeout_sec, "clean": "true", } url = f"{base_url}?{urllib.parse.urlencode(params)}" body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url=url, data=body, headers={"Content-Type": "application/json"}, method="POST", ) ``` ### Technical Analysis The script places the Apify API token directly in the request URL. TLS protects the URL while it travels between the client and the HTTPS endpoint, but it does not prevent the complete URL from being captured by local diagnostics, HTTP instrumentation, reverse proxies, observability platforms, exception reports, or server-side access logs. Credentials should be transmitted through an authorization header because infrastructure commonly treats headers—particularly `Authorization`—as sensitive and applies redaction controls. Query parameters are more likely to be logged without redaction. The outbound request is necessary for the Skill’s declared lead-collection functionality, but putting the credential in the URL is not necessary and exceeds secure minimum-disclosure requirements. ### Attack Path 1. A user configures a valid `APIFY_TOKEN` and invokes the Skill. 2. `run_actor` inserts the token into the URL as the `token` query parameter. 3. A proxy, monitoring agent, request debugger, API access log, or error-reporting system records the complete request URL. 4. A person or compromised service with access to those records extracts the to ...[truncated 687 chars]
Remediation
## Remediation Suggestions - Remove the `token` field from the URL query parameters. - Send the credential in an authorization header, subject to confirmation of the Apify API’s supported authentication format: ```python params = { "timeout": timeout_sec, "clean": "true", } url = f"{base_url}?{urllib.parse.urlencode(params)}" req = urllib.request.Request( url=url, data=json.dumps(payload).encode("utf-8"), headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}", }, method="POST", ) ``` - Configure HTTP clients, proxies, and telemetry systems to redact authorization headers and known secret values. - Avoid including request URLs or request objects in exceptions where credentials could be present. - Use a narrowly scoped Apify token with only the permissions required to run the designated actor and retrieve its output. - Rotate the token if the existing implementation has been used in an environment where full URLs may have been retained.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/apollo_like_leads_actor.py:174
Finding
Apify Token Accepted and Documented as a Command-Line Argument## Vulnerability Details **File Location**: `scripts/apollo_like_leads_actor.py`, line 174; supplementary documentation at `SKILL.md`, lines 52–57 **Vulnerability Type**: Credential exposure through process arguments and shell history **Risk Level**: Medium ```python common.add_argument("--apify-token", help="Apify API token (fallback: APIFY_TOKEN env)") ``` The Skill documentation explicitly presents the insecure argument-based workflow: ```bash python3 scripts/apollo_like_leads_actor.py run \ --apify-token 'apify_api_xxx' \ --input-json '{"max_results":50,"person_location_country":["United States"]}' ``` ### Technical Analysis Passing credentials as command-line arguments can expose them through shell history, process inspection facilities, endpoint telemetry, job-runner logs, audit records, and command-capture tooling. Quoting the token prevents shell expansion but does not keep it out of the process argument vector or shell history. The script already supports the `APIFY_TOKEN` environment variable, so exposing a CLI secret option is not necessary for the declared function. Environment variables are not universally secret, but they generally avoid routine shell-history and command-line capture. A managed secret store or protected credential file is preferable in automation environments. ### Attack Path 1. A user follows the documented example and supplies a real token through `--apify-token`. 2. The shell records the command in history, or local process and execution telemetry captures the argument vector. 3. Another local user, administrator, support operator, telemetry reader, or attacker with access to those records obtains the token. 4. The attacker reuses the token against the Apify API. 5. The attacker accesses resources or consumes services within the permission scope of the token. This path requires access to the host, command history, process information, or collected telemetry; it is not a re ...[truncated 445 chars]
Remediation
## Remediation Suggestions - Remove the `--apify-token` option and obtain the token from `APIFY_TOKEN` or an approved secret-management integration. - Remove the argument-based authentication example from `SKILL.md`. - If interactive entry is required, use `getpass.getpass()` so the token is not echoed or stored in shell history. - For automation, inject the token through the execution platform’s protected secret facility rather than placing it in command text. - Restrict the token to the minimum Apify permissions and resources required by this Skill. - Document safe credential rotation and revocation procedures. - Warn users to rotate credentials previously passed on the command line if shell history or process telemetry may have retained them.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Tainted flow: 'req' from os.getenv (line 141, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
)

    try:
        with urllib.request.urlopen(req, timeout=min(timeout_sec + 30, 3600)) as response:
            raw = response.read().decode("utf-8", errors="replace")
            status_code = getattr(response, "status", 200)
    except urllib.error.HTTPError as exc:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README explicitly promotes scraping B2B leads, verified emails, and contact data, but does not warn users about privacy, consent, data protection, or platform/anti-spam compliance obligations. In a lead-generation skill, this omission can normalize collection and downstream use of personal data without safeguards, increasing the risk of unlawful processing, spam abuse, or policy violations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill documents capabilities that rely on environment secrets, file access, and outbound network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. That weakens least-privilege controls and transparency, making it easier for an agent/runtime to grant broader access than users expect when running a third-party data collection workflow.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to send lead-search filters and retrieve contact data through an external Apify actor, but it does not clearly warn that both query criteria and collected personal/business contact data will be transmitted to and processed by a third-party service. This creates a privacy and compliance risk because users may disclose sensitive targeting data or process personal data without informed consent or appropriate vendor review.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The manifest advertises lead scraping and actor execution but does not warn users that their request may trigger external data collection and transmission to Apify. In this context, the omission is meaningful because the skill is specifically designed for B2B lead harvesting, which can implicate privacy, consent, and terms-of-service concerns if users are not clearly informed.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The default prompt broadly instructs the agent to use the scraping skill whenever lead collection is needed, without constraining when it is appropriate, what data may be collected, or whether the user has authorized external scraping. In a skill that triggers third-party actor execution and B2B data harvesting, this can cause overuse, unauthorized data collection, or unreviewed transmission of user queries to an external service.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The guide explicitly enables collection of emails and phone numbers for B2B leads without any accompanying privacy notice, lawful-basis guidance, or restrictions on handling personal contact data. In a lead-generation skill, this omission can normalize bulk personal-data collection and downstream misuse, increasing compliance, privacy, and abuse risk.

External Transmission

Medium
Category
Data Exfiltration
Content
def run_actor(token: str, actor_id: str, payload: Dict[str, Any], timeout_sec: int) -> Dict[str, Any]:
    base_url = f"https://api.apify.com/v2/acts/{actor_id}/run-sync-get-dataset-items"
    params = {
        "token": token,
        "timeout": timeout_sec,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.