T09 · Insecure Skill Coding Practices
- Location
spotcast_workflow.py:40- Finding
Bearer credential disclosure through an unrestricted API base URL override
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
spotcast_workflow.py:40Bearer credential disclosure through an unrestricted API base URL override
spotcast_workflow.py:18Unpinned third-party dependency installation
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
start = time.time()
while time.time() - start < MAX_POLL_TIME:
resp = requests.get(url, headers=get_headers())
resp.raise_for_status()
data = resp.json()
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ── 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")
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ── 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()
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ── 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()
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ── 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},
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ── 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(),
)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ── 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()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
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
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
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
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
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
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
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:
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.
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.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
Example request:
curl -X POST https://api.sealegs.ai/v3/spotcast \
-H "Authorization: Bearer $SEALEGS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
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.
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.
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.
curl -X POST https://api.sealegs.ai/v3/spotcast \
-H "Authorization: Bearer $SEALEGS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
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.
curl -X POST https://api.sealegs.ai/v3/spotcast \
-H "Authorization: Bearer $SEALEGS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
_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
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
_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
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
_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
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
_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
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
_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
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
_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
No suspicious patterns detected.