securenow 7.7.7 → 7.7.8

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/firewall.js CHANGED
@@ -36,10 +36,11 @@ async function status(args, flags) {
36
36
  ['App scope', appKey || 'all apps'],
37
37
  ['Environment', data.environment || environment],
38
38
  ['Last updated', data.updatedAt || 'unknown'],
39
- ['Allowed IPs', data.allowlistCount != null ? `${data.allowlistCount} total (${data.allowlistExactCount} exact + ${data.allowlistCidrCount} CIDR ranges)` : '0'],
40
- ['Allowlist updated', data.allowlistUpdatedAt || 'never'],
41
- ['Sync TTL', `${data.ttl || 60}s`],
42
- ]);
39
+ ['Allowed IPs', data.allowlistCount != null ? `${data.allowlistCount} total (${data.allowlistExactCount} exact + ${data.allowlistCidrCount} CIDR ranges)` : '0'],
40
+ ['Allowlist updated', data.allowlistUpdatedAt || 'never'],
41
+ ['Rate limit rules', String(data.rateLimitCount ?? 0)],
42
+ ['Sync TTL', `${data.ttl || 60}s`],
43
+ ]);
43
44
  if (data.allowlistCount > 0) {
44
45
  console.log('');
45
46
  console.log(` ${ui.c.yellow('!')} Allowlist is active — only ${data.allowlistCount} IP(s) can reach your app`);
@@ -0,0 +1,245 @@
1
+ 'use strict';
2
+
3
+ const { api, requireAuth } = require('./client');
4
+ const config = require('./config');
5
+ const ui = require('./ui');
6
+
7
+ function resolveApp(flags) {
8
+ return flags.app || config.getDefaultApp();
9
+ }
10
+
11
+ function resolveEnvironment(flags, fallback = null) {
12
+ return flags.env || flags.environment || fallback;
13
+ }
14
+
15
+ function parseWindow(value) {
16
+ if (value == null || value === '') return 60;
17
+ if (/^\d+$/.test(String(value))) return Number(value);
18
+ const raw = String(value).trim().toLowerCase();
19
+ const match = raw.match(/^(\d+)\s*(s|sec|second|seconds|m|min|minute|minutes|h|hr|hour|hours)$/);
20
+ if (!match) {
21
+ ui.error('Window must be seconds or look like 30s, 1m, or 1h.');
22
+ process.exit(1);
23
+ }
24
+ const n = Number(match[1]);
25
+ const unit = match[2][0];
26
+ return unit === 'h' ? n * 3600 : unit === 'm' ? n * 60 : n;
27
+ }
28
+
29
+ function buildQuery(flags) {
30
+ const query = {};
31
+ if (flags.page) query.page = flags.page;
32
+ if (flags.limit) query.limit = flags.limit;
33
+ if (flags.status) query.status = flags.status;
34
+ if (flags.search) query.search = flags.search;
35
+ const appKey = resolveApp(flags);
36
+ if (appKey) query.appKey = appKey;
37
+ const environment = resolveEnvironment(flags, null);
38
+ if (environment) query.environment = environment;
39
+ return query;
40
+ }
41
+
42
+ function describeTarget(rule) {
43
+ const parts = [];
44
+ if (rule.ip) parts.push(rule.ip);
45
+ if (rule.method && rule.method !== 'ALL') parts.push(rule.method);
46
+ if (rule.pathPattern) parts.push(`${rule.pathMatchMode || 'prefix'}:${rule.pathPattern}`);
47
+ return parts.join(' ') || 'all traffic';
48
+ }
49
+
50
+ async function list(args, flags) {
51
+ requireAuth();
52
+ const s = ui.spinner('Fetching rate limits');
53
+ try {
54
+ const data = await api.get('/rate-limits', { query: buildQuery(flags) });
55
+ const rules = data.rateLimits || [];
56
+ s.stop(`Found ${rules.length} rate-limit rule${rules.length !== 1 ? 's' : ''}${data.total ? ` (${data.total} total)` : ''}`);
57
+
58
+ if (flags.json) { ui.json(data); return; }
59
+
60
+ console.log('');
61
+ const rows = rules.map((r) => [
62
+ ui.c.dim(ui.truncate(r.id || r._id, 12)),
63
+ r.status === 'active' ? ui.statusBadge('active') : ui.statusBadge(r.status || 'unknown'),
64
+ describeTarget(r),
65
+ `${r.limit}/${r.windowSeconds}s`,
66
+ r.applicationKey || ui.c.dim('all apps'),
67
+ r.environment || ui.c.dim('all envs'),
68
+ r.expiresAt ? new Date(r.expiresAt).toLocaleString() : ui.c.dim('permanent'),
69
+ ui.timeAgo(r.createdAt),
70
+ ]);
71
+ ui.table(['ID', 'Status', 'Target', 'Limit', 'App', 'Env', 'Expires', 'Created'], rows);
72
+ console.log('');
73
+ } catch (err) {
74
+ s.fail('Failed to fetch rate limits');
75
+ throw err;
76
+ }
77
+ }
78
+
79
+ async function show(args, flags) {
80
+ requireAuth();
81
+ const id = args[0];
82
+ if (!id) {
83
+ ui.error('Usage: securenow ratelimit show <id>');
84
+ process.exit(1);
85
+ }
86
+ const s = ui.spinner('Fetching rate limit');
87
+ try {
88
+ const data = await api.get(`/rate-limits/${id}`);
89
+ const r = data.rateLimit;
90
+ s.stop('Rate limit loaded');
91
+ if (flags.json) { ui.json(r); return; }
92
+ console.log('');
93
+ ui.heading(r.name || 'Rate limit');
94
+ console.log('');
95
+ ui.keyValue([
96
+ ['ID', r.id || r._id || id],
97
+ ['Status', r.status || '-'],
98
+ ['Target', describeTarget(r)],
99
+ ['Limit', `${r.limit}/${r.windowSeconds}s`],
100
+ ['Key by', r.keyBy || 'ip'],
101
+ ['App', r.applicationKey || 'all apps'],
102
+ ['Environment', r.environment || 'all envs'],
103
+ ['Reason', r.reason || '-'],
104
+ ['Expires', r.expiresAt || 'permanent'],
105
+ ]);
106
+ console.log('');
107
+ } catch (err) {
108
+ s.fail('Failed to fetch rate limit');
109
+ throw err;
110
+ }
111
+ }
112
+
113
+ async function add(args, flags) {
114
+ requireAuth();
115
+ const ip = args[0] || flags.ip || '';
116
+ const pathPattern = flags.route || flags.path || flags.pattern || '';
117
+
118
+ if (!ip && !pathPattern) {
119
+ ui.error('Provide an IP/CIDR, --route, or both.');
120
+ ui.info('Example: securenow ratelimit add 203.0.113.10 --limit 30 --window 1m --duration 24h');
121
+ process.exit(1);
122
+ }
123
+ if (!flags.limit) {
124
+ ui.error('Pass --limit <count>.');
125
+ process.exit(1);
126
+ }
127
+
128
+ const appKey = resolveApp(flags);
129
+ const environment = resolveEnvironment(flags, 'production');
130
+ const body = {
131
+ ip,
132
+ pathPattern,
133
+ pathMatchMode: flags.mode || flags['path-mode'] || (pathPattern ? 'prefix' : 'prefix'),
134
+ method: flags.method || 'ALL',
135
+ keyBy: flags['key-by'] || flags.keyBy || 'ip',
136
+ limit: Number(flags.limit),
137
+ windowSeconds: parseWindow(flags.window || flags['window-seconds']),
138
+ reason: flags.reason || '',
139
+ name: flags.name || '',
140
+ duration: flags.duration || '',
141
+ expiresAt: flags.expiresAt || flags.expires || '',
142
+ };
143
+ if (appKey) body.appKey = appKey;
144
+ if (environment) body.environment = environment;
145
+
146
+ const s = ui.spinner('Creating rate limit');
147
+ try {
148
+ const data = await api.post('/rate-limits', body);
149
+ s.stop('Rate limit created');
150
+ if (flags.json) { ui.json(data); return; }
151
+ const r = data.rateLimit;
152
+ console.log('');
153
+ console.log(` ${ui.c.green('ACTIVE')} ${describeTarget(r)} at ${r.limit}/${r.windowSeconds}s`);
154
+ console.log(` ${ui.c.dim('ID:')} ${r.id || r._id}`);
155
+ console.log('');
156
+ } catch (err) {
157
+ s.fail('Failed to create rate limit');
158
+ throw err;
159
+ }
160
+ }
161
+
162
+ async function setStatus(args, flags, status) {
163
+ requireAuth();
164
+ const id = args[0];
165
+ if (!id) {
166
+ ui.error(`Usage: securenow ratelimit ${status === 'active' ? 'enable' : 'disable'} <id>`);
167
+ process.exit(1);
168
+ }
169
+ const s = ui.spinner(`${status === 'active' ? 'Enabling' : 'Disabling'} rate limit`);
170
+ try {
171
+ const data = await api.put(`/rate-limits/${id}`, { status });
172
+ s.stop(status === 'active' ? 'Rate limit enabled' : 'Rate limit disabled');
173
+ if (flags.json) ui.json(data);
174
+ } catch (err) {
175
+ s.fail('Failed to update rate limit');
176
+ throw err;
177
+ }
178
+ }
179
+
180
+ async function enable(args, flags) { return setStatus(args, flags, 'active'); }
181
+ async function disable(args, flags) { return setStatus(args, flags, 'disabled'); }
182
+
183
+ async function remove(args, flags) {
184
+ requireAuth();
185
+ const id = args[0];
186
+ if (!id) {
187
+ ui.error('Usage: securenow ratelimit remove <id> [--reason "..."]');
188
+ process.exit(1);
189
+ }
190
+ if (!flags.force && !flags.yes) {
191
+ const ok = await ui.confirm('Remove this rate-limit rule?');
192
+ if (!ok) { ui.info('Cancelled'); return; }
193
+ }
194
+ const s = ui.spinner('Removing rate limit');
195
+ try {
196
+ const opts = flags.reason ? { query: { reason: flags.reason } } : undefined;
197
+ const data = await api.delete(`/rate-limits/${id}`, opts);
198
+ s.stop('Rate limit removed');
199
+ if (flags.json) ui.json(data);
200
+ } catch (err) {
201
+ s.fail('Failed to remove rate limit');
202
+ throw err;
203
+ }
204
+ }
205
+
206
+ async function test(args, flags) {
207
+ requireAuth();
208
+ const ip = args[0] || flags.ip;
209
+ if (!ip) {
210
+ ui.error('Usage: securenow ratelimit test <ip> --path /api/login [--method POST]');
211
+ process.exit(1);
212
+ }
213
+ const query = {
214
+ ip,
215
+ path: flags.path || flags.route || '/',
216
+ method: flags.method || 'GET',
217
+ };
218
+ const appKey = resolveApp(flags);
219
+ if (appKey) query.appKey = appKey;
220
+ const environment = resolveEnvironment(flags, 'production');
221
+ if (environment) query.environment = environment;
222
+
223
+ const s = ui.spinner('Testing rate-limit match');
224
+ try {
225
+ const data = await api.get('/rate-limits/check', { query });
226
+ s.stop('Rate-limit check complete');
227
+ if (flags.json) { ui.json(data); return; }
228
+ console.log('');
229
+ if (data.limited) {
230
+ console.log(` ${ui.c.bold(ui.c.yellow('RATE LIMITED'))} - ${data.matches.length} matching rule${data.matches.length !== 1 ? 's' : ''}`);
231
+ for (const r of data.matches.slice(0, 5)) {
232
+ console.log(` ${ui.c.dim(r.id || r._id)} ${describeTarget(r)} ${r.limit}/${r.windowSeconds}s`);
233
+ }
234
+ } else {
235
+ console.log(` ${ui.c.bold(ui.c.green('NOT LIMITED'))} - no matching rate-limit rules`);
236
+ }
237
+ console.log(` ${ui.c.dim(`App scope: ${appKey || 'all apps'} - Environment: ${data.environment || environment}`)}`);
238
+ console.log('');
239
+ } catch (err) {
240
+ s.fail('Failed to test rate limit');
241
+ throw err;
242
+ }
243
+ }
244
+
245
+ module.exports = { list, show, add, enable, disable, remove, test };
package/cli.js CHANGED
@@ -253,6 +253,48 @@ const COMMANDS = {
253
253
  },
254
254
  defaultSub: 'status',
255
255
  },
