@vigilbase/cli 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLOUDFLARE.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `vigilbase cf` and `vigilbase cloudflare` use the authenticated Vigilbase One API. Run `vigilbase login` first (signing in isn't supported on Windows yet). Your organization, permissions, customer links, and allowed zones determine access. The CLI never requires a Cloudflare API key.
4
4
 
5
- `cf checkup` runs on One's servers. `cloudflare-checkup` (also `local cloudflare-checkup`) runs on your machine with Cloudflare's own `cf` CLI and needs no Vigilbase login: one command, `npx @vigilbase/cli cloudflare-checkup`, verifies your work email with a code, installs cf if needed, signs in read-only, collects and uploads after you confirm. It works while vigilbase.com has local checkups switched on; otherwise it says so before asking for your email and points you to the checkup in your browser at https://vigilbase.com/tools/cloudflare-checkup (see [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/LOCAL.md)).
5
+ `cf checkup` runs on One's servers. `cloudflare-checkup` (also `local cloudflare-checkup`) runs on your machine with Cloudflare's own `cf` CLI and needs no Vigilbase login: one command, `npx @vigilbase/cli cloudflare-checkup`, verifies your work email with a code, installs cf if needed, signs in read-only, collects and uploads after you confirm. It works while vigilbase.com has local checkups switched on; otherwise it says so before asking for your email and points you to the checkup in your browser at https://vigilbase.com/tools/cloudflare-checkup (see [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/LOCAL.md)).
6
6
 
7
7
  Customer provider access requires an `active` or `trialing` Cloudflare service
8
8
  entitlement for the selected account. Historical/inactive rows and legacy account
@@ -92,10 +92,11 @@ Every path below is relative to the configured One origin. `customerId`, `accoun
92
92
  | `speed-settings` | `GET /api/cloudflare/zones/Z/speed-settings?customerId=C` | Read provider settings; internal permission checks |
93
93
  | `audit-logs`, `zero-trust`, `developer-platform`, `magic-transit` | `GET /api/cloudflare/accounts/A/RESOURCE?customerId=C` | Read provider account resource |
94
94
  | `analytics` | `GET /api/services/cloudflare/analytics?days=N&customerId=C&accountId=A` or `zoneId=Z` | Read usage/provider analytics |
95
- | Checkup status | `GET /api/services/cloudflare/checkups?customerId=C&accountId=A` | Read completed checkup/configuration |
95
+ | Checkup status | `GET /api/services/cloudflare/checkups?customerId=C&accountId=A` | Read completed checkup/configuration; `report` is deprecated, read `latestRun.results` |
96
96
  | Checkup start | `POST /api/services/cloudflare/checkups/start`, JSON `{customerId?, accountId?}` | Starts scanner and persists a run; no DNS/WAF configuration edits |
97
97
  | Checkup progress | `GET /api/services/cloudflare/checkups/progress?toolRunId=ID` | Read up to 500 persisted events per response |
98
98
  | Checkup result | `GET /api/tool-runs/ID` | Read stored run; server may claim eligible run ownership for the organization |
99
+ | (no command) Checkup PDF | `GET /api/services/cloudflare/checkups/report?toolRunId=ID`, or `?customerId=C&accountId=A` for the latest completed run | Read stored run as the shared PDF report; never starts a scan |
99
100
 
100
101
  Account audit logs require service-read permissions. Zero Trust, Developer Platform and Magic Transit require deployment-read permissions. Speed settings require internal access plus service/deployment read permissions. One verifies tenant/account/zone bindings for these reads; account-wide reads may be unavailable for grants restricted to individual zones.
101
102
 
@@ -105,6 +106,12 @@ The API may return empty audit logs, WAF rules, speed settings or other account
105
106
 
106
107
  Analytics prefers scoped stored usage when available and otherwise uses provider analytics. Live zone analytics covers the most recent 23 hours regardless of `--days`; account analytics uses the requested period. Zero-valued results can represent unavailable data. JSON mode preserves the API's shape, including either `analytics` or `bandwidth`.
107
108
 
109
+ In text mode, `cf checkup result` and `cf checkup` print a readable summary of the stored result instead of its raw fields: the grade and score, the check counts, the account's domains (worst score first, up to 15, with plan, grade and failed, warning and passed counts), the top 8 priorities (each check once, failed before warnings, then by the severity the scanner gave it, with the number of domains it affects and the first line of its recommendation) and the full report link when the result has one. Longer lists end with how many more there are and where to see them, for example `… 94 more findings in the full report (--json lists them all)` (without a report link: `… 94 more findings (--json lists them all)`). These caps are the ones the web report, the PDF and the emails use. On a terminal, the grade, counts and a bar of the checks are coloured and lines fit the width; `NO_COLOR` or `FORCE_COLOR=0` turns colours off. `--json` returns the API envelope unchanged, with every check and field.
110
+
111
+ The `cf checkup --json` envelope (`GET /api/services/cloudflare/checkups`) carries the latest completed run as `latestRun`, with the stored Cloudflare Checkup result in `latestRun.results`. Its `report` field is deprecated and kept only for scripts written against the earlier envelope; responses name it in the `X-Vigilbase-Deprecated-Fields: report` header. Use `latestRun.results`. `report` is now derived from that stored result: the account, `generatedAt` (the scan time), the grade and score (unlocked results only), `scope` on a zone-scoped checkup, the old `summary` counts, `metadata.dnssecEnabled` and `metadata.wafEnabled`, and per zone its status, plan, grade, score, check counts, `dnssec` and `waf` when those checks ran, and its issues. Each issue keeps `id` (the check ID), `severity` (`critical`, `warning` or `info`, mapped as before), `title`, `description` and `recommendation`, and adds the check's `status` and the scanner's own `checkSeverity`. Checks that could not run are not issues. Fields the stored result cannot answer are omitted rather than estimated: per-zone `dnsRecords`, `firewall`, `paused` and `developmentMode`, and `metadata.firewallProtected` and `metadata.apexConfigured`. `report` is `null` when there is no completed run.
112
+
113
+ The same run is available as the shared PDF report from `GET /api/services/cloudflare/checkups/report?toolRunId=ID`, or for an account's latest completed run from `GET /api/services/cloudflare/checkups/report?customerId=C&accountId=A` (account administrators). The account form never starts a scan: without a completed run it answers 404 `No completed checkup for this account; start one via POST /checkups/start`. `POST /api/services/cloudflare/checkups/analyze` takes `{ "toolRunId": ID }`; the earlier body, a checkup report, is refused with 400.
114
+
108
115
  Checkup progress reads once and does not poll automatically. Run the command again to refresh. Status describes the latest completed run; use its returned run ID or the ID from `start` with `progress` to inspect an in-progress scan. `result` verifies that the selected run is a Cloudflare checkup and reports when no result is available yet.
109
116
 
110
117
  Malformed IDs, conflicting selections, extra positional arguments and unsupported scope combinations fail before a network request. Authentication, permission, HTTP and explicit `success:false` API failures return a nonzero exit status. Provider configuration writes, token management, cache purge, account deletion and DNS edits are not exposed by this command group.
package/COMMANDS.md CHANGED
@@ -12,11 +12,11 @@ case.
12
12
 
13
13
  | Area | Complete command and API reference |
14
14
  | --- | --- |
15
- | Cloudflare | [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/CLOUDFLARE.md) |
16
- | Security | [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/SECURITY.md) |
17
- | Customers, organizations, monitoring, support | [ONE.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/ONE.md) |
18
- | Local Cloudflare Checkup (cf on your machine) | [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/LOCAL.md) |
19
- | Sign-in, credentials, configuration, troubleshooting | [README.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/README.md) |
15
+ | Cloudflare | [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/CLOUDFLARE.md) |
16
+ | Security | [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/SECURITY.md) |
17
+ | Customers, organizations, monitoring, support | [ONE.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/ONE.md) |
18
+ | Local Cloudflare Checkup (cf on your machine) | [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/LOCAL.md) |
19
+ | Sign-in, credentials, configuration, troubleshooting | [README.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/README.md) |
20
20
 
21
21
  The references list the supported operations, filters, response and pagination
22
22
  behaviour, API endpoints and access requirements of the CLI.
package/LOCAL.md CHANGED
@@ -15,7 +15,7 @@ before it asks for your email; when local mode is off it stops with
15
15
  your browser at <https://vigilbase.com/tools/cloudflare-checkup>.
16
16
 
17
17
  `vigilbase cf checkup` runs on Vigilbase One's servers with stored credentials
18
- (see [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/CLOUDFLARE.md));
18
+ (see [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/CLOUDFLARE.md));
19
19
  `vigilbase cloudflare-checkup` runs on your machine and needs no Vigilbase
20
20
  login.
21
21
 
@@ -35,7 +35,7 @@ update the global install with `npm install -g @vigilbase/cli@latest`. With
35
35
  Yarn 4.15+ or pnpm 11+, `yarn dlx` and `pnpm dlx` run an older release for a
36
36
  day after each release, and pnpm 10.16+ with `minimumReleaseAge` set refuses
37
37
  the new version; use `npx` (see
38
- [Troubleshooting](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/README.md#troubleshooting)).
38
+ [Troubleshooting](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/README.md#troubleshooting)).
39
39
 
40
40
  The command does everything, in this order, and stops at the first problem
41
41
  before any slower step:
@@ -44,7 +44,8 @@ before any slower step:
44
44
  2. Asks vigilbase.com whether local checkups are available and accept this CLI
45
45
  version (nothing about you is sent). If not, it stops here, before asking
46
46
  for your email.
47
- 3. Asks for your work email (`Work email for your results:`), and
47
+ 3. Uses your verified Vigilbase CLI login without email prompts or codes.
48
+ If signed out, asks for your work email (`Work email for your results:`), and
48
49
  vigilbase.com emails you a 6-digit code. You enter it at
49
50
  `Code from the email:` (`123456`, `123 456` and `123-456` all work), and
50
51
  the CLI prints `✓ Verified j***@acme.com`.
@@ -90,6 +91,14 @@ $ npx @vigilbase/cli cloudflare-checkup
90
91
  ◇ Upload 182 KB to vigilbase.com? › Yes
91
92
  │
92
93
  └ ✓ Done. 31 passed · 6 warnings · 4 failed · 30 reads not collected locally
94
+ ████████████████████████░░░░░░░░ 31 of 41 free checks passed
95
+
96
+ Top findings
97
+ ✗ critical Account MFA Enforcement account-wide
98
+ ✗ high DNSSEC on 3 domains
99
+ ▲ medium Always Use HTTPS on 6 domains
100
+ +412 more checks in the full report
101
+
93
102
  Results: https://vigilbase.com/tools/run/0b7f3c1e-1d2a-4c55-9e1a-8d3f2b6c9a10 (opening in your browser)
94
103
  A link has also been emailed to j***@acme.com.
95
104
  Remove the read-only Cloudflare login and the cf Vigilbase installed later with:
@@ -122,6 +131,11 @@ Results go to j***@acme.com. Upload 182 KB to https://vigilbase.com? [y/N] y
122
131
  ✓ Uploaded. Free summary: 31 passed · 6 warnings · 4 failed · 30 reads not collected locally
123
132
  Results: https://vigilbase.com/tools/run/0b7f3c1e-1d2a-4c55-9e1a-8d3f2b6c9a10
124
133
  A link has also been emailed to j***@acme.com.
134
+ Top findings:
135
+ ✗ critical Account MFA Enforcement · account-wide
136
+ ✗ high DNSSEC · on 3 domains
137
+ ▲ medium Always Use HTTPS · on 6 domains
138
+ +412 more checks in the full report
125
139
  Remove the read-only Cloudflare login and the cf Vigilbase installed later with: npx @vigilbase/cli cloudflare-checkup logout
126
140
  ```
127
141
 
@@ -142,7 +156,7 @@ and `vigilbase local cloudflare-checkup run` are the same command.
142
156
 
143
157
  | Option | Meaning |
144
158
  | --- | --- |
145
- | `--email <address>` | Your work email; without it, the CLI asks for it. |
159
+ | `--email <address>` | Override the signed-in address; a different address requires email verification. Signed out, the CLI asks when omitted. |
146
160
  | `--account-id <id>` | Skip the account picker (32 hex characters). |
147
161
  | `--out <file>` | Write the snapshot here instead of the default path; `--overwrite` replaces an existing file. |
148
162
  | `--yes` | Answer yes to installing cf and to the upload question. |
@@ -237,10 +251,22 @@ the terminal width, also after a resize.
237
251
  - If the Cloudflare sign-in fails or is cancelled, it asks `Try signing in
238
252
  again?`, up to twice more. While cf signs in, the terminal belongs to cf, so
239
253
  its own messages appear between the steps.
254
+ - The result ends with a short card from vigilbase.com's free report: a bar
255
+ of the free checks (green passed, yellow warnings, red failed; without
256
+ colour, the share that passed), up to five top findings (failed before
257
+ warnings, then by the severity vigilbase.com gave them; each check once, with
258
+ the number of domains it affects, never their names; any more are counted:
259
+ `… 2 more findings on the results page`) and how many more checks the full
260
+ report has. Long names are cut to the terminal width.
240
261
  - The results link is underlined, and clickable (an OSC 8 link) in terminals
241
262
  known to support it (`FORCE_HYPERLINK=0` turns that off, `1` turns it on).
242
263
  When stdout is not a terminal (`> results.txt`), the plain result lines go to
243
264
  stdout as well.
265
+ - In the plain output, the `✓ Uploaded. Free summary: …`, `Results: …` and
266
+ email lines come first and keep their wording, so scripts can match them;
267
+ the top findings (`Top findings:`, one indented line each and, past five,
268
+ `… N more findings on the results page`) and the
269
+ `+N more checks in the full report` line follow them.
244
270
  - Ctrl-C at any point gives the terminal back (raw mode off, cursor shown),
245
271
  says what happened (for example `Cancelled. Nothing was uploaded.`) and
246
272
  exits 130. While cf or npm runs, Ctrl-C, SIGTERM and SIGHUP stop them and
@@ -264,8 +290,34 @@ between the items of a summary line, in every step, spinner and message.
264
290
 
265
291
  ## Email verification and the checkup session
266
292
 
267
- The CLI verifies your email itself; there is no browser step and no bot check
268
- on this path.
293
+ Snapshot uploads retry network failures and HTTP 503 responses twice using
294
+ the same bytes and session. A 503's `retryAfterSec` or `Retry-After` delay is
295
+ honoured up to 60 seconds per retry; longer delays stop with retry guidance.
296
+ Without a server delay, retries wait 2 seconds, then 5 seconds. Challenges and
297
+ the local-mode kill switch are never retried. The saved snapshot remains
298
+ available for a later `submit` if these attempts fail.
299
+
300
+ When you are signed in with `vigilbase login`, both the one command and
301
+ `submit` use your verified account identity and skip email prompts and codes.
302
+ The CLI sends its login token only to One's
303
+ `POST /api/tools/cloudflare-checkup/local-identity`. One checks the live
304
+ identity and verified mailbox, then signs a proof valid for at most two minutes
305
+ and tied to this checkup's run ID. The CLI exchanges that proof at
306
+ `POST …/local-session/identity` for an upload session. vigilbase.com never
307
+ receives the login token. Session renewal obtains a fresh proof for the same
308
+ run; it does not send an email or create a second checkup. Proofs and session
309
+ codes stay in memory and are never printed or passed to cf or npm.
310
+
311
+ A missing, expired or rejected login uses the email flow below. `--email`
312
+ matching the live identity also skips verification; a different address must
313
+ verify by email. Signed-in users may use their verified account mailbox even
314
+ when it is a personal address; signed-out email verification still requires a
315
+ business address. Availability, rate limits, the upload confirmation and
316
+ Cloudflare's own sign-in apply to both paths.
317
+
318
+ The CLI verifies signed-out users' email itself; there is no browser step and
319
+ no bot check on this path.
320
+
269
321
 
270
322
  - **Availability first.** Before asking for your email, the one command and
271
323
  `submit` send `GET …/local-session/availability`, with no email, no
@@ -538,7 +590,7 @@ passed.
538
590
  | account | `accounts get` | single |
539
591
  | zones | `zones list --account-id A --per-page 50 --page N` | until an empty page |
540
592
  | members | `accounts members list --per-page 50 --page N` | until an empty page |
541
- | account rulesets | `rulesets list --per-page 100` | one call |
593
+ | account rulesets | `rulesets list --per-page 50` | one call |
542
594
  | account access rules | `firewall access-rules list --per-page 50 --page N` | until an empty page |
543
595
  | Access apps, IdPs, service tokens | `zero-trust access applications list`, `zero-trust identity-providers list`, `zero-trust access service-tokens list` (`--per-page 50 --page N`) | until an empty page |
544
596
  | Gateway rules, locations | `zero-trust gateway rules list`, `zero-trust gateway locations list` | single |
@@ -547,11 +599,14 @@ passed.
547
599
  | DNSSEC | `dns dnssec get --zone Z` | single |
548
600
  | DNS records | `dns records list --zone Z --per-page 100 --page N` | until an empty page (cap 100) |
549
601
  | Universal SSL, Total TLS | `ssl universal settings get --zone Z`, `acm total-tls get --zone Z` | single |
550
- | zone rulesets | `rulesets list --zone Z --per-page 100` | one call |
602
+ | zone rulesets | `rulesets list --zone Z --per-page 50` | one call |
551
603
  | ruleset detail | `rulesets get R --zone Z` | single |
552
604
  | zone access rules | `firewall access-rules list --zone Z --per-page 100 --page N` | until an empty page |
553
605
  | page rules, bot management, page shield | `page-rules list --zone Z`, `bot-management get --zone Z`, `client-side-security settings get --zone Z` | single |
554
606
  | API Shield | `api-security schema-validation settings get --zone Z`, `api-security schema-validation schemas list --zone Z --omit-source --per-page 100 --page N` | single / until an empty page |
607
+ | Edge certificate packs | `ssl certificate-packs list --zone Z --status all --per-page 50 --page N` | until an empty page |
608
+ | Uploaded custom certificates | `custom-certificates list --zone Z --per-page 50 --page N` | until an empty page |
609
+ | API operation enforcement | `web-assets operations list --zone Z --feature schema_info --per-page 50 --page N` | until an empty page |
555
610
 
556
611
  The only other cf argv are `--version`, `auth whoami`,
557
612
  `auth create <p> [--no-browser] --scopes …` (sign-in, interactive;
@@ -574,7 +629,7 @@ for `accounts list`, `accounts get` or `zones list` the run stops with
574
629
  cf strips pagination metadata, so lists are paged until a page comes back empty
575
630
  (one extra call per list, immune to server-side page clamps). A page whose first
576
631
  item repeats the previous page's is `invalid_output` (cf ignoring `--page`). The
577
- rulesets lists are cursor-only and the cursor is not printed: 100 or more items
632
+ rulesets lists are cursor-only and the cursor is not printed: 50 or more items
578
633
  are recorded as `possibly_truncated`.
579
634
 
580
635
  ## Process and environment policy
@@ -662,9 +717,11 @@ Each cf response is projected immediately and the raw text discarded. The file
662
717
  shield `enabled`;
663
718
  - DNS A/AAAA records with **pseudonymised IPs** (the first two IPv4 or three IPv6
664
719
  groups are kept, the host part becomes a per-prefix counter,
665
- `per_prefix_sequential/1`); names only for non-proxied records;
666
- - ruleset `id, phase`, `name: 'ddos'` markers, and rules reduced to action,
667
- `enabled:false`, a `true`/geo-block expression marker, managed-ruleset IDs and
720
+ `per_prefix_sequential/1`); DNS hostnames, including proxied CNAMEs without their targets;
721
+ - certificate status, covered hostnames and expiry; API operation schema IDs and
722
+ mitigation actions, without API endpoint paths, schema source or certificate keys;
723
+ - ruleset `id, phase, kind`, `name: 'ddos'` markers, and rules reduced to action,
724
+ `enabled:false`, a `true`/`false`/geo-block expression marker, execution target IDs and
668
725
  name keywords; IDs and descriptions only on disabled managed rules and
669
726
  block-everything rules; identical rules run-length encoded;
670
727
  - legacy page-rule action IDs, bot-management and schema-validation settings.
@@ -697,6 +754,14 @@ legacy Filters, previous-version WAF packages, previous-version Rate Limiting,
697
754
  and WAF traffic analytics (GraphQL). They are recorded as `not_collected`, shown
698
755
  as **Not collected locally** and never scored as passing.
699
756
 
757
+ Effective managed WAF scope and overrides are also explicitly not collected:
758
+ V1 projections omit account ruleset details, scope expressions and customer
759
+ overrides. They cannot establish effective coverage, even when basic deployment
760
+ evidence is present. Certificate and API-operation inputs are optional additions;
761
+ older V1 snapshots remain valid and report those missing inputs rather than
762
+ assuming protection. Browser scans include the full effective WAF checks and
763
+ bounded analytics, with Free-plan restrictions and sampling labeled separately.
764
+
700
765
  The plan and add-on review is not collected either: the snapshot has no entry
701
766
  for add-on entitlements or the Smart Shield setting. The PDF report shows each
702
767
  zone's plan from the zone list and lists Advanced Bot Management, Advanced Rate
@@ -776,6 +841,16 @@ Windows on ARM is not supported. The checkup needs no Vigilbase sign-in, so it
776
841
  works on Windows even though `vigilbase login` doesn't yet. pnpm and Volta
777
842
  global installs are found automatically; otherwise pass `--cf-bin` as above.
778
843
 
844
+ ## Report analytics
845
+
846
+ Unlocked results and PDFs include check outcomes, evaluation coverage, category
847
+ findings and domain comparisons from the same evaluator used by browser scans.
848
+ Local reports remain labelled self-reported. Traffic analytics are not collected
849
+ by the CLI: reports say so explicitly rather than showing unmeasured zeroes.
850
+ Older V1 snapshots remain accepted; optional inputs absent from them produce
851
+ coverage gaps, not passing results. No additional upload fields or permissions
852
+ are required for the configuration charts.
853
+
779
854
  ## Cleanup
780
855
 
781
856
  ```bash
package/ONE.md CHANGED
@@ -95,4 +95,4 @@ vigilbase support tickets [--all] [--limit 1..100] [--customer-id <uuid>] [--jso
95
95
 
96
96
  ## Related commands and validation
97
97
 
98
- See [README.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/README.md) for sign-in and configuration, [COMMANDS.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/COMMANDS.md) for account, billing, public scanners, run history and Watchdog, [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/CLOUDFLARE.md) for `cf`/`cloudflare`, and [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/SECURITY.md) for security commands. The read operations above do not replace the existing explicitly confirmed write commands.
98
+ See [README.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/README.md) for sign-in and configuration, [COMMANDS.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/COMMANDS.md) for account, billing, public scanners, run history and Watchdog, [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/CLOUDFLARE.md) for `cf`/`cloudflare`, and [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/SECURITY.md) for security commands. The read operations above do not replace the existing explicitly confirmed write commands.
package/README.md CHANGED
@@ -28,11 +28,11 @@ supported on Windows yet; public tools and `cloudflare-checkup` still work there
28
28
 
29
29
  | Reference | Covers |
30
30
  | --- | --- |
31
- | [COMMANDS.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/COMMANDS.md) | Core commands, aliases, global options, API mapping, sign-in, output |
32
- | [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/CLOUDFLARE.md) | Accounts, zones, DNS, WAF, analytics, Zero Trust, Workers/Pages, audit, checkups |
33
- | [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/SECURITY.md) | Incidents, logs, intelligence, analytics, assets, connections, policies, notifications, reports |
34
- | [ONE.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/ONE.md) | Customer records, billing, reports, investigations, organizations, monitoring, support |
35
- | [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/LOCAL.md) | `cloudflare-checkup`: verifies your work email, collects your Cloudflare configuration on your machine with Cloudflare's `cf` and uploads it after you confirm |
31
+ | [COMMANDS.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/COMMANDS.md) | Core commands, aliases, global options, API mapping, sign-in, output |
32
+ | [CLOUDFLARE.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/CLOUDFLARE.md) | Accounts, zones, DNS, WAF, analytics, Zero Trust, Workers/Pages, audit, checkups |
33
+ | [SECURITY.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/SECURITY.md) | Incidents, logs, intelligence, analytics, assets, connections, policies, notifications, reports |
34
+ | [ONE.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/ONE.md) | Customer records, billing, reports, investigations, organizations, monitoring, support |
35
+ | [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/LOCAL.md) | `cloudflare-checkup`: verifies your work email, collects your Cloudflare configuration on your machine with Cloudflare's `cf` and uploads it after you confirm |
36
36
 
37
37
  These files ship with the package. Built-in help: `vigilbase <command> --help`
38
38
  or `vigilbase help <command>`; `vigilbase help <command> <subcommand>` (for
@@ -124,15 +124,18 @@ npx @vigilbase/cli cloudflare-checkup
124
124
  This one command (also `vigilbase cloudflare-checkup` or
125
125
  `vigilbase local cloudflare-checkup`) runs the Vigilbase Cloudflare Checkup
126
126
  on your machine with Cloudflare's own `cf` CLI. It needs no Vigilbase sign-in.
127
- It asks for your work email and vigilbase.com emails you a 6-digit code to
128
- enter. It then installs cf if needed (it asks first), signs in to Cloudflare
127
+ If you are signed in with `vigilbase login`, it uses your verified account
128
+ identity without asking for an email or code. Otherwise it asks for your work
129
+ email and vigilbase.com emails you a 6-digit code to enter. It then installs cf if needed (it asks first), signs in to Cloudflare
129
130
  read-only, lets you choose the account, collects, and uploads the snapshot only
130
131
  after you confirm. It prints the results link and opens it in your browser on
131
132
  macOS and Linux; the results also go to your verified email.
132
133
 
133
134
  cf owns your Cloudflare login: this CLI never reads, prints, stores or uploads
134
- it. Your email address goes to vigilbase.com only so it can send the code; the
135
- CLI never prints, logs or stores it and shows only the masked `j***@acme.com`.
135
+ it. Your account token is sent only to One; vigilbase.com receives a short-lived
136
+ proof tied to this checkup. Checkup output shows only the masked
137
+ `j***@acme.com` address. `--email` can select a different recipient, which
138
+ requires email verification.
136
139
  In a terminal the steps show as an interactive timeline; pipes, CI,
137
140
  `TERM=dumb` and `--json` get plain lines.
138
141
 
@@ -146,7 +149,7 @@ read-only Cloudflare API token instead. When you are done:
146
149
  npx @vigilbase/cli cloudflare-checkup logout # removes the read-only cf login and the cf it installed
147
150
  ```
148
151
 
149
- See [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.5/files/LOCAL.md) for
152
+ See [LOCAL.md](https://app.unpkg.com/@vigilbase/cli@0.2.6/files/LOCAL.md) for
150
153
  every step, what is uploaded and the error codes.
151
154
 
152
155
  ## Public tools and sign-in
@@ -1,4 +1,8 @@
1
1
  import { z } from 'zod';
2
+ import { shouldUseColor } from '../lib/banner.js';
3
+ import { checkupReportLines, readCheckupReport, styleGrade } from '../lib/checkup-report.js';
4
+ import { sanitizeTerminal, singleLine } from '../lib/format.js';
5
+ import { ASCII_SYMBOLS, UNICODE_SYMBOLS, createStyles, stripAnsi, supportsUnicode } from '../lib/tui.js';
2
6
  import { CloudflareClient, accountResourceSchema, cloudflareScopeSchema, workspaceSectionSchema, } from '../lib/cloudflare-client.js';
3
7
  import { assertCommandFlags, asArray, asRecord, ok, outputJson, requireOneClient, withSpinner, } from './helpers.js';
4
8
  import { InvalidArgumentError, inRange, oneOf, unknownSubcommand } from './legacy-validation.js';
@@ -72,6 +76,45 @@ function fields(ctx, data) {
72
76
  writeLine(ctx, `${key.padEnd(20)} ${typeof value === 'object' ? JSON.stringify(value) : String(value)}`);
73
77
  }
74
78
  }
79
+ /**
80
+ * Text output of the checkup commands. On a terminal with colour (stdout a
81
+ * TTY, TERM not dumb, NO_COLOR and FORCE_COLOR=0 unset, not CI), lines carry
82
+ * the palette's SGR styles and are written to stdout directly; everywhere
83
+ * else they go through the writer as plain text. Every payload string in a
84
+ * line was sanitised when it was read, so the only escape sequences a line
85
+ * can hold are the palette's own styles; a line with anything else is
86
+ * written plain.
87
+ */
88
+ function checkupOutput(ctx) {
89
+ const stream = ctx.stdoutStream;
90
+ const terminal = ctx.mode === 'text' && stream?.isTTY === true && (ctx.env.TERM ?? '').toLowerCase() !== 'dumb';
91
+ const color = terminal && shouldUseColor(stream, ctx.env);
92
+ const width = Math.floor(Number(stream?.columns) || 0);
93
+ const palette = {
94
+ style: createStyles(color),
95
+ // Plain output keeps the Unicode symbols, like the CLI's other plain lines; legacy consoles get ASCII.
96
+ symbols: terminal && !supportsUnicode(ctx.env) ? ASCII_SYMBOLS : UNICODE_SYMBOLS,
97
+ color,
98
+ columns: terminal ? (width > 0 ? Math.max(40, width) : 80) : null,
99
+ };
100
+ return {
101
+ palette,
102
+ line: (text = '') => {
103
+ const visible = stripAnsi(text);
104
+ const styledOnly = !/\u001b(?!\[[0-9;]*m)/.test(text) && sanitizeTerminal(visible) === visible;
105
+ if (color && stream && styledOnly)
106
+ stream.write(`${text}\n`);
107
+ else
108
+ writeLine(ctx, visible);
109
+ },
110
+ };
111
+ }
112
+ /** `grade D 47`, the server's grade in its band's colour. */
113
+ function gradeLine(palette, grade, score) {
114
+ const scoreText = typeof score === 'number' || typeof score === 'string' ? singleLine(String(score), 10) : '';
115
+ return `grade ${styleGrade(palette.style, singleLine(String(grade), 3))}${scoreText ? ` ${scoreText}` : ''}`;
116
+ }
117
+ const FULL_DATA_HINT = 'For every check and field, add --json.';
75
118
  function zones(ctx, values) {
76
119
  const items = asArray(values).map(asRecord).filter((item) => item !== null);
77
120
  if (!items.length)
@@ -331,13 +374,21 @@ export const handleCloudflare = async (ctx) => {
331
374
  if (run.tool_name !== 'cloudflare-checkup')
332
375
  throw new Error('This run is not a Cloudflare checkup.');
333
376
  return emit(payload, (data) => {
334
- writeLine(ctx, `checkup ${String(data.status)}`);
377
+ const out = checkupOutput(ctx);
378
+ out.line(`checkup ${singleLine(String(data.status), 40)}`);
335
379
  if (data.grade)
336
- writeLine(ctx, `grade ${String(data.grade)} ${String(data.grade_score ?? '')}`);
337
- if (data.results)
338
- fields(ctx, asRecord(data.results) ?? { results: data.results });
339
- else
340
- writeLine(ctx, 'No result is available yet.');
380
+ out.line(gradeLine(out.palette, data.grade, data.grade_score));
381
+ const view = readCheckupReport(data.results);
382
+ if (!view) {
383
+ if (data.error_message)
384
+ out.line(`error ${singleLine(String(data.error_message))}`);
385
+ out.line('No result is available yet.');
386
+ return;
387
+ }
388
+ for (const line of checkupReportLines(out.palette, view))
389
+ out.line(line);
390
+ out.line();
391
+ out.line(out.palette.style.dim(FULL_DATA_HINT));
341
392
  });
342
393
  }
343
394
  const isStart = action === 'start' || action === 'run';
@@ -349,12 +400,27 @@ export const handleCloudflare = async (ctx) => {
349
400
  }
350
401
  const run = asRecord(data.latestRun);
351
402
  const report = asRecord(data.report);
352
- writeLine(ctx, `checkup ${data.requiresSetup ? 'setup required' : String(run?.status ?? (report ? 'completed' : 'no completed runs'))}`);
353
- if (run?.id)
354
- writeLine(ctx, `run ${String(run.id)}`);
355
- if (run?.grade ?? report?.grade)
356
- writeLine(ctx, `grade ${String(run?.grade ?? report?.grade)}`);
357
- if (report)
358
- fields(ctx, report);
403
+ const out = checkupOutput(ctx);
404
+ out.line(`checkup ${data.requiresSetup ? 'setup required' : singleLine(String(run?.status ?? (report ? 'completed' : 'no completed runs')), 40)}`);
405
+ const runId = typeof run?.id === 'string' ? singleLine(run.id, 80) : null;
406
+ if (runId)
407
+ out.line(`run ${runId}`);
408
+ if (run?.grade)
409
+ out.line(gradeLine(out.palette, run.grade, run.grade_score));
410
+ else if (report?.grade)
411
+ out.line(gradeLine(out.palette, report.grade, report.score));
412
+ // The run's stored result; One's summary report of it for runs that have none.
413
+ const view = readCheckupReport(run?.results) ?? readCheckupReport(report);
414
+ if (!view)
415
+ return;
416
+ const lines = checkupReportLines(out.palette, view);
417
+ for (const line of lines)
418
+ out.line(line);
419
+ if (runId || lines.length)
420
+ out.line();
421
+ if (runId)
422
+ out.line(out.palette.style.dim(`Details: vigilbase cf checkup result ${runId}`));
423
+ if (lines.length)
424
+ out.line(out.palette.style.dim(FULL_DATA_HINT));
359
425
  });
360
426
  };
