Back to skill

Security audit

Scrape.ly

Security checks for vulnerabilities and agentic risk

Overview

The verdict is suspicious because scanners report that authenticated requests may send the API token to an unintended server after a redirect or API address override; the scan is partial.

Consider the suspicious verdict and incomplete scan before installing. The main reported concern is that requests carrying an API token may send it to an unintended server; verify that the API destination is trusted and that redirects or address overrides cannot redirect authenticated requests elsewhere.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (6)

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

Critical
Category
Data Flow
Confidence
96% confidence
Finding

Sending the token to the intended API is legitimate, but this request uses urllib's default redirect handling, which can forward the explicitly supplied Authorization header to a different origin. An attacker-controlled redirect, or an untrusted API-base override, could therefore expose the bearer token; the code does not enforce an approved HTTPS destination.

Content

Scanner excerpt · scripts/scrapely_agent.py (reported line 98)May include surrounding context.

python
headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=data, method=method, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            raw = resp.read(MAX_RESPONSE_BYTES + 1)
            if len(raw) > MAX_RESPONSE_BYTES:
                raise ApiError(0, "response-too-large", f"The API response is larger than {MAX_RESPONSE_BYTES} bytes.")

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

Critical
Category
Data Flow
Confidence
96% confidence
Finding

This separate record-fetching path also attaches the API bearer token and uses default urllib redirect handling without origin restrictions. Although authenticating to the service is expected, a cross-origin redirect can forward the token to another server, and an unchecked API-base override can send it directly to an unintended endpoint.

Content

Scanner excerpt · scripts/scrapely_agent.py (reported line 326)May include surrounding context.

python
url = api_url() + f"/v2/key-value-stores/{store}/records/{urllib.parse.quote(args.key)}"
    req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token(True)}", "User-Agent": f"scrapely-agent-skill/{VERSION}"})
    try:
        with urllib.request.urlopen(req, timeout=60) as resp:
            ctype = resp.headers.get("Content-Type", "")
            raw = resp.read(MAX_OUTPUT_CHARS + 1)
    except urllib.error.HTTPError as exc:

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, network.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
5. **Get more results** page by page, keeping only the fields you need:
   `python3 <skill-dir>/scripts/scrapely_agent.py output <runId> --offset 20 --limit 50 --fields title,url,price`
   Some Actors write a single summary record instead of items; read it with `python3 <skill-dir>/scripts/scrapely_agent.py record <runId>` (key `OUTPUT`).
6. **Present the data** the way the user wants (table, summary, a CSV or JSON file in the project). For large datasets, give the user the dataset link: `https://api.scrape.ly/v2/datasets/<datasetId>/items?format=csv` (they need their token to open it).

If a run is still running when the command returns, check it later with `python3 <skill-dir>/scripts/scrapely_agent.py status <runId>`. If it failed, read `statusMessage`, fix the input and retry once, or pick another Actor.

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 51)May include surrounding context.

md
## Rules

- Runs cost money from the user's prepaid balance. Don't start large or repeated runs without asking.
- Don't log in to sites or scrape behind a login with the user's credentials.
- Keep results out of the conversation when they're big: save them to a file and show a short sample.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/scrapely_agent.py (reported line 173)May include surrounding context.

python
def cmd_search(args: argparse.Namespace) -> dict:
    data = request("GET", "/v2/store", params={"responseFormat": "agent", "search": args.query, "limit": args.limit, "offset": args.offset}, auth=False)["data"]
    items = []
    for it in data.get("items", []):
        if "actor" not in it:  # server without the agent format: compact the full entry