Back to skill

Security audit

Archon Keymaster

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent DID toolkit, but it needs review because it handles wallet secrets while using unsafe remote/dependency execution patterns and plaintext private-key handling.

Review before installing. Use a test wallet first, avoid running the documented curl | sh command, install and pin dependencies separately before loading ARCHON_PASSPHRASE, and treat any mnemonic or nsec output as highly sensitive because logs or agent transcripts may capture it.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:359
Finding
Mutable Remote Installer Is Piped Directly into a Shell<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:359-362` **Vulnerability Type**: Unverified remote code execution **Risk Level**: Critical ### Vulnerable Code ```bash Install `nak` CLI: ```bash curl -sSL https://raw.githubusercontent.com/fiatjaf/nak/master/install.sh | sh ``` ``` ### Technical Analysis The installation instructions download a shell script from the mutable `master` branch of a third-party GitHub repository and immediately execute it. The command does not pin an immutable commit, verify a checksum or signature, or provide an opportunity to review the downloaded artifact before execution. Consequently, the code that executes can change after this Skill has been reviewed. A compromise of the repository, maintainer account, release process, DNS or TLS trust chain could turn this prerequisite into arbitrary code execution. Executing a remote installer is not required for the Skill’s core DID functionality and exceeds the minimum privileges necessary for documenting a Nostr CLI prerequisite. ### Attack Path 1. An attacker compromises the referenced repository or its maintainer account, or otherwise causes malicious content to be served from the URL. 2. The attacker modifies `install.sh` on the mutable `master` branch. 3. A user follows the Skill’s documented prerequisite command. 4. `curl` downloads the modified content and streams it directly to `sh`. 5. The payload executes with the user’s privileges before its contents or integrity can be inspected. 6. The payload can access the user’s Archon wallet, plaintext passphrase file, Nostr key, files, network credentials, and any other resources available to that account. ### Impact Assessment Successful exploitation grants arbitrary command execution with all privileges of the invoking user. In the expected deployment context, that account may have access to: - `~/.archon.env`, including `ARCHON_PASSPHRASE` - `~/.archon.wallet.json` - `~/.clawstr/secret.key` - Derived identity k ...[truncated 227 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `curl | sh` instruction. 2. Use an immutable, reviewed release artifact or commit rather than a mutable branch. 3. Download the artifact separately and verify a maintainer-published cryptographic signature or pinned SHA-256 checksum before execution. 4. Display or document the downloaded script so users can review it before running it. 5. Prefer a trusted operating-system package manager or an official, versioned release channel. 6. Run installation without wallet secrets loaded into the environment and with the least privileges possible. 7. Document the exact expected version and integrity value. A safer pattern is: ```bash curl --fail --show-error --location \ --output nak-install.sh \ "https://raw.githubusercontent.com/fiatjaf/nak/<immutable-commit>/install.sh" printf '%s %s\n' '<trusted-sha256>' 'nak-install.sh' | sha256sum --check - less nak-install.sh sh nak-install.sh ``` ]]>

T08 · Insecure Dependencies

Error
Location
scripts/nostr/derive-nostr.sh:10
Finding
Unpinned npm and npx Dependencies Execute in a Secret-Bearing Environment<![CDATA[ ## Vulnerability Details **File Location**: `scripts/nostr/derive-nostr.sh:10-24`; representative wallet operations at `scripts/identity/create-id.sh:99-116`; bare `npx @didcid/keymaster` calls occur throughout the script suite **Vulnerability Type**: Unsafe dependency retrieval and execution **Risk Level**: High ### Vulnerable Code ```bash # Get mnemonic from keymaster MNEMONIC=$(npx @didcid/keymaster show-mnemonic 2>/dev/null) if [ -z "$MNEMONIC" ]; then echo "Error: Could not get mnemonic. Is ARCHON_PASSPHRASE set?" >&2 exit 1 fi # Install dependencies if needed if [ ! -f "$DEPS_DIR/node_modules/bip39/package.json" ]; then echo "Installing dependencies..." >&2 mkdir -p "$DEPS_DIR" cd "$DEPS_DIR" npm install --silent bip39 @scure/bip32 secp256k1 bech32 >/dev/null 2>&1 cd - >/dev/null fi ``` Representative sensitive wallet invocations: ```bash WALLET_OUTPUT=$(npx @didcid/keymaster create-wallet 2>&1) echo "$WALLET_OUTPUT" # Display mnemonic explicitly (CLI no longer prints it during create-wallet) npx @didcid/keymaster show-mnemonic | tr -d '\r' read -p "Name for your DID (e.g., 'main', 'work'): " DID_NAME DID_NAME="${DID_NAME:-main}" echo "" echo "Creating DID '$DID_NAME'..." npx @didcid/keymaster create-id "$DID_NAME" ``` ### Technical Analysis The project does not include a package manifest or lockfile pinning the versions and integrity hashes of `@didcid/keymaster`, `bip39`, `@scure/bip32`, `secp256k1`, and `bech32`. The Nostr script invokes `npm install` without versions. npm installation may execute package lifecycle scripts. Bare `npx @didcid/keymaster` may retrieve and execute the current registry version when the package is not already installed locally. These operations inherit the caller’s environment, which is expected to contain `ARCHON_PASSPHRASE` and `ARCHON_WALLET_PATH`. Because package resolution is mutable, a compromised maintainer account, registry entry, transitive dependency, or newly published malicious ...[truncated 1392 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a project-level `package.json` and committed lockfile with exact versions and integrity metadata. 2. Install dependencies in a controlled setup phase with `npm ci`, not dynamically during secret-bearing operations. 3. Use `npm ci --ignore-scripts` where dependency lifecycle scripts are not required. 4. Audit any lifecycle scripts that cannot be disabled. 5. Invoke a pinned local executable, such as `./node_modules/.bin/keymaster`, rather than bare `npx`. 6. Prevent `npx` from installing missing packages at runtime, for example by using an equivalent of `--no-install` or `--offline` supported by the deployed npm version. 7. Perform dependency installation before exporting `ARCHON_PASSPHRASE`. 8. Use a sanitized environment for package-management operations. 9. Do not suppress all npm diagnostics; retain auditable installation logs that exclude secrets. 10. Add automated dependency integrity and provenance checks to the release process. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/nostr/derive-nostr.sh:29
Finding
Nostr Derivation Prints a Private Key and Encourages Plaintext Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/nostr/derive-nostr.sh:29-54`; related storage instructions at `SKILL.md:370-377` **Vulnerability Type**: Sensitive key disclosure through output and plaintext storage **Risk Level**: High ### Vulnerable Code ```bash # Run derivation cd "$DEPS_DIR" node --input-type=commonjs <<EOF const bip39 = require('bip39'); const { HDKey } = require('@scure/bip32'); const secp256k1 = require('secp256k1'); const { bech32 } = require('bech32'); const mnemonic = '${MNEMONIC}'; const seed = bip39.mnemonicToSeedSync(mnemonic); const hdkey = HDKey.fromMasterSeed(seed); // Archon uses Bitcoin BIP44 path const derived = hdkey.derive("m/44'/0'/0'/0/0"); const privKey = derived.privateKey; const pubKey = secp256k1.publicKeyCreate(privKey, false); const pubKeyX = Buffer.from(pubKey.slice(1, 33)).toString('hex'); function toBech32(prefix, hex) { const bytes = Buffer.from(hex, 'hex'); const words = bech32.toWords(bytes); return bech32.encode(prefix, words, 1000); } const nsec = toBech32('nsec', Buffer.from(privKey).toString('hex')); const npub = toBech32('npub', pubKeyX); console.log('nsec:', nsec); console.log('npub:', npub); console.log('pubkey:', pubKeyX); EOF ``` The documentation then recommends: ```bash mkdir -p ~/.clawstr echo "nsec1..." > ~/.clawstr/secret.key chmod 600 ~/.clawstr/secret.key ``` ### Technical Analysis The script obtains the wallet’s master mnemonic, embeds it into generated JavaScript, derives a private key, and prints the resulting `nsec` directly to standard output. Standard output may be retained by agent transcripts, terminal logging, CI logs, shell wrappers, monitoring systems, or redirected files. The documentation recommends storing the private key as plaintext. It applies `chmod 600` only after creating the file, so the file’s initial permissions depend on the caller’s `umask`. On a permissive configuration, another local user could potentially read the key during the interva ...[truncated 1612 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not print `nsec` by default. Print only the public `npub` and public key. 2. Require an explicit, interactive opt-in before exporting private material. 3. Store private keys in an operating-system keychain, hardware-backed store, or dedicated encrypted keystore. 4. If file export is required, set `umask 077` before creation and create the file atomically with mode `0600`. 5. Refuse to overwrite an existing key file or follow symlinks. 6. Keep secret output separate from normal output and prevent agent or CI logging. 7. Pass mnemonic material through a protected file descriptor or stdin rather than interpolating it into generated source code. 8. Clear shell variables containing the mnemonic as soon as derivation completes. 9. Warn users that terminal transcripts, agent conversations, clipboard history, and command logs must not contain `nsec`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/crypto/verify-file.sh:36
Finding
Predictable Temporary Files Allow Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/crypto/verify-file.sh:36-60`; related deterministic sidecar at `scripts/crypto/sign-file.sh:44-56` **Vulnerability Type**: Insecure temporary-file handling and local symlink race **Risk Level**: Medium ### Vulnerable Code From `scripts/crypto/verify-file.sh`: ```bash # Verify file if npx @didcid/keymaster verify-file "$FILE" 2>&1 | tee /tmp/verify-output.txt; then echo "" echo "✓ Signature valid" # Try to extract signer info from file if command -v jq >/dev/null 2>&1; then SIGNER=$(jq -r '.proof.verificationMethod // .issuer // "unknown"' "$FILE" 2>/dev/null) CREATED=$(jq -r '.proof.created // "unknown"' "$FILE" 2>/dev/null) if [ "$SIGNER" != "unknown" ]; then echo "Signed by: $SIGNER" fi if [ "$CREATED" != "unknown" ]; then echo "Signed at: $CREATED" fi fi else echo "" echo "✗ Signature verification FAILED" echo "" echo "This file may have been tampered with, or the signature is invalid." exit 1 fi rm -f /tmp/verify-output.txt ``` From `scripts/crypto/sign-file.sh`: ```bash # Create temp file for safety TEMP_FILE="${FILE}.signing.tmp" # Sign file (outputs to stdout) if npx @didcid/keymaster sign-file "$FILE" > "$TEMP_FILE" 2>&1; then # Replace original with signed version mv "$TEMP_FILE" "$FILE" echo "✓ File signed (signature added in place)" echo "" echo "Others can verify with:" echo " ./verify-file.sh $FILE" else echo "✗ Signing failed" rm -f "$TEMP_FILE" exit 1 fi ``` ### Technical Analysis `verify-file.sh` writes to the globally predictable path `/tmp/verify-output.txt`. Shell redirection through `tee` follows symbolic links. A local attacker can create that path as a symlink to a file writable by the victim, causing the target to be truncated and replaced with verification output. The temporary output is not subsequently rea ...[truncated 2068 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `/tmp/verify-output.txt` entirely because the script never reads it: ```bash if npx @didcid/keymaster verify-file "$FILE" 2>&1; then ... fi ``` 2. If temporary output must be retained, create it securely: ```bash umask 077 TMPFILE=$(mktemp "${TMPDIR:-/tmp}/archon-verify.XXXXXX") trap 'rm -f -- "$TMPFILE"' EXIT HUP INT TERM ``` 3. For signing, create the temporary file with `mktemp` in the same directory as the destination so final replacement remains atomic. 4. Check that the temporary path is a regular file and was created exclusively by the current process. 5. Do not follow symbolic links; use platform facilities supporting `O_NOFOLLOW` and exclusive creation where available. 6. Validate that signing output is valid JSON and contains the expected signature before replacing the original. 7. Preserve appropriate ownership and permissions when replacing the original file. 8. Quote cleanup variables safely and install cleanup traps immediately after temporary-file creation. 9. Consider retaining a backup or requiring explicit consent before destructive in-place replacement. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (184)

