securenow 7.8.1 → 8.0.0

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/mcp/catalog.js CHANGED
@@ -13,7 +13,7 @@ Primary goals:
13
13
  - Use the latest published SecureNow npm package. Require securenow@7.8.0 or newer for split admin/runtime credentials.
14
14
  - By default, enable tracing, logs, POST request body capture, multipart metadata capture, and the SecureNow firewall.
15
15
  - If I explicitly ask for firewall-only mode, keep the same install/login/verification gates, but use firewall-only preload and do not add tracing, logging, or OTel instrumentation.
16
- - The firewall must protect the selected SecureNow app, use SecureNow's own blocklist/allowlist/IPDB data, and respect that app's SecureNow IPDB confidence threshold. Do not add custom IP reputation providers or custom auto-blocking.
16
+ - The firewall must protect the selected SecureNow app, use SecureNow's own blocklist/allowlist/IPDB data, and respect that app's SecureNow AI IPDB confidence threshold. Do not add custom IP reputation providers or custom auto-blocking.
17
17
  - Do not confuse IP Allowlist with Trusted IPs. IP Allowlist is restrictive deny-by-default: when any allowlist entry exists for an app/environment, only listed IPs can reach it and all other IPs are blocked. Use Trusted IPs for known-safe monitors, office/VPN traffic, or false-positive suppression. Only use allowlist after explicit human approval to lock the app/environment to known IPs.
18
18
 
19
19
  Safety rules:
@@ -64,7 +64,7 @@ Runbook:
64
64
  Local credentials should use config.runtime.deploymentEnvironment="local". Production runtime credentials should use "production". The app key stays the same; traces, logs, firewall status, forensics, and CLI/MCP queries are scoped by environment.
65
65
  9. Verify firewall and threshold:
66
66
  - Run npx securenow firewall apps and npx securenow firewall status.
67
- - Confirm the selected app is present, firewallEnabled is true, and the SecureNow IPDB confidence threshold is visible.
67
+ - Confirm the selected app is present, firewallEnabled is true, and the SecureNow AI IPDB confidence threshold is visible.
68
68
  - If firewallEnabled is false, run the documented per-app enable command, for example npx securenow firewall enable --app <appKey>, then verify again.
69
69
  10. End-to-end proof:
70
70
  - Run npx securenow doctor.
