Back to skill

Security audit

Password Generator

Security checks for vulnerabilities and agentic risk

Overview

This password generator does not appear to steal data, but it automatically saves plaintext passwords and uses weak randomness, so it should be reviewed before use.

Install only if you are comfortable with every generated password being printed and appended in plaintext under the OpenClaw workspace memory directory. Prefer a version that uses cryptographic randomness and does not save passwords unless you explicitly opt in, ideally using a real password manager or encrypted secret store.

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/generate_password.py:4
Finding

Passwords Generated with a Non-Cryptographic Pseudorandom Number Generator

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_password.py, lines 4–24
Vulnerability Type: Use of a cryptographically insecure pseudorandom number generator
Risk Level: High

Vulnerable Code

python
import random
import string
import os
from datetime import datetime

def generate_password(min_length=12, max_length=16, use_uppercase=True, use_lowercase=True, use_digits=True, use_symbols=True):
    """生成随机长度随机密码"""
    # 随机选择长度
    length = random.randint(min_length, max_length)
    
    chars = ''
    
    if use_uppercase:
        chars += string.ascii_uppercase
    if use_lowercase:
        chars += string.ascii_lowercase
    if use_digits:
        chars += string.digits
    if use_symbols:
        chars += string.punctuation
    
    if not chars:
        chars = string.ascii_letters + string.digits
    
    password = ''.join(random.choice(chars) for _ in range(length))

Technical Analysis

The script uses Python's random module for both password length selection and character selection. This module is based on the deterministic Mersenne Twister algorithm and is not designed for passwords, authentication secrets, session tokens, or other security-sensitive values.

If an attacker can infer or recover the generator state—for example, by observing enough outputs, accessing the process state, or correlating outputs with other uses of the same process-wide generator—the attacker may predict subsequent generated passwords. The use of a broad character set does not compensate for a predictable random source.

In addition, selecting every character independently from one combined pool does not guarantee the documented inclusion of at least one uppercase letter, one lowercase letter, one digit, and one symbol.

Attack Path

  1. A victim invokes the skill to generate a password and uses that password for an account or protected resource.
  2. An attacker ...[truncated 1053 chars]
Remediation
View remediation

Remediation Suggestions

  • Replace random.choice() with secrets.choice().
  • Select the length using secrets.randbelow(max_length - min_length + 1) + min_length.
  • Build separate enabled character classes and select at least one character from each enabled class.
  • Fill the remaining positions using the combined enabled character pool.
  • Securely randomize the final character order using a cryptographically secure Fisher–Yates shuffle driven by secrets.randbelow().
  • Validate that min_length can accommodate the number of required character classes.
  • Add tests verifying the length bounds and required character-class guarantees.

Example secure design:

python
import secrets
import string

def secure_shuffle(items):
    for index in range(len(items) - 1, 0, -1):
        swap_index = secrets.randbelow(index + 1)
        items[index], items[swap_index] = items[swap_index], items[index]

def generate_password(min_length=12, max_length=16):
    classes = [
        string.ascii_uppercase,
        string.ascii_lowercase,
        string.digits,
        string.punctuation,
    ]

    if min_length < len(classes) or max_length < min_length:
        raise ValueError("Invalid password length constraints")

    length = min_length + secrets.randbelow(max_length - min_length + 1)
    characters = [secrets.choice(character_class) for character_class in classes]
    combined = ''.join(classes)
    characters.extend(
        secrets.choice(combined) for _ in range(length - len(characters))
    )
    secure_shuffle(characters)
    return ''.join(characters), length

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_password.py:34
Finding

Generated Passwords Are Persisted in Plaintext Without Enforced Access Controls

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_password.py, lines 34–56
Vulnerability Type: Plaintext storage of sensitive credentials with implicit filesystem permissions
Risk Level: Medium

Vulnerable Code

python
    # 保存到文件
    memory_dir = '/root/.openclaw/workspace/memory'
    os.makedirs(memory_dir, exist_ok=True)
    
    password_file = os.path.join(memory_dir, 'passwords.md')
    date = datetime.now().strftime('%Y-%m-%d')
    
    # 添加新密码
    new_entry = f"""
## {date}

- **随机密码**
  - 密码: `{password}`
  - 长度: {length} 位 (12-16位随机)
  - 字符: 大小写字母 + 数字 + 符号
"""
    
    with open(password_file, 'a') as f:
        f.write(new_entry)
    
    print(f"\n密码已保存到: {password_file}")
    return password, length

