nsauditor-ai 0.2.39 → 0.2.41
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 +50 -10
- package/cli.mjs +160 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,9 +17,13 @@ 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 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 stays **not yet proven**, its verification gate not having run against published bytes. 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 stays `not yet proven`**: this ships the code that makes the verification gate satisfiable, which is not a gate that has passed.
|
|
21
|
+
|
|
22
|
+
**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 is not yet proven — 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 is thereby reachable and **not yet proven** — its verification gate has not run against published bytes, so a produced signature is not yet evidence anything vouches for.
|
|
23
|
+
|
|
20
24
|
**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.
|
|
21
25
|
|
|
22
|
-
**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
|
|
26
|
+
**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 **not yet proven**.
|
|
23
27
|
|
|
24
28
|
See **[CHANGELOG.md](./CHANGELOG.md)** for the full per-release history.
|
|
25
29
|
|
|
@@ -60,7 +64,7 @@ If you're heading into a **SOC 2, HIPAA, NIST CSF 2.0, PCI DSS, ISO 27001, CIS C
|
|
|
60
64
|
- **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
|
|
61
65
|
- **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)
|
|
62
66
|
- **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
|
|
63
|
-
- 🔐 **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
|
|
67
|
+
- 🔐 **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 **not yet proven** — its verification gate has not run against published bytes, so do not plan around a produced signature as verified evidence.
|
|
64
68
|
- 🏛️ **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.
|
|
65
69
|
- 🔗 **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.
|
|
66
70
|
- 🗄️ **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
|
|
@@ -92,6 +96,7 @@ How Marketplace fulfillment works (ZDE and air-gap preserved — no runtime AWS
|
|
|
92
96
|
| **Pro features (vulnerability assessment)** | | | |
|
|
93
97
|
| CVE matching + MITRE ATT&CK mapping | — | ✅ | ✅ |
|
|
94
98
|
| Risk scoring + prioritization | — | ✅ | ✅ |
|
|
99
|
+
| Exploit intelligence — **CISA KEV** + **FIRST EPSS** joined onto the CVE matches, exploit-first ordering (a KEV-listed MEDIUM outranks an unexploited CRITICAL); stores are operator-populated — no feed data ships | — | ✅ | ✅ |
|
|
95
100
|
| Parallel analysis agents | — | ✅ | ✅ |
|
|
96
101
|
| **Enterprise — cloud scanning** | | | |
|
|
97
102
|
| 28 enterprise plugins — 27 cloud (AWS / Azure / GCP) + 1 network-scan zero-trust check | — | — | ✅ |
|
|
@@ -107,7 +112,7 @@ How Marketplace fulfillment works (ZDE and air-gap preserved — no runtime AWS
|
|
|
107
112
|
| Multi-framework `--compliance soc2,hipaa,nist-csf,pci-dss,iso-27001,cis-v8,gdpr` from one scan | — | — | ✅ |
|
|
108
113
|
| **Enterprise — auditor-grade evidence** | | | |
|
|
109
114
|
| 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`) | — | — | ✅ |
|
|
110
|
-
| Suppression workflow with approver identity verification (Ed25519 SIGNING
|
|
115
|
+
| Suppression workflow with approver identity verification (Ed25519 SIGNING reachable 0.35.0, not yet proven) | — | — | ✅ |
|
|
111
116
|
| Chain-of-custody manifests | — | — | ✅ |
|
|
112
117
|
| SLA / MTTR tracking + compensating controls | — | — | ✅ |
|
|
113
118
|
| Recurring-scan attestation (Type II operating-effectiveness) | — | — | ✅ |
|
|
@@ -299,8 +304,8 @@ Results land in `./out/<host>_<timestamp>/`:
|
|
|
299
304
|
| 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** |
|
|
300
305
|
| 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** |
|
|
301
306
|
| 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** |
|
|
302
|
-
| — | 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
|
|
303
|
-
| — | **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
|
|
307
|
+
| — | 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 not yet proven — the gate has not run against published bytes.) |
|
|
308
|
+
| — | **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 but not yet proven). 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. |
|
|
304
309
|
| — | **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. |
|
|
305
310
|
| — | **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. |
|
|
306
311
|
| — | **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. |
|
|
@@ -832,6 +837,41 @@ attestation is marked `REPORT INVALID FOR AUDIT` are **counted and named** rathe
|
|
|
832
837
|
averaged into a clean verdict, and an empty history exits **3** with status `no_evidence` —
|
|
833
838
|
absence of evidence is a finding, not a pass.
|
|
834
839
|
|
|
840
|
+
**The approval commands (CE 0.2.40 / Enterprise 0.35.0).** Four more `compliance` subcommands, all
|
|
841
|
+
forwarding to Enterprise — they need `@nsasoft/nsauditor-ai-ee` installed and exit 2 with an
|
|
842
|
+
explanatory message if it is absent. CLI-only; the MCP surface does not reach them.
|
|
843
|
+
|
|
844
|
+
```bash
|
|
845
|
+
# Create an approval keypair — private half 0600, plus an identity-registry member
|
|
846
|
+
# (pass --email/--role/--team too, or the member prints REPLACE ME placeholders)
|
|
847
|
+
nsauditor-ai compliance keygen --key ~/.nsauditor/approver.pem --approver "Ann Approver"
|
|
848
|
+
|
|
849
|
+
# Record an approval; SIGNS it when NSAUDITOR_SIGNING_KEY names a local Ed25519 key (not yet proven)
|
|
850
|
+
NSAUDITOR_SIGNING_KEY=~/.nsauditor/approver.pem \
|
|
851
|
+
nsauditor-ai compliance suppress --suppressions ./out/suppressions.json \
|
|
852
|
+
--source auth_agent --title-pattern "SSH password authentication enabled" \
|
|
853
|
+
--status accepted_risk --rationale "compensating control at the perimeter" \
|
|
854
|
+
--approver "Ann Approver" --attestation-level approver
|
|
855
|
+
|
|
856
|
+
# List every approval across a scan history with its expiry status
|
|
857
|
+
nsauditor-ai compliance review --history ./out
|
|
858
|
+
|
|
859
|
+
# Re-approve before expiry (appended to the record's renewals[] chain)
|
|
860
|
+
nsauditor-ai compliance renew --suppressions ./out/suppressions.json \
|
|
861
|
+
--id supp-abc123 --rationale "quarterly re-review" --approver "Ann Approver"
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
Three refusals and a warning you will meet rather than a happy path: `keygen` will not overwrite an
|
|
865
|
+
existing signing key (every signature it made becomes unverifiable, and nothing says so until an
|
|
866
|
+
auditor checks an archived approval); a malformed signing key fails the command and writes
|
|
867
|
+
**nothing**; `awskms:` references are refused in this release; and `renew` on a SIGNED approval
|
|
868
|
+
**invalidates its signature** — the expiry and the renewal record are inside the signed payload — so
|
|
869
|
+
it warns loudly and tells you to re-approve.
|
|
870
|
+
|
|
871
|
+
⚠️ **`--flag=value` is not supported**, here or on any flag this CLI has: use `--flag value`.
|
|
872
|
+
Ed25519 suppression signing is reachable from Enterprise 0.35.0 and **not yet proven** — its
|
|
873
|
+
verification gate has not run against published bytes.
|
|
874
|
+
|
|
835
875
|
**Compliance evidence (`NSAUDITOR_*` — Enterprise, all opt-in):**
|
|
836
876
|
|
|
837
877
|
Every variable added from Enterprise 0.33.0 onward carries the `NSAUDITOR_` prefix, so a
|
|
@@ -862,11 +902,11 @@ NSAUDITOR_SIGNING_KEY= # A REFERENCE, never key material: keychain:LA
|
|
|
862
902
|
One of these is deliberately **not** a capability yet and the other is proven on one path only —
|
|
863
903
|
saying which is which is the point:
|
|
864
904
|
|
|
865
|
-
- **`NSAUDITOR_SIGNING_KEY` is
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
905
|
+
- **`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 — a capability that is **not yet proven**, because its verification gate has
|
|
906
|
+
not run against published bytes, so a produced signature is not yet evidence anything vouches
|
|
907
|
+
for. The record's `algorithm` and `backend` fields were frozen *before* the first
|
|
908
|
+
signature could reach a customer archive, because retrofitting that later breaks every
|
|
909
|
+
auditor holding one.
|
|
870
910
|
- **`NSAUDITOR_TSA_URL` is wired, and since 2026-08-07 it is proven — on the npm path only.**
|
|
871
911
|
The live check this note used to say was outstanding has been run: through the installed
|
|
872
912
|
binary, against a real Time-Stamp Authority, the auditor procedure `openssl ts -verify`
|
package/cli.mjs
CHANGED
|
@@ -678,6 +678,29 @@ export async function parseArgs(argv) {
|
|
|
678
678
|
args.slaPolicy = slaPolicyVal === undefined ? undefined : slaPolicyVal;
|
|
679
679
|
const attestWindowVal = get('window');
|
|
680
680
|
args.attestWindow = (attestWindowVal && attestWindowVal !== true) ? attestWindowVal : null;
|
|
681
|
+
|
|
682
|
+
// ── the approval surface (EE 0.35.0) ────────────────────────────────────────
|
|
683
|
+
// Parsed HERE with every other flag rather than read from `process.argv` in the
|
|
684
|
+
// subcommand, so one parser owns the `--flag value` / `--flag=value` / boolean
|
|
685
|
+
// shapes. `attest` taught this: a second parser is a second set of edge cases.
|
|
686
|
+
const str = (name) => { const v = get(name); return (v && v !== true) ? v : null; };
|
|
687
|
+
args.approvalArgs = {
|
|
688
|
+
suppressions: str('suppressions'),
|
|
689
|
+
keyPath: str('key'),
|
|
690
|
+
source: str('source'),
|
|
691
|
+
titlePattern: str('title-pattern') ?? str('title_pattern'),
|
|
692
|
+
status: str('status'),
|
|
693
|
+
rationale: str('rationale'),
|
|
694
|
+
approver: str('approver'),
|
|
695
|
+
email: str('email'),
|
|
696
|
+
role: str('role'),
|
|
697
|
+
team: str('team'),
|
|
698
|
+
suppressionId: str('id'),
|
|
699
|
+
expiresInDays: str('expires-in-days'),
|
|
700
|
+
attestationLevel: str('attestation-level'),
|
|
701
|
+
compensatingControl: str('compensating-control'),
|
|
702
|
+
force: !!get('force'),
|
|
703
|
+
};
|
|
681
704
|
const frameworkVal = get('framework');
|
|
682
705
|
args.framework = (frameworkVal && frameworkVal !== true) ? frameworkVal : null;
|
|
683
706
|
|
|
@@ -955,10 +978,20 @@ function maxSeverityInConclusion(conclusion) {
|
|
|
955
978
|
* EE raises this as an `NsauditorConfigError` from `resolveComplianceEnvOpts`, and exports
|
|
956
979
|
* that function specifically so CE can fail fast at startup with the SAME definition of
|
|
957
980
|
* "offline". Until now nothing called it, and the veto's only route into CE was through
|
|
958
|
-
* `enrichScan` —
|
|
959
|
-
* the scan: it silently
|
|
960
|
-
* report). A quiet skip wearing a fail-fast's name, one repo over from the test
|
|
961
|
-
* forbids exactly that.
|
|
981
|
+
* `enrichScan` — which at the time meant a bare `catch {}` on the scan path that did not
|
|
982
|
+
* abort the scan: it silently dropped the ENTIRE EE stage (intelligence, agents, the
|
|
983
|
+
* compliance report). A quiet skip wearing a fail-fast's name, one repo over from the test
|
|
984
|
+
* that forbids exactly that.
|
|
985
|
+
*
|
|
986
|
+
* ⚠️ THAT CATCH IS NO LONGER BARE — the tense above is deliberate and was wrong until now.
|
|
987
|
+
* The import and the call carry SEPARATE catches (see the scan path below): absence stays
|
|
988
|
+
* silent, because a Community user legitimately has no EE, while FAILURE is named and
|
|
989
|
+
* `conclusion.result.eeEnrichmentError` is set. This docblock argued from its own file's
|
|
990
|
+
* corrected defect, in the present tense, forty lines from the code that fixes it — and it
|
|
991
|
+
* was the FOURTH site of one dead premise, the other three being EE's §16.1 row, the
|
|
992
|
+
* `nsauditor_env` docblock and that test's assertion message. The preflight's REASON is
|
|
993
|
+
* unaffected: a contradiction between two operator settings should stop the run at startup
|
|
994
|
+
* regardless of how loudly a later failure would report itself.
|
|
962
995
|
*
|
|
963
996
|
* Deliberately NOT gated on the command and NOT gated on a licence tier — a contradiction
|
|
964
997
|
* between two operator settings is a configuration error under every command, and
|
|
@@ -1008,7 +1041,7 @@ export async function preflightGrcIfRequested(env, opts = {}) {
|
|
|
1008
1041
|
|
|
1009
1042
|
export async function main() {
|
|
1010
1043
|
const args = await parseArgs(process.argv);
|
|
1011
|
-
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports, compliance, complianceScope, complianceHistory, slaPolicy, attestWindow, framework, awsRegion } = args;
|
|
1044
|
+
const { cmd, host, plugins, insecureHttps, hostFile, parallel, failOn, outputFormat, watch, intervalMinutes, webhookUrl, alertSeverity, ports, compliance, complianceScope, complianceHistory, slaPolicy, attestWindow, framework, awsRegion, approvalArgs } = args;
|
|
1012
1045
|
|
|
1013
1046
|
// Version: handled before license verification so it works without a key.
|
|
1014
1047
|
// CE-0.1.30.1 — closes the discovery-flag UX gap where pre-fix
|
|
@@ -1075,6 +1108,26 @@ Scan options:
|
|
|
1075
1108
|
|
|
1076
1109
|
Compliance subcommands:
|
|
1077
1110
|
nsauditor-ai compliance attest --history <dir> [--framework <fw>] [--window 6m|12m|90d]
|
|
1111
|
+
nsauditor-ai compliance keygen --key <path> [--approver <name>] [--email <e>] [--role <r>]
|
|
1112
|
+
[--team <t>] [--force]
|
|
1113
|
+
Generate an Ed25519 approval keypair (private 0600) and print
|
|
1114
|
+
the identity-registry member to paste. Refuses to overwrite an
|
|
1115
|
+
existing key without --force: regenerating makes every
|
|
1116
|
+
signature that key ever made unverifiable. Enterprise.
|
|
1117
|
+
nsauditor-ai compliance suppress --suppressions <file> --source <s> --title-pattern <p>
|
|
1118
|
+
--status <accepted_risk|false_positive> --rationale <text>
|
|
1119
|
+
--approver <name> [--expires-in-days <n>]
|
|
1120
|
+
[--attestation-level <approver|deployment>]
|
|
1121
|
+
[--compensating-control <text>]
|
|
1122
|
+
Records an approval. SIGNS it when NSAUDITOR_SIGNING_KEY names
|
|
1123
|
+
a local Ed25519 PEM; records it unsigned otherwise, and the
|
|
1124
|
+
report labels which. A malformed key fails HERE, never on a
|
|
1125
|
+
scan. Enterprise.
|
|
1126
|
+
nsauditor-ai compliance review --history <dir>
|
|
1127
|
+
Expiry review across a scan-history root. Enterprise.
|
|
1128
|
+
nsauditor-ai compliance renew --suppressions <file> --id <id> --rationale <text>
|
|
1129
|
+
--approver <name> [--expires-in-days <n>]
|
|
1130
|
+
Extends an approval and records the renewal chain. Enterprise.
|
|
1078
1131
|
Aggregate the per-scan attestation records in
|
|
1079
1132
|
<dir> into a multi-period (Type II) recurring-scan
|
|
1080
1133
|
attestation. Reads scan_attestation_<fw>.json from
|
|
@@ -2121,8 +2174,109 @@ Docs: https://www.nsauditor.com/ai/ | Pricing: https://www.nsauditor.com/ai/
|
|
|
2121
2174
|
// because nothing here decides anything.
|
|
2122
2175
|
if (cmd === 'compliance') {
|
|
2123
2176
|
const sub = process.argv[3];
|
|
2177
|
+
|
|
2178
|
+
// ── THE APPROVAL SURFACE (EE 0.35.0) ───────────────────────────────────
|
|
2179
|
+
// `suppress` / `review` / `renew` / `keygen`. Same THIN-FORWARD discipline as
|
|
2180
|
+
// `attest` below: EE owns validation, defaults, the signing decision and every
|
|
2181
|
+
// refusal; CE contributes a flag surface and an exit code and decides nothing.
|
|
2182
|
+
//
|
|
2183
|
+
// ⚠️ CLI-ONLY BY RATIFIED SCOPE (D4 condition 1). These are NOT exposed as MCP
|
|
2184
|
+
// tools — the GRC-push precedent — because an approval surface reachable from a
|
|
2185
|
+
// chat client is its own decision and nobody has made it.
|
|
2186
|
+
//
|
|
2187
|
+
// ⚠️ NOTHING HERE FLIPS A CLAIM. Publishing the surface is not proving it: per D6
|
|
2188
|
+
// the three-part gate runs against the published registry bytes and the hedges
|
|
2189
|
+
// flip at N+1. If you are reading this while editing marketing copy, the answer
|
|
2190
|
+
// is still "not yet".
|
|
2191
|
+
if (sub === 'suppress' || sub === 'review' || sub === 'renew' || sub === 'keygen') {
|
|
2192
|
+
let ee;
|
|
2193
|
+
try {
|
|
2194
|
+
ee = await import('@nsasoft/nsauditor-ai-ee');
|
|
2195
|
+
} catch {
|
|
2196
|
+
console.error(`compliance ${sub} requires the Enterprise package (@nsasoft/nsauditor-ai-ee).`);
|
|
2197
|
+
process.exit(2);
|
|
2198
|
+
}
|
|
2199
|
+
const A = approvalArgs ?? {};
|
|
2200
|
+
const suppressionsPath = A.suppressions;
|
|
2201
|
+
try {
|
|
2202
|
+
if (sub === 'keygen') {
|
|
2203
|
+
const out = await ee.keygenCommand({
|
|
2204
|
+
privateKeyPath: A.keyPath,
|
|
2205
|
+
approver: A.approver || undefined,
|
|
2206
|
+
email: A.email || undefined,
|
|
2207
|
+
role: A.role || undefined,
|
|
2208
|
+
team: A.team || undefined,
|
|
2209
|
+
force: A.force,
|
|
2210
|
+
});
|
|
2211
|
+
console.log(`wrote ${out.privateKeyPath} (0600) and ${out.publicKeyPath} (0644)`);
|
|
2212
|
+
console.log('\nAdd this member to your identity registry:\n');
|
|
2213
|
+
console.log(JSON.stringify(out.registryMember, null, 2));
|
|
2214
|
+
console.log('\nThe private key never leaves this machine. Scanners do not need it — '
|
|
2215
|
+
+ 'signing happens here, at approval time.');
|
|
2216
|
+
process.exit(0);
|
|
2217
|
+
}
|
|
2218
|
+
if (sub === 'review') {
|
|
2219
|
+
const { rows, summary } = await ee.reviewCommand({ rootDir: complianceHistory });
|
|
2220
|
+
console.log(`Suppressions: ${summary.total} total · ${summary.active} active · `
|
|
2221
|
+
+ `${summary.approaching} approaching expiry · ${summary.expired} expired · `
|
|
2222
|
+
+ `${summary.no_expiry} without expiry`);
|
|
2223
|
+
for (const r of rows) {
|
|
2224
|
+
console.log(` [${r.expiryStatus}] ${r.suppressionId ?? '(no-id)'} — ${r.approver ?? '(no approver)'}`);
|
|
2225
|
+
}
|
|
2226
|
+
process.exit(0);
|
|
2227
|
+
}
|
|
2228
|
+
// `suppress` and `renew` both write, and both may SIGN — EE decides, from the
|
|
2229
|
+
// signing reference this forwards. A malformed reference fails HERE, at the
|
|
2230
|
+
// command, and never on a scan (D4).
|
|
2231
|
+
const signingKeyRaw = process.env.NSAUDITOR_SIGNING_KEY || undefined;
|
|
2232
|
+
if (sub === 'suppress') {
|
|
2233
|
+
const out = await ee.suppressCommand({
|
|
2234
|
+
suppressionsPath,
|
|
2235
|
+
match: { source: A.source, titlePattern: A.titlePattern },
|
|
2236
|
+
status: A.status,
|
|
2237
|
+
rationale: A.rationale,
|
|
2238
|
+
approver: A.approver,
|
|
2239
|
+
expiresInDays: A.expiresInDays ? Number(A.expiresInDays) : undefined,
|
|
2240
|
+
compensating_control: A.compensatingControl || undefined,
|
|
2241
|
+
// ⚠️ PROVENANCE, BECAUSE ONLY THIS LINE KNOWS IT. EE cannot tell an env-sourced
|
|
2242
|
+
// reference from a hand-passed one — it receives an argument either way — so a
|
|
2243
|
+
// malformed key used to tell the operator who set NSAUDITOR_SIGNING_KEY about "the
|
|
2244
|
+
// supplied signing key", naming no setting on the one path where naming it is the
|
|
2245
|
+
// point. The token is what sends them back to the variable they actually typed.
|
|
2246
|
+
}, {
|
|
2247
|
+
signingKeyRaw,
|
|
2248
|
+
signingKeyFrom: 'environment',
|
|
2249
|
+
attestationLevel: A.attestationLevel || undefined,
|
|
2250
|
+
});
|
|
2251
|
+
const sig = out.suppression.signature;
|
|
2252
|
+
console.log(`wrote ${out.file} — ${out.suppression.id} (${out.suppression.status})`);
|
|
2253
|
+
console.log(sig
|
|
2254
|
+
? ` signed: ${sig.algorithm}/${sig.backend}`
|
|
2255
|
+
+ `${sig.attestationLevel ? ` at level ${sig.attestationLevel}` : ', identity model not declared'}`
|
|
2256
|
+
: ' unsigned — set NSAUDITOR_SIGNING_KEY to a local Ed25519 PEM to sign approvals');
|
|
2257
|
+
process.exit(0);
|
|
2258
|
+
}
|
|
2259
|
+
const out = await ee.renewCommand({
|
|
2260
|
+
suppressionsPath,
|
|
2261
|
+
suppressionId: A.suppressionId,
|
|
2262
|
+
rationale: A.rationale,
|
|
2263
|
+
approver: A.approver,
|
|
2264
|
+
expiresInDays: A.expiresInDays ? Number(A.expiresInDays) : undefined,
|
|
2265
|
+
});
|
|
2266
|
+
console.log(`renewed ${out.suppression?.id ?? A.suppressionId} in ${out.file ?? suppressionsPath}`);
|
|
2267
|
+
process.exit(0);
|
|
2268
|
+
} catch (err) {
|
|
2269
|
+
// Refusals arrive here and must stay loud: a malformed signing key, an
|
|
2270
|
+
// `awskms:` reference this release does not sign with, an existing key file
|
|
2271
|
+
// keygen will not overwrite. Every one of them is the operator learning
|
|
2272
|
+
// something true before an artifact exists.
|
|
2273
|
+
console.error(`Fatal: ${err.message}`);
|
|
2274
|
+
process.exit(2);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2124
2278
|
if (sub !== 'attest') {
|
|
2125
|
-
console.error('Usage: nsauditor-ai compliance attest
|
|
2279
|
+
console.error('Usage: nsauditor-ai compliance <attest|suppress|review|renew|keygen> …');
|
|
2126
2280
|
process.exit(2);
|
|
2127
2281
|
}
|
|
2128
2282
|
if (typeof complianceHistory !== 'string' || complianceHistory.length === 0) {
|