@@ -189,7 +189,7 @@ const TOOLS = [
189
189
  {
190
190
  name: 'securenow_auth_status',
191
191
  title: 'SecureNow Auth Status',
192
- description: 'Show local SecureNow credential source, selected app, and masked firewall key.',
192
+ description: 'Show local SecureNow credential source, selected app, and masked runtime API key.',
193
193
  scope: null,
194
194
  localOnly: true,
195
195
  readOnly: true,
@@ -238,7 +238,7 @@ const TOOLS = [
238
238
  {
239
239
  name: 'securenow_firewall_apps',
240
240
  title: 'List Firewall Apps',
241
- description: 'List applications with firewall toggle and SecureNow IPDB threshold state.',
241
+ description: 'List applications with firewall toggle and SecureNow AI IPDB threshold state.',
242
242
  scope: 'applications:read',
243
243
  readOnly: true,
244
244
  method: 'GET',
@@ -298,8 +298,8 @@ const TOOLS = [
298
298
  },
299
299
  {
300
300
  name: 'securenow_firewall_set_threshold',
301
- title: 'Set SecureNow IPDB Threshold',
302
- description: 'Set the per-app SecureNow IPDB confidence threshold. Write action; requires confirmation.',
301
+ title: 'Set SecureNow AI IPDB Threshold',
302
+ description: 'Set the per-app SecureNow AI IPDB confidence threshold. Write action; requires confirmation.',
303
303
  scope: 'applications:write',
304
304
  readOnly: false,
305
305
  confirm: true,
@@ -309,7 +309,7 @@ const TOOLS = [
309
309
  bodyFields: ['confidenceMinimum', 'environment'],
310
310
  inputSchema: objectSchema({
311
311
  appKey: string('Application key UUID.'),
312
- confidenceMinimum: number('Minimum SecureNow IPDB confidence score.', { minimum: 0, maximum: 100 }),
312
+ confidenceMinimum: number('Minimum SecureNow AI IPDB confidence score.', { minimum: 0, maximum: 100 }),
313
313
  ...environmentInput,
314
314
  ...confirmSchema,
315
315
  }, ['appKey', 'confidenceMinimum', 'confirm', 'reason']),
@@ -475,6 +475,190 @@ const TOOLS = [
475
475
  search: string('Optional search over IP, rule, path, or verdict.'),
476
476
  }),
477
477
  },
478
+ {
479
+ name: 'securenow_human_actions_grouped_list',
480
+ title: 'List Grouped Human Actions',
481
+ description: 'List Requires Human work as root-cause groups with batch-safety preview instead of one row per IP/action.',
482
+ scope: 'notifications:read',
483
+ readOnly: true,
484
+ method: 'GET',
485
+ endpoint: '/notifications/approval-task-groups',
486
+ queryFields: ['limit', 'page', 'search'],
487
+ inputSchema: objectSchema({
488
+ ...pagingInput,
489
+ search: string('Optional search over IP, rule, path, action, or verdict.'),
490
+ }),
491
+ },
492
+ {
493
+ name: 'securenow_alert_review_runs_list',
494
+ title: 'List Alert Review Root Causes',
495
+ description: 'List pending alert-review runs as root-cause groups with batch-action preview, rather than row-by-row noisy alert triage.',
496
+ scope: 'notifications:read',
497
+ readOnly: true,
498
+ method: 'GET',
499
+ endpoint: '/notifications/alert-review-runs',
500
+ queryFields: ['limit', 'page', 'search', 'rootCauseKey', 'groupKey', 'includeRuns', 'maxScan'],
501
+ inputSchema: objectSchema({
502
+ ...pagingInput,
503
+ search: string('Optional search over rule, route, IP, evidence, or proposed guard.'),
504
+ rootCauseKey: string('Optional root-cause group key to filter to one group.'),
505
+ groupKey: string('Compatibility alias for rootCauseKey.'),
506
+ includeRuns: boolean('Whether to include compact matching run rows in the response.'),
507
+ maxScan: number('Maximum pending-review rows to scan before grouping. Defaults to 500, capped at 1000.', { minimum: 1, maximum: 1000 }),
508
+ }),
509
+ },
510
+ {
511
+ name: 'securenow_alert_review_runs_count',
512
+ title: 'Count Alert Review Runs',
513
+ description: 'Count pending alert-review rows and the root-cause groups they collapse into.',
514
+ scope: 'notifications:read',
515
+ readOnly: true,
516
+ method: 'GET',
517
+ endpoint: '/notifications/alert-review-runs/count',
518
+ queryFields: ['search', 'maxScan'],
519
+ inputSchema: objectSchema({
520
+ search: string('Optional search over rule, route, IP, evidence, or proposed guard.'),
521
+ maxScan: number('Maximum pending-review rows to scan before grouping. Defaults to 500, capped at 1000.', { minimum: 1, maximum: 1000 }),
522
+ }),
523
+ },
524
+ {
525
+ name: 'securenow_alert_review_run_get',
526
+ title: 'Get Alert Review Run',
527
+ description: 'Fetch one pending-review alert run with its root-cause key, evidence-quality score, and similar open siblings.',
528
+ scope: 'notifications:read',
529
+ readOnly: true,
530
+ method: 'GET',
531
+ endpoint: '/notifications/alert-review-runs/:id',
532
+ pathParams: ['id'],
533
+ queryFields: ['maxScan'],
534
+ inputSchema: objectSchema({
535
+ id: string('Notification id for the alert review run.'),
536
+ maxScan: number('Maximum pending-review rows to scan for similar siblings. Defaults to 500, capped at 1000.', { minimum: 1, maximum: 1000 }),
537
+ }, ['id']),
538
+ },
539
+ {
540
+ name: 'securenow_alert_review_run_resolve',
541
+ title: 'Resolve Alert Review Run',
542
+ description: 'Close one pending alert-review row with an auditable no-write decision report.',
543
+ scope: 'notifications:write',
544
+ readOnly: false,
545
+ confirm: true,
546
+ method: 'POST',
547
+ endpoint: '/notifications/alert-review-runs/:id/resolve',
548
+ pathParams: ['id'],
549
+ fixedBody: { reportSource: 'mcp' },
550
+ bodyFields: ['confirm', 'reason', 'decisionReport', 'decisionSummary', 'outcome', 'evidence', 'reviewedHistory', 'traceIds', 'paths', 'methods', 'statusCodes', 'userAgents', 'missingProof', 'recommendations'],
551
+ inputSchema: objectSchema({
552
+ id: string('Notification id for the alert review run.'),
553
+ ...decisionReportInput,
554
+ ...confirmSchema,
555
+ }, ['id', 'confirm', 'reason']),
556
+ },
557
+ {
558
+ name: 'securenow_alert_review_run_dismiss',
559
+ title: 'Dismiss Alert Review Run',
560
+ description: 'Dismiss one pending alert-review row with an audit report and no enforcement write.',
561
+ scope: 'notifications:write',
562
+ readOnly: false,
563
+ confirm: true,
564
+ method: 'POST',
565
+ endpoint: '/notifications/alert-review-runs/:id/dismiss',
566
+ pathParams: ['id'],
567
+ fixedBody: { reportSource: 'mcp' },
568
+ bodyFields: ['confirm', 'reason', 'decisionReport', 'decisionSummary', 'outcome', 'evidence', 'reviewedHistory', 'traceIds', 'paths', 'methods', 'statusCodes', 'userAgents', 'missingProof', 'recommendations'],
569
+ inputSchema: objectSchema({
570
+ id: string('Notification id for the alert review run.'),
571
+ ...decisionReportInput,
572
+ ...confirmSchema,
573
+ }, ['id', 'confirm', 'reason']),
574
+ },
575
+ {
576
+ name: 'securenow_alert_review_run_keep_alive',
577
+ title: 'Keep Alert Rule Active',
578
+ description: 'Mark the noisy/paused alert rule as reviewed, keep it active, and close the pending alert-review row with an audit report.',
579
+ scope: 'notifications:write',
580
+ readOnly: false,
581
+ confirm: true,
582
+ method: 'POST',
583
+ endpoint: '/notifications/alert-review-runs/:id/keep-alive',
584
+ pathParams: ['id'],
585
+ fixedBody: { reportSource: 'mcp' },
586
+ bodyFields: ['confirm', 'reason', 'reviewNote', 'decisionReport', 'decisionSummary', 'evidence', 'reviewedHistory', 'traceIds', 'paths', 'methods', 'statusCodes', 'userAgents', 'missingProof', 'recommendations'],
587
+ inputSchema: objectSchema({
588
+ id: string('Notification id for the alert review run.'),
589
+ reviewNote: string('Optional rule-review note. Defaults to reason.'),
590
+ ...decisionReportInput,
591
+ ...confirmSchema,
592
+ }, ['id', 'confirm', 'reason']),
593
+ },
594
+ {
595
+ name: 'securenow_alert_review_run_apply_group_decision',
596
+ title: 'Apply Alert Review Group Decision',
597
+ description: 'Apply one verified no-write decision report to every currently matching alert-review row in a root-cause group, leaving non-matching rows open.',
598
+ scope: 'notifications:write',
599
+ readOnly: false,
600
+ confirm: true,
601
+ method: 'POST',
602
+ endpoint: '/notifications/alert-review-runs/apply-group-decision',
603
+ fixedBody: { reportSource: 'mcp' },
604
+ bodyFields: ['confirm', 'reason', 'rootCauseKey', 'groupKey', 'prototypeRunId', 'notificationIds', 'status', 'outcome', 'allowMixed', 'previewOnly', 'maxRows', 'maxScan', 'search', 'decisionReport', 'decisionSummary', 'evidence', 'reviewedHistory', 'traceIds', 'paths', 'methods', 'statusCodes', 'userAgents', 'missingProof', 'recommendations'],
605
+ inputSchema: objectSchema({
606
+ rootCauseKey: string('Root-cause group key from securenow_alert_review_runs_list.'),
607
+ groupKey: string('Compatibility alias for rootCauseKey.'),
608
+ prototypeRunId: string('Optional notification id whose root-cause key should be used.'),
609
+ notificationIds: arrayOfStrings('Optional subset of eligible notification ids to close. Leave empty to apply to every eligible row in the group.'),
610
+ status: string('Resolution status: resolved or dismissed. Defaults to resolved.'),
611
+ allowMixed: boolean('Allow a mixed decision/attribution group only after every row was manually reviewed. Defaults to false.'),
612
+ previewOnly: boolean('When true, returns the eligible rows without writing.'),
613
+ maxRows: number('Maximum rows to close in this batch. Defaults to 100, capped at 250.', { minimum: 1, maximum: 250 }),
614
+ maxScan: number('Maximum pending-review rows to scan before grouping. Defaults to 500, capped at 1000.', { minimum: 1, maximum: 1000 }),
615
+ search: string('Optional search filter applied before grouping.'),
616
+ ...decisionReportInput,
617
+ ...confirmSchema,
618
+ }, ['confirm', 'reason']),
619
+ },
620
+ {
621
+ name: 'securenow_alert_review_apply_group_decision',
622
+ title: 'Apply Alert Review Group Decision',
623
+ description: 'Compatibility alias for securenow_alert_review_run_apply_group_decision.',
624
+ scope: 'notifications:write',
625
+ readOnly: false,
626
+ confirm: true,
627
+ method: 'POST',
628
+ endpoint: '/notifications/alert-review-runs/apply-group-decision',
629
+ fixedBody: { reportSource: 'mcp' },
630
+ bodyFields: ['confirm', 'reason', 'rootCauseKey', 'groupKey', 'prototypeRunId', 'notificationIds', 'status', 'outcome', 'allowMixed', 'previewOnly', 'maxRows', 'maxScan', 'search', 'decisionReport', 'decisionSummary', 'evidence', 'reviewedHistory', 'traceIds', 'paths', 'methods', 'statusCodes', 'userAgents', 'missingProof', 'recommendations'],
631
+ inputSchema: objectSchema({
632
+ rootCauseKey: string('Root-cause group key from securenow_alert_review_runs_list.'),
633
+ groupKey: string('Compatibility alias for rootCauseKey.'),
634
+ prototypeRunId: string('Optional notification id whose root-cause key should be used.'),
635
+ notificationIds: arrayOfStrings('Optional subset of eligible notification ids to close. Leave empty to apply to every eligible row in the group.'),
636
+ status: string('Resolution status: resolved or dismissed. Defaults to resolved.'),
637
+ allowMixed: boolean('Allow a mixed decision/attribution group only after every row was manually reviewed. Defaults to false.'),
638
+ previewOnly: boolean('When true, returns the eligible rows without writing.'),
639
+ maxRows: number('Maximum rows to close in this batch. Defaults to 100, capped at 250.', { minimum: 1, maximum: 250 }),
640
+ maxScan: number('Maximum pending-review rows to scan before grouping. Defaults to 500, capped at 1000.', { minimum: 1, maximum: 1000 }),
641
+ search: string('Optional search filter applied before grouping.'),
642
+ ...decisionReportInput,
643
+ ...confirmSchema,
644
+ }, ['confirm', 'reason']),
645
+ },
646
+ {
647
+ name: 'securenow_alert_review_group_similar',
648
+ title: 'List Similar Alert Review Runs',
649
+ description: 'Fetch the current rows in one alert-review root-cause group for preview before a batch decision.',
650
+ scope: 'notifications:read',
651
+ readOnly: true,
652
+ method: 'GET',
653
+ endpoint: '/notifications/alert-review-runs',
654
+ queryFields: ['rootCauseKey', 'groupKey', 'includeRuns', 'maxScan'],
655
+ fixedQuery: { includeRuns: true },
656
+ inputSchema: objectSchema({
657
+ rootCauseKey: string('Root-cause group key from securenow_alert_review_runs_list.'),
658
+ groupKey: string('Compatibility alias for rootCauseKey.'),
659
+ maxScan: number('Maximum pending-review rows to scan before grouping. Defaults to 500, capped at 1000.', { minimum: 1, maximum: 1000 }),
660
+ }),
661
+ },
478
662
  {
479
663
  name: 'securenow_human_action_report',
480
664
  title: 'Get Human Action Report',
@@ -532,6 +716,25 @@ const TOOLS = [
532
716
  ...confirmSchema,
533
717
  }, ['notificationId', 'ip', 'confirm', 'reason']),
534
718
  },
719
+ {
720
+ name: 'securenow_human_action_close_no_write',
721
+ title: 'Close Human Action Without Enforcement',
722
+ description: 'Close one Requires Human IP row with an auditable no-write decision report. Use for ambiguous, skipped, rule-tuning-needed, new-rule-needed, rate-limited, or already-handled rows.',
723
+ scope: 'notifications:write',
724
+ readOnly: false,
725
+ confirm: true,
726
+ method: 'POST',
727
+ endpoint: '/notifications/:notificationId/ips/:ip/close-no-write',
728
+ pathParams: ['notificationId', 'ip'],
729
+ fixedBody: { reportSource: 'mcp' },
730
+ bodyFields: ['reason', 'decisionReport', 'decisionSummary', 'outcome', 'evidence', 'reviewedHistory', 'traceIds', 'paths', 'methods', 'statusCodes', 'userAgents', 'missingProof', 'recommendations'],
731
+ inputSchema: objectSchema({
732
+ notificationId: string('Notification id from the human action row.'),
733
+ ip: string('IP address for the row to close.'),
734
+ ...decisionReportInput,
735
+ ...confirmSchema,
736
+ }, ['notificationId', 'ip', 'confirm', 'reason']),
737
+ },
535
738
  {
536
739
  name: 'securenow_human_action_false_positive',
537
740
  title: 'Mark Human Action False Positive',
@@ -805,6 +1008,69 @@ const TOOLS = [
805
1008
  ...confirmSchema,
806
1009
  }, ['id', 'confirm', 'reason']),
807
1010
  },
1011
+ {
1012
+ name: 'securenow_rate_limit_list',
1013
+ title: 'List Rate Limits',
1014
+ description: 'List rate-limit remediation rules with app/environment scope.',
1015
+ scope: 'rate_limits:read',
1016
+ readOnly: true,
1017
+ method: 'GET',
1018
+ endpoint: '/rate-limits',
1019
+ queryFields: ['status', 'search', 'page', 'limit', 'appKey', 'environment'],
1020
+ inputSchema: objectSchema({
1021
+ status: string('Rule status: active, disabled, removed, or all. Defaults to active.'),
1022
+ search: string('Optional IP/path/name search.'),
1023
+ ...pagingInput,
1024
+ appKey: string('Optional application key scope.'),
1025
+ ...environmentInput,
1026
+ }),
1027
+ },
1028
+ {
1029
+ name: 'securenow_rate_limit_get',
1030
+ title: 'Get Rate Limit',
1031
+ description: 'Fetch one rate-limit remediation rule.',
1032
+ scope: 'rate_limits:read',
1033
+ readOnly: true,
1034
+ method: 'GET',
1035
+ endpoint: '/rate-limits/:id',
1036
+ pathParams: ['id'],
1037
+ inputSchema: objectSchema({
1038
+ id: string('Rate-limit rule id.'),
1039
+ }, ['id']),
1040
+ },
1041
+ {
1042
+ name: 'securenow_rate_limit_test',
1043
+ title: 'Test Rate Limit Match',
1044
+ description: 'Check whether an IP/path/method would match an active rate-limit rule.',
1045
+ scope: 'rate_limits:read',
1046
+ readOnly: true,
1047
+ method: 'GET',
1048
+ endpoint: '/rate-limits/check',
1049
+ queryFields: ['ip', 'path', 'method', 'appKey', 'environment'],
1050
+ inputSchema: objectSchema({
1051
+ ip: string('IPv4 address to test.'),
1052
+ path: string('Request path to test. Defaults to /.'),
1053
+ method: string('HTTP method to test. Defaults to GET.'),
1054
+ appKey: string('Optional application key scope.'),
1055
+ ...environmentInput,
1056
+ }, ['ip']),
1057
+ },
1058
+ {
1059
+ name: 'securenow_rate_limit_remove',
1060
+ title: 'Remove Rate Limit',
1061
+ description: 'Remove a rate-limit remediation rule. Write action; requires confirmation.',
1062
+ scope: 'rate_limits:write',
1063
+ readOnly: false,
1064
+ confirm: true,
1065
+ method: 'DELETE',
1066
+ endpoint: '/rate-limits/:id',
1067
+ pathParams: ['id'],
1068
+ bodyFields: ['reason'],
1069
+ inputSchema: objectSchema({
1070
+ id: string('Rate-limit rule id.'),
1071
+ ...confirmSchema,
1072
+ }, ['id', 'confirm', 'reason']),
1073
+ },
808
1074
  {
809
1075
  name: 'securenow_rate_limit_parse',
810
1076
  title: 'Parse Rate Limit Text',
@@ -908,6 +1174,256 @@ const TOOLS = [
908
1174
  ...confirmSchema,
909
1175
  }, ['id', 'sqlQuery', 'confirm', 'reason']),
910
1176
  },
1177
+ {
1178
+ name: 'securenow_alert_rule_instant_update',
1179
+ title: 'Update Instant Alert Rule',
1180
+ description: 'Patch instant rule conditions/config with version/hash guards and before/after seeded tests. System rules require applyGlobally:true and admin permission. Write action; requires confirmation.',
1181
+ scope: 'alerts:write',
1182
+ readOnly: false,
1183
+ confirm: true,
1184
+ method: 'PUT',
1185
+ endpoint: '/alert-rules/:id/instant',
1186
+ pathParams: ['id'],
1187
+ bodyFields: [
1188
+ 'operations',
1189
+ 'conditions',
1190
+ 'enabled',
1191
+ 'source',
1192
+ 'matchMode',
1193
+ 'executionMode',
1194
+ 'severity',
1195
+ 'throttle',
1196
+ 'expectedRuleVersion',
1197
+ 'expectedCurrentInstantHash',
1198
+ 'expectedCurrentRuleHash',
1199
+ 'applyGlobally',
1200
+ 'dryRun',
1201
+ 'reactivatePausedCopies',
1202
+ 'fixtures',
1203
+ 'reviewNotificationId',
1204
+ 'reviewNote',
1205
+ 'reason',
1206
+ ],
1207
+ fixedBody: { auditSource: 'mcp' },
1208
+ inputSchema: objectSchema({
1209
+ id: string('Alert rule id. Fetch with securenow_alert_rule_get first to get ruleVersion and instantHash.'),
1210
+ operations: {
1211
+ type: 'array',
1212
+ items: { type: 'object', additionalProperties: true },
1213
+ description: 'Condition patch operations: add_condition, remove_condition, update_condition. Use zero-based index/conditionIndex for remove/update.',
1214
+ },
1215
+ conditions: {
1216
+ type: 'array',
1217
+ items: { type: 'object', additionalProperties: true },
1218
+ description: 'Optional full replacement instant conditions array. Use operations for smaller patches.',
1219
+ },
1220
+ enabled: boolean('Whether instant detection is enabled.'),
1221
+ source: string('Instant event source. Currently trace.'),
1222
+ matchMode: string('Condition match mode: all or any.'),
1223
+ executionMode: string('Rule execution mode: instant or hybrid when instant detection is enabled.'),
1224
+ severity: string('Optional severity override: critical, high, medium, or low.'),
1225
+ throttle: { type: 'object', additionalProperties: true, description: 'Throttle patch, for example { enabled:true, minutes:15 }.' },
1226
+ expectedRuleVersion: number('Required current reviewState.reviewVersion from securenow_alert_rule_get.', { minimum: 1 }),
1227
+ expectedCurrentInstantHash: string('Required SHA-256 hash of the current instant config from securenow_alert_rule_get.'),
1228
+ expectedCurrentRuleHash: string('Optional SHA-256 hash of the current instant/throttle/severity surface from securenow_alert_rule_get.'),
1229
+ applyGlobally: boolean('Required true for system rules. Updates every customer copy of the system rule.'),
1230
+ dryRun: boolean('When true, validates and runs before/after seeded tests without saving.'),
1231
+ reactivatePausedCopies: boolean('Whether to reactivate paused noisy copies after the instant rule is tuned.'),
1232
+ fixtures: {
1233
+ type: 'array',
1234
+ items: { type: 'object', additionalProperties: true },
1235
+ description: 'Optional seeded fixtures. Each may include kind attack/benign, label, path, method, requestBody, requestHeaders, and expectMatch.',
1236
+ },
1237
+ reviewNotificationId: string('Optional notification id tied to this review/tuning action.'),
1238
+ reviewNote: string('Optional review note. Defaults to reason.'),
1239
+ ...confirmSchema,
1240
+ }, ['id', 'expectedRuleVersion', 'expectedCurrentInstantHash', 'confirm', 'reason']),
1241
+ },
1242
+ {
1243
+ name: 'securenow_alert_rule_conditions_get',
1244
+ title: 'Get Alert Rule Conditions',
1245
+ description: 'Fetch instant rule conditions with rule version and hash guards for safe condition-level tuning.',
1246
+ scope: 'alerts:read',
1247
+ readOnly: true,
1248
+ method: 'GET',
1249
+ endpoint: '/alert-rules/:id/conditions',
1250
+ pathParams: ['id'],
1251
+ inputSchema: objectSchema({
1252
+ id: string('Alert rule id.'),
1253
+ }, ['id']),
1254
+ },
1255
+ {
1256
+ name: 'securenow_alert_rule_condition_update',
1257
+ title: 'Update Alert Rule Condition',
1258
+ description: 'Patch instant alert-rule conditions with version/hash guards and seeded before/after tests. Compatibility alias for condition-level tuning.',
1259
+ scope: 'alerts:write',
1260
+ readOnly: false,
1261
+ confirm: true,
1262
+ method: 'PUT',
1263
+ endpoint: '/alert-rules/:id/instant',
1264
+ pathParams: ['id'],
1265
+ bodyFields: [
1266
+ 'confirm',
1267
+ 'operations',
1268
+ 'conditions',
1269
+ 'enabled',
1270
+ 'source',
1271
+ 'matchMode',
1272
+ 'executionMode',
1273
+ 'expectedRuleVersion',
1274
+ 'expectedCurrentInstantHash',
1275
+ 'expectedCurrentRuleHash',
1276
+ 'applyGlobally',
1277
+ 'reactivatePausedCopies',
1278
+ 'fixtures',
1279
+ 'reviewNotificationId',
1280
+ 'reviewNote',
1281
+ 'reason',
1282
+ ],
1283
+ fixedBody: { auditSource: 'mcp' },
1284
+ inputSchema: objectSchema({
1285
+ id: string('Alert rule id. Fetch with securenow_alert_rule_conditions_get first to get ruleVersion and instantHash.'),
1286
+ operations: {
1287
+ type: 'array',
1288
+ items: { type: 'object', additionalProperties: true },
1289
+ description: 'Condition patch operations: add_condition, remove_condition, update_condition. Use zero-based index/conditionIndex for remove/update.',
1290
+ },
1291
+ conditions: {
1292
+ type: 'array',
1293
+ items: { type: 'object', additionalProperties: true },
1294
+ description: 'Optional full replacement instant conditions array. Use operations for smaller patches.',
1295
+ },
1296
+ enabled: boolean('Whether instant detection is enabled.'),
1297
+ source: string('Instant event source. Currently trace.'),
1298
+ matchMode: string('Condition match mode: all or any.'),
1299
+ executionMode: string('Rule execution mode: instant or hybrid when instant detection is enabled.'),
1300
+ expectedRuleVersion: number('Required current reviewState.reviewVersion from securenow_alert_rule_conditions_get.', { minimum: 1 }),
1301
+ expectedCurrentInstantHash: string('Required SHA-256 hash of the current instant config from securenow_alert_rule_conditions_get.'),
1302
+ expectedCurrentRuleHash: string('Optional SHA-256 hash of the current instant/throttle/severity surface from securenow_alert_rule_conditions_get.'),
1303
+ applyGlobally: boolean('Required true for system rules. Updates every customer copy of the system rule.'),
1304
+ reactivatePausedCopies: boolean('Whether to reactivate paused noisy copies after the condition is tuned.'),
1305
+ fixtures: {
1306
+ type: 'array',
1307
+ items: { type: 'object', additionalProperties: true },
1308
+ description: 'Optional seeded fixtures. Each may include kind attack/benign, label, path, method, requestBody, requestHeaders, and expectMatch.',
1309
+ },
1310
+ reviewNotificationId: string('Optional notification id tied to this review/tuning action.'),
1311
+ reviewNote: string('Optional review note. Defaults to reason.'),
1312
+ ...confirmSchema,
1313
+ }, ['id', 'expectedRuleVersion', 'expectedCurrentInstantHash', 'confirm', 'reason']),
1314
+ },
1315
+ {
1316
+ name: 'securenow_alert_rule_condition_candidate_test',
1317
+ title: 'Test Alert Rule Condition Candidate',
1318
+ description: 'Dry-run a condition-level instant-rule patch without saving it, including seeded benign/attack fixture checks.',
1319
+ scope: 'alerts:write',
1320
+ readOnly: false,
1321
+ confirm: true,
1322
+ method: 'PUT',
1323
+ endpoint: '/alert-rules/:id/instant',
1324
+ pathParams: ['id'],
1325
+ bodyFields: [
1326
+ 'confirm',
1327
+ 'operations',
1328
+ 'conditions',
1329
+ 'enabled',
1330
+ 'source',
1331
+ 'matchMode',
1332
+ 'executionMode',
1333
+ 'expectedRuleVersion',
1334
+ 'expectedCurrentInstantHash',
1335
+ 'expectedCurrentRuleHash',
1336
+ 'applyGlobally',
1337
+ 'fixtures',
1338
+ 'reviewNotificationId',
1339
+ 'reviewNote',
1340
+ 'reason',
1341
+ ],
1342
+ fixedBody: { auditSource: 'mcp', dryRun: true },
1343
+ inputSchema: objectSchema({
1344
+ id: string('Alert rule id. Fetch with securenow_alert_rule_conditions_get first to get ruleVersion and instantHash.'),
1345
+ operations: {
1346
+ type: 'array',
1347
+ items: { type: 'object', additionalProperties: true },
1348
+ description: 'Candidate condition operations to test.',
1349
+ },
1350
+ conditions: {
1351
+ type: 'array',
1352
+ items: { type: 'object', additionalProperties: true },
1353
+ description: 'Optional full replacement instant conditions array to test.',
1354
+ },
1355
+ enabled: boolean('Whether instant detection would be enabled.'),
1356
+ source: string('Instant event source. Currently trace.'),
1357
+ matchMode: string('Condition match mode: all or any.'),
1358
+ executionMode: string('Rule execution mode: instant or hybrid when instant detection is enabled.'),
1359
+ expectedRuleVersion: number('Required current reviewState.reviewVersion from securenow_alert_rule_conditions_get.', { minimum: 1 }),
1360
+ expectedCurrentInstantHash: string('Required SHA-256 hash of the current instant config from securenow_alert_rule_conditions_get.'),
1361
+ expectedCurrentRuleHash: string('Optional SHA-256 hash of the current instant/throttle/severity surface from securenow_alert_rule_conditions_get.'),
1362
+ applyGlobally: boolean('Required true for system rules, even for admin dry-runs.'),
1363
+ fixtures: {
1364
+ type: 'array',
1365
+ items: { type: 'object', additionalProperties: true },
1366
+ description: 'Optional seeded fixtures. Each may include kind attack/benign, label, path, method, requestBody, requestHeaders, and expectMatch.',
1367
+ },
1368
+ reviewNotificationId: string('Optional notification id tied to this review/tuning action.'),
1369
+ reviewNote: string('Optional review note. Defaults to reason.'),
1370
+ ...confirmSchema,
1371
+ }, ['id', 'expectedRuleVersion', 'expectedCurrentInstantHash', 'confirm', 'reason']),
1372
+ },
1373
+ {
1374
+ name: 'securenow_alert_rule_condition_diff',
1375
+ title: 'Diff Alert Rule Condition Candidate',
1376
+ description: 'Return the current instant conditions, candidate conditions, hashes, and before/after fixture behavior without saving.',
1377
+ scope: 'alerts:write',
1378
+ readOnly: false,
1379
+ confirm: true,
1380
+ method: 'PUT',
1381
+ endpoint: '/alert-rules/:id/instant',
1382
+ pathParams: ['id'],
1383
+ bodyFields: [
1384
+ 'confirm',
1385
+ 'operations',
1386
+ 'conditions',
1387
+ 'enabled',
1388
+ 'source',
1389
+ 'matchMode',
1390
+ 'executionMode',
1391
+ 'expectedRuleVersion',
1392
+ 'expectedCurrentInstantHash',
1393
+ 'expectedCurrentRuleHash',
1394
+ 'applyGlobally',
1395
+ 'fixtures',
1396
+ 'reason',
1397
+ ],
1398
+ fixedBody: { auditSource: 'mcp', dryRun: true },
1399
+ inputSchema: objectSchema({
1400
+ id: string('Alert rule id. Fetch with securenow_alert_rule_conditions_get first to get ruleVersion and instantHash.'),
1401
+ operations: {
1402
+ type: 'array',
1403
+ items: { type: 'object', additionalProperties: true },
1404
+ description: 'Candidate condition operations to diff.',
1405
+ },
1406
+ conditions: {
1407
+ type: 'array',
1408
+ items: { type: 'object', additionalProperties: true },
1409
+ description: 'Optional full replacement instant conditions array to diff.',
1410
+ },
1411
+ enabled: boolean('Whether instant detection would be enabled.'),
1412
+ source: string('Instant event source. Currently trace.'),
1413
+ matchMode: string('Condition match mode: all or any.'),
1414
+ executionMode: string('Rule execution mode: instant or hybrid when instant detection is enabled.'),
1415
+ expectedRuleVersion: number('Required current reviewState.reviewVersion from securenow_alert_rule_conditions_get.', { minimum: 1 }),
1416
+ expectedCurrentInstantHash: string('Required SHA-256 hash of the current instant config from securenow_alert_rule_conditions_get.'),
1417
+ expectedCurrentRuleHash: string('Optional SHA-256 hash of the current instant/throttle/severity surface from securenow_alert_rule_conditions_get.'),
1418
+ applyGlobally: boolean('Required true for system rules, even for admin dry-runs.'),
1419
+ fixtures: {
1420
+ type: 'array',
1421
+ items: { type: 'object', additionalProperties: true },
1422
+ description: 'Optional seeded fixtures used to explain benign removals and attack coverage.',
1423
+ },
1424
+ ...confirmSchema,
1425
+ }, ['id', 'expectedRuleVersion', 'expectedCurrentInstantHash', 'confirm', 'reason']),
1426
+ },
911
1427
  {
912
1428
  name: 'securenow_alert_rule_test',
913
1429
  title: 'Test Alert Rule',
@@ -1355,7 +1871,7 @@ const PROMPTS = [
1355
1871
  {
1356
1872
  name: 'investigate_human_action_row',
1357
1873
  title: 'Investigate Human Action Row',
1358
- description: 'Use MCP tools to deeply review one Requires Human row and either block the IP or mark a scoped false positive.',
1874
+ description: 'Use MCP tools to deeply review one Requires Human row and reach a safe audited outcome.',
1359
1875
  arguments: [
1360
1876
  { name: 'rowNumber', description: '1-based row number from the Requires Human queue.', required: true },
1361
1877
  { name: 'page', description: 'Queue page number. Defaults to 1.', required: false },
@@ -1466,12 +1982,16 @@ function promptMessages(name, args = {}) {
1466
1982
  `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.`,
1467
1983
  'Read the AI report, finalDecision, investigation steps, findings, proofs, metadata paths/user agents/status codes, and trace IDs.',
1468
1984
  'Open trace evidence with securenow_traces_show and correlated logs with securenow_logs_for_trace when trace IDs are available.',
1469
- 'Return one clear outcome: Block IP, Rate Limit, False Positive, Rule Tuning Needed, or Ambiguous. If evidence is ambiguous, stop and explain what is missing.',
1985
+ 'Return one clear outcome: Block IP, Rate Limit, False Positive, Rule Tuning Applied/Needed, New Alert Rule Needed, Case Action Approved/Rejected, or Ambiguous/Skipped with exact missing proof.',
1986
+ 'A row can be finished without an enforcement write when no write is safe. In that case record or prepare a structured no-write decision report explaining the missing proof, missing permission, or vague guard.',
1987
+ 'If evidence is mixed, split the decision by IP/cluster. Block only proven malicious IPs, false-positive only exact benign shapes, rate-limit only route-specific volume abuse, and leave ambiguous members with missing proof.',
1988
+ 'If the right fix is global/system rule tuning and the MCP tool returns access denied, do not create customer-specific false positives. Prepare the exact attack-preserving guard/SQL, dry-run it if possible, and record outcome=rule_tuning with missingProof naming the missing permission.',
1989
+ 'If an AI proposed action says to use a shared benign shape but does not provide exact path/method/status/user-agent/body/header conditions, do not execute it. Record the missing guard fields instead.',
1470
1990
  'Use rate limiting only as temporary soft remediation for repeated route-specific abuse such as login brute force, credential stuffing, scraping/API bursts, enumeration, recon/probing, or repeated noisy payloads where risk/impact evidence is below the block threshold.',
1471
1991
  'Do not rate-limit instead of blocking when there is confirmed exploit success, token/data exposure, SSRF reachability, file read, RCE, persistence, malware/C2, or riskScore >= 85 with high-confidence malicious evidence. Do not rate-limit benign false positives, trusted monitors, app-server/proxy attribution problems, isolated one-off requests, or broad noisy rules that need alert tuning.',
1472
1992
  'Do not create or recommend IP Allowlist entries during investigations unless the user explicitly asks to lock the app/environment to a known set of IPs. For safe monitors, offices, VPNs, or false positives, use Trusted IPs or scoped false-positive exclusions instead.',
1473
1993
  confirmWrites
1474
- ? 'The user requested execution. If evidence supports the decision, call securenow_human_action_block, securenow_rate_limit_create_from_text plus securenow_human_action_decision_report_add(outcome=rate_limited), or securenow_human_action_false_positive with confirm:true, a precise reason, and a decisionReport containing summary, evidence, reviewedHistory, traceIds, and missingProof when relevant. If you skip/mark ambiguous but still need to record the audit trail, call securenow_human_action_decision_report_add.'
1994
+ ? 'The user requested execution. If evidence supports the decision, call securenow_human_action_block, securenow_rate_limit_create_from_text plus securenow_human_action_decision_report_add(outcome=rate_limited), or securenow_human_action_false_positive with confirm:true, a precise reason, and a decisionReport containing summary, evidence, reviewedHistory, traceIds, and missingProof when relevant. If no enforcement write is safe, call securenow_human_action_decision_report_add with outcome=rule_tuning, new_alert_rule, ambiguous, skipped, or other so the audit trail is complete without changing IP status.'
1475
1995
  : 'Do not execute write tools yet. Prepare the recommended decision and exact tool call the user can approve.',
1476
1996
  '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.',
1477
1997
  'False positives must be narrow: app + alert rule + path + method/status/user-agent/body evidence where possible. Never globally trust an IP by default.',
@@ -1493,15 +2013,19 @@ function promptMessages(name, args = {}) {
1493
2013
  'Work my SecureNow Requires Human queue like a senior security analyst using the MCP tools.',
1494
2014
  `Review up to ${limit} row(s), most urgent first.${args.search ? ` Search filter: ${args.search}.` : ''}`,
1495
2015
  'Start with securenow_human_actions_list. For each row, call securenow_notifications_get and securenow_human_action_report, inspect the AI report/investigation steps/proofs/trace IDs, and fetch trace/log evidence where useful.',
1496
- 'For each row choose exactly one outcome: Block IP, Rate Limit, False Positive, Rule Tuning Needed, or Skip because evidence is insufficient. Explain skipped rows.',
2016
+ 'For each row choose exactly one outcome: Block IP, Rate Limit, False Positive, Rule Tuning Applied/Needed, New Alert Rule Needed, Case Action Approved/Rejected, or Ambiguous/Skipped with exact missing proof.',
2017
+ 'Finished does not always mean enforcement changed. A row is also finished when a structured no-write decision report records the exact proof gap, missing permission, or vague guard and no weaker substitute write is used.',
2018
+ 'If a global/system rule update is the right fix but access is denied, do not mark customer-specific false positives just to clear the row. Prepare the exact attack-preserving guard/SQL, dry-run it when possible, and record outcome=rule_tuning with missingProof.',
2019
+ 'If evidence is mixed, do partial resolution: block proven malicious IPs, false-positive exact benign shapes, rate-limit route-specific volume abuse, and leave ambiguous IPs with their missing proof.',
2020
+ 'Reject vague AI proposed actions. If the prompt/action lacks concrete rule id, path, method, status, user-agent/body/header guard, benign samples, and attack examples that still match, record it as ambiguous or rule_tuning needed.',
1497
2021
  'Use Rate Limit only for repeated route-specific abuse where temporary friction is safer than blocking: login brute force/credential stuffing, password reset/account enumeration, scraping/API bursts, path or ID enumeration, recon/probing, or repeated noisy payloads without confirmed exploit success.',
1498
2022
  'Do not rate-limit confirmed high-risk attacks that should be blocked, benign traffic that should be false-positive scoped, broad noisy rules that need tuning, app-server/proxy attribution problems, or isolated one-off requests.',
1499
2023
  'Do not create or recommend IP Allowlist entries while working this queue unless the user explicitly approves deny-by-default allowlist mode for the whole app/environment. Use Trusted IPs for trusted/bypass cases.',
1500
2024
  confirmWrites
1501
- ? 'The user requested execution. For supported decisions, call the correct write tool with confirm:true, a precise reason, and a decisionReport containing summary, evidence, reviewedHistory, traceIds, and missingProof when relevant, then continue. For skipped/ambiguous/rule-tuning-needed rows that should be auditable without changing IP status, use securenow_human_action_decision_report_add.'
2025
+ ? 'The user requested execution. For supported decisions, call the correct write tool with confirm:true, a precise reason, and a decisionReport containing summary, evidence, reviewedHistory, traceIds, and missingProof when relevant, then continue. For skipped/ambiguous/rule-tuning-needed/new-rule-needed rows that should be auditable without changing enforcement status, use securenow_human_action_decision_report_add or securenow_human_case_decision_report_add.'
1502
2026
  : 'Do not execute write tools yet. Produce a row-by-row action plan and exact MCP write calls for user approval.',
1503
2027
  'For block decisions, use securenow_human_action_block. For rate-limit decisions, use securenow_rate_limit_create_from_text and then securenow_human_action_decision_report_add with outcome=rate_limited. 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.',
1504
- 'End with counts: handled, proposed block, rate limits created/proposed, proposed false positive, rule tuning needed, skipped, still waiting.',
2028
+ 'End with counts: handled, blocked/proposed block, rate limits created/proposed, false positives, rule tuning/admin permission blockers with exact guard, partial resolutions, skipped with missing proof, and still waiting.',
1505
2029
  ].join('\n'),
1506
2030
  },
1507
2031
  },
@@ -1523,7 +2047,7 @@ function promptMessages(name, args = {}) {
1523
2047
  'Start with securenow_blocklist_stats, then securenow_blocklist_pending_list({ page: 1, limit, environment }).',
1524
2048
  'For each pending block, inspect id, IP, source, reason, metadata.riskScore, metadata.aiRiskScore, metadata.abuseConfidenceScore, automation rule, linked notification, age, app, and environment.',
1525
2049
  'When investigationNotificationId exists, fetch securenow_notifications_get for that case and use the linked IP report/history as evidence.',
1526
- 'Approve only when the row has clear malicious evidence, riskScore >= 90, or SecureNow IPDB evidence score >= 80 with no false-positive/test signal.',
2050
+ 'Approve only when the row has clear malicious evidence, riskScore >= 90, or SecureNow AI IPDB evidence score >= 80 with no false-positive/test signal.',
1527
2051
  'Reject stale, ambiguous, synthetic/test, self-traffic, or false-positive rows. Prefer narrow rule/exclusion tuning when the same benign pattern repeats.',
1528
2052
  confirmWrites
1529
2053
  ? 'The user requested execution. Use securenow_blocklist_pending_approve or securenow_blocklist_pending_reject with confirm:true and a precise reason. Use bulk tools only after every selected row satisfies the same reviewed policy.'
@@ -1547,7 +2071,7 @@ function promptMessages(name, args = {}) {
1547
2071
  text: [
1548
2072
  'Configure SecureNow default automation using the MCP tools.',
1549
2073
  `Review environment context: ${environment}. Built-in defaults apply to all apps and all environments unless the customer narrows them later.`,
1550
- 'Use one canonical product score for automation decisions: riskScore. SecureNow IPDB / AbuseIPDB score and AI confidence remain supporting evidence.',
2074
+ 'Use one canonical product score for automation decisions: riskScore. SecureNow AI IPDB score and AI confidence remain supporting evidence.',
1551
2075
  'Built-in defaults are active by default:',
1552
2076
  '- Critical Risk Auto-Block: riskScore>=95, TTL 168h.',
1553
2077
  '- High Risk Auto-Block: riskScore>=90 AND riskScore<95, TTL 72h.',
@@ -1619,7 +2143,7 @@ function buildApiRequest(tool, rawArgs = {}) {
1619
2143
  endpoint = endpoint.replace(`:${key}`, encodeURIComponent(String(args[key])));
1620
2144
  }
1621
2145
 
1622
- const query = {};
2146
+ const query = { ...(tool.fixedQuery || {}) };
1623
2147
  for (const key of tool.queryFields || []) {
1624
2148
  let value = args[key];
1625
2149
  if (tool.normalize && typeof tool.normalize[key] === 'function') {