securenow 5.15.0 → 5.15.2

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/NPM_README.md CHANGED
@@ -241,6 +241,9 @@ npx securenow notifications read-all
241
241
  ```bash
242
242
  # View alert rules, channels, and history
243
243
  npx securenow alerts rules
244
+ npx securenow alerts rules show <rule-id>
245
+ npx securenow alerts rules update <rule-id> --applications-all
246
+ npx securenow alerts rules update <rule-id> --apps key1,key2
244
247
  npx securenow alerts channels
245
248
  npx securenow alerts history --limit 20
246
249
  ```
@@ -394,7 +397,9 @@ fi
394
397
  | | `notifications unread` | Unread count |
395
398
  | | `notifications read <id>` | Mark read |
396
399
  | | `notifications read-all` | Mark all read |
397
- | | `alerts rules` | Alert rules |
400
+ | | `alerts rules` | List rules (status, apps, schedule) |
401
+ | | `alerts rules show <id>` | Rule detail |
402
+ | | `alerts rules update <id> --applications-all` / `--apps k1,k2` | Application scope |
398
403
  | | `alerts channels` | Alert channels |
399
404
  | | `alerts history` | Alert history |
400
405
  | **Investigate** | `ip <addr>` | IP intelligence |
package/README.md CHANGED
@@ -300,7 +300,10 @@ Most users won't need this — just add `-r securenow/register` to your existing
300
300
  | `securenow notifications unread` | Show unread count |
301
301
  | `securenow notifications read <id>` | Mark notification as read |
302
302
  | `securenow notifications read-all` | Mark all as read |
303
- | `securenow alerts rules` | List alert rules |
303
+ | `securenow alerts rules` | List alert rules (status, applications, schedule) |
304
+ | `securenow alerts rules show <id>` | Show one rule (includes all-apps vs explicit apps) |
305
+ | `securenow alerts rules update <id> --applications-all` | Set rule to all current & future apps |
306
+ | `securenow alerts rules update <id> --apps k1,k2` | Scope rule to specific app keys |
304
307
  | `securenow alerts channels` | List alert channels |
305
308
  | `securenow alerts history` | View alert history |
306
309
 
package/SKILL-CLI.md CHANGED
@@ -174,7 +174,10 @@ securenow notifications unread # unread count
174
174
 
175
175
  ```bash
176
176
  securenow alerts # list alert rules (default)
177
- securenow alerts rules # list alert rules
177
+ securenow alerts rules # list alert rules (columns: Status, Applications, Schedule)
178
+ securenow alerts rules show <id> # one rule; JSON: --json
179
+ securenow alerts rules update <id> --applications-all # all current & future apps
180
+ securenow alerts rules update <id> --apps key1,key2 # explicit app keys only
178
181
  securenow alerts channels # list alert channels (Slack, email, etc.)
179
182
  securenow alerts history [--limit N] # past triggered alerts
180
183
  ```
