diff --git a/DISASMER_PUBLIC_TREE.json b/DISASMER_PUBLIC_TREE.json index 143f706..d884b57 100644 --- a/DISASMER_PUBLIC_TREE.json +++ b/DISASMER_PUBLIC_TREE.json @@ -1,7 +1,7 @@ { "kind": "disasmer-filtered-public-tree", - "source_commit": "8ac74137c6035c1f9f995a2357684131483ed1e3", - "release_name": "dryrun-8ac74137c603", + "source_commit": "bc3642c6900c2551532a7e2263e822fd1c668ba8", + "release_name": "dryrun-bc3642c6900c", "filtered_out": [ "private/**", "experiments/**", diff --git a/crates/disasmer-cli/src/main.rs b/crates/disasmer-cli/src/main.rs index d043a31..3d48d2f 100644 --- a/crates/disasmer-cli/src/main.rs +++ b/crates/disasmer-cli/src/main.rs @@ -7903,6 +7903,27 @@ mod tests { } } + #[test] + fn cli_has_no_direct_hosted_account_creation_command() { + for args in [ + &["disasmer", "signup"][..], + &["disasmer", "account", "create"], + &["disasmer", "login", "--create-account"], + ] { + let error = Cli::try_parse_from(args).unwrap_err().to_string(); + assert!( + error.contains("unrecognized subcommand") || error.contains("unexpected argument"), + "expected no direct account creation command for {args:?}, got {error}" + ); + } + + let mut command = Cli::command(); + let help = command.render_help().to_string(); + assert!(help.contains("Hosted account creation happens in the browser login flow.")); + assert!(!help.contains("disasmer signup")); + assert!(!help.contains("account create")); + } + #[test] fn admin_bootstrap_reports_self_hosted_cli_only_path() { let temp = tempfile::tempdir().unwrap(); diff --git a/scripts/cli-first-contract-smoke.js b/scripts/cli-first-contract-smoke.js index 2885c9d..9096234 100644 --- a/scripts/cli-first-contract-smoke.js +++ b/scripts/cli-first-contract-smoke.js @@ -76,6 +76,21 @@ expect( "hosted signup provider and password policy criteria", /approved external identity-provider requirement[\s\S]*disasmer_native_password_signup_allowed: false/ ); +expect( + criteria, + "hosted provider normalization criteria", + /Google sign-in may be treated as OIDC; GitHub sign-in is OAuth2 social login[\s\S]*normalizes Google as `google_oidc` and GitHub as `github_oauth2` from an Authentik-issued OIDC session[\s\S]*does not consume provider-specific tokens directly/ +); +expect( + criteria, + "hosted CLI cannot create accounts directly", + /The CLI cannot create a hosted account directly[\s\S]*parser coverage rejects `disasmer signup`, `disasmer account create`, and `disasmer login --create-account`/ +); +expect( + criteria, + "hosted safe claim failure criteria", + /If identity is ambiguous, missing required claims, unverified where required, or blocked by policy[\s\S]*exactly one approved external identity provider[\s\S]*stable subject[\s\S]*verified email/ +); expect( criteria, "hosted signup sanitized failure criteria", diff --git a/scripts/cli-output-mode-smoke.js b/scripts/cli-output-mode-smoke.js index 7c25055..32db227 100644 --- a/scripts/cli-output-mode-smoke.js +++ b/scripts/cli-output-mode-smoke.js @@ -2,17 +2,31 @@ const assert = require("assert"); const cp = require("child_process"); +const fs = require("fs"); +const os = require("os"); const path = require("path"); const repo = path.resolve(__dirname, ".."); const project = path.join(repo, "examples/launch-build-demo"); +const isolatedCwd = fs.mkdtempSync(path.join(os.tmpdir(), "disasmer-cli-output-")); -function disasmer(args, env = {}) { +function disasmer(args, env = {}, cwd = repo) { return cp.execFileSync( "cargo", - ["run", "-q", "-p", "disasmer-cli", "--bin", "disasmer", "--", ...args], + [ + "run", + "-q", + "--manifest-path", + path.join(repo, "Cargo.toml"), + "-p", + "disasmer-cli", + "--bin", + "disasmer", + "--", + ...args, + ], { - cwd: repo, + cwd, encoding: "utf8", env: { ...process.env, @@ -22,8 +36,8 @@ function disasmer(args, env = {}) { ); } -function json(args, env) { - return JSON.parse(disasmer(args, env)); +function json(args, env, cwd) { + return JSON.parse(disasmer(args, env, cwd)); } function assertHuman(name, output, requiredPatterns) { @@ -88,7 +102,7 @@ assert(doctorJson.node_readiness_summary.next_actions.length >= 2); const authJson = json(["auth", "status", "--json"], { DISASMER_TOKEN: "token", DISASMER_TOKEN_EXPIRES_AT: "2026-07-04T00:00:00Z", -}); +}, isolatedCwd); assert.strictEqual(authJson.session.kind, "human"); assert.strictEqual(authJson.session.token_expiry_posture, "expires_at"); assert.strictEqual(authJson.session.expires_at, "2026-07-04T00:00:00Z");