Technical Analysis

Every generated password is appended in plaintext to /root/.openclaw/workspace/memory/passwords.md. The script does not request explicit per-use consent, provide a non-persistent default, encrypt the stored credentials, enforce retention limits, or explicitly set owner-only permissions.

The effective permissions of the directory and file depend on the existing filesystem state and process umask. Therefore, this finding does not establish that the file is universally readable on every installation. However, the code itself does not guarantee that only the intended user can read it. Storing credentials in an agent memory directory also increases exposure to workspace readers, backup systems, indexing processes, later agent operations, and accidental disclosure.

The behavior is documented in SKILL.md, so it is not hidden. Nevertheless, documenting plaintext credential retention does not remove the confidentiality risk.

Attack Path

  1. A user invokes the skill one or more times, causing generated passwords to accumulate in passwords.md.
  2. The user applies one or more generated passwords to accounts or protected resources ...[truncated 1096 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not persist generated passwords by default.
  • Return the password only to the requesting user and require explicit opt-in before writing it to disk.
  • Prefer integration with a trusted password manager rather than an agent memory file.
  • If local storage is unavoidable, create the directory with mode 0700 and the file with mode 0600; verify permissions even when they already exist.
  • Use an atomic, no-follow file creation strategy to reduce symlink and race-condition risks.
  • Encrypt retained credentials using a key that is not stored beside the encrypted data.
  • Add retention controls so stale credentials are deleted rather than accumulated indefinitely.
  • Avoid printing secrets to logs or interfaces that may retain command output.
  • Clearly disclose the destination, retention behavior, and security properties before storage.

For owner-only file creation, use an explicit descriptor rather than relying solely on the process umask:

python
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND
if hasattr(os, "O_NOFOLLOW"):
    flags |= os.O_NOFOLLOW

fd = os.open(password_file, flags, 0o600)
os.chmod(password_file, 0o600)

with os.fdopen(fd, "a", encoding="utf-8") as password_output:
    password_output.write(new_entry)

This permission hardening reduces local exposure but does not address the broader risk of retaining plaintext credentials. Avoiding persistence or using a dedicated encrypted password manager remains preferable.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose is password generation, but the actual behavior also persistently stores the generated password in a local file under memory. This is dangerous because it silently converts a secret-generation function into secret retention, creating credential exposure risk if memory files are later read, indexed, synced, or leaked.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill lacks a clear warning that generated passwords are automatically written to a memory file. Because passwords are highly sensitive secrets, undisclosed persistence materially increases the risk of credential compromise through later access to memory artifacts.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill persistently writes newly generated passwords to /root/.openclaw/workspace/memory/passwords.md even though its stated purpose is only to generate passwords. Storing secrets on disk unnecessarily expands exposure to later compromise, accidental disclosure, backup leakage, or access by other tools/users on the system.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Persistent storage of generated passwords is unjustified for a password-generation utility and directly contradicts the principle of minimizing secret exposure. A generated password should be ephemeral unless the user explicitly requests secure storage, because plaintext archival creates a durable target for attackers.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill performs file-write behavior by instructing execution of a script and saving generated passwords to a local memory file, but it does not declare any tool scope or permissions. This weakens reviewability and containment, and can allow password material to be persisted without explicit authorization boundaries.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger phrases are broad and map to very common user requests such as generating or creating a password, without clarifying that invocation will also write the password to persistent memory. Broad activation increases the chance the skill runs in contexts where the user only expected ephemeral output, causing unintended secret storage.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description and all user-facing instructions are written only in Chinese, including the trigger phrases the skill expects. There is no indication that the skill is intentionally limited to a Chinese-speaking or region-specific context, and no user opt-in or language choice is offered.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script only informs the user after saving the password, which means the user is not given meaningful prior notice or consent before a sensitive secret is persisted. This increases the chance that users will unknowingly leave credentials in plaintext on disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

Natural-language strings and docstrings throughout the script are entirely in Chinese, including user-facing output, with no indication that language selection is configurable or intentionally region-scoped. Under the policy, forcing a specific language without user opt-in can be a locale-policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.