@vigilbase/cli
Version:
Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.
111 lines (110 loc) • 7.44 kB
JavaScript
import { z } from 'zod';
const record = z.record(z.string(), z.unknown());
const rows = z.array(record);
const passthrough = (shape) => z.object(shape).passthrough();
/** Wire envelopes match One's flarehawk-ui normalizers; JSON retains evidence fields. */
export const securityResponses = {
analytics: passthrough({ data: record }),
analyticsRows: passthrough({ data: rows }),
dnsSecurity: passthrough({ data: passthrough({ series: rows, top_domains: rows }) }),
providerEvents: passthrough({ data: rows, hasData: z.boolean(), count: z.number(), provider: z.string(), dataset: z.string(), aggregates: record.nullable() }),
deviceHealth: passthrough({ data: passthrough({ timeseries: rows, devices: rows }) }),
appTests: passthrough({ data: passthrough({ timeseries: rows, tests: rows }) }),
gateway: passthrough({ data: passthrough({ surface: z.enum(['dns', 'http', 'network']), timeseries: rows, top: rows, secondary: rows }) }),
access: passthrough({ data: passthrough({ timeseries: rows, apps: rows, users: rows, idps: rows, geo: rows }) }),
sessions: passthrough({ data: passthrough({ timeseries: rows, users: rows }) }),
devicePosture: passthrough({ data: passthrough({ checks: rows }) }),
accountInventory: passthrough({ data: passthrough({ connections: rows, assetDatasets: rows, logpushJobs: rows }) }),
accountAudit: passthrough({ data: passthrough({ summary: record, over_time: rows, top_actions: rows, top_resource_products: rows, outcomes: rows, recent_changes: rows }) }),
accountIngestion: passthrough({ data: passthrough({ volume: rows, by_asset: rows }) }),
accountPosture: passthrough({ data: passthrough({ posture: rows, heatmap: rows }) }),
providers: passthrough({ providers: rows }),
notifications: passthrough({ notifications: rows, unreadCount: z.number().int().nonnegative() }),
notificationSettings: passthrough({ channels: rows, preferences: record.nullish() }),
preferences: passthrough({ preferences: record }),
slackChannels: passthrough({ channels: rows }),
ipList: passthrough({ entries: rows }),
assetRemediation: passthrough({ effective: record }),
autonomy: passthrough({ settings: record }),
datasets: passthrough({ datasets: z.array(z.union([z.string(), record])) }),
defaultAsset: passthrough({ asset_id: z.string().uuid().nullable() }),
graph: passthrough({ asset_id: z.string(), domain_id: z.string(), dataset: z.string(), nodes: rows, edges: rows }),
availableAssets: passthrough({ assets: rows }),
connectionRemediation: passthrough({ cascading_default: record }),
logpushStatus: passthrough({ stored: record, asset: record, receipt: record, canRecreate: z.boolean(), warnings: z.array(z.string()), updatedAt: z.string() }),
workerStats: passthrough({ period: record, requests: record, errors: record, cpuTime: record, queue: record.nullable(), links: record, queueMetricsError: z.string().nullable(), updatedAt: z.string() }),
reportSettings: passthrough({ enabled: z.boolean(), delivery_day: z.string(), delivery_time_utc: z.string(), email_recipients: z.array(z.string()), include_traffic: z.boolean(), include_incidents: z.boolean(), include_top_threats: z.boolean() }),
alerts: passthrough({ alerts: rows }),
alert: passthrough({ alert: record }),
shareLinks: passthrough({ share_links: rows }),
lifecycleEmails: passthrough({ enabled: z.boolean() }),
broadcastEmails: passthrough({ marketingEmailEnabled: z.boolean(), releaseEmailEnabled: z.boolean(), releaseInAppEnabled: z.boolean() }),
applications: passthrough({ grants: rows }),
overview: passthrough({ overview: passthrough({ stats: record }) }),
incidents: passthrough({ incidents: rows, total: z.number() }),
incident: passthrough({ incident: record }),
timeline: passthrough({ events: rows, next_cursor: z.string().nullable(), degraded: z.boolean() }),
logs: passthrough({ logs: rows }),
assets: passthrough({ domains: rows }),
detections: passthrough({ rules: rows.optional(), detections: rows.optional() })
.refine((value) => value.rules !== undefined || value.detections !== undefined),
connections: passthrough({ connections: rows, total: z.number() }),
connection: passthrough({ connection: record }),
reports: passthrough({ reports: rows, total: z.number() }),
report: passthrough({ report: record }),
audit: passthrough({ events: rows, next_cursor: z.string().nullable() }),
intel: passthrough({ query: z.string(), query_type: z.enum(['ip', 'asn', 'cidr', 'ja3', 'ja4']), quota: record }),
quota: z.union([passthrough({ quota: record }), passthrough({ used: z.number(), remaining: z.number().nullable() })]),
update: z.union([
passthrough({ ok: z.literal(true), status: z.string() }),
passthrough({ success: z.literal(true), incident_id: z.string(), status: z.string() }),
]),
};
export class SecurityClient {
one;
constructor(one) {
this.one = one;
}
async request(input) {
// Paths are generated by the command grammar. Never accept upstream URLs or tenant claims.
if (!/^\/[a-z0-9/-]+$/i.test(input.path) || input.path.includes('..')) {
throw new Error('Invalid security API path.');
}
const params = new URLSearchParams();
for (const [key, value] of Object.entries(input.query)) {
if (/^(customerId|customer_id|organizationId|organization_id|org_id)$/.test(key)) {
throw new Error('Tenant identity must use the validated customer scope.');
}
if (value !== undefined)
params.set(key, String(value));
}
if (input.customerId)
params.set('customerId', z.string().uuid().parse(input.customerId));
const query = params.size ? `?${params}` : '';
const payload = await this.one.requestJson(`/api/flarehawk${input.path}${query}`, input.body ? {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input.body),
} : undefined);
if (payload.success === false || payload.ok === false) {
throw new Error('Security request was unsuccessful.');
}
const parsed = securityResponses[input.view].safeParse(payload);
if (!parsed.success)
throw new Error(`Invalid security ${input.view} response from One.`);
if (input.body && (payload.status !== input.body.status ||
(payload.incident_id !== undefined && payload.incident_id !== input.path.split('/').at(-1)))) {
throw new Error('Incident update response did not match the requested incident and status.');
}
if (input.view === 'intel' && (payload.query !== input.query.q || payload.query_type !== input.query.type)) {
throw new Error('Intel response did not match the requested lookup.');
}
if (input.view === 'providerEvents' && (payload.provider !== input.query.provider_id || payload.dataset !== input.query.dataset_id)) {
throw new Error('Provider events response did not match the requested provider and dataset.');
}
if (input.view === 'gateway' && payload.data.surface !== input.query.surface) {
throw new Error('Gateway response did not match the requested surface.');
}
return payload;
}
}