@@ -77,7 +77,10 @@ export type CommandContext = {
77
77
  publicClient: VigilbaseClient;
78
78
  env: NodeJS.ProcessEnv;
79
79
  stdin?: NodeJS.ReadableStream;
80
- /** Raw stdout, only to tell whether it is a terminal; text output still goes through `writer`. */
80
+ /**
81
+ * Raw stdout, to tell whether it is a terminal; text output still goes through `writer`, except the
82
+ * coloured checkup report of `cf checkup` (see `checkupOutput` in cloudflare.ts), built from sanitised text.
83
+ */
81
84
  stdoutStream?: NodeJS.WritableStream & {
82
85
  isTTY?: boolean;
83
86
  };
@@ -3,9 +3,9 @@
3
3
  * ≡ `vigilbase local cloudflare-checkup [run] [options]`, plus the advanced
4
4
  * `login|doctor|accounts|collect|inspect|submit|logout` subcommands.
5
5
  *
6
- * The one command verifies the user's work email (vigilbase.com emails a
7
- * 6-digit code), finds (or, after asking, installs) Cloudflare's cf CLI, signs
8
- * in to Cloudflare with a read-only cf login, lets the user choose the
6
+ * The one command uses the signed-in Vigilbase identity, or verifies a work
7
+ * email with a 6-digit code, then finds (or, after asking, installs) Cloudflare's
8
+ * cf CLI, signs in to Cloudflare with a read-only cf login, lets the user choose the
9
9
  * account, collects a minimised snapshot with a fixed allowlist of read-only
