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.
- package/CONSUMING-APPS-GUIDE.md +455 -0
- package/NPM_README.md +2029 -0
- package/README.md +297 -40
- package/SKILL-API.md +634 -0
- package/SKILL-CLI.md +454 -0
- package/cidr.js +83 -0
- package/cli/apps.js +585 -0
- package/cli/auth.js +280 -0
- package/cli/client.js +115 -0
- package/cli/config.js +173 -0
- package/cli/diagnostics.js +387 -0
- package/cli/firewall.js +100 -0
- package/cli/fp.js +638 -0
- package/cli/init.js +201 -0
- package/cli/monitor.js +440 -0
- package/cli/run.js +148 -0
- package/cli/security.js +980 -0
- package/cli/ui.js +386 -0
- package/cli/utils.js +127 -0
- package/cli.js +466 -455
- package/console-instrumentation.js +147 -136
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +1377 -455
- package/docs/API-KEYS-GUIDE.md +233 -0
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/AUTO-BODY-CAPTURE.md +1 -1
- package/docs/AUTO-SETUP-SUMMARY.md +331 -0
- package/docs/AUTO-SETUP.md +4 -4
- package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
- package/docs/BODY-CAPTURE-FIX.md +261 -0
- package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
- package/docs/CHANGELOG-NEXTJS.md +1 -35
- package/docs/COMPLETION-REPORT.md +408 -0
- package/docs/CUSTOMER-GUIDE.md +16 -16
- package/docs/EASIEST-SETUP.md +5 -5
- package/docs/ENVIRONMENT-VARIABLES.md +880 -652
- package/docs/EXPRESS-BODY-CAPTURE.md +13 -12
- package/docs/EXPRESS-SETUP-GUIDE.md +719 -720
- package/docs/FINAL-SOLUTION.md +335 -0
- package/docs/FIREWALL-GUIDE.md +426 -0
- package/docs/IMPLEMENTATION-SUMMARY.md +410 -0
- package/docs/INDEX.md +22 -4
- package/docs/LOGGING-GUIDE.md +701 -708
- package/docs/LOGGING-QUICKSTART.md +234 -255
- package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +323 -0
- package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
- package/docs/NEXTJS-GUIDE.md +14 -14
- package/docs/NEXTJS-QUICKSTART.md +1 -1
- package/docs/NEXTJS-SETUP-COMPLETE.md +795 -0
- package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
- package/docs/NUXT-GUIDE.md +166 -0
- package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
- package/docs/REDACTION-EXAMPLES.md +1 -1
- package/docs/REQUEST-BODY-CAPTURE.md +19 -10
- package/docs/SOLUTION-SUMMARY.md +312 -0
- package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
- package/examples/README.md +6 -6
- package/examples/instrumentation-with-auto-capture.ts +1 -1
- package/examples/nextjs-env-example.txt +2 -2
- package/examples/nextjs-instrumentation.js +1 -1
- package/examples/nextjs-instrumentation.ts +1 -1
- package/examples/nextjs-with-logging-example.md +6 -6
- package/examples/nextjs-with-options.ts +1 -1
- package/examples/test-nextjs-setup.js +1 -1
- package/firewall-cloud.js +212 -0
- package/firewall-iptables.js +139 -0
- package/firewall-only.js +38 -0
- package/firewall-tcp.js +74 -0
- package/firewall.js +720 -0
- package/free-trial-banner.js +174 -0
- package/nextjs-auto-capture.js +199 -207
- package/nextjs-middleware.js +186 -181
- package/nextjs-webpack-config.js +88 -53
- package/nextjs-wrapper.js +158 -158
- package/nextjs.d.ts +1 -1
- package/nextjs.js +639 -647
- package/nuxt-server-plugin.mjs +423 -0
- package/nuxt.d.ts +60 -0
- package/nuxt.mjs +75 -0
- package/package.json +186 -164
- package/postinstall.js +6 -6
- package/register.d.ts +1 -1
- package/register.js +39 -4
- package/resolve-ip.js +77 -0
- package/tracing.d.ts +2 -1
- package/tracing.js +295 -34
- package/web-vite.mjs +239 -156
- package/LICENSE +0 -15
package/NPM_README.md
ADDED
|
@@ -0,0 +1,2029 @@
|
|
|
1
|
+
# SecureNow - Complete OpenTelemetry Observability for Node.js
|
|
2
|
+
|
|
3
|
+
OpenTelemetry instrumentation library for Node.js, Next.js, and Nuxt applications. Send distributed traces and logs to any OTLP-compatible observability backend.
|
|
4
|
+
|
|
5
|
+
**Features:**
|
|
6
|
+
- Zero-config automatic instrumentation
|
|
7
|
+
- Distributed tracing for all popular frameworks
|
|
8
|
+
- Automatic logging with console instrumentation
|
|
9
|
+
- Built-in sensitive data redaction
|
|
10
|
+
- Request body capture for debugging
|
|
11
|
+
- Multi-layer firewall -- auto-blocks IPs from your SecureNow blocklist
|
|
12
|
+
- `withSecureNow()` config wrapper for Next.js -- eliminates manual `serverExternalPackages`
|
|
13
|
+
- `securenow init` CLI scaffolds instrumentation files for any framework
|
|
14
|
+
- `securenow/firewall-only` entry point for firewall without tracing overhead
|
|
15
|
+
- Fully configurable via environment variables
|
|
16
|
+
- Single `-r securenow/register` flag -- works for both CJS and ESM apps
|
|
17
|
+
- Native Nuxt 3 module (`securenow/nuxt`)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Table of Contents
|
|
22
|
+
|
|
23
|
+
- [Installation](#installation)
|
|
24
|
+
- [Quick Start](#quick-start)
|
|
25
|
+
- [CLI -- Command Line Interface](#cli--command-line-interface)
|
|
26
|
+
- [Framework-Specific Setup](#framework-specific-setup)
|
|
27
|
+
- [Express.js](#expressjs)
|
|
28
|
+
- [Next.js](#nextjs)
|
|
29
|
+
- [Nuxt 3](#nuxt-3)
|
|
30
|
+
- [Fastify](#fastify)
|
|
31
|
+
- [NestJS](#nestjs)
|
|
32
|
+
- [Koa](#koa)
|
|
33
|
+
- [Hapi](#hapi)
|
|
34
|
+
- [Firewall -- Automatic IP Blocking](#firewall--automatic-ip-blocking)
|
|
35
|
+
- [Environment Variables Reference](#environment-variables-reference)
|
|
36
|
+
- [Logging Setup](#logging-setup)
|
|
37
|
+
- [Request Body Capture](#request-body-capture)
|
|
38
|
+
- [Advanced Configuration](#advanced-configuration)
|
|
39
|
+
- [TypeScript Support](#typescript-support)
|
|
40
|
+
- [Troubleshooting](#troubleshooting)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install securenow
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or with yarn:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
yarn add securenow
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### 1. Automatic Setup (Recommended)
|
|
61
|
+
|
|
62
|
+
Run the init command after installing:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx securenow init --key snk_live_abc123...
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This detects your framework and:
|
|
69
|
+
- **Next.js**: Creates `instrumentation.ts`, suggests `withSecureNow()` for `next.config.js`
|
|
70
|
+
- **Nuxt 3**: Suggests adding `securenow/nuxt` to modules
|
|
71
|
+
- **Express / Node.js**: Shows how to add `-r securenow/register` to your start script
|
|
72
|
+
- **All**: Writes `SECURENOW_API_KEY` to `.env.local` when `--key` is provided
|
|
73
|
+
|
|
74
|
+
### 2. Manual Setup
|
|
75
|
+
|
|
76
|
+
#### Set Environment Variables
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Required: Your application identifier
|
|
80
|
+
export SECURENOW_APPID=my-app
|
|
81
|
+
|
|
82
|
+
# Required: Your OTLP collector endpoint
|
|
83
|
+
export SECURENOW_INSTANCE=http://your-otlp-collector:4318
|
|
84
|
+
|
|
85
|
+
# Optional: Enable logging
|
|
86
|
+
export SECURENOW_LOGGING_ENABLED=1
|
|
87
|
+
|
|
88
|
+
# Optional: Enable the firewall (set your API key)
|
|
89
|
+
export SECURENOW_API_KEY=snk_live_abc123...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Run Your Application
|
|
93
|
+
|
|
94
|
+
Add `-r securenow/register` to your start command -- that's the only change:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
node -r securenow/register app.js
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
ESM and CJS apps are both handled automatically (Node >=20.6 auto-registers the ESM loader hook via `module.register()`).
|
|
101
|
+
|
|
102
|
+
**package.json** example:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
"scripts": {
|
|
106
|
+
"start": "node -r securenow/register app.js",
|
|
107
|
+
"dev": "node -r securenow/register --watch app.js"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Alternative: Use NODE_OPTIONS** so your existing scripts stay unchanged:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
NODE_OPTIONS="-r securenow/register" npm start
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**CJS only: Code-based initialization**
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
// At the very top of your main file, before any other require
|
|
121
|
+
require('securenow/register');
|
|
122
|
+
|
|
123
|
+
const express = require('express');
|
|
124
|
+
const app = express();
|
|
125
|
+
// ...
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
You'll see confirmation in the console:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
[securenow] OTel SDK started -> http://your-otlp-collector:4318/v1/traces
|
|
132
|
+
[securenow] Firewall: ENABLED
|
|
133
|
+
[securenow] Firewall: synced 142 blocked IPs (138 exact + 4 CIDR ranges)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## CLI -- Command Line Interface
|
|
139
|
+
|
|
140
|
+
The `securenow` CLI gives you full access to the SecureNow platform from the terminal -- no browser required for day-to-day workflows. Zero additional dependencies.
|
|
141
|
+
|
|
142
|
+
**Full CLI/SDK parity (v6.1.0+):** every SDK export has a matching CLI command. `redactSensitiveData` → `securenow redact`, `createMatcher` → `securenow cidr match`, `getLogger().emit()` → `securenow log send`, `SECURENOW_TEST_SPAN` → `securenow test-span`, `node -r securenow/firewall-only` → `securenow run --firewall-only`. False-positive triage (`fp create`, `fp ai-fill`, `fp mark`) works from the terminal without the web dashboard.
|
|
143
|
+
|
|
144
|
+
### Getting Started
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Log in (opens browser for OAuth)
|
|
148
|
+
npx securenow login
|
|
149
|
+
|
|
150
|
+
# Or use a token for CI/headless environments
|
|
151
|
+
npx securenow login --token <YOUR_JWT>
|
|
152
|
+
|
|
153
|
+
# Log in for this project only (per-project credentials)
|
|
154
|
+
npx securenow login --local
|
|
155
|
+
|
|
156
|
+
# Check who you're logged in as (shows auth source)
|
|
157
|
+
npx securenow whoami
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Project Setup
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Auto-detect framework and scaffold instrumentation files
|
|
164
|
+
npx securenow init
|
|
165
|
+
|
|
166
|
+
# Pass your API key to auto-write it to .env.local
|
|
167
|
+
npx securenow init --key snk_live_abc123...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
For Next.js projects, `init` creates `instrumentation.ts` (or `.js` if no TypeScript) and tells you how to update `next.config.js` with `withSecureNow()`. For Nuxt, it suggests adding `securenow/nuxt` to your modules. For Express/Node, it shows the `-r securenow/register` flag.
|
|
171
|
+
|
|
172
|
+
### Managing Applications
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# List all applications
|
|
176
|
+
npx securenow apps
|
|
177
|
+
|
|
178
|
+
# Create a new application
|
|
179
|
+
npx securenow apps create my-production-app --hosts api.example.com,app.example.com
|
|
180
|
+
|
|
181
|
+
# Get application details (including the app key)
|
|
182
|
+
npx securenow apps info <app-id>
|
|
183
|
+
|
|
184
|
+
# Set a default app so you don't need --app on every command
|
|
185
|
+
npx securenow apps default <app-key>
|
|
186
|
+
|
|
187
|
+
# Delete an application
|
|
188
|
+
npx securenow apps delete <app-id> --force
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Viewing Traces
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# List recent traces (uses default app, or specify --app)
|
|
195
|
+
npx securenow traces
|
|
196
|
+
npx securenow traces --app <key> --limit 50
|
|
197
|
+
|
|
198
|
+
# Show detailed spans for a trace
|
|
199
|
+
npx securenow traces show <traceId>
|
|
200
|
+
|
|
201
|
+
# AI-powered security analysis of a trace
|
|
202
|
+
npx securenow traces analyze <traceId>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Viewing Logs
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# List recent logs
|
|
209
|
+
npx securenow logs
|
|
210
|
+
npx securenow logs --app <key> --minutes 30 --level error
|
|
211
|
+
|
|
212
|
+
# Show logs for a specific trace
|
|
213
|
+
npx securenow logs trace <traceId>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Notifications
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# List notifications
|
|
220
|
+
npx securenow notifications
|
|
221
|
+
|
|
222
|
+
# Check unread count
|
|
223
|
+
npx securenow notifications unread
|
|
224
|
+
|
|
225
|
+
# Mark as read
|
|
226
|
+
npx securenow notifications read <id>
|
|
227
|
+
npx securenow notifications read-all
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Alerting
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# View alert rules, channels, and history
|
|
234
|
+
npx securenow alerts rules
|
|
235
|
+
npx securenow alerts rules show <rule-id>
|
|
236
|
+
npx securenow alerts rules update <rule-id> --applications-all
|
|
237
|
+
npx securenow alerts rules update <rule-id> --apps key1,key2
|
|
238
|
+
npx securenow alerts channels
|
|
239
|
+
npx securenow alerts history --limit 20
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### IP Intelligence & Blocklist
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Look up any IP -- geo, abuse score, verdict, risk factors
|
|
246
|
+
npx securenow ip 203.0.113.42
|
|
247
|
+
|
|
248
|
+
# Show traces from a specific IP
|
|
249
|
+
npx securenow ip traces 203.0.113.42
|
|
250
|
+
|
|
251
|
+
# Manage the blocklist
|
|
252
|
+
npx securenow blocklist
|
|
253
|
+
npx securenow blocklist add 203.0.113.42 --reason "Brute force scanner"
|
|
254
|
+
npx securenow blocklist remove <id>
|
|
255
|
+
npx securenow blocklist stats
|
|
256
|
+
|
|
257
|
+
# Manage trusted IPs
|
|
258
|
+
npx securenow trusted
|
|
259
|
+
npx securenow trusted add 10.0.0.1 --label "Office VPN"
|
|
260
|
+
npx securenow trusted remove <id>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Forensic Queries
|
|
264
|
+
|
|
265
|
+
Ask questions in plain English -- the AI translates them to SQL and runs them against your data.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Run a forensic query
|
|
269
|
+
npx securenow forensics "show top 10 attacking IPs in the last hour"
|
|
270
|
+
npx securenow forensics "which endpoints had 5xx errors today"
|
|
271
|
+
|
|
272
|
+
# Browse the saved query library
|
|
273
|
+
npx securenow forensics library
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### API Map
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# View all discovered API endpoints
|
|
280
|
+
npx securenow api-map
|
|
281
|
+
|
|
282
|
+
# API map statistics
|
|
283
|
+
npx securenow api-map stats
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Analytics & Dashboard
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Response code analytics
|
|
290
|
+
npx securenow analytics --app <key>
|
|
291
|
+
|
|
292
|
+
# Full dashboard overview (apps, protection status, unread alerts)
|
|
293
|
+
npx securenow status
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Instances
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# List ClickHouse instances
|
|
300
|
+
npx securenow instances
|
|
301
|
+
|
|
302
|
+
# Test an instance connection
|
|
303
|
+
npx securenow instances test <id>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### False-Positive Management
|
|
307
|
+
|
|
308
|
+
Full FP triage from the terminal — no dashboard required.
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# Browse & inspect rules
|
|
312
|
+
npx securenow fp
|
|
313
|
+
npx securenow fp show <rule-id>
|
|
314
|
+
|
|
315
|
+
# Create a rule from AI-drafted conditions
|
|
316
|
+
npx securenow fp ai-fill --description "Stripe webhook POST to /api/stripe/webhook"
|
|
317
|
+
npx securenow fp create --conditions '<ai-output>' --rule-scope any_rule --reason "Stripe webhook"
|
|
318
|
+
|
|
319
|
+
# Create with safe-value presets (shorthand)
|
|
320
|
+
npx securenow fp create \
|
|
321
|
+
--path /api/events --method POST \
|
|
322
|
+
--path-safe standard --ua-safe standard --headers-safe standard \
|
|
323
|
+
--query-keys page,limit --reason "Event webhook"
|
|
324
|
+
|
|
325
|
+
# Test & dry-run before committing
|
|
326
|
+
npx securenow fp test-body '{"user":"admin"}' --conditions '[...]'
|
|
327
|
+
npx securenow fp dry-run --conditions '[...]' # last 3 days of live traces
|
|
328
|
+
|
|
329
|
+
# Mark a notification's IP as FP in one shot
|
|
330
|
+
npx securenow fp mark <notification-id> <ip> --rule-scope this_rule --reason "Known partner IP"
|
|
331
|
+
|
|
332
|
+
# Edit / delete
|
|
333
|
+
npx securenow fp edit <id> --active false
|
|
334
|
+
npx securenow fp delete <id> --yes
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Telemetry -- Emit Logs and Spans From the Shell
|
|
338
|
+
|
|
339
|
+
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**.
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Send a structured log record to your OTLP collector
|
|
343
|
+
npx securenow log send "Deployment completed" --level info --attrs version=1.2.3,service=api
|
|
344
|
+
npx securenow log send "Backup failed" --level error --attrs host=db-01
|
|
345
|
+
|
|
346
|
+
# Emit a test span to verify the collector accepts OTLP traffic
|
|
347
|
+
npx securenow test-span
|
|
348
|
+
npx securenow test-span "ci.smoke-test" # custom span name
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Both commands use the resolved `SECURENOW_INSTANCE` / `OTEL_EXPORTER_OTLP_*` endpoints and honor `OTEL_EXPORTER_OTLP_HEADERS` for API-key auth. Non-zero exit on HTTP errors so CI/cron can detect failures.
|
|
352
|
+
|
|
353
|
+
### Utilities -- Redaction, CIDR, Diagnostics
|
|
354
|
+
|
|
355
|
+
SDK helpers surfaced as CLI commands so agents (and humans) can validate behavior without writing Node.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# Redact sensitive fields (password, token, card, ssn, etc.)
|
|
359
|
+
npx securenow redact '{"user":"alice","password":"s3cret","card":"4242"}'
|
|
360
|
+
npx securenow redact @request.json --fields internal_id,sessionHash
|
|
361
|
+
|
|
362
|
+
# CIDR — match an IP against a list, or parse a range
|
|
363
|
+
npx securenow cidr match 10.0.0.5 10.0.0.0/8,192.168.1.0/24 # exit 0 = hit, 2 = miss
|
|
364
|
+
npx securenow cidr parse 10.0.0.0/24 # network, broadcast, mask, size
|
|
365
|
+
|
|
366
|
+
# Show resolved config (service name, endpoints, env vars, firewall layers)
|
|
367
|
+
npx securenow env # human-readable
|
|
368
|
+
npx securenow env --json # pipe to jq
|
|
369
|
+
|
|
370
|
+
# End-to-end diagnostic: probe OTLP + API endpoints
|
|
371
|
+
npx securenow doctor # exits 0 if healthy, 1 otherwise
|
|
372
|
+
npx securenow doctor --json
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Configuration
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# View all config
|
|
379
|
+
npx securenow config get
|
|
380
|
+
|
|
381
|
+
# Set values
|
|
382
|
+
npx securenow config set apiUrl https://custom-api.example.com
|
|
383
|
+
npx securenow config set defaultApp <app-key>
|
|
384
|
+
npx securenow config set format json
|
|
385
|
+
|
|
386
|
+
# Show config file paths
|
|
387
|
+
npx securenow config path
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Config files are stored in `~/.securenow/` (global) or `.securenow/` in the project root (per-project):
|
|
391
|
+
|
|
392
|
+
| File | Description |
|
|
393
|
+
|------|-------------|
|
|
394
|
+
| `~/.securenow/config.json` | API URL, default app, output format |
|
|
395
|
+
| `~/.securenow/credentials.json` | Auth token — global (file permissions: 0600) |
|
|
396
|
+
| `.securenow/credentials.json` | Auth token — project-local (use `login --local`) |
|
|
397
|
+
|
|
398
|
+
**Resolution order:** `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
|
|
399
|
+
|
|
400
|
+
### Global Flags
|
|
401
|
+
|
|
402
|
+
Every command supports these flags:
|
|
403
|
+
|
|
404
|
+
| Flag | Short | Description |
|
|
405
|
+
|------|-------|-------------|
|
|
406
|
+
| `--json` | `-j` | Output as JSON for scripting and CI/CD |
|
|
407
|
+
| `--help` | | Show help for the command |
|
|
408
|
+
| `--app <key>` | | Override the default application key |
|
|
409
|
+
|
|
410
|
+
### Environment Variables
|
|
411
|
+
|
|
412
|
+
| Variable | Description |
|
|
413
|
+
|----------|-------------|
|
|
414
|
+
| `SECURENOW_TOKEN` | JWT token — overrides all file-based credentials |
|
|
415
|
+
| `SECURENOW_API_URL` | Override the API base URL |
|
|
416
|
+
| `SECURENOW_DEBUG` | Show stack traces on errors |
|
|
417
|
+
| `NO_COLOR` | Disable colored output |
|
|
418
|
+
|
|
419
|
+
### Multi-Project Sessions
|
|
420
|
+
|
|
421
|
+
Use `--local` to maintain separate logins per project on the same machine:
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
# In project A — log in as user-a@company.com
|
|
425
|
+
cd ~/projects/project-a
|
|
426
|
+
npx securenow login --local
|
|
427
|
+
|
|
428
|
+
# In project B — log in as user-b@company.com
|
|
429
|
+
cd ~/projects/project-b
|
|
430
|
+
npx securenow login --local
|
|
431
|
+
|
|
432
|
+
# Each project uses its own credentials independently
|
|
433
|
+
npx securenow whoami # Shows auth source: project (.securenow/)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
You can also use the `SECURENOW_TOKEN` env var for per-terminal sessions without touching any files.
|
|
437
|
+
|
|
438
|
+
### CI/CD Integration
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Authenticate with a token in CI (env var — no file needed)
|
|
442
|
+
SECURENOW_TOKEN=$MY_SECRET npx securenow logs --json
|
|
443
|
+
|
|
444
|
+
# Or use login with explicit token
|
|
445
|
+
npx securenow login --token $SECURENOW_TOKEN
|
|
446
|
+
|
|
447
|
+
# Use --json for machine-readable output
|
|
448
|
+
npx securenow logs --json --level error | jq '.logs'
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Complete Command Reference
|
|
452
|
+
|
|
453
|
+
| Category | Command | Description |
|
|
454
|
+
|----------|---------|-------------|
|
|
455
|
+
| **Setup** | `init` | Auto-scaffold instrumentation for your framework |
|
|
456
|
+
| **Auth** | `login` | Authenticate via browser, `--token`, or `--local` |
|
|
457
|
+
| | `logout` | Clear credentials (`--local` for project only) |
|
|
458
|
+
| | `whoami` | Show session info and auth source |
|
|
459
|
+
| **Apps** | `apps` | List applications |
|
|
460
|
+
| | `apps create <name>` | Create application |
|
|
461
|
+
| | `apps info <id>` | Application details |
|
|
462
|
+
| | `apps delete <id>` | Delete application |
|
|
463
|
+
| | `apps default <key>` | Set default app |
|
|
464
|
+
| **Observe** | `traces` | List traces |
|
|
465
|
+
| | `traces show <id>` | Trace details |
|
|
466
|
+
| | `traces analyze <id>` | AI trace analysis |
|
|
467
|
+
| | `logs` | List logs |
|
|
468
|
+
| | `logs trace <id>` | Logs for a trace |
|
|
469
|
+
| | `analytics` | Response analytics |
|
|
470
|
+
| | `status` | Dashboard overview |
|
|
471
|
+
| **Detect** | `notifications` | List notifications |
|
|
472
|
+
| | `notifications unread` | Unread count |
|
|
473
|
+
| | `notifications read <id>` | Mark read |
|
|
474
|
+
| | `notifications read-all` | Mark all read |
|
|
475
|
+
| | `alerts rules` | List rules (status, apps, schedule) |
|
|
476
|
+
| | `alerts rules show <id>` | Rule detail |
|
|
477
|
+
| | `alerts rules update <id> --applications-all` / `--apps k1,k2` | Application scope |
|
|
478
|
+
| | `alerts channels` | Alert channels |
|
|
479
|
+
| | `alerts history` | Alert history |
|
|
480
|
+
| **Investigate** | `ip <addr>` | IP intelligence |
|
|
481
|
+
| | `ip traces <addr>` | Traces from IP |
|
|
482
|
+
| | `forensics "<query>"` | NL forensic query |
|
|
483
|
+
| | `forensics library` | Saved queries |
|
|
484
|
+
| | `api-map` | API endpoints |
|
|
485
|
+
| | `api-map stats` | API stats |
|
|
486
|
+
| **Firewall** | `firewall status` | Firewall status and API key info |
|
|
487
|
+
| | `firewall test-ip <ip>` | Check if an IP would be blocked |
|
|
488
|
+
| | `run --firewall-only <script>` | Preload firewall without OTel tracing overhead |
|
|
489
|
+
| **Remediate** | `blocklist` | Blocked IPs |
|
|
490
|
+
| | `blocklist add <ip>` | Block IP |
|
|
491
|
+
| | `blocklist remove <id>` | Unblock IP |
|
|
492
|
+
| | `blocklist stats` | Block stats |
|
|
493
|
+
| | `allowlist` | Allowed IPs (restrict-mode) |
|
|
494
|
+
| | `allowlist add <ip>` | Allow IP (`--label`, `--reason`) |
|
|
495
|
+
| | `allowlist remove <id>` | Remove from allowlist |
|
|
496
|
+
| | `trusted` | Trusted IPs |
|
|
497
|
+
| | `trusted add <ip>` | Add trusted IP |
|
|
498
|
+
| | `trusted remove <id>` | Remove trusted |
|
|
499
|
+
| **False Positives** | `fp` / `fp list` | List exclusion rules |
|
|
500
|
+
| | `fp show <id>` | Rule detail |
|
|
501
|
+
| | `fp create --conditions '[...]'` | Create raw exclusion rule |
|
|
502
|
+
| | `fp create --path /api/events --method POST --path-safe standard` | Safe-value preset helper |
|
|
503
|
+
| | `fp edit <id>` | Edit rule (`--active`, `--conditions`) |
|
|
504
|
+
| | `fp delete <id>` | Delete rule |
|
|
505
|
+
| | `fp test-body '<json>' --conditions '[...]'` | Test conditions against a payload |
|
|
506
|
+
| | `fp dry-run --conditions '[...]'` | Dry-run against last 3 days of traces |
|
|
507
|
+
| | `fp ai-fill --description "..."` | AI-generate exclusion conditions |
|
|
508
|
+
| | `fp mark <notification-id> <ip>` | Mark an IP as FP on a notification |
|
|
509
|
+
| **Telemetry** | `log send "<msg>" --level info --attrs k=v` | Emit an OTLP log record |
|
|
510
|
+
| | `test-span [<name>]` | Emit a test span to the collector |
|
|
511
|
+
| **Utilities** | `redact '<json>' [--fields f1,f2]` | Redact sensitive fields (accepts `@file.json`) |
|
|
512
|
+
| | `cidr match <ip> <cidrs>` | IP vs. CIDR list (exit 0 hit / 2 miss) |
|
|
513
|
+
| | `cidr parse <cidr>` | Parse CIDR (network, broadcast, mask, size) |
|
|
514
|
+
| | `env [--json]` | Show resolved config (service name, endpoints, env vars) |
|
|
515
|
+
| | `doctor [--json]` | Probe OTLP + API endpoints, check config |
|
|
516
|
+
| **Settings** | `instances` | List instances |
|
|
517
|
+
| | `instances test <id>` | Test connection |
|
|
518
|
+
| | `config get` | Show config |
|
|
519
|
+
| | `config set <k> <v>` | Set config value |
|
|
520
|
+
| | `config path` | Config file paths |
|
|
521
|
+
| | `version` | Show version |
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## Framework-Specific Setup
|
|
526
|
+
|
|
527
|
+
> **v5.6.0+:** When `SECURENOW_LOGGING_ENABLED=1`, all `console.log`/`warn`/`error`/`info`/`debug` calls
|
|
528
|
+
> are **automatically** forwarded as OTLP log records. The separate `require('securenow/console-instrumentation')` is no longer needed (but still available for backward compat).
|
|
529
|
+
|
|
530
|
+
### Express.js
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
npm install securenow express
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
```javascript
|
|
537
|
+
// app.js
|
|
538
|
+
require('securenow/register');
|
|
539
|
+
const express = require('express');
|
|
540
|
+
|
|
541
|
+
const app = express();
|
|
542
|
+
app.use(express.json());
|
|
543
|
+
|
|
544
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
545
|
+
|
|
546
|
+
app.post('/tasks', (req, res) => {
|
|
547
|
+
console.log('Created task:', req.body.title);
|
|
548
|
+
res.status(201).json({ id: '1', title: req.body.title });
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
app.listen(3000, () => console.log('Express running on port 3000'));
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
node app.js
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
#### With PM2
|
|
559
|
+
|
|
560
|
+
```javascript
|
|
561
|
+
// ecosystem.config.cjs
|
|
562
|
+
module.exports = {
|
|
563
|
+
apps: [{
|
|
564
|
+
name: 'my-app',
|
|
565
|
+
script: './app.js',
|
|
566
|
+
node_args: '-r securenow/register',
|
|
567
|
+
env: {
|
|
568
|
+
SECURENOW_APPID: 'your-app-key',
|
|
569
|
+
SECURENOW_INSTANCE: 'https://freetrial.securenow.ai:4318',
|
|
570
|
+
SECURENOW_API_KEY: 'snk_live_abc123...',
|
|
571
|
+
SECURENOW_LOGGING_ENABLED: '1',
|
|
572
|
+
SECURENOW_NO_UUID: '1',
|
|
573
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
574
|
+
}
|
|
575
|
+
}]
|
|
576
|
+
};
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
> **Important:** Always use `node_args: '-r securenow/register'` in PM2 configs. Without it, PM2 restarts won't load the SDK, and the firewall won't activate.
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
### Fastify
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
npm install securenow fastify
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
```javascript
|
|
590
|
+
// app.js
|
|
591
|
+
require('securenow/register');
|
|
592
|
+
const Fastify = require('fastify');
|
|
593
|
+
const fastify = Fastify({ logger: true });
|
|
594
|
+
|
|
595
|
+
fastify.get('/health', async () => ({ status: 'ok' }));
|
|
596
|
+
|
|
597
|
+
fastify.post('/tasks', {
|
|
598
|
+
schema: { body: { type: 'object', required: ['title'], properties: { title: { type: 'string' } } } }
|
|
599
|
+
}, async (request) => {
|
|
600
|
+
console.log('Created task:', request.body.title);
|
|
601
|
+
return { id: '1', title: request.body.title };
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
fastify.listen({ port: 3000 }, (err) => {
|
|
605
|
+
if (err) { fastify.log.error(err); process.exit(1); }
|
|
606
|
+
console.log('Fastify running on port 3000');
|
|
607
|
+
});
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
> **Important:** Set `SECURENOW_CAPTURE_BODY=0` with Fastify -- the body capture hook conflicts with Fastify's internal stream handling.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
### Koa
|
|
615
|
+
|
|
616
|
+
```bash
|
|
617
|
+
npm install securenow koa @koa/router koa-bodyparser
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
```javascript
|
|
621
|
+
// app.js
|
|
622
|
+
require('securenow/register');
|
|
623
|
+
const Koa = require('koa');
|
|
624
|
+
const Router = require('@koa/router');
|
|
625
|
+
const bodyParser = require('koa-bodyparser');
|
|
626
|
+
|
|
627
|
+
const app = new Koa();
|
|
628
|
+
const router = new Router();
|
|
629
|
+
|
|
630
|
+
router.get('/health', (ctx) => { ctx.body = { status: 'ok' }; });
|
|
631
|
+
|
|
632
|
+
router.post('/tasks', (ctx) => {
|
|
633
|
+
console.log('Created task:', ctx.request.body.title);
|
|
634
|
+
ctx.status = 201;
|
|
635
|
+
ctx.body = { id: '1', title: ctx.request.body.title };
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
app.use(bodyParser());
|
|
639
|
+
app.use(router.routes());
|
|
640
|
+
app.use(router.allowedMethods());
|
|
641
|
+
|
|
642
|
+
app.listen(3000, () => console.log('Koa running on port 3000'));
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
### NestJS
|
|
648
|
+
|
|
649
|
+
```bash
|
|
650
|
+
npm install securenow @nestjs/core @nestjs/common reflect-metadata ts-node typescript
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
NestJS uses TypeScript -- securenow is loaded via `-r` flags instead of in-code `require()`:
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
// app.ts
|
|
657
|
+
import 'reflect-metadata';
|
|
658
|
+
import { NestFactory } from '@nestjs/core';
|
|
659
|
+
import { Module, Controller, Get, Post, Body } from '@nestjs/common';
|
|
660
|
+
|
|
661
|
+
@Controller()
|
|
662
|
+
class AppController {
|
|
663
|
+
@Get('health')
|
|
664
|
+
health() { return { status: 'ok' }; }
|
|
665
|
+
|
|
666
|
+
@Post('tasks')
|
|
667
|
+
create(@Body() body: { title: string }) {
|
|
668
|
+
console.log('Created task:', body.title);
|
|
669
|
+
return { id: '1', title: body.title };
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
@Module({ controllers: [AppController] })
|
|
674
|
+
class AppModule {}
|
|
675
|
+
|
|
676
|
+
async function bootstrap() {
|
|
677
|
+
const app = await NestFactory.create(AppModule);
|
|
678
|
+
await app.listen(3000);
|
|
679
|
+
console.log('NestJS running on port 3000');
|
|
680
|
+
}
|
|
681
|
+
bootstrap();
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
```bash
|
|
685
|
+
node -r securenow/register -r ts-node/register app.ts
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
PM2 config:
|
|
689
|
+
|
|
690
|
+
```javascript
|
|
691
|
+
{
|
|
692
|
+
name: 'my-nestjs-app',
|
|
693
|
+
script: 'app.ts',
|
|
694
|
+
interpreter: 'node',
|
|
695
|
+
node_args: '-r securenow/register -r ts-node/register',
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
### Hapi
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
npm install securenow @hapi/hapi
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
```javascript
|
|
708
|
+
// app.js
|
|
709
|
+
require('securenow/register');
|
|
710
|
+
const Hapi = require('@hapi/hapi');
|
|
711
|
+
|
|
712
|
+
const init = async () => {
|
|
713
|
+
const server = Hapi.server({ port: 3000, host: '0.0.0.0' });
|
|
714
|
+
|
|
715
|
+
server.route({ method: 'GET', path: '/health', handler: () => ({ status: 'ok' }) });
|
|
716
|
+
|
|
717
|
+
server.route({
|
|
718
|
+
method: 'POST', path: '/tasks',
|
|
719
|
+
options: { payload: { parse: true, allow: 'application/json' } },
|
|
720
|
+
handler: (request, h) => {
|
|
721
|
+
console.log('Created task:', request.payload.title);
|
|
722
|
+
return h.response({ id: '1', title: request.payload.title }).code(201);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
await server.start();
|
|
727
|
+
console.log('Hapi running on port 3000');
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
init().catch((err) => { console.error(err); process.exit(1); });
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
> **Important:** Set `SECURENOW_CAPTURE_BODY=0` with Hapi -- the body capture hook consumes the request stream before Hapi's payload parser.
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
### h3 (UnJS / Nitro)
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
npm install securenow h3
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
```javascript
|
|
744
|
+
// app.js
|
|
745
|
+
require('securenow/register');
|
|
746
|
+
const { createApp, createRouter, defineEventHandler, readBody, setResponseStatus, toNodeListener } = require('h3');
|
|
747
|
+
const http = require('http');
|
|
748
|
+
|
|
749
|
+
const app = createApp();
|
|
750
|
+
const router = createRouter();
|
|
751
|
+
|
|
752
|
+
router.get('/health', defineEventHandler(() => ({ status: 'ok' })));
|
|
753
|
+
|
|
754
|
+
router.post('/tasks', defineEventHandler(async (event) => {
|
|
755
|
+
const body = await readBody(event);
|
|
756
|
+
console.log('Created task:', body.title);
|
|
757
|
+
setResponseStatus(event, 201);
|
|
758
|
+
return { id: '1', title: body.title };
|
|
759
|
+
}));
|
|
760
|
+
|
|
761
|
+
app.use(router);
|
|
762
|
+
|
|
763
|
+
http.createServer(toNodeListener(app)).listen(3000, () => {
|
|
764
|
+
console.log('h3 running on port 3000');
|
|
765
|
+
});
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
### Polka
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
npm install securenow polka
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Polka is minimalist -- no built-in body parser:
|
|
777
|
+
|
|
778
|
+
```javascript
|
|
779
|
+
// app.js
|
|
780
|
+
require('securenow/register');
|
|
781
|
+
const polka = require('polka');
|
|
782
|
+
|
|
783
|
+
function jsonBody(req, res, next) {
|
|
784
|
+
if (req.method === 'GET' || req.method === 'DELETE') return next();
|
|
785
|
+
let data = '';
|
|
786
|
+
req.on('data', chunk => { data += chunk; });
|
|
787
|
+
req.on('end', () => { try { req.body = JSON.parse(data); } catch { req.body = {}; } next(); });
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function sendJson(res, status, body) {
|
|
791
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
792
|
+
res.end(JSON.stringify(body));
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
polka()
|
|
796
|
+
.use(jsonBody)
|
|
797
|
+
.get('/health', (req, res) => sendJson(res, 200, { status: 'ok' }))
|
|
798
|
+
.post('/tasks', (req, res) => {
|
|
799
|
+
console.log('Created task:', req.body.title);
|
|
800
|
+
sendJson(res, 201, { id: '1', title: req.body.title });
|
|
801
|
+
})
|
|
802
|
+
.listen(3000, () => console.log('Polka running on port 3000'));
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
### Micro / Raw HTTP
|
|
808
|
+
|
|
809
|
+
For apps using Node's bare `http` module:
|
|
810
|
+
|
|
811
|
+
```bash
|
|
812
|
+
npm install securenow
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
```javascript
|
|
816
|
+
// app.js
|
|
817
|
+
require('securenow/register');
|
|
818
|
+
const http = require('http');
|
|
819
|
+
|
|
820
|
+
function sendJson(res, status, body) {
|
|
821
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
822
|
+
res.end(JSON.stringify(body));
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function readBody(req) {
|
|
826
|
+
return new Promise((resolve) => {
|
|
827
|
+
let d = ''; req.on('data', c => { d += c; }); req.on('end', () => { try { resolve(JSON.parse(d)); } catch { resolve({}); } });
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
async function handler(req, res) {
|
|
832
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
833
|
+
if (url.pathname === '/health') return sendJson(res, 200, { status: 'ok' });
|
|
834
|
+
if (url.pathname === '/tasks' && req.method === 'POST') {
|
|
835
|
+
const body = await readBody(req);
|
|
836
|
+
console.log('Created task:', body.title);
|
|
837
|
+
return sendJson(res, 201, { id: '1', title: body.title });
|
|
838
|
+
}
|
|
839
|
+
sendJson(res, 404, { error: 'Not found' });
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
http.createServer(handler).listen(3000, () => console.log('HTTP running on port 3000'));
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
### Hono (ESM)
|
|
848
|
+
|
|
849
|
+
```bash
|
|
850
|
+
npm install securenow hono @hono/node-server
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
Hono uses ESM -- preload via `-r` flag (the ESM hook is auto-registered on Node >=20.6):
|
|
854
|
+
|
|
855
|
+
```javascript
|
|
856
|
+
// app.mjs
|
|
857
|
+
import { serve } from '@hono/node-server';
|
|
858
|
+
import { Hono } from 'hono';
|
|
859
|
+
|
|
860
|
+
const app = new Hono();
|
|
861
|
+
|
|
862
|
+
app.get('/health', (c) => c.json({ status: 'ok' }));
|
|
863
|
+
|
|
864
|
+
app.post('/tasks', async (c) => {
|
|
865
|
+
const body = await c.req.json();
|
|
866
|
+
console.log('Created task:', body.title);
|
|
867
|
+
return c.json({ id: '1', title: body.title }, 201);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
serve({ fetch: app.fetch, port: 3000 }, () => console.log('Hono running on port 3000'));
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
```bash
|
|
874
|
+
node -r securenow/register app.mjs
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
> **Important:** Set `SECURENOW_CAPTURE_BODY=0` with Hono. Do **not** add `require('securenow/register')` inside `.mjs` files.
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
### Feathers
|
|
882
|
+
|
|
883
|
+
```bash
|
|
884
|
+
npm install securenow @feathersjs/feathers @feathersjs/express @feathersjs/errors
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
Feathers uses Express transport -- same setup as Express:
|
|
888
|
+
|
|
889
|
+
```javascript
|
|
890
|
+
// app.js
|
|
891
|
+
require('securenow/register');
|
|
892
|
+
const feathers = require('@feathersjs/feathers');
|
|
893
|
+
const express = require('@feathersjs/express');
|
|
894
|
+
const errors = require('@feathersjs/errors');
|
|
895
|
+
|
|
896
|
+
class TaskService {
|
|
897
|
+
constructor() { this.tasks = []; this.nextId = 1; }
|
|
898
|
+
async find() { return this.tasks; }
|
|
899
|
+
async create(data) {
|
|
900
|
+
if (!data.title) throw new errors.BadRequest('title is required');
|
|
901
|
+
const task = { id: String(this.nextId++), title: data.title };
|
|
902
|
+
this.tasks.push(task);
|
|
903
|
+
console.log('Created task:', task.id);
|
|
904
|
+
return task;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const app = express(feathers());
|
|
909
|
+
app.use(express.json());
|
|
910
|
+
app.configure(express.rest());
|
|
911
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
912
|
+
app.use('/tasks', new TaskService());
|
|
913
|
+
app.use(express.errorHandler());
|
|
914
|
+
|
|
915
|
+
app.listen(3000, () => console.log('Feathers running on port 3000'));
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
---
|
|
919
|
+
|
|
920
|
+
### Next.js
|
|
921
|
+
|
|
922
|
+
See [Next.js Complete Guide](./docs/NEXTJS-SETUP-COMPLETE.md) for the full reference.
|
|
923
|
+
|
|
924
|
+
#### Option A: `withSecureNow()` wrapper (v5.13.0+ -- Recommended)
|
|
925
|
+
|
|
926
|
+
One wrapper handles everything: `serverExternalPackages` (Next 15) or `experimental.serverComponentsExternalPackages` (Next 14), `instrumentationHook`, and webpack warning suppression.
|
|
927
|
+
|
|
928
|
+
**1. Update `next.config.js`:**
|
|
929
|
+
|
|
930
|
+
```javascript
|
|
931
|
+
const { withSecureNow } = require('securenow/nextjs-webpack-config');
|
|
932
|
+
|
|
933
|
+
module.exports = withSecureNow({
|
|
934
|
+
// your existing config -- reactStrictMode, images, rewrites, etc.
|
|
935
|
+
});
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
**2. Create `instrumentation.ts` (or `.js`):**
|
|
939
|
+
|
|
940
|
+
```typescript
|
|
941
|
+
export async function register() {
|
|
942
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
943
|
+
const { registerSecureNow } = require('securenow/nextjs');
|
|
944
|
+
registerSecureNow();
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
Or run `npx securenow init` to auto-generate this file.
|
|
950
|
+
|
|
951
|
+
**3. Set environment variables in `.env.local`:**
|
|
952
|
+
|
|
953
|
+
```env
|
|
954
|
+
SECURENOW_APPID=my-nextjs-app
|
|
955
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
956
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
957
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
958
|
+
SECURENOW_NO_UUID=1
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
That's it. `withSecureNow()` auto-detects your Next.js version and configures:
|
|
962
|
+
- **Next.js 15+**: Sets `serverExternalPackages` with all 13 required OTel packages
|
|
963
|
+
- **Next.js 14**: Sets `experimental.serverComponentsExternalPackages` and `experimental.instrumentationHook: true`
|
|
964
|
+
- **Both**: Suppresses webpack warnings from OpenTelemetry instrumentation packages
|
|
965
|
+
|
|
966
|
+
#### Option B: Manual configuration
|
|
967
|
+
|
|
968
|
+
If you prefer not to use the wrapper, manually add the packages:
|
|
969
|
+
|
|
970
|
+
```javascript
|
|
971
|
+
// next.config.js (Next.js 15+)
|
|
972
|
+
module.exports = {
|
|
973
|
+
serverExternalPackages: [
|
|
974
|
+
'securenow',
|
|
975
|
+
'@opentelemetry/sdk-node',
|
|
976
|
+
'@opentelemetry/auto-instrumentations-node',
|
|
977
|
+
'@opentelemetry/instrumentation-http',
|
|
978
|
+
'@opentelemetry/exporter-trace-otlp-http',
|
|
979
|
+
'@opentelemetry/exporter-logs-otlp-http',
|
|
980
|
+
'@opentelemetry/sdk-logs',
|
|
981
|
+
'@opentelemetry/instrumentation',
|
|
982
|
+
'@opentelemetry/resources',
|
|
983
|
+
'@opentelemetry/semantic-conventions',
|
|
984
|
+
'@opentelemetry/api',
|
|
985
|
+
'@opentelemetry/api-logs',
|
|
986
|
+
'@vercel/otel',
|
|
987
|
+
],
|
|
988
|
+
};
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
```javascript
|
|
992
|
+
// next.config.js (Next.js 14)
|
|
993
|
+
module.exports = {
|
|
994
|
+
experimental: {
|
|
995
|
+
instrumentationHook: true,
|
|
996
|
+
serverComponentsExternalPackages: [
|
|
997
|
+
'securenow',
|
|
998
|
+
'@opentelemetry/sdk-node',
|
|
999
|
+
'@opentelemetry/auto-instrumentations-node',
|
|
1000
|
+
'@opentelemetry/instrumentation-http',
|
|
1001
|
+
'@opentelemetry/exporter-trace-otlp-http',
|
|
1002
|
+
'@opentelemetry/exporter-logs-otlp-http',
|
|
1003
|
+
'@opentelemetry/sdk-logs',
|
|
1004
|
+
'@opentelemetry/instrumentation',
|
|
1005
|
+
'@opentelemetry/resources',
|
|
1006
|
+
'@opentelemetry/semantic-conventions',
|
|
1007
|
+
'@opentelemetry/api',
|
|
1008
|
+
'@opentelemetry/api-logs',
|
|
1009
|
+
'@vercel/otel',
|
|
1010
|
+
],
|
|
1011
|
+
},
|
|
1012
|
+
};
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
**Why is this needed?** Next.js bundles server code with webpack, which breaks OpenTelemetry's dynamic `require()` calls and monkey-patching. Externalizing these packages keeps them as normal Node.js `require()` calls at runtime. The `withSecureNow()` wrapper handles this automatically.
|
|
1016
|
+
|
|
1017
|
+
---
|
|
1018
|
+
|
|
1019
|
+
### Nuxt 3
|
|
1020
|
+
|
|
1021
|
+
```bash
|
|
1022
|
+
npm install securenow
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
Add the module to `nuxt.config.ts`:
|
|
1026
|
+
|
|
1027
|
+
```typescript
|
|
1028
|
+
export default defineNuxtConfig({
|
|
1029
|
+
modules: ['securenow/nuxt'],
|
|
1030
|
+
});
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
`.env`:
|
|
1034
|
+
|
|
1035
|
+
```env
|
|
1036
|
+
SECURENOW_APPID=my-nuxt-app
|
|
1037
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
1038
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
That's it -- the Nuxt module handles OTel SDK initialization, Nitro externalization, firewall activation, and request tracing automatically. Optional config:
|
|
1042
|
+
|
|
1043
|
+
```typescript
|
|
1044
|
+
export default defineNuxtConfig({
|
|
1045
|
+
modules: ['securenow/nuxt'],
|
|
1046
|
+
securenow: {
|
|
1047
|
+
captureBody: true,
|
|
1048
|
+
logging: true,
|
|
1049
|
+
noUuid: true,
|
|
1050
|
+
},
|
|
1051
|
+
});
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
The Nuxt server plugin (v5.13.0+) initializes the firewall independently from OpenTelemetry, so IP blocking works even if tracing encounters an error.
|
|
1055
|
+
|
|
1056
|
+
---
|
|
1057
|
+
|
|
1058
|
+
### Compatibility Matrix
|
|
1059
|
+
|
|
1060
|
+
| Framework | Traces | Logs | Body Capture | Firewall | Notes |
|
|
1061
|
+
|-----------|--------|------|--------------|----------|-------|
|
|
1062
|
+
| Express | Yes | Yes | Yes | Yes | Fully compatible |
|
|
1063
|
+
| Fastify | Yes | Yes | **No** | Yes | `SECURENOW_CAPTURE_BODY=0` required |
|
|
1064
|
+
| Koa | Yes | Yes | Yes | Yes | Needs `koa-bodyparser` |
|
|
1065
|
+
| NestJS | Yes | Yes | Yes | Yes | Use `-r ts-node/register` |
|
|
1066
|
+
| Hapi | Yes | Yes | **No** | Yes | `SECURENOW_CAPTURE_BODY=0` required |
|
|
1067
|
+
| h3 | Yes | Yes | Yes | Yes | Uses `toNodeListener()` |
|
|
1068
|
+
| Polka | Yes | Yes | Yes | Yes | Needs manual body parser |
|
|
1069
|
+
| Micro/HTTP | Yes | Yes | Yes | Yes | Full control |
|
|
1070
|
+
| Hono | Yes | Yes | **No** | Yes | `SECURENOW_CAPTURE_BODY=0`; ESM `-r` flag |
|
|
1071
|
+
| Feathers | Yes | Yes | Yes | Yes | Uses Express transport |
|
|
1072
|
+
| Next.js | Yes | Yes | Yes | Yes | Use `instrumentation.ts` + `withSecureNow()` |
|
|
1073
|
+
| Nuxt 3 | Yes | Yes | Yes | Yes | Use `securenow/nuxt` module |
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
## Firewall -- Automatic IP Blocking
|
|
1078
|
+
|
|
1079
|
+
SecureNow can automatically block IPs from your blocklist at the application layer. No code changes -- just set an API key and the firewall activates.
|
|
1080
|
+
|
|
1081
|
+
### Enable the Firewall
|
|
1082
|
+
|
|
1083
|
+
```bash
|
|
1084
|
+
# Add to your .env
|
|
1085
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
That's it. On startup, you'll see:
|
|
1089
|
+
|
|
1090
|
+
```
|
|
1091
|
+
[securenow] Firewall: ENABLED
|
|
1092
|
+
[securenow] Firewall: Layer 1 (HTTP 403) active
|
|
1093
|
+
[securenow] Firewall: synced 142 blocked IPs (138 exact + 4 CIDR ranges)
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
### How It Works
|
|
1097
|
+
|
|
1098
|
+
The firewall uses a version-based sync protocol for efficiency:
|
|
1099
|
+
|
|
1100
|
+
1. **Version check** every 10 seconds (lightweight HEAD-like request with ETag)
|
|
1101
|
+
2. **Full blocklist sync** only when the version changes (or every 5 minutes as a safety net)
|
|
1102
|
+
3. **In-memory matching** with a pre-compiled set (exact IPs) and sorted CIDR list for sub-millisecond lookups
|
|
1103
|
+
4. **Exponential backoff** with jitter when the API is temporarily unreachable
|
|
1104
|
+
5. **Allowlist support** -- trusted IPs are never blocked, even if they appear on the blocklist
|
|
1105
|
+
6. **Localhost fallback** -- when the configured API URL is unreachable (ECONNREFUSED), the SDK automatically tries `http://localhost:4000` for co-located deployments
|
|
1106
|
+
|
|
1107
|
+
After you block an IP in the dashboard or CLI, it typically takes 10-15 seconds to propagate to all running instances.
|
|
1108
|
+
|
|
1109
|
+
### Firewall-Only Mode (No Tracing)
|
|
1110
|
+
|
|
1111
|
+
If you only need IP blocking without OpenTelemetry tracing overhead, use the standalone entry point:
|
|
1112
|
+
|
|
1113
|
+
```bash
|
|
1114
|
+
# Manual preload flag
|
|
1115
|
+
node -r securenow/firewall-only app.js
|
|
1116
|
+
|
|
1117
|
+
# Or via the CLI (v6.1.0+) — same effect, clearer intent
|
|
1118
|
+
securenow run --firewall-only app.js
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
Or in `package.json`:
|
|
1122
|
+
|
|
1123
|
+
```json
|
|
1124
|
+
"scripts": {
|
|
1125
|
+
"start": "node -r securenow/firewall-only app.js"
|
|
1126
|
+
}
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
This is useful when:
|
|
1130
|
+
- You only need IP blocking, not observability
|
|
1131
|
+
- You want to minimize startup time and memory footprint
|
|
1132
|
+
- You're adding the firewall to a project that uses a different tracing solution
|
|
1133
|
+
- For Next.js, this avoids the need for `serverExternalPackages` entirely
|
|
1134
|
+
|
|
1135
|
+
Environment variables for firewall-only mode:
|
|
1136
|
+
|
|
1137
|
+
```bash
|
|
1138
|
+
SECURENOW_API_KEY=snk_live_abc123... # Required
|
|
1139
|
+
SECURENOW_API_URL=https://api.securenow.ai # Optional (auto-detected)
|
|
1140
|
+
SECURENOW_FIREWALL_ENABLED=1 # Default: 1
|
|
1141
|
+
SECURENOW_FIREWALL_TCP=1 # Optional: Layer 2
|
|
1142
|
+
SECURENOW_FIREWALL_IPTABLES=1 # Optional: Layer 3
|
|
1143
|
+
SECURENOW_FIREWALL_CLOUD=cloudflare # Optional: Layer 4
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
### Blocking Layers
|
|
1147
|
+
|
|
1148
|
+
The firewall supports four layers -- Layer 1 is always on, the rest are opt-in:
|
|
1149
|
+
|
|
1150
|
+
| Layer | Env Var | Description |
|
|
1151
|
+
|-------|---------|-------------|
|
|
1152
|
+
| **Layer 1: HTTP** | *(always on)* | Returns 403 Forbidden with a security alert page. Works with proxy headers. |
|
|
1153
|
+
| **Layer 2: TCP** | `SECURENOW_FIREWALL_TCP=1` | `socket.destroy()` -- zero bytes sent back |
|
|
1154
|
+
| **Layer 3: iptables** | `SECURENOW_FIREWALL_IPTABLES=1` | Kernel-level DROP (Linux, requires root) |
|
|
1155
|
+
| **Layer 4: Cloud WAF** | `SECURENOW_FIREWALL_CLOUD=cloudflare` | Pushes to Cloudflare, AWS WAF, or GCP Cloud Armor |
|
|
1156
|
+
|
|
1157
|
+
### Blocked Page
|
|
1158
|
+
|
|
1159
|
+
When an IP is blocked at Layer 1, the user sees a full-page security alert with:
|
|
1160
|
+
- Their detected IP address
|
|
1161
|
+
- A warning that malicious activity was detected
|
|
1162
|
+
- Contact information (`contact@securenow.ai`) for false positives
|
|
1163
|
+
|
|
1164
|
+
### Get an API Key
|
|
1165
|
+
|
|
1166
|
+
```bash
|
|
1167
|
+
npx securenow login
|
|
1168
|
+
npx securenow firewall status
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
Or create one from the dashboard with the `firewall:read` scope.
|
|
1172
|
+
|
|
1173
|
+
See the [Firewall Guide](./docs/FIREWALL-GUIDE.md) for the full reference.
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## Environment Variables Reference
|
|
1178
|
+
|
|
1179
|
+
### Required Variables
|
|
1180
|
+
|
|
1181
|
+
| Variable | Description | Example |
|
|
1182
|
+
|----------|-------------|---------|
|
|
1183
|
+
| `SECURENOW_APPID` | Your application identifier. Used as the service name in traces. | `my-app` |
|
|
1184
|
+
| `SECURENOW_INSTANCE` | Base URL of your OTLP collector endpoint. | `http://localhost:4318` |
|
|
1185
|
+
|
|
1186
|
+
### Optional Configuration
|
|
1187
|
+
|
|
1188
|
+
#### Service Naming
|
|
1189
|
+
|
|
1190
|
+
| Variable | Description | Default |
|
|
1191
|
+
|----------|-------------|---------|
|
|
1192
|
+
| `OTEL_SERVICE_NAME` | Alternative to SECURENOW_APPID. Standard OpenTelemetry variable. | - |
|
|
1193
|
+
| `SECURENOW_NO_UUID` | Set to `1` to disable UUID suffix on service name. Useful for clustered apps. | `0` |
|
|
1194
|
+
| `SECURENOW_STRICT` | Set to `1` to exit process if SECURENOW_APPID is not set in cluster mode. | `0` |
|
|
1195
|
+
|
|
1196
|
+
#### Connection Settings
|
|
1197
|
+
|
|
1198
|
+
| Variable | Description | Default |
|
|
1199
|
+
|----------|-------------|---------|
|
|
1200
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Alternative to SECURENOW_INSTANCE. Standard OpenTelemetry variable. | - |
|
|
1201
|
+
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Override traces endpoint specifically. | `{SECURENOW_INSTANCE}/v1/traces` |
|
|
1202
|
+
| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Override logs endpoint specifically. | `{SECURENOW_INSTANCE}/v1/logs` |
|
|
1203
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | Headers to send with OTLP exports. Format: `key1=value1,key2=value2` | - |
|
|
1204
|
+
|
|
1205
|
+
#### Logging
|
|
1206
|
+
|
|
1207
|
+
| Variable | Description | Default |
|
|
1208
|
+
|----------|-------------|---------|
|
|
1209
|
+
| `SECURENOW_LOGGING_ENABLED` | Enable automatic logging to OTLP backend. Set to `1` to enable, `0` to disable. | `1` |
|
|
1210
|
+
|
|
1211
|
+
#### Request Body Capture
|
|
1212
|
+
|
|
1213
|
+
| Variable | Description | Default |
|
|
1214
|
+
|----------|-------------|---------|
|
|
1215
|
+
| `SECURENOW_CAPTURE_BODY` | Enable request body capture in traces. Set to `1` to enable. | `0` |
|
|
1216
|
+
| `SECURENOW_MAX_BODY_SIZE` | Maximum body size to capture in bytes. Bodies larger than this are truncated. | `10240` (10KB) |
|
|
1217
|
+
| `SECURENOW_SENSITIVE_FIELDS` | Comma-separated list of additional field names to redact. | - |
|
|
1218
|
+
| `SECURENOW_CAPTURE_MULTIPART` | Enable multipart/form-data capture. Streams through the request to extract text field values and file metadata (name, filename, content-type, size) without buffering file content. Set to `1` to enable. | `0` |
|
|
1219
|
+
|
|
1220
|
+
**Default sensitive fields (auto-redacted):** `password`, `passwd`, `pwd`, `secret`, `token`, `api_key`, `apikey`, `access_token`, `auth`, `credentials`, `mysql_pwd`, `stripeToken`, `card`, `cardnumber`, `ccv`, `cvc`, `cvv`, `ssn`, `pin`
|
|
1221
|
+
|
|
1222
|
+
#### Instrumentation Control
|
|
1223
|
+
|
|
1224
|
+
| Variable | Description | Default |
|
|
1225
|
+
|----------|-------------|---------|
|
|
1226
|
+
| `SECURENOW_DISABLE_INSTRUMENTATIONS` | Comma-separated list of instrumentation packages to disable. | - |
|
|
1227
|
+
|
|
1228
|
+
**Example:** `SECURENOW_DISABLE_INSTRUMENTATIONS=fs,dns` disables filesystem and DNS instrumentations.
|
|
1229
|
+
|
|
1230
|
+
#### Firewall
|
|
1231
|
+
|
|
1232
|
+
| Variable | Description | Default |
|
|
1233
|
+
|----------|-------------|---------|
|
|
1234
|
+
| `SECURENOW_API_KEY` | API key with `firewall:read` scope. Enables the firewall when set. | - |
|
|
1235
|
+
| `SECURENOW_API_URL` | SecureNow API base URL. Auto-detected for co-located deployments (falls back to `http://localhost:4000` on ECONNREFUSED). | `https://api.securenow.ai` |
|
|
1236
|
+
| `SECURENOW_FIREWALL_ENABLED` | Master kill-switch. Set to `0` to disable. | `1` |
|
|
1237
|
+
| `SECURENOW_FIREWALL_VERSION_INTERVAL` | Seconds between version checks (lightweight ETag-based). | `10` |
|
|
1238
|
+
| `SECURENOW_FIREWALL_SYNC_INTERVAL` | Full blocklist refresh interval in seconds (safety net). | `300` |
|
|
1239
|
+
| `SECURENOW_FIREWALL_FAIL_MODE` | `open` (allow when unavailable) or `closed` (block all). | `open` |
|
|
1240
|
+
| `SECURENOW_FIREWALL_STATUS_CODE` | HTTP status code for blocked requests. | `403` |
|
|
1241
|
+
| `SECURENOW_FIREWALL_LOG` | Log blocked requests and sync events to console. Set to `0` to silence. | `1` |
|
|
1242
|
+
| `SECURENOW_FIREWALL_TCP` | Enable Layer 2 TCP blocking. | `0` |
|
|
1243
|
+
| `SECURENOW_FIREWALL_IPTABLES` | Enable Layer 3 iptables blocking. | `0` |
|
|
1244
|
+
| `SECURENOW_FIREWALL_CLOUD` | Cloud WAF provider: `cloudflare`, `aws`, or `gcp`. | - |
|
|
1245
|
+
| `SECURENOW_FIREWALL_CLOUD_DRY_RUN` | Log cloud pushes without applying changes. | `0` |
|
|
1246
|
+
| `SECURENOW_TRUSTED_PROXIES` | Comma-separated trusted proxy IPs. | - |
|
|
1247
|
+
|
|
1248
|
+
See [Firewall Guide](./docs/FIREWALL-GUIDE.md) for complete details on all layers.
|
|
1249
|
+
|
|
1250
|
+
#### Debugging
|
|
1251
|
+
|
|
1252
|
+
| Variable | Description | Default |
|
|
1253
|
+
|----------|-------------|---------|
|
|
1254
|
+
| `OTEL_LOG_LEVEL` | OpenTelemetry SDK log level. Options: `debug`, `info`, `warn`, `error` | `none` |
|
|
1255
|
+
| `SECURENOW_TEST_SPAN` | Set to `1` to emit a test span on startup. | `0` |
|
|
1256
|
+
|
|
1257
|
+
#### Environment
|
|
1258
|
+
|
|
1259
|
+
| Variable | Description | Default |
|
|
1260
|
+
|----------|-------------|---------|
|
|
1261
|
+
| `NODE_ENV` | Deployment environment name. Sent as `deployment.environment` attribute. | `production` |
|
|
1262
|
+
|
|
1263
|
+
---
|
|
1264
|
+
|
|
1265
|
+
## Entry Points Reference
|
|
1266
|
+
|
|
1267
|
+
SecureNow provides multiple entry points depending on your needs:
|
|
1268
|
+
|
|
1269
|
+
| Entry Point | Usage | Includes Tracing | Includes Firewall | Notes |
|
|
1270
|
+
|-------------|-------|-------------------|-------------------|-------|
|
|
1271
|
+
| `securenow/register` | `node -r securenow/register app.js` | Yes | Yes | Default -- full tracing + firewall |
|
|
1272
|
+
| `securenow/firewall-only` | `node -r securenow/firewall-only app.js` | No | Yes | Firewall only, no OTel overhead |
|
|
1273
|
+
| `securenow/nextjs` | `require('securenow/nextjs').registerSecureNow()` | Yes | Yes | Next.js instrumentation hook |
|
|
1274
|
+
| `securenow/nuxt` | `modules: ['securenow/nuxt']` | Yes | Yes | Nuxt 3 module |
|
|
1275
|
+
| `securenow/nextjs-webpack-config` | `withSecureNow(config)` | - | - | Next.js config wrapper |
|
|
1276
|
+
| `securenow/firewall` | `require('securenow/firewall').init({...})` | No | Yes | Programmatic firewall API |
|
|
1277
|
+
| `securenow/tracing` | `require('securenow/tracing')` | Yes | No | Programmatic tracing API |
|
|
1278
|
+
|
|
1279
|
+
---
|
|
1280
|
+
|
|
1281
|
+
## Logging Setup
|
|
1282
|
+
|
|
1283
|
+
### Automatic Console Logging
|
|
1284
|
+
|
|
1285
|
+
Since **v5.6.0**, when `SECURENOW_LOGGING_ENABLED=1`, all console calls are automatically forwarded as OTLP log records:
|
|
1286
|
+
|
|
1287
|
+
```javascript
|
|
1288
|
+
// At the top of your main file
|
|
1289
|
+
require('securenow/register');
|
|
1290
|
+
|
|
1291
|
+
// With SECURENOW_LOGGING_ENABLED=1, all console logs are automatically sent
|
|
1292
|
+
console.log('Application started');
|
|
1293
|
+
console.info('User action', { userId: 123, action: 'login' });
|
|
1294
|
+
console.warn('Deprecation warning');
|
|
1295
|
+
console.error('Error occurred', { error: 'Something failed' });
|
|
1296
|
+
console.debug('Debug info');
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
**Severity mapping:**
|
|
1300
|
+
- `console.log()` -> INFO
|
|
1301
|
+
- `console.info()` -> INFO
|
|
1302
|
+
- `console.warn()` -> WARN
|
|
1303
|
+
- `console.error()` -> ERROR
|
|
1304
|
+
- `console.debug()` -> DEBUG
|
|
1305
|
+
|
|
1306
|
+
**Environment variable:**
|
|
1307
|
+
```bash
|
|
1308
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
### Direct Logger API
|
|
1312
|
+
|
|
1313
|
+
For more control, use the OpenTelemetry logger API:
|
|
1314
|
+
|
|
1315
|
+
```javascript
|
|
1316
|
+
require('securenow/register');
|
|
1317
|
+
const { getLogger } = require('securenow/tracing');
|
|
1318
|
+
|
|
1319
|
+
// Get a logger instance
|
|
1320
|
+
const logger = getLogger('my-module', '1.0.0');
|
|
1321
|
+
|
|
1322
|
+
// Emit structured logs
|
|
1323
|
+
logger.emit({
|
|
1324
|
+
severityNumber: 9, // INFO
|
|
1325
|
+
severityText: 'INFO',
|
|
1326
|
+
body: 'User logged in',
|
|
1327
|
+
attributes: {
|
|
1328
|
+
userId: 123,
|
|
1329
|
+
username: 'john',
|
|
1330
|
+
sessionId: 'abc123',
|
|
1331
|
+
},
|
|
1332
|
+
});
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
**Severity numbers:**
|
|
1336
|
+
- `5` - DEBUG
|
|
1337
|
+
- `9` - INFO
|
|
1338
|
+
- `13` - WARN
|
|
1339
|
+
- `17` - ERROR
|
|
1340
|
+
|
|
1341
|
+
### Using with NODE_OPTIONS
|
|
1342
|
+
|
|
1343
|
+
```bash
|
|
1344
|
+
# Enable tracing + logging (console auto-forwarding is built-in since v5.6.0)
|
|
1345
|
+
NODE_OPTIONS="-r securenow/register" \
|
|
1346
|
+
SECURENOW_APPID=my-app \
|
|
1347
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318 \
|
|
1348
|
+
SECURENOW_LOGGING_ENABLED=1 \
|
|
1349
|
+
node app.js
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
---
|
|
1353
|
+
|
|
1354
|
+
## Request Body Capture
|
|
1355
|
+
|
|
1356
|
+
SecureNow can capture HTTP request bodies in traces for debugging purposes. This is disabled by default.
|
|
1357
|
+
|
|
1358
|
+
### Enable Body Capture
|
|
1359
|
+
|
|
1360
|
+
```bash
|
|
1361
|
+
export SECURENOW_CAPTURE_BODY=1
|
|
1362
|
+
export SECURENOW_MAX_BODY_SIZE=10240 # 10KB (optional)
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
### Supported Content Types
|
|
1366
|
+
|
|
1367
|
+
- `application/json`
|
|
1368
|
+
- `application/x-www-form-urlencoded`
|
|
1369
|
+
- `application/graphql`
|
|
1370
|
+
- `multipart/form-data` (requires `SECURENOW_CAPTURE_MULTIPART=1`)
|
|
1371
|
+
|
|
1372
|
+
### Multipart Body Capture (v5.8.0+)
|
|
1373
|
+
|
|
1374
|
+
Enable with `SECURENOW_CAPTURE_MULTIPART=1` to capture multipart/form-data requests. Uses a streaming parser that never buffers file content -- memory stays at ~few KB regardless of upload size.
|
|
1375
|
+
|
|
1376
|
+
**What gets captured:**
|
|
1377
|
+
- **Text fields** -- field name and value (up to 1000 chars), with sensitive fields auto-redacted
|
|
1378
|
+
- **File fields** -- metadata only: field name, filename, content-type, and size in bytes
|
|
1379
|
+
|
|
1380
|
+
**Example trace attribute:**
|
|
1381
|
+
```json
|
|
1382
|
+
{
|
|
1383
|
+
"fields": { "description": "My upload", "token": "[REDACTED]" },
|
|
1384
|
+
"files": [
|
|
1385
|
+
{ "field": "avatar", "filename": "photo.jpg", "contentType": "image/jpeg", "size": 524288 },
|
|
1386
|
+
{ "field": "resume", "filename": "cv.pdf", "contentType": "application/pdf", "size": 1048576 }
|
|
1387
|
+
]
|
|
1388
|
+
}
|
|
1389
|
+
```
|
|
1390
|
+
|
|
1391
|
+
File binary content is never stored in traces.
|
|
1392
|
+
|
|
1393
|
+
### Sensitive Data Redaction
|
|
1394
|
+
|
|
1395
|
+
All request bodies are automatically scanned and sensitive fields are redacted:
|
|
1396
|
+
|
|
1397
|
+
**Automatically redacted fields:** `password`, `secret`, `token`, `api_key`, `card`, `cvv`, `ssn`, and more.
|
|
1398
|
+
|
|
1399
|
+
**Add custom fields to redact:**
|
|
1400
|
+
|
|
1401
|
+
```bash
|
|
1402
|
+
export SECURENOW_SENSITIVE_FIELDS="custom_secret,internal_token"
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
### Example
|
|
1406
|
+
|
|
1407
|
+
```javascript
|
|
1408
|
+
// POST /api/users with body:
|
|
1409
|
+
{
|
|
1410
|
+
"email": "user@example.com",
|
|
1411
|
+
"password": "secret123",
|
|
1412
|
+
"name": "John Doe"
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// Captured in trace as:
|
|
1416
|
+
{
|
|
1417
|
+
"email": "user@example.com",
|
|
1418
|
+
"password": "[REDACTED]",
|
|
1419
|
+
"name": "John Doe"
|
|
1420
|
+
}
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1423
|
+
---
|
|
1424
|
+
|
|
1425
|
+
## Advanced Configuration
|
|
1426
|
+
|
|
1427
|
+
### Complete Example with All Options
|
|
1428
|
+
|
|
1429
|
+
```bash
|
|
1430
|
+
# Service identification
|
|
1431
|
+
export SECURENOW_APPID=my-production-app
|
|
1432
|
+
export SECURENOW_NO_UUID=1
|
|
1433
|
+
export SECURENOW_STRICT=1
|
|
1434
|
+
|
|
1435
|
+
# OTLP backend
|
|
1436
|
+
export SECURENOW_INSTANCE=http://collector.example.com:4318
|
|
1437
|
+
export OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key"
|
|
1438
|
+
|
|
1439
|
+
# Logging
|
|
1440
|
+
export SECURENOW_LOGGING_ENABLED=1
|
|
1441
|
+
|
|
1442
|
+
# Request body capture
|
|
1443
|
+
export SECURENOW_CAPTURE_BODY=1
|
|
1444
|
+
export SECURENOW_MAX_BODY_SIZE=20480
|
|
1445
|
+
export SECURENOW_SENSITIVE_FIELDS="internal_id,session_key"
|
|
1446
|
+
|
|
1447
|
+
# Firewall
|
|
1448
|
+
export SECURENOW_API_KEY=snk_live_abc123...
|
|
1449
|
+
export SECURENOW_FIREWALL_TCP=1
|
|
1450
|
+
export SECURENOW_FIREWALL_VERSION_INTERVAL=10
|
|
1451
|
+
export SECURENOW_FIREWALL_SYNC_INTERVAL=300
|
|
1452
|
+
|
|
1453
|
+
# Environment
|
|
1454
|
+
export NODE_ENV=production
|
|
1455
|
+
|
|
1456
|
+
# Debugging
|
|
1457
|
+
export OTEL_LOG_LEVEL=info
|
|
1458
|
+
|
|
1459
|
+
# Disable specific instrumentations
|
|
1460
|
+
export SECURENOW_DISABLE_INSTRUMENTATIONS=fs,dns
|
|
1461
|
+
|
|
1462
|
+
# Run application
|
|
1463
|
+
NODE_OPTIONS="-r securenow/register" node app.js
|
|
1464
|
+
```
|
|
1465
|
+
|
|
1466
|
+
### Using Multiple Logger Instances
|
|
1467
|
+
|
|
1468
|
+
```javascript
|
|
1469
|
+
const { getLogger } = require('securenow/tracing');
|
|
1470
|
+
|
|
1471
|
+
const authLogger = getLogger('auth-service', '1.0.0');
|
|
1472
|
+
const dbLogger = getLogger('database', '1.0.0');
|
|
1473
|
+
const apiLogger = getLogger('api-handler', '1.0.0');
|
|
1474
|
+
|
|
1475
|
+
authLogger.emit({
|
|
1476
|
+
severityNumber: 9,
|
|
1477
|
+
severityText: 'INFO',
|
|
1478
|
+
body: 'User authenticated',
|
|
1479
|
+
attributes: { userId: 123 },
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
dbLogger.emit({
|
|
1483
|
+
severityNumber: 13,
|
|
1484
|
+
severityText: 'WARN',
|
|
1485
|
+
body: 'Slow query detected',
|
|
1486
|
+
attributes: { queryTime: 5000 },
|
|
1487
|
+
});
|
|
1488
|
+
```
|
|
1489
|
+
|
|
1490
|
+
### Check if Logging is Enabled
|
|
1491
|
+
|
|
1492
|
+
```javascript
|
|
1493
|
+
const { isLoggingEnabled } = require('securenow/tracing');
|
|
1494
|
+
|
|
1495
|
+
if (isLoggingEnabled()) {
|
|
1496
|
+
console.log('Logging is enabled');
|
|
1497
|
+
} else {
|
|
1498
|
+
console.log('Logging is disabled');
|
|
1499
|
+
}
|
|
1500
|
+
```
|
|
1501
|
+
|
|
1502
|
+
### Programmatic Configuration
|
|
1503
|
+
|
|
1504
|
+
While environment variables are recommended, you can also configure programmatically:
|
|
1505
|
+
|
|
1506
|
+
```javascript
|
|
1507
|
+
// Set environment variables before requiring securenow
|
|
1508
|
+
process.env.SECURENOW_APPID = 'my-app';
|
|
1509
|
+
process.env.SECURENOW_INSTANCE = 'http://localhost:4318';
|
|
1510
|
+
process.env.SECURENOW_LOGGING_ENABLED = '1';
|
|
1511
|
+
|
|
1512
|
+
// Then initialize (console log forwarding is automatic since v5.6.0)
|
|
1513
|
+
require('securenow/register');
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
---
|
|
1517
|
+
|
|
1518
|
+
## TypeScript Support
|
|
1519
|
+
|
|
1520
|
+
SecureNow includes full TypeScript definitions.
|
|
1521
|
+
|
|
1522
|
+
### Type Definitions
|
|
1523
|
+
|
|
1524
|
+
```typescript
|
|
1525
|
+
import { getLogger, isLoggingEnabled, Logger, LogRecord } from 'securenow/tracing';
|
|
1526
|
+
|
|
1527
|
+
// Get a typed logger
|
|
1528
|
+
const logger: Logger | null = getLogger('my-service', '1.0.0');
|
|
1529
|
+
|
|
1530
|
+
// Emit a log with type checking
|
|
1531
|
+
if (logger) {
|
|
1532
|
+
const logRecord: LogRecord = {
|
|
1533
|
+
severityNumber: 9,
|
|
1534
|
+
severityText: 'INFO',
|
|
1535
|
+
body: 'User action',
|
|
1536
|
+
attributes: {
|
|
1537
|
+
userId: 123,
|
|
1538
|
+
action: 'login',
|
|
1539
|
+
},
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
logger.emit(logRecord);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// Check if enabled
|
|
1546
|
+
const enabled: boolean = isLoggingEnabled();
|
|
1547
|
+
```
|
|
1548
|
+
|
|
1549
|
+
### Next.js with TypeScript
|
|
1550
|
+
|
|
1551
|
+
```typescript
|
|
1552
|
+
// instrumentation.ts
|
|
1553
|
+
export async function register() {
|
|
1554
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
1555
|
+
const { registerSecureNow } = require('securenow/nextjs');
|
|
1556
|
+
registerSecureNow();
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
```
|
|
1560
|
+
|
|
1561
|
+
```javascript
|
|
1562
|
+
// next.config.js
|
|
1563
|
+
const { withSecureNow } = require('securenow/nextjs-webpack-config');
|
|
1564
|
+
|
|
1565
|
+
module.exports = withSecureNow({
|
|
1566
|
+
reactStrictMode: true,
|
|
1567
|
+
});
|
|
1568
|
+
```
|
|
1569
|
+
|
|
1570
|
+
### NestJS with TypeScript
|
|
1571
|
+
|
|
1572
|
+
Use `-r securenow/register -r ts-node/register` flags instead of in-code require:
|
|
1573
|
+
|
|
1574
|
+
```typescript
|
|
1575
|
+
// main.ts (run with: node -r securenow/register -r ts-node/register main.ts)
|
|
1576
|
+
import { NestFactory } from '@nestjs/core';
|
|
1577
|
+
import { AppModule } from './app.module';
|
|
1578
|
+
|
|
1579
|
+
async function bootstrap() {
|
|
1580
|
+
const app = await NestFactory.create(AppModule);
|
|
1581
|
+
await app.listen(3000);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
bootstrap();
|
|
1585
|
+
```
|
|
1586
|
+
|
|
1587
|
+
---
|
|
1588
|
+
|
|
1589
|
+
## Troubleshooting
|
|
1590
|
+
|
|
1591
|
+
### Traces Not Appearing
|
|
1592
|
+
|
|
1593
|
+
**Check 1: Verify environment variables**
|
|
1594
|
+
|
|
1595
|
+
```bash
|
|
1596
|
+
echo $SECURENOW_APPID
|
|
1597
|
+
echo $SECURENOW_INSTANCE
|
|
1598
|
+
```
|
|
1599
|
+
|
|
1600
|
+
Both should output values.
|
|
1601
|
+
|
|
1602
|
+
**Check 2: Verify OTLP collector is running**
|
|
1603
|
+
|
|
1604
|
+
```bash
|
|
1605
|
+
curl http://localhost:4318/v1/traces
|
|
1606
|
+
# Should return 200 or 405 (method not allowed)
|
|
1607
|
+
```
|
|
1608
|
+
|
|
1609
|
+
**Check 3: Enable debug logging**
|
|
1610
|
+
|
|
1611
|
+
```bash
|
|
1612
|
+
export OTEL_LOG_LEVEL=debug
|
|
1613
|
+
node app.js
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
Look for lines like:
|
|
1617
|
+
```
|
|
1618
|
+
[securenow] OTel SDK started -> http://localhost:4318/v1/traces
|
|
1619
|
+
```
|
|
1620
|
+
|
|
1621
|
+
**Check 4: Verify initialization order**
|
|
1622
|
+
|
|
1623
|
+
SecureNow must be required BEFORE any other modules:
|
|
1624
|
+
|
|
1625
|
+
```javascript
|
|
1626
|
+
// Correct
|
|
1627
|
+
require('securenow/register');
|
|
1628
|
+
const express = require('express');
|
|
1629
|
+
|
|
1630
|
+
// Wrong -- too late!
|
|
1631
|
+
const express = require('express');
|
|
1632
|
+
require('securenow/register');
|
|
1633
|
+
```
|
|
1634
|
+
|
|
1635
|
+
### Firewall Not Blocking IPs
|
|
1636
|
+
|
|
1637
|
+
**Check 1: Is `SECURENOW_API_KEY` set?**
|
|
1638
|
+
|
|
1639
|
+
```bash
|
|
1640
|
+
echo $SECURENOW_API_KEY
|
|
1641
|
+
```
|
|
1642
|
+
|
|
1643
|
+
**Check 2: Is the IP in the blocklist?**
|
|
1644
|
+
|
|
1645
|
+
```bash
|
|
1646
|
+
npx securenow blocklist
|
|
1647
|
+
npx securenow firewall test-ip 1.2.3.4
|
|
1648
|
+
```
|
|
1649
|
+
|
|
1650
|
+
**Check 3: Check startup logs for sync status**
|
|
1651
|
+
|
|
1652
|
+
You should see:
|
|
1653
|
+
```
|
|
1654
|
+
[securenow] Firewall: ENABLED
|
|
1655
|
+
[securenow] Firewall: synced 142 blocked IPs
|
|
1656
|
+
```
|
|
1657
|
+
|
|
1658
|
+
If you see `initial sync failed`, check the API URL and key.
|
|
1659
|
+
|
|
1660
|
+
**Check 4: Wait for propagation**
|
|
1661
|
+
|
|
1662
|
+
After blocking an IP, it takes 10-15 seconds to propagate (one version-check interval).
|
|
1663
|
+
|
|
1664
|
+
**Check 5: Are you behind a proxy?**
|
|
1665
|
+
|
|
1666
|
+
Set `SECURENOW_TRUSTED_PROXIES` to your proxy's IP so the firewall sees the real client IP.
|
|
1667
|
+
|
|
1668
|
+
**Check 6: Using PM2?**
|
|
1669
|
+
|
|
1670
|
+
Make sure `node_args: '-r securenow/register'` is in your `ecosystem.config.cjs`. Without it, PM2 restarts skip the SDK entirely.
|
|
1671
|
+
|
|
1672
|
+
### Logs Not Appearing
|
|
1673
|
+
|
|
1674
|
+
**Check 1: Is logging enabled?**
|
|
1675
|
+
|
|
1676
|
+
```bash
|
|
1677
|
+
echo $SECURENOW_LOGGING_ENABLED
|
|
1678
|
+
# Should output: 1
|
|
1679
|
+
```
|
|
1680
|
+
|
|
1681
|
+
**Check 2: Verify console instrumentation is loaded**
|
|
1682
|
+
|
|
1683
|
+
```javascript
|
|
1684
|
+
require('securenow/register');
|
|
1685
|
+
```
|
|
1686
|
+
|
|
1687
|
+
**Check 3: Check console output**
|
|
1688
|
+
|
|
1689
|
+
You should see:
|
|
1690
|
+
```
|
|
1691
|
+
[securenow] Logging: ENABLED -> http://localhost:4318/v1/logs
|
|
1692
|
+
```
|
|
1693
|
+
|
|
1694
|
+
**Check 4: Verify OTLP logs endpoint**
|
|
1695
|
+
|
|
1696
|
+
```bash
|
|
1697
|
+
curl http://localhost:4318/v1/logs
|
|
1698
|
+
# Should return 200 or 405
|
|
1699
|
+
```
|
|
1700
|
+
|
|
1701
|
+
### Request Body Not Captured
|
|
1702
|
+
|
|
1703
|
+
**Check 1: Is body capture enabled?**
|
|
1704
|
+
|
|
1705
|
+
```bash
|
|
1706
|
+
export SECURENOW_CAPTURE_BODY=1
|
|
1707
|
+
```
|
|
1708
|
+
|
|
1709
|
+
**Check 2: Verify content type**
|
|
1710
|
+
|
|
1711
|
+
Body capture only works for:
|
|
1712
|
+
- `application/json`
|
|
1713
|
+
- `application/x-www-form-urlencoded`
|
|
1714
|
+
- `application/graphql`
|
|
1715
|
+
|
|
1716
|
+
**Check 3: Check body size**
|
|
1717
|
+
|
|
1718
|
+
Bodies larger than `SECURENOW_MAX_BODY_SIZE` are truncated:
|
|
1719
|
+
|
|
1720
|
+
```bash
|
|
1721
|
+
export SECURENOW_MAX_BODY_SIZE=20480 # Increase to 20KB
|
|
1722
|
+
```
|
|
1723
|
+
|
|
1724
|
+
### High Memory Usage
|
|
1725
|
+
|
|
1726
|
+
**Option 1: Disable body capture**
|
|
1727
|
+
|
|
1728
|
+
```bash
|
|
1729
|
+
export SECURENOW_CAPTURE_BODY=0
|
|
1730
|
+
```
|
|
1731
|
+
|
|
1732
|
+
**Option 2: Reduce body size limit**
|
|
1733
|
+
|
|
1734
|
+
```bash
|
|
1735
|
+
export SECURENOW_MAX_BODY_SIZE=5120 # 5KB
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1738
|
+
**Option 3: Disable specific instrumentations**
|
|
1739
|
+
|
|
1740
|
+
```bash
|
|
1741
|
+
export SECURENOW_DISABLE_INSTRUMENTATIONS=fs,dns,net
|
|
1742
|
+
```
|
|
1743
|
+
|
|
1744
|
+
### Next.js Instrumentation Not Working
|
|
1745
|
+
|
|
1746
|
+
**Check 1: Using `withSecureNow()`?**
|
|
1747
|
+
|
|
1748
|
+
```javascript
|
|
1749
|
+
const { withSecureNow } = require('securenow/nextjs-webpack-config');
|
|
1750
|
+
module.exports = withSecureNow({ /* your config */ });
|
|
1751
|
+
```
|
|
1752
|
+
|
|
1753
|
+
This auto-handles `serverExternalPackages` / `experimental.serverComponentsExternalPackages` and `instrumentationHook` based on your Next.js version.
|
|
1754
|
+
|
|
1755
|
+
**Check 2: Verify instrumentation file location**
|
|
1756
|
+
|
|
1757
|
+
`instrumentation.ts` or `instrumentation.js` must be in the project root (same level as `app/` or `pages/`).
|
|
1758
|
+
|
|
1759
|
+
**Check 3: Check for OTel MODULE_NOT_FOUND errors**
|
|
1760
|
+
|
|
1761
|
+
If you see `MODULE_NOT_FOUND` for `@opentelemetry/*` packages, your `next.config.js` is missing the externalization. Use `withSecureNow()` to fix this automatically.
|
|
1762
|
+
|
|
1763
|
+
**Check 4: Restart dev server**
|
|
1764
|
+
|
|
1765
|
+
```bash
|
|
1766
|
+
# Kill the server and restart
|
|
1767
|
+
npm run dev
|
|
1768
|
+
```
|
|
1769
|
+
|
|
1770
|
+
### PM2 Cluster Issues
|
|
1771
|
+
|
|
1772
|
+
**Problem: Different service names for each worker**
|
|
1773
|
+
|
|
1774
|
+
**Solution: Use SECURENOW_NO_UUID**
|
|
1775
|
+
|
|
1776
|
+
```bash
|
|
1777
|
+
export SECURENOW_NO_UUID=1
|
|
1778
|
+
```
|
|
1779
|
+
|
|
1780
|
+
This uses the same service name for all workers.
|
|
1781
|
+
|
|
1782
|
+
**Problem: Workers not instrumented / firewall not active**
|
|
1783
|
+
|
|
1784
|
+
**Solution: Use node_args in PM2 config**
|
|
1785
|
+
|
|
1786
|
+
```javascript
|
|
1787
|
+
// ecosystem.config.cjs
|
|
1788
|
+
module.exports = {
|
|
1789
|
+
apps: [{
|
|
1790
|
+
name: 'my-app',
|
|
1791
|
+
script: './app.js',
|
|
1792
|
+
instances: 4,
|
|
1793
|
+
node_args: '-r securenow/register',
|
|
1794
|
+
env: {
|
|
1795
|
+
SECURENOW_APPID: 'my-app',
|
|
1796
|
+
SECURENOW_INSTANCE: 'http://localhost:4318',
|
|
1797
|
+
SECURENOW_API_KEY: 'snk_live_abc123...',
|
|
1798
|
+
}
|
|
1799
|
+
}]
|
|
1800
|
+
};
|
|
1801
|
+
```
|
|
1802
|
+
|
|
1803
|
+
Without `node_args`, PM2 starts your script directly without the securenow preload, so neither tracing nor the firewall will be active.
|
|
1804
|
+
|
|
1805
|
+
---
|
|
1806
|
+
|
|
1807
|
+
## Best Practices
|
|
1808
|
+
|
|
1809
|
+
### 1. Use Environment Variables
|
|
1810
|
+
|
|
1811
|
+
Don't hardcode configuration. Use environment variables:
|
|
1812
|
+
|
|
1813
|
+
```javascript
|
|
1814
|
+
// Bad
|
|
1815
|
+
process.env.SECURENOW_APPID = 'hardcoded-value';
|
|
1816
|
+
|
|
1817
|
+
// Good -- use .env file or export
|
|
1818
|
+
// .env
|
|
1819
|
+
SECURENOW_APPID=my-app
|
|
1820
|
+
SECURENOW_INSTANCE=http://localhost:4318
|
|
1821
|
+
```
|
|
1822
|
+
|
|
1823
|
+
### 2. Use Structured Logging
|
|
1824
|
+
|
|
1825
|
+
Pass objects to console methods for better filtering:
|
|
1826
|
+
|
|
1827
|
+
```javascript
|
|
1828
|
+
// Less useful
|
|
1829
|
+
console.log('User 123 logged in');
|
|
1830
|
+
|
|
1831
|
+
// Better -- structured attributes
|
|
1832
|
+
console.log('User logged in', {
|
|
1833
|
+
userId: 123,
|
|
1834
|
+
email: 'user@example.com',
|
|
1835
|
+
timestamp: new Date().toISOString(),
|
|
1836
|
+
});
|
|
1837
|
+
```
|
|
1838
|
+
|
|
1839
|
+
### 3. Choose Appropriate Severity Levels
|
|
1840
|
+
|
|
1841
|
+
```javascript
|
|
1842
|
+
// Normal operations
|
|
1843
|
+
console.log('Request processed');
|
|
1844
|
+
console.info('User created', { userId: 123 });
|
|
1845
|
+
|
|
1846
|
+
// Warnings
|
|
1847
|
+
console.warn('API rate limit approaching', { remaining: 10 });
|
|
1848
|
+
|
|
1849
|
+
// Errors
|
|
1850
|
+
console.error('Database connection failed', { error: err.message });
|
|
1851
|
+
|
|
1852
|
+
// Debug (only in development)
|
|
1853
|
+
console.debug('Cache hit', { key: 'user:123' });
|
|
1854
|
+
```
|
|
1855
|
+
|
|
1856
|
+
### 4. Don't Log Sensitive Data
|
|
1857
|
+
|
|
1858
|
+
Even though SecureNow automatically redacts common sensitive fields, avoid logging:
|
|
1859
|
+
|
|
1860
|
+
```javascript
|
|
1861
|
+
// Bad
|
|
1862
|
+
console.log('Login attempt', {
|
|
1863
|
+
email: 'user@example.com',
|
|
1864
|
+
password: 'secret123', // Will be redacted, but don't log it!
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1867
|
+
// Good
|
|
1868
|
+
console.log('Login attempt', {
|
|
1869
|
+
email: 'user@example.com',
|
|
1870
|
+
timestamp: Date.now(),
|
|
1871
|
+
});
|
|
1872
|
+
```
|
|
1873
|
+
|
|
1874
|
+
### 5. Use Different Logger Instances for Different Modules
|
|
1875
|
+
|
|
1876
|
+
```javascript
|
|
1877
|
+
// auth-service.js
|
|
1878
|
+
const authLogger = getLogger('auth-service', '1.0.0');
|
|
1879
|
+
|
|
1880
|
+
// database.js
|
|
1881
|
+
const dbLogger = getLogger('database', '1.0.0');
|
|
1882
|
+
|
|
1883
|
+
// api.js
|
|
1884
|
+
const apiLogger = getLogger('api', '1.0.0');
|
|
1885
|
+
```
|
|
1886
|
+
|
|
1887
|
+
### 6. Enable Body Capture Only in Development
|
|
1888
|
+
|
|
1889
|
+
```bash
|
|
1890
|
+
# .env.development
|
|
1891
|
+
SECURENOW_CAPTURE_BODY=1
|
|
1892
|
+
|
|
1893
|
+
# .env.production
|
|
1894
|
+
SECURENOW_CAPTURE_BODY=0
|
|
1895
|
+
```
|
|
1896
|
+
|
|
1897
|
+
---
|
|
1898
|
+
|
|
1899
|
+
## Supported Runtimes
|
|
1900
|
+
|
|
1901
|
+
- **Node.js:** 18+
|
|
1902
|
+
- **Frameworks:** Express, Next.js, Nuxt 3, Fastify, NestJS, Koa, Hapi, and more
|
|
1903
|
+
- **Databases:** PostgreSQL, MySQL, MongoDB, Redis
|
|
1904
|
+
- **HTTP Clients:** axios, fetch, node-fetch, got, request
|
|
1905
|
+
- **GraphQL:** Apollo Server, GraphQL.js
|
|
1906
|
+
- **Message Queues:** Redis, BullMQ
|
|
1907
|
+
- **And many more via OpenTelemetry auto-instrumentation**
|
|
1908
|
+
|
|
1909
|
+
---
|
|
1910
|
+
|
|
1911
|
+
## Automatic Instrumentations
|
|
1912
|
+
|
|
1913
|
+
SecureNow automatically instruments:
|
|
1914
|
+
|
|
1915
|
+
- HTTP/HTTPS (incoming and outgoing)
|
|
1916
|
+
- Express.js
|
|
1917
|
+
- Fastify
|
|
1918
|
+
- Koa
|
|
1919
|
+
- Hapi
|
|
1920
|
+
- Next.js
|
|
1921
|
+
- PostgreSQL
|
|
1922
|
+
- MySQL/MySQL2
|
|
1923
|
+
- MongoDB
|
|
1924
|
+
- Redis
|
|
1925
|
+
- GraphQL
|
|
1926
|
+
- gRPC
|
|
1927
|
+
- DNS
|
|
1928
|
+
- File System
|
|
1929
|
+
- And 50+ more libraries
|
|
1930
|
+
|
|
1931
|
+
No code changes needed!
|
|
1932
|
+
|
|
1933
|
+
---
|
|
1934
|
+
|
|
1935
|
+
## Architecture
|
|
1936
|
+
|
|
1937
|
+
```
|
|
1938
|
+
Your Application
|
|
1939
|
+
(Express/Next.js/etc)
|
|
1940
|
+
|
|
|
1941
|
+
v
|
|
1942
|
+
+---------------------------+
|
|
1943
|
+
| SecureNow Library |
|
|
1944
|
+
| +---------------------+ |
|
|
1945
|
+
| | Firewall Layer | | <-- Blocks IPs before they reach your app
|
|
1946
|
+
| +---------------------+ |
|
|
1947
|
+
| | Auto-instrumentation | |
|
|
1948
|
+
| | Console wrapper | |
|
|
1949
|
+
| | Body capture | |
|
|
1950
|
+
| +---------------------+ |
|
|
1951
|
+
+---------------------------+
|
|
1952
|
+
|
|
|
1953
|
+
+---------+---------+
|
|
1954
|
+
| |
|
|
1955
|
+
v v
|
|
1956
|
+
+-----------+ +-----------+
|
|
1957
|
+
| OTel SDK | | SecureNow |
|
|
1958
|
+
| OTLP | | API |
|
|
1959
|
+
| Exporters | | (blocklist|
|
|
1960
|
+
+-----------+ | sync) |
|
|
1961
|
+
| +-----------+
|
|
1962
|
+
v
|
|
1963
|
+
+-----------+
|
|
1964
|
+
| Your OTLP |
|
|
1965
|
+
| Backend |
|
|
1966
|
+
+-----------+
|
|
1967
|
+
```
|
|
1968
|
+
|
|
1969
|
+
---
|
|
1970
|
+
|
|
1971
|
+
## Performance
|
|
1972
|
+
|
|
1973
|
+
- **Minimal overhead:** < 1% CPU and memory impact
|
|
1974
|
+
- **Batch export:** Traces and logs are batched before sending
|
|
1975
|
+
- **Async processing:** No blocking of application threads
|
|
1976
|
+
- **Configurable:** Disable instrumentations you don't need
|
|
1977
|
+
- **Firewall:** Sub-millisecond IP lookups using pre-compiled hash sets and sorted CIDR lists
|
|
1978
|
+
- **Smart sync:** Version-based polling with ETag/304 eliminates unnecessary data transfer
|
|
1979
|
+
|
|
1980
|
+
---
|
|
1981
|
+
|
|
1982
|
+
## Security
|
|
1983
|
+
|
|
1984
|
+
- **Automatic redaction** of sensitive fields (passwords, tokens, keys)
|
|
1985
|
+
- **Configurable** sensitive field patterns
|
|
1986
|
+
- **No data stored** locally -- everything sent to your OTLP backend
|
|
1987
|
+
- **Multi-layer firewall** -- blocks IPs at HTTP, TCP, OS, and cloud-edge levels
|
|
1988
|
+
- **API keys** with granular scopes, IP allowlisting, and SHA-256 hashing
|
|
1989
|
+
- **Allowlist support** -- trusted IPs are never blocked
|
|
1990
|
+
- **Fail-open by default** -- network issues never accidentally block legitimate traffic
|
|
1991
|
+
- **Open source** -- audit the code yourself
|
|
1992
|
+
|
|
1993
|
+
---
|
|
1994
|
+
|
|
1995
|
+
## License
|
|
1996
|
+
|
|
1997
|
+
ISC
|
|
1998
|
+
|
|
1999
|
+
---
|
|
2000
|
+
|
|
2001
|
+
## Support
|
|
2002
|
+
|
|
2003
|
+
- **Documentation:** [GitHub Docs](https://github.com/your-repo/securenow-npm/tree/main/docs)
|
|
2004
|
+
- **Issues:** [GitHub Issues](https://github.com/your-repo/securenow-npm/issues)
|
|
2005
|
+
- **Examples:** [GitHub Examples](https://github.com/your-repo/securenow-npm/tree/main/examples)
|
|
2006
|
+
|
|
2007
|
+
---
|
|
2008
|
+
|
|
2009
|
+
## Changelog
|
|
2010
|
+
|
|
2011
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
2012
|
+
|
|
2013
|
+
---
|
|
2014
|
+
|
|
2015
|
+
## Contributing
|
|
2016
|
+
|
|
2017
|
+
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
2018
|
+
|
|
2019
|
+
---
|
|
2020
|
+
|
|
2021
|
+
## Related Packages
|
|
2022
|
+
|
|
2023
|
+
- [@opentelemetry/sdk-node](https://www.npmjs.com/package/@opentelemetry/sdk-node) - Core OpenTelemetry SDK
|
|
2024
|
+
- [@opentelemetry/auto-instrumentations-node](https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-node) - Auto-instrumentation
|
|
2025
|
+
- [@opentelemetry/exporter-trace-otlp-http](https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-http) - OTLP exporter
|
|
2026
|
+
|
|
2027
|
+
---
|
|
2028
|
+
|
|
2029
|
+
**Made with care for the Node.js community**
|