Back to skill

Security audit

Superwise Drift Detection Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its drift-detection purpose, but its production scheduler exposes an unauthenticated public endpoint that can repeatedly trigger data uploads, Superwise policy actions, and Telegram alerts.

Review before installing or deploying. Use this only with a trusted Superwise account, a trusted inference endpoint, and data that is approved for third-party processing. Do not expose scheduler.py publicly as-is: add authentication to /run-check, prefer POST, add rate limits and a single-run lock, validate the inference endpoint, and pin dependencies before production use.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scheduler.py:49
Finding

Unauthenticated Public Endpoint Triggers Privileged Drift-Detection Operations

Content
View full analysis

Vulnerability Details

File Location: scheduler.py:49-59
Vulnerability Type: Missing authentication and authorization on a state-changing endpoint
Risk Level: High

Vulnerable Code

python
@app.route("/run-check")
def run_check():
    """HTTP endpoint for OpenClaw to trigger a manual drift check."""
    logger.info("[scheduler] Manual /run-check triggered via HTTP")
    try:
        result = skill_run()
        return jsonify({"status": "ok", "result": result})
    except Exception as exc:
        logger.error(f"[scheduler] /run-check failed: {exc}", exc_info=True)
        return jsonify({"status": "error", "message": str(exc)}), 500

The endpoint is exposed through the public web service configured in render.yaml:2-6:

yaml
services:
  - type: web
    name: superwise-drift-detection-skill
    env: python
    buildCommand: pip install -r requirements.txt
    startCommand: python scheduler.py

Technical Analysis

The /run-check route performs no authentication, authorization, request-signature validation, or source restriction. It also uses HTTP GET for an operation with substantial side effects.

Every request invokes skill_run(), which:

  1. Fetches records from the configured inference endpoint.
  2. Uploads those records to Superwise.
  3. Assigns fresh UUIDs to records, preventing natural deduplication between runs.
  4. Triggers and polls configured drift policies.
  5. Sends a Telegram notification.

Because the included Render configuration deploys scheduler.py as a web service, the endpoint is expected to be reachable from the internet unless external access controls are added separately. There is also no rate limit, concurrency guard, or idempotency control.

