nsauditor-ai 0.1.27 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.mjs +107 -16
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -90,7 +90,7 @@ function redactSensitiveForAI(input, targetHost) {
|
|
|
90
90
|
|
|
91
91
|
/* ------------------------- OpenAI & reporting -------------------------- */
|
|
92
92
|
|
|
93
|
-
async function maybeSendToOpenAI({ host, results, conclusion, promptMode = 'basic' }) {
|
|
93
|
+
async function maybeSendToOpenAI({ host, results, conclusion, promptMode = 'basic', outDir: presetOutDir = null }) {
|
|
94
94
|
// --- env & opts -----------------------------------------------------------
|
|
95
95
|
const sendEnabled = parseBool(process.env.AI_ENABLED);
|
|
96
96
|
const redactEnabled = parseBool(process.env.OPENAI_REDACT, true);
|
|
@@ -108,17 +108,17 @@ async function maybeSendToOpenAI({ host, results, conclusion, promptMode = 'basi
|
|
|
108
108
|
: await resolveSecret(process.env.OPENAI_API_KEY);
|
|
109
109
|
const key = keyRaw ? String(keyRaw).trim() : null;
|
|
110
110
|
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
// Per-scan folder. Caller (scanSingleHost) may pass a pre-computed outDir so
|
|
112
|
+
// that EE enrichment + compliance artifacts share the same folder as the AI
|
|
113
|
+
// outputs — otherwise compute one here for legacy callers.
|
|
114
|
+
let outDir = presetOutDir;
|
|
115
|
+
if (!outDir) {
|
|
116
|
+
const baseOutDir = resolveBaseOutDir();
|
|
117
|
+
await fsp.mkdir(baseOutDir, { recursive: true });
|
|
118
|
+
const ts = nowStamp();
|
|
119
|
+
const runDir = `${safeHost(host)}_${ts}`;
|
|
120
|
+
outDir = path.join(baseOutDir, runDir);
|
|
121
|
+
}
|
|
122
122
|
await fsp.mkdir(outDir, { recursive: true });
|
|
123
123
|
|
|
124
124
|
// Paths (fixed names inside per-scan folder)
|
|
@@ -522,6 +522,14 @@ async function maybeSendToOpenAI({ host, results, conclusion, promptMode = 'basi
|
|
|
522
522
|
async function parseArgs(argv) {
|
|
523
523
|
const args = { cmd: 'scan', host: undefined, plugins: 'all', insecureHttps: false };
|
|
524
524
|
const a = argv.slice(2);
|
|
525
|
+
// Help: bare `--help`/`-h`/`help` or completely empty invocation.
|
|
526
|
+
// Recognized before the scan-default so it doesn't crash with
|
|
527
|
+
// "--host or --host-file is required" on a help request.
|
|
528
|
+
if (a.length === 0 || a[0] === '--help' || a[0] === '-h' || a[0] === 'help' ||
|
|
529
|
+
a.includes('--help') || a.includes('-h')) {
|
|
530
|
+
args.cmd = 'help';
|
|
531
|
+
return args;
|
|
532
|
+
}
|
|
525
533
|
if (a.length && !a[0].startsWith('--')) args.cmd = a[0];
|
|
526
534
|
|
|
527
535
|
const get = (name) => {
|
|
@@ -536,7 +544,7 @@ async function parseArgs(argv) {
|
|
|
536
544
|
const p = get('plugins');
|
|
537
545
|
if (p && p !== true && p.toLowerCase() !== 'all') {
|
|
538
546
|
args.plugins = p.split(',').map((s) => s.trim()).filter(Boolean);
|
|
539
|
-
} else if (p && p.toLowerCase() === 'all') {
|
|
547
|
+
} else if (p && p !== true && p.toLowerCase() === 'all') {
|
|
540
548
|
args.plugins = 'all';
|
|
541
549
|
}
|
|
542
550
|
args.insecureHttps = !!(get('insecure-https') || get('insecure_https'));
|
|
@@ -570,6 +578,13 @@ async function parseArgs(argv) {
|
|
|
570
578
|
const alertSev = get('alert-severity') || get('alert_severity') || null;
|
|
571
579
|
args.alertSeverity = (alertSev && alertSev !== true) ? alertSev.toLowerCase() : 'high';
|
|
572
580
|
|
|
581
|
+
// Compliance: framework selector + scope file. Forwarded to EE's
|
|
582
|
+
// runCompliancePhase via enrichScan(). No-op without an EE Enterprise license.
|
|
583
|
+
const complianceVal = get('compliance');
|
|
584
|
+
args.compliance = (complianceVal && complianceVal !== true) ? complianceVal : null;
|
|
585
|
+
const complianceScopeVal = get('compliance-scope') || get('compliance_scope');
|
|
586
|
+
args.complianceScope = (complianceScopeVal && complianceScopeVal !== true) ? complianceScopeVal : null;
|
|
587
|
+
|
|
573
588
|
return args;
|
|
574
589
|
}
|
|
575
590
|
|
|
@@ -599,17 +614,34 @@ async function scanSingleHost(pm, host, plugins, opts, promptMode) {
|
|
|
599
614
|
conclusion.result.techniques = techniques;
|
|
600
615
|
}
|
|
601
616
|
|
|
617
|
+
// Pre-compute the per-scan output folder so EE enrichment, compliance
|
|
618
|
+
// artifacts, and AI outputs all land in the same directory. maybeSendToOpenAI
|
|
619
|
+
// will reuse this presetOutDir below.
|
|
620
|
+
const baseOutDir = resolveBaseOutDir();
|
|
621
|
+
await fsp.mkdir(baseOutDir, { recursive: true });
|
|
622
|
+
const ts = nowStamp();
|
|
623
|
+
const outDir = path.join(baseOutDir, `${safeHost(host)}_${ts}`);
|
|
624
|
+
await fsp.mkdir(outDir, { recursive: true });
|
|
625
|
+
|
|
602
626
|
// EE enrichment hook — no-op if @nsasoft/nsauditor-ai-ee is not installed
|
|
627
|
+
// or the license tier doesn't grant intelligenceEngine. Compliance + outDir
|
|
628
|
+
// are forwarded so EE can write scan_finding_queue.json and SOC 2 artifacts.
|
|
603
629
|
try {
|
|
604
630
|
const { enrichScan } = await import('@nsasoft/nsauditor-ai-ee');
|
|
605
|
-
const eeEnrichment = await enrichScan(conclusion, {
|
|
631
|
+
const eeEnrichment = await enrichScan(conclusion, {
|
|
632
|
+
host,
|
|
633
|
+
outDir,
|
|
634
|
+
compliance: opts.compliance ?? process.env.COMPLIANCE_FRAMEWORKS ?? null,
|
|
635
|
+
complianceScope: opts.complianceScope ?? null,
|
|
636
|
+
onWarn: (msg) => console.warn(`[EE] ${msg}`),
|
|
637
|
+
});
|
|
606
638
|
if (eeEnrichment?.enrichedPrompt) {
|
|
607
639
|
conclusion.result = conclusion.result || {};
|
|
608
640
|
conclusion.result.eeEnrichment = eeEnrichment;
|
|
609
641
|
}
|
|
610
642
|
} catch { /* EE not installed — CE proceeds unchanged */ }
|
|
611
643
|
|
|
612
|
-
const { file_paths: ai_file_paths, ai_conclusion } = await maybeSendToOpenAI({ host, results, conclusion, promptMode });
|
|
644
|
+
const { file_paths: ai_file_paths, ai_conclusion } = await maybeSendToOpenAI({ host, results, conclusion, promptMode, outDir });
|
|
613
645
|
|
|
614
646
|
// --- Scan history: record & compare ---
|
|
615
647
|
let scanDiff = null;
|
|
@@ -724,7 +756,64 @@ function maxSeverityInConclusion(conclusion) {
|
|
|
724
756
|
}
|
|
725
757
|
|
|
726
758
|
async function main() {
|
|
727
|
-
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports } = await parseArgs(process.argv);
|
|
759
|
+
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports, compliance, complianceScope } = await parseArgs(process.argv);
|
|
760
|
+
|
|
761
|
+
// Help: handled before license verification so it works without a key.
|
|
762
|
+
if (cmd === 'help') {
|
|
763
|
+
console.log(`nsauditor-ai — Modular AI-assisted network security audit platform
|
|
764
|
+
|
|
765
|
+
Usage:
|
|
766
|
+
nsauditor-ai [scan] --host <ip|cidr|hostname> [options]
|
|
767
|
+
nsauditor-ai [scan] --host-file <path> [options]
|
|
768
|
+
nsauditor-ai license <subcommand>
|
|
769
|
+
nsauditor-ai security <subcommand>
|
|
770
|
+
nsauditor-ai validate
|
|
771
|
+
nsauditor-ai help
|
|
772
|
+
|
|
773
|
+
Scan options:
|
|
774
|
+
--host, --ip, --target <h> Target host, IP, or CIDR
|
|
775
|
+
--host-file <path> File with one host per line
|
|
776
|
+
--plugins <list|all> Plugins to run (e.g. 001,003,020 or "all"; default: all)
|
|
777
|
+
--ports <range> Override port list (e.g. 22,80,443 or 1-1000)
|
|
778
|
+
--out <dir> Output directory for scan artifacts
|
|
779
|
+
--parallel <n> Parallel host concurrency (default 1)
|
|
780
|
+
--fail-on <severity> Exit non-zero if any finding ≥ severity
|
|
781
|
+
--output-format <fmt> Additional report format: sarif | csv | md
|
|
782
|
+
--insecure-https Skip TLS validation on probed HTTPS targets
|
|
783
|
+
--watch CTEM continuous mode
|
|
784
|
+
--interval <minutes> Watch interval (default 60)
|
|
785
|
+
--webhook-url <url> Send delta alerts (must be public; private/loopback blocked)
|
|
786
|
+
--alert-severity <sev> Min severity to alert on (default: high)
|
|
787
|
+
--compliance <framework> Run compliance mapping (e.g. soc2). Enterprise only.
|
|
788
|
+
--compliance-scope <path> JSON file describing the assessment scope
|
|
789
|
+
|
|
790
|
+
License subcommands:
|
|
791
|
+
nsauditor-ai license --status Show active tier, org, seats, expiry
|
|
792
|
+
nsauditor-ai license --capabilities List active capabilities for current tier
|
|
793
|
+
|
|
794
|
+
Security subcommands (macOS Keychain):
|
|
795
|
+
nsauditor-ai security set <KEY> Store a secret (read from stdin)
|
|
796
|
+
nsauditor-ai security delete <KEY> Remove a secret
|
|
797
|
+
nsauditor-ai security list List stored secrets (masked)
|
|
798
|
+
nsauditor-ai security get <KEY> Echo a secret (avoid in shared shells)
|
|
799
|
+
|
|
800
|
+
Environment:
|
|
801
|
+
NSAUDITOR_LICENSE_KEY Pro/Enterprise license JWT (env var; takes precedence)
|
|
802
|
+
NSA_ALLOW_ALL_HOSTS=1 Permit RFC1918 / loopback (local-network auditing)
|
|
803
|
+
CLOUD_PROVIDER=aws|gcp|azure Required for cloud scanner plugins (020/021/022)
|
|
804
|
+
AI_PROVIDER=openai|claude|ollama AI provider for report generation
|
|
805
|
+
COMPLIANCE_TSA_URL RFC 3161 timestamp authority for SOC 2 attestation
|
|
806
|
+
|
|
807
|
+
Examples:
|
|
808
|
+
nsauditor-ai scan --host 10.0.0.1 --plugins all
|
|
809
|
+
CLOUD_PROVIDER=aws AWS_PROFILE=default \\
|
|
810
|
+
nsauditor-ai scan --host aws --plugins 020
|
|
811
|
+
nsauditor-ai scan --host 10.0.0.0/24 --plugins all --compliance soc2
|
|
812
|
+
nsauditor-ai license --status
|
|
813
|
+
|
|
814
|
+
Docs: https://www.nsauditor.com/ai/ | Pricing: https://www.nsauditor.com/ai/pricing/`);
|
|
815
|
+
process.exit(0);
|
|
816
|
+
}
|
|
728
817
|
|
|
729
818
|
// Verify license JWT at startup (~5ms for ES256). Populates _verifiedTier
|
|
730
819
|
// so all subsequent getTierFromEnv() calls return the cryptographically
|
|
@@ -844,6 +933,8 @@ async function main() {
|
|
|
844
933
|
|
|
845
934
|
const opts = { insecureHttps };
|
|
846
935
|
if (ports) opts.ports = ports;
|
|
936
|
+
if (compliance) opts.compliance = compliance;
|
|
937
|
+
if (complianceScope) opts.complianceScope = complianceScope;
|
|
847
938
|
const pm = await PluginManager.create(`${__dirname}/plugins`);
|
|
848
939
|
const promptMode = String(process.env.OPENAI_PROMPT_MODE || 'basic').toLowerCase().trim();
|
|
849
940
|
|