main
ts 108 lines 4.06 KB
Raw
1 // API client for the Detection Catalog — a discovery surface over CoPilot Searches.
2 // Backend routes live under the existing copilot_searches router because the
3 // catalog reads from the same in-memory rules cache; the URLs are namespaced
4 // under /catalog/* so they don't conflict with the rules-grid endpoints.
5
6 import type {
7 CatalogComplianceFrameworksResponse,
8 CatalogComplianceResponse,
9 CatalogCoverageGapsResponse,
10 CatalogLogTestRequest,
11 CatalogLogTestResponse,
12 CatalogStatsResponse,
13 CatalogStoryDetailResponse,
14 CatalogStoryListResponse,
15 CatalogWazuhRuleDetailResponse,
16 CatalogWazuhRulesResponse
17 } from "@/types/detectionCatalog.d"
18 import type { FlaskBaseResponse } from "@/types/flask.d"
19 import { HttpClient } from "../httpClient"
20
21 export default {
22 /** Top-level metrics for the catalog overview pane. */
23 getStats() {
24 return HttpClient.get<FlaskBaseResponse & CatalogStatsResponse>(`/copilot_searches/catalog/stats`)
25 },
26
27 /** List every analytic story with per-story aggregated summary fields. */
28 listStories() {
29 return HttpClient.get<FlaskBaseResponse & CatalogStoryListResponse>(`/copilot_searches/catalog/stories`)
30 },
31
32 /**
33 * Detail payload for one analytic story (description, why-it-matters narrative,
34 * detections table, data sources, references). The backend uses ``{story_name:path}``
35 * so spaces and other characters are tolerated — we encodeURIComponent here too.
36 */
37 getStory(storyName: string) {
38 return HttpClient.get<FlaskBaseResponse & CatalogStoryDetailResponse>(
39 `/copilot_searches/catalog/stories/${encodeURIComponent(storyName)}`
40 )
41 },
42
43 /**
44 * List the full Wazuh ruleset for the Wazuh Rules tab. Returns the whole
45 * corpus (~3–5k rules) in a single shot — pagination/filtering happens
46 * client-side. When the Wazuh Manager is unreachable, ``available=false``
47 * and ``unavailable_reason`` carries the human-readable cause.
48 *
49 * Pass ``customerCode`` to scope the firing-stats columns (Hits 30d /
50 * Hits 7d / Last fired) to a single customer's alerts. The rule list
51 * itself is unchanged — every rule is still returned — but rules without
52 * any hits for that customer get zeros.
53 */
54 listWazuhRules(customerCode?: string) {
55 return HttpClient.get<FlaskBaseResponse & CatalogWazuhRulesResponse>(`/copilot_searches/catalog/wazuh-rules`, {
56 params: customerCode ? { customer_code: customerCode } : {}
57 })
58 },
59
60 /** Full meta payload for one Wazuh rule (header, compliance, if-then details, …). */
61 getWazuhRule(ruleId: number) {
62 return HttpClient.get<FlaskBaseResponse & CatalogWazuhRuleDetailResponse>(
63 `/copilot_searches/catalog/wazuh-rules/${ruleId}`
64 )
65 },
66
67 /**
68 * MITRE techniques NOT covered by any rule across either corpus —
69 * the "where are our blind spots?" view. Sub-techniques are collapsed
70 * into their parents server-side (a hit on T1059.001 covers T1059).
71 */
72 listCoverageGaps() {
73 return HttpClient.get<FlaskBaseResponse & CatalogCoverageGapsResponse>(
74 `/copilot_searches/catalog/coverage-gaps`
75 )
76 },
77
78 /**
79 * Run a raw log line through Wazuh's logtest engine. Returns the matched
80 * rule (if any) + the full alert envelope. Stateless — no Wazuh session
81 * is created. Wrapped by the backend with mitre_matrix tactic-name
82 * enrichment so the result matches the rest of the catalog.
83 */
84 runLogTest(payload: CatalogLogTestRequest) {
85 return HttpClient.post<FlaskBaseResponse & CatalogLogTestResponse>(
86 `/copilot_searches/catalog/wazuh-rules/test`,
87 payload
88 )
89 },
90
91 /** List the compliance frameworks the Compliance tab can pivot by. */
92 listComplianceFrameworks() {
93 return HttpClient.get<FlaskBaseResponse & CatalogComplianceFrameworksResponse>(
94 `/copilot_searches/catalog/compliance/frameworks`
95 )
96 },
97
98 /**
99 * Wazuh rules grouped by control IDs for the given framework. Each group
100 * carries rule count + total firing hits — the "what coverage do we have
101 * for PCI 10.2.4?" answer in one round-trip.
102 */
103 getCompliancePivot(framework: string) {
104 return HttpClient.get<FlaskBaseResponse & CatalogComplianceResponse>(
105 `/copilot_searches/catalog/compliance/${encodeURIComponent(framework)}`
106 )
107 }
108 }