securenow 7.5.1 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CONSUMING-APPS-GUIDE.md +2 -0
  2. package/NPM_README.md +201 -237
  3. package/README.md +73 -26
  4. package/SKILL-API.md +209 -205
  5. package/SKILL-CLI.md +71 -64
  6. package/app-config.js +479 -83
  7. package/cli/apiKey.js +1 -1
  8. package/cli/apps.js +1 -1
  9. package/cli/config.js +31 -12
  10. package/cli/credentials.js +88 -0
  11. package/cli/diagnostics.js +68 -104
  12. package/cli/firewall.js +29 -14
  13. package/cli/init.js +208 -206
  14. package/cli/monitor.js +107 -43
  15. package/cli/security.js +24 -12
  16. package/cli/utils.js +2 -1
  17. package/cli.js +71 -39
  18. package/console-instrumentation.js +1 -1
  19. package/docs/ENVIRONMENT-VARIABLES.md +137 -863
  20. package/docs/ENVIRONMENTS.md +60 -0
  21. package/docs/EXPRESS-SETUP-GUIDE.md +3 -0
  22. package/docs/FIREWALL-GUIDE.md +3 -0
  23. package/docs/INDEX.md +6 -8
  24. package/docs/LOGGING-GUIDE.md +3 -0
  25. package/docs/MCP-GUIDE.md +8 -0
  26. package/docs/NEXTJS-GUIDE.md +3 -0
  27. package/docs/NEXTJS-QUICKSTART.md +24 -16
  28. package/docs/NUXT-GUIDE.md +3 -0
  29. package/docs/QUICKSTART-BODY-CAPTURE.md +3 -0
  30. package/docs/REQUEST-BODY-CAPTURE.md +3 -0
  31. package/firewall-cloud.js +10 -10
  32. package/firewall-only.js +25 -23
  33. package/firewall.js +47 -29
  34. package/free-trial-banner.js +1 -1
  35. package/mcp/catalog.js +104 -17
  36. package/nextjs-auto-capture.d.ts +7 -4
  37. package/nextjs-auto-capture.js +7 -7
  38. package/nextjs-middleware.js +4 -3
  39. package/nextjs-wrapper.js +6 -6
  40. package/nextjs.d.ts +36 -25
  41. package/nextjs.js +47 -55
  42. package/nuxt-server-plugin.mjs +35 -51
  43. package/nuxt.d.ts +29 -23
  44. package/package.json +1 -1
  45. package/postinstall.js +27 -61
  46. package/register.d.ts +19 -33
  47. package/register.js +8 -8
  48. package/resolve-ip.js +4 -5
  49. package/tracing.d.ts +21 -19
  50. package/tracing.js +34 -42
package/mcp/catalog.js CHANGED
@@ -5,7 +5,77 @@ const path = require('path');
5
5
 
6
6
  const TEXT = 'text/plain';
7
7
  const JSON_MIME = 'application/json';
