@vigilbase/cli
Version:
Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.
226 lines (211 loc) • 9.4 kB
JavaScript
import { CLOUDFLARE_HELP } from '../commands/cloudflare.js';
import { LOCAL_HELP } from '../commands/local.js';
import { ONE_HELP } from '../commands/one.js';
import { SECURITY_HELP } from '../commands/security.js';
import { isOneAuthRequiredTool, ONE_AUTH_REQUIRED_TOOLS } from './one-client.js';
export const HELP_TAGLINE = 'one · security · tools';
/** vigilbase.com's public catalog, with each tool's shortcut command. `help <tool>` answers offline. */
const PUBLIC_TOOL_SHORTCUTS = {
'security-headers': 'headers <url>',
'email-security': 'email <domain>',
};
const PUBLIC_TOOLS = Object.keys(PUBLIC_TOOL_SHORTCUTS);
function isPublicTool(name) {
return Object.hasOwn(PUBLIC_TOOL_SHORTCUTS, name);
}
/** A tool from the catalog the CLI knows offline: it runs as `run <tool> <target>`, not as a command. */
export function isCatalogTool(name) {
return isPublicTool(name) || isOneAuthRequiredTool(name);
}
/** Column where descriptions start in the main help: two-space indent, term, at least two spaces. */
const DESCRIPTION_COLUMN = 31;
/** Help lines fit an 80-column terminal. */
const HELP_WIDTH = 80;
/**
* Where a wrapped line continues: under the description of a row, under the text of a usage
* line, at the indent of a note, or at the margin for prose.
*/
function continuationIndent(line) {
const described = /^ {2}\S.*? {2,}(?=\S)/.exec(line);
if (described)
return described[0].length;
if (line.startsWith('Usage: '))
return 'Usage: '.length;
const indent = /^ */.exec(line)[0].length;
return indent === 0 ? 0 : Math.max(indent, 4);
}
/** Where to cut a line at most `width` long: before an option group, else at a space, else after a |. */
function breakPoint(line, from, width) {
const head = line.slice(0, width + 1);
const bracket = head.lastIndexOf(' [');
if (bracket > from + (width - from) / 2)
return { cut: bracket, skip: 1 };
const space = head.lastIndexOf(' ');
if (space > from)
return { cut: space, skip: 1 };
const bar = line.slice(0, width).lastIndexOf('|');
return bar > from ? { cut: bar + 1, skip: 0 } : null;
}
/** Wraps help lines longer than HELP_WIDTH, keeping descriptions aligned. */
function wrapHelp(text) {
return text.split('\n').flatMap((line) => {
const lines = [];
const pad = ' '.repeat(continuationIndent(line));
let rest = line;
// Past the first cut, only cut after the indent, so every line is shorter than the last.
let from = /^ */.exec(line)[0].length;
while (rest.length > HELP_WIDTH) {
const at = breakPoint(rest, from, HELP_WIDTH);
if (!at)
break;
lines.push(rest.slice(0, at.cut).trimEnd());
rest = `${pad}${rest.slice(at.cut + at.skip).trimStart()}`;
from = pad.length;
}
return [...lines, rest];
}).join('\n');
}
/** Two-column help rows whose descriptions all start at DESCRIPTION_COLUMN. */
function helpRows(rows) {
return rows
.map(([term, description]) => ` ${term.padEnd(DESCRIPTION_COLUMN - 4)} ${description}`)
.join('\n');
}
const GLOBAL_OPTIONS = helpRows([
['-j, --json', 'JSON output'],
['-h, --help', 'Help for the current command'],
['-V, --version', 'Installed version'],
['--timeout <milliseconds>', 'Request timeout (default: 60000)'],
['--base-url <origin>', 'Public API origin'],
['--one-url <origin>', 'Vigilbase One origin'],
['--auth-url <origin>', 'Identity issuer (default: auth.vigilbase.com)'],
['--client-id <id>', 'Device authorization client ID'],
]);
export const HELP_TEXT = wrapHelp(`Usage: vigilbase <command>
Public tools (no login)
list List available tools
run <tool> <target> Run a scan
headers <url> Check security headers
email <domain> Check email security
describe <tool> · skill Tool details / skill markdown
badge <domain> · doctor Badge URL · connectivity
Auth-required tools (after login)
${ONE_AUTH_REQUIRED_TOOLS.map((tool) => ` run ${tool} <target>`).join('\n')}
On this machine (no login)
cloudflare-checkup Run the Cloudflare Checkup on this machine in one
command (alias: local cloudflare-checkup)
Account (login required)
login · logout · whoami Session
account [services|org|…] Account overview
account endpoints Endpoint inventory
account sessions Active sessions
account invites [list|create]
billing [summary|portal] Billing summary / Stripe portal
runs [list|get <id>] Tool run history
cf <command> Cloudflare accounts, zones, DNS, WAF, analytics
security <command> Incidents, logs, assets, detections, intel, reports
watchdog [list|scans] Watchdog targets / scans
customers <command> Customers, billing, reports, investigations
orgs <command> Organizations, members, permission roles
monitoring <command> Health, probes, metrics, customer targets
support tickets Support ticket history
Options
${GLOBAL_OPTIONS}
Use vigilbase <command> --help or vigilbase help <tool> for usage.
Examples
vigilbase headers https://vigilbase.com
vigilbase email vigilbase.com --json
vigilbase login
vigilbase account --json
vigilbase runs list --json
vigilbase cf zones
vigilbase security incidents --severity high
`);
/** How to run a catalog tool: `help <tool>` and `<tool> --help`, since tools run as `run <tool> <target>`. */
function helpForTool(tool) {
if (isPublicTool(tool)) {
return `Usage: vigilbase run ${tool} <target> [--json] [--via public|one] [--public]
Public tool, no login needed. Shortcut: vigilbase ${PUBLIC_TOOL_SHORTCUTS[tool]}
Details: vigilbase describe ${tool}`;
}
if (isOneAuthRequiredTool(tool)) {
return `Usage: vigilbase run ${tool} <target> [--json]
Runs through Vigilbase One: run \`vigilbase login\` first.
Details: vigilbase describe ${tool}`;
}
return null;
}
export const HELP_USAGE = 'Usage: vigilbase help [<command> | <tool>]';
export function helpForCommand(command) {
const help = commandHelp(command);
return help === null ? null : wrapHelp(help);
}
function commandHelp(command) {
switch (command) {
case 'help':
return `${HELP_USAGE}
Shows the usage of a command or of a tool, for example vigilbase help run or
vigilbase help website-security. Without one, shows every command.`;
case 'tools':
case 'list':
return `Usage: vigilbase list [--json]`;
case 'run':
return `Usage: vigilbase run <tool> <target> [--json] [--via public|one] [--public]
Public: ${PUBLIC_TOOLS.join(', ')}
Login: ${ONE_AUTH_REQUIRED_TOOLS.join(', ')}
Run vigilbase help <tool> for a tool's usage.`;
case 'describe':
return `Usage: vigilbase describe <tool> [--json] [--no-skill]`;
case 'skill':
return `Usage: vigilbase skill <tool> [--json]`;
case 'badge':
return `Usage: vigilbase badge <domain> [--style default|compact|mini] [--json]`;
case 'doctor':
return `Usage: vigilbase doctor [--json]
Checks the public tools API, your session (Identity's userinfo endpoint when
signed in through Identity, otherwise One's session endpoint) and any saved
sign-in independently. Login is optional. Exits with status 1 if any check
fails. Use --base-url, --one-url, --auth-url and --timeout to check other
environments.`;
case 'account':
return `Usage: vigilbase account [services|org|endpoints|sessions|invites|billing] [--json]
Requires \`vigilbase login\`.
account invites create <email>|--email <email> [--role <role>] --confirm`;
case 'runhistory':
case 'runs':
return `Usage: vigilbase runs [list|get <UUID>] [--json]
List only: --tool <name>, --limit <1..200> (default 50).`;
case 'cf':
case 'cloudflare':
return CLOUDFLARE_HELP;
case 'security':
return SECURITY_HELP;
case 'customers':
case 'orgs':
case 'monitoring':
case 'support':
return ONE_HELP[command];
case 'watchdog':
return `Usage: vigilbase watchdog [list|scans] [--json]
Scans only: --target-id <UUID>, --limit <1..100> (default 20).`;
case 'local':
case 'cloudflare-checkup':
return LOCAL_HELP;
case 'billing':
return `Usage: vigilbase billing [summary|portal] [--json]
Portal only: --no-open. URLs must use HTTPS on billing.stripe.com.`;
case 'login':
return `Usage: vigilbase login [--json] [--no-open] [--token-stdin | --token <bearer>]
Prefer --token-stdin with piped input to keep tokens out of command arguments.`;
case 'logout':
return `Usage: vigilbase logout [--json]`;
case 'whoami':
return `Usage: vigilbase whoami [--json]`;
case 'headers':
return `Usage: vigilbase headers <url> [--json] [--via public|one] [--public]`;
case 'email':
return `Usage: vigilbase email <domain> [--json] [--via public|one] [--public]`;
default:
return helpForTool(command);
}
}