Back to skill

Security audit

Koan Team

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly coherent for Koan team coordination, but it bundles broad SDK code with persistent identity keys and network behaviors that deserve careful review before installation.

Install only if you are comfortable with a Koan SDK keeping long-lived identity keys and chat history under ~/.koan. Prefer an OS keychain or encrypted vault, keep the directory URL at the default HTTPS Koan endpoint unless you intentionally trust another server, and avoid using untrusted peer identifiers with the bundled chat-log helpers.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
python/koan_sdk.py:177
Finding
Private Keys Stored in Plaintext on Linux and Other Unsupported Platforms<![CDATA[ ## Vulnerability Details **File Location**: - `python/koan_sdk.py:177-203` - `node/koan-sdk.mjs:152-173` **Vulnerability Type**: Plaintext storage of cryptographic private keys **Risk Level**: High ### Vulnerable Code Python implementation: ```python signing_private_key = base64.b64encode( self._signing_key.private_bytes( serialization.Encoding.DER, serialization.PrivateFormat.PKCS8, serialization.NoEncryption(), ) ).decode() encryption_private_key = base64.b64encode( self._encryption_key.private_bytes( serialization.Encoding.DER, serialization.PrivateFormat.PKCS8, serialization.NoEncryption(), ) ).decode() data = { 'koanId': self.koan_id, 'signingPublicKey': self.signing_public_key_b64, 'encryptionPublicKey': self.encryption_public_key_b64, } private_blob = json.dumps({ 'signingPrivateKey': signing_private_key, 'encryptionPrivateKey': encryption_private_key, }) if sys.platform.startswith('win'): data['privateKeyStorage'] = {'scheme': 'windows-dpapi'} data['protectedPrivateKeys'] = _dpapi_protect_text(private_blob) elif sys.platform == 'darwin': account = _macos_keychain_account(self.signing_public_key_b64) _macos_keychain_set(account, private_blob) data['privateKeyStorage'] = { 'scheme': 'macos-keychain', 'service': KEYCHAIN_SERVICE, 'account': account, } else: data['privateKeyStorage'] = {'scheme': 'plaintext'} data['signingPrivateKey'] = signing_private_key data['encryptionPrivateKey'] = encryption_private_key IDENTITY_FILE.write_text(json.dumps(data, indent=2), encoding='utf-8') try: os.chmod(IDENTITY_FILE, 0o600) except Exception: pass ``` Node.js implementation: ```javascript const signingPrivateKey = this._signingPrivateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64'); const encryptionPrivateKey = this._encryptionPrivateKey.export({ type: 'pkcs8', format: 'der' }).t ...[truncated 3216 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not silently fall back to plaintext private-key storage. 2. Integrate with a supported OS credential store on Linux, such as Secret Service through an established keyring library. 3. Where no credential store is available, encrypt PKCS#8 private keys using a user-supplied passphrase and a modern password-based key derivation function. 4. Prefer hardware-backed or vault-backed signing so the private signing key is non-exportable. 5. Fail closed or require explicit, prominently displayed approval before using plaintext storage. 6. Retain restrictive permissions, but treat them as defense in depth rather than encryption. 7. Document key rotation and revocation procedures for previously generated plaintext identities. 8. Avoid retaining decoded private-key strings longer than necessary and clear temporary buffers where the runtime permits. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
python/koan_sdk.py:255
Finding
Custom Directory Configuration Can Transmit Authentication Data to Arbitrary or Plaintext Network Endpoints<![CDATA[ ## Vulnerability Details **File Location**: - `python/koan_sdk.py:255-268, 422-429` - `node/koan-sdk.mjs:218-245, 399-406` **Vulnerability Type**: Unrestricted destination configuration and insecure transport **Risk Level**: Medium ### Vulnerable Code Python implementation: ```python class KoanClient: """HTTP client for the Koan Protocol directory.""" def __init__(self, identity: KoanIdentity, directory_url: str = None): self.identity = identity self.directory_url = directory_url or _load_config().get( 'directoryUrl', DEFAULT_DIRECTORY ) def _request(self, method: str, path: str, body=None, auth=False): url = f"{self.directory_url}{path}" headers = {'Content-Type': 'application/json; charset=utf-8'} if auth: sign_path = urlparse(path).path headers.update(self.identity.auth_headers(method, sign_path)) data = json.dumps(body, ensure_ascii=False).encode('utf-8') if body else None req = Request(url, data=data, headers=headers, method=method) try: with urlopen(req) as resp: return json.loads(resp.read()) ``` ```python if cmd == 'init': name = sys.argv[2] if len(sys.argv) > 2 else input("Agent name: ") directory = sys.argv[3] if len(sys.argv) > 3 else DEFAULT_DIRECTORY identity = KoanIdentity.generate(name) identity.save() _save_config({'directoryUrl': directory}) ``` Node.js implementation: ```javascript constructor(identity, directoryUrl) { this.identity = identity; this.directoryUrl = directoryUrl || loadConfig().directoryUrl || DEFAULT_DIRECTORY; } _request(method, urlPath, body, auth) { return new Promise((resolve, reject) => { const url = new URL(urlPath, this.directoryUrl); const mod = url.protocol === 'https:' ? https : http; const headers = { 'Content-Type': 'application/json; charset=utf-8' }; if (auth) { Object.assign( headers, ...[truncated 3664 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for all non-development network destinations. 2. Use an allowlist containing `https://koanmesh.com` by default. 3. Require explicit, high-friction human approval before enabling a custom origin. 4. Reject URLs containing embedded credentials or unsupported schemes. 5. Reject loopback, link-local, private-network, and otherwise sensitive destinations unless a documented local-development mode is explicitly enabled. 6. Disable cross-origin redirects or revalidate the destination before forwarding authentication headers. 7. Bind signatures to the canonical origin and a cryptographic digest of the request body, in addition to the timestamp, method, and path. 8. Use a short server-enforced timestamp window and replay protection. 9. Clearly display the final destination before any authenticated request is sent. 10. Store production and development endpoint configuration separately so development settings cannot silently persist into normal operation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
python/koan_sdk.py:365
Finding
Unsanitized Peer Identifier Allows Chat-Log Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: - `python/koan_sdk.py:365-383` - `node/koan-sdk.mjs:337-355` - User-controlled CLI input reaches these methods at `python/koan_sdk.py:465-470` and `node/koan-sdk.mjs:443-448` **Vulnerability Type**: Path traversal and arbitrary file append **Risk Level**: Medium ### Vulnerable Code Python implementation: ```python def log_chat(self, peer: str, direction: str, intent: str, payload: dict): CHATS_DIR.mkdir(parents=True, exist_ok=True) entry = { 'ts': datetime.now(timezone.utc).isoformat(), 'direction': direction, 'from': self.identity.koan_id if direction == 'sent' else peer, 'to': peer if direction == 'sent' else self.identity.koan_id, 'intent': intent, 'payload': payload, } with open(CHATS_DIR / f'{peer}.jsonl', 'a', encoding='utf-8') as f: f.write(json.dumps(entry, ensure_ascii=False) + '\n') def recent_chats(self, peer: str, limit=20) -> list: log_file = CHATS_DIR / f'{peer}.jsonl' if not log_file.exists(): return [] lines = log_file.read_text(encoding='utf-8').strip().split('\n') return [json.loads(l) for l in lines[-limit:]] ``` ```python elif cmd == 'send': to = sys.argv[2] msg = ' '.join(sys.argv[3:]) result = client.send(to, 'greeting', {'message': msg}) client.log_chat(to, 'sent', 'greeting', {'message': msg}) print(json.dumps(result, indent=2, ensure_ascii=False)) ``` Node.js implementation: ```javascript logChat(peer, direction, intent, payload) { fs.mkdirSync(CHATS_DIR, { recursive: true }); const entry = { ts: new Date().toISOString(), direction, from: direction === 'sent' ? this.identity.koanId : peer, to: direction === 'sent' ? peer : this.identity.koanId, intent, payload, }; fs.appendFileSync( path.join(CHATS_DIR, `${peer}.jsonl`), JSON.stringify(entry) + '\n' ); } recentChats(peer, limit = 20) { const f = path.join(CHATS_DIR, `${peer ...[truncated 3080 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use the peer identifier directly as a filename. 2. Derive the filename from a cryptographic hash or URL-safe encoding of the complete peer identifier. 3. If readable filenames are required, enforce a strict allowlist of permitted characters and reject path separators, absolute paths, drive prefixes, null bytes, and `.` or `..` components. 4. Resolve the final path and verify that it remains beneath the resolved `CHATS_DIR` path before every read or write. 5. Open files using secure filesystem APIs relative to a pre-opened directory where the platform supports them. 6. Consider protections against symbolic-link traversal when the chat directory may be writable by another actor. 7. Apply the same validation to both `log_chat`/`logChat` and `recent_chats`/`recentChats`. 8. Add tests covering Unix traversal, Windows separators, absolute paths, drive-letter paths, and encoded or mixed-separator variants. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented behavior materially understates the broader capabilities inherited through the prerequisite ecosystem, including identity registration, local persistence of keys/chat logs, broader messaging, and other networked operations. This mismatch is dangerous because operators may grant or trust the skill for limited team-joining actions while it actually enables handling of sensitive credentials and wider remote interactions without enforceable approval controls.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const CONFIG_FILE = path.join(KOAN_DIR, 'config.json');
const CHATS_DIR = path.join(KOAN_DIR, 'chats');
const DEFAULT_DIRECTORY = 'https://koanmesh.com';
const KEYCHAIN_SERVICE = 'koan-protocol-sdk';

function runCommand(command, args, envExtras = {}) {
  const result = spawnSync(command, args, {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return runPowerShell(script, { KOAN_CIPHER: ciphertext });
}

function macosKeychainAccount(signingPublicKeyB64) {
  return `koan-${crypto.createHash('sha256').update(signingPublicKeyB64).digest('hex').slice(0, 32)}`;
}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return runPowerShell(script, { KOAN_CIPHER: ciphertext });
}

function macosKeychainAccount(signingPublicKeyB64) {
  return `koan-${crypto.createHash('sha256').update(signingPublicKeyB64).digest('hex').slice(0, 32)}`;
}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return runPowerShell(script, { KOAN_CIPHER: ciphertext });
}

function macosKeychainAccount(signingPublicKeyB64) {
  return `koan-${crypto.createHash('sha256').update(signingPublicKeyB64).digest('hex').slice(0, 32)}`;
}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return runPowerShell(script, { KOAN_CIPHER: ciphertext });
}

function macosKeychainAccount(signingPublicKeyB64) {
  return `koan-${crypto.createHash('sha256').update(signingPublicKeyB64).digest('hex').slice(0, 32)}`;
}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return runPowerShell(script, { KOAN_CIPHER: ciphertext });
}

function macosKeychainAccount(signingPublicKeyB64) {
  return `koan-${crypto.createHash('sha256').update(signingPublicKeyB64).digest('hex').slice(0, 32)}`;
}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return runPowerShell(script, { KOAN_CIPHER: ciphertext });
}

function macosKeychainAccount(signingPublicKeyB64) {
  return `koan-${crypto.createHash('sha256').update(signingPublicKeyB64).digest('hex').slice(0, 32)}`;
}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def _run_command(command: list[str], env_extra: dict | None = None) -> str:
    env = os.environ.copy()
    if env_extra:
        env.update(env_extra)
    proc = subprocess.run(command, capture_output=True, text=True, env=env)
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return _run_powershell(script, {'KOAN_CIPHER': ciphertext})


def _macos_keychain_account(signing_public_key_b64: str) -> str:
    digest = hashlib.sha256(signing_public_key_b64.encode()).hexdigest()[:32]
    return f'koan-{digest}'
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
node/koan-sdk.mjs:27

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
node/koan-sdk.mjs:112

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
python/koan_sdk.py:164