@cccsaurora/howler-ui 2.17.0-dev.617 → 2.18.0-dev.626

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.
Files changed (193) hide show
  1. package/api/index.d.ts +0 -2
  2. package/api/index.js +2 -4
  3. package/api/search/index.d.ts +1 -2
  4. package/api/search/index.js +1 -2
  5. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  6. package/components/app/App.js +0 -14
  7. package/components/app/providers/FavouritesProvider.js +2 -2
  8. package/components/app/providers/HitSearchProvider.d.ts +1 -0
  9. package/components/app/providers/HitSearchProvider.js +11 -6
  10. package/components/app/providers/HitSearchProvider.test.js +32 -11
  11. package/components/app/providers/LocalStorageProvider.js +1 -1
  12. package/components/app/providers/ParameterProvider.d.ts +2 -9
  13. package/components/app/providers/ParameterProvider.js +240 -165
  14. package/components/app/providers/ParameterProvider.test.js +14 -307
  15. package/components/elements/EditRow.d.ts +4 -1
  16. package/components/elements/EditRow.js +4 -4
  17. package/components/elements/PluginTypography.d.ts +1 -2
  18. package/components/elements/PluginTypography.js +2 -3
  19. package/components/elements/UserList.d.ts +2 -5
  20. package/components/elements/UserList.js +5 -14
  21. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  22. package/components/elements/display/ChipPopper.d.ts +1 -1
  23. package/components/elements/display/ChipPopper.js +1 -1
  24. package/components/elements/display/HowlerCard.js +1 -1
  25. package/components/elements/display/Modal.js +0 -1
  26. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  27. package/components/elements/display/icons/BundleButton.js +32 -0
  28. package/components/elements/hit/HitBanner.js +48 -28
  29. package/components/elements/hit/HitCard.js +1 -1
  30. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  31. package/components/elements/hit/HitOutline.js +7 -3
  32. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  33. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  34. package/components/elements/hit/HitRelated.d.ts +1 -1
  35. package/components/elements/hit/HitRelated.js +3 -30
  36. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  37. package/components/elements/hit/related/PivotLink.js +1 -1
  38. package/components/elements/hit/related/RelatedLink.d.ts +1 -0
  39. package/components/elements/hit/related/RelatedLink.js +2 -2
  40. package/components/elements/view/ViewTitle.js +1 -1
  41. package/components/hooks/useHitActions.d.ts +1 -1
  42. package/components/hooks/useHitActions.js +2 -2
  43. package/components/hooks/useHitSelection.js +24 -3
  44. package/components/hooks/useLocalStorage.d.ts +13 -0
  45. package/components/hooks/useLocalStorage.js +53 -0
  46. package/components/hooks/useLocalStorageItem.d.ts +18 -0
  47. package/components/hooks/useLocalStorageItem.js +78 -0
  48. package/components/hooks/useLocalStorageItem.test.d.ts +1 -0
  49. package/components/hooks/useLocalStorageItem.test.js +144 -0
  50. package/components/hooks/useMyLocalStorage.js +2 -2
  51. package/components/hooks/useMyPreferences.js +1 -10
  52. package/components/hooks/useMySearch.js +2 -2
  53. package/components/hooks/useMySitemap.js +1 -4
  54. package/components/hooks/useMyTheme.js +2 -9
  55. package/components/routes/action/view/ActionSearch.js +1 -1
  56. package/components/routes/advanced/QueryBuilder.js +1 -1
  57. package/components/routes/analytics/AnalyticDetails.js +2 -2
  58. package/components/routes/analytics/AnalyticSearch.js +1 -1
  59. package/components/routes/help/ApiDocumentation.js +1 -1
  60. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  61. package/components/routes/help/BundleDocumentation.js +12 -0
  62. package/components/routes/help/HitDocumentation.js +3 -1
  63. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  64. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  65. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  66. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  67. package/components/routes/hits/search/HitContextMenu.js +2 -3
  68. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  69. package/components/routes/hits/search/InformationPane.js +28 -6
  70. package/components/routes/hits/search/LayoutSettings.d.ts +3 -0
  71. package/components/routes/hits/search/LayoutSettings.js +18 -0
  72. package/components/routes/hits/search/QuerySettings.js +1 -2
  73. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  74. package/components/routes/hits/search/SearchPane.js +37 -13
  75. package/components/routes/hits/search/ViewLink.js +1 -1
  76. package/components/routes/hits/search/grid/EnhancedCell.js +1 -1
  77. package/components/routes/hits/view/HitViewer.js +4 -3
  78. package/components/routes/home/AnalyticCard.d.ts +2 -3
  79. package/components/routes/home/AnalyticCard.js +2 -2
  80. package/components/routes/home/ViewCard.js +1 -1
  81. package/components/routes/home/ViewRefresh.d.ts +23 -0
  82. package/components/routes/home/ViewRefresh.js +67 -0
  83. package/components/routes/home/index.js +9 -46
  84. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  85. package/components/routes/overviews/OverviewViewer.js +2 -2
  86. package/components/routes/settings/LocalSection.js +2 -1
  87. package/locales/en/translation.json +6 -42
  88. package/locales/fr/translation.json +4 -35
  89. package/models/WithMetadata.d.ts +1 -2
  90. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  91. package/models/entities/generated/Howler.d.ts +4 -0
  92. package/models/entities/generated/Rule.d.ts +10 -2
  93. package/models/entities/generated/Threat.d.ts +2 -2
  94. package/package.json +3 -18
  95. package/plugins/clue/components/ClueTypography.js +2 -2
  96. package/plugins/clue/utils.d.ts +1 -2
  97. package/utils/constants.d.ts +4 -3
  98. package/utils/constants.js +1 -0
  99. package/api/search/case.d.ts +0 -4
  100. package/api/search/case.js +0 -8
  101. package/api/v2/case/index.d.ts +0 -6
  102. package/api/v2/case/index.js +0 -18
  103. package/api/v2/index.d.ts +0 -4
  104. package/api/v2/index.js +0 -6
  105. package/api/v2/search/facet.d.ts +0 -3
  106. package/api/v2/search/facet.js +0 -12
  107. package/api/v2/search/index.d.ts +0 -5
  108. package/api/v2/search/index.js +0 -24
  109. package/components/elements/ObjectDetails.d.ts +0 -6
  110. package/components/elements/case/CaseCard.d.ts +0 -8
  111. package/components/elements/case/CaseCard.js +0 -39
  112. package/components/elements/case/CasePreview.d.ts +0 -6
  113. package/components/elements/case/CasePreview.js +0 -17
  114. package/components/elements/case/StatusIcon.d.ts +0 -5
  115. package/components/elements/case/StatusIcon.js +0 -13
  116. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -8
  117. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  118. package/components/elements/hit/related/RelatedRecords.js +0 -63
  119. package/components/elements/observable/ObservableCard.d.ts +0 -5
  120. package/components/elements/observable/ObservableCard.js +0 -7
  121. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  122. package/components/elements/observable/ObservablePreview.js +0 -12
  123. package/components/hooks/useRelatedRecords.d.ts +0 -13
  124. package/components/hooks/useRelatedRecords.js +0 -32
  125. package/components/routes/cases/CaseViewer.d.ts +0 -2
  126. package/components/routes/cases/CaseViewer.js +0 -24
  127. package/components/routes/cases/Cases.d.ts +0 -2
  128. package/components/routes/cases/Cases.js +0 -101
  129. package/components/routes/cases/constants.d.ts +0 -5
  130. package/components/routes/cases/constants.js +0 -5
  131. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  132. package/components/routes/cases/detail/AlertPanel.js +0 -32
  133. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  134. package/components/routes/cases/detail/CaseDashboard.js +0 -49
  135. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  136. package/components/routes/cases/detail/CaseDetails.js +0 -61
  137. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  138. package/components/routes/cases/detail/CaseOverview.js +0 -43
  139. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -6
  140. package/components/routes/cases/detail/CaseSidebar.js +0 -36
  141. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  142. package/components/routes/cases/detail/CaseTask.js +0 -57
  143. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  144. package/components/routes/cases/detail/ItemPage.js +0 -93
  145. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  146. package/components/routes/cases/detail/RelatedCasePanel.js +0 -31
  147. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  148. package/components/routes/cases/detail/TaskPanel.js +0 -52
  149. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -12
  150. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -19
  151. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  152. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -27
  153. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -13
  154. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -134
  155. package/components/routes/cases/detail/sidebar/types.d.ts +0 -3
  156. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  157. package/components/routes/cases/detail/sidebar/utils.js +0 -25
  158. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  159. package/components/routes/cases/hooks/useCase.js +0 -38
  160. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  161. package/components/routes/cases/modals/ResolveModal.js +0 -59
  162. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  163. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  164. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  165. package/components/routes/observables/ObservableViewer.js +0 -27
  166. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  167. package/models/entities/generated/Case.d.ts +0 -28
  168. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  169. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  170. package/models/entities/generated/EmailParent.d.ts +0 -19
  171. package/models/entities/generated/Enrichments.d.ts +0 -7
  172. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  173. package/models/entities/generated/HttpResponse.d.ts +0 -11
  174. package/models/entities/generated/Item.d.ts +0 -9
  175. package/models/entities/generated/Observable.d.ts +0 -84
  176. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  177. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  178. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  179. package/models/entities/generated/ObservableFile.d.ts +0 -36
  180. package/models/entities/generated/ObservableHowler.d.ts +0 -44
  181. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  182. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  183. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  184. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  185. package/models/entities/generated/ObservableSource.d.ts +0 -23
  186. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  187. package/models/entities/generated/ObservableTls.d.ts +0 -12
  188. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  189. package/models/entities/generated/Task.d.ts +0 -10
  190. package/utils/typeUtils.d.ts +0 -7
  191. package/utils/typeUtils.js +0 -18
  192. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  193. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