256
+ ratelimit: {
257
+ desc: 'Manage soft rate-limit remediation rules',
258
+ usage: 'securenow ratelimit <list|add|show|test|enable|disable|remove> [options]',
259
+ flags: {
260
+ app: 'Scope to app key (defaults to logged-in app)',
261
+ env: 'Scope to environment (default for create/test: production)',
262
+ environment: 'Alias for --env',
263
+ json: 'Output as JSON',
264
+ limit: 'Allowed requests per window',
265
+ window: 'Window size, e.g. 30s, 1m, 1h',
266
+ duration: 'Expiry, e.g. 24h or 7d',
267
+ route: 'Path pattern such as /api/login',
268
+ path: 'Alias for --route',
269
+ mode: 'Path mode: exact, prefix, or regex',
270
+ method: 'HTTP method, or ALL',
271
+ reason: 'Audit reason',
272
+ },
273
+ sub: {
274
+ list: { desc: 'List rate-limit remediation rules', run: (a, f) => require('./cli/rateLimits').list(a, f) },
275
+ add: { desc: 'Create a rate-limit rule', usage: 'securenow ratelimit add [ip] --route /api/login --limit 5 --window 1m --duration 24h', run: (a, f) => require('./cli/rateLimits').add(a, f) },
276
+ show: { desc: 'Show one rate-limit rule', usage: 'securenow ratelimit show <id>', run: (a, f) => require('./cli/rateLimits').show(a, f) },
277
+ test: { desc: 'Check whether a request would match rate-limit rules', usage: 'securenow ratelimit test <ip> --path /api/login --method POST', run: (a, f) => require('./cli/rateLimits').test(a, f) },
278
+ enable: { desc: 'Enable a rate-limit rule', usage: 'securenow ratelimit enable <id>', run: (a, f) => require('./cli/rateLimits').enable(a, f) },
279
+ disable: { desc: 'Disable a rate-limit rule', usage: 'securenow ratelimit disable <id>', run: (a, f) => require('./cli/rateLimits').disable(a, f) },
280
+ remove: { desc: 'Remove a rate-limit rule', usage: 'securenow ratelimit remove <id> [--reason "..."]', run: (a, f) => require('./cli/rateLimits').remove(a, f) },
281
+ },
282
+ defaultSub: 'list',
283
+ },
284
+ 'rate-limit': {
285
+ desc: 'Alias for ratelimit',
286
+ usage: 'securenow rate-limit <list|add|show|test|enable|disable|remove> [options]',
287
+ sub: {
288
+ list: { desc: 'List rate-limit remediation rules', run: (a, f) => require('./cli/rateLimits').list(a, f) },
289
+ add: { desc: 'Create a rate-limit rule', usage: 'securenow rate-limit add [ip] --route /api/login --limit 5 --window 1m --duration 24h', run: (a, f) => require('./cli/rateLimits').add(a, f) },
290
+ show: { desc: 'Show one rate-limit rule', usage: 'securenow rate-limit show <id>', run: (a, f) => require('./cli/rateLimits').show(a, f) },
291
+ test: { desc: 'Check whether a request would match rate-limit rules', usage: 'securenow rate-limit test <ip> --path /api/login --method POST', run: (a, f) => require('./cli/rateLimits').test(a, f) },
292
+ enable: { desc: 'Enable a rate-limit rule', usage: 'securenow rate-limit enable <id>', run: (a, f) => require('./cli/rateLimits').enable(a, f) },
293
+ disable: { desc: 'Disable a rate-limit rule', usage: 'securenow rate-limit disable <id>', run: (a, f) => require('./cli/rateLimits').disable(a, f) },
294
+ remove: { desc: 'Remove a rate-limit rule', usage: 'securenow rate-limit remove <id> [--reason "..."]', run: (a, f) => require('./cli/rateLimits').remove(a, f) },
295
+ },
296
+ defaultSub: 'list',
297
+ },
256
298
  automation: {
257
299
  desc: 'Manage automation rules for blocklist actions',
258
300
  usage: 'securenow automation <list|defaults|show|create|update|dry-run|execute|delete> [rule-id] [options]',
@@ -555,7 +597,7 @@ function showHelp(commandName) {
555
597
  'Detect & Respond': ['human', 'notifications', 'alerts', 'fp'],
556
598
  'Investigate': ['ip', 'forensics'],
557
599
  'Firewall': ['firewall'],
558
- 'Remediation': ['automation', 'blocklist', 'allowlist', 'trusted'],
600
+ 'Remediation': ['automation', 'ratelimit', 'blocklist', 'allowlist', 'trusted'],
559
601
  'Telemetry': ['log', 'test-span'],
560
602
  'Utilities': ['redact', 'cidr', 'doctor', 'env', 'mcp'],
561
603
  'Settings': ['instances', 'config', 'version'],
package/firewall.js CHANGED
@@ -30,11 +30,16 @@ let _remoteEnabled = true;
30
30
  let _lastRemoteEnabled = null;
31
31
 
32
32
  // Allowlist state
33
- let _allowlistMatcher = null;
34
- let _allowlistRawIps = [];
35
- let _lastAllowlistModified = null;
36
- let _lastAllowlistVersion = null;
37
- let _lastAllowlistSyncEtag = null;
33
+ let _allowlistMatcher = null;
34
+ let _allowlistRawIps = [];
35
+ let _lastAllowlistModified = null;
36
+ let _lastAllowlistVersion = null;
37
+ let _lastAllowlistSyncEtag = null;
38
+
39
+ // Rate-limit policy state is synced in Phase 1 for forward compatibility.
40
+ // Enforcement is intentionally added in the next SDK phase.
41
+ let _rateLimitRules = [];
42
+ let _lastRateLimitVersion = null;
38
43
 
39
44
  // Circuit breaker
40
45
  const CIRCUIT_OPEN_THRESHOLD = 5;
@@ -295,6 +300,7 @@ function doUnifiedSync(callback) {
295
300
  if (_options.environment) headers['X-SecureNow-Environment'] = _options.environment;
296
301
  if (_lastVersion) headers['X-Blocklist-Version'] = _lastVersion;
297
302
  if (_lastAllowlistVersion) headers['X-Allowlist-Version'] = _lastAllowlistVersion;
303
+ if (_lastRateLimitVersion) headers['X-Rate-Limit-Version'] = _lastRateLimitVersion;
298
304
  if (_lastUnifiedEtag) headers['If-None-Match'] = _lastUnifiedEtag;
299
305
 
300
306
  httpGet(url, headers, 8000, (err, res, data) => {
@@ -364,13 +370,24 @@ function doUnifiedSync(callback) {
364
370
  }
365
371
  }
366
372
 
367
- if (body.allowlistIps) {
368
- _allowlistRawIps = body.allowlistIps;
369
- _allowlistMatcher = createMatcher(body.allowlistIps);
370
- alChanged = true;
371
- }
372
-
373
- callback(null, { blChanged, alChanged });
373
+ if (body.allowlistIps) {
374
+ _allowlistRawIps = body.allowlistIps;
375
+ _allowlistMatcher = createMatcher(body.allowlistIps);
376
+ alChanged = true;
377
+ }
378
+
379
+ if (body.rateLimits) {
380
+ const newVer = body.rateLimits.version;
381
+ if (newVer !== _lastRateLimitVersion) {
382
+ _lastRateLimitVersion = newVer;
383
+ }
384
+ }
385
+
386
+ if (Array.isArray(body.rateLimitRules)) {
387
+ _rateLimitRules = body.rateLimitRules;
388
+ }
389
+
390
+ callback(null, { blChanged, alChanged, rlChanged: Array.isArray(body.rateLimitRules) });
374
391
  } catch (e) {
375
392
  callback(new Error(`Failed to parse sync response: ${e.message}`));
376
393
  }
@@ -557,11 +574,14 @@ function pollOnce(callback) {
557
574
  const s = _matcher.stats();
558
575
  fwLog('[securenow] Firewall: re-synced %d blocked IPs (%d exact + %d CIDR ranges)', s.total, s.exact, s.cidr);
559
576
  }
560
- if (result.alChanged && _options.log && _allowlistMatcher) {
561
- const s = _allowlistMatcher.stats();
562
- fwLog('[securenow] Firewall: re-synced %d allowed IPs (%d exact + %d CIDR ranges)', s.total, s.exact, s.cidr);
563
- }
564
- }
577
+ if (result.alChanged && _options.log && _allowlistMatcher) {
578
+ const s = _allowlistMatcher.stats();
579
+ fwLog('[securenow] Firewall: re-synced %d allowed IPs (%d exact + %d CIDR ranges)', s.total, s.exact, s.cidr);
580
+ }
581
+ if (result.rlChanged && _options.log) {
582
+ fwLog('[securenow] Firewall: re-synced %d rate-limit rules (enforcement pending SDK phase 2)', _rateLimitRules.length);
583
+ }
584
+ }
565
585
  callback(null);
566
586
  };
