securenow 6.0.2 → 6.1.0

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 (87) hide show
  1. package/CONSUMING-APPS-GUIDE.md +455 -0
  2. package/NPM_README.md +2029 -0
  3. package/README.md +297 -40
  4. package/SKILL-API.md +634 -0
  5. package/SKILL-CLI.md +454 -0
  6. package/cidr.js +83 -0
  7. package/cli/apps.js +585 -0
  8. package/cli/auth.js +280 -0
  9. package/cli/client.js +115 -0
  10. package/cli/config.js +173 -0
  11. package/cli/diagnostics.js +387 -0
  12. package/cli/firewall.js +100 -0
  13. package/cli/fp.js +638 -0
  14. package/cli/init.js +201 -0
  15. package/cli/monitor.js +440 -0
  16. package/cli/run.js +148 -0
  17. package/cli/security.js +980 -0
  18. package/cli/ui.js +386 -0
  19. package/cli/utils.js +127 -0
  20. package/cli.js +466 -455
  21. package/console-instrumentation.js +147 -136
  22. package/docs/ALL-FRAMEWORKS-QUICKSTART.md +1377 -455
  23. package/docs/API-KEYS-GUIDE.md +233 -0
  24. package/docs/ARCHITECTURE.md +3 -3
  25. package/docs/AUTO-BODY-CAPTURE.md +1 -1
  26. package/docs/AUTO-SETUP-SUMMARY.md +331 -0
  27. package/docs/AUTO-SETUP.md +4 -4
  28. package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
  29. package/docs/BODY-CAPTURE-FIX.md +261 -0
  30. package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
  31. package/docs/CHANGELOG-NEXTJS.md +1 -35
  32. package/docs/COMPLETION-REPORT.md +408 -0
  33. package/docs/CUSTOMER-GUIDE.md +16 -16
  34. package/docs/EASIEST-SETUP.md +5 -5
  35. package/docs/ENVIRONMENT-VARIABLES.md +880 -652
  36. package/docs/EXPRESS-BODY-CAPTURE.md +13 -12
  37. package/docs/EXPRESS-SETUP-GUIDE.md +719 -720
  38. package/docs/FINAL-SOLUTION.md +335 -0
  39. package/docs/FIREWALL-GUIDE.md +426 -0
  40. package/docs/IMPLEMENTATION-SUMMARY.md +410 -0
  41. package/docs/INDEX.md +22 -4
  42. package/docs/LOGGING-GUIDE.md +701 -708
  43. package/docs/LOGGING-QUICKSTART.md +234 -255
  44. package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +323 -0
  45. package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
  46. package/docs/NEXTJS-GUIDE.md +14 -14
  47. package/docs/NEXTJS-QUICKSTART.md +1 -1
  48. package/docs/NEXTJS-SETUP-COMPLETE.md +795 -0
  49. package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
  50. package/docs/NUXT-GUIDE.md +166 -0
  51. package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
  52. package/docs/REDACTION-EXAMPLES.md +1 -1
  53. package/docs/REQUEST-BODY-CAPTURE.md +19 -10
  54. package/docs/SOLUTION-SUMMARY.md +312 -0
  55. package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
  56. package/examples/README.md +6 -6
  57. package/examples/instrumentation-with-auto-capture.ts +1 -1
  58. package/examples/nextjs-env-example.txt +2 -2
  59. package/examples/nextjs-instrumentation.js +1 -1
  60. package/examples/nextjs-instrumentation.ts +1 -1
  61. package/examples/nextjs-with-logging-example.md +6 -6
  62. package/examples/nextjs-with-options.ts +1 -1
  63. package/examples/test-nextjs-setup.js +1 -1
  64. package/firewall-cloud.js +212 -0
  65. package/firewall-iptables.js +139 -0
  66. package/firewall-only.js +38 -0
  67. package/firewall-tcp.js +74 -0
  68. package/firewall.js +720 -0
  69. package/free-trial-banner.js +174 -0
  70. package/nextjs-auto-capture.js +199 -207
  71. package/nextjs-middleware.js +186 -181
  72. package/nextjs-webpack-config.js +88 -53
  73. package/nextjs-wrapper.js +158 -158
  74. package/nextjs.d.ts +1 -1
  75. package/nextjs.js +639 -647
  76. package/nuxt-server-plugin.mjs +423 -0
  77. package/nuxt.d.ts +60 -0
  78. package/nuxt.mjs +75 -0
  79. package/package.json +186 -164
  80. package/postinstall.js +6 -6
  81. package/register.d.ts +1 -1
  82. package/register.js +39 -4
  83. package/resolve-ip.js +77 -0
  84. package/tracing.d.ts +2 -1
  85. package/tracing.js +295 -34
  86. package/web-vite.mjs +239 -156
  87. package/LICENSE +0 -15
