nsauditor-ai 0.1.26 → 0.1.28
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/README.md +14 -3
- package/cli.mjs +42 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,13 +49,18 @@ NSAuditor AI is available in three editions:
|
|
|
49
49
|
| Advanced CTEM + trend analysis | — | ✅ | ✅ |
|
|
50
50
|
| Cloud scanners (AWS/GCP/Azure) | — | — | ✅ |
|
|
51
51
|
| Zero Trust assessment | — | — | ✅ |
|
|
52
|
-
|
|
|
52
|
+
| SOC 2 compliance (7 covered + 5 partial controls) | — | — | ✅ |
|
|
53
|
+
| SLA/MTTR tracking + compensating controls | — | — | ✅ |
|
|
54
|
+
| Recurring-scan attestation (Type II evidence) | — | — | ✅ |
|
|
55
|
+
| GRC platform connector (Vanta) | — | — | ✅ |
|
|
56
|
+
| WORM evidence storage (S3 Object Lock) | — | — | ✅ |
|
|
57
|
+
| Tabletop simulation + SIEM correlation | — | — | ✅ |
|
|
53
58
|
| Docker per-scan isolation | — | — | ✅ |
|
|
54
59
|
| Air-gapped deployment | — | — | ✅ |
|
|
55
60
|
|
|
56
61
|
**This repository is the Community Edition** — fully functional, MIT-licensed, no restrictions. Pro and Enterprise features are available via the [`@nsasoft/nsauditor-ai-ee`](https://www.nsauditor.com/ai/pricing) package.
|
|
57
62
|
|
|
58
|
-
→ [
|
|
63
|
+
→ [Get Pro or Enterprise](https://www.nsauditor.com/ai/pricing/)
|
|
59
64
|
|
|
60
65
|
---
|
|
61
66
|
|
|
@@ -165,6 +170,12 @@ Results land in `./out/<host>_<timestamp>/`:
|
|
|
165
170
|
| 021 | GCP Cloud Scanner | Enterprise | Firewall rules + IAM bindings |
|
|
166
171
|
| 022 | Azure Cloud Scanner | Enterprise | NSG rules + RBAC analysis |
|
|
167
172
|
| 023 | Zero Trust Checker | Enterprise | Segmentation, encryption, identity, lateral movement scoring |
|
|
173
|
+
| — | SOC 2 Compliance Engine | Enterprise | AICPA TSC 2017 control mapping, chain-of-custody, RFC 3161 timestamps, suppression workflow |
|
|
174
|
+
| — | SLA & MTTR Tracking | Enterprise | Per-severity SLA targets, compensating-control flow, finding lifecycle |
|
|
175
|
+
| — | Recurring-Scan Attestation | Enterprise | Multi-scan chronological matrix, cadence gap detection, scope drift (CC8.1) |
|
|
176
|
+
| — | GRC Platform Connector | Enterprise | Native API push to Vanta with retry/backoff, idempotency, rate-limit handling |
|
|
177
|
+
| — | WORM Evidence Storage | Enterprise | S3 Object Lock COMPLIANCE-mode, resource redaction, SHA-256 manifest |
|
|
178
|
+
| — | Tabletop Simulation | Enterprise | Probe-event manifest + SIEM detection correlation, configurable coverage bands |
|
|
168
179
|
|
|
169
180
|
---
|
|
170
181
|
|
|
@@ -588,7 +599,7 @@ License keys are delivered automatically via Stripe webhook — no manual proces
|
|
|
588
599
|
|
|
589
600
|
No license key? Everything in this repository works perfectly without one. The CE is not crippled — it's a complete, production-ready security scanner.
|
|
590
601
|
|
|
591
|
-
→ [Pricing](https://www.nsauditor.com/ai/pricing
|
|
602
|
+
→ [Pricing](https://www.nsauditor.com/ai/pricing/) · [Enterprise contact](https://www.nsauditor.com/ai/enterprise)
|
|
592
603
|
|
|
593
604
|
---
|
|
594
605
|
|
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)
|
|
@@ -536,7 +536,7 @@ async function parseArgs(argv) {
|
|
|
536
536
|
const p = get('plugins');
|
|
537
537
|
if (p && p !== true && p.toLowerCase() !== 'all') {
|
|
538
538
|
args.plugins = p.split(',').map((s) => s.trim()).filter(Boolean);
|
|
539
|
-
} else if (p && p.toLowerCase() === 'all') {
|
|
539
|
+
} else if (p && p !== true && p.toLowerCase() === 'all') {
|
|
540
540
|
args.plugins = 'all';
|
|
541
541
|
}
|
|
542
542
|
args.insecureHttps = !!(get('insecure-https') || get('insecure_https'));
|
|
@@ -570,6 +570,13 @@ async function parseArgs(argv) {
|
|
|
570
570
|
const alertSev = get('alert-severity') || get('alert_severity') || null;
|
|
571
571
|
args.alertSeverity = (alertSev && alertSev !== true) ? alertSev.toLowerCase() : 'high';
|
|
572
572
|
|
|
573
|
+
// Compliance: framework selector + scope file. Forwarded to EE's
|
|
574
|
+
// runCompliancePhase via enrichScan(). No-op without an EE Enterprise license.
|
|
575
|
+
const complianceVal = get('compliance');
|
|
576
|
+
args.compliance = (complianceVal && complianceVal !== true) ? complianceVal : null;
|
|
577
|
+
const complianceScopeVal = get('compliance-scope') || get('compliance_scope');
|
|
578
|
+
args.complianceScope = (complianceScopeVal && complianceScopeVal !== true) ? complianceScopeVal : null;
|
|
579
|
+
|
|
573
580
|
return args;
|
|
574
581
|
}
|
|
575
582
|
|
|
@@ -599,17 +606,34 @@ async function scanSingleHost(pm, host, plugins, opts, promptMode) {
|
|
|
599
606
|
conclusion.result.techniques = techniques;
|
|
600
607
|
}
|
|
601
608
|
|
|
609
|
+
// Pre-compute the per-scan output folder so EE enrichment, compliance
|
|
610
|
+
// artifacts, and AI outputs all land in the same directory. maybeSendToOpenAI
|
|
611
|
+
// will reuse this presetOutDir below.
|
|
612
|
+
const baseOutDir = resolveBaseOutDir();
|
|
613
|
+
await fsp.mkdir(baseOutDir, { recursive: true });
|
|
614
|
+
const ts = nowStamp();
|
|
615
|
+
const outDir = path.join(baseOutDir, `${safeHost(host)}_${ts}`);
|
|
616
|
+
await fsp.mkdir(outDir, { recursive: true });
|
|
617
|
+
|
|
602
618
|
// EE enrichment hook — no-op if @nsasoft/nsauditor-ai-ee is not installed
|
|
619
|
+
// or the license tier doesn't grant intelligenceEngine. Compliance + outDir
|
|
620
|
+
// are forwarded so EE can write scan_finding_queue.json and SOC 2 artifacts.
|
|
603
621
|
try {
|
|
604
622
|
const { enrichScan } = await import('@nsasoft/nsauditor-ai-ee');
|
|
605
|
-
const eeEnrichment = await enrichScan(conclusion, {
|
|
623
|
+
const eeEnrichment = await enrichScan(conclusion, {
|
|
624
|
+
host,
|
|
625
|
+
outDir,
|
|
626
|
+
compliance: opts.compliance ?? process.env.COMPLIANCE_FRAMEWORKS ?? null,
|
|
627
|
+
complianceScope: opts.complianceScope ?? null,
|
|
628
|
+
onWarn: (msg) => console.warn(`[EE] ${msg}`),
|
|
629
|
+
});
|
|
606
630
|
if (eeEnrichment?.enrichedPrompt) {
|
|
607
631
|
conclusion.result = conclusion.result || {};
|
|
608
632
|
conclusion.result.eeEnrichment = eeEnrichment;
|
|
609
633
|
}
|
|
610
634
|
} catch { /* EE not installed — CE proceeds unchanged */ }
|
|
611
635
|
|
|
612
|
-
const { file_paths: ai_file_paths, ai_conclusion } = await maybeSendToOpenAI({ host, results, conclusion, promptMode });
|
|
636
|
+
const { file_paths: ai_file_paths, ai_conclusion } = await maybeSendToOpenAI({ host, results, conclusion, promptMode, outDir });
|
|
613
637
|
|
|
614
638
|
// --- Scan history: record & compare ---
|
|
615
639
|
let scanDiff = null;
|
|
@@ -724,7 +748,7 @@ function maxSeverityInConclusion(conclusion) {
|
|
|
724
748
|
}
|
|
725
749
|
|
|
726
750
|
async function main() {
|
|
727
|
-
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports } = await parseArgs(process.argv);
|
|
751
|
+
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports, compliance, complianceScope } = await parseArgs(process.argv);
|
|
728
752
|
|
|
729
753
|
// Verify license JWT at startup (~5ms for ES256). Populates _verifiedTier
|
|
730
754
|
// so all subsequent getTierFromEnv() calls return the cryptographically
|
|
@@ -844,6 +868,8 @@ async function main() {
|
|
|
844
868
|
|
|
845
869
|
const opts = { insecureHttps };
|
|
846
870
|
if (ports) opts.ports = ports;
|
|
871
|
+
if (compliance) opts.compliance = compliance;
|
|
872
|
+
if (complianceScope) opts.complianceScope = complianceScope;
|
|
847
873
|
const pm = await PluginManager.create(`${__dirname}/plugins`);
|
|
848
874
|
const promptMode = String(process.env.OPENAI_PROMPT_MODE || 'basic').toLowerCase().trim();
|
|
849
875
|
|