Back to skill

Security audit

Follow Github

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its GitHub digest purpose, but it warrants Review because it stores service credentials locally, installs scheduled jobs, and can load remote prompt instructions without integrity checks.

Install only if you are comfortable with a scheduled local tool that reads a GitHub token, may store Telegram or Resend keys, and sends digest content to configured delivery services. Keep prompts.remoteUrl unset unless you fully trust and control that source, restrict ~/.follow-github permissions, use narrowly scoped expiring tokens, and review any cron job it creates.

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

T01 · Skill Instruction Hijacking

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

Unsigned Remote Prompts Can Hijack Agent Instructions

Content
View full analysis
Remediation
View remediation

T01 · Skill Instruction Hijacking

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

Attacker-Controlled GitHub Metadata Is Passed to the LLM Without a Prompt-Injection Boundary

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:87
Finding

Credential File Creation Does Not Enforce Restrictive Filesystem Permissions

Content
View full analysis
~/.follow-github/.env << 'ENVEOF' # GitHub Personal Access Token (read-only public access) GITHUB_TOKEN=paste_your_token_here # Telegram bot token (only if using Telegram delivery — set up in Step 7) # TELEGRAM_BOT_TOKEN= # Resend API key (only if using email delivery — set up in Step 7) # RESEND_API_KEY= ENVEOF ``` ### Technical Analysis The onboarding instructions create a directory and plaintext `.env` file containing GitHub, Telegram, and Resend credentials. They do not set the directory to mode `0700`, set a restrictive `umask`, or set the credential file to mode `0600`. The resulting access permissions depend on the user's environment and current umask. On a multi-user system with an unusually permissive umask or inherited directory permissions, other local users or processes may be able to read the credentials. Plaintext storage is operationally reasonable for these command-line integrations, but the permissions must be explicitly constrained. ### Attack Path 1. The onboarding flow runs in an environment with permissive default creation permissions. 2. `~/.follow-github` and `.env` are created without explicit owner-only modes. 3. Another local account or process with filesystem access reads `.env`. 4. The attacker uses the exposed token against the corresponding external service. ### Impact Assessment Exposure of the GitHub PAT permits the operations granted to that token. The documentation recommends public, read-only GitHub permissions, which limits but does not eliminate impact. Exposure of the Telegram token could allow control of the user's bot and sending or reading bot-accessible updates. Exposure of the Resend key could permit unauthorized email operations within the key's service permissions and quota. This flaw does n ...[truncated 41 chars]
Remediation
View remediation
~/.follow-github/.env << 'ENVEOF' GITHUB_TOKEN=paste_your_token_here # TELEGRAM_BOT_TOKEN= # RESEND_API_KEY= ENVEOF ) chmod 600 ~/.follow-github/.env ``` Additionally: 1. Verify the file is a regular file owned by the current user before loading it. 2. Refuse to load credentials from group-writable or world-readable files. 3. Recommend narrowly scoped, expiring tokens and document token rotation procedures. 4. Prefer an operating-system credential store or secret manager where available. 5. Apply similarly restrictive permissions to configuration and state files because they contain delivery addresses, chat identifiers, and user activity history. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:342
Finding

Predictable Shared Temporary File Enables Symlink Clobbering and Digest Disclosure