8
- const MARKDOWN = 'text/markdown';
8
+ const MARKDOWN = 'text/markdown';
9
+
10
+ const UNIVERSAL_SECURENOW_SETUP_PROMPT = `You are working in an existing JavaScript or TypeScript app. Set up SecureNow end-to-end for the framework/runtime already used by this repo. Treat this as a real onboarding, not just a package install.
11
+
12
+ Primary goals:
13
+ - Use the latest published SecureNow npm package. Require securenow@7.5.1 or newer.
14
+ - By default, enable tracing, logs, POST request body capture, multipart metadata capture, and the SecureNow firewall.
15
+ - If I explicitly ask for firewall-only mode, keep the same install/login/verification gates, but use firewall-only preload and do not add tracing, logging, or OTel instrumentation.
16
+ - The firewall must protect the selected SecureNow app, use SecureNow's own blocklist/allowlist/IPDB data, and respect that app's SecureNow IPDB confidence threshold. Do not add custom IP reputation providers or custom auto-blocking.
17
+
18
+ Safety rules:
19
+ - Do not print full API keys, JWTs, tokens, or .securenow/credentials.json. Mask secrets.
20
+ - Do not commit secrets. Ensure .securenow/ is in .gitignore.
21
+ - Do not manually browse to a SecureNow auth URL. Always start auth with npx securenow login so the CLI generates the required callback and state.
22
+ - If the browser says "Missing callback parameter", you opened the wrong URL: rerun npx securenow login from the project root.
23
+ - Do not skip login, app selection, firewall connection, or verification unless I explicitly say to.
24
+ - Preserve existing middleware, proxy, instrumentation, Docker, PM2, and start scripts.
25
+
26
+ Runbook:
27
+ 1. Identify the project root, package manager, framework, start/build/test scripts, process manager files, Docker files, and existing middleware/proxy/instrumentation.
28
+ 2. Install or upgrade SecureNow with the detected package manager, using securenow@latest. Verify the actual installed version with:
29
+ node -p "require('./node_modules/securenow/package.json').version"
30
+ npx securenow version
31
+ Stop and fix the install if either is below 7.5.1 or npx still resolves an older local package.
32
+ 3. Read the installed package surface before editing files: node_modules/securenow/package.json, README/NPM_README, SKILL-API, SKILL-CLI, docs/MCP-GUIDE.md if present, npx securenow help, and relevant subcommand help for login/init/firewall/doctor/env/test-span/log/mcp.
33
+ 4. Mandatory auth gate:
34
+ - Run npx securenow whoami from the project root.
35
+ - If not logged in, run npx securenow login from the project root and wait for the browser flow.
36
+ - After the CLI exits, rerun npx securenow whoami.
37
+ - Do not proceed to app edits or verification until whoami succeeds.
38
+ 5. Validate project-local credentials without exposing secrets:
39
+ - Confirm .securenow/credentials.json exists.
40
+ - Confirm it has SecureNow's default config/explanations block.
41
+ - Confirm it has an app key/name/instance and a firewall API key after login/app selection.
42
+ - Confirm .securenow/ is ignored by git.
43
+ 6. Run npx securenow init. If it fails with ui.header is not a function or another CLI bug, upgrade to securenow@latest, verify >=7.5.1, and retry. Do not silently ignore init failures.
44
+ 7. Configure the least invasive framework-specific integration:
45
+ - Next.js: preserve instrumentation.js/ts. Register securenow/nextjs only when NEXT_RUNTIME is nodejs. In ESM files, use createRequire before require("securenow/nextjs"). Include require("securenow/nextjs-auto-capture") for body capture. For Next 15+, add securenow to serverExternalPackages. For older Next.js, use experimental.serverComponentsExternalPackages. Preserve proxy.js/middleware.js.
46
+ - Nuxt/Nitro: use the documented securenow/nuxt module or Nitro server plugin.
47
+ - Express/Fastify/NestJS/Koa/Hapi/Hono/raw Node: preload securenow/register through existing scripts, NODE_OPTIONS, PM2 node_args, Docker CMD, or the process manager already used.
48
+ - Firewall-only: preload securenow/firewall-only or use the documented securenow run --firewall-only command. Do not add OTel/tracing/logging in this mode.
49
+ - Vite/browser-only: use only documented browser integration and state that server firewall protection requires a server runtime.
50
+ 8. Do not create or require a .env file for local development or production. The SDK reads defaults from .securenow/credentials.json:
51
+ - config.logging.enabled: true
52
+ - config.capture.body: true
53
+ - config.capture.multipart: true
54
+ - config.firewall.enabled: true
55
+ - config.firewall.failMode: "open"
56
+ - config.capture.maxBodySize: 10240
57
+ For production, run npx securenow credentials runtime --env production, store the resulting JSON as a deployment secret file, and mount/copy it to <app-root>/.securenow/credentials.json. Do not recommend env vars unless the user explicitly asks for legacy fallbacks.
58
+ Local credentials should use config.runtime.deploymentEnvironment="local". Production runtime credentials should use "production". The app key stays the same; traces, logs, firewall status, forensics, and CLI/MCP queries are scoped by environment.
59
+ 9. Verify firewall and threshold:
60
+ - Run npx securenow firewall apps and npx securenow firewall status.
61
+ - Confirm the selected app is present, firewallEnabled is true, and the SecureNow IPDB confidence threshold is visible.
62
+ - If firewallEnabled is false, run the documented per-app enable command, for example npx securenow firewall enable --app <appKey>, then verify again.
63
+ 10. End-to-end proof:
64
+ - Run npx securenow doctor.
65
+ - Run npx securenow env and confirm loggingEnabled, captureBody, captureMultipart, and firewallEnabled resolve to true or 1 from credentials/defaults, unless I explicitly requested firewall-only.
66
+ - If available and not in firewall-only mode, send telemetry:
67
+ npx securenow test-span securenow.onboarding
68
+ npx securenow log send "SecureNow onboarding test" --level info
69
+ - Run the repo build/test command if available.
70
+ - For MCP-capable clients, optionally smoke-test npx securenow mcp with the securenow_auth_status tool.
71
+
72
+ Final response:
73
+ - List every changed file.
74
+ - Summarize installed SecureNow version and linked app name/key, masking secrets.
75
+ - Show verification commands and pass/fail result.
76
+ - Mention skipped checks and why.
77
+ - Provide exact command(s) to start the protected app.`;
78
+
9
79
 
