securenow 5.14.0 → 5.15.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 +20 -14
- package/NPM_README.md +368 -124
- package/README.md +23 -17
- package/SKILL-API.md +597 -0
- package/SKILL-CLI.md +395 -0
- package/cli.js +1 -1
- package/docs/FIREWALL-GUIDE.md +35 -12
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -43,30 +43,33 @@ See the [All Frameworks Quick Start](./docs/ALL-FRAMEWORKS-QUICKSTART.md) for te
|
|
|
43
43
|
|
|
44
44
|
### For Next.js Applications
|
|
45
45
|
|
|
46
|
-
**The easiest way to add observability to Next.js!**
|
|
47
|
-
|
|
48
46
|
```bash
|
|
49
|
-
#
|
|
47
|
+
# 1. Install
|
|
50
48
|
npm install securenow
|
|
49
|
+
|
|
50
|
+
# 2. Auto-scaffold instrumentation files
|
|
51
|
+
npx securenow init --key snk_live_abc123...
|
|
51
52
|
```
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
- Detect your Next.js project
|
|
55
|
-
- Create `instrumentation.ts` (or `.js`)
|
|
56
|
-
- Create `.env.local` template
|
|
54
|
+
This creates `instrumentation.ts` and tells you to wrap your `next.config.js`:
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
```javascript
|
|
57
|
+
// next.config.js
|
|
58
|
+
const { withSecureNow } = require('securenow/nextjs-webpack-config');
|
|
59
|
+
|
|
60
|
+
module.exports = withSecureNow({
|
|
61
|
+
// your existing config
|
|
62
|
+
});
|
|
63
|
+
```
|
|
59
64
|
|
|
60
|
-
|
|
65
|
+
`withSecureNow()` auto-detects Next.js 14 vs 15 and sets the correct externalization config. No manual `serverExternalPackages` list needed.
|
|
66
|
+
|
|
67
|
+
Configure `.env.local`:
|
|
61
68
|
|
|
62
69
|
```bash
|
|
63
70
|
SECURENOW_APPID=my-nextjs-app
|
|
64
71
|
SECURENOW_INSTANCE=http://your-otlp-collector:4318
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
**Alternative:** Use the CLI command
|
|
68
|
-
```bash
|
|
69
|
-
npx securenow init
|
|
72
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
70
73
|
```
|
|
71
74
|
|
|
72
75
|
**Done!** See [Next.js Complete Guide](./docs/NEXTJS-GUIDE.md) for details.
|
|
@@ -95,13 +98,16 @@ SECURENOW_APPID=my-nuxt-app
|
|
|
95
98
|
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
96
99
|
```
|
|
97
100
|
|
|
98
|
-
**Done!** All server-side requests are now traced automatically. See the [Nuxt 3 Complete Guide](./docs/NUXT-GUIDE.md) for details.
|
|
101
|
+
**Done!** All server-side requests are now traced automatically. The firewall also activates automatically when `SECURENOW_API_KEY` is set. See the [Nuxt 3 Complete Guide](./docs/NUXT-GUIDE.md) for details.
|
|
99
102
|
|
|
100
103
|
---
|
|
101
104
|
|
|
102
|
-
### CLI
|
|
105
|
+
### CLI -- Manage Everything from the Terminal
|
|
103
106
|
|
|
104
107
|
```bash
|
|
108
|
+
# Set up your project (auto-detects framework, creates instrumentation files)
|
|
109
|
+
npx securenow init --key snk_live_abc123...
|
|
110
|
+
|
|
105
111
|
# Authenticate
|
|
106
112
|
npx securenow login
|
|
107
113
|
|
|
@@ -337,7 +343,7 @@ Most users won't need this — just add `-r securenow/register` to your existing
|
|
|
337
343
|
| `securenow config get` | Show all config values |
|
|
338
344
|
| `securenow config set <key> <value>` | Set a config value |
|
|
339
345
|
| `securenow config path` | Show config file locations |
|
|
340
|
-
| `securenow init` |
|
|
346
|
+
| `securenow init [--key <KEY>]` | Auto-scaffold instrumentation for your framework |
|
|
341
347
|
| `securenow version` | Show CLI version |
|
|
342
348
|
|
|
343
349
|
### Global Flags
|
package/SKILL-API.md
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
# SecureNow SDK — Agent Skill
|
|
2
|
+
|
|
3
|
+
Instrument any Node.js application with OpenTelemetry tracing, structured logging, request body capture, and a multi-layer IP firewall. Supports Express, Fastify, NestJS, Koa, Hapi, Next.js, Nuxt 3, Vite (browser), and raw `http.createServer` — with zero code changes for most setups.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install securenow
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Install This Skill in Cursor
|
|
12
|
+
|
|
13
|
+
Save this file as `.cursor/skills/securenow-api/SKILL.md` in your project. Your AI agent will auto-discover it whenever you ask about integrating securenow, configuring tracing, setting up the firewall, or instrumenting any framework.
|
|
14
|
+
|
|
15
|
+
## Quick Start — Any Node.js Framework
|
|
16
|
+
|
|
17
|
+
### 1. Set Environment Variables
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# .env or .env.local
|
|
21
|
+
SECURENOW_APPID=my-app
|
|
22
|
+
SECURENOW_INSTANCE=https://your-collector:4318
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Run With Instrumentation
|
|
26
|
+
|
|
27
|
+
**Option A — CLI (recommended):**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx securenow run src/index.js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Option B — Node preload flag:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
node -r securenow/register src/index.js
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Option C — Auto-setup for Next.js:**
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx securenow init --key snk_live_...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
That's it. Traces and logs flow to your OTLP collector. No code changes for Express, Fastify, NestJS, Koa, Hapi, and raw Node.
|
|
46
|
+
|
|
47
|
+
### 3. Enable the Firewall (Optional)
|
|
48
|
+
|
|
49
|
+
Add one more env var to auto-activate IP blocking:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The firewall syncs your blocklist and enforces it on every request — zero code changes.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Import Map
|
|
60
|
+
|
|
61
|
+
| Import | Purpose | Type |
|
|
62
|
+
|--------|---------|------|
|
|
63
|
+
| `securenow` / `securenow/register` | Auto-register tracing + firewall (side-effect) | Preload (`-r`) |
|
|
64
|
+
| `securenow/tracing` | Core OTel SDK; exports `getLogger()`, `isLoggingEnabled()` | CJS |
|
|
65
|
+
| `securenow/nextjs` | Next.js instrumentation; exports `registerSecureNow(options?)` | CJS |
|
|
66
|
+
| `securenow/nextjs-webpack-config` | Next.js config wrapper; exports `withSecureNow()`, `getSecureNowWebpackConfig()`, `EXTERNAL_PACKAGES` | CJS |
|
|
67
|
+
| `securenow/nextjs-middleware` | Edge middleware body capture; exports `middleware()`, `redactSensitiveData()`, `DEFAULT_SENSITIVE_FIELDS` | CJS |
|
|
68
|
+
| `securenow/nextjs-wrapper` | Route handler wrappers; exports `withSecureNow()`, `withSecureNowAsync()`, `captureRequestBody()`, `redactSensitiveData()` | CJS |
|
|
69
|
+
| `securenow/nextjs-auto-capture` | Auto-patch Next request for body capture; exports `patchNextRequest()`, `safeBodyCapture()`, `redactSensitiveData()`, `isBodyCaptureEnabled()` | CJS |
|
|
70
|
+
| `securenow/nuxt` | Nuxt 3 module (add to `modules` array) | ESM |
|
|
71
|
+
| `securenow/firewall` | Standalone firewall; exports `init()`, `shutdown()`, `getStats()`, `getMatcher()`, `getAllowlistMatcher()` | CJS |
|
|
72
|
+
| `securenow/firewall-only` | Preload: dotenv + firewall only, no tracing | Preload (`-r`) |
|
|
73
|
+
| `securenow/cidr` | CIDR utilities; exports `createMatcher()`, `ipToInt()`, `parseCidr()`, `matchesCidr()` | CJS |
|
|
74
|
+
| `securenow/resolve-ip` | IP resolution; exports `resolveClientIp()`, `resolveSocketIp()`, `isFromTrustedProxy()` | CJS |
|
|
75
|
+
| `securenow/console-instrumentation` | Console→OTLP bridge; exports `originalConsole`, `restoreConsole()` | CJS |
|
|
76
|
+
| `securenow/web-vite` | Browser OTel (document load, fetch, XHR, user interaction); default export `startSecurenowWeb()` | ESM |
|
|
77
|
+
| `securenow/register-vite` | CJS bridge for Vite preload | CJS |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Framework Integration Guides
|
|
82
|
+
|
|
83
|
+
### Express / Fastify / NestJS / Koa / Hapi / Raw Node
|
|
84
|
+
|
|
85
|
+
No code changes. Use the preload:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
node -r securenow/register app.js
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or with the CLI:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx securenow run app.js
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**PM2:**
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// ecosystem.config.cjs
|
|
101
|
+
module.exports = {
|
|
102
|
+
apps: [{
|
|
103
|
+
name: 'my-app',
|
|
104
|
+
script: './app.js',
|
|
105
|
+
instances: 4,
|
|
106
|
+
node_args: '-r securenow/register',
|
|
107
|
+
env: {
|
|
108
|
+
SECURENOW_APPID: 'my-app',
|
|
109
|
+
SECURENOW_INSTANCE: 'https://your-collector:4318',
|
|
110
|
+
SECURENOW_NO_UUID: '1',
|
|
111
|
+
},
|
|
112
|
+
}],
|
|
113
|
+
};
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Docker:**
|
|
117
|
+
|
|
118
|
+
```dockerfile
|
|
119
|
+
ENV SECURENOW_APPID=my-app
|
|
120
|
+
ENV SECURENOW_INSTANCE=https://your-collector:4318
|
|
121
|
+
CMD ["node", "-r", "securenow/register", "app.js"]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Next.js
|
|
127
|
+
|
|
128
|
+
Three files to touch:
|
|
129
|
+
|
|
130
|
+
**1. `next.config.js`** (or `.mjs` / `.ts`):
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const { withSecureNow } = require('securenow/nextjs-webpack-config');
|
|
134
|
+
|
|
135
|
+
module.exports = withSecureNow({
|
|
136
|
+
// your existing config
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`withSecureNow()` auto-detects Next.js version:
|
|
141
|
+
- **>=15**: sets `serverExternalPackages`
|
|
142
|
+
- **<15**: sets `experimental.serverComponentsExternalPackages` + `experimental.instrumentationHook` + webpack ignore rules
|
|
143
|
+
|
|
144
|
+
**2. `instrumentation.ts`** (or `.js`, can be in `src/`):
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
export async function register() {
|
|
148
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
149
|
+
const { registerSecureNow } = require('securenow/nextjs');
|
|
150
|
+
registerSecureNow();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`registerSecureNow(options?)` accepts:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
interface RegisterOptions {
|
|
159
|
+
serviceName?: string; // override SECURENOW_APPID
|
|
160
|
+
endpoint?: string; // override SECURENOW_INSTANCE
|
|
161
|
+
noUuid?: boolean; // override SECURENOW_NO_UUID
|
|
162
|
+
captureBody?: boolean; // override SECURENOW_CAPTURE_BODY
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
On Vercel it uses `@vercel/otel`; self-hosted uses vanilla `@opentelemetry/sdk-node`.
|
|
167
|
+
|
|
168
|
+
**3. `.env.local`:**
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
SECURENOW_APPID=my-nextjs-app
|
|
172
|
+
SECURENOW_INSTANCE=https://your-collector:4318
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Next.js Body Capture
|
|
176
|
+
|
|
177
|
+
**Option A — Auto-capture (recommended):**
|
|
178
|
+
|
|
179
|
+
Add to your `instrumentation.ts`:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
export async function register() {
|
|
183
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
184
|
+
const { registerSecureNow } = require('securenow/nextjs');
|
|
185
|
+
registerSecureNow({ captureBody: true });
|
|
186
|
+
require('securenow/nextjs-auto-capture');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Option B — Middleware:**
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// middleware.ts
|
|
195
|
+
export { middleware } from 'securenow/nextjs-middleware';
|
|
196
|
+
export const config = { matcher: ['/api/:path*'] };
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Option C — Per-route wrapper:**
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { withSecureNow } from 'securenow/nextjs-wrapper';
|
|
203
|
+
|
|
204
|
+
export const POST = withSecureNow(async (req) => {
|
|
205
|
+
// handler
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Next.js with `securenow init`
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
npx securenow init --key snk_live_abc123...
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Auto-detects Next.js, creates `instrumentation.ts`, suggests `next.config` changes, writes API key to `.env.local`.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### Nuxt 3
|
|
220
|
+
|
|
221
|
+
**`nuxt.config.ts`:**
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
export default defineNuxtConfig({
|
|
225
|
+
modules: ['securenow/nuxt'],
|
|
226
|
+
securenow: {
|
|
227
|
+
// optional overrides (defaults come from env vars)
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**`.env`:**
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
SECURENOW_APPID=my-nuxt-app
|
|
236
|
+
SECURENOW_INSTANCE=https://your-collector:4318
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The Nuxt module auto-configures Nitro externals, runtime config, and a server plugin that sets up OTel tracing + logging + firewall.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### Vite / Browser
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
import startSecurenowWeb from 'securenow/web-vite';
|
|
247
|
+
|
|
248
|
+
startSecurenowWeb({
|
|
249
|
+
serviceName: 'my-frontend',
|
|
250
|
+
endpoint: 'https://your-collector:4318',
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Instruments document load, fetch, XMLHttpRequest, and user interactions with browser-side OpenTelemetry.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Firewall — Multi-Layer IP Blocking
|
|
259
|
+
|
|
260
|
+
The firewall auto-activates when `SECURENOW_API_KEY` is set. It syncs your blocklist from the SecureNow API and enforces it across up to four layers:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
Layer 4: Cloud/Edge WAF → blocked at CDN (Cloudflare, AWS WAF, GCP Cloud Armor)
|
|
264
|
+
Layer 3: OS Firewall → kernel-level DROP (iptables/nftables)
|
|
265
|
+
Layer 2: TCP Socket → socket.destroy() before HTTP parsing
|
|
266
|
+
Layer 1: HTTP Handler → 403 JSON response (always active)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Activate
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# .env
|
|
273
|
+
SECURENOW_API_KEY=snk_live_abc123... # auto-activates Layer 1
|
|
274
|
+
SECURENOW_FIREWALL_TCP=1 # opt-in Layer 2
|
|
275
|
+
SECURENOW_FIREWALL_IPTABLES=1 # opt-in Layer 3 (Linux, needs root)
|
|
276
|
+
SECURENOW_FIREWALL_CLOUD=cloudflare # opt-in Layer 4
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Firewall-Only Mode (No Tracing Overhead)
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
node -r securenow/firewall-only app.js
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Loads only dotenv + firewall. No OpenTelemetry, no tracing, no external packages needed.
|
|
286
|
+
|
|
287
|
+
### Programmatic Firewall API
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
const firewall = require('securenow/firewall');
|
|
291
|
+
|
|
292
|
+
await firewall.init({
|
|
293
|
+
apiKey: process.env.SECURENOW_API_KEY,
|
|
294
|
+
apiUrl: 'https://api.securenow.ai',
|
|
295
|
+
syncInterval: 300, // full sync every 5 min
|
|
296
|
+
versionCheckInterval: 10, // lightweight version check every 10s
|
|
297
|
+
failMode: 'open', // 'open' or 'closed'
|
|
298
|
+
statusCode: 403,
|
|
299
|
+
log: true,
|
|
300
|
+
tcp: false,
|
|
301
|
+
iptables: false,
|
|
302
|
+
cloud: null, // 'cloudflare' | 'aws' | 'gcp'
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const stats = firewall.getStats();
|
|
306
|
+
const matcher = firewall.getMatcher(); // (ip) => boolean
|
|
307
|
+
const allowMatcher = firewall.getAllowlistMatcher();
|
|
308
|
+
|
|
309
|
+
await firewall.shutdown();
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Cloud WAF Providers
|
|
313
|
+
|
|
314
|
+
**Cloudflare:**
|
|
315
|
+
```bash
|
|
316
|
+
SECURENOW_FIREWALL_CLOUD=cloudflare
|
|
317
|
+
CLOUDFLARE_API_TOKEN=your-token
|
|
318
|
+
CLOUDFLARE_ACCOUNT_ID=your-account-id
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**AWS WAF:**
|
|
322
|
+
```bash
|
|
323
|
+
SECURENOW_FIREWALL_CLOUD=aws
|
|
324
|
+
AWS_WAF_IP_SET_ID=your-ip-set-id
|
|
325
|
+
# + standard AWS credentials (env, profile, or IAM role)
|
|
326
|
+
```
|
|
327
|
+
Requires peer dep: `npm install @aws-sdk/client-wafv2`
|
|
328
|
+
|
|
329
|
+
**GCP Cloud Armor:**
|
|
330
|
+
```bash
|
|
331
|
+
SECURENOW_FIREWALL_CLOUD=gcp
|
|
332
|
+
GCP_PROJECT_ID=your-project
|
|
333
|
+
GCP_SECURITY_POLICY=your-policy-name
|
|
334
|
+
```
|
|
335
|
+
Requires peer dep: `npm install @google-cloud/compute`
|
|
336
|
+
|
|
337
|
+
**Dry-run (log only, no actual WAF changes):**
|
|
338
|
+
```bash
|
|
339
|
+
SECURENOW_FIREWALL_CLOUD_DRY_RUN=1
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Logging
|
|
345
|
+
|
|
346
|
+
Logging is enabled by default (`SECURENOW_LOGGING_ENABLED=1`). Logs are exported to your OTLP collector alongside traces.
|
|
347
|
+
|
|
348
|
+
### Get a Logger
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
const { getLogger } = require('securenow/tracing');
|
|
352
|
+
|
|
353
|
+
const logger = getLogger('my-module', '1.0.0');
|
|
354
|
+
if (logger) {
|
|
355
|
+
logger.emit({ body: 'User login succeeded', severityText: 'INFO', attributes: { userId: '123' } });
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Console Instrumentation
|
|
360
|
+
|
|
361
|
+
When tracing is active, `console.log/warn/error` are automatically patched to emit OTLP log records correlated with the active trace span. To access the original console:
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
const { originalConsole, restoreConsole } = require('securenow/console-instrumentation');
|
|
365
|
+
originalConsole.log('This bypasses OTLP');
|
|
366
|
+
restoreConsole(); // undo the patch
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## IP Resolution Utilities
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
const { resolveClientIp, resolveSocketIp, isFromTrustedProxy } = require('securenow/resolve-ip');
|
|
375
|
+
|
|
376
|
+
// From an HTTP request (respects X-Forwarded-For from trusted proxies)
|
|
377
|
+
const clientIp = resolveClientIp(req);
|
|
378
|
+
|
|
379
|
+
// Direct socket IP
|
|
380
|
+
const socketIp = resolveSocketIp(req);
|
|
381
|
+
|
|
382
|
+
// Check if request comes from a trusted proxy
|
|
383
|
+
const trusted = isFromTrustedProxy(req);
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### CIDR Matching
|
|
387
|
+
|
|
388
|
+
```javascript
|
|
389
|
+
const { createMatcher, ipToInt, parseCidr, matchesCidr } = require('securenow/cidr');
|
|
390
|
+
|
|
391
|
+
const isBlocked = createMatcher(['10.0.0.0/8', '192.168.1.0/24']);
|
|
392
|
+
isBlocked('10.0.0.5'); // true
|
|
393
|
+
isBlocked('8.8.8.8'); // false
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Sensitive Data Redaction
|
|
399
|
+
|
|
400
|
+
Available from multiple entry points (`securenow/nextjs-middleware`, `securenow/nextjs-wrapper`, `securenow/nextjs-auto-capture`):
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
const { redactSensitiveData, DEFAULT_SENSITIVE_FIELDS } = require('securenow/nextjs-middleware');
|
|
404
|
+
|
|
405
|
+
const safe = redactSensitiveData({ username: 'alice', password: 's3cret', token: 'abc' });
|
|
406
|
+
// { username: 'alice', password: '[REDACTED]', token: '[REDACTED]' }
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Auto-redacted fields:** `password`, `passwd`, `pwd`, `secret`, `token`, `api_key`, `apikey`, `access_token`, `auth`, `credentials`, `mysql_pwd`, `stripeToken`, `card`, `cardnumber`, `ccv`, `cvc`, `cvv`, `ssn`, `pin`.
|
|
410
|
+
|
|
411
|
+
Add custom fields via `SECURENOW_SENSITIVE_FIELDS=field1,field2`.
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Environment Variables — Complete Reference
|
|
416
|
+
|
|
417
|
+
### Required
|
|
418
|
+
|
|
419
|
+
| Variable | Description | Default |
|
|
420
|
+
|----------|-------------|---------|
|
|
421
|
+
| `SECURENOW_APPID` | Service name / app identifier | *(auto-generated with UUID)* |
|
|
422
|
+
| `SECURENOW_INSTANCE` | OTLP collector base URL | `https://freetrial.securenow.ai:4318` |
|
|
423
|
+
|
|
424
|
+
### Service Naming
|
|
425
|
+
|
|
426
|
+
| Variable | Description | Default |
|
|
427
|
+
|----------|-------------|---------|
|
|
428
|
+
| `OTEL_SERVICE_NAME` | OpenTelemetry standard; overrides `SECURENOW_APPID` | — |
|
|
429
|
+
| `SECURENOW_NO_UUID` | `1` to use exact app ID without UUID suffix | `0` |
|
|
430
|
+
| `SECURENOW_STRICT` | `1` to exit if APPID missing in PM2 cluster | `0` |
|
|
431
|
+
|
|
432
|
+
### OTLP Connection
|
|
433
|
+
|
|
434
|
+
| Variable | Description | Default |
|
|
435
|
+
|----------|-------------|---------|
|
|
436
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Standard OTel endpoint; overrides `SECURENOW_INSTANCE` | — |
|
|
437
|
+
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Override traces endpoint specifically | `{instance}/v1/traces` |
|
|
438
|
+
| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Override logs endpoint specifically | `{instance}/v1/logs` |
|
|
439
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | Comma-separated `key=value` headers for OTLP requests | — |
|
|
440
|
+
|
|
441
|
+
### Behavior
|
|
442
|
+
|
|
443
|
+
| Variable | Description | Default |
|
|
444
|
+
|----------|-------------|---------|
|
|
445
|
+
| `SECURENOW_LOGGING_ENABLED` | Enable OTLP log export | `1` |
|
|
446
|
+
| `SECURENOW_CAPTURE_BODY` | Capture HTTP request bodies | `0` |
|
|
447
|
+
| `SECURENOW_MAX_BODY_SIZE` | Max body size in bytes | `10240` |
|
|
448
|
+
| `SECURENOW_CAPTURE_MULTIPART` | Capture multipart/form-data (streaming, metadata only) | `0` |
|
|
449
|
+
| `SECURENOW_SENSITIVE_FIELDS` | Comma-separated extra fields to redact | — |
|
|
450
|
+
| `SECURENOW_DISABLE_INSTRUMENTATIONS` | Comma-separated packages to skip (e.g. `fs,dns`) | — |
|
|
451
|
+
| `SECURENOW_TEST_SPAN` | `1` to emit a test span on startup | `0` |
|
|
452
|
+
| `SECURENOW_HIDE_BANNER` | `1` to suppress free-trial upgrade banner | `0` |
|
|
453
|
+
| `OTEL_LOG_LEVEL` | SDK log level: `none`, `error`, `warn`, `info`, `debug` | `none` |
|
|
454
|
+
| `NODE_ENV` | Sent as `deployment.environment` attribute | `production` |
|
|
455
|
+
|
|
456
|
+
### Firewall
|
|
457
|
+
|
|
458
|
+
| Variable | Description | Default |
|
|
459
|
+
|----------|-------------|---------|
|
|
460
|
+
| `SECURENOW_API_KEY` | API key (`snk_live_...`); activates firewall when set | — |
|
|
461
|
+
| `SECURENOW_API_URL` | SecureNow API base URL | `https://api.securenow.ai` |
|
|
462
|
+
| `SECURENOW_FIREWALL_ENABLED` | Master kill-switch (`0` to disable) | `1` |
|
|
463
|
+
| `SECURENOW_FIREWALL_SYNC_INTERVAL` | Full blocklist refresh interval in seconds | `300` |
|
|
464
|
+
| `SECURENOW_FIREWALL_FAIL_MODE` | `open` (allow all when unavailable) or `closed` | `open` |
|
|
465
|
+
| `SECURENOW_FIREWALL_STATUS_CODE` | HTTP status for blocked requests | `403` |
|
|
466
|
+
| `SECURENOW_FIREWALL_LOG` | Log blocked requests | `1` |
|
|
467
|
+
| `SECURENOW_FIREWALL_TCP` | Enable Layer 2 TCP blocking | `0` |
|
|
468
|
+
| `SECURENOW_FIREWALL_IPTABLES` | Enable Layer 3 iptables/nftables | `0` |
|
|
469
|
+
| `SECURENOW_FIREWALL_CLOUD` | Cloud WAF: `cloudflare`, `aws`, or `gcp` | — |
|
|
470
|
+
| `SECURENOW_FIREWALL_CLOUD_DRY_RUN` | `1` to log cloud pushes without applying | `0` |
|
|
471
|
+
| `SECURENOW_TRUSTED_PROXIES` | Comma-separated trusted proxy IPs | — |
|
|
472
|
+
|
|
473
|
+
### Cloud WAF Provider Variables
|
|
474
|
+
|
|
475
|
+
| Provider | Variables |
|
|
476
|
+
|----------|-----------|
|
|
477
|
+
| Cloudflare | `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID` |
|
|
478
|
+
| AWS WAF | `AWS_WAF_IP_SET_ID`, `AWS_WAF_IP_SET_NAME`, `AWS_WAF_SCOPE` |
|
|
479
|
+
| GCP | `GCP_PROJECT_ID`, `GCP_SECURITY_POLICY` |
|
|
480
|
+
|
|
481
|
+
### Priority Order
|
|
482
|
+
|
|
483
|
+
**Service name:** `OTEL_SERVICE_NAME` > `SECURENOW_APPID` > auto-generated
|
|
484
|
+
|
|
485
|
+
**Endpoint:** `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` > `OTEL_EXPORTER_OTLP_ENDPOINT` > `SECURENOW_INSTANCE` > `https://freetrial.securenow.ai:4318`
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Recipes for Agentic AI
|
|
490
|
+
|
|
491
|
+
### Add Observability to an Existing Express App
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
npm install securenow
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Create `.env`:
|
|
498
|
+
```
|
|
499
|
+
SECURENOW_APPID=my-express-api
|
|
500
|
+
SECURENOW_INSTANCE=https://your-collector:4318
|
|
501
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
502
|
+
SECURENOW_CAPTURE_BODY=1
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Update `package.json`:
|
|
506
|
+
```json
|
|
507
|
+
{ "scripts": { "start": "node -r securenow/register src/index.js" } }
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
No code changes to the application needed.
|
|
511
|
+
|
|
512
|
+
### Add Observability + Firewall to a Next.js App
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
npm install securenow
|
|
516
|
+
npx securenow init --key snk_live_abc123...
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
The `init` command creates `instrumentation.ts` and suggests `next.config` changes. Then set env vars in `.env.local`:
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
SECURENOW_APPID=my-nextjs-app
|
|
523
|
+
SECURENOW_INSTANCE=https://your-collector:4318
|
|
524
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
525
|
+
SECURENOW_CAPTURE_BODY=1
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Enable Firewall With Zero Tracing Overhead
|
|
529
|
+
|
|
530
|
+
For apps that only need IP blocking:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
node -r securenow/firewall-only app.js
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Set `SECURENOW_API_KEY` in the environment. No other configuration needed.
|
|
537
|
+
|
|
538
|
+
### Production Hardened Configuration
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
SECURENOW_APPID=prod-api
|
|
542
|
+
SECURENOW_INSTANCE=https://collector.prod.internal:4318
|
|
543
|
+
SECURENOW_NO_UUID=1
|
|
544
|
+
SECURENOW_STRICT=1
|
|
545
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
546
|
+
SECURENOW_CAPTURE_BODY=0
|
|
547
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
548
|
+
SECURENOW_FIREWALL_TCP=1
|
|
549
|
+
SECURENOW_FIREWALL_SYNC_INTERVAL=30
|
|
550
|
+
SECURENOW_FIREWALL_FAIL_MODE=open
|
|
551
|
+
OTEL_LOG_LEVEL=error
|
|
552
|
+
NODE_ENV=production
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Instrument a Docker Container
|
|
556
|
+
|
|
557
|
+
```dockerfile
|
|
558
|
+
FROM node:20-slim
|
|
559
|
+
WORKDIR /app
|
|
560
|
+
COPY package*.json ./
|
|
561
|
+
RUN npm ci --omit=dev
|
|
562
|
+
COPY . .
|
|
563
|
+
|
|
564
|
+
ENV SECURENOW_APPID=my-service
|
|
565
|
+
ENV SECURENOW_INSTANCE=http://otel-collector:4318
|
|
566
|
+
ENV SECURENOW_NO_UUID=1
|
|
567
|
+
ENV SECURENOW_API_KEY=snk_live_abc123...
|
|
568
|
+
|
|
569
|
+
CMD ["node", "-r", "securenow/register", "src/index.js"]
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Kubernetes with Separate Trace/Log Collectors
|
|
573
|
+
|
|
574
|
+
```bash
|
|
575
|
+
SECURENOW_APPID=k8s-service
|
|
576
|
+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://tempo:4318/v1/traces
|
|
577
|
+
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://loki:4318/v1/logs
|
|
578
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
579
|
+
SECURENOW_NO_UUID=1
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Verification
|
|
585
|
+
|
|
586
|
+
On startup, securenow logs its configuration:
|
|
587
|
+
|
|
588
|
+
```
|
|
589
|
+
[securenow] pid=12345 SECURENOW_APPID="my-app" → service.name=my-app
|
|
590
|
+
[securenow] OTel SDK started → https://collector:4318/v1/traces
|
|
591
|
+
[securenow] Logging: ENABLED → https://collector:4318/v1/logs
|
|
592
|
+
[securenow] Request body capture: ENABLED (max: 10240 bytes)
|
|
593
|
+
[securenow] Firewall: ENABLED
|
|
594
|
+
[securenow] Firewall: synced 142 blocked IPs
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Use `OTEL_LOG_LEVEL=debug` and `SECURENOW_TEST_SPAN=1` to troubleshoot connectivity issues.
|