T09 · Insecure Skill Coding Practices
- Location
src/cli.ts:13- Finding
Secret Key Stored in Plaintext and Exposed Through CLI Output
- Content
View full analysis
Vulnerability Details
File Location:
src/cli.ts:13-33;src/storage.ts:33-37
Vulnerability Type: Plaintext credential storage and disclosure
Risk Level: HighVulnerable Code
src/storage.ts:33-37:ts export const saveKey = async (api: OpenClawPluginApi, key: string) => { const keyPath = getKeyPath(api); await fs.mkdir(path.dirname(keyPath), { recursive: true }); await fs.writeFile(keyPath, key, "utf-8"); };src/cli.ts:13-33:ts omni .command("status") .description("Show current Secret Key and blacklisted skills") .action(async () => { const keyPath = getKeyPath(api); let keyContent = "❌ NO SECRET KEY SAVED"; try { keyContent = await fs.readFile(keyPath, "utf-8"); } catch (e) { // File doesn't exist yet } const blacklist = await getInterceptedTools(api); console.log("\n" + "=".repeat(50)); console.log("📂 OMNIPERMISSION CONFIGURATION"); console.log("-".repeat(50)); console.log(`🔑 SECRET KEY:\n${keyContent.trim() || "Empty"}`); console.log("-".repeat(50)); console.log( `🚫 BLACKLISTED SKILLS: ${blacklist.length > 0 ? blacklist.join(", ") : "None (Pass-through mode)"}`, );Technical Analysis
The OmniPersona authentication secret is written directly to
omni_key.txtwithout encryption or an explicitly restrictive file mode. The effective permissions consequently depend on the process umask and surrounding state-directory permissions.More critically, the
statuscommand reads the credential and prints its complete value to standard output. This creates additional disclosure channels, including terminal capture, automated logs, support bundles, agent transcripts, and command-execution interfaces exposed to less-trusted users or agents.The project documentation explicitly discusses deployments where an agent can access the OpenClaw CLI. In such a deploy ...[truncated 1322 chars]
- Remediation
View remediation
Remediation Suggestions
- Remove the full secret from the
statuscommand. Display only a masked value or short non-sensitive fingerprint, such as the final four characters. - Store the credential in an operating-system credential manager or secret-management service where available.
- If file storage is unavoidable, create the file with mode
0o600and verify or repair the permissions of existing files:ts await fs.writeFile(keyPath, key, { encoding: "utf-8", mode: 0o600, }); await fs.chmod(keyPath, 0o600); - Ensure the containing state directory is accessible only to the OpenClaw service account.
- Prevent secrets from appearing in application logs, CLI diagnostics, error messages, support bundles, and agent-visible command output.
- Rotate credentials that may already have been exposed through
omnipermission status.
- Remove the full secret from the