567
587
 
@@ -639,12 +659,15 @@ function startSyncLoop() {
639
659
  const s = _matcher.stats();
640
660
  fwLog('[securenow] Firewall: synced %d blocked IPs (%d exact + %d CIDR ranges)', s.total, s.exact, s.cidr);
641
661
  }
642
- if (_options.log && _allowlistMatcher) {
643
- const s = _allowlistMatcher.stats();
644
- if (s.total > 0) fwLog('[securenow] Firewall: synced %d allowed IPs (%d exact + %d CIDR ranges)', s.total, s.exact, s.cidr);
645
- }
646
- });
647
- }
662
+ if (_options.log && _allowlistMatcher) {
663
+ const s = _allowlistMatcher.stats();
664
+ if (s.total > 0) fwLog('[securenow] Firewall: synced %d allowed IPs (%d exact + %d CIDR ranges)', s.total, s.exact, s.cidr);
665
+ }
666
+ if (_options.log && _rateLimitRules.length > 0) {
667
+ fwLog('[securenow] Firewall: synced %d rate-limit rules (enforcement pending SDK phase 2)', _rateLimitRules.length);
668
+ }
669
+ });
670
+ }
648
671
 
649
672
  initialSync();
650
673
  scheduleNextPoll();
@@ -655,14 +678,17 @@ function startSyncLoop() {
655
678
  // Force a full re-fetch by clearing versions so unified endpoint returns full data
656
679
  const savedBlVer = _lastVersion;
657
680
  const savedAlVer = _lastAllowlistVersion;
681
+ const savedRlVer = _lastRateLimitVersion;
658
682
  const savedUnifiedEtag = _lastUnifiedEtag;
659
683
  _lastVersion = null;
660
684
  _lastAllowlistVersion = null;
685
+ _lastRateLimitVersion = null;
661
686
  _lastUnifiedEtag = null;
662
687
  pollOnce((err) => {
663
688
  if (err) {
664
689
  _lastVersion = savedBlVer;
665
690
  _lastAllowlistVersion = savedAlVer;
691
+ _lastRateLimitVersion = savedRlVer;
666
692
  _lastUnifiedEtag = savedUnifiedEtag;
667
693
  }
668
694
  });
@@ -912,9 +938,10 @@ function shutdown() {
912
938
  function getStats() {
913
939
  return {
914
940
  ..._stats,
915
- matcher: _matcher ? _matcher.stats() : null,
916
- allowlistMatcher: _allowlistMatcher ? _allowlistMatcher.stats() : null,
917
- initialized: _initialized,
941
+ matcher: _matcher ? _matcher.stats() : null,
942
+ allowlistMatcher: _allowlistMatcher ? _allowlistMatcher.stats() : null,
943
+ rateLimitRules: _rateLimitRules.length,
944
+ initialized: _initialized,
918
945
  circuitState: _circuitState,
919
946
  consecutiveErrors: _consecutiveErrors,
920
947
  unifiedSync: _useUnifiedSync,
@@ -927,8 +954,9 @@ function getStats() {
927
954
  // Layers (TCP / iptables / cloud) read the matcher to populate kernel-level
928
955
  // rules. When the remote toggle is off, return null so they treat the policy
929
956
  // as "no IPs to block" without us mutating the cached matcher.
930
- function getMatcher() { return _remoteEnabled === false ? null : _matcher; }
931
- function getAllowlistMatcher() { return _remoteEnabled === false ? null : _allowlistMatcher; }
932
- function isRemoteEnabled() { return _remoteEnabled !== false; }
933
-
934
- module.exports = { init, shutdown, getStats, getMatcher, getAllowlistMatcher, isRemoteEnabled };
957
+ function getMatcher() { return _remoteEnabled === false ? null : _matcher; }
958
+ function getAllowlistMatcher() { return _remoteEnabled === false ? null : _allowlistMatcher; }
959
+ function getRateLimitRules() { return _remoteEnabled === false ? [] : _rateLimitRules.slice(); }
960
+ function isRemoteEnabled() { return _remoteEnabled !== false; }
961
+
962
+ module.exports = { init, shutdown, getStats, getMatcher, getAllowlistMatcher, getRateLimitRules, isRemoteEnabled };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "7.7.7",
3
+ "version": "7.7.8",
4
4
  "description": "OpenTelemetry instrumentation for Node.js, Next.js, and Nuxt - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",