Back to skill

Security audit

Get笔记同步

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it stores reusable account credentials in local plaintext files and includes a deletion helper without enough safeguards.

Review before installing. Use a private output directory, keep it out of shared drives and repositories, and treat .token-cache.json and .auth-state.json like passwords because they can grant access to your notes account. Do not run the dedupe helper unless you have backups and understand it permanently deletes duplicate Markdown files.

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/api.js:36
Finding
Long-Lived Authentication Credentials Stored in Plaintext Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/api.js:36-44`, with related credential persistence at `scripts/api.js:149-165` and `scripts/refresh-token.js:75-94` **Vulnerability Type**: Plaintext storage of reusable authentication credentials **Risk Level**: Medium ### Vulnerable Code ```js function saveTokenCache(data) { fs.writeFileSync(CONFIG.tokenCacheFile, JSON.stringify({ token: data.token, tokenExpireAt: data.tokenExpireAt || null, refreshToken: data.refreshToken || null, refreshTokenExpireAt: data.refreshTokenExpireAt || null, savedAt: new Date().toISOString(), }, null, 2), 'utf8'); } ``` The browser authentication state and extracted credentials are also persisted: ```js const authInfo = await page.evaluate(() => ({ token: localStorage.getItem('token'), tokenExpireAt: localStorage.getItem('token_expire_at'), refreshToken: localStorage.getItem('refresh_token'), refreshTokenExpireAt: localStorage.getItem('refresh_token_expire_at'), })); token = authInfo.token; await context.storageState({ path: CONFIG.authStateFile }); saveTokenCache({ token: authInfo.token, tokenExpireAt: authInfo.tokenExpireAt ? parseInt(authInfo.tokenExpireAt) : null, refreshToken: authInfo.refreshToken || null, refreshTokenExpireAt: authInfo.refreshTokenExpireAt ? parseInt(authInfo.refreshTokenExpireAt) : null, }); ``` The separate refresh utility similarly persists authentication data without explicitly restricting file permissions: ```js const authInfo = await page.evaluate(() => ({ token: localStorage.getItem('token'), tokenExpireAt: localStorage.getItem('token_expire_at'), refreshToken: localStorage.getItem('refresh_token'), refreshTokenExpireAt: localStorage.getItem('refresh_token_expire_at') })); if (!authInfo.token) { console.error('❌ Failed to get token from page. May need manual re-login.'); process.exit(1); } // Save new token fs.writeFileSync(T ...[truncated 3260 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store refresh tokens and session credentials in an operating-system credential manager, such as macOS Keychain, Windows Credential Manager, or a Linux Secret Service implementation. 2. If file-based storage is unavoidable, create credential files with owner-only permissions: ```js fs.writeFileSync(filePath, content, { encoding: 'utf8', mode: 0o600, }); ``` 3. After writing Playwright storage state, explicitly set and verify its permissions: ```js await context.storageState({ path: CONFIG.authStateFile }); fs.chmodSync(CONFIG.authStateFile, 0o600); ``` 4. Before reading a credential file, use `lstat` and `stat` to reject symbolic links, unexpected owners, and group/world-readable permissions. 5. Avoid duplicating the refresh token across `.token-cache.json` and `.auth-state.json`. Retain only the minimum authentication material needed for synchronization. 6. Ship a `.gitignore` containing at least: ```gitignore .token-cache.json .auth-state.json .sync-state.json ``` 7. Add startup checks that warn or fail securely if sensitive files are tracked by Git or have unsafe permissions. 8. Document how users can revoke active sessions and refresh tokens after suspected exposure. 9. Consider encrypting any unavoidable on-disk credential cache with a key held by the operating-system credential manager. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The finding indicates the skill may mark notes as synced, reconcile local state, and intentionally skip downloading some older notes while still updating metadata. This creates integrity and auditability risks because users may believe content was fully synchronized when in fact local state was advanced without fetching all note data.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The finding indicates the skill may mark notes as synced, reconcile local state, and intentionally skip downloading some older notes while still updating metadata. This creates integrity and auditability risks because users may believe content was fully synchronized when in fact local state was advanced without fetching all note data.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The finding indicates the skill may mark notes as synced, reconcile local state, and intentionally skip downloading some older notes while still updating metadata. This creates integrity and auditability risks because users may believe content was fully synchronized when in fact local state was advanced without fetching all note data.

