Back to skill

Security audit

puget-sound-salmon-report

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its fishing-report purpose, but its optional email mode can pull dynamic mailing lists and use stored mail-provider credentials with insufficient scoping and disclosure.

Review this before installing if you plan to use --email or schedule it. Use dedicated low-privilege SMTP/ESP credentials, avoid arbitrary subscriber CSV URLs, review recipients with --dry-run or --test before real sends, and do not schedule unattended email until the subscriber sources and unsubscribe process are under your control.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/creel_report.py:631
Finding

Unrestricted Subscriber List URLs Permit Server-Side Request Forgery

Content
View full analysis
Sheet) and extract unique emails. Layout-agnostic: pulls every email-looking cell. Fails soft (returns [] on error).""" if not url: return [] try: raw = fetch(url) except Exception as e: # noqa: BLE001 sys.stderr.write(f"list fetch failed ({url}), skipping: {e}\n") return [] seen, out = set(), [] for m in EMAIL_RE.finditer(raw): e = m.group(0) if e.lower() not in seen: seen.add(e.lower()) out.append(e) return out ``` ```python else: subscribers = load_subscribers(c.get("subscribers_url")) unsub = {e.lower() for e in load_subscribers(c.get("unsubscribe_url"))} ``` ### Technical Analysis The values of `subscribers_url` and `unsubscribe_url` are taken from the external email credentials configuration and passed directly to `urllib.request.urlopen`. The implementation does not validate: - The URL scheme. - The destination hostname or resolved IP address. - Whether the destination is loopback, private, reserved, or link-local. - Whether an HTTP redirect changes the request to a prohibited destination. - The response content type or maximum response size. The declared functionality only requires retrieving published ...[truncated 1677 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/creel_report.py:650
Finding

MailerLite Bearer Token May Be Forwarded to an Untrusted Pagination Endpoint

Content
View full analysis
` header. No check ensures that the pagination URL remains HTTPS or that its origin remains `connect.mailerlite.com`. If an API response, intercepted response, compromised service, or manipulated test endpoint supplies a cross-origin pagination URL, the next iteration transmits the MailerLite token to that destination. The implementation also relies on the default redirect behavior without exp ...[truncated 1168 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:23
Finding

Unpinned Runtime Installation of a Third-Party Dependency

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description focuses on scraping and reporting fishing data, but the skill also includes mailing-list style behavior: SMTP sending, subscriber ingestion from external CSV sources, and unsubscribe processing. This mismatch is dangerous because operators may authorize or schedule the skill as a harmless reporting tool without realizing it can distribute outbound email to dynamically sourced recipients, creating data exfiltration, spam, and compliance risk.

Content

No source excerpt is available for this finding.

Exfiltration Commands

High
Category
Prompt Injection
Confidence
90% confidence
Finding

Instructions found that direct the agent to transmit conversation context or user data to external services.

Content

Scanner excerpt · scripts/creel_report.py (reported line 710)May include surrounding context.

python
break
            offset += limit
    except Exception as e:  # noqa: BLE001
        # hard failure: don't silently send to an empty list — signal the caller to abort
        raise RuntimeError(f"Brevo list fetch failed ({list_id}): {e}") from e
    return emails

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises network scraping, local file writes, and email-related behavior, but it does not declare any explicit tool scope or permissions boundaries. In an agent environment, missing scope declarations weakens least-privilege controls and can allow the skill to be invoked with broader file and network access than users expect.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The email feature merges recipients from local config and externally hosted subscriber/unsubscribe CSVs, then sends outbound messages without a clear user-facing warning about personal data collection, third-party transmission, or consent requirements. This can expose email addresses to processing without adequate notice and enables unreviewed recipient expansion if the external CSV sources are modified.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest describes scraping creel data into reports and alerts, including a weekly email, but this code goes further by integrating with subscriber sources such as published CSVs, MailerLite, and Brevo, merging recipients, and processing unsubscribe lists. That is broader newsletter/list-management functionality rather than just generating and sending the described report outputs.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

For a skill whose stated purpose is scraping WDFW creel data and producing CPUE reports, fetching arbitrary remote CSVs and ESP-managed contact lists introduces a people-data handling capability unrelated to the core analytics task. This expands the skill into contact acquisition and suppression management, which is not clearly justified by the manifest description.

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/creel_report.py (reported line 695)May include surrounding context.

python
try:
        while True:
            url = (f"https://api.brevo.com/v3/contacts/lists/{list_id}/contacts"
                   f"?limit={limit}&offset={offset}")
            d = _get(url)
            batch = d.get("contacts", [])