Back to skill

Security audit

URL to Markdown - Web Page Extract API

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward Scavio URL extraction integration, with the main caution that submitted URLs are sent to a third-party service.

Install only if you are comfortable sending target URLs and fetched page content to Scavio. Do not use it for private, signed, tokenized, internal, or confidential links unless the user explicitly accepts that third-party processing risk, and prefer isolated environments for optional SDK installs.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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

Warning
Location
SKILL.md:109
Finding
Disclosure of Sensitive URL Data to a Third-Party Extraction Service<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 109–148 **Vulnerability Type**: Sensitive information disclosure through third-party URL processing **Risk Level**: Medium ### Vulnerable Code ```python import requests BASE = "https://api.scavio.dev" # Your key from https://scavio.dev. Load it from your environment or secret # store in real code - keep it out of source control. API_KEY = "sk_your_key_here" HEADERS = {"Authorization": f"Bearer {API_KEY}"} # 1. The common case: a page as clean Markdown, 1 credit page = requests.post(f"{BASE}/api/v1/extract", headers=HEADERS, json={"url": "https://example.com/pricing"}).json() ``` The generic extraction function forwards any supplied URL: ```python def read(url, format="markdown"): """normal (1cr) -> advanced (1cr) -> ultra (2cr). Only the successful call is billed.""" for mode in ("normal", "advanced", "ultra"): r = requests.post(f"{BASE}/api/v1/extract", headers=HEADERS, json={"url": url, "format": format, "mode": mode}) if r.status_code == 200: data = r.json()["data"] if data["content_length"]: return data if r.status_code == 400: break # bad or blocked URL - a higher tier will not fix it return None ``` ### Technical Analysis The Skill's declared purpose requires a remote service to fetch and transform web pages, so transmitting a target URL to Scavio is functionally necessary. The API credential is also sent only to the declared HTTPS API endpoint through the `Authorization` header, which is consistent with normal API authentication. However, the generic `read` function forwards the complete user-supplied URL to `https://api.scavio.dev/api/v1/extract` without checking whether the URL contains sensitive data. URLs can carry bearer secrets in their path, fragment, or query parameters, including: - Signed object-storage download parameters - Password-res ...[truncated 1878 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly disclose that complete target URLs are sent to Scavio for server-side retrieval. 2. Detect common credential-bearing query parameters such as `token`, `key`, `signature`, `sig`, `auth`, `session`, `code`, and cloud-provider signed URL fields. 3. Require explicit user confirmation before transmitting a URL that appears signed, private, or tokenized. 4. Remove tracking and nonessential query parameters before submission where doing so will not change the requested resource. 5. Do not automatically redact parameters required to retrieve the resource; instead, explain the third-party disclosure and request informed confirmation. 6. Avoid recording complete target URLs in local logs. Log only the origin or a redacted representation. 7. Document Scavio's URL retention, access-control, subprocessors, and deletion practices or link directly to the applicable privacy documentation. 8. Where confidentiality is required, provide a local-fetch alternative that does not disclose the target URL to a third-party extraction provider. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:190
Finding
Third-Party SDK Installation Without Integrity or Provenance Verification<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 190–210 **Vulnerability Type**: Unverified third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```bash pip install scavio==0.15.0 ``` ```python from scavio import ScavioClient client = ScavioClient() # reads SCAVIO_API_KEY page = client.extract("https://example.com/pricing") plain = client.extract("https://example.com/blog/post", format="text") spa = client.extract("https://example.com/app/docs", mode="advanced") # still 1 credit ``` ```bash npm install scavio@0.15.0 ``` ### Technical Analysis The Skill recommends installing Python and JavaScript packages from public package registries. Both dependencies are pinned to version `0.15.0`, which reduces exposure to unexpected future upgrades. However, the instructions do not provide: - Package integrity hashes - A lockfile - Signature or provenance verification - Verified registry package links - Source repository and release verification guidance - Isolation or least-privilege installation requirements Package installation and subsequent import execute code maintained outside this audited project. Python packages can run build or installation logic, while npm packages can define lifecycle scripts. Imported SDK code also runs with the privileges of the invoking process and can access environment variables, including `SCAVIO_API_KEY`. No evidence was found that either named package or version is malicious. The risk is conditional on registry compromise, publisher-account compromise, package replacement, or another software supply-chain event. The direct HTTPS API examples already included in the Skill can perform the declared function without installing either SDK, so dependency installation is optional rather than necessary. ### Attack Path 1. A user follows the optional SDK instructions. 2. The package manager retrieves `scavio==0.15.0` or `scavio@0.15.0` from its configured registry. 3. If the registry ...[truncated 1073 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer the documented direct HTTPS API flow when an SDK is not necessary. 2. Link to verified official package registry entries and the authoritative source repository. 3. Publish and verify package checksums, signatures, or registry provenance attestations. 4. Use lockfiles and hash-enforced installation where supported, such as a hashed Python requirements file and a committed npm lockfile. 5. Install dependencies in an isolated virtual environment or container under a non-privileged account. 6. Do not use `sudo` or administrator privileges for package installation. 7. Disable npm lifecycle scripts where they are unnecessary and operationally compatible. 8. Review the package source and release provenance before using it in an environment containing production credentials. 9. Limit the SDK process's access to secrets, files, and network destinations according to least-privilege principles. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Vague Triggers

