UNPKG

@vigilbase/cli

Version:

Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.

113 lines (112 loc) • 4.07 kB
import { spawn, spawnSync } from 'node:child_process'; import { validateOneOrigin } from './config.js'; /** Stripe Customer Portal uses this exact host; never trust a returned URL by type alone. */ export function validateStripePortalUrl(raw) { if (typeof raw !== 'string' || /[\u0000-\u0020\u007f-\u009f]/.test(raw)) { throw new Error('Billing portal returned an invalid Stripe URL.'); } let url; try { url = new URL(raw); } catch { throw new Error('Billing portal returned an invalid Stripe URL.'); } if (url.protocol !== 'https:' || url.hostname !== 'billing.stripe.com' || url.port || url.username || url.password || url.hash) { throw new Error('Billing portal URL must be an approved HTTPS Stripe URL.'); } return url.href; } /** * Detect environments where opening a GUI browser is impossible/undesirable. */ export function isHeadlessEnvironment(env = process.env) { if (env.VIGILBASE_CLI_NO_OPEN === '1') return true; if (env.CI === '1' || env.CI === 'true') return true; if (env.GITHUB_ACTIONS === 'true') return true; if (process.platform === 'linux' && !env.DISPLAY && !env.WAYLAND_DISPLAY) { return true; } return false; } function commandExists(command) { if (process.platform === 'win32') { const result = spawnSync('where', [command], { encoding: 'utf8' }); return result.status === 0; } const result = spawnSync('sh', ['-c', `command -v ${JSON.stringify(command)}`], { encoding: 'utf8', }); return result.status === 0; } function launch(command, args) { if (!commandExists(command)) return false; try { const child = spawn(command, args, { detached: true, stdio: 'ignore' }); child.on('error', () => undefined); child.unref(); return true; } catch { return false; } } /** * Best-effort open of a URL in the user's default browser. */ export function openUrlInBrowser(url, env = process.env) { if (env.VIGILBASE_CLI_NO_OPEN === '1') { return { isOpened: false, reason: 'disabled' }; } if (isHeadlessEnvironment(env)) { return { isOpened: false, reason: 'headless' }; } const platform = process.platform; if (platform === 'darwin') { return launch('open', [url]) ? { isOpened: true } : { isOpened: false, reason: 'failed' }; } if (platform === 'win32') { // Shell URL launchers interpret metacharacters. Print the URL for manual opening. return { isOpened: false, reason: 'unsupported' }; } if (platform === 'linux') { for (const [cmd, args] of [ ['xdg-open', [url]], ['gio', ['open', url]], ['sensible-browser', [url]], ['wslview', [url]], ]) { if (launch(cmd, [...args])) return { isOpened: true }; } return { isOpened: false, reason: 'failed' }; } return { isOpened: false, reason: 'unsupported' }; } /** * Build the device-auth verification URL on the configured One origin. * Ignores a mis-reported host so `vigilbase login` always opens Vigilbase One. */ /** Format user codes like ABCD-EFGH for easier reading aloud. */ export function formatUserCode(code) { const compact = code.replace(/[^a-zA-Z0-9]/g, '').toUpperCase(); if (compact.length === 8) { return `${compact.slice(0, 4)}-${compact.slice(4)}`; } return compact || code; } export function resolveDeviceVerificationUrl(oneBaseUrl, device) { const base = validateOneOrigin(oneBaseUrl); // Always pass the hyphenated code so the One UI can show ABCD-EFGH. const prettyCode = formatUserCode(device.user_code); const fallback = `${base}/device?user_code=${encodeURIComponent(prettyCode)}`; // One's verification page is fixed. Never print/open server-controlled userinfo, // redirect parameters, fragments, or paths, even when they share One's origin. return fallback; }