Back to skill

Security audit

Second Brain Ai

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its note-vault purpose, but its vault boundary can be bypassed through symbolic links, which could expose or modify files outside the chosen vault.

Install only for vaults you control, avoid placing symbolic links inside the configured vault, and be cautious using write operations until the package rejects symlinks and enforces realpath containment before every read and write.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/lib/common.js:54
Finding
Symbolic Link Traversal Allows Access Outside the Configured Vault<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/common.js:54-65`; write impact occurs through `scripts/append_note.js:58-66` **Vulnerability Type**: Symbolic link traversal and insufficient filesystem boundary validation **Risk Level**: High ### Vulnerable Code ```js function readVaultDir(dir, files = [], ignorePatterns = null) { if (!fs.existsSync(dir)) return files; const patterns = ignorePatterns || loadIgnorePatterns(); for (const item of fs.readdirSync(dir)) { const fullPath = path.join(dir, item); if (shouldIgnore(fullPath, patterns)) continue; const stat = fs.statSync(fullPath); if (stat.isDirectory()) readVaultDir(fullPath, files, patterns); else if (item.endsWith('.md')) files.push(fullPath); } return files; } ``` The discovered path is subsequently trusted by the append operation: ```js const filePath = findNoteFile(data.title); if (!filePath) return { error: `Note not found: ${data.title}` }; const append = buildAppendBlock(data); if (append.error) return { error: append.error }; let content = fs.readFileSync(filePath, 'utf-8').replace(/\s*$/, ''); content += append.block; content = updateFrontmatterUpdated(content, new Date().toISOString().split('T')[0]); fs.writeFileSync(filePath, content, 'utf-8'); ``` ### Technical Analysis The recursive vault scanner uses `fs.statSync`, which follows symbolic links. It does not use `fs.lstatSync` to identify and reject links, nor does it canonicalize candidate paths with `fs.realpathSync` and verify that the resulting path remains beneath the canonical vault root. Consequently, a Markdown-named symbolic link inside the vault can reference a file outside the vault. A symbolic link to an external directory can likewise cause the scanner to recurse through external directories and collect their Markdown files. All read-oriented features that rely on `readVaultDir`, including search, backlink lookup, context-pack generation, related-note discovery, and link ...[truncated 2102 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Canonicalize the vault root once: ```js const VAULT_REAL_PATH = fs.realpathSync(VAULT_PATH); ``` 2. Use `fs.lstatSync` before following any directory entry and reject symbolic links unless the product explicitly needs them: ```js const stat = fs.lstatSync(fullPath); if (stat.isSymbolicLink()) continue; ``` 3. Canonicalize every candidate file or directory and enforce containment: ```js function assertInsideVault(candidate) { const real = fs.realpathSync(candidate); if (real !== VAULT_REAL_PATH && !real.startsWith(VAULT_REAL_PATH + path.sep)) { throw new Error('Path resolves outside the configured vault'); } return real; } ``` 4. Perform the containment check immediately before every read and write, not only during initial discovery. This reduces time-of-check/time-of-use exposure if filesystem entries are replaced after scanning. 5. Track visited canonical directory paths during recursion to prevent symbolic-link cycles, bind mounts, or repeated traversal. 6. For writes, open the validated target defensively and reject symbolic links at the final path component where platform support permits. Revalidate the parent directory and target directly before writing. 7. Add tests covering file symlinks, directory symlinks, links escaping through multiple levels, recursive cycles, and replacement of a validated file with a link before append. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/search_notes.js:20
Finding
User-Controlled Search Terms Are Executed as Regular Expressions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/search_notes.js:20-24`; duplicated in `scripts/build_context_pack.js:28-32` **Vulnerability Type**: Regular-expression injection and potential denial of service **Risk Level**: Medium ### Vulnerable Code In `scripts/search_notes.js`: ```js let score = 0; for (const term of terms) { if (title.toLowerCase().includes(term)) score += 10; const matches = contentLower.match(new RegExp(term, 'g')); if (matches) score += matches.length; } ``` The same unsafe construction appears in `scripts/build_context_pack.js`: ```js for (const term of topicTerms) { if (title.toLowerCase().includes(term)) score += 25; const matches = contentLower.match(new RegExp(term, 'g')); if (matches) score += matches.length * 3; } ``` ### Technical Analysis The `query` or `topic` value is split into terms, and each term is passed directly to the `RegExp` constructor. Therefore, terms are interpreted as regular-expression syntax rather than literal search strings. A malformed expression can throw a syntax error. Because file-processing loops use broad exception handlers elsewhere in these functions, such errors may be silently discarded on a per-file basis, producing incomplete or empty results rather than a clear validation error. More importantly, a valid expression with catastrophic backtracking characteristics can require disproportionate CPU time when evaluated against large Markdown content. The expression is applied repeatedly across every scanned note, amplifying the cost. Node.js regular-expression evaluation occurs on the main event-loop thread, so a slow match can block the entire process. ### Attack Path 1. An untrusted caller supplies a crafted `query` to `search_notes` or a crafted `topic` to `build_context_pack`. 2. The input is split into terms without escaping regex metacharacters. 3. Each term is compiled using `new RegExp(term, 'g')`. 4. The resulting expression is evaluated against the full ...[truncated 967 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer literal string matching instead of regular expressions. For example, count occurrences using `indexOf` in a bounded loop. 2. If regular expressions remain necessary, escape all metacharacters before compilation: ```js function escapeRegExp(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } const matches = contentLower.match( new RegExp(escapeRegExp(term), 'g') ); ``` 3. Validate and cap query length, term count, and individual term length before scanning the vault. 4. Avoid silently swallowing regex and file-processing errors. Return a clear validation error for invalid input and log bounded diagnostic information where appropriate. 5. Consider limiting the maximum note size scanned or processing large vaults in isolated workers with execution timeouts. 6. Apply the same correction consistently in both `search_notes.js` and `build_context_pack.js`. 7. Add tests for regex metacharacters, malformed patterns, nested quantifiers, very long terms, empty terms, and large Markdown files. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The 'First-Success Path', sample prompts, expected outputs, and real task examples are presented entirely in Chinese, while earlier sections are in English and there is no statement that the skill is China-region-specific or that Chinese is optional. This creates a natural-language locale policy issue because the skill documentation implicitly steers usage toward a single language without user choice.

Static analysis

No suspicious patterns detected.