securenow 7.7.14 → 7.7.16
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/NPM_README.md +65 -121
- package/README.md +19 -24
- package/SKILL-API.md +491 -490
- package/SKILL-CLI.md +8 -8
- package/app-config.js +146 -43
- package/cli/apps.js +589 -597
- package/cli/auth.js +1 -3
- package/cli/config.js +37 -9
- package/cli/credentials.js +1 -1
- package/cli/diagnostics.js +40 -10
- package/cli/init.js +1 -0
- package/firewall-only.js +1 -0
- package/firewall.js +62 -10
- package/free-trial-banner.js +2 -2
- package/mcp/catalog.js +2 -2
- package/nextjs.d.ts +67 -63
- package/nextjs.js +93 -52
- package/nuxt-server-plugin.mjs +7 -11
- package/nuxt.d.ts +42 -38
- package/nuxt.mjs +1 -1
- package/package.json +1 -1
- package/tracing.d.ts +2 -1
- package/tracing.js +75 -57
- package/web-vite.mjs +105 -15
package/nextjs.js
CHANGED
|
@@ -35,8 +35,6 @@ const appConfig = require('./app-config');
|
|
|
35
35
|
const { resolveClientIpWithDetails } = require('./resolve-ip');
|
|
36
36
|
const otelResources = require('@opentelemetry/resources');
|
|
37
37
|
|
|
38
|
-
const env = appConfig.env;
|
|
39
|
-
|
|
40
38
|
let isRegistered = false;
|
|
41
39
|
|
|
42
40
|
function requireRuntimeModule(name) {
|
|
@@ -126,7 +124,7 @@ function redactGraphQLQuery(query, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
|
|
|
126
124
|
* Register SecureNow OpenTelemetry for Next.js using @vercel/otel
|
|
127
125
|
* @param {Object} options - Optional configuration
|
|
128
126
|
* @param {string} options.serviceName - Service name (defaults to .securenow/credentials.json app.key/app.name)
|
|
129
|
-
* @param {string} options.endpoint -
|
|
127
|
+
* @param {string} options.endpoint - Advanced OTLP endpoint override (defaults to the SecureNow ingest gateway)
|
|
130
128
|
* @param {string} options.environment - deployment.environment override (defaults to config.runtime.deploymentEnvironment)
|
|
131
129
|
* @param {boolean} options.noUuid - Don't append UUID to service name
|
|
132
130
|
*/
|
|
@@ -144,24 +142,26 @@ function registerSecureNow(options = {}) {
|
|
|
144
142
|
}
|
|
145
143
|
|
|
146
144
|
// Detect environment outside try block for error handling
|
|
147
|
-
const isVercel = !!(env
|
|
145
|
+
const isVercel = !!(process.env.VERCEL || process.env.VERCEL_ENV || process.env.VERCEL_URL);
|
|
148
146
|
let deploymentEnvironment = appConfig.resolveDeploymentEnvironment();
|
|
149
147
|
|
|
150
148
|
try {
|
|
151
149
|
console.log('[securenow] Next.js integration loading (pid=%d)', process.pid);
|
|
152
150
|
|
|
153
151
|
// -------- Configuration --------
|
|
154
|
-
// Resolution order: explicit options -> .securenow/credentials.json ->
|
|
152
|
+
// Resolution order: explicit options -> .securenow/credentials.json -> package.json#name.
|
|
153
|
+
// Telemetry goes to the stable ingest gateway by default; the API gateway
|
|
154
|
+
// routes by app.key to the dashboard-selected instance.
|
|
155
155
|
const resolvedApp = appConfig.resolveAll();
|
|
156
156
|
|
|
157
157
|
const rawBase = (options.serviceName || resolvedApp.appId || '').trim().replace(/^['"]|['"]$/g, '');
|
|
158
158
|
const baseName = rawBase || null;
|
|
159
159
|
// Default: auto-disable suffix when logged in (appId is the routing UUID
|
|
160
|
-
// and the dashboard does exact match). opts.noUuid or
|
|
161
|
-
// override.
|
|
160
|
+
// and the dashboard does exact match). opts.noUuid or config.runtime.noUuid
|
|
161
|
+
// can still override.
|
|
162
162
|
const noUuid = appConfig.resolveNoUuid({ noUuid: options.noUuid });
|
|
163
163
|
deploymentEnvironment = appConfig.normalizeDeploymentEnvironment(
|
|
164
|
-
options.environment || resolvedApp.deploymentEnvironment
|
|
164
|
+
options.environment || resolvedApp.deploymentEnvironment
|
|
165
165
|
);
|
|
166
166
|
|
|
167
167
|
// service.name
|
|
@@ -170,7 +170,7 @@ function registerSecureNow(options = {}) {
|
|
|
170
170
|
serviceName = noUuid ? baseName : `${baseName}-${randomUUID()}`;
|
|
171
171
|
} else {
|
|
172
172
|
serviceName = `nextjs-app-${randomUUID()}`;
|
|
173
|
-
console.warn('[securenow]
|
|
173
|
+
console.warn('[securenow] No app identity resolved. Using fallback: %s', serviceName);
|
|
174
174
|
console.warn('[securenow] Run `npx securenow login` and `npx securenow init` to write .securenow/credentials.json');
|
|
175
175
|
}
|
|
176
176
|
|
|
@@ -180,24 +180,32 @@ function registerSecureNow(options = {}) {
|
|
|
180
180
|
const tracesUrl = resolvedEndpoints.tracesUrl;
|
|
181
181
|
const logsUrl = resolvedEndpoints.logsUrl;
|
|
182
182
|
const headers = resolvedEndpoints.headers;
|
|
183
|
+
const otelLogLevel = String(appConfig.configValue('otel.logLevel', '') || '').toLowerCase();
|
|
184
|
+
const isDevelopmentRuntime = process.env.NODE_ENV === 'development';
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
186
|
+
// @vercel/otel still reads OTel process variables internally. These are
|
|
187
|
+
// derived from credentials JSON; customers do not set them.
|
|
188
|
+
if (isVercel) {
|
|
189
|
+
if (!process.env.OTEL_SERVICE_NAME) process.env.OTEL_SERVICE_NAME = serviceName;
|
|
190
|
+
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) process.env.OTEL_EXPORTER_OTLP_ENDPOINT = endpointBase;
|
|
191
|
+
if (!process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = tracesUrl;
|
|
192
|
+
const headerString = appConfig.headersToString(headers);
|
|
193
|
+
if (headerString && !process.env.OTEL_EXPORTER_OTLP_HEADERS) process.env.OTEL_EXPORTER_OTLP_HEADERS = headerString;
|
|
194
|
+
}
|
|
187
195
|
|
|
188
196
|
console.log('[securenow] Next.js App -> service.name=%s', serviceName);
|
|
189
197
|
console.log('[securenow] Environment: %s', deploymentEnvironment);
|
|
190
198
|
|
|
191
199
|
// -------- Body Capture Configuration --------
|
|
192
200
|
// Opt-out default: set config.capture.body=false (or options.captureBody=false) to disable.
|
|
193
|
-
const captureBody = options.captureBody ??
|
|
194
|
-
const maxBodySize =
|
|
195
|
-
const customSensitiveFields = (
|
|
201
|
+
const captureBody = options.captureBody ?? appConfig.boolConfig('capture.body', true);
|
|
202
|
+
const maxBodySize = appConfig.numberConfig('capture.maxBodySize', 10240, 1024);
|
|
203
|
+
const customSensitiveFields = appConfig.listConfig('capture.sensitiveFields');
|
|
196
204
|
const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
|
|
197
205
|
|
|
198
206
|
// -------- Log environment detection --------
|
|
199
207
|
if (!isVercel) {
|
|
200
|
-
console.log('[securenow]
|
|
208
|
+
console.log('[securenow] Self-hosted environment detected (EC2/PM2) - using vanilla SDK');
|
|
201
209
|
}
|
|
202
210
|
|
|
203
211
|
// -------- Use different initialization based on environment --------
|
|
@@ -331,8 +339,8 @@ function registerSecureNow(options = {}) {
|
|
|
331
339
|
}
|
|
332
340
|
|
|
333
341
|
// Debug log in development
|
|
334
|
-
if (
|
|
335
|
-
console.log('[securenow]
|
|
342
|
+
if (isDevelopmentRuntime || otelLogLevel === 'debug') {
|
|
343
|
+
console.log('[securenow] Captured IP: %s (from: %s)',
|
|
336
344
|
primaryIp,
|
|
337
345
|
ipDetails.source || 'unknown'
|
|
338
346
|
);
|
|
@@ -345,8 +353,8 @@ function registerSecureNow(options = {}) {
|
|
|
345
353
|
|
|
346
354
|
} catch (error) {
|
|
347
355
|
// Silently fail to not break the request
|
|
348
|
-
if (
|
|
349
|
-
console.error('[securenow]
|
|
356
|
+
if (otelLogLevel === 'debug') {
|
|
357
|
+
console.error('[securenow] Error in requestHook:', error.message);
|
|
350
358
|
}
|
|
351
359
|
}
|
|
352
360
|
},
|
|
@@ -370,11 +378,11 @@ function registerSecureNow(options = {}) {
|
|
|
370
378
|
// the remote end (ECONNRESET / "socket hang up"). These transient errors
|
|
371
379
|
// sometimes escape as unhandled exceptions or rejections. We catch them
|
|
372
380
|
// here and log at debug level instead of crashing the host app.
|
|
373
|
-
const _TRANSIENT_CODES = new Set(['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EPIPE', 'EAI_AGAIN']);
|
|
381
|
+
const _TRANSIENT_CODES = new Set(['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EPIPE', 'EAI_AGAIN', 'ENOTFOUND']);
|
|
374
382
|
function _isOtlpTransientError(err) {
|
|
375
383
|
if (!err) return false;
|
|
376
384
|
if (_TRANSIENT_CODES.has(err.code)) return true;
|
|
377
|
-
if (typeof err.message === 'string' && /socket hang up|ECONNRESET
|
|
385
|
+
if (typeof err.message === 'string' && /socket hang up|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|timed out/i.test(err.message)) return true;
|
|
378
386
|
return false;
|
|
379
387
|
}
|
|
380
388
|
function _looksLikeOtlpStack(err) {
|
|
@@ -383,21 +391,53 @@ function registerSecureNow(options = {}) {
|
|
|
383
391
|
return /OTLPTraceExporter|OTLPLogExporter|otlp|exporter.*http|BatchSpanProcessor|BatchLogRecordProcessor/i.test(s)
|
|
384
392
|
|| /node:_http_client|ClientRequest|TLSSocket/i.test(s);
|
|
385
393
|
}
|
|
386
|
-
|
|
394
|
+
function _looksLikeConfiguredOtlpEndpoint(err) {
|
|
395
|
+
const text = `${err && err.hostname || ''} ${err && err.host || ''} ${err && err.message || ''}`;
|
|
396
|
+
try {
|
|
397
|
+
const hosts = [new URL(tracesUrl).hostname, new URL(logsUrl).hostname].filter(Boolean);
|
|
398
|
+
return hosts.some((host) => host && text.includes(host));
|
|
399
|
+
} catch (_) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const _diagDebug = otelLogLevel === 'debug';
|
|
404
|
+
let _lastSuppressedOtlpErrorLogAt = 0;
|
|
405
|
+
function _originalConsole(method) {
|
|
406
|
+
const originals = console.__securenow_original || console.__securenowOriginalConsole;
|
|
407
|
+
return (originals && originals[method]) || console[method] || console.log;
|
|
408
|
+
}
|
|
409
|
+
function _formatOtlpError(err) {
|
|
410
|
+
if (!err) return 'unknown error';
|
|
411
|
+
const parts = [err.message || String(err)];
|
|
412
|
+
if (err.code && !parts[0].includes(err.code)) parts.push(`code=${err.code}`);
|
|
413
|
+
if (err.syscall) parts.push(`syscall=${err.syscall}`);
|
|
414
|
+
if (err.hostname) parts.push(`host=${err.hostname}`);
|
|
415
|
+
return parts.join(' ');
|
|
416
|
+
}
|
|
417
|
+
function _reportSuppressedOtlpError(kind, err, origin) {
|
|
418
|
+
if (otelLogLevel === 'none') return;
|
|
419
|
+
const now = Date.now();
|
|
420
|
+
if (!_diagDebug && now - _lastSuppressedOtlpErrorLogAt < 60_000) return;
|
|
421
|
+
_lastSuppressedOtlpErrorLogAt = now;
|
|
422
|
+
const method = _diagDebug ? 'debug' : 'error';
|
|
423
|
+
_originalConsole(method).call(
|
|
424
|
+
console,
|
|
425
|
+
'[securenow] OTLP exporter %s suppressed (%s). Telemetry may be missing until the ingest endpoint is reachable: %s',
|
|
426
|
+
kind,
|
|
427
|
+
origin || 'async',
|
|
428
|
+
_formatOtlpError(err)
|
|
429
|
+
);
|
|
430
|
+
}
|
|
387
431
|
process.on('uncaughtException', (err, origin) => {
|
|
388
|
-
if (_isOtlpTransientError(err) && _looksLikeOtlpStack(err)) {
|
|
389
|
-
|
|
390
|
-
console.debug('[securenow] Suppressed transient OTLP exporter error (%s): %s', origin, err.message);
|
|
391
|
-
}
|
|
432
|
+
if (_isOtlpTransientError(err) && (_looksLikeOtlpStack(err) || _looksLikeConfiguredOtlpEndpoint(err))) {
|
|
433
|
+
_reportSuppressedOtlpError('error', err, origin);
|
|
392
434
|
return;
|
|
393
435
|
}
|
|
394
436
|
throw err;
|
|
395
437
|
});
|
|
396
438
|
process.on('unhandledRejection', (reason) => {
|
|
397
|
-
if (_isOtlpTransientError(reason) && _looksLikeOtlpStack(reason)) {
|
|
398
|
-
|
|
399
|
-
console.debug('[securenow] Suppressed transient OTLP exporter rejection: %s', reason && reason.message);
|
|
400
|
-
}
|
|
439
|
+
if (_isOtlpTransientError(reason) && (_looksLikeOtlpStack(reason) || _looksLikeConfiguredOtlpEndpoint(reason))) {
|
|
440
|
+
_reportSuppressedOtlpError('rejection', reason, 'unhandledRejection');
|
|
401
441
|
return;
|
|
402
442
|
}
|
|
403
443
|
throw reason;
|
|
@@ -455,11 +495,11 @@ function registerSecureNow(options = {}) {
|
|
|
455
495
|
});
|
|
456
496
|
|
|
457
497
|
sdk.start();
|
|
458
|
-
console.log('[securenow]
|
|
498
|
+
console.log('[securenow] Vanilla SDK initialized for self-hosted environment');
|
|
459
499
|
|
|
460
500
|
// -------- Logging (self-hosted only) --------
|
|
461
501
|
// Opt-out default: set config.logging.enabled=false to disable.
|
|
462
|
-
const loggingEnabled =
|
|
502
|
+
const loggingEnabled = appConfig.boolConfig('logging.enabled', true);
|
|
463
503
|
if (loggingEnabled) {
|
|
464
504
|
try {
|
|
465
505
|
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
|
|
@@ -511,7 +551,7 @@ function registerSecureNow(options = {}) {
|
|
|
511
551
|
_emitLog(SeverityNumber.ERROR, 'ERROR', args);
|
|
512
552
|
};
|
|
513
553
|
|
|
514
|
-
console.log('[securenow]
|
|
554
|
+
console.log('[securenow] Logging: ENABLED -> %s', logsUrl);
|
|
515
555
|
|
|
516
556
|
// Auto-log every incoming HTTP request/response
|
|
517
557
|
try {
|
|
@@ -559,51 +599,51 @@ function registerSecureNow(options = {}) {
|
|
|
559
599
|
}
|
|
560
600
|
return originalEmit.apply(this, arguments);
|
|
561
601
|
};
|
|
562
|
-
console.log('[securenow]
|
|
602
|
+
console.log('[securenow] HTTP request logging: ENABLED');
|
|
563
603
|
} catch (_) {}
|
|
564
604
|
|
|
565
605
|
// Graceful shutdown for logs
|
|
566
606
|
process.on('SIGTERM', async () => { try { await loggerProvider.shutdown(); } catch (_) {} try { requireRuntimeModule('./firewall').shutdown(); } catch (_) {} });
|
|
567
607
|
process.on('SIGINT', async () => { try { await loggerProvider.shutdown(); } catch (_) {} try { requireRuntimeModule('./firewall').shutdown(); } catch (_) {} });
|
|
568
608
|
} catch (e) {
|
|
569
|
-
console.warn('[securenow]
|
|
609
|
+
console.warn('[securenow] Logging setup failed (missing @opentelemetry/exporter-logs-otlp-http or @opentelemetry/sdk-logs):', e.message);
|
|
570
610
|
}
|
|
571
611
|
} else {
|
|
572
|
-
console.log('[securenow]
|
|
612
|
+
console.log('[securenow] Logging: DISABLED (config.logging.enabled=false)');
|
|
573
613
|
}
|
|
574
614
|
}
|
|
575
615
|
|
|
576
616
|
isRegistered = true;
|
|
577
617
|
|
|
578
|
-
// Free trial banner (optional
|
|
618
|
+
// Free trial banner (optional - may not be bundled in standalone builds)
|
|
579
619
|
try {
|
|
580
620
|
const { isFreeTrial, patchHttpForBanner } = requireRuntimeModule('./free-trial-banner');
|
|
581
|
-
if (isFreeTrial(endpointBase) &&
|
|
621
|
+
if (isFreeTrial(endpointBase) && !appConfig.boolConfig('runtime.hideBanner', false)) {
|
|
582
622
|
patchHttpForBanner();
|
|
583
623
|
}
|
|
584
624
|
} catch (_) {}
|
|
585
625
|
|
|
586
|
-
console.log('[securenow]
|
|
587
|
-
console.log('[securenow]
|
|
588
|
-
console.log('[securenow]
|
|
589
|
-
console.log('[securenow]
|
|
590
|
-
console.log('[securenow]
|
|
591
|
-
console.log('[securenow]
|
|
592
|
-
console.log('[securenow]
|
|
593
|
-
console.log('[securenow]
|
|
594
|
-
console.log('[securenow]
|
|
626
|
+
console.log('[securenow] OpenTelemetry started for Next.js -> %s', tracesUrl);
|
|
627
|
+
console.log('[securenow] Auto-capturing comprehensive request metadata:');
|
|
628
|
+
console.log('[securenow] - IP addresses (x-forwarded-for, x-real-ip, socket)');
|
|
629
|
+
console.log('[securenow] - User-Agent, Referer, Origin, Accept headers');
|
|
630
|
+
console.log('[securenow] - Protocol, Host, Port (proxy-aware)');
|
|
631
|
+
console.log('[securenow] - Geographic data (Vercel/Cloudflare)');
|
|
632
|
+
console.log('[securenow] - Request IDs, CSRF tokens, Auth presence');
|
|
633
|
+
console.log('[securenow] - Response status, content-type, content-length');
|
|
634
|
+
console.log('[securenow] Body capture DISABLED at HTTP instrumentation level (prevents Next.js conflicts)');
|
|
595
635
|
if (captureBody) {
|
|
596
|
-
console.log('[securenow]
|
|
636
|
+
console.log('[securenow] For body capture in Next.js, use: import "securenow/nextjs-auto-capture"');
|
|
597
637
|
}
|
|
598
638
|
|
|
599
639
|
// Optional test span
|
|
600
|
-
if (
|
|
640
|
+
if (appConfig.boolConfig('runtime.testSpan', false)) {
|
|
601
641
|
const api = require('@opentelemetry/api');
|
|
602
642
|
const tracer = api.trace.getTracer('securenow-nextjs');
|
|
603
643
|
const span = tracer.startSpan('securenow.nextjs.startup');
|
|
604
644
|
span.setAttribute('next.runtime', process.env.NEXT_RUNTIME || 'nodejs');
|
|
605
645
|
span.end();
|
|
606
|
-
console.log('[securenow]
|
|
646
|
+
console.log('[securenow] Test span created');
|
|
607
647
|
}
|
|
608
648
|
|
|
609
649
|
} catch (error) {
|
|
@@ -615,7 +655,7 @@ function registerSecureNow(options = {}) {
|
|
|
615
655
|
}
|
|
616
656
|
}
|
|
617
657
|
|
|
618
|
-
// Firewall
|
|
658
|
+
// Firewall - runs independently from OTel so it works even if tracing fails.
|
|
619
659
|
// Key and environment come from .securenow/credentials.json (written by
|
|
620
660
|
// login/init or credentials runtime), so no .env entry is needed.
|
|
621
661
|
const firewallOptions = appConfig.resolveFirewallOptions();
|
|
@@ -626,6 +666,7 @@ function registerSecureNow(options = {}) {
|
|
|
626
666
|
appKey: firewallOptions.appKey,
|
|
627
667
|
environment: deploymentEnvironment || firewallOptions.environment,
|
|
628
668
|
apiUrl: firewallOptions.apiUrl,
|
|
669
|
+
apiUrlFallbacks: firewallOptions.apiUrlFallbacks,
|
|
629
670
|
versionCheckInterval: firewallOptions.versionCheckInterval,
|
|
630
671
|
syncInterval: firewallOptions.syncInterval,
|
|
631
672
|
failMode: firewallOptions.failMode,
|
package/nuxt-server-plugin.mjs
CHANGED
|
@@ -26,8 +26,6 @@ const { resolveClientIpWithDetails } = nodeRequire('./resolve-ip');
|
|
|
26
26
|
|
|
27
27
|
// ── Helpers ──
|
|
28
28
|
|
|
29
|
-
const env = appConfig.env;
|
|
30
|
-
|
|
31
29
|
function createResource(attributes) {
|
|
32
30
|
if (typeof otelResources.resourceFromAttributes === 'function') {
|
|
33
31
|
return otelResources.resourceFromAttributes(attributes);
|
|
@@ -74,7 +72,7 @@ function getRuntimeOptions() {
|
|
|
74
72
|
export default defineNitroPlugin(async (nitroApp) => {
|
|
75
73
|
const opts = getRuntimeOptions();
|
|
76
74
|
|
|
77
|
-
// Resolution order: opts -> .securenow/credentials.json ->
|
|
75
|
+
// Resolution order: opts -> .securenow/credentials.json -> package.json#name
|
|
78
76
|
const resolvedApp = appConfig.resolveAll();
|
|
79
77
|
|
|
80
78
|
// ── Naming ──
|
|
@@ -125,12 +123,9 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
125
123
|
// Opt-out default: set config.capture.body=false (or opts.captureBody=false) to disable.
|
|
126
124
|
const captureBody =
|
|
127
125
|
opts.captureBody ??
|
|
128
|
-
|
|
129
|
-
const maxBodySize =
|
|
130
|
-
const customSensitiveFields = (
|
|
131
|
-
.split(',')
|
|
132
|
-
.map((s) => s.trim())
|
|
133
|
-
.filter(Boolean);
|
|
126
|
+
appConfig.boolConfig('capture.body', true);
|
|
127
|
+
const maxBodySize = appConfig.numberConfig('capture.maxBodySize', 10240, 1024);
|
|
128
|
+
const customSensitiveFields = appConfig.listConfig('capture.sensitiveFields');
|
|
134
129
|
const allSensitiveFields = [...DEFAULT_SENSITIVE_FIELDS, ...customSensitiveFields];
|
|
135
130
|
|
|
136
131
|
// ── HTTP instrumentation ──
|
|
@@ -238,7 +233,7 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
238
233
|
// Opt-out default: set config.logging.enabled=false (or opts.logging=false) to disable.
|
|
239
234
|
const loggingEnabled =
|
|
240
235
|
opts.logging ??
|
|
241
|
-
|
|
236
|
+
appConfig.boolConfig('logging.enabled', true);
|
|
242
237
|
|
|
243
238
|
let loggerProvider = null;
|
|
244
239
|
|
|
@@ -314,7 +309,7 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
314
309
|
// ── Free trial banner ──
|
|
315
310
|
try {
|
|
316
311
|
const { isFreeTrial, patchHttpForBanner } = await import('./free-trial-banner.js');
|
|
317
|
-
if (isFreeTrial(endpointBase) &&
|
|
312
|
+
if (isFreeTrial(endpointBase) && !appConfig.boolConfig('runtime.hideBanner', false)) {
|
|
318
313
|
patchHttpForBanner();
|
|
319
314
|
}
|
|
320
315
|
} catch {
|
|
@@ -331,6 +326,7 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
331
326
|
appKey: firewallOptions.appKey,
|
|
332
327
|
environment: deploymentEnvironment || firewallOptions.environment,
|
|
333
328
|
apiUrl: firewallOptions.apiUrl,
|
|
329
|
+
apiUrlFallbacks: firewallOptions.apiUrlFallbacks,
|
|
334
330
|
versionCheckInterval: firewallOptions.versionCheckInterval,
|
|
335
331
|
syncInterval: firewallOptions.syncInterval,
|
|
336
332
|
failMode: firewallOptions.failMode,
|
package/nuxt.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SecureNow Nuxt 3 Module TypeScript Declarations
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface SecureNowNuxtOptions {
|
|
1
|
+
/**
|
|
2
|
+
* SecureNow Nuxt 3 Module TypeScript Declarations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface SecureNowNuxtOptions {
|
|
6
6
|
/**
|
|
7
7
|
* Service name for OpenTelemetry traces.
|
|
8
8
|
* @default .securenow/credentials.json app.key/app.name
|
|
@@ -11,7 +11,11 @@ export interface SecureNowNuxtOptions {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* OTLP endpoint base URL.
|
|
14
|
-
* @default
|
|
14
|
+
* @default https://ingest.securenow.ai
|
|
15
|
+
*
|
|
16
|
+
* Advanced OTLP endpoint override. Normal SecureNow apps should leave this
|
|
17
|
+
* unset so the ingest gateway can route by app.key to the dashboard-selected
|
|
18
|
+
* instance.
|
|
15
19
|
*/
|
|
16
20
|
endpoint?: string;
|
|
17
21
|
|
|
@@ -20,14 +24,14 @@ export interface SecureNowNuxtOptions {
|
|
|
20
24
|
* @default .securenow/credentials.json config.runtime.deploymentEnvironment
|
|
21
25
|
*/
|
|
22
26
|
environment?: string;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Don't append UUID to service name (useful when running a single instance).
|
|
26
|
-
* @default false
|
|
27
|
-
*/
|
|
28
|
-
noUuid?: boolean;
|
|
29
|
-
|
|
30
|
-
/**
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Don't append UUID to service name (useful when running a single instance).
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
noUuid?: boolean;
|
|
33
|
+
|
|
34
|
+
/**
|
|
31
35
|
* Capture request bodies (POST/PUT/PATCH) on traced spans.
|
|
32
36
|
* Sensitive fields are automatically redacted.
|
|
33
37
|
* @default true from .securenow/credentials.json secure defaults
|
|
@@ -40,27 +44,27 @@ export interface SecureNowNuxtOptions {
|
|
|
40
44
|
*/
|
|
41
45
|
logging?: boolean;
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
declare module 'nuxt/schema' {
|
|
45
|
-
interface NuxtConfig {
|
|
46
|
-
securenow?: SecureNowNuxtOptions;
|
|
47
|
-
}
|
|
48
|
-
interface NuxtOptions {
|
|
49
|
-
securenow?: SecureNowNuxtOptions;
|
|
50
|
-
}
|
|
51
|
-
interface RuntimeConfig {
|
|
52
|
-
securenow?: SecureNowNuxtOptions;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
declare module '@nuxt/schema' {
|
|
57
|
-
interface NuxtConfig {
|
|
58
|
-
securenow?: SecureNowNuxtOptions;
|
|
59
|
-
}
|
|
60
|
-
interface NuxtOptions {
|
|
61
|
-
securenow?: SecureNowNuxtOptions;
|
|
62
|
-
}
|
|
63
|
-
interface RuntimeConfig {
|
|
64
|
-
securenow?: SecureNowNuxtOptions;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
47
|
+
|
|
48
|
+
declare module 'nuxt/schema' {
|
|
49
|
+
interface NuxtConfig {
|
|
50
|
+
securenow?: SecureNowNuxtOptions;
|
|
51
|
+
}
|
|
52
|
+
interface NuxtOptions {
|
|
53
|
+
securenow?: SecureNowNuxtOptions;
|
|
54
|
+
}
|
|
55
|
+
interface RuntimeConfig {
|
|
56
|
+
securenow?: SecureNowNuxtOptions;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
declare module '@nuxt/schema' {
|
|
61
|
+
interface NuxtConfig {
|
|
62
|
+
securenow?: SecureNowNuxtOptions;
|
|
63
|
+
}
|
|
64
|
+
interface NuxtOptions {
|
|
65
|
+
securenow?: SecureNowNuxtOptions;
|
|
66
|
+
}
|
|
67
|
+
interface RuntimeConfig {
|
|
68
|
+
securenow?: SecureNowNuxtOptions;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/nuxt.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* });
|
|
12
12
|
*
|
|
13
13
|
* Environment variables (same as all SecureNow integrations):
|
|
14
|
-
*
|
|
14
|
+
* Configuration is read from .securenow/credentials.json.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { defineNuxtModule, createResolver, addServerPlugin } from '@nuxt/kit';
|
package/package.json
CHANGED
package/tracing.d.ts
CHANGED
|
@@ -173,7 +173,8 @@ export const loggerProvider: LoggerProvider | null;
|
|
|
173
173
|
* generated JSON to .securenow/credentials.json in the running app.
|
|
174
174
|
*
|
|
175
175
|
* Main config fields:
|
|
176
|
-
* - app.key / app.name
|
|
176
|
+
* - app.key / app.name
|
|
177
|
+
* - config.otel.endpoint only for advanced/self-hosted collector overrides
|
|
177
178
|
* - apiKey
|
|
178
179
|
* - config.runtime.deploymentEnvironment
|
|
179
180
|
* - config.logging.enabled
|