Back to skill

Security audit

Pm Proto Prd Pin

Security checks for vulnerabilities and agentic risk

Overview

The skill is a legitimate PRD annotation tool, but it stores powerful cloud/GitHub write credentials in browser storage and ships an unauthenticated local write server with exploitable rendering weaknesses.

Install only if you trust the project context and are comfortable reviewing the code first. Avoid broad or long-lived GitHub PATs and cloud service keys; use narrowly scoped, disposable credentials. Do not expose the bundled local server on a network, and fix authentication/CORS, Markdown sanitization, and browser credential storage before using this for confidential or team-shared PRD data.

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

Error
Location
assets/server.js:31
Finding

Unauthenticated Cross-Origin Disk Writes Enable Persistent PRD Poisoning and Stored Script Execution

Content
View full analysis

Vulnerability Details

File Location: assets/server.js:31-37, 46-87; assets/prd-pin-tool.js:3788-3855, 4624-4647
Vulnerability Type: Unauthenticated cross-origin file write chained with unsafe Markdown link rendering
Risk Level: High

Technical Analysis

The local persistence server permits requests from every web origin and exposes its write endpoint without authentication.

Relevant code from assets/server.js:31-37:

javascript
// 允许跨域 (CORS)
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

if (req.method === 'OPTIONS') {
  res.writeHead(204);
  res.end();
  return;
}

The /api/save-prd endpoint accepts attacker-supplied JSON and writes its data field into a JavaScript file without authenticating the caller.

Relevant code from assets/server.js:46-87:

