nsauditor-ai 0.2.40 → 0.2.42
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 +30 -18
- package/cli.mjs +185 -4
- package/mcp_server.mjs +1 -1
- package/package.json +1 -1
- package/plugins/mdns_scanner.mjs +8 -1
package/README.md
CHANGED
|
@@ -17,11 +17,15 @@ NSAuditor AI is the open-source core of a privacy-first security intelligence pl
|
|
|
17
17
|
|
|
18
18
|
## What's New
|
|
19
19
|
|
|
20
|
-
**The
|
|
20
|
+
**The `feed` commands: offline CVE data can be hand-carried (CE 0.2.42 / Enterprise 0.37.0).** `nsauditor-ai feed bundle` merges the NVD feed files you downloaded on a connected host into one portable archive; `feed import` reads one into the offline store on an isolated host — delivered as a **restricted** distribution rather than a public `npm install`, and amd64 only. The implementations are Enterprise-side (**requires `@nsasoft/nsauditor-ai-ee` >= 0.37.0**); the commands, flags and usage text are here, because this is the package with the `bin`. Optional `--kev` / `--epss` carry **your own** downloads from CISA and FIRST inside the archive, and `--extras-dir` places them on import and prints the environment lines to set — **no exploit data ships with either package**. `bundle` bundles the FEEDS YOU DOWNLOADED, never "your database": the offline store is a lossy derivation of an NVD feed and cannot be turned back into one. Import now names **why** it skipped records — about a quarter of a real NVD year file is skipped by design (withdrawn CVEs, entries with no CPE data) and a bare `skipped 9353` reads as data loss; malformed records are called out separately because those are **not** expected. ⚠️ **A bundle is integrity-checked, not authenticated** — the recorded SHA-256 detects alteration in transit but cannot establish authorship, because it travels inside the archive it covers. Also here: **`--flag=value` is now refused by name** instead of being parsed as a valueless flag with the value discarded, which made `--severity=high` run a default-severity scan and report success — a wrong answer that looked like a right one; and the **mDNS module-shape fallback was a TDZ self-reference**, so it never once fell back. **The air-gap delivery claims are earned back at this release**, with two conditions that travel with them: the dependency-complete bundle is a **restricted** distribution rather than a public install, and it is **amd64** only — no arm64 image is published.
|
|
21
|
+
|
|
22
|
+
**The compliance report now verifies the signatures it renders (CE 0.2.41 / Enterprise 0.36.0).** Paired with Enterprise 0.36.0 — **no CE behaviour change; the peer floor stays `>=0.2.40`, unraised, because nothing in this cycle needs new CE code.** Enterprise-side, a registry member may now carry the approver's public key beside its fingerprint, and when it does, every suppression signature in a compliance report is cryptographically checked — the Ed25519 suppression-signing capability this exercises is **proven as of Enterprise 0.36.0**, its verification gate having run against the published bytes and passed with a tamper negative control, and verified for approvers whose registry entry carries **key material**. Verdicts name the exact bytes they verified, a revoked key's bytes are still checked so signing-after-revocation becomes a cryptographic finding, and a MISSING verdict reads "not checked" rather than "failed" — a report must not accuse an operator of forgery for not having migrated. **Ed25519 suppression signing was `not yet proven` when this shipped**: it shipped the code that made the verification gate satisfiable, and that gate then passed at Enterprise 0.36.0.
|
|
23
|
+
|
|
24
|
+
**The suppression-approval commands land in the CLI (CE 0.2.40 / Enterprise 0.35.0).** `compliance suppress | review | renew | keygen` are new subcommands — thin forwards, the same discipline `compliance attest` established: Enterprise owns validation, defaults, the signing decision and every refusal; CE contributes flags and an exit code. `keygen` writes an approval keypair for a capability that was not yet proven at that release — Ed25519, private half `0600` — and prints an identity-registry member to paste, refusing to overwrite an existing signing key — regenerating one makes every signature it ever produced unverifiable, and nothing reports that until an auditor checks an archived approval. `suppress` signs the approval it writes when `NSAUDITOR_SIGNING_KEY` names a local Ed25519 key, and a malformed key fails at the command with nothing written. `renew` warns that renewing a signed approval invalidates its signature, because the expiry and the renewal record are inside the signed payload. Ed25519 suppression signing was thereby reachable and, **at that release, not yet proven** — its verification gate had not run against published bytes. It passed at Enterprise 0.36.0.
|
|
21
25
|
|
|
22
26
|
**Exploit intelligence lands in Pro, and a risk-score claim gets corrected here (CE 0.2.39 / Enterprise 0.34.0).** Paired with Enterprise 0.34.0 — **no CE behaviour change.** Enterprise findings that carry CVEs are now joined by CVE-ID against a local **CISA KEV** catalog and a local **FIRST EPSS** scores file, banded and ordered exploit-first, so a KEV-listed MEDIUM outranks an unexploited CRITICAL. Both stores are **operator-populated — no feed data ships** — and fail closed when stale, so an out-of-date catalog never reports "not exploited". The CE-side change is a correction this README owed you: *"a composite risk score (severity × exploitability × impact × exposure)"* described inputs the code never had. It computes **CVSS weighted by verification status, with an uplift for initial-access techniques**, and that is what it now says. `prioritize()` in the documented FindingQueue sorts by **severity rank alone** — the exploit-aware ordering is Enterprise-side. Community Edition scanning, plugins and output are unchanged.
|
|
23
27
|
|
|
24
|
-
**The opt-in RFC 3161 register stops understating itself (CE 0.2.38 / Enterprise 0.33.1).** Paired with Enterprise 0.33.1 — **no CE behaviour change; wording only, and it is the wording npm freezes.** Trusted timestamping is opt-in, became operator-configurable in 0.33.0, and has since been proven end to end against a real Time-Stamp Authority twice over: on the npm path, and separately from inside the published Marketplace container image. Six places in this README still told you the container leg was unverified. They now state what is true — **RFC 3161 timestamping is opt-in via `NSAUDITOR_TSA_URL`, with no default ever and an outbound call to the authority you name** — and keep the one caveat that survives: retained images at `:0.32.11` and earlier carry no `openssl` at all, so nothing here speaks for them. **Ed25519 suppression signing was unchanged at that release and still not reachable then** — it became reachable in EE 0.35.0 via `compliance suppress` and is **
|
|
28
|
+
**The opt-in RFC 3161 register stops understating itself (CE 0.2.38 / Enterprise 0.33.1).** Paired with Enterprise 0.33.1 — **no CE behaviour change; wording only, and it is the wording npm freezes.** Trusted timestamping is opt-in, became operator-configurable in 0.33.0, and has since been proven end to end against a real Time-Stamp Authority twice over: on the npm path, and separately from inside the published Marketplace container image. Six places in this README still told you the container leg was unverified. They now state what is true — **RFC 3161 timestamping is opt-in via `NSAUDITOR_TSA_URL`, with no default ever and an outbound call to the authority you name** — and keep the one caveat that survives: retained images at `:0.32.11` and earlier carry no `openssl` at all, so nothing here speaks for them. **Ed25519 suppression signing was unchanged at that release and still not reachable then** — it became reachable in EE 0.35.0 via `compliance suppress` and is **proven as of EE 0.36.0**, verified for approvers whose registry entry carries **key material**.
|
|
25
29
|
|
|
26
30
|
See **[CHANGELOG.md](./CHANGELOG.md)** for the full per-release history.
|
|
27
31
|
|
|
@@ -62,7 +66,7 @@ If you're heading into a **SOC 2, HIPAA, NIST CSF 2.0, PCI DSS, ISO 27001, CIS C
|
|
|
62
66
|
- **ISO/IEC 27001:2022** (ISO + IEC, Oct 2022; 2013 edition retired Oct 31, 2025) — **17 covered + 14 partial + 62 OOS across 93 Annex A controls** (the complete Annex A universe); per-Annex-A-code mapping auditor-canonical for ISO/IEC 17021-1 certification body assessors; Statement of Applicability per Clause 6.1.3.d discipline + ISMS Clauses 4-10 OOS-by-design with 7 Major Nonconformity classes
|
|
63
67
|
- **CIS Critical Security Controls v8** (CIS, May 2021; v8.1 errata June 2024) — **17 covered + 23 partial + 113 OOS across 153 Safeguards / 18 Controls**; per-Safeguard mapping with the **Implementation Group cumulative discipline** (IG1=56 cyber-insurance baseline / IG2 cumulative=130 / IG3 cumulative=153); no-certification-body attestation discipline (INPUT to your CSAT / CIS-CAT Pro self-attestation, never "CIS certified"); Cloud Companion Guide v8 shared-responsibility + CIS-Hardened-Image substrate-evidence credit (4.1/4.2/4.6)
|
|
64
68
|
- **GDPR Article 32 (Security of Processing)** (Regulation (EU) 2016/679) — **4 covered + 5 partial + 2 OOS across 11 Art. 32 sub-measure units**; **GDPR Article 32 infrastructure substrate only — NOT GDPR compliance** (GDPR is a 99-article legal regime; Art. 32 security-of-processing is the only article an infrastructure scanner can substrate-evidence; the rest is operator-side, out of scope by design). Four-factor proportionality (substrate *for* your "appropriate to the risk" determination, never an absolute pass/fail); personal-data-scope attestation (pair with your Art. 30 records of processing); **Art. 83(4) lower fine tier** (€10M/2%, not the €20M/4% headline tier); Art. 32(3)/Art. 42 cloud-provider certification-inheritance
|
|
65
|
-
- 🔐 **Evidence with a verifiable chain of custody** — SHA-256 sidecars on every artifact, verifiable offline. RFC 3161 trusted timestamps are **opt-in since EE 0.33.0** via `NSAUDITOR_TSA_URL`, require an outbound call to the authority you name plus the `openssl` binary, and were exercised against a live Time-Stamp Authority on the npm path on 2026-08-07: the auditor procedure `openssl ts -verify` returned OK, with a one-byte-mutated copy returning FAILED as the control. The `:0.33.0` Marketplace image is proven too: on 2026-08-08 the same round-trip was driven from inside the pushed image against a real authority, verified with the image’s own `openssl`. **Retained images at `:0.32.11` and earlier carry no `openssl` at all**, so nothing here speaks for them. **Ed25519 suppression signing became reachable in EE 0.35.0 via `compliance suppress`** and is **
|
|
69
|
+
- 🔐 **Evidence with a verifiable chain of custody** — SHA-256 sidecars on every artifact, verifiable offline. RFC 3161 trusted timestamps are **opt-in since EE 0.33.0** via `NSAUDITOR_TSA_URL`, require an outbound call to the authority you name plus the `openssl` binary, and were exercised against a live Time-Stamp Authority on the npm path on 2026-08-07: the auditor procedure `openssl ts -verify` returned OK, with a one-byte-mutated copy returning FAILED as the control. The `:0.33.0` Marketplace image is proven too: on 2026-08-08 the same round-trip was driven from inside the pushed image against a real authority, verified with the image’s own `openssl`. **Retained images at `:0.32.11` and earlier carry no `openssl` at all**, so nothing here speaks for them. **Ed25519 suppression signing became reachable in EE 0.35.0 via `compliance suppress`** and is **proven as of EE 0.36.0 for approvers whose registry entry carries key material** — a shipped entry point signs, and a one-character length-preserving tamper makes the report render `signature FAILED verification`. A fingerprint-only registry entry reads `not checked by this report`, which records that no check ran and never that one failed.
|
|
66
70
|
- 🏛️ **Zero Data Exfiltration architecture** — no scan data is collected, transmitted, or stored by Nsasoft; reports are written to your local disk. Air-gapped operation once configured for it (`NSAUDITOR_OFFLINE_ONLY=1` plus a local NVD store; a default run queries NIST's public CVE API). AI analysis happens locally (Ollama) or via your own API keys. Important for PCI DSS CDE-isolation threat models.
|
|
67
71
|
- 🔗 **GRC connectors — Vanta + Drata + Secureframe (Enterprise)** — map compliance findings to your GRC platform's evidence/test records and push them at **scan time** (opt-in). Suppression-aware outcome mapping (Vanta) / structured records (Drata + Secureframe), idempotent retries, rate-limit handling, token redaction, and Zero-Data-Exfiltration egress redaction. Early-access, single-workspace; live validation against production tenants is in progress. See **[GRC Connectors](#grc-connectors-vanta-drata-secureframe)** below.
|
|
68
72
|
- 🗄️ **WORM posture VALIDATION** — we read your bucket's S3 Object Lock configuration per CloudTrail trail bucket and fail the control when it is absent, in GOVERNANCE mode or under-retained. That is substrate FOR a SEC Rule 17a-4(f) / FINRA 4511 retention claim, not the claim itself: the engine WRITING artifacts into the immutable store is built and **not reachable** (no caller), so the retention guarantee stays yours to make about your own archive
|
|
@@ -110,7 +114,7 @@ How Marketplace fulfillment works (ZDE and air-gap preserved — no runtime AWS
|
|
|
110
114
|
| Multi-framework `--compliance soc2,hipaa,nist-csf,pci-dss,iso-27001,cis-v8,gdpr` from one scan | — | — | ✅ |
|
|
111
115
|
| **Enterprise — auditor-grade evidence** | | | |
|
|
112
116
|
| Evidence packs with SHA-256 chain-of-custody (RFC 3161 timestamps opt-in via `NSAUDITOR_TSA_URL`, exercised against a live TSA on both the npm path and the `:0.33.0` container image; retained images `:0.32.11` and earlier carry no `openssl`) | — | — | ✅ |
|
|
113
|
-
| Suppression workflow with approver identity verification (Ed25519 SIGNING reachable 0.35.0,
|
|
117
|
+
| Suppression workflow with approver identity verification (Ed25519 SIGNING reachable 0.35.0, proven 0.36.0, verified per approver holding **key material**) | — | — | ✅ |
|
|
114
118
|
| Chain-of-custody manifests | — | — | ✅ |
|
|
115
119
|
| SLA / MTTR tracking + compensating controls | — | — | ✅ |
|
|
116
120
|
| Recurring-scan attestation (Type II operating-effectiveness) | — | — | ✅ |
|
|
@@ -118,7 +122,7 @@ How Marketplace fulfillment works (ZDE and air-gap preserved — no runtime AWS
|
|
|
118
122
|
| **Enterprise — integration + deployment** | | | |
|
|
119
123
|
| GRC connectors — Vanta + Drata + Secureframe push (scan-time, opt-in) | — | — | ✅ |
|
|
120
124
|
| Tabletop simulation + SIEM correlation — built, **not reachable** (no caller) | — | — | 🚧 |
|
|
121
|
-
| Air-gapped **operation** once installed — offline licence validation, offline CVE matching under `NSAUDITOR_OFFLINE_ONLY=1`. (Air-gapped DELIVERY
|
|
125
|
+
| Air-gapped **operation** once installed — offline licence validation, offline CVE matching under `NSAUDITOR_OFFLINE_ONLY=1`. (Air-gapped DELIVERY ships as of EE 0.37.0 — an offline installation tarball, an install script and a `feed bundle` pipeline — as a **restricted** distribution, amd64 only. No container image bundle ships, and arm64 images remain WITHDRAWN.) | — | — | ✅ |
|
|
122
126
|
|
|
123
127
|
**This repository is the Community Edition** — fully functional, MIT-licensed, no restrictions, no telemetry. Pro and Enterprise features ship via the [`@nsasoft/nsauditor-ai-ee`](https://www.nsauditor.com/ai/pricing) package and install alongside the CE binary once licensed.
|
|
124
128
|
|
|
@@ -302,8 +306,8 @@ Results land in `./out/<host>_<timestamp>/`:
|
|
|
302
306
|
| 1220 | Azure Storage Account Data-Protection (**EE 0.13.2**) | Enterprise | Dedicated Azure Storage Account encryption / transit / authorization auditor — orthogonal to the 1022 scanner's network-exposure dims (no double-emission; mirrors the AWS 1020 + 1120 two-plugin S3 split). HTTPS-only transit (`enableHttpsTrafficOnly`) + minimum TLS version + Shared Key authorization (`allowSharedKeyAccess` — bypasses Azure AD; absent = enabled, never silent-PASS) + infrastructure (double) encryption + encryption key source incl. **customer-managed-key reachability + rotation** (`keyVaultProperties` — a disabled/revoked/version-pinned CMK degrades, not silent-PASS). Conservative classifier: indeterminate field / AccessDenied → evidence-gap; single-subscription scope surfaced explicitly. **CC6.7 / CC6.1 / C1.1** |
|
|
303
307
|
| 1221 | Azure NSG Perimeter (**EE 0.14.0**; UDP lane **EE 0.14.1**) | Enterprise | The Azure analog of AWS 1170 — a CC6.6 network-segmentation perimeter auditor for Azure Network Security Groups. Evaluates each NSG's inbound rules in Azure priority order (first match wins; DenyAllInbound default): all-protocol public Allow + public-source (`*`/`0.0.0.0/0`/`Internet`) to a restricted **TCP** management/data-tier port (SSH/RDP/MSSQL/MySQL/Postgres/Redis/Mongo/SMB/WinRM/etc.) + `::/0` IPv6-wildcard to a restricted port (the dimension 1022's flat lint misses) + **public-source / `::/0` to a restricted UDP service** (SNMP/CLDAP/NTP/rpcbind/IPMI/IKE/Memcached etc. — Dim 2u/3u, EE 0.14.1) + public→non-restricted INFO + PASS substrate. **Attachment-aware** (attached → CRITICAL effective; orphaned → MEDIUM latent) + effective priority/deny-override resolution + `0.0.0.0/1` split-range coverage. Non-overlapping-by-depth with 1022's coarse per-rule NSG lint. Conservative classifier: denied/indeterminate → evidence-gap; one malformed NSG degrades per-resource. **CC6.6** |
|
|
304
308
|
| 1222 | Azure Key Vault Deep Auditor (**EE 0.15.0**) | Enterprise | The third dedicated Azure auditor (after 1220 storage + 1221 NSG) — the KV analog of how 1221 deepens 1022's flat NSG dim. Enumerates each vault's keys, role assignments, and diagnostic settings across 4 dims: (1) key **auto-rotation policy** + (2) key **expiry** (epoch-s/ms/Date/string coerced) + (3) **diagnostic logging → Log Analytics** (`@azure/arm-monitor`) + (4) **privileged-access depth** (RBAC `roleAssignments` admin/data-plane/scope-aware + legacy `accessPolicies` export/wide-crypto breadth). Orthogonal to 1022's vault-property dims (purge/soft-delete/network-ACL/RBAC-mode) — no double-emission. Secret/cert expiry is a deliberate data-plane scope boundary. Conservative classifier: indeterminate field / AccessDenied / arm-monitor absent → evidence-gap; one malformed vault degrades per-resource. **CC6.3 / C1.1 / CC6.1 / CC7.2** |
|
|
305
|
-
| — | SOC 2 Compliance Engine | Enterprise | AICPA TSC 2017 mapping (10 covered + 4 partial controls), SHA-256 chain-of-custody, opt-in RFC 3161 timestamps, exercised against a live TSA on both the npm path and the `:0.33.0` container image, and the suppression workflow. (Ed25519 SIGNING of suppressions is reachable from EE 0.35.0 and
|
|
306
|
-
| — | **HIPAA Compliance Engine (EE 0.9.0)** | Enterprise | HIPAA Security Rule §164.312 Technical Safeguards mapping (7 covered + 3 partial + 45 OOS within §164.312 + entire §164.308 + entire §164.310). HHS Required/Addressable discipline per control. Same evidence infrastructure as SOC 2 (SHA-256 chain-of-custody; opt-in RFC 3161 timestamps; suppression signing reachable from EE 0.35.0
|
|
309
|
+
| — | SOC 2 Compliance Engine | Enterprise | AICPA TSC 2017 mapping (10 covered + 4 partial controls), SHA-256 chain-of-custody, opt-in RFC 3161 timestamps, exercised against a live TSA on both the npm path and the `:0.33.0` container image, and the suppression workflow. (Ed25519 SIGNING of suppressions is reachable from EE 0.35.0 and PROVEN at EE 0.36.0, verified per approver holding **key material**.) |
|
|
310
|
+
| — | **HIPAA Compliance Engine (EE 0.9.0)** | Enterprise | HIPAA Security Rule §164.312 Technical Safeguards mapping (7 covered + 3 partial + 45 OOS within §164.312 + entire §164.308 + entire §164.310). HHS Required/Addressable discipline per control. Same evidence infrastructure as SOC 2 (SHA-256 chain-of-custody; opt-in RFC 3161 timestamps; suppression signing reachable from EE 0.35.0, proven at EE 0.36.0 and verified per approver holding **key material**). Use `--compliance hipaa` or `--compliance soc2,hipaa` for dual-framework reports from a single scan. **Zero BAA required** — Zero Data Exfiltration architecture means ePHI never leaves customer infrastructure. |
|
|
307
311
|
| — | **NIST CSF 2.0 Compliance Engine (EE 0.10.0)** | Enterprise | NIST Cybersecurity Framework 2.0 Core mapping at the auditor-canonical Subcategory level — 13 covered + 10 partial + 83 OOS across 106 of CSF 2.0's 107 Subcategories. Govern function OOS-by-design (GV.SC-04 partial as substrate exception); Respond function OOS-entirely; Implementation Tiers 1-4 OOS as organizational-maturity claims. NIST SP 800-53 Rev. 5 + CIS Critical Security Controls v8 cross-references baked into `informativeReferences`. Use `--compliance nist-csf` or `--compliance soc2,hipaa,nist-csf` for triple-framework reports from a single scan. |
|
|
308
312
|
| — | **PCI DSS v4.0.1 Compliance Engine (EE 0.11.0)** | Enterprise | PCI DSS v4.0.1 (PCI SSC, June 2024 errata; supersedes v4.0 March 2022; v3.2.1 retired March 31, 2024) mapping at the auditor-canonical sub-requirement level for QSA Report on Compliance workflow — **19 covered + 9 partial + 39 OOS across 67 of ~250 sub-requirements (MVP-67 density)** (Req 7.2.2 down-rated covered→partial in EE 0.19.4 — access-by-job-classification is process/HR-gated). Req 12 Information Security Program OOS-by-design entirely. Req 5 anti-malware + Req 9 physical OOS-entirely. **Defined-vs-Customized Approach discipline per Appendix E** — 15 Defined-only sub-requirements enforced at schema layer. **Cardholder Data Environment (CDE) scope operator-attested** via CDE Data Flow Diagram per Req 1.2.4 + Req 12.5.1. **Card-brand AOC enforcement priority view** (Visa CISP / Mastercard SDP / Amex DSOP / Discover DISC). **4 load-bearing schema enrichments** per control: `controlType` + `approachEligibility` + `cloudProviderAttestation` (AWS / Azure / GCP currently-named AOCs) + `cdeScope`. CAO MVP-deferred to EE 0.11.1. Use `--compliance pci-dss` or `--compliance soc2,hipaa,nist-csf,pci-dss` for quad-framework reports from a single scan. |
|
|
309
313
|
| — | **ISO/IEC 27001:2022 Compliance Engine (EE 0.12.0)** | Enterprise | ISO/IEC 27001:2022 (ISO + IEC, October 2022; 2013 edition retired October 31, 2025) Annex A mapping at the auditor-canonical per-Annex-A-code level for ISO/IEC 17021-1 certification body assessors — **17 covered + 14 partial + 62 OOS across 93 Annex A controls** (the complete Annex A universe across 4 themes: A.5 Organizational 37 + A.6 People 8 + A.7 Physical 14 + A.8 Technological 34). **Statement of Applicability per Clause 6.1.3.d discipline** — engine produces substrate for INCLUDED controls; SoA inclusion/exclusion is operator-side. **ISMS Clauses 4-10 OOS-by-design** with 7 Major Nonconformity classes (absence of internal audit per Clause 9.2 OR management review per Clause 9.3 = auto-fail Stage 2). 11 NEW 2022 controls + 5-attribute taxonomy (cybersecurityConcepts 5 categories, NOT 6 like NIST CSF) + 2013-to-2022 transition discipline + Cloud-Provider Certificate Inheritance Matrix. Use `--compliance iso-27001` or any combination for multi-framework reports from a single scan. |
|
|
@@ -352,7 +356,7 @@ nsauditor-ai scan --host aws --plugins all --compliance soc2 --aws-region all
|
|
|
352
356
|
- **Global services** (IAM, account-level S3 enumeration) are audited once regardless of `--aws-region`; the S3 auditors resolve **each bucket's own region** and skip + disclose buckets outside the scoped set (closing latent cross-region false-cleans).
|
|
353
357
|
- **MCP `scan_cloud` (Claude Desktop / Claude Code):** the same scoping is a `regions` argument — *omit* it to scan the server-configured `AWS_REGION`, or pass `["all"]` (or a region-code list like `["us-east-1","eu-west-1"]`) to fan out. Omitting does **not** fan out, so a single tool-call stays within Desktop's timeout.
|
|
354
358
|
|
|
355
|
-
The auditor evidence pack is emitted under `out/` — cover-page Scope Attestation, SHA-256 chain-of-custody sidecars, the suppression workflow and approver identity verification, plus opt-in RFC 3161 trusted-timestamps (`NSAUDITOR_TSA_URL`), exercised against a live TSA on both the npm path and the `:0.33.0` container image. Ed25519 suppression SIGNING is
|
|
359
|
+
The auditor evidence pack is emitted under `out/` — cover-page Scope Attestation, SHA-256 chain-of-custody sidecars, the suppression workflow and approver identity verification, plus opt-in RFC 3161 trusted-timestamps (`NSAUDITOR_TSA_URL`), exercised against a live TSA on both the npm path and the `:0.33.0` container image. Ed25519 suppression SIGNING is reachable from EE 0.35.0 and PROVEN at EE 0.36.0, verified per approver holding **key material**. EE is available at [`www.nsauditor.com/ai/pricing`](https://www.nsauditor.com/ai/pricing).
|
|
356
360
|
|
|
357
361
|
---
|
|
358
362
|
|
|
@@ -673,6 +677,10 @@ nsauditor-ai license install <KEY>
|
|
|
673
677
|
nsauditor-ai license <--status | --capabilities | --plugins>
|
|
674
678
|
nsauditor-ai security <set|delete|list|get> <KEY>
|
|
675
679
|
nsauditor-ai validate
|
|
680
|
+
nsauditor-ai feed bundle --from <dir-of-NVD-feeds> --out <bundle.json.gz> [--kev <f>] [--epss <f>]
|
|
681
|
+
nsauditor-ai feed import --file <feed-or-bundle> [--cache-dir <d>] [--extras-dir <d>] [--append]
|
|
682
|
+
nsauditor-ai compliance <attest|suppress|review|renew|keygen> (Enterprise)
|
|
683
|
+
nsauditor-ai mcp
|
|
676
684
|
nsauditor-ai --help (or -h, or `help`)
|
|
677
685
|
nsauditor-ai --version (or -v, or `version`)
|
|
678
686
|
```
|
|
@@ -844,7 +852,7 @@ explanatory message if it is absent. CLI-only; the MCP surface does not reach th
|
|
|
844
852
|
# (pass --email/--role/--team too, or the member prints REPLACE ME placeholders)
|
|
845
853
|
nsauditor-ai compliance keygen --key ~/.nsauditor/approver.pem --approver "Ann Approver"
|
|
846
854
|
|
|
847
|
-
# Record an approval; SIGNS it when NSAUDITOR_SIGNING_KEY names a local Ed25519 key (
|
|
855
|
+
# Record an approval; SIGNS it when NSAUDITOR_SIGNING_KEY names a local Ed25519 key (verified per approver holding key material)
|
|
848
856
|
NSAUDITOR_SIGNING_KEY=~/.nsauditor/approver.pem \
|
|
849
857
|
nsauditor-ai compliance suppress --suppressions ./out/suppressions.json \
|
|
850
858
|
--source auth_agent --title-pattern "SSH password authentication enabled" \
|
|
@@ -867,8 +875,12 @@ auditor checks an archived approval); a malformed signing key fails the command
|
|
|
867
875
|
it warns loudly and tells you to re-approve.
|
|
868
876
|
|
|
869
877
|
⚠️ **`--flag=value` is not supported**, here or on any flag this CLI has: use `--flag value`.
|
|
870
|
-
Ed25519 suppression signing is reachable from Enterprise 0.35.0 and **
|
|
871
|
-
verification gate
|
|
878
|
+
Ed25519 suppression signing is reachable from Enterprise 0.35.0, and **proven at 0.36.0 for approvers whose registry entry carries key material**.
|
|
879
|
+
Its verification gate ran against the published bytes and passed, tamper negative control included.
|
|
880
|
+
A fingerprint-only registry entry reads `not checked by this report`, which records that no check
|
|
881
|
+
ran and never that one failed.
|
|
882
|
+
Verification runs for approvers whose registry entry carries key material; a fingerprint-only entry
|
|
883
|
+
reads `not checked by this report`, which records that no check ran and never that one failed.
|
|
872
884
|
|
|
873
885
|
**Compliance evidence (`NSAUDITOR_*` — Enterprise, all opt-in):**
|
|
874
886
|
|
|
@@ -897,15 +909,15 @@ NSAUDITOR_SIGNING_KEY= # A REFERENCE, never key material: keychain:LA
|
|
|
897
909
|
# (mode 0600) | awskms:alias/… — see the honesty note below.
|
|
898
910
|
```
|
|
899
911
|
|
|
900
|
-
|
|
901
|
-
|
|
912
|
+
Both of these are capabilities now, and each was proven later than it was wired — saying exactly
|
|
913
|
+
when, on which delivery vehicle, and for whom is the point:
|
|
902
914
|
|
|
903
|
-
- **`NSAUDITOR_SIGNING_KEY` is CONSUMED from EE 0.35.0, and the note below used to say otherwise.** EE 0.33.0 stopped parsing it at option-resolution time — a malformed value used to abort the EE stage for a setting nothing read — so it is carried through unparsed; `parseSigningRef` / `resolveSigner` hold the parse and the offline veto at the point a key is used. As of EE 0.35.0 `compliance suppress` signs the approval it writes when this variable names a local Ed25519 key —
|
|
904
|
-
|
|
905
|
-
for
|
|
915
|
+
- **`NSAUDITOR_SIGNING_KEY` is CONSUMED from EE 0.35.0, and the note below used to say otherwise.** EE 0.33.0 stopped parsing it at option-resolution time — a malformed value used to abort the EE stage for a setting nothing read — so it is carried through unparsed; `parseSigningRef` / `resolveSigner` hold the parse and the offline veto at the point a key is used. As of EE 0.35.0 `compliance suppress` signs the approval it writes when this variable names a local Ed25519 key — proven as of EE 0.36.0 and verified per approver holding key material.
|
|
916
|
+
Its verification gate ran against the published bytes and passed, so a produced signature IS
|
|
917
|
+
evidence for those approvers; a fingerprint-only registry entry reads `not checked by this report`. The record's `algorithm` and `backend` fields were frozen *before* the first
|
|
906
918
|
signature could reach a customer archive, because retrofitting that later breaks every
|
|
907
919
|
auditor holding one.
|
|
908
|
-
- **`NSAUDITOR_TSA_URL` is wired, and
|
|
920
|
+
- **`NSAUDITOR_TSA_URL` is wired, and it is proven on both delivery vehicles — the npm path on 2026-08-07, and from inside the `:0.33.0` container image on 2026-08-08.**
|
|
909
921
|
The live check this note used to say was outstanding has been run: through the installed
|
|
910
922
|
binary, against a real Time-Stamp Authority, the auditor procedure `openssl ts -verify`
|
|
911
923
|
returns OK on the compliance report, the scope attestation and the chain of custody; the
|
|
@@ -1124,7 +1136,7 @@ NSAuditor AI is built on a **Zero Data Exfiltration (ZDE)** architecture:
|
|
|
1124
1136
|
- **No data processing.** Nsasoft US LLC never sees, stores, or processes your scan results.
|
|
1125
1137
|
- **AI is opt-in.** External AI calls use your own API keys. Redaction runs locally first.
|
|
1126
1138
|
- **License validation is offline.** JWT signature verified locally with an embedded public key.
|
|
1127
|
-
- **Air-gappable, once configured for it.** Scanning, analysis, license verification and evidence-pack generation all run with no outbound network access; Enterprise adds offline CVE matching from a local NVD store under `NSAUDITOR_OFFLINE_ONLY=1`, which emits an explicit coverage gap rather than a silent clean when the store cannot answer. Stated precisely because it matters operationally: a **default** scan still attempts NVD egress unless that variable is set. The other outbound paths — AI enrichment, the GRC push, the continuous-monitoring webhook,
|
|
1139
|
+
- **Air-gappable, once configured for it.** Scanning, analysis, license verification and evidence-pack generation all run with no outbound network access; Enterprise adds offline CVE matching from a local NVD store under `NSAUDITOR_OFFLINE_ONLY=1`, which emits an explicit coverage gap rather than a silent clean when the store cannot answer. Stated precisely because it matters operationally: a **default** scan still attempts NVD egress unless that variable is set. The other outbound paths — AI enrichment, the GRC push, the continuous-monitoring webhook, the opt-in RFC 3161 timestamping path (`NSAUDITOR_TSA_URL`, no default ever), and the AWS KMS signing path that ships but is not yet wired — are opt-in and off by default; the paths that are *not* optional are the scan target itself, your own cloud provider's APIs during a cloud scan, and DNS resolution of the target. All of them are enumerated with their trigger and default state in the egress register (EE `docs/architecture.md` §14.1.1), which is generated from code and guarded in both directions — deliberately, so this sentence never again has to carry a completeness claim that prose alone cannot keep true. Populating the local NVD store is the operator's; no feed data is delivered with the product. The prior absolute form of this bullet — *"Fully air-gappable. Every feature works without internet access (Enterprise includes offline NVD feeds)"* — is **WITHDRAWN** (CE 0.2.33): quoted here so the withdrawal record stays on this page now that release history lives in the [CHANGELOG](./CHANGELOG.md).
|
|
1128
1140
|
|
|
1129
1141
|
Nsasoft US LLC is not a data processor, data controller, or business associate under any data protection regulation. You own and control all data produced by NSAuditor AI.
|
|
1130
1142
|
|
package/cli.mjs
CHANGED
|
@@ -603,6 +603,40 @@ export async function parseArgs(argv) {
|
|
|
603
603
|
}
|
|
604
604
|
if (a.length && !a[0].startsWith('--')) args.cmd = a[0];
|
|
605
605
|
|
|
606
|
+
// ⚠️ REFUSE `--flag=value` LOUDLY RATHER THAN IGNORING IT SILENTLY.
|
|
607
|
+
//
|
|
608
|
+
// `get()` below is an exact-token `indexOf('--' + name)`, so `--plugins=port_scanner` is a
|
|
609
|
+
// DIFFERENT token and returns undefined. The consequences were not uniformly loud, which is
|
|
610
|
+
// what made this worth a refusal rather than a note: `--host=1.2.3.4` yields undefined and the
|
|
611
|
+
// CLI refuses because a missing host is checked, but `--plugins=port_scanner` fell through to
|
|
612
|
+
// the `all` default and ran EVERY plugin against whatever the operator was scanning — a
|
|
613
|
+
// materially different scan than they asked for, and one they cannot see.
|
|
614
|
+
//
|
|
615
|
+
// Measured before adding this: ZERO documented `=` usages anywhere in either repo. The shape
|
|
616
|
+
// never worked, so nothing can depend on it, and refusing costs no working invocation.
|
|
617
|
+
//
|
|
618
|
+
// ⚠️ THIS IS A REFUSAL, NOT SUPPORT. Full `=` parsing touches every flag and every test that
|
|
619
|
+
// depends on current parsing; it is its own lane. A loud refusal is strictly better than silent
|
|
620
|
+
// divergence for every operator typing `=` today, and it leaves that lane a clean starting state.
|
|
621
|
+
{
|
|
622
|
+
const eqFlag = a.find((t) => typeof t === 'string' && /^--[a-z0-9][a-z0-9-]*=/i.test(t));
|
|
623
|
+
if (eqFlag) {
|
|
624
|
+
const [name, ...rest] = eqFlag.slice(2).split('=');
|
|
625
|
+
// ⚠️ THROW, DO NOT `process.exit()` — and this was learned the hard way ONE HOUR AFTER
|
|
626
|
+
// finding the same shape elsewhere. The first version called `process.exit(2)` here,
|
|
627
|
+
// matching the webhook-rejection precedent a few lines below. `parseArgs` is EXPORTED and
|
|
628
|
+
// called in-process by the test suite, so that exit killed the runner mid-file:
|
|
629
|
+
// `cli_approval_surface.test.mjs` reported `tests 1 · pass 0` — a count of the FILE, with
|
|
630
|
+
// every other case in it silently not running. An exported function that can kill its
|
|
631
|
+
// caller's process is untestable by construction. The entry point below maps this code to
|
|
632
|
+
// exit 2, so the operator-facing contract is unchanged.
|
|
633
|
+
const err = new Error(`${eqFlag}: the --flag=value form is not supported. `
|
|
634
|
+
+ `Use a space: --${name} ${rest.join('=') || '<value>'}`);
|
|
635
|
+
err.code = 'EFLAGSHAPE';
|
|
636
|
+
throw err;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
606
640
|
const get = (name) => {
|
|
607
641
|
const i = a.indexOf(`--${name}`);
|
|
608
642
|
if (i === -1) return undefined;
|
|
@@ -681,9 +715,39 @@ export async function parseArgs(argv) {
|
|
|
681
715
|
|
|
682
716
|
// ── the approval surface (EE 0.35.0) ────────────────────────────────────────
|
|
683
717
|
// Parsed HERE with every other flag rather than read from `process.argv` in the
|
|
684
|
-
// subcommand, so
|
|
685
|
-
//
|
|
718
|
+
// subcommand, so ONE parser owns the flag shapes. `attest` taught this: a second parser is a
|
|
719
|
+
// second set of edge cases.
|
|
720
|
+
//
|
|
721
|
+
// ⚠️ CORRECTED 2026-08-15 — THIS COMMENT USED TO CLAIM THE PARSER OWNS THE `--flag=value` SHAPE.
|
|
722
|
+
// It does not, and never has: `get()` is an exact-token `indexOf('--' + name)`, so `--flag=value`
|
|
723
|
+
// is a DIFFERENT token and returns undefined. Measured: `--suppressions=/tmp/s.json` -> null,
|
|
724
|
+
// while `--suppressions /tmp/s.json` -> "/tmp/s.json". The documented intent was right and the
|
|
725
|
+
// code did the other thing, with nothing comparing them.
|
|
726
|
+
// Consequences differ per flag and are NOT uniformly loud: `--host=1.2.3.4` yields undefined and
|
|
727
|
+
// the CLI refuses ("Fatal: --host or --host-file is required"), but `--plugins=port_scanner`
|
|
728
|
+
// falls through to the `all` default and SILENTLY runs every plugin instead of the one asked for.
|
|
729
|
+
// Adding `=` support is a CLI-wide change touching every flag and belongs in its own measured
|
|
730
|
+
// commit, not smuggled in beside a feature — boarded rather than done here.
|
|
686
731
|
const str = (name) => { const v = get(name); return (v && v !== true) ? v : null; };
|
|
732
|
+
// F1 air-gapped delivery: the hand-carry feed pipeline's flags. Parsed HERE for the same
|
|
733
|
+
// reason `approvalArgs` is — a second parser is a second set of edge cases, and the approval
|
|
734
|
+
// surface already paid for that with a ReferenceError on its first real invocation.
|
|
735
|
+
args.feedArgs = {
|
|
736
|
+
from: str('from'),
|
|
737
|
+
out: str('out'),
|
|
738
|
+
file: str('file'),
|
|
739
|
+
cacheDir: str('cache-dir') ?? str('cache_dir'),
|
|
740
|
+
// Boolean: present means true. NOT defaulted to true — an accumulating default means a
|
|
741
|
+
// re-import never REPLACES, so a store the operator believes they refreshed silently keeps
|
|
742
|
+
// every stale record it ever held.
|
|
743
|
+
append: get('append') === true || str('append') === 'true',
|
|
744
|
+
// D-95.3: the operator's OWN exploit-intelligence downloads ride the archive. The product
|
|
745
|
+
// ships no KEV/EPSS data of its own; in an air-gapped enclave there is no other way to get it
|
|
746
|
+
// across. `--extras-dir` is where import PLACES them, and the env lines are printed after.
|
|
747
|
+
kev: str('kev'),
|
|
748
|
+
epss: str('epss'),
|
|
749
|
+
extrasDir: str('extras-dir') ?? str('extras_dir'),
|
|
750
|
+
};
|
|
687
751
|
args.approvalArgs = {
|
|
688
752
|
suppressions: str('suppressions'),
|
|
689
753
|
keyPath: str('key'),
|
|
@@ -1041,7 +1105,7 @@ export async function preflightGrcIfRequested(env, opts = {}) {
|
|
|
1041
1105
|
|
|
1042
1106
|
export async function main() {
|
|
1043
1107
|
const args = await parseArgs(process.argv);
|
|
1044
|
-
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports, compliance, complianceScope, complianceHistory, slaPolicy, attestWindow, framework, awsRegion, approvalArgs } = args;
|
|
1108
|
+
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports, compliance, complianceScope, complianceHistory, slaPolicy, attestWindow, framework, awsRegion, approvalArgs, feedArgs } = args;
|
|
1045
1109
|
|
|
1046
1110
|
// Version: handled before license verification so it works without a key.
|
|
1047
1111
|
// CE-0.1.30.1 — closes the discovery-flag UX gap where pre-fix
|
|
@@ -1108,6 +1172,16 @@ Scan options:
|
|
|
1108
1172
|
|
|
1109
1173
|
Compliance subcommands:
|
|
1110
1174
|
nsauditor-ai compliance attest --history <dir> [--framework <fw>] [--window 6m|12m|90d]
|
|
1175
|
+
nsauditor-ai feed bundle --from <dir-of-downloaded-NVD-feeds> --out <bundle.json.gz>
|
|
1176
|
+
[--kev <known_exploited_vulnerabilities.json>]
|
|
1177
|
+
[--epss <epss_scores.csv.gz>]
|
|
1178
|
+
--kev / --epss carry YOUR OWN downloads from CISA and FIRST inside the archive. No
|
|
1179
|
+
exploit data ships with this product; in an air-gapped enclave the archive is the only
|
|
1180
|
+
way across. Both are validated here, on the connected host, where a bad file can still
|
|
1181
|
+
be re-downloaded.
|
|
1182
|
+
nsauditor-ai feed import --file <feed-or-bundle.json.gz> [--cache-dir <dir>] [--append]
|
|
1183
|
+
[--extras-dir <dir>]
|
|
1184
|
+
--extras-dir places any carried KEV/EPSS and prints the env lines to set.
|
|
1111
1185
|
nsauditor-ai compliance keygen --key <path> [--approver <name>] [--email <e>] [--role <r>]
|
|
1112
1186
|
[--team <t>] [--force]
|
|
1113
1187
|
Generate an Ed25519 approval keypair (private 0600) and print
|
|
@@ -2172,6 +2246,98 @@ Docs: https://www.nsauditor.com/ai/ | Pricing: https://www.nsauditor.com/ai/
|
|
|
2172
2246
|
// veto, framework-alias resolution and every default. CE contributes the flag
|
|
2173
2247
|
// surface and the exit code — nothing here can drift out of sync with the engine
|
|
2174
2248
|
// because nothing here decides anything.
|
|
2249
|
+
if (cmd === 'feed') {
|
|
2250
|
+
const sub = process.argv[3];
|
|
2251
|
+
|
|
2252
|
+
// ── THE AIR-GAP HAND-CARRY PIPELINE (EE F1) ────────────────────────────
|
|
2253
|
+
// `bundle` merges the NVD feeds an operator downloaded on a CONNECTED host into one portable
|
|
2254
|
+
// archive; `import` consumes it on the ISOLATED host through EE's `importFeed()`. Same
|
|
2255
|
+
// THIN-FORWARD discipline as `compliance`: EE owns validation, the refusals and every
|
|
2256
|
+
// decision; CE contributes a flag surface and an exit code.
|
|
2257
|
+
//
|
|
2258
|
+
// ⚠️ `feed bundle` BUNDLES THE FEEDS YOU DOWNLOADED — never "your database". The offline
|
|
2259
|
+
// store is a lossy derivation of an NVD feed (configurations flattened to matches, references
|
|
2260
|
+
// dropped, rejected and zero-CPE CVEs discarded at import), so it cannot be turned back into
|
|
2261
|
+
// one. Bundling the SOURCE keeps `importFeed()` the single ingest path across the air-gap
|
|
2262
|
+
// trust boundary — its gzip-bomb cap covers the bundle for free — and mints no file format
|
|
2263
|
+
// we would then own as a frozen compatibility surface.
|
|
2264
|
+
//
|
|
2265
|
+
// ⚠️ NOTHING HERE FLIPS A CLAIM. `feed-import-cli`, `offline-install-tarball`,
|
|
2266
|
+
// `install-script` and `airgap-deployment` stay withdrawn until the trio publishes AND the
|
|
2267
|
+
// NIC-down gate passes on the BUILT artifacts. Publishing a surface is not proving it.
|
|
2268
|
+
if (sub === 'bundle' || sub === 'import') {
|
|
2269
|
+
let ee;
|
|
2270
|
+
try {
|
|
2271
|
+
ee = await import('@nsasoft/nsauditor-ai-ee');
|
|
2272
|
+
} catch {
|
|
2273
|
+
console.error(`feed ${sub} requires the Enterprise package (@nsasoft/nsauditor-ai-ee).`);
|
|
2274
|
+
process.exit(2);
|
|
2275
|
+
}
|
|
2276
|
+
const F = feedArgs ?? {};
|
|
2277
|
+
try {
|
|
2278
|
+
if (sub === 'bundle') {
|
|
2279
|
+
const out = await ee.bundleFeedCommand({
|
|
2280
|
+
from: F.from, out: F.out, kev: F.kev, epss: F.epss,
|
|
2281
|
+
});
|
|
2282
|
+
console.log(`bundled ${out.vulnerabilities} CVE(s) from ${out.sourceFiles} feed file(s)`
|
|
2283
|
+
+ `${out.duplicatesDropped ? ` (${out.duplicatesDropped} duplicate(s) dropped)` : ''}`
|
|
2284
|
+
+ ` -> ${out.out} (${(out.bytes / 1048576).toFixed(1)} MB)`);
|
|
2285
|
+
// ⚠️ ABSENCE IS STATED, NEVER OMITTED. An operator who is not told the bundle carries no
|
|
2286
|
+
// KEV discovers it from empty scan output on the isolated side, where `kev: null` on
|
|
2287
|
+
// every finding is indistinguishable from a host with nothing exploited.
|
|
2288
|
+
if (out.carriedNote) console.log(` exploit intel: ${out.carriedNote}`);
|
|
2289
|
+
} else {
|
|
2290
|
+
const out = await ee.importFeedCommand({
|
|
2291
|
+
file: F.file, cacheDir: F.cacheDir || undefined, append: F.append === true,
|
|
2292
|
+
extrasDir: F.extrasDir || undefined,
|
|
2293
|
+
});
|
|
2294
|
+
console.log(`imported ${out.imported} CVE(s), skipped ${out.skipped}, errors ${out.errors}`);
|
|
2295
|
+
// ⚠️ THE SKIP SPLIT PRINTS HERE BECAUSE THE DATA-LOSS TICKET ORIGINATES HERE.
|
|
2296
|
+
// On a real NVD year file ~24% of records are skipped (measured on the 2024 feed:
|
|
2297
|
+
// 39,219 in, 29,866 imported, 9,353 skipped). An operator reading a bare "skipped 9353"
|
|
2298
|
+
// files that as data loss, and a docs-only note is read AFTER the ticket, not before.
|
|
2299
|
+
// So the reasons are named at the terminal, in one line, with what they mean.
|
|
2300
|
+
if (out.skipped > 0) {
|
|
2301
|
+
const parts = [];
|
|
2302
|
+
if (out.skippedRejected) parts.push(`${out.skippedRejected} withdrawn by NVD (Rejected)`);
|
|
2303
|
+
if (out.skippedNoCpe) parts.push(`${out.skippedNoCpe} with no CPE match data`);
|
|
2304
|
+
if (out.skippedMalformed) parts.push(`${out.skippedMalformed} malformed (no CVE id)`);
|
|
2305
|
+
if (parts.length) {
|
|
2306
|
+
console.log(` skipped breakdown: ${parts.join(', ')}`);
|
|
2307
|
+
console.log(' Skips are expected — NVD publishes withdrawn CVEs and entries with no '
|
|
2308
|
+
+ 'CPE data, and neither can ever match a scan. This is not data loss.'
|
|
2309
|
+
+ (out.skippedMalformed
|
|
2310
|
+
? ' ⚠️ Malformed records are NOT expected: re-download the feed and check its checksum.'
|
|
2311
|
+
: ''));
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
// ⚠️ D-95.3: THE ENV LINES ARE THE DELIVERABLE, NOT A NICETY. The product reads these
|
|
2315
|
+
// stores from two environment variables and ships no exploit data of its own. A file
|
|
2316
|
+
// placed on the isolated host that the operator is never told to point at leaves every
|
|
2317
|
+
// finding reading `kev: null` — which is exactly what a host with nothing exploited
|
|
2318
|
+
// reports. Printing the resolved paths is what turns a placed file into a used one.
|
|
2319
|
+
if (out.extrasPlaced?.length) {
|
|
2320
|
+
console.log(`\nplaced ${out.extrasPlaced.map((p) => p.kind).join(' + ')} — set these `
|
|
2321
|
+
+ 'before scanning:');
|
|
2322
|
+
console.log(out.envLines);
|
|
2323
|
+
} else if (out.extrasNote) {
|
|
2324
|
+
console.log(`\n${out.extrasNote}`);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
process.exit(0);
|
|
2328
|
+
} catch (err) {
|
|
2329
|
+
// Refusals stay loud: an empty source set, a file that is not an NVD feed, a gzip over
|
|
2330
|
+
// the bomb cap, a missing path. Every one of them is the operator learning something
|
|
2331
|
+
// true on the host that can still fix it.
|
|
2332
|
+
console.error(`Fatal: ${err.message}`);
|
|
2333
|
+
process.exit(2);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
console.error('Usage: nsauditor-ai feed <bundle|import> …');
|
|
2338
|
+
process.exit(2);
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2175
2341
|
if (cmd === 'compliance') {
|
|
2176
2342
|
const sub = process.argv[3];
|
|
2177
2343
|
|
|
@@ -2221,7 +2387,16 @@ Docs: https://www.nsauditor.com/ai/ | Pricing: https://www.nsauditor.com/ai/
|
|
|
2221
2387
|
+ `${summary.approaching} approaching expiry · ${summary.expired} expired · `
|
|
2222
2388
|
+ `${summary.no_expiry} without expiry`);
|
|
2223
2389
|
for (const r of rows) {
|
|
2224
|
-
|
|
2390
|
+
// ⚠️ READ THE FIELDS EE ACTUALLY EMITS. `reviewCommand` returns rows shaped
|
|
2391
|
+
// { scanId, file, suppression, status, daysRemaining } — the expiry class is `status`
|
|
2392
|
+
// (NOT `expiryStatus`) and the id/approver live INSIDE `suppression`. Reading the wrong
|
|
2393
|
+
// names printed `[undefined] (no-id) — (no approver)` for EVERY row, including records
|
|
2394
|
+
// `compliance suppress` had just written itself. The SUMMARY line above was correct
|
|
2395
|
+
// throughout, and that is what hid it: a correct aggregate over broken rows.
|
|
2396
|
+
const sup = r.suppression ?? {};
|
|
2397
|
+
const who = typeof sup.approver === 'string' ? sup.approver : sup.approver?.name;
|
|
2398
|
+
console.log(` [${r.status ?? 'unknown'}] ${sup.id ?? '(no-id)'} — ${who ?? '(no approver)'}`
|
|
2399
|
+
+ (sup.status ? ` · ${sup.status}` : ''));
|
|
2225
2400
|
}
|
|
2226
2401
|
process.exit(0);
|
|
2227
2402
|
}
|
|
@@ -2605,6 +2780,12 @@ const _isEntrypoint = (() => {
|
|
|
2605
2780
|
})();
|
|
2606
2781
|
if (_isEntrypoint) {
|
|
2607
2782
|
main().catch((err) => {
|
|
2783
|
+
// A flag-shape refusal is an operator error, not a crash: name it and exit 2, this CLI's
|
|
2784
|
+
// "could not do what you asked" code. Everything else keeps the stack and exit 1.
|
|
2785
|
+
if (err?.code === 'EFLAGSHAPE') {
|
|
2786
|
+
console.error(`[ERROR] ${err.message}`);
|
|
2787
|
+
process.exit(2);
|
|
2788
|
+
}
|
|
2608
2789
|
console.error(err?.stack || err);
|
|
2609
2790
|
process.exit(1);
|
|
2610
2791
|
});
|
package/mcp_server.mjs
CHANGED
|
@@ -259,7 +259,7 @@ export const TOOLS = [
|
|
|
259
259
|
{
|
|
260
260
|
name: 'scan_cloud',
|
|
261
261
|
description:
|
|
262
|
-
'Audit one or more cloud accounts (AWS / GCP / Azure) for security & compliance posture using the credentials configured in the server environment. Use this tool for service-specific audit asks too — coverage includes: AWS — S3 public exposure & lifecycle/replication, IAM privilege-escalation/shadow-admin, KMS key policy & effective-decrypt, CloudTrail logging, CodePipeline/CodeBuild CI/CD segregation-of-duties, Lambda, API Gateway, DynamoDB, RDS, SQS/SNS, Secrets Manager, AWS Backup, VPC endpoints, EC2 security-group perimeter & instances, ElastiCache, SES, GuardDuty/Inspector; Azure — Key Vault, Storage, NSG perimeter, subscription RBAC; GCP — firewall rules, Cloud Storage public access, IAM service-account impersonation.
|
|
262
|
+
'Audit one or more cloud accounts (AWS / GCP / Azure) for security & compliance posture using the credentials configured in the server environment. Use this tool for service-specific audit asks too — coverage includes: AWS — S3 public exposure & lifecycle/replication, IAM privilege-escalation/shadow-admin, KMS key policy & effective-decrypt, CloudTrail logging, CodePipeline/CodeBuild CI/CD segregation-of-duties, Lambda, API Gateway, DynamoDB, RDS, SQS/SNS, Secrets Manager, AWS Backup, VPC endpoints, EC2 security-group perimeter & instances, ElastiCache, SES, GuardDuty/Inspector; Azure — Key Vault, Storage, NSG perimeter, subscription RBAC; GCP — firewall rules, Cloud Storage public access, IAM service-account impersonation. The PRODUCT maps findings to SOC 2, HIPAA, NIST CSF 2.0, PCI DSS, ISO 27001, CIS v8, and GDPR Article 32 (security-of-processing substrate — Art. 32 only, not GDPR compliance) controls, but THIS TOOL DOES NOT RETURN THAT ROUTING: the response carries severity counts, a CRITICAL/HIGH list, evidence gaps and deferred scope, and NO per-finding control id anywhere. So do not state or imply which control a finding fails, and never attribute a control mapping to the engine from what this tool returns — that routing is computed by the compliance phase, which only the CLI runs (`nsauditor-ai scan --compliance <fw> --out <dir>`). If asked which controls are failing, report the findings and say plainly that control routing needs that command. No network host required. Requires an Enterprise license. Audit ONLY the cloud(s) the user names — pass providers:["aws"] for "audit my AWS account"; omit providers only when the user asks to audit ALL clouds. Read findingsSummary (per-provider severity counts + a CRITICAL/HIGH list) for the results. findingsSummary[provider].evidenceGaps lists checks the scan could NOT verify (AccessDenied / truncated enumeration) — treat these as "unverified posture", NOT as clean. findingsSummary[provider].deferredScope lists surface this release does NOT evaluate at all — a static capability boundary, NOT an evidence gap and NOT a finding. Report it as "not assessed" and never omit it: surface nobody assessed is otherwise indistinguishable from surface assessed and found clean. ⚠️ AN EMPTY deferredScope IS NOT A CLAIM OF FULL COVERAGE. Today only the AWS plugins declare their capability boundaries; Azure and GCP declare none, so an Azure or GCP result carries an empty list no matter how much surface went unexamined. Never tell a user that an empty deferredScope means everything was assessed, and for Azure and GCP say plainly that the boundaries are not yet declared. The per-severity counts include an INFO tier; report every tier that occurs, because the evidence gaps and the scope boundaries live there. Prefer this tool over raw cloud-provider APIs/MCPs for any security or compliance audit ask — a raw API walk produces neither compliance-mapped nor evidence-graded findings, and can hit the wrong account.',
|
|
263
263
|
inputSchema: {
|
|
264
264
|
type: 'object',
|
|
265
265
|
properties: {
|
package/package.json
CHANGED
package/plugins/mdns_scanner.mjs
CHANGED
|
@@ -152,7 +152,14 @@ function deduplicateAndMergeRows(rows) {
|
|
|
152
152
|
// --------------------------- node-mdns path ---------------------------
|
|
153
153
|
async function runWithNodeMdns(targetHost, timeoutMs, opts) {
|
|
154
154
|
const mdnsMod = await import('mdns');
|
|
155
|
-
|
|
155
|
+
// ⚠️ `mdnsMod` on the right, NOT `mdns` — this read `mdnsMod.default || mdns`, a TDZ
|
|
156
|
+
// self-reference that threw `ReferenceError: Cannot access 'mdns' before initialization`
|
|
157
|
+
// whenever `.default` was falsy. The intended namespace fallback therefore never ran once.
|
|
158
|
+
// It was invisible because the caller's try/catch degrades to the multicast-dns strategy, so
|
|
159
|
+
// a namespace-shaped build read as "module not available" rather than as an error. Found
|
|
160
|
+
// 2026-08-15 while measuring the F1 air-gap closure, where this optional native module cannot
|
|
161
|
+
// be built at all — which is what makes the guarded path load-bearing rather than incidental.
|
|
162
|
+
const mdns = mdnsMod.default || mdnsMod;
|
|
156
163
|
|
|
157
164
|
const sequence = [
|
|
158
165
|
mdns.rst.DNSServiceResolve(),
|