package/SKILL-CLI.md ADDED
@@ -0,0 +1,454 @@
1
+ # SecureNow CLI — Agent Skill
2
+
3
+ Use the `securenow` CLI to perform security DevOps from the terminal: manage apps, investigate threats, control the firewall, analyze traces, handle false positives, and run instrumented Node.js processes. Every command supports `--json` for machine-readable output.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Install globally (recommended for CLI usage)
9
+ npm install -g securenow
10
+
11
+ # Or install per-project and use via npx
12
+ npm install securenow
13
+ npx securenow <command>
14
+ ```
15
+
16
+ **Full parity with the SDK:** every capability the `securenow` Node SDK exposes has a CLI counterpart — redaction, CIDR matching, log/span emission, firewall preload, config inspection. If you can do it in code, you can do it from the terminal.
17
+
18
+ ### Authenticate
19
+
20
+ ```bash
21
+ securenow login # opens browser OAuth; stores JWT in ~/.securenow/credentials.json
22
+ securenow login --token <JWT> # headless / CI login (get token from dashboard Settings)
23
+ securenow login --local # save credentials to this project only (.securenow/)
24
+ securenow whoami # verify session (shows auth source)
25
+ ```
26
+
27
+ **Per-project credentials:** Use `--local` to keep separate logins in different project directories on the same machine. Credentials resolve in order: `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
28
+
29
+ ### Integrate With Your App
30
+
31
+ The CLI can also instrument any Node.js app at launch — no code changes:
32
+
33
+ ```bash
34
+ # Express, Fastify, NestJS, Koa, Hapi, raw Node.js
35
+ securenow run src/index.js
36
+
37
+ # Or via the Node preload flag
38
+ node -r securenow/register src/index.js
39
+ ```
40
+
41
+ For Next.js, run the interactive scaffolding:
42
+
43
+ ```bash
44
+ securenow init --key snk_live_...
45
+ ```
46
+
47
+ This auto-detects your framework and creates the necessary `instrumentation.ts`, `next.config.js` changes, and writes your API key to `.env.local`.
48
+
49
+ ### Install This Skill in Cursor
50
+
51
+ Save this file as `.cursor/skills/securenow-cli/SKILL.md` in your project. Your AI agent will auto-discover it whenever you ask about CLI commands, security investigations, or firewall management.
52
+
53
+ ## Configuration
54
+
55
+ Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-project):
56
+
57
+ | File | Content |
58
+ |------|---------|
59
+ | `~/.securenow/config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
60
+ | `~/.securenow/credentials.json` | `token`, `email`, `expiresAt` (global) |
61
+ | `.securenow/credentials.json` | `token`, `email`, `expiresAt` (project-local, use `login --local`) |
62
+
63
+ **Credential resolution order:** `SECURENOW_TOKEN` env var → `.securenow/credentials.json` (project) → `~/.securenow/credentials.json` (global).
64
+
65
+ ```bash
66
+ securenow config set apiUrl https://api.securenow.ai
67
+ securenow config set defaultApp my-app-key
68
+ securenow config get # show all
69
+ securenow config get defaultApp # show one
70
+ securenow config path # print file paths + active auth source
71
+ ```
72
+
73
+ Environment overrides: `SECURENOW_TOKEN` (JWT), `SECURENOW_API_URL`, `SECURENOW_APP_URL`, `SECURENOW_APP` (default app key).
74
+
75
+ ## Global Flags
76
+
77
+ | Flag | Short | Effect |
78
+ |------|-------|--------|
79
+ | `--json` | `-j` | JSON output (pipe-friendly) |
80
+ | `--help` | | Show help for any command |
81
+ | `--verbose` | `-v` | Verbose output |
82
+ | `--force` | `-f` | Skip confirmations |
83
+ | `--yes` | `-y` | Auto-confirm prompts |
84
+
85
+ Debug mode: `SECURENOW_DEBUG=1 securenow <cmd>` prints stack traces on errors.
86
+
87
+ ---
88
+
89
+ ## Command Reference
90
+
91
+ ### Run — Instrument Any Node.js App
92
+
93
+ ```bash
94
+ securenow run <script> # auto-detect CJS/ESM, inject OTel preload
95
+ securenow run --watch src/index.js # pass Node flags through
96
+ securenow run --inspect src/server.js --port 3000
97
+ securenow run --firewall-only app.js # preload firewall only (no tracing overhead)
98
+ securenow src/index.js # shorthand — auto-detected as "run"
99
+ ```
100
+
101
+ Spawns `node --require securenow/register [--import otel/hook.mjs] <script>`. ESM detection uses nearest `package.json` `"type"` field or `.mjs`/`.cjs` extension. With `--firewall-only`, uses `securenow/firewall-only` instead (dotenv + firewall, no OpenTelemetry).
102
+
103
+ ### Authentication
104
+
105
+ ```bash
106
+ securenow login # browser-based OAuth (stores in global ~/.securenow/)
107
+ securenow login --token <JWT> # headless / CI login
108
+ securenow login --local # save credentials to project .securenow/ (per-project session)
109
+ securenow logout # clear active credentials (local if present, else global)
110
+ securenow logout --local # clear project-local credentials only
111
+ securenow whoami # show email, user ID, API URL, auth source, expiry, default app
112
+ ```
113
+
114
+ ### Applications
115
+
116
+ ```bash
117
+ securenow apps # list all apps (default subcommand)
118
+ securenow apps list # same as above
119
+ securenow apps create <name> [--hosts h1,h2] [--instance <id>] # interactive instance picker
120
+ securenow apps info <id> # show app details
121
+ securenow apps delete <id> [--force] # delete an app
122
+ securenow apps default <app-key> # set default app for all commands
123
+ securenow apps discover [appId] [--domain example.com] # discover subdomains, add as apps
124
+ securenow apps scan [--yes] # scan all app domains for new subdomains
125
+ ```
126
+
127
+ ### Init — Project Setup
128
+
129
+ ```bash
130
+ securenow init [--key <API_KEY>]
131
+ ```
132
+
133
+ Auto-detects framework (Next.js, Nuxt, Express, Fastify, Koa, Hapi, Node) from `package.json`. Then:
134
+ - **Next.js**: creates `instrumentation.ts/js`, suggests `withSecureNow()` in `next.config`
135
+ - **Nuxt**: tells you to add `securenow/nuxt` to modules
136
+ - **Node/Express/etc.**: suggests adding `-r securenow/register` to start script
137
+ - Writes `SECURENOW_API_KEY` to `.env.local` or `.env` if `--key` provided
138
+
139
+ ### Dashboard & Status
140
+
141
+ ```bash
142
+ securenow status [--app <key>] # dashboard overview
143
+ securenow analytics [--app <key>] # response analytics
144
+ ```
145
+
146
+ ---
147
+
148
+ ### Traces
149
+
150
+ ```bash
151
+ securenow traces [--app <key>] [--limit N] [--start ISO] [--end ISO]
152
+ securenow traces list --app my-app --limit 50
153
+ securenow traces show <traceId> # full trace detail with spans
154
+ securenow traces analyze <traceId> # AI-powered trace analysis
155
+ ```
156
+
157
+ ### Logs
158
+
159
+ ```bash
160
+ securenow logs [--app <key>] [--limit N] [--minutes M] [--level error|warn|info]
161
+ securenow logs list --app my-app --minutes 30 --level error
162
+ securenow logs trace <traceId> # logs correlated to a specific trace
163
+ ```
164
+
165
+ ### Notifications
166
+
167
+ ```bash
168
+ securenow notifications [--limit N] [--page P]
169
+ securenow notifications list --limit 20
170
+ securenow notifications read <id> # mark one as read
171
+ securenow notifications read-all # mark all as read
172
+ securenow notifications unread # unread count
173
+ ```
174
+
175
+ ### Alerts
176
+
177
+ ```bash
178
+ securenow alerts # list alert rules (default)
179
+ securenow alerts rules # list alert rules (columns: Status, Applications, Schedule)
180
+ securenow alerts rules show <id> # one rule; JSON: --json
181
+ securenow alerts rules update <id> --applications-all # all current & future apps
182
+ securenow alerts rules update <id> --apps key1,key2 # explicit app keys only
183
+ securenow alerts channels # list alert channels (Slack, email, etc.)
184
+ securenow alerts history [--limit N] # past triggered alerts
185
+ ```
186
+
187
+ ---
188
+
189
+ ### IP Intelligence
190
+
191
+ ```bash
192
+ securenow ip <ip-address> # lookup (geo, ASN, threat score, reputation)
193
+ securenow ip lookup <ip-address> # same as above
194
+ securenow ip traces <ip-address> # traces originating from this IP
195
+ ```
196
+
197
+ ### Forensics — Natural Language Security Queries
198
+
199
+ ```bash
200
+ securenow forensics "show me all SQL injection attempts in the last 24h"
201
+ securenow forensics query "top 10 IPs by blocked requests" --app my-app
202
+ securenow forensics chat --app my-app # interactive forensics chat session
203
+ securenow forensics library # view saved/template queries
204
+ ```
205
+
206
+ ### API Map
207
+
208
+ ```bash
209
+ securenow api-map # list discovered API endpoints
210
+ securenow api-map list # same
211
+ securenow api-map stats # endpoint statistics
212
+ ```
213
+
214
+ ---
215
+
216
+ ### Firewall
217
+
218
+ ```bash
219
+ securenow firewall # show status (default)
220
+ securenow firewall status # layers, sync time, blocked count, API key info
221
+ securenow firewall test-ip <ip> # check if IP would be blocked
222
+ ```
223
+
224
+ ### Blocklist — Block Malicious IPs
225
+
226
+ ```bash
227
+ securenow blocklist # list blocked IPs
228
+ securenow blocklist list
229
+ securenow blocklist add <ip> [--reason "Brute force"]
230
+ securenow blocklist remove <id>
231
+ securenow blocklist stats # block counts, top reasons
232
+ ```
233
+
234
+ ### Allowlist — Restrict to Known IPs
235
+
236
+ ```bash
237
+ securenow allowlist # list allowed IPs
238
+ securenow allowlist list
239
+ securenow allowlist add <ip> [--label "Office"] [--reason "Corporate VPN"]
240
+ securenow allowlist remove <id>
241
+ securenow allowlist stats
242
+ ```
243
+
244
+ ### Trusted Proxies
245
+
246
+ ```bash
247
+ securenow trusted # list trusted IPs
248
+ securenow trusted list
249
+ securenow trusted add <ip> [--label "CloudFlare edge"]
250
+ securenow trusted remove <id>
251
+ ```
252
+
253
+ ---
254
+
255
+ ### False Positive Management
256
+
257
+ The `fp` command manages exclusion rules that prevent known-safe traffic from triggering security alerts.
258
+
259
+ ```bash
260
+ securenow fp # list all exclusion rules
261
+ securenow fp list
262
+ securenow fp show <id> # rule details
263
+ securenow fp delete <id> [--yes]
264
+
265
+ # Create exclusion rules
266
+ securenow fp create \
267
+ --conditions '[{"field":"http.target","op":"starts_with","value":"/api/health"}]' \
268
+ --match-mode all \
269
+ --rule-scope any_rule \
270
+ --reason "Health check endpoint"
271
+
272
+ # Shorthand safe-value presets
273
+ securenow fp create \
274
+ --path /api/events \
275
+ --method POST \
276
+ --path-safe standard \
277
+ --ua-safe standard \
278
+ --headers-safe standard \
279
+ --query-keys page,limit \
280
+ --headers-keys host,content-type \
281
+ --reason "Event webhook"
282
+
283
+ # Edit an existing rule
284
+ securenow fp edit <id> [--active true|false] [--conditions '[...]']
285
+
286
+ # Test conditions against a request body
287
+ securenow fp test-body '{"user":"admin"}' --conditions '[{"field":"body.user","op":"eq","value":"admin"}]'
288
+ securenow fp test-body @request.json --conditions '[...]'
289
+
290
+ # Dry-run conditions against the last 3 days of live traces
291
+ securenow fp dry-run --conditions '[{"field":"http.target","op":"starts_with","value":"/api/webhook"}]'
292
+
293
+ # AI-generate exclusion conditions from a description
294
+ securenow fp ai-fill --description "Stripe webhook POST to /api/stripe/webhook" \
295
+ --context '{"method":"POST","path":"/api/stripe/webhook"}'
296
+
297
+ # Mark an IP as false positive on a specific notification
298
+ securenow fp mark <notification-id> <ip> \
299
+ [--conditions '[...]'] \
300
+ [--reason "Known partner IP"] \
301
+ [--rule-scope this_rule|specific_rules|all_existing|any_rule] \
302
+ [--target-rules id1,id2]
303
+ ```
304
+
305
+ **Condition fields:** `http.target`, `http.method`, `http.url`, `http.user_agent`, `http.request.header.*`, `body.*`, `http.status_code`, `net.peer.ip`, and more.
306
+
307
+ **Operators:** `eq`, `neq`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex`, `in`, `not_in`, `exists`, `not_exists`, `gt`, `lt`, `gte`, `lte`.
308
+
309
+ **Match modes:** `all` (AND logic), `any` (OR logic).
310
+
311
+ **Rule scopes:** `this_rule` (single alert rule), `specific_rules` (comma-separated IDs via `--target-rules`), `all_existing` (all current rules), `any_rule` (all current and future rules).
312
+
313
+ **Safe-value presets:** `standard` or `strict`. These auto-generate conditions that whitelist common safe patterns for paths, query strings, user-agents, and headers.
314
+
315
+ ---
316
+
317
+ ### Instances
318
+
319
+ ```bash
320
+ securenow instances # list ClickHouse instances
321
+ securenow instances list
322
+ securenow instances test <id> # test connection
323
+ ```
324
+
325
+ ---
326
+
327
+ ### Telemetry — Emit Logs and Spans From the Shell
328
+
329
+ Mirrors the SDK's `getLogger()` and tracing APIs. Useful for cron jobs, shell scripts, and CI pipelines that need to push events into SecureNow without booting the full OTel SDK.
330
+
331
+ ```bash
332
+ # Send a structured log record to the OTLP collector
333
+ securenow log send "Deployment completed" --level info --attrs version=1.2.3,service=api
334
+ securenow log send "Backup failed" --level error --attrs host=db-01
335
+
336
+ # Emit a test span to verify the collector accepts OTLP traffic
337
+ securenow test-span
338
+ securenow test-span "ci.smoke-test" # custom span name
339
+ ```
340
+
341
+ Both commands use the resolved `SECURENOW_INSTANCE` / `OTEL_EXPORTER_OTLP_*` endpoints. Honors `OTEL_EXPORTER_OTLP_HEADERS` for API-key auth. Returns non-zero on HTTP errors so CI/cron can detect failures.
342
+
343
+ ### Utilities — Redaction, CIDR, Diagnostics
344
+
345
+ These mirror the SDK exports (`redactSensitiveData`, `createMatcher`, `parseCidr`) so agents can validate behavior without writing Node.
346
+
347
+ ```bash
348
+ # Redact sensitive fields (password, token, card, ssn, etc.)
349
+ securenow redact '{"user":"alice","password":"s3cret","card":"4242"}'
350
+ securenow redact @request.json --fields internal_id,sessionHash
351
+
352
+ # CIDR operations — match an IP against a list, or parse a range
353
+ securenow cidr match 10.0.0.5 10.0.0.0/24,192.168.0.0/16
354
+ securenow cidr parse 10.0.0.0/24 # network, broadcast, mask, size
355
+
356
+ # Show resolved config (service name, endpoints, env vars, firewall layers)
357
+ securenow env # human-readable
358
+ securenow env --json # pipe to jq
359
+
360
+ # End-to-end diagnostic: probe OTLP + API endpoints, check config
361
+ securenow doctor # exits 0 if healthy, 1 otherwise
362
+ securenow doctor --json
363
+ ```
364
+
365
+ The `redact` command accepts a JSON string or `@path/to/file.json`, layers your `--fields` flag on top of `DEFAULT_SENSITIVE_FIELDS`, and also honors `SECURENOW_SENSITIVE_FIELDS` from the env. Exit code from `cidr match` is `0` if the IP matches the list, `2` otherwise — scriptable.
366
+
367
+ ---
368
+
369
+ ## Workflow Examples for Agentic AI
370
+
371
+ ### Investigate a Security Alert
372
+
373
+ ```bash
374
+ securenow notifications list --limit 5 --json
375
+ # IMPORTANT: parse the ipInvestigations array for each notification — see statuses below
376
+ securenow ip <attacker-ip> --json
377
+ securenow ip traces <attacker-ip> --json
378
+ securenow traces show <trace-id> --json
379
+ securenow traces analyze <trace-id> --json
380
+ # Decision: block the IP
381
+ securenow blocklist add <attacker-ip> --reason "Automated: SQL injection detected"
382
+ ```
383
+
384
+ #### Notification IP Investigation Statuses
385
+
386
+ Each notification contains an `ipInvestigations` array. **Every IP has its own `status` field** that you MUST check before taking action or raising flags. Do not rely only on top-level notification fields — always cross-reference each IP's individual investigation status.
387
+
388
+ | Status | Meaning | Action |
389
+ |--------|---------|--------|
390
+ | `open` | Still needs review — no determination yet | Investigate: run `securenow ip <ip>`, check traces, decide to block or dismiss |
391
+ | `false_positive` | Trusted/dismissed by exclusion rule, verified bot, or trusted IP list | **Skip** — greyed out in UI, no action needed |
392
+ | `blocked` | Already on the user's blocklist | **Skip** — already handled |
393
+ | `clean` | Pipeline analyzed and cleared as benign | **Skip** — verified safe |
394
+
395
+ **Critical workflow rule:** When investigating notifications, iterate `ipInvestigations[]` and **only flag or act on IPs with `status: "open"`**. IPs marked `false_positive`, `blocked`, or `clean` have already been triaged — do not re-flag them.
396
+
397
+ ### Triage and Suppress a False Positive
398
+
399
+ ```bash
400
+ securenow notifications list --json
401
+ # Identify a false positive notification
402
+ securenow fp ai-fill --description "Stripe webhook calls to /api/stripe/webhook"
403
+ # Review the suggested conditions, then create the rule
404
+ securenow fp create --conditions '<ai-suggested-conditions>' --rule-scope any_rule --reason "Stripe webhook"
405
+ # Or directly mark the notification's IP as FP
406
+ securenow fp mark <notification-id> <ip> --rule-scope this_rule --reason "Known Stripe IP"
407
+ ```
408
+
409
+ ### Onboard a New Application
410
+
411
+ ```bash
412
+ securenow apps create my-new-app --hosts api.example.com,app.example.com
413
+ securenow apps default my-new-app
414
+ securenow init --key snk_live_abc123...
415
+ securenow run src/index.js
416
+ securenow status --json
417
+ ```
418
+
419
+ ### Security Posture Check
420
+
421
+ ```bash
422
+ securenow status --json
423
+ securenow firewall status --json
424
+ securenow blocklist stats --json
425
+ securenow api-map stats --json
426
+ securenow forensics "summarize all attacks in the last 7 days"
427
+ ```
428
+
429
+ ### Discover Attack Surface
430
+
431
+ ```bash
432
+ securenow apps discover --domain example.com
433
+ securenow apps scan --yes
434
+ securenow api-map list --json
435
+ securenow api-map stats --json
436
+ ```
437
+
438
+ ---
439
+
440
+ ## Output Parsing
441
+
442
+ All commands support `--json` for structured output. When piping to other tools or parsing programmatically, always use `--json`. Table output is the default for human readability.
443
+
444
+ ## Error Handling
445
+
446
+ | Exit code / Error | Meaning | Recovery |
447
+ |------------------|---------|----------|
448
+ | `Session expired` | JWT expired | `securenow login` (or `login --local`) |
449
+ | `Not logged in` | No token found | `securenow login` or set `SECURENOW_TOKEN` env var |
450
+ | `Access denied (403)` | Insufficient plan or permissions | Upgrade plan or check user role |
451
+ | `Cannot connect` | API unreachable | Check `SECURENOW_API_URL` or network |
452
+ | `Unknown command` | Typo or unrecognized command | `securenow help` |
453
+
454
+ Set `SECURENOW_DEBUG=1` for full stack traces on any error.
package/cidr.js ADDED
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Bitmask-based CIDR matching. No regex on user input — prevents ReDoS.
5
+ * All operations use unsigned 32-bit integers for IPv4 addresses.
6
+ */
7
+
8
+ function ipToInt(ip) {
9
+ const parts = ip.split('.');
10
+ if (parts.length !== 4) return null;
11
+ let result = 0;
12
+ for (let i = 0; i < 4; i++) {
13
+ const n = parseInt(parts[i], 10);
14
+ if (isNaN(n) || n < 0 || n > 255) return null;
15
+ result = (result << 8) + n;
16
+ }
17
+ return result >>> 0;
18
+ }
19
+
20
+ function parseCidr(cidr) {
21
+ const slash = cidr.indexOf('/');
22
+ if (slash === -1) return null;
23
+ const ip = cidr.slice(0, slash);
24
+ const bits = parseInt(cidr.slice(slash + 1), 10);
25
+ if (isNaN(bits) || bits < 0 || bits > 32) return null;
26
+ const network = ipToInt(ip);
27
+ if (network === null) return null;
28
+ const mask = bits === 0 ? 0 : (~((1 << (32 - bits)) - 1)) >>> 0;
29
+ return { network: (network & mask) >>> 0, mask };
30
+ }
31
+
32
+ function matchesCidr(ipInt, cidrEntry) {
33
+ return ((ipInt & cidrEntry.mask) >>> 0) === cidrEntry.network;
34
+ }
35
+
36
+ /**
37
+ * Create a matcher from a list of IPs and CIDRs.
38
+ * Returns { isBlocked(ip), stats() }.
39
+ * Exact IPs use a Set for O(1) lookup; CIDRs use array scan (typically < 100 entries).
40
+ */
41
+ function createMatcher(ipList) {
42
+ const exactSet = new Set();
43
+ const cidrRanges = [];
44
+
45
+ for (const entry of ipList) {
46
+ const trimmed = (entry || '').trim();
47
+ if (!trimmed) continue;
48
+
49
+ if (trimmed.includes('/')) {
50
+ const parsed = parseCidr(trimmed);
51
+ if (parsed) cidrRanges.push(parsed);
52
+ } else {
53
+ const normalized = trimmed.replace(/^::ffff:/, '');
54
+ exactSet.add(normalized);
55
+ }
56
+ }
57
+
58
+ function isBlocked(ip) {
59
+ if (!ip) return false;
60
+ const normalized = ip.replace(/^::ffff:/, '');
61
+
62
+ if (exactSet.has(normalized)) return true;
63
+
64
+ if (cidrRanges.length > 0) {
65
+ const ipInt = ipToInt(normalized);
66
+ if (ipInt !== null) {
67
+ for (const cidr of cidrRanges) {
68
+ if (matchesCidr(ipInt, cidr)) return true;
69
+ }
70
+ }
71
+ }
72
+
73
+ return false;
74
+ }
75
+
76
+ function stats() {
77
+ return { exact: exactSet.size, cidr: cidrRanges.length, total: exactSet.size + cidrRanges.length };
78
+ }
79
+
80
+ return { isBlocked, stats };
81
+ }
82
+
83
+ module.exports = { ipToInt, parseCidr, matchesCidr, createMatcher };