10
80
  function string(description) {
11
81
  return { type: 'string', description };
@@ -83,6 +153,10 @@ const timeRangeInput = {
83
153
  to: string('End time as ISO 8601, optional.'),
84
154
  };
85
155
 
156
+ const environmentInput = {
157
+ environment: string('Deployment environment scope: production, staging, preview, local, test, or all. Default for investigations is production.'),
158
+ };
159
+
86
160
  const TOOLS = [
87
161
  {
88
162
  name: 'securenow_auth_status',
@@ -151,7 +225,8 @@ const TOOLS = [
151
225
  readOnly: true,
152
226
  method: 'GET',
153
227
  endpoint: '/firewall/status',
154
- inputSchema: objectSchema({}),
228
+ queryFields: ['environment'],
229
+ inputSchema: objectSchema({ ...environmentInput }),
155
230
  },
156
231
  {
157
232
  name: 'securenow_firewall_enable',
@@ -163,9 +238,11 @@ const TOOLS = [
163
238
  method: 'PATCH',
164
239
  endpoint: '/firewall/app/:appKey',
165
240
  pathParams: ['appKey'],
241
+ bodyFields: ['environment'],
166
242
  fixedBody: { enabled: true },
167
243
  inputSchema: objectSchema({
168
244
  appKey: string('Application key UUID.'),
245
+ ...environmentInput,
169
246
  ...confirmSchema,
170
247
  }, ['appKey', 'confirm', 'reason']),
171
248
  },
@@ -180,9 +257,11 @@ const TOOLS = [
180
257
  method: 'PATCH',
181
258
  endpoint: '/firewall/app/:appKey',
182
259
  pathParams: ['appKey'],
260
+ bodyFields: ['environment'],
183
261
  fixedBody: { enabled: false },
184
262
  inputSchema: objectSchema({
185
263
  appKey: string('Application key UUID.'),
264
+ ...environmentInput,
186
265
  ...confirmSchema,
187
266
  }, ['appKey', 'confirm', 'reason']),
188
267
  },
@@ -196,10 +275,11 @@ const TOOLS = [
196
275
  method: 'PATCH',
197
276
  endpoint: '/firewall/app/:appKey',
198
277
  pathParams: ['appKey'],
199
- bodyFields: ['confidenceMinimum'],
278
+ bodyFields: ['confidenceMinimum', 'environment'],
200
279
  inputSchema: objectSchema({
201
280
  appKey: string('Application key UUID.'),
202
281
  confidenceMinimum: number('Minimum SecureNow IPDB abuse confidence score.', { minimum: 0, maximum: 100 }),
282
+ ...environmentInput,
203
283
  ...confirmSchema,
204
284
  }, ['appKey', 'confidenceMinimum', 'confirm', 'reason']),
205
285
  },
@@ -212,8 +292,10 @@ const TOOLS = [
212
292
  method: 'GET',
213
293
  endpoint: '/firewall/check/:ip',
214
294
  pathParams: ['ip'],
295
+ queryFields: ['environment'],
215
296
  inputSchema: objectSchema({
216
297
  ip: string('IPv4 address to test.'),
298
+ ...environmentInput,
217
299
  }, ['ip']),
218
300
  },
219
301
  {
@@ -224,10 +306,11 @@ const TOOLS = [
224
306
  readOnly: true,
225
307
  method: 'GET',
226
308
  endpoint: '/traces',
227
- queryFields: ['appKeys', 'from', 'to', 'limit'],
309
+ queryFields: ['appKeys', 'environment', 'from', 'to', 'limit'],
228
310
  normalize: { appKeys: normalizeAppKeys },
229
311
  inputSchema: objectSchema({
230
312
  ...appKeysInput,
313
+ ...environmentInput,
231
314
  ...timeRangeInput,
232
315
  limit: number('Maximum traces to return.', { minimum: 1, maximum: 200 }),
233
316
  }, ['appKeys']),
@@ -241,11 +324,12 @@ const TOOLS = [
241
324
  method: 'GET',
242
325
  endpoint: '/traces/:traceId',
243
326
  pathParams: ['traceId'],
244
- queryFields: ['appKeys'],
327
+ queryFields: ['appKeys', 'environment'],
245
328
  normalize: { appKeys: normalizeAppKeys },
246
329
  inputSchema: objectSchema({
247
330
  traceId: string('Trace id.'),
248
331
  ...appKeysInput,
332
+ ...environmentInput,
249
333
  }, ['traceId', 'appKeys']),
250
334
  },
251
335
  {
@@ -256,10 +340,11 @@ const TOOLS = [
256
340
  readOnly: true,
257
341
  method: 'GET',
258
342
  endpoint: '/logs',
259
- queryFields: ['appKeys', 'from', 'to', 'severity', 'limit'],
343
+ queryFields: ['appKeys', 'environment', 'from', 'to', 'severity', 'limit'],
260
344
  normalize: { appKeys: normalizeAppKeys },
261
345
  inputSchema: objectSchema({
262
346
  ...appKeysInput,
347
+ ...environmentInput,
263
348
  ...timeRangeInput,
264
349
  severity: string('Optional severity filter.'),
265
350
  limit: number('Maximum logs to return.', { minimum: 1, maximum: 500 }),
@@ -274,11 +359,12 @@ const TOOLS = [
274
359
  method: 'GET',
275
360
  endpoint: '/logs/trace/:traceId',
276
361
  pathParams: ['traceId'],
277
- queryFields: ['appKeys'],
362
+ queryFields: ['appKeys', 'environment'],
278
363
  normalize: { appKeys: normalizeAppKeys },
279
364
  inputSchema: objectSchema({
280
365
  traceId: string('Trace id.'),
281
366
  ...appKeysInput,
367
+ ...environmentInput,
282
368
  }, ['traceId', 'appKeys']),
283
369
  },
284
370
  {
@@ -339,8 +425,12 @@ const TOOLS = [
339
425
  method: 'GET',
340
426
  endpoint: '/ip/:ip/traces',
341
427
  pathParams: ['ip'],
428
+ queryFields: ['appKeys', 'environment'],
429
+ normalize: { appKeys: normalizeAppKeys },
342
430
  inputSchema: objectSchema({
343
431
  ip: string('IPv4 address.'),
432
+ ...appKeysInput,
433
+ ...environmentInput,
344
434
  }, ['ip']),
345
435
  },
346
436
  {
@@ -352,11 +442,12 @@ const TOOLS = [
352
442
  confirm: true,
353
443
  method: 'POST',
354
444
  endpoint: '/forensics/query',
355
- bodyFields: ['query', 'applicationId', 'instanceId'],
445
+ bodyFields: ['query', 'applicationId', 'instanceId', 'environment'],
356
446
  inputSchema: objectSchema({
357
447
  query: string('Natural-language forensic query.'),
358
448
  applicationId: string('Optional application database id.'),
359
449
  instanceId: string('Optional ClickHouse instance id.'),
450
+ ...environmentInput,
360
451
  ...confirmSchema,
361
452
  }, ['query', 'confirm', 'reason']),
362
453
  },
@@ -596,6 +687,7 @@ const PROMPTS = [
596
687
  arguments: [
597
688
  { name: 'ip', description: 'IP address to investigate.', required: true },
598
689
  { name: 'appKeys', description: 'Optional comma-separated app keys.', required: false },
690
+ { name: 'environment', description: 'Environment to investigate. Defaults to production; use all only when explicitly needed.', required: false },
599
691
  ],
600
692
  },
601
693
  ];
@@ -608,15 +700,9 @@ function promptMessages(name, args = {}) {
608
700
  content: {
609
701
  type: 'text',
610
702
  text: [
611
- 'Set up SecureNow end-to-end for this JavaScript/TypeScript project.',
612
703
  args.projectRoot ? `Project root: ${args.projectRoot}` : null,
613
- 'Install or upgrade to securenow@7.5.0 or newer first.',
614
- 'Run npx securenow whoami. If not logged in, run npx securenow login and stop until the browser flow completes.',
615
- 'After login, verify whoami succeeds and that .securenow/credentials.json contains the selected app and firewall API key.',
616
- 'Use the least invasive integration for the detected framework.',
617
- 'Keep tracing, logs, POST body capture, multipart metadata capture, and the per-app firewall enabled by default.',
618
- 'Verify with build/test plus npx securenow firewall apps/status and npx securenow env/doctor when available.',
619
- ].filter(Boolean).join('\n'),
704
+ UNIVERSAL_SECURENOW_SETUP_PROMPT,
705
+ ].filter(Boolean).join('\n\n'),
620
706
  },
621
707
  },
622
708
  ];
@@ -632,7 +718,7 @@ function promptMessages(name, args = {}) {
632
718
  'Verify SecureNow default-on protection for this project.',
633
719
  args.appKey ? `App key: ${args.appKey}` : null,
634
720
  'Check npx securenow whoami, npx securenow api-key show, npx securenow firewall apps, and npx securenow firewall status.',
635
- 'Confirm traces, logs, SECURENOW_CAPTURE_BODY, SECURENOW_CAPTURE_MULTIPART, and firewall are enabled by default unless explicitly set to 0.',
721
+ 'Confirm traces, logs, capture.body, capture.multipart, and firewall.enabled are enabled by .securenow/credentials.json defaults unless explicitly set false.',
636
722
  'Do not print full tokens or API keys.',
637
723
  ].filter(Boolean).join('\n'),
638
724
  },
@@ -649,6 +735,7 @@ function promptMessages(name, args = {}) {
649
735
  text: [
650
736
  `Investigate IP ${args.ip || '<ip>'} with SecureNow.`,
651
737
  args.appKeys ? `Scope to app keys: ${args.appKeys}` : null,
738
+ `Environment scope: ${args.environment || 'production'}.`,
652
739
  'Use IP intelligence first, then related traces/logs, then recommend remediation.',
653
740
  'Only block, allow, or trust the IP after explicit user confirmation.',
654
741
  ].filter(Boolean).join('\n'),
@@ -15,10 +15,13 @@
15
15
  * }
16
16
  * ```
17
17
  *
18
- * Environment Variables:
19
- * - SECURENOW_CAPTURE_BODY=0 - Disable body capture (default: enabled)
20
- * - SECURENOW_MAX_BODY_SIZE=10240 - Max body size in bytes (default: 10KB)
21
- * - SECURENOW_SENSITIVE_FIELDS=field1,field2 - Additional sensitive fields to redact
18
+ * Configuration:
19
+ * - config.capture.body=false - Disable body capture (default: enabled)
20
+ * - config.capture.maxBodySize=10240 - Max body size in bytes (default: 10KB)
21
+ * - config.capture.sensitiveFields=["field"] - Additional sensitive fields to redact
22
+ *
23
+ * The SDK reads these from .securenow/credentials.json in local development
24
+ * and production. Legacy env vars are fallback-only for existing installs.
22
25
  *
23
26
  * Features:
24
27
  * - Zero code changes required in API routes
@@ -17,6 +17,8 @@
17
17
  */
18
18
 
19
19
  const { trace } = require('@opentelemetry/api');
20
+ const appConfig = require('./app-config');
21
+ const env = appConfig.env;
20
22
 
21
23
  // Default sensitive fields to redact
22
24
  const DEFAULT_SENSITIVE_FIELDS = [
@@ -54,8 +56,8 @@ async function safeBodyCapture(request, span) {
54
56
 
55
57
  try {
56
58
  const contentType = request.headers.get('content-type') || '';
57
- const maxBodySize = Math.max(1024, parseInt(process.env.SECURENOW_MAX_BODY_SIZE, 10) || 10240);
58
- const customSensitiveFields = (process.env.SECURENOW_SENSITIVE_FIELDS || '').split(',').map(s => s.trim()).filter(Boolean);
59
+ const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
60
+ const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
59
61
  const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
60
62
 
61
63
  // Only for supported types
@@ -125,8 +127,8 @@ async function safeBodyCapture(request, span) {
125
127
  * Check if body capture is enabled
126
128
  */
127
129
  function isBodyCaptureEnabled() {
128
- // Opt-out default: set SECURENOW_CAPTURE_BODY=0 to disable.
129
- return !/^(0|false)$/i.test(String(process.env.SECURENOW_CAPTURE_BODY ?? ''));
130
+ // Opt-out default: set config.capture.body=false to disable.
131
+ return !/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_BODY') ?? ''));
130
132
  }
131
133
 
132
134
  /**
@@ -183,7 +185,7 @@ if (isBodyCaptureEnabled()) {
183
185
  console.warn('[securenow] 💡 Body capture disabled. Use manual approach if needed.');
184
186
  }
185
187
  } else {
186
- console.log('[securenow] 📝 Automatic body capture: DISABLED (SECURENOW_CAPTURE_BODY=0)');
188
+ console.log('[securenow] Automatic body capture: DISABLED (config.capture.body=false)');
187
189
  }
188
190
 
189
191
  module.exports = {
@@ -194,5 +196,3 @@ module.exports = {
194
196
  };
195
197
 
196
198
 
197
-
198
-
@@ -16,6 +16,8 @@
16
16
  */
17
17
 
18
18
  const { trace, context, SpanStatusCode } = require('@opentelemetry/api');
19
+ const appConfig = require('./app-config');
20
+ const env = appConfig.env;
19
21
 
20
22
  // Default sensitive fields to redact
21
23
  const DEFAULT_SENSITIVE_FIELDS = [
@@ -99,8 +101,8 @@ async function middleware(request) {
99
101
 
100
102
  try {
101
103
  const contentType = request.headers.get('content-type') || '';
102
- const maxBodySize = Math.max(1024, parseInt(process.env.SECURENOW_MAX_BODY_SIZE, 10) || 10240);
103
- const customSensitiveFields = (process.env.SECURENOW_SENSITIVE_FIELDS || '').split(',').map(s => s.trim()).filter(Boolean);
104
+ const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
105
+ const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
104
106
  const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
105
107
 
106
108
  // Only capture supported types
@@ -183,4 +185,3 @@ module.exports = {
183
185
 
184
186
 
185
187
 
186
-
package/nextjs-wrapper.js CHANGED
@@ -16,6 +16,8 @@
16
16
  */
17
17
 
18
18
  const { trace } = require('@opentelemetry/api');
19
+ const appConfig = require('./app-config');
20
+ const env = appConfig.env;
19
21
 
20
22
  // Default sensitive fields to redact
21
23
  const DEFAULT_SENSITIVE_FIELDS = [
@@ -49,8 +51,8 @@ function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
49
51
  * Capture body from Request object (clone to avoid consuming)
50
52
  */
51
53
  async function captureRequestBody(request) {
52
- // Opt-out default: set SECURENOW_CAPTURE_BODY=0 to disable.
53
- const captureBody = !/^(0|false)$/i.test(String(process.env.SECURENOW_CAPTURE_BODY ?? ''));
54
+ // Opt-out default: set config.capture.body=false to disable.
55
+ const captureBody = !/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_BODY') ?? ''));
54
56
 
55
57
  if (!captureBody) return;
56
58
  if (!['POST', 'PUT', 'PATCH'].includes(request.method)) return;
@@ -60,8 +62,8 @@ async function captureRequestBody(request) {
60
62
 
61
63
  try {
62
64
  const contentType = request.headers.get('content-type') || '';
63
- const maxBodySize = Math.max(1024, parseInt(process.env.SECURENOW_MAX_BODY_SIZE, 10) || 10240);
64
- const customSensitiveFields = (process.env.SECURENOW_SENSITIVE_FIELDS || '').split(',').map(s => s.trim()).filter(Boolean);
65
+ const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
66
+ const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
65
67
  const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
66
68
 
67
69
  // Only for supported types
@@ -154,5 +156,3 @@ module.exports = {
154
156
  };
155
157
 
156
158
 
157
-
158
-
package/nextjs.d.ts CHANGED
@@ -3,17 +3,23 @@
3
3
  */
4
4
 
5
5
  export interface RegisterOptions {
6
- /**
7
- * Service name for OpenTelemetry traces
8
- * @default process.env.SECURENOW_APPID || process.env.OTEL_SERVICE_NAME
9
- */
10
- serviceName?: string;
11
-
12
- /**
13
- * OTLP endpoint for traces
14
- * @default process.env.SECURENOW_INSTANCE || 'https://freetrial.securenow.ai:4318'
15
- */
16
- endpoint?: string;
6
+ /**
7
+ * Service name for OpenTelemetry traces
8
+ * @default .securenow/credentials.json app.key/app.name
9
+ */
10
+ serviceName?: string;
11
+
12
+ /**
13
+ * OTLP endpoint for traces
14
+ * @default .securenow/credentials.json app.instance, or https://freetrial.securenow.ai:4318
15
+ */
16
+ endpoint?: string;
17
+
18
+ /**
19
+ * Deployment environment sent as OpenTelemetry deployment.environment.
20
+ * @default .securenow/credentials.json config.runtime.deploymentEnvironment
21
+ */
22
+ environment?: string;
17
23
 
18
24
  /**
19
25
  * Don't append UUID to service name
@@ -21,12 +27,12 @@ export interface RegisterOptions {
21
27
  */
22
28
  noUuid?: boolean;
23
29
 
24
- /**
25
- * Enable request body capture (Next.js middleware required)
26
- * @default false
27
- */
28
- captureBody?: boolean;
29
- }
30
+ /**
31
+ * Enable request body capture
32
+ * @default true from .securenow/credentials.json secure defaults
33
+ */
34
+ captureBody?: boolean;
35
+ }
30
36
 
31
37
  /**
32
38
  * Register SecureNow OpenTelemetry instrumentation for Next.js
@@ -34,14 +40,19 @@ export interface RegisterOptions {
34
40
  * @param options - Optional configuration options
35
41
  *
36
42
  * @example
37
- * ```typescript
38
- * // instrumentation.ts
39
- * import { registerSecureNow } from 'securenow/nextjs';
40
- *
41
- * export function register() {
42
- * registerSecureNow();
43
- * }
44
- * ```
43
+ * ```typescript
44
+ * // instrumentation.ts
45
+ * import { createRequire } from 'node:module';
46
+ *
47
+ * const require = createRequire(import.meta.url);
48
+ *
49
+ * export async function register() {
50
+ * if (process.env.NEXT_RUNTIME !== 'nodejs') return;
51
+ * const { registerSecureNow } = require('securenow/nextjs');
52
+ * registerSecureNow({ captureBody: true });
53
+ * require('securenow/nextjs-auto-capture');
54
+ * }
55
+ * ```
45
56
  *
46
57
  * @example
47
58
  * ```typescript