X Bookmark Triage

Security checks across malware telemetry and agentic risk

Overview

This skill does what it claims, but it gets persistent credentials and can automatically remove X/Twitter bookmarks without a fresh confirmation, so it needs careful review before use.

Install only if you are comfortable with the skill reading your X bookmarks, sending bookmark content/URLs to third-party services, posting results to Discord, and potentially removing bookmarks automatically. Use --dry-run first, prefer --no-unbookmark or read-only OAuth scopes unless deletion is intentional, store tokens in a real secret manager where possible, and review any cron/launchd/systemd setup before enabling unattended polling.

SkillSpector

By NVIDIA
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (23)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill describes use of environment variables, shell commands, OAuth flows, and persistent scripts, but the manifest does not declare corresponding permissions or capabilities. This creates a transparency and consent gap: a user may invoke the skill without realizing it can access secrets, execute local commands, and install automation, increasing the chance of over-privileged or surprising behavior.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The testing snippet instructs users to send the refresh token as a Bearer token to the X API, which is an incorrect and unsafe OAuth usage pattern. This can cause users to mishandle a long-lived secret, normalize exposing it in commands/logs, and create auth failures that encourage insecure debugging or token leakage.

Context-Inappropriate Capability

Medium
Confidence
72% confidence
Finding
The script invokes a generic external bus emitter script with the full inherited environment, even though this capability is not necessary for bookmark polling itself. Because the target script is external to this file and receives process.env, secrets such as OAuth tokens may be exposed to another component, creating a covert data-flow boundary and increasing the blast radius if that helper is modified or replaced.

Context-Inappropriate Capability

Low
Confidence
82% confidence
Finding
The wrapper reads secrets from a broader local secret store and injects them into the process environment, including Discord and Anthropic credentials. Even if needed by the downstream workflow, this expands the trust boundary and increases exposure if poll-channel.js or any child process logs, leaks, or misuses environment variables.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The README advertises very broad natural-language triggers like "Triage my bookmarks" and "Set up the bookmark pipeline" that can overlap with ordinary user requests in an agent environment. In a system that auto-routes skills based on phrase matching, this can cause unintended activation of a workflow that reads bookmarks, posts content to Discord, and may unbookmark items, creating a real risk of unauthorized or surprising actions.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The trigger phrase "knowledge intake" is generic enough to appear in normal workspace conversation, making accidental invocation more likely. In a skill that can fetch external content, post to Discord, and optionally delete bookmarks, unintended activation expands the chance of unwanted data transfer or destructive side effects.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The description says bookmarked content is captured, scored with Claude Haiku, and posted to Discord, but it does not prominently warn that bookmark contents and URLs may be sent to multiple external services for processing. This is a data-handling risk because users may assume bookmarks remain private while the skill transmits them to fxtwitter, Anthropic, markdown.new, and Discord.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill emphasizes that it can optionally unbookmark items so the user's list stays clean, but it does not present this as a destructive action with an explicit warning in setup and usage. Because unbookmarking removes items from the user's account, accidental or misunderstood execution could cause unintended loss of saved references.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script deletes bookmarks by default after triage, including items already marked as seen, without any interactive confirmation or explicit opt-in at execution time. Because bookmark deletion is user-data modification and not easily reversible in bulk, a misconfigured run, logic error, or unexpected seen-state can cause unintended loss of the user's saved items.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script automatically deletes bookmarks after triage, including already-seen items, with no runtime confirmation, feature flag, or explicit opt-in at the deletion point. In this skill context, bookmarks are user data and deletion is irreversible from the automation's perspective, so a logic bug or mis-triage could silently destroy a user's saved backlog.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script fetches user-supplied URLs via third-party services (fxtwitter and markdown.new), which means bookmarked or manually dropped content is automatically transmitted to external processors. This can leak sensitive URLs, internal links, or private content without an explicit user-facing disclosure or allowlist, which is especially risky in an automation skill that polls and processes links unattended.

Ssd 1

High
Confidence
98% confidence
Finding
Fetched page or tweet text is inserted directly into the LLM prompt, allowing hostile prompt content to manipulate the model's classification, tags, proposed actions, or downstream message formatting. In this skill context, untrusted internet content is the core input, so prompt injection is not hypothetical: an attacker can craft a page or tweet that causes misleading triage, suppresses warnings, or pushes deceptive recommendations into Discord and the workspace event stream.

