securenow 5.0.4 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,7 +98,7 @@ pnpm add securenow
98
98
  SECURENOW_APPID=my-app-name
99
99
 
100
100
  # Optional: Your SigNoz/OTLP collector endpoint
101
- # Default: http://46.62.173.237:4318
101
+ # Default: https://freetrial.securenow.ai:4318
102
102
  SECURENOW_INSTANCE=http://your-signoz-server:4318
103
103
 
104
104
  # Optional: Enable Logging
package/cli.js CHANGED
@@ -59,7 +59,7 @@ export function register() {
59
59
  SECURENOW_APPID=my-nextjs-app
60
60
 
61
61
  # Optional: Your SigNoz/OpenTelemetry collector endpoint
62
- # Default: http://46.62.173.237:4318
62
+ # Default: https://freetrial.securenow.ai:4318
63
63
  SECURENOW_INSTANCE=http://your-signoz-server:4318
64
64
 
65
65
  # Optional: API key or authentication headers
@@ -9,7 +9,7 @@ Complete reference for all environment variables supported by SecureNow.
9
9
  | Variable | Type | Default | Description |
10
10
  |----------|------|---------|-------------|
11
11
  | **SECURENOW_APPID** | Required | - | Application identifier / service name |
12
- | **SECURENOW_INSTANCE** | Required | `http://46.62.173.237:4318` | OTLP collector base URL |
12
+ | **SECURENOW_INSTANCE** | Required | `https://freetrial.securenow.ai:4318` | OTLP collector base URL |
13
13
  | **SECURENOW_LOGGING_ENABLED** | Optional | `1` | Enable/disable logging |
14
14
  | **SECURENOW_NO_UUID** | Optional | `0` | Disable UUID suffix on service name |
15
15
  | **SECURENOW_STRICT** | Optional | `0` | Exit if APPID missing in cluster mode |
@@ -72,7 +72,7 @@ export SECURENOW_INSTANCE=http://collector.example.com:4318
72
72
  export SECURENOW_INSTANCE=https://collector.example.com:4318
73
73
  ```
74
74
 
75
- **Default:** `http://46.62.173.237:4318` (if not set)
75
+ **Default:** `https://freetrial.securenow.ai:4318` (if not set)
76
76
 
77
77
  **Notes:**
78
78
  - Used to construct traces and logs endpoints
@@ -540,7 +540,7 @@ When multiple variables are set, this is the priority order:
540
540
  2. `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` (for logs)
541
541
  3. `OTEL_EXPORTER_OTLP_ENDPOINT`
542
542
  4. `SECURENOW_INSTANCE`
543
- 5. Default: `http://46.62.173.237:4318` (lowest priority)
543
+ 5. Default: `https://freetrial.securenow.ai:4318` (lowest priority)
544
544
 
545
545
  ---
546
546
 
@@ -39,7 +39,7 @@ export SECURENOW_LOGGING_ENABLED=1
39
39
  # Required: Your app identifier
40
40
  export SECURENOW_APPID=my-app
41
41
 
42
- # Optional: Your SigNoz endpoint (defaults to http://46.62.173.237:4318)
42
+ # Optional: Your SigNoz endpoint (defaults to https://freetrial.securenow.ai:4318)
43
43
  export SECURENOW_INSTANCE=http://your-signoz-server:4318
44
44
 
45
45
  # Optional: SigNoz Cloud authentication
@@ -5,7 +5,7 @@
5
5
  SECURENOW_APPID=my-nextjs-app
6
6
 
7
7
  # Optional: Your SigNoz/OpenTelemetry collector endpoint
8
- # Default: http://46.62.173.237:4318
8
+ # Default: https://freetrial.securenow.ai:4318
9
9
  SECURENOW_INSTANCE=http://your-signoz-server:4318
10
10
 
11
11
  # Optional: API Key or authentication headers
@@ -0,0 +1,154 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Free Trial Banner — auto-injects a visible "Testing Environment" banner
5
+ * into HTML pages served by apps using the SecureNow free trial instance.
6
+ *
7
+ * Opt-out: set SECURENOW_HIDE_BANNER=1
8
+ */
9
+
10
+ const FREETRIAL_HOST = 'freetrial.securenow.ai';
11
+
12
+ function isFreeTrial(endpointBase) {
13
+ return !!endpointBase && endpointBase.includes(FREETRIAL_HOST);
14
+ }
15
+
16
+ /* istanbul ignore next — runs in browser, not Node */
17
+ function _bannerClientCode() {
18
+ if (window.__snBanner) return;
19
+ window.__snBanner = 1;
20
+
21
+ function create() {
22
+ if (document.getElementById('sn-ft-banner')) return;
23
+
24
+ var d = document.createElement('div');
25
+ d.id = 'sn-ft-banner';
26
+ d.style.cssText =
27
+ 'position:fixed;top:0;left:0;right:0;z-index:2147483647;' +
28
+ 'background:#FEF3CD;color:#856404;padding:10px 16px;' +
29
+ 'font-family:system-ui,-apple-system,sans-serif;font-size:13px;' +
30
+ 'text-align:center;border-bottom:2px solid #FFE69C;' +
31
+ 'display:flex;align-items:center;justify-content:center;gap:6px;' +
32
+ 'box-shadow:0 2px 8px rgba(0,0,0,0.12)';
33
+
34
+ var icon = document.createElement('span');
35
+ icon.textContent = '\u26a0\ufe0f';
36
+ d.appendChild(icon);
37
+
38
+ var msg = document.createElement('span');
39
+ var strong = document.createElement('strong');
40
+ strong.textContent = 'Testing Environment:';
41
+ msg.appendChild(strong);
42
+ msg.appendChild(document.createTextNode(
43
+ ' Only add test applications. For production usage, please '
44
+ ));
45
+
46
+ var link = document.createElement('a');
47
+ link.href = 'https://securenow.ai/contact';
48
+ link.target = '_blank';
49
+ link.rel = 'noopener';
50
+ link.style.cssText = 'color:#664D03;font-weight:600;text-decoration:underline';
51
+ link.textContent = 'contact our team for a demo';
52
+ msg.appendChild(link);
53
+ msg.appendChild(document.createTextNode('.'));
54
+ d.appendChild(msg);
55
+
56
+ var close = document.createElement('button');
57
+ close.textContent = '\u00d7';
58
+ close.style.cssText =
59
+ 'background:none;border:none;color:#856404;font-size:18px;' +
60
+ 'cursor:pointer;margin-left:12px;padding:0 4px;line-height:1';
61
+ close.onclick = function () { d.style.display = 'none'; };
62
+ d.appendChild(close);
63
+
64
+ document.body.prepend(d);
65
+ }
66
+
67
+ if (document.readyState === 'loading') {
68
+ document.addEventListener('DOMContentLoaded', create);
69
+ } else {
70
+ create();
71
+ }
72
+ }
73
+
74
+ var BANNER_SCRIPT =
75
+ '<script data-securenow-banner>(' +
76
+ _bannerClientCode.toString() +
77
+ ')()</scr' + 'ipt>';
78
+
79
+ /**
80
+ * Monkey-patch http.ServerResponse to inject the banner script into HTML
81
+ * responses. Searches for `<head...>` and inserts the script right after it.
82
+ * Skips compressed responses and non-HTML content types.
83
+ */
84
+ function patchHttpForBanner() {
85
+ try {
86
+ var http = require('http');
87
+ var _origWrite = http.ServerResponse.prototype.write;
88
+ var _origEnd = http.ServerResponse.prototype.end;
89
+
90
+ function maybeInject(res, chunk) {
91
+ if (res._snBannerDone || !chunk) return chunk;
92
+
93
+ if (res._snIsHtml === undefined) {
94
+ var ct = res.getHeader('content-type');
95
+ var ce = res.getHeader('content-encoding');
96
+ res._snIsHtml = !!(ct && String(ct).includes('text/html') && !ce);
97
+ }
98
+ if (!res._snIsHtml) {
99
+ res._snBannerDone = true;
100
+ return chunk;
101
+ }
102
+
103
+ var isStr = typeof chunk === 'string';
104
+ var isBuf = Buffer.isBuffer(chunk);
105
+ if (!isStr && !isBuf) return chunk;
106
+
107
+ var str = isStr ? chunk : chunk.toString('utf8');
108
+ var headIdx = str.indexOf('<head');
109
+ if (headIdx === -1) return chunk;
110
+
111
+ var gt = str.indexOf('>', headIdx);
112
+ if (gt === -1) return chunk;
113
+
114
+ res._snBannerDone = true;
115
+ var result = str.slice(0, gt + 1) + BANNER_SCRIPT + str.slice(gt + 1);
116
+ return isStr ? result : Buffer.from(result, 'utf8');
117
+ }
118
+
119
+ http.ServerResponse.prototype.write = function (chunk, encoding, cb) {
120
+ try {
121
+ var modified = maybeInject(this, chunk);
122
+ if (modified !== chunk) {
123
+ var enc = typeof encoding === 'function' ? 'utf8' : encoding;
124
+ var callback = typeof encoding === 'function' ? encoding : cb;
125
+ return _origWrite.call(this, modified, enc, callback);
126
+ }
127
+ } catch (_) { /* never break the app */ }
128
+ return _origWrite.call(this, chunk, encoding, cb);
129
+ };
130
+
131
+ http.ServerResponse.prototype.end = function (chunk, encoding, cb) {
132
+ try {
133
+ var modified = maybeInject(this, chunk);
134
+ if (modified !== chunk) {
135
+ var enc = typeof encoding === 'function' ? 'utf8' : encoding;
136
+ var callback = typeof encoding === 'function' ? encoding : cb;
137
+ try {
138
+ if (this.getHeader('content-length')) {
139
+ this.setHeader('content-length', Buffer.byteLength(modified));
140
+ }
141
+ } catch (_) { /* headers already sent */ }
142
+ return _origEnd.call(this, modified, enc, callback);
143
+ }
144
+ } catch (_) { /* never break the app */ }
145
+ return _origEnd.call(this, chunk, encoding, cb);
146
+ };
147
+
148
+ console.log('[securenow] Free trial banner injection enabled');
149
+ } catch (err) {
150
+ console.warn('[securenow] Could not setup free trial banner:', err.message);
151
+ }
152
+ }
153
+
154
+ module.exports = { isFreeTrial, patchHttpForBanner, BANNER_SCRIPT };
package/nextjs.d.ts CHANGED
@@ -11,7 +11,7 @@ export interface RegisterOptions {
11
11
 
12
12
  /**
13
13
  * OTLP endpoint for traces
14
- * @default process.env.SECURENOW_INSTANCE || 'http://46.62.173.237:4318'
14
+ * @default process.env.SECURENOW_INSTANCE || 'https://freetrial.securenow.ai:4318'
15
15
  */
16
16
  endpoint?: string;
17
17
 
package/nextjs.js CHANGED
@@ -257,10 +257,11 @@ function registerSecureNow(options = {}) {
257
257
  options.endpoint ||
258
258
  env('SECURENOW_INSTANCE') ||
259
259
  env('OTEL_EXPORTER_OTLP_ENDPOINT') ||
260
- 'http://46.62.173.237:4318'
260
+ 'https://freetrial.securenow.ai:4318'
261
261
  ).replace(/\/$/, '');
262
262
 
263
263
  const tracesUrl = env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
264
+ const logsUrl = env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`;
264
265
 
265
266
  // Set environment variables for @vercel/otel to pick up
266
267
  process.env.OTEL_SERVICE_NAME = serviceName;
@@ -504,9 +505,69 @@ function registerSecureNow(options = {}) {
504
505
 
505
506
  sdk.start();
506
507
  console.log('[securenow] 🎯 Vanilla SDK initialized for self-hosted environment');
508
+
509
+ // -------- Logging (self-hosted only) --------
510
+ const loggingEnabled = String(env('SECURENOW_LOGGING_ENABLED')) === '1' || String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() === 'true';
511
+ if (loggingEnabled) {
512
+ try {
513
+ const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
514
+ const { LoggerProvider, BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
515
+
516
+ const logExporter = new OTLPLogExporter({
517
+ url: logsUrl,
518
+ headers: parseHeaders(env('OTEL_EXPORTER_OTLP_HEADERS')),
519
+ });
520
+
521
+ const loggerProvider = new LoggerProvider({
522
+ resource: new Resource({
523
+ [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
524
+ [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: env('NODE_ENV') || 'production',
525
+ }),
526
+ });
527
+ loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
528
+
529
+ // Patch console to forward logs as OTLP log records
530
+ const logger = loggerProvider.getLogger('console', '1.0.0');
531
+ const SeverityNumber = { INFO: 9, WARN: 13, ERROR: 17 };
532
+ const origLog = console.log;
533
+ const origWarn = console.warn;
534
+ const origError = console.error;
535
+
536
+ console.log = (...args) => {
537
+ origLog.apply(console, args);
538
+ try { logger.emit({ severityNumber: SeverityNumber.INFO, severityText: 'INFO', body: args.map(String).join(' ') }); } catch (_) {}
539
+ };
540
+ console.warn = (...args) => {
541
+ origWarn.apply(console, args);
542
+ try { logger.emit({ severityNumber: SeverityNumber.WARN, severityText: 'WARN', body: args.map(String).join(' ') }); } catch (_) {}
543
+ };
544
+ console.error = (...args) => {
545
+ origError.apply(console, args);
546
+ try { logger.emit({ severityNumber: SeverityNumber.ERROR, severityText: 'ERROR', body: args.map(String).join(' ') }); } catch (_) {}
547
+ };
548
+
549
+ console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
550
+
551
+ // Graceful shutdown for logs
552
+ const origShutdown = sdk.shutdown?.bind(sdk);
553
+ process.on('SIGTERM', async () => { try { await loggerProvider.shutdown(); } catch (_) {} });
554
+ process.on('SIGINT', async () => { try { await loggerProvider.shutdown(); } catch (_) {} });
555
+ } catch (e) {
556
+ console.warn('[securenow] ⚠️ Logging setup failed (missing @opentelemetry/exporter-logs-otlp-http or @opentelemetry/sdk-logs):', e.message);
557
+ }
558
+ } else {
559
+ console.log('[securenow] 📋 Logging: DISABLED (set SECURENOW_LOGGING_ENABLED=1 to enable)');
560
+ }
507
561
  }
508
562
 
509
563
  isRegistered = true;
564
+
565
+ // Free trial banner
566
+ const { isFreeTrial, patchHttpForBanner } = require('./free-trial-banner');
567
+ if (isFreeTrial(endpointBase) && String(env('SECURENOW_HIDE_BANNER')) !== '1') {
568
+ patchHttpForBanner();
569
+ }
570
+
510
571
  console.log('[securenow] ✅ OpenTelemetry started for Next.js → %s', tracesUrl);
511
572
  console.log('[securenow] 📊 Auto-capturing comprehensive request metadata:');
512
573
  console.log('[securenow] • IP addresses (x-forwarded-for, x-real-ip, socket)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "5.0.4",
3
+ "version": "5.2.0",
4
4
  "description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces and logs to SigNoz or any OTLP backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",
@@ -85,6 +85,7 @@
85
85
  "nextjs-wrapper.js",
86
86
  "nextjs-wrapper.d.ts",
87
87
  "cli.js",
88
+ "free-trial-banner.js",
88
89
  "postinstall.js",
89
90
  "register-vite.js",
90
91
  "web-vite.mjs",
package/postinstall.js CHANGED
@@ -151,7 +151,7 @@ function createEnvTemplate(targetPath) {
151
151
  SECURENOW_APPID=my-nextjs-app
152
152
 
153
153
  # Optional: Your SigNoz/OpenTelemetry collector endpoint
154
- # Default: http://46.62.173.237:4318
154
+ # Default: https://freetrial.securenow.ai:4318
155
155
  SECURENOW_INSTANCE=http://your-signoz-server:4318
156
156
 
157
157
  # Optional: API key or authentication headers
package/tracing.js CHANGED
@@ -245,7 +245,7 @@ const httpInstrumentation = new HttpInstrumentation({
245
245
  });
246
246
 
247
247
  // -------- Logging Configuration --------
248
- const loggingEnabled = String(env('SECURENOW_LOGGING_ENABLED')) !== '0' && String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() !== 'false';
248
+ const loggingEnabled = String(env('SECURENOW_LOGGING_ENABLED')) === '1' || String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() === 'true';
249
249
 
250
250
  // Create shared resource for both traces and logs
251
251
  const sharedResource = new Resource({
@@ -257,7 +257,6 @@ const sharedResource = new Resource({
257
257
 
258
258
  // Initialize LoggerProvider if logging is enabled
259
259
  let loggerProvider = null;
260
- let globalLogger = null;
261
260
 
262
261
  if (loggingEnabled) {
263
262
  const logExporter = new OTLPLogExporter({
@@ -270,8 +269,6 @@ if (loggingEnabled) {
270
269
  resource: sharedResource,
271
270
  });
272
271
  loggerProvider.addLogRecordProcessor(batchLogProcessor);
273
-
274
- globalLogger = loggerProvider.getLogger('securenow', '1.0.0');
275
272
  }
276
273
 
277
274
  // -------- SDK --------
@@ -306,12 +303,21 @@ const sdk = new NodeSDK({
306
303
  const tracer = api.trace.getTracer('securenow-smoke');
307
304
  const span = tracer.startSpan('securenow.startup.smoke'); span.end();
308
305
  }
306
+
307
+ // Free trial banner
308
+ const { isFreeTrial, patchHttpForBanner } = require('./free-trial-banner');
309
+ if (isFreeTrial(endpointBase) && String(env('SECURENOW_HIDE_BANNER')) !== '1') {
310
+ patchHttpForBanner();
311
+ }
309
312
  } catch (e) {
310
313
  console.error('[securenow] OTel start failed:', e && e.stack || e);
311
314
  }
312
315
  })();
313
316
 
317
+ let shuttingDown = false;
314
318
  async function safeShutdown(sig) {
319
+ if (shuttingDown) return;
320
+ shuttingDown = true;
315
321
  try {
316
322
  await Promise.resolve(sdk.shutdown?.());
317
323
  if (loggerProvider) {
package/web-vite.mjs CHANGED
@@ -43,7 +43,7 @@ function parseHeaders(str?: string) {
43
43
 
44
44
  // ---- endpoints (same defaults as tracing.js) ----
45
45
  const endpointBase =
46
- (env('SECURENOW_INSTANCE') || env('OTEL_EXPORTER_OTLP_ENDPOINT') || 'http://46.62.173.237:4318')
46
+ (env('SECURENOW_INSTANCE') || env('OTEL_EXPORTER_OTLP_ENDPOINT') || 'https://freetrial.securenow.ai:4318')
47
47
  .replace(/\/$/, '');
48
48
  const tracesUrl =
49
49
  env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
@@ -144,9 +144,70 @@ export function startSecurenowWeb() {
144
144
  console.log('[securenow] Web OTel started → %s', tracesUrl);
145
145
  }
146
146
 
147
+ // ---- Free trial banner (browser DOM injection) ----
148
+ function injectFreeTrialBanner(): void {
149
+ const FREETRIAL_HOST = 'freetrial.securenow.ai';
150
+ const hideBanner = String(env('SECURENOW_HIDE_BANNER')) === '1';
151
+ if (hideBanner || !endpointBase.includes(FREETRIAL_HOST)) return;
152
+ if (typeof document === 'undefined') return;
153
+
154
+ function create(): void {
155
+ if (document.getElementById('sn-ft-banner')) return;
156
+
157
+ const d = document.createElement('div');
158
+ d.id = 'sn-ft-banner';
159
+ d.style.cssText =
160
+ 'position:fixed;top:0;left:0;right:0;z-index:2147483647;' +
161
+ 'background:#FEF3CD;color:#856404;padding:10px 16px;' +
162
+ 'font-family:system-ui,-apple-system,sans-serif;font-size:13px;' +
163
+ 'text-align:center;border-bottom:2px solid #FFE69C;' +
164
+ 'display:flex;align-items:center;justify-content:center;gap:6px;' +
165
+ 'box-shadow:0 2px 8px rgba(0,0,0,0.12)';
166
+
167
+ const icon = document.createElement('span');
168
+ icon.textContent = '\u26a0\ufe0f';
169
+ d.appendChild(icon);
170
+
171
+ const msg = document.createElement('span');
172
+ const strong = document.createElement('strong');
173
+ strong.textContent = 'Testing Environment:';
174
+ msg.appendChild(strong);
175
+ msg.appendChild(document.createTextNode(
176
+ ' Only add test applications. For production usage, please '
177
+ ));
178
+
179
+ const link = document.createElement('a');
180
+ link.href = 'https://securenow.ai/contact';
181
+ link.target = '_blank';
182
+ link.rel = 'noopener';
183
+ link.style.cssText = 'color:#664D03;font-weight:600;text-decoration:underline';
184
+ link.textContent = 'contact our team for a demo';
185
+ msg.appendChild(link);
186
+ msg.appendChild(document.createTextNode('.'));
187
+ d.appendChild(msg);
188
+
189
+ const close = document.createElement('button');
190
+ close.textContent = '\u00d7';
191
+ close.style.cssText =
192
+ 'background:none;border:none;color:#856404;font-size:18px;' +
193
+ 'cursor:pointer;margin-left:12px;padding:0 4px;line-height:1';
194
+ close.onclick = () => { d.style.display = 'none'; };
195
+ d.appendChild(close);
196
+
197
+ document.body.prepend(d);
198
+ }
199
+
200
+ if (document.readyState === 'loading') {
201
+ document.addEventListener('DOMContentLoaded', create);
202
+ } else {
203
+ create();
204
+ }
205
+ }
206
+
147
207
  // Auto-start
148
208
  try {
149
209
  startSecurenowWeb();
210
+ injectFreeTrialBanner();
150
211
  } catch (e: any) {
151
212
  if (String(e?.message) !== '__SECURENOW_NO_START__') {
152
213
  console.error('[securenow/web-vite] failed to start:', e);