Back to skill

Security audit

Polymarket Sports Arbitrage

Security checks for vulnerabilities and agentic risk

Overview

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.

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
sports_arbitrage.py:203
Finding

API Key May Be Disclosed Through Request Exception Logging

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
clawhub.json:4
Finding

Unpinned Third-Party Runtime Dependency

Content
View full analysis
Remediation
View remediation
" ], "env": [] } ``` 2. Prefer a platform-supported lockfile that records the full transitive dependency graph rather than pinning only the direct dependency. 3. Where supported, require cryptographic hashes for downloaded wheels and reject artifacts whose hashes do not match the reviewed lock data. 4. Use a trusted package index over TLS and prevent untrusted project-level configuration from replacing or supplementing the package source. 5. Run automated dependency vulnerability and provenance checks when updating the lockfile. 6. Review and test dependency updates before deployment instead of automatically resolving the latest available release during installation. 7. Execute the Skill with restricted filesystem and network permissions so that compromise of any dependency has limited impact. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Tainted flow: 'params' from os.environ.get (line 205, 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 · sports_arbitrage.py (reported line 212)May include surrounding context.

python
"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 []

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

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.

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

python
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"

Tainted flow: 'RESULTS_FILE' from os.environ.get (line 36, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
87% confidence
Finding

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.

Content

Scanner excerpt · sports_arbitrage.py (reported line 417)May include surrounding context.

python
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).",

Tainted flow: 'RESULTS_FILE' from os.environ.get (line 36, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
85% confidence
Finding

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.

Content

Scanner excerpt · sports_arbitrage.py (reported line 475)May include surrounding context.

python
# 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

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.