Back to skill

Security audit

Private Secrets

Security checks for vulnerabilities and agentic risk

Overview

This is a simple local secret manager, but it stores credentials in plaintext and its helper script can let crafted secret names or values execute code.

Review this before installing. Do not store real API keys, passwords, tokens, or production credentials with this version unless the script is fixed to treat inputs as data, use a secure secret store or encrypted file with strict permissions, and avoid printing secrets by default.

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
private-secrets.sh:24
Finding

Arbitrary JavaScript and OS Command Execution Through Unescaped Secret Inputs

Content
View full analysis

Vulnerability Details

File Location: private-secrets.sh, lines 24-30 and 50-61
Vulnerability Type: JavaScript injection leading to arbitrary command execution
Risk Level: High

Vulnerable code:

bash
      # Use node to update JSON
      node -e "
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('$SECRETS_FILE', 'utf8'));
data['$NAME'] = '$VALUE';
fs.writeFileSync('$SECRETS_FILE', JSON.stringify(data, null, 2));
console.log('已添加: $NAME');
"

The get operation uses the same unsafe source-code construction:

bash
      node -e "
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('$SECRETS_FILE', 'utf8'));
if (data['$NAME']) {
    console.log(data['$NAME']);
} else {
    console.log('未找到: $NAME');
    process.exit(1);
}
"

Technical Analysis

The script inserts the user-controlled NAME and VALUE arguments directly into JavaScript source passed to node -e. These values are not encoded, escaped, validated, or passed as data parameters.

An attacker can include JavaScript quote delimiters and statements in a value, terminate the intended string literal, and inject additional JavaScript. Because Node.js exposes APIs such as require('child_process'), successful JavaScript injection can be escalated directly to operating-system command execution.

Shell quoting around the node -e argument does not prevent this vulnerability. The shell first substitutes the variables, and Node.js subsequently parses the resulting text as executable JavaScript source.

Attack Path

  1. The attacker gains the ability to invoke the skill or influence a secret name or value supplied to its add command.
  2. The attacker supplies a value containing a closing quote, an injected JavaScript statement, and a JavaScript comment marker. A conceptual payload can invoke require('child_process').execSync(...).
  3. The shell expands $VALUE inside ...[truncated 1369 chars]
Remediation
View remediation

Remediation Suggestions

  • Never concatenate user input into source passed to node -e.

  • Move the JavaScript implementation into a standalone script and obtain the command, name, value, and file path through process.argv.

  • If an inline program must be retained, pass values after the program and read them from process.argv, for example:

    bash
    node - "$SECRETS_FILE" "$NAME" "$VALUE" <<'NODE'
    const fs = require('fs');
    const [file, name, value] = process.argv.slice(2);
    const data = JSON.parse(fs.readFileSync(file, 'utf8'));
    data[name] = value;
    fs.writeFileSync(file, JSON.stringify(data, null, 2));
    NODE
    
  • Validate secret names against an explicit policy, such as a conservative length limit and an allowlist of letters, digits, underscores, periods, and hyphens.

  • Treat secret values strictly as opaque data; do not attempt to sanitize them for insertion into executable source.

  • Add regression tests using quotes, backslashes, newlines, comment markers, and JavaScript syntax in both names and values.

  • Return generic errors without including untrusted data in an executable or otherwise interpreted context.

T09 · Insecure Skill Coding Practices

Warning
Location
private-secrets.sh:5
Finding

Plaintext Secret Storage Without Enforced File Permissions or Symlink Protection

Content
View full analysis

Vulnerability Details

File Location: private-secrets.sh, lines 5-9 and 27-28; SKILL.md, lines 11 and 31-35
Vulnerability Type: Insecure storage of sensitive information
Risk Level: Medium

Vulnerable initialization code:

bash
SECRETS_FILE="/workspace/skills/private-secrets-1.0.0/secrets.json"

# Initialize file if not exists
if [ ! -f "$SECRETS_FILE" ]; then
    echo '{}' > "$SECRETS_FILE"
fi

Vulnerable write operation:

