securenow 5.17.1 → 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.
Files changed (85) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +40 -243
  3. package/cli.js +455 -425
  4. package/console-instrumentation.js +136 -147
  5. package/docs/ALL-FRAMEWORKS-QUICKSTART.md +455 -1339
  6. package/docs/ARCHITECTURE.md +3 -3
  7. package/docs/AUTO-BODY-CAPTURE.md +1 -1
  8. package/docs/AUTO-SETUP.md +4 -4
  9. package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
  10. package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
  11. package/docs/CHANGELOG-NEXTJS.md +1 -1
  12. package/docs/CUSTOMER-GUIDE.md +16 -16
  13. package/docs/EASIEST-SETUP.md +5 -5
  14. package/docs/ENVIRONMENT-VARIABLES.md +652 -880
  15. package/docs/EXPRESS-BODY-CAPTURE.md +12 -13
  16. package/docs/EXPRESS-SETUP-GUIDE.md +720 -719
  17. package/docs/INDEX.md +4 -22
  18. package/docs/LOGGING-GUIDE.md +708 -701
  19. package/docs/LOGGING-QUICKSTART.md +239 -234
  20. package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
  21. package/docs/NEXTJS-GUIDE.md +14 -14
  22. package/docs/NEXTJS-QUICKSTART.md +1 -1
  23. package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
  24. package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
  25. package/docs/REDACTION-EXAMPLES.md +1 -1
  26. package/docs/REQUEST-BODY-CAPTURE.md +10 -19
  27. package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
  28. package/examples/README.md +6 -6
  29. package/examples/instrumentation-with-auto-capture.ts +1 -1
  30. package/examples/nextjs-env-example.txt +2 -2
  31. package/examples/nextjs-instrumentation.js +1 -1
  32. package/examples/nextjs-instrumentation.ts +1 -1
  33. package/examples/nextjs-with-logging-example.md +6 -6
  34. package/examples/nextjs-with-options.ts +1 -1
  35. package/examples/test-nextjs-setup.js +1 -1
  36. package/nextjs-auto-capture.js +207 -199
  37. package/nextjs-middleware.js +181 -186
  38. package/nextjs-webpack-config.js +53 -88
  39. package/nextjs-wrapper.js +158 -158
  40. package/nextjs.d.ts +1 -1
  41. package/nextjs.js +135 -190
  42. package/package.json +45 -67
  43. package/postinstall.js +6 -6
  44. package/register.d.ts +1 -1
  45. package/register.js +4 -39
  46. package/tracing.d.ts +1 -2
  47. package/tracing.js +22 -287
  48. package/web-vite.mjs +156 -239
  49. package/CONSUMING-APPS-GUIDE.md +0 -455
  50. package/NPM_README.md +0 -1958
  51. package/SKILL-API.md +0 -600
  52. package/SKILL-CLI.md +0 -419
  53. package/cidr.js +0 -83
  54. package/cli/apps.js +0 -585
  55. package/cli/auth.js +0 -280
  56. package/cli/client.js +0 -115
  57. package/cli/config.js +0 -173
  58. package/cli/firewall.js +0 -100
  59. package/cli/fp.js +0 -638
  60. package/cli/init.js +0 -201
  61. package/cli/monitor.js +0 -545
  62. package/cli/run.js +0 -133
  63. package/cli/security.js +0 -1064
  64. package/cli/ui.js +0 -386
  65. package/docs/API-KEYS-GUIDE.md +0 -233
  66. package/docs/AUTO-SETUP-SUMMARY.md +0 -331
  67. package/docs/BODY-CAPTURE-FIX.md +0 -261
  68. package/docs/COMPLETION-REPORT.md +0 -408
  69. package/docs/FINAL-SOLUTION.md +0 -335
  70. package/docs/FIREWALL-GUIDE.md +0 -426
  71. package/docs/IMPLEMENTATION-SUMMARY.md +0 -410
  72. package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +0 -323
  73. package/docs/NEXTJS-SETUP-COMPLETE.md +0 -795
  74. package/docs/NUXT-GUIDE.md +0 -166
  75. package/docs/SOLUTION-SUMMARY.md +0 -312
  76. package/firewall-cloud.js +0 -212
  77. package/firewall-iptables.js +0 -139
  78. package/firewall-only.js +0 -38
  79. package/firewall-tcp.js +0 -74
  80. package/firewall.js +0 -720
  81. package/free-trial-banner.js +0 -174
  82. package/nuxt-server-plugin.mjs +0 -423
  83. package/nuxt.d.ts +0 -60
  84. package/nuxt.mjs +0 -75
  85. package/resolve-ip.js +0 -77
