Back to skill

Security audit

Weekly Report Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill is a plausible weekly report generator, but it has enough privacy disclosure gaps and an unsafe HTML report path that users should review it carefully before installing.

Install only if you are comfortable giving it access to the GitHub data covered by your token. Use a fine-grained read-only GitHub token limited to the needed repositories, prefer specifying --repos explicitly, review generated reports before sharing them, and avoid opening HTML reports from repositories where untrusted users can control issue or PR titles until HTML escaping is fixed.

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

Warning
Location
scripts/generate-report.mjs:127
Finding

Stored HTML Injection Through Unescaped GitHub Metadata

Content
View full analysis

Vulnerability Details

File Location: scripts/generate-report.mjs:127, 169, 191-201, 297-318
Vulnerability Type: Stored HTML/script injection caused by unescaped external content
Risk Level: Medium

Vulnerable Code

js
const issues = filtered
  .filter(e => e.type === 'IssuesEvent' && e.payload.action === 'closed')
  .map(e => ({
    title: e.payload.issue.title,
    repo: e.repo.name,
    url: e.payload.issue.html_url,
    labels: e.payload.issue.labels.map(l => l.name)
  }));

// ...

return prs
  .filter(pr => pr.merged_at && new Date(pr.merged_at) >= start && new Date(pr.merged_at) <= end)
  .map(pr => ({
    title: pr.title,
    repo: `${owner}/${name}`,
    url: pr.html_url,
    mergedAt: pr.merged_at,
    number: pr.number
  }));

// ...

lines.push(`- ${issue.title} \`${issue.repo}\` #${issue.url.split('/').pop()}`);

// ...

lines.push(`- ${status} ${pr.title} \`${pr.repo}\` #${pr.number}`);

// ...

<body>
${mdToHTML(md)}
</body>
</html>`;
}

function mdToHTML(md) {
  return md
    .replace(/^# (.*)$/gm, '<h1>$1</h1>')
    .replace(/^## (.*)$/gm, '<h2>$1</h2>')
    .replace(/^### (.*)$/gm, '<h3>$1</h3>')
    .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
    .replace(/`([^`]+)`/g, '<code>$1</code>')
    .replace(/^- (.*)$/gm, '<li>$1</li>')
    .replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
    .replace(/^> (.*)$/gm, '<blockquote>$1</blockquote>')
    .replace(/^---$/gm, '<hr>')
    .replace(/\n\n/g, '</p><p>')
    .replace(/\|(.+)\|/g, (m) => {
      const cells = m.split('|').filter(c => c.trim());
      if (cells.some(c => c.includes('---'))) return '';
      return '<tr>' + cells.map(c => `<td>${c.trim()}</td>`).join('') + '</tr>';
    });
}

Technical Analysis

Issue and pull-request titles obtained from the GitHub API are externally controlled values. They are interpolated directly into the generated Markdown and subsequently converted to HTML by mdToHTML().

The converter performs regular-expression substitutions ...[truncated 2348 chars]

Remediation
View remediation

Remediation Suggestions

  1. Encode all untrusted values before inserting them into HTML. At minimum, convert &, <, >, ", and ' to their corresponding HTML entities.
  2. Prefer a maintained Markdown rendering library configured to reject or escape raw HTML.
  3. Apply a well-maintained HTML sanitizer after Markdown conversion. Use a strict allowlist of required tags and attributes, and prohibit event-handler attributes, scripts, unsafe URLs, embedded objects, and active content.
  4. Keep data and markup separate where possible. Construct DOM elements using text nodes rather than assembling HTML strings.
  5. Treat all GitHub-derived fields as untrusted, including titles, commit messages, repository names, labels, and URLs.
  6. Add regression tests with payloads containing <script> tags, event-handler attributes, malformed tags, quotes, encoded markup, and dangerous URL schemes.
  7. Consider applying a restrictive Content Security Policy to generated HTML as defense in depth, while not relying on CSP as a substitute for contextual output encoding.
  8. Document that GitHub API access leaves the local host despite local report processing, and recommend fine-grained, read-only GitHub tokens with access limited to required repositories.
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

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 · scripts/generate-report.mjs (reported line 345)May include surrounding context.

js
--help, -h              Show this help

Environment Variables:
  GITHUB_TOKEN            Personal Access Token for GitHub API
  FEISHU_APP_ID           Feishu app ID (optional)
  FEISHU_APP_SECRET       Feishu app secret (optional)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README advertises convenience features but does not clearly warn that the skill aggregates potentially sensitive organizational data from code repositories, calendars, and task systems into a single synthesized report. This increases the risk of users authorizing or invoking it without informed consent, which can lead to unintended exposure of private project names, work patterns, meeting details, or task contents.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README makes a strong privacy claim that 'all data [is] processed locally, nothing stored remotely,' but the documented functionality explicitly depends on accessing external GitHub and Feishu services. Even if storage is local, this wording can mislead users into underestimating that sensitive work activity is transmitted to third-party APIs and handled under those services' security models.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill declares broad data-access capabilities in metadata and references environment-based credentials and networked integrations, but it does not explicitly constrain tool usage with a permissions or allowed-tools policy. In practice, this creates an overbroad execution surface where the agent may access environment variables or make network calls beyond what users reasonably expect for report generation, increasing the risk of unintended data exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill markets itself as a simple weekly report generator but does not clearly warn that it may read sensitive work and personal data from GitHub, calendars, reminders, and possibly private repositories. This lack of transparent disclosure can lead users to invoke the skill without understanding the scope of data collection, causing privacy violations or inadvertent summarization of confidential information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script automatically queries GitHub APIs using a personal access token and derives user activity, repository issues, PRs, and commits without any explicit consent prompt or privacy warning at runtime. In a skill context that generates polished summaries, this can surprise users and expose sensitive work patterns or private-repo metadata into generated reports.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