Medium
Confidence
89% confidence
Finding
The trigger guidance is broad enough that an agent may invoke this skill whenever a user shares a URL, even if the user did not clearly consent to sending that URL to an external service. Because the tool causes network egress to a third party and may fetch sensitive or private links, overbroad invocation increases the risk of unintended data disclosure and surprising tool use.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill does not prominently warn users that submitted URLs are transmitted to an external service for server-side fetching. This can mislead users into providing internal, private, or sensitive links without understanding that the URL itself and resulting content may leave the local trust boundary.

External Transmission

Medium
Category
Data Exfiltration
Content
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# 1. The common case: a page as clean Markdown, 1 credit
page = requests.post(f"{BASE}/api/v1/extract", headers=HEADERS,
    json={"url": "https://example.com/pricing"}).json()

print(page["data"]["content"][:500], page["data"]["content_length"])
Confidence
86% confidence
Finding
This example performs an authenticated POST to a third-party API and transmits a user-supplied URL for remote fetching. While this is the intended functionality, it is still a real egress path: if invoked on sensitive URLs, it can expose private endpoints, signed links, or confidential targets to the external provider.

External Transmission

Medium
Category
Data Exfiltration
Content
print(page["data"]["content"][:500], page["data"]["content_length"])

# 2. Plain text for an embedding pipeline, still 1 credit
plain = requests.post(f"{BASE}/api/v1/extract", headers=HEADERS,
    json={"url": "https://example.com/blog/post", "format": "text"}).json()

# 3. A client-rendered page: advanced renders JavaScript and STILL costs 1 credit
Confidence
86% confidence
Finding
This call sends a URL to an external extraction service, creating the same data-transfer risk as the other examples. Even though the feature is expected, the example normalizes third-party transmission without pairing it with warnings about confidentiality, link sensitivity, or user approval.

External Transmission

Medium
Category
Data Exfiltration
Content
json={"url": "https://example.com/blog/post", "format": "text"}).json()

# 3. A client-rendered page: advanced renders JavaScript and STILL costs 1 credit
spa = requests.post(f"{BASE}/api/v1/extract", headers=HEADERS,
    json={"url": "https://example.com/app/docs", "mode": "advanced"}).json()

# 4. Raw HTML to parse yourself
Confidence
86% confidence
Finding
This example again demonstrates external transmission of a URL to Scavio, here with advanced rendering enabled. The rendering mode may increase the amount of remote interaction performed against the target page, making unintended processing of sensitive or authenticated pages more consequential if users are not warned.

External Transmission

Medium
Category
Data Exfiltration
Content
def read(url, format="markdown"):
    """normal (1cr) -> advanced (1cr) -> ultra (2cr). Only the successful call is billed."""
    for mode in ("normal", "advanced", "ultra"):
        r = requests.post(f"{BASE}/api/v1/extract", headers=HEADERS,
                          json={"url": url, "format": format, "mode": mode})
        if r.status_code == 200:
            data = r.json()["data"]
Confidence
90% confidence
Finding
The retry/escalation helper automates repeated external submissions of a user-provided URL across multiple fetch tiers. Although failures are not billed, this pattern can silently increase third-party exposure and remote probing of a target URL without an additional user decision, which is risky for sensitive or private links.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:40