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/SKILL-CLI.md CHANGED
@@ -35,13 +35,13 @@ securenow login --token <JWT> # headless / CI login (get token from dashboard
35
35
  securenow whoami # verify session (shows email, app, auth source)
36
36
  ```
37
37
 
38
- **Zero-config flow (v7+):** the browser step lets the user pick (or create) an app. The CLI stores the app's **key (UUID)**, **name**, and **instance URL** in `.securenow/credentials.json`. The SDK reads this file at boot and sends traces/logs to the right app bucket — **no env vars required for local dev or production**.
38
+ **Zero-config flow (v7+):** the browser step lets the user pick (or create) an app. The CLI stores the app's **key (UUID)** and **name** in `.securenow/credentials.json`. The SDK sends traces/logs to the default SecureNow ingestion gateway, which routes by app key — **no env vars or per-instance collector URLs required for local dev or production**.
39
39
 
40
40
  **Default-on security (v7.5.1+):** after picking or creating the app, `securenow login` turns on that app's firewall toggle, mints an API key with `firewall:read + blocklist:read + allowlist:read` scopes, and writes it into `.securenow/credentials.json`. Traces, logs, POST body capture, multipart metadata capture, and the firewall are enabled by default. No `SECURENOW_API_KEY` env var is needed. To add or rotate a key later without re-running login, use `securenow api-key set snk_live_...` (see [API Key Management](#api-key-management) below).
41
41
 
42
- Credentials resolve in order: project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order. Legacy env vars are fallback-only for existing deployments and do not choose the credentials filename.
42
+ Credentials resolve in order: project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order. Runtime config is credentials-json based; legacy env fallbacks are disabled unless `SECURENOW_ENABLE_LEGACY_ENV=1` is set and never choose the credentials filename.
43
43
 
44
- The **firewall API key** should live in the same credentials file as `apiKey`. Legacy `SECURENOW_API_KEY` overrides are honored only when they start with `snk_live_`.
44
+ The **firewall API key** should live in the same credentials file as `apiKey`.
45
45
 
46
46
  For CI / Docker / production, use `securenow credentials runtime --env production` to generate a tokenless runtime file, then mount/copy it as `.securenow/credentials.json`. Since v7.7.2, mounting the generated `.securenow/credentials.production.json` filename directly also works when `credentials.json` is absent.
47
47
 
@@ -83,7 +83,7 @@ Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-proje
83
83
  | `.securenow/credentials.json` | `token`, `email`, `expiresAt`, `apiKey`, `app`, `config`, `_securenow.explanations` (project-local default) |
84
84
  | `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `securenow credentials runtime --env <environment>`; read in a fixed order, not selected from env vars |
85
85
 
86
- **Credential resolution order:** `.securenow/credentials.json` (project) -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> `~/.securenow/credentials.json` (global) -> global named runtime credentials in the same fixed order. Legacy env vars are fallback-only for existing deployments.
86
+ **Credential resolution order:** `.securenow/credentials.json` (project) -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> `~/.securenow/credentials.json` (global) -> global named runtime credentials in the same fixed order. Legacy env fallbacks are disabled unless `SECURENOW_ENABLE_LEGACY_ENV=1` is set.
87
87
 
88
88
  **Firewall API key resolution (v7.5.1+):** project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order. Use `securenow login` for default setup or `securenow api-key set` to rotate a key without touching env vars.
89
89
 
@@ -95,7 +95,7 @@ securenow config get defaultApp # show one
95
95
  securenow config path # print file paths + active auth source
96
96
  ```
97
97
 
98
- Legacy CLI overrides still exist for existing automation (`SECURENOW_TOKEN`, `SECURENOW_API_URL`, `SECURENOW_APP_URL`, `SECURENOW_APP`), but file credentials are the default path.
98
+ Legacy CLI overrides still exist for operator automation, but runtime SDK config should stay in the credentials file.
99
99
 
100
100
  ## Global Flags
101
101
 
@@ -172,7 +172,7 @@ securenow init [--env local] [--key <API_KEY>]
172
172
  ```
173
173
 
174
174
  Auto-detects framework (Next.js, Nuxt, Express, Fastify, Koa, Hapi, Node) from `package.json`. Then:
175
- - **Credentials**: ensures `.securenow/credentials.json` exists, has secure defaults/explanations, and `.securenow/` is gitignored
175
+ - **Credentials**: ensures `.securenow/credentials.json` exists, has secure defaults/explanations, and local credential JSON files are gitignored without ignoring the whole `.securenow/` directory
176
176
  - **Next.js**: creates `instrumentation.ts/js` with `securenow/nextjs` + `securenow/nextjs-auto-capture`, and adds `serverExternalPackages: ['securenow']` plus `outputFileTracingIncludes` when the config can be patched safely