package/nextjs-wrapper.js CHANGED
@@ -1,158 +1,158 @@
1
- /**
2
- * SecureNow Next.js API Route Wrapper for Body Capture
3
- *
4
- * This approach is NON-INVASIVE and runs INSIDE your handler,
5
- * so it never blocks or interferes with middleware or routing.
6
- *
7
- * Usage:
8
- *
9
- * import { withSecureNow } from 'securenow/nextjs-wrapper';
10
- *
11
- * export const POST = withSecureNow(async (request) => {
12
- * // Your handler code - request.body is available as parsed JSON
13
- * const data = await request.json();
14
- * return Response.json({ success: true });
15
- * });
16
- */
17
-
18
- const { trace } = require('@opentelemetry/api');
19
-
20
- // Default sensitive fields to redact
21
- const DEFAULT_SENSITIVE_FIELDS = [
22
- 'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
23
- 'access_token', 'auth', 'credentials', 'mysql_pwd', 'stripeToken',
24
- 'card', 'cardnumber', 'ccv', 'cvc', 'cvv', 'ssn', 'pin',
25
- ];
26
-
27
- /**
28
- * Redact sensitive fields from an object
29
- */
30
- function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
31
- if (!obj || typeof obj !== 'object') return obj;
32
-
33
- const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
34
-
35
- for (const key of Object.keys(redacted)) {
36
- const lowerKey = key.toLowerCase();
37
-
38
- if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
39
- redacted[key] = '[REDACTED]';
40
- } else if (typeof redacted[key] === 'object' && redacted[key] !== null) {
41
- redacted[key] = redactSensitiveData(redacted[key], sensitiveFields);
42
- }
43
- }
44
-
45
- return redacted;
46
- }
47
-
48
- /**
49
- * Capture body from Request object (clone to avoid consuming)
50
- */
51
- async function captureRequestBody(request) {
52
- const captureBody = String(process.env.SECURENOW_CAPTURE_BODY) === '1' ||
53
- String(process.env.SECURENOW_CAPTURE_BODY).toLowerCase() === 'true';
54
-
55
- if (!captureBody) return;
56
- if (!['POST', 'PUT', 'PATCH'].includes(request.method)) return;
57
-
58
- const span = trace.getActiveSpan();
59
- if (!span) return;
60
-
61
- try {
62
- 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 allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
66
-
67
- // Only for supported types
68
- if (!contentType.includes('application/json') &&
69
- !contentType.includes('application/graphql') &&
70
- !contentType.includes('application/x-www-form-urlencoded')) {
71
- return;
72
- }
73
-
74
- // Clone to avoid consuming the original
75
- const cloned = request.clone();
76
- const bodyText = await cloned.text();
77
-
78
- if (bodyText.length > maxBodySize) {
79
- span.setAttribute('http.request.body', `[TOO LARGE: ${bodyText.length} bytes]`);
80
- span.setAttribute('http.request.body.size', bodyText.length);
81
- return;
82
- }
83
-
84
- // Parse and redact based on type
85
- let redacted;
86
- if (contentType.includes('application/json') || contentType.includes('application/graphql')) {
87
- try {
88
- const parsed = JSON.parse(bodyText);
89
- redacted = redactSensitiveData(parsed, allSensitiveFields);
90
- span.setAttributes({
91
- 'http.request.body': JSON.stringify(redacted).substring(0, maxBodySize),
92
- 'http.request.body.type': contentType.includes('graphql') ? 'graphql' : 'json',
93
- 'http.request.body.size': bodyText.length,
94
- });
95
- } catch (e) {
96
- span.setAttribute('http.request.body', '[INVALID JSON]');
97
- }
98
- } else if (contentType.includes('application/x-www-form-urlencoded')) {
99
- const params = new URLSearchParams(bodyText);
100
- const parsed = Object.fromEntries(params);
101
- redacted = redactSensitiveData(parsed, allSensitiveFields);
102
- span.setAttributes({
103
- 'http.request.body': JSON.stringify(redacted).substring(0, maxBodySize),
104
- 'http.request.body.type': 'form',
105
- 'http.request.body.size': bodyText.length,
106
- });
107
- }
108
- } catch (error) {
109
- // Silently fail - never block the request
110
- }
111
- }
112
-
113
- /**
114
- * Wrap a Next.js API route handler to capture body
115
- * This is OPTIONAL and NON-INVASIVE - only use on routes where you want body capture
116
- */
117
- function withSecureNow(handler) {
118
- return async function wrappedHandler(request, context) {
119
- // Capture body asynchronously (doesn't block handler)
120
- captureRequestBody(request).catch(() => {
121
- // Ignore errors silently
122
- });
123
-
124
- // Call original handler immediately - no blocking!
125
- return handler(request, context);
126
- };
127
- }
128
-
129
- /**
130
- * Alternative: Auto-capture wrapper that tries to capture AFTER handler runs
131
- * This is even safer as it never interferes with the handler logic
132
- */
133
- function withSecureNowAsync(handler) {
134
- return async function wrappedHandler(request, context) {
135
- // Try to capture body in background (non-blocking)
136
- const capturePromise = captureRequestBody(request);
137
-
138
- // Run handler
139
- const response = await handler(request, context);
140
-
141
- // Wait for capture to finish (but don't fail if it doesn't)
142
- await capturePromise.catch(() => {});
143
-
144
- return response;
145
- };
146
- }
147
-
148
- module.exports = {
149
- withSecureNow,
150
- withSecureNowAsync,
151
- captureRequestBody,
152
- redactSensitiveData,
153
- DEFAULT_SENSITIVE_FIELDS,
154
- };
155
-
156
-
157
-
158
-
1
+ /**
2
+ * SecureNow Next.js API Route Wrapper for Body Capture
3
+ *
4
+ * This approach is NON-INVASIVE and runs INSIDE your handler,
5
+ * so it never blocks or interferes with middleware or routing.
6
+ *
7
+ * Usage:
8
+ *
9
+ * import { withSecureNow } from 'securenow/nextjs-wrapper';
10
+ *
11
+ * export const POST = withSecureNow(async (request) => {
12
+ * // Your handler code - request.body is available as parsed JSON
13
+ * const data = await request.json();
14
+ * return Response.json({ success: true });
15
+ * });
16
+ */
17
+
18
+ const { trace } = require('@opentelemetry/api');
19
+
20
+ // Default sensitive fields to redact
21
+ const DEFAULT_SENSITIVE_FIELDS = [
22
+ 'password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey',
23
+ 'access_token', 'auth', 'credentials', 'mysql_pwd', 'stripeToken',
24
+ 'card', 'cardnumber', 'ccv', 'cvc', 'cvv', 'ssn', 'pin',
25
+ ];
26
+
27
+ /**
28
+ * Redact sensitive fields from an object
29
+ */
30
+ function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
31
+ if (!obj || typeof obj !== 'object') return obj;
32
+
33
+ const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
34
+
35
+ for (const key in redacted) {
36
+ const lowerKey = key.toLowerCase();
37
+
38
+ if (sensitiveFields.some(field => lowerKey.includes(field.toLowerCase()))) {
39
+ redacted[key] = '[REDACTED]';
40
+ } else if (typeof redacted[key] === 'object' && redacted[key] !== null) {
41
+ redacted[key] = redactSensitiveData(redacted[key], sensitiveFields);
42
+ }
43
+ }
44
+
45
+ return redacted;
46
+ }
47
+
48
+ /**
49
+ * Capture body from Request object (clone to avoid consuming)
50
+ */
51
+ async function captureRequestBody(request) {
52
+ const captureBody = String(process.env.SECURENOW_CAPTURE_BODY) === '1' ||
53
+ String(process.env.SECURENOW_CAPTURE_BODY).toLowerCase() === 'true';
54
+
55
+ if (!captureBody) return;
56
+ if (!['POST', 'PUT', 'PATCH'].includes(request.method)) return;
57
+
58
+ const span = trace.getActiveSpan();
59
+ if (!span) return;
60
+
61
+ try {
62
+ const contentType = request.headers.get('content-type') || '';
63
+ const maxBodySize = parseInt(process.env.SECURENOW_MAX_BODY_SIZE || '10240');
64
+ const customSensitiveFields = (process.env.SECURENOW_SENSITIVE_FIELDS || '').split(',').map(s => s.trim()).filter(Boolean);
65
+ const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
66
+
67
+ // Only for supported types
68
+ if (!contentType.includes('application/json') &&
69
+ !contentType.includes('application/graphql') &&
70
+ !contentType.includes('application/x-www-form-urlencoded')) {
71
+ return;
72
+ }
73
+
74
+ // Clone to avoid consuming the original
75
+ const cloned = request.clone();
76
+ const bodyText = await cloned.text();
77
+
78
+ if (bodyText.length > maxBodySize) {
79
+ span.setAttribute('http.request.body', `[TOO LARGE: ${bodyText.length} bytes]`);
80
+ span.setAttribute('http.request.body.size', bodyText.length);
81
+ return;
82
+ }
83
+
84
+ // Parse and redact based on type
85
+ let redacted;
86
+ if (contentType.includes('application/json') || contentType.includes('application/graphql')) {
87
+ try {
88
+ const parsed = JSON.parse(bodyText);
89
+ redacted = redactSensitiveData(parsed, allSensitiveFields);
90
+ span.setAttributes({
91
+ 'http.request.body': JSON.stringify(redacted).substring(0, maxBodySize),
92
+ 'http.request.body.type': contentType.includes('graphql') ? 'graphql' : 'json',
93
+ 'http.request.body.size': bodyText.length,
94
+ });
95
+ } catch (e) {
96
+ span.setAttribute('http.request.body', '[INVALID JSON]');
97
+ }
98
+ } else if (contentType.includes('application/x-www-form-urlencoded')) {
99
+ const params = new URLSearchParams(bodyText);
100
+ const parsed = Object.fromEntries(params);
101
+ redacted = redactSensitiveData(parsed, allSensitiveFields);
102
+ span.setAttributes({
103
+ 'http.request.body': JSON.stringify(redacted).substring(0, maxBodySize),
104
+ 'http.request.body.type': 'form',
105
+ 'http.request.body.size': bodyText.length,
106
+ });
107
+ }
108
+ } catch (error) {
109
+ // Silently fail - never block the request
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Wrap a Next.js API route handler to capture body
115
+ * This is OPTIONAL and NON-INVASIVE - only use on routes where you want body capture
116
+ */
117
+ function withSecureNow(handler) {
118
+ return async function wrappedHandler(request, context) {
119
+ // Capture body asynchronously (doesn't block handler)
120
+ captureRequestBody(request).catch(() => {
121
+ // Ignore errors silently
122
+ });
123
+
124
+ // Call original handler immediately - no blocking!
125
+ return handler(request, context);
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Alternative: Auto-capture wrapper that tries to capture AFTER handler runs
131
+ * This is even safer as it never interferes with the handler logic
132
+ */
133
+ function withSecureNowAsync(handler) {
134
+ return async function wrappedHandler(request, context) {
135
+ // Try to capture body in background (non-blocking)
136
+ const capturePromise = captureRequestBody(request);
137
+
138
+ // Run handler
139
+ const response = await handler(request, context);
140
+
141
+ // Wait for capture to finish (but don't fail if it doesn't)
142
+ await capturePromise.catch(() => {});
143
+
144
+ return response;
145
+ };
146
+ }
147
+
148
+ module.exports = {
149
+ withSecureNow,
150
+ withSecureNowAsync,
151
+ captureRequestBody,
152
+ redactSensitiveData,
153
+ DEFAULT_SENSITIVE_FIELDS,
154
+ };
155
+
156
+
157
+
158
+
package/nextjs.d.ts CHANGED
@@ -51,7 +51,7 @@ export interface RegisterOptions {
51
51
  * export function register() {
52
52
  * registerSecureNow({
53
53
  * serviceName: 'my-nextjs-app',
54
- * endpoint: 'http://your-otlp-backend.example.com:4318',
54
+ * endpoint: 'http://signoz.company.com:4318',
55
55
  * noUuid: true,
56
56
  * });
57
57
  * }