securenow 7.6.9 → 7.7.1
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 +17 -3
- package/README.md +9 -4
- package/SKILL-API.md +2 -2
- package/SKILL-CLI.md +36 -22
- package/app-config.js +81 -3
- package/cli/automation.js +275 -0
- package/cli/config.js +8 -8
- package/cli/credentials.js +2 -1
- package/cli/firewall.js +29 -12
- package/cli/human.js +96 -2
- package/cli/security.js +171 -42
- package/cli.js +71 -28
- package/mcp/catalog.js +327 -15
- package/nextjs.js +22 -23
- package/nuxt-server-plugin.mjs +13 -8
- package/package.json +1 -1
- package/resolve-ip.js +135 -60
- package/tracing.js +25 -4
package/cli.js
CHANGED
|
@@ -164,7 +164,7 @@ const COMMANDS = {
|
|
|
164
164
|
},
|
|
165
165
|
human: {
|
|
166
166
|
desc: 'Work the human action queue prepared by SecureNow AI',
|
|
167
|
-
usage: 'securenow human <list|show|block|fp|prompt|work> [row|notificationId:ip] [options]',
|
|
167
|
+
usage: 'securenow human <list|show|block|fp|action|prompt|work> [row|notificationId:ip] [options]',
|
|
168
168
|
flags: {
|
|
169
169
|
json: 'Output as JSON',
|
|
170
170
|
page: 'Queue page number',
|
|
@@ -178,6 +178,9 @@ const COMMANDS = {
|
|
|
178
178
|
'rule-scope': 'False-positive scope: this_rule | specific_rules | all_existing | any_rule',
|
|
179
179
|
'create-exclusion': 'Create a restrictive exclusion when marking false positive',
|
|
180
180
|
'apply-existing': 'Apply false-positive decision to existing matching rows',
|
|
181
|
+
status: 'Case action status: approved, rejected, executed, failed, or proposed',
|
|
182
|
+
'action-key': 'Case-level proposed action key',
|
|
183
|
+
result: 'Case action result JSON',
|
|
181
184
|
mode: 'Prompt mode label for MCP prompt output',
|
|
182
185
|
},
|
|
183
186
|
sub: {
|
|
@@ -185,6 +188,7 @@ const COMMANDS = {
|
|
|
185
188
|
show: { desc: 'Show one row with AI report, proofs, DAG, and trace links', usage: 'securenow human show <row|notificationId:ip>', run: (a, f) => require('./cli/human').show(a, f) },
|
|
186
189
|
block: { desc: 'Approve the AI block recommendation for a row', usage: 'securenow human block <row|notificationId:ip> --yes --reason "..."', run: (a, f) => require('./cli/human').block(a, f) },
|
|
187
190
|
fp: { desc: 'Mark a row as a scoped false positive', usage: 'securenow human fp <row|notificationId:ip> --yes --reason "..."', run: (a, f) => require('./cli/human').fp(a, f) },
|
|
191
|
+
action: { desc: 'Approve/reject/execute a case-level proposed action', usage: 'securenow human action <row|notificationId> [actionKey] --status approved --yes --reason "..."', run: (a, f) => require('./cli/human').action(a, f) },
|
|
188
192
|
prompt: { desc: 'Print a Codex/Claude MCP prompt for row or queue work', usage: 'securenow human prompt [row|notificationId:ip] [--limit 10]', run: (a, f) => require('./cli/human').prompt(a, f) },
|
|
189
193
|
work: { desc: 'List the queue and print the MCP runbook to work it deeply', usage: 'securenow human work [--limit 10]', run: (a, f) => require('./cli/human').work(a, f) },
|
|
190
194
|
},
|
|
@@ -226,47 +230,86 @@ const COMMANDS = {
|
|
|
226
230
|
},
|
|
227
231
|
defaultSub: 'list',
|
|
228
232
|
},
|
|
229
|
-
firewall: {
|
|
230
|
-
desc: 'Firewall status, per-app toggle, and IP testing',
|
|
231
|
-
usage: 'securenow firewall <subcommand> [options]',
|
|
232
|
-
flags: { app: 'App key (defaults to logged-in app)', json: 'Output as JSON' },
|
|
233
|
+
firewall: {
|
|
234
|
+
desc: 'Firewall status, per-app toggle, and IP testing',
|
|
235
|
+
usage: 'securenow firewall <subcommand> [options]',
|
|
236
|
+
flags: { app: 'App key (defaults to logged-in app)', json: 'Output as JSON' },
|
|
233
237
|
sub: {
|
|
234
238
|
status: { desc: 'Show firewall status, layers, and blocklist info', flags: { env: 'Environment (default: production)', environment: 'Alias for --env' }, run: (a, f) => require('./cli/firewall').status(a, f) },
|
|
235
239
|
apps: { desc: 'List apps with their firewall on/off state', run: (a, f) => require('./cli/firewall').appsList(a, f) },
|
|
236
240
|
enable: { desc: 'Turn the firewall ON for an app environment', usage: 'securenow firewall enable [--app <key>] [--env production]', flags: { app: 'App key (defaults to logged-in app)', env: 'Environment (default: production)', environment: 'Alias for --env' }, run: (a, f) => require('./cli/firewall').enable(a, f) },
|
|
237
241
|
disable: { desc: 'Turn the firewall OFF for an app environment', usage: 'securenow firewall disable [--app <key>] [--env local]', flags: { app: 'App key (defaults to logged-in app)', env: 'Environment (default: production)', environment: 'Alias for --env' }, run: (a, f) => require('./cli/firewall').disable(a, f) },
|
|
238
242
|
'test-ip': { desc: 'Check if an IP would be blocked', usage: 'securenow firewall test-ip <ip> [--env production]', flags: { env: 'Environment (default: production)', environment: 'Alias for --env' }, run: (a, f) => require('./cli/firewall').testIp(a, f) },
|
|
239
|
-
},
|
|
240
|
-
defaultSub: 'status',
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
desc: 'Manage
|
|
244
|
-
usage: 'securenow
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
243
|
+
},
|
|
244
|
+
defaultSub: 'status',
|
|
245
|
+
},
|
|
246
|
+
automation: {
|
|
247
|
+
desc: 'Manage automation rules for blocklist actions',
|
|
248
|
+
usage: 'securenow automation <list|show|create|update|dry-run|execute|delete> [rule-id] [options]',
|
|
249
|
+
flags: {
|
|
250
|
+
json: 'Output as JSON',
|
|
251
|
+
body: 'Full JSON body for create/update',
|
|
252
|
+
file: 'Read JSON body from file',
|
|
253
|
+
name: 'Rule name',
|
|
254
|
+
description: 'Rule description',
|
|
255
|
+
conditions: 'Conditions JSON array',
|
|
256
|
+
actions: 'Actions JSON array',
|
|
257
|
+
logic: 'Condition logic: AND or OR',
|
|
258
|
+
status: 'Rule status: active or disabled',
|
|
259
|
+
app: 'Comma-separated app key(s) to scope the rule',
|
|
260
|
+
apps: 'Alias for --app',
|
|
261
|
+
'applications-all': 'Scope rule to all apps',
|
|
262
|
+
env: 'Comma-separated environment(s) to scope the rule',
|
|
263
|
+
environment: 'Alias for --env',
|
|
264
|
+
environments: 'Alias for --env',
|
|
265
|
+
'environments-all': 'Scope rule to all environments',
|
|
266
|
+
limit: 'Dry-run notification scan limit',
|
|
267
|
+
'sample-limit': 'Dry-run sample count',
|
|
268
|
+
yes: 'Confirm execute/delete',
|
|
269
|
+
force: 'Alias for --yes',
|
|
270
|
+
},
|
|
271
|
+
sub: {
|
|
272
|
+
list: { desc: 'List automation rules', run: (a, f) => require('./cli/automation').list(a, f) },
|
|
273
|
+
show: { desc: 'Show automation rule details', usage: 'securenow automation show <rule-id>', run: (a, f) => require('./cli/automation').show(a, f) },
|
|
274
|
+
create: { desc: 'Create automation rule', usage: 'securenow automation create --name "..." --conditions \'[...]\' --actions \'[...]\'', run: (a, f) => require('./cli/automation').create(a, f) },
|
|
275
|
+
update: { desc: 'Update automation rule', usage: 'securenow automation update <rule-id> --body \'{...}\'', run: (a, f) => require('./cli/automation').update(a, f) },
|
|
276
|
+
'dry-run': { desc: 'Preview automation matches without writing blocklist entries', usage: 'securenow automation dry-run <rule-id>', run: (a, f) => require('./cli/automation').dryRun(a, f) },
|
|
277
|
+
execute: { desc: 'Execute an automation rule now', usage: 'securenow automation execute <rule-id> --yes', run: (a, f) => require('./cli/automation').execute(a, f) },
|
|
278
|
+
delete: { desc: 'Delete an automation rule', usage: 'securenow automation delete <rule-id> --yes', run: (a, f) => require('./cli/automation').remove(a, f) },
|
|
279
|
+
},
|
|
280
|
+
defaultSub: 'list',
|
|
281
|
+
},
|
|
282
|
+
blocklist: {
|
|
283
|
+
desc: 'Manage IP blocklist',
|
|
284
|
+
usage: 'securenow blocklist <subcommand> [options]',
|
|
285
|
+
flags: { app: 'Scope to app key', env: 'Scope to environment', environment: 'Alias for --env', page: 'Page number', limit: 'Max results', json: 'Output as JSON' },
|
|
286
|
+
sub: {
|
|
287
|
+
list: { desc: 'List blocked IPs', run: (a, f) => require('./cli/security').blocklistList(a, f) },
|
|
288
|
+
add: { desc: 'Block an IP', usage: 'securenow blocklist add <ip> [--app <key>] [--env production] [--reason <reason>]', run: (a, f) => require('./cli/security').blocklistAdd(a, f) },
|
|
248
289
|
remove: { desc: 'Unblock an IP', usage: 'securenow blocklist remove <id>', run: (a, f) => require('./cli/security').blocklistRemove(a, f) },
|
|
249
290
|
stats: { desc: 'Blocklist statistics', run: (a, f) => require('./cli/security').blocklistStats(a, f) },
|
|
250
291
|
},
|
|
251
292
|
defaultSub: 'list',
|
|
252
293
|
},
|
|
253
|
-
allowlist: {
|
|
254
|
-
desc: 'Manage IP allowlist (only allow listed IPs)',
|
|
255
|
-
usage: 'securenow allowlist <subcommand> [options]',
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
294
|
+
allowlist: {
|
|
295
|
+
desc: 'Manage IP allowlist (only allow listed IPs)',
|
|
296
|
+
usage: 'securenow allowlist <subcommand> [options]',
|
|
297
|
+
flags: { app: 'Scope to app key(s), comma-separated', env: 'Scope to environment', environment: 'Alias for --env', page: 'Page number', limit: 'Max results', json: 'Output as JSON' },
|
|
298
|
+
sub: {
|
|
299
|
+
list: { desc: 'List allowed IPs', run: (a, f) => require('./cli/security').allowlistList(a, f) },
|
|
300
|
+
add: { desc: 'Allow an IP', usage: 'securenow allowlist add <ip> [--app <key>] [--env local] [--label <label>] [--reason <reason>]', run: (a, f) => require('./cli/security').allowlistAdd(a, f) },
|
|
259
301
|
remove: { desc: 'Remove an allowed IP', usage: 'securenow allowlist remove <id>', run: (a, f) => require('./cli/security').allowlistRemove(a, f) },
|
|
260
302
|
stats: { desc: 'Allowlist statistics', run: (a, f) => require('./cli/security').allowlistStats(a, f) },
|
|
261
303
|
},
|
|
262
304
|
defaultSub: 'list',
|
|
263
305
|
},
|
|
264
|
-
trusted: {
|
|
265
|
-
desc: 'Manage trusted IPs',
|
|
266
|
-
usage: 'securenow trusted <subcommand> [options]',
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
306
|
+
trusted: {
|
|
307
|
+
desc: 'Manage trusted IPs',
|
|
308
|
+
usage: 'securenow trusted <subcommand> [options]',
|
|
309
|
+
flags: { app: 'Scope to app key(s), comma-separated', env: 'Scope to environment', environment: 'Alias for --env', json: 'Output as JSON' },
|
|
310
|
+
sub: {
|
|
311
|
+
list: { desc: 'List trusted IPs', run: (a, f) => require('./cli/security').trustedList(a, f) },
|
|
312
|
+
add: { desc: 'Add trusted IP', usage: 'securenow trusted add <ip> [--app <key>] [--env local] [--label <label>]', run: (a, f) => require('./cli/security').trustedAdd(a, f) },
|
|
270
313
|
remove: { desc: 'Remove trusted IP', usage: 'securenow trusted remove <id>', run: (a, f) => require('./cli/security').trustedRemove(a, f) },
|
|
271
314
|
},
|
|
272
315
|
defaultSub: 'list',
|
|
@@ -499,8 +542,8 @@ function showHelp(commandName) {
|
|
|
499
542
|
'Observe': ['traces', 'logs', 'analytics'],
|
|
500
543
|
'Detect & Respond': ['human', 'notifications', 'alerts', 'fp'],
|
|
501
544
|
'Investigate': ['ip', 'forensics'],
|
|
502
|
-
'Firewall': ['firewall'],
|
|
503
|
-
'Remediation': ['blocklist', 'allowlist', 'trusted'],
|
|
545
|
+
'Firewall': ['firewall'],
|
|
546
|
+
'Remediation': ['automation', 'blocklist', 'allowlist', 'trusted'],
|
|
504
547
|
'Telemetry': ['log', 'test-span'],
|
|
505
548
|
'Utilities': ['redact', 'cidr', 'doctor', 'env', 'mcp'],
|
|
506
549
|
'Settings': ['instances', 'config', 'version'],
|
package/mcp/catalog.js
CHANGED
|
@@ -230,8 +230,11 @@ const TOOLS = [
|
|
|
230
230
|
readOnly: true,
|
|
231
231
|
method: 'GET',
|
|
232
232
|
endpoint: '/firewall/status',
|
|
233
|
-
queryFields: ['environment'],
|
|
234
|
-
inputSchema: objectSchema({
|
|
233
|
+
queryFields: ['environment', 'appKey'],
|
|
234
|
+
inputSchema: objectSchema({
|
|
235
|
+
appKey: string('Optional application key UUID to scope the status check.'),
|
|
236
|
+
...environmentInput,
|
|
237
|
+
}),
|
|
235
238
|
},
|
|
236
239
|
{
|
|
237
240
|
name: 'securenow_firewall_enable',
|
|
@@ -297,9 +300,10 @@ const TOOLS = [
|
|
|
297
300
|
method: 'GET',
|
|
298
301
|
endpoint: '/firewall/check/:ip',
|
|
299
302
|
pathParams: ['ip'],
|
|
300
|
-
queryFields: ['environment'],
|
|
303
|
+
queryFields: ['environment', 'appKey'],
|
|
301
304
|
inputSchema: objectSchema({
|
|
302
305
|
ip: string('IPv4 address to test.'),
|
|
306
|
+
appKey: string('Optional application key UUID to test the app/environment toggle and scoped lists.'),
|
|
303
307
|
...environmentInput,
|
|
304
308
|
}, ['ip']),
|
|
305
309
|
},
|
|
@@ -421,6 +425,19 @@ const TOOLS = [
|
|
|
421
425
|
id: string('Notification id.'),
|
|
422
426
|
}, ['id']),
|
|
423
427
|
},
|
|
428
|
+
{
|
|
429
|
+
name: 'securenow_notifications_batch_get',
|
|
430
|
+
title: 'Get Notifications Batch',
|
|
431
|
+
description: 'Fetch multiple notification/case records in one request.',
|
|
432
|
+
scope: 'notifications:read',
|
|
433
|
+
readOnly: true,
|
|
434
|
+
method: 'POST',
|
|
435
|
+
endpoint: '/notifications/batch',
|
|
436
|
+
bodyFields: ['ids'],
|
|
437
|
+
inputSchema: objectSchema({
|
|
438
|
+
ids: arrayOfStrings('Notification ids to fetch, up to 50.'),
|
|
439
|
+
}, ['ids']),
|
|
440
|
+
},
|
|
424
441
|
{
|
|
425
442
|
name: 'securenow_human_actions_list',
|
|
426
443
|
title: 'List Human Action Queue',
|
|
@@ -496,6 +513,26 @@ const TOOLS = [
|
|
|
496
513
|
...confirmSchema,
|
|
497
514
|
}, ['notificationId', 'ip', 'confirm', 'reason']),
|
|
498
515
|
},
|
|
516
|
+
{
|
|
517
|
+
name: 'securenow_human_case_action_update',
|
|
518
|
+
title: 'Update Case-Level Human Action',
|
|
519
|
+
description: 'Approve, reject, execute, or fail a case-level proposed action such as tune_rule or create_exclusion. Write action; requires confirmation.',
|
|
520
|
+
scope: 'notifications:write',
|
|
521
|
+
readOnly: false,
|
|
522
|
+
confirm: true,
|
|
523
|
+
method: 'PUT',
|
|
524
|
+
endpoint: '/notifications/:notificationId/agent-case/actions/:actionKey',
|
|
525
|
+
pathParams: ['notificationId', 'actionKey'],
|
|
526
|
+
bodyFields: ['status', 'result'],
|
|
527
|
+
reasonInResult: true,
|
|
528
|
+
inputSchema: objectSchema({
|
|
529
|
+
notificationId: string('Notification id from the case-action row.'),
|
|
530
|
+
actionKey: string('Proposed action key from the row, for example tune_rule:...'),
|
|
531
|
+
status: string('New status: proposed, approved, rejected, executed, or failed.'),
|
|
532
|
+
result: { type: 'object', additionalProperties: true, description: 'Optional structured result/audit details.' },
|
|
533
|
+
...confirmSchema,
|
|
534
|
+
}, ['notificationId', 'actionKey', 'status', 'confirm', 'reason']),
|
|
535
|
+
},
|
|
499
536
|
{
|
|
500
537
|
name: 'securenow_ip_lookup',
|
|
501
538
|
title: 'IP Intelligence Lookup',
|
|
@@ -567,6 +604,258 @@ const TOOLS = [
|
|
|
567
604
|
instanceId: string('Optional ClickHouse instance id.'),
|
|
568
605
|
}),
|
|
569
606
|
},
|
|
607
|
+
{
|
|
608
|
+
name: 'securenow_automation_rules_list',
|
|
609
|
+
title: 'List Automation Rules',
|
|
610
|
+
description: 'List blocklist automation rules with app/environment scope and stats.',
|
|
611
|
+
scope: 'automation:read',
|
|
612
|
+
readOnly: true,
|
|
613
|
+
method: 'GET',
|
|
614
|
+
endpoint: '/automation-rules',
|
|
615
|
+
inputSchema: objectSchema({}),
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
name: 'securenow_automation_rule_get',
|
|
619
|
+
title: 'Get Automation Rule',
|
|
620
|
+
description: 'Fetch one automation rule.',
|
|
621
|
+
scope: 'automation:read',
|
|
622
|
+
readOnly: true,
|
|
623
|
+
method: 'GET',
|
|
624
|
+
endpoint: '/automation-rules/:id',
|
|
625
|
+
pathParams: ['id'],
|
|
626
|
+
inputSchema: objectSchema({
|
|
627
|
+
id: string('Automation rule id.'),
|
|
628
|
+
}, ['id']),
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
name: 'securenow_automation_rule_create',
|
|
632
|
+
title: 'Create Automation Rule',
|
|
633
|
+
description: 'Create a blocklist automation rule. Write action; requires confirmation.',
|
|
634
|
+
scope: 'automation:write',
|
|
635
|
+
readOnly: false,
|
|
636
|
+
confirm: true,
|
|
637
|
+
method: 'POST',
|
|
638
|
+
endpoint: '/automation-rules',
|
|
639
|
+
bodyFields: ['name', 'description', 'conditions', 'conditionLogic', 'actions', 'applicationsAll', 'applicationKeys', 'environmentsAll', 'environments'],
|
|
640
|
+
inputSchema: objectSchema({
|
|
641
|
+
name: string('Rule name.'),
|
|
642
|
+
description: string('Optional rule description.'),
|
|
643
|
+
conditions: { type: 'array', items: { type: 'object', additionalProperties: true }, description: 'Condition array. Fields include abuseConfidenceScore, riskScore, alertName, alertTag, attackType, path, environment.' },
|
|
644
|
+
conditionLogic: string('AND or OR.'),
|
|
645
|
+
actions: { type: 'array', items: { type: 'object', additionalProperties: true }, description: 'Action array, for example [{ "type":"addToBlocklist", "config":{ "reason":"...", "ttlHours":24 }}].' },
|
|
646
|
+
applicationsAll: boolean('Apply to all applications.'),
|
|
647
|
+
applicationKeys: arrayOfStrings('Application keys when not applying to all applications.'),
|
|
648
|
+
environmentsAll: boolean('Apply to all environments.'),
|
|
649
|
+
environments: arrayOfStrings('Deployment environments when not applying to all environments.'),
|
|
650
|
+
...confirmSchema,
|
|
651
|
+
}, ['name', 'conditions', 'actions', 'confirm', 'reason']),
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: 'securenow_automation_rule_update',
|
|
655
|
+
title: 'Update Automation Rule',
|
|
656
|
+
description: 'Update a blocklist automation rule. Write action; requires confirmation.',
|
|
657
|
+
scope: 'automation:write',
|
|
658
|
+
readOnly: false,
|
|
659
|
+
confirm: true,
|
|
660
|
+
method: 'PUT',
|
|
661
|
+
endpoint: '/automation-rules/:id',
|
|
662
|
+
pathParams: ['id'],
|
|
663
|
+
bodyFields: ['name', 'description', 'conditions', 'conditionLogic', 'actions', 'status', 'applicationsAll', 'applicationKeys', 'environmentsAll', 'environments'],
|
|
664
|
+
inputSchema: objectSchema({
|
|
665
|
+
id: string('Automation rule id.'),
|
|
666
|
+
name: string('Rule name.'),
|
|
667
|
+
description: string('Optional rule description.'),
|
|
668
|
+
status: string('active or disabled.'),
|
|
669
|
+
conditions: { type: 'array', items: { type: 'object', additionalProperties: true }, description: 'Condition array.' },
|
|
670
|
+
conditionLogic: string('AND or OR.'),
|
|
671
|
+
actions: { type: 'array', items: { type: 'object', additionalProperties: true }, description: 'Action array.' },
|
|
672
|
+
applicationsAll: boolean('Apply to all applications.'),
|
|
673
|
+
applicationKeys: arrayOfStrings('Application keys when not applying to all applications.'),
|
|
674
|
+
environmentsAll: boolean('Apply to all environments.'),
|
|
675
|
+
environments: arrayOfStrings('Deployment environments when not applying to all environments.'),
|
|
676
|
+
...confirmSchema,
|
|
677
|
+
}, ['id', 'confirm', 'reason']),
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: 'securenow_automation_rule_dry_run',
|
|
681
|
+
title: 'Dry-Run Automation Rule',
|
|
682
|
+
description: 'Preview automation matches without writing blocklist entries.',
|
|
683
|
+
scope: 'automation:read',
|
|
684
|
+
readOnly: true,
|
|
685
|
+
method: 'POST',
|
|
686
|
+
endpoint: '/automation-rules/:id/dry-run',
|
|
687
|
+
pathParams: ['id'],
|
|
688
|
+
bodyFields: ['limit', 'sampleLimit'],
|
|
689
|
+
inputSchema: objectSchema({
|
|
690
|
+
id: string('Automation rule id.'),
|
|
691
|
+
limit: number('Maximum notifications to scan.', { minimum: 1, maximum: 2000 }),
|
|
692
|
+
sampleLimit: number('Maximum sample matches to return.', { minimum: 1, maximum: 50 }),
|
|
693
|
+
}, ['id']),
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
name: 'securenow_automation_rule_execute',
|
|
697
|
+
title: 'Execute Automation Rule',
|
|
698
|
+
description: 'Execute an automation rule and add matching IPs to the blocklist. Write action; requires confirmation.',
|
|
699
|
+
scope: 'automation:write',
|
|
700
|
+
readOnly: false,
|
|
701
|
+
destructive: true,
|
|
702
|
+
confirm: true,
|
|
703
|
+
method: 'POST',
|
|
704
|
+
endpoint: '/automation-rules/:id/execute',
|
|
705
|
+
pathParams: ['id'],
|
|
706
|
+
inputSchema: objectSchema({
|
|
707
|
+
id: string('Automation rule id.'),
|
|
708
|
+
...confirmSchema,
|
|
709
|
+
}, ['id', 'confirm', 'reason']),
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
name: 'securenow_automation_rule_delete',
|
|
713
|
+
title: 'Delete Automation Rule',
|
|
714
|
+
description: 'Delete an automation rule. Write action; requires confirmation.',
|
|
715
|
+
scope: 'automation:write',
|
|
716
|
+
readOnly: false,
|
|
717
|
+
destructive: true,
|
|
718
|
+
confirm: true,
|
|
719
|
+
method: 'DELETE',
|
|
720
|
+
endpoint: '/automation-rules/:id',
|
|
721
|
+
pathParams: ['id'],
|
|
722
|
+
inputSchema: objectSchema({
|
|
723
|
+
id: string('Automation rule id.'),
|
|
724
|
+
...confirmSchema,
|
|
725
|
+
}, ['id', 'confirm', 'reason']),
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
name: 'securenow_alert_rules_list',
|
|
729
|
+
title: 'List Alert Rules',
|
|
730
|
+
description: 'List alert rules, including system rules and app scope.',
|
|
731
|
+
scope: 'alerts:read',
|
|
732
|
+
readOnly: true,
|
|
733
|
+
method: 'GET',
|
|
734
|
+
endpoint: '/alert-rules',
|
|
735
|
+
inputSchema: objectSchema({}),
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
name: 'securenow_alert_rule_get',
|
|
739
|
+
title: 'Get Alert Rule',
|
|
740
|
+
description: 'Fetch one alert rule.',
|
|
741
|
+
scope: 'alerts:read',
|
|
742
|
+
readOnly: true,
|
|
743
|
+
method: 'GET',
|
|
744
|
+
endpoint: '/alert-rules/:id',
|
|
745
|
+
pathParams: ['id'],
|
|
746
|
+
inputSchema: objectSchema({
|
|
747
|
+
id: string('Alert rule id.'),
|
|
748
|
+
}, ['id']),
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
name: 'securenow_alert_rule_update',
|
|
752
|
+
title: 'Update Alert Rule',
|
|
753
|
+
description: 'Update alert rule scope/status/schedule/throttle or record a review action. Write action; requires confirmation.',
|
|
754
|
+
scope: 'alerts:write',
|
|
755
|
+
readOnly: false,
|
|
756
|
+
confirm: true,
|
|
757
|
+
method: 'PUT',
|
|
758
|
+
endpoint: '/alert-rules/:id',
|
|
759
|
+
pathParams: ['id'],
|
|
760
|
+
bodyFields: ['name', 'description', 'status', 'applicationsAll', 'applications', 'schedule', 'throttle', 'alertChannelIds', 'reviewAction', 'reviewNote', 'reviewNotificationId'],
|
|
761
|
+
inputSchema: objectSchema({
|
|
762
|
+
id: string('Alert rule id.'),
|
|
763
|
+
name: string('Rule name for custom rules.'),
|
|
764
|
+
description: string('Rule description for custom rules.'),
|
|
765
|
+
status: string('Active, Disabled, or Paused.'),
|
|
766
|
+
applicationsAll: boolean('Scope rule to all applications.'),
|
|
767
|
+
applications: arrayOfStrings('Application keys when applicationsAll is false.'),
|
|
768
|
+
schedule: { type: 'object', additionalProperties: true, description: 'Schedule patch.' },
|
|
769
|
+
throttle: { type: 'object', additionalProperties: true, description: 'Throttle patch.' },
|
|
770
|
+
alertChannelIds: arrayOfStrings('Alert channel ids.'),
|
|
771
|
+
reviewAction: string('Rule review action, e.g. keep_active or saved_new_version.'),
|
|
772
|
+
reviewNote: string('Review note.'),
|
|
773
|
+
reviewNotificationId: string('Notification id tied to this review.'),
|
|
774
|
+
...confirmSchema,
|
|
775
|
+
}, ['id', 'confirm', 'reason']),
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
name: 'securenow_alert_rule_test',
|
|
779
|
+
title: 'Test Alert Rule',
|
|
780
|
+
description: 'Start a live or dry-run alert rule test.',
|
|
781
|
+
scope: 'alerts:write',
|
|
782
|
+
readOnly: false,
|
|
783
|
+
confirm: true,
|
|
784
|
+
method: 'POST',
|
|
785
|
+
endpoint: '/alert-rules/:id/test',
|
|
786
|
+
pathParams: ['id'],
|
|
787
|
+
bodyFields: ['applicationKey', 'mode'],
|
|
788
|
+
inputSchema: objectSchema({
|
|
789
|
+
id: string('Alert rule id.'),
|
|
790
|
+
applicationKey: string('Application key to test.'),
|
|
791
|
+
mode: string('dry_run or live.'),
|
|
792
|
+
...confirmSchema,
|
|
793
|
+
}, ['id', 'confirm', 'reason']),
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
name: 'securenow_alert_rule_test_result',
|
|
797
|
+
title: 'Get Alert Rule Test Result',
|
|
798
|
+
description: 'Poll alert rule test status and results.',
|
|
799
|
+
scope: 'alerts:read',
|
|
800
|
+
readOnly: true,
|
|
801
|
+
method: 'GET',
|
|
802
|
+
endpoint: '/alert-rules/:id/test/:testId',
|
|
803
|
+
pathParams: ['id', 'testId'],
|
|
804
|
+
inputSchema: objectSchema({
|
|
805
|
+
id: string('Alert rule id.'),
|
|
806
|
+
testId: string('Alert rule test id.'),
|
|
807
|
+
}, ['id', 'testId']),
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
name: 'securenow_alert_rule_exclusions_list',
|
|
811
|
+
title: 'List Alert Rule Exclusions',
|
|
812
|
+
description: 'List exclusions embedded on one alert rule.',
|
|
813
|
+
scope: 'alerts:read',
|
|
814
|
+
readOnly: true,
|
|
815
|
+
method: 'GET',
|
|
816
|
+
endpoint: '/alert-rules/:id/exclusions',
|
|
817
|
+
pathParams: ['id'],
|
|
818
|
+
inputSchema: objectSchema({
|
|
819
|
+
id: string('Alert rule id.'),
|
|
820
|
+
}, ['id']),
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
name: 'securenow_alert_rule_exclusion_add',
|
|
824
|
+
title: 'Add Alert Rule Exclusion',
|
|
825
|
+
description: 'Add a restrictive exclusion to one alert rule. Write action; requires confirmation.',
|
|
826
|
+
scope: 'alerts:write',
|
|
827
|
+
readOnly: false,
|
|
828
|
+
confirm: true,
|
|
829
|
+
method: 'POST',
|
|
830
|
+
endpoint: '/alert-rules/:id/exclusions',
|
|
831
|
+
pathParams: ['id'],
|
|
832
|
+
bodyFields: ['conditions', 'matchMode', 'reason', 'pathPattern', 'isActive'],
|
|
833
|
+
inputSchema: objectSchema({
|
|
834
|
+
id: string('Alert rule id.'),
|
|
835
|
+
conditions: { type: 'array', items: { type: 'object', additionalProperties: true }, description: 'Restrictive exclusion conditions.' },
|
|
836
|
+
matchMode: string('all or any.'),
|
|
837
|
+
reason: string('Exclusion reason.'),
|
|
838
|
+
pathPattern: string('Optional path pattern.'),
|
|
839
|
+
isActive: boolean('Whether the exclusion is active.'),
|
|
840
|
+
...confirmSchema,
|
|
841
|
+
}, ['id', 'confirm', 'reason']),
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
name: 'securenow_alert_rule_exclusion_remove',
|
|
845
|
+
title: 'Remove Alert Rule Exclusion',
|
|
846
|
+
description: 'Remove an alert rule exclusion. Write action; requires confirmation.',
|
|
847
|
+
scope: 'alerts:write',
|
|
848
|
+
readOnly: false,
|
|
849
|
+
confirm: true,
|
|
850
|
+
method: 'DELETE',
|
|
851
|
+
endpoint: '/alert-rules/:id/exclusions/:exclusionId',
|
|
852
|
+
pathParams: ['id', 'exclusionId'],
|
|
853
|
+
inputSchema: objectSchema({
|
|
854
|
+
id: string('Alert rule id.'),
|
|
855
|
+
exclusionId: string('Exclusion id.'),
|
|
856
|
+
...confirmSchema,
|
|
857
|
+
}, ['id', 'exclusionId', 'confirm', 'reason']),
|
|
858
|
+
},
|
|
570
859
|
{
|
|
571
860
|
name: 'securenow_blocklist_list',
|
|
572
861
|
title: 'List Blocklist',
|
|
@@ -575,8 +864,12 @@ const TOOLS = [
|
|
|
575
864
|
readOnly: true,
|
|
576
865
|
method: 'GET',
|
|
577
866
|
endpoint: '/blocklist',
|
|
578
|
-
queryFields: ['page', 'limit'],
|
|
579
|
-
inputSchema: objectSchema({
|
|
867
|
+
queryFields: ['page', 'limit', 'appKey', 'environment'],
|
|
868
|
+
inputSchema: objectSchema({
|
|
869
|
+
...pagingInput,
|
|
870
|
+
appKey: string('Optional application key scope.'),
|
|
871
|
+
...environmentInput,
|
|
872
|
+
}),
|
|
580
873
|
},
|
|
581
874
|
{
|
|
582
875
|
name: 'securenow_blocklist_add',
|
|
@@ -587,12 +880,14 @@ const TOOLS = [
|
|
|
587
880
|
confirm: true,
|
|
588
881
|
method: 'POST',
|
|
589
882
|
endpoint: '/blocklist',
|
|
590
|
-
bodyFields: ['ip', 'reason', 'expiresAt', 'metadata'],
|
|
883
|
+
bodyFields: ['ip', 'reason', 'expiresAt', 'metadata', 'appKey', 'environment'],
|
|
591
884
|
inputSchema: objectSchema({
|
|
592
885
|
ip: string('IPv4 address or CIDR.'),
|
|
593
886
|
reason: string('Reason for blocking.'),
|
|
594
887
|
expiresAt: string('Optional expiry time as ISO 8601.'),
|
|
595
888
|
metadata: { type: 'object', additionalProperties: true, description: 'Optional metadata.' },
|
|
889
|
+
appKey: string('Optional application key to scope this block. Omit for all apps.'),
|
|
890
|
+
...environmentInput,
|
|
596
891
|
...confirmSchema,
|
|
597
892
|
}, ['ip', 'confirm', 'reason']),
|
|
598
893
|
},
|
|
@@ -629,8 +924,12 @@ const TOOLS = [
|
|
|
629
924
|
readOnly: true,
|
|
630
925
|
method: 'GET',
|
|
631
926
|
endpoint: '/allowlist',
|
|
632
|
-
queryFields: ['page', 'limit'],
|
|
633
|
-
inputSchema: objectSchema({
|
|
927
|
+
queryFields: ['page', 'limit', 'appKey', 'environment'],
|
|
928
|
+
inputSchema: objectSchema({
|
|
929
|
+
...pagingInput,
|
|
930
|
+
appKey: string('Optional application key scope.'),
|
|
931
|
+
...environmentInput,
|
|
932
|
+
}),
|
|
634
933
|
},
|
|
635
934
|
{
|
|
636
935
|
name: 'securenow_allowlist_add',
|
|
@@ -641,7 +940,7 @@ const TOOLS = [
|
|
|
641
940
|
confirm: true,
|
|
642
941
|
method: 'POST',
|
|
643
942
|
endpoint: '/allowlist',
|
|
644
|
-
bodyFields: ['ip', 'label', 'reason', 'expiresAt', 'applicationsAll', 'applicationKeys'],
|
|
943
|
+
bodyFields: ['ip', 'label', 'reason', 'expiresAt', 'applicationsAll', 'applicationKeys', 'environment'],
|
|
645
944
|
inputSchema: objectSchema({
|
|
646
945
|
ip: string('IPv4 address or CIDR.'),
|
|
647
946
|
label: string('Human-readable label.'),
|
|
@@ -649,6 +948,7 @@ const TOOLS = [
|
|
|
649
948
|
expiresAt: string('Optional expiry time as ISO 8601.'),
|
|
650
949
|
applicationsAll: boolean('Apply to all applications.'),
|
|
651
950
|
applicationKeys: arrayOfStrings('Application keys to scope this allowlist entry to.'),
|
|
951
|
+
...environmentInput,
|
|
652
952
|
...confirmSchema,
|
|
653
953
|
}, ['ip', 'confirm', 'reason']),
|
|
654
954
|
},
|
|
@@ -675,7 +975,11 @@ const TOOLS = [
|
|
|
675
975
|
readOnly: true,
|
|
676
976
|
method: 'GET',
|
|
677
977
|
endpoint: '/trusted-ips',
|
|
678
|
-
|
|
978
|
+
queryFields: ['appKey', 'environment'],
|
|
979
|
+
inputSchema: objectSchema({
|
|
980
|
+
appKey: string('Optional application key scope.'),
|
|
981
|
+
...environmentInput,
|
|
982
|
+
}),
|
|
679
983
|
},
|
|
680
984
|
{
|
|
681
985
|
name: 'securenow_trusted_add',
|
|
@@ -686,13 +990,14 @@ const TOOLS = [
|
|
|
686
990
|
confirm: true,
|
|
687
991
|
method: 'POST',
|
|
688
992
|
endpoint: '/trusted-ips',
|
|
689
|
-
bodyFields: ['ip', 'label', 'note', 'applicationsAll', 'applicationKeys'],
|
|
993
|
+
bodyFields: ['ip', 'label', 'note', 'applicationsAll', 'applicationKeys', 'environment'],
|
|
690
994
|
inputSchema: objectSchema({
|
|
691
995
|
ip: string('IPv4 address or CIDR.'),
|
|
692
996
|
label: string('Human-readable label.'),
|
|
693
997
|
note: string('Optional note.'),
|
|
694
998
|
applicationsAll: boolean('Apply to all applications.'),
|
|
695
999
|
applicationKeys: arrayOfStrings('Application keys to scope this trusted IP to.'),
|
|
1000
|
+
...environmentInput,
|
|
696
1001
|
...confirmSchema,
|
|
697
1002
|
}, ['ip', 'confirm', 'reason']),
|
|
698
1003
|
},
|
|
@@ -883,10 +1188,11 @@ function promptMessages(name, args = {}) {
|
|
|
883
1188
|
`Fetch page=${page}, limit=${limit} with securenow_human_actions_list, select row ${rowNumber}, then call securenow_notifications_get and securenow_human_action_report for that notificationId and IP.`,
|
|
884
1189
|
'Read the AI report, finalDecision, DAG steps, findings, proofs, metadata paths/user agents/status codes, and trace IDs.',
|
|
885
1190
|
'Open trace evidence with securenow_traces_show and correlated logs with securenow_logs_for_trace when trace IDs are available.',
|
|
886
|
-
'Return one
|
|
1191
|
+
'Return one clear outcome: Block IP, False Positive, Rule Tuning Needed, or Ambiguous. If evidence is ambiguous, stop and explain what is missing.',
|
|
887
1192
|
confirmWrites
|
|
888
1193
|
? 'The user requested execution. If evidence supports the decision, call securenow_human_action_block or securenow_human_action_false_positive with confirm:true and a precise reason.'
|
|
889
1194
|
: 'Do not execute write tools yet. Prepare the recommended decision and exact tool call the user can approve.',
|
|
1195
|
+
'If many IPs share the same benign path/status/user-agent pattern, recommend tightening the alert rule with a precise guard instead of reviewing each IP as malicious.',
|
|
890
1196
|
'False positives must be narrow: app + alert rule + path + method/status/user-agent/body evidence where possible. Never globally trust an IP by default.',
|
|
891
1197
|
].join('\n'),
|
|
892
1198
|
},
|
|
@@ -906,12 +1212,12 @@ function promptMessages(name, args = {}) {
|
|
|
906
1212
|
'Work my SecureNow Requires Human queue like a senior security analyst using the MCP tools.',
|
|
907
1213
|
`Review up to ${limit} row(s), most urgent first.${args.search ? ` Search filter: ${args.search}.` : ''}`,
|
|
908
1214
|
'Start with securenow_human_actions_list. For each row, call securenow_notifications_get and securenow_human_action_report, inspect the AI report/DAG/proofs/trace IDs, and fetch trace/log evidence where useful.',
|
|
909
|
-
'For each row choose exactly one outcome: Block IP, False Positive, or Skip because evidence is insufficient. Explain skipped rows.',
|
|
1215
|
+
'For each row choose exactly one outcome: Block IP, False Positive, Rule Tuning Needed, or Skip because evidence is insufficient. Explain skipped rows.',
|
|
910
1216
|
confirmWrites
|
|
911
1217
|
? 'The user requested execution. For supported decisions, call the correct write tool with confirm:true and a precise reason, then continue.'
|
|
912
1218
|
: 'Do not execute write tools yet. Produce a row-by-row action plan and exact MCP write calls for user approval.',
|
|
913
|
-
'For block decisions, use securenow_human_action_block. For false positives, use securenow_human_action_false_positive with restrictive conditions.
|
|
914
|
-
'End with counts: handled, proposed block, proposed false positive, skipped, still waiting.',
|
|
1219
|
+
'For block decisions, use securenow_human_action_block. For false positives, use securenow_human_action_false_positive with restrictive conditions. For case-level tune_rule/create_exclusion rows, inspect securenow_notifications_get and then use securenow_human_case_action_update only when the action is safe to approve/reject.',
|
|
1220
|
+
'End with counts: handled, proposed block, proposed false positive, rule tuning needed, skipped, still waiting.',
|
|
915
1221
|
].join('\n'),
|
|
916
1222
|
},
|
|
917
1223
|
},
|
|
@@ -991,6 +1297,12 @@ function buildApiRequest(tool, rawArgs = {}) {
|
|
|
991
1297
|
if (tool.reasonAsNote && !body.note && args.reason) {
|
|
992
1298
|
body.note = args.reason;
|
|
993
1299
|
}
|
|
1300
|
+
if (tool.reasonInResult && args.reason) {
|
|
1301
|
+
body.result = {
|
|
1302
|
+
...(body.result && typeof body.result === 'object' ? body.result : {}),
|
|
1303
|
+
reason: args.reason,
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
994
1306
|
|
|
995
1307
|
return {
|
|
996
1308
|
method: tool.method,
|