Back to skill

Security audit

ESPN Fantasy Co-Manager

Security checks for vulnerabilities and agentic risk

Overview

This skill is not clearly malicious, but it gives an agent durable ESPN account access and live roster-changing authority with weak execution gates.

Install only if you are comfortable giving the agent durable access to your ESPN fantasy account. Before use, require explicit confirmation for every roster, trade, and waiver action; disable or avoid the systemd timer unless you want background monitoring; protect or delete stored cookies and browser profiles; and remove the 2FA-code logging/no-auth VNC pattern before entering real credentials.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/tx.mjs:38
Finding

Add/drop transactions can execute without enforced user approval

Content
View full analysis

Vulnerability Details

File Location: scripts/tx.mjs:38-47
Vulnerability Type: Missing authorization gate for consequential roster transactions
Risk Level: Medium

Complete Code Snippet

js
// Add a player, drop another (pair them — a bare ADD fails if the roster is full).
export async function addDrop({ season, leagueId, teamId, addId, dropId, week }) {
  const items = [{ playerId: addId, type: 'ADD', toTeamId: teamId }];
  if (dropId) items.push({ playerId: dropId, type: 'DROP', fromTeamId: teamId });
  return post(txUrl(season, leagueId), {
    isLeagueManager: false, teamId, type: 'FREEAGENT', scoringPeriodId: week, executionType: 'EXECUTE', items,
  }, 'addDrop');
}

Technical Analysis

The Skill's documented authorization model requires add/drop actions to be proposed and approved by the user. Specifically, references/strategy.md:24-38 states that waiver adds and drops follow a “propose → approve” workflow.

The exported addDrop function does not technically enforce that policy. When called with valid transaction identifiers, it constructs a FREEAGENT transaction with executionType: 'EXECUTE' and immediately submits it through the authenticated ESPN API path. It has no approval token, confirmation parameter, interactive prompt, or dry-run default.

The caller controls season, leagueId, teamId, addId, dropId, and week. Therefore, an Agent invocation caused by erroneous reasoning, manipulated contextual input, or another direct module caller can bypass the documented human-approval boundary. The stored ESPN cookies provide the authority needed to apply the transaction.

This is a reachable authorization-control weakness, but the reviewed project does not provide evidence that its author intended to abuse it.

Attack Path

  1. The user authenticates through the Skill, producing reusable ESPN session cookies.
  2. An Agent or another local caller imports addDrop from scripts/tx.mjs.
  3. The caller supplie ...[truncated 876 chars]
Remediation
View remediation

Remediation Suggestions

  1. Make add/drop operations dry-run-only by default and return the complete proposed transaction without sending it.
  2. Require an explicit execution flag and a validated approval artifact tied to the exact league, team, week, add-player ID, and drop-player ID.
  3. Generate the approval artifact only after displaying the full transaction to the user and receiving affirmative confirmation.
  4. Reject stale, reused, or mismatched approvals.
  5. Keep the approval check inside the executable helper so direct module calls cannot bypass it.
  6. Re-read the roster immediately before execution and verify that the target team, player IDs, and roster state still match the approved proposal.
  7. Record the approval, finalized request, response status, and transaction identifier in an audit log without recording authentication cookies.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (31)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 64)May include surrounding context.

md
`references/espn-fantasy-api.md`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 106)May include surrounding context.

md
`references/espn-fantasy-api.md`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 77)May include surrounding context.

md
Never poll the ESPN API from the model (token burn + bot risk). Instead run `scripts/monitor.mjs`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 102)May include surrounding context.

md
Never poll the ESPN API from the model (token burn + bot risk). Instead run `scripts/monitor.mjs`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 98)May include surrounding context.

md
- `scripts/login-interactive.mjs` — full flow: iframe fill, 2FA wait, rate-limit backoff,

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The 'Quick Lineup' endpoint is especially risky because it does not merely recommend a lineup; the text explicitly notes it computes and applies the optimal lineup server-side. Without an up-front warning that invoking this endpoint immediately changes the user's active roster configuration, an agent could trigger irreversible competitive consequences without informed consent.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