Content
View full analysis
' > /tmp/gh-digest.txt cd ${CLAUDE_SKILL_DIR}/scripts && node deliver.js --file /tmp/gh-digest.txt 2>/dev/null ``` ### Technical Analysis The delivery workflow writes digest content to a constant filename under the shared `/tmp` directory. It does not: - Create the file exclusively - Verify that the destination is not a symbolic link - Set owner-only permissions - Use a user-specific private temporary directory - Remove the file after delivery A local attacker can pre-create `/tmp/gh-digest.txt` as a symbolic link. When the skill redirects output to that path, the shell follows the link and truncates or overwrites the linked file if the invoking user has permission to write it. Separately, the predictable path and potentially permissive mode can expose digest content, and stale content remains after delivery. ### Attack Path 1. A local attacker predicts that the scheduled or manually invoked workflow will use `/tmp/gh-digest.txt`. 2. The attacker creates that path as a symbolic link to a file writable by the victim, or monitors the predictable path for newly written content. 3. The digest workflow executes the shell redirection. 4. The shell follows the symbolic link and overwrites the target, or creates a digest file with permissions that permit unintended reading. 5. `deliver.js` reads from the path, and the temporary data remains unless removed by another process. ### Impact Assessment The symlink condition can overwrite or truncate files that the invoking account is authorized to write. It cannot directly overwrite files beyond that account's existing filesystem privileges. The confidentiality impact includes disclosure of repository activity, followed-user information, release summaries, and other digest content to local users or processes. ]] ...[truncated 1 chars]
Remediation
View remediation
' | node "${CLAUDE_SKILL_DIR}/scripts/deliver.js" ``` If a file is required, use secure creation and guaranteed cleanup: ```bash umask 077 digest_file="$(mktemp "${TMPDIR:-/tmp}/gh-digest.XXXXXX")" || exit 1 trap 'rm -f -- "$digest_file"' EXIT HUP INT TERM printf '%s' '' > "$digest_file" node "${CLAUDE_SKILL_DIR}/scripts/deliver.js" --file "$digest_file" ``` Also validate in `deliver.js` that file inputs are regular files where practical, and avoid following attacker-controlled symbolic links by opening files with platform-supported no-follow and exclusive-creation protections. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Rogue AgentSelf-Modification, Session Persistence
Findings (45)

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 60)May include surrounding context.

