securenow 7.6.6 → 7.6.8

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 (68) hide show
  1. package/NPM_README.md +13 -13
  2. package/README.md +21 -37
  3. package/app-config.js +5 -3
  4. package/cli/config.js +4 -3
  5. package/cli/diagnostics.js +54 -15
  6. package/cli/run.js +40 -11
  7. package/firewall-only.js +1 -1
  8. package/firewall.js +88 -57
  9. package/mcp/catalog.js +1 -1
  10. package/nextjs-webpack-config.js +3 -15
  11. package/nextjs.js +21 -23
  12. package/nuxt-server-plugin.mjs +20 -10
  13. package/package.json +33 -34
  14. package/register.js +1 -1
  15. package/tracing.js +17 -7
  16. package/web-vite.mjs +23 -13
  17. package/CONSUMING-APPS-GUIDE.md +0 -463
  18. package/docs/ALL-FRAMEWORKS-QUICKSTART.md +0 -1388
  19. package/docs/API-KEYS-GUIDE.md +0 -278
  20. package/docs/ARCHITECTURE.md +0 -408
  21. package/docs/AUTO-BODY-CAPTURE.md +0 -412
  22. package/docs/AUTO-SETUP-SUMMARY.md +0 -331
  23. package/docs/AUTO-SETUP.md +0 -419
  24. package/docs/AUTOMATIC-IP-CAPTURE.md +0 -359
  25. package/docs/BODY-CAPTURE-FIX.md +0 -261
  26. package/docs/BODY-CAPTURE-QUICKSTART.md +0 -147
  27. package/docs/CHANGELOG-NEXTJS.md +0 -235
  28. package/docs/COMPLETION-REPORT.md +0 -408
  29. package/docs/CUSTOMER-GUIDE.md +0 -364
  30. package/docs/EASIEST-SETUP.md +0 -342
  31. package/docs/ENVIRONMENT-VARIABLES.md +0 -166
  32. package/docs/ENVIRONMENTS.md +0 -60
  33. package/docs/EXPRESS-BODY-CAPTURE.md +0 -1028
  34. package/docs/EXPRESS-SETUP-GUIDE.md +0 -722
  35. package/docs/FINAL-SOLUTION.md +0 -335
  36. package/docs/FIREWALL-GUIDE.md +0 -440
  37. package/docs/IMPLEMENTATION-SUMMARY.md +0 -410
  38. package/docs/INDEX.md +0 -222
  39. package/docs/LOGGING-GUIDE.md +0 -704
  40. package/docs/LOGGING-QUICKSTART.md +0 -221
  41. package/docs/MCP-GUIDE.md +0 -58
  42. package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +0 -323
  43. package/docs/NEXTJS-BODY-CAPTURE.md +0 -368
  44. package/docs/NEXTJS-GUIDE.md +0 -392
  45. package/docs/NEXTJS-QUICKSTART.md +0 -83
  46. package/docs/NEXTJS-SETUP-COMPLETE.md +0 -795
  47. package/docs/NEXTJS-WEBPACK-WARNINGS.md +0 -267
  48. package/docs/NEXTJS-WRAPPER-APPROACH.md +0 -414
  49. package/docs/NUXT-GUIDE.md +0 -173
  50. package/docs/QUICKSTART-BODY-CAPTURE.md +0 -293
  51. package/docs/REDACTION-EXAMPLES.md +0 -484
  52. package/docs/REQUEST-BODY-CAPTURE.md +0 -587
  53. package/docs/SOLUTION-SUMMARY.md +0 -312
  54. package/docs/VERCEL-OTEL-MIGRATION.md +0 -255
  55. package/examples/README.md +0 -265
  56. package/examples/express-with-logging.js +0 -137
  57. package/examples/instrumentation-with-auto-capture.ts +0 -41
  58. package/examples/next.config.js +0 -37
  59. package/examples/nextjs-api-route-with-body-capture.ts +0 -54
  60. package/examples/nextjs-env-example.txt +0 -32
  61. package/examples/nextjs-instrumentation.js +0 -36
  62. package/examples/nextjs-instrumentation.ts +0 -36
  63. package/examples/nextjs-middleware.js +0 -37
  64. package/examples/nextjs-middleware.ts +0 -37
  65. package/examples/nextjs-with-logging-example.md +0 -301
  66. package/examples/nextjs-with-options.ts +0 -36
  67. package/examples/test-nextjs-setup.js +0 -70
  68. package/postinstall.js +0 -296
