T09 · Insecure Skill Coding Practices
- Location
agentbus_cli.py:154- Finding
Persistent Nostr Private Keys Are Stored with Potentially Overly Permissive Filesystem Permissions
- Content
View full analysis
Vulnerability Details
File Location:
agentbus_cli.py, lines 154-169
Vulnerability Type: Insecure private-key storage
Risk Level: MediumVulnerable Code
python def load_or_create_keys(agent_name: str) -> NostrKeys: key_dir = Path.home() / ".agentbus" / "keys" key_dir.mkdir(parents=True, exist_ok=True) key_path = key_dir / f"{agent_name}.json" if key_path.exists(): try: parsed = json.loads(key_path.read_text()) privkey = parsed.get("privkey") pubkey = parsed.get("pubkey") if privkey and pubkey: return NostrKeys(pubkey=pubkey, privkey=privkey) except Exception: pass privkey_bytes = os.urandom(32) privkey_hex = privkey_bytes.hex() pubkey_hex = _privkey_to_pubkey_hex(privkey_hex) key_path.write_text(json.dumps({"pubkey": pubkey_hex, "privkey": privkey_hex})) return NostrKeys(pubkey=pubkey_hex, privkey=privkey_hex)Technical Analysis
The application stores the long-term Nostr private key in plaintext under
~/.agentbus/keys. Neither the directory nor the key file is created with explicitly restrictive permissions.Actual permissions therefore depend on the process umask. With a common umask of
022, the directory may be created as0755and the key file as0644. On a multi-user system, this can allow other local users to traverse the key directory and read the private-key file.The key path is also written using
Path.write_text()without atomic exclusive creation or symlink protection. If an attacker can manipulate the key directory or target path, this can introduce additional file-replacement or symlink risks.Attack Path
- A victim runs the CLI without
--ephemeral-keys. load_or_create_keys()creates~/.agentbus/keys/<agent>.json.- The operating-system umask results in permissions that allow another local acco ...[truncated 1056 chars]
- A victim runs the CLI without
- Remediation
View remediation
Remediation Suggestions
-
Create the key directory with mode
0700and verify its effective permissions:python key_dir.mkdir(parents=True, mode=0o700, exist_ok=True) os.chmod(key_dir, 0o700) -
Create new key files atomically and exclusively with mode
0600, for example usingos.open()withO_CREAT | O_EXCL | O_WRONLY:python fd = os.open(key_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as handle: json.dump({"pubkey": pubkey_hex, "privkey": privkey_hex}, handle) -
Reject symbolic links and non-regular files before loading an existing key file.
-
Verify that existing key files are owned by the current user and are not accessible to group or other users. Fail closed or repair permissions when this condition is not met.
-
Consider using an operating-system credential store or encrypted keyring rather than a plaintext JSON file.
-
Document key rotation procedures for identities whose key files may already have been exposed.
-
