@sagentix-inc/evidence-collector 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +167 -6
  2. package/THIRD_PARTY_NOTICES.md +14 -14
  3. package/apps/sos-app/src/data/checks/aws.json +2 -2
  4. package/apps/sos-app/src/data/checks/checkov.json +59 -59
  5. package/apps/sos-app/src/data/checks/configmgr.json +45 -0
  6. package/apps/sos-app/src/data/checks/endpoint_central.json +45 -0
  7. package/apps/sos-app/src/data/checks/google_endpoints.json +39 -0
  8. package/apps/sos-app/src/data/checks/ivanti_mdm.json +51 -0
  9. package/apps/sos-app/src/data/checks/kubescape.json +1 -1
  10. package/apps/sos-app/src/data/checks/ninjaone.json +39 -0
  11. package/apps/sos-app/src/data/checks/prowler_aws.json +478 -464
  12. package/apps/sos-app/src/data/checks/prowler_azure.json +137 -137
  13. package/apps/sos-app/src/data/checks/prowler_cloudflare.json +2 -2
  14. package/apps/sos-app/src/data/checks/prowler_gcp.json +99 -92
  15. package/apps/sos-app/src/data/checks/prowler_github.json +2 -2
  16. package/apps/sos-app/src/data/checks/prowler_googleworkspace.json +2 -2
  17. package/apps/sos-app/src/data/checks/prowler_kubernetes.json +75 -75
  18. package/apps/sos-app/src/data/checks/prowler_m365.json +69 -69
  19. package/apps/sos-app/src/data/checks/prowler_okta.json +2 -2
  20. package/apps/sos-app/src/data/checks/workspace_one.json +45 -0
  21. package/apps/sos-app/src/data/fedramp_rules.json +1 -1
  22. package/collector/package.json +1 -1
  23. package/collector/src/agent.js +312 -0
  24. package/collector/src/channel.js +220 -0
  25. package/collector/src/cli.js +160 -89
  26. package/collector/src/release_keys.js +9 -0
  27. package/collector/src/report.js +300 -0
  28. package/collector/src/sources/aws/checks.js +4 -1
  29. package/collector/src/sources/azure/checks.js +4 -2
  30. package/collector/src/sources/configmgr.js +286 -0
  31. package/collector/src/sources/device_sources.js +11 -1
  32. package/collector/src/sources/endpoint_central.js +345 -0
  33. package/collector/src/sources/gcp/api.js +38 -2
  34. package/collector/src/sources/gcp.js +6 -4
  35. package/collector/src/sources/google_endpoints.js +171 -0
  36. package/collector/src/sources/ivanti_mdm.js +276 -0
  37. package/collector/src/sources/ninjaone.js +271 -0
  38. package/collector/src/sources/oauth.js +4 -2
  39. package/collector/src/sources/prowler.js +1 -1
  40. package/collector/src/sources/workspace_one.js +226 -0
  41. package/package.json +1 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The collector runs in the customer's own CI. It reads the customer's systems with read-only credentials, runs checks, signs what it saw with an Ed25519 key and pushes the result to sos-app at `POST /api/collect`. sos-app maps each check to NIST SP 800-53 Rev. 5 controls and FedRAMP 20x KSIs itself. The collector reads the check list in `apps/sos-app/src/data/checks/<source>.json`, so it sends only check ids sos-app knows.
4
4
 
5
- Version 0.2.1 has 31 sources and 2,776 checks in its catalogue (counted from `src/sources/index.ts` and the catalogues on 2026-10-27; 2,768 on 2026-10-20, after the Windows, JumpCloud, Iru and Defender for Endpoint sources). Most of them come from engines the customer already runs (Prowler 824, DISA STIG profiles 1,347, ScubaGoggles and ScubaGear 267); the rest are the collector's own reads of Cloudflare, the clouds, identity providers, GitHub, device managers, the Mac it runs on and, since 2026-10-18, websites by URL. The first ten sources' 134 checks alone name 80 of the 610 SP 800-53 Rev. 5 controls and enhancements sos-app tracks (58 distinct base controls, counted 2026-10-20); policies cover 20 more, and the rest are procedures a person describes.
5
+ Version 0.3.0 has 31 sources and 2,776 checks in its catalogue (counted from `src/sources/index.ts` and the catalogues on 2026-10-27, unchanged from 0.2.1; 2,768 on 2026-10-20, after the Windows, JumpCloud, Iru and Defender for Endpoint sources). Most of them come from engines the customer already runs (Prowler 824, DISA STIG profiles 1,347, ScubaGoggles and ScubaGear 267); the rest are the collector's own reads of Cloudflare, the clouds, identity providers, GitHub, device managers, the Mac it runs on and, since 2026-10-18, websites by URL. The first ten sources' 134 checks alone name 80 of the 610 SP 800-53 Rev. 5 controls and enhancements sos-app tracks (58 distinct base controls, counted 2026-10-20); policies cover 20 more, and the rest are procedures a person describes.
6
6
 
7
7
  ## Data flow
8
8
 