10
10
  * cf commands, shows what it contains, uploads exactly that file to
11
11
  * vigilbase.com after an explicit confirmation and opens the results page.
@@ -17,9 +17,12 @@ import { homedir } from 'node:os';
17
17
  import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
18
18
  import { z } from 'zod';
19
19
  import { CLI_VERSION } from '../version.js';
20
+ import { verifySignedInRequester } from '../local/cloudflare-checkup/identity-verification.js';
21
+ import { localResultPlainLines, readCheckupReport } from '../lib/checkup-report.js';
20
22
  import { resolveConfigDir } from '../lib/config.js';
21
23
  import { isHeadlessEnvironment, openUrlInBrowser } from '../lib/open-url.js';
22
24
  import { shouldShowProgress } from '../lib/progress.js';
25
+ import { UNICODE_SYMBOLS, createStyles } from '../lib/tui.js';
23
26
  import { CF_SUPPORTED, NOT_COLLECTABLE_ZONE_KINDS, PRODUCTION_ORIGIN, RECOMMENDED_CF_SCOPES, SNAPSHOT_COLLECTOR_NAME, SNAPSHOT_LIMITS, SNAPSHOT_SCHEMA_VERSION, } from '../local/cloudflare-checkup/contract.js';
24
27
  import { CF_DAMAGED_HINT, CF_NOT_FOUND_ADVANCED_HINT, ENV_TOKEN_AUTH_HINT, LOCAL_ERROR_HINTS, LocalCheckupError, SNAPSHOT_STALE_FILE_HINT, } from '../local/cloudflare-checkup/errors.js';