package/cli/security.js CHANGED
@@ -10,6 +10,39 @@ function resolveApp(flags) {
10
10
 
11
11
  // ── Alert Rules ──
12
12
 
13
+ function formatRuleApplicationsCell(rule) {
14
+ if (rule.applicationsAll) {
15
+ return ui.c.cyan('all apps');
16
+ }
17
+ const keys = rule.applications || [];
18
+ if (keys.length === 0) return ui.c.dim('—');
19
+ const joined = keys.join(', ');
20
+ return joined.length > 48 ? `${joined.slice(0, 45)}…` : joined;
21
+ }
22
+
23
+ function ruleStatusBadge(rule) {
24
+ const st = rule.status || (rule.enabled !== false ? 'Active' : 'Disabled');
25
+ if (st === 'Active') return ui.statusBadge('active');
26
+ if (st === 'Disabled') return ui.statusBadge('disabled');
27
+ if (st === 'Paused') return ui.statusBadge('paused');
28
+ return st;
29
+ }
30
+
31
+ /** Dispatch: list | show <id> | update <id> ... */
32
+ async function alertRulesRoute(args, flags) {
33
+ const sub = args[0];
34
+ if (sub === 'show') {
35
+ return alertRuleShow(args.slice(1), flags);
36
+ }
37
+ if (sub === 'update') {
38
+ return alertRuleUpdate(args.slice(1), flags);
39
+ }
40
+ if (sub === 'list') {
41
+ return alertRulesList(args.slice(1), flags);
42
+ }
43
+ return alertRulesList(args, flags);
44
+ }
45
+
13
46
  async function alertRulesList(args, flags) {
14
47
  requireAuth();
15
48
  const s = ui.spinner('Fetching alert rules');
@@ -21,15 +54,16 @@ async function alertRulesList(args, flags) {
21
54
  if (flags.json) { ui.json(rules); return; }
22
55
 
23
56
  console.log('');
24
- const rows = rules.map(r => [
57
+ const rows = rules.map((r) => [
25
58
  ui.c.dim(ui.truncate(r._id, 12)),
26
- r.name || r.type || '—',
27
- ui.statusBadge(r.enabled !== false ? 'enabled' : 'disabled'),
28
- r.severity ? ui.statusBadge(r.severity) : '—',
29
- r.type || '—',
30
- r.serviceName || ui.c.dim('all'),
59
+ r.name || '—',
60
+ ruleStatusBadge(r),
61
+ formatRuleApplicationsCell(r),
62
+ r.schedule?.enabled === false ? ui.c.dim('off') : (r.schedule?.description || r.schedule?.cronExpression || '—'),
31
63
  ]);
32
- ui.table(['ID', 'Name', 'Status', 'Severity', 'Type', 'App'], rows);
64
+ ui.table(['ID', 'Name', 'Status', 'Applications', 'Schedule'], rows);
65
+ console.log('');
66
+ console.log(ui.c.dim(' Applications: "all apps" = all current & future active apps. show <id> · update <id> --applications-all · --apps k1,k2'));
33
67
  console.log('');
34
68
  } catch (err) {
35
69
  s.fail('Failed to fetch alert rules');
@@ -37,6 +71,105 @@ async function alertRulesList(args, flags) {
37
71
  }
38
72
  }
39
73
 
74
+ async function alertRuleShow(args, flags) {
75
+ requireAuth();
76
+ const id = args[0];
77
+ if (!id) {
78
+ ui.error('Usage: securenow alerts rules show <rule-id>');
79
+ process.exit(1);
80
+ }
81
+ const s = ui.spinner('Fetching alert rule');
82
+ try {
83
+ const data = await api.get(`/alert-rules/${id}`);
84
+ const r = data.alertRule;
85
+ s.stop('');
86
+
87
+ if (flags.json) {
88
+ ui.json(r);
89
+ return;
90
+ }
91
+
92
+ console.log('');
93
+ ui.heading(r.name || 'Alert rule');
94
+ console.log('');
95
+ const appLine = r.applicationsAll
96
+ ? 'All applications (current & future)'
97
+ : (r.applications && r.applications.length > 0 ? r.applications.join(', ') : '—');
98
+ ui.keyValue([
99
+ ['ID', r._id || r.id || id],
100
+ ['Status', r.status || '—'],
101
+ ['System rule', r.isSystem ? 'yes' : 'no'],
102
+ ['Applications', appLine],
103
+ ['Schedule', r.schedule?.enabled === false ? 'disabled' : (r.schedule?.description || r.schedule?.cronExpression || '—')],
104
+ ['Throttle', r.throttle?.enabled ? `${r.throttle.minutes} min` : 'off'],
105
+ ['Query', r.queryMappingId?.name || '—'],
106
+ ]);
107
+ console.log('');
108
+ } catch (err) {
109
+ s.fail('Failed to fetch alert rule');
110
+ throw err;
111
+ }
112
+ }
113
+
114
+ async function alertRuleUpdate(args, flags) {
115
+ requireAuth();
116
+ const id = args[0];
117
+ if (!id) {
118
+ ui.error('Usage: securenow alerts rules update <rule-id> (--applications-all | --apps <k1,k2>)');
119
+ process.exit(1);
120
+ }
121
+
122
+ const hasAll =
123
+ flags['applications-all'] === true ||
124
+ flags['applications-all'] === 'true';
125
+ const hasNoAll =
126
+ flags['no-applications-all'] === true ||
127
+ flags['no-applications-all'] === 'true';
128
+ const appsStr = flags.apps;
129
+
130
+ if (hasAll && hasNoAll) {
131
+ ui.error('Use only one of --applications-all or --no-applications-all');
132
+ process.exit(1);
133
+ }
134
+ if (hasAll && appsStr) {
135
+ ui.error('Cannot combine --applications-all with --apps');
136
+ process.exit(1);
137
+ }
138
+
139
+ const body = {};
140
+ if (hasAll) {
141
+ body.applicationsAll = true;
142
+ body.applications = [];
143
+ } else if (hasNoAll || appsStr) {
144
+ body.applicationsAll = false;
145
+ if (!appsStr) {
146
+ ui.error('Pass --apps key1,key2 when using --no-applications-all, or omit --no-applications-all and use --apps only');
147
+ process.exit(1);
148
+ }
149
+ body.applications = String(appsStr)
150
+ .split(',')
151
+ .map((x) => x.trim())
152
+ .filter(Boolean);
153
+ if (body.applications.length === 0) {
154
+ ui.error('No application keys parsed from --apps');
155
+ process.exit(1);
156
+ }
157
+ } else {
158
+ ui.error('Nothing to update. Use --applications-all or --apps key1,key2');
159
+ process.exit(1);
160
+ }
161
+
162
+ const s = ui.spinner('Updating alert rule');
163
+ try {
164
+ await api.put(`/alert-rules/${id}`, body);
165
+ s.stop('Updated');
166
+ ui.success('Alert rule updated');
167
+ } catch (err) {
168
+ s.fail('Failed to update alert rule');
169
+ throw err;
170
+ }
171
+ }
172
+
40
173
  // ── Alert Channels ──
41
174
 
42
175
  async function alertChannelsList(args, flags) {
@@ -901,7 +1034,10 @@ async function analytics(args, flags) {
901
1034
  }
902
1035
 
903
1036
  module.exports = {
1037
+ alertRulesRoute,
904
1038
  alertRulesList,
1039
+ alertRuleShow,
1040
+ alertRuleUpdate,
905
1041
  alertChannelsList,
906
1042
  alertHistoryList,
907
1043
  blocklistList,
package/cli/ui.js CHANGED
@@ -302,6 +302,7 @@ function statusBadge(status) {
302
302
  trusted: c.green('■ trusted'),
303
303
  enabled: c.green('● enabled'),
304
304
  disabled: c.dim('○ disabled'),
305
+ paused: c.yellow('◆ paused'),
305
306
  critical: c.red('▲ critical'),
306
307
  high: c.red('▲ high'),
307
308
  medium: c.yellow('▲ medium'),
package/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
4
  const ui = require('./cli/ui');
@@ -119,7 +119,16 @@ const COMMANDS = {
119
119
  desc: 'Manage alerting',
120
120
  usage: 'securenow alerts <subcommand> [options]',
121
121
  sub: {
122
- rules: { desc: 'List alert rules', run: (a, f) => require('./cli/security').alertRulesList(a, f) },
122
+ rules: {
123
+ desc: 'List, show, or update alert rules',
124
+ flags: {
125
+ json: 'Output as JSON',
126
+ 'applications-all': 'With update: scope rule to all apps',
127
+ 'no-applications-all': 'With update: scope to explicit --apps list',
128
+ apps: 'Comma-separated app keys (with update)',
129
+ },
130
+ run: (a, f) => require('./cli/security').alertRulesRoute(a, f),
131
+ },
123
132
  channels: { desc: 'List alert channels', run: (a, f) => require('./cli/security').alertChannelsList(a, f) },
124
133
  history: { desc: 'View alert history', flags: { limit: 'Max results' }, run: (a, f) => require('./cli/security').alertHistoryList(a, f) },
125
134
  },
@@ -805,6 +805,9 @@ The SecureNow CLI is your terminal command center. Below is every command organi
805
805
  | `securenow issues show <id>` | Full issue detail + AI analysis |
806
806
  | `securenow issues resolve <id>` | Mark issue as resolved |
807
807
  | `securenow alerts rules` | List alert rules |
808
+ | `securenow alerts rules show <id>` | One rule (all-apps vs explicit keys) |
809
+ | `securenow alerts rules update <id> --applications-all` | All current & future apps |
810
+ | `securenow alerts rules update <id> --apps k1,k2` | Explicit app keys only |
808
811
  | `securenow alerts channels` | List alert channels (email, webhook, Slack) |
809
812
  | `securenow alerts history --limit 50` | View past triggered alerts |
810
813
  | `securenow notifications` | List notifications |
@@ -1171,6 +1174,11 @@ Configure alert rules and channels from the [dashboard](https://app.securenow.ai
1171
1174
  # List your alert rules
1172
1175
  npx securenow alerts rules
1173
1176
 
1177
+ # Show one rule / set application scope (all apps vs explicit keys)
1178
+ npx securenow alerts rules show <rule-id>
1179
+ npx securenow alerts rules update <rule-id> --applications-all
1180
+ npx securenow alerts rules update <rule-id> --apps key1,key2
1181
+
1174
1182
  # List alert channels (email, Slack, webhook)
1175
1183
  npx securenow alerts channels
1176
1184
 
@@ -87,6 +87,8 @@ Restrict an API key to specific applications. When set, the key can only access
87
87
 
88
88
  Leave empty to allow access to all applications on your account.
89
89
 
90
+ **Alert rules:** Keys that are scoped to specific applications **cannot** create or update alert rules with **`applicationsAll: true`** (“all applications”). Use explicit app keys on each rule instead. Unscoped keys may use all-apps mode.
91
+
90
92
  ---
91
93
 
92
94
  ## IP Allowlisting
package/firewall.js CHANGED
@@ -395,6 +395,7 @@ p{font-size:.9rem;line-height:1.7;color:#a1a1aa;margin-bottom:.5rem}
395
395
  .contact a{color:#f87171;text-decoration:none;font-weight:500}
396
396
  .contact a:hover{text-decoration:underline}
397
397
  .footer{margin-top:2rem;font-size:.7rem;color:#3f3f46}
398
+ .powered{margin-top:1.5rem;font-size:.75rem;color:#52525b}.powered a{color:#a1a1aa;text-decoration:none;font-weight:600;transition:color .2s}.powered a:hover{color:#f87171;text-decoration:underline}
398
399
  </style>
399
400
  </head>
400
401
  <body>
@@ -408,6 +409,7 @@ p{font-size:.9rem;line-height:1.7;color:#a1a1aa;margin-bottom:.5rem}
408
409
  <div class="divider"></div>
409
410
  <p class="contact">If you believe this is a mistake, please contact us at<br><a href="mailto:contact@securenow.ai?subject=Blocked%20IP%20Appeal%20-%20${encodeURIComponent(maskedIp)}&body=IP:%20${encodeURIComponent(maskedIp)}%0ATimestamp:%20${encodeURIComponent(new Date().toISOString())}%0A%0APlease%20describe%20why%20you%20believe%20this%20block%20is%20incorrect:">contact@securenow.ai</a><br>Include your IP address and the time of this incident.</p>
410
411
  <p class="footer">Ref: ${maskedIp} &mdash; ${new Date().toISOString()} &mdash; HTTP 403</p>
412
+ <p class="powered">Protected by <a href="https://securenow.ai" rel="dofollow" target="_blank">SecureNow</a></p>
411
413
  </div>
412
414
  </body>
413
415
  </html>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "5.15.0",
3
+ "version": "5.15.2",
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",