@@ -68,7 +68,7 @@ Either apply `iac/cloudflare/evidence-role` in your own account (it creates an a
68
68
  | Account | Zero Trust Read | Gateway rules, device posture rules |
69
69
  | Account | Workers R2 Storage Read | r2.dev public access per bucket |
70
70
 
71
- The names are Cloudflare's API names, matched per endpoint against `x-api-token-group` in Cloudflare's OpenAPI document (github.com/cloudflare/api-schemas). Two are not fully confirmed. Zero Trust Read: the OpenAPI names no group for `GET /accounts/{id}/gateway/rules` or `/devices/posture`. Account API Tokens Read: the OpenAPI names it, but the permissions reference page does not list it. If a group is missing, its checks come back `skipped`, never `pass`.
71
+ The names are Cloudflare's API names, matched per endpoint against `x-api-token-group` in Cloudflare's OpenAPI document (github.com/cloudflare/api-schemas). Two are not fully confirmed. Zero Trust Read: the OpenAPI names no group for `GET /accounts/{id}/gateway/rules` or `/devices/posture`. Account API Tokens Read: the permissions reference page does not list it, but the API reference page for `GET /accounts/{id}/tokens` names it under Accepted Permissions (read 2026-10-29), as the OpenAPI does. If a group is missing, its checks come back `skipped`, never `pass`.
72
72
 
73
73
  Store the token as the CI secret `CLOUDFLARE_API_TOKEN_READONLY`.
74
74
 
@@ -89,7 +89,28 @@ node src/cli.ts run --source cloudflare \
89
89
  - `--out <file>`: also write `{endpoint, keyId, publicKey, signature, body}` to a file. `body` is the exact string sent.
90
90
  - `--catalog <dir>`: the check catalogue (default `../apps/sos-app/src/data/checks`). A check id not in it is dropped with a warning, because sos-app would refuse the whole push.
91
91
 
92
- Exit codes: 0 sent (or dry run); 1 error (unreadable key, network failure, a failed engine run); 2 usage; 3 nothing to send, because every check was skipped; 4 sos-app refused the push, with its message printed (401 bad signature or revoked key, 409 replay, 400 unknown check or stale `issuedAt`).
92
+ Exit codes: 0 sent (or dry run); 1 error (unreadable key, network failure, or a source that failed: its checks went as `skipped` and everything else was still pushed and reported); 2 usage; 3 nothing to send, because every check was skipped; 4 sos-app refused the push, with its message printed (401 bad signature or revoked key, 409 replay, 400 unknown check or stale `issuedAt`); 75 `agent` only, after it wrote a signed update (below). `run` never exits 75.
93
+
94
+ ### Run report, check-in and resident mode (0.3.0)
95
+
96
+ Since 0.3.0 the collector keeps a channel to sos-app beside the push, defined in `apps/sos-app/docs/COLLECTOR_CHANNEL.md`. Every message is signed with the collector key, exactly as a push is, and every connection still starts on the client's side.
97
+
98
+ **The run never stops.** Each source runs inside a guard. A source that throws gives one `skipped` result per check it declares, with the detail `collector error in <source>: <message>`, and the run goes on to the next source, the push and the report. The run then exits 1, so a scheduler still shows the failure.
99
+
100
+ **Check-in.** Before any source reads, the run sends `POST /api/collect/checkin` (version, commit, runner, mode, operating system, Node version). sos-app answers `none`, `notify`, `approval-needed` or `update`. An `update` is taken only when its target verifies against a Sagentix release public key in `src/release_keys.ts` (Ed25519 over `sagentix-collector-release/1\n<version>\n<commit>\n<image or empty>\n<date>`) and names a 40-hex commit; then a server, Mac or Windows runner writes `sagentix-collector.ref` beside the config (line 1 the commit, line 2 the version), and a Docker runner writes `sagentix-collector.image` (the runner image with its `@sha256:` digest, only `ghcr.io/sagentix-advisors-inc/sagentix-collector-runner`). Both are mode 0600. The generated script reads the file on its next start; the current run goes on with the code it started with. A GitHub Actions runner never writes one (its workflow file pins the commit). A check-in that fails (network, or a 404 from an sos-app older than 0.3.0), a bad signature or a malformed target writes nothing and never stops the run; it is a warning in the log and the report. A dry run checks in when it has a key, and never writes either file.
101
+
102
+ **Run report.** After the pushes, every run writes `sagentix-run-report.json` (mode 0600) beside the config, or in the working directory without one, and sends those exact bytes to `POST /api/collect/report`. It holds, per source, the time taken, the pass, fail and skipped counts, any error with its stack, and up to 50 warnings; the shape of every outbound call (method, host, path with ids replaced by `:id`, status, count, total time; never a query string, a header or a body); each push's status and run id; and every line the run wrote to stderr. Before anything is written, the values of environment variables named `*_TOKEN`, `*_SECRET`, `*_KEY`, `*_PASSWORD`, `*_KEY_JSON` and `SAGENTIX_COLLECTOR_KEY`, JSON Web Tokens, `Bearer`/`Basic` values, PEM blocks and AWS access key ids become `<secret>`, and email addresses become `<email>`. The report stays under 512 KiB (1,000 log lines of at most 500 characters; the log is trimmed first, then calls, then stacks, and a last log line says so). If the send fails, the report is kept as `sagentix-pending-report.json` and sent first by the next run with `"late": true`; a 400 or 413 answer drops it instead, since it would be refused again. A lost report never changes the exit code. A dry run writes the report and sends nothing.
103
+
104
+ The runner in both messages comes from `SAGENTIX_RUNNER` (the generated files set it), else `github` under GitHub Actions, `mac` on macOS, `windows` on Windows, `docker` when `/.dockerenv` exists, else `server`. The commit is the collector checkout's own (`git rev-parse HEAD`, asked only when `src/cli.ts` is tracked by that repository), or `SAGENTIX_COLLECTOR_COMMIT` in the runner image, else `unknown`.
105
+
106
+ **Resident mode.** `node src/cli.ts agent --config sagentix-collector.config.json` keeps one outbound WebSocket open to `wss://<sos-app>/api/collect/connect`, authenticated in the query string (`key`, `ts`, and `sig`, the base64url signature over `sagentix-connect/1\n<key id>\n<ts>`). It says `hello`, answers `ping` with `pong`, pings every 50 seconds, and reconnects after 1 second, doubling to 5 minutes. On `{"type":"run","id":…}` it starts the same `run --config <file>` as a child process (same Node, same flags), never two at once, and reports `started`, then `done` or `failed` with the exit code; on `{"type":"checkin"}` it checks in. It also checks in on every connect and every 6 hours. Nothing else is accepted: the channel cannot carry a command, a file or a configuration, and anything else received is logged and ignored. When a check-in writes a signed update, the agent exits 75 once no run is going (a run in progress finishes first): Docker's wrapper loop restarts the container on the new digest, and a LaunchAgent (`KeepAlive`) or systemd unit restarts the script, which checks out the ref on start. SIGINT or SIGTERM stops it with 0.
107
+
108
+ | File beside the config | Written by | Read by |
109
+ | --- | --- | --- |
110
+ | `sagentix-run-report.json` | every run | you; the same bytes go to sos-app |
111
+ | `sagentix-pending-report.json` | a run whose report send failed | the next run, which sends it first and removes it |
112
+ | `sagentix-collector.ref` | a check-in with a signed update (server, Mac, Windows) | the generated script, on its next start |
113
+ | `sagentix-collector.image` | a check-in with a signed update (Docker) | the Docker wrapper, on its next start |
93
114
 
94
115
  ### What each result says about its read (format 3)
95
116
 
@@ -542,7 +563,7 @@ The eight organization checks report once per organization (resource `github org
542
563
 
543
564
  ## Device sources
544
565
 
545
- Four sources read what laptops look like: `fleet` (the open-source Fleet server in front of fleetd and osquery), `intune`, `jamf` and `kandji` (Iru Endpoint Management, formerly Kandji). Sagentix approved this on 2026-10-15, with Fleet as the device agent and Intune and Jamf Pro as the second path for customers who already run one of them; `kandji` joined on 2026-10-20 for small Mac-based firms. They use the same GET-only client as the identity sources (`src/sources/readonly_http.ts`), and their shared helpers live in `src/sources/device_common.ts`. A fifth, `defender_endpoint` (2026-10-20), reads the EDR and antivirus side of the same laptops from Microsoft Defender for Endpoint.
566
+ Four sources read what laptops look like: `fleet` (the open-source Fleet server in front of fleetd and osquery), `intune`, `jamf` and `kandji` (Iru Endpoint Management, formerly Kandji). Sagentix approved this on 2026-10-15, with Fleet as the device agent and Intune and Jamf Pro as the second path for customers who already run one of them; `kandji` joined on 2026-10-20 for small Mac-based firms. They use the same GET-only client as the identity sources (`src/sources/readonly_http.ts`), and their shared helpers live in `src/sources/device_common.ts`. A fifth, `defender_endpoint` (2026-10-20), reads the EDR and antivirus side of the same laptops from Microsoft Defender for Endpoint. Six more joined on 2026-10-28 for customers who already run another endpoint manager: `workspace_one` (Omnissa Workspace ONE UEM), `configmgr` (Microsoft Configuration Manager, read from a domain-joined Windows host as a service account, with no stored secret), `google_endpoints` (Google endpoint management, through the `google_workspace` service-account key), `ivanti_mdm` (Ivanti Neurons for MDM), `ninjaone` (NinjaOne) and `endpoint_central` (ManageEngine Endpoint Central, cloud edition only).
546
567
 
547
568
  Each device check reports once per device. A device is named by the vendor's id and its hardware serial (`fleet host 12 serial C02…`, `intune device <id> serial …`, `jamf computer 7 serial …`); Defender's machine entity has no serial, so its devices are `defender device <machine id>`. Hostnames, computer names, display names, user principal names, email addresses and usernames are never put in a result; the Intune device list asks Graph for seven fields with `$select`, and the Jamf call never requests the `USER_AND_LOCATION` section. When a device has not reported a value, the result is `skipped`. A 401 or 403 also gives `skipped` with the HTTP status in the detail, so an expired token shows as a collector error rather than a fleet of failing laptops.
548
569
 
@@ -660,6 +681,145 @@ Lists are read 10,000 rows at a time (the documented `$top` ceiling), following
660
681
 
661
682
  Real-time protection has no check. No documented field of these APIs reports it (`avMode` is the antivirus mode, not the real-time setting), and the collector does not guess from undocumented columns. The antivirus checks report once per machine in the health export, which covers Windows, macOS and Linux (`osKind`).
662
683
 
684
+ ### Omnissa Workspace ONE UEM (`workspace_one`)
685
+
686
+ Workspace ONE UEM is Omnissa's product since VMware sold its end-user computing business; the console and API hosts still use `awmdm.com`. Every endpoint, field and permission below was read on 2026-10-28 in the release 2607 OpenAPI documents of the Omnissa Developer Portal (`mdmv3.json`, `mdmv1.json`), in "Using UEM Functionality With a REST API" (docs.omnissa.com, page dated 2026-08-25) and in the portal's Bruno getting-started page; the header of `src/sources/workspace_one.ts` quotes them.
687
+
688
+ **Create:** an admin role holding only the REST permission **REST API Devices Read** (REST > Devices, marked Read Only), then an OAuth client with that role under Groups & Settings > Configurations > OAuth Client Management. The secret is shown once. OAuth is supported "only in SaaS environments", so an on-premises console is not supported.
689
+
690
+ **Env:** `WS1_API_URL` (https only, the REST API URL from Groups & Settings > All Settings > System > Advanced > API > REST API, e.g. `https://as1234.awmdm.com`), `WS1_TOKEN_URL` (one of `https://na.uemauth.workspaceone.com/connect/token` for the United States and Canada, `https://emea.uemauth.workspaceone.com/connect/token` for the United Kingdom and Germany, `https://apac.uemauth.workspaceone.com/connect/token` for India, Japan, Singapore, Australia and Hong Kong; any other host is refused), `WS1_CLIENT_ID`, `WS1_CLIENT_SECRET`.
691
+
692
+ **Options:** `--workspace-one-min-os <platform>=<version>` (repeatable: `macos`, `windows`, `ios`, `android`, `chromeos`, `linux` or a `device_type` such as `APPLE_OS_X`; without it `os_minimum` is skipped), `--workspace-one-seen-days` (default 14), `--workspace-one-max-devices` (default 2000).
693
+
694
+ The device list is `GET /api/mdm/devices/search` on MDM API V3 (`Accept: application/json;version=3`), 500 devices a page from page 0 until `total`. Encryption is one `GET /api/mdm/devices/{uuid}/security/encryption-status` call per device (MDM API V1); a 401 or 403 on it stops the calls and skips the check. No rate limit is stated on the pages read; a 429 is retried after its `Retry-After`. Devices that are retired, unenrolled, being wiped or blacklisted are skipped. Firewall and screen lock are left out: no firewall field is documented, and the only per-device passcode fields come from `GET /devices/{id}/security`, whose answer also carries `PersonalRecoveryKey`, `StaticRecoveryKey` and `UnlockPin`, so the collector never asks for it.
695
+
696
+ | Check (`workspace_one.`…) | What passes |
697
+ | --- | --- |
698
+ | `disk_encryption` | `encryption_status` is true |
699
+ | `os_minimum` | `operating_system` at or above `--workspace-one-min-os` for the device type |
700
+ | `last_seen` | `last_seen` within `--workspace-one-seen-days` |
701
+ | `enrolled` | `enrollment_status` is `ENROLLED`; `PENDING_AGENT`, `REGISTERED` and the other not-yet states fail |
702
+ | `compliant` | `compliance_status` is `COMPLIANT`; `NOT_COMPLIANT`, `BLOCKED` and `QUARANTINED` fail; pending and unknown are skipped |
703
+ | `compromised` | `compromised_status` is false |
704
+
705
+ ### Microsoft Configuration Manager (`configmgr`)
706
+
707
+ Configuration Manager (ConfigMgr, once MECM and SCCM) is on premises, so this source reads the site's **administration service**, the SMS Provider's OData REST API at `https://<SMS Provider FQDN>/AdminService/wmi/<Class>`, from inside the network. Every route, class and property below was read on 2026-10-28 on learn.microsoft.com (administration service overview, set-up, usage and FAQ; the class references; role-based administration); the dates are in the header of `src/sources/configmgr.ts`.
708
+
709
+ **Create:** a domain service account, added under Administration > Security > Administrative Users > Add User or Group with the built-in security role **Read-only Analyst** ("Grants permissions to view all Configuration Manager objects") and the All Systems collection. Microsoft's pages allow only domain users or groups there.
710
+
711
+ **Runner:** Windows only, and it needs network reach. The administration service takes Windows authentication, which Node's `fetch` cannot send, so each GET is Windows PowerShell 5.1 `Invoke-WebRequest -Method Get -UseDefaultCredentials` (the call Microsoft's usage page documents, with the status code kept), run as the service account itself: a scheduled task on a domain-joined Windows host that reaches the SMS Provider on port 443. That host must trust the provider's certificate (the site's self-signed one by default, or a PKI certificate). No secret is stored. The cloud management gateway route is not supported: it needs a Microsoft Entra token for a user account, and no page documents an app-only token for it.
712
+
713
+ **Env:** `CONFIGMGR_PROVIDER`, the SMS Provider's FQDN (e.g. `cm01.corp.example.com`). Requests go to that host under `/AdminService/` only, with redirects off.
714
+
715
+ **Options:** `--configmgr-seen-days` (default 14), `--configmgr-signature-days` (default 7), `--configmgr-patch-days` (default 30), `--configmgr-max-devices` (default 10000).
716
+
717
+ Four GETs: `SMS_CombinedDeviceResources`, `SMS_SoftwareUpdate?$filter=IsDeployed eq true`, `SMS_UpdateComplianceStatus?$filter=Status eq 2 or Status eq 0` and `SMS_G_System_CI_ComplianceState`. `@odata.nextLink` is followed when the service gives one; no page documents its paging or rate limits. Unknown and obsolete device records are not counted. Disk encryption, firewall, OS version and screen lock are left out: no documented server class carries them (BitLocker reports read hardware inventory classes the reference does not document, and the device classes give only a free-form OS string). A configuration baseline that tests them shows in `baselines_compliant`.
718
+
719
+ | Check (`configmgr.`…) | What passes |
720
+ | --- | --- |
721
+ | `client_installed` | `IsClient` is true; a discovered device without a client fails and skips the rest |
722
+ | `last_seen` | `LastActiveTime` (client health), else `LastPolicyRequest`, within `--configmgr-seen-days` |
723
+ | `antimalware_enabled` | `EPEnabled` and `EPAntivirusEnabled` are true; skipped when Configuration Manager does not manage the antimalware |
724
+ | `antimalware_signatures_current` | `EPAntivirusSignatureLastUpdateDateTime` within `--configmgr-signature-days` |
725
+ | `updates_installed` | No deployed, unexpired update published (`DatePosted`) more than `--configmgr-patch-days` ago has `Status` 2 (required); `Status` 0 (unknown) gives skipped |
726
+ | `baselines_compliant` | No configuration item is `ComplianceState` 2 (Non-Compliant) or 4 (Error), and one is 1 (Compliant) |
727
+
728
+ A client that never ran an update scan has no row in `SMS_UpdateComplianceStatus` and passes `updates_installed`; read `last_seen` beside it. A device is named by its `ResourceID` and serial; `Name`, `UserName` and `UserDomainName` come back and never reach a result.
729
+
730
+ ### Google endpoint management (`google_endpoints`)
731
+
732
+ The devices a Google Workspace or Cloud Identity customer manages: Windows, macOS, Linux and ChromeOS computers that report through Endpoint Verification, and Android and iOS phones. The source reads the Cloud Identity Devices API, `GET https://cloudidentity.googleapis.com/v1/devices`, with GET only. Every parameter, field and scope below was read on 2026-10-28 in the devices.list reference (last updated 2025-12-16), the cloudidentity v1 discovery document (revision 20261026), "Set up the Devices API" and Endpoint Verification's "Device attributes" page (both last updated 2026-10-26).
733
+
734
+ **Create:** nothing new beyond `google_workspace`'s service account. Google says "Authenticating directly to the Devices API using a service account isn't supported", so it goes through domain-wide delegation: enable the Cloud Identity API in the service account's project, then in the Admin console (Security > Access and data control > API controls > Manage Domain Wide Delegation, as a super administrator) edit the client and add `https://www.googleapis.com/auth/cloud-identity.devices.readonly`. The token the collector asks for holds that scope only. `google_workspace`'s browser sign-in does not ask for it, so this source needs the service-account key.
735
+
736
+ **Env:** the `google_workspace` names: `GOOGLE_SA_KEY_FILE`, `GOOGLE_ADMIN_EMAIL` (the admin the service account acts as) and `GOOGLE_CUSTOMER_ID` (default `my_customer`).
737
+
738
+ **Options:** `--google-endpoints-min-os <deviceType>=<version>` (repeatable: `mac_os`, `windows`, `linux`, `chrome_os`, `android`, `ios`, or `macos`/`chromeos`; without it `os_minimum` is skipped), `--google-endpoints-sync-days` (default 14), `--google-endpoints-max-devices` (default 5000).
739
+
740
+ The list asks for `view=USER_ASSIGNED_DEVICES` ("all devices with at least one user registered on the device") at the documented maximum of 100 a page, following `nextPageToken`. No Devices API quota page was found; a 429 or a 403 `rateLimitExceeded` is retried. Screen lock is left out: Endpoint Verification collects `is_secured_with_screenlock` for access levels, but the Devices API's Device resource has no such field. The Admin console privilege a delegated (non-super) admin needs for this call is not named on the pages read.
741
+
742
+ | Check (`google_endpoints.`…) | What passes |
743
+ | --- | --- |
744
+ | `disk_encryption` | `encryptionState` is `ENCRYPTED`; `NOT_ENCRYPTED` fails; `UNSUPPORTED_BY_DEVICE` is skipped |
745
+ | `firewall` | Endpoint Verification's `additionalSignals.is_os_native_firewall_enabled` is true; devices without Endpoint Verification are skipped |
746
+ | `os_minimum` | `osVersion` at or above `--google-endpoints-min-os` for the `deviceType` |
747
+ | `last_sync` | `lastSyncTime` within `--google-endpoints-sync-days` |
748
+ | `compromised` | `compromisedState` is `UNCOMPROMISED` |
749
+
750
+ A device is named by its id (from `name`, `devices/{id}`) and serial. Host names, other accounts on the device, MAC addresses, IMEI and browser profiles never reach a result.
751
+
752
+ ### Ivanti Neurons for MDM (formerly MobileIron Cloud)
753
+
754
+ Ivanti renamed MobileIron Cloud to Ivanti Neurons for MDM; its API keeps the old names in places (the Basics page is still `MobileIron Cloud API Basics.htm`, and device spaces are `dmPartitionId`). Every endpoint, parameter and field below was read on 2026-10-28 in the Customer Integration API Guide (https://help.ivanti.com/mi/help/en_us/CLD/8x/api/default.htm, revision history last entry 2026-10-15), its OpenAPI file (`test.json`, version NA2) and the Administrator Guide 128; the pages are cited in the header of `src/sources/ivanti_mdm.ts`.
755
+
756
+ **Create:** under Users → Users, **+Add** → **Single User**, a dedicated user with a password, then **Actions** → **Assign Roles** → only **Device Read Only** (space-specific) for the device space to read. The guide says the device search and Get Device Details need "Tenant Admin credentials", its name for the API role behind every tenant-level call, and names Device Read Only only for other device GETs; whether Device Read Only alone is accepted for these two is not stated. The collector sends GET only, so a role that falls short gives HTTP 403 and `skipped`, never a write. Basic authentication does not work when the admin portal requires a password and PIN; token-based (OIDC client) authentication is not supported yet, because its token request sends the client id and secret in a Basic header and `oauth.ts` sends them in the form body.
757
+
758
+ **Env:** `IVANTI_MDM_URL` (the tenant's admin portal origin, https only), `IVANTI_MDM_USERNAME`, `IVANTI_MDM_PASSWORD`.
759
+
760
+ **Options:** `--ivanti-mdm-space-id` (default: `defaultDmPartitionId` from `GET /api/v1/metadata/tenant`), `--ivanti-mdm-min-os <platformType>=<version>` (repeatable: `osx=15.0` or `macos=15.0`, `ios=18.0`, `android=14`; without it `os_minimum` is skipped), `--ivanti-mdm-checkin-days` (default 14), `--ivanti-mdm-max-devices` (default 5000).
761
+
762
+ The device search, `GET /api/v1/device?dmPartitionId=…&rows=500`, is sorted by `id` and paged with `searchAfterValues[0]=<last id>`, the method the guide gives for large tenants; 500 rows is its documented ceiling, and paging stops at `totalCount`. Each Mac costs one `GET /api/v1/device/{id}` for FileVault, firewall and SIP; a 401 or 403 there stops those calls. No numeric rate limit is published; the guide tells integrations to retry 429 and 503, which the shared client does. Retired, wiped and pending devices (`registrationState` other than `ACTIVE`) are counted in the detail, not judged. A GDPR profile on the API user obfuscates serial numbers in the answers.
763
+
764
+ | Check (`ivanti_mdm.`…) | What passes |
765
+ | --- | --- |
766
+ | `device_compliant` | `complianceState` is true |
767
+ | `disk_encryption` | Mac: `osxFdeEnabled` is true; other devices reporting `bitLockerEncryptionStatus`: `FULLY_ENCRYPTED` (`UNKNOWN` is skipped); iOS and Android are not judged |
768
+ | `os_minimum` | `platformVersion` at or above the `--ivanti-mdm-min-os` floor for its `platformType` |
769
+ | `last_checkin` | `lastCheckin` within `--ivanti-mdm-checkin-days` |
770
+ | `passcode_compliant` | iOS and Android: `passcodeCompliantWithProfiles` (else `passcodeCompliant`) is true |
771
+ | `firewall` | Mac: `osxFirewallSettings.firewallEnabled` is true |
772
+ | `system_integrity` | Mac: `osxSystemIntegrityProtectionEnabled` is true |
773
+
774
+ Left out: the Windows firewall (`firewallStatus` has no documented meaning), pending OS updates (`availableOSUpdates` is an undescribed string list) and the idle time before lock (no field). The `platformType` a Windows 10 or 11 desktop reports is not in the documented enum, so BitLocker is read wherever a device reports `bitLockerEncryptionStatus`. A device is named by its Ivanti id and serial; names, email addresses, phone numbers, IMEI, location and IP addresses are never put in a result.
775
+
776
+ ### NinjaOne
777
+
778
+ The collector reads the NinjaOne Public API v2 with an API Services (machine-to-machine) client app holding one scope, Monitoring, which NinjaOne's registration page describes as "read-only access to monitoring data and organization structure". Endpoints, fields and scopes were read on 2026-10-28 in the Public API reference (https://app.ninjarmm.com/apidocs-beta/, `NinjaRMM-API-v2.yaml` version 2.0.9-draft) and its articles on machine-to-machine apps and the client-credentials flow. The same reference answered that day on the five instances: `app`, `us2`, `eu`, `ca` and `oc` `.ninjarmm.com`.
779
+
780
+ **Create:** under Configuration → Apps → API → Client App IDs, click **Add**, choose **API Services (machine-to-machine)**, tick only the **Monitoring** scope and keep only the **Client Credentials** grant type. **Management** and **Control** change devices and open remote access; the collector never asks for them. The token request is `POST /ws/oauth/token` with `scope=monitoring`, the only POST.
781
+
782
+ **Env:** `NINJAONE_URL` (one of the five instances above, https only; any other host is refused), `NINJAONE_CLIENT_ID`, `NINJAONE_CLIENT_SECRET`.
783
+
784
+ **Options:** `--ninjaone-min-os <platform>=<version>` (repeatable, platform `windows`, `macos` or `linux`, e.g. `windows=10.0.26100`; without it `os_minimum` is skipped once), `--ninjaone-contact-days` (default 14), `--ninjaone-max-pending-patches` (default 0), `--ninjaone-max-devices` (default 5000), `--ninjaone-include-servers`.
785
+
786
+ The source calls `GET /v2/devices` (paged with `after`, the last node id) and four query reports, `computer-systems`, `volumes?include=bl`, `operating-systems` and `device-health`, so a fleet of any size costs a handful of calls. The reports page by a named server-side cursor; the spec calls the parameter only "Cursor name", so the collector passes back the last answer's `cursor.name` and stops at an empty page. Workstations are judged (`WINDOWS_WORKSTATION`, `MAC`, `LINUX_WORKSTATION`); servers only with `--ninjaone-include-servers`, network and cloud monitors never. The spec's global security block names `monitoring`, `management` and `control` and no operation used here overrides it, so that Monitoring alone answers rests on the registration page's wording; a 401 or 403 gives `skipped`. The spec does not state the unit of `lastContact`: below 1e11 it is read as epoch seconds, above as milliseconds. No endpoint reports the firewall, the screen lock or FileVault, so those checks do not exist.
787
+
788
+ | Check (`ninjaone.`…) | What passes |
789
+ | --- | --- |
790
+ | `disk_encryption` | On a Windows device, the `C:` volume's `bitLockerStatus` has `conversionStatus` `FULLY_ENCRYPTED` and `protectionStatus` `PROTECTED`; Macs and Linux hosts are skipped |
791
+ | `os_minimum` | Windows `10.0.<buildNumber>`, or the version in the OS name elsewhere, at or above `--ninjaone-min-os` |
792
+ | `os_patches` | `failedOSPatchesCount` is 0 and `pendingOSPatchesCount` at most `--ninjaone-max-pending-patches` |
793
+ | `last_contact` | `lastContact` within `--ninjaone-contact-days` |
794
+ | `approved` | `approvalStatus` is `APPROVED`; `PENDING` or `STAGED` fails, `DECOMMISSIONED` is skipped |
795
+
796
+ A device is `ninjaone device <id> serial <serial>`, the serial from the computer-systems report. Display, system, DNS and NetBIOS names, the assigned owner, `userData`, logged-on users and tags are never put in a result.
797
+
798
+ ### ManageEngine Endpoint Central (cloud)
799
+
800
+ The collector reads the Endpoint Central cloud API with a Zoho self client whose refresh token was generated for two scopes, both with the operation READ: `DesktopCentralCloud.Inventory.READ` and `DesktopCentralCloud.PatchMgmt.READ`. Every endpoint, scope, `Accept` type, field and request limit below was read on 2026-10-28 in the cloud API guide (https://www.manageengine.com/products/desktop-central/help/api/cloud/), and role permissions in `help/access-management/user_role_and_permission.html`, same date.
801
+
802
+ **Create:** first a user with the predefined **Guest** role, which has Read Access to Inventory and Patch Management; the token acts as the user who makes it, and a missing role answers 401 `UAC_UNAUTHORIZED`. Signed in as that user, open the **Self Client** in the Zoho API console of the organization's data centre, generate a code for exactly the two scopes above under **Generate Code**, and exchange it once at `POST {accounts}/oauth/v2/token` (`grant_type=authorization_code`) for a refresh token that does not expire. The collector trades it for a one-hour access token (`grant_type=refresh_token`), the only POST, with the parameters in the query string as the guide shows them.
803
+
804
+ **Env:** `ENDPOINT_CENTRAL_URL` (https only, one of the nine data centres on the guide's "Multiple data centres" page: `https://endpointcentral.manageengine.<com|eu|in|com.au|cn|jp|ca|uk|sa>`), `ENDPOINT_CENTRAL_CLIENT_ID`, `ENDPOINT_CENTRAL_CLIENT_SECRET`, `ENDPOINT_CENTRAL_REFRESH_TOKEN`, and optionally `ENDPOINT_CENTRAL_ACCOUNTS_URL`. The accounts server defaults to `https://accounts.zoho.<same suffix>`, as that page lists; for Canada, Zoho's own `accounts.zoho.com/oauth/serverinfo` names `https://accounts.zohocloud.ca` instead, so set the variable if the default answers an error.
805
+
806
+ **Options:** `--endpoint-central-min-os <platform>=<version>` (repeatable, platform `windows`, `macos` or `linux`; without it `os_minimum` is skipped once), `--endpoint-central-contact-days` (default 14), `--endpoint-central-max-missing-os-patches` (default 0), `--endpoint-central-max-computers` (default 500), `--endpoint-central-call-gap-ms` (default 600).
807
+
808
+ The computer list is `GET /dcapi/inventory/viewData/scanComputers?toolID=2131` (Accept `application/InventoryMobileAPI.v1+json`), followed by `Links.next` until it is null. Each computer with an installed agent then costs two calls, `GET /dcapi/inventory/computers/{resourceID}/diskEncryptionStatus` and `/firewall`, each with its own documented `Accept` type. Those endpoints allow 120 calls a minute and lock the caller out for 5 minutes beyond that, so the calls are spaced 600 ms apart: 500 computers take about ten minutes. The patch report, `GET /dcapi/patch/reports/systemHealth` (50 calls a minute), has no resource id; a row is matched to a computer by name and domain, and a name two computers share is skipped rather than guessed. A 401 or 403 on a per-computer endpoint skips that check for the rest of the run and stops its calls.
809
+
810
+ The on-premises edition is not supported. Its documented API (`/api/1.4/desktop/authentication`) signs in with a console user's password, and no page documents the `/dcapi` endpoints or a read-only API key for it. The screen lock has no check: no endpoint used reports it.
811
+
812
+ | Check (`endpoint_central.`…) | What passes |
813
+ | --- | --- |
814
+ | `disk_encryption` | Every drive in `diskEncryptionStatusDetails` reports `Encrypted`; any `Not Encrypted` fails, removable drives included |
815
+ | `firewall` | `domainProfileStatus`, `privateProfileStatus` and `publicProfileStatus` all `Enabled`, or a third-party firewall with `protectionStatus` `Enabled`; neither reported is skipped |
816
+ | `os_minimum` | `os_version` at or above `--endpoint-central-min-os` for the computer's `os_platform` |
817
+ | `os_patches` | `MissingOsPatches` at most `--endpoint-central-max-missing-os-patches`; third-party, driver and BIOS patches are not judged |
818
+ | `last_contact` | `agent_last_contact_time` within `--endpoint-central-contact-days` |
819
+ | `agent_installed` | `installation_status` 22 (installed); 21, 23, 24 and 29 fail |
820
+
821
+ A computer is `endpoint_central computer <resource_id> serial <service tag>`. Computer names, the logged-on user, FQDN, IP and MAC addresses, and the patch report's owner fields are never put in a result.
822
+
663
823
  ### This Windows PC (`windows`)
664
824
 
665
825
  For a firm with no device management, which for a small Canadian defence supplier usually means Windows laptops without Intune. The source reads the Windows 10 or 11 PC the collector runs on, through Windows PowerShell 5.1 (`powershell.exe -NoProfile -NonInteractive -EncodedCommand`) and `whoami.exe`. It is the Windows counterpart of `macos`: no credential, no network call, nothing written to the PC, and it runs as the signed-in user. Off Windows, every check is skipped.
@@ -737,7 +897,8 @@ A TLS 1.1 probe that this machine's OpenSSL cannot make, or a network failure, g
737
897
 
738
898
  ## Layout
739
899
 
740
- - `src/cli.ts`: `keygen`, `run` and `config-schema`; exit codes.
900
+ - `src/cli.ts`: `keygen`, `run`, `agent` and `config-schema`; exit codes; the guard around each source.
901
+ - `src/channel.ts`, `src/report.ts`, `src/agent.ts`, `src/release_keys.ts`: the check-in and signed updates, the run report and its scrubbing, resident mode, and the release public keys; tests in `test/channel.test.ts`.
741
902
  - `src/config.ts` and `collector.config.schema.json`: the config file and its schema (a test holds the two equal).
742
903
  - `src/sources/web.ts`: websites by URL, with proof of control; tests in `test/web.test.ts` over a fake network.
743
904
  - `src/payload.ts`: the v2 body, catalogue vetting, the 5,000-result limit.
@@ -745,7 +906,7 @@ A TLS 1.1 probe that this machine's OpenSSL cannot make, or a network failure, g
745
906
  - `src/sources/index.ts`: the list of sources. A new source is a file in `src/sources/` that exports `{ name, options, emits, describe, run }`, plus one line in this list, plus its catalogue JSON in sos-app.
746
907
  - `src/sources/cloudflare.ts` and `src/sources/cloudflare/`: the API client (GET only), zone and account checks, and the OPA sub-source.
747
908
  - `src/sources/entra.ts`, `okta.ts`, `jumpcloud.ts`, `google_workspace.ts`, `github.ts`: the identity and code sources (JumpCloud also reads devices, with `device_common.ts`); `readonly_http.ts` (GET-only client, retry, pagination, redaction) serves all five and `oauth.ts` (token requests) serves Entra ID and Google; `google_oauth.ts` and `src/google_login.ts` are Google's browser sign-in and its token file (`test/google_oauth.test.ts`). Their tests are `test/{entra,okta,jumpcloud,google_workspace,github}.test.ts` over the fakes and shared contract suite in `test/rest_fakes.ts`.
748
- - `src/sources/fleet.ts`, `intune.ts`, `jamf.ts`, `kandji.ts`, `defender_endpoint.ts`: the device sources, listed through `device_sources.ts`, with helpers in `device_common.ts`; tests in `test/{fleet,intune,jamf,kandji,defender_endpoint}.test.ts`.
909
+ - `src/sources/fleet.ts`, `intune.ts`, `jamf.ts`, `kandji.ts`, `defender_endpoint.ts`, `workspace_one.ts`, `configmgr.ts`, `google_endpoints.ts`, `ivanti_mdm.ts`, `ninjaone.ts`, `endpoint_central.ts`: the device sources, listed through `device_sources.ts`, with helpers in `device_common.ts`; tests in `test/{fleet,intune,jamf,kandji,defender_endpoint,workspace_one,configmgr,google_endpoints,ivanti_mdm,ninjaone,endpoint_central}.test.ts`.
749
910
  - `src/sources/macos.ts` and `windows.ts`: the Mac or Windows PC the collector runs on, read with the operating system's own tools through an injectable command runner; also listed through `device_sources.ts`, tested in `test/{macos,windows}.test.ts` with fake runners.
750
911
  - `src/v1/`: the v1 FedRAMP push behind `--fedramp-target`.
751
912
  - `src/sources/prowler.ts` and `runner/`: Prowler as an engine (`--source prowler`) and the container that runs it; see `runner/README.md`. More than 5,000 results (or 4 MB) go out as several signed pushes, each with its own nonce.
@@ -5,7 +5,7 @@ The collector runs these programs as separate processes, or reads their output.
5
5
  ## Prowler
6
6
 
7
7
  - Project: https://github.com/prowler-cloud/prowler
8
- - Version: 5.45.0 (git tag `5.45.0`, commit bffc8844c1c9a3666e37a2ada4ee05f62e1446b0; PyPI `prowler==5.45.0`)
8
+ - Version: 5.47.0 (git tag `5.47.0`, commit 555a4cc469907e26ef007eb79af2b6cb4bddd6c6; PyPI `prowler==5.47.0`)
9
9
  - Licence: Apache License 2.0
10
10
  - Copyright: Copyright @ 2024 Toni de la Fuente (as stated in Prowler's LICENSE file)
11
11
  - Used for: the `prowler` source (`src/sources/prowler.ts`) runs the `prowler` command or reads its OCSF output. `apps/sos-app/src/data/checks/prowler_<provider>.json` holds Prowler check ids, check titles (`CheckTitle`), severities and the FedRAMP 20x KSI and NIST SP 800-53 mappings, all read from that release by `apps/sos-app/scripts/build_prowler_catalog.py`.
@@ -13,7 +13,7 @@ The collector runs these programs as separate processes, or reads their output.
13
13
  - Texts copied: `apps/sos-app/src/data/guidance/prowler_remediation_<provider>.json` holds, for each check in that provider's catalogue, Prowler's own `Risk` and `Remediation` texts (`Recommendation.Text` and `Url`, and `Code.CLI`, `Terraform`, `NativeIaC`, `Other`) from the same release, written by the same script. They are Prowler's work under the Apache License 2.0, shipped unmodified with this attribution and a `notice` line in each file; empty fields become null and each field is cut at 4,000 characters. sos-app shows them in its per-control guides.
14
14
  - Prowler ships no NOTICE file at this tag.
15
15
 
16
- The licence text, as distributed with Prowler 5.45.0:
16
+ The licence text, as distributed with Prowler 5.47.0:
17
17
 
18
18
  ```text
19
19
  Apache License
@@ -264,13 +264,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
264
264
  ## Trivy
265
265
 
266
266
  - Project: https://github.com/aquasecurity/trivy
267
- - Version: 0.75.0 (git tag `v0.75.0`, commit 591e9799316a602e703f0b484f6c6d7b234ec8f3)
267
+ - Version: 0.76.0 (git tag `v0.76.0`, commit 531360e5d2a88e732373f1614ba99a38738538b8)
268
268
  - Licence: Apache License 2.0
269
269
  - Copyright: Copyright 2019-2020 Aqua Security Software Ltd. (as stated in Trivy's NOTICE file; the LICENSE appendix keeps the template line `Copyright [yyyy] [name of copyright owner]`)
270
270
  - Used for: the runner image bundles `trivy` so a job can write the JSON report (`--format json`) that the `trivy` source (`src/sources/trivy.ts`) reads. `apps/sos-app/src/data/checks/trivy.json` names the checks.
271
- - Built from source: the runner image fetches the tag `v0.75.0` (commit 591e9799316a602e703f0b484f6c6d7b234ec8f3) and compiles it with Go 1.27.3 (see Go below), raising modules to the floors in `collector/runner/go-module-fixes.txt`. Changes: none to the project's own source; `go.mod` and `go.sum` move only where a floor applies.
271
+ - Built from source: the runner image fetches the tag `v0.76.0` (commit 531360e5d2a88e732373f1614ba99a38738538b8) and compiles it with Go 1.27.3 (see Go below), raising modules to the floors in `collector/runner/go-module-fixes.txt`. Changes: none to the project's own source; `go.mod` and `go.sum` move only where a floor applies.
272
272
 
273
- The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `v0.75.0` differs only in its appendix template. Trivy ships this NOTICE file at the tag, reproduced as distributed:
273
+ The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `v0.76.0` differs only in its appendix template. Trivy ships this NOTICE file at the tag, reproduced as distributed:
274
274
 
275
275
  ```text
276
276
  Trivy
@@ -329,14 +329,14 @@ The licence terms (sections 1 to 9 and the end of terms) are identical, word for
329
329
  ## Open Policy Agent (OPA)
330
330
 
331
331
  - Project: https://github.com/open-policy-agent/opa
332
- - Version: 1.21.1 (git tag `v1.21.1`, commit 2a109e54103370d2ef288782ef3cb4c8a37902b2)
332
+ - Version: 1.21.2 (git tag `v1.21.2`, commit 3f7474e6b3c631821d8aac25ca444f7e75a56483)
333
333
  - Licence: Apache License 2.0
334
334
  - Copyright: none stated; the LICENSE appendix keeps the template line `Copyright {yyyy} {name of copyright owner}`
335
335
  - Used for: ScubaGoggles evaluates its Rego baselines with the bundled `opa`, and the `cloudflare` source (`src/sources/cloudflare/opa.ts`) runs `opa eval` over `iac/policy/cloudflare` when given `--tofu-state`.
336
- - Built from source: the runner image fetches the tag `v1.21.1` (commit 2a109e54103370d2ef288782ef3cb4c8a37902b2) and compiles it with Go 1.27.3 (see Go below), raising modules to the floors in `collector/runner/go-module-fixes.txt`. Changes: none to the project's own source; `go.mod` and `go.sum` move only where a floor applies.
336
+ - Built from source: the runner image fetches the tag `v1.21.2` (commit 3f7474e6b3c631821d8aac25ca444f7e75a56483) and compiles it with Go 1.27.3 (see Go below), raising modules to the floors in `collector/runner/go-module-fixes.txt`. Changes: none to the project's own source; `go.mod` and `go.sum` move only where a floor applies.
337
337
  - OPA ships no NOTICE file at this tag.
338
338
 
339
- The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `v1.21.1` differs only in its appendix template.
339
+ The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `v1.21.2` differs only in its appendix template.
340
340
 
341
341
  ## ScubaGoggles
342
342
 
@@ -549,14 +549,14 @@ For further information, please contact The MITRE Corporation, Contracts Managem
549
549
  ## Kubescape
550
550
 
551
551
  - Project: https://github.com/kubescape/kubescape (CNCF)
552
- - Version: 4.0.15 (git tag `v4.0.15`, commit 16cfe102f11551a6455fe9bf8e37d7083da90484)
552
+ - Version: 4.0.16 (git tag `v4.0.16`, commit d0ea07bcd9d7ab1640dcd6527d0e68a34f10da0e)
553
553
  - Licence: Apache License 2.0
554
554
  - Copyright: none stated; the LICENSE appendix keeps the template line `Copyright [yyyy] [name of copyright owner]`
555
555
  - Used for: the runner image bundles `kubescape` so a job can write the JSON report (`--format json`) that the `kubescape` source (`src/sources/kubescape.ts`) reads. It runs offline from `/opt/kubescape/artifacts` (the regolibrary files below), with its version check off (`KS_SKIP_UPDATE_CHECK=true`).
556
- - Built from source: the runner image fetches the tag `v4.0.15` (commit 16cfe102f11551a6455fe9bf8e37d7083da90484) and compiles it with Go 1.27.3 (see Go below), raising modules to the floors in `collector/runner/go-module-fixes.txt`. Changes: none to the project's own source; `go.mod` and `go.sum` move only where a floor applies.
556
+ - Built from source: the runner image fetches the tag `v4.0.16` (commit d0ea07bcd9d7ab1640dcd6527d0e68a34f10da0e) and compiles it with Go 1.27.3 (see Go below), raising modules to the floors in `collector/runner/go-module-fixes.txt`. Changes: none to the project's own source; `go.mod` and `go.sum` move only where a floor applies.
557
557
  - Kubescape ships no NOTICE file at this tag.
558
558
 
559
- The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `v4.0.15` differs only in leading white space and its appendix template.
559
+ The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `v4.0.16` differs only in leading white space and its appendix template.
560
560
 
561
561
  ## Kubescape regolibrary
562
562
 
@@ -573,12 +573,12 @@ The licence terms (sections 1 to 9 and the end of terms) are identical, word for
573
573
  ## Checkov
574
574
 
575
575
  - Project: https://github.com/bridgecrewio/checkov
576
- - Version: 3.3.27 (git tag `3.3.27`, commit 22ba0d2077cd3a09fc1547ae80be9ffe4e471e6c; PyPI `checkov==3.3.27`)
576
+ - Version: 3.3.29 (git tag `3.3.29`, commit e40023ab262c7c8b7b0a8f0975072b42d1b601f9; PyPI `checkov==3.3.29`)
577
577
  - Licence: Apache License 2.0
578
578
  - Copyright: Copyright 2019 Palo Alto Networks (as stated in Checkov's LICENSE appendix)
579
579
  - Used for: the runner image installs Checkov in `/opt/checkov` so a job can write the JSON report (`-o json`) that the `checkov` source (`src/sources/checkov.ts`) reads, with `BC_SKIP_MAPPING=TRUE` and `CKV_SKIP_PACKAGE_UPDATE_CHECK=true` so it fetches neither Prisma Cloud's mapping nor PyPI's latest version. `apps/sos-app/src/data/checks/checkov.json` names 55 Checkov check ids, each checked to exist at that tag by `apps/sos-app/scripts/build_checkov_catalog.py`; its titles and its control mapping are Sagentix's own words, not Checkov's.
580
- - Downloaded from: https://files.pythonhosted.org/packages/26/ec/0042c3eecc84f394867554efdb25425b0b84f690997de512924850009c71/checkov-3.3.27-py3-none-any.whl (SHA-256 f162b1fee91e204cc5891baa7e3dc1775fe9843dd11a0809986b9bce806528a2, the digest PyPI lists for that file). Its Python dependencies come from PyPI at install time, unpinned, as Prowler's do.
580
+ - Downloaded from: https://files.pythonhosted.org/packages/63/4a/26ecbf2abac34993b7dba06379c6b8a03f078323856a917ed47370610a67/checkov-3.3.29-py3-none-any.whl (SHA-256 9a8b3918788faf8e18b181d826d284744b22b9d99c53cf1b67a2e84f0596796f, the digest PyPI lists for that file). Its Python dependencies come from PyPI at install time, unpinned, as Prowler's do.
581
581
  - Changes: none
582
582
  - Checkov ships no NOTICE file at this tag.
583
583
 
584
- The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `3.3.27` differs only in leading white space and its appendix, which carries the copyright line above.
584
+ The licence terms (sections 1 to 9 and the end of terms) are identical, word for word, to the Apache License 2.0 text reproduced under Prowler above; the file at `3.3.29` differs only in leading white space and its appendix, which carries the copyright line above.
@@ -127,8 +127,8 @@
127
127
  },
128
128
  {
129
129
  "id": "aws.access_analyzer_enabled",
130
- "title": "IAM Access Analyzer has an active analyzer in the region",
131
- "scope": "Each region given; the analyzer's findings are not read.",
130
+ "title": "IAM Access Analyzer has an active external-access analyzer in the region",
131
+ "scope": "Each region given; an ACTIVE analyzer of type ACCOUNT or ORGANIZATION (external access) passes, an unused-access or internal-access analyzer alone does not; the analyzer's findings are not read.",
132
132
  "evidenceType": "Configuration"
133
133
  },
134
134
  {