Back to skill

Security audit

github-search

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate GitHub research purpose, but its bundled scripts can turn user-supplied arguments into shell command execution while using inherited environment credentials.

Review before installing. The skill is coherent as a GitHub research helper, but it should not be run on untrusted or agent-generated repository names, sort values, or order values until the shell-based curl calls are replaced with structured HTTP requests and strict allowlists. Avoid setting GITHUB_TOKEN in the environment for this skill unless you accept that the current implementation may expose it if command injection is triggered.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/repo-detail.mjs:13
Finding
Shell Command Injection Through Repository Name<![CDATA[ ## Vulnerability Details **File Location**: `scripts/repo-detail.mjs`, lines 13–62 **Vulnerability Type**: OS command injection through unsafe shell command construction **Risk Level**: High ### Vulnerable Code ```javascript function parseArgs() { const args = process.argv.slice(2); if (args.length < 1) { return null; } return args[0]; // repo full name, e.g., "microsoft/autogen" } // Call GitHub API async function fetchRepoDetails(repoFullName) { const url = `${GITHUB_API}/${repoFullName}`; const headers = [ '-H "Accept: application/vnd.github.v3+json"', '-H "User-Agent: GitHub-Research-Skill"' ]; if (process.env.GITHUB_TOKEN) { headers.push(`-H "Authorization: token ${process.env.GITHUB_TOKEN}"`); } const cmd = `curl -s ${headers.join(' ')} "${url}"`; try { const result = execSync(cmd, { encoding: 'utf-8', timeout: 30000 }); return JSON.parse(result); } catch (error) { console.error('Error fetching repo details:', error.message); return null; } } // Fetch contributor statistics async function fetchContributors(repoFullName) { const url = `${GITHUB_API}/${repoFullName}/contributors?per_page=10`; const headers = [ '-H "Accept: application/vnd.github.v3+json"', '-H "User-Agent: GitHub-Research-Skill"' ]; if (process.env.GITHUB_TOKEN) { headers.push(`-H "Authorization: token ${process.env.GITHUB_TOKEN}"`); } const cmd = `curl -s ${headers.join(' ')} "${url}"`; try { const result = execSync(cmd, { encoding: 'utf-8', timeout: 30000 }); return JSON.parse(result); } catch (error) { return []; } } ``` The later validation is insufficient: ```javascript const [owner, repo] = repoFullName.split('/'); if (!owner || !repo) { console.error('❌ 格式错误,请使用 "owner/repo" 格式'); process.exit(1); } ``` ### Technical Analysis The repository name originates directly from a command-line argument and is interpolated into a shell command passed ...[truncated 2350 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove shell command construction and use Node.js HTTP functionality: ```javascript async function githubRequest(url) { const headers = { Accept: 'application/vnd.github.v3+json', 'User-Agent': 'GitHub-Research-Skill' }; if (process.env.GITHUB_TOKEN) { headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`; } const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`GitHub API request failed with status ${response.status}`); } return response.json(); } ``` 2. Construct URLs using the `URL` API rather than string-based shell commands. 3. Strictly validate the repository identifier before making a request. Require exactly one slash and allow only expected GitHub name characters and lengths. For example: ```javascript const REPOSITORY_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9._-]{1,100}$/; if (!REPOSITORY_PATTERN.test(repoFullName)) { throw new Error('Invalid repository identifier'); } ``` 4. If invoking `curl` is unavoidable, use `execFileSync()` or `spawnSync()` with a fixed executable and a separate argument array, with `shell: false`. Do not concatenate arguments into a command string. 5. Avoid exposing authorization values in command strings, process listings, or error output. Keep tokens in HTTP header objects and ensure thrown errors do not include them. 6. Add regression tests using repository arguments containing quotes, semicolons, command substitutions, newlines, redirections, extra slashes, and option-like prefixes. Tests should verify rejection and confirm that no child shell is invoked. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/github-search.mjs:96
Finding
Shell Command Injection Through Search Sort and Order Parameters<![CDATA[ ## Vulnerability Details **File Location**: `scripts/github-search.mjs`, lines 27–55 and 96–115 **Vulnerability Type**: OS command injection through unvalidated URL parameters **Risk Level**: High ### Vulnerable Code The command-line parser accepts unrestricted values for `sort` and `order`: ```javascript function parseArgs() { const args = process.argv.slice(2); const options = { query: '', language: null, minStars: 100, maxStars: null, updatedWithin: 365, createdAfter: null, sort: 'stars', order: 'desc', limit: 10, output: 'table' }; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (!arg.startsWith('--')) { options.query = arg; } else if (arg === '--language' || arg === '-l') { options.language = args[++i]; } else if (arg === '--min-stars') { options.minStars = parseInt(args[++i]); } else if (arg === '--max-stars') { options.maxStars = parseInt(args[++i]); } else if (arg === '--updated-within') { options.updatedWithin = parseInt(args[++i]); } else if (arg === '--created-after') { options.createdAfter = args[++i]; } else if (arg === '--sort') { options.sort = args[++i]; } else if (arg === '--order') { options.order = args[++i]; } else if (arg === '--limit' || arg === '-n') { options.limit = parseInt(args[++i]); } else if (arg === '--output' || arg === '-o') { options.output = args[++i]; } } return options; } ``` Those values are inserted into a shell command: ```javascript async function searchGitHub(query, sort, order, perPage = 30) { const url = `${GITHUB_API}?q=${encodeURIComponent(query)}&sort=${sort}&order=${order}&per_page=${perPage}`; const headers = [ '-H "Accept: application/vnd.github.v3+json"', '-H "User-Agent: GitHub-Research-Skill"' ]; // Add authentication if a token is available if (process.env.GITHUB_TOKEN) { headers.push(` ...[truncated 2657 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `curl` and `execSync()` with Node.js `fetch()` and the `URL` API: ```javascript async function searchGitHub(query, sort, order, perPage = 30) { const allowedSort = new Set(['stars', 'forks', 'help-wanted-issues', 'updated']); const allowedOrder = new Set(['asc', 'desc']); if (!allowedSort.has(sort)) { throw new Error('Invalid sort value'); } if (!allowedOrder.has(order)) { throw new Error('Invalid order value'); } const url = new URL('https://api.github.com/search/repositories'); url.searchParams.set('q', query); url.searchParams.set('sort', sort); url.searchParams.set('order', order); url.searchParams.set('per_page', String(perPage)); const headers = { Accept: 'application/vnd.github.v3+json', 'User-Agent': 'GitHub-Research-Skill' }; if (process.env.GITHUB_TOKEN) { headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`; } const response = await fetch(url, { headers }); if (!response.ok) { throw new Error(`GitHub API request failed with status ${response.status}`); } return response.json(); } ``` 2. Enforce an explicit allowlist for `sort` and `order` during argument parsing and again at the network boundary. 3. Validate all numeric options: - Require finite integers. - Reject missing option values. - Restrict `limit` to the GitHub API maximum. - Reject negative or unreasonable date windows. 4. If `curl` must be retained, invoke it using `execFileSync('curl', argumentArray)` or `spawnSync()` with `shell: false`. Each header, URL, and option must be a separate array element. 5. Keep the token out of shell command strings and diagnostic messages. 6. Add security tests for `--sort` and `--order` values containing command substitutions, quotes, semicolons, backticks, newlines, redirections, and malformed or missing values. Tests should verify tha ...[truncated 87 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (18)

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/github-search.mjs "agent memory"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The skill documentation is written entirely in Chinese, including usage instructions, parameters, and examples, with no indication that other languages are supported or that Chinese is required for a region-specific purpose. This can violate language/locale policy because it imposes a specific language on users without opt-in or justification.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill demonstrates capabilities that involve shell execution, network access, and environment-variable use, but it does not declare any tool scope or permissions boundaries. That increases the chance an agent runtime will invoke the skill with broader-than-necessary privileges, making misuse of shell/network operations or secret-bearing environment variables harder to govern.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
SQP-3 applies to all file types and covers language or locale policy violations. The description and instructions consistently force Chinese-language interaction/documentation, with no opt-in, alternative language option, or justification that the skill is intended only for a Chinese-language or region-specific audience.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
User-influenced values flow into a shell command via the URL, sort, and order parameters, and the command is executed by execSync. encodeURIComponent protects the query portion, but sort and order are not validated and shell metacharacters could break out of the quoted URL, leading to arbitrary command execution in the context running the skill.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The script builds a shell command string and executes it with execSync while also pulling credentials from GITHUB_TOKEN. Although the feature goal is legitimate, invoking curl through the shell unnecessarily expands the attack surface and can expose the token or enable command injection if supposedly constrained inputs such as sort/order are manipulated.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script reads `process.env.GITHUB_TOKEN` and, if present, transmits it in an Authorization header to GitHub. This is access to sensitive credentials and network transmission of authentication data, but the usage/help text and nearby comments do not warn the user that an environment token may be consumed and sent.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The script builds a shell command string and executes it with execSync using untrusted input derived from the repoFullName argument and an environment-sourced GitHub token. Because repoFullName is interpolated into a quoted shell command without strict validation or escaping, an attacker can potentially achieve command injection or break command structure, which is especially dangerous in an agent skill that may process external/user-supplied repository names.

External Transmission

Medium
Category
Data Exfiltration
Content
import { execSync } from 'child_process';

const GITHUB_API = 'https://api.github.com/repos';

// 解析参数
function parseArgs() {
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import { execSync } from 'child_process';

const GITHUB_API = 'https://api.github.com/repos';

// 解析参数
function parseArgs() {
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script explicitly formats dates with the 'zh-CN' locale, which hard-codes a language/locale choice rather than adapting to user preference. This is reinforced by multiple Chinese user-facing messages throughout the script, with no opt-in or alternative locale handling.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The documentation instructs users to export a GitHub token but does not warn that the credential will be consumed by authenticated requests or describe safe handling practices. This can lead to users placing long-lived tokens in insecure shells, logs, shared environments, or transcripts, increasing the risk of accidental credential exposure.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/github-search.mjs:105

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/repo-detail.mjs:36