Security Notes
MoAI-ADK v3.0.0 security hardening — CWE-732/214/345 mappings and self-audit procedures
An agentic harness is a system that hands execution authority to agents. The more authority a system delegates, the more its harness trust depends on the security of its credentials and update paths. This page summarizes the user-visible security changes introduced as of MoAI-ADK v3.0.0. Each item includes its CWE mapping, the changed behavior, and self-audit commands.
SPEC-V3R5-SECURITY-CRIT-001 (PR #1032, merge commit 03a2552a2) corrected 3 P0 release-blocker security defects found in code review between v2.14.0 and v3.0.0. This page codifies that correction, and the procedures for users to verify the new protections in their own environments, as official 4-locale guidance.
All three defects relate to the GLM integration + auto-update paths.
- CWE-732 / CWE-552 —
.claude/settings.local.jsonfile mode enforced to0o600(owner-only read/write) - CWE-214 —
moai cgtmux environment-variable injection goes via source-file instead of argv (GLM token no longer visible in argv) - CWE-345 —
moai updatechecksum verification is mandatory (update refused on download failure)
Each item is locked by regression tests, blocking future regressions.
When .claude/settings.local.json is created or updated, its file permission is enforced to 0o600 (owner-only read/write). Previously it was created as 0o644 (owner read/write + group/world read), so on multi-user workstations other local users could read sensitive credentials such as ANTHROPIC_AUTH_TOKEN.
- Attacker: a low-privilege local user on the same host
- Attack surface: the group/world read permission of
.claude/settings.local.json - Leaked information: GLM API token (
ANTHROPIC_AUTH_TOKEN), OAuth refresh token, othersettings.Envvalues - CWE mapping: CWE-732 (Incorrect Permission Assignment for Critical Resource), CWE-552 (Files or Directories Accessible to External Parties)
internal/hook/settings_io.go— thesecureSettingsMode os.FileMode = 0o600constant + thewriteSettingsSecurehelperinternal/hook/session_start.go— allsettings.local.jsonwriters, includingensureGLMCredentials,ensureClaudeEnvFileinternal/hook/session_end.go— GLM keys write-back path
Check the permission of your existing settings.local.json.
# Linux
stat -c '%a' .claude/settings.local.json
# Expected: 600
# macOS
stat -f '%A' .claude/settings.local.json
# Expected: 600If the permission shows 644 or any looser value, MoAI-ADK automatically corrects it to 0o600 at the next session start. To correct it immediately:
chmod 0600 .claude/settings.local.jsonWorkflows that expect group-readable (the very rare scenario of a separate OS user reading the same project directory) may break. This trade-off is intentional; security is the clear priority.
When moai cg (CG mode) injects the GLM token (ANTHROPIC_AUTH_TOKEN) into the tmux session environment, it uses the source-file channel (tmux source-file <tmp>) instead of the argv channel (tmux set-environment <KEY> <VALUE>). The token is no longer exposed in plaintext to ps auxe, /proc/<pid>/cmdline, auditd logs, sysmon traces, or crash dumps.
Since CG mode is the key savings mechanism of tokenomics (Claude leader + GLM workers, 60-70% savings), the security of its credential path is especially important.
- A temp file is created under
~/.moai/run/withmkstemp(mode0o600automatic + explicitchmod 0o600) - A single
set-environment -t <session> <KEY> <VALUE>line is written to the temp file tmux source-file <tmp>has tmux read that file and inject it into the environment- Immediately after injection, the temp file is unlinked with
os.Remove
Only the temp file path is exposed in argv; the token itself is not.
- Attacker: a local user on the same host + system log collection (
ps,/proc, auditd, sysmon) - Attack surface: the argv channel of tmux env injection
- Leaked information: momentary visibility of the GLM API token
- CWE mapping: CWE-214 (Invocation of Process Using Visible Sensitive Information)
internal/tmux/session.go— theInjectSensitiveEnvmethod,sensitiveTempDir = ".moai/run",mkstemp+chmod 0o600+tmux source-file+os.Removeinternal/tmux/errors.go— theErrTmuxSensitiveInjectFailedsentinelinternal/hook/glm_tmux.go— inensureTmuxGLMEnv, onlyANTHROPIC_AUTH_TOKENbranches to the sensitive path (other non-sensitive values such as URLs and model names keep the existing argv path)
Values that are not tokens — CLAUDE_CONFIG_DIR (a directory path), ANTHROPIC_BASE_URL (a URL), ANTHROPIC_DEFAULT_*_MODEL (model names) — keep the argv path. This is explicit intent and unrelated to token leakage risk.
If the source-file injection fails (disk full, tmux source-file failure, etc.), it does not fall back to argv and leak — it returns the ErrTmuxSensitiveInjectFailed sentinel error and aborts the injection itself. Not falling back to convenience on failure is the heart of this design.
Check whether the token is exposed in argv while CG mode is running.
# Inside a new tmux session after running moai cg
ps auxe | grep -i 'tmux set-environment.*ANTHROPIC_AUTH_TOKEN'
# Expected: 0 matches (token is not present in argv)Check that the temp file is properly unlinked.
ls -la ~/.moai/run/ 2>/dev/null
# Expected: empty directory or no stale filesIf residual files remain in ~/.moai/run/ after session end, they can be removed manually (not a security threat — the files were already subject to an unlink attempt).
The ~/.moai/.env.glm source file must keep 0o600 permission in your environment. The moai glm command sets this automatically.
stat -c '%a' ~/.moai/.env.glm # Linux: 600
stat -f '%A' ~/.moai/.env.glm # macOS: 600Details: CG Mode
The moai update auto-update flow cannot bypass checksum verification. If downloading or parsing the release’s checksums.txt fails, it returns the sentinel error ErrChecksumUnavailable and aborts the update flow — the binary download is not attempted.
The checksums.txt download is retried 3 times with exponential backoff.
| Attempt | Wait time |
|---|---|
| 1st (immediate) | 0s |
| 2nd retry | 2s wait |
| 3rd retry | 4s wait |
| No further retries | Fails after ~6s total wait |
(Internal implementation: base delay 2s × 2^(attempt-1) exponential backoff)
If all retries fail, it terminates with the ErrChecksumUnavailable sentinel. No bypass option such as --skip-checksum exists.
If downloadAndVerify is reached with the version.Checksum field as an empty string, the binary download does not proceed and ErrChecksumUnavailable is returned. Dual protection (checker stage + updater stage) blocks silent bypass.
- Attacker: a network MITM (cannot block everything, but can selectively block/throttle just the
checksums.txtURL) - Attack surface: the silent fallback that used to install the binary even without checksums.txt
- Consequence: unwarned installation of an unsigned backdoored binary
- CWE mapping: CWE-345 (Insufficient Verification of Data Authenticity)
internal/update/checker.go—downloadChecksumWithRetry(checksumsURL, archiveName, maxAttempts, baseDelay)(defaultChecksumMaxAttempts=3,defaultChecksumBaseDelay=2*time.Second), theErrChecksumUnavailablesentinelinternal/update/updater.go— thedownloadAndVerifyempty-checksum guard- The domain whitelist (
https://github.com/modu-ai/moai-adk/...) is preserved as-is (no change to the SSRF surface)
# Verify release info + checksums.txt existence
moai update --check-only
# Normal flow (on success)
moai update
# Example output: Downloaded checksums.txt (verified)
# When checksums.txt download fails (intentional block, e.g. running after a VPN disconnect)
moai update
# Example output: error: checksum unavailable: persistent retry failure after 3 attemptsIf the ErrChecksumUnavailable message appears, check the following.
- Verify network connectivity (
curl -I https://github.com/modu-ai/moai-adk/releases/latest) - Verify your proxy / firewall allows the GitHub release asset domain
- Possible transient GitHub CDN outage — retry shortly
- No bypass option such as
--skip-checksumis provided — this is intentional policy
For persistent blockage, manual binary installation is recommended.
# Manual installation (verify integrity yourself)
curl -fsSL https://adk.mo.ai.kr/install.sh | bashDetails: Updating
All five items can be checked at once.
# 1. CWE-732 — settings.local.json permission
stat -c '%a' .claude/settings.local.json 2>/dev/null \
|| stat -f '%A' .claude/settings.local.json 2>/dev/null
# Expected: 600
# 2. CWE-214 — token argv exposure while CG mode is running (with cg mode active)
ps auxe 2>/dev/null | grep -i 'tmux set-environment.*ANTHROPIC_AUTH_TOKEN'
# Expected: 0 matches
# 3. CWE-214 — tmux sensitive temp directory integrity
ls -la ~/.moai/run/ 2>/dev/null
# Expected: empty directory or no stale files
# 4. CWE-345 — Update flow checksum behavior
moai update --check-only
# Expected: release + checksums.txt verified normally
# 5. GLM source file permission (user responsibility)
stat -c '%a' ~/.moai/.env.glm 2>/dev/null \
|| stat -f '%A' ~/.moai/.env.glm 2>/dev/null
# Expected: 600 (when the file exists)If all 5 items meet the expected values, the v3.0.0 security hardening is functioning correctly.
CHANGELOG v3.0.0 Security section
SPEC-V3R5-SECURITY-CRIT-001— upstream source of truth, statusimplementedv0.2.0- PR #1032 merge commit
03a2552a2
b48bd86cb— M1 settings.local.json 0o600 hardening (CWE-732/552)10776c4b8— M2 tmux sensitive env source-file injection (CWE-214)ee1335282— M3 mandatory checksum verification with retry (CWE-345)b4e7115cb— M4 cross-cutting verification + frontmatter
- CWE-732 — Incorrect Permission Assignment for Critical Resource
- CWE-552 — Files or Directories Accessible to External Parties
- CWE-214 — Invocation of Process Using Visible Sensitive Information
- CWE-345 — Insufficient Verification of Data Authenticity
- settings.json Guide — the
settings.local.jsonpermissions section - Updating — the checksum verification section
- CG Mode — the tmux environment-variable injection security model