Back to skill

Security audit

Security Hardener

Security checks for vulnerabilities and agentic risk

Overview

This security-hardening skill is mostly purpose-aligned, but its fix mode can copy sensitive OpenClaw configuration into a persistent backup without explicitly securing that backup file.

Review the script before using fix mode. Prefer running audit first, make your own protected backup, and avoid running fix on configs containing plaintext secrets until backup files are created with 0600 permissions or manually chmodded afterward.

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/hardener.py:284
Finding

Sensitive Configuration Backup May Be Created with Permissive Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/hardener.py:284-286
Vulnerability Type: Insecure plaintext backup permissions
Risk Level: Medium

Vulnerable Code

python
# Create backup
backup = config_path.with_suffix(f".json.bak-security-{datetime.now().strftime('%Y%m%d-%H%M%S')}")
backup.write_text(json.dumps(config, indent=2))
print(f"Backup saved to: {backup}")

The subsequent permission-hardening logic only covers openclaw.json and .env, not the newly created backup:

python
# Fix permissions
if not only or "permissions" in only:
    for filename in ["openclaw.json", ".env"]:
        filepath = config_path.parent / filename
        if filepath.exists():
            os.chmod(filepath, 0o600)
            print(f"  Fixed: {filename} permissions → 600")
            fixes_applied += 1

Technical Analysis

The fix command loads the entire OpenClaw configuration and serializes it into a plaintext backup using Path.write_text(). The project explicitly recognizes that this configuration may contain API keys or tokens.

Path.write_text() does not request a restrictive file mode. When creating a new file, its permissions are determined by the default creation mode and the process umask. Under a common 022 umask, the resulting backup may be created as 0644, making it readable by other local users.

Although the command can apply mode 0600 to openclaw.json and .env, the backup filename is not included in that operation. Therefore, running a security-hardening command can create a less-protected copy of the same sensitive data. The fixed filename pattern also lacks exclusive-creation safeguards, so defensive creation should additionally avoid writing through a pre-existing path.

Attack Path

  1. An OpenClaw configuration contains plaintext API keys, tokens, or other sensitive deployment settings.
  2. A user invokes python scripts/hardener.py fix.
  3. The command loads the complete configuration and creates a t ...[truncated 1388 chars]
Remediation
View remediation

Remediation Suggestions

  1. Create backup files with mode 0600 from the outset rather than relying on the process umask.
  2. Use exclusive, atomic creation so an existing file or symbolic link cannot be followed or overwritten.
  3. Keep the backup in the same trusted configuration directory only after verifying that the directory is not accessible to unauthorized users.
  4. Verify the final file mode before reporting that the backup was successfully created.
  5. Include security backups in permission audits and remediation logic.
  6. Consider warning users that backups preserve any plaintext credentials contained in the original configuration.
  7. Where practical, avoid retaining plaintext credentials in configuration files and support environment-variable references without copying resolved secret values.

A safer implementation can use low-level exclusive creation:

python
backup = config_path.with_suffix(
    f".json.bak-security-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
)
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
fd = os.open(backup, flags, 0o600)

try:
    with os.fdopen(fd, "w", encoding="utf-8") as backup_file:
        json.dump(config, backup_file, indent=2)
        backup_file.write("\n")
except Exception:
    try:
        backup.unlink()
    except FileNotFoundError:
        pass
    raise

if stat.S_IMODE(backup.stat().st_mode) != 0o600:
    raise PermissionError(f"Backup has unsafe permissions: {backup}")

As defense in depth, the permission checker should inspect backup files matching the application's backup naming convention and either harden them to 0600 or clearly report them as exposed.

Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (11)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
| Check | Severity | Description |
|-------|----------|-------------|
| `gateway-bind` | CRITICAL | Gateway not bound to loopback |
| `exposed-keys` | CRITICAL | API keys in config instead of .env |
| `insecure-auth` | HIGH | `allowInsecureAuth` or `dangerouslyDisableDeviceAuth` enabled |
| `exec-sandbox` | HIGH | exec sandbox mode not set to restricted |
| `file-perms` | HIGH | Config files readable by others (not 600) |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/hardener.py (reported line 205)May include surrounding context.

