T09 · Insecure Skill Coding Practices
- 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: MediumVulnerable 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.jsonand.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 += 1Technical Analysis
The
fixcommand loads the entire OpenClaw configuration and serializes it into a plaintext backup usingPath.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 common022umask, the resulting backup may be created as0644, making it readable by other local users.Although the command can apply mode
0600toopenclaw.jsonand.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
- An OpenClaw configuration contains plaintext API keys, tokens, or other sensitive deployment settings.
- A user invokes
python scripts/hardener.py fix. - The command loads the complete configuration and creates a t ...[truncated 1388 chars]
- Remediation
View remediation
Remediation Suggestions
- Create backup files with mode
0600from the outset rather than relying on the process umask. - Use exclusive, atomic creation so an existing file or symbolic link cannot be followed or overwritten.
- Keep the backup in the same trusted configuration directory only after verifying that the directory is not accessible to unauthorized users.
- Verify the final file mode before reporting that the backup was successfully created.
- Include security backups in permission audits and remediation logic.
- Consider warning users that backups preserve any plaintext credentials contained in the original configuration.
- 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
0600or clearly report them as exposed.- Create backup files with mode