The exception handler returns str(exc) directly to the remote caller. Depending on the failure, this may disclose internal URLs, third-party response details, dataset identifiers, or opera ...[truncated 1329 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace the state-changing GET route with a POST route.
  2. Require authentication using either:
    • A high-entropy bearer token stored as a deployment secret.
    • A cryptographically verified OpenClaw webhook signature.
    • Mutual TLS or an identity-aware reverse proxy.
  3. Compare authentication tokens using a constant-time comparison.
  4. Restrict the endpoint to trusted source networks where deployment infrastructure permits it.
  5. Add per-client and global rate limits.
  6. Add a process-wide or distributed lock so only one drift check can run at a time.
  7. Introduce idempotency controls to prevent duplicate ingestion.
  8. Queue long-running checks asynchronously and return 202 Accepted rather than occupying the web request.
  9. Return a generic error response to callers and retain detailed exceptions only in protected server logs.
  10. Ensure monitoring alerts distinguish scheduled checks from authenticated manual checks.

T09 · Insecure Skill Coding Practices

Warning
Location
skill.py:77
Finding

Unrestricted Inference Endpoint Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: skill.py:77-91
Vulnerability Type: Server-Side Request Forgery through an unrestricted configurable URL
Risk Level: Medium

Vulnerable Code

python
url = os.getenv("INFERENCE_ENDPOINT_URL")
if not url:
    raise RuntimeError("INFERENCE_ENDPOINT_URL is not set in .env")

try:
    resp = requests.get(url, timeout=30)
    resp.raise_for_status()
    data = resp.json()
    records = data.get("records", [])
    print(f"[skill] Fetched {len(records)} inference records from {url}")
    return records
except requests.RequestException as exc:
    raise RuntimeError(f"[skill] Failed to fetch inference records: {exc}") from exc

Technical Analysis

The application makes a server-side request to the value of INFERENCE_ENDPOINT_URL without validating:

  • The URL scheme.
  • The destination hostname.
  • The resolved IP address.
  • The destination port.
  • Redirect targets.
  • Whether the destination belongs to an approved inference service.

Python Requests follows HTTP redirects by default. Consequently, validating only the initially configured hostname outside this function would still be insufficient unless every redirect destination is revalidated.

The process may run in a cloud environment with access to loopback interfaces, private network services, or infrastructure metadata endpoints that are inaccessible to ordinary external clients. If a targeted internal response contains a JSON object with a records list, those records are subsequently passed to _ingest_inference_records() and uploaded to Superwise.

Exploitation requires influence over the environment setting, its DNS resolution, or the behavior of the configured remote endpoint. This limits direct exploitation by arbitrary unauthenticated users, but the missing network-boundary enforcement creates a dangerous trust assumption.

Attack Path

  1. An attacker or compromised deployme ...[truncated 1575 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require https:// for production inference endpoints.
  2. Maintain an explicit allowlist of approved inference hostnames.
  3. Resolve the destination before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges for both IPv4 and IPv6.
  4. Defend against DNS rebinding by validating the actual connection destination, not only an earlier DNS lookup.
  5. Disable redirects with allow_redirects=False, or revalidate every redirect destination before following it.
  6. Restrict destination ports to the ports required by approved inference services.
  7. Reject URLs containing embedded user credentials.
  8. Apply outbound firewall or platform egress rules so the service cannot access metadata and internal administrative networks.
  9. Enforce strict response size and JSON schema limits before loading and processing records.
  10. Consider configuring the inference service by an administrator-controlled identifier rather than accepting a general-purpose URL.

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unpinned Runtime Dependencies Create a Supply-Chain Execution Risk

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-7
Vulnerability Type: Unconstrained third-party dependency installation
Risk Level: Medium

Vulnerable Code

text
# Core skill dependencies
superwise-api
requests
pandas
python-dotenv
flask
apscheduler

The deployment executes these unconstrained installations through render.yaml:5:

yaml
buildCommand: pip install -r requirements.txt

Technical Analysis

None of the runtime dependencies is pinned to an exact reviewed version, and no package hashes are provided. Each installation or deployment can therefore resolve to a different set of direct and transitive dependency versions.

Python package installation may execute package build backends or installation-related code. A malicious, compromised, or unexpectedly changed dependency can consequently execute during the build process. Runtime dependencies also operate inside a service that has access to Superwise credentials, Telegram credentials, dataset identifiers, model records, and outbound network connectivity.

This finding does not establish that any currently named package is malicious. The vulnerability is the absence of reproducible, integrity-verified dependency resolution, which leaves future deployments exposed to package compromise and incompatible updates.

Attack Path

  1. A direct or transitive dependency account or distribution channel is compromised, or a malicious new release is published.
  2. The application is rebuilt on Render or dependencies are reinstalled according to SKILL.md.
  3. pip resolves the unconstrained dependency name to the newly published version.
  4. Malicious build or runtime code executes within the deployment environment.
  5. The compromised package accesses environment secrets, model data, or network resources available to the service.
  6. The package can exfiltrate those assets or alter drift-detection behavior.

I

...[truncated 663 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin every direct dependency to an exact reviewed version.
  2. Generate a complete lock file that includes transitive dependencies.
  3. Use hash verification, for example with pip-compile --generate-hashes and pip install --require-hashes.
  4. Build packages from a trusted, explicitly configured package index.
  5. Separate production dependencies from optional example dependencies.
  6. Run automated vulnerability and package-provenance scanning in CI.
  7. Review dependency updates before merging rather than resolving newest versions during deployment.
  8. Use a restricted build environment that does not expose production secrets.
  9. Run the deployed service as a non-privileged user and minimize environment variables available to the process.
  10. Rebuild from a reviewed lock file on a regular patch schedule.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (74)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Confidence
85% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · skill.py (reported line 77)May include surrounding context.

python
��──────────────────────────────────

def _fetch_inference_records() -> list[dict]:
    """
    Fetch new inference records from the model's prediction endpoint.

    The endpoint should return JSON: {"records": [{col: val, ...}, ...]}
    Adapt the response parsing below if your endpoint uses a different shape.
    """
    url = os.getenv("INFERENCE_ENDPOINT_URL")
    if not url:
        raise RuntimeError("INFERENCE_ENDPOINT_URL is not set in .env")

    try:
        resp = requests.get(url, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        records = data.get("records", [])
        print(f"[skill] Fetched {len(records)} inference records from {url}")
        return records
    except requests.RequestException as exc:
        raise RuntimeError(f"[skill] Failed to fetch inference records: {exc}") from exc


# ── Inference ingestion ──────────────────

Tainted flow: 'url' from os.getenv (line 77, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · skill.py (reported line 82)May include surrounding context.

python
raise RuntimeError("INFERENCE_ENDPOINT_URL is not set in .env")

    try:
        resp = requests.get(url, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        records = data.get("records", [])

Tainted flow: 'headers' from os.getenv (line 262, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · skill.py (reported line 273)May include surrounding context.

python
if auto_trigger:
            try:
                resp = requests.post(
                    f"{_POLICIES_BASE}/{policy_id}/trigger", headers=headers, timeout=15
                )
                if resp.status_code in (200, 202, 204):

Tainted flow: 'headers' from os.getenv (line 262, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · skill.py (reported line 287)May include surrounding context.

python
policy: dict = {}
        for attempt in range(1, _POLL_MAX + 1):
            try:
                resp = requests.get(
                    f"{_POLICIES_BASE}/{policy_id}", headers=headers, timeout=15
                )
                resp.raise_for_status()

Tainted flow: 'token' from os.getenv (line 334, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · skill.py (reported line 342)May include surrounding context.

python
return False

    try:
        resp = requests.post(
            f"https://api.telegram.org/bot{token}/sendMessage",
            json={"chat_id": chat_id, "text": message, "parse_mode": "HTML"},
            timeout=10,

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This finding is partially substantiated by the markdown: the skill claims to use OpenClaw's existing Telegram connection but explicitly states that _send_telegram() consumes TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID directly from environment variables. That discrepancy matters because it broadens secret handling requirements and may cause the skill to access or require direct messaging credentials beyond what a user expects from a platform-integrated alert path.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This file implements ETF forecasting and external financial-data retrieval, which is unrelated to the declared drift-detection skill. Capability mismatch is dangerous because it expands the skill's effective behavior beyond user expectations and review scope, creating hidden attack surface and making it easier to smuggle unauthorized functions into a seemingly benign monitoring integration.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code performs stock-market forecasting and reaches out to yfinance for market data, a capability not justified by the skill's stated role of ML feature-drift detection. In this context, unrelated external data access increases supply-chain and exfiltration surface, and unauthorized forecasting logic could be abused to trigger external calls or operational actions outside the approved use case.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup_dataset.py (reported line 45)May include surrounding context.

python
def _require_env(key: str) -> str:
    val = os.getenv(key)
    if not val:
        print(f"ERROR: {key} is not set. Add it to your .env file.")
        sys.exit(1)
    return val

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup_dataset.py (reported line 193)May include surrounding context.

python
def _require_env(key: str) -> str:
    val = os.getenv(key)
    if not val:
        print(f"ERROR: {key} is not set. Add it to your .env file.")
        sys.exit(1)
    return val

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup_dataset.py (reported line 208)May include surrounding context.

python
def _require_env(key: str) -> str:
    val = os.getenv(key)
    if not val:
        print(f"ERROR: {key} is not set. Add it to your .env file.")
        sys.exit(1)
    return val

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill.py (reported line 79)May include surrounding context.

python
"""
    url = os.getenv("INFERENCE_ENDPOINT_URL")
    if not url:
        raise RuntimeError("INFERENCE_ENDPOINT_URL is not set in .env")

    try:
        resp = requests.get(url, timeout=30)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code transmits full inference records to Superwise, a third-party service, without any built-in minimization, redaction, or explicit warning about external data sharing. If records contain PII, secrets, customer data, or model-sensitive features, the integration can create serious data leakage and regulatory exposure.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · skill.py (reported line 222)May include surrounding context.

python
if primary:
        policies.append(("primary", primary))

    for key, val in sorted(os.environ.items()):
        if key.startswith("SUPERWISE_DRIFT_POLICY_ID_") and val:
            label = key[len("SUPERWISE_DRIFT_POLICY_ID_"):].lower()
            policies.append((label, val))

Static analysis

No suspicious patterns detected.