This code transmits authenticated requests to GitHub's external API and fetches account activity for report generation. While expected for the feature, it still sends identifying token-backed requests and retrieves potentially sensitive private-work metadata, which is more security-relevant in an agent skill that may run with limited user visibility.

Content

Scanner excerpt · scripts/generate-report.mjs (reported line 112)May include surrounding context.

js
async function fetchGitHubUserEvents(token, start, end) {
  const username = await fetchGitHubUsername(token);
  const url = `https://api.github.com/users/${username}/events?per_page=100`;
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json' }
  });

External Transmission

Medium
Category
Data Exfiltration
Confidence
83% confidence
Finding

The script calls the GitHub user endpoint with a bearer token to identify the account, which is an external transmission of credential-backed metadata. In context, this is part of normal functionality, but it still increases privacy sensitivity because it links the token to user identity and downstream activity collection.

Content

Scanner excerpt · scripts/generate-report.mjs (reported line 141)May include surrounding context.

js
}

async function fetchGitHubUsername(token) {
  const res = await fetch('https://api.github.com/user', {
    headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json' }
  });
  const data = await res.json();

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

This endpoint fetches closed issues for repositories using an authenticated GitHub request, potentially collecting private project metadata for summary generation. The danger is not code execution but privacy leakage and silent collection if users do not realize the breadth of data being retrieved.

Content

Scanner excerpt · scripts/generate-report.mjs (reported line 149)May include surrounding context.

js
}

async function fetchRepoIssues(token, owner, name, start, end) {
  const url = `https://api.github.com/repos/${owner}/${name}/issues?state=closed&since=${start.toISOString()}&per_page=50`;
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json' }
  });

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

This code retrieves closed pull requests from GitHub over the network with bearer-token authentication, which can expose sensitive engineering activity in generated output. In a reporting skill, that context makes the transmission more sensitive because the retrieved data is transformed into a durable, shareable report.

Content

Scanner excerpt · scripts/generate-report.mjs (reported line 161)May include surrounding context.

js
}

async function fetchRepoPRs(token, owner, name, start, end) {
  const url = `https://api.github.com/repos/${owner}/${name}/pulls?state=closed&per_page=50`;
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.v3+json' }
  });

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The usage section instructs users to invoke the skill with a Chinese phrase alongside an English phrase, but it does not clarify whether multilingual triggering is optional, supported equally, or user-selectable. This can create an implicit language/locale expectation without explicit opt-in.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
87% confidence
Finding

The dependency uses a caret range (^3.6.0), which allows newer compatible versions to be installed over time. This can introduce supply-chain risk because builds are not fully reproducible and a compromised or breaking upstream release could be pulled in without explicit review. In this weekly-report skill, the package is lightweight and the dependencies are common libraries, which lowers immediate danger, but it still represents a real security hygiene weakness.

Content

Scanner excerpt · package.json (reported line 25)May include surrounding context.

json
"author": "fx-world888",
  "license": "MIT",
  "dependencies": {
    "date-fns": "^3.6.0",
    "dotenv": "^16.4.5"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
87% confidence
Finding

The dependency uses a caret range (^16.4.5), permitting automatic resolution to later releases within the major version. This weakens reproducibility and increases exposure to malicious or vulnerable upstream package updates, especially relevant because dotenv affects environment-variable handling and is often loaded at startup. In this skill's context the overall impact remains low, but the package-management practice is still a legitimate supply-chain concern.

Content

Scanner excerpt · package.json (reported line 26)May include surrounding context.

json
"license": "MIT",
  "dependencies": {
    "date-fns": "^3.6.0",
    "dotenv": "^16.4.5"
  }
}

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The date display function hard-codes the 'en-US' locale, which imposes a specific language/locale format on all users. There is no opt-in, configuration option, or documented reason for this locale restriction, so it conflicts with the policy against forcing a language or locale without user choice.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill description says it scans GitHub issues/PRs, calendar events, reminders, and project files to generate a weekly report. In this implementation, reminders are explicitly stubbed out with an empty result, there is no calendar integration at all, and no project files are read; the code primarily fetches GitHub data and writes output files.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The inline CLI documentation lists FEISHU_APP_ID and FEISHU_APP_SECRET as supported environment variables, implying Feishu-related behavior. No Feishu code path exists anywhere in the script, so the documentation overstates the implemented capabilities.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The script writes weekly reports containing potentially sensitive work summaries to predictable local files in the project directory without an explicit warning or confirmation. This can unintentionally persist internal project names, issue titles, PR titles, and commit messages on disk where other tools or users may later access them.

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/generate-report.mjs:73