package/firewall.js CHANGED
@@ -45,12 +45,15 @@ let _circuitOpenedAt = 0;
45
45
  let _pollInflight = false;
46
46
  let _retryAfterUntil = 0;
47
47
 
48
- // Keep-alive agents reuse TCP connections across polls (TLS handshake once)
49
- const _httpAgent = new http.Agent({ keepAlive: true, maxSockets: 2, keepAliveMsecs: 30_000 });
50
- const _httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 2, keepAliveMsecs: 30_000 });
48
+ // Firewall control-plane traffic is low-frequency and correctness-critical.
49
+ // Use non-keep-alive agents so clustered apps do not reuse sockets that an
50
+ // upstream load balancer has silently closed between sync cycles.
51
+ const _httpAgent = new http.Agent({ keepAlive: false });
52
+ const _httpsAgent = new https.Agent({ keepAlive: false });
51
53
  const EVENT_FLUSH_INTERVAL_MS = 2_000;
52
54
  const EVENT_BATCH_SIZE = 25;
53
55
  const EVENT_QUEUE_MAX = 1_000;
56
+ const TRANSIENT_NETWORK_CODES = new Set(['ECONNRESET', 'EPIPE', 'ETIMEDOUT', 'EAI_AGAIN']);
54
57
 
55
58
  // Unified sync uses /firewall/sync (v2). Falls back to legacy on 404.
56
59
  let _useUnifiedSync = true;
@@ -119,67 +122,95 @@ function jitter(baseMs) {
119
122
  return baseMs * (0.8 + Math.random() * 0.4);
120
123
  }
121
124
 
