Skip to main content

Summary

A single-character difference — out.warn(authError) vs out.error(authError); process.exit(1) — determines whether a CLI tool enforces authentication or merely suggests it. In the STRATT CLI, all 4 write commands (publish, deprecate, gate, new) used warn, meaning unauthenticated users could publish units to R2, deprecate production units, and approve gate decisions.

The Problem

The auth check function checkPermission(operation) was correctly implemented — it verified JWT tokens, checked role hierarchy, and returned descriptive error messages. But the calling code treated the result as advisory:
const authError = await checkPermission("publish");
if (authError) {
  out.warn(authError);  // Continues execution!
}
Two commands (publish, gate) had the warn pattern. Two commands (deprecate, new) had no auth check at all.

The Fix

if (authError) {
  out.error({ error: authError }, authError);
  process.exit(EXIT.VALIDATION_FAIL);
}
Additionally, checkDomainAccess() was defined but never called from any command. We wired it into all write paths so domain-restricted tokens can’t write to unauthorized domains.

The Lesson

When auditing CLI security, search for the pattern warn(auth or warn(permission across all commands. Every warning about authentication is a security hole — auth decisions must be binary: proceed or exit. The broader pattern: if a function returns an error and the caller ignores it by warning, the function might as well not exist. Auth checks that warn are security theatre.