Back to skill

Security audit

Mastodon Scout

Security checks for vulnerabilities and agentic risk

Overview

The skill is a mostly coherent read-only Mastodon helper, but it can send the user's bearer token to any configured instance URL without validating the destination.

Review this before installing. Use only a read-scope token, set MASTODON_INSTANCE to the exact trusted HTTPS instance that issued that token, and do not let prompts or untrusted examples choose --instance. Revoke and rotate the token if it may have been used with an unknown or non-HTTPS instance.

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

Error
Location
scripts/mastodon_scout.py:78
Finding
OAuth Bearer Token Can Be Disclosed to an Arbitrary or Plaintext Endpoint## Vulnerability Details **File Location**: `scripts/mastodon_scout.py:78-84`, `scripts/mastodon_scout.py:105-116`, and credential-bearing call sites through `scripts/mastodon_scout.py:146` **Related Documentation**: `SKILL.md:27` and `SKILL.md:45-46` **Vulnerability Type**: Unvalidated credential-bearing network destination **Risk Level**: High ### Vulnerable Code The API helper attaches the OAuth token to every supplied URL: ```python def api_get(url, token): req = urllib.request.Request(url, headers={ 'Authorization': f'Bearer {token}', 'Accept': 'application/json', }) try: with urllib.request.urlopen(req) as resp: return resp.status, resp.read().decode('utf-8') except urllib.error.HTTPError as e: return e.code, e.read().decode('utf-8') ``` The destination is controlled by a command-line option or environment variable without validating its scheme or origin: ```python parser.add_argument('--instance', default=os.environ.get('MASTODON_INSTANCE', 'https://mastodon.social')) parser.add_argument('--limit', type=int, default=int(os.environ.get('LIMIT', '20'))) parser.add_argument('--json', action='store_true', dest='raw_json') args = parser.parse_args() token = os.environ.get('MASTODON_TOKEN', '') if not token: print('Error: MASTODON_TOKEN is not set', file=sys.stderr) sys.exit(1) base = args.instance.rstrip('/') if args.command == 'home': status, body = api_get(f'{base}/api/v1/timelines/home?limit={args.limit}', token) ``` Other commands similarly send the token to URLs derived from `base`: ```python elif args.command == 'user-tweets': status, body = api_get(f'{base}/api/v1/accounts/verify_credentials', token) check(status, body) acct_id = json.loads(body)['id'] status, body = api_get(f'{base}/api/v1/accounts/{acct_id}/statuses?limit={args.limit}', token) elif args.command == 'mentions ...[truncated 3772 chars]
Remediation
## Remediation Suggestions 1. **Require HTTPS** - Parse the instance with `urllib.parse.urlsplit`. - Reject every scheme other than `https`. - Reject missing hostnames, URL fragments, and embedded usernames or passwords. - Permit HTTP only through an explicit development-only override that never uses production credentials. 2. **Bind credentials to an exact origin** - Store each token together with the exact normalized scheme, hostname, and port of the issuing instance. - Refuse to send a token when the requested origin differs from the token's configured origin. - Prefer instance-specific variables or configuration records instead of one global token combined with an arbitrary URL. 3. **Constrain redirects** - Disable automatic redirects for authenticated API requests or implement a custom redirect handler. - If redirects are needed, follow them only when the destination has the same validated HTTPS origin. - Never forward the `Authorization` header across origins. 4. **Validate the instance before constructing API URLs** - Accept only an origin, not arbitrary paths or query strings. - Normalize internationalized hostnames and ports before comparison. - Consider an explicit trusted-instance allowlist for automated agent environments. 5. **Require informed confirmation for origin changes** - Display the exact normalized destination hostname before first use. - Require explicit user approval before associating a token with a new instance. - Do not infer permission to transmit credentials merely because an untrusted prompt supplied `--instance`. 6. **Apply defense in depth** - Continue recommending minimal read-only OAuth scopes. - Document that Mastodon tokens are instance-specific and must not be reused across unrelated hosts. - Avoid logging authorization headers and redact tokens from all errors. - Provide token revocation and rotation instructions ...[truncated 24 chars]
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (5)

Credential Access

High
Category
Privilege Escalation
Content
- **Redirect URI**: `urn:ietf:wg:oauth:2.0:oob`
  - **Scopes**: **CRITICAL — only select `read`** (uncheck write, follow, push)

**Step 3: Get Access Token**
- Click Submit, then open the created application
- Copy the **"Your access token"** value
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
**Step 3: Get Access Token**
- Click Submit, then open the created application
- Copy the **"Your access token"** value

**Step 4: Set Environment Variable**
```bash
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill requires environment-variable access and outbound network access to operate, but it does not explicitly declare a tool scope such as permissions or allowed-tools. That mismatch can cause the agent runtime or reviewer to underestimate the skill's capabilities, reducing transparency and increasing the chance of unintended token exposure or external requests being made without clear policy review.

Vague Triggers

Low
Confidence
87% confidence
Finding
The trigger section lists phrases like "show my mastodon timeline" and "get my mastodon posts" without defining stricter activation boundaries or exclusion conditions. Although Mastodon-specific, these are still free-form requests and the file does not clearly state when the skill should not activate, which can make invocation scope ambiguous.

Intent-Code Divergence

Low
Confidence
88% confidence
Finding
The module docstring describes a Mastodon client, but the exposed command is named 'user-tweets', which refers to Twitter/X terminology rather than Mastodon posts. This creates an intent-level documentation/interface mismatch that can mislead callers about what resource is being fetched, even though the underlying code queries Mastodon account statuses.

Static analysis

No suspicious patterns detected.