python
| Check | Severity | Description |
|-------|----------|-------------|
| `gateway-bind` | CRITICAL | Gateway not bound to loopback |
| `exposed-keys` | CRITICAL | API keys in config instead of .env |
| `insecure-auth` | HIGH | `allowInsecureAuth` or `dangerouslyDisableDeviceAuth` enabled |
| `exec-sandbox` | HIGH | exec sandbox mode not set to restricted |
| `file-perms` | HIGH | Config files readable by others (not 600) |

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 · scripts/hardener.py (reported line 26)May include surrounding context.

python
(r"gsk_[a-zA-Z0-9]{20,}", "Groq API Key"),
    (r"AIza[a-zA-Z0-9_-]{35}", "Google API Key"),
    (r"tvly-[a-zA-Z0-9_-]{20,}", "Tavily API Key"),
    (r"ghp_[a-zA-Z0-9]{36}", "GitHub Personal Access Token"),
    (r"xoxb-[0-9]+-[a-zA-Z0-9]+", "Slack Bot Token"),
    (r"sk-ant-oat01-[a-zA-Z0-9_-]+", "Anthropic OAuth Token"),
]

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/hardener.py (reported line 216)May include surrounding context.

python
"""Check that sensitive files have correct permissions."""
    c = SecurityCheck("file-perms", "HIGH", "Sensitive files should have 600 permissions")
    issues = []
    for filename in ["openclaw.json", ".env"]:
        filepath = config_dir / filename
        if filepath.exists():
            mode = filepath.stat().st_mode

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/hardener.py (reported line 225)May include surrounding context.

python
"""Check that sensitive files have correct permissions."""
    c = SecurityCheck("file-perms", "HIGH", "Sensitive files should have 600 permissions")
    issues = []
    for filename in ["openclaw.json", ".env"]:
        filepath = config_dir / filename
        if filepath.exists():
            mode = filepath.stat().st_mode

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/hardener.py (reported line 327)May include surrounding context.

python
"""Check that sensitive files have correct permissions."""
    c = SecurityCheck("file-perms", "HIGH", "Sensitive files should have 600 permissions")
    issues = []
    for filename in ["openclaw.json", ".env"]:
        filepath = config_dir / filename
        if filepath.exists():
            mode = filepath.stat().st_mode

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The markdown advertises an "Auto-Fix" feature with automatic backup, which implies configuration changes, but it does not clearly warn users that running the fix mode will modify system files. Under the markdown-file criteria, behaviors that affect user data or system integrity should be disclosed plainly to the user.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README shows commands for fix and for writing a report to security-audit.md, but it does not include an accompanying warning that fix mode alters the deployment or that report mode writes output files. For markdown descriptions, potentially integrity-affecting or data-writing behaviors should be called out explicitly.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises and instructs use of file read, file write, and shell capabilities, including an auto-fix mode that modifies configuration files, but it does not declare any explicit tool scope or permission boundaries. This creates an avoidable trust and containment gap: an agent may invoke broad filesystem or shell actions without the skill clearly constraining what paths or commands are permitted.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/hardener.py (reported line 225)May include surrounding context.

python
issues.append(f"{filename}: {oct(perms)} (readable by group/others)")
    if issues:
        c.details = "; ".join(issues)
        c.fix = "Run: chmod 600 ~/.openclaw/openclaw.json ~/.openclaw/.env"
    else:
        c.passed = True
        c.details = "File permissions are correct"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The auto-fix command modifies the target configuration and changes file permissions immediately once invoked, with no interactive confirmation, dry-run default, or per-change approval. In a security-hardening tool this is not inherently malicious, but it is a real safety issue because a mistaken path, automation misuse, or unexpected config structure could cause unintended or disruptive changes.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.