Ae1

High
Category
analysis-evasion
Content
cd <skill-dir> && OUTPUT_DIR="<your-output-dir>" node scripts/sync.js
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This markdown file states that notes are automatically synced to local Markdown files and that authentication is silently refreshed, but it does not clearly warn users that note content and authentication artifacts will be written and cached locally. Because the skill handles potentially sensitive transcripts and stores token-related state files, the description should disclose the privacy and local-storage impact more explicitly.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill documentation describes behavior that requires environment access and network access, but it does not declare any tool scope such as permissions or allowed-tools. This weakens least-privilege controls and makes it harder for a user or platform to understand and constrain what the skill can access before execution.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill syncs voice-note transcripts and summaries into local Markdown files, which may contain personal, business, or regulated information, but the documentation does not warn users about this sensitive-data replication. Copying such content to local storage increases exposure through local indexing, backup systems, sharing, and accidental disclosure.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly writes JWTs, refresh tokens, and browser auth state to local cache files but does not warn users that credentials will persist on disk. If those files are readable by other users, included in backups, or committed accidentally, they could enable unauthorized access to the user's biji.com account.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This is a manifest file, so SQP-2 does not apply directly as a code-file check, but the package description is the only visible user-facing description here and it states that the skill syncs data from biji.com to local Markdown files. That implies both network access and local file writes, yet there is no warning or disclosure about these behaviors in the description.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code persists long-lived authentication material, including JWTs, refresh tokens, and browser auth state, to predictable local files in the project directory. If those files are readable by other local users, accidentally committed, included in backups, or exfiltrated by unrelated malware, an attacker can reuse the tokens to access the user's notes account without re-authenticating.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest describes this skill as synchronizing voice notes from biji.com into local Markdown, which implies fetching and writing note content locally. This script instead performs local cleanup by identifying duplicates and permanently deleting files, a distinct maintenance/destructive behavior not conveyed by the manifest description.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script irreversibly deletes files with fs.unlinkSync() without any confirmation, dry-run, or recovery mechanism. In a skill that manages user notes, this increases the chance of accidental data loss from misconfiguration of OUTPUT_DIR, malformed note_id extraction, or incorrect duplicate-selection heuristics.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
For a skill described as syncing voice notes to local Markdown, reading local files and writing synced notes are expected. Automatically deleting files is a stronger destructive capability that is not obviously required by the stated purpose, especially in a standalone deduplication utility with no confirmation step.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This file generates multiple user-visible strings exclusively in Chinese, including note categories, section headings, and content-matching logic, such as '客户', '会议', '附件', and '原文'. Because the skill forces a specific language in its outputs with no documented language selection or opt-in, it may violate language/locale policy requirements.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script extracts authentication material from browser localStorage and persists the JWT to a plaintext cache file on disk. While this appears intended to support offline reuse and automatic sync rather than credential theft, storing bearer tokens locally increases exposure to local compromise, accidental inclusion in backups or repos, and token reuse by other processes if filesystem permissions are weak.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The file hard-codes Chinese strings such as "Get笔记" and "原文" in output paths and filenames, and later uses the same locale-specific terms in console output. This imposes a specific language/locale on users and generated artifacts without offering a language choice or documenting a region-specific requirement.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The natural-language content of the skill documentation is entirely in Chinese, which can impose a specific language on users without offering an alternative or explicit opt-in. Under the language/locale policy, this is a potential policy violation unless the locale restriction is documented and justified.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "Sky Chen",
  "license": "MIT",
  "dependencies": {
    "playwright": "^1.40.0"
  },
  "engines": {
    "node": ">=18.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The script emits multiple user-facing status messages in Chinese, such as the banner and step descriptions, with no indication that the skill is intentionally region-specific or that users can choose another language. This can violate a language/locale policy when the skill is expected to be generally usable across locales.

Natural-Language Policy Violations

Low
Confidence
75% confidence
Finding
The code is bound to `https://www.biji.com/note`, and surrounding messages imply a fixed workflow for that site without any indication that this is a region- or locale-specific skill. Under the language/locale policy, fixed locale behavior should be justified or made explicit to users.

Static analysis

Detected: suspicious.potential_exfiltration

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/api.js:32