securenow 5.18.0 → 6.0.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/LICENSE +15 -0
- package/README.md +40 -239
- package/cli.js +455 -415
- package/console-instrumentation.js +136 -147
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +455 -1339
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/AUTO-BODY-CAPTURE.md +1 -1
- package/docs/AUTO-SETUP.md +4 -4
- package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
- package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
- package/docs/CHANGELOG-NEXTJS.md +1 -1
- package/docs/CUSTOMER-GUIDE.md +16 -16
- package/docs/EASIEST-SETUP.md +5 -5
- package/docs/ENVIRONMENT-VARIABLES.md +652 -880
- package/docs/EXPRESS-BODY-CAPTURE.md +12 -13
- package/docs/EXPRESS-SETUP-GUIDE.md +720 -719
- package/docs/INDEX.md +4 -22
- package/docs/LOGGING-GUIDE.md +708 -701
- package/docs/LOGGING-QUICKSTART.md +239 -234
- 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-WRAPPER-APPROACH.md +1 -1
- package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
- package/docs/REDACTION-EXAMPLES.md +1 -1
- package/docs/REQUEST-BODY-CAPTURE.md +10 -19
- 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/nextjs-auto-capture.js +207 -199
- package/nextjs-middleware.js +181 -186
- package/nextjs-webpack-config.js +53 -88
- package/nextjs-wrapper.js +158 -158
- package/nextjs.d.ts +1 -1
- package/nextjs.js +135 -190
- package/package.json +45 -67
- package/postinstall.js +6 -6
- package/register.d.ts +1 -1
- package/register.js +4 -39
- package/tracing.d.ts +1 -2
- package/tracing.js +22 -287
- package/web-vite.mjs +156 -239
- package/CONSUMING-APPS-GUIDE.md +0 -455
- package/NPM_README.md +0 -1933
- package/SKILL-API.md +0 -600
- package/SKILL-CLI.md +0 -409
- package/cidr.js +0 -83
- package/cli/apps.js +0 -585
- package/cli/auth.js +0 -280
- package/cli/client.js +0 -115
- package/cli/config.js +0 -173
- package/cli/firewall.js +0 -100
- package/cli/fp.js +0 -638
- package/cli/init.js +0 -201
- package/cli/monitor.js +0 -440
- package/cli/run.js +0 -133
- package/cli/security.js +0 -1064
- package/cli/ui.js +0 -386
- package/docs/API-KEYS-GUIDE.md +0 -233
- package/docs/AUTO-SETUP-SUMMARY.md +0 -331
- package/docs/BODY-CAPTURE-FIX.md +0 -261
- package/docs/COMPLETION-REPORT.md +0 -408
- package/docs/FINAL-SOLUTION.md +0 -335
- package/docs/FIREWALL-GUIDE.md +0 -426
- package/docs/IMPLEMENTATION-SUMMARY.md +0 -410
- package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +0 -323
- package/docs/NEXTJS-SETUP-COMPLETE.md +0 -795
- package/docs/NUXT-GUIDE.md +0 -166
- package/docs/SOLUTION-SUMMARY.md +0 -312
- package/firewall-cloud.js +0 -212
- package/firewall-iptables.js +0 -139
- package/firewall-only.js +0 -38
- package/firewall-tcp.js +0 -74
- package/firewall.js +0 -720
- package/free-trial-banner.js +0 -174
- package/nuxt-server-plugin.mjs +0 -423
- package/nuxt.d.ts +0 -60
- package/nuxt.mjs +0 -75
- package/resolve-ip.js +0 -77
package/nextjs.js
CHANGED
|
@@ -5,39 +5,21 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Usage in Next.js app:
|
|
7
7
|
*
|
|
8
|
-
* 1.
|
|
9
|
-
*
|
|
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
|
-
* ],
|
|
26
|
-
* };
|
|
27
|
-
*
|
|
28
|
-
* 2. Create instrumentation.ts (or .js) in your project root:
|
|
8
|
+
* 1. Create instrumentation.ts (or .js) in your project root:
|
|
29
9
|
*
|
|
30
10
|
* import { registerSecureNow } from 'securenow/nextjs';
|
|
31
11
|
* export function register() {
|
|
32
12
|
* registerSecureNow();
|
|
33
13
|
* }
|
|
34
14
|
*
|
|
35
|
-
*
|
|
15
|
+
* 2. Set environment variables:
|
|
36
16
|
* SECURENOW_APPID=my-nextjs-app
|
|
37
|
-
* SECURENOW_INSTANCE=http://your-
|
|
17
|
+
* SECURENOW_INSTANCE=http://your-signoz-host:4318
|
|
18
|
+
*
|
|
19
|
+
* That's it! 🎉 No webpack warnings!
|
|
38
20
|
*/
|
|
39
21
|
|
|
40
|
-
const {
|
|
22
|
+
const { v4: uuidv4 } = require('uuid');
|
|
41
23
|
|
|
42
24
|
const env = k => process.env[k] ?? process.env[k.toUpperCase()] ?? process.env[k.toLowerCase()];
|
|
43
25
|
|
|
@@ -68,9 +50,10 @@ function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
|
|
|
68
50
|
|
|
69
51
|
const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
|
|
70
52
|
|
|
71
|
-
for (const key
|
|
53
|
+
for (const key in redacted) {
|
|
72
54
|
const lowerKey = key.toLowerCase();
|
|
73
55
|
|
|
56
|
+
// Check if field is sensitive
|
|
74
57
|
if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
|
|
75
58
|
redacted[key] = '[REDACTED]';
|
|
76
59
|
} else if (typeof redacted[key] === 'object' && redacted[key] !== null) {
|
|
@@ -82,10 +65,6 @@ function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
|
|
|
82
65
|
return redacted;
|
|
83
66
|
}
|
|
84
67
|
|
|
85
|
-
function escapeRegex(str) {
|
|
86
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
68
|
/**
|
|
90
69
|
* Redact sensitive data from GraphQL query strings
|
|
91
70
|
*/
|
|
@@ -97,10 +76,10 @@ function redactGraphQLQuery(query, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
|
|
|
97
76
|
// Redact sensitive fields in GraphQL arguments and variables
|
|
98
77
|
// Matches patterns like: password: "value" or password:"value" or password:'value'
|
|
99
78
|
sensitiveFields.forEach(field => {
|
|
100
|
-
|
|
79
|
+
// Match field: "value" or field: 'value' or field:"value" (with optional spaces)
|
|
101
80
|
const patterns = [
|
|
102
|
-
new RegExp(`(${
|
|
103
|
-
new RegExp(`(${
|
|
81
|
+
new RegExp(`(${field}\\s*:\\s*["'])([^"']+)(["'])`, 'gi'),
|
|
82
|
+
new RegExp(`(${field}\\s*:\\s*)([^\\s,})\n]+)`, 'gi'),
|
|
104
83
|
];
|
|
105
84
|
|
|
106
85
|
patterns.forEach(pattern => {
|
|
@@ -117,6 +96,115 @@ function redactGraphQLQuery(query, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
|
|
|
117
96
|
return redacted;
|
|
118
97
|
}
|
|
119
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Parse and capture request body safely
|
|
101
|
+
*/
|
|
102
|
+
async function captureRequestBody(request, maxSize = 10240) {
|
|
103
|
+
try {
|
|
104
|
+
const contentType = request.headers['content-type'] || '';
|
|
105
|
+
let body = '';
|
|
106
|
+
|
|
107
|
+
// Collect body chunks
|
|
108
|
+
const chunks = [];
|
|
109
|
+
let size = 0;
|
|
110
|
+
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
request.on('data', (chunk) => {
|
|
113
|
+
size += chunk.length;
|
|
114
|
+
if (size <= maxSize) {
|
|
115
|
+
chunks.push(chunk);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
request.on('end', () => {
|
|
120
|
+
if (size > maxSize) {
|
|
121
|
+
resolve({
|
|
122
|
+
captured: false,
|
|
123
|
+
reason: `Body too large (${size} bytes > ${maxSize} bytes)`,
|
|
124
|
+
size
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
body = Buffer.concat(chunks).toString('utf8');
|
|
130
|
+
|
|
131
|
+
// Parse based on content type
|
|
132
|
+
if (contentType.includes('application/json')) {
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(body);
|
|
135
|
+
resolve({
|
|
136
|
+
captured: true,
|
|
137
|
+
type: 'json',
|
|
138
|
+
body: parsed,
|
|
139
|
+
size
|
|
140
|
+
});
|
|
141
|
+
} catch (e) {
|
|
142
|
+
resolve({
|
|
143
|
+
captured: true,
|
|
144
|
+
type: 'json',
|
|
145
|
+
body: body.substring(0, 1000),
|
|
146
|
+
parseError: true,
|
|
147
|
+
size
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
} else if (contentType.includes('application/graphql')) {
|
|
151
|
+
// GraphQL queries need redaction too!
|
|
152
|
+
resolve({
|
|
153
|
+
captured: true,
|
|
154
|
+
type: 'graphql',
|
|
155
|
+
body: body, // Will be redacted later
|
|
156
|
+
size
|
|
157
|
+
});
|
|
158
|
+
} else if (contentType.includes('multipart/form-data')) {
|
|
159
|
+
// Multipart is NOT captured (files can be huge)
|
|
160
|
+
resolve({
|
|
161
|
+
captured: false,
|
|
162
|
+
type: 'multipart',
|
|
163
|
+
reason: 'Multipart data not captured (file uploads)',
|
|
164
|
+
size
|
|
165
|
+
});
|
|
166
|
+
} else if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
167
|
+
try {
|
|
168
|
+
const params = new URLSearchParams(body);
|
|
169
|
+
const parsed = Object.fromEntries(params);
|
|
170
|
+
resolve({
|
|
171
|
+
captured: true,
|
|
172
|
+
type: 'form',
|
|
173
|
+
body: parsed,
|
|
174
|
+
size
|
|
175
|
+
});
|
|
176
|
+
} catch (e) {
|
|
177
|
+
resolve({
|
|
178
|
+
captured: true,
|
|
179
|
+
type: 'form',
|
|
180
|
+
body: body.substring(0, 1000),
|
|
181
|
+
size
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
resolve({
|
|
186
|
+
captured: true,
|
|
187
|
+
type: 'text',
|
|
188
|
+
body: body.substring(0, 1000),
|
|
189
|
+
size
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
request.on('error', () => {
|
|
195
|
+
resolve({ captured: false, reason: 'Stream error' });
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Timeout after 100ms
|
|
199
|
+
setTimeout(() => {
|
|
200
|
+
resolve({ captured: false, reason: 'Timeout' });
|
|
201
|
+
}, 100);
|
|
202
|
+
});
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return { captured: false, reason: error.message };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
120
208
|
/**
|
|
121
209
|
* Register SecureNow OpenTelemetry for Next.js using @vercel/otel
|
|
122
210
|
* @param {Object} options - Optional configuration
|
|
@@ -157,9 +245,9 @@ function registerSecureNow(options = {}) {
|
|
|
157
245
|
// service.name
|
|
158
246
|
let serviceName;
|
|
159
247
|
if (baseName) {
|
|
160
|
-
serviceName = noUuid ? baseName : `${baseName}-${
|
|
248
|
+
serviceName = noUuid ? baseName : `${baseName}-${uuidv4()}`;
|
|
161
249
|
} else {
|
|
162
|
-
serviceName = `nextjs-app-${
|
|
250
|
+
serviceName = `nextjs-app-${uuidv4()}`;
|
|
163
251
|
console.warn('[securenow] ⚠️ No SECURENOW_APPID or OTEL_SERVICE_NAME provided. Using fallback: %s', serviceName);
|
|
164
252
|
console.warn('[securenow] 💡 Set SECURENOW_APPID=your-app-name in .env.local for better tracking');
|
|
165
253
|
}
|
|
@@ -173,11 +261,11 @@ function registerSecureNow(options = {}) {
|
|
|
173
261
|
).replace(/\/$/, '');
|
|
174
262
|
|
|
175
263
|
const tracesUrl = env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
|
|
176
|
-
const logsUrl = env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`;
|
|
177
264
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
265
|
+
// Set environment variables for @vercel/otel to pick up
|
|
266
|
+
process.env.OTEL_SERVICE_NAME = serviceName;
|
|
267
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = endpointBase;
|
|
268
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = tracesUrl;
|
|
181
269
|
|
|
182
270
|
console.log('[securenow] 🚀 Next.js App → service.name=%s', serviceName);
|
|
183
271
|
|
|
@@ -185,7 +273,7 @@ function registerSecureNow(options = {}) {
|
|
|
185
273
|
const captureBody = String(env('SECURENOW_CAPTURE_BODY')) === '1' ||
|
|
186
274
|
String(env('SECURENOW_CAPTURE_BODY')).toLowerCase() === 'true' ||
|
|
187
275
|
options.captureBody === true;
|
|
188
|
-
const maxBodySize =
|
|
276
|
+
const maxBodySize = parseInt(env('SECURENOW_MAX_BODY_SIZE') || '10240'); // 10KB default
|
|
189
277
|
const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
190
278
|
const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
|
|
191
279
|
|
|
@@ -219,19 +307,14 @@ function registerSecureNow(options = {}) {
|
|
|
219
307
|
const clientIp = headers['x-client-ip'];
|
|
220
308
|
const socketIp = request.socket?.remoteAddress;
|
|
221
309
|
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
primaryIp = realIp || cfConnectingIp || clientIp || primaryIp;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
310
|
+
// Primary IP (first in chain is the real client)
|
|
311
|
+
const primaryIp =
|
|
312
|
+
(forwardedFor ? forwardedFor.split(',')[0]?.trim() : null) ||
|
|
313
|
+
realIp ||
|
|
314
|
+
cfConnectingIp ||
|
|
315
|
+
clientIp ||
|
|
316
|
+
socketIp ||
|
|
317
|
+
'unknown';
|
|
235
318
|
|
|
236
319
|
// ======== PROTOCOL & CONNECTION ========
|
|
237
320
|
const scheme = headers['x-forwarded-proto'] ||
|
|
@@ -421,126 +504,9 @@ function registerSecureNow(options = {}) {
|
|
|
421
504
|
|
|
422
505
|
sdk.start();
|
|
423
506
|
console.log('[securenow] 🎯 Vanilla SDK initialized for self-hosted environment');
|
|
424
|
-
|
|
425
|
-
// -------- Logging (self-hosted only) --------
|
|
426
|
-
const loggingEnabled = String(env('SECURENOW_LOGGING_ENABLED')) === '1' || String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() === 'true';
|
|
427
|
-
if (loggingEnabled) {
|
|
428
|
-
try {
|
|
429
|
-
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
|
|
430
|
-
const { LoggerProvider, BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
|
|
431
|
-
|
|
432
|
-
const logExporter = new OTLPLogExporter({
|
|
433
|
-
url: logsUrl,
|
|
434
|
-
headers: parseHeaders(env('OTEL_EXPORTER_OTLP_HEADERS')),
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
const loggerProvider = new LoggerProvider({
|
|
438
|
-
resource: new Resource({
|
|
439
|
-
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
440
|
-
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: env('NODE_ENV') || 'production',
|
|
441
|
-
}),
|
|
442
|
-
});
|
|
443
|
-
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
444
|
-
|
|
445
|
-
// Patch console to forward logs as OTLP log records
|
|
446
|
-
const logger = loggerProvider.getLogger('console', '1.0.0');
|
|
447
|
-
const SeverityNumber = { INFO: 9, WARN: 13, ERROR: 17 };
|
|
448
|
-
const origLog = console.log;
|
|
449
|
-
const origWarn = console.warn;
|
|
450
|
-
const origError = console.error;
|
|
451
|
-
|
|
452
|
-
const { context: otelContext, trace: otelTrace } = require('@opentelemetry/api');
|
|
453
|
-
function _emitLog(sn, st, args) {
|
|
454
|
-
try {
|
|
455
|
-
const activeCtx = otelContext.active();
|
|
456
|
-
const spanCtx = otelTrace.getSpanContext(activeCtx);
|
|
457
|
-
logger.emit({
|
|
458
|
-
severityNumber: sn,
|
|
459
|
-
severityText: st,
|
|
460
|
-
body: args.map(String).join(' '),
|
|
461
|
-
...(spanCtx && { context: activeCtx }),
|
|
462
|
-
});
|
|
463
|
-
} catch (_) {}
|
|
464
|
-
}
|
|
465
|
-
console.log = (...args) => {
|
|
466
|
-
origLog.apply(console, args);
|
|
467
|
-
_emitLog(SeverityNumber.INFO, 'INFO', args);
|
|
468
|
-
};
|
|
469
|
-
console.warn = (...args) => {
|
|
470
|
-
origWarn.apply(console, args);
|
|
471
|
-
_emitLog(SeverityNumber.WARN, 'WARN', args);
|
|
472
|
-
};
|
|
473
|
-
console.error = (...args) => {
|
|
474
|
-
origError.apply(console, args);
|
|
475
|
-
_emitLog(SeverityNumber.ERROR, 'ERROR', args);
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
|
|
479
|
-
|
|
480
|
-
// Auto-log every incoming HTTP request/response
|
|
481
|
-
try {
|
|
482
|
-
const http = require('http');
|
|
483
|
-
const originalEmit = http.Server.prototype.emit;
|
|
484
|
-
http.Server.prototype.emit = function (event, req, res) {
|
|
485
|
-
if (event === 'request' && req && res) {
|
|
486
|
-
const start = Date.now();
|
|
487
|
-
const method = req.method;
|
|
488
|
-
const url = req.url;
|
|
489
|
-
res.on('finish', () => {
|
|
490
|
-
const reqCtx = otelContext.active();
|
|
491
|
-
const reqSpanCtx = otelTrace.getSpanContext(reqCtx);
|
|
492
|
-
const duration = Date.now() - start;
|
|
493
|
-
const status = res.statusCode;
|
|
494
|
-
const ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.socket?.remoteAddress || '-';
|
|
495
|
-
const ua = req.headers['user-agent'] || '-';
|
|
496
|
-
const body = `${method} ${url} ${status} ${duration}ms ip=${ip} ua=${ua}`;
|
|
497
|
-
const severity = status >= 500 ? SeverityNumber.ERROR : status >= 400 ? SeverityNumber.WARN : SeverityNumber.INFO;
|
|
498
|
-
const severityText = status >= 500 ? 'ERROR' : status >= 400 ? 'WARN' : 'INFO';
|
|
499
|
-
origLog.call(console, '[securenow] %s %s %d %dms', method, url, status, duration);
|
|
500
|
-
try {
|
|
501
|
-
logger.emit({
|
|
502
|
-
severityNumber: severity,
|
|
503
|
-
severityText,
|
|
504
|
-
body,
|
|
505
|
-
attributes: {
|
|
506
|
-
'http.method': method,
|
|
507
|
-
'http.url': url,
|
|
508
|
-
'http.status_code': status,
|
|
509
|
-
'http.duration_ms': duration,
|
|
510
|
-
'http.client_ip': String(ip).split(',')[0].trim(),
|
|
511
|
-
'http.user_agent': ua,
|
|
512
|
-
},
|
|
513
|
-
...(reqSpanCtx && { context: reqCtx }),
|
|
514
|
-
});
|
|
515
|
-
} catch (_) {}
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
return originalEmit.apply(this, arguments);
|
|
519
|
-
};
|
|
520
|
-
console.log('[securenow] 📋 HTTP request logging: ENABLED');
|
|
521
|
-
} catch (_) {}
|
|
522
|
-
|
|
523
|
-
// Graceful shutdown for logs
|
|
524
|
-
process.on('SIGTERM', async () => { try { await loggerProvider.shutdown(); } catch (_) {} try { require('./firewall').shutdown(); } catch (_) {} });
|
|
525
|
-
process.on('SIGINT', async () => { try { await loggerProvider.shutdown(); } catch (_) {} try { require('./firewall').shutdown(); } catch (_) {} });
|
|
526
|
-
} catch (e) {
|
|
527
|
-
console.warn('[securenow] ⚠️ Logging setup failed (missing @opentelemetry/exporter-logs-otlp-http or @opentelemetry/sdk-logs):', e.message);
|
|
528
|
-
}
|
|
529
|
-
} else {
|
|
530
|
-
console.log('[securenow] 📋 Logging: DISABLED (set SECURENOW_LOGGING_ENABLED=1 to enable)');
|
|
531
|
-
}
|
|
532
507
|
}
|
|
533
508
|
|
|
534
509
|
isRegistered = true;
|
|
535
|
-
|
|
536
|
-
// Free trial banner (optional — may not be bundled in standalone builds)
|
|
537
|
-
try {
|
|
538
|
-
const { isFreeTrial, patchHttpForBanner } = require('./free-trial-banner');
|
|
539
|
-
if (isFreeTrial(endpointBase) && String(env('SECURENOW_HIDE_BANNER')) !== '1') {
|
|
540
|
-
patchHttpForBanner();
|
|
541
|
-
}
|
|
542
|
-
} catch (_) {}
|
|
543
|
-
|
|
544
510
|
console.log('[securenow] ✅ OpenTelemetry started for Next.js → %s', tracesUrl);
|
|
545
511
|
console.log('[securenow] 📊 Auto-capturing comprehensive request metadata:');
|
|
546
512
|
console.log('[securenow] • IP addresses (x-forwarded-for, x-real-ip, socket)');
|
|
@@ -572,27 +538,6 @@ function registerSecureNow(options = {}) {
|
|
|
572
538
|
console.error('[securenow] Make sure OpenTelemetry dependencies are installed');
|
|
573
539
|
}
|
|
574
540
|
}
|
|
575
|
-
|
|
576
|
-
// Firewall — runs independently from OTel so it works even if tracing fails
|
|
577
|
-
const firewallApiKey = env('SECURENOW_API_KEY');
|
|
578
|
-
if (firewallApiKey && env('SECURENOW_FIREWALL_ENABLED') !== '0') {
|
|
579
|
-
try {
|
|
580
|
-
require('./firewall').init({
|
|
581
|
-
apiKey: firewallApiKey,
|
|
582
|
-
apiUrl: env('SECURENOW_API_URL') || 'https://api.securenow.ai',
|
|
583
|
-
versionCheckInterval: parseInt(env('SECURENOW_FIREWALL_VERSION_INTERVAL'), 10) || 10,
|
|
584
|
-
syncInterval: parseInt(env('SECURENOW_FIREWALL_SYNC_INTERVAL'), 10) || 300,
|
|
585
|
-
failMode: env('SECURENOW_FIREWALL_FAIL_MODE') || 'open',
|
|
586
|
-
statusCode: parseInt(env('SECURENOW_FIREWALL_STATUS_CODE'), 10) || 403,
|
|
587
|
-
log: env('SECURENOW_FIREWALL_LOG') !== '0',
|
|
588
|
-
tcp: env('SECURENOW_FIREWALL_TCP') === '1',
|
|
589
|
-
iptables: env('SECURENOW_FIREWALL_IPTABLES') === '1',
|
|
590
|
-
cloud: env('SECURENOW_FIREWALL_CLOUD') || null,
|
|
591
|
-
});
|
|
592
|
-
} catch (e) {
|
|
593
|
-
console.warn('[securenow] Firewall init failed:', e.message);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
541
|
}
|
|
597
542
|
|
|
598
543
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securenow",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "OpenTelemetry instrumentation for Node.js
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces and logs to SigNoz or any OTLP backend",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "register.js",
|
|
7
7
|
"types": "register.d.ts",
|
|
@@ -11,6 +11,16 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"postinstall": "node postinstall.js || exit 0"
|
|
13
13
|
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/securenow/securenow-npm.git"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://securenow.ai",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/securenow/securenow-npm/issues",
|
|
21
|
+
"email": "support@securenow.ai"
|
|
22
|
+
},
|
|
23
|
+
"author": "SecureNow <support@securenow.ai> (https://securenow.ai)",
|
|
14
24
|
"keywords": [
|
|
15
25
|
"opentelemetry",
|
|
16
26
|
"otel",
|
|
@@ -20,24 +30,16 @@
|
|
|
20
30
|
"observability",
|
|
21
31
|
"apm",
|
|
22
32
|
"monitoring",
|
|
23
|
-
"cli",
|
|
24
33
|
"nextjs",
|
|
25
34
|
"next.js",
|
|
35
|
+
"signoz",
|
|
26
36
|
"instrumentation",
|
|
27
37
|
"telemetry",
|
|
28
38
|
"distributed-tracing",
|
|
29
39
|
"node",
|
|
30
40
|
"express",
|
|
31
41
|
"fastify",
|
|
32
|
-
"nestjs"
|
|
33
|
-
"nuxt",
|
|
34
|
-
"nuxt3",
|
|
35
|
-
"nitro",
|
|
36
|
-
"vue",
|
|
37
|
-
"firewall",
|
|
38
|
-
"ip-blocking",
|
|
39
|
-
"waf",
|
|
40
|
-
"security"
|
|
42
|
+
"nestjs"
|
|
41
43
|
],
|
|
42
44
|
"exports": {
|
|
43
45
|
".": {
|
|
@@ -72,24 +74,6 @@
|
|
|
72
74
|
"default": "./nextjs-wrapper.js"
|
|
73
75
|
},
|
|
74
76
|
"./nextjs-webpack-config": "./nextjs-webpack-config.js",
|
|
75
|
-
"./package.json": "./package.json",
|
|
76
|
-
"./nuxt": {
|
|
77
|
-
"types": "./nuxt.d.ts",
|
|
78
|
-
"import": "./nuxt.mjs",
|
|
79
|
-
"default": "./nuxt.mjs"
|
|
80
|
-
},
|
|
81
|
-
"./firewall": {
|
|
82
|
-
"default": "./firewall.js"
|
|
83
|
-
},
|
|
84
|
-
"./firewall-only": {
|
|
85
|
-
"default": "./firewall-only.js"
|
|
86
|
-
},
|
|
87
|
-
"./cidr": {
|
|
88
|
-
"default": "./cidr.js"
|
|
89
|
-
},
|
|
90
|
-
"./resolve-ip": {
|
|
91
|
-
"default": "./resolve-ip.js"
|
|
92
|
-
},
|
|
93
77
|
"./register-vite": "./register-vite.js",
|
|
94
78
|
"./web-vite": {
|
|
95
79
|
"import": "./web-vite.mjs",
|
|
@@ -111,45 +95,52 @@
|
|
|
111
95
|
"nextjs-wrapper.js",
|
|
112
96
|
"nextjs-wrapper.d.ts",
|
|
113
97
|
"nextjs-webpack-config.js",
|
|
114
|
-
"nuxt.mjs",
|
|
115
|
-
"nuxt.d.ts",
|
|
116
|
-
"nuxt-server-plugin.mjs",
|
|
117
98
|
"cli.js",
|
|
118
|
-
"cli/",
|
|
119
|
-
"free-trial-banner.js",
|
|
120
|
-
"resolve-ip.js",
|
|
121
|
-
"cidr.js",
|
|
122
|
-
"firewall.js",
|
|
123
|
-
"firewall-only.js",
|
|
124
|
-
"firewall-tcp.js",
|
|
125
|
-
"firewall-iptables.js",
|
|
126
|
-
"firewall-cloud.js",
|
|
127
99
|
"postinstall.js",
|
|
128
100
|
"register-vite.js",
|
|
129
101
|
"web-vite.mjs",
|
|
130
102
|
"examples/",
|
|
131
|
-
"docs/",
|
|
103
|
+
"docs/ALL-FRAMEWORKS-QUICKSTART.md",
|
|
104
|
+
"docs/ARCHITECTURE.md",
|
|
105
|
+
"docs/AUTO-BODY-CAPTURE.md",
|
|
106
|
+
"docs/CHANGELOG-NEXTJS.md",
|
|
107
|
+
"docs/NEXTJS-WEBPACK-WARNINGS.md",
|
|
108
|
+
"docs/AUTO-SETUP.md",
|
|
109
|
+
"docs/AUTOMATIC-IP-CAPTURE.md",
|
|
110
|
+
"docs/BODY-CAPTURE-QUICKSTART.md",
|
|
111
|
+
"docs/CUSTOMER-GUIDE.md",
|
|
112
|
+
"docs/EASIEST-SETUP.md",
|
|
113
|
+
"docs/ENVIRONMENT-VARIABLES.md",
|
|
114
|
+
"docs/EXPRESS-BODY-CAPTURE.md",
|
|
115
|
+
"docs/EXPRESS-SETUP-GUIDE.md",
|
|
116
|
+
"docs/INDEX.md",
|
|
117
|
+
"docs/LOGGING-GUIDE.md",
|
|
118
|
+
"docs/LOGGING-QUICKSTART.md",
|
|
119
|
+
"docs/NEXTJS-BODY-CAPTURE.md",
|
|
120
|
+
"docs/NEXTJS-GUIDE.md",
|
|
121
|
+
"docs/NEXTJS-QUICKSTART.md",
|
|
122
|
+
"docs/NEXTJS-WRAPPER-APPROACH.md",
|
|
123
|
+
"docs/QUICKSTART-BODY-CAPTURE.md",
|
|
124
|
+
"docs/REDACTION-EXAMPLES.md",
|
|
125
|
+
"docs/REQUEST-BODY-CAPTURE.md",
|
|
126
|
+
"docs/VERCEL-OTEL-MIGRATION.md",
|
|
132
127
|
"README.md",
|
|
133
|
-
"
|
|
134
|
-
"CONSUMING-APPS-GUIDE.md",
|
|
135
|
-
"SKILL-CLI.md",
|
|
136
|
-
"SKILL-API.md"
|
|
128
|
+
"LICENSE"
|
|
137
129
|
],
|
|
138
130
|
"dependencies": {
|
|
139
131
|
"@opentelemetry/api": "1.7.0",
|
|
140
|
-
"@opentelemetry/api-logs": "0.47.0",
|
|
132
|
+
"@opentelemetry/api-logs": "^0.47.0",
|
|
141
133
|
"@opentelemetry/auto-instrumentations-node": "0.47.0",
|
|
142
|
-
"@opentelemetry/exporter-logs-otlp-http": "0.47.0",
|
|
134
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.47.0",
|
|
143
135
|
"@opentelemetry/exporter-trace-otlp-http": "0.47.0",
|
|
144
136
|
"@opentelemetry/instrumentation": "0.47.0",
|
|
145
137
|
"@opentelemetry/instrumentation-document-load": "0.47.0",
|
|
146
138
|
"@opentelemetry/instrumentation-fetch": "0.47.0",
|
|
147
|
-
"@opentelemetry/instrumentation-http": "0.
|
|
148
|
-
"@opentelemetry/instrumentation-mongodb": "0.46.0",
|
|
139
|
+
"@opentelemetry/instrumentation-http": "^0.208.0",
|
|
149
140
|
"@opentelemetry/instrumentation-user-interaction": "0.47.0",
|
|
150
141
|
"@opentelemetry/instrumentation-xml-http-request": "0.47.0",
|
|
151
142
|
"@opentelemetry/resources": "1.20.0",
|
|
152
|
-
"@opentelemetry/sdk-logs": "0.47.0",
|
|
143
|
+
"@opentelemetry/sdk-logs": "^0.47.0",
|
|
153
144
|
"@opentelemetry/sdk-node": "0.47.0",
|
|
154
145
|
"@opentelemetry/sdk-trace-web": "1.20.0",
|
|
155
146
|
"@opentelemetry/semantic-conventions": "1.20.0",
|
|
@@ -158,28 +149,15 @@
|
|
|
158
149
|
"uuid": "^9.0.0"
|
|
159
150
|
},
|
|
160
151
|
"peerDependencies": {
|
|
161
|
-
"next": ">=13.0.0"
|
|
162
|
-
"nuxt": ">=3.0.0",
|
|
163
|
-
"@aws-sdk/client-wafv2": ">=3.0.0",
|
|
164
|
-
"@google-cloud/compute": ">=4.0.0"
|
|
152
|
+
"next": ">=13.0.0"
|
|
165
153
|
},
|
|
166
154
|
"peerDependenciesMeta": {
|
|
167
155
|
"next": {
|
|
168
156
|
"optional": true
|
|
169
|
-
},
|
|
170
|
-
"nuxt": {
|
|
171
|
-
"optional": true
|
|
172
|
-
},
|
|
173
|
-
"@aws-sdk/client-wafv2": {
|
|
174
|
-
"optional": true
|
|
175
|
-
},
|
|
176
|
-
"@google-cloud/compute": {
|
|
177
|
-
"optional": true
|
|
178
157
|
}
|
|
179
158
|
},
|
|
180
159
|
"overrides": {
|
|
181
|
-
"@opentelemetry/api": "1.7.0"
|
|
182
|
-
"@opentelemetry/api-logs": "0.47.0"
|
|
160
|
+
"@opentelemetry/api": "1.7.0"
|
|
183
161
|
},
|
|
184
162
|
"sideEffects": true,
|
|
185
163
|
"license": "ISC"
|
package/postinstall.js
CHANGED
|
@@ -53,7 +53,7 @@ export function register() {
|
|
|
53
53
|
* SECURENOW_APPID=my-nextjs-app
|
|
54
54
|
*
|
|
55
55
|
* Optional:
|
|
56
|
-
* SECURENOW_INSTANCE=http://your-
|
|
56
|
+
* SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
57
57
|
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
58
58
|
* OTEL_LOG_LEVEL=info
|
|
59
59
|
*/
|
|
@@ -77,7 +77,7 @@ export function register() {
|
|
|
77
77
|
* SECURENOW_APPID=my-nextjs-app
|
|
78
78
|
*
|
|
79
79
|
* Optional:
|
|
80
|
-
* SECURENOW_INSTANCE=http://your-
|
|
80
|
+
* SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
81
81
|
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
82
82
|
* OTEL_LOG_LEVEL=info
|
|
83
83
|
*
|
|
@@ -150,9 +150,9 @@ function createEnvTemplate(targetPath) {
|
|
|
150
150
|
# Required: Your application identifier
|
|
151
151
|
SECURENOW_APPID=my-nextjs-app
|
|
152
152
|
|
|
153
|
-
# Optional: Your
|
|
153
|
+
# Optional: Your SigNoz/OpenTelemetry collector endpoint
|
|
154
154
|
# Default: https://freetrial.securenow.ai:4318
|
|
155
|
-
SECURENOW_INSTANCE=http://your-
|
|
155
|
+
SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
156
156
|
|
|
157
157
|
# Optional: API key or authentication headers
|
|
158
158
|
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key-here"
|
|
@@ -270,14 +270,14 @@ async function setup() {
|
|
|
270
270
|
console.log('│ │');
|
|
271
271
|
console.log('│ 1. Edit .env.local and set: │');
|
|
272
272
|
console.log('│ SECURENOW_APPID=your-app-name │');
|
|
273
|
-
console.log('│ SECURENOW_INSTANCE=http://
|
|
273
|
+
console.log('│ SECURENOW_INSTANCE=http://signoz:4318 │');
|
|
274
274
|
if (shouldCreateMiddleware) {
|
|
275
275
|
console.log('│ SECURENOW_CAPTURE_BODY=1 │');
|
|
276
276
|
}
|
|
277
277
|
console.log('│ │');
|
|
278
278
|
console.log('│ 2. Run your app: npm run dev │');
|
|
279
279
|
console.log('│ │');
|
|
280
|
-
console.log('│ 3. Check
|
|
280
|
+
console.log('│ 3. Check SigNoz for traces! │');
|
|
281
281
|
console.log('│ │');
|
|
282
282
|
if (shouldCreateMiddleware) {
|
|
283
283
|
console.log('│ 📝 Body capture enabled with auto-redaction │');
|
package/register.d.ts
CHANGED