Back to skill

Security audit

AP Daily Report

Security checks for vulnerabilities and agentic risk

Overview

The skill has a clear daily-report purpose, but it should be reviewed because it can write files, generate PDFs, schedule delivery, and send reports to a hard-coded WeChat recipient using unsafe conversion code.

Install only if you intend this exact workflow: reading and writing the specified Obsidian vault path, creating a PDF, and sending it to the listed WeChat recipient/account on a schedule. Before use, add an explicit approval step before sending, restrict output paths, and replace the converter's shell string execution and raw HTML interpolation with safer argument passing and escaping/sanitization.

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/convert-ap-report.mjs:142
Finding

Shell Command Injection Through the PDF Output Path

Content
View full analysis
&1 || true`, { stdio: 'pipe', timeout: 30000 }); const generated = tmpHtml.replace(/\.html$/, '.pdf'); if (existsSync(generated)) { renameSync(generated, resolve(outputPdf)); console.log('OK'); } else { // Fallback: try node with puppeteer directly console.log('Fallback: trying direct puppeteer render...'); execSync(`node -e " const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({headless:'new'}); const page = await browser.newPage(); await page.goto('file://${tmpHtml}', {waitUntil:'networkidle0'}); await page.pdf({path:'${resolve(outputPdf)}', format:'A4', margin:{top:'18mm',bottom:'18mm',left:'18mm',right:'18mm'}, printBackground:true}); await browser.close(); console.log('OK'); })(); "`, { stdio: 'inherit', timeout: 30000 }); } } catch (e) { ``` ### Technical Analysis The script accepts `outputPdf` from `process.argv` and derives `tmpHtml` from it. These path values are interpolated directly into command strings passed to `execSync`, which invokes a system shell. Placing `tmpHtml` inside double quotes does not make it safe. POSIX shells still process command substitution with `$()` and backticks inside double-quoted strings. The fallback is additionally vulnerable because the paths are embedded in JavaScript source that is itself embedded in a shell command. No shell escaping, JavaScript string escaping, path allowlisting, or output-directory restriction is applied. Consequently, a caller who can influence the output filename can cause the shell to interpret attacker-supplied synta ...[truncated 1296 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/convert-ap-report.mjs:65
Finding

Unescaped Report Content Rendered as Active HTML

Content
View full analysis
So What: ${c}`; } else if (line.startsWith('- 🎯') || line.match(/^-\s*🎯/)) { const c = line.replace(/^-\s*🎯\s*\*\*(?:Action Item[^::]*[::])\*\*\s*/, '').replace(/\*$/g, '').trim(); content += `
Action Item: ${c}
`; } else if (line.startsWith('🔗')) { // Extract URLs from this line const urls = line.match(/https?:\/\/[^\s)]+/g) || []; const linksHTML = urls.map(u => { // Shorten URL for display let display = u; try { display = new URL(u).hostname + (u.length > 60 ? '...' : ''); } catch(e) {} return `${display}`; }).join('   '); content += `

Source: ${linksHTML}

`; } else if (!line.startsWith('- ') && !line.startsWith('#') && !line.startsWith('---') && !line.startsWith('<')) { // Detect English vs Chinese paragraph if (/^[A-Z]/.test(line)) { currentEnP += (currentEnP ? ' ' : '') + line; } else { if (currentEnP) { content += `

${currentEnP}

`; currentEnP = ''; } content += `

${line}

`; } } else if (line.startsWith('- ') && !line.startsWith('- 💡') && !line.startsWith('- 🎯')) { // bullet point content += `

• ${line.slice(2).replace(/^\*\*|!\[.*?\]\(.*?\)/g, '').trim()}

`; } i++; } if (currentEnP) content += `

${currentEnP}

`; ``` The resulting content i ...[truncated 2870 chars]
Remediation
View remediation
`, `"`, and `'` according to its HTML context. 2. Apply separate encoding for text nodes and attribute values. Do not use a text-node escaping function as a substitute for URL or attribute validation. 3. Parse source URLs with `new URL()` and allow only explicitly approved schemes, preferably `https:`. Reject malformed URLs and encode accepted values before placing them in `href`. 4. Sanitize the complete generated HTML using a strict allowlist. Permit only the minimal elements and attributes required for report formatting, and prohibit: - `script`, `iframe`, `object`, `embed`, and `form` - Event-handler attributes such as `onclick` and `onerror` - Inline scripts and unsafe URL schemes - Unexpected remote resources 5. Disable JavaScript before loading the document: ```js await page.setJavaScriptEnabled(false); ``` 6. Intercept browser requests and block all network access during rendering. Allow only the generated local HTML and explicitly required local assets. 7. Add a restrictive Content Security Policy, for example denying scripts, frames, objects, forms, and external connections. 8. Render inside a sandboxed, minimally privileged environment with no sensitive browser profile, credentials, or unnecessary filesystem access. 9. Add regression tests using malicious headings, summaries, action items, links, images, event attributes, and script-like markup to verify that the output contains inert text only. ]]>
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
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (3)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill directs the agent to perform external side effects: write a file into a local Obsidian vault, invoke a shell command to generate a PDF, and send that PDF to a specific WeChat recipient. Because these actions are embedded as routine workflow steps without an explicit confirmation or warning boundary, a user invoking the skill for setup, editing, debugging, or report generation could trigger unintended data creation and outbound delivery.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The generated HTML sets lang="zh-CN", which forces a specific locale in the output regardless of user preference. The file does not document this as an intentional region-specific constraint or provide any opt-in or override, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The cron prompt explicitly instructs generation of the daily report in Chinese and the template uses Chinese-specific naming, but the file does not state that the skill is intentionally limited to Chinese output or allow an alternative language. That can violate language/locale policy when a skill imposes a language without user opt-in.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/convert-ap-report.mjs:147