Credential Access

High
Category
Privilege Escalation
Content
**Credential Systems:**
- Proof of humanity (verified by trusted sources)
- Skill certifications
- Access tokens
- Age verification (prove >18 without revealing exact age)

## How It Works
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The supplied code chunk's primary function is transferring an asset from one DID to another using `transfer-asset`. That capability is not represented in the declared description, which focuses on identity management, credentials, messaging, Nostr, file crypto, aliases, authorization, groups, and polls. While the script operates in the broader Archon/DID ecosystem, asset transfer is a distinct undeclared capability and materially different from the listed functions. The environment loading is incidental support logic, not the mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code chunk is narrowly focused on updating an asset's image by taking an asset DID and a local image file, validating environment configuration, and calling a keymaster command. The declared description lists many DID-related capabilities, but does not mention asset management or updating asset images. While this may be adjacent to identity/DID tooling, the specific primary purpose of this script is materially different from the explicitly declared features, so this is best classified as a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The supplied code chunk has a narrow purpose: updating an asset record by DID using JSON input, relying on Archon wallet environment variables. The declared description covers many DID-related functions, but it does not mention asset creation or asset updating. Since this code exposes a specific capability—asset management/update—that is not represented in the declared purpose, this is a description-behavior mismatch. The environment loading is only supportive and not itself a mismatch.

