Back to skill

Security audit

Follow Builders

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its digest purpose, but needs Review because it can store delivery keys locally, create scheduled jobs, and follow mutable prompt instructions fetched from GitHub.

Install only if you are comfortable with a digest skill that can set up recurring jobs and optionally send messages through your Telegram bot or Resend account. Prefer in-chat/stdout delivery if you do not need unattended delivery; if using Telegram or email, restrict ~/.follow-builders permissions, protect and rotate the tokens, and review any cron job it creates. The biggest review item is that digest prompts can update from GitHub after installation, so the skill's summarization instructions can change without a new package review.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
scripts/prepare-digest.js:32
Finding

Mutable Remote Prompts Can Modify Agent Instructions After Installation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:134
Finding

Credential File Is Created Without Explicit Restrictive Permissions

Content
View full analysis
~/.follow-builders/.env << 'ENVEOF' # Telegram bot token (only if using Telegram delivery) # TELEGRAM_BOT_TOKEN=paste_your_token_here # Resend API key (only if using email delivery) # RESEND_API_KEY=paste_your_key_here ENVEOF ``` ### Technical Analysis The onboarding instructions create a directory and `.env` file containing Telegram or Resend credentials without explicitly setting restrictive permissions. Actual permissions depend on the user's current `umask`. Under common permissive configurations, the directory may be created as mode `0755` and the file as mode `0644`. On a multi-user system, this can permit other local users to traverse the directory and read the credential file. The application later loads these credentials through `dotenv`, but it does not validate ownership or reject files accessible by group or other users. Plaintext local storage is functionally necessary for unattended scheduled delivery unless a system credential manager is used. However, the storage should be limited to the owning user. ### Attack Path 1. A user selects Telegram or email delivery during onboarding. 2. The Skill creates `~/.follow-builders/.env` using the user's current `umask`. 3. The user places a Telegram bot token or Resend API key in the file. 4. On a shared host with permissive resulting permissions, another local account reads the file. 5. The attacker reuses the exposed credential against the corresponding external service. This path requires an attacker with local access to the same machine and sufficient filesystem traversal permissions. ### Impact Assessment Exposure of a Telegram bot token may allow an attacker to operate the bot, send messages as the bot, and query updates available to that bot, subject to Telegra ...[truncated 406 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:345
Finding

Predictable Shared Temporary File Creates Symlink and Digest Disclosure Risk

