securenow 6.1.0 → 7.0.0-anas
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/app-config.js +130 -0
- package/cli/apps.js +24 -1
- package/cli/auth.js +26 -8
- package/cli/config.js +31 -2
- package/cli/diagnostics.js +2 -2
- package/cli.js +8 -5
- package/console-instrumentation.js +1 -1
- package/nextjs-auto-capture.js +3 -4
- package/nextjs-wrapper.js +2 -2
- package/nextjs.js +19 -20
- package/nuxt-server-plugin.mjs +22 -20
- package/package.json +7 -21
- package/postinstall.js +36 -8
- package/tracing.js +26 -14
package/app-config.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared app-configuration resolver.
|
|
5
|
+
*
|
|
6
|
+
* Used by both the SDK (tracing.js, nextjs.js, nuxt-server-plugin.mjs) and
|
|
7
|
+
* the CLI to answer three questions with a single source of truth:
|
|
8
|
+
*
|
|
9
|
+
* - Which app key routes this telemetry? (resolveAppKey)
|
|
10
|
+
* - What service.name label to show? (resolveAppId)
|
|
11
|
+
* - Which OTLP collector to hit? (resolveInstance)
|
|
12
|
+
*
|
|
13
|
+
* Resolution order (first non-empty wins):
|
|
14
|
+
*
|
|
15
|
+
* 1. Explicit environment variable
|
|
16
|
+
* (SECURENOW_API_KEY / SECURENOW_APPID / SECURENOW_INSTANCE)
|
|
17
|
+
* 2. Project-local credentials (./.securenow/credentials.json)
|
|
18
|
+
* 3. Global credentials (~/.securenow/credentials.json)
|
|
19
|
+
* 4. package.json#name (for appId only)
|
|
20
|
+
* 5. Hard default / null
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const os = require('os');
|
|
26
|
+
|
|
27
|
+
const FREE_TRIAL_INSTANCE = 'https://freetrial.securenow.ai:4318';
|
|
28
|
+
|
|
29
|
+
function readJsonSafe(filepath) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadLocalCredentials() {
|
|
38
|
+
try {
|
|
39
|
+
const cwd = typeof process !== 'undefined' && process.cwd ? process.cwd() : '.';
|
|
40
|
+
return readJsonSafe(path.join(cwd, '.securenow', 'credentials.json'));
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function loadGlobalCredentials() {
|
|
47
|
+
try {
|
|
48
|
+
return readJsonSafe(path.join(os.homedir(), '.securenow', 'credentials.json'));
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function loadCredentials() {
|
|
55
|
+
return loadLocalCredentials() || loadGlobalCredentials() || null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function loadPackageJsonName() {
|
|
59
|
+
try {
|
|
60
|
+
const cwd = typeof process !== 'undefined' && process.cwd ? process.cwd() : '.';
|
|
61
|
+
const pkg = readJsonSafe(path.join(cwd, 'package.json'));
|
|
62
|
+
if (pkg && typeof pkg.name === 'string' && pkg.name.trim()) {
|
|
63
|
+
return pkg.name.trim().replace(/^@[^/]+\//, '');
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function pick(value) {
|
|
70
|
+
if (value === undefined || value === null) return null;
|
|
71
|
+
const str = String(value).trim();
|
|
72
|
+
return str.length > 0 ? str : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveAppKey() {
|
|
76
|
+
const fromEnv =
|
|
77
|
+
pick(process.env.SECURENOW_API_KEY) ||
|
|
78
|
+
pick(process.env.securenow);
|
|
79
|
+
if (fromEnv) return fromEnv;
|
|
80
|
+
|
|
81
|
+
const creds = loadCredentials();
|
|
82
|
+
if (creds && creds.app && pick(creds.app.key)) return pick(creds.app.key);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolveAppId() {
|
|
87
|
+
const fromEnv =
|
|
88
|
+
pick(process.env.OTEL_SERVICE_NAME) ||
|
|
89
|
+
pick(process.env.SECURENOW_APPID);
|
|
90
|
+
if (fromEnv) return fromEnv;
|
|
91
|
+
|
|
92
|
+
const creds = loadCredentials();
|
|
93
|
+
if (creds && creds.app && pick(creds.app.id)) return pick(creds.app.id);
|
|
94
|
+
|
|
95
|
+
return loadPackageJsonName();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function resolveInstance() {
|
|
99
|
+
const fromEnv =
|
|
100
|
+
pick(process.env.SECURENOW_INSTANCE) ||
|
|
101
|
+
pick(process.env.securenow_instance) ||
|
|
102
|
+
pick(process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
|
|
103
|
+
if (fromEnv) return fromEnv.replace(/\/$/, '');
|
|
104
|
+
|
|
105
|
+
const creds = loadCredentials();
|
|
106
|
+
if (creds && creds.app && pick(creds.app.instance)) {
|
|
107
|
+
return pick(creds.app.instance).replace(/\/$/, '');
|
|
108
|
+
}
|
|
109
|
+
return FREE_TRIAL_INSTANCE;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resolveAll() {
|
|
113
|
+
return {
|
|
114
|
+
appKey: resolveAppKey(),
|
|
115
|
+
appId: resolveAppId(),
|
|
116
|
+
instance: resolveInstance(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
FREE_TRIAL_INSTANCE,
|
|
122
|
+
resolveAppKey,
|
|
123
|
+
resolveAppId,
|
|
124
|
+
resolveInstance,
|
|
125
|
+
resolveAll,
|
|
126
|
+
loadCredentials,
|
|
127
|
+
loadLocalCredentials,
|
|
128
|
+
loadGlobalCredentials,
|
|
129
|
+
loadPackageJsonName,
|
|
130
|
+
};
|
package/cli/apps.js
CHANGED
|
@@ -295,7 +295,30 @@ async function setDefault(args) {
|
|
|
295
295
|
process.exit(1);
|
|
296
296
|
}
|
|
297
297
|
config.setConfigValue('defaultApp', key);
|
|
298
|
-
|
|
298
|
+
|
|
299
|
+
// Mirror into credentials.json so the SDK picks this app up with zero env vars.
|
|
300
|
+
try {
|
|
301
|
+
requireAuth();
|
|
302
|
+
const appData = await api.get(`/applications/${key}`);
|
|
303
|
+
const app = appData.application || appData;
|
|
304
|
+
let inst = null;
|
|
305
|
+
if (app.instanceId) {
|
|
306
|
+
try {
|
|
307
|
+
const instData = await api.get(`/instances/${app.instanceId}`);
|
|
308
|
+
inst = instData.instance || null;
|
|
309
|
+
} catch {}
|
|
310
|
+
}
|
|
311
|
+
config.setApp({
|
|
312
|
+
key: app.key,
|
|
313
|
+
id: app.name || app.key,
|
|
314
|
+
instance: instanceUrl(inst),
|
|
315
|
+
});
|
|
316
|
+
ui.success(`Default application set to ${ui.c.bold(app.name || key)}`);
|
|
317
|
+
ui.info(`SDK will use ${ui.c.dim(instanceUrl(inst))} for telemetry`);
|
|
318
|
+
} catch {
|
|
319
|
+
// If we can't reach the API (offline / invalid key), still set the CLI default.
|
|
320
|
+
ui.success(`Default application set to ${ui.c.bold(key)}`);
|
|
321
|
+
}
|
|
299
322
|
}
|
|
300
323
|
|
|
301
324
|
function extractRootDomains(hosts) {
|
package/cli/auth.js
CHANGED
|
@@ -44,6 +44,7 @@ async function loginWithBrowser() {
|
|
|
44
44
|
|
|
45
45
|
return new Promise((resolve, reject) => {
|
|
46
46
|
let pendingToken = null;
|
|
47
|
+
let pendingApp = null;
|
|
47
48
|
|
|
48
49
|
const server = http.createServer((req, res) => {
|
|
49
50
|
const url = new URL(req.url, `http://127.0.0.1`);
|
|
@@ -52,6 +53,9 @@ async function loginWithBrowser() {
|
|
|
52
53
|
const token = url.searchParams.get('token');
|
|
53
54
|
const error = url.searchParams.get('error');
|
|
54
55
|
const returnedState = url.searchParams.get('state');
|
|
56
|
+
const appKey = url.searchParams.get('app_key');
|
|
57
|
+
const appId = url.searchParams.get('app_id');
|
|
58
|
+
const appInstance = url.searchParams.get('app_instance');
|
|
55
59
|
|
|
56
60
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
57
61
|
|
|
@@ -71,6 +75,13 @@ async function loginWithBrowser() {
|
|
|
71
75
|
|
|
72
76
|
if (token) {
|
|
73
77
|
pendingToken = token;
|
|
78
|
+
if (appKey || appId || appInstance) {
|
|
79
|
+
pendingApp = {
|
|
80
|
+
key: appKey || null,
|
|
81
|
+
id: appId || null,
|
|
82
|
+
instance: appInstance || null,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
74
85
|
const payload = decodeJwtPayload(token);
|
|
75
86
|
const email = payload?.email || 'unknown account';
|
|
76
87
|
const safeEmail = email.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
@@ -127,9 +138,11 @@ async function loginWithBrowser() {
|
|
|
127
138
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
128
139
|
res.end('{"ok":true}');
|
|
129
140
|
const token = pendingToken;
|
|
141
|
+
const app = pendingApp;
|
|
130
142
|
pendingToken = null;
|
|
143
|
+
pendingApp = null;
|
|
131
144
|
server.close();
|
|
132
|
-
resolve(token);
|
|
145
|
+
resolve({ token, app });
|
|
133
146
|
return;
|
|
134
147
|
}
|
|
135
148
|
|
|
@@ -183,7 +196,8 @@ async function loginWithToken(token) {
|
|
|
183
196
|
}
|
|
184
197
|
|
|
185
198
|
async function login(args, flags) {
|
|
186
|
-
|
|
199
|
+
// Default is project-local. Pass --global to save to ~/.securenow/ instead.
|
|
200
|
+
const local = flags.global ? false : true;
|
|
187
201
|
|
|
188
202
|
if (flags.token) {
|
|
189
203
|
const token = flags.token;
|
|
@@ -196,7 +210,7 @@ async function login(args, flags) {
|
|
|
196
210
|
if (local) config.ensureLocalGitignore();
|
|
197
211
|
console.log('');
|
|
198
212
|
ui.success(`Logged in as ${ui.c.bold(email)}`);
|
|
199
|
-
|
|
213
|
+
ui.info(local ? 'Credentials saved to project .securenow/ (local)' : 'Credentials saved to ~/.securenow/ (global)');
|
|
200
214
|
if (exp) {
|
|
201
215
|
const days = Math.ceil((exp - Date.now()) / (1000 * 60 * 60 * 24));
|
|
202
216
|
ui.info(`Session expires in ${days} days`);
|
|
@@ -205,16 +219,19 @@ async function login(args, flags) {
|
|
|
205
219
|
}
|
|
206
220
|
|
|
207
221
|
try {
|
|
208
|
-
const token = await loginWithBrowser();
|
|
222
|
+
const { token, app } = await loginWithBrowser();
|
|
209
223
|
const payload = decodeJwtPayload(token);
|
|
210
224
|
const email = payload?.email || 'unknown';
|
|
211
225
|
const exp = payload?.exp ? payload.exp * 1000 : null;
|
|
212
226
|
|
|
213
|
-
config.setAuth(token, email, exp, { local });
|
|
227
|
+
config.setAuth(token, email, exp, { local, app });
|
|
214
228
|
if (local) config.ensureLocalGitignore();
|
|
215
229
|
console.log('');
|
|
216
230
|
ui.success(`Logged in as ${ui.c.bold(email)}`);
|
|
217
|
-
|
|
231
|
+
ui.info(local ? 'Credentials saved to project .securenow/ (local)' : 'Credentials saved to ~/.securenow/ (global)');
|
|
232
|
+
if (app && app.id) {
|
|
233
|
+
ui.info(`Linked to app ${ui.c.bold(app.id)}${app.key ? ` (${ui.c.dim(app.key)})` : ''}`);
|
|
234
|
+
}
|
|
218
235
|
if (exp) {
|
|
219
236
|
const days = Math.ceil((exp - Date.now()) / (1000 * 60 * 60 * 24));
|
|
220
237
|
ui.info(`Session expires in ${days} days`);
|
|
@@ -235,7 +252,8 @@ async function login(args, flags) {
|
|
|
235
252
|
}
|
|
236
253
|
|
|
237
254
|
async function logout(args, flags) {
|
|
238
|
-
|
|
255
|
+
// Default: clear project-local. --global clears ~/.securenow/.
|
|
256
|
+
const local = !(flags && flags.global);
|
|
239
257
|
const creds = config.loadCredentials();
|
|
240
258
|
config.clearCredentials({ local });
|
|
241
259
|
if (creds.email) {
|
|
@@ -243,7 +261,7 @@ async function logout(args, flags) {
|
|
|
243
261
|
} else {
|
|
244
262
|
ui.success('Logged out');
|
|
245
263
|
}
|
|
246
|
-
|
|
264
|
+
ui.info(local ? 'Cleared project-local credentials (.securenow/)' : 'Cleared global credentials (~/.securenow/)');
|
|
247
265
|
}
|
|
248
266
|
|
|
249
267
|
async function whoami() {
|
package/cli/config.js
CHANGED
|
@@ -118,8 +118,35 @@ function getToken() {
|
|
|
118
118
|
return creds.token;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function setAuth(token, email, expiresAt, { local = false } = {}) {
|
|
122
|
-
|
|
121
|
+
function setAuth(token, email, expiresAt, { local = false, app = null } = {}) {
|
|
122
|
+
const payload = { token, email, expiresAt };
|
|
123
|
+
if (app && (app.key || app.id || app.instance)) {
|
|
124
|
+
payload.app = {
|
|
125
|
+
key: app.key || null,
|
|
126
|
+
id: app.id || null,
|
|
127
|
+
instance: app.instance || null,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
saveCredentials(payload, { local });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getApp() {
|
|
134
|
+
const creds = loadCredentials();
|
|
135
|
+
return creds && creds.app ? creds.app : null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function setApp(app, { local } = {}) {
|
|
139
|
+
const useLocal = local === true || (local == null && hasLocalCredentials());
|
|
140
|
+
const targetFile = useLocal ? LOCAL_CREDENTIALS_FILE : CREDENTIALS_FILE;
|
|
141
|
+
const existing = loadJSON(targetFile);
|
|
142
|
+
saveJSON(targetFile, {
|
|
143
|
+
...existing,
|
|
144
|
+
app: {
|
|
145
|
+
key: app.key || null,
|
|
146
|
+
id: app.id || null,
|
|
147
|
+
instance: app.instance || null,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
123
150
|
}
|
|
124
151
|
|
|
125
152
|
function ensureLocalGitignore() {
|
|
@@ -164,6 +191,8 @@ module.exports = {
|
|
|
164
191
|
clearCredentials,
|
|
165
192
|
getToken,
|
|
166
193
|
setAuth,
|
|
194
|
+
getApp,
|
|
195
|
+
setApp,
|
|
167
196
|
getAuthSource,
|
|
168
197
|
hasLocalCredentials,
|
|
169
198
|
ensureLocalGitignore,
|
package/cli/diagnostics.js
CHANGED
|
@@ -27,8 +27,8 @@ function resolvedConfig() {
|
|
|
27
27
|
const headers = process.env.OTEL_EXPORTER_OTLP_HEADERS || '';
|
|
28
28
|
const apiKey = process.env.SECURENOW_API_KEY || '';
|
|
29
29
|
const apiUrl = config.getApiUrl();
|
|
30
|
-
const loggingEnabled = process.env.SECURENOW_LOGGING_ENABLED
|
|
31
|
-
const captureBody = process.env.SECURENOW_CAPTURE_BODY
|
|
30
|
+
const loggingEnabled = !/^(0|false)$/i.test(String(process.env.SECURENOW_LOGGING_ENABLED ?? ''));
|
|
31
|
+
const captureBody = !/^(0|false)$/i.test(String(process.env.SECURENOW_CAPTURE_BODY ?? ''));
|
|
32
32
|
const firewallEnabled =
|
|
33
33
|
!!apiKey && process.env.SECURENOW_FIREWALL_ENABLED !== '0';
|
|
34
34
|
|
package/cli.js
CHANGED
|
@@ -54,15 +54,18 @@ const COMMANDS = {
|
|
|
54
54
|
run: (a, f) => require('./cli/init').init(a, f),
|
|
55
55
|
},
|
|
56
56
|
login: {
|
|
57
|
-
desc: 'Authenticate with SecureNow',
|
|
58
|
-
usage: 'securenow login [--token <TOKEN>] [--
|
|
59
|
-
flags: {
|
|
57
|
+
desc: 'Authenticate with SecureNow (saves to project .securenow/ by default)',
|
|
58
|
+
usage: 'securenow login [--token <TOKEN>] [--global]',
|
|
59
|
+
flags: {
|
|
60
|
+
token: 'Authenticate with a token directly',
|
|
61
|
+
global: 'Save credentials to ~/.securenow/ (shared across all projects)',
|
|
62
|
+
},
|
|
60
63
|
run: (a, f) => require('./cli/auth').login(a, f),
|
|
61
64
|
},
|
|
62
65
|
logout: {
|
|
63
66
|
desc: 'Clear stored credentials',
|
|
64
|
-
usage: 'securenow logout [--
|
|
65
|
-
flags: {
|
|
67
|
+
usage: 'securenow logout [--global]',
|
|
68
|
+
flags: { global: 'Clear global credentials (~/.securenow/) instead of project-local' },
|
|
66
69
|
run: (a, f) => require('./cli/auth').logout(a, f),
|
|
67
70
|
},
|
|
68
71
|
whoami: {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
const tracing = require('./tracing');
|
|
25
25
|
|
|
26
26
|
if (!tracing.isLoggingEnabled()) {
|
|
27
|
-
console.warn('[securenow] Console instrumentation loaded but logging is
|
|
27
|
+
console.warn('[securenow] Console instrumentation loaded but logging is disabled (SECURENOW_LOGGING_ENABLED=0).');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// Get a logger instance
|
package/nextjs-auto-capture.js
CHANGED
|
@@ -125,9 +125,8 @@ async function safeBodyCapture(request, span) {
|
|
|
125
125
|
* Check if body capture is enabled
|
|
126
126
|
*/
|
|
127
127
|
function isBodyCaptureEnabled() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return enabled;
|
|
128
|
+
// Opt-out default: set SECURENOW_CAPTURE_BODY=0 to disable.
|
|
129
|
+
return !/^(0|false)$/i.test(String(process.env.SECURENOW_CAPTURE_BODY ?? ''));
|
|
131
130
|
}
|
|
132
131
|
|
|
133
132
|
/**
|
|
@@ -184,7 +183,7 @@ if (isBodyCaptureEnabled()) {
|
|
|
184
183
|
console.warn('[securenow] 💡 Body capture disabled. Use manual approach if needed.');
|
|
185
184
|
}
|
|
186
185
|
} else {
|
|
187
|
-
console.log('[securenow] 📝 Automatic body capture: DISABLED (
|
|
186
|
+
console.log('[securenow] 📝 Automatic body capture: DISABLED (SECURENOW_CAPTURE_BODY=0)');
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
module.exports = {
|
package/nextjs-wrapper.js
CHANGED
|
@@ -49,8 +49,8 @@ function redactSensitiveData(obj, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
|
|
|
49
49
|
* Capture body from Request object (clone to avoid consuming)
|
|
50
50
|
*/
|
|
51
51
|
async function captureRequestBody(request) {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// Opt-out default: set SECURENOW_CAPTURE_BODY=0 to disable.
|
|
53
|
+
const captureBody = !/^(0|false)$/i.test(String(process.env.SECURENOW_CAPTURE_BODY ?? ''));
|
|
54
54
|
|
|
55
55
|
if (!captureBody) return;
|
|
56
56
|
if (!['POST', 'PUT', 'PATCH'].includes(request.method)) return;
|
package/nextjs.js
CHANGED
|
@@ -144,13 +144,11 @@ function registerSecureNow(options = {}) {
|
|
|
144
144
|
console.log('[securenow] Next.js integration loading (pid=%d)', process.pid);
|
|
145
145
|
|
|
146
146
|
// -------- Configuration --------
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
).trim().replace(/^['"]|['"]$/g, '');
|
|
153
|
-
|
|
147
|
+
// Resolution order: explicit options → env → .securenow/credentials.json → package.json#name
|
|
148
|
+
const appConfig = require('./app-config');
|
|
149
|
+
const resolvedApp = appConfig.resolveAll();
|
|
150
|
+
|
|
151
|
+
const rawBase = (options.serviceName || resolvedApp.appId || '').trim().replace(/^['"]|['"]$/g, '');
|
|
154
152
|
const baseName = rawBase || null;
|
|
155
153
|
const noUuid = options.noUuid ?? (String(env('SECURENOW_NO_UUID')) === '1' || String(env('SECURENOW_NO_UUID')).toLowerCase() === 'true');
|
|
156
154
|
|
|
@@ -160,17 +158,18 @@ function registerSecureNow(options = {}) {
|
|
|
160
158
|
serviceName = noUuid ? baseName : `${baseName}-${randomUUID()}`;
|
|
161
159
|
} else {
|
|
162
160
|
serviceName = `nextjs-app-${randomUUID()}`;
|
|
163
|
-
console.warn('[securenow] ⚠️ No
|
|
164
|
-
console.warn('[securenow] 💡
|
|
161
|
+
console.warn('[securenow] ⚠️ No app identity resolved. Using fallback: %s', serviceName);
|
|
162
|
+
console.warn('[securenow] 💡 Run `npx securenow login` or set SECURENOW_APPID in .env.local');
|
|
165
163
|
}
|
|
166
164
|
|
|
167
165
|
// -------- Endpoint Configuration --------
|
|
168
|
-
const endpointBase = (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
166
|
+
const endpointBase = (options.endpoint || resolvedApp.instance).replace(/\/$/, '');
|
|
167
|
+
|
|
168
|
+
// If credentials file provided an app key, surface it via x-api-key for collector routing.
|
|
169
|
+
if (resolvedApp.appKey && !env('OTEL_EXPORTER_OTLP_HEADERS') && !env('SECURENOW_API_KEY')) {
|
|
170
|
+
process.env.SECURENOW_API_KEY = resolvedApp.appKey;
|
|
171
|
+
process.env.OTEL_EXPORTER_OTLP_HEADERS = `x-api-key=${resolvedApp.appKey}`;
|
|
172
|
+
}
|
|
174
173
|
|
|
175
174
|
const tracesUrl = env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
|
|
176
175
|
const logsUrl = env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`;
|
|
@@ -182,9 +181,8 @@ function registerSecureNow(options = {}) {
|
|
|
182
181
|
console.log('[securenow] 🚀 Next.js App → service.name=%s', serviceName);
|
|
183
182
|
|
|
184
183
|
// -------- Body Capture Configuration --------
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
options.captureBody === true;
|
|
184
|
+
// Opt-out default: set SECURENOW_CAPTURE_BODY=0 (or options.captureBody=false) to disable.
|
|
185
|
+
const captureBody = options.captureBody ?? !/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_BODY') ?? ''));
|
|
188
186
|
const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
|
|
189
187
|
const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
190
188
|
const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
|
|
@@ -461,7 +459,8 @@ function registerSecureNow(options = {}) {
|
|
|
461
459
|
console.log('[securenow] 🎯 Vanilla SDK initialized for self-hosted environment');
|
|
462
460
|
|
|
463
461
|
// -------- Logging (self-hosted only) --------
|
|
464
|
-
|
|
462
|
+
// Opt-out default: set SECURENOW_LOGGING_ENABLED=0 to disable.
|
|
463
|
+
const loggingEnabled = !/^(0|false)$/i.test(String(env('SECURENOW_LOGGING_ENABLED') ?? ''));
|
|
465
464
|
if (loggingEnabled) {
|
|
466
465
|
try {
|
|
467
466
|
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
|
|
@@ -565,7 +564,7 @@ function registerSecureNow(options = {}) {
|
|
|
565
564
|
console.warn('[securenow] ⚠️ Logging setup failed (missing @opentelemetry/exporter-logs-otlp-http or @opentelemetry/sdk-logs):', e.message);
|
|
566
565
|
}
|
|
567
566
|
} else {
|
|
568
|
-
console.log('[securenow] 📋 Logging: DISABLED (
|
|
567
|
+
console.log('[securenow] 📋 Logging: DISABLED (SECURENOW_LOGGING_ENABLED=0)');
|
|
569
568
|
}
|
|
570
569
|
}
|
|
571
570
|
|
package/nuxt-server-plugin.mjs
CHANGED
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
SpanStatusCode,
|
|
19
19
|
} from '@opentelemetry/api';
|
|
20
20
|
import { v4 as uuidv4 } from 'uuid';
|
|
21
|
+
import { createRequire } from 'node:module';
|
|
22
|
+
|
|
23
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
24
|
+
const appConfig = nodeRequire('./app-config');
|
|
21
25
|
|
|
22
26
|
// ── Helpers ──
|
|
23
27
|
|
|
@@ -73,14 +77,11 @@ function getRuntimeOptions() {
|
|
|
73
77
|
export default defineNitroPlugin((nitroApp) => {
|
|
74
78
|
const opts = getRuntimeOptions();
|
|
75
79
|
|
|
76
|
-
//
|
|
77
|
-
const
|
|
78
|
-
opts.serviceName ||
|
|
79
|
-
env('OTEL_SERVICE_NAME') ||
|
|
80
|
-
env('SECURENOW_APPID') ||
|
|
81
|
-
''
|
|
82
|
-
).trim().replace(/^['"]|['"]$/g, '');
|
|
80
|
+
// Resolution order: opts → env → .securenow/credentials.json → package.json#name
|
|
81
|
+
const resolvedApp = appConfig.resolveAll();
|
|
83
82
|
|
|
83
|
+
// ── Naming ──
|
|
84
|
+
const rawBase = (opts.serviceName || resolvedApp.appId || '').trim().replace(/^['"]|['"]$/g, '');
|
|
84
85
|
const baseName = rawBase || null;
|
|
85
86
|
const noUuid =
|
|
86
87
|
opts.noUuid ??
|
|
@@ -93,23 +94,24 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
93
94
|
} else {
|
|
94
95
|
serviceName = `nuxt-app-${uuidv4()}`;
|
|
95
96
|
console.warn(
|
|
96
|
-
'[securenow] ⚠️ No
|
|
97
|
+
'[securenow] ⚠️ No app identity resolved. Using fallback: %s',
|
|
97
98
|
serviceName,
|
|
98
99
|
);
|
|
99
100
|
console.warn(
|
|
100
|
-
'[securenow] 💡
|
|
101
|
+
'[securenow] 💡 Run `npx securenow login` or set SECURENOW_APPID in .env',
|
|
101
102
|
);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
const serviceInstanceId = `${baseName || 'securenow'}-${uuidv4()}`;
|
|
105
106
|
|
|
106
107
|
// ── Endpoints ──
|
|
107
|
-
const endpointBase = (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
const endpointBase = (opts.endpoint || resolvedApp.instance).replace(/\/$/, '');
|
|
109
|
+
|
|
110
|
+
// Surface credentials-file app key as x-api-key for collector routing.
|
|
111
|
+
if (resolvedApp.appKey && !env('OTEL_EXPORTER_OTLP_HEADERS') && !env('SECURENOW_API_KEY')) {
|
|
112
|
+
process.env.SECURENOW_API_KEY = resolvedApp.appKey;
|
|
113
|
+
process.env.OTEL_EXPORTER_OTLP_HEADERS = `x-api-key=${resolvedApp.appKey}`;
|
|
114
|
+
}
|
|
113
115
|
|
|
114
116
|
const tracesUrl =
|
|
115
117
|
env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
|
|
@@ -129,10 +131,10 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
129
131
|
});
|
|
130
132
|
|
|
131
133
|
// ── Body capture config ──
|
|
134
|
+
// Opt-out default: set SECURENOW_CAPTURE_BODY=0 (or opts.captureBody=false) to disable.
|
|
132
135
|
const captureBody =
|
|
133
136
|
opts.captureBody ??
|
|
134
|
-
(String(env('SECURENOW_CAPTURE_BODY')
|
|
135
|
-
String(env('SECURENOW_CAPTURE_BODY')).toLowerCase() === 'true');
|
|
137
|
+
!/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_BODY') ?? ''));
|
|
136
138
|
const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
|
|
137
139
|
const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '')
|
|
138
140
|
.split(',')
|
|
@@ -238,10 +240,10 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
238
240
|
);
|
|
239
241
|
|
|
240
242
|
// ── Logging ──
|
|
243
|
+
// Opt-out default: set SECURENOW_LOGGING_ENABLED=0 (or opts.logging=false) to disable.
|
|
241
244
|
const loggingEnabled =
|
|
242
245
|
opts.logging ??
|
|
243
|
-
(String(env('SECURENOW_LOGGING_ENABLED')
|
|
244
|
-
String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() === 'true');
|
|
246
|
+
!/^(0|false)$/i.test(String(env('SECURENOW_LOGGING_ENABLED') ?? ''));
|
|
245
247
|
|
|
246
248
|
let loggerProvider = null;
|
|
247
249
|
|
|
@@ -302,7 +304,7 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
302
304
|
}
|
|
303
305
|
} else {
|
|
304
306
|
console.log(
|
|
305
|
-
'[securenow] 📋 Logging: DISABLED (
|
|
307
|
+
'[securenow] 📋 Logging: DISABLED (SECURENOW_LOGGING_ENABLED=0)',
|
|
306
308
|
);
|
|
307
309
|
}
|
|
308
310
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securenow",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-anas",
|
|
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",
|
|
@@ -94,6 +94,9 @@
|
|
|
94
94
|
"./web-vite": {
|
|
95
95
|
"import": "./web-vite.mjs",
|
|
96
96
|
"default": "./web-vite.mjs"
|
|
97
|
+
},
|
|
98
|
+
"./app-config": {
|
|
99
|
+
"default": "./app-config.js"
|
|
97
100
|
}
|
|
98
101
|
},
|
|
99
102
|
"files": [
|
|
@@ -127,6 +130,7 @@
|
|
|
127
130
|
"postinstall.js",
|
|
128
131
|
"register-vite.js",
|
|
129
132
|
"web-vite.mjs",
|
|
133
|
+
"app-config.js",
|
|
130
134
|
"examples/",
|
|
131
135
|
"docs/",
|
|
132
136
|
"README.md",
|
|
@@ -153,29 +157,11 @@
|
|
|
153
157
|
"@opentelemetry/sdk-node": "0.47.0",
|
|
154
158
|
"@opentelemetry/sdk-trace-web": "1.20.0",
|
|
155
159
|
"@opentelemetry/semantic-conventions": "1.20.0",
|
|
156
|
-
"@vercel/otel": "^1.14.0",
|
|
157
160
|
"dotenv": "^17.2.1",
|
|
158
161
|
"uuid": "^9.0.0"
|
|
159
162
|
},
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"nuxt": ">=3.0.0",
|
|
163
|
-
"@aws-sdk/client-wafv2": ">=3.0.0",
|
|
164
|
-
"@google-cloud/compute": ">=4.0.0"
|
|
165
|
-
},
|
|
166
|
-
"peerDependenciesMeta": {
|
|
167
|
-
"next": {
|
|
168
|
-
"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
|
-
}
|
|
163
|
+
"optionalDependencies": {
|
|
164
|
+
"@vercel/otel": "^1.14.0"
|
|
179
165
|
},
|
|
180
166
|
"overrides": {
|
|
181
167
|
"@opentelemetry/api": "1.7.0",
|
package/postinstall.js
CHANGED
|
@@ -11,6 +11,35 @@ const fs = require('fs');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const readline = require('readline');
|
|
13
13
|
|
|
14
|
+
// Make sure `.securenow/` is in the project's .gitignore so credentials never get committed.
|
|
15
|
+
function ensureGitignore() {
|
|
16
|
+
try {
|
|
17
|
+
// Skip if we're not in an npm install of a user project
|
|
18
|
+
// (e.g., securenow's own CI, or nested install under another node_modules).
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
if (!fs.existsSync(path.join(cwd, 'package.json'))) return;
|
|
21
|
+
if (cwd.includes(`${path.sep}node_modules${path.sep}`)) return;
|
|
22
|
+
|
|
23
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
24
|
+
const entry = '.securenow/';
|
|
25
|
+
const header = '# SecureNow local credentials';
|
|
26
|
+
|
|
27
|
+
if (fs.existsSync(gitignorePath)) {
|
|
28
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
29
|
+
const alreadyListed = content.split('\n').some((line) => line.trim() === entry);
|
|
30
|
+
if (!alreadyListed) {
|
|
31
|
+
const prefix = content.endsWith('\n') ? '' : '\n';
|
|
32
|
+
fs.appendFileSync(gitignorePath, `${prefix}\n${header}\n${entry}\n`);
|
|
33
|
+
}
|
|
34
|
+
} else if (fs.existsSync(path.join(cwd, '.git'))) {
|
|
35
|
+
// Only create a new .gitignore if this is actually a git repo.
|
|
36
|
+
fs.writeFileSync(gitignorePath, `${header}\n${entry}\n`);
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Non-fatal
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
14
43
|
// Check if we're in a Next.js project
|
|
15
44
|
function isNextJsProject() {
|
|
16
45
|
try {
|
|
@@ -81,9 +110,8 @@ export function register() {
|
|
|
81
110
|
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
82
111
|
* OTEL_LOG_LEVEL=info
|
|
83
112
|
*
|
|
84
|
-
* Optional:
|
|
85
|
-
* SECURENOW_CAPTURE_BODY=
|
|
86
|
-
* (Also create middleware.ts to activate - run: npx securenow init)
|
|
113
|
+
* Optional: Disable request body capture (enabled by default)
|
|
114
|
+
* SECURENOW_CAPTURE_BODY=0
|
|
87
115
|
*/
|
|
88
116
|
`;
|
|
89
117
|
|
|
@@ -160,8 +188,8 @@ SECURENOW_INSTANCE=http://your-otlp-backend:4318
|
|
|
160
188
|
# Optional: Log level (debug|info|warn|error)
|
|
161
189
|
# OTEL_LOG_LEVEL=info
|
|
162
190
|
|
|
163
|
-
# Optional:
|
|
164
|
-
# SECURENOW_CAPTURE_BODY=
|
|
191
|
+
# Optional: Disable request body capture (enabled by default)
|
|
192
|
+
# SECURENOW_CAPTURE_BODY=0
|
|
165
193
|
# SECURENOW_MAX_BODY_SIZE=10240
|
|
166
194
|
# SECURENOW_SENSITIVE_FIELDS=email,phone
|
|
167
195
|
`;
|
|
@@ -176,6 +204,9 @@ function isTypeScriptProject() {
|
|
|
176
204
|
|
|
177
205
|
// Main setup function
|
|
178
206
|
async function setup() {
|
|
207
|
+
// Always make sure .securenow/ is gitignored (cheap, non-destructive).
|
|
208
|
+
ensureGitignore();
|
|
209
|
+
|
|
179
210
|
// Skip if not in Next.js project
|
|
180
211
|
if (!isNextJsProject()) {
|
|
181
212
|
console.log('[securenow] Not a Next.js project, skipping auto-setup');
|
|
@@ -271,9 +302,6 @@ async function setup() {
|
|
|
271
302
|
console.log('│ 1. Edit .env.local and set: │');
|
|
272
303
|
console.log('│ SECURENOW_APPID=your-app-name │');
|
|
273
304
|
console.log('│ SECURENOW_INSTANCE=http://your-otlp-backend:4318 │');
|
|
274
|
-
if (shouldCreateMiddleware) {
|
|
275
|
-
console.log('│ SECURENOW_CAPTURE_BODY=1 │');
|
|
276
|
-
}
|
|
277
305
|
console.log('│ │');
|
|
278
306
|
console.log('│ 2. Run your app: npm run dev │');
|
|
279
307
|
console.log('│ │');
|
package/tracing.js
CHANGED
|
@@ -268,14 +268,25 @@ const diagLevel = (env('OTEL_LOG_LEVEL') || '').toLowerCase();
|
|
|
268
268
|
console.log('[securenow] preload loaded pid=%d', process.pid);
|
|
269
269
|
})();
|
|
270
270
|
|
|
271
|
-
// -------- endpoints --------
|
|
272
|
-
|
|
271
|
+
// -------- endpoints & app resolution --------
|
|
272
|
+
// Resolution order for endpoint/appId/apiKey: env → .securenow/credentials.json → package.json#name → defaults.
|
|
273
|
+
const appConfig = require('./app-config');
|
|
274
|
+
const resolvedApp = appConfig.resolveAll();
|
|
275
|
+
|
|
276
|
+
const endpointBase = resolvedApp.instance.replace(/\/$/, '');
|
|
273
277
|
const tracesUrl = env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
|
|
274
278
|
const logsUrl = env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`;
|
|
279
|
+
|
|
280
|
+
// If the credentials file provided an app key and no OTLP headers are set,
|
|
281
|
+
// surface it as x-api-key so the collector can route telemetry to the right app bucket.
|
|
282
|
+
if (resolvedApp.appKey && !env('OTEL_EXPORTER_OTLP_HEADERS') && !env('SECURENOW_API_KEY')) {
|
|
283
|
+
process.env.SECURENOW_API_KEY = resolvedApp.appKey;
|
|
284
|
+
process.env.OTEL_EXPORTER_OTLP_HEADERS = `x-api-key=${resolvedApp.appKey}`;
|
|
285
|
+
}
|
|
275
286
|
const headers = parseHeaders(env('OTEL_EXPORTER_OTLP_HEADERS'));
|
|
276
287
|
|
|
277
288
|
// -------- naming rules --------
|
|
278
|
-
const rawBase = (
|
|
289
|
+
const rawBase = (resolvedApp.appId || '').trim().replace(/^['"]|['"]$/g, '');
|
|
279
290
|
const baseName = rawBase || null;
|
|
280
291
|
const noUuid = String(env('SECURENOW_NO_UUID')) === '1' || String(env('SECURENOW_NO_UUID')).toLowerCase() === 'true';
|
|
281
292
|
const strict = String(env('SECURENOW_STRICT')) === '1' || String(env('SECURENOW_STRICT')).toLowerCase() === 'true';
|
|
@@ -302,12 +313,11 @@ const instancePrefix = baseName || 'securenow';
|
|
|
302
313
|
const serviceInstanceId = `${instancePrefix}-${uuidv4()}`;
|
|
303
314
|
|
|
304
315
|
// Loud line per worker to prove what was used
|
|
305
|
-
console.log('[securenow] pid=%d
|
|
316
|
+
console.log('[securenow] pid=%d appId=%s instance=%s apiKey=%s → service.name=%s instance.id=%s',
|
|
306
317
|
process.pid,
|
|
307
|
-
JSON.stringify(
|
|
308
|
-
JSON.stringify(
|
|
309
|
-
|
|
310
|
-
JSON.stringify(env('SECURENOW_STRICT')),
|
|
318
|
+
JSON.stringify(baseName),
|
|
319
|
+
JSON.stringify(endpointBase),
|
|
320
|
+
resolvedApp.appKey ? 'set' : 'none',
|
|
311
321
|
serviceName,
|
|
312
322
|
serviceInstanceId
|
|
313
323
|
);
|
|
@@ -319,12 +329,13 @@ for (const n of (env('SECURENOW_DISABLE_INSTRUMENTATIONS') || '').split(',').map
|
|
|
319
329
|
}
|
|
320
330
|
|
|
321
331
|
// -------- Body Capture Configuration --------
|
|
322
|
-
|
|
332
|
+
// Opt-out defaults: set =0 or =false to disable.
|
|
333
|
+
const captureBody = !/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_BODY') ?? ''));
|
|
323
334
|
const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
|
|
324
335
|
const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
325
336
|
const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
|
|
326
337
|
|
|
327
|
-
const captureMultipart = String(env('SECURENOW_CAPTURE_MULTIPART')
|
|
338
|
+
const captureMultipart = !/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_MULTIPART') ?? ''));
|
|
328
339
|
|
|
329
340
|
// -------- Trusted proxy IP resolution --------
|
|
330
341
|
const { resolveClientIp, isFromTrustedProxy, LOOPBACK_RE } = require('./resolve-ip');
|
|
@@ -445,7 +456,7 @@ const httpInstrumentation = new HttpInstrumentation({
|
|
|
445
456
|
} else {
|
|
446
457
|
span.setAttribute('http.request.body', '[MULTIPART - NOT CAPTURED]');
|
|
447
458
|
span.setAttribute('http.request.body.type', 'multipart');
|
|
448
|
-
span.setAttribute('http.request.body.note', '
|
|
459
|
+
span.setAttribute('http.request.body.note', 'Multipart capture disabled (SECURENOW_CAPTURE_MULTIPART=0)');
|
|
449
460
|
}
|
|
450
461
|
}
|
|
451
462
|
}
|
|
@@ -456,7 +467,8 @@ const httpInstrumentation = new HttpInstrumentation({
|
|
|
456
467
|
});
|
|
457
468
|
|
|
458
469
|
// -------- Logging Configuration --------
|
|
459
|
-
|
|
470
|
+
// Opt-out default: set =0 or =false to disable.
|
|
471
|
+
const loggingEnabled = !/^(0|false)$/i.test(String(env('SECURENOW_LOGGING_ENABLED') ?? ''));
|
|
460
472
|
|
|
461
473
|
// Create shared resource for both traces and logs
|
|
462
474
|
const sharedResource = new Resource({
|
|
@@ -572,7 +584,7 @@ const sdk = new NodeSDK({
|
|
|
572
584
|
if (loggingEnabled) {
|
|
573
585
|
console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
|
|
574
586
|
} else {
|
|
575
|
-
console.log('[securenow] 📋 Logging: DISABLED (
|
|
587
|
+
console.log('[securenow] 📋 Logging: DISABLED (SECURENOW_LOGGING_ENABLED=0)');
|
|
576
588
|
}
|
|
577
589
|
if (captureBody) {
|
|
578
590
|
console.log('[securenow] 📝 Request body capture: ENABLED (max: %d bytes, redacting %d sensitive fields)', maxBodySize, allSensitiveFields.length);
|
|
@@ -636,7 +648,7 @@ module.exports = {
|
|
|
636
648
|
loggerProvider,
|
|
637
649
|
getLogger: (name = 'default', version = '1.0.0') => {
|
|
638
650
|
if (!loggerProvider) {
|
|
639
|
-
console.warn('[securenow] Logging is
|
|
651
|
+
console.warn('[securenow] Logging is disabled (SECURENOW_LOGGING_ENABLED=0). Remove the override to enable.');
|
|
640
652
|
return null;
|
|
641
653
|
}
|
|
642
654
|
return loggerProvider.getLogger(name, version);
|