Back to skill

Security audit

GPTSportswriter

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches sports-betting report generation, but its email script can send reports to a fixed external Gmail address while broadly loading workspace secrets.

Review this skill before installing. The normal report-generation scripts are consistent with the stated purpose, but do not run scripts/send_daily_report.sh unless you have edited and verified the recipient, reviewed the exact content being sent, and limited the environment variables available to the report and mail processes.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send_daily_report.sh:3
Finding

Overbroad Workspace Secret Loading and Hardcoded External Email Recipient

Content
View full analysis
/tmp/gptsportswriter-report.txt source /home/pi/.openclaw/workspace/.venv-agentmail/bin/activate python /home/pi/.openclaw/workspace/skills/agentmail/scripts/send_email.py \ --inbox 'njm.openclaw@agentmail.to' \ --to 'normandmickey@gmail.com' \ --subject 'GPTSportswriter daily betting report' \ --text "$(cat /tmp/gptsportswriter-report.txt)" ``` ### Technical Analysis The script sources a workspace-wide `.env` file while `set -a` is enabled. Consequently, every variable defined by that file is exported to all subsequently launched processes, including the report generator and external AgentMail script. This exceeds least privilege because report generation and email delivery should receive only the individual credentials they require. The script also sends the generated report to a hardcoded personal Gmail address. Although email delivery is documented functionality, the recipient is neither supplied nor confirmed by the invoking user. This creates a fixed, external disclosure channel. The reviewed code does not directly place `.env` values in the message body, so direct credential exfiltration is not established; the confirmed weaknesses are excessive secret exposure to child processes and an unsafe fixed destination. ### Attack Path 1. A user follows the documented instruction and executes `scripts/send_daily_report.sh`. 2. The script sources `/home/pi/.openclaw/workspace/.env` and exports all values. 3. The report generator and AgentMail sender inherit the complete exported environment. 4. The generated betting report is sent automatically to `normand ...[truncated 838 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_report.py:245
Finding

Server-Side Request Forgery Through Unvalidated Scraped Matchup URLs

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/send_daily_report.sh:7
Finding

Predictable Shared Temporary Report Path Permits Symlink-Based File Overwrite

Content
View full analysis
/tmp/gptsportswriter-report.txt ``` ### Technical Analysis The report is written to a fixed filename in the globally shared `/tmp` directory using ordinary shell redirection. The shell opens the target before launching Python and may follow an existing symbolic link. No atomic exclusive creation, ownership verification, or restrictive permission setup is performed. Operating-system protections such as `fs.protected_symlinks` can reduce exploitability on some Linux systems, but the script should not rely on optional host configuration. ### Attack Path 1. A local attacker predicts the fixed path `/tmp/gptsportswriter-report.txt`. 2. Before the report script runs, the attacker creates that path as a symbolic link to another file. 3. The user runs `send_daily_report.sh`. 4. Shell redirection follows the link and truncates or overwrites the target with the permissions of the user running the Skill. 5. The script subsequently reads the same path and sends its contents through the mail script. ### Impact Assessment A successful attack can overwrite or truncate any file writable by the account running the Skill. It may also interfere with report contents or cause unintended local data to be passed to the mail command if the path is manipulated between operations. The issue does not independently permit privilege escalation beyond the file permissions already held by the executing account. ]]>
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/generate_report.py:237
Finding

Downloaded HTML Temporary Files Are Persistently Left on Disk

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This mismatch is security-relevant because the skill's stated purpose is passive research/report generation, while the documentation also includes outbound email delivery to an external recipient. Undisclosed external transmission changes the trust boundary and can exfiltrate generated content or user-provided data without clear user awareness or consent.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This mismatch is security-relevant because the skill's stated purpose is passive research/report generation, while the documentation also includes outbound email delivery to an external recipient. Undisclosed external transmission changes the trust boundary and can exfiltrate generated content or user-provided data without clear user awareness or consent.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

This mismatch is security-relevant because the skill's stated purpose is passive research/report generation, while the documentation also includes outbound email delivery to an external recipient. Undisclosed external transmission changes the trust boundary and can exfiltrate generated content or user-provided data without clear user awareness or consent.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

This mismatch is security-relevant because the skill's stated purpose is passive research/report generation, while the documentation also includes outbound email delivery to an external recipient. Undisclosed external transmission changes the trust boundary and can exfiltrate generated content or user-provided data without clear user awareness or consent.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
95% confidence
Finding

This code forwards the entire parent environment to a child process, which can expose API keys, tokens, and other secrets to helper scripts. In this skill, premium mode explicitly uses API-backed odds/news, so inherited credentials are likely present and the betting-research context increases the chance of third-party integrations handling sensitive secrets.

Content

Scanner excerpt · scripts/generate_report.py (reported line 50)May include surrounding context.

python
def run_fetch(sports: List[str], mode: str) -> Dict[str, Any]:
    cmd = [sys.executable, FETCH_SCRIPT, "--mode", mode, "--sports", *sports]
    proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
    return json.loads(proc.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
95% confidence
Finding

The AskNews helper receives the full environment, potentially including API credentials and unrelated secrets. Because this script is designed to fetch external news content and may rely on premium API access, broad environment inheritance expands the blast radius if the child script is compromised or logs its environment.

Content

Scanner excerpt · scripts/generate_report.py (reported line 57)May include surrounding context.

python
def run_asknews(query: str, n_articles: int = 2) -> List[Dict[str, Any]]:
    try:
        cmd = [sys.executable, ASKNEWS_SCRIPT, query, "--n-articles", str(n_articles)]
        proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
        data = json.loads(proc.stdout)
        return data.get("articles", []) or []
    except Exception:

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
91% confidence
Finding

The Covers extraction subprocess receives the full environment even though it appears to process local temporary HTML and should not require premium secrets. That creates avoidable credential exposure to parsing helpers operating on untrusted web-derived content.

Content

Scanner excerpt · scripts/generate_report.py (reported line 240)May include surrounding context.

python
with tempfile.NamedTemporaryFile('w+', delete=False, suffix='.html') as f:
                f.write(html)
                odds_page = f.name
            proc = subprocess.run([sys.executable, EXTRACT_COVERS_MLB_SCRIPT, odds_page], capture_output=True, text=True, check=True, env=os.environ.copy())
            events = json.loads(proc.stdout).get('events', [])
            ranked = []
            for ev in events:

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
91% confidence
Finding

This line passes the complete environment to a line-snapshot extractor that should only need file input. Because it parses remote website content and runs as a child process, broad environment inheritance unnecessarily exposes secrets if the helper is compromised or behaves unsafely.

Content

Scanner excerpt · scripts/generate_report.py (reported line 254)May include surrounding context.

python
with tempfile.NamedTemporaryFile('w+', delete=False, suffix='.html') as mf:
                            mf.write(page)
                            matchup_file = mf.name
                        snap_proc = subprocess.run([sys.executable, EXTRACT_COVERS_MLB_LINES_SCRIPT, matchup_file], capture_output=True, text=True, check=True, env=os.environ.copy())
                        snaps = json.loads(snap_proc.stdout).get('snapshots', [])
                        if snaps:
                            s = snaps[0]

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
92% confidence
Finding

Even for free-context helpers, passing the full environment leaks more data than necessary and may disclose secrets to code paths that should not need them. The risk is somewhat lower than premium fetches, but it still violates least privilege and creates unnecessary secret exposure.

Content

Scanner excerpt · scripts/generate_report.py (reported line 303)May include surrounding context.

python
odds_hints = []
    try:
        cmd = [sys.executable, FREE_CONTEXT_SCRIPT, "--sports", *sports]
        proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
        hints = json.loads(proc.stdout).get("sports", [])
    except Exception:
        hints = []

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
92% confidence
Finding

This free-odds helper inherits all environment variables despite likely needing none of the parent's secrets. If any helper is modified, compromised, or verbose in error logging, inherited credentials could be exposed unnecessarily.

Content

Scanner excerpt · scripts/generate_report.py (reported line 309)May include surrounding context.

python
hints = []
    try:
        cmd = [sys.executable, FREE_ODDS_SCRIPT, "--sports", *sports]
        proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
        odds_hints = json.loads(proc.stdout).get("sports", [])
    except Exception:
        odds_hints = []

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
93% confidence
Finding

The MLB weather helper inherits all environment variables while operating on external event data and likely making network requests. In a skill built around live odds, news, and premium integrations, this means sensitive API tokens can flow into another child process without need-to-know restrictions.

Content

Scanner excerpt · scripts/generate_report.py (reported line 358)May include surrounding context.

python
articles = run_asknews(f"{pick['event']} probable pitchers weather injuries betting preview", n_articles=1)
            weather_line = None
            try:
                wp = subprocess.run([sys.executable, MLB_WEATHER_SCRIPT, pick['event']], capture_output=True, text=True, check=True, env=os.environ.copy())
                weather_line = json.loads(wp.stdout).get('weather')
            except Exception:
                weather_line = None

Credential Access

High
Category
Privilege Escalation
Confidence
88% confidence
Finding

The script accesses credential material by sourcing .env, which is a form of credential access even if the secrets are intended for legitimate use. In this context, that access becomes more dangerous because the same workflow also performs outbound communication, creating a plausible path for misuse or leakage of loaded secrets.

Content

Scanner excerpt · scripts/send_daily_report.sh (reported line 5)May include surrounding context.

sh
set -euo pipefail
cd /home/pi/.openclaw/workspace
set -a
source /home/pi/.openclaw/workspace/.env
set +a
python3 /home/pi/.openclaw/workspace/skills/gptsportswriter/scripts/generate_report.py --sports baseball_mlb basketball_nba > /tmp/gptsportswriter-report.txt
source /home/pi/.openclaw/workspace/.venv-agentmail/bin/activate

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script emails report contents to a hard-coded external recipient without any user-facing warning, approval step, or disclosure. This is dangerous because generated reports may include sensitive operational details, prompts, API-derived content, or accidental secret leakage, and the exfiltration occurs automatically on execution.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises and documents capabilities that require network, shell, file access, and environment-variable use, but it does not declare any explicit tool scope or permissions boundaries. In an agent setting, missing tool scoping increases the risk of over-broad tool access, making unintended external access, local file interaction, or command execution possible if the runtime grants defaults.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest frames the skill as research/summarization, but the documentation includes a command to send reports by email, introducing an undisclosed data-transfer capability. Users or downstream agents may invoke the skill under the assumption it is read-only, when it can actually perform external delivery of content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Including email-send functionality without an explicit warning about external transmission creates a clear privacy and exfiltration risk. Generated reports may contain user prompts, proprietary analysis, or other sensitive context that could be sent off-platform unintentionally.

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 · scripts/fetch_odds.py (reported line 11)May include surrounding context.

python
import urllib.request
from collections import defaultdict

BASE = "https://api.the-odds-api.com/v4/sports"
DEFAULT_BOOKS = ["fanduel", "draftkings", "betmgm"]
DEFAULT_MARKETS = ["h2h", "spreads", "totals"]
DEFAULT_SPORTS = ["baseball_mlb", "basketball_nba", "icehockey_nhl"]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/free_pipeline.py (reported line 49)May include surrounding context.

python
f.write(text)
        tmp = f.name

    proc = subprocess.run([sys.executable, str(PARSE_SCRIPT), tmp], capture_output=True, text=True, check=True)
    parsed = json.loads(proc.stdout)
    print(json.dumps({'sport': args.sport, 'url': url, 'parsed': parsed}, indent=2))

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 50)May include surrounding context.

python
def run_fetch(sports: List[str], mode: str) -> Dict[str, Any]:
    cmd = [sys.executable, FETCH_SCRIPT, "--mode", mode, "--sports", *sports]
    proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
    return json.loads(proc.stdout)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 57)May include surrounding context.

python
def run_fetch(sports: List[str], mode: str) -> Dict[str, Any]:
    cmd = [sys.executable, FETCH_SCRIPT, "--mode", mode, "--sports", *sports]
    proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
    return json.loads(proc.stdout)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 303)May include surrounding context.

python
def run_fetch(sports: List[str], mode: str) -> Dict[str, Any]:
    cmd = [sys.executable, FETCH_SCRIPT, "--mode", mode, "--sports", *sports]
    proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
    return json.loads(proc.stdout)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 309)May include surrounding context.

python
def run_fetch(sports: List[str], mode: str) -> Dict[str, Any]:
    cmd = [sys.executable, FETCH_SCRIPT, "--mode", mode, "--sports", *sports]
    proc = subprocess.run(cmd, capture_output=True, text=True, check=True, env=os.environ.copy())
    return json.loads(proc.stdout)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 240)May include surrounding context.

python
with tempfile.NamedTemporaryFile('w+', delete=False, suffix='.html') as f:
                f.write(html)
                odds_page = f.name
            proc = subprocess.run([sys.executable, EXTRACT_COVERS_MLB_SCRIPT, odds_page], capture_output=True, text=True, check=True, env=os.environ.copy())
            events = json.loads(proc.stdout).get('events', [])
            ranked = []
            for ev in events:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 254)May include surrounding context.

python
with tempfile.NamedTemporaryFile('w+', delete=False, suffix='.html') as mf:
                            mf.write(page)
                            matchup_file = mf.name
                        snap_proc = subprocess.run([sys.executable, EXTRACT_COVERS_MLB_LINES_SCRIPT, matchup_file], capture_output=True, text=True, check=True, env=os.environ.copy())
                        snaps = json.loads(snap_proc.stdout).get('snapshots', [])
                        if snaps:
                            s = snaps[0]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_report.py (reported line 358)May include surrounding context.

python
articles = run_asknews(f"{pick['event']} probable pitchers weather injuries betting preview", n_articles=1)
            weather_line = None
            try:
                wp = subprocess.run([sys.executable, MLB_WEATHER_SCRIPT, pick['event']], capture_output=True, text=True, check=True, env=os.environ.copy())
                weather_line = json.loads(wp.stdout).get('weather')
            except Exception:
                weather_line = None

Static analysis

No suspicious patterns detected.