Content
View full analysis
' > /tmp/fb-digest.txt cd ${CLAUDE_SKILL_DIR}/scripts && node deliver.js --file /tmp/fb-digest.txt 2>/dev/null ``` ### Technical Analysis The delivery workflow writes generated content to the fixed path `/tmp/fb-digest.txt`. Shared temporary directories are generally writable by all local users, and a predictable filename can be pre-created or replaced with a symbolic link. Depending on operating-system symlink protections and the privileges of the account running the Skill, shell redirection may follow an attacker-created link and overwrite another file writable by that account. The digest file is also not deleted after delivery, so sensitive or private digest content may remain on disk. Its readability depends on the user's `umask` and filesystem configuration. The temporary file is unnecessary because `deliver.js` already supports reading the digest from standard input. ### Attack Path 1. A local attacker predicts that the Skill will use `/tmp/fb-digest.txt`. 2. Before a scheduled or manual digest run, the attacker creates that path as a symbolic link to a file writable by the victim account, or monitors the predictable path. 3. The Skill performs shell redirection to `/tmp/fb-digest.txt`. 4. On a system where the link is followed, the digest overwrites or appends content to the linked target according to shell redirection behavior. 5. Alternatively, if the temporary file is created with permissive permissions, the attacker reads the digest before or after delivery. 6. The file remains after execution because the workflow does not remove it. This attack requires local access and is mitigated on systems with effective protected-symlink controls, but the implementation should not rely on such controls. ### Impact Assessment Possible impact includ ...[truncated 452 chars]
Remediation
View remediation
"$tmp_file" node "${CLAUDE_SKILL_DIR}/scripts/deliver.js" --file "$tmp_file" ``` Additional hardening should include: 1. Never reuse a fixed filename in a shared directory. 2. Set mode `0600` before writing sensitive content. 3. Keep the temporary file open where possible to reduce replacement races. 4. Delete the file on both successful and failed delivery. 5. Avoid suppressing all standard error output during setup and testing, because doing so can hide delivery and filesystem failures. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
Findings (47)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Confidence
85% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · scripts/deliver.js (reported line 85)May include surrounding context.

js
h > 0) {
    if (remaining.length <= MAX_LEN) {
      chunks.push(remaining);
      break;
    }
    // Try to split at a newline near the limit
    let splitAt = remaining.lastIndexOf('\n', MAX_LEN);
    if (splitAt < MAX_LEN * 0.5) splitAt = MAX_LEN;
    chunks.push(remaining.slice(0, splitAt));
    remaining = remaining.slice(splitAt);
  }

  for (const chunk of chunks) {
    const res = await fetch(
      `https://api.telegram.org/bot${botToken}/sendMessage`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          chat_id: chatId,
          text: chunk,
          parse_mode: 'Markdown',
          disable_web_page_preview: true
        })
      }
    );

    if (!res.ok) {
      const err = await res.json();
      // If Markdown parsing fails, retry without parse_mode
      if (err.description && err.description.includes("can't parse")) {
        await fetch(
          `https://api.telegram.org/bot${botToken}/

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose is content curation, but the instructions also collect credentials, read and write local config files, create cron jobs, and send outbound messages and email. This mismatch can mislead users and reviewers about the real trust boundary, causing them to approve a more privileged automation skill than intended.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

The instructions direct the user to obtain a Telegram bot token and place it into a local .env file, giving the skill access to a reusable credential. Exposure of this token would let an attacker control the bot's messaging behavior and potentially harvest chat metadata or send spam/phishing from the user's bot.

Content

Scanner excerpt · SKILL.md (reported line 98)May include surrounding context.

  1. Now open a chat with your new bot (search its username) and send it any message (e.g. "hi")
  2. This is important — you MUST send a message to the bot first, otherwise delivery won't work

Then add the token to the .env file. To get the chat ID, run:

bash
curl -s "https://api.telegram.org/bot<TOKEN>/getUpdates" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result'][0]['message']['chat']['id'])" 2>/dev/null || echo "No messages found — make sure you sent a message to your bot first"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

The skill uses curl piped into python to process remote Telegram API responses during onboarding. While the Python is inline rather than remotely fetched code, this pattern still combines external network input with command-line processing and normalizes unsafe shell-driven handling of untrusted data and secrets.

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

Then add the token to the .env file. To get the chat ID, run:

bash
curl -s "https://api.telegram.org/bot<TOKEN>/getUpdates" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result'][0]['message']['chat']['id'])" 2>/dev/null || echo "No messages found — make sure you sent a message to your bot first"

Save the chat ID in config.json under delivery.chatId.

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

The skill asks for a Resend API key and instructs the user to place it in a local .env file, exposing a credential that can send outbound email on the user's behalf. If leaked, the key could be used for spam, impersonation, or service abuse that impacts account reputation and billing.

Content

Scanner excerpt · SKILL.md (reported line 113)May include surrounding context.

md
3. Go to API Keys in the dashboard
4. Create a new key and copy it

Add the key to the .env file.

**If they choose on-demand:**
Set `delivery.method` to `"stdout"`. Tell them: "No problem — just type /ai

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The explicit creation of ~/.follow-builders/.env establishes a predictable plaintext secret location for delivery credentials. Predictable secret paths are easier for other local tools, malicious processes, or accidental sync/backup systems to discover and exfiltrate.

Content

Scanner excerpt · SKILL.md (reported line 132)May include surrounding context.

All content is fetched centrally. Skip to Step 6.

If the user chose Telegram or Email delivery: Create the .env file with only the delivery key they need:

bash
mkdir -p ~/.follow-builders

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The sample .env content explicitly includes placeholders for TELEGRAM_BOT_TOKEN and RESEND_API_KEY, normalizing insecure secret handling and encouraging users to paste high-value credentials into a local file. This increases the likelihood of credential exposure through file access, backups, or support sharing.

Content

Scanner excerpt · SKILL.md (reported line 136)May include surrounding context.

bash
mkdir -p ~/.follow-builders
cat > ~/.follow-builders/.env << 'ENVEOF'
# Telegram bot token (only if using Telegram delivery)
# TELEGRAM_BOT_TOKEN=paste_your_token_here

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deliver.js (reported line 15)May include surrounding context.

js
//   node deliver.js --file /path/to/digest.txt
//
// The script reads delivery config from ~/.follow-builders/config.json
// and API keys from ~/.follow-builders/.env
//
// Delivery methods:
//   - "telegram": sends via Telegram Bot API (needs TELEGRAM_BOT_TOKEN + chat ID)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/fetch-content.js (reported line 331)May include surrounding context.

js
//   node deliver.js --file /path/to/digest.txt
//
// The script reads delivery config from ~/.follow-builders/config.json
// and API keys from ~/.follow-builders/.env
//
// Delivery methods:
//   - "telegram": sends via Telegram Bot API (needs TELEGRAM_BOT_TOKEN + chat ID)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deliver.js (reported line 33)May include surrounding context.

js
const USER_DIR = join(homedir(), '.follow-builders');
const CONFIG_PATH = join(USER_DIR, 'config.json');
const STATE_PATH = join(USER_DIR, 'state.json');
const ENV_PATH = join(USER_DIR, '.env');

// How far back to look for new content (overridable via --lookback-hours flag)
const DEFAULT_LOOKBACK_HOURS = 24;

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deliver.js (reported line 176)May include surrounding context.

js
const USER_DIR = join(homedir(), '.follow-builders');
const CONFIG_PATH = join(USER_DIR, 'config.json');
const STATE_PATH = join(USER_DIR, 'state.json');
const ENV_PATH = join(USER_DIR, '.env');

// How far back to look for new content (overridable via --lookback-hours flag)
const DEFAULT_LOOKBACK_HOURS = 24;

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deliver.js (reported line 190)May include surrounding context.

js
const USER_DIR = join(homedir(), '.follow-builders');
const CONFIG_PATH = join(USER_DIR, 'config.json');
const STATE_PATH = join(USER_DIR, 'state.json');
const ENV_PATH = join(USER_DIR, '.env');

// How far back to look for new content (overridable via --lookback-hours flag)
const DEFAULT_LOOKBACK_HOURS = 24;

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/fetch-content.js (reported line 27)May include surrounding context.

js
const USER_DIR = join(homedir(), '.follow-builders');
const CONFIG_PATH = join(USER_DIR, 'config.json');
const STATE_PATH = join(USER_DIR, 'state.json');
const ENV_PATH = join(USER_DIR, '.env');

// How far back to look for new content (overridable via --lookback-hours flag)
const DEFAULT_LOOKBACK_HOURS = 24;

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/fetch-content.js (reported line 339)May include surrounding context.

js
const USER_DIR = join(homedir(), '.follow-builders');
const CONFIG_PATH = join(USER_DIR, 'config.json');
const STATE_PATH = join(USER_DIR, 'state.json');
const ENV_PATH = join(USER_DIR, '.env');

// How far back to look for new content (overridable via --lookback-hours flag)
const DEFAULT_LOOKBACK_HOURS = 24;

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/fetch-content.js (reported line 118)May include surrounding context.

js
// Prune entries older than 7 days to prevent the file from growing forever
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
  for (const [id, ts] of Object.entries(state.seenTweets)) {
    if (ts < cutoff) delete state.seenTweets[id];
  }
  for (const [id, ts] of Object.entries(state.seenVideos)) {
    if (ts < cutoff) delete state.seenVideos[id];

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/fetch-content.js (reported line 121)May include surrounding context.

js
// Prune entries older than 7 days to prevent the file from growing forever
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
  for (const [id, ts] of Object.entries(state.seenTweets)) {
    if (ts < cutoff) delete state.seenTweets[id];
  }
  for (const [id, ts] of Object.entries(state.seenVideos)) {
    if (ts < cutoff) delete state.seenVideos[id];

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/generate-feed.js (reported line 51)May include surrounding context.

js
// Prune entries older than 7 days to prevent the file from growing forever
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
  for (const [id, ts] of Object.entries(state.seenTweets)) {
    if (ts < cutoff) delete state.seenTweets[id];
  }
  for (const [id, ts] of Object.entries(state.seenVideos)) {
    if (ts < cutoff) delete state.seenVideos[id];

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/generate-feed.js (reported line 54)May include surrounding context.

js
// Prune entries older than 7 days to prevent the file from growing forever
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
  for (const [id, ts] of Object.entries(state.seenTweets)) {
    if (ts < cutoff) delete state.seenTweets[id];
  }
  for (const [id, ts] of Object.entries(state.seenVideos)) {
    if (ts < cutoff) delete state.seenVideos[id];

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill requests environment access and performs network-capable operations, but it does not declare a scoped permission model such as allowed tools or explicit permissions. This weakens reviewability and allows the skill to invoke sensitive capabilities beyond what a user would infer from a digest skill.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The invocation description includes broad trigger phrases like wanting AI industry insights or invoking /ai, which can cause the skill to activate in more contexts than users expect. Overbroad invocation increases the chance of accidental execution of privileged behaviors such as file writes, credential prompts, or scheduling.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill collects third-party delivery credentials for Telegram and Resend even though the core advertised function is producing digests. Credential collection materially increases sensitivity because compromise of the skill, logs, or local files could expose tokens usable for external messaging abuse.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The skill transmits data to an external service, the Telegram Bot API, during setup and later delivery. External transmission is security-relevant because it moves data and identifiers outside the local environment and could be abused if content or tokens are mishandled.

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

Then add the token to the .env file. To get the chat ID, run:

bash
curl -s "https://api.telegram.org/bot<TOKEN>/getUpdates" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['result'][0]['message']['chat']['id'])" 2>/dev/null || echo "No messages found — make sure you sent a message to your bot first"

Save the chat ID in config.json under delivery.chatId.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 111)May include surrounding context.

md
1. Go to https://resend.com
2. Sign up (free tier gives 100 emails/day — more than enough)
3. Go to API Keys in the dashboard
4. Create a new key and copy it

Add the key to the .env file.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documentation says no API keys are needed for stdout delivery, but the manifest still requires SUPADATA_API_KEY environment access. This inconsistency can cause unnecessary exposure of secrets to a workflow that claims not to need them, violating least privilege.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill writes sensitive delivery credentials to ~/.follow-builders/.env in plaintext and does so without prominent security warnings or guidance on filesystem protections. Plaintext local secret storage is risky because other local processes, backups, shell history, or accidental disclosure can expose reusable tokens.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access, suspicious.install_untrusted_source

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/deliver.js:174

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/generate-feed.js:278

Install source points to URL shortener or raw IP.

Warn
Code
suspicious.install_untrusted_source
Location
feed-x.json:13