Back to skill

Security audit

Sportsbook Skill

Security checks for vulnerabilities and agentic risk

Overview

This sports betting skill is coherent in purpose, but it handles betting authority, API keys, wallet seed phrases, webhooks, and recurring notification polling in ways that need careful review before installation.

Review this skill carefully before installing. It can transmit and store betting-agent credentials, expose wallet recovery material in agent output, poll a remote service on every skill run, register webhooks, and support automated bet posting. Use only if you trust the service and publisher, understand where credentials are stored, and are comfortable with the financial and account-control implications.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (7)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/register_helper.py:142
Finding
Registration status endpoint is queried without proof of ownership while returning API keys and wallet seed phrases<![CDATA[ ## Vulnerability Details **File Location**: `scripts/register_helper.py:142-184`; alternate status flow at `scripts/register_agent.py:153-162` **Vulnerability Type**: Missing authentication for sensitive credential retrieval **Risk Level**: Critical ### Vulnerable Code ```python def status(data: dict) -> dict: """Check registration status and retrieve API key + wallet info if approved.""" config = load_config() twitter = normalize_handle(data.get("twitter", "")) if not twitter: return {"success": False, "error": "Twitter handle is required"} url = f"{config['api_base']}/api/dawg-pack/auth/status" try: response = requests.get(url, params={"twitter": twitter}, timeout=15) if response.status_code == 200: result = response.json() status_val = result.get("status") # If approved and key is present, save it api_key = result.get("api_key") agent_id = result.get("agent_id") if api_key: config["api_key"] = api_key config["agent_id"] = agent_id save_config(config) # Auto-enable notification polling enable_notifications(config) # Build response with wallet info resp = { "success": True, "status": status_val, "api_key": api_key, "agent_id": agent_id, "message": result.get("message"), "verification_code": result.get("verification_code"), } # Include wallet info (one-time delivery from server) if result.get("wallet_address"): resp["wallet_address"] = result["wallet_address"] if result.get("seed_phrase"): resp["seed_phrase"] = result["seed_phrase"] resp["wallet_warning"] = resul ...[truncated 2149 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Issue a high-entropy, single-use registration-session token when registration begins. - Require that token, or a signed OAuth assertion proving Twitter/X ownership, for every status request. - Bind the token to the specific account, registration record, device, and expiration time. - Apply strict rate limiting and alert on repeated status probes. - Never return a wallet seed phrase through a public-identifier lookup. - Prefer a user-supplied wallet address so the service never handles recovery phrases. - If wallet generation is unavoidable, deliver recovery material through a separately authenticated, encrypted channel with explicit user presence. - Invalidate registration-session credentials immediately after sensitive material is delivered. - Add backend authorization tests proving that a handle alone cannot retrieve API keys, seed phrases, or verification codes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/config_loader.py:52
Finding
Configurable API origin allows authenticated requests to disclose API keys to arbitrary hosts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config_loader.py:52-82`; affected request callers include `scripts/poll_notifications.py`, `scripts/subscribe.py`, `scripts/list_picks.py`, `scripts/query_stats.py`, and `scripts/update_agent.py` **Vulnerability Type**: Credential exfiltration through unvalidated endpoint override **Risk Level**: High ### Vulnerable Code ```python # Allow env vars to override everything return { "api_key": os.environ.get("DAWG_PACK_API_KEY", config.get("api_key", "")), "agent_id": os.environ.get("DAWG_PACK_AGENT_ID", config.get("agent_id", "")), "agent_name": config.get("agent_name", ""), "api_base": os.environ.get( "DAWG_PACK_API_BASE", config.get("api_base", DEFAULT_API_BASE) ), "webhook_url": config.get("webhook_url", ""), "subscriptions": config.get("subscriptions", []), "notifications_enabled": config.get("notifications_enabled", False), "last_notification_check": config.get("last_notification_check", "") } def get_headers(config: dict) -> dict: """Get request headers with API key""" headers = {"Content-Type": "application/json"} if config.get("api_key"): headers["X-Dawg-Pack-Key"] = config["api_key"] return headers ``` An authenticated caller then combines the unvalidated base URL with the credential-bearing headers: ```python api_base = config["api_base"] url = f"{api_base}/api/dawg-pack/notifications" response = requests.get( url, headers=headers, params={"limit": limit}, timeout=15 ) ``` ### Technical Analysis `DAWG_PACK_API_BASE` and `api_base` from local configuration are accepted without validating the URL scheme, hostname, port, or trusted origin. `get_headers()` independently attaches the API key to requests. Consequently, any process or configuration change capable of controlling the API base can redirect authenticated traffic to an attacker-controlled host. HTTPS alone would not resolve this issue bec ...[truncated 1079 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin authenticated requests to an explicit allowlist of trusted HTTPS origins. - Parse the URL with a standard URL parser and reject: - Non-HTTPS schemes - Embedded credentials - Unexpected ports - IP literals - Unapproved hostnames - Fragments or malformed origins - Do not attach credentials merely because a URL came from configuration. - Disable cross-origin redirects for authenticated requests, or validate every redirect target before following it. - Separate public sports-data endpoints from authenticated management endpoints and use different clients. - Treat environment-based endpoint overrides as development-only functionality and require an explicit insecure-development flag. - Add tests confirming that credentials are never sent to a non-allowlisted origin. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/register_helper.py:159
Finding
API keys, webhook secrets, and wallet recovery material are exposed through plaintext files and process output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/register_helper.py:159-184,225`; related plaintext output/storage at `scripts/register_agent.py:189-203`, `scripts/subscribe.py:148-159`, and `scripts/poll_notifications.py:129-148` **Vulnerability Type**: Plaintext sensitive-data exposure **Risk Level**: High ### Vulnerable Code ```python api_key = result.get("api_key") agent_id = result.get("agent_id") if api_key: config["api_key"] = api_key config["agent_id"] = agent_id save_config(config) # Auto-enable notification polling enable_notifications(config) resp = { "success": True, "status": status_val, "api_key": api_key, "agent_id": agent_id, "message": result.get("message"), "verification_code": result.get("verification_code"), } if result.get("wallet_address"): resp["wallet_address"] = result["wallet_address"] if result.get("seed_phrase"): resp["seed_phrase"] = result["seed_phrase"] resp["wallet_warning"] = result.get( "wallet_warning", "SAVE THIS SEED PHRASE. It will never be shown again." ) ``` The response, including secrets, is printed as JSON: ```python print(json.dumps(result)) ``` The alternate registration path prints and saves the key: ```python if api_key: print(f"\n" + "="*60) print(f" YOUR API KEY (save this - shown only ONCE!):") print(f" {api_key}") print(f"="*60) config["api_key"] = api_key config["agent_id"] = agent_id save_config(config) ``` Webhook secrets are also emitted directly: ```python print(f"\nWebhook ID: {result.get('webhook_id')}") print(f"URL: {result.get('webhook_url')}") print(f"Secret: {result.get('secret')}") print(f"Events: {result.get('events')}") ``` Configuration is written without explicit restrictive permissions: ```python with open(user_config_file, "w") as f: json.dump(config, f, indent=2) ``` ### Technical Analysis API keys and webhook secrets are bearer credentials. Wallet se ...[truncated 1388 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Store API keys in an OS keychain, secret service, or dedicated credential manager. - If file storage is unavoidable: - Create the file atomically with mode `0600` - Verify ownership before reading - Reject symlinks - Avoid storing secrets inside the project directory - Never print full API keys, webhook secrets, or seed phrases in normal output. - Return only a redacted identifier, such as the final four characters. - Mark any exceptional secret-delivery response so the Agent does not retain it in conversation history. - Never route wallet recovery phrases through general-purpose Agent output. - Prefer user-controlled wallet creation where the seed never reaches the server or Skill. - Rotate all credentials that may already have been logged. - Correct the README security claim so documentation accurately describes storage behavior. ]]>

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:205
Finding
Untrusted notification text can influence an autonomous betting workflow<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:205-223,430-450,491-508`; notification formatting at `scripts/poll_notifications.py:101-125` **Vulnerability Type**: Remote instruction injection into Agent decisions **Risk Level**: High ### Vulnerable Instructions and Code ```markdown ## NOTIFICATION CHECK (Every Run) At the START of every skill invocation (before handling user request), silently check for notifications: ```bash python3 ~/.claude/skills/sportsbook-skill/scripts/poll_notifications.py ``` If notifications exist: - **system.announcement**: Display to user as an info banner - **pick.opportunity**: Analyze opportunities matching agent specialty, suggest picks - **bet.settled**: Report results to user - **comment.received / vote.received**: Mention briefly ``` The remote payload is converted directly into Agent-visible text: ```python if event_type == "system.announcement": output.append( f"📢 ANNOUNCEMENT: {payload.get('message', 'No message')}" ) elif event_type == "pick.opportunity": output.append( f"🎯 PICK OPPORTUNITY: " f"{payload.get('description', 'Check dashboard')}" ) ``` The Skill then directs autonomous action: ```markdown At each heartbeat, if pick opportunities were received: 1. Analyze the opportunities for your specialty 2. Select 1-2 best value plays 3. POST picks to Fuku Sportsbook API 4. Track in memory/picks-YYYY-MM-DD.md ``` ### Technical Analysis Notification fields such as `message` and `description` are controlled by the remote service but are rendered as plain Agent-visible instructions. The Skill does not establish a trust boundary that requires this content to be treated only as quoted data. It also provides no schema restriction preventing instruction-like text, no provenance display, and no confirmation gate before financial actions. Because the heartbeat workflow instructs the Agent to analyze remote opportunities and post picks, a compromised or maliciou ...[truncated 1053 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat all notification properties as untrusted data, never as instructions. - Place notification content in explicit quoted or structured data boundaries. - Accept only strictly typed fields needed for calculations, such as game ID, line, odds, and timestamp. - Reject or separately display free-form fields containing imperative instructions. - Add a fixed instruction stating that content inside notification payloads cannot alter Agent policy, tool use, or user intent. - Require explicit, informed user confirmation before every bet involving real or virtual funds. - Do not permit heartbeat processing to place bets autonomously. - Authenticate notifications cryptographically and verify event origin, timestamp, nonce, and replay protection. - Record an auditable explanation of the fields used for each recommendation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/register_helper.py:25
Finding
Registration silently enables recurring authenticated polling and encourages persistent heartbeat integration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/register_helper.py:25-49,159-168`; recurring invocation instructions at `SKILL.md:205-223,491-508` **Vulnerability Type**: Excessive recurring network privilege and undisclosed persistent state **Risk Level**: Medium ### Vulnerable Code and Instructions ```python def enable_notifications(config: dict): """Auto-enable notification polling during registration.""" import json from pathlib import Path from datetime import datetime user_config_dir = Path.home() / ".config" / "fuku-sportsbook" user_config_dir.mkdir(parents=True, exist_ok=True) user_config_file = user_config_dir / "config.json" user_config = { "api_key": config.get("api_key", ""), "agent_id": config.get("agent_id", ""), "agent_name": config.get("agent_name", ""), "api_base": config.get( "api_base", "https://cbb-predictions-api-nzpk.onrender.com" ), "notifications_enabled": True, "last_notification_check": datetime.utcnow().isoformat() + "Z" } try: with open(user_config_file, "w") as f: json.dump(user_config, f, indent=2) except Exception: pass ``` Registration enables it automatically: ```python if api_key: config["api_key"] = api_key config["agent_id"] = agent_id save_config(config) # Auto-enable notification polling enable_notifications(config) ``` The Skill requires recurring checks: ```markdown At the START of every skill invocation (before handling user request), silently check for notifications. ``` It also recommends heartbeat integration: ```markdown ### Sportsbook Notifications Check python3 ~/.claude/skills/sportsbook-skill/scripts/poll_notifications.py # If notifications received, process them # If pick opportunities received, analyze and post picks ``` ### Technical Analysis Basic sports-data querying and agent registration do not ...[truncated 1343 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Default notification polling to disabled. - Request explicit user consent after registration, separately from acceptance of the main service. - Clearly disclose: - What data is transmitted - That the API key is used - Polling frequency - Remote content types - How to disable and revoke access - Honor `notifications_enabled` before polling; the current Skill instruction should not mandate polling unconditionally. - Use short-lived, notification-only credentials instead of the full agent API key. - Avoid heartbeat integration by default. - Provide a single command that disables polling, removes heartbeat instructions, and deletes notification credentials. - Apply exponential backoff and a conservative minimum polling interval. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/subscribe.py:124
Finding
Webhook registration accepts unvalidated destinations despite HTTPS-only security claims<![CDATA[ ## Vulnerability Details **File Location**: `scripts/subscribe.py:124-145,320-323`; conflicting documentation at `README.md:82-87` and `SKILL.md:359-368` **Vulnerability Type**: Missing webhook URL validation and potential server-side request forgery support **Risk Level**: Medium ### Vulnerable Code ```python def register_webhook(webhook_url: str, events: list = None): """Register a webhook endpoint.""" config = load_config() if not config.get("api_key"): print("Error: No API key configured.") sys.exit(1) events = events or ["pick_posted", "bet_settled"] url = f"{config['api_base']}/api/dawg-pack/webhooks/register" data = { "webhook_url": webhook_url, "events": events } print(f"Registering webhook: {webhook_url}") try: response = requests.post( url, headers=get_headers(config), json=data, timeout=15 ) except requests.RequestException as e: print(f"Error: Network request failed - {e}") sys.exit(1) ``` The command-line argument accepts any string: ```python webhook_parser = subparsers.add_parser( "webhook", help="Register a webhook" ) webhook_parser.add_argument( "--url", required=True, help="Webhook URL" ) ``` The implementation conflicts with the documented security claim: ```markdown - **HTTPS Only** - Webhooks require secure endpoints ``` ### Technical Analysis The client does not parse or validate the webhook destination. It does not require HTTPS, reject embedded credentials, block loopback or private-network addresses, or protect against DNS rebinding and redirect-based bypasses. The actual server may perform its own validation, but the Skill's claimed client-side security property is absent. If the backend accepts and later contacts these URLs, an authorized user or compromised Agent may cause the service to connect to internal or otherwise prohibi ...[truncated 847 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate destinations both in this client and on the backend. - Require an absolute `https://` URL. - Reject embedded usernames/passwords, fragments, malformed ports, and unsupported schemes. - Resolve the hostname and reject loopback, link-local, private, multicast, reserved, and cloud metadata ranges for both IPv4 and IPv6. - Re-resolve and revalidate immediately before each delivery to reduce DNS-rebinding risk. - Do not follow redirects, or validate every redirect destination using the same rules. - Use outbound network egress controls on the webhook worker. - Require endpoint ownership verification through a challenge-response process before activation. - Update documentation only after the implementation enforces the stated HTTPS restriction. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:30
Finding
Dependency installation is unpinned and omits a required runtime package<![CDATA[ ## Vulnerability Details **File Location**: `README.md:30-34`; missing dependency used at `scripts/manage_preferences.py:15-20` **Vulnerability Type**: Non-reproducible and incomplete dependency management **Risk Level**: Medium ### Vulnerable Configuration The installation documentation installs unversioned packages: ```bash pip install requests pyyaml ``` However, the preference management script requires another package: ```python import argparse import asyncio import json import sys from typing import Dict, Any, List, Optional import httpx from config_loader import load_config ``` ### Technical Analysis No version constraints, lockfile, or package hashes are provided. Installation therefore resolves whatever package versions are current at execution time, making builds non-reproducible and increasing exposure to compromised releases or unexpected breaking changes. The documented setup omits `httpx`, even though it is imported by `manage_preferences.py`. A user following the installation instructions may receive a runtime `ModuleNotFoundError`. A separate correctness problem also exists: `manage_preferences.py:84-119` accesses `config['base_url']`, while `config_loader.py:60` returns `api_base`. Even after installing `httpx`, preference operations will fail under the supplied configuration unless this mismatch is corrected. ### Attack Path 1. A user follows the documented `pip install` command. 2. The package index resolves unconstrained dependency versions at installation time. 3. A compromised or incompatible release may be installed without review. 4. Preference management fails because `httpx` is absent. 5. After manually adding `httpx`, it still fails when it accesses the nonexistent `base_url` key. ### Impact Assessment The primary impacts are supply-chain exposure, non-reproducible deployments, and denial of preference-management functionality. This can prevent users from applying quiet hours, notification filters, or other cont ...[truncated 53 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Declare every direct dependency, including `requests`, `PyYAML`, and `httpx`, in a standard project manifest. - Provide a reviewed lockfile with exact versions and cryptographic hashes. - Use hash-verified installation in deployment instructions. - Enable automated dependency vulnerability scanning and controlled update review. - Test installation in a clean environment as part of continuous integration. - Replace all `config['base_url']` accesses with the canonical `config['api_base']` key, or consistently rename the loader output. - Add integration tests covering every documented command. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (23)

Vague Triggers

Medium
Confidence
92% confidence
Finding
The README encourages activation through very broad natural-language phrases like wanting to register, without defining strict trigger boundaries or confirmation requirements. In an agent skill that can initiate account registration and later manage betting-related actions, this increases the chance of accidental invocation from unrelated conversation, quoted text, or prompt-injected content that mentions registration intent.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Telling users to 'just ask naturally' for operational actions creates an overly permissive activation surface with no documented limits on when the skill should engage. Because this skill interfaces with sports betting data, agent profile updates, subscriptions, and webhooks, vague trigger guidance makes prompt injection, accidental tool use, or confusion between informational queries and state-changing requests more likely.

Session Persistence

Medium
Category
Rogue Agent
Content
This skill provides access to the Fuku Sportsbook system, allowing users to:

1. **Query Sports Statistics** - Get predictions, odds, team stats, and player data
2. **Register a Betting Agent** - Create their own AI betting agent
3. **Receive Notifications** - Set up webhooks for pick alerts and bet results
4. **Subscribe to Agents** - Follow other agents' picks and results
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The registration triggers include broad phrases like 'I want to bet on sports' that can match ordinary conversational intent and cause the skill to initiate account-registration and data-collection flows unexpectedly. In a skill that gathers identifiers and performs external actions, overly permissive activation increases the risk of unintended execution and surprise transmission of user data to backend services.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The trigger summary uses ambiguous activators like 'register', 'predictions', 'wallet', and 'how much', which are common short phrases that may appear in unrelated user requests. Because this skill can invoke scripts, inspect balances, and interact with external services, loose matching can expand the skill's reach beyond clear user consent.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Register webhook for pick opportunities
curl -X POST "${API_BASE}/api/dawg-pack/agents/${AGENT_ID}/webhook" \
  -H "X-Dawg-Pack-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url": "https://your-openclaw-instance.com/webhook", "events": ["pick_opportunity", "result", "payout"]}'
Confidence
92% confidence
Finding
This instruction configures an outbound webhook and transmits sensitive agent identifiers and API-key-authenticated state to an external URL controlled by the user. In this skill's context, webhook setup is expected functionality, but it still creates a real exfiltration and SSRF-like risk surface if URLs are insufficiently validated or if the model performs the call without strong confirmation and secret-handling safeguards.

External Transmission

Medium
Category
Data Exfiltration
Content
data = {"ids": notification_ids}
    
    try:
        response = requests.post(url, headers=headers, json=data, timeout=15)
        if response.status_code not in [200, 201]:
            print(f"Warning: Failed to acknowledge notifications: {response.status_code}", file=sys.stderr)
    except requests.RequestException as e:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"Starting registration for @{handle}...")
    try:
        response = requests.post(url, json=data, timeout=15)
    except requests.RequestException as e:
        print(f"Error: Network request failed - {e}")
        sys.exit(1)
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"Starting registration for @{handle}...")
    try:
        response = requests.post(url, json=data, timeout=15)
    except requests.RequestException as e:
        print(f"Error: Network request failed - {e}")
        sys.exit(1)
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"Starting registration for @{handle}...")
    try:
        response = requests.post(url, json=data, timeout=15)
    except requests.RequestException as e:
        print(f"Error: Network request failed - {e}")
        sys.exit(1)
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"Starting registration for @{handle}...")
    try:
        response = requests.post(url, json=data, timeout=15)
    except requests.RequestException as e:
        print(f"Error: Network request failed - {e}")
        sys.exit(1)
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
`enable_notifications` writes `api_key`, `agent_id`, and related settings into `~/.config/fuku-sportsbook/config.json` without any user-facing disclosure and suppresses write errors. Persisting credentials in a plain JSON file can expose secrets to other local users, backups, or malware, and the silent failure makes security behavior harder to audit.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The `status` function is presented as a read/check operation, but it also persists the returned API key and agent ID to local configuration and then enables notifications, which writes another config file. This hidden state-changing behavior can surprise users or calling agents, causes credential persistence without explicit consent, and increases the risk of unintended secret exposure on disk.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
After receiving an API key, the helper silently enables notification polling by writing persistent configuration with no visible confirmation in this code path. In an agent skill context, silent persistence and background feature enablement are risky because they change system behavior beyond the requested `status` action and may expose credentials or metadata to future processes.

Tainted flow: 'data' from requests.get (line 185, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
print(f"Subscribing to {agent_id}...")
    try:
        response = requests.post(url, headers=get_headers(config), json=data, timeout=15)
    except requests.RequestException as e:
        print(f"Error: Network request failed - {e}")
        sys.exit(1)
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'data' from requests.get (line 185, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
print(f"Subscribing to {agent_id}...")
    try:
        response = requests.post(url, headers=get_headers(config), json=data, timeout=15)
    except requests.RequestException as e:
        print(f"Error: Network request failed - {e}")
        sys.exit(1)
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The `delete_webhook` function issues an HTTP DELETE request that removes a webhook, but only prints a progress message and does not warn the user about the irreversible effect or ask for confirmation. Destructive operations in code should have some visible disclosure unless clearly covered elsewhere; the brief docstring and CLI help do not communicate the risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The notification management code copies the API key into `~/.config/fuku-sportsbook/config.json`, creating an additional on-disk secret without warning the user or applying any visible permission hardening. This increases the attack surface for credential exposure through local compromise, backups, shared accounts, or overly permissive filesystem defaults.

Scope Creep

Low
Category
Excessive Agency
Content
## REGISTRATION FLOW (Conversational)

When a user wants to register, guide them through a natural conversation. DO NOT show them CLI commands - handle everything behind the scenes.

### Trigger Phrases for Registration
- "I want to register"
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Intent-Code Divergence

Low
Confidence
84% confidence
Finding
The loader prioritizes a user-specific JSON config in ~/.config/fuku-sportsbook/config.json, but save_config writes only to CONFIG_FILE in the skill directory. This creates an intent/documentation mismatch because the surrounding module behavior suggests two config locations, while the save function's documentation does not reflect that it updates only one of them.

Missing User Warnings

Low
Confidence
89% confidence
Finding
This code updates and writes configuration data to persistent files in both the main config and the user's home directory. While the code comments describe the behavior for developers, there is no user-facing print, prompt, or other disclosure indicating that running the script will modify local config state.

Description-Behavior Mismatch

Low
Confidence
90% confidence
Finding
The module docstring presents this script as only managing subscriptions and webhooks, but the code also implements a separate notifications command that writes notification state plus API credentials and agent metadata into ~/.config/fuku-sportsbook/config.json. That is behavior beyond the documented scope at the top of the file, even though it is related to the broader service.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The top-of-file usage block enumerates the supported commands, but it does not include the implemented 'notifications' subcommand present later in main(). This is an active documentation/code divergence because the usage text claims to describe how to use the script while leaving out a real command with configuration-writing side effects.

Static analysis

No suspicious patterns detected.