The README explicitly endorses autonomous execution of lineup changes without per-action human approval. While lineup changes are framed as 'reversible,' they still cause real account actions and can create unwanted or mistimed transactions if the agent acts on stale data, bad projections, or the wrong league context. In this skill, the danger is elevated because it operates on a real authenticated ESPN account with durable session cookies and write helpers.

Content

Scanner excerpt · README.md (reported line 34)May include surrounding context.

md
## Design principles

- **Win-now** — best possible lineup every week.
- **Auto-execute the reversible** (lineups). Ask before anything irreversible or social.
- **Never auto-accept an incoming trade** — evaluate, recommend, human decides.
- **Don't poll from the model** — run a cheap watcher, surface only new events.
- **Log every move + the reasoning.**

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill clearly instructs use of shell commands, browser automation, local secret files, and persistent session handling, but it declares no explicit tool scope or permissions boundary. That creates an authorization gap where an agent may invoke powerful shell capabilities without an upfront, machine-readable restriction, increasing the chance of unintended command execution or unsafe filesystem/process access.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
81% confidence
Finding

Enabling a systemd user timer creates ongoing persistence beyond the immediate session, causing the skill to continue running periodic monitoring code automatically. Even if intended for convenience, persistence increases attack surface and operational risk because future executions may occur without fresh user awareness, continue consuming stored credentials/cookies, or surface private league activity unexpectedly.

Content

Scanner excerpt · SKILL.md (reported line 88)May include surrounding context.

text
~/.config/systemd/user/espn-monitor.service   # Type=oneshot -> node monitor.mjs
~/.config/systemd/user/espn-monitor.timer     # OnUnitActiveSec=12min
systemctl --user enable --now espn-monitor.timer

Surface alerts cheaply from a heartbeat: if state/espn-alerts.jsonl is non-empty, relay it to the user then truncate; if empty, stay silent.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill exposes write-capable operations such as lineup changes, add/drop actions, and transaction helpers without a clear requirement to warn the user and obtain confirmation immediately before execution. In this context, accidental or ambiguous execution could directly alter the user's fantasy roster, waivers, or trades, causing irreversible account-side changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This documentation provides concrete write operations for lineup and roster changes against a live ESPN account without clearly stating that these calls perform real, user-visible account modifications. In an agent skill context, that omission increases the risk that an agent will execute destructive or unintended actions without explicit user confirmation, especially because the skill is designed to persist authenticated cookies and operate on a real account.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trade proposal instructions enable sending live offers to other teams in a league, which is an outward-facing action affecting other users, but the section lacks a clear warning about that real-world interaction. In this skill context, that is dangerous because an authenticated agent could initiate social or competitive actions on behalf of the user without adequately signaling that the action leaves the user's account and impacts another participant.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

The documented approach creates a persistent headed display session for login automation and reuses a browser profile containing authenticated state. In the context of a skill that logs into ESPN and preserves cookies for reuse, this materially increases the attack surface for session hijacking or unauthorized reuse by local actors or later processes.

Content

Scanner excerpt · references/login-flow.md (reported line 51)May include surrounding context.

md
- **Headed browser needs a display, and there isn't a usable one.** `:0` is root/SDDM and
  `:10` belongs to another user; no Xvfb; no passwordless sudo. Solution: **TigerVNC**
  (`vncserver` + `Xvnc` are installed) to create our own virtual headed display:
  `vncserver :1 -geometry 1366x900 -depth 24 -SecurityTypes None -localhost yes`
  then `DISPLAY=:1 XAUTHORITY=$HOME/.Xauthority`. See `scripts/start-display.sh`.
- Playwright module: install playwright locally (`npm i playwright`) or set `PLAYWRIGHT_PATH`,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The guidance explicitly recommends starting TigerVNC with -SecurityTypes None, which disables VNC authentication entirely. Even with -localhost yes, this weakens protection around a headed browser session handling ESPN credentials, 2FA, and authenticated cookies; any local user or process able to reach the VNC endpoint could observe or interfere with the session.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file documents persisting espn_s2 and SWID into .secrets/espn-cookies.txt, and these values are authenticated session secrets that can be reused to access the user's ESPN account without re-entering credentials. Storing them in a flat file without stronger controls, lifecycle guidance, encryption, or user warning increases the risk of account takeover if the host, backups, logs, or neighboring processes are compromised.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The document frames the skill as 'an AI co-manager, not an autopilot,' implying the human remains in control of substantive actions. But later it explicitly states that setting the optimal lineup is 'auto-execute' by default, which is a direct autonomous management action rather than merely advisory behavior.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
88% confidence
Finding