25
28
  import { CF_TOKEN_ENV, buildChildEnv, childEnvVariableNames, hasEnvToken, missingEnvTokenMessage, strippedTokenNotice, } from '../local/cloudflare-checkup/cf/child-env.js';
@@ -53,15 +56,17 @@ Runs the Vigilbase Cloudflare Checkup on this machine, in one command:
53
56
 
54
57
  ${NPX_COMMAND} cloudflare-checkup
55
58
 
56
- It asks for your work email and emails you a 6-digit code to enter, installs
57
- Cloudflare's cf CLI if it is missing (asks first), signs in to Cloudflare with
59
+ It uses your Vigilbase login, skipping email prompts and codes. Signed out,
60
+ it verifies your work email with a 6-digit code. It installs Cloudflare's cf
61
+ CLI if it is missing (asks first), signs in to Cloudflare with
58
62
  a read-only cf login, lets you choose the account, collects a snapshot, shows
59
63
  what it contains, uploads it only after you confirm and opens the results.
60
64
  cf owns your Cloudflare login; this CLI never reads, prints, stores or
61
65
  uploads it.
62
66
 
63
67
  Options (the default command, also called run):
64
- --email <address> Your work email (asked for when omitted)
68
+ --email <address> Override the signed-in email; a different
69
+ address requires email verification
65
70
  --account-id <id> Skip the account picker
