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 +6 -1
- package/README.md +4 -1
- package/SKILL-CLI.md +4 -1
- package/cli/security.js +143 -7
- package/cli/ui.js +1 -0
- package/cli.js +11 -2
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +8 -0
- package/docs/API-KEYS-GUIDE.md +2 -0
- package/firewall.js +2 -0
- package/package.json +1 -1
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` |
|
|
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 ||
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
r.
|
|
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', '
|
|
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: {
|
|
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
|
|
package/docs/API-KEYS-GUIDE.md
CHANGED
|
@@ -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} — ${new Date().toISOString()} — 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