javascript
if (pathname === '/api/save-prd' && req.method === 'POST') {
  let body = '';
  req.on('data', chunk => {
    body += chunk;
  });

  req.on('end', () => {
    try {
      const payload = JSON.parse(body);
      const { page, data, allData } = payload;
      
      if (!page) {
        res.writeHead(400, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ success: false, error: '缺少 page 标识' }));
        return;
      }

      const jsDir = path.join(BASE_DIR, 'assets', 'js');
      if (!fs.existsSync(jsDir)) {
        fs.mkdirSync(jsDir, { recursive: true });
      }

      // 1.1 写入页面专属数据文件 prd-data-[page].js
      const cleanPageName = page.replace('.html', '').replace(/^prd-data-/, '');
      const pageFileName = `prd-data-${cleanPageName}.js`;
      const pageFilePath = path.join(jsDir, pageFileName);
      
      const pageJsContent = `/**
 * PRD 需求数据 - ${cleanPageName}
 * 本地实时保存于: ${new Date().toLocaleString()}
 */
window.INITIAL_PRD_DATA = ${JSON.stringify(data || [], null, 2)};
`;
      fs.writeFileS
...[truncated 5568 chars]
Remediation
View remediation

Remediation Suggestions

  1. Bind the development server to loopback explicitly

    javascript
    server.listen(PORT, '127.0.0.1', () => {
      // startup logging
    });
    

    This reduces exposure to other devices, although it does not by itself prevent browser-to-localhost attacks.

  2. Require authentication for every write operation

    • Generate a cryptographically random token when the server starts.
    • Require the token in an authorization header for /api/save-prd.
    • Compare tokens using a timing-safe comparison.
    • Do not expose the token through unauthenticated static files or logs accessible to other users.
  3. Replace wildcard CORS with an exact allowlist

    • Permit only explicitly configured prototype origins.
    • Reject unexpected, absent, and null origins for browser-facing write requests.
    • Return Vary: Origin.
    • Do not reflect arbitrary origins.
    • Treat CORS as defense in depth rather than a replacement for authentication.
  4. Validate and constrain the page value

    Apply a strict allowlist, for example:

    javascript
    if (!/^[A-Za-z0-9_-]+(?:\.html)?$/.test(page)) {
      // reject request
    }
    

    Resolve the final path and verify it remains under the intended assets/js directory before writing.

  5. Limit request size

    Stop buffering and return an error once the request exceeds an appropriate maximum size. This also prevents unauthenticated memory exhaustion.

  6. Use safe Markdown rendering

    • Construct links using DOM APIs.
    • Set link labels with textContent.
    • Assign validated URLs through element properties rather than string interpolation.
    • Permit only expected schemes such as https:, http:, and approved relative links.
    • Reject javascript:, data:, vbscript:, and malformed schemes.
  7. Sanitize generated markup before insertion

    If HTML rendering remains necessary, process it with a maintained sanitizer configured to remove:

...[truncated 749 chars]

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (30)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- 资源放置:将 `prd-pin-tool.js` 与 `vendor/vditor` 置于 `public/assets/js/` 和 `public/assets/vendor/`;

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 · assets/prd-pin-tool.js (reported line 276)May include surrounding context.

js
kvSyncSuccessToast: 'Specifications persisted to cloud KV store in real-time!',
      kvPullSuccessToast: 'Pulled and synced latest specifications from cloud KV!',
      ghModalTitle: 'GitHub Pages Owner Authentication & Cloud Sync',
      ghModalDesc: 'Configure a Fine-Grained Personal Access Token (PAT) for this prototype to unlock <strong>[Serverless, Direct Commit to GitHub]</strong> real-time cloud persistence.',
      ghOwnerLabel: 'Repository Owner',
      ghRepoLabel: 'Repository Name',
      ghBranchLabel: 'Target Branch',

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 · assets/prd-pin-tool.js (reported line 280)May include surrounding context.

js
kvSyncSuccessToast: 'Specifications persisted to cloud KV store in real-time!',
      kvPullSuccessToast: 'Pulled and synced latest specifications from cloud KV!',
      ghModalTitle: 'GitHub Pages Owner Authentication & Cloud Sync',
      ghModalDesc: 'Configure a Fine-Grained Personal Access Token (PAT) for this prototype to unlock <strong>[Serverless, Direct Commit to GitHub]</strong> real-time cloud persistence.',
      ghOwnerLabel: 'Repository Owner',
      ghRepoLabel: 'Repository Name',
      ghBranchLabel: 'Target Branch',

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Cloudflare, Supabase, and JSONBin secret keys are stored in localStorage, exposing long-lived write credentials to any script executing on the page. In this tool, those credentials unlock direct cloud writes, so compromise can enable unauthorized data modification or destruction across environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

A GitHub PAT is persisted in localStorage, making it accessible to any JavaScript running in the origin, including injected scripts, compromised dependencies, or XSS payloads. Because the token grants repository write access, theft can lead to unauthorized commits, code tampering, and broader supply-chain compromise.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The authentication/verification flow performs real persistence writes using the user-supplied key as part of 'testing' or unlocking access. That contradicts the read-only/visitor-only trust model and means a verification action can modify production PRD data, causing unintended state changes, data corruption, or overwrite by anyone with a valid key.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · assets/vendor/vditor/index.min.js (reported line 1)May include surrounding context.

js
=m.e,e.SMILESRender=f.Y,e.markmapRender=h.K,e.flowchartRender=s.D,e.chartRender=o.v,e.abcRender=r.$,e.mindmapRender=v.l,e.plantumlRender=y.M,e.outlineRender=g.N,e.mediaRender=p.l,e.speechRender=_,e.lazyLoadImageRender=d,e.md2html=A,e.preview=N,e.setCodeTheme=D.h,e.setContentTheme=w.H,e}()},913:(e,t,n)=>{"use strict";n.d(t,{Y:()=>i,g:()=>r});var r="3.10.8",i=function(){function e(){}return e.ZWSP="​",e.DROP_EDITOR="application/editor",e.MOBILE_WIDTH=520,e.CLASS_MENU_DISABLED="vditor-menu--disabled",e.EDIT_TOOLBARS=["emoji","headings","bold","italic","strike","link","list","ordered-list","outdent","indent","check","line","quote","code","inline-code","insert-after","insert-before","upload","record","table"],e.CODE_THEME=["a11y-dark","agate","an-old-hope","androidstudio","arta","atom-one-dark","atom-one-dark-reasonable","base16/3024","base16/apathy","base16/apprentice","base16/ashes","base16/atelier-cave","base16/atelier-dune","base16/atelier-estuary","base16/atelier-forest","base16/atel

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This markdown file documents multiple behaviors that can write PRD data to remote services (Cloudflare D1, Supabase, JSONBin, GitHub) and to the local filesystem, but it does not clearly warn users that their edits may be transmitted off-device or persisted irreversibly. For markdown files, SQP-2 applies when descriptions omit warnings about behaviors affecting user data, privacy, or system integrity.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The documented admin-unlock flow asks users to enter an admin secret directly into the UI and indicates that authorization state is injected into sessionStorage. In agent-assisted or browser-based contexts, prompting for sensitive credentials in-page increases the risk of credential disclosure, phishing-style collection, or accidental exposure through logs, extensions, screenshots, or compromised frontend code.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill hard-codes a mandatory language-confirmation gate and restricts generated output to four preset languages, which can override or delay the user's actual request if they ask in another language or do not want this workflow. In an agent setting, rigid instruction precedence like this can reduce user control and create a form of policy hijacking where the skill imposes its own interaction contract instead of adapting to the user's intent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 1113)May include surrounding context.

js
try {
      const headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' };
      if (secretKey) headers['X-Master-Key'] = secretKey.trim();
      const resp = await fetch(`https://api.jsonbin.io/v3/b/${binId.trim()}/latest?_t=${Date.now()}`, { headers });
      if (resp.ok) {
        const json = await resp.json();
        return json.record || json;

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 1207)May include surrounding context.

js
try {
      const headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' };
      if (secretKey) headers['X-Master-Key'] = secretKey.trim();
      const resp = await fetch(`https://api.jsonbin.io/v3/b/${binId.trim()}/latest?_t=${Date.now()}`, { headers });
      if (resp.ok) {
        const json = await resp.json();
        return json.record || json;

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 1221)May include surrounding context.

js
try {
      const headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' };
      if (secretKey) headers['X-Master-Key'] = secretKey.trim();
      const resp = await fetch(`https://api.jsonbin.io/v3/b/${binId.trim()}/latest?_t=${Date.now()}`, { headers });
      if (resp.ok) {
        const json = await resp.json();
        return json.record || json;

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The tool transmits PRD content and authorization secrets to third-party services directly from the browser, but without strong contextual disclosure at the operation point users may not realize sensitive business content and keys are leaving the local environment. In a product-spec tool, PRD data can contain confidential roadmap, workflow, and internal system details.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 1207)May include surrounding context.

js
const cleanKey = secretKey ? secretKey.trim() : DEFAULT_MASTER_KEY;
    if (!binId) {
      const createResp = await fetch('https://api.jsonbin.io/v3/b', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-Master-Key': cleanKey, 'X-Bin-Name': `prd-data-${pageKey}`, 'X-Bin-Private': 'false' },
        body: JSON.stringify(payload)

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The Cloudflare 'test/verify' path only probes a health endpoint and does not validate the provided admin secret or confirm write authorization. This can falsely present the system as verified/unlocked when the credential is invalid, leading to broken access control assumptions and unsafe operator behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 2065)May include surrounding context.

js
if (!token || !owner || !repo) return { success: false, message: '请完整输入 Token 与仓库信息' };
    try {
      const cleanToken = token.trim();
      const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
        headers: {
          'Authorization': `Bearer ${cleanToken}`,
          'Accept': 'application/vnd.github.v3+json'

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 2085)May include surrounding context.

js
if (!token || !owner || !repo) return { success: false, message: '请完整输入 Token 与仓库信息' };
    try {
      const cleanToken = token.trim();
      const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
        headers: {
          'Authorization': `Bearer ${cleanToken}`,
          'Accept': 'application/vnd.github.v3+json'

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 2107)May include surrounding context.

js
if (!token || !owner || !repo) return { success: false, message: '请完整输入 Token 与仓库信息' };
    try {
      const cleanToken = token.trim();
      const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
        headers: {
          'Authorization': `Bearer ${cleanToken}`,
          'Accept': 'application/vnd.github.v3+json'

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · assets/prd-pin-tool.js (reported line 2126)May include surrounding context.

js
if (!token || !owner || !repo) return { success: false, message: '请完整输入 Token 与仓库信息' };
    try {
      const cleanToken = token.trim();
      const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
        headers: {
          'Authorization': `Bearer ${cleanToken}`,
          'Accept': 'application/vnd.github.v3+json'

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The generated standalone document always sets <html lang="zh-CN"> even though the tool otherwise supports multiple languages and allows runtime language switching. This forces a specific locale in generated output without user opt-in, which conflicts with the stated multilingual behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script's natural-language comment and all user-facing messages are exclusively in Chinese. Under the policy, forcing a specific language without user opt-in or a documented justification is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file includes upload and network-transmission features such as XMLHttpRequest-based file upload, link-to-image conversion, and remote media/resource fetching. In an unknown skill, these capabilities can exfiltrate user data or files if enabled or misconfigured, even if they originate from a general-purpose editor library.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file includes upload and network-transmission features such as XMLHttpRequest-based file upload, link-to-image conversion, and remote media/resource fetching. In an unknown skill, these capabilities can exfiltrate user data or files if enabled or misconfigured, even if they originate from a general-purpose editor library.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This JavaScript bundle contains embedded user-facing help/about strings in Chinese such as "Markdown 使用指南" and related guidance links, but there is no evident user opt-in for that locale within those strings. Under the policy rule, hard-coded locale-specific user-facing language without a user choice is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
assets/prd-pin-tool.js:1739