Back to skill

Security audit

SeaLegs AI Marine Forecast API

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a normal SeaLegs marine forecast API skill, but its included Python workflow can send the API key to an arbitrary API base URL from the environment.

Review before installing or running the Python workflow. Use the skill only with the legitimate SeaLegs endpoint, do not set SEALEGS_API_BASE_URL unless you fully trust the destination, keep SEALEGS_API_KEY out of shared .env files, and avoid putting sensitive personal details in forecast metadata or webhook-bound fields.

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

Error
Location
spotcast_workflow.py:40
Finding

Bearer credential disclosure through an unrestricted API base URL override

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
spotcast_workflow.py:18
Finding

Unpinned third-party dependency installation

Content
View full analysis
Remediation
View remediation
--hash=sha256: ``` Use `pip-compile --generate-hashes` or an equivalent dependency-locking tool to capture transitive dependencies. 3. Install with hash enforcement: ```bash python -m pip install --require-hashes -r requirements.txt ``` 4. Explicitly use the intended trusted package index and prevent unexpected fallback indexes where organizational policy permits: ```bash python -m pip install \ --index-url https://pypi.org/simple \ --require-hashes \ -r requirements.txt ``` 5. Run dependency vulnerability and provenance checks in CI, and update pinned versions through a controlled review process. 6. Install dependencies in an isolated virtual environment as a non-privileged user rather than using administrative or system-wide installation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (53)

Tainted flow: 'url' from os.environ.get (line 66, 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 · spotcast_workflow.py (reported line 70)May include surrounding context.

python
start = time.time()

    while time.time() - start < MAX_POLL_TIME:
        resp = requests.get(url, headers=get_headers())
        resp.raise_for_status()
        data = resp.json()

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 104)May include surrounding context.

python
# ── 1. Check account balance ──────────────────────────────────────────
    print("1. Checking account balance...")
    resp = requests.get(f"{BASE_URL}/account/balance", headers=get_headers())
    resp.raise_for_status()
    balance = resp.json()
    print(f"   Credits: {balance['credit_balance']} available")

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 118)May include surrounding context.

python
print("\n2. Creating SpotCast for Miami...")
    tomorrow = (datetime.now(timezone.utc) + timedelta(days=1)).strftime("%Y-%m-%dT00:00:00Z")

    resp = requests.post(
        f"{BASE_URL}/spotcast",
        headers=get_headers(),
        json={

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 152)May include surrounding context.

python
# ── 4. Get the full SpotCast ──────────────────────────────────────────
    print("\n4. Retrieving SpotCast details...")
    resp = requests.get(f"{BASE_URL}/spotcast/{spotcast_id}", headers=get_headers())
    resp.raise_for_status()
    sc = resp.json()

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 169)May include surrounding context.

python
# ── 5. Refresh the SpotCast ───────────────────────────────────────────
    print("\n5. Refreshing with latest weather data...")
    resp = requests.post(
        f"{BASE_URL}/spotcast/{spotcast_id}/refresh", headers=get_headers()
    )
    resp.raise_for_status()

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 183)May include surrounding context.

python
# ── 7. List forecast history ──────────────────────────────────────────
    print("\n7. Listing forecast history...")
    resp = requests.get(
        f"{BASE_URL}/spotcast/{spotcast_id}/forecasts",
        headers=get_headers(),
        params={"limit": 5},

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 196)May include surrounding context.

python
# ── 8. Get a specific forecast ────────────────────────────────────────
    print(f"\n8. Getting original forecast {forecast_id}...")
    resp = requests.get(
        f"{BASE_URL}/spotcast/{spotcast_id}/forecast/{forecast_id}",
        headers=get_headers(),
    )

Tainted flow: 'BASE_URL' from os.environ.get (line 50, 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 · spotcast_workflow.py (reported line 207)May include surrounding context.

python
# ── 9. List all SpotCasts ─────────────────────────────────────────────
    print("\n9. Listing all SpotCasts...")
    resp = requests.get(
        f"{BASE_URL}/spotcasts", headers=get_headers(), params={"limit": 5}
    )
    resp.raise_for_status()

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 · spotcast_workflow.py (reported line 25)May include surrounding context.

python
export SEALEGS_API_KEY=sk_live_your_key_here
  python examples/spotcast_workflow.py

  # Option 2: Create examples/.env file (gitignored)
  echo "SEALEGS_API_KEY=sk_live_your_key_here" > examples/.env
  python examples/spotcast_workflow.py

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 · spotcast_workflow.py (reported line 26)May include surrounding context.

python
export SEALEGS_API_KEY=sk_live_your_key_here
  python examples/spotcast_workflow.py

  # Option 2: Create examples/.env file (gitignored)
  echo "SEALEGS_API_KEY=sk_live_your_key_here" > examples/.env
  python examples/spotcast_workflow.py

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 · spotcast_workflow.py (reported line 40)May include surrounding context.

python
export SEALEGS_API_KEY=sk_live_your_key_here
  python examples/spotcast_workflow.py

  # Option 2: Create examples/.env file (gitignored)
  echo "SEALEGS_API_KEY=sk_live_your_key_here" > examples/.env
  python examples/spotcast_workflow.py

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 · spotcast_workflow.py (reported line 41)May include surrounding context.

python
import requests

# Load .env file from the examples directory if it exists
_env_file = Path(__file__).parent / ".env"
if _env_file.exists():
    with open(_env_file) as _f:
        for _line in _f:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill requires environment access and network egress to an external API, but it does not declare an explicit tool scope such as permissions or allowed-tools. That weakens least-privilege controls and transparency for reviewers and users, making it easier for a host agent to grant broader capabilities than necessary.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation describes sending forecast requests with latitude, longitude, optional vessel details, webhook URLs, and metadata to a third-party service, but it does not warn users that precise location and trip-related data leave the local environment. This creates a privacy and consent risk because users may unknowingly transmit sensitive geolocation or itinerary information externally.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 180)May include surrounding context.