66
71
  --out <file> Where to save the snapshot (default:
67
72
  <config dir>/checkups/…); --overwrite
@@ -81,8 +86,8 @@ Options (the default command, also called run):
81
86
  (default) or a loopback origin
82
87
  --timeout <milliseconds> · --json
83
88
 
84
- Without a terminal, add --yes: the email (unless --email is given) and the
85
- code are read from stdin, one line each, after their prompts on stderr.
89
+ Without a terminal, add --yes. When email verification is needed, the email
90
+ (unless --email is given) and code are read from stdin after their prompts.
86
91
 
87
92
  Advanced commands:
88
93
  login Create or refresh the read-only cf profile
@@ -93,7 +98,7 @@ Advanced commands:
93
98
  collect --out <file> Collect only [--overwrite] [--account-id];
94
99
  --dry-run shows every cf command (offline)
95
100
  inspect <file> Validate a snapshot and show what it contains
96
- submit <file> Verify your email, then upload a snapshot
101
+ submit <file> Identify yourself, then upload a snapshot
97
102
  [--email] [--yes|--confirm] [--no-open]
98
103
  [--base-url] [--timeout]
99
104
  logout Delete the read-only cf profile and the cf
@@ -1272,14 +1277,23 @@ function assertWithinLimits(server, loaded) {
1272
1277
  throw new LocalCheckupError('SNAPSHOT_TOO_LARGE', `The snapshot is over the vigilbase.com limit (${formatBytes(maxBytes)}, ${maxZones} zones). Nothing was uploaded.`);
1273
1278
  }