177
177
  - **Existing Next.js files**: prints a Codex/Claude-ready merge prompt when instrumentation or config already exists or is too custom to safely patch
178
178
  - **Nuxt**: tells you to add `securenow/nuxt` to modules
@@ -473,7 +473,7 @@ securenow test-span
473
473
  securenow test-span "ci.smoke-test" # custom span name
474
474
  ```
475
475
 
476
- Both commands use the resolved collector settings from `.securenow/credentials.json` (`app.instance`, `config.otel.*`) and return non-zero on HTTP errors so CI/cron can detect failures.
476
+ Both commands use the default SecureNow ingestion gateway plus optional advanced `config.otel.*` overrides from `.securenow/credentials.json`, and return non-zero on HTTP errors so CI/cron can detect failures.
477
477
 
478
478
  ### Utilities — Redaction, CIDR, Diagnostics
479
479
 
@@ -497,7 +497,7 @@ securenow doctor # exits 0 if healthy, 1 otherwise
497
497
  securenow doctor --json
498
498
  ```
499
499
 
500
- The `redact` command accepts a JSON string or `@path/to/file.json`, layers your `--fields` flag on top of `DEFAULT_SENSITIVE_FIELDS`, and also honors `SECURENOW_SENSITIVE_FIELDS` from the env. Exit code from `cidr match` is `0` if the IP matches the list, `2` otherwise — scriptable.
500
+ The `redact` command accepts a JSON string or `@path/to/file.json` and layers your `--fields` flag on top of `DEFAULT_SENSITIVE_FIELDS`. Exit code from `cidr match` is `0` if the IP matches the list, `2` otherwise — scriptable.
501
501
 
502
502
  ---
503
503
 
package/app-config.js CHANGED
@@ -8,17 +8,19 @@
8
8
  * ./.securenow/credentials.production.json are also accepted when the
9
9
  * canonical file is not present. Filename selection is deterministic and does
10
10
  * not read environment variables.
11
- * Legacy environment variables are only fallback inputs for existing installs;
12
- * every SDK setting has a file-backed equivalent so customers do not need .env
13
- * files.
11
+ * Legacy environment fallbacks are disabled by default; every SDK setting has
12
+ * a file-backed equivalent so customers do not need .env files.
14
13
  */
15
14
 
16
15
  const fs = require('fs');
17
16
  const path = require('path');
18
17
  const os = require('os');
19
18
 
20
- const FREE_TRIAL_INSTANCE = 'https://freetrial.securenow.ai:4318';
19
+ const FREE_TRIAL_INSTANCE = 'https://ingest.securenow.ai';
21
20
  const DEFAULT_API_URL = 'https://api.securenow.ai';
21
+ const DEFAULT_FIREWALL_API_URL = FREE_TRIAL_INSTANCE;
22
+ const LEGACY_SECURENOW_GATEWAY = 'https://api.securenow.ai/api/otlp';
23
+ const LEGACY_ENV_FALLBACK_FLAG = 'SECURENOW_ENABLE_LEGACY_ENV';
22
24
  const CONFIG_SCHEMA_VERSION = 2;