Example request:

bash
curl -X POST https://api.sealegs.ai/v3/spotcast \
  -H "Authorization: Bearer $SEALEGS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The webhook section states that metadata is echoed back exactly as sent, but it does not warn that user-supplied metadata may be forwarded to external endpoints. If sensitive values are placed in metadata, they can be disclosed to third-party webhook receivers or logs, expanding the data exposure surface.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The workflow instructs users to submit precise location and vessel details to an external API, but it does not disclose that this data leaves the local environment and is processed by a third-party service. This can lead to uninformed sharing of potentially sensitive trip-planning information and creates privacy/compliance risk rather than a direct exploit path.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

This step sends latitude, longitude, vessel type, vessel length, and location metadata to an external endpoint using an API key. In a marine forecast skill this transmission is functionally expected, but it still represents real outbound data flow and becomes risky if users are not clearly informed or if sensitive trip details are included.

Content

Scanner excerpt · spotcast-workflow.md (reported line 12)May include surrounding context.

Step 1: Create the SpotCast

bash
curl -X POST https://api.sealegs.ai/v3/spotcast \
  -H "Authorization: Bearer $SEALEGS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

This step sends latitude, longitude, vessel type, vessel length, and location metadata to an external endpoint using an API key. In a marine forecast skill this transmission is functionally expected, but it still represents real outbound data flow and becomes risky if users are not clearly informed or if sensitive trip details are included.

Content

Scanner excerpt · spotcast-workflow.md (reported line 12)May include surrounding context.

Step 1: Create the SpotCast

bash
curl -X POST https://api.sealegs.ai/v3/spotcast \
  -H "Authorization: Bearer $SEALEGS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 158)May include surrounding context.

md
_key, _, _value = _line.partition("=")
                os.environ.setdefault(_key.strip(), _value.strip())

BASE_URL = os.environ.get("SEALEGS_API_BASE_URL", "https://api.sealegs.ai/v3").rstrip("/")
API_KEY = os.environ.get("SEALEGS_API_KEY")

POLL_INTERVAL = 10  # seconds

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 180)May include surrounding context.

md
_key, _, _value = _line.partition("=")
                os.environ.setdefault(_key.strip(), _value.strip())

BASE_URL = os.environ.get("SEALEGS_API_BASE_URL", "https://api.sealegs.ai/v3").rstrip("/")
API_KEY = os.environ.get("SEALEGS_API_KEY")

POLL_INTERVAL = 10  # seconds

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 203)May include surrounding context.

md
_key, _, _value = _line.partition("=")
                os.environ.setdefault(_key.strip(), _value.strip())

BASE_URL = os.environ.get("SEALEGS_API_BASE_URL", "https://api.sealegs.ai/v3").rstrip("/")
API_KEY = os.environ.get("SEALEGS_API_KEY")

POLL_INTERVAL = 10  # seconds

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 204)May include surrounding context.

md
_key, _, _value = _line.partition("=")
                os.environ.setdefault(_key.strip(), _value.strip())

BASE_URL = os.environ.get("SEALEGS_API_BASE_URL", "https://api.sealegs.ai/v3").rstrip("/")
API_KEY = os.environ.get("SEALEGS_API_KEY")

POLL_INTERVAL = 10  # seconds

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 205)May include surrounding context.

md
_key, _, _value = _line.partition("=")
                os.environ.setdefault(_key.strip(), _value.strip())

BASE_URL = os.environ.get("SEALEGS_API_BASE_URL", "https://api.sealegs.ai/v3").rstrip("/")
API_KEY = os.environ.get("SEALEGS_API_KEY")

POLL_INTERVAL = 10  # seconds

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 212)May include surrounding context.

md
_key, _, _value = _line.partition("=")
                os.environ.setdefault(_key.strip(), _value.strip())

BASE_URL = os.environ.get("SEALEGS_API_BASE_URL", "https://api.sealegs.ai/v3").rstrip("/")
API_KEY = os.environ.get("SEALEGS_API_KEY")

POLL_INTERVAL = 10  # seconds

Static analysis

No suspicious patterns detected.