The scorpiox codebase has a broad data handling footprint spanning configuration management, conversation persistence, multi-provider API credential handling, email services with TLS/DKIM, traffic capture, and session logging. Three critical findings were identified: (1) hardcoded API keys compiled into the WASM binary, (2) unsalted SHA-256 password hashing for email authentication, and (3) predictable temporary file paths creating symlink attack surfaces. The codebase demonstrates good practices in some areas (TLS via mbedTLS, redacted API key logging, session data isolation) but has significant gaps in credential management and temporary file security.
Overall Risk Rating: HIGH┌──────────────────────────────────────────────────────────────────────┐
│ DATA FLOW DIAGRAM │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ User Input ──► sx.c (TUI) ──► Provider (API) ──► AI Response │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ Bash exec Conversation HTTP Request Display │
│ (sx_exec) .json save (credentials) (sxui_*) │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ /tmp files .scorpiox/ Traffic capture Session log │
│ sessions/ .scorpiox/traffics/ events.jsonl │
│ │
│ Config cascade: │
│ defaults → exe_dir/scorpiox-env.txt → ~/.claude/scorpiox-env.txt │
│ → CWD/.scorpiox/scorpiox-env.txt │
│ │
│ Email subsystem: │
│ SMTP/IMAP ──► sxmail_auth (SHA256) ──► maildir storage │
│ ──► TLS (mbedTLS) ──► DKIM (RSA-SHA256) │
└──────────────────────────────────────────────────────────────────────┘
| File Path | Operation | Module | Purpose | Sensitivity |
|---|---|---|---|---|
exe_dir/scorpiox-env.txt | Read | sx_config.c | Global configuration | HIGH — contains API keys, SSH passwords |
~/.claude/scorpiox-env.txt | Read | sx_config.c | User-level config override | HIGH — contains API keys |
.scorpiox/scorpiox-env.txt | Read/Write | sx_config.c | Project-level config | HIGH — contains API keys |
accounts.txt (email) | Read | sxmail_auth.c | User:hash credential pairs | HIGH — password hashes |
filter.txt (email) | Read | sxmail_filter.c | Email routing rules | LOW |
| File Path | Operation | Module | Purpose | Sensitivity |
|---|---|---|---|---|
.scorpiox/sessions/ | Read/Write | sx_conversation.c | Full conversation history | HIGH — user messages, tool inputs/outputs |
.scorpiox/sessions/ | Append | sx_log.c | Debug/info/error log | MEDIUM — may contain keys at DEBUG level |
.scorpiox/sessions/ | Append | sx_session.c | Structured event stream | MEDIUM |
.scorpiox/sessions/ | Append | sx_trace.c | Data flow trace with tool inputs | HIGH — tool_input includes bash commands |
.scorpiox/sessions/ | Write | sx_session.c | Frozen config at session start | HIGH — full config dump |
.scorpiox/sessions/ | Write | sx_session.c | Session metadata & summary | LOW |
.scorpiox/sessions/ | Write | scorpiox-traffic.c | MITM traffic capture | CRITICAL — full HTTP request/response bodies |
.scorpiox/conversations/ | Read/Write | sx_conversation.c | Legacy conversation format | HIGH |
| File Path | Operation | Module | Purpose | Security |
|---|---|---|---|---|
/tmp/sxmux/sxmux- | Create | sxmux_session.c | Unix domain socket | Dir created with 0700 ✓ |
/tmp/sxmux/sxmux- | Read/Write | sxmux_session.c | Session PID tracking | Predictable name ⚠ |
/tmp/sx_emit_ | Write | sx.c | Event emission temp file | Predictable name ⚠ |
/tmp/sx_voice.wav | Write | scorpiox-voice.c | Voice recording | Fixed path — race condition ⚠ |
/tmp/sx_voice_result.txt | Read/Write | scorpiox-voice.c | Voice transcription result | Fixed path — race condition ⚠ |
/tmp/sx_voice_ | Write | scorpiox-whatsapp.c | WhatsApp voice msg | Predictable (timestamp) ⚠ |
/tmp/sx_post_ | Write | scorpiox-server.c | HTTP POST body temp | Predictable ⚠ |
/tmp/sx_mcp_ | Write | sx_mcp.c | MCP config temp | Predictable ⚠ |
/tmp/sx_slirp_ | Create | scorpiox-unshare.c | Slirp4netns socket | Predictable ⚠ |
/tmp/sx_builtin_ | Write | sx_slashcmd.c | Built-in command script | Predictable ⚠ |
/tmp/vault-XXXXXX | Create | scorpiox-vault-git.c | Backup temp dir | Uses mkdtemp() ✓ |
| Write | sx.c | Editor temp file | Uses TMPDIR, PID-based |
| File Path | Operation | Module | Purpose | Sensitivity |
|---|---|---|---|---|
Maildir tmp/ subdirectory | Write→Rename | sxmail_maildir.c | Atomic mail delivery | Contains email content |
Maildir new/ subdirectory | Read/Rename | sxmail_maildir.c | New message storage | Contains email content |
Maildir cur/ subdirectory | Read | sxmail_maildir.c | Read message storage | Contains email content |
~/.scorpiox/server-email.log | Append | scorpiox-server-email.c | Email server log | MEDIUM |
| TLS cert file | Read | sxmail_tls.c | Server certificate | HIGH |
| TLS key file | Read | sxmail_tls.c | Private key | CRITICAL |
| DKIM key file | Read | sxmail_dkim.c | DKIM signing key | CRITICAL |
| File Path | Operation | Module | Purpose |
|---|---|---|---|
.scorpiox/logs/scorpiox-tmux.log | Append | scorpiox-tmux.c | Tmux manager log |
.scorpiox/traffics/ | Write | scorpiox-traffic.c | MITM proxy log |
.scorpiox/traffics/ | Write | scorpiox-traffic.c | Traffic capture log |
/var/log/scorpiox-dns/ | Write | scorpiox-dns.c | DNS audit log |
| PII Type | Location | Context | Risk |
|---|---|---|---|
| Email addresses | sxmail_smtp.c, sxmail_imap.c | SMTP MAIL FROM/RCPT TO, IMAP mailbox | Stored in mail queue, logged in server |
| Email content | sxmail_maildir.c, sxmail_queue.c | Full message bodies | Stored on disk in plaintext |
| User credentials | sxmail_auth.c | Username + SHA256 hash | Stored in accounts file |
| Conversation content | sx_conversation.c | All user messages and AI responses | Stored in JSON on disk |
| Bash command output | sx_conversation.c, sx_trace.c | Tool results stored as conversation messages | May contain arbitrary file contents, env vars |
| JWT claims | scorpiox-server.c | user_id, email from JWT | Logged at INFO level |
| Username | scorpiox-usage.c, scorpiox-unshare.c | USER, USERNAME env vars | Used for container setup |
SX_INFO(" [VAR] %s=%s", key, value). If sensitive variables (API keys, tokens) are set, they will appear in logs.| Credential | Storage Method | Risk Level |
|---|---|---|
ANTHROPIC_API_KEY | Config file (scorpiox-env.txt) / env var | MEDIUM — plaintext in file |
ANTHROPIC_ANTIGRAVITY_KEY | Hardcoded in sx_config_embedded.c | CRITICAL — sk-6088a10dc3c1473cac567069b0e557f6 |
ANTHROPIC_ZAI_KEY | Hardcoded in sx_config_embedded.c | CRITICAL — 9c6fba5db3f84613aaf8700b05990835.ue19jY48d9GmddqX |
GEMINI_API_KEY | Hardcoded in sx_config_embedded.c | CRITICAL — AIzaSyDsvlLnCdMFXnlCLxurMaAO5RKI8IQHVA8 |
OPENAI_API_KEY | Config file / env var | MEDIUM — plaintext in file |
CLAUDE_CODE_SSH_PASS | Hardcoded in sx_config_embedded.c | CRITICAL — xboxone |
CODEX_SSH_PASS | Config file | MEDIUM — plaintext in file |
SMTP_PASS | Config file | MEDIUM — plaintext in file |
SERVER_JWT_SECRET | Config file / env var | HIGH — HMAC signing key |
HTTP_RELAY_KEY | Config file / env var | MEDIUM |
TCP_API_KEY | Config file | MEDIUM |
| TLS private key | File on disk (path in config) | HIGH — must be file-permission protected |
| DKIM private key | File on disk (path in config) | HIGH — must be file-permission protected |
| Git PAT tokens | Injected into URLs at runtime | HIGH — appear in https://PAT@host/ URLs |
libsxutil/sx_config_embedded.c (auto-generated, compiled into WASM)
{"ANTHROPIC_ANTIGRAVITY_KEY", "sk-6088a10dc3c1473cac567069b0e557f6"},
{"ANTHROPIC_ZAI_KEY", "9c6fba5db3f84613aaf8700b05990835.ue19jY48d9GmddqX"},
{"GEMINI_API_KEY", "AIzaSyDsvlLnCdMFXnlCLxurMaAO5RKI8IQHVA8"},
{"CLAUDE_CODE_SSH_PASS", "xboxone"},
These secrets are baked into the compiled binary and can be trivially extracted. The WASM binary is distributed to end users, making this a secret exposure vulnerability.
sx_provider_openai.c:452 — Logs key=(set) or key=(none) instead of actual valuesx_provider_openai_wasm.c:659 — Logs key=* instead of actual valuesx_provider_anthropic.c:127 — Logs key=* for API key presencescorpiox-agent.c:817 — SX_INFO(" [VAR] %s=%s", key, value) logs all environment variables including potentially sensitive ones without filteringsx_config.c:640 — SX_INFO("config: wrote %s=%s to %s", key, value, path) logs config writes including API key valuesscorpiox-env.txt ──► sx_config_init() ──► in-memory g_cfg struct
│ │
▼ ▼
Environment vars ──► getenv() ──────────► Provider data->api_key[256]
│
▼
setenv() before exec
(child process inherits)
│
▼
HTTP headers:
"x-api-key: <key>"
"Authorization: Bearer <key>"
| Algorithm | Library | Module | Purpose | Assessment |
|---|---|---|---|---|
| TLS 1.2/1.3 | mbedTLS | sxmail_tls.c | Email transport encryption | ✓ Proper implementation |
| RSA-SHA256 | mbedTLS | sxmail_dkim.c | DKIM email signing | ✓ RFC 6376 compliant |
| SHA-256 | mbedTLS | sxmail_auth.c | Password hashing | ⚠ UNSALTED — weak |
| HMAC-SHA256 | Custom (scorpiox-server.c) | JWT verification | ✓ Standard JWT validation | |
| SHA-256 | Custom (scorpiox-server.c:149) | JWT HMAC component | ✓ RFC 6234 | |
| CTR-DRBG | mbedTLS | sxmail_tls.c, sxmail_dkim.c | Cryptographic RNG | ✓ Proper seeding from entropy |
| Base64 | mbedTLS + custom | sxmail_dkim.c, sxmail_auth.c | Encoding (not encryption) | N/A |
sxmail_auth.c:121-130
int sxmail_auth_hash_password(const char pass, char out, size_t out_sz) {
if (!pass || !out || out_sz < 65) return -1;
unsigned char digest[32];
mbedtls_sha256((const unsigned char *)pass, strlen(pass), digest, 0);
for (int i = 0; i < 32; i++)
snprintf(out + i * 2, 3, "%02x", digest[i]);
out[64] = '\0';
return 0;
}
Issues:
The TLS implementation in sxmail_tls.c uses mbedTLS correctly:
mbedtls_ctr_drbg_seed()mbedtls_x509_crt_parse_file()mbedtls_pk_parse_keyfile()| Pattern | Count | Security |
|---|---|---|
mkdtemp() | 1 (scorpiox-vault-git.c) | ✓ Secure |
mkstemp() | 0 | — Not used anywhere |
Predictable /tmp/sx_* paths | 8+ locations | ⚠ Insecure |
Fixed /tmp/sx_voice.wav | 1 | ⚠ Race condition / symlink attack |
/tmp/sx_voice.wav and /tmp/sx_voice_result.txt — Fixed paths, no O_EXCL, no ownership check/tmp/sx_post__.txt — Predictable (PID + timestamp), could be pre-created as symlink/tmp/sx_mcp_.json — PID-predictable/tmp/sx_builtin_.sh — Name-predictable and executed as shell script/tmp/sxmux/ directory created with mkdir(..., 0700) — restricts access to ownerscorpiox-vault-git.c properly uses mkdtemp() for temp directorysx.c:707 — remove(tmpfile) after editor use ✓sxmail_maildir.c — Cleans up failed deliveries with remove()/unlink() ✓sxmux_session.c — Cleans up stale sockets and PID files ✓/tmp/sx_voice.wav ⚠/tmp/sx_post_* ⚠sx_log.c with 5 levels: ERROR, WARN, INFO, DEBUG, TRACEsx_log_set_file()) or stderr[HH:MM:SS.mmm]| Risk | Location | Detail |
|---|---|---|
| HIGH | scorpiox-agent.c:817 | SX_INFO(" [VAR] %s=%s", key, value) — logs ALL env vars unfiltered |
| HIGH | sx_config.c:640 | SX_INFO("config: wrote %s=%s ...") — logs config writes including API keys |
| MEDIUM | sx_provider_openai.c:426 | SX_DEBUG("openai: found OPENAI_API_KEY len=%zu", strlen(v)) — leaks key length |
| MEDIUM | sx_session.c config-snapshot | Full config dump at session start may include secrets |
| LOW | sx_provider_.c | API key presence logged as or (set) — properly redacted |
| LOW** | sxmail_dkim.c:324 | Logs key file path (not key contents) |
| Log | Path | Contains |
|---|---|---|
| Session log | .scorpiox/sessions/ | All SX_LOG output for session |
| Events log | .scorpiox/sessions/ | Structured events |
| Trace log | .scorpiox/sessions/ | Tool inputs, data flow |
| Email log | ~/.scorpiox/server-email.log | SMTP/IMAP operations |
| Tmux log | .scorpiox/logs/scorpiox-tmux.log | Session manager operations |
| Traffic log | .scorpiox/traffics/ | Full HTTP capture |
| DNS audit | /var/log/scorpiox-dns/ | DNS query log |
| Data Type | Location | Retention | Encryption |
|---|---|---|---|
| Conversations | .scorpiox/sessions/ | Indefinite | None |
| Session logs | .scorpiox/sessions/ | Indefinite | None |
| Traffic captures | .scorpiox/traffics/ | Until --clear | None |
| Email messages | Maildir hierarchy | Indefinite | None |
| Config snapshots | .scorpiox/sessions/ | Indefinite | None |
| DNS audit logs | /var/log/scorpiox-dns/ | Indefinite | None |
SESSION_RETENTION_DAYS config key exists (default: 7) but no automatic cleanup code was found that enforces it.scorpiox/sessions/ is automatically added to .gitignore ✓scorpiox-traffic.c) can be manually cleared via --clear flag which runs rm -rf .scorpiox/traffics| Variable | Module | Sensitivity | Purpose |
|---|---|---|---|
OPENAI_API_KEY | sx_provider_openai.c | HIGH | OpenAI API authentication |
GEMINI_API_KEY | sx_provider_gemini.c | HIGH | Google Gemini API authentication |
AGENTS_PAT | scorpiox-agent.c | HIGH | Git Personal Access Token |
SCORPIOX_HOME | Multiple | MEDIUM | Installation directory |
HOME / USERPROFILE | Multiple (19 calls) | LOW | User home directory |
EDITOR / VISUAL | sx.c | LOW | Text editor preference |
SHELL | sxmux_pane.c, sx_pty.c | LOW | Default shell |
USER / USERNAME | scorpiox-usage.c | LOW | Current username |
CURL_CA_BUNDLE | sx_platform.c | LOW | CA certificate path |
TZ | sx_systemprompt.c | LOW | Timezone |
TERM | scorpiox-unshare.c | LOW | Terminal type |
PATH | sx_platform.c, scorpiox-unshare.c | LOW | Executable search path |
IMAGE_BASE_URL | scorpiox-wsl.c, scorpiox-unshare.c | LOW | Container image source |
SXMUX_INIT_CMD | sxmux_pane.c | LOW | Initial mux command |
scorpiox-agent.c reads environment variables from a file and calls setenv() for each — this could be used to inject arbitrary env vars into child processessetenv() to pass API keys to child processes (e.g., scorpiox-openai binary)https://PAT@host/path — may appear in process listings| # | Risk | Severity | Location | Recommendation |
|---|---|---|---|---|
| C1 | Hardcoded API keys in WASM binary | CRITICAL | sx_config_embedded.c | Remove all secrets from compiled code; use runtime-only config or secure vault |
| C2 | Hardcoded SSH password in WASM binary | CRITICAL | sx_config_embedded.c | "xboxone" — rotate immediately, remove from source |
| C3 | Unsalted SHA-256 password hashing | CRITICAL | sxmail_auth.c:121 | Migrate to Argon2id or bcrypt with per-user salt |
| # | Risk | Severity | Location | Recommendation |
|---|---|---|---|---|
| H1 | Plaintext API keys in config files | HIGH | sx_config.c, scorpiox-env.txt | Encrypt sensitive config values or use OS keychain |
| H2 | Unfiltered env var logging | HIGH | scorpiox-agent.c:817 | Filter sensitive keys (API_KEY, PASS, SECRET, TOKEN) from log output |
| H3 | Config snapshot may contain secrets | HIGH | sx_session.c | Redact sensitive keys in config-snapshot.txt |
| H4 | Traffic capture stores full HTTP bodies | HIGH | scorpiox-traffic.c | Warn users; auto-redact Authorization headers |
| H5 | No data-at-rest encryption | HIGH | All persistence | Encrypt conversation.json, session logs, and config at rest |
| H6 | Conversation stores all tool outputs | HIGH | sx_conversation.c | Consider content filtering/redaction for sensitive outputs |
| # | Risk | Severity | Location | Recommendation |
|---|---|---|---|---|
| M1 | Predictable temp file paths | MEDIUM | 8+ locations | Use mkstemp()/mkdtemp() instead of predictable names |
| M2 | Fixed temp file paths (voice) | MEDIUM | scorpiox-voice.c | Race condition / symlink attack — use mkstemp() |
| M3 | Git PAT in URL visible to ps | MEDIUM | scorpiox-agent.c, scorpiox-server.c | Use credential helper or git-credential-store |
| M4 | No session retention enforcement | MEDIUM | sx_session.c | Implement automatic cleanup per SESSION_RETENTION_DAYS |
| M5 | Config value logged on write | MEDIUM | sx_config.c:640 | Redact values for keys matching sensitive patterns |
| M6 | Builtin script written to predictable /tmp path | MEDIUM | sx_slashcmd.c:567 | /tmp/sx_builtin_ could be pre-planted — use mkstemp() |
| # | Risk | Severity | Location | Recommendation |
|---|---|---|---|---|
| L1 | Debug log reveals API key length | LOW | sx_provider_openai.c:426 | Minor info leak; consider removing |
| L2 | Email log in fallback /tmp path | LOW | scorpiox-server-email.c:486 | Falls back to /tmp if HOME unset |
| L3 | JWT claims logged at INFO | LOW | scorpiox-server.c:502 | user_id/email in logs — acceptable for audit |
sx_config_embedded.c — they are compromisedKEY, PASS, SECRET, TOKEN)/tmp/sx_* paths with mkstemp()/mkdtemp()SESSION_RETENTION_DAYS