23
25
  const CREDENTIAL_FILE_ENVIRONMENTS = Object.freeze([
24
26
  'staging',
@@ -92,13 +94,13 @@ const CONFIG_EXPLANATIONS = Object.freeze({
92
94
  'apiKey': 'Scoped firewall API key (`snk_live_...`) minted by login or `securenow api-key set`. Secret: do not commit.',
93
95
  'app.key': 'SecureNow application routing UUID. The SDK uses this as OTel service.name so dashboard queries match exactly.',
94
96
  'app.name': 'Human-readable app label shown in CLI output.',
95
- 'app.instance': 'OTLP collector base URL for this app. Production should provide this same credentials file at .securenow/credentials.json.',
97
+ 'app.routing': 'Telemetry uses the default SecureNow ingestion gateway and routes by app.key; runtime credentials do not expose per-instance collector URLs.',
96
98
  'config.logging.enabled': 'Secure default: console log forwarding is on. Set false to disable OTLP logs.',
97
99
  'config.capture.body': 'Secure default: JSON, GraphQL, and form request bodies are captured with redaction.',
98
100
  'config.capture.multipart': 'Secure default: multipart text fields and file metadata are captured. File content is never buffered.',
99
101
  'config.capture.maxBodySize': 'Maximum body bytes captured per request. Default 10KB limits memory and sensitive data exposure.',
100
102
  'config.capture.sensitiveFields': 'Extra field-name fragments to redact in addition to SecureNow built-ins.',
101
- 'config.otel.endpoint': 'Optional OTLP base endpoint override. Usually app.instance is enough.',
103
+ 'config.otel.endpoint': 'Optional OTLP base endpoint override for advanced/self-hosted collectors. Leave null for the SecureNow ingestion gateway.',
102
104
  'config.otel.tracesEndpoint': 'Optional full traces endpoint override, for split collectors.',
103
105
  'config.otel.logsEndpoint': 'Optional full logs endpoint override, for split collectors.',
104
106
  'config.otel.headers': 'Optional OTLP headers. The SDK auto-adds x-api-key from app.key when missing.',
@@ -110,7 +112,7 @@ const CONFIG_EXPLANATIONS = Object.freeze({
110
112
  'config.runtime.testSpan': 'If true, emit a startup smoke span. Prefer `npx securenow test-span` for manual checks.',
111
113
  'config.runtime.hideBanner': 'Hide the free-trial response banner when using the managed free-trial collector.',
112
114
  'config.firewall.enabled': 'Secure default: app firewall enforcement starts when apiKey is present and the dashboard toggle is on.',
113
- 'config.firewall.apiUrl': 'SecureNow API base URL for firewall sync.',
115
+ 'config.firewall.apiUrl': 'Optional SecureNow firewall control-plane base URL. Leave unset/default so hosted SDKs sync through the SecureNow ingest gateway.',
114
116
  'config.firewall.versionCheckInterval': 'Seconds between lightweight firewall version/ETag checks.',
115
117
  'config.firewall.syncInterval': 'Seconds between safety-net full firewall blocklist syncs.',
116
118
  'config.firewall.failMode': 'open allows traffic if SecureNow is temporarily unreachable; closed blocks all on sync failure.',
@@ -392,11 +394,14 @@ function mergeCredentials(globalCredentials, localCredentials) {
392
394
  function withCredentialDefaults(credentials) {
393
395
  if (!credentials || typeof credentials !== 'object') return null;
394
396
  const out = clone(credentials);
397
+ if (out.app && typeof out.app === 'object' && !Array.isArray(out.app)) {
398
+ delete out.app.instance;
399
+ }
395
400
  out.config = mergeMissing(out.config, DEFAULT_CONFIG);
396
401
  out._securenow = mergeMissing(out._securenow, {
397
402
  schemaVersion: CONFIG_SCHEMA_VERSION,
398
403
  note: 'Local SecureNow credentials and secure SDK defaults. This file may contain secrets; keep .securenow/ in .gitignore.',
399
- precedence: 'The same credentials file works in local development and production. Legacy environment variables are only fallback inputs when this file does not define a value.',
404
+ precedence: `The same credentials file works in local development and production. SDK runtime config is read from credentials JSON. Legacy environment fallbacks are disabled unless ${LEGACY_ENV_FALLBACK_FLAG}=1 is set.`,
400
405
  explanations: CONFIG_EXPLANATIONS,
401
406
  });
402
407
  return out;
@@ -412,10 +417,82 @@ function getPath(obj, dotted) {
412
417
  return cur;
413
418
  }
414
419
 
420
+ function legacyEnvFallbackEnabled() {
421
+ const raw =
422
+ process.env[LEGACY_ENV_FALLBACK_FLAG] ??
423
+ process.env.SECURENOW_ALLOW_ENV_CONFIG ??
424
+ process.env.SECURENOW_LEGACY_ENV;
425
+ return /^(1|true|yes)$/i.test(String(raw || '').trim());
426
+ }
427
+
415
428
  function rawEnv(key) {
429
+ if (!legacyEnvFallbackEnabled()) return undefined;
416
430
  return process.env[key] ?? process.env[key.toUpperCase()] ?? process.env[key.toLowerCase()];
417
431
  }
418
432
 
433
+ function normalizeInstanceEndpoint(value) {
434
+ if (value === undefined || value === null || value === '') return value;
435
+ const endpoint = String(value).trim().replace(/\/$/, '');
436
+ if (!endpoint) return endpoint;
437
+ if (endpoint === LEGACY_SECURENOW_GATEWAY) return FREE_TRIAL_INSTANCE;
438
+
439
+ try {
440
+ const parseable = /^[a-z][a-z0-9+.-]*:\/\//i.test(endpoint) ? endpoint : `https://${endpoint}`;
441
+ const url = new URL(parseable);
442
+ if (url.port === '4318' && url.hostname.toLowerCase().endsWith('.securenow.ai')) {
443
+ return FREE_TRIAL_INSTANCE;
444
+ }
445
+ } catch {}
446
+
447
+ return endpoint;
448
+ }
449
+
450
+ function normalizeSignalEndpoint(value, signalType) {
451
+ if (value === undefined || value === null || value === '') return value;
452
+ const endpoint = String(value).trim().replace(/\/$/, '');
453
+ const signalPath = `/v1/${signalType}`;
454
+ if (endpoint.endsWith(signalPath)) {
455
+ const base = normalizeInstanceEndpoint(endpoint.slice(0, -signalPath.length));
456
+ return `${base}${signalPath}`;
457
+ }
458
+ return endpoint;
459
+ }
460
+
461
+ function normalizeFirewallApiUrl(value) {
462
+ const raw = pick(value);
463
+ if (raw == null) return DEFAULT_FIREWALL_API_URL;
464
+ const endpoint = normalizeInstanceEndpoint(raw);
465
+ if (!endpoint) return DEFAULT_FIREWALL_API_URL;
466
+ const trimmed = String(endpoint).trim().replace(/\/$/, '').replace(/\/api(?:\/v1)?$/i, '');
467
+
468
+ // api.securenow.ai was the historical SDK default. Hosted runtime sync now
469
+ // shares the ingest gateway so telemetry and firewall state fail or recover
470
+ // together, and so customer apps need only one public SecureNow egress host.
471
+ if (trimmed === DEFAULT_API_URL || trimmed === LEGACY_SECURENOW_GATEWAY) {
472
+ return DEFAULT_FIREWALL_API_URL;
473
+ }
474
+ if (trimmed === DEFAULT_FIREWALL_API_URL) return DEFAULT_FIREWALL_API_URL;
475
+
476
+ return trimmed;
477
+ }
478
+
479
+ function resolveFirewallApiUrl() {
480
+ return normalizeFirewallApiUrl(configValue('firewall.apiUrl', DEFAULT_API_URL));
481
+ }
482
+
483
+ function resolveFirewallApiFallbacks(primary = resolveFirewallApiUrl()) {
484
+ const normalizedPrimary = normalizeFirewallApiUrl(primary);
485
+ const fallbacks = [];
486
+
487
+ if (normalizedPrimary === DEFAULT_FIREWALL_API_URL) {
488
+ fallbacks.push(DEFAULT_API_URL);
489
+ } else if (normalizedPrimary === DEFAULT_API_URL) {
490
+ fallbacks.push(DEFAULT_FIREWALL_API_URL);
491
+ }
492
+
493
+ return uniq(fallbacks.filter((url) => url && url !== normalizedPrimary));
494
+ }
495
+
419
496
  function pick(value) {
420
497
  if (value === undefined || value === null) return null;
421
498
  if (typeof value === 'string') {
@@ -514,6 +591,22 @@ function resolveConfigPath(configPath, envKeys = [], fallback) {
514
591
  return fallback;
515
592
  }
516
593
 
594
+ function configValue(configPath, fallback) {
595
+ return resolveConfigPath(configPath, [], fallback);
596
+ }
597
+
598
+ function boolConfig(configPath, fallback = false) {
599
+ return parseBool(configValue(configPath), fallback);
600
+ }
601
+
602
+ function numberConfig(configPath, fallback, min) {
603
+ return parseNumber(configValue(configPath), fallback, min);
604
+ }
605
+
606
+ function listConfig(configPath) {
607
+ return parseList(configValue(configPath));
608
+ }
609
+
517
610
  function resolveAppKey() {
518
611
  const creds = loadCredentials();
519
612
  if (creds && creds.app && pick(creds.app.key)) return String(pick(creds.app.key));
@@ -559,18 +652,13 @@ function resolveApiKey() {
559
652
 
560
653
  function resolveInstance() {
561
654
  const fromConfig = pick(resolveConfigPath('otel.endpoint'));
562
- if (fromConfig) return String(fromConfig).replace(/\/$/, '');
563
-
564
- const creds = loadCredentials();
565
- if (creds && creds.app && pick(creds.app.instance)) {
566
- return String(pick(creds.app.instance)).replace(/\/$/, '');
567
- }
655
+ if (fromConfig) return normalizeInstanceEndpoint(fromConfig);
568
656
 
569
657
  const fromEnv =
570
658
  pick(rawEnv('SECURENOW_INSTANCE')) ||
571
659
  pick(rawEnv('securenow_instance')) ||
572
660
  pick(rawEnv('OTEL_EXPORTER_OTLP_ENDPOINT'));
573
- if (fromEnv) return String(fromEnv).replace(/\/$/, '');
661
+ if (fromEnv) return normalizeInstanceEndpoint(fromEnv);
574
662
 
575
663
  return FREE_TRIAL_INSTANCE;
576
664
  }
@@ -587,13 +675,7 @@ function resolveAll() {
587
675
  }
588
676
 
589
677
  function resolveDeploymentEnvironment() {
590
- return normalizeDeploymentEnvironment(
591
- resolveConfigPath('runtime.deploymentEnvironment', [
592
- 'SECURENOW_ENVIRONMENT',
593
- 'SECURENOW_DEPLOYMENT_ENVIRONMENT',
594
- 'NODE_ENV',
595
- ])
596
- );
678
+ return normalizeDeploymentEnvironment(resolveConfigPath('runtime.deploymentEnvironment'));
597
679
  }
598
680
 
599
681
  function resolveNoUuid(opts = {}) {
@@ -643,11 +725,15 @@ function listEnv(key) {
643
725
  }
644
726
 
645
727
  function resolveOtlpHeaders() {
646
- const headers = parseHeaders(resolveConfigPath('otel.headers', ['OTEL_EXPORTER_OTLP_HEADERS'], {}));
728
+ const headers = parseHeaders(configValue('otel.headers', {}));
647
729
  const appKey = resolveAppKey();
648
730
  if (appKey && !headers['x-api-key']) {
649
731
  headers['x-api-key'] = appKey;
650
732
  }
733
+ const deploymentEnvironment = resolveDeploymentEnvironment();
734
+ if (deploymentEnvironment && !headers['x-securenow-environment']) {
735
+ headers['x-securenow-environment'] = deploymentEnvironment;
736
+ }
651
737
  return headers;
652
738
  }
653
739
 
@@ -656,11 +742,13 @@ function resolveOtlpHeaderString() {
656
742
  }
657
743
 
658
744
  function resolveEndpoints(options = {}) {
659
- const endpointBase = String(options.endpoint || resolveInstance()).replace(/\/$/, '');
745
+ const endpointBase = String(normalizeInstanceEndpoint(options.endpoint || resolveInstance())).replace(/\/$/, '');
746
+ const tracesOverride = configValue('otel.tracesEndpoint');
747
+ const logsOverride = configValue('otel.logsEndpoint');
660
748
  return {
661
749
  endpointBase,
662
- tracesUrl: env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`,
663
- logsUrl: env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`,
750
+ tracesUrl: normalizeSignalEndpoint(tracesOverride, 'traces') || `${endpointBase}/v1/traces`,
751
+ logsUrl: normalizeSignalEndpoint(logsOverride, 'logs') || `${endpointBase}/v1/logs`,
664
752
  headers: resolveOtlpHeaders(),
665
753
  };
666
754
  }
@@ -673,33 +761,35 @@ function resolveFirewallEnabled() {
673
761
  }
674
762
 
675
763
  function resolveFirewallOptions() {
764
+ const apiUrl = resolveFirewallApiUrl();
676
765
  return {
677
766
  apiKey: resolveApiKey(),
678
767
  appKey: resolveAppKey() || null,
679
768
  environment: resolveDeploymentEnvironment(),
680
769
  enabled: resolveFirewallEnabled(),
681
- apiUrl: env('SECURENOW_API_URL') || DEFAULT_API_URL,
682
- versionCheckInterval: numberEnv('SECURENOW_FIREWALL_VERSION_INTERVAL', 10, 1),
683
- syncInterval: numberEnv('SECURENOW_FIREWALL_SYNC_INTERVAL', 3600, 1),
684
- failMode: env('SECURENOW_FIREWALL_FAIL_MODE') || 'open',
685
- statusCode: numberEnv('SECURENOW_FIREWALL_STATUS_CODE', 403, 100),
686
- log: boolEnv('SECURENOW_FIREWALL_LOG', true),
687
- tcp: boolEnv('SECURENOW_FIREWALL_TCP', false),
688
- iptables: boolEnv('SECURENOW_FIREWALL_IPTABLES', false),
689
- cloud: env('SECURENOW_FIREWALL_CLOUD') || null,
690
- cloudDryRun: boolEnv('SECURENOW_FIREWALL_CLOUD_DRY_RUN', false),
770
+ apiUrl,
771
+ apiUrlFallbacks: resolveFirewallApiFallbacks(apiUrl),
772
+ versionCheckInterval: numberConfig('firewall.versionCheckInterval', 10, 1),
773
+ syncInterval: numberConfig('firewall.syncInterval', 3600, 1),
774
+ failMode: configValue('firewall.failMode', 'open') || 'open',
775
+ statusCode: numberConfig('firewall.statusCode', 403, 100),
776
+ log: boolConfig('firewall.log', true),
777
+ tcp: boolConfig('firewall.tcp', false),
778
+ iptables: boolConfig('firewall.iptables', false),
779
+ cloud: configValue('firewall.cloud', null) || null,
780
+ cloudDryRun: boolConfig('firewall.cloudDryRun', false),
691
781
  cloudflare: {
692
- apiToken: env('CLOUDFLARE_API_TOKEN') || null,
693
- accountId: env('CLOUDFLARE_ACCOUNT_ID') || null,
782
+ apiToken: resolveConfigPath('firewall.cloudflare.apiToken', [], null) || null,
783
+ accountId: resolveConfigPath('firewall.cloudflare.accountId', [], null) || null,
694
784
  },
695
785
  aws: {
696
- wafIpSetId: env('AWS_WAF_IP_SET_ID') || null,
697
- wafIpSetName: env('AWS_WAF_IP_SET_NAME') || 'securenow-blocklist',
698
- wafScope: env('AWS_WAF_SCOPE') || 'REGIONAL',
786
+ wafIpSetId: resolveConfigPath('firewall.aws.wafIpSetId', [], null) || null,
787
+ wafIpSetName: resolveConfigPath('firewall.aws.wafIpSetName', [], 'securenow-blocklist') || 'securenow-blocklist',
788
+ wafScope: resolveConfigPath('firewall.aws.wafScope', [], 'REGIONAL') || 'REGIONAL',
699
789
  },
700
790
  gcp: {
701
- projectId: env('GCP_PROJECT_ID') || null,
702
- securityPolicy: env('GCP_SECURITY_POLICY') || null,
791
+ projectId: resolveConfigPath('firewall.gcp.projectId', [], null) || null,
792
+ securityPolicy: resolveConfigPath('firewall.gcp.securityPolicy', [], null) || null,
703
793
  },
704
794
  };
705
795
  }
@@ -707,6 +797,9 @@ function resolveFirewallOptions() {
707
797
  module.exports = {
708
798
  FREE_TRIAL_INSTANCE,
709
799
  DEFAULT_API_URL,
800
+ DEFAULT_FIREWALL_API_URL,
801
+ LEGACY_SECURENOW_GATEWAY,
802
+ LEGACY_ENV_FALLBACK_FLAG,
710
803
  CONFIG_SCHEMA_VERSION,
711
804
  CREDENTIAL_FILE_ENVIRONMENTS,
712
805
  DEFAULT_CONFIG,
@@ -715,12 +808,17 @@ module.exports = {
715
808
  withCredentialDefaults,
716
809
  mergeCredentials,
717
810
  resolveConfigPath,
811
+ configValue,
812
+ boolConfig,
813
+ numberConfig,
814
+ listConfig,
718
815
  resolveAppKey,
719
816
  resolveAppName,
720
817
  resolveAppId,
721
818
  resolveApiKey,
722
819
  resolveInstance,
723
820
  normalizeDeploymentEnvironment,
821
+ legacyEnvFallbackEnabled,
724
822
  resolveDeploymentEnvironment,
725
823
  resolveAll,
726
824
  resolveNoUuid,
@@ -728,12 +826,17 @@ module.exports = {
728
826
  resolveOtlpHeaderString,
729
827
  resolveEndpoints,
730
828
  resolveFirewallEnabled,
829
+ normalizeFirewallApiUrl,
830
+ resolveFirewallApiUrl,
831
+ resolveFirewallApiFallbacks,
731
832
  resolveFirewallOptions,
732
833
  env,
733
834
  boolEnv,
734
835
  numberEnv,
735
836
  listEnv,
736
837
  parseHeaders,
838
+ normalizeInstanceEndpoint,
839
+ normalizeSignalEndpoint,
737
840
  headersToString,
738
841
  credentialRelativePaths,
739
842
  resolveLocalCredentialsFile,