securenow 7.5.1 → 7.6.1
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/CONSUMING-APPS-GUIDE.md +2 -0
- package/NPM_README.md +201 -237
- package/README.md +73 -26
- package/SKILL-API.md +205 -205
- package/SKILL-CLI.md +71 -64
- package/app-config.js +479 -83
- package/cli/apiKey.js +1 -1
- package/cli/apps.js +1 -1
- package/cli/config.js +31 -12
- package/cli/credentials.js +88 -0
- package/cli/diagnostics.js +68 -104
- package/cli/firewall.js +29 -14
- package/cli/init.js +211 -212
- package/cli/monitor.js +107 -43
- package/cli/security.js +24 -12
- package/cli/utils.js +2 -1
- package/cli.js +72 -40
- package/console-instrumentation.js +1 -1
- package/docs/ENVIRONMENT-VARIABLES.md +137 -863
- package/docs/ENVIRONMENTS.md +60 -0
- package/docs/EXPRESS-SETUP-GUIDE.md +3 -0
- package/docs/FIREWALL-GUIDE.md +3 -0
- package/docs/INDEX.md +6 -8
- package/docs/LOGGING-GUIDE.md +3 -0
- package/docs/MCP-GUIDE.md +8 -0
- package/docs/NEXTJS-GUIDE.md +3 -0
- package/docs/NEXTJS-QUICKSTART.md +22 -16
- package/docs/NUXT-GUIDE.md +3 -0
- package/docs/QUICKSTART-BODY-CAPTURE.md +3 -0
- package/docs/REQUEST-BODY-CAPTURE.md +3 -0
- package/firewall-cloud.js +10 -10
- package/firewall-only.js +25 -23
- package/firewall.js +47 -29
- package/free-trial-banner.js +1 -1
- package/mcp/catalog.js +104 -17
- package/nextjs-auto-capture.d.ts +7 -4
- package/nextjs-auto-capture.js +7 -7
- package/nextjs-middleware.js +4 -3
- package/nextjs-wrapper.js +6 -6
- package/nextjs.d.ts +36 -25
- package/nextjs.js +48 -55
- package/nuxt-server-plugin.mjs +35 -51
- package/nuxt.d.ts +29 -23
- package/package.json +1 -1
- package/postinstall.js +27 -61
- package/register.d.ts +19 -33
- package/register.js +8 -8
- package/resolve-ip.js +4 -5
- package/tracing.d.ts +21 -19
- package/tracing.js +34 -42
package/register.js
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
// On older Node versions it falls back to a warning.
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
// 1.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} catch (e) {
|
|
14
|
-
|
|
15
|
-
}
|
|
9
|
+
// 1. Load dotenv quietly only for legacy installs. Normal local and production
|
|
10
|
+
// configuration comes from .securenow/credentials.json via app-config.js.
|
|
11
|
+
try {
|
|
12
|
+
require('dotenv').config();
|
|
13
|
+
} catch (e) {
|
|
14
|
+
// dotenv is optional.
|
|
15
|
+
}
|
|
16
16
|
|
|
17
17
|
// 2. Auto-register the ESM loader hook so customers never need --import
|
|
18
18
|
(() => {
|
|
@@ -22,7 +22,7 @@ try {
|
|
|
22
22
|
const pkgPath = path.resolve(process.cwd(), 'package.json');
|
|
23
23
|
if (!fs.existsSync(pkgPath)) return;
|
|
24
24
|
|
|
25
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
25
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8').replace(/^\uFEFF/, ''));
|
|
26
26
|
if (pkg.type !== 'module') return;
|
|
27
27
|
|
|
28
28
|
// Already registered via --import?
|
package/resolve-ip.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const os = require('os');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const appConfig = require('./app-config');
|
|
4
5
|
|
|
5
6
|
const LOOPBACK_RE = /^(127\.|::1$|::ffff:127\.)/;
|
|
6
7
|
const PRIVATE_IP_RE = /^(127\.|::1$|::ffff:127\.|10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|f[cd][0-9a-f]{2}:)/;
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
const trustedProxySet =
|
|
10
|
-
? new Set(trustedProxyCsv.split(',').map(s => s.trim()).filter(Boolean))
|
|
11
|
-
: null;
|
|
9
|
+
const trustedProxies = appConfig.listEnv('SECURENOW_TRUSTED_PROXIES');
|
|
10
|
+
const trustedProxySet = trustedProxies.length ? new Set(trustedProxies) : null;
|
|
12
11
|
|
|
13
12
|
let _hostIp = null;
|
|
14
13
|
function getHostIp() {
|
package/tracing.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ export function getLogger(name?: string, version?: string): Logger | null;
|
|
|
142
142
|
/**
|
|
143
143
|
* Check if logging is enabled
|
|
144
144
|
*
|
|
145
|
-
* @returns true
|
|
145
|
+
* @returns true when logging resolves enabled from .securenow/credentials.json defaults
|
|
146
146
|
*
|
|
147
147
|
* @example
|
|
148
148
|
* ```typescript
|
|
@@ -162,22 +162,24 @@ export function isLoggingEnabled(): boolean;
|
|
|
162
162
|
export const loggerProvider: LoggerProvider | null;
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* -
|
|
177
|
-
* -
|
|
178
|
-
* -
|
|
179
|
-
* -
|
|
180
|
-
* -
|
|
181
|
-
* -
|
|
182
|
-
* -
|
|
165
|
+
* Configuration:
|
|
166
|
+
*
|
|
167
|
+
* Local development reads .securenow/credentials.json first. Run
|
|
168
|
+
* `npx securenow login` to write app identity/firewall key and
|
|
169
|
+
* `npx securenow init` to ensure secure defaults and explanations.
|
|
170
|
+
*
|
|
171
|
+
* Production uses the same file shape. Run
|
|
172
|
+
* `npx securenow credentials runtime --env production`, then mount/copy the
|
|
173
|
+
* generated JSON to .securenow/credentials.json in the running app.
|
|
174
|
+
*
|
|
175
|
+
* Main config fields:
|
|
176
|
+
* - app.key / app.name / app.instance
|
|
177
|
+
* - apiKey
|
|
178
|
+
* - config.runtime.deploymentEnvironment
|
|
179
|
+
* - config.logging.enabled
|
|
180
|
+
* - config.capture.body / multipart / maxBodySize / sensitiveFields
|
|
181
|
+
* - config.otel.endpoint / tracesEndpoint / logsEndpoint / headers
|
|
182
|
+
* - config.firewall.enabled and layer/provider options
|
|
183
|
+
*
|
|
184
|
+
* Legacy env vars are fallback-only for existing installs.
|
|
183
185
|
*/
|
package/tracing.js
CHANGED
|
@@ -38,17 +38,9 @@ const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventi
|
|
|
38
38
|
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
|
|
39
39
|
const { MongoDBInstrumentation } = require('@opentelemetry/instrumentation-mongodb');
|
|
40
40
|
const { v4: uuidv4 } = require('uuid');
|
|
41
|
+
const appConfig = require('./app-config');
|
|
41
42
|
|
|
42
|
-
const env =
|
|
43
|
-
const parseHeaders = str => {
|
|
44
|
-
const out = {}; if (!str) return out;
|
|
45
|
-
for (const raw of String(str).split(',')) {
|
|
46
|
-
const s = raw.trim(); if (!s) continue;
|
|
47
|
-
const i = s.indexOf('='); if (i === -1) continue;
|
|
48
|
-
out[s.slice(0, i).trim().toLowerCase()] = s.slice(i + 1).trim();
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
};
|
|
43
|
+
const env = appConfig.env;
|
|
52
44
|
|
|
53
45
|
// Default sensitive fields to redact from request bodies
|
|
54
46
|
const DEFAULT_SENSITIVE_FIELDS = [
|
|
@@ -273,35 +265,31 @@ const diagLevel = (env('OTEL_LOG_LEVEL') || '').toLowerCase();
|
|
|
273
265
|
})();
|
|
274
266
|
|
|
275
267
|
// -------- endpoints & app resolution --------
|
|
276
|
-
// Resolution order for endpoint/appId/apiKey:
|
|
277
|
-
const appConfig = require('./app-config');
|
|
268
|
+
// Resolution order for endpoint/appId/apiKey: .securenow/credentials.json -> legacy env fallback -> package.json#name -> defaults.
|
|
278
269
|
const resolvedApp = appConfig.resolveAll();
|
|
270
|
+
const resolvedEndpoints = appConfig.resolveEndpoints();
|
|
279
271
|
|
|
280
|
-
const endpointBase =
|
|
281
|
-
const tracesUrl =
|
|
282
|
-
const logsUrl =
|
|
272
|
+
const endpointBase = resolvedEndpoints.endpointBase;
|
|
273
|
+
const tracesUrl = resolvedEndpoints.tracesUrl;
|
|
274
|
+
const logsUrl = resolvedEndpoints.logsUrl;
|
|
283
275
|
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
process.env.SECURENOW_API_KEY = resolvedApp.appKey;
|
|
288
|
-
process.env.OTEL_EXPORTER_OTLP_HEADERS = `x-api-key=${resolvedApp.appKey}`;
|
|
289
|
-
}
|
|
290
|
-
const headers = parseHeaders(env('OTEL_EXPORTER_OTLP_HEADERS'));
|
|
276
|
+
// resolveEndpoints() also adds x-api-key from the credentials app key when
|
|
277
|
+
// explicit OTLP headers did not provide one.
|
|
278
|
+
const headers = resolvedEndpoints.headers;
|
|
291
279
|
|
|
292
280
|
// -------- naming rules --------
|
|
293
281
|
const rawBase = (resolvedApp.appId || '').trim().replace(/^['"]|['"]$/g, '');
|
|
294
282
|
const baseName = rawBase || null;
|
|
295
283
|
// Auto-disables the per-worker suffix when we resolved a routing UUID from
|
|
296
284
|
// credentials — the dashboard does exact-match IN on service.name, so any
|
|
297
|
-
// suffix breaks routing.
|
|
285
|
+
// suffix breaks routing. config.runtime.noUuid can override.
|
|
298
286
|
const noUuid = appConfig.resolveNoUuid();
|
|
299
287
|
const strict = String(env('SECURENOW_STRICT')) === '1' || String(env('SECURENOW_STRICT')).toLowerCase() === 'true';
|
|
300
288
|
const inPm2Cluster = !!(process.env.NODE_APP_INSTANCE || process.env.pm_id);
|
|
301
289
|
|
|
302
290
|
// Fail fast in cluster if base is missing (no more "free" names)
|
|
303
291
|
if (!baseName && inPm2Cluster && strict) {
|
|
304
|
-
console.error('[securenow] FATAL:
|
|
292
|
+
console.error('[securenow] FATAL: app identity missing in cluster (pid=%d). Exiting due to config.runtime.strict=true.', process.pid);
|
|
305
293
|
// small delay so the log flushes
|
|
306
294
|
setTimeout(() => process.exit(1), 10);
|
|
307
295
|
}
|
|
@@ -336,7 +324,7 @@ for (const n of (env('SECURENOW_DISABLE_INSTRUMENTATIONS') || '').split(',').map
|
|
|
336
324
|
}
|
|
337
325
|
|
|
338
326
|
// -------- Body Capture Configuration --------
|
|
339
|
-
// Opt-out defaults: set =
|
|
327
|
+
// Opt-out defaults: set config.capture.body=false to disable.
|
|
340
328
|
const captureBody = !/^(0|false)$/i.test(String(env('SECURENOW_CAPTURE_BODY') ?? ''));
|
|
341
329
|
const maxBodySize = Math.max(1024, parseInt(env('SECURENOW_MAX_BODY_SIZE'), 10) || 10240);
|
|
342
330
|
const customSensitiveFields = (env('SECURENOW_SENSITIVE_FIELDS') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
@@ -481,7 +469,7 @@ const loggingEnabled = !/^(0|false)$/i.test(String(env('SECURENOW_LOGGING_ENABLE
|
|
|
481
469
|
const sharedResource = new Resource({
|
|
482
470
|
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
483
471
|
[SemanticResourceAttributes.SERVICE_INSTANCE_ID]: serviceInstanceId,
|
|
484
|
-
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
|
|
472
|
+
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: appConfig.resolveDeploymentEnvironment(),
|
|
485
473
|
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.npm_package_version || undefined,
|
|
486
474
|
});
|
|
487
475
|
|
|
@@ -591,7 +579,7 @@ const sdk = new NodeSDK({
|
|
|
591
579
|
if (loggingEnabled) {
|
|
592
580
|
console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
|
|
593
581
|
} else {
|
|
594
|
-
console.log('[securenow]
|
|
582
|
+
console.log('[securenow] Logging: DISABLED (config.logging.enabled=false)');
|
|
595
583
|
}
|
|
596
584
|
if (captureBody) {
|
|
597
585
|
console.log('[securenow] 📝 Request body capture: ENABLED (max: %d bytes, redacting %d sensitive fields)', maxBodySize, allSensitiveFields.length);
|
|
@@ -614,21 +602,25 @@ const sdk = new NodeSDK({
|
|
|
614
602
|
// Firewall — auto-activates only when a real snk_live_ key is resolvable.
|
|
615
603
|
// resolveApiKey() enforces the prefix, so we skip cleanly when the app has
|
|
616
604
|
// only an app-routing UUID (or nothing at all) — no 401 polling loops.
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
if (firewallApiKey && env('SECURENOW_FIREWALL_ENABLED') !== '0') {
|
|
605
|
+
const firewallOptions = appConfig.resolveFirewallOptions();
|
|
606
|
+
if (firewallOptions.apiKey && firewallOptions.enabled) {
|
|
620
607
|
require('./firewall').init({
|
|
621
|
-
apiKey:
|
|
622
|
-
appKey:
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
608
|
+
apiKey: firewallOptions.apiKey,
|
|
609
|
+
appKey: firewallOptions.appKey,
|
|
610
|
+
environment: firewallOptions.environment,
|
|
611
|
+
apiUrl: firewallOptions.apiUrl,
|
|
612
|
+
versionCheckInterval: firewallOptions.versionCheckInterval,
|
|
613
|
+
syncInterval: firewallOptions.syncInterval,
|
|
614
|
+
failMode: firewallOptions.failMode,
|
|
615
|
+
statusCode: firewallOptions.statusCode,
|
|
616
|
+
log: firewallOptions.log,
|
|
617
|
+
tcp: firewallOptions.tcp,
|
|
618
|
+
iptables: firewallOptions.iptables,
|
|
619
|
+
cloud: firewallOptions.cloud,
|
|
620
|
+
cloudDryRun: firewallOptions.cloudDryRun,
|
|
621
|
+
cloudflare: firewallOptions.cloudflare,
|
|
622
|
+
aws: firewallOptions.aws,
|
|
623
|
+
gcp: firewallOptions.gcp,
|
|
632
624
|
});
|
|
633
625
|
}
|
|
634
626
|
} catch (e) {
|
|
@@ -659,7 +651,7 @@ module.exports = {
|
|
|
659
651
|
loggerProvider,
|
|
660
652
|
getLogger: (name = 'default', version = '1.0.0') => {
|
|
661
653
|
if (!loggerProvider) {
|
|
662
|
-
console.warn('[securenow] Logging is disabled (
|
|
654
|
+
console.warn('[securenow] Logging is disabled (config.logging.enabled=false). Enable it in .securenow/credentials.json to use getLogger().');
|
|
663
655
|
return null;
|
|
664
656
|
}
|
|
665
657
|
return loggerProvider.getLogger(name, version);
|