Ae1

High
Category
analysis-evasion
Content
./scripts/schemas/create-schema.sh <schema-file.json>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/schemas/create-schema.sh <schema-file.json>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/schemas/create-schema.sh <schema-file.json>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/schemas/list-schemas.sh
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/schemas/get-schema.sh <schema-did-or-alias>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Chaining Abuse

High
Category
Tool Misuse
Content
Install `nak` CLI:
```bash
curl -sSL https://raw.githubusercontent.com/fiatjaf/nak/master/install.sh | sh
```

### Derive Nostr Keys
Confidence
99% confidence
Finding
`curl ... | sh` chains network retrieval directly into shell execution, eliminating any inspection step and enabling instant arbitrary code execution from a mutable remote source. In this skill's context, that code may then access wallet files, passphrases, and other cryptographic key material, making the blast radius severe.

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/vote-poll.sh <poll-did> <vote-index>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/view-poll.sh did:cid:bagaaiera...
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/view-poll.sh did:cid:bagaaiera...
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/view-poll.sh did:cid:bagaaiera...
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/send-ballot.sh "$BALLOT" "$POLL"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/update-poll.sh "$BALLOT"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/view-ballot.sh "$BALLOT"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/send-poll.sh <poll-did>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/polls/send-poll.sh <poll-did>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Static analysis

No suspicious patterns detected.