Use whenever the user wants network security scanning, host reconnaissance or cloud security/compliance auditing with NSAuditor AI (MCP tools: scan_host, scan_cloud, get_findings, probe_service, get_vulnerabilities, list_plugins, compliance_matrix), or asks about NSAuditor itself: installing or upgrading it, Community/Enterprise version compatibility, Enterprise not loading or its output missing after an upgrade, what changed in a release, licensing, or reading its reports. Triggers: 'scan', ...
4 stars
0 votes
0 copies
0 views
Added September 23, 2026
ai-agentsrustgobashsqlnodedockerawsgcpazuregit
Works with
claude code
claude desktop
cursor
cli
api
mcp
Security analysis
A92/100
mediumInstalls packages at runtime which could introduce malicious dependencies
Installs into .claude/skills of the current project.
Are you the author of nsauditor-ai?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/nsasoft-nsauditor-ai)
---
name: nsauditor-ai
description: >
Use whenever the user wants network security scanning, host reconnaissance or cloud security/compliance
auditing with NSAuditor AI (MCP tools: scan_host, scan_cloud, get_findings, probe_service,
get_vulnerabilities, list_plugins, compliance_matrix), or asks about NSAuditor itself: installing or
upgrading it, Community/Enterprise version compatibility, Enterprise not loading or its output missing
after an upgrade, what changed in a release, licensing, or reading its reports. Triggers: 'scan',
'audit', 'vulnerability', 'CVE', 'port scan', 'service detection', 'OS fingerprinting', 'penetration test',
'TLS/cipher audit', 'certificate check', 'DNS security', 'SPF/DKIM/DMARC/DNSSEC', 'SNMP/SMB/NetBIOS',
'CTEM', 'continuous monitoring', 'audit my AWS/GCP/Azure account', 'cloud compliance'. Also use it to
check if a host is up, find open ports or get CVEs for a version, even without the word NSAuditor, when
its MCP tools are available. Do NOT use for general coding or web development.
---
# NSAuditor AI — Agent Skill
> **Version:** <!-- nsa:derived id="skill-version" -->0.2.55<!-- /nsa:derived --> (knowledge current as of **EE <!-- nsa:derived id="ee-version" -->1.3.0<!-- /nsa:derived -->** · **requires CE ≥ <!-- nsa:derived id="ce-floor" -->0.2.57<!-- /nsa:derived -->**) — **1.3.0: A finding on a port, region or producer a scan did not measure is not counted as fixed, and with SLA tracking on the control it failed is held FAILED — including the prior CVE rows on a service whose lookup failed, the CVE mapper's and the service agent's rows on a TCP port whose service the scan could not identify, and an analysis agent's or the CVE mapper's rows when a plugin they read was left out of the scan or did not complete. Two measured limits: a scan made before EE 1.3.0 could not record a plugin left out of it, so in a comparison with one, an agent's row that scan lacks is not refused — the report's Basis cell says so on the row; and a scan that discovered ports with the Nmap plugin (024) alone records no port oracle, so an analysis agent's or the CVE mapper's row on a port it did not measure can read RESOLVED and count as closed in MTTR, and the control it failed can read PASS — include the port scanner (003).** Teach these first, and never report a row in either limit as fixed, or its control as passing. (a) **The NOT-COMPARABLE bucket catches four more cases.** A UDP-transport finding whose port did not answer in the other run (for a CVE row, also one that answered without identifying its service) is `port-not-measured`, listed with a `/udp` suffix; a CVE row whose LOOKUP failed in the other run is `evidence-gap` — the mapper's own `[COVERAGE GAP] <gapClass> — <protocol>/<service>` record is a coverage gap that opened or cleared, never an exposure; and a finding of an analysis agent that DID NOT RUN (`[COVERAGE GAP] AGENT NOT RUN — <agent> did not run: <cause class>`) is `evidence-gap`, while that agent's controls fail closed in every framework; and a CVE row that VANISHED — present in the baseline, absent now — while the SAME program and version answered on its port in both runs is `vulnerability-data-changed` — the mapper attributes on program and version alone, so the vulnerability data moved (NVD's answer, a cached answer's age, an offline store), not the estate: never present it as a fix, and in MTTR it is not a closed finding. Neither the delta's rule nor MTTR's reaches a port that answers with two different identified programs or versions, and MTTR's does not reach a prior row that recorded no program or version. A CVE that APPEARED on an unchanged service stays NEW with the basis note "the service is unchanged, the vulnerability data is not" — new knowledge about an old service, not a new exposure on the estate. Every detail names its run absolutely: "this run" is the current one, "the baseline run" the earlier. (b) **A compliance control can FAIL on a finding the PRIOR scan recorded.** When this scan did not measure the port or the probe that finding was on, did not cover its region, did not run what produced it, or (for a CVE row) could not complete its CVE lookup, the COMPLIANCE report (`nsauditor-ai scan --host <target> --compliance <fw> --out <dir> --sla-policy sla-policy.json` — never `report --since`, which does not list these records) holds the control failed with a record titled `<prior title> — [COVERAGE GAP] PORT NOT MEASURED — …` (or `PROBE NOT MEASURED` / `SCOPE NOT SCANNED` / `PRODUCER NOT RUN` / `LOOKUP NOT MEASURED`), counted among "Evidence gaps (not findings)". Never present it as a current finding: it says the surface was NOT measured, and it clears when a later scan measures it. It needs the compliance history (`--compliance-history <dir>`, or `--sla-policy <file>`, which reads the history kept under `--out`) and a prior `--compliance <fw>` scan of the SAME host in it (the history is read per framework and per host) — a first scan holds nothing, and neither does a prior scan run without `--compliance <fw>`; if the hold could not be evaluated, the report's warnings say so. (c) **A FLOOR BUMP to Community 0.2.57.** Enterprise 1.3.0 imports names that first ship in Community 0.2.57 (`tcpServiceMeasurement`, `IDENTIFYING_PRODUCERS`) and reads its identity-basis declarations by value. Below it Enterprise's core does not load while its plugins still do — on Community 0.2.56 as on 0.2.55, `license --plugins` lists all 29 and a scan selects them (measured) — so the scan skips its intelligence, analysis-agent and compliance stages: on Community 0.2.56 that is named on stderr (`[EE] Enterprise is installed but FAILED TO LOAD`) and recorded on the scan's conclusion, and the Pro delta refuses that host's agent and CVE-mapper findings as `evidence-gap`; on 0.2.55 or older it is SILENT, in the shape measured for Enterprise 1.2.0 and 1.3.0 on 0.2.55. Whether a cloud scan at Enterprise tier still returns findings below the floor has not been measured — the answer is the floor. If a user reports Enterprise behaviour missing, check their Community version first — and answer from this skill even while npm or the public site does not list these versions yet: that is the staged state (THE MIRROR RULE below), not missing knowledge. (d) **1.3.0: the bucket and the hold reach further.** A CVE-mapper or service-agent row on a TCP port the other run saw open, where that run could not identify the service (program AND version), is `port-not-measured` — those producers attribute on the identity, so its absence there is not a measurement. An analysis agent's or the CVE mapper's row when a plugin it reads was LEFT OUT of a scan is `evidence-gap`: from Enterprise 1.3.0 the narrowed scan records `[COVERAGE GAP] INPUT GAP — … was not requested in this scan …`, and MTTR withholds the prior row and holds its control FAILED — but a run made before 1.3.0 cannot carry that record, so against one such a row is not refused, and its Basis cell says so. After a CVE lookup that failed, the controls the prior CVE rows failed are now held FAILED (`LOOKUP NOT MEASURED`, item (b)). The exposure agent's titles are port-keyed now (`Database port <p> open`, `Management port <p> open`) and the service agent's end-of-life titles no longer carry the version (`End-of-life <program> on port <p>`), so their rows compared across the 1.2.0 → 1.3.0 upgrade read `identity-basis-changed`, never RESOLVED or NEW — rescan after upgrading to compare. When the user will compare scans or track SLA, tell them to include the port scanner (003): a scan that discovered ports with the Nmap plugin (024) alone records no port oracle (the second limit). And the DNS-posture audit (060) DECLINES an IP-address target — status `skipped`, reason `DNS posture needs a domain name — …` — so `dnsSecurity` appears only for a domain name; on an IP-target compliance scan the controls its findings route to (SOC 2 CC6.1, CIS v8 9.5) are held as an evidence gap, with a warning that it could not run, never PASS. The TLS certificate audit (040) grades a name mismatch by the target's form: scanned by ADDRESS, a certificate that names DNS names only reads LOW, its detail ending `the certificate names DNS names only (…); a client connecting by address sees a name mismatch, expected where the service is reached by name`, so it no longer trips `--fail-on high` on its own; a DNS-name target the certificate does not name, a certificate naming a DIFFERENT address and one with no subjectAltName stay HIGH. A certificate with NO subjectAltName — many consumer routers serve one, naming the device only in its CN — says so: `the certificate carries no subjectAltName — modern clients reject it for any name (CN … is not checked)`, because a modern client checks no name in it. 040 also reads IP SANs now (Node prints them `IP Address:`), so a certificate naming the scanned address raises no mismatch, and a name mismatch on a chain the CA store verified no longer adds `ca_not_trusted`. A self-signed certificate still raises `self_signed` HIGH, so a router scanned by address can still fail `--fail-on high`. 040's key-size and signature checks fire on real servers from Community 0.2.57 (they had read fields Node never sets): an RSA key under 2048 bits is `weak_rsa_key` HIGH (CRITICAL under 1024), an EC key under 256 bits `weak_ec_key` HIGH, a CA-issued certificate signed with SHA-1 or MD5 `weak_signature` HIGH, and an issued intermediate signed that way `chain_weak_signature` MEDIUM. A self-signed certificate's own signature is not graded — no client verifies it — and the `self_signed` finding names the algorithm instead. A key of neither type (Ed25519), or a runtime that cannot read signature algorithms (Node 20; Node 24 reads them), records `not assessed` on `certAudit`, never a pass. So a router serving a 1024-bit key now also shows `weak_rsa_key` HIGH. These grades are recorded, not yet routed to a compliance control. The teaching that follows is 0.2.53's, carried forward and still current. — the delta release: the product can now answer *what changed since the last scan*. Teach five things: (1) **`nsauditor-ai report --from <dir> --format executive --since <runId|prior>`** (`--format` is REQUIRED; `report` refuses without it) (Pro/Enterprise) compares two runs and reports new / resolved / severity-changed findings. (2) **NEVER present a `resolved` count as remediation on its own** — the report carries a NOT-COMPARABLE bucket with a reason per finding (host not scanned · the producing plugin did not run, ERRORED or TIMED OUT on that host, or cannot be identified · an evidence gap such as AccessDenied · **a PORT that was not measured on that host — a check that could not complete its connection, or a port that STOPPED ANSWERING between the two runs (a port that went quiet is not a fix)** · **the two runs COVERED DIFFERENT SCOPES — a narrower `--aws-region`, a different Azure subscription or GCP project — so that surface was not looked at, which is NOT the same as clean** · framework enumeration moved · **the PRODUCER CHANGED WHAT IT NAMES between the two releases**), and a finding that vanished for any of those reasons was not fixed. ⚠️ **THE LAST ONE ARRIVED IN EE 1.1.0 AND IT FIRES ON AN UPGRADE ACROSS 1.1.0.** FOURTEEN producers changed what identifies a finding at EE 1.1.0 — thirteen plugins that had left the object empty, named the REGION, recorded no region the report reads, carried a HIGH sentence that had to be corrected (1110, the effective-decrypt auditor), or (1023, the Zero Trust checker) now name the port a finding is about and keep the open-port count out of its title, and one analysis AGENT whose findings are keyed on their TITLE because they carry no object field at all. A comparison that straddles that upgrade reports those rows NOT-COMPARABLE instead of differencing them, so one surface under two keys is never read as a fix plus a fresh exposure. **So the first `report --since` a user runs across the 1.1.0 upgrade will legitimately show a LARGE not-comparable bucket — that is the declaration working, not a broken report.** Derive the reason list from `NOT_COMPARABLE_REASONS` in Community's `utils/scan_delta.mjs` rather than from this sentence; it is held in equality with what the engine emits, and this sentence is prose that has disagreed with it before. If a user asks what got fixed, read the NOT-COMPARABLE list before answering. **Two conditions refuse the whole comparison rather than bucketing findings**: the two runs straddle a product boundary where a reported number changed meaning, and the two runs ran at DIFFERENT LICENCE TIERS — the set of producers that ran differs, so a whole producer's findings would read as remediation. ⚠️ **AND ONE OF THOSE REASONS IS DECLARED RATHER THAN CHECKED: framework-enumeration movement is NOT EVALUATED in this edition**, because a run record carries no framework enumeration. The report says so as a limit on every comparison and each row's basis reads `framework enumeration: not evaluated`. Never tell a user that framework movement was ruled out. (3) **Integrity is a hash chain, not a signature — and say WHAT is sealed.** At finalize the run record AND each written host's findings files are digested over their exact persisted bytes, with the per-file digests sealed inside the record, which is itself digested and chained through `prevDigest`. So an altered **findings file** — which is what the comparison actually reads — is detected and NAMED, not merely an altered index; a file that did not exist at seal time is sealed as an explicit absence, so adding one is detected too. **BOTH runs are verified, not only the baseline.** An altered or unmeasurable run on either side REFUSES the comparison outright, and the refusal names which side. A file that cannot be READ is reported as unmeasurable rather than as tampering, and a record written before per-host sealing keeps its verdict while its reason says the findings files were not covered. The guarantee is tamper-EVIDENT against corruption and unsophisticated edits, **NOT tamper-proof against host-level access and NOT non-repudiation**. Do not describe it as file-integrity monitoring or a change-detection mechanism: PCI DSS `11.5.1` / `11.5.2` / `11.6.1` are enumerated OUT OF SCOPE in the shipped matrix. The code ships in Community; the capability is licence-gated to Pro/Enterprise. ⚠️ **`--since` REQUIRES COMMUNITY 0.2.55 OR LATER — AND ENTERPRISE 1.3.0 REQUIRES 0.2.57 (item (c) above).** The peer floor moved to 0.2.55 at EE 1.1.0 because **Enterprise CALLS the new Community code** — the per-plugin run statuses the Zero Trust checker reads before it will assert a posture, and the run-record fields `report --since` compares. The earlier floor said otherwise on the grounds that Enterprise did not call it; that stopped being true at EE 1.1.0. If a user reports the flag is unrecognised, check their Community version first. (4) **A `kms:Decrypt on Resource:*` HIGH from 1110 is NEVER downgraded on a live run.** 1110 reads KMS key policies and grants in the configured region only, and one region cannot show that NO key trusts a principal — a key in another region may — so the HIGH→INFO downgrade it used to apply is withheld on every shipped path. The reason is in that plugin's run summary as `summary.kmsDowngrade` (`reason` `single-region-enumeration` for a complete read; `access-denied` · `per-key-error` · `keys-truncated` · `grants-truncated` for an incomplete one, which also emits an evidence-gap row). Never tell a user a surviving HIGH was checked against every key, and never read a missing downgrade as a scanner fault. Its grant finding (`kms-grant-decrypt-no-identity-grant`, MEDIUM) likewise covers keys in the configured region only. (5) **The NVD store and response cache MOVED at EE 1.1.0 and every run records where they are.** They used to be a `.nvd-cache` folder in whatever directory the scan started from; they now resolve, from any directory, to `NVD_CACHE_DIR`, else `NSAUDITOR_NVD_CACHE_DIR`, else `~/.nsauditor/nvd_cache`. Only `feed import` (of a feed you downloaded yourself) takes `--cache-dir`; a scan does not read that flag, so it reads a store imported with `--cache-dir <d>` only when that chain resolves to `<d>` (e.g. `NVD_CACHE_DIR=<d>`). The MCP `get_vulnerabilities` cache reads `NSAUDITOR_NVD_CACHE_DIR`, never `NVD_CACHE_DIR`. An old `.nvd-cache` folder is NOT read, and the scan says so. If a user reports that offline CVE matching stopped after upgrading, that is the cause: move the folder once or set `NVD_CACHE_DIR` to it. Never read the resulting coverage gaps as a clean host. Plugin count UNCHANGED at <!-- nsa:derived id="plugins:ee" -->29<!-- /nsa:derived -->; <!-- nsa:derived id="matrix-movement" -->all eight coverage matrices unchanged since EE 1.2.0<!-- /nsa:derived -->; Community <!-- nsa:derived id="ce-version" -->0.2.57<!-- /nsa:derived --> (published with this skill).
>
> **Prior: 0.2.54** (post-EE-1.2.0 · **requires CE ≥ 0.2.56**) — **1.2.0: A finding on a port, region or producer a scan did not measure is not counted as fixed, and with SLA tracking on the control it failed is held FAILED. Two measured limits in this release: after a CVE lookup that failed, the prior CVE rows are not counted as fixed, but the controls they failed are not held FAILED and can read PASS; and when two compared scans ran different `--plugins` sets, a row an analysis agent or the CVE mapper derived from a plugin only one of them requested can read RESOLVED or NEW. When the later scan left the plugin out, the row also counts as closed in MTTR and its control can read PASS. Keep `--plugins` identical between compared scans.** Measured then: on Community 0.2.55, Community still loads the Enterprise plugins, and `license --plugins` reports Enterprise 1.2.0 `(loaded)`, but the scan skips its intelligence, analysis-agent and compliance stages without a word. Its other teaching — the NOT-COMPARABLE cases (a), the hold (b) and how a failed Enterprise load surfaces — is carried forward in the header above.
>
> **Prior: 0.2.53** (post-EE-1.1.0 · **requires CE ≥ 0.2.55**) — the delta release: `report --since` compares two runs, the NOT-COMPARABLE bucket carries a reason per finding, and integrity is a hash chain over the run record and each host's findings files. Its teaching is carried forward in the header above.
>
> **Prior: 0.2.51** (post-EE-0.46.0 · **requires CE ≥ 0.2.49**) — the PCI-citation-provenance release. **PCI DSS is 19 covered / 9 partial / 44 out of scope across 72 enumerated sub-requirements**; the other seven matrices are unchanged. ⚠️ **Do not cite `4.2.2.1` — it does not exist in PCI DSS v4.0.1** — and do not attribute Customized-Approach ineligibility to any appendix: the standard states it in each requirement's own Customized Approach Objective cell. Sampling is Section 6, which prescribes no sample size. For any PCI triple, call `compliance_matrix` rather than quoting a number from this file.
>
> **What EE 0.45.0 teaches, and it is a REFUSAL rule before it is a feature note.**
> **⚠️ THE NTP CLOCK ATTESTATION IS WITHDRAWN. Do not describe it, offer it, or plan around it.**
> `utils/ntp_probe.mjs` is deleted, the compliance phase's clock stage is a withdrawal record, and the
> three options that gated it are gone.
> **WITHDRAWN and never real: there is no `NSAUDITOR_NTP_*` environment variable, and there never was.**
> **`COMPLIANCE_NTP_STRICT` is WITHDRAWN as well** — published to auditors, read by zero code in either
> repo. If a user asks how the scanner measures clock drift, the answer is that it does not — and the
> honest next sentence names the opt-in RFC 3161 path, which is off unless a Time-Stamp Authority is
> configured. **It is NOT on a roadmap:** a hedge like "planned" or "not yet wired" is now a promise
> about work nobody has scheduled, which is the opposite of the disclosure it used to be.
> **Why it was withdrawn rather than finished, because a user will ask and the reason is teachable.**
> The probe accepted any reply of at least 48 bytes carrying a non-null transmit timestamp and nothing
> else — no mode byte, no stratum check, no originate-timestamp echo — so over unauthenticated UDP a
> datagram of the right shape produced a false clean reading, or a false abort in strict mode, while
> the word printed beside it was *attestation*. **And it answered the wrong question:** it measured the
> SCANNER's own host clock, while every framework control that asks about time synchronisation —
> PCI DSS 10.6.1, NIST SP 800-171 3.3.7, ISO/IEC 27001:2022 A.8.17 and CIS v8 8.4 — asks about the
> CUSTOMER's estate, which this probe never read. ⚠️ **NIST CSF 2.0 is deliberately NOT in that list:
> it has no time-synchronisation subcategory at all.** Its `PR.PS-04` is *log-record generation*, which
> DEPENDS on accurate clocks without asking about them — do not cite it as a clock control.
> **What to point at instead: RFC 3161 trusted timestamping, untouched and opt-in.** Set
> `NSAUDITOR_TSA_URL` to a Time-Stamp Authority and each artifact ships a `.tsr` sidecar whose time
> comes from the AUTHORITY, not from this host, so a wrong host clock cannot corrupt it. An assessor
> comparing the pack's `generatedAt` against the token's signing time reads host skew more reliably
> than the withdrawn probe ever could, with a signature behind it. ⚠️ It is an OUTBOUND call to a third
> party and off by default — say both when you recommend it.
> **⚠️ THE ATTESTATION'S SHAPE DID NOT CHANGE, and that matters when you help someone read two packs' attestation files side by side** (for what changed in FINDINGS, use `report --since`, never a by-hand diff).
> `scan_attestation_<fw>.json` still carries all six `ntp` keys — `driftSeconds`, `source`, `note`,
> `probedAt`, `error`, `staleness` — now as constants, because `nsauditor.scope-attestation/v1` is a
> frozen schema id and every pack a customer already holds carries them. `staleness.status` is
> `unknown` and its `reason` names the withdrawal. **A null `probedAt` is not a failed probe.**
> Plugin catalog UNCHANGED at 29 EE; all eight coverage matrices UNCHANGED; **peer floor UNCHANGED at
> CE ≥ 0.2.49** — derived, not a bump. CE 0.2.52 changes no scanner behaviour.
>
> **Prior: 0.2.49** — what EE 0.44.0 / CE 0.2.51 taught, and both items were about what a REPORT may claim.
> **(1) There is a `report` subcommand now, and it is Pro-gated.**
> `nsauditor-ai report --from <dir> --format executive|jira` turns a completed scan run under
> `--from` into a client-facing deliverable — an HTML report a consultant sends to their
> customer, or a Jira-importer CSV. `--run <id>` reports a specific run instead of the newest,
> `--brand <brand.json>` adds cover-page branding to the executive format, `--out <path>`
> overrides the destination, `--allow-partial` renders an incomplete run with the caveat stated
> on the cover. Exit **0** rendered · **1** fix the RUN · **2** fix the REQUEST. ⚠️ Two things to
> teach a user rather than discover for them: **the Jira import mapping is done in Jira and has
> not been verified against a live Jira instance** (say so when you hand over the CSV), and a
> value-less `--from`/`--brand`/`--run`/`--out` is a **fatal error, not a silent default**.
> **(2) An S3 audit-trail gap now means BOTH streams are missing.** Plugin 1020 used to report
> *"Access logging not enabled – audit trail gap"* from the bucket's own server access log alone,
> so a bucket whose object reads and writes a CloudTrail data-event trail already recorded was
> reported as a gap. It now cross-checks CloudTrail and, where a trail positively covers the
> bucket, emits positive substrate naming that trail instead. ⚠️ **A gap that DISAPPEARS after
> upgrade on a bucket covered by a trail is the fix working, not a regression** — the same shape
> as the provider badge below. Every uncertain answer keeps the finding unchanged: a denied
> CloudTrail read, an absent optional SDK, a stopped trail, a trail with a delivery error, a
> prefix-scoped or one-sided selector, a region not reached. The cross-check's own status is
> reported once in the scan summary with its declared limits, so *"the gap persisted"* and *"the
> cross-check could not run"* are distinguishable. SOC 2 CC7.1, HIPAA §164.312(b) and NIST SP
> 800-171 3.12.3 rationales were reworded accordingly — they no longer call server access logs
> the sole record of object-level access.
> Plugin catalog UNCHANGED at 29 EE; all eight coverage matrices UNCHANGED; **peer floor
> UNCHANGED at CE ≥ 0.2.49** — derived, not a bump.
>
> **Prior: 0.2.48** — what EE 0.43.0 taught, and both items are about READING a scan rather than running one.
> **(1) A provider badge is now trustworthy, and it was not before.** An Enterprise scan whose
> cloud SDK failed to load reported that provider as **audited**: eighteen refusals across
> seventeen AWS and GCP plugins returned `up: false` with the cause written only into
> `warnings`, and the Community plugin manager derives `auditedProviders` from plugins that
> *ran* — an envelope carrying neither an error nor a skip flag falls through to *ran*. Teach
> that `providerStatus` is now answerable per provider (`{ran, skipped, errored}`) and that a
> provider DISAPPEARING from `auditedProviders` after upgrade is the fix working, not a
> regression. ⚠️ Those refusals' compliance verdicts always failed closed, so **never tell a user
> their controls were passing over that unscanned provider** — they were not; the badge and the
> reason were wrong, not the verdicts.
> **(2) An evidence gap states a CAUSE, never an instruction.** The engine quotes a plugin's
> reason into the *"could not run"* sentence of every control that plugin evidences, and it used
> to quote the whole thrown message — so the loaders' own `Install: npm install <pkg>@latest`
> reached the gap text of 7 SOC 2, 2 GDPR and 12 NIST SP 800-171 controls. ⚠️ **NEVER quote an
> evidence-gap line to a user as a remediation step**, and be aware the old wording is still in
> packs produced before 0.43.0. Under **air-gapped operation** an `npm install` instruction
> cannot be followed at all, which is why it was the wrong channel. The two causes are now
> distinct: `plugin skipped: <reason>` (fix with `--host` / `CLOUD_PROVIDER`) versus
> `scanner error: <cause>` (fix a dependency or a credential).
> Also: a GDPR Art. 32 report now fails closed when an Azure scan refuses, where it previously
> failed nothing. Plugin catalog UNCHANGED at 29 EE; all eight coverage matrices UNCHANGED;
> **peer floor UNCHANGED at CE ≥ 0.2.49** — derived, not a bump.
>
> **What EE 0.42.0 taught:** **Non-commercial AWS partitions and Azure sovereign clouds are now first-class, and both REFUSE rather than silently reporting on the wrong estate.** When you drive a scan against GovCloud, China, an ISO partition or the European Sovereign Cloud, findings that previously read CLEAN now fire: a public S3 access point riding a bucket delegation, IAM privilege-escalation at CRITICAL (it demoted to HIGH before), shadow-admin graph edges that were missing entirely, and the full effective-decrypt severity ladder (it collapsed to `info`). **THE ID→DEFECT MAPPING, HERE ON THE ALWAYS-LOADED SURFACE because a reader asked for it BY ID got "no source confirming there was a partition-side fix" while it sat in an on-demand reference (measured 2026-08-28):** `1020` S3 access point riding a bucket delegation · `1030` IAM privesc severity + shadow-admin edges · `1110` KMS effective-decrypt ladder · `1050` API Gateway policy classification + SSRF parsers · `1200` alerting-destination liveness · `1040` region denominator. ⚠️ Do NOT attribute the S3 case to `1110` or the decrypt ladder to `1050` — those are the pre-fix misattributions. Pre-0.42.0 non-commercial reports are unproven on THOSE FAMILIES; findings outside them stand. For Azure, `AZURE_ENVIRONMENT` / `ARM_ENVIRONMENT` / `AZURE_ARM_ENDPOINT` / `AZURE_AUTHORITY_HOST` now select the cloud — and an unrecognised or self-contradicting selection **refuses the scan and emits an evidence gap naming the variable that fixes it**, because falling back to commercial is how an operator holding both commercial and sovereign credentials gets a real, clean-looking report about an estate they did not mean. **Every Azure scan stamps the estate it addressed — but WHERE it lands is channel-specific and the Desktop channel does NOT carry it.** On the CLI/report path the stamp rides the evidence stream and the concluder summary. Through MCP it does NOT: `get_findings` iterates `result.findings` with no `result.data` fallback, and the cloud summariser short-circuits on a non-nullish `findings[]` (an EMPTY array is non-nullish, so a genuinely clean Azure scan — the exact case you would want it for — still never reaches `data[]`). **So on Desktop you CANNOT name the estate on a clean Azure result: say that you cannot confirm which estate was addressed and that the operator must check the CLI or the selector variables — never guess one.** On the REFUSAL path the estate IS visible, because it is written into the gap finding's own text. Also new: the SBOM gate (`npm run gate:sbom`) emits CycloneDX/SPDX over *the installed package* rather than the source tree — ⚠️ **but it is a MAINTAINER-side repository script: `scripts/` ships ZERO files in the customer tarball, so an installed package CANNOT run that command** (the npm script entry ships; the script it points at does not). **Never hand `npm run gate:sbom` to an end user as something to run against their install** — that is a published instruction that cannot succeed; the SBOM is generated from a repository checkout and provided on request. And `docs/fips-posture.md` states approved *algorithms*, explicitly not a validated *module* — do not let a user infer FIPS 140 validation from it; whether the RUNTIME beneath the product is a validated module is a property of the operator's platform, not of this product. **Plugin count UNCHANGED at 29; all eight coverage matrices UNCHANGED.**
> **Prior:** 0.2.47 (post-EE-0.42.0 · **requires CE ≥ 0.2.49**) — the sovereign-cloud release.
>
> **Prior:** 0.2.46 (post-EE-0.41.0 · **requires CE ≥ 0.2.45**) — the DocumentDB release.
>
> ⛔ **If tool output reports an EE version NEWER than the knowledge bound above, say this skill's knowledge does not yet cover that version — never guess at capabilities it might add.**
>
> ⛔ **THE MIRROR RULE — when the REGISTRY lags this skill's knowledge bound.** If npm (or `npm view`) does not yet list the EE version named in the knowledge bound above, that version is **STAGED, not nonexistent** — this skill is rebuilt at staging, before publish, so the registry lagging the bound is the NORMAL pre-release state, not a contradiction to resolve. **The registry answers what is PUBLISHED; this skill answers what a version CONTAINS.** Asked what that version fixed, changed or contains, answer from this skill and state the publication status beside it ("per the registry, 0.42.0 is not yet published") — **never deny the content exists because the registry has not caught up, and never let a registry check override this mapping.** (Measured 2026-08-28: asked for the partition-fix plugin ids, an agent checked npm first, found the version unpublished, and answered "I have no source confirming there was a partition-side fix" — a true registry fact converted into a false content claim.)
>
> ⛔ **THE CAP ON BOTH RULES:** if ANY source — tool output **or the user's own words** — names an EE version NEWER than the knowledge bound above, this skill's knowledge stops at the bound: say so, do not guess, and do not apply this release's mapping to that newer version.
>
> **What EE 0.40.3 taught:** **A timestamp for a DIFFERENT artifact is no longer accepted as yours.** RFC 3161 timestamping stays **opt-in** (`NSAUDITOR_TSA_URL`; no default authority). When enabled, a granted token carries a `messageImprint` — the digest it attests — and nothing compared it to the digest our request carried, so a replay, a caching proxy or an authority fault could return a valid timestamp for someone else's bytes and it was written as evidence, while the auditor's own `openssl ts -verify` printed `message imprint mismatch` → FAILED over it. Refused now, before any write, as **`tsa_imprint_mismatch`**, naming both digests. Two more codes join it: **`tsa_response_malformed`** (bytes RFC 3161 does not admit inside the response; openssl refuses such a file outright) and **`tsa_token_unreadable`** (neither this reader nor openssl could decode the token). **Eight refusal codes** total. ⚠️ **Teach the OPPOSITE direction too, because it is a LOOSENING and a user may report it as a change:** a **BER-encoded** token is no longer refused when openssl can read it. 0.40.2 refused those outright; CMS permits BER and `openssl ts -verify` accepts them, so the old behaviour discarded genuine evidence from authorities whose stacks nobody here has surveyed. A refusal now requires **both** readers to fail — so timestamps that start working again on 0.40.3 are the fix, not a regression. **The chain-of-custody envelope now records `artifacts[].tsa.imprintVerified`** (an explicit boolean) plus `imprintUnverifiedReason` when the comparison could not be made. ⚠️ Teach that **the absence of `imprintUnverified` is NOT the presence of verification** — read the boolean. And a run configured for timestamping that TIMESTAMPED fewer artifacts than it attempted now says so ONCE, carrying the refusal codes, so *"zero `.tsr` files"* is no longer indistinguishable from *"timestamping was never configured"*. ⚠️ **THE PUBLISHED AUDITOR INSTRUCTION CHANGED AND THE OLD ONE CANNOT BE RUN:** verify with `openssl ts -verify -in <file>.tsr -data <file> -CAfile <ca-bundle>`. The prior text passed `-queryfile` AND `-data` together — mutually exclusive; openssl exits 1 with **no verdict at all** — and named a `.tsq` that no pack contains, because the request is built in a temp directory and deleted. If a user quotes the old command and reports that verification failed, that is why. ⚠️ **NOT RETROACTIVE:** packs timestamped before 0.40.3 were never checked for imprint match; re-verify them, and treat a `.tsr` that fails as never having been a valid timestamp for that artifact. ⚠️ `signed: true` attests **protocol status and what the token SAYS it attests, never cryptography**. Plugin catalog UNCHANGED at 28 EE / 55 overall; all eight coverage matrices UNCHANGED; peer floor UNCHANGED at CE ≥ 0.2.45.
>
> **Prior:** 0.2.44 (post-EE-0.40.2) — **A TSA rejection is no longer written as evidence.** RFC 3161 timestamping stays opt-in (`NSAUDITOR_TSA_URL`; no default authority). When enabled, the response status is now read before anything reaches disk: a refusal writes NO `.tsr`, leaves any sidecar from a prior success byte-identical, and records `signed: false` with one of five reason codes — `tsa_submit_failed` · `tsa_rejected` · `tsa_not_granted` · `tsa_response_no_token` · `tsa_response_unparseable` — in the chain-of-custody envelope. Teach the reading: **a missing `.tsr` beside a `.sha256` means REFUSED, not failed** — the per-artifact ledger is `artifacts[].tsa.signed` / `.error` in the envelope, and `tsa_rejected` (the authority declined; fix the request) routes to a different action than `tsa_not_granted` (waiting or revocation; ask again later). ⚠️ **`signed: true` attests protocol status, never cryptography** — it means the authority granted and enclosed a token; `openssl ts -verify` remains the only thing that adjudicates the token itself. ⚠️ Packs produced by earlier versions against a rejecting authority may carry a ~50-byte rejection recorded as signed (real tokens run ~4.6 KB) — a tiny `.tsr` that fails `openssl ts -verify` is that class, and re-signing under 0.40.2 refuses honestly instead. Also taught: **the TSA policy-OID option (`NSAUDITOR_TSA_POLICY_OID`) works on OpenSSL ≥ 3.0 hosts for the first time** — OpenSSL 3.0 renamed the query flag to `-tspolicy` while LibreSSL kept `-policy`, both report a 3.x version, and the fix attempts the modern spelling and falls back; when the legacy spelling was used the signing result carries `tsaPolicyFlagFallback`. This is the option commercial TSAs (DigiCert / GlobalSign / Sectigo) require, so an operator who saw `unexpected_failure` with a policy OID configured should re-try on 0.40.2. And **parenthesised `(Dim N)` internal dimension codes are gone from GCP finding prose** — bare `Dim N` references remain in some GCP IAM finding text, a known census-pinned residual, not a new defect to report. Plugin catalog UNCHANGED at 28 EE / 55 overall; all eight coverage matrices UNCHANGED; peer floor UNCHANGED at CE ≥ 0.2.45.
>
> **Prior:** 0.2.43 (post-EE-0.40.0 · **requires CE ≥ 0.2.45 — RAISED**) — the eighth-framework release. **NIST SP 800-171 Rev 2 ships as `--compliance nist-800-171`, and CE registers the stem, so an older CE REJECTS the framework name outright** — that is what makes this floor a real bump rather than a paired bump. Teach the scope as **"NIST SP 800-171 evidence substrate for CMMC Level 2 preparation."** ⚠️ **WITHDRAWN — deliberately not claimed, in these exact words: "CMMC certified", "CMMC compliant", "CMMC ready", "FedRAMP authorized", any MET / NOT MET verdict, and any SPRS score.** Each of those is a C3PAO determination and none is a product output; a misrepresented SP 800-171 posture is the shape the DOJ Civil Cyber-Fraud Initiative settles, which is why this is a refusal rule for the agent and not a wording preference. All 110 Rev 2 requirements are enumerated — **2 covered / 49 partial / 59 out of scope**, each with a written reason — and coverage is claimed at the SP 800-171A **determination-statement (objective)** level, which is why only two clear the bar: a requirement counts as covered only when EVERY objective is technical system state the scan reads directly. ⚠️ **Rev 2 is PINNED**: CMMC assesses Rev 2 by rule, and Rev 3 is a different 97-requirement universe with organization-defined parameters — answering a Rev 3 question with Rev 2 output is drift. **CUI scope is the operator's assertion**: the scanner cannot see CUI, cannot tell FCI from CUI, and cannot see an enclave boundary. Also in this release: a KMS key in an account or project the operator may not control is no longer reported as customer key custody — it becomes an evidence gap, never a violation, because a cross-account CMK is recommended architecture. Plugin catalog UNCHANGED at 28 EE / 55 overall; the seven prior coverage matrices UNCHANGED.
>
> **Prior:** 0.2.42 (post-EE-0.39.0 · **requires CE ≥ 0.2.43**) — the coverage-honesty release. ⚠️ **Teach `deferredScope` as a per-PLUGIN declaration, never as a provider or coverage inventory.** Every GCP and Azure plugin now declares what it does NOT evaluate (1021, 1022, 1024, 1025, 1220, 1221, 1222), joining the AWS plugins that already did — 8 to 12 static boundaries each, emitted at `run()` scope on the audited path **including over an empty estate**, because "zero findings" is otherwise indistinguishable from "assessed and clean". A declaration is a **static capability boundary**: it is NOT an evidence gap and NOT a finding, it routes to ZERO compliance controls by design, and it must be reported as "not assessed". ⚠️ **An empty or short `deferredScope` is NEVER a claim of full coverage** — not every plugin declares its boundaries, so the list bounds only what the DECLARING plugins state. Never tell a user that an empty list means everything was assessed. Two AWS declarations (1080 Lambda, 1130 Backup) are now emitted ONCE PER RUN rather than once per region, so a fleet reads nine once-per-run disclosures — a count that no longer scales with the estate. And **GCP impersonation now REFUSES rather than degrading**: an impersonated client that yields no Authorization header fails loudly by name with the target principal, surfacing as `up:false` plus a warning that fires scanner-down synthesis. ⚠️ Teach that refusal as a PASS of the fail-closed design, never as a regression — the alternative it replaced was silent anonymous requests reporting FALSE evidence gaps. Plugin catalog UNCHANGED at 28 EE / 55 overall; every coverage matrix UNCHANGED.
>
> **Prior:** 0.2.41 (post-EE-0.38.0 · **requires CE ≥ 0.2.43**) — the sign-pack / verify-pack release. ⚠️ **This entry described those commands as "reachable and not yet proven" when written, and that is now STALE in the underclaim direction.** The published-bytes gate RAN against the EE 0.38.0 registry bytes on 2026-08-17 and PASSED, so the capability is PROVEN for an operator-held key over one framework's envelope and the artifacts it enumerates, never a vendor attestation. What has not changed, and never will, is that scope. `compliance sign-pack` signs a chain-of-custody envelope with an operator-held Ed25519 key; `compliance verify-pack` establishes authorship AND recomputes every enumerated artifact hash against disk. A verified signature proves the holder of a key asserted authorship of ONE framework's envelope, relative to operator key custody — never a vendor attestation, never proof the scan ran. It covers that envelope and its four artifacts, not the pack. Exit 2 means the run could not measure and is never a failure.
>
> **Prior:** 0.2.40 (post-EE-0.37.0 · **requires CE ≥ 0.2.42**) — the air-gap feed release. `feed bundle` and `feed import` let NVD feed files downloaded on a connected host be merged into one archive, hand-carried, and imported into the offline store on an isolated host — delivered as a **restricted** distribution rather than a public `npm install`, and amd64 only. ⚠️ Teach these precisely: **`feed 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, so never describe this as a store export or a backup. **No KEV or EPSS data ships with the product**; `--kev` / `--epss` carry the operator's own downloads from CISA and FIRST, and `--extras-dir` places them on import and prints the environment lines to set. **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; never describe an imported bundle as trusted or verified-as-genuine. **Import skips about a quarter of a real NVD year file by design** (withdrawn CVEs, entries with no CPE match data) — that is not data loss, while malformed records are a different signal meaning re-download. The air-gap delivery claims are EARNED BACK at this release, every one of them as a **restricted** distribution and **amd64 only** — the offline installation tarball is restricted, the install script is restricted, the feed-import CLI moves the feeds you downloaded, and air-gapped deployment is restricted and amd64 only — because the artifacts exist and the delivery gate passes on the built bytes. Teach them WITH their conditions, which are part of the claim and not footnotes: the bundle is a **restricted** distribution rather than a public `npm install`, and it is **amd64** only. **arm64 images remain WITHDRAWN** — never describe an arm64 enclave as supported.
>
> **Prior:** 0.2.39 (post-EE-0.36.0 · **requires CE ≥ 0.2.40**) — the verification release. Every compliance report now cryptographically checks each suppression signature — the Ed25519 suppression-signing capability this exercises is **proven as of EE 0.36.0 and verified for approvers whose registry entry carries key material**, its verification gate having run against the published bytes and passed — and writes `report.signatureVerification`. A MISSING verdict means NOT CHECKED, never failed; `cryptoValid` is absent when unanswerable, never `false`. Verification runs for approvers whose registry entry carries key material.
>
> **Prior:** 0.2.38 (post-EE-0.35.0 · **requires CE ≥ 0.2.40**) — the approval-surface release. Four Enterprise commands are now reachable from the CLI: `compliance suppress | review | renew | keygen`. `keygen` creates an Ed25519 approval keypair for a capability that was not yet proven at that release, writes the private half `0600` and prints an identity-registry member to paste; it refuses to overwrite an existing signing key. `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 — the expiry and the renewal record live inside the signed payload, so the record afterwards reads `signature does not match payload`, which an auditor cannot distinguish from real tampering. ⚠️ **Teach this precisely: Ed25519 suppression signing became REACHABLE at that release and was NOT YET PROVEN then. It was PROVEN at EE 0.36.0** — the verification gate ran against the published bytes and passed. Verification runs for approvers whose registry entry carries key material; for a fingerprint-only entry a report reads `not checked`, which records that no check ran and never that one failed. These commands are CLI-only — they are not MCP-reachable. Plugin catalog UNCHANGED at 28 EE / 55 overall; every coverage matrix UNCHANGED.
>
> **Prior:** 0.2.37 (post-EE-0.34.0 · **requires CE ≥ 0.2.39**) — the exploit-intelligence release. Enterprise findings that carry CVEs are joined by CVE-ID against a local **CISA KEV** catalog and a local **FIRST EPSS** scores file, banded `KNOWN_EXPLOITED` / `ELEVATED` / `BASELINE`, and the finding queue is ordered exploit-first — a KEV-listed MEDIUM outranks an unexploited CRITICAL. ⚠️ Teach this accurately: **no feed data ships with the product.** Both stores are operator-populated via `NSAUDITOR_EXPLOIT_KEV_STORE` / `NSAUDITOR_EXPLOIT_EPSS_STORE`, the same rule as the offline NVD store, and both **fail closed when stale** (KEV 14-day / EPSS 10-day windows) so an out-of-date catalog never reports "not exploited". `riskScore` is UNCHANGED — `exploitPriority` is a new axis beside it, not a re-weighting. ⚠️ One correction to this package's own prose: the risk score is **CVSS weighted by verification status with an initial-access uplift**, not "severity × exploitability × impact × exposure" — that phrasing named an exploitability input that did not exist until this release. Plugin catalog UNCHANGED at 28 EE / 55 overall; every coverage matrix UNCHANGED.
> **Prior:** 0.2.36 (post-EE-0.33.0 · **requires CE ≥ 0.2.37**) — a wording-only patch on top of the wiring release: the RFC 3161 row said the capability was "implemented but not yet wired to a flag", which EE 0.33.0 falsified and the live-TSA smokes then falsified twice over (npm path 2026-08-07, from inside the `:0.33.0` container image 2026-08-08). It is opt-in via `NSAUDITOR_TSA_URL`, proven on both delivery vehicles, and the only surviving caveat is version scope — retained images `:0.32.11` and earlier carry no `openssl`. **Prior:** 0.2.35 — the wiring release. Three CE entry points are new and the skill should teach them: `compliance attest` (Type II recurring-scan attestation over a directory of prior scans; exit 3 on an empty history), and `--sla-policy <file>` / `--compliance-history <dir>`, which reach the SLA/MTTR engine. A startup posture veto refuses `NSAUDITOR_OFFLINE_ONLY=1` together with a configured outbound path (exit 2). ⚠️ Two corrections to this package's own prose: the `compliance_check` disclosure now carries the WITHDRAWN marker so an instrument can tell it from a claim, and the Enterprise row no longer calls plugin 1023 a cloud auditor — it scores zero-trust posture from a NETWORK-host scan and cannot be run by selecting it with `--plugins`. Plugin catalog UNCHANGED at 28 EE / 55 overall; every coverage matrix UNCHANGED.
> **Prior:** 0.2.34 (post-EE-0.32.11 — **a correction release.** The Verification Engine was WITHDRAWN at EE 0.32.7; this package went on presenting it as a shipped Phase 4, glossing `VERIFIED` as probe-confirmed, and selling "verification probes" in the pricing table. All corrected — the status enum stays, the active-probe gloss goes, and findings are stated to be emitted UNVERIFIED. Upstream: `scan_cloud` summaries now surface the INFO tier and `deferredScope` boundaries; a new `compliance_matrix` MCP tool returns the shipped coverage matrix for any shipped framework, derived at call time; `pdfExport` withdrawn and every capability now ships a description. Plugin catalog UNCHANGED at 28 EE / 55 overall; every coverage matrix UNCHANGED.)
> **Prior:** 0.2.33 (post-EE-0.32.10 — no knowledge change.)
> **Prior:** 0.2.32 (post-EE-0.32.9 — the skill package's own provenance sweep.)
> **Prior:** 0.2.30 (post-EE-0.32.7 — a **matrix-neutral** release that does two things. (1) **Cross-framework routing:** the network-scan analysis agents' findings now route in **every shipped compliance framework**, not just SOC 2 — a host serving cleartext or exposing SMB previously failed the SOC 2 report and read clean in every other framework off the same scan; that gap is closed (mutation-proven guard; every `coverageSummary` block byte-identical). (2) **Capability-claim honesty pass:** several Pro/Enterprise capabilities advertised without a shipping implementation — verification engine, branded reports, usage metering, Docker per-scan isolation, and the ZDE "policy engine" / Enterprise-CTEM datastore framings — are **withdrawn**; the real cores (the code-enforced ZDE read-only guarantee, Pro-tier CTEM retention) ship and are described honestly, and the `verifiers/` stub files no longer ship. Plugin count UNCHANGED at 28; every coverage matrix UNCHANGED.)
NSAuditor AI is a modular, AI-assisted network security audit platform with 27+ scanner
plugins, CVE matching, MITRE ATT&CK mapping, and Zero Data Exfiltration by design. This
skill teaches you how to operate it via MCP tools and CLI.
---
## MCP Tools Reference
NSAuditor AI exposes tools via Model Context Protocol (stdio transport). Available tools
depend on the license tier (Community / Pro / Enterprise).
### Community Edition Tools (always available)
#### `scan_host`
Run a full plugin scan against a target host. Executes ALL enabled plugins in priority
order (discovery → service probes → OS detection → result fusion).
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `host` | string | ✅ | — | Target hostname or IP address |
Every plugin runs under the server's `PLUGIN_TIMEOUT_MS` (default 30000), including a plugin that
declares a longer budget of its own; there is no per-call timeout. A plugin that runs out of time
reads `timeout` in the result's `manifest` — its surface was NOT measured, which is never a clean
result.
⚠️ **What `scan_host` returns, and what it does NOT.** Services, the OS, and the flags the service checks leave on
each service record: weak SSH algorithms, an SNMP default community, weak TLS protocols / ciphers and a self-signed
certificate, and the MCP server checks — `mcpAnonymousAccess`, `mcpAnonymousToolList`, `mcpCleartextTransport`,
`mcpDeprecatedProtocol`, `mcpInspectorExposed`. Since Community 0.2.57 the records also carry the HTTP probe's methods
(006: `dangerousMethods`, with `methodsTested` false when no Allow header was read), the NetBIOS/SMB null-session check
(014: `nullSessionAllowed` and `shares`), the TLS-certificate audit (040: `certAudit`) and the debug-endpoint audit
(050: `tribeHealth`, only when TCP 8080 is open). For a domain-name target the DNS-security audit (060: `dnsSecurity`)
lands on the 53/udp record when the scan found a 53/udp service, otherwise in the conclusion's `evidence`; for an
IP-address target 060 DECLINES — its `manifest` entry reads `skipped` with the reason, the `markdown` says
`DNS-security audit (060) not tested`, and nothing lands — so DNS posture is unknown, never clean. Anonymous FTP
login, zone transfer and the SMB null session are tested only when the MCP server's environment enables them
(`FTP_CHECK_ANON=true`; `DNS_CHECK_AXFR=true` with `DNS_AXFR_DOMAIN`; `SMB_NULL_SESSION=true`) — all off by default.
A null `anonymousLogin`, `axfrAllowed`, `nullSessionAllowed` or `dangerousMethods` means NOT TESTED, never "not
allowed" or "none": `anonymousLoginTested`, `axfrTested` and `nullSessionTested` are `true` when measured, else the
reason (`opt-in-off`, `no-domain`, `no-answer`). With the Enterprise package, the zero-trust assessment (1023) reaches
the conclusion only as one score line in its `evidence`, not its per-dimension findings. It does NOT look up CVEs and
does NOT run the Enterprise analysis agents or exploit intelligence (those run in the CLI scan's enrichment). So
`Security findings: 0` in its `markdown` is NOT a clean verdict and not a statement that the host has no known
vulnerabilities: that count is the service-check findings above — the MCP server flags and the 040 / 050 / 060 audit
entries included — and nothing else. Look CVEs up with `get_vulnerabilities` (Pro) on
each service whose `cpe` names a concrete version (a `*` version is not a lookup for what is deployed). A service with
a program and version but `cpe: null` — a program outside the scanner's CPE table; on the measured router, the
firmware's HTTP server — gets NO lookup from either path: build a CPE for it (`references/workflows.md`), or report its
CVE coverage as unknown, never as clean. With the Enterprise package and a Pro or Enterprise licence,
`nsauditor-ai scan --host <host>` runs the CVE mapper and the agents, and joins exploit intelligence when a KEV / EPSS
store is configured. Measured on a router in an earlier release: the Markdown `scan_host` uses, rendered over the CLI
scan's own conclusion for that host, read `Security findings: 0` while that CLI scan carried 16 CVEs and 5
analysis-agent findings, and plugins 040 and 060 had recorded 2 and 3 HIGH findings that did not reach `scan_host`'s
response then. Those audit entries land on the records now and are counted; the CVEs and the agent findings still are
not.
**Returns:** `{ host, conclusion, manifest[], pluginsRan, markdown }` — `conclusion.result` is the fused
record (`summary` is a one-line string; `host`, `services[]`, `evidence[]`), and `manifest[]`
is every plugin's `{ id, name, status, reason, duration_ms }`; `pluginsRan` is the number of `manifest[]` entries
whose status is `ran`. There is no `findings` array. See
`references/schemas.md`.
**Example:**
```json
{ "host": "192.168.1.1" }
```
**Important:**
- For RFC 1918 / private IPs, the MCP server must have `NSA_ALLOW_ALL_HOSTS=1` set.
- The server blocks loopback (127.x, ::1), link-local (169.254.x, fe80:), and cloud
metadata endpoints (169.254.169.254) — this is SSRF protection, not a bug.
- Plugins with unmet requirements auto-skip (e.g., SSH scanner skips if port 22 is closed).
---
#### `compliance_matrix`
Return the SHIPPED compliance coverage matrix for a framework — how many controls are Covered, Partial and
Out of scope, with the control ids and the per-group out-of-scope reasons.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `framework` | string | ❌ | `soc2` \| `hipaa` \| `nist-csf` \| `pci-dss` \| `iso-27001` \| `cis-v8` \| `gdpr` \| `nist-800-171` \| `all` (default) |
**ALWAYS call this before stating or tabulating any coverage matrix, and quote what it returns.** Do NOT derive a
matrix from the plugin inventory, from a scan result, or from documentation — coverage is a property of the shipped
framework maps, not of the plugin list, and a derived matrix will disagree with the customer's own report.
⚠️ **QUOTE the severity header the scan printed — never re-type it from the findings you read.** `scan_cloud` renders one line per provider, `N CRITICAL · N HIGH · N MEDIUM · N LOW · N INFO · N PASS`, and **the INFO column is load-bearing: it carries the evidence gaps and the deferred-scope boundaries.** Re-typing that line while summarising is how the column goes missing — measured 2026-09-18, a GCP reply reported `5 CRITICAL · 2 HIGH · 11 MEDIUM · 0 LOW · 11 PASS` over a header that read `… 0 LOW · 3 INFO · 11 PASS`. Every other number was right. **This is the exact regression the product itself fixed at EE 0.32.11**, when a header could read `…0 LOW · 17 PASS` over 62 unreported INFO records — reproduced one layer up, in the retelling. If you report tiers at all, report every tier the product printed, INFO included, even when it is zero.
⚠️ **Never collapse a coverage triple into a single percentage or a "fully covers" figure.** State covered, partial and out of scope as three separate numbers with their ids; a percentage or a "fully" phrasing folds partial into covered and overstates coverage — a 19 / 9 / 44 matrix is 19 covered, not "about 39% fully."
⚠️ `outOfScope` is the **flattened sub-criterion count**, not the number of out-of-scope groups: SOC 2 returns 37
ids across 11 groups. Publishing the group count yields a plausible-looking 10 / 4 / 11.
It **fails closed** — if the Enterprise pack is not installed or its data is unreadable it raises rather than
returning an empty matrix, because an empty matrix is what gets filled in with a guess.
---
#### `list_plugins`
List all available scanner plugins with metadata.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| *(none)* | — | — | — |
**Returns:** Array of `{ id, name, priority, requirements }`
**When to use:** Before a scan to understand available plugins, or to help the user select
specific plugins for a targeted probe.
---
### Pro Tools (Pro license required)
#### `probe_service` *(Pro license required)*
Run a single plugin against a specific host:port for deep-dive investigation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `host` | string | ✅ | Target hostname or IP |
| `pluginName` | string | ✅ | Plugin ID (e.g. `"002"`) or its full `name` as `list_plugins` returns it, in any case (e.g. `"SSH Scanner"`); `ssh_scanner` matches nothing (`Unknown plugin`) |
| `port` | number | ✅ | Target port number |
**Returns:** Raw plugin output with full evidence for that specific service.
**Common plugin IDs:**
| ID | Name | Best For |
|----|------|----------|
| 002 | SSH Scanner | Banner, version, weak algorithms/ciphers |
| 004 | FTP Banner Check | FTP daemon identification, anonymous login (only with `FTP_CHECK_ANON=true`) |
| 006 | HTTP Probe | Server headers, tokens, vendor hints |
| 007 | SNMP Scanner | Device info via sysDescr, hardware/firmware |
| 009 | dns_scanner | DNS server version (CHAOS query) |
| 010 | Webapp Detector | Technology fingerprinting from a fixed in-house signature table |
| 011 | TLS Scanner | TLS versions, the cipher negotiated per version, deprecation |
| 014 | NetBIOS/SMB Scanner | SMB/NetBIOS enumeration, null sessions |
| 040 | TLS Certificate & Cipher Auditor | Certificate chain, expiry, weak ciphers |
| 050 | TRIBE v2 Neural API Security Probe | Debug leaks, stack traces, CORS misconfig on a TRIBE v2 API |
| 060 | DNS Security Auditor | SPF/DKIM/DMARC, DNSSEC, zone transfer |
---
#### `get_vulnerabilities` *(Pro license required)*
Look up known CVEs for a CPE string via the NVD 2.0 API.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `cpe` | string | ✅ | CPE 2.3 format (see CPE guide below) |
| `maxResults` | number | ❌ | Max CVE results to return |
**Returns:** `{ cpe, totalResults, cves[] }` — each CVE is `{ cveId, description, cvssScore, severity,
vectorString, published, lastModified }`; the CVSS fields are NVD's v3.1 metric, else v3.0, else `null`.
`totalResults` counts the CVEs returned, after `maxResults`.
**CPE Construction Guide:**
Format: `cpe:2.3:a:<vendor>:<product>:<version>:*:*:*:*:*:*:*`
| Detected Program | Detected Version | CPE String |
|------------------|------------------|------------|
| OpenSSH | 8.9p1 | `cpe:2.3:a:openbsd:openssh:8.9:p1:*:*:*:*:*:*` (the suffix is the `update` field) |
| Apache httpd | 2.4.54 | `cpe:2.3:a:apache:http_server:2.4.54:*:*:*:*:*:*:*` |
| nginx | 1.24.0 | `cpe:2.3:a:f5:nginx:1.24.0:*:*:*:*:*:*:*` (a `cpe` the scanner returns spells it `nginx:nginx`: if one spelling returns no CVEs, retry with the other; an empty result is not a clean service) |
| OpenSSL | 3.0.8 | `cpe:2.3:a:openssl:openssl:3.0.8:*:*:*:*:*:*:*` |
| ISC BIND | 9.18.12 | `cpe:2.3:a:isc:bind:9.18.12:*:*:*:*:*:*:*` |
| vsftpd | 3.0.5 | `cpe:2.3:a:beasts:vsftpd:3.0.5:*:*:*:*:*:*:*` |
| Samba | 4.17.5 | `cpe:2.3:a:samba:samba:4.17.5:*:*:*:*:*:*:*` |
| Log4j | 2.14.1 | `cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*` |
| MySQL | 8.0.32 | `cpe:2.3:a:oracle:mysql:8.0.32:*:*:*:*:*:*:*` |
| PostgreSQL | 15.2 | `cpe:2.3:a:postgresql:postgresql:15.2:*:*:*:*:*:*:*` |
**Tip:** If vendor is ambiguous, search NVD with just the product name first.
---
### Enterprise Tools (license gated)
These tools return a license upgrade prompt on CE installations:
| Tool | Tier | Purpose |
|------|------|---------|
| `scan_cloud` | Enterprise | Audit one or more cloud accounts (AWS / GCP / Azure) for security & compliance posture using the server-configured credentials. No network host needed. Input: `{ providers?: ("aws"\|"gcp"\|"azure")[], regions?: string[] }` — **pass only the cloud(s) the user names** (`providers:["aws"]` for "audit my AWS account"); omit `providers` only when the user asks to audit ALL clouds. Use this (not `scan_host`) when the user asks to "audit my AWS account", "audit my AWS and Azure accounts", or "check my cloud compliance". CE/Pro callers get an upgrade message. **`regions` (AWS only)** — AWS region codes (e.g. `["us-east-1","eu-west-1"]`) or `["all"]`. **Default — omit `regions` (MOST requests):** a plain "audit my AWS account", a "quick check", or any request that names no region AND does not explicitly ask for all/every/whole-account/complete/full coverage → **OMIT `regions`** (the auditors that take their region from the client audit the server-configured `AWS_REGION` and no other; the three that enumerate their own region list — CloudTrail trail discovery (1040), GuardDuty/Inspector (1200), EC2 instances (1210) — still attempt every enabled region; do NOT fan out or batch). Omitting does NOT scan all regions with every auditor. **Specific regions:** when the user names region(s), pass exactly those. **All regions — ONLY on an explicit "all regions / every region / whole account / full coverage" request:** use the discover-then-batch approach in the region-scope note below — do NOT pass a single `["all"]` call and rely on it (it fans every regional plugin across all enabled regions and usually EXCEEDS the host's MCP tool-call timeout, e.g. Claude Desktop's, returning nothing). Unknown region codes are rejected before the scan runs (the WHOLE call fails — fix the region and re-call; never silently drop it). |
| `get_findings` | Enterprise | Drill into the findings of the MOST RECENT `scan_cloud` scan — a per-provider, **per-session** cache (NOT live state; cleared when the MCP server restarts). Input: `{ scanId?, provider?, plugin?, severity?, category?, cursor?, limit? }`. Use it AFTER `scan_cloud` when the summary's **category rollup** shows a category you want to expand to specific resources, or when you need the FULL untruncated text of a finding. Pass the **`scanId` from the `scan_cloud` summary footer** + the `provider`; filter by `category`/`severity`/`plugin`; paginate with `cursor`/`limit` (server-capped at 20 — follow `nextCursor`). If you get a **"re-run scan_cloud"** error the cache was cleared or superseded — **re-run `scan_cloud`, do NOT retry `get_findings`**. CE/Pro callers get the same upgrade message as `scan_cloud`. |
> **Interpreting `scan_cloud` results — never report a false clean:** read **`findingsSummary`** for the findings — it maps each provider to `counts` (per-severity totals) and a `findings` list of the CRITICAL/HIGH items (`{severity, plugin, title}`); report those. A cloud was effectively audited only if it appears in `auditedProviders`. If the result has `audited: false`, any `notes` entries, or `pluginsRan: 0`, the cloud was **NOT** audited (no plugins, missing credentials, or skipped) — report the gap explicitly; an empty result is **not** a clean pass. Do not infer "clean" from an empty `findingsSummary` when the cloud is not in `auditedProviders`. **Beyond CRITICAL/HIGH, `findingsSummary[provider].rollup` groups the remaining findings by `category` with counts (count-descending) across **MEDIUM, LOW *and* INFO** — and the INFO tier is not optional reading: every deferred-scope boundary is INFO, and an evidence gap carries its own finding's severity, which can be any tier up to HIGH, so read every tier. ⚠️ This sentence said "MEDIUM + LOW" until EE 0.39.0, which was FALSE against the summariser's own three rollup buckets, and false in the direction that hides the tier a reader most needs. Report every tier that occurs; these are actionable too: a category like `sqs-age-alarm-missing` or `*-public` is a real gap, not noise, and reporting only CRITICAL/HIGH while the rollup is non-empty is itself a false clean. To enumerate the specific resources behind a rollup category, or to read a finding's full untruncated text, call `get_findings` with the `scanId` from the summary footer + the `category`.**
> **Reporting `scan_cloud` region scope — never overstate coverage:** Report the regions you ACTUALLY scanned, derived from the `regions` you **passed** — NOT from the findings. If you OMITTED `regions`, the auditors that take their region from the client scanned only the server-default region (`AWS_REGION`) — say that, and that the account's OTHER enabled regions were NOT covered by them (offer to re-run for all regions). The three auditors that enumerate their own region list — CloudTrail trail discovery (1040), GuardDuty/Inspector (1200), EC2 instances (1210) — attempt every enabled region when `regions` is omitted. Say one of them covered every region ONLY if it `ran` and carries no multi-region evidence gap (a single-region fallback, or errored or denied regions); any region such a gap names was NOT audited, say so. 1040's CloudWatch-alarm and AWS Config checks cover the configured region only. Never call the whole scan single-region when these three ran. **Never escalate a single-region or "quick" request into a multi-region scan.** Do NOT claim "all regions" / "every region" / "across N regions" for the scan as a whole because those three list per-region findings: their coverage is NOT evidence the other auditors ran outside the region(s) you passed. When you DO pass `regions`, their region-scoped checks follow the list you passed.
> **Full all-region coverage — discover then batch** (use ONLY when the user explicitly asked for all/every/whole-account/complete/full region coverage; NEVER for a plain or "quick" request — those stay on the `regions` default above, with no explicit fan-out): a single `regions:["all"]` call usually exceeds the host's MCP tool-call timeout (e.g. Claude Desktop's) and returns nothing. Reliable pattern: (1) run a default scan (omit `regions`) — its GuardDuty/Inspector findings enumerate every enabled region, giving you the full list while the region-scoped auditors cover the default region; (2) scan the REMAINING regions in small batches (3–5 region codes per `regions:[...]` call) across successive calls until every enabled region is covered; (3) merge and report the TOTAL number of regions actually covered — **count** them, don't guess. If you try `["all"]` and it times out, that result is INCOMPLETE — fall back to batching and continue until complete; never report a timed-out or partial scan as full coverage.
> **⚠️ COMPLIANCE IS A CLI SURFACE, NOT AN MCP TOOL.** `compliance_check` is WITHDRAWN — there is no such tool and there never was, and the evidence pack is produced by `nsauditor-ai scan --host <target> --compliance <fw> --out <dir>`, which runs the compliance PHASE and writes `scan_compliance_<framework>.{json,md,html}` plus its attestation and chain-of-custody sidecars. `scan_cloud` **never runs that phase** and maps no finding to any framework, so no MCP call produces a pack. Use `compliance_matrix` to state COVERAGE (what is covered / partial / out of scope); use the CLI to produce EVIDENCE.
>
> The framework detail below is accurate and worth keeping — it was attached to a tool name that does not exist:
>
> SOC 2 (AICPA TSC 2017) + HIPAA (§164.312 Technical Safeguards) + NIST CSF 2.0 Core + PCI DSS v4.0.1 (sub-requirement-level for QSA RoC; PCI SSC June 2024 errata) + ISO/IEC 27001:2022 (per-Annex-A-code-level for ISO/IEC 17021-1 certification body assessors; ISO + IEC October 2022; 2013 edition retired October 31, 2025) + **CIS Critical Security Controls v8** (per-Safeguard-level; Center for Internet Security May 2021, v8.1 errata June 2024) + **GDPR Article 32 (Security of Processing)** (sub-measure-level; Regulation (EU) 2016/679; **Art. 32 infrastructure substrate only, NOT GDPR compliance**) gap analysis — these and **NIST SP 800-171 Rev 2** (below) have all shipped (SOC 2 EE 0.3.x; HIPAA EE 0.9.0; NIST CSF 2.0 EE 0.10.0; PCI DSS v4.0.1 EE 0.11.0; ISO/IEC 27001:2022 EE 0.12.0; CIS Controls v8 EE 0.13.0; **GDPR Article 32 EE 0.20.0**; NIST SP 800-171 Rev 2 EE 0.40.0). Multi-framework via `--compliance all` (shorthand for every shipped framework; EE 0.31.4) or `--compliance soc2,hipaa,nist-csf,pci-dss,iso-27001,cis-v8,gdpr,nist-800-171` (any CSV subset; aliases `nist`/`pci`/`iso`/`cis`, and `800-171` or `cmmc` for `nist-800-171` — ⚠️ `nist` stays NIST CSF, because two NIST publications now ship and the bare vendor name is ambiguous); an unknown token **fails fast** (no "Framework load failed" stub). One scan produces one complete auditor-ready evidence pack per framework requested. **NIST SP 800-171 Rev 2** (requirement-level `3.x.y` for the matrix, SP 800-171A determination-statement level `3.x.y[a]` for evidence; EE F2 cycle) is the eighth. ⚠️ **TEACH ITS SCOPING DOCTRINE VERBATIM AND NEVER PARAPHRASE IT: "NIST SP 800-171 evidence substrate for CMMC Level 2 preparation."** WITHDRAWN — deliberately not claimed, in these exact words: "CMMC certified / compliant / ready", "FedRAMP authorized", never a MET / NOT MET verdict, and never an SPRS score — a Level 2 certificate is issued to a CONTRACTOR by a C3PAO under the CMMC rule, and FedRAMP authorizes cloud service offerings, which this product is not. Only **2 of the 110** Rev 2 requirements are `covered`, and that is the design rather than a shortfall: one objective scored NOT MET fails its whole requirement, so a requirement is covered only when EVERY one of its determination statements is a statement of technical state the scan reads directly. Each mapped requirement carries `assessmentObjectives` and `objectivesEvidenced`; `partialBasis` names which of three shortfalls applies. CUI scope is the OPERATOR's assertion — the scanner cannot see CUI, cannot tell FCI from CUI, and cannot see an enclave boundary. The SSP (3.12.4) and POA&M (3.12.2) are operator artifacts this engine informs and never produces. ⚠️ Its requirement ids collide EXACTLY with PCI DSS sub-requirement ids (`3.5.1` is real in both), so ALWAYS qualify a citation. **CIS Controls v8**: 17 covered + 23 partial + 113 OOS across 153 Safeguards / 18 Controls. **Implementation Group cumulative discipline** — IG1=56 (cyber-insurance baseline; ~50-70% of mid-market policies require IG1 attestation), IG2 cumulative=130, IG3 cumulative=153; smallest-IG-membership tagging (NEVER report IG2 as 74-of-74 in isolation). **No-certification-body attestation discipline** — engine output is INPUT to CSAT / CIS-CAT Pro self-attestation OR a SOC 2 auditor cross-validating CIS scope, never "CIS certified." Cloud Companion Guide v8 shared-responsibility + CIS-Hardened-Image substrate-evidence credit (Safeguards 4.1/4.2/4.6) + 5 Security Functions (NOT 6 — no Govern) + 6 Asset Types + MS-ISAC/EI-ISAC/H-ISAC sector baselines + v7.1-to-v8 cross-reference. CIS Safeguard examples: `3.3` Data Access Control Lists, `5.4` Restrict Administrator Privileges, `6.3` MFA for Externally-Exposed Applications, `8.2` Collect Audit Logs, `11.4` Isolated Recovery Data Instance. ISO 27001 Annex A code examples: `A.5.15` Access control, `A.5.23` NEW 2022 Cloud services, `A.8.5` Secure authentication, `A.8.9` NEW 2022 Configuration management, `A.8.16` NEW 2022 Monitoring activities, `A.8.24` Use of cryptography. Statement of Applicability per Clause 6.1.3.d discipline + ISMS Clauses 4-10 OOS-by-design framing (7 Major Nonconformity classes — absence of internal audit per Clause 9.2 or management review per Clause 9.3 = auto-fail Stage 2) + 5-attribute taxonomy NEW in 2022 (controlType / informationSecurityProperties / cybersecurityConcepts [5 categories, NOT 6 like NIST CSF 2.0] / operationalCapabilities / securityDomains) + 2013-to-2022 transition discipline. Pair with ISO-aware GRC (Drata ISO 27001 / Vanta ISO 27001 / AuditBoard / OneTrust ISMS / Secureframe ISO 27001) for SoA workflow + internal audit + management review. PCI DSS sub-requirement examples: `Req 1.2.1` NSC config standards, `Req 8.4.1` MFA on non-console admin, `Req 10.2.1` audit logs enabled, `Req 11.3.1` quarterly internal vuln scans. Defined-vs-Customized Approach discipline (PCI DSS v4.0.1 states Customized-Approach ineligibility in each requirement's own Customized Approach Objective cell, not in any appendix; EE ships the derived set in `data/standards/pci-dss-v4_0_1-ids.json`; none of the mapped sub-requirements is Defined-only today, every main-body ineligible id sits in an out-of-scope group, the Appendix A2 / A3 ineligible ids are enumerated in no framework file, and `Req 12.3.2` is a third state — part of the Customized Approach and required of those who use it, neither eligible nor ineligible; CHD Scope operator-attested via CDE DFD per Req 1.2.4; card-brand AOC enforcement view — Visa CISP / Mastercard SDP / Amex DSOP / Discover DISC). **NIST SP 800-171 Rev 2** (evidence substrate for **CMMC Level 2 preparation** — all 110 Rev 2 requirements enumerated, claimed at the SP 800-171A determination-statement level; Rev 2 is pinned because CMMC assesses Rev 2 by rule and Rev 3 is a different 97-requirement universe). ⚠️ **Never tell a user this produces a CMMC certification, FedRAMP authorization, MET/NOT MET verdict or SPRS score — it produces none of those, and each is a C3PAO determination.** **GRC push (Enterprise, opt-in):** set `COMPLIANCE_GRC_PROVIDER=vanta` (or `drata` / `secureframe`) + `COMPLIANCE_GRC_TOKEN` to map the findings to the platform's evidence/test records and push them at scan time (outbound to the vendor you configure; finding content is **NOT redacted by default** — `COMPLIANCE_GRC_REDACTION` unset means `off`, and a Vanta push then carries finding text and `host:port` targets in the clear; set it to `hash` or `remove` to scrub them; token never serialized; the Vanta·Drata·Secureframe connector trio is complete — Secureframe records model; all three are early-access and live-tenant validation is not yet complete).
### Evidence gaps — never read one as a pass (post EE 0.32.9)
A finding whose text opens `Evidence gap (…)`, or which carries `details.evidenceGap: true`,
means **the scanner could not verify that surface** — it is NOT a clean result and must never
be summarised as one. Two shapes to recognise:
- `Evidence gap (multi-region enumeration incomplete): …` / `Evidence gap (scan time budget
exceeded): …` — enumeration did not finish. Report what was NOT covered, not what was.
- `Evidence gap: the <source> scanner could not run (<reason>) …` — an entire cloud's
scanner failed to start (an optional SDK absent, credentials unusable). Every in-scope
control of that cloud is a gap. **Do not describe a multi-cloud scan as clean when one
cloud's scanner never ran.**
If a scan predates EE 0.32.9, its gap findings lead with a retired prefix that no framework
anchor matches, so they route to no control — the engine detects this and warns. Advise a
re-scan rather than reusing the old artifact.
### Deferred scope — a declared boundary is neither a gap nor a finding (post EE 0.39.0)
`findingsSummary[provider].deferredScope` lists surface this release does **not evaluate at all**.
It is a **static capability boundary**, and all three of the following are load-bearing:
- **It is NOT an evidence gap.** A gap means the scanner tried to read something and could not
(AccessDenied, truncated enumeration) — fixable with permissions. A deferred-scope entry means no
code was ever written to look. Never merge the two lists, and never suggest granting permissions
to close one.
- **It is NOT a finding.** It routes to **ZERO** compliance controls by design, carries no severity
judgement about the estate, and must never be reported under "issues found" or counted as a
failure. Report it as **"not assessed"**.
- **It is emitted even over an EMPTY estate**, deliberately: an account with no resources yields
zero findings whether or not anything was examined, so the declaration is the only thing that
tells those two apart.
⚠️ **AN EMPTY OR SHORT `deferredScope` IS NEVER A CLAIM OF FULL COVERAGE.** Not every plugin
declares its boundaries, so the list bounds only what the **declaring** plugins state — it is not a
coverage inventory. Where the list is empty or short, say plainly that boundaries are declared
per-plugin and this is not an inventory. **Never tell a user that an empty list means everything was
assessed.** State this as a per-plugin invariant, never as a provider roster: a roster sentence goes
stale on the next Enterprise release, which is exactly why the CE tool description stopped carrying
one at CE 0.2.44.
Since EE 0.39.0 all three clouds declare: AWS through eleven plugins (1020 through its S3 access-point helper, then 1060, 1070, 1080, 1090, 1100, 1110, 1120, 1130, 1140 and 1230 — 1140 and 1230 declare since EE 0.41.0), Azure through four (1022, 1220,
1221, 1222) and GCP through three (1021, 1024, 1025). Before that release only AWS declared, and the
asymmetry was itself the hazard — **one provider disclosing is what makes another's silence read as
completeness.** The AWS declarations are also now emitted once per RUN rather than once per region,
so the count no longer scales with the estate and no entry is region-stamped.
### Cross-framework routing — cite the engine, do NOT freehand-map (post EE 0.32.7)
When a user asks which controls a finding maps to, **read it from the engine's compliance
pack (the `scan_compliance_<framework>.json` artifact the CLI writes) — do not infer
a mapping from general security knowledge.** The engine deliberately routes some findings
*narrowly*, and a plausible-looking freehand mapping will overclaim. The non-obvious
dispositions to know:
- **Missing HSTS header → SOC 2 CC6.7 and NIST SP 800-171 3.13.15.** It is deliberately
**not** mapped to HIPAA §164.312(e)(1), ISO A.8.9, CIS 3.10, NIST CSF 2.0, PCI DSS, or GDPR.
The finding fires only on an endpoint whose transport **is** encrypted (the header governs
a *future* client's downgrade, not the observed session), so failing a transmission-security
or secure-configuration control in those frameworks on one absent response header would
overclaim — a Required HIPAA standard or a CIS Safeguard flipped on a single-header
inference. The 800-171 rule routes it to 3.13.15 (authenticity of communications sessions),
a **partial** requirement; its rationale reads the finding as corroborating substrate that
applies where the operator asserts the endpoint serves CUI-bearing sessions. (EE 0.32.7 cut
the routing to SOC 2 alone; the 800-171 rule arrived with that framework in EE 0.40.0.)
**It fires on port 443 only, where the HTTP probe (006) received the HTTPS response:** since Community 0.2.57 that
probe keeps the `Strict-Transport-Security` header on its port-443 HTTPS record, and the CLI scan's crypto agent
(Enterprise package, Pro or Enterprise licence) reads it there. On any other port, over plain HTTP, or where no HTTPS
response arrived (a certificate the client rejects needs `--insecure-https`), the header is not read, so the absence
of this finding says nothing about whether HSTS is set there.
- **Aggregate open-port count** and the **opportunistic-STARTTLS / port-inferred cleartext**
variants route to **SOC 2 only** — each is a breadth heuristic or a self-declared
*unverifiable* observation, not a per-transmission determination.
- Otherwise, network-scan analysis-agent findings (`crypto_agent` / `exposure_agent`) route
across **every shipped** framework where the control subject matches: a **confirmed**
cleartext channel fails HIPAA §164.312(e)(1), ISO A.8.24, NIST CSF PR.DS-02, CIS 3.10,
PCI DSS 4.2.1, GDPR Art.32(1)(a)-encryption-in-transit, NIST SP 800-171 3.13.8 and
SOC 2 CC6.7, among other controls — read the full list from the pack.
If unsure, say the pack is what decides it and offer to run the CLI — never assert a control
mapping the engine did not emit, and never claim an MCP call produced a pack.
---
### Suppressions — the workflow SHIPS; signing an approval is opt-in (proven at EE 0.36.0)
An operator can suppress a compliance violation as accepted-risk or false-positive (`nsauditor-ai compliance suppress … --status <accepted_risk|false_positive>`, Enterprise). That suppression workflow **ships and is reachable** from the CLI; it is not an MCP tool. The report lists each suppressed finding with its OWN suppression's status — ACCEPTED RISK or FALSE POSITIVE — and its approver. A control whose violations are ALL suppressed reads FALSE POSITIVE when every one of those suppressions is a false positive and ACCEPTED RISK otherwise; one unsuppressed violation keeps the control FAIL. (The finding-queue status `FALSE_POSITIVE` in `references/schemas.md` is a different field.) The suppression is applied before the pack is written, so it sits **inside** the hashed artifact.
⚠️ **Ed25519 SIGNING of suppressions became reachable in EE 0.35.0 via `compliance suppress` and was PROVEN at EE 0.36.0, verified for approvers whose registry entry carries key material** — the verification gate ran against the published bytes and passed, tamper negative control included. Present a produced signature as verified evidence only **for approvers whose registry entry carries key material**; a fingerprint-only registry entry makes a report read `not checked by this report`, which records that no check ran and must never be reported as a failure. The signer backends and the frozen `algorithm` / `backend` record fields are groundwork, deliberately landed before reachability because retrofitting algorithm agility once signatures exist in customer archives would break every auditor holding one. How it is produced: `nsauditor-ai compliance keygen --key <path>` writes an Ed25519 keypair (private half `0600`) and prints the identity-registry member to paste, including the public key material a report needs to verify; while `NSAUDITOR_SIGNING_KEY` names that local key file, `compliance suppress` signs the approval it writes, and without it the approval is recorded unsigned and the report says `unsigned — documentation-only approval` (a KMS or keychain reference is refused at the command). Where the approver's registry entry carries that key material, a signature that verifies renders `signed (approver)`, `signed (deployment)` or `signed, identity model not declared`, and a one-character edit to the approval's signed content renders `🔴 signature FAILED verification`, with the attestation at `not_authenticated`. Even verified, `approver` is the operator's DECLARATION — the signature proves the registered key signed the record, not who holds that key; corroborate against the identity registry. Setting the key later does not sign approvals already recorded, and `compliance renew` on a signed approval invalidates its signature (re-approve with the key configured instead).
**Call an approval's signature verified only where the report's attestation for it reads `signed (…)` or `signed, identity model not declared`.** `unsigned` is a documentation-only approval. `signature NOT authenticated` — in an Appendix B cell, `🔴 signature FAILED verification` or a `signed — …` qualifier such as `not checked by this report` — means nothing may lean on that signature; only `FAILED verification` says the record and its signature disagree (tampering, or a `renew` after signing). If asked what the SHA-256 chain-of-custody covers: each framework's `scan_chain_of_custody_<fw>.json` lists the artifacts it covers with their digests, beside a `.sha256` sidecar per file. On its own a digest detects corruption, or an edit by something that did not recompute it; it cannot prove the pack was unaltered, because whoever edits an artifact can recompute its digest. If the operator signed that envelope with `compliance sign-pack`, `compliance verify-pack` exposes an edit by anyone who cannot re-sign — for that one framework's envelope and the artifacts it enumerates, under an operator-held key. An opt-in RFC 3161 `.tsr` (`NSAUDITOR_TSA_URL`) fixes each timestamped artifact's digest at the Time-Stamp Authority's time, so a later edit cannot be backdated. None of these says who suppressed a finding or why — the suppression record does (approver, rationale, dates), and only a signature the report verifies ties it to a registered key; an assessor asking about suppressions usually wants that.
⚠️ **Disambiguation:** "suppression" also appears in this package in the unrelated **AWS SES email suppression list** sense (plugin 1190). They are different subjects; check which one is being asked about.
## Five-Phase Pipeline Architecture
NSAuditor AI follows an institutional five-phase pipeline:
```
Phase 1: DISCOVERY (CE) License → Plugin loading → PluginManager.run() → Concluder
Output: Fused scan with summary, OS, services[], evidence[]
↓
Phase 2: BASIC ANALYSIS (CE) Redaction → MITRE mapping → AI analysis (any provider)
Output: Admin raw JSON/HTML + AI reports + scan history
↓
[ License Gate: Pro required ]
↓
Phase 3: INTELLIGENCE (Pro) CPE generation → NVD CVE lookup → parallel ANALYSIS agents, which
read the collected scan evidence and send no probes of their own:
• Auth Agent (Telnet, default SNMP community; anonymous FTP only
with FTP_CHECK_ANON=true)
• Crypto Agent (TLS, ciphers, certificates)
• Config Agent (default SNMP community; RPC / NetBIOS open on a Linux host)
• Service Agent (end-of-life versions, from an offline table)
• Exposure Agent, Enterprise only (open database, management and
lateral-movement ports)
Output: Structured finding queue
↓
Phase 4: VERIFICATION WITHDRAWN — not shipped and not planned. The finding-status
(withdrawn) field exists; no active probe sets it. Every finding is
emitted UNVERIFIED, which never means "tried and could not be
confirmed" (see Security Constraints, item 5).
↓
Phase 5: SCORING (Pro/Ent) Risk scoring → Pro AI prompts → Compliance mapping
Output: Risk report + compliance report (Markdown, HTML, JSON)
```
> **Phase 4 is WITHDRAWN and on no roadmap; it stays in the diagram only so the pipeline's shape is
> legible.** The Verification Engine was withdrawn as a capability claim at EE 0.32.7 and is not planned. Do not describe
> findings as probe-confirmed (it is WITHDRAWN, not merely unused), and do not tell an
> operator a finding was "verified" — see
> `references/schemas.md` § Finding Statuses.
---
## Plugin Reference (56 scanners — 27 Community + 29 Enterprise)
**`references/plugins.md` is the authoritative catalog.** The counts above are derived from the
shipped plugin files; verify any of them with `nsauditor-ai license --plugins`, which prints the
live total and marks each Enterprise plugin `✓ active` or `✗ requires: <tier>`.
The Community set groups roughly as service probes, host/network discovery, and intelligence /
meta plugins, plus three deep-audit Community plugins (040 TLS Certificate & Cipher Auditor, 050
TRIBE v2 Neural API Security Probe, 060 DNS Security Auditor): `scan_host` runs them on every tier (050 only when
TCP 8080 is open) and returns their findings on the records — `certAudit`, `tribeHealth`, `dnsSecurity` (060's only for
a domain-name target, in the conclusion's evidence when the scan found no 53/udp service; it declines an IP address);
`probe_service` (Pro) runs one of them against one port.
> A per-plugin list used to be duplicated here and drifted: it claimed **18** Enterprise plugins
> while enumerating **15**, against **28** on disk. One catalog, in `references/plugins.md`.
## Workflow Recipes
See `references/workflows.md` for detailed multi-step patterns:
1. **Full Security Audit** — list_plugins → scan_host → get_vulnerabilities per service (over MCP: no analysis
agents and no exploit intelligence — the CLI scan runs them)
2. **Targeted Service Investigation** — probe_service(pluginId) → get_vulnerabilities
3. **Subnet Discovery** — CLI: `nsauditor-ai scan --host <CIDR> --parallel 10`
4. **CI/CD Pipeline** — SARIF output with `--fail-on` severity gating
5. **Continuous Monitoring (CTEM)** — `--watch --interval <min> --webhook-url <url>` (its webhook alerts a host
whose scan changed and that carries a finding at or above `--alert-severity`; the first cycle sets the baseline
and alerts nobody — `references/workflows.md` §5)
6. **AI-Powered Report** — Scan with AI provider (OpenAI/Claude/Ollama) + redaction
### Decision Tree: Which Tool to Use
```
User wants to...
├── Scan a host comprehensively → scan_host (services + service checks; NO CVE lookup —
│ follow with get_vulnerabilities per service `cpe`, or the CLI scan)
├── Audit a cloud account (AWS/GCP/Azure) → scan_cloud (Enterprise)
├── Check a specific service/port → probe_service (Pro)
├── Look up CVEs for software version → get_vulnerabilities (Pro)
├── See available plugins → list_plugins
├── Audit TLS certificates → probe_service (Pro) with plugin 040, one port — or scan_host:
│ `certAudit` on each port 040 audited
├── Check DNS security (SPF/DKIM/DMARC) → probe_service (Pro) with plugin 060 — or scan_host on the domain name:
│ `dnsSecurity` (on the 53/udp record, else in the conclusion's evidence)
├── Detect debug leaks / CORS issues → probe_service (Pro) with plugin 050 — or scan_host: `tribeHealth`,
│ only when TCP 8080 is open
├── Scan a subnet → CLI with --parallel (not MCP)
├── Set up continuous monitoring → CLI with --watch (not MCP)
│ (its webhook alerts a changed host carrying a finding at or above
│ --alert-severity; never on the first cycle — workflows.md §5)
├── Compare two scans → CLI (Pro): report --from <dir> --format executive --since prior
│ (not MCP; NEVER by hand — a by-hand diff reads a finding that
│ vanished unmeasured as fixed; report --since refuses it, but
│ the header's two measured limits still apply — compare runs
│ made by Enterprise 1.3.0 or later, and include the port scanner 003)
├── State framework COVERAGE → compliance_matrix (any tier)
└── Produce a compliance EVIDENCE PACK → CLI with --compliance (not MCP)
```
---
## Data Schemas
See `references/schemas.md` for complete structures:
- **Scan Result** (`scan_host`) — `{ host, conclusion{ result{ summary, host, services[], evidence[] } }, manifest[], pluginsRan, markdown }`
- **ServiceRecord** — `{ port, protocol, service, program, version, status, banner, evidence[] }`
- **Finding** (Pro/Enterprise queue) — `{ id, category, status, title, severity, cvss, target, evidence{ source, cve[], mitre[], raw }, remediation{ summary, effort, references[] }, riskScore }`
- **CVE Response** (`get_vulnerabilities`) — `{ cpe, totalResults, cves[]{ cveId, cvssScore, severity, vectorString } }`
- **Plugin Interface** — `{ id, name, priority, run(), conclude(), requirements }`
- **SARIF Output** — 2.1.0 format for CI/CD consumers
---
## Security Constraints
**CRITICAL — Always observe these constraints:**
1. **Zero Data Exfiltration (ZDE)** is a claim about a PARTY, not about the host:
No customer data is collected, transmitted, or stored by Nsasoft US LLC. Scan-derived data can
reach third parties. ON BY DEFAULT at Pro and above: CVE matching queries NIST's NVD with the CPE
(vendor, product, exact version) of each detected service that local NVD data does not already
answer; `NSAUDITOR_OFFLINE_ONLY=1` turns that off (CVE matching then needs a populated local NVD
store). OPT-IN: AI analysis (redaction below); GRC push (finding text plus host:port identifiers,
sent as they are unless `COMPLIANCE_GRC_REDACTION` is `hash` or `remove`, which fingerprints or
removes both); `--webhook-url` alerts (findings in the clear); and `get_vulnerabilities` (the CPE
it is given, to NVD; `NSAUDITOR_OFFLINE_ONLY` does not stop it). EE's egress register
(`utils/egress_register.mjs`) lists the outbound paths, each with its trigger and whether it is on
by default. Never tell a user "nothing leaves", and never suggest enabling an opt-in path without
the user's decision.
2. **SSRF Protection:** The MCP server refuses loopback, unspecified, link-local and cloud-metadata addresses
(`169.254.169.254`, `100.100.100.200`, `fd00:ec2::254`) in any spelling, whether written as the target or returned
for a host name — every answer is checked — and refuses a name that does not resolve. Private ranges (RFC 1918,
CGNAT `100.64/10`, `fc00::/7`) are refused too unless `NSA_ALLOW_ALL_HOSTS` is `1`, `true`, `yes` or `on`, which
admits private ranges only, never loopback, link-local or metadata. It takes one host: a CIDR or URL-shaped string
is refused. The check does not pin the answer, so a name whose answer changes between the check and the scan (DNS
rebinding) is not caught. Set the variable **only** for legitimate local network auditing.
3. **AI Redaction:** with AI analysis on and `OPENAI_REDACT` at its default (on), the scan payload
(host, summary, services, evidence) is scrubbed at every tier: the host field becomes
`[REDACTED_HOST]`; private IPv4 becomes `[REDACTED_HOST]` in the summary and `[REDACTED_IP]` in
services and evidence; public IPv4 becomes `[IP]`; link-local and full-form IPv6 and colon-form
MACs (`[MAC]`) are masked; serial numbers become `[REDACTED_HIDDEN]`, as do values under keys
containing a word listed in `CONFIDENTIAL_KEYWORDS` (empty by default). **That payload is NOT
scrubbed of** email addresses, internal hostnames, SNMP community strings, Bearer tokens, AWS keys,
file paths or compressed IPv6 (`2001:db8::1`); ports, service names, versions and other evidence
text go as they are. At Pro and above, the findings block added to the prompt is also masked by
default: target hosts, private IPv4, emails, MACs, internal hostnames and `community=<value>`
strings (a community quoted in a finding title is not); AWS keys, Bearer tokens and file paths
only with `NSA_AI_REDACT_LEVEL=strict`. With Ollama at its default localhost URL, the AI call
stays on the host.
4. **Scan Authorization:** ALWAYS confirm the user has authorization to scan the target.
Never scan hosts without explicit user instruction. Unauthorized scanning is illegal.
5. **Non-Destructive:** every scanner probe is a read-only query — NSAuditor AI never
exploits vulnerabilities or modifies target systems. (Active *verification* probes are
WITHDRAWN — not shipped and not planned; findings are emitted UNVERIFIED.) Every row of the
finding queue carries `UNVERIFIED`, `[COVERAGE GAP]` rows included, so the status says nothing
about a row: report a finding as what the scanner detected, never as "tried and could not be
confirmed". What says a check did NOT measure is a `[COVERAGE GAP]` title, an
`evidenceGap: true` marker or a `coverage UNVERIFIED` summary line.
---
## Configuration
### Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `NSA_ALLOW_ALL_HOSTS` | unset | `1`, `true`, `yes` or `on` (nothing else) admits private ranges — RFC 1918, CGNAT, `fc00::/7`. Over MCP it never admits loopback, link-local or metadata; on the CLI it lifts the whole scan-entry guard |
| `PLUGIN_TIMEOUT_MS` | 30000 | Per-plugin budget. `scan_host` and `probe_service` bind every plugin to it; on the CLI a plugin that declares its own budget outranks it |
| `PLUGIN_TIMEOUT_CEILING_MS` | 120000 | Upper bound on any plugin's declared budget — the setting that caps every plugin on the CLI |
| `AI_ENABLED` | false | Enable AI analysis |
| `AI_PROVIDER` | openai | `openai` · `claude` · `ollama` |
| `OPENAI_API_KEY` | — | OpenAI API key (or `keychain:OPENAI_API_KEY`) |
| `ANTHROPIC_API_KEY` | — | Claude/Anthropic API key |
| `OPENAI_MODEL` | gpt-4o-mini | OpenAI model name |
| `ANTHROPIC_MODEL` | claude-sonnet-4-6 | Anthropic model name (used when `AI_PROVIDER=claude`) |
| `OPENAI_REDACT` | true | On the CLI, masks the host, most IP addresses, colon-form MACs and serials in the AI scan payload — NOT emails, internal hostnames, Bearer tokens or AWS keys (see **AI Redaction** above) |
| `CONFIDENTIAL_KEYWORDS` | unset — no keyword scrub | Comma-separated substrings, e.g. `password,token,secret`: on the CLI, while `OPENAI_REDACT` is on, any AI-payload key containing one (any case) has its value replaced with `[REDACTED_HIDDEN]`. Unset, a key such as `password` is not masked for its name (serial-number keys — `serial`, `serialNumber`, `sn` — are masked either way) |
| `NSAUDITOR_LICENSE_KEY` | — | Pro/Enterprise JWT license key |
| `COMPLIANCE_GRC_PROVIDER` | — | **Enterprise** — opt-in scan-time GRC push: `vanta`, `drata`, or `secureframe`. Needs `COMPLIANCE_GRC_TOKEN`; optional `COMPLIANCE_GRC_BASE_URL` / `COMPLIANCE_GRC_CONTROL_MAP` / `COMPLIANCE_GRC_REDACTION` (`off`/`hash`/`remove`, default `off`). Finding content is **NOT redacted by default**: with `off`, a Vanta push carries finding text and `host:port` targets in the clear; `hash` or `remove` scrubs them. Token never serialized. Early-access; live-tenant validation not yet complete. |
| `SCAN_OUT_PATH` | out/ | Output directory for scan results |
| `SMB_NULL_SESSION` | false | Allow SMB null session probe |
| `FTP_CHECK_ANON` | false | Let the FTP check try an anonymous login — without it no scan reports anonymous FTP |
| `DNS_CHECK_AXFR` / `DNS_AXFR_DOMAIN` | false / — | Let the DNS check try a zone transfer of that zone — without both, `axfrAllowed` stays null (not tested) |
| `ENABLE_SYN_SCAN` | false | Enable Nmap TCP SYN scanning (requires root) |
### Plugin-Specific Timeouts
| Variable | Default | Plugin |
|----------|---------|--------|
| `TLS_SCANNER_TIMEOUT_MS` | 8000 | TLS Scanner |
| `HTTP_TIMEOUT_MS` | 6000 | HTTP Probe |
| `WAPPALYZER_TIMEOUT_MS` | 15000 | Webapp Detector |
| `DNS_TIMEOUT_MS`, else `DNS_SCANNER_TIMEOUT_MS` | 2000 | DNS Scanner |
| `OPENSEARCH_SCANNER_TIMEOUT_MS` | 6000 | OpenSearch Scanner |
---
## Installation & Setup
```bash
# Install globally
npm install -g nsauditor-ai
# Start MCP server (stdio transport). Your MCP client starts it itself (configs below); run by hand, it
# exits at startup unless NSA_MCP_AUTH_KEY holds the value `nsauditor-ai mcp install-key` prints (⚠️ below)
nsauditor-ai-mcp
# Never `npx nsauditor-ai-mcp`: the server is a bin inside the nsauditor-ai package, and when npx does not
# find that bin it looks the name up on the npm registry instead — which never starts this server
```
### Agent Integration
**Claude Code:**
```bash
nsauditor-ai mcp install-key
claude mcp add nsauditor-ai --env NSA_MCP_AUTH_KEY=<from: nsauditor-ai mcp install-key> -- nsauditor-ai-mcp
```
Run `install-key` once per machine, and use the `NSA_MCP_AUTH_KEY` value in the snippet it prints:
`keychain:NSA_MCP_AUTH_KEY` when it stored the key in the macOS Keychain, otherwise the literal key.
**Claude Desktop** (`claude_desktop_config.json`) — paste the block `nsauditor-ai mcp install-key` prints, which names node and the server script by absolute path, and add the other variables to its `env`:
```json
{
"mcpServers": {
"nsauditor-ai": {
"command": "<from: nsauditor-ai mcp install-key — the absolute path to node>",
"args": ["<from: nsauditor-ai mcp install-key — the absolute path to bin/nsauditor-ai-mcp.mjs>"],
"env": {
"NSA_ALLOW_ALL_HOSTS": "1",
"NSA_MCP_AUTH_KEY": "<from: nsauditor-ai mcp install-key>",
"PLUGIN_TIMEOUT_MS": "5000"
}
}
}
}
```
`NSA_ALLOW_ALL_HOSTS: "1"` lets Claude scan your own network — private addresses such as `192.168.x.x` or `10.x.x.x`. It admits private ranges only (RFC 1918, CGNAT `100.64/10`, `fc00::/7`): the server still refuses loopback, link-local and cloud-metadata addresses in any spelling, whether written as the host or returned for a host name — every answer is checked. Only `1`, `true`, `yes` or `on` turn it on. The check does not pin the answer, so a name whose answer changes between the check and the scan (DNS rebinding) is not caught. `install-key` does not print it, so add it yourself, and remove it if you only scan public hosts.
`PLUGIN_TIMEOUT_MS` bounds each plugin `scan_host` runs, not the call: the plugins run one after
another, so the call takes roughly the sum of their times. On one router, a `scan_host` call timed out
in Claude Desktop on 2026-08-10 and one returned within 138 s on 2026-09-30, so do not promise either
outcome: a call that times out returned no result — say so, and never report the host as clean.
> ⚠️ **`NSA_MCP_AUTH_KEY` is REQUIRED — the server refuses to start without it.** Generate one with
> `nsauditor-ai mcp install-key`, then put the SAME value in the `env` block above. Without it the MCP
> server exits at startup and the client shows the tools as unavailable. (`NSA_MCP_AUTH_DISABLE=1`
> exists as an escape hatch and warns on stderr; it is not the recommended path.)
**Cursor / Windsurf / VS Code:**
Add to your MCP configuration with the same command/args pattern.
---
## Editions & Licensing
| Edition | Price | Key Features |
|---------|-------|-------------|
| **Community** | Free / MIT | 27 plugins (service probes + host/network discovery + intelligence/meta), basic AI, CTEM, SARIF, scan history |
| **Pro** | $49/mo | + CVE matching, risk scoring, analysis agents, the single-plugin and CVE-lookup MCP tools (`probe_service`, `get_vulnerabilities`) |
| **Enterprise** | $2k+/yr | + 29 enterprise plugins (1020-1230 range) — 28 cloud-substrate auditors covering AWS / GCP / Azure plus `1023 Zero Trust Assessment` (which declares no cloud provider and scores zero-trust posture from a NETWORK-host scan — it never runs on a cloud pass, and selecting it by id on its own does not run it either) — against every shipped framework (SOC 2 10 covered + 4 partial; HIPAA; NIST CSF 2.0; PCI DSS v4.0.1; ISO/IEC 27001:2022; CIS Controls v8; GDPR Art. 32 infrastructure substrate; NIST SP 800-171 Rev 2 evidence substrate for CMMC Level 2 preparation); SOC 2 evidence-pack generation; SHA-256 chain-of-custody attestations (RFC 3161 timestamping is opt-in via the `NSAUDITOR_TSA_URL` environment variable — there is no CLI flag and no default, it makes an outbound call to the Time-Stamp Authority you name, and it was exercised against a live Time-Stamp Authority on BOTH delivery vehicles — the npm path and, from inside the pushed `:0.33.0` Marketplace image, on 2026-08-08; retained images `:0.32.11` and earlier carry no `openssl`); air-gapped operation (offline licensing + offline CVE matching under `NSAUDITOR_OFFLINE_ONLY=1`) |
→ [Pricing](https://www.nsauditor.com/ai/pricing/)
---
## Error Handling
| Error | Cause | Resolution |
|-------|-------|-----------|
| SSRF block (`Scanning loopback, link-local, or metadata addresses is not allowed via MCP`) | The target, or any address its name resolves to, is loopback, unspecified (`0.x`, `::`), link-local or a cloud-metadata address — refused with or without `NSA_ALLOW_ALL_HOSTS` — or is private (RFC 1918, CGNAT `100.64/10`, IPv6 `fc00::/7`) while `NSA_ALLOW_ALL_HOSTS` is not `1`, `true`, `yes` or `on`. The message names loopback for a private target too | Private target: set `NSA_ALLOW_ALL_HOSTS=1` in the MCP server env, then fully quit and relaunch the client. A loopback, link-local or metadata target — written so, or reached through what a name resolves to — is refused over MCP with or without it; scan it from the CLI, where `NSA_ALLOW_ALL_HOSTS=1` lifts the whole guard |
| Unresolvable host (`Host could not be resolved via MCP: <host>`) | The name does not resolve — with or without `NSA_ALLOW_ALL_HOSTS` — or the string is not one host: a CIDR or URL-shaped string (`10.0.0.0/8`, `host:80`) is refused this way | Check the spelling; pass one host name or IP address; scan a range from the CLI (`--host <CIDR>`) |
| License gate (`🔒`) | Pro/Enterprise tool on CE | Upgrade license or use CE alternative |
| Plugin timeout (`timeout` in `manifest`) | Network unreachable / slow target | Not measured, never clean. Raise `PLUGIN_TIMEOUT_MS` in the server env, or scan from the CLI, where a plugin that declares its own budget gets it |
| No DNS banner | Provider blocks CHAOS/TXT queries | Expected; not all DNS servers expose version |
| CPE format error | Malformed CPE string | Use `cpe:2.3:a:vendor:product:version:*:*:*:*:*:*:*` |
| No services found | Host down or heavily firewalled | Try `NSA_VERBOSE=true` to debug; check connectivity |
| AI analysis failed | Bad API key or provider down | Check `AI_PROVIDER` and API key env vars |
---
## MITRE ATT&CK Mapping
The CLI's report tags what it finds with MITRE techniques (the `scan_host` tool does not return them):
| Finding Type | Technique | ID |
|-------------|-----------|-----|
| SSH vulnerability | Remote Services: SSH | T1021.004 |
| SMB vulnerability | Remote Services: SMB | T1021.002 |
| FTP anonymous login | Valid Accounts | T1078 |
| DNS zone transfer | Gather Victim Network Info | T1590.002 |
| SNMP default community | Network Sniffing | T1040 |
| TLS weakness | Adversary-in-the-Middle | T1557 |
| Debug/stack trace exposure | Gather Victim Host Info | T1592 |
| Weak authentication | Brute Force | T1110 |
---
## Output Formats
| File | Format | Purpose |
|------|--------|---------|
| `scan_conclusion_raw.json` | JSON | Full unredacted scan data (admin) |
| `scan_conclusion_raw.html` | HTML | Admin dashboard with filters |
| `scan_response_ai_payload.json` | JSON | Redacted payload sent to AI |
| `scan_response_ai.html` | HTML | Styled report with CVE links, severity badges |
| `scan_response_ai.txt` | Markdown | AI vulnerability assessment (text) |
| SARIF | JSON | CI/CD integration (GitHub Advanced Security, Azure DevOps) |
| CSV | CSV | Tabular export of findings |
| JSONL | JSONL | Scan history for CTEM delta analysis |