T09 · Insecure Skill Coding Practices
- Location
sports_arbitrage.py:203- Finding
API Key May Be Disclosed Through Request Exception Logging
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This sports-odds scanner is mostly purpose-aligned, but it handles an API key and automated file writes in ways users should review before installing.
Review before installing if you will use a real ODDS_API_KEY. Run it only in an environment where scheduler/stdout logs are private, consider rotating any key exposed in logs, set RESULTS_FILE to a dedicated safe path, and prefer a version that redacts apiKey values and pins dependencies.
sports_arbitrage.py:203API Key May Be Disclosed Through Request Exception Logging
clawhub.json:4Unpinned Third-Party Runtime Dependency
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
"oddsFormat": ODDS_FORMAT,
}
try:
resp = requests.get(url, params=params, timeout=15)
if resp.status_code == 401:
log.warning("ODDS_API_KEY is invalid or expired for sport: %s", sport_key)
return []
The skill appears to require network access, environment variable access, and file write capability, but the manifest does not declare any explicit tool scope or permissions. This creates an avoidable least-privilege gap: a runtime may grant broader capabilities than reviewers expect, making it harder to audit what the skill is allowed to do and increasing the risk of unintended data access or misuse if the implementation changes or is compromised.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
SPORTS = [s.strip() for s in SPORTS_RAW.split(",") if s.strip()]
# The Odds API base URL
ODDS_API_BASE = "https://api.the-odds-api.com/v4"
# Odds format: decimal (European) for easier arb math
ODDS_FORMAT = "decimal"
The script writes JSON output to a path fully controlled by the RESULTS_FILE environment variable without any restriction or path validation. In environments where untrusted users can influence environment variables or execution context, this can overwrite arbitrary files writable by the process, potentially causing data loss or clobbering application state.
combined = combined[-500:]
try:
with open(RESULTS_FILE, "w") as fh:
json.dump(combined, fh, indent=2)
log.info(
"Results saved to %s (%d new, %d total).",
This second write path has the same issue: it creates or overwrites a file at an environment-controlled location when no results file exists. Even though the content is only an empty JSON array, arbitrary file creation in attacker-chosen writable locations can still be abused for file clobbering or operational disruption.
# Still write an empty-run marker so the file always exists
if not os.path.exists(RESULTS_FILE):
try:
with open(RESULTS_FILE, "w") as fh:
json.dump([], fh)
except OSError:
pass
This code performs a file write to RESULTS_FILE, which is safety-relevant under the rule for code files. Although the implementation has comments and success logs, those are internal developer/operator cues rather than an explicit user disclosure that the skill will persist scan results to disk, and there is no confirmation step before writing.
No suspicious patterns detected.