javascript
const data = JSON.parse(fs.readFileSync('$SECRETS_FILE', 'utf8'));
data['$NAME'] = '$VALUE';
fs.writeFileSync('$SECRETS_FILE', JSON.stringify(data, null, 2));

The documentation explicitly confirms that the file is not encrypted:

markdown
- 此文件存储在本地,未加密
- 建议定期备份
- 如需更高安全性,可使用加密工具手动加密文件

Technical Analysis

API keys, passwords, tokens, and other secrets are serialized directly into a plaintext JSON file. Anyone able to read that file can recover every stored value without requiring the skill or additional authentication.

File initialization uses ordinary shell redirection and does not set a restrictive umask or explicitly apply mode 0600. Consequently, the resulting permissions depend on the caller's environment and may permit access by other local users or processes.

The script also does not verify that the path is a regular file owned by the expected user. It does not reject symbolic links, and its existence check is separate from subsequent reads and writes. If another principal can manipulate the containing directory or path, this creates symlink and time-of-check/time-of-use risks. Node.js then follows the path during readFileSync and writeFileSync.

The use of a predictable, fixed path increases the consequences of weak directory permissions. No atomic replacement strategy or integrity protection is used, so interruptions or concurrent writes can also corrupt the secret database.

Attack Path

**Plainte ...[truncated 1785 chars]

Remediation
View remediation

Remediation Suggestions

  • Prefer an operating-system credential store, managed secrets service, or keychain instead of a plaintext JSON file.
  • If file-based storage is unavoidable, encrypt values using authenticated encryption and keep the encryption key outside the same file and directory.
  • Create the parent directory with mode 0700 and ensure it is owned by the expected account.
  • Set a restrictive umask, such as 077, before creating any secret material.
  • Atomically create the file with exclusive-create semantics and mode 0600; do not rely on default shell redirection permissions.
  • Before every operation, use lstat to reject symbolic links and verify that the target is a regular file owned by the expected user.
  • Open files with protections equivalent to O_NOFOLLOW where the platform supports them.
  • Write updates to a securely created temporary file in the same protected directory, set mode 0600, synchronize it, and atomically rename it over the destination.
  • Add locking or an equivalent concurrency mechanism to prevent lost updates and JSON corruption.
  • Do not recommend ordinary unencrypted backups of the file. Backups must receive encryption and access controls at least as strong as the primary storage.
  • Update the documentation so that it does not characterize plaintext storage as secure and clearly states the local disclosure threat model.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (7)

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The skill explicitly stores secrets in a local plaintext file (secrets.json) and also provides a command to read back secret contents. This creates a clear credential exposure risk: any local process, user, backup, log, or misconfigured permission affecting that path could disclose API keys, passwords, or tokens.

Content

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

md
## 存储位置

`/workspace/skills/private-secrets-1.0.0/secrets.json`

## 使用方式

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 · private-secrets.sh (reported line 5)May include surrounding context.

sh
# Private Secrets Skill - Manage private information

SECRETS_FILE="/workspace/skills/private-secrets-1.0.0/secrets.json"

# Initialize file if not exists
if [ ! -f "$SECRETS_FILE" ]; then

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill description and invocation examples are written entirely in Chinese, including the command phrases users are expected to type. This creates a language policy concern because it implicitly requires Chinese for use and does not indicate that other languages are supported or that the language choice is optional.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script stores secret material in a predictable JSON file on disk and never warns the user that the values are persisted in plaintext. In a shared workspace or environment with backups, logs, snapshots, or lax file permissions, this can expose credentials or other sensitive data to unintended parties.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The get command prints secret contents directly to stdout, which can leak sensitive values into terminal scrollback, logs, transcripts, agent output capture, or other monitoring systems. In an agent skill context, stdout is especially risky because responses may be recorded or relayed automatically.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

User-visible strings in the script are presented in Chinese, and there is no indication that the skill is region-specific or that users can select another language. This creates a natural-language policy concern because the skill imposes a locale without opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

Multiple help and status messages are emitted only in Chinese across listing, retrieval, and usage output. Without documented regional scope or a configurable language option, this constitutes a locale-policy issue in the skill's natural-language interface.

Content

No source excerpt is available for this finding.