Automatically executing lineup changes allows the agent to take account-affecting actions without a per-action human check. In a skill that controls a real ESPN fantasy team using persisted authenticated session cookies, mistakes, prompt-induced misjudgments, or manipulated external inputs could cause unwanted roster decisions and reputational or competitive harm to the user.

Content

Scanner excerpt · references/strategy.md (reported line 25)May include surrounding context.

md
## Design principles

- **Win-now.** Objective = field the best possible lineup every week.
- **Auto-execute the reversible stuff** (lineups). Ask before anything irreversible or social
  (adds/drops, trades, anything that touches other managers).
- **Never auto-accept an incoming trade.** Evaluate, then recommend approve/deny with reasoning.
  The human decides.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
90% confidence
Finding

Declaring 'Set optimal lineup | auto-execute' operationalizes autonomous state-changing actions on a live third-party account. Given the skill's ability to authenticate and act through a headed browser and persisted cookies, this increases the chance of unauthorized or erroneous actions if the agent misinterprets instructions, league context, or player availability.

Content

Scanner excerpt · references/strategy.md (reported line 36)May include surrounding context.

md
| Action | Default |
|---|---|
| Set optimal lineup | auto-execute (reversible) |
| Send trade offers | ping human first (unless explicitly broadened) |
| Incoming trade offers | **never auto-accept** — recommend, human decides |
| Waiver adds/drops | propose → approve |

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/strategy.md (reported line 44)May include surrounding context.

md
Broaden these only when the human explicitly says so. Trades are the sharp edge: an ESPN trade
requires the *other* owner to accept — you can only send offers and accept incoming ones, so
"live" trade autonomy means "can send without asking", not "can force".

## Weekly rhythm (the core)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code launches a persistent Playwright browser context rooted at a fixed on-disk profile path, which will retain session state, cookies, and other browser artifacts across runs. In this skill's context, that likely includes ESPN authentication state and sensitive cookies (espn_s2, SWID), so compromise of the host, workspace, or overly broad file permissions could enable account takeover or replay of an authenticated session.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script persists espn_s2 and SWID cookies to disk, which are effectively bearer credentials for the ESPN fantasy session. Although the file is written with restrictive permissions, storing reusable session cookies on disk creates a theft/reuse risk if the workspace, host, backups, or related processes are compromised, and the skill context explicitly aims to retain these cookies for future authenticated API access.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script logs the 2FA code in plaintext via log('CODE_SUBMIT ' + code), and the logger writes to both stdout and a persistent file under workspace state. A 2FA code is sensitive authentication material, and storing it unnecessarily increases the risk of disclosure through log access, backups, or later compromise of the workspace.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The monitor collects and persists league-wide trade proposals and transaction activity, including events not involving the user's team. In the context of a skill intended to manage a specific user's ESPN fantasy team, this exceeds least-privilege behavior and creates unnecessary access to other league members' activity, which can expose private league information and broaden the blast radius if alert files or logs are accessed by other components.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/start-display.sh (reported line 19)May include surrounding context.

sh
echo "display ${DISP} already running"
else
  printf 'espn2fa\n' | vncpasswd -f > "$HOME/.vnc/passwd" 2>/dev/null || true
  chmod 600 "$HOME/.vnc/passwd" 2>/dev/null || true
  vncserver "${DISP}" -geometry 1366x900 -depth 24 -SecurityTypes None -localhost yes >>"$LOG" 2>&1
  sleep 3
fi

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The code reads persistent ESPN session cookies from a local secrets file and automatically sends them in authenticated request headers. While this is expected for the skill’s functionality, it creates a real risk because any invocation of this module can act as the user without an explicit re-consent step, and the broad cookie reuse increases the blast radius if the file or process is misused.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.potential_exfiltration

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/monitor.mjs:16

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/tx.mjs:16