@@ -1,134 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Article, BookRounded, CheckCircle, ChevronRight, Folder as FolderIcon, Lightbulb, Link as LinkIcon, TableChart, Visibility } from '@mui/icons-material';
3
- import { Skeleton, Stack, Typography, useTheme } from '@mui/material';
4
- import api from '@cccsaurora/howler-ui/api';
5
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
6
- import { omit } from 'lodash-es';
7
- import { useCallback, useEffect, useMemo, useState } from 'react';
8
- import { Link, useLocation } from 'react-router-dom';
9
- import { ESCALATION_COLORS } from '@cccsaurora/howler-ui/utils/constants';
10
- import { buildTree } from './utils';
11
- // Static map: item type → MUI icon component (avoids re-creating closures on each render)
12
- const ICON_FOR_TYPE = {
13
- case: BookRounded,
14
- observable: Visibility,
15
- hit: CheckCircle,
16
- table: TableChart,
17
- lead: Lightbulb,
18
- reference: LinkIcon
19
- };
20
- const CaseFolder = ({ case: _case, folder, name, step = -1, rootCaseId, pathPrefix = '' }) => {
21
- const theme = useTheme();
22
- const location = useLocation();
23
- const { dispatchApi } = useMyApi();
24
- const [open, setOpen] = useState(true);
25
- const [caseStates, setCaseStates] = useState({});
26
- const [hitMetadata, setHitMetadata] = useState({});
27
- const tree = useMemo(() => folder || buildTree(_case?.items), [folder, _case?.items]);
28
- const currentRootCaseId = rootCaseId || _case?.case_id;
29
- // Stable string key so the effect only re-runs when the actual hit IDs change,
30
- // not on every array reference change.
31
- const hitIds = useMemo(() => tree.leaves
32
- ?.filter(l => l.type?.toLowerCase() === 'hit')
33
- .map(l => l.id)
34
- .filter(id => !!id) ?? [], [tree.leaves]);
35
- useEffect(() => {
36
- if (hitIds.length < 1) {
37
- return;
38
- }
39
- dispatchApi(api.search.hit.post({ query: `howler.id:(${hitIds.join(' OR ')})` }), { throwError: false }).then(result => {
40
- if ((result?.items?.length ?? 0) < 1)
41
- return;
42
- setHitMetadata(Object.fromEntries(result.items.map(hit => [hit.howler.id, hit.howler])));
43
- });
44
- }, [hitIds, dispatchApi]);
45
- // Returns the MUI colour token for the item's escalation, or undefined if none.
46
- const getEscalationColor = (itemType, itemKey, leafId) => {
47
- if (itemType === 'hit' && leafId) {
48
- const color = ESCALATION_COLORS[hitMetadata[leafId]?.escalation];
49
- if (color)
50
- return color;
51
- }
52
- if (itemType === 'case' && itemKey) {
53
- const color = ESCALATION_COLORS[caseStates[itemKey]?.data?.escalation];
54
- if (color)
55
- return color;
56
- }
57
- return undefined;
58
- };
59
- const toggleCase = useCallback((item, itemKey) => {
60
- const resolvedKey = itemKey || item.path || item.id;
61
- if (!resolvedKey)
62
- return;
63
- const prev = caseStates[resolvedKey] ?? { open: false, loading: false, data: null };
64
- const shouldOpen = !prev.open;
65
- setCaseStates(current => ({ ...current, [resolvedKey]: { ...prev, open: shouldOpen } }));
66
- if (!shouldOpen || !item.id || prev.data || prev.loading)
67
- return;
68
- setCaseStates(current => ({
69
- ...current,
70
- [resolvedKey]: { ...(current[resolvedKey] ?? prev), loading: true }
71
- }));
72
- dispatchApi(api.v2.case.get(item.id), { throwError: false })
73
- .then(caseResponse => {
74
- if (!caseResponse)
75
- return;
76
- setCaseStates(current => ({ ...current, [resolvedKey]: { ...current[resolvedKey], data: caseResponse } }));
77
- })
78
- .finally(() => {
79
- setCaseStates(current => ({ ...current, [resolvedKey]: { ...current[resolvedKey], loading: false } }));
80
- });
81
- }, [caseStates, dispatchApi]);
82
- return (_jsxs(Stack, { sx: { overflow: 'visible' }, children: [name && (_jsxs(Stack, { direction: "row", pl: step * 1.5, py: 0.25, sx: {
83
- cursor: 'pointer',
84
- transition: theme.transitions.create('background', { duration: 50 }),
85
- background: 'transparent',
86
- '&:hover': {
87
- background: theme.palette.grey[800]
88
- }
89
- }, onClick: () => setOpen(_open => !_open), children: [_jsx(ChevronRight, { fontSize: "small", color: "disabled", sx: [
90
- { transition: theme.transitions.create('transform', { duration: 100 }), transform: 'rotate(0deg)' },
91
- open && { transform: 'rotate(90deg)' }
92
- ] }), _jsx(FolderIcon, { fontSize: "small", color: "disabled" }), _jsx(Typography, { variant: "caption", color: "textSecondary", sx: { userSelect: 'none', pl: 0.5, textWrap: 'nowrap' }, children: name })] })), open && (_jsxs(_Fragment, { children: [Object.entries(omit(tree, 'leaves')).map(([path, subfolder]) => (_jsx(CaseFolder, { name: path, case: _case, folder: subfolder, step: step + 1, rootCaseId: currentRootCaseId, pathPrefix: pathPrefix }, `${_case?.case_id}-${path}`))), tree.leaves?.map(leaf => {
93
- const itemType = leaf.type?.toLowerCase();
94
- const isCase = itemType === 'case';
95
- const fullRelativePath = [pathPrefix, leaf.path].filter(Boolean).join('/');
96
- const itemKey = fullRelativePath || leaf.id;
97
- const nodeState = itemKey ? caseStates[itemKey] : null;
98
- const isCaseOpen = !!nodeState?.open;
99
- const isCaseLoading = !!nodeState?.loading;
100
- const nestedCase = nodeState?.data ?? null;
101
- const itemPath = itemType !== 'reference'
102
- ? fullRelativePath
103
- ? `/cases/${currentRootCaseId}/${fullRelativePath}`
104
- : `/cases/${currentRootCaseId}`
105
- : leaf.value;
106
- const escalationColor = getEscalationColor(itemType, itemKey, leaf.id);
107
- const iconColor = escalationColor ?? 'inherit';
108
- const leafColor = escalationColor ? `${escalationColor}.light` : 'text.secondary';
109
- const Icon = ICON_FOR_TYPE[itemType ?? ''] ?? Article;
110
- return (_jsxs(Stack, { children: [_jsxs(Stack, { direction: "row", pl: step * 1.5 + 1, py: 0.25, sx: [
111
- {
112
- cursor: 'pointer',
113
- overflow: 'visible',
114
- color: `${theme.palette.text.secondary} !important`,
115
- textDecoration: 'none',
116
- transition: theme.transitions.create('background', { duration: 100 }),
117
- background: 'transparent',
118
- '&:hover': {
119
- background: theme.palette.grey[800]
120
- }
121
- },
122
- decodeURIComponent(location.pathname) === itemPath && {
123
- background: theme.palette.grey[800]
124
- }
125
- ], onClick: () => isCase && toggleCase(leaf, itemKey), component: Link, to: itemPath, target: itemType === 'reference' ? '_blank' : undefined, rel: itemType === 'reference' ? 'noopener noreferrer' : undefined, children: [_jsx(ChevronRight, { fontSize: "small", sx: [
126
- !isCase && { opacity: 0 },
127
- isCase && {
128
- transition: theme.transitions.create('transform', { duration: 100 }),
129
- transform: isCaseOpen ? 'rotate(90deg)' : 'rotate(0deg)'
130
- }
131
- ] }), _jsx(Icon, { fontSize: "small", color: iconColor }), _jsx(Typography, { variant: "caption", color: leafColor, sx: { userSelect: 'none', pl: 0.5, textWrap: 'nowrap' }, children: leaf.path?.split('/').pop() || leaf.id })] }), isCase && isCaseOpen && isCaseLoading && (_jsx(Stack, { pl: step * 1.5 + 4, py: 0.25, children: _jsx(Skeleton, { width: 140, height: 16 }) })), isCase && isCaseOpen && nestedCase && (_jsx(CaseFolder, { case: nestedCase, step: step + 1, rootCaseId: currentRootCaseId, pathPrefix: fullRelativePath }))] }, `${_case?.case_id}-${leaf.id}-${leaf.path}`));
132
- })] }))] }));
133
- };
134
- export default CaseFolder;
@@ -1,3 +0,0 @@
1
- import type { Item } from 'models/entities/generated/Item';
2
-
3
- export type Tree = { leaves?: Item[]; [folder: string]: Tree | Item[] };
@@ -1,3 +0,0 @@
1
- import type { Item } from '@cccsaurora/howler-ui/models/entities/generated/Item';
2
- import type { Tree } from './types';
3
- export declare const buildTree: (items?: Item[]) => Tree;
@@ -1,25 +0,0 @@
1
- import { get, set } from 'lodash-es';
2
- export const buildTree = (items = []) => {
3
- // Root tree node stores direct children in `leaves` and nested folders as object keys.
4
- const tree = { leaves: [] };
5
- items.forEach(item => {
6
- // Ignore items that cannot be placed in the folder structure.
7
- if (!item?.path) {
8
- return;
9
- }
10
- // Split path into folder segments + item name, then remove the item name.
11
- const parts = item.path.split('/');
12
- parts.pop();
13
- if (parts.length > 0) {
14
- // Use dot notation so lodash `get/set` can address nested folder objects.
15
- const key = parts.join('.');
16
- const size = get(tree, key)?.leaves?.length || 0;
17
- // Append this item to the folder's `leaves` array.
18
- set(tree, `${key}.leaves.${size}`, item);
19
- return;
20
- }
21
- // Items without parent folders are top-level leaves.
22
- tree.leaves.push(item);
23
- });
24
- return tree;
25
- };
@@ -1,13 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- interface CaseArguments {
3
- case?: Case;
4
- caseId?: string;
5
- }
6
- interface CaseResult {
7
- case: Case;
8
- updateCase: (update: Partial<Case>) => Promise<void>;
9
- loading: boolean;
10
- missing: boolean;
11
- }
12
- declare const useCase: (args: CaseArguments) => CaseResult;
13
- export default useCase;
@@ -1,38 +0,0 @@
1
- import api from '@cccsaurora/howler-ui/api';
2
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
3
- import { useCallback, useEffect, useState } from 'react';
4
- const useCase = ({ caseId, case: providedCase }) => {
5
- const { dispatchApi } = useMyApi();
6
- const [loading, setLoading] = useState(false);
7
- const [missing, setMissing] = useState(false);
8
- const [_case, setCase] = useState(providedCase);
9
- useEffect(() => {
10
- if (providedCase) {
11
- setCase(providedCase);
12
- }
13
- }, [providedCase]);
14
- useEffect(() => {
15
- if (caseId) {
16
- setLoading(true);
17
- dispatchApi(api.v2.case.get(caseId), { throwError: false })
18
- .then(setCase)
19
- .finally(() => setLoading(false));
20
- }
21
- }, [caseId, dispatchApi]);
22
- const updateCase = useCallback(async (_updatedCase) => {
23
- if (!_case?.case_id) {
24
- return;
25
- }
26
- try {
27
- setCase(await dispatchApi(api.v2.case.put(_case.case_id, _updatedCase)));
28
- }
29
- catch (e) {
30
- setMissing(true);
31
- }
32
- finally {
33
- return;
34
- }
35
- }, [_case?.case_id, dispatchApi]);
36
- return { case: _case, updateCase, loading, missing };
37
- };
38
- export default useCase;
@@ -1,7 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import { type FC } from 'react';
3
- declare const ResolveModal: FC<{
4
- case: Case;
5
- onConfirm: () => void;
6
- }>;
7
- export default ResolveModal;
@@ -1,59 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { OpenInNew } from '@mui/icons-material';
3
- import { Autocomplete, Box, Button, Card, Chip, Divider, IconButton, LinearProgress, Skeleton, Stack, TextField, Typography } from '@mui/material';
4
- import api from '@cccsaurora/howler-ui/api';
5
- import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
6
- import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
7
- import AnalyticLink from '@cccsaurora/howler-ui/components/elements/hit/elements/AnalyticLink';
8
- import EscalationChip from '@cccsaurora/howler-ui/components/elements/hit/elements/EscalationChip';
9
- import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
10
- import useHitActions from '@cccsaurora/howler-ui/components/hooks/useHitActions';
11
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
12
- import { uniq } from 'lodash-es';
13
- import { useContext, useEffect, useMemo, useState } from 'react';
14
- import { useTranslation } from 'react-i18next';
15
- import { Link } from 'react-router-dom';
16
- import useCase from '../hooks/useCase';
17
- const ResolveModal = ({ case: _case, onConfirm }) => {
18
- const { t } = useTranslation();
19
- const { dispatchApi } = useMyApi();
20
- const { close } = useContext(ModalContext);
21
- const { config } = useContext(ApiConfigContext);
22
- const { updateCase } = useCase({ case: _case });
23
- const [loading, setLoading] = useState(true);
24
- const [rationale, setRationale] = useState('');
25
- const [assessment, setAssessment] = useState(null);
26
- const [hits, setHits] = useState([]);
27
- const hitIds = useMemo(() => uniq((_case?.items ?? []).filter(item => item.type === 'hit').map(item => item.id)), [_case?.items]);
28
- const { assess } = useHitActions(hits);
29
- useEffect(() => {
30
- (async () => {
31
- try {
32
- const result = await dispatchApi(api.search.hit.post({
33
- query: `howler.id:(${hitIds.join(' OR ')}) AND -howler.status:resolved`,
34
- metadata: ['analytic']
35
- }));
36
- setHits(result.items);
37
- }
38
- finally {
39
- setLoading(false);
40
- }
41
- })();
42
- }, [dispatchApi, hitIds]);
43
- const handleConfirm = async () => {
44
- setLoading(true);
45
- try {
46
- await assess(assessment, true, rationale);
47
- await updateCase({ status: 'resolved' });
48
- onConfirm();
49
- close();
50
- }
51
- finally {
52
- setLoading(false);
53
- }
54
- };
55
- return (_jsxs(Stack, { spacing: 2, p: 2, alignItems: "start", sx: { minWidth: 'min(1000px, 60vw)', maxHeight: '100%', height: '100%' }, children: [_jsx(Typography, { variant: "h4", children: t('modal.cases.resolve') }), _jsx(Typography, { children: t('modal.cases.resolve.description') }), _jsxs(Stack, { spacing: 1, overflow: "auto", width: "100%", flex: 1, children: [_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Box, { flex: 1, children: _jsx(TextField, { size: "small", fullWidth: true, placeholder: t('modal.rationale.label'), value: rationale, onChange: ev => setRationale(ev.target.value) }) }), _jsx(Box, { flex: 1, children: _jsx(Autocomplete, { size: "small", value: assessment, onChange: (_ev, _assessment) => setAssessment(_assessment), options: config.lookups['howler.assessment'], disablePortal: true, renderInput: params => (_jsx(TextField, { ...params, placeholder: t('hit.details.actions.assessment'), fullWidth: true })) }) })] }), _jsxs(Stack, { position: "relative", children: [_jsx(Divider, {}), _jsx(LinearProgress, { sx: { opacity: +loading } })] }), loading
56
- ? hitIds.map(id => _jsx(Skeleton, { variant: "rounded", height: "40px", width: "100%" }, id))
57
- : hits.map(hit => (_jsx(Card, { sx: { p: 1, flexShrink: 0 }, children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, width: "100%", children: [_jsx(AnalyticLink, { hit: hit, compressed: true, alignSelf: "center" }), _jsx(EscalationChip, { hit: hit, layout: HitLayout.DENSE }), _jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, size: "small", color: "primary" }), _jsx("div", { style: { flex: 1 } }), _jsx(IconButton, { size: "small", component: Link, to: `/hits/${hit.howler.id}`, children: _jsx(OpenInNew, { fontSize: "small" }) })] }) }, hit.howler.id)))] }), _jsxs(Stack, { direction: "row", spacing: 1, alignSelf: "end", children: [_jsx(Button, { variant: "outlined", color: "error", onClick: close, children: t('cancel') }), _jsx(Button, { variant: "outlined", color: "success", disabled: loading || !assessment || !rationale, onClick: handleConfirm, children: t('confirm') })] })] }));
58
- };
59
- export default ResolveModal;
@@ -1,2 +0,0 @@
1
- declare const _default: import("react").NamedExoticComponent<{}>;
2
- export default _default;
@@ -1,20 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { FilterList } from '@mui/icons-material';
3
- import { Autocomplete, TextField } from '@mui/material';
4
- import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
5
- import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
6
- import { memo } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { useContextSelector } from 'use-context-selector';
9
- const FILTER_OPTIONS = [
10
- { label: 'hit.search.filter.hit', value: 'hit' },
11
- { label: 'hit.search.filter.observable', value: 'observable' }
12
- ];
13
- const IndexPicker = () => {
14
- const { t } = useTranslation();
15
- const indexes = useContextSelector(ParameterContext, ctx => ctx.indexes);
16
- const setIndexes = useContextSelector(ParameterContext, ctx => ctx.setIndexes);
17
- const selectedOptions = FILTER_OPTIONS.filter(opt => indexes.includes(opt.value));
18
- return (_jsx(ChipPopper, { icon: _jsx(FilterList, { fontSize: "small" }), label: selectedOptions.map(opt => t(opt.label)).join(', '), minWidth: "225px", slotProps: { chip: { size: 'small' } }, children: _jsx(Autocomplete, { size: "small", multiple: true, options: FILTER_OPTIONS, value: selectedOptions, onChange: (_ev, values) => values.length > 0 && setIndexes(values.map(val => val.value)), isOptionEqualToValue: (opt, val) => opt.value === val.value, getOptionLabel: opt => t(opt.label), renderInput: params => _jsx(TextField, { ...params }) }) }));
19
- };
20
- export default memo(IndexPicker);
@@ -1,7 +0,0 @@
1
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
2
- import { type FC } from 'react';
3
- declare const ObservableViewer: FC<{
4
- observable?: Observable;
5
- observableId?: string;
6
- }>;
7
- export default ObservableViewer;
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Skeleton } from '@mui/material';
3
- import api from '@cccsaurora/howler-ui/api';
4
- import ObjectDetails from '@cccsaurora/howler-ui/components/elements/ObjectDetails';
5
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
6
- import { useEffect, useState } from 'react';
7
- const ObservableViewer = ({ observable: provided, observableId }) => {
8
- const { dispatchApi } = useMyApi();
9
- const [observable, setObservable] = useState(null);
10
- useEffect(() => {
11
- if (provided) {
12
- setObservable(provided);
13
- }
14
- }, [provided]);
15
- useEffect(() => {
16
- if (observableId) {
17
- dispatchApi(api.v2.search.post('observable', { query: `howler.id:${observableId}`, rows: 1 }), {
18
- throwError: false
19
- }).then(res => setObservable(res.items[0]));
20
- }
21
- }, [dispatchApi, observableId]);
22
- if (!observable) {
23
- return;
24
- }
25
- return _jsx(Box, { p: 1, children: observable ? _jsx(ObjectDetails, { obj: observable }) : _jsx(Skeleton, { height: 120 }) });
26
- };
27
- export default ObservableViewer;
@@ -1,12 +0,0 @@
1
- import type { FileHash } from './FileHash';
2
-
3
- /**
4
- * NOTE: This is an auto-generated file. Don't edit this manually.
5
- */
6
- export interface AttachmentsFile {
7
- extension?: string;
8
- hash?: FileHash;
9
- mime_type?: string;
10
- name?: string;
11
- size?: number;
12
- }
@@ -1,28 +0,0 @@
1
- import type { Enrichments } from './Enrichments';
2
- import type { Item } from './Item';
3
- import type { Rule } from './Rule';
4
- import type { Task } from './Task';
5
-
6
- /**
7
- * NOTE: This is an auto-generated file. Don't edit this manually.
8
- */
9
- export interface Case {
10
- case_id?: string;
11
- created?: string;
12
- end?: string;
13
- enrichments?: Enrichments;
14
- escalation?: string;
15
- indicators?: string[];
16
- items?: Item[];
17
- overview?: string;
18
- participants?: string[];
19
- rules?: Rule[];
20
- status?: string;
21
- start?: string;
22
- summary?: string;
23
- targets?: string[];
24
- tasks?: Task[];
25
- threats?: string[];
26
- title?: string;
27
- updated?: string;
28
- }
@@ -1,19 +0,0 @@
1
- import type { AutonomousSystems } from './AutonomousSystems';
2
- import type { Geo } from './Geo';
3
- import type { Nat } from './Nat';
4
-
5
- /**
6
- * NOTE: This is an auto-generated file. Don't edit this manually.
7
- */
8
- export interface DestinationOriginal {
9
- address?: string;
10
- autonomous_systems?: AutonomousSystems;
11
- bytes?: number;
12
- domain?: string;
13
- geo?: Geo;
14
- ip?: string;
15
- mac?: string;
16
- nat?: Nat;
17
- packets?: number;
18
- port?: number;
19
- }
@@ -1,8 +0,0 @@
1
- import type { AttachmentsFile } from './AttachmentsFile';
2
-
3
- /**
4
- * NOTE: This is an auto-generated file. Don't edit this manually.
5
- */
6
- export interface EmailAttachment {
7
- file?: AttachmentsFile;
8
- }
@@ -1,19 +0,0 @@
1
- import type { Bcc } from './Bcc';
2
- import type { Cc } from './Cc';
3
- import type { From } from './From';
4
- import type { To } from './To';
5
-
6
- /**
7
- * NOTE: This is an auto-generated file. Don't edit this manually.
8
- */
9
- export interface EmailParent {
10
- bcc?: Bcc;
11
- cc?: Cc;
12
- destination?: string;
13
- from?: From;
14
- message_id?: string;
15
- origination_timestamp?: string;
16
- source?: string;
17
- subject?: string;
18
- to?: To;
19
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * NOTE: This is an auto-generated file. Don't edit this manually.
3
- */
4
- export interface Enrichments {
5
- annotations?: string;
6
- path?: string;
7
- }
@@ -1,21 +0,0 @@
1
- import type { IndicatorEmail } from './IndicatorEmail';
2
- import type { IndicatorFile } from './IndicatorFile';
3
-
4
- /**
5
- * NOTE: This is an auto-generated file. Don't edit this manually.
6
- */
7
- export interface EnrichmentsIndicator {
8
- confidence?: string;
9
- description?: string;
10
- email?: IndicatorEmail;
11
- file?: IndicatorFile;
12
- first_seen?: string;
13
- ip?: string;
14
- last_seen?: string;
15
- port?: number;
16
- provider?: string;
17
- reference?: string;
18
- scanner_stats?: number;
19
- sightings?: number;
20
- type?: string;
21
- }
@@ -1,11 +0,0 @@
1
- import type { Body } from './Body';
2
-
3
- /**
4
- * NOTE: This is an auto-generated file. Don't edit this manually.
5
- */
6
- export interface HttpResponse {
7
- body?: Body;
8
- bytes?: number;
9
- mime_type?: string;
10
- status_code?: number;
11
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * NOTE: This is an auto-generated file. Don't edit this manually.
3
- */
4
- export interface Item {
5
- id?: string;
6
- path?: string;
7
- type?: string;
8
- value?: string;
9
- }
@@ -1,84 +0,0 @@
1
- import type { Agent } from './Agent';
2
- import type { Assemblyline } from './Assemblyline';
3
- import type { Aws } from './Aws';
4
- import type { Azure } from './Azure';
5
- import type { Cbs } from './Cbs';
6
- import type { Clue } from './Clue';
7
- import type { Container } from './Container';
8
- import type { Dns } from './Dns';
9
- import type { Ecs } from './Ecs';
10
- import type { Error } from './Error';
11
- import type { Event } from './Event';
12
- import type { Faas } from './Faas';
13
- import type { Gcp } from './Gcp';
14
- import type { Group } from './Group';
15
- import type { Host } from './Host';
16
- import type { Interface } from './Interface';
17
- import type { Network } from './Network';
18
- import type { ObservableCloud } from './ObservableCloud';
19
- import type { ObservableDestination } from './ObservableDestination';
20
- import type { ObservableEmail } from './ObservableEmail';
21
- import type { ObservableFile } from './ObservableFile';
22
- import type { ObservableHowler } from './ObservableHowler';
23
- import type { ObservableHttp } from './ObservableHttp';
24
- import type { ObservableObserver } from './ObservableObserver';
25
- import type { ObservableOrganization } from './ObservableOrganization';
26
- import type { ObservableProcess } from './ObservableProcess';
27
- import type { ObservableSource } from './ObservableSource';
28
- import type { ObservableThreat } from './ObservableThreat';
29
- import type { ObservableTls } from './ObservableTls';
30
- import type { Registry } from './Registry';
31
- import type { Related } from './Related';
32
- import type { Rule } from './Rule';
33
- import type { Server } from './Server';
34
- import type { Url } from './Url';
35
- import type { User } from './User';
36
- import type { UserAgent } from './UserAgent';
37
- import type { Vulnerability } from './Vulnerability';
38
-
39
- /**
40
- * NOTE: This is an auto-generated file. Don't edit this manually.
41
- */
42
- export interface Observable {
43
- agent?: Agent;
44
- assemblyline?: Assemblyline;
45
- aws?: Aws;
46
- azure?: Azure;
47
- cbs?: Cbs;
48
- cloud?: ObservableCloud;
49
- clue?: Clue;
50
- container?: Container;
51
- destination?: ObservableDestination;
52
- dns?: Dns;
53
- ecs?: Ecs;
54
- email?: ObservableEmail;
55
- error?: Error;
56
- event?: Event;
57
- faas?: Faas;
58
- file?: ObservableFile;
59
- gcp?: Gcp;
60
- group?: Group;
61
- host?: Host;
62
- howler: ObservableHowler;
63
- http?: ObservableHttp;
64
- interface?: Interface;
65
- labels?: { [index: string]: string };
66
- message?: string;
67
- network?: Network;
68
- observer?: ObservableObserver;
69
- organization?: ObservableOrganization;
70
- process?: ObservableProcess;
71
- registry?: Registry;
72
- related?: Related;
73
- rule?: Rule;
74
- server?: Server;
75
- source?: ObservableSource;
76
- tags?: string[];
77
- threat?: ObservableThreat;
78
- timestamp: string;
79
- tls?: ObservableTls;
80
- url?: Url;
81
- user?: User;
82
- user_agent?: UserAgent;
83
- vulnerability?: Vulnerability;
84
- }
@@ -1,20 +0,0 @@
1
- import type { CloudAccount } from './CloudAccount';
2
- import type { Instance } from './Instance';
3
- import type { Machine } from './Machine';
4
- import type { Project } from './Project';
5
- import type { Service } from './Service';
6
-
7
- /**
8
- * NOTE: This is an auto-generated file. Don't edit this manually.
9
- */
10
- export interface ObservableCloud {
11
- account?: CloudAccount;
12
- availability_zone?: string;
13
- instance?: Instance;
14
- machine?: Machine;
15
- project?: Project;
16
- provider?: string;
17
- region?: string;
18
- service?: Service;
19
- tenant_id?: string;
20
- }
@@ -1,23 +0,0 @@
1
- import type { AutonomousSystems } from './AutonomousSystems';
2
- import type { DestinationOriginal } from './DestinationOriginal';
3
- import type { Geo } from './Geo';
4
- import type { Nat } from './Nat';
5
- import type { User } from './User';
6
-
7
- /**
8
- * NOTE: This is an auto-generated file. Don't edit this manually.
9
- */
10
- export interface ObservableDestination {
11
- address?: string;
12
- autonomous_systems?: AutonomousSystems;
13
- bytes?: number;
14
- domain?: string;
15
- geo?: Geo;
16
- ip?: string;
17
- mac?: string;
18
- nat?: Nat;
19
- original?: DestinationOriginal;
20
- packets?: number;
21
- port?: number;
22
- user?: User;
23
- }