1274
1279
  }
1275
- /**
1276
- * The email verification (prompts on stderr, answers from piped stdin
1277
- * without a terminal), then `✓ Verified j***@acme.com`. No address is sent
1278
- * before one is entered, and cf is not touched before it is verified.
1279
- */
1280
+ /** Use the live signed-in identity, or verify an address when no matching login is available. */
1280
1281
  async function verifyRequester(ctx, options, origin) {
1282
+ const client = new VerificationClient(clientOptions(ctx, origin));
1283
+ const configDir = options.configDir;
1284
+ const identity = configDir ? await busy(ctx, 'Using your Vigilbase identity', () => verifySignedInRequester({
1285
+ config: { ...ctx.config, configDir }, client, locale: emailLocale(ctx.env), email: options.email,
1286
+ warn: (message) => ctx.writer.stderr(`Warning: ${message}`),
1287
+ })) : null;
1288
+ if (identity) {
1289
+ if (ctx.tui)
1290
+ ctx.tui.step(`Signed in as ${identity.requester}`);
1291
+ else
1292
+ say(ctx, `✓ Signed in as ${identity.requester}`);
1293
+ return identity;
1294
+ }
1281
1295
  const verified = await verifyEmail({
1282
- client: new VerificationClient(clientOptions(ctx, origin)),
1296
+ client,
1283
1297
  io: promptIO(ctx),
1284
1298
  locale: emailLocale(ctx.env),
1285
1299
  ...(options.email !== undefined ? { email: options.email } : {}),
@@ -1293,10 +1307,10 @@ async function verifyRequester(ctx, options, origin) {
1293
1307
  return verified;
1294
1308
  }
1295
1309
  /**
1296
- * The checkup the verified email started. A session that expired (a long
1297
- * collection, or a slow answer to the upload question) is renewed once with
1298
- * the token and code still in memory; vigilbase.com mints the next session
1299
- * code for the same checkup. The snapshot file is kept either way.
1310
+ * The checkup the verified requester started. An expired session is renewed
1311
+ * once, using a fresh identity proof or the email token and code still in
1312
+ * memory. vigilbase.com mints the next session code for the same checkup.
1313
+ * The snapshot file is kept either way.
1300
1314
  */
1301
1315
  class CheckupSession {
1302
1316
  ctx;
@@ -1323,7 +1337,7 @@ class CheckupSession {
1323
1337
  }
1324
1338
  catch (error) {
1325
1339
  if (error instanceof LocalCheckupError && (error.code === 'VERIFICATION_EXPIRED' || error.code === 'VERIFICATION_INVALID')) {
1326
- throw new LocalCheckupError('SESSION_EXPIRED', 'The checkup session expired, and the code from the email can no longer renew it.', error.status !== undefined ? { status: error.status } : {});
1340
+ throw new LocalCheckupError('SESSION_EXPIRED', 'The checkup session expired, and its identity verification can no longer renew it.', error.status !== undefined ? { status: error.status } : {});
1327
1341
  }
1328
1342
  throw error;
1329
1343
  }
@@ -1365,7 +1379,7 @@ function noteSavedSnapshot(ctx, error, path, origin) {
1365
1379
  if (ctx.tui)
1366
1380
  ctx.tui.uploadLater(submit);
1367
1381
  else
1368
- ctx.writer.stderr(`The snapshot stays saved. To upload it later without collecting again, run ${submit} (it verifies your email again).`);
1382
+ ctx.writer.stderr(`The snapshot stays saved. To upload it later without collecting again, run ${submit} (it checks your identity again).`);
1369
1383
  }
1370
1384
  /** Session checks that need no upload: CLI version, single use, and the bound account. */
1371
1385
  function assertSessionUsable(session, accountId) {
@@ -1515,7 +1529,7 @@ async function runInspect(ctx, options) {
1515
1529
  return ` ${kind.padEnd(34)} ${String(kindCounts.total).padStart(5)}${notOk ? ` (${notOk} not ok)` : ''}`;
1516
1530
  }),
1517
1531
  'Free text and identifiers in this file:',
1518
- ` member emails ${categories.memberEmails} · access-rule values ${categories.accessRuleValues} · non-proxied DNS names ${categories.dnsHostnames} · pseudonymised IPs ${categories.pseudonymizedIps} · rule descriptions ${categories.ruleDescriptions}`,
1532
+ ` member emails ${categories.memberEmails} · access-rule values ${categories.accessRuleValues} · DNS and certificate hostnames ${categories.dnsHostnames} · pseudonymised IPs ${categories.pseudonymizedIps} · rule descriptions ${categories.ruleDescriptions}`,
1519
1533
  'No credentials are included, and origin IPs are pseudonymised.',
1520
1534
  ];
1521
1535
  for (const line of lines)
@@ -1635,6 +1649,10 @@ async function uploadLoaded(ctx, input) {
1635
1649
  ].filter((part) => part !== null);
1636
1650
  const summary = `${result.replayed ? '✓ Already uploaded (same file); nothing changed.' : '✓ Uploaded.'} Free summary: ${parts.join(' · ')}`;
1637
1651
  const emailed = result.resultsUrl !== undefined ? `A link has also been emailed to ${requester}.` : `A link to the results has been emailed to ${requester}.`;
1652
+ // The free checks the server sent (names and zone counts, never zone names) and its premium count.
1653
+ const report = readCheckupReport(result.report);
1654
+ // Appended after the lines above, which stay as they were for scripts.
1655
+ const more = report ? localResultPlainLines(report, { style: createStyles(false), symbols: UNICODE_SYMBOLS, color: false, columns: null }) : [];
1638
1656
  if (ctx.tui) {
1639
1657
  const opening = result.resultsUrl !== undefined && openResults(ctx, result.resultsUrl, origin);
1640
1658
  ctx.tui.uploaded({
@@ -1643,7 +1661,8 @@ async function uploadLoaded(ctx, input) {
1643
1661
  ...(warnings !== undefined ? { warnings } : {}),
1644
1662
  ...(failedChecks !== undefined ? { failed: failedChecks } : {}),
1645
1663
  ...(result.resultsUrl !== undefined ? { resultsUrl: result.resultsUrl } : {}),
1646
- }, [summary, ...(result.resultsUrl !== undefined ? [`Results: ${result.resultsUrl}`] : []), emailed]);
1664
+ ...(report ? { report } : {}),
1665
+ }, [summary, ...(result.resultsUrl !== undefined ? [`Results: ${result.resultsUrl}`] : []), emailed, ...more]);
1647
1666
  }
1648
1667
  else {
1649
1668
  ctx.writer.stdout(summary);
@@ -1652,6 +1671,8 @@ async function uploadLoaded(ctx, input) {
1652
1671
  openResults(ctx, result.resultsUrl, origin);
1653
1672
  }
1654
1673
  ctx.writer.stdout(emailed);
1674
+ for (const line of more)
1675
+ ctx.writer.stdout(line);
1655
1676
  }
1656
1677
  }
1657
1678
  return {