Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/security-recommendations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"clerk": minor
---

Add `clerk security`, a security audit for a Clerk instance. `clerk security audit` grades the instance against 19 recommendations (bot protection, breached-password detection, brute-force lockout, device trust, MFA, session limits, sign-up restrictions, and more), prints a report grouped by severity, and exits 1 when a critical recommendation is unmet (tunable with `--fail-on`). In agent mode or with `--json` every finding carries the exact `clerk config patch` payload that closes it. `clerk security fix <ids...>` applies the patches as one config patch with a diff, a confirmation, and server-side `--dry-run`, then reports the new grade and the remaining gaps. Bare `clerk security fix` opens a checklist; `--all` applies every critical and recommended gap, with `--good-to-have` opting into the rest. Checks that need a product decision, such as which second factors to offer, ask interactively or take `--factors` / `--strategy` from agents. `clerk security checks` lists the catalog offline.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Commands:
link [options] Link this project to a Clerk application
mcp Manage the Clerk remote MCP server connection for AI editors and CLIs
open Open Clerk resources in your browser
security Audit an instance against Clerk's security recommendations
telemetry Control CLI usage telemetry (status, disable, enable)
unlink [options] Unlink this project from its Clerk application
update [options] Update the Clerk CLI to the latest version
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-core/src/cli-program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { registerTelemetry } from "./commands/telemetry/index.ts";
import { registerToggles } from "./commands/toggles/index.ts";
import { registerApi } from "./commands/api/index.ts";
import { registerDoctor } from "./commands/doctor/index.ts";
import { registerSecurity } from "./commands/security/index.ts";
import { registerMcp } from "./commands/mcp/index.ts";
import { registerSwitchEnv } from "./commands/switch-env/index.ts";
import { registerCompletion } from "./commands/completion/index.ts";
Expand Down Expand Up @@ -77,6 +78,7 @@ const registrants: CommandRegistrant[] = [
registerToggles,
registerApi,
registerDoctor,
registerSecurity,
registerMcp,
registerSwitchEnv,
registerCompletion,
Expand Down
21 changes: 16 additions & 5 deletions packages/cli-core/src/commands/completion/__complete.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { CommandUnknownOpts, Option } from "@commander-js/extra-typings";
import { CHECKS } from "../security/catalog.ts";
import { KNOWN_DASHBOARD_PATHS } from "../open/dashboard-paths.ts";

const DIRECTIVE = {
Expand Down Expand Up @@ -52,6 +53,12 @@ const KNOWN_OPTION_VALUES: Record<string, Completion[]> = {
{ name: "latest", description: "Latest stable release" },
{ name: "canary", description: "Latest canary (pre-release) build" },
],
"--factors": [
{ name: "authenticator", description: "Authenticator app (TOTP)" },
{ name: "backup-code", description: "Backup codes" },
{ name: "sms", description: "SMS code" },
{ name: "authenticator,backup-code", description: "Authenticator app and backup codes" },
],
"--for": [
{ name: "orgs", description: "Organizations only" },
{ name: "users", description: "Users only" },
Expand All @@ -70,6 +77,7 @@ const KNOWN_POSITIONAL_COMPLETIONS: Record<string, Completion[]> = {
name: path,
description: "Dashboard subpath",
})),
"security fix": CHECKS.map((check) => ({ name: check.id, description: check.title })),
};

/**
Expand Down Expand Up @@ -211,11 +219,14 @@ function completeArguments(
consumedCount: number,
): CompletionResult {
const registeredArgs = cmd.registeredArguments;
if (consumedCount >= registeredArgs.length) {
return EMPTY_NO_FILE;
}

const arg = registeredArgs[consumedCount];
const last = registeredArgs.at(-1);
const arg =
consumedCount < registeredArgs.length
? registeredArgs[consumedCount]
: last?.variadic
? last
: undefined;
if (!arg) return EMPTY_NO_FILE;

// Prefer strict Commander choices when available.
if (arg?.argChoices) {
Expand Down
3 changes: 3 additions & 0 deletions packages/cli-core/src/commands/config/apply-patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface ApplyPatchOptions {
warning?: string;
/** Pre-fetched current config; skips the extra GET when caller already has it. */
currentConfig?: Record<string, unknown>;
/** Receives the response body: the written document, or the projection under `--dry-run`. */
onWritten?: (body: Record<string, unknown>) => void;
}

/** Fetch + diff + confirm + PATCH, matching `clerk config patch` semantics. */
Expand Down Expand Up @@ -67,6 +69,7 @@ export async function applyConfigPatch(opts: ApplyPatchOptions): Promise<boolean
);

log.debug(`config: ${JSON.stringify(result.body)}`);
opts.onWritten?.(result.body);
if (dryRun) {
log.success("[dry-run] Validation passed — no changes applied");
} else {
Expand Down
Loading