securenow 4.0.6 → 4.0.9
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/README.md +4 -3
- package/cli.js +4 -1
- package/docs/ARCHITECTURE.md +408 -0
- package/{AUTO-BODY-CAPTURE.md → docs/AUTO-BODY-CAPTURE.md} +3 -0
- package/docs/AUTO-SETUP-SUMMARY.md +331 -0
- package/{AUTO-SETUP.md → docs/AUTO-SETUP.md} +3 -0
- package/{AUTOMATIC-IP-CAPTURE.md → docs/AUTOMATIC-IP-CAPTURE.md} +3 -0
- package/{BODY-CAPTURE-FIX.md → docs/BODY-CAPTURE-FIX.md} +3 -0
- package/{BODY-CAPTURE-QUICKSTART.md → docs/BODY-CAPTURE-QUICKSTART.md} +147 -147
- package/docs/CHANGELOG-NEXTJS.md +235 -0
- package/docs/COMPLETION-REPORT.md +408 -0
- package/{EASIEST-SETUP.md → docs/EASIEST-SETUP.md} +3 -0
- package/docs/EXPRESS-BODY-CAPTURE.md +1027 -0
- package/{FINAL-SOLUTION.md → docs/FINAL-SOLUTION.md} +3 -0
- package/docs/IMPLEMENTATION-SUMMARY.md +410 -0
- package/docs/INDEX.md +129 -0
- package/{NEXTJS-BODY-CAPTURE-COMPARISON.md → docs/NEXTJS-BODY-CAPTURE-COMPARISON.md} +3 -0
- package/docs/NEXTJS-WEBPACK-WARNINGS.md +267 -0
- package/{NEXTJS-WRAPPER-APPROACH.md → docs/NEXTJS-WRAPPER-APPROACH.md} +3 -0
- package/{QUICKSTART-BODY-CAPTURE.md → docs/QUICKSTART-BODY-CAPTURE.md} +3 -0
- package/{REDACTION-EXAMPLES.md → docs/REDACTION-EXAMPLES.md} +3 -0
- package/{REQUEST-BODY-CAPTURE.md → docs/REQUEST-BODY-CAPTURE.md} +575 -575
- package/{SOLUTION-SUMMARY.md → docs/SOLUTION-SUMMARY.md} +3 -0
- package/docs/VERCEL-OTEL-MIGRATION.md +255 -0
- package/examples/README.md +3 -0
- package/examples/instrumentation-with-auto-capture.ts +3 -0
- package/examples/next.config.js +3 -0
- package/examples/nextjs-api-route-with-body-capture.ts +3 -0
- package/examples/nextjs-env-example.txt +3 -0
- package/examples/nextjs-instrumentation.js +3 -0
- package/examples/nextjs-instrumentation.ts +3 -0
- package/examples/nextjs-middleware.js +3 -0
- package/examples/nextjs-middleware.ts +3 -0
- package/examples/nextjs-with-options.ts +3 -0
- package/examples/test-nextjs-setup.js +3 -0
- package/nextjs-auto-capture.js +3 -0
- package/nextjs-middleware.js +3 -0
- package/nextjs-wrapper.js +3 -0
- package/nextjs.js +174 -72
- package/package.json +3 -19
- package/postinstall.js +310 -310
- package/tracing.js +287 -287
- /package/{CUSTOMER-GUIDE.md → docs/CUSTOMER-GUIDE.md} +0 -0
- /package/{NEXTJS-BODY-CAPTURE.md → docs/NEXTJS-BODY-CAPTURE.md} +0 -0
- /package/{NEXTJS-GUIDE.md → docs/NEXTJS-GUIDE.md} +0 -0
- /package/{NEXTJS-QUICKSTART.md → docs/NEXTJS-QUICKSTART.md} +0 -0
package/postinstall.js
CHANGED
|
@@ -1,310 +1,310 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* SecureNow Post-Install Script
|
|
6
|
-
*
|
|
7
|
-
* Automatically detects Next.js projects and offers to create instrumentation file
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const readline = require('readline');
|
|
13
|
-
|
|
14
|
-
// Check if we're in a Next.js project
|
|
15
|
-
function isNextJsProject() {
|
|
16
|
-
try {
|
|
17
|
-
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
18
|
-
if (!fs.existsSync(packageJsonPath)) return false;
|
|
19
|
-
|
|
20
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
21
|
-
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
22
|
-
|
|
23
|
-
return !!deps.next;
|
|
24
|
-
} catch (error) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Check if instrumentation file already exists
|
|
30
|
-
function hasInstrumentationFile() {
|
|
31
|
-
const files = [
|
|
32
|
-
'instrumentation.ts',
|
|
33
|
-
'instrumentation.js',
|
|
34
|
-
'src/instrumentation.ts',
|
|
35
|
-
'src/instrumentation.js'
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
return files.some(file => fs.existsSync(path.join(process.cwd(), file)));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Create TypeScript instrumentation file
|
|
42
|
-
function createTsInstrumentation(targetPath) {
|
|
43
|
-
const content = `import { registerSecureNow } from 'securenow/nextjs';
|
|
44
|
-
|
|
45
|
-
export function register() {
|
|
46
|
-
registerSecureNow();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Configuration via .env.local:
|
|
51
|
-
*
|
|
52
|
-
* Required:
|
|
53
|
-
* SECURENOW_APPID=my-nextjs-app
|
|
54
|
-
*
|
|
55
|
-
* Optional:
|
|
56
|
-
* SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
57
|
-
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
58
|
-
* OTEL_LOG_LEVEL=info
|
|
59
|
-
*/
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
fs.writeFileSync(targetPath, content, 'utf8');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Create JavaScript instrumentation file
|
|
66
|
-
function createJsInstrumentation(targetPath) {
|
|
67
|
-
const content = `const { registerSecureNow } = require('securenow/nextjs');
|
|
68
|
-
|
|
69
|
-
export function register() {
|
|
70
|
-
registerSecureNow();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Configuration via .env.local:
|
|
75
|
-
*
|
|
76
|
-
* Required:
|
|
77
|
-
* SECURENOW_APPID=my-nextjs-app
|
|
78
|
-
*
|
|
79
|
-
* Optional:
|
|
80
|
-
* SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
81
|
-
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
82
|
-
* OTEL_LOG_LEVEL=info
|
|
83
|
-
*
|
|
84
|
-
* Optional: Enable request body capture
|
|
85
|
-
* SECURENOW_CAPTURE_BODY=1
|
|
86
|
-
* (Also create middleware.ts to activate - run: npx securenow init)
|
|
87
|
-
*/
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
|
-
fs.writeFileSync(targetPath, content, 'utf8');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Create TypeScript middleware file
|
|
94
|
-
function createTsMiddleware(targetPath) {
|
|
95
|
-
const content = `// SecureNow Middleware - Automatic Request Body Capture
|
|
96
|
-
// This enables capturing JSON, GraphQL, and Form request bodies
|
|
97
|
-
// with automatic sensitive field redaction
|
|
98
|
-
|
|
99
|
-
export { middleware } from 'securenow/nextjs-middleware';
|
|
100
|
-
|
|
101
|
-
export const config = {
|
|
102
|
-
matcher: '/api/:path*', // Apply to all API routes
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Bodies are captured with:
|
|
107
|
-
* - Automatic redaction of passwords, tokens, cards, etc.
|
|
108
|
-
* - Size limits (configurable via SECURENOW_MAX_BODY_SIZE)
|
|
109
|
-
* - JSON, GraphQL, Form data support
|
|
110
|
-
*
|
|
111
|
-
* Configure in .env.local:
|
|
112
|
-
* SECURENOW_MAX_BODY_SIZE=20480
|
|
113
|
-
* SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
114
|
-
*/
|
|
115
|
-
`;
|
|
116
|
-
|
|
117
|
-
fs.writeFileSync(targetPath, content, 'utf8');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Create JavaScript middleware file
|
|
121
|
-
function createJsMiddleware(targetPath) {
|
|
122
|
-
const content = `// SecureNow Middleware - Automatic Request Body Capture
|
|
123
|
-
// This enables capturing JSON, GraphQL, and Form request bodies
|
|
124
|
-
// with automatic sensitive field redaction
|
|
125
|
-
|
|
126
|
-
export { middleware } from 'securenow/nextjs-middleware';
|
|
127
|
-
|
|
128
|
-
export const config = {
|
|
129
|
-
matcher: '/api/:path*', // Apply to all API routes
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Bodies are captured with:
|
|
134
|
-
* - Automatic redaction of passwords, tokens, cards, etc.
|
|
135
|
-
* - Size limits (configurable via SECURENOW_MAX_BODY_SIZE)
|
|
136
|
-
* - JSON, GraphQL, Form data support
|
|
137
|
-
*
|
|
138
|
-
* Configure in .env.local:
|
|
139
|
-
* SECURENOW_MAX_BODY_SIZE=20480
|
|
140
|
-
* SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
141
|
-
*/
|
|
142
|
-
`;
|
|
143
|
-
|
|
144
|
-
fs.writeFileSync(targetPath, content, 'utf8');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Create .env.local template
|
|
148
|
-
function createEnvTemplate(targetPath) {
|
|
149
|
-
const content = `# SecureNow Configuration
|
|
150
|
-
# Required: Your application identifier
|
|
151
|
-
SECURENOW_APPID=my-nextjs-app
|
|
152
|
-
|
|
153
|
-
# Optional: Your SigNoz/OpenTelemetry collector endpoint
|
|
154
|
-
# Default: http://46.62.173.237:4318
|
|
155
|
-
SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
156
|
-
|
|
157
|
-
# Optional: API key or authentication headers
|
|
158
|
-
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key-here"
|
|
159
|
-
|
|
160
|
-
# Optional: Log level (debug|info|warn|error)
|
|
161
|
-
# OTEL_LOG_LEVEL=info
|
|
162
|
-
|
|
163
|
-
# Optional: Enable request body capture (requires middleware.ts)
|
|
164
|
-
# SECURENOW_CAPTURE_BODY=1
|
|
165
|
-
# SECURENOW_MAX_BODY_SIZE=10240
|
|
166
|
-
# SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
167
|
-
`;
|
|
168
|
-
|
|
169
|
-
fs.writeFileSync(targetPath, content, 'utf8');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Check if TypeScript is used
|
|
173
|
-
function isTypeScriptProject() {
|
|
174
|
-
return fs.existsSync(path.join(process.cwd(), 'tsconfig.json'));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Main setup function
|
|
178
|
-
async function setup() {
|
|
179
|
-
// Skip if not in Next.js project
|
|
180
|
-
if (!isNextJsProject()) {
|
|
181
|
-
console.log('[securenow] Not a Next.js project, skipping auto-setup');
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Skip if instrumentation file already exists
|
|
186
|
-
if (hasInstrumentationFile()) {
|
|
187
|
-
console.log('[securenow] ✅ Instrumentation file already exists');
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
console.log('\n┌─────────────────────────────────────────────────┐');
|
|
192
|
-
console.log('│ 🎉 SecureNow installed successfully! │');
|
|
193
|
-
console.log('│ Next.js project detected │');
|
|
194
|
-
console.log('└─────────────────────────────────────────────────┘\n');
|
|
195
|
-
|
|
196
|
-
// Check if we're in CI/non-interactive environment
|
|
197
|
-
if (process.env.CI || !process.stdin.isTTY) {
|
|
198
|
-
console.log('[securenow] ℹ️ Non-interactive environment detected');
|
|
199
|
-
console.log('[securenow] 💡 To complete setup, run: npx securenow init');
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Ask user if they want to auto-setup
|
|
204
|
-
const rl = readline.createInterface({
|
|
205
|
-
input: process.stdin,
|
|
206
|
-
output: process.stdout
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
rl.question('Would you like to automatically create instrumentation file? (Y/n) ', (answer) => {
|
|
210
|
-
const shouldCreate = !answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
211
|
-
|
|
212
|
-
if (!shouldCreate) {
|
|
213
|
-
console.log('\n[securenow] No problem! To setup later, run: npx securenow init');
|
|
214
|
-
rl.close();
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
const useTypeScript = isTypeScriptProject();
|
|
220
|
-
const srcExists = fs.existsSync(path.join(process.cwd(), 'src'));
|
|
221
|
-
|
|
222
|
-
// Determine file path
|
|
223
|
-
const fileName = useTypeScript ? 'instrumentation.ts' : 'instrumentation.js';
|
|
224
|
-
const filePath = srcExists
|
|
225
|
-
? path.join(process.cwd(), 'src', fileName)
|
|
226
|
-
: path.join(process.cwd(), fileName);
|
|
227
|
-
|
|
228
|
-
// Create instrumentation file
|
|
229
|
-
if (useTypeScript) {
|
|
230
|
-
createTsInstrumentation(filePath);
|
|
231
|
-
} else {
|
|
232
|
-
createJsInstrumentation(filePath);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
console.log(`\n✅ Created ${srcExists ? 'src/' : ''}${fileName}`);
|
|
236
|
-
|
|
237
|
-
// Ask about middleware for body capture
|
|
238
|
-
rl.question('\nWould you like to enable request body capture? (y/N) ', (middlewareAnswer) => {
|
|
239
|
-
const shouldCreateMiddleware = middlewareAnswer && (middlewareAnswer.toLowerCase() === 'y' || middlewareAnswer.toLowerCase() === 'yes');
|
|
240
|
-
|
|
241
|
-
if (shouldCreateMiddleware) {
|
|
242
|
-
try {
|
|
243
|
-
const middlewareName = useTypeScript ? 'middleware.ts' : 'middleware.js';
|
|
244
|
-
const middlewarePath = srcExists
|
|
245
|
-
? path.join(process.cwd(), 'src', middlewareName)
|
|
246
|
-
: path.join(process.cwd(), middlewareName);
|
|
247
|
-
|
|
248
|
-
if (useTypeScript) {
|
|
249
|
-
createTsMiddleware(middlewarePath);
|
|
250
|
-
} else {
|
|
251
|
-
createJsMiddleware(middlewarePath);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
console.log(`✅ Created ${srcExists ? 'src/' : ''}${middlewareName}`);
|
|
255
|
-
console.log(' → Captures JSON, GraphQL, Form bodies with auto-redaction');
|
|
256
|
-
} catch (error) {
|
|
257
|
-
console.warn(`⚠️ Could not create middleware: ${error.message}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Create .env.local if it doesn't exist
|
|
262
|
-
const envPath = path.join(process.cwd(), '.env.local');
|
|
263
|
-
if (!fs.existsSync(envPath)) {
|
|
264
|
-
createEnvTemplate(envPath);
|
|
265
|
-
console.log('✅ Created .env.local template');
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
console.log('\n┌─────────────────────────────────────────────────┐');
|
|
269
|
-
console.log('│ 🚀 Next Steps: │');
|
|
270
|
-
console.log('│ │');
|
|
271
|
-
console.log('│ 1. Edit .env.local and set: │');
|
|
272
|
-
console.log('│ SECURENOW_APPID=your-app-name │');
|
|
273
|
-
console.log('│ SECURENOW_INSTANCE=http://signoz:4318 │');
|
|
274
|
-
if (shouldCreateMiddleware) {
|
|
275
|
-
console.log('│ SECURENOW_CAPTURE_BODY=1 │');
|
|
276
|
-
}
|
|
277
|
-
console.log('│ │');
|
|
278
|
-
console.log('│ 2. Run your app: npm run dev │');
|
|
279
|
-
console.log('│ │');
|
|
280
|
-
console.log('│ 3. Check SigNoz for traces! │');
|
|
281
|
-
console.log('│ │');
|
|
282
|
-
if (shouldCreateMiddleware) {
|
|
283
|
-
console.log('│ 📝 Body capture enabled with auto-redaction │');
|
|
284
|
-
}
|
|
285
|
-
console.log('│ 📚 Full guide: npm docs securenow │');
|
|
286
|
-
console.log('└─────────────────────────────────────────────────┘\n');
|
|
287
|
-
|
|
288
|
-
rl.close();
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
} catch (error) {
|
|
292
|
-
console.error('\n❌ Failed to create instrumentation file:', error.message);
|
|
293
|
-
console.log('💡 You can create it manually or run: npx securenow init');
|
|
294
|
-
rl.close();
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Run setup if this is a new installation (not being installed as a dependency of another package)
|
|
300
|
-
if (require.main === module || process.env.npm_config_global !== 'true') {
|
|
301
|
-
setup().catch(err => {
|
|
302
|
-
console.error('[securenow] Setup error:', err);
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
module.exports = { setup };
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SecureNow Post-Install Script
|
|
6
|
+
*
|
|
7
|
+
* Automatically detects Next.js projects and offers to create instrumentation file
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const readline = require('readline');
|
|
13
|
+
|
|
14
|
+
// Check if we're in a Next.js project
|
|
15
|
+
function isNextJsProject() {
|
|
16
|
+
try {
|
|
17
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
18
|
+
if (!fs.existsSync(packageJsonPath)) return false;
|
|
19
|
+
|
|
20
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
21
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
22
|
+
|
|
23
|
+
return !!deps.next;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if instrumentation file already exists
|
|
30
|
+
function hasInstrumentationFile() {
|
|
31
|
+
const files = [
|
|
32
|
+
'instrumentation.ts',
|
|
33
|
+
'instrumentation.js',
|
|
34
|
+
'src/instrumentation.ts',
|
|
35
|
+
'src/instrumentation.js'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
return files.some(file => fs.existsSync(path.join(process.cwd(), file)));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Create TypeScript instrumentation file
|
|
42
|
+
function createTsInstrumentation(targetPath) {
|
|
43
|
+
const content = `import { registerSecureNow } from 'securenow/nextjs';
|
|
44
|
+
|
|
45
|
+
export function register() {
|
|
46
|
+
registerSecureNow();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configuration via .env.local:
|
|
51
|
+
*
|
|
52
|
+
* Required:
|
|
53
|
+
* SECURENOW_APPID=my-nextjs-app
|
|
54
|
+
*
|
|
55
|
+
* Optional:
|
|
56
|
+
* SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
57
|
+
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
58
|
+
* OTEL_LOG_LEVEL=info
|
|
59
|
+
*/
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Create JavaScript instrumentation file
|
|
66
|
+
function createJsInstrumentation(targetPath) {
|
|
67
|
+
const content = `const { registerSecureNow } = require('securenow/nextjs');
|
|
68
|
+
|
|
69
|
+
export function register() {
|
|
70
|
+
registerSecureNow();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Configuration via .env.local:
|
|
75
|
+
*
|
|
76
|
+
* Required:
|
|
77
|
+
* SECURENOW_APPID=my-nextjs-app
|
|
78
|
+
*
|
|
79
|
+
* Optional:
|
|
80
|
+
* SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
81
|
+
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
82
|
+
* OTEL_LOG_LEVEL=info
|
|
83
|
+
*
|
|
84
|
+
* Optional: Enable request body capture
|
|
85
|
+
* SECURENOW_CAPTURE_BODY=1
|
|
86
|
+
* (Also create middleware.ts to activate - run: npx securenow init)
|
|
87
|
+
*/
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Create TypeScript middleware file
|
|
94
|
+
function createTsMiddleware(targetPath) {
|
|
95
|
+
const content = `// SecureNow Middleware - Automatic Request Body Capture
|
|
96
|
+
// This enables capturing JSON, GraphQL, and Form request bodies
|
|
97
|
+
// with automatic sensitive field redaction
|
|
98
|
+
|
|
99
|
+
export { middleware } from 'securenow/nextjs-middleware';
|
|
100
|
+
|
|
101
|
+
export const config = {
|
|
102
|
+
matcher: '/api/:path*', // Apply to all API routes
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Bodies are captured with:
|
|
107
|
+
* - Automatic redaction of passwords, tokens, cards, etc.
|
|
108
|
+
* - Size limits (configurable via SECURENOW_MAX_BODY_SIZE)
|
|
109
|
+
* - JSON, GraphQL, Form data support
|
|
110
|
+
*
|
|
111
|
+
* Configure in .env.local:
|
|
112
|
+
* SECURENOW_MAX_BODY_SIZE=20480
|
|
113
|
+
* SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
114
|
+
*/
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create JavaScript middleware file
|
|
121
|
+
function createJsMiddleware(targetPath) {
|
|
122
|
+
const content = `// SecureNow Middleware - Automatic Request Body Capture
|
|
123
|
+
// This enables capturing JSON, GraphQL, and Form request bodies
|
|
124
|
+
// with automatic sensitive field redaction
|
|
125
|
+
|
|
126
|
+
export { middleware } from 'securenow/nextjs-middleware';
|
|
127
|
+
|
|
128
|
+
export const config = {
|
|
129
|
+
matcher: '/api/:path*', // Apply to all API routes
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Bodies are captured with:
|
|
134
|
+
* - Automatic redaction of passwords, tokens, cards, etc.
|
|
135
|
+
* - Size limits (configurable via SECURENOW_MAX_BODY_SIZE)
|
|
136
|
+
* - JSON, GraphQL, Form data support
|
|
137
|
+
*
|
|
138
|
+
* Configure in .env.local:
|
|
139
|
+
* SECURENOW_MAX_BODY_SIZE=20480
|
|
140
|
+
* SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
141
|
+
*/
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Create .env.local template
|
|
148
|
+
function createEnvTemplate(targetPath) {
|
|
149
|
+
const content = `# SecureNow Configuration
|
|
150
|
+
# Required: Your application identifier
|
|
151
|
+
SECURENOW_APPID=my-nextjs-app
|
|
152
|
+
|
|
153
|
+
# Optional: Your SigNoz/OpenTelemetry collector endpoint
|
|
154
|
+
# Default: http://46.62.173.237:4318
|
|
155
|
+
SECURENOW_INSTANCE=http://your-signoz-server:4318
|
|
156
|
+
|
|
157
|
+
# Optional: API key or authentication headers
|
|
158
|
+
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key-here"
|
|
159
|
+
|
|
160
|
+
# Optional: Log level (debug|info|warn|error)
|
|
161
|
+
# OTEL_LOG_LEVEL=info
|
|
162
|
+
|
|
163
|
+
# Optional: Enable request body capture (requires middleware.ts)
|
|
164
|
+
# SECURENOW_CAPTURE_BODY=1
|
|
165
|
+
# SECURENOW_MAX_BODY_SIZE=10240
|
|
166
|
+
# SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check if TypeScript is used
|
|
173
|
+
function isTypeScriptProject() {
|
|
174
|
+
return fs.existsSync(path.join(process.cwd(), 'tsconfig.json'));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Main setup function
|
|
178
|
+
async function setup() {
|
|
179
|
+
// Skip if not in Next.js project
|
|
180
|
+
if (!isNextJsProject()) {
|
|
181
|
+
console.log('[securenow] Not a Next.js project, skipping auto-setup');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Skip if instrumentation file already exists
|
|
186
|
+
if (hasInstrumentationFile()) {
|
|
187
|
+
console.log('[securenow] ✅ Instrumentation file already exists');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
console.log('\n┌─────────────────────────────────────────────────┐');
|
|
192
|
+
console.log('│ 🎉 SecureNow installed successfully! │');
|
|
193
|
+
console.log('│ Next.js project detected │');
|
|
194
|
+
console.log('└─────────────────────────────────────────────────┘\n');
|
|
195
|
+
|
|
196
|
+
// Check if we're in CI/non-interactive environment
|
|
197
|
+
if (process.env.CI || !process.stdin.isTTY) {
|
|
198
|
+
console.log('[securenow] ℹ️ Non-interactive environment detected');
|
|
199
|
+
console.log('[securenow] 💡 To complete setup, run: npx securenow init');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Ask user if they want to auto-setup
|
|
204
|
+
const rl = readline.createInterface({
|
|
205
|
+
input: process.stdin,
|
|
206
|
+
output: process.stdout
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
rl.question('Would you like to automatically create instrumentation file? (Y/n) ', (answer) => {
|
|
210
|
+
const shouldCreate = !answer || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
211
|
+
|
|
212
|
+
if (!shouldCreate) {
|
|
213
|
+
console.log('\n[securenow] No problem! To setup later, run: npx securenow init');
|
|
214
|
+
rl.close();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const useTypeScript = isTypeScriptProject();
|
|
220
|
+
const srcExists = fs.existsSync(path.join(process.cwd(), 'src'));
|
|
221
|
+
|
|
222
|
+
// Determine file path
|
|
223
|
+
const fileName = useTypeScript ? 'instrumentation.ts' : 'instrumentation.js';
|
|
224
|
+
const filePath = srcExists
|
|
225
|
+
? path.join(process.cwd(), 'src', fileName)
|
|
226
|
+
: path.join(process.cwd(), fileName);
|
|
227
|
+
|
|
228
|
+
// Create instrumentation file
|
|
229
|
+
if (useTypeScript) {
|
|
230
|
+
createTsInstrumentation(filePath);
|
|
231
|
+
} else {
|
|
232
|
+
createJsInstrumentation(filePath);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.log(`\n✅ Created ${srcExists ? 'src/' : ''}${fileName}`);
|
|
236
|
+
|
|
237
|
+
// Ask about middleware for body capture
|
|
238
|
+
rl.question('\nWould you like to enable request body capture? (y/N) ', (middlewareAnswer) => {
|
|
239
|
+
const shouldCreateMiddleware = middlewareAnswer && (middlewareAnswer.toLowerCase() === 'y' || middlewareAnswer.toLowerCase() === 'yes');
|
|
240
|
+
|
|
241
|
+
if (shouldCreateMiddleware) {
|
|
242
|
+
try {
|
|
243
|
+
const middlewareName = useTypeScript ? 'middleware.ts' : 'middleware.js';
|
|
244
|
+
const middlewarePath = srcExists
|
|
245
|
+
? path.join(process.cwd(), 'src', middlewareName)
|
|
246
|
+
: path.join(process.cwd(), middlewareName);
|
|
247
|
+
|
|
248
|
+
if (useTypeScript) {
|
|
249
|
+
createTsMiddleware(middlewarePath);
|
|
250
|
+
} else {
|
|
251
|
+
createJsMiddleware(middlewarePath);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log(`✅ Created ${srcExists ? 'src/' : ''}${middlewareName}`);
|
|
255
|
+
console.log(' → Captures JSON, GraphQL, Form bodies with auto-redaction');
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.warn(`⚠️ Could not create middleware: ${error.message}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Create .env.local if it doesn't exist
|
|
262
|
+
const envPath = path.join(process.cwd(), '.env.local');
|
|
263
|
+
if (!fs.existsSync(envPath)) {
|
|
264
|
+
createEnvTemplate(envPath);
|
|
265
|
+
console.log('✅ Created .env.local template');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
console.log('\n┌─────────────────────────────────────────────────┐');
|
|
269
|
+
console.log('│ 🚀 Next Steps: │');
|
|
270
|
+
console.log('│ │');
|
|
271
|
+
console.log('│ 1. Edit .env.local and set: │');
|
|
272
|
+
console.log('│ SECURENOW_APPID=your-app-name │');
|
|
273
|
+
console.log('│ SECURENOW_INSTANCE=http://signoz:4318 │');
|
|
274
|
+
if (shouldCreateMiddleware) {
|
|
275
|
+
console.log('│ SECURENOW_CAPTURE_BODY=1 │');
|
|
276
|
+
}
|
|
277
|
+
console.log('│ │');
|
|
278
|
+
console.log('│ 2. Run your app: npm run dev │');
|
|
279
|
+
console.log('│ │');
|
|
280
|
+
console.log('│ 3. Check SigNoz for traces! │');
|
|
281
|
+
console.log('│ │');
|
|
282
|
+
if (shouldCreateMiddleware) {
|
|
283
|
+
console.log('│ 📝 Body capture enabled with auto-redaction │');
|
|
284
|
+
}
|
|
285
|
+
console.log('│ 📚 Full guide: npm docs securenow │');
|
|
286
|
+
console.log('└─────────────────────────────────────────────────┘\n');
|
|
287
|
+
|
|
288
|
+
rl.close();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.error('\n❌ Failed to create instrumentation file:', error.message);
|
|
293
|
+
console.log('💡 You can create it manually or run: npx securenow init');
|
|
294
|
+
rl.close();
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Run setup if this is a new installation (not being installed as a dependency of another package)
|
|
300
|
+
if (require.main === module || process.env.npm_config_global !== 'true') {
|
|
301
|
+
setup().catch(err => {
|
|
302
|
+
console.error('[securenow] Setup error:', err);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
module.exports = { setup };
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|