securenow 7.7.13 → 7.7.15

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/cli/auth.js CHANGED
@@ -75,7 +75,6 @@ async function loginWithBrowser() {
75
75
  const returnedState = url.searchParams.get('state');
76
76
  const appKey = url.searchParams.get('app_key');
77
77
  const appName = url.searchParams.get('app_name');
78
- const appInstance = url.searchParams.get('app_instance');
79
78
  const apiKey = url.searchParams.get('api_key');
80
79
 
81
80
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
@@ -96,11 +95,10 @@ async function loginWithBrowser() {
96
95
 
97
96
  if (token) {
98
97
  pendingToken = token;
99
- if (appKey || appName || appInstance) {
98
+ if (appKey || appName) {
100
99
  pendingApp = {
101
100
  key: appKey || null,
102
101
  name: appName || null,
103
- instance: appInstance || null,
104
102
  };
105
103
  }
106
104
  if (apiKey && apiKey.startsWith('snk_live_')) {
package/cli/config.js CHANGED
@@ -153,11 +153,10 @@ function getToken() {
153
153
  function setAuth(token, email, expiresAt, { local = false, app = null, enableFirewall = false } = {}) {
154
154
  const targetFile = credentialsFileForLocal(local);
155
155
  const payload = { ...loadJSON(targetFile), token, email, expiresAt };
156
- if (app && (app.key || app.name || app.instance)) {
156
+ if (app && (app.key || app.name)) {
157
157
  payload.app = {
158
158
  key: app.key || null,
159
159
  name: app.name || null,
160
- instance: app.instance || null,
161
160
  };
162
161
  }
163
162
  saveJSON(
@@ -201,23 +200,52 @@ function setApp(app, { local } = {}) {
201
200
  app: {
202
201
  key: app.key || null,
203
202
  name: app.name || null,
204
- instance: app.instance || null,
205
203
  },
206
204
  }) || {});
207
205
  }
208
206
 
209
207
  function ensureLocalGitignore() {
210
208
  const gitignorePath = path.join(process.cwd(), '.gitignore');
211
- const entry = '.securenow/';
209
+ const legacyEntry = '.securenow/';
210
+ const entries = [
211
+ '.securenow/credentials.json',
212
+ '.securenow/credentials.*.json',
213
+ '!.securenow/credentials.example.json',
214
+ '!.securenow/credentials.*.example.json',
215
+ ];
212
216
  try {
217
+ let content = '';
213
218
  if (fs.existsSync(gitignorePath)) {
214
- const content = fs.readFileSync(gitignorePath, 'utf8');
215
- if (!content.split('\n').some(line => line.trim() === entry)) {
216
- fs.appendFileSync(gitignorePath, `\n# SecureNow local credentials\n${entry}\n`);
219
+ content = fs.readFileSync(gitignorePath, 'utf8');
220
+ }
221
+
222
+ const lines = content ? content.split(/\r?\n/) : [];
223
+ let replacedLegacyEntry = false;
224
+ const nextLines = [];
225
+
226
+ for (const line of lines) {
227
+ if (line.trim() === legacyEntry) {
228
+ if (!replacedLegacyEntry) {
229
+ nextLines.push(...entries);
230
+ replacedLegacyEntry = true;
231
+ }
232
+ continue;
217
233
  }
218
- } else {
219
- fs.writeFileSync(gitignorePath, `# SecureNow local credentials\n${entry}\n`);
234
+ nextLines.push(line);
235
+ }
236
+
237
+ const hasLine = (entry) => nextLines.some(line => line.trim() === entry);
238
+ const missing = entries.filter(entry => !hasLine(entry));
239
+ if (!replacedLegacyEntry && missing.length === 0) return;
240
+
241
+ let nextContent = nextLines.join('\n').replace(/\s*$/, '');
242
+
243
+ if (missing.length > 0) {
244
+ const block = ['# SecureNow local credential files', '# Keep .securenow/ itself trackable for repo-owned docs/templates.', ...missing].join('\n');
245
+ nextContent = nextContent ? `${nextContent}\n\n${block}` : block;
220
246
  }
247
+
248
+ fs.writeFileSync(gitignorePath, `${nextContent}\n`);
221
249
  } catch {}
222
250
  }
223
251
 
@@ -25,7 +25,6 @@ function buildRuntimeCredentials(options = {}) {
25
25
  app: {
26
26
  key: creds.app?.key || null,
27
27
  name: creds.app?.name || null,
28
- instance: creds.app?.instance || appConfig.FREE_TRIAL_INSTANCE,
29
28
  },
30
29
  config: {
31
30
  ...(creds.config || {}),
@@ -41,6 +40,7 @@ function buildRuntimeCredentials(options = {}) {
41
40
  _securenow: {
42
41
  ...(creds._securenow || {}),
43
42
  note: 'Runtime SecureNow credentials and SDK defaults. Mount or copy this JSON as .securenow/credentials.json or .securenow/credentials.<environment>.json in production. Do not commit it.',
43
+ routing: 'Telemetry is sent to the default SecureNow ingestion gateway. The gateway routes by app.key, so runtime credentials do not expose per-instance collector URLs.',
44
44
  runtimeOnly: 'This file intentionally omits CLI OAuth fields: token, email, and expiresAt.',
45
45
  production: 'Production can use this same file shape instead of environment variables.',
46
46
  },
@@ -36,10 +36,10 @@ function resolvedConfig(options = {}) {
36
36
  apiKey,
37
37
  firewallLocalEnabled,
38
38
  apiUrl: config.getApiUrl(),
39
- loggingEnabled: appConfig.boolEnv('SECURENOW_LOGGING_ENABLED', true),
40
- captureBody: appConfig.boolEnv('SECURENOW_CAPTURE_BODY', true),
41
- captureMultipart: appConfig.boolEnv('SECURENOW_CAPTURE_MULTIPART', true),
42
- otelLogLevel: (process.env.OTEL_LOG_LEVEL != null ? process.env.OTEL_LOG_LEVEL : appConfig.env('OTEL_LOG_LEVEL')) || 'error',
39
+ loggingEnabled: appConfig.boolConfig('logging.enabled', true),
40
+ captureBody: appConfig.boolConfig('capture.body', true),
41
+ captureMultipart: appConfig.boolConfig('capture.multipart', true),
42
+ otelLogLevel: appConfig.configValue('otel.logLevel', 'error') || 'error',
43
43
  firewallEnabled,
44
44
  firewallLayers: {
45
45
  http: firewallEnabled,
@@ -467,8 +467,12 @@ async function doctor(_args, flags) {
467
467
  if (!cfg.appKey) {
468
468
  warnings.push('No app key resolved. Run `npx securenow login` or set app.key in .securenow/credentials.json.');
469
469
  }
470
- if (cfg.instance === 'https://freetrial.securenow.ai:4318') {
471
- warnings.push('Using the free-trial collector. For production, set app.instance in .securenow/credentials.json.');
470
+ if (cfg.instance === 'https://ingest.securenow.ai') {
471
+ warnings.push('Using the SecureNow ingest gateway. Dedicated instances are routed server-side after policy checks.');
472
+ } else if (cfg.instance === 'https://api.securenow.ai/api/otlp') {
473
+ warnings.push('Using the legacy SecureNow telemetry gateway path. Regenerate credentials so telemetry uses https://ingest.securenow.ai.');
474
+ } else if (cfg.instance === 'https://freetrial.securenow.ai:4318') {
475
+ warnings.push('Using the legacy free-trial collector directly. Regenerate credentials so telemetry flows through https://ingest.securenow.ai.');
472
476
  }
473
477
  if (!cfg.apiKey && token) {
474
478
  warnings.push('CLI/MCP is authenticated with your SecureNow session token. Runtime firewall enforcement key is missing. Run `npx securenow login` or `npx securenow api-key set snk_live_...` to refresh .securenow/credentials.json.');
package/cli/init.js CHANGED
@@ -234,6 +234,7 @@ function printAgentPrompt(kind, filename, major, project) {
234
234
  console.log([
235
235
  'Set up SecureNow in this existing Next.js project without using .env files.',
236
236
  'Use .securenow/credentials.json for local and production configuration; do not add .env files.',
237
+ 'Ignore only .securenow/credentials.json and .securenow/credentials.*.json in git; keep the .securenow/ directory itself trackable for repo-owned files.',
237
238
  commands
238
239
  ? `Use the project scripts for verification when appropriate: ${commands}. Ask before starting long-running dev/start servers, and ask which command to use if these scripts are not the right customer workflow.`
239
240
  : 'Ask the customer which command starts, builds, and tests this app because package.json does not expose an obvious script.',
@@ -7,10 +7,10 @@
7
7
  * Opt-out: set config.runtime.hideBanner=true in .securenow/credentials.json
8
8
  */
9
9
 
10
- const FREETRIAL_HOST = 'freetrial.securenow.ai';
10
+ const FREE_TRIAL_HOSTS = ['ingest.securenow.ai', 'freetrial.securenow.ai'];
11
11
 
12
12
  function isFreeTrial(endpointBase) {
13
- return !!endpointBase && endpointBase.includes(FREETRIAL_HOST);
13
+ return !!endpointBase && FREE_TRIAL_HOSTS.some((host) => endpointBase.includes(host));
14
14
  }
15
15
 
16
16
  /* istanbul ignore next — runs in browser, not Node */
package/mcp/catalog.js CHANGED
@@ -17,7 +17,7 @@ Primary goals:
17
17
 
18
18
  Safety rules:
19
19
  - Do not print full API keys, JWTs, tokens, or .securenow/credentials.json. Mask secrets.
20
- - Do not commit secrets. Ensure .securenow/ is in .gitignore.
20
+ - Do not commit secrets. Ignore only local SecureNow credential files (.securenow/credentials.json and .securenow/credentials.*.json); keep the .securenow/ directory itself trackable for repo-owned docs/templates.
21
21
  - Do not manually browse to a SecureNow auth URL. Always start auth with npx securenow login so the CLI generates the required callback and state.
22
22
  - If the browser says "Missing callback parameter", you opened the wrong URL: rerun npx securenow login from the project root.
23
23
  - Do not skip login, app selection, firewall connection, or verification unless I explicitly say to.
@@ -42,7 +42,7 @@ Runbook:
42
42
  - Confirm .securenow/credentials.json exists.
43
43
  - Confirm it has SecureNow's default config/explanations block.
44
44
  - Confirm it has an app key/name/instance and a firewall API key after login/app selection.
45
- - Confirm .securenow/ is ignored by git.
45
+ - Confirm .securenow/credentials.json and any .securenow/credentials.*.json runtime files are ignored by git, without ignoring the entire .securenow/ directory.
46
46
  6. Run npx securenow init. If it fails with ui.header is not a function or another CLI bug, upgrade to securenow@latest, verify >=7.5.1, and retry. Do not silently ignore init failures.
47
47
  7. Configure the least invasive framework-specific integration:
48
48
  - Next.js: preserve instrumentation.js/ts. Register securenow/nextjs only when NEXT_RUNTIME is nodejs. In ESM files, use createRequire before require("securenow/nextjs"). Include require("securenow/nextjs-auto-capture") for body capture. For Next 15+, add securenow to serverExternalPackages. For older Next.js, use experimental.serverComponentsExternalPackages. Preserve proxy.js/middleware.js.
@@ -1,14 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * Next.js configuration helpers for SecureNow
4
+ * Next.js configuration helpers for SecureNow
5
5
  *
6
6
  * Usage (recommended — zero-list approach):
7
7
  *
8
- * const { withSecureNow } = require('securenow/nextjs-webpack-config');
9
- * module.exports = withSecureNow({
10
- * // your existing next.config options
11
- * });
8
+ * const { withSecureNow } = require('securenow/nextjs-webpack-config');
9
+ * module.exports = withSecureNow({
10
+ * // your existing next.config options
11
+ * });
12
+ *
13
+ * This externalizes SecureNow for server bundles and includes the package in
14
+ * standalone output so runtime modules such as the firewall are available.
12
15
  *
13
16
  * Legacy webpack-only helper (still exported for backwards compat):
14
17
  *
@@ -19,6 +22,8 @@
19
22
  const EXTERNAL_PACKAGES = [
20
23
  'securenow',
21
24
  ];
25
+ const OUTPUT_TRACE_INCLUDE_KEY = '/*';
26
+ const SECURENOW_OUTPUT_TRACE_INCLUDE = './node_modules/securenow/**/*';
22
27
 
23
28
  function detectNextMajor() {
24
29
  try {
@@ -30,8 +35,9 @@ function detectNextMajor() {
30
35
  }
31
36
 
32
37
  /**
33
- * Wrap a Next.js config object to auto-externalize SecureNow + OTel
34
- * packages and enable the instrumentation hook.
38
+ * Wrap a Next.js config object to auto-externalize SecureNow,
39
+ * include runtime files in standalone output, and enable the
40
+ * instrumentation hook.
35
41
  *
36
42
  * module.exports = withSecureNow({ reactStrictMode: true });
37
43
  */
@@ -48,16 +54,18 @@ function withSecureNow(userConfig) {
48
54
  ...(cfg.serverExternalPackages || []),
49
55
  ...EXTERNAL_PACKAGES,
50
56
  ]);
51
- } else {
52
- cfg.experimental = { ...(cfg.experimental || {}) };
53
- cfg.experimental.instrumentationHook = true;
57
+ } else {
58
+ cfg.experimental = { ...(cfg.experimental || {}) };
59
+ cfg.experimental.instrumentationHook = true;
54
60
  cfg.experimental.serverComponentsExternalPackages = dedup([
55
61
  ...(cfg.experimental.serverComponentsExternalPackages || []),
56
62
  ...EXTERNAL_PACKAGES,
57
- ]);
58
- }
59
-
60
- const origWebpack = cfg.webpack;
63
+ ]);
64
+ }
65
+
66
+ cfg.outputFileTracingIncludes = mergeOutputFileTracingIncludes(cfg.outputFileTracingIncludes);
67
+
68
+ const origWebpack = cfg.webpack;
61
69
  cfg.webpack = (config, options) => {
62
70
  const c = origWebpack ? origWebpack(config, options) : config;
63
71
  return getSecureNowWebpackConfig(c, options);
@@ -66,9 +74,24 @@ function withSecureNow(userConfig) {
66
74
  return cfg;
67
75
  }
68
76
 
69
- function dedup(arr) {
70
- return [...new Set(arr)];
71
- }
77
+ function dedup(arr) {
78
+ return [...new Set(arr)];
79
+ }
80
+
81
+ function mergeOutputFileTracingIncludes(existing) {
82
+ const includes = { ...(existing || {}) };
83
+ const current = includes[OUTPUT_TRACE_INCLUDE_KEY];
84
+
85
+ if (Array.isArray(current)) {
86
+ includes[OUTPUT_TRACE_INCLUDE_KEY] = dedup([...current, SECURENOW_OUTPUT_TRACE_INCLUDE]);
87
+ } else if (typeof current === 'string') {
88
+ includes[OUTPUT_TRACE_INCLUDE_KEY] = dedup([current, SECURENOW_OUTPUT_TRACE_INCLUDE]);
89
+ } else if (!current) {
90
+ includes[OUTPUT_TRACE_INCLUDE_KEY] = [SECURENOW_OUTPUT_TRACE_INCLUDE];
91
+ }
92
+
93
+ return includes;
94
+ }
72
95
 
73
96
  /**
74
97
  * Legacy: suppress OTel webpack warnings and add externals.
@@ -97,4 +120,4 @@ function getSecureNowWebpackConfig(config, options) {
97
120
  return config;
98
121
  }
99
122
 
100
- module.exports = { withSecureNow, getSecureNowWebpackConfig, EXTERNAL_PACKAGES };
123
+ module.exports = { withSecureNow, getSecureNowWebpackConfig, EXTERNAL_PACKAGES };
package/nextjs.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- /**
2
- * SecureNow Next.js Integration TypeScript Declarations
3
- */
4
-
5
- export interface RegisterOptions {
1
+ /**
2
+ * SecureNow Next.js Integration TypeScript Declarations
3
+ */
4
+
5
+ export interface RegisterOptions {
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 RegisterOptions {
11
11
 
12
12
  /**
13
13
  * OTLP endpoint for traces
14
- * @default .securenow/credentials.json app.instance, or https://freetrial.securenow.ai:4318
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,26 +24,26 @@ export interface RegisterOptions {
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
26
- * @default false
27
- */
28
- noUuid?: boolean;
29
-
27
+
28
+ /**
29
+ * Don't append UUID to service name
30
+ * @default false
31
+ */
32
+ noUuid?: boolean;
33
+
30
34
  /**
31
35
  * Enable request body capture
32
36
  * @default true from .securenow/credentials.json secure defaults
33
37
  */
34
38
  captureBody?: boolean;
35
39
  }
36
-
37
- /**
38
- * Register SecureNow OpenTelemetry instrumentation for Next.js
39
- *
40
- * @param options - Optional configuration options
41
- *
42
- * @example
40
+
41
+ /**
42
+ * Register SecureNow OpenTelemetry instrumentation for Next.js
43
+ *
44
+ * @param options - Optional configuration options
45
+ *
46
+ * @example
43
47
  * ```typescript
44
48
  * // instrumentation.ts
45
49
  * import { createRequire } from 'node:module';
@@ -53,46 +57,46 @@ export interface RegisterOptions {
53
57
  * require('securenow/nextjs-auto-capture');
54
58
  * }
55
59
  * ```
56
- *
57
- * @example
58
- * ```typescript
59
- * // With custom options
60
- * import { registerSecureNow } from 'securenow/nextjs';
61
- *
62
- * export function register() {
63
- * registerSecureNow({
64
- * serviceName: 'my-nextjs-app',
65
- * endpoint: 'http://your-otlp-backend.example.com:4318',
66
- * noUuid: true,
67
- * });
68
- * }
69
- * ```
70
- */
71
- export function registerSecureNow(options?: RegisterOptions): void;
72
-
73
- /**
74
- * Default sensitive fields that are automatically redacted from traces
75
- */
76
- export const DEFAULT_SENSITIVE_FIELDS: readonly string[];
77
-
78
- /**
79
- * Redact sensitive fields from an object
80
- * @param obj - Object to redact
81
- * @param sensitiveFields - Array of field names to redact (case-insensitive substring match)
82
- * @returns Redacted copy of the object
83
- */
84
- export function redactSensitiveData<T = any>(
85
- obj: T,
86
- sensitiveFields?: string[]
87
- ): T;
88
-
89
- /**
90
- * Redact sensitive data from GraphQL query strings
91
- * @param query - GraphQL query string
92
- * @param sensitiveFields - Array of field names to redact
93
- * @returns Redacted query string
94
- */
95
- export function redactGraphQLQuery(
96
- query: string,
97
- sensitiveFields?: string[]
98
- ): string;
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // With custom options
64
+ * import { registerSecureNow } from 'securenow/nextjs';
65
+ *
66
+ * export function register() {
67
+ * registerSecureNow({
68
+ * serviceName: 'my-nextjs-app',
69
+ * endpoint: 'http://your-otlp-backend.example.com:4318',
70
+ * noUuid: true,
71
+ * });
72
+ * }
73
+ * ```
74
+ */
75
+ export function registerSecureNow(options?: RegisterOptions): void;
76
+
77
+ /**
78
+ * Default sensitive fields that are automatically redacted from traces
79
+ */
80
+ export const DEFAULT_SENSITIVE_FIELDS: readonly string[];
81
+
82
+ /**
83
+ * Redact sensitive fields from an object
84
+ * @param obj - Object to redact
85
+ * @param sensitiveFields - Array of field names to redact (case-insensitive substring match)
86
+ * @returns Redacted copy of the object
87
+ */
88
+ export function redactSensitiveData<T = any>(
89
+ obj: T,
90
+ sensitiveFields?: string[]
91
+ ): T;
92
+
93
+ /**
94
+ * Redact sensitive data from GraphQL query strings
95
+ * @param query - GraphQL query string
96
+ * @param sensitiveFields - Array of field names to redact
97
+ * @returns Redacted query string
98
+ */
99
+ export function redactGraphQLQuery(
100
+ query: string,
101
+ sensitiveFields?: string[]
102
+ ): string;