Credential Access

High
Category
Privilege Escalation
Content
`Bookmark sweep: ${triaged} triaged, ${skipped} skipped`,
      JSON.stringify({ triaged, skipped, total: bookmarks.length }),
      'knowledge'
    ], { env: process.env });
  } else {
    console.log('[bookmark-poll] emit-event.sh not available — skipping bus event');
  }
Confidence
87% confidence
Finding
.env

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Load credentials from .env file if it exists (non-OpenClaw users)
ENV_FILE="${OPENCLAW_WORKSPACE:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}/.env"
if [ -f "$ENV_FILE" ]; then
  set -a
  source "$ENV_FILE"
Confidence
93% confidence
Finding
.env"

Session Persistence

Medium
Category
Rogue Agent
Content
2. Save the new token to `data/x-oauth2-new-refresh-token.txt`
3. Log a warning: `⚠️ Refresh token rotated — update X_OAUTH2_REFRESH_TOKEN in your env`

**You must update `X_OAUTH2_REFRESH_TOKEN`** in your `.env` file or plist after each rotation, or the next run will fail with `invalid_grant`. The new token is always at `data/x-oauth2-new-refresh-token.txt`.

---
Confidence
88% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
# → Opens browser, captures callback, saves to data/x-oauth2-token-cache.json
```

Store credentials in your gateway plist (or env):
```
X_OAUTH2_CLIENT_ID     — app OAuth 2.0 client ID
X_OAUTH2_CLIENT_SECRET — app OAuth 2.0 client secret
Confidence
91% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
### 4. Register the launchd Poller (Optional — for automated polling)

**Note:** `scripts/ai.watson.knowledge-intake-poll.plist` is a template. Customize it first:

1. Edit the file to replace `/PATH/TO/skills/x-bookmark-triage/scripts/poll-channel.js` with the actual path
2. Replace `YOUR_BOT_TOKEN`, `YOUR_ANTHROPIC_KEY`, `YOUR_CHANNEL_ID`, `/PATH/TO/workspace` with real values
Confidence
95% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# After customizing the plist:
cp scripts/ai.watson.knowledge-intake-poll.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/ai.watson.knowledge-intake-poll.plist
```

Or use OpenClaw to manage it — see `references/cron-setup.md`.
Confidence
86% confidence
Finding
launchctl load

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# After customizing the plist:
cp scripts/ai.watson.knowledge-intake-poll.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/ai.watson.knowledge-intake-poll.plist
```

Or use OpenClaw to manage it — see `references/cron-setup.md`.
Confidence
86% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
X rotates refresh tokens on each use. The scripts:
1. Detect rotation (new token in response)
2. Save new token to `data/x-oauth2-new-refresh-token.txt`
3. Log a warning to update the plist env var

Watson auto-detects and updates the secrets file. For non-Watson deployments, monitor for this warning and update `X_OAUTH2_REFRESH_TOKEN` in your env.
Confidence
92% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
}

  if (data.refresh_token && data.refresh_token !== REFRESH_TOKEN) {
    console.log('⚠️  Refresh token rotated — update X_OAUTH2_REFRESH_TOKEN in plist');
    fs.writeFileSync(
      path.join(WORKSPACE, 'data/x-oauth2-new-refresh-token.txt'),
      data.refresh_token,
Confidence
70% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
// Save new refresh token if rotated
  if (data.refresh_token && data.refresh_token !== REFRESH_TOKEN) {
    console.log('[bookmark-poll] ⚠️  Refresh token rotated — update X_OAUTH2_REFRESH_TOKEN in plist');
    // Save to a file for the next run (protect with 0o600)
    fs.writeFileSync(
      path.join(WORKSPACE, 'data/x-oauth2-new-refresh-token.txt'),
Confidence
84% confidence
Finding
plist

Tool Parameter Abuse

High
Category
Tool Misuse
Content
Discord #knowledge-intake  (structured card)
                │
                ▼
    X API DELETE /bookmarks/:id  (unbookmark)
```

## Prerequisites
Confidence
93% confidence
Finding
DELETE /bookmarks/:id

VirusTotal

65/65 vendors flagged this skill as clean.

View on VirusTotal