Back to skill

Security audit

Seats.aero Award Monitor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated purpose, but review is warranted because its Seats.aero API key could be exposed if an authenticated request is redirected off-site.

Install only if you are comfortable giving the skill a Seats.aero API key and letting it persist local watcher state. Prefer running it with a scoped API key, avoid untrusted state/config paths, back up watcher configuration before using replacement options, and consider fixing redirect handling before production use.

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/seats_client.py:42
Finding
API Credential May Be Disclosed Through Cross-Origin HTTP Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/seats_client.py`, lines 42–59 **Vulnerability Type**: Authorization-header disclosure through automatically followed redirects **Risk Level**: Medium ### Vulnerable Code ```python req = urllib.request.Request( url, method="GET", headers={ "Accept": "application/json", "Partner-Authorization": self.api_key, "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Accept-Language": "en-US,en;q=0.9", "Accept-Encoding": "gzip, deflate", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-site", "Referer": "https://seats.aero/", "Origin": "https://seats.aero", }, ) try: with urllib.request.urlopen(req, timeout=self.timeout_seconds) as resp: ``` ### Technical Analysis The client correctly uses HTTPS and must send the `Partner-Authorization` API credential to the declared Seats.aero Partner API. This initial credential transmission is necessary for the Skill's advertised functionality. However, `urllib.request.urlopen()` follows HTTP redirects automatically. The code neither validates the redirect destination nor installs a redirect handler that removes `Partner-Authorization` when the request origin changes. Custom request headers can consequently be propagated to a redirected request. A redirect from the trusted Seats.aero endpoint to a different origin could expose the API key to that origin. Exploitation depends on the trusted endpoint, or an upstream component controlling its responses, returning a cross-origin redirect. No attacker-controlled base URL or confirmed open redirect was found in the reviewed project, so the issue does not provide a direct standalone exploitation mechanism. The network behavior flagged in `scripts/check_awards.py` is limited to reading `SEATS_AERO_API_KEY` and passing ...[truncated 1382 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic redirects for authenticated requests, or implement a custom `HTTPRedirectHandler`. 2. Permit redirects only when all of the following remain true: - The destination scheme is `https`. - The normalized hostname is exactly `seats.aero`, or another explicitly approved Seats.aero API hostname. - The destination port is the expected HTTPS port. 3. Remove `Partner-Authorization` from every redirected request when the scheme, host, or port changes. 4. Prefer rejecting cross-origin redirects rather than retrying them without credentials, unless such redirects are explicitly required by the API contract. 5. Set a small maximum redirect count to prevent redirect loops and unnecessary authenticated requests. 6. Add regression tests with a local HTTP server that verifies: - Same-origin redirects follow the intended policy. - Cross-origin redirects are rejected. - Authorization headers never reach a different origin. - HTTPS-to-HTTP redirects are rejected. 7. Rotate the API key if logs or runtime evidence indicate that a cross-origin redirect has already occurred. ]]>
Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented purpose emphasizes passive monitoring and alerting, but the skill also includes administrative functions that modify persisted watcher configuration and date ranges, including bulk updates. This mismatch can mislead operators and agents into granting or invoking the skill in contexts where write-capable maintenance actions were not expected, creating integrity and change-management risk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises and demonstrates capabilities to read environment variables, write/read local files, and make network requests, but it declares no explicit tool scope or permission boundary. In an agent ecosystem, this weakens least-privilege controls and can allow the skill to be invoked with broader access than its documented purpose requires, increasing the blast radius if the skill is misused or modified.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
--state-backend json --state-json /path/to/monitor.json \
  --origin PVG --start 2026-08-01 --end 2026-08-15

# Skip confirmation prompt
python3 <skill-dir>/scripts/update_watcher_dates.py \
  --origin PVG --start 2026-08-01 --end 2026-08-15 --yes
Confidence
85% 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.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
--state-backend json --state-json /path/to/monitor.json \
  --origin PVG --start 2026-08-01 --end 2026-08-15

# Skip confirmation prompt
python3 <skill-dir>/scripts/update_watcher_dates.py \
  --origin PVG --start 2026-08-01 --end 2026-08-15 --yes
Confidence
85% 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.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This Python file contains natural-language comments and, more importantly, user-facing alert messages in Chinese, such as the strings assembled in `build_alert_message`. The skill does not offer any user opt-in or locale selection, so it imposes a specific language on users, which matches the language/locale policy violation criteria.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest describes monitoring award availability and persisting idempotent watcher state, which implies storing runtime state about checks and alerts. However, this script also supports administrative mutation of the watcher configuration set itself via `--watchers-import` and optional full replacement of existing DB watchers, which is broader than persisting state and changes the configured monitoring scope.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
In this code file, setting `replace=True` causes an unconditional `DELETE FROM watchers`, which is a destructive operation affecting persisted user data. There is no confirmation prompt, visible warning, or explanatory comment/docstring near the operation to disclose that all watcher records will be removed.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
When `replace=True`, the JSON-backed store resets `self.data["watchers"]` to an empty object before saving, which removes previously stored watcher configuration data. The code does not provide any confirmation, warning, or nearby disclosure that this mode overwrites existing persisted data.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The client hard-codes an "Accept-Language: en-US,en;q=0.9" header, which imposes a specific language/locale preference in requests. This is a natural-language policy concern because the file provides no user opt-in or explanation that the integration is intentionally US-English specific.

Static analysis

No suspicious patterns detected.