js
let remaining = text;
  while (remaining.length > 0) {
    if (remaining.length <= MAX_LEN) {
      chunks.push(remaining);
      break;
    }
    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 (err.description && err.description.includes("can't parse")) {
        await fetch(
          `https://api.telegram.org/bot${botToken}/sendMessage`,
          {
            method: 'POST',

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.md (reported line 173)May include surrounding context.

  1. 用下面命令取 chat ID:
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 — send a message to your bot first"

Email 配置

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.zh-CN.md (reported line 167)May include surrounding context.

  1. 用下面命令取 chat ID:
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 — send a message to your bot first"

Email 配置

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a digest generator, but it also documents persistent local state management and a configurable remote prompt source via prompts.remoteUrl. Allowing remote prompt content introduces a code/data trust boundary that is not disclosed in the user-facing purpose and could let an attacker alter future behavior through external content.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a digest generator, but it also documents persistent local state management and a configurable remote prompt source via prompts.remoteUrl. Allowing remote prompt content introduces a code/data trust boundary that is not disclosed in the user-facing purpose and could let an attacker alter future behavior through external content.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
All data is fetched **live** from GitHub's API and `github.com/trending` —
there is no central feed. Each user runs their own fetches with their own
GitHub Personal Access Token.

## Detecting Platform

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · README.md (reported line 273)May include surrounding context.

md
All data is fetched **live** from GitHub's API and `github.com/trending` —
there is no central feed. Each user runs their own fetches with their own
GitHub Personal Access Token.

## Detecting Platform

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
All data is fetched **live** from GitHub's API and `github.com/trending` —
there is no central feed. Each user runs their own fetches with their own
GitHub Personal Access Token.

## Detecting Platform

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
All data is fetched **live** from GitHub's API and `github.com/trending` —
there is no central feed. Each user runs their own fetches with their own
GitHub Personal Access Token.

## Detecting Platform

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The skill directs users to store long-lived credentials in a plaintext .env file under the home directory. Plaintext secret storage increases the risk of credential theft via local compromise, overbroad file access by other tools, backups, or accidental disclosure through later file reads.

Content

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

The token is saved to ~/.follow-github/.env — never committed to git, never sent anywhere except GitHub's own API."

Create the .env file:

bash
mkdir -p ~/.follow-github
cat > ~/.follow-github/.env << 'ENVEOF'

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

Explicitly writing GITHUB_TOKEN into ~/.follow-github/.env normalizes insecure handling of secrets and leaves a reusable authentication token on disk. Even read-only public scopes still enable API consumption tied to the user's identity and can aid profiling or abuse if stolen.

Content

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

Create the .env file:

bash
mkdir -p ~/.follow-github
cat > ~/.follow-github/.env << 'ENVEOF'
# GitHub Personal Access Token (read-only public access)
GITHUB_TOKEN=paste_your_token_here

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The .env template encourages storage of multiple secrets—GitHub, Telegram, and Resend—in one plaintext file. Consolidating several high-value credentials in a single predictable location increases blast radius if that file is read, copied, or leaked.

Content

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

bash
mkdir -p ~/.follow-github
cat > ~/.follow-github/.env << 'ENVEOF'
# GitHub Personal Access Token (read-only public access)
GITHUB_TOKEN=paste_your_token_here

# Telegram bot token (only if using Telegram delivery — set up in Step 7)

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

The skill pipes untrusted network data from curl directly into python for parsing in a shell one-liner. While the inline Python here is static, this pattern still expands attack surface and normalizes remote-data-to-interpreter pipelines that are brittle, hard to review, and unsafe to encourage in agent skills.

Content

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

  1. Fetch the chat ID with:
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 — send a message to your bot first"

Save chat ID as delivery.chatId.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
86% confidence
Finding

The skill explicitly instructs the agent to read and display prompt files on request. Prompt files may contain sensitive operational instructions, internal guardrails, or remotely sourced content that can be exfiltrated to the user, enabling prompt extraction and increasing the chance of downstream manipulation.

Content

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

md
- "Show my settings" → Read and display config.json in a friendly format
- "Who am I following?" → Tell them to check https://github.com/<their username>?tab=following
  (we don't cache the following list — it's fetched fresh each digest)
- "Show my prompts" → Read and display prompt files

After any configuration change, confirm what you changed.

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-github/config.json
// and API keys from ~/.follow-github/.env
// ============================================================================

import { readFile } from 'fs/promises';

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 26)May include surrounding context.

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

async function getDigestText() {
  const args = process.argv.slice(2);

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 142)May include surrounding context.

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

async function getDigestText() {
  const args = process.argv.slice(2);

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 152)May include surrounding context.

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

async function getDigestText() {
  const args = process.argv.slice(2);

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/prepare-digest.js (reported line 27)May include surrounding context.

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

async function getDigestText() {
  const args = process.argv.slice(2);

Known Vulnerable Dependency: undici==7.25.0 — 12 advisory(ies): CVE-2026-6733 (undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse); CVE-2026-13697 (undici vulnerable to cross-user information disclosure and parse-time crash via ); CVE-2026-16728 (undici vulnerable to downstream response desynchronization via retry interceptor) +9 more

High
Category
Supply Chain
Confidence
95% confidence
Finding

The lockfile pins transitive dependency undici to 7.25.0, and the provided advisory data indicates multiple known HIGH-severity issues affecting that version. Because this skill fetches GitHub content and likely makes outbound HTTP requests, a vulnerable HTTP client library can expose the fetcher to response desynchronization, cross-user data leakage, request/response mix-ups, or denial of service when interacting with attacker-controlled or intermediary network endpoints.

Content

No source excerpt is available for this finding.

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/prepare-digest.js (reported line 65)May include surrounding context.

js
// Prune entries older than 30 days
  const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
  for (const [id, ts] of Object.entries(state.seenEvents)) {
    if (ts < cutoff) delete state.seenEvents[id];
  }
  for (const [id, ts] of Object.entries(state.seenHotRepos)) {
    if (ts < cutoff) delete state.seenHotRepos[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/prepare-digest.js (reported line 68)May include surrounding context.

js
// Prune entries older than 30 days
  const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
  for (const [id, ts] of Object.entries(state.seenEvents)) {
    if (ts < cutoff) delete state.seenEvents[id];
  }
  for (const [id, ts] of Object.entries(state.seenHotRepos)) {
    if (ts < cutoff) delete state.seenHotRepos[id];

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/prepare-digest.js (reported line 333)May include surrounding context.

js
errors.push(`prompt missing: ${filename}`);
    }
  }
  return prompts;
}

// -- Main --------------------------------------------------------------------

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 · README.md (reported line 180)May include surrounding context.

md
If you choose email delivery, the agent should guide you through:

1. Create a free account at https://resend.com
2. Create an API key in the Resend dashboard
3. Save it as `RESEND_API_KEY` in `~/.follow-github/.env`
4. Put your destination email address into `config.delivery.email`

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill requires environment access and performs network operations, but it does not declare an explicit tool scope such as permissions or allowed-tools. This weakens the security boundary by making powerful capabilities implicit, reducing reviewability and increasing the chance the skill is invoked with broader access than users expect.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

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