122
- function agentFor(url) {
123
- return url.startsWith('https') ? _httpsAgent : _httpAgent;
124
- }
125
-
126
- function httpGet(url, extraHeaders, timeout, callback) {
127
- const mod = url.startsWith('https') ? https : http;
128
- const parsed = new URL(url);
129
-
130
- const req = mod.request({
131
- hostname: parsed.hostname,
132
- port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
133
- path: parsed.pathname + parsed.search,
134
- method: 'GET',
135
- headers: {
136
- 'Authorization': `Bearer ${_options.apiKey}`,
137
- 'User-Agent': 'securenow-firewall-sdk',
138
- ...extraHeaders,
139
- },
140
- timeout,
141
- agent: agentFor(url),
142
- }, (res) => {
143
- let data = '';
144
- res.on('data', (chunk) => { data += chunk; });
145
- res.on('end', () => { callback(null, res, data); });
146
- });
147
-
148
- req.on('error', (err) => callback(err));
149
- req.on('timeout', () => { req.destroy(); callback(new Error('Request timed out')); });
150
- req.end();
125
+ function agentFor(url) {
126
+ return url.startsWith('https') ? _httpsAgent : _httpAgent;
151
127
  }
152
128
 
153
- function httpPostJson(url, body, timeout, callback) {
129
+ function isTransientNetworkError(err) {
130
+ if (!err) return false;
131
+ if (err.code && TRANSIENT_NETWORK_CODES.has(err.code)) return true;
132
+ return /socket hang up|connection reset|ECONNRESET/i.test(String(err.message || ''));
133
+ }
134
+
135
+ function formatRequestError(err) {
136
+ if (!err) return 'unknown error';
137
+ const parts = [err.message || String(err)];
138
+ if (err.code && !parts[0].includes(err.code)) parts.push(`code=${err.code}`);
139
+ if (err.syscall) parts.push(`syscall=${err.syscall}`);
140
+ return parts.join(' ');
141
+ }
142
+
143
+ function requestOnce(method, url, body, extraHeaders, timeout, callback) {
154
144
  const mod = url.startsWith('https') ? https : http;
155
145
  const parsed = new URL(url);
156
- const payload = JSON.stringify(body || {});
146
+ const payload = body == null ? null : JSON.stringify(body || {});
157
147
 
158
148
  const req = mod.request({
159
149
  hostname: parsed.hostname,
160
150
  port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
161
151
  path: parsed.pathname + parsed.search,
162
- method: 'POST',
152
+ method,
163
153
  headers: {
164
154
  'Authorization': `Bearer ${_options.apiKey}`,
165
155
  'User-Agent': 'securenow-firewall-sdk',
166
- 'Content-Type': 'application/json',
167
- 'Content-Length': Buffer.byteLength(payload),
156
+ 'Connection': 'close',
157
+ ...(payload
158
+ ? {
159
+ 'Content-Type': 'application/json',
160
+ 'Content-Length': Buffer.byteLength(payload),
161
+ }
162
+ : {}),
163
+ ...extraHeaders,
168
164
  },
169
165
  timeout,
170
166
  agent: agentFor(url),
171
167
  }, (res) => {
172
168
  let data = '';
173
169
  res.on('data', (chunk) => { data += chunk; });
174
- res.on('end', () => { callback(null, res, data); });
170
+ res.on('end', () => { done(null, res, data); });
175
171
  });
176
172
 
177
- req.on('error', (err) => callback(err));
178
- req.on('timeout', () => { req.destroy(); callback(new Error('Request timed out')); });
179
- req.write(payload);
173
+ let finished = false;
174
+ function done(...args) {
175
+ if (finished) return;
176
+ finished = true;
177
+ callback(...args);
178
+ }
179
+
180
+ req.on('error', (err) => done(err));
181
+ req.on('timeout', () => {
182
+ const err = new Error('Request timed out');
183
+ err.code = 'ETIMEDOUT';
184
+ done(err);
185
+ req.destroy(err);
186
+ });
187
+ if (payload) req.write(payload);
180
188
  req.end();
181
189
  }
182
190
 
191
+ function requestWithRetry(method, url, body, extraHeaders, timeout, callback) {
192
+ requestOnce(method, url, body, extraHeaders, timeout, (err, res, data) => {
193
+ if (!err || !isTransientNetworkError(err)) return callback(err, res, data);
194
+
195
+ if (_options && _options.log) {
196
+ console.warn('[securenow] Firewall: transient API socket error, retrying once:', formatRequestError(err));
197
+ }
198
+
199
+ const retryTimer = setTimeout(() => {
200
+ requestOnce(method, url, body, extraHeaders, timeout, callback);
201
+ }, 100);
202
+ if (retryTimer.unref) retryTimer.unref();
203
+ });
204
+ }
205
+
206
+ function httpGet(url, extraHeaders, timeout, callback) {
207
+ requestWithRetry('GET', url, null, extraHeaders, timeout, callback);
208
+ }
209
+
210
+ function httpPostJson(url, body, timeout, callback) {
211
+ requestWithRetry('POST', url, body, {}, timeout, callback);
212
+ }
213
+
183
214
  function scheduleEventFlush() {
184
215
  if (_eventTimer || _eventQueue.length === 0) return;
185
216
  _eventTimer = setTimeout(() => {
@@ -202,7 +233,7 @@ function flushFirewallEvents() {
202
233
  httpPostJson(url, { events: batch }, 5000, (err, res) => {
203
234
  if (err || !res || res.statusCode >= 400) {
204
235
  if (_options.log) {
205
- const msg = err ? err.message : `API returned ${res.statusCode}`;
236
+ const msg = err ? formatRequestError(err) : `API returned ${res.statusCode}`;
206
237
  console.warn('[securenow] Firewall: failed to report blocked-request ledger:', msg);
207
238
  }
208
239
  }
@@ -448,19 +479,19 @@ function doLegacyPoll(callback) {
448
479
  callback(null, { blChanged, alChanged });
449
480
  }
450
481
 
451
- if (blChanged) {
452
- legacyBlocklistSync((err, changed, stats) => {
453
- if (err && _options.log) console.warn('[securenow] Firewall: sync failed (using stale list):', err.message);
454
- else if (changed && stats && _options.log) console.log('[securenow] Firewall: re-synced %d blocked IPs', stats.total);
455
- syncDone();
456
- });
457
- }
458
- if (alChanged) {
459
- legacyAllowlistSync((err, changed, stats) => {
460
- if (err && _options.log) console.warn('[securenow] Firewall: allowlist sync failed:', err.message);
461
- else if (changed && stats && _options.log) console.log('[securenow] Firewall: re-synced %d allowed IPs', stats.total);
462
- syncDone();
463
- });
482
+ if (blChanged) {
483
+ legacyBlocklistSync((err, changed, stats) => {
484
+ if (err && _options.log) console.warn('[securenow] Firewall: sync failed (using stale list):', formatRequestError(err));
485
+ else if (changed && stats && _options.log) console.log('[securenow] Firewall: re-synced %d blocked IPs', stats.total);
486
+ syncDone();
487
+ });
488
+ }
489
+ if (alChanged) {
490
+ legacyAllowlistSync((err, changed, stats) => {
491
+ if (err && _options.log) console.warn('[securenow] Firewall: allowlist sync failed:', formatRequestError(err));
492
+ else if (changed && stats && _options.log) console.log('[securenow] Firewall: re-synced %d allowed IPs', stats.total);
493
+ syncDone();
494
+ });
464
495
  }
465
496
  }
466
497
 
@@ -495,7 +526,7 @@ function pollOnce(callback) {
495
526
  _consecutiveErrors++;
496
527
  _stats.errors++;
497
528
  maybeOpenCircuit();
498
- if (_options.log) console.warn('[securenow] Firewall: poll failed:', err.message);
529
+ if (_options.log) console.warn('[securenow] Firewall: poll failed:', formatRequestError(err));
499
530
  return callback(err);
500
531
  }
501
532
  _consecutiveErrors = 0;
@@ -565,7 +596,7 @@ function startSyncLoop() {
565
596
  if (retryTimer.unref) retryTimer.unref();
566
597
  return;
567
598
  }
568
- if (_options.log) console.warn('[securenow] Firewall: initial sync failed:', err.message);
599
+ if (_options.log) console.warn('[securenow] Firewall: initial sync failed:', formatRequestError(err));
569
600
  if (_options.failMode === 'closed') {
570
601
  _matcher = { isBlocked: () => true, stats: () => ({ exact: 0, cidr: 0, total: 0 }) };
571
602
  }
package/mcp/catalog.js CHANGED
@@ -32,7 +32,7 @@ Runbook:
32
32
  node -p "require('./node_modules/securenow/package.json').version"
33
33
  npx securenow version
34
34
  Stop and fix the install if either is below 7.5.1 or npx still resolves an older local package.
35
- 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.
35
+ 3. Read the installed package surface before editing files: node_modules/securenow/package.json, README/NPM_README, SKILL-API, SKILL-CLI, npx securenow help, and relevant subcommand help for login/init/firewall/doctor/env/test-span/log/mcp.
36
36
  4. Mandatory auth gate:
37
37
  - Run npx securenow whoami from the project root.
38
38
  - If not logged in, run npx securenow login from the project root and wait for the browser flow.
@@ -16,21 +16,9 @@
16
16
  * module.exports = { webpack: (config, opts) => getSecureNowWebpackConfig(config, opts) };
17
17
  */
18
18
 
19
- const EXTERNAL_PACKAGES = [
20
- 'securenow',
21
- '@opentelemetry/sdk-node',
22
- '@opentelemetry/auto-instrumentations-node',
23
- '@opentelemetry/instrumentation-http',
24
- '@opentelemetry/exporter-trace-otlp-http',
25
- '@opentelemetry/exporter-logs-otlp-http',
26
- '@opentelemetry/sdk-logs',
27
- '@opentelemetry/instrumentation',
28
- '@opentelemetry/resources',
29
- '@opentelemetry/semantic-conventions',
30
- '@opentelemetry/api',
31
- '@opentelemetry/api-logs',
32
- '@vercel/otel',
33
- ];
19
+ const EXTERNAL_PACKAGES = [
20
+ 'securenow',
21
+ ];
34
22
 
35
23
  function detectNextMajor() {
36
24
  try {
package/nextjs.js CHANGED
@@ -5,31 +5,19 @@
5
5
  *
6
6
  * Usage in Next.js app:
7
7
  *
8
- * 1. Add serverExternalPackages to next.config.js (REQUIRED to avoid webpack bundling issues):
8
+ * 1. Add securenow to serverExternalPackages in next.config.js:
9
9
  *
10
10
  * const nextConfig = {
11
- * serverExternalPackages: [
12
- * "securenow",
13
- * "@opentelemetry/sdk-node",
14
- * "@opentelemetry/auto-instrumentations-node",
15
- * "@opentelemetry/instrumentation-http",
16
- * "@opentelemetry/exporter-trace-otlp-http",
17
- * "@opentelemetry/exporter-logs-otlp-http",
18
- * "@opentelemetry/sdk-logs",
19
- * "@opentelemetry/instrumentation",
20
- * "@opentelemetry/resources",
21
- * "@opentelemetry/semantic-conventions",
22
- * "@opentelemetry/api",
23
- * "@opentelemetry/api-logs",
24
- * "@vercel/otel",
25
- * ],
11
+ * serverExternalPackages: ["securenow"],
26
12
  * };
27
13
  *
28
14
  * 2. Create instrumentation.ts (or .js) in your project root:
29
15
  *
30
- * import { registerSecureNow } from 'securenow/nextjs';
31
- * export function register() {
32
- * registerSecureNow();
16
+ * export async function register() {
17
+ * if (process.env.NEXT_RUNTIME !== "nodejs") return;
18
+ * const securenowNext = await import("securenow/nextjs");
19
+ * securenowNext.registerSecureNow({ captureBody: true });
20
+ * await import("securenow/nextjs-auto-capture");
33
21
  * }
34
22
  *
35
23
  * 3. Run `npx securenow login` and `npx securenow init`.
@@ -39,11 +27,22 @@
39
27
 
40
28
  const { randomUUID } = require('crypto');
41
29
  const appConfig = require('./app-config');
30
+ const otelResources = require('@opentelemetry/resources');
42
31
 
43
32
  const env = appConfig.env;
44
33
 
45
34
  let isRegistered = false;
46
35
 
36
+ function createResource(attributes) {
37
+ if (typeof otelResources.resourceFromAttributes === 'function') {
38
+ return otelResources.resourceFromAttributes(attributes);
39
+ }
40
+ if (typeof otelResources.Resource === 'function') {
41
+ return new otelResources.Resource(attributes);
42
+ }
43
+ throw new Error('Unsupported @opentelemetry/resources version');
44
+ }
45
+
47
46
  // Default sensitive fields to redact from request bodies
48
47
  const DEFAULT_SENSITIVE_FIELDS = [
49
48
  'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
@@ -430,7 +429,6 @@ function registerSecureNow(options = {}) {
430
429
  // -------- Self-Hosted (EC2/PM2): Use Vanilla OpenTelemetry SDK --------
431
430
  const { NodeSDK } = require('@opentelemetry/sdk-node');
432
431
  const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
433
- const { Resource } = require('@opentelemetry/resources');
434
432
  const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
435
433
 
436
434
  const traceExporter = new OTLPTraceExporter({
@@ -442,7 +440,7 @@ function registerSecureNow(options = {}) {
442
440
  serviceName: serviceName,
443
441
  traceExporter: traceExporter,
444
442
  instrumentations: [httpInstrumentation],
445
- resource: new Resource({
443
+ resource: createResource({
446
444
  [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
447
445
  [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: deploymentEnvironment,
448
446
  [SemanticResourceAttributes.SERVICE_VERSION]: process.env.npm_package_version || undefined,
@@ -466,12 +464,12 @@ function registerSecureNow(options = {}) {
466
464
  });
467
465
 
468
466
  const loggerProvider = new LoggerProvider({
469
- resource: new Resource({
467
+ resource: createResource({
470
468
  [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
471
469
  [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: deploymentEnvironment,
472
470
  }),
471
+ processors: [new BatchLogRecordProcessor(logExporter)],
473
472
  });
474
- loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
475
473
 
476
474
  // Patch console to forward logs as OTLP log records
477
475
  const logger = loggerProvider.getLogger('console', '1.0.0');
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { NodeSDK } from '@opentelemetry/sdk-node';
11
11
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
12
- import { Resource } from '@opentelemetry/resources';
12
+ import * as otelResources from '@opentelemetry/resources';
13
13
  import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
14
14
  import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
15
15
  import {
@@ -17,8 +17,8 @@ import {
17
17
  trace as otelTrace,
18
18
  SpanStatusCode,
19
19
  } from '@opentelemetry/api';
20
- import { v4 as uuidv4 } from 'uuid';
21
20
  import { createRequire } from 'node:module';
21
+ import { randomUUID } from 'node:crypto';
22
22
 
23
23
  const nodeRequire = createRequire(import.meta.url);
24
24
  const appConfig = nodeRequire('./app-config');
@@ -27,6 +27,16 @@ const appConfig = nodeRequire('./app-config');
27
27
 
28
28
  const env = appConfig.env;
29
29
 
30
+ function createResource(attributes) {
31
+ if (typeof otelResources.resourceFromAttributes === 'function') {
32
+ return otelResources.resourceFromAttributes(attributes);
33
+ }
34
+ if (typeof otelResources.Resource === 'function') {
35
+ return new otelResources.Resource(attributes);
36
+ }
37
+ throw new Error('Unsupported @opentelemetry/resources version');
38
+ }
39
+
30
40
  const DEFAULT_SENSITIVE_FIELDS = [
31
41
  'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
32
42
  'access_token', 'auth', 'credentials', 'mysql_pwd', 'stripeToken',
@@ -79,9 +89,9 @@ export default defineNitroPlugin(async (nitroApp) => {
79
89
 
80
90
  let serviceName;
81
91
  if (baseName) {
82
- serviceName = noUuid ? baseName : `${baseName}-${uuidv4()}`;
92
+ serviceName = noUuid ? baseName : `${baseName}-${randomUUID()}`;
83
93
  } else {
84
- serviceName = `nuxt-app-${uuidv4()}`;
94
+ serviceName = `nuxt-app-${randomUUID()}`;
85
95
  console.warn(
86
96
  '[securenow] ⚠️ No app identity resolved. Using fallback: %s',
87
97
  serviceName,
@@ -91,7 +101,7 @@ export default defineNitroPlugin(async (nitroApp) => {
91
101
  );
92
102
  }
93
103
 
94
- const serviceInstanceId = `${baseName || 'securenow'}-${uuidv4()}`;
104
+ const serviceInstanceId = `${baseName || 'securenow'}-${randomUUID()}`;
95
105
 
96
106
  // ── Endpoints ──
97
107
  const resolvedEndpoints = appConfig.resolveEndpoints({ endpoint: opts.endpoint || resolvedApp.instance });
@@ -101,7 +111,7 @@ export default defineNitroPlugin(async (nitroApp) => {
101
111
  const headers = resolvedEndpoints.headers;
102
112
 
103
113
  // ── Resource ──
104
- const resource = new Resource({
114
+ const resource = createResource({
105
115
  [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
106
116
  [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: serviceInstanceId,
107
117
  [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: deploymentEnvironment,
@@ -237,10 +247,10 @@ export default defineNitroPlugin(async (nitroApp) => {
237
247
  );
238
248
 
239
249
  const logExporter = new OTLPLogExporter({ url: logsUrl, headers });
240
- loggerProvider = new LoggerProvider({ resource });
241
- loggerProvider.addLogRecordProcessor(
242
- new BatchLogRecordProcessor(logExporter),
243
- );
250
+ loggerProvider = new LoggerProvider({
251
+ resource,
252
+ processors: [new BatchLogRecordProcessor(logExporter)],
253
+ });
244
254
 
245
255
  const logger = loggerProvider.getLogger('console', '1.0.0');
246
256
  const SEV = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "7.6.6",
3
+ "version": "7.6.8",
4
4
  "description": "OpenTelemetry instrumentation for Node.js, Next.js, and Nuxt - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",
@@ -9,9 +9,6 @@
9
9
  "securenow": "cli.js",
10
10
  "securenow-mcp": "mcp/server.js"
11
11
  },
12
- "scripts": {
13
- "postinstall": "node postinstall.js || exit 0"
14
- },
15
12
  "keywords": [
16
13
  "opentelemetry",
17
14
  "otel",
@@ -135,47 +132,49 @@
135
132
  "firewall-tcp.js",
136
133
  "firewall-iptables.js",
137
134
  "firewall-cloud.js",
138
- "postinstall.js",
139
135
  "register-vite.js",
140
136
  "web-vite.mjs",
141
137
  "app-config.js",
142
- "examples/",
143
- "docs/",
144
138
  "README.md",
145
139
  "NPM_README.md",
146
- "CONSUMING-APPS-GUIDE.md",
147
140
  "SKILL-CLI.md",
148
141
  "SKILL-API.md"
149
142
  ],
150
143
  "dependencies": {
151
- "@opentelemetry/api": "1.7.0",
152
- "@opentelemetry/api-logs": "0.47.0",
153
- "@opentelemetry/auto-instrumentations-node": "0.47.0",
154
- "@opentelemetry/exporter-logs-otlp-http": "0.47.0",
155
- "@opentelemetry/exporter-trace-otlp-http": "0.47.0",
156
- "@opentelemetry/instrumentation": "0.47.0",
157
- "@opentelemetry/instrumentation-document-load": "0.47.0",
158
- "@opentelemetry/instrumentation-fetch": "0.47.0",
159
- "@opentelemetry/instrumentation-http": "0.47.0",
160
- "@opentelemetry/instrumentation-mongodb": "0.46.0",
161
- "@opentelemetry/instrumentation-user-interaction": "0.47.0",
162
- "@opentelemetry/instrumentation-xml-http-request": "0.47.0",
163
- "@opentelemetry/resources": "1.20.0",
164
- "@opentelemetry/sdk-logs": "0.47.0",
165
- "@opentelemetry/sdk-node": "0.47.0",
166
- "@opentelemetry/sdk-trace-web": "1.20.0",
167
- "@opentelemetry/semantic-conventions": "1.20.0",
168
- "dotenv": "^17.2.1",
169
- "uuid": "^9.0.0"
144
+ "@opentelemetry/api": "1.9.1",
145
+ "@opentelemetry/api-logs": "0.218.0",
146
+ "@opentelemetry/auto-instrumentations-node": "0.76.0",
147
+ "@opentelemetry/core": "2.7.1",
148
+ "@opentelemetry/exporter-logs-otlp-http": "0.218.0",
149
+ "@opentelemetry/exporter-trace-otlp-http": "0.218.0",
150
+ "@opentelemetry/instrumentation": "0.218.0",
151
+ "@opentelemetry/instrumentation-document-load": "0.63.0",
152
+ "@opentelemetry/instrumentation-fetch": "0.218.0",
153
+ "@opentelemetry/instrumentation-http": "0.218.0",
154
+ "@opentelemetry/instrumentation-mongodb": "0.71.0",
155
+ "@opentelemetry/instrumentation-user-interaction": "0.62.0",
156
+ "@opentelemetry/instrumentation-xml-http-request": "0.218.0",
157
+ "@opentelemetry/resources": "2.7.1",
158
+ "@opentelemetry/sdk-logs": "0.218.0",
159
+ "@opentelemetry/sdk-metrics": "2.7.1",
160
+ "@opentelemetry/sdk-node": "0.218.0",
161
+ "@opentelemetry/sdk-trace-base": "2.7.1",
162
+ "@opentelemetry/sdk-trace-web": "2.7.1",
163
+ "@opentelemetry/semantic-conventions": "1.41.1",
164
+ "dotenv": "17.2.1"
170
165
  },
171
166
  "optionalDependencies": {
172
- "@vercel/otel": "1.10.4"
173
- },
174
- "overrides": {
175
- "@opentelemetry/api": "1.7.0",
176
- "@opentelemetry/api-logs": "0.47.0",
177
- "protobufjs": "^7.5.5"
167
+ "@vercel/otel": "2.1.2"
178
168
  },
179
169
  "sideEffects": true,
180
- "license": "ISC"
170
+ "license": "ISC",
171
+ "directories": {
172
+ "doc": "docs",
173
+ "example": "examples"
174
+ },
175
+ "devDependencies": {},
176
+ "scripts": {
177
+ "test": "echo \"Error: no test specified\" && exit 1"
178
+ },
179
+ "author": ""
181
180
  }
package/register.js CHANGED
@@ -9,7 +9,7 @@
9
9
  // 1. Load dotenv quietly only for legacy installs. Normal local and production
10
10
  // configuration comes from .securenow/credentials.json via app-config.js.
11
11
  try {
12
- require('dotenv').config();
12
+ require('dotenv').config({ quiet: true });
13
13
  } catch (e) {
14
14
  // dotenv is optional.
15
15
  }
package/tracing.js CHANGED
@@ -33,15 +33,25 @@ const { NodeSDK } = require('@opentelemetry/sdk-node');
33
33
  const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
34
34
  const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
35
35
  const { LoggerProvider, BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
36
- const { Resource } = require('@opentelemetry/resources');
36
+ const otelResources = require('@opentelemetry/resources');
37
37
  const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
38
38
  const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
39
39
  const { MongoDBInstrumentation } = require('@opentelemetry/instrumentation-mongodb');
40
- const { v4: uuidv4 } = require('uuid');
40
+ const { randomUUID } = require('crypto');
41
41
  const appConfig = require('./app-config');
42
42
 
43
43
  const env = appConfig.env;
44
44
 
45
+ function createResource(attributes) {
46
+ if (typeof otelResources.resourceFromAttributes === 'function') {
47
+ return otelResources.resourceFromAttributes(attributes);
48
+ }
49
+ if (typeof otelResources.Resource === 'function') {
50
+ return new otelResources.Resource(attributes);
51
+ }
52
+ throw new Error('Unsupported @opentelemetry/resources version');
53
+ }
54
+
45
55
  // Default sensitive fields to redact from request bodies
46
56
  const DEFAULT_SENSITIVE_FIELDS = [
47
57
  'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
@@ -297,15 +307,15 @@ if (!baseName && inPm2Cluster && strict) {
297
307
  // service.name
298
308
  let serviceName;
299
309
  if (baseName) {
300
- serviceName = noUuid ? baseName : `${baseName}-${uuidv4()}`;
310
+ serviceName = noUuid ? baseName : `${baseName}-${randomUUID()}`;
301
311
  } else {
302
312
  // last-resort fallback (only if STRlCT is off). You can rename this to make it obvious in monitoring.
303
- serviceName = `securenow-free-${uuidv4()}`;
313
+ serviceName = `securenow-free-${randomUUID()}`;
304
314
  }
305
315
 
306
316
  // service.instance.id = <appid-or-fallback>-<uuid> (unique per worker)
307
317
  const instancePrefix = baseName || 'securenow';
308
- const serviceInstanceId = `${instancePrefix}-${uuidv4()}`;
318
+ const serviceInstanceId = `${instancePrefix}-${randomUUID()}`;
309
319
 
310
320
  // Loud line per worker to prove what was used
311
321
  console.log('[securenow] pid=%d appId=%s instance=%s apiKey=%s → service.name=%s instance.id=%s',
@@ -466,7 +476,7 @@ const httpInstrumentation = new HttpInstrumentation({
466
476
  const loggingEnabled = !/^(0|false)$/i.test(String(env('SECURENOW_LOGGING_ENABLED') ?? ''));
467
477
 
468
478
  // Create shared resource for both traces and logs
469
- const sharedResource = new Resource({
479
+ const sharedResource = createResource({
470
480
  [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
471
481
  [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: serviceInstanceId,
472
482
  [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: appConfig.resolveDeploymentEnvironment(),
@@ -485,8 +495,8 @@ if (loggingEnabled) {
485
495
  const batchLogProcessor = new BatchLogRecordProcessor(logExporter);
486
496
  loggerProvider = new LoggerProvider({
487
497
  resource: sharedResource,
498
+ processors: [batchLogProcessor],
488
499
  });
489
- loggerProvider.addLogRecordProcessor(batchLogProcessor);
490
500
 
491
501
  // Auto-patch console.* so every log/warn/error becomes an OTel log record
492
502
  const _logger = loggerProvider.getLogger('console', '1.0.0');