@cccsaurora/howler-ui 2.13.0-dev.96 → 2.13.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/api/hit/index.d.ts +1 -1
- package/api/hit/index.js +6 -2
- package/api/search/index.d.ts +1 -0
- package/api/view/index.d.ts +1 -1
- package/api/view/index.js +2 -2
- package/commons/components/notification/elements/item/NotificationItemDate.js +2 -2
- package/commons/components/utils/hooks/useEnv.d.ts +1 -1
- package/components/app/App.js +1 -3
- package/components/app/drawers/ApiKeyDrawer.js +4 -4
- package/components/app/hooks/useMatchers.d.ts +9 -0
- package/components/app/hooks/useMatchers.js +82 -0
- package/components/app/hooks/useMatchers.test.d.ts +1 -0
- package/components/app/hooks/useMatchers.test.js +237 -0
- package/components/app/hooks/useTitle.js +5 -4
- package/components/app/providers/AnalyticProvider.d.ts +0 -4
- package/components/app/providers/AnalyticProvider.js +1 -44
- package/components/app/providers/ApiConfigProvider.js +2 -1
- package/components/app/providers/HitProvider.d.ts +2 -1
- package/components/app/providers/HitProvider.js +8 -2
- package/components/app/providers/HitSearchProvider.d.ts +2 -1
- package/components/app/providers/HitSearchProvider.js +2 -1
- package/components/app/providers/SocketProvider.js +1 -1
- package/components/app/providers/ViewProvider.d.ts +1 -1
- package/components/app/providers/ViewProvider.js +3 -3
- package/components/app/providers/ViewProvider.test.d.ts +1 -0
- package/components/app/providers/ViewProvider.test.js +150 -0
- package/components/elements/display/ActionButton.d.ts +8 -0
- package/components/elements/display/ActionButton.js +18 -0
- package/components/elements/display/handlebars/helpers.js +17 -2
- package/components/elements/display/json/JSONViewer.d.ts +2 -0
- package/components/elements/display/json/JSONViewer.js +6 -13
- package/components/elements/hit/HitActions.js +4 -6
- package/components/elements/hit/HitBanner.js +13 -5
- package/components/elements/hit/HitCard.js +0 -5
- package/components/elements/hit/HitComments.js +5 -4
- package/components/elements/hit/HitOutline.d.ts +2 -2
- package/components/elements/hit/HitOutline.js +11 -21
- package/components/elements/hit/HitOverview.js +7 -4
- package/components/elements/hit/HitSummary.d.ts +2 -1
- package/components/elements/hit/HitSummary.js +8 -7
- package/components/elements/hit/aggregate/HitGraph.d.ts +1 -1
- package/components/elements/hit/aggregate/HitGraph.js +7 -7
- package/components/elements/hit/elements/HitTimestamp.js +8 -8
- package/components/elements/hit/related/PivotLink.js +11 -5
- package/components/elements/hit/related/RelatedIcon.d.ts +8 -0
- package/components/elements/hit/related/RelatedIcon.js +32 -0
- package/components/elements/hit/related/RelatedLink.js +4 -25
- package/components/hooks/useMyChart.d.ts +1 -1
- package/components/hooks/useMyChart.js +1 -1
- package/components/routes/advanced/QueryBuilder.js +47 -11
- package/components/routes/advanced/QueryEditor.js +8 -13
- package/components/routes/analytics/AnalyticOverview.d.ts +1 -1
- package/components/routes/analytics/AnalyticOverview.js +1 -1
- package/components/routes/analytics/AnalyticOverviews.d.ts +1 -1
- package/components/routes/analytics/AnalyticOverviews.js +1 -1
- package/components/routes/analytics/AnalyticSearch.js +14 -2
- package/components/routes/analytics/AnalyticTemplates.d.ts +1 -1
- package/components/routes/analytics/AnalyticTemplates.js +10 -7
- package/components/routes/analytics/RuleView.d.ts +1 -1
- package/components/routes/analytics/RuleView.js +1 -1
- package/components/routes/analytics/TriageSettings.d.ts +1 -1
- package/components/routes/analytics/TriageSettings.js +1 -1
- package/components/routes/analytics/widgets/Assessment.d.ts +1 -1
- package/components/routes/analytics/widgets/Assessment.js +1 -1
- package/components/routes/analytics/widgets/Created.d.ts +1 -1
- package/components/routes/analytics/widgets/Created.js +1 -1
- package/components/routes/analytics/widgets/Detection.d.ts +1 -1
- package/components/routes/analytics/widgets/Detection.js +1 -1
- package/components/routes/analytics/widgets/Escalation.d.ts +1 -1
- package/components/routes/analytics/widgets/Escalation.js +1 -1
- package/components/routes/analytics/widgets/Stacked.d.ts +1 -1
- package/components/routes/analytics/widgets/Stacked.js +1 -1
- package/components/routes/analytics/widgets/Status.d.ts +1 -1
- package/components/routes/analytics/widgets/Status.js +1 -1
- package/components/routes/help/SearchDocumentation.js +2 -1
- package/components/routes/help/TemplateDocumentation.js +5 -5
- package/components/routes/hits/search/HitBrowser.js +2 -2
- package/components/routes/hits/search/HitContextMenu.js +6 -7
- package/components/routes/hits/search/HitQuery.js +2 -1
- package/components/routes/hits/search/InformationPane.js +75 -78
- package/components/routes/hits/search/SearchPane.js +3 -9
- package/components/routes/hits/search/grid/AddColumnModal.js +10 -5
- package/components/routes/hits/search/grid/HitGrid.js +6 -5
- package/components/routes/hits/search/shared/CustomSpan.js +6 -6
- package/components/routes/hits/view/HitViewer.js +18 -26
- package/components/routes/home/index.js +4 -4
- package/components/routes/overviews/OverviewViewer.js +33 -31
- package/components/routes/settings/SecuritySection.js +2 -2
- package/components/routes/templates/TemplateViewer.js +27 -36
- package/components/routes/templates/Templates.js +4 -11
- package/components/routes/views/ViewComposer.js +8 -1
- package/components/routes/views/Views.js +25 -9
- package/index.js +7 -0
- package/locales/en/help/search.json +17 -0
- package/locales/en/translation.json +12 -3
- package/locales/fr/help/search.json +17 -0
- package/locales/fr/translation.json +12 -4
- package/models/WithMetadata.d.ts +10 -0
- package/models/WithMetadata.js +1 -0
- package/models/entities/generated/ApiType.d.ts +7 -0
- package/package.json +112 -111
- package/plugins/borealis/components/BorealisTypography.js +4 -2
- package/setupTests.d.ts +1 -0
- package/setupTests.js +12 -0
- package/tests/MockLocalStorage.d.ts +5 -0
- package/tests/MockLocalStorage.js +44 -0
- package/tests/server-handlers.d.ts +5 -0
- package/tests/server-handlers.js +97 -0
- package/tests/server.d.ts +3 -0
- package/tests/server.js +5 -0
- package/utils/constants.js +2 -2
- package/utils/stringUtils.d.ts +1 -0
- package/utils/stringUtils.js +9 -0
- package/utils/utils.js +3 -3
- package/components/app/providers/DossierProvider.d.ts +0 -16
- package/components/app/providers/DossierProvider.js +0 -82
- package/components/app/providers/TemplateProvider.d.ts +0 -14
- package/components/app/providers/TemplateProvider.js +0 -103
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Article
|
|
3
|
-
import {
|
|
2
|
+
import { Article } from '@mui/icons-material';
|
|
3
|
+
import { Stack, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
|
|
4
4
|
import api from '@cccsaurora/howler-ui/api';
|
|
5
5
|
import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
|
|
6
|
-
import { TemplateContext } from '@cccsaurora/howler-ui/components/app/providers/TemplateProvider';
|
|
7
6
|
import { TuiListProvider } from '@cccsaurora/howler-ui/components/elements/addons/lists';
|
|
8
7
|
import { TuiListMethodContext } from '@cccsaurora/howler-ui/components/elements/addons/lists/TuiListProvider';
|
|
9
8
|
import ItemManager from '@cccsaurora/howler-ui/components/elements/display/ItemManager';
|
|
10
9
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
11
10
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
12
|
-
import { useCallback, useContext, useEffect,
|
|
11
|
+
import { useCallback, useContext, useEffect, useState } from 'react';
|
|
13
12
|
import { useTranslation } from 'react-i18next';
|
|
14
13
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
15
|
-
import { useContextSelector } from 'use-context-selector';
|
|
16
14
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
17
15
|
import TemplateCard from './TemplateCard';
|
|
18
16
|
const TemplatesBase = () => {
|
|
@@ -22,11 +20,9 @@ const TemplatesBase = () => {
|
|
|
22
20
|
const { dispatchApi } = useMyApi();
|
|
23
21
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
24
22
|
const { load } = useContext(TuiListMethodContext);
|
|
25
|
-
const templates = useContextSelector(TemplateContext, ctx => ctx.templates);
|
|
26
23
|
const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
|
|
27
24
|
const [phrase, setPhrase] = useState('');
|
|
28
25
|
const [offset, setOffset] = useState(parseInt(searchParams.get('offset')) || 0);
|
|
29
|
-
const [showBuiltins, setShowBuiltins] = useState(true);
|
|
30
26
|
const [response, setResponse] = useState(null);
|
|
31
27
|
const [types, setTypes] = useState([]);
|
|
32
28
|
const [hasError, setHasError] = useState(false);
|
|
@@ -100,15 +96,12 @@ const TemplatesBase = () => {
|
|
|
100
96
|
}
|
|
101
97
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
98
|
}, [offset]);
|
|
103
|
-
const builtInTemplates = useMemo(() => templates.filter(template => template.type === 'readonly'), [templates]);
|
|
104
99
|
const renderer = useCallback((item, className) => _jsx(TemplateCard, { template: item, className: className }), []);
|
|
105
100
|
return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, searchFilters: _jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", children: _jsxs(ToggleButtonGroup, { sx: { display: 'grid', gridTemplateColumns: '1fr 1fr', alignSelf: 'start' }, size: "small", value: types, onChange: (__, _types) => {
|
|
106
101
|
if (_types) {
|
|
107
102
|
setTypes(_types.length < 2 ? _types : []);
|
|
108
103
|
}
|
|
109
|
-
}, children: [_jsx(ToggleButton, { value: "personal", "aria-label": "personal", children: t('route.templates.manager.personal') }), _jsx(ToggleButton, { value: "global", "aria-label": "global", children: t('route.templates.manager.global') })] }) }), aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.templates.search.prompt') }),
|
|
110
|
-
offset < 1 &&
|
|
111
|
-
builtInTemplates.length > 0 && (_jsx(Card, { sx: { p: 1, mb: 1 }, children: _jsxs(Stack, { children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Typography, { children: t('route.templates.builtin.show') }), _jsx(Tooltip, { title: t(`route.templates.builtin.${showBuiltins ? 'hide' : 'show'}`), children: _jsx(IconButton, { size: "small", onClick: () => setShowBuiltins(!showBuiltins), children: _jsx(KeyboardArrowDown, { fontSize: "small", sx: { transition: 'rotate 250ms', rotate: showBuiltins ? '180deg' : '0deg' } }) }) })] }), _jsxs(Collapse, { in: showBuiltins, children: [_jsx(Box, { sx: { mt: 1 } }), builtInTemplates.map(template => renderer(template))] })] }) })), renderer: ({ item }, classRenderer) => renderer(item.item, classRenderer()), response: response, onSelect: (item) => navigate(`/templates/view?type=${item.item.type}&analytic=${item.item.analytic}${item.item.detection ? '&detection=' + item.item.detection : ''}`), onCreate: () => navigate('/templates/view'), createPrompt: "route.templates.create", searchPrompt: "route.templates.manager.search", createIcon: _jsx(Article, { sx: { mr: 1 } }) }));
|
|
104
|
+
}, children: [_jsx(ToggleButton, { value: "personal", "aria-label": "personal", children: t('route.templates.manager.personal') }), _jsx(ToggleButton, { value: "global", "aria-label": "global", children: t('route.templates.manager.global') })] }) }), aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.templates.search.prompt') }), renderer: ({ item }, classRenderer) => renderer(item.item, classRenderer()), response: response, onSelect: (item) => navigate(`/templates/view?type=${item.item.type}&analytic=${item.item.analytic}${item.item.detection ? '&detection=' + item.item.detection : ''}`), onCreate: () => navigate('/templates/view'), createPrompt: "route.templates.create", searchPrompt: "route.templates.manager.search", createIcon: _jsx(Article, { sx: { mr: 1 } }) }));
|
|
112
105
|
};
|
|
113
106
|
const Templates = () => {
|
|
114
107
|
return (_jsx(TuiListProvider, { children: _jsx(TemplatesBase, {}) }));
|
|
@@ -72,7 +72,14 @@ const ViewComposer = () => {
|
|
|
72
72
|
navigate(`/views/${newView.view_id}`);
|
|
73
73
|
}
|
|
74
74
|
else {
|
|
75
|
-
await editView(routeParams.id,
|
|
75
|
+
await editView(routeParams.id, {
|
|
76
|
+
title,
|
|
77
|
+
type,
|
|
78
|
+
query,
|
|
79
|
+
sort,
|
|
80
|
+
span,
|
|
81
|
+
settings: { advance_on_triage: advanceOnTriage }
|
|
82
|
+
});
|
|
76
83
|
}
|
|
77
84
|
showSuccessMessage(t(routeParams.id ? 'route.views.update.success' : 'route.views.create.success'));
|
|
78
85
|
}
|
|
@@ -39,7 +39,7 @@ const ViewsBase = () => {
|
|
|
39
39
|
const [phrase, setPhrase] = useState('');
|
|
40
40
|
const [offset, setOffset] = useState(parseInt(searchParams.get('offset')) || 0);
|
|
41
41
|
const [response, setResponse] = useState(null);
|
|
42
|
-
const [
|
|
42
|
+
const [type, setType] = useState(searchParams.get('type') || null);
|
|
43
43
|
const [hasError, setHasError] = useState(false);
|
|
44
44
|
const [searching, setSearching] = useState(false);
|
|
45
45
|
const [favouritesOnly, setFavouritesOnly] = useState(false);
|
|
@@ -58,7 +58,7 @@ const ViewsBase = () => {
|
|
|
58
58
|
setSearchParams(searchParams, { replace: true });
|
|
59
59
|
const searchTerm = phrase ? `*${sanitizeLuceneQuery(phrase)}*` : '*';
|
|
60
60
|
const phraseQuery = FIELDS_TO_SEARCH.map(_field => `${_field}:${searchTerm}`).join(' OR ');
|
|
61
|
-
const typeQuery = `(type:global OR owner:(${user.username} OR none)) AND type:(${
|
|
61
|
+
const typeQuery = `(type:global OR owner:(${user.username} OR none)) AND type:(${type ?? '*'}${type === 'personal' ? ' OR readonly' : ''})`;
|
|
62
62
|
const favouritesQuery = favouritesOnly && user.favourite_views.length > 0 ? ` AND view_id:(${user.favourite_views.join(' OR ')})` : '';
|
|
63
63
|
setResponse(await dispatchApi(api.search.view.post({
|
|
64
64
|
query: `(${phraseQuery}) AND ${typeQuery}${favouritesQuery}`,
|
|
@@ -78,7 +78,7 @@ const ViewsBase = () => {
|
|
|
78
78
|
searchParams,
|
|
79
79
|
user.username,
|
|
80
80
|
user.favourite_views,
|
|
81
|
-
|
|
81
|
+
type,
|
|
82
82
|
favouritesOnly,
|
|
83
83
|
dispatchApi,
|
|
84
84
|
pageCount,
|
|
@@ -132,9 +132,29 @@ const ViewsBase = () => {
|
|
|
132
132
|
setDefaultViewLoading(false);
|
|
133
133
|
}
|
|
134
134
|
}, [fetchViews]);
|
|
135
|
+
const onTypeChange = useCallback(async (_type) => {
|
|
136
|
+
setType(_type);
|
|
137
|
+
if (_type) {
|
|
138
|
+
searchParams.delete('type');
|
|
139
|
+
searchParams.set('type', _type);
|
|
140
|
+
setSearchParams(searchParams, { replace: true });
|
|
141
|
+
}
|
|
142
|
+
else if (searchParams.has('type')) {
|
|
143
|
+
searchParams.delete('type');
|
|
144
|
+
setSearchParams(searchParams, { replace: true });
|
|
145
|
+
}
|
|
146
|
+
}, [searchParams, setSearchParams]);
|
|
135
147
|
useEffect(() => {
|
|
148
|
+
let changed = false;
|
|
136
149
|
if (!searchParams.has('offset')) {
|
|
137
150
|
searchParams.set('offset', '0');
|
|
151
|
+
changed = true;
|
|
152
|
+
}
|
|
153
|
+
if (searchParams.has('type') && !['personal', 'global'].includes(searchParams.get('type'))) {
|
|
154
|
+
searchParams.delete('type');
|
|
155
|
+
changed = true;
|
|
156
|
+
}
|
|
157
|
+
if (changed) {
|
|
138
158
|
setSearchParams(searchParams, { replace: true });
|
|
139
159
|
}
|
|
140
160
|
}, [searchParams, setSearchParams]);
|
|
@@ -150,12 +170,8 @@ const ViewsBase = () => {
|
|
|
150
170
|
onSearch();
|
|
151
171
|
}
|
|
152
172
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
153
|
-
}, [offset, favouritesOnly,
|
|
154
|
-
return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, searchFilters: _jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", children: _jsxs(ToggleButtonGroup, { sx: { display: 'grid', gridTemplateColumns: '1fr 1fr' }, size: "small", value:
|
|
155
|
-
if (_types) {
|
|
156
|
-
setTypes(_types.length < 2 ? _types : []);
|
|
157
|
-
}
|
|
158
|
-
}, children: [_jsx(ToggleButton, { value: "personal", "aria-label": "personal", children: t('route.views.manager.personal') }), _jsx(ToggleButton, { value: "global", "aria-label": "global", children: t('route.views.manager.global') })] }) }), aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.views.search.prompt') }), afterSearch: size(views) > 0 ? (_jsx(Autocomplete, { open: defaultViewOpen, loading: defaultViewLoading, onOpen: onDefaultViewOpen, onClose: () => setDefaultViewOpen(false), options: Object.values(omitBy(views, isNull)), renderOption: ({ key, ...props }, o) => (_createElement("li", { ...props, key: key },
|
|
173
|
+
}, [offset, favouritesOnly, type]);
|
|
174
|
+
return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, searchFilters: _jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", children: _jsxs(ToggleButtonGroup, { sx: { display: 'grid', gridTemplateColumns: '1fr 1fr' }, size: "small", value: type, exclusive: true, onChange: (__, _type) => onTypeChange(_type), children: [_jsx(ToggleButton, { value: "personal", "aria-label": "personal", children: t('route.views.manager.personal') }), _jsx(ToggleButton, { value: "global", "aria-label": "global", children: t('route.views.manager.global') })] }) }), aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.views.search.prompt') }), afterSearch: size(views) > 0 ? (_jsx(Autocomplete, { open: defaultViewOpen, loading: defaultViewLoading, onOpen: onDefaultViewOpen, onClose: () => setDefaultViewOpen(false), options: Object.values(omitBy(views, isNull)), renderOption: ({ key, ...props }, o) => (_createElement("li", { ...props, key: key },
|
|
159
175
|
_jsxs(Stack, { children: [_jsx(Typography, { variant: "body1", children: t(o.title) }), _jsx(Typography, { variant: "caption", children: _jsx("code", { children: o.query }) })] }))), renderInput: params => (_jsx(TextField, { ...params, label: t('route.views.manager.default'), sx: { minWidth: '300px' } })), filterOptions: (_views, { inputValue }) => _views.filter(v => t(v.title).toLowerCase().includes(inputValue.toLowerCase()) ||
|
|
160
176
|
v.query.toLowerCase().includes(inputValue.toLowerCase())), getOptionLabel: (v) => t(v.title), isOptionEqualToValue: (view, value) => view.view_id === value.view_id, value: views[defaultView] ?? null, onChange: (_, option) => setDefaultView(option?.view_id) })) : (_jsx(Skeleton, { variant: "rounded", width: "300px", height: "initial" })), belowSearch: _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Checkbox, { size: "small", disabled: user.favourite_views?.length < 1, checked: favouritesOnly, onChange: (_, checked) => setFavouritesOnly(checked) }), _jsx(Typography, { variant: "body1", sx: theme => ({ color: theme.palette.text.disabled }), children: t('route.views.manager.favourites') })] }), renderer: ({ item }, classRenderer) => (_jsx(Card, { variant: "outlined", sx: { p: 1, mb: 1, transitionProperty: 'border-color', '&:hover': { borderColor: 'primary.main' } }, className: classRenderer(), children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, sx: { color: 'inherit', textDecoration: 'none' }, component: Link, to: `/views/${item.item.view_id}`, children: [_jsx(ViewTitle, { ...item.item }), _jsx(FlexOne, {}), ((item.item.owner === user.username && item.item.type !== 'readonly') ||
|
|
161
177
|
(item.item.type === 'global' && user.is_admin)) && (_jsx(Tooltip, { title: t('button.edit'), children: _jsx(IconButton, { component: Link, to: `/views/${item.item.view_id}/edit?query=${item.item.query}`, children: _jsx(Edit, {}) }) })), item.item.owner === user.username && item.item.type !== 'readonly' && (_jsx(Tooltip, { title: t('button.delete'), children: _jsx(IconButton, { onClick: event => onDelete(event, item.item.view_id), children: _jsx(Clear, {}) }) })), item.item.type === 'global' && item.item.owner !== user.username && (_jsx(Tooltip, { title: item.item.owner, children: _jsx("div", { children: _jsx(HowlerAvatar, { sx: { width: 24, height: 24, marginRight: '8px !important', marginLeft: '8px !important' }, userId: item.item.owner }) }) })), _jsx(Tooltip, { title: t('button.pin'), children: _jsx(IconButton, { onClick: e => onFavourite(e, item.item.view_id), children: user.favourite_views?.includes(item.item.view_id) ? _jsx(Star, {}) : _jsx(StarBorder, {}) }) })] }) }, item.item.view_id)), response: response, searchPrompt: "route.views.manager.search", onCreate: () => navigate('/views/create'), createPrompt: "route.views.create", createIcon: _jsx(SavedSearch, { sx: { mr: 1 } }) }));
|
package/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import '@fontsource/roboto';
|
|
3
3
|
import App from '@cccsaurora/howler-ui/components/app/App';
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import duration from 'dayjs/plugin/duration';
|
|
6
|
+
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
7
|
+
import utc from 'dayjs/plugin/utc';
|
|
4
8
|
import '@cccsaurora/howler-ui/i18n';
|
|
5
9
|
import 'index.css';
|
|
6
10
|
// import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
|
|
7
11
|
import * as ReactDOM from 'react-dom/client';
|
|
12
|
+
dayjs.extend(utc);
|
|
13
|
+
dayjs.extend(duration);
|
|
14
|
+
dayjs.extend(relativeTime);
|
|
8
15
|
// This is where you can inject UI plugins to modify Howler's interface.
|
|
9
16
|
// howlerPluginStore.install(new ExamplePlugin());
|
|
10
17
|
ReactDOM.createRoot(document.getElementById('root')).render(_jsx(App, {}));
|
|
@@ -26,6 +26,23 @@
|
|
|
26
26
|
"fields.idx_view": "View index",
|
|
27
27
|
"fields.important": "Important",
|
|
28
28
|
"fields.important.text": "When using the search engine in the UI, your search query is performed on all these indexes at the same time unless you explicitly specify which index you want to target.",
|
|
29
|
+
"fields.textvskeywords": "Text vs Keywords",
|
|
30
|
+
"fields.textvskeywords.description": "The text field and the keyword field in Howler have differences which grant them their own use cases. These differences are shown as follows:",
|
|
31
|
+
"fields.textvskeywords.keywordfamily": "The keyword family is optimized for fields that represent individual data and includes the following field types:",
|
|
32
|
+
"fields.textvskeywords.wildcard": "wildcard",
|
|
33
|
+
"fields.textvskeywords.wildcard.description": "The wildcard type is optimized for fields with large values or high cardinality. This is the case for most telemetry fields.",
|
|
34
|
+
"fields.textvskeywords.keyword": "keyword",
|
|
35
|
+
"fields.textvskeywords.keyword.description": "Used for structured content with defined values, such as Howler specific fields. These fields do not support wildcard searches.",
|
|
36
|
+
"fields.textvskeywords.constantkeyword": "constant_keyword",
|
|
37
|
+
"fields.textvskeywords.constantkeyword.description": "For keyword fields that always contain the same value. This type should seldom be necessary.",
|
|
38
|
+
"fields.textvskeywords.keyword.more.info": "For more information regarding the Keyword field please checkout the official Elasticsearch documentation:",
|
|
39
|
+
"fields.textvskeywords.textfamily": "The Text family is optimized for searching unstructured human-readable content and includes the following field types:",
|
|
40
|
+
"fields.textvskeywords.text": "text",
|
|
41
|
+
"fields.textvskeywords.text.description": "Traditional field type for full-text content such as the body of an email or document.",
|
|
42
|
+
"fields.textvskeywords.matchonlytext": "match_only_text",
|
|
43
|
+
"fields.textvskeywords.matchonlytext.description": "A space-optimized variant of text that disables scoring and performs slower on queries that need positions. It is best suited for indexing log messages.",
|
|
44
|
+
"fields.textvskeywords.text.more.info": "For more information regarding the Text field please checkout the official Elasticsearch documentation:",
|
|
45
|
+
"fields.textvskeywords.text.keyword.compare": "To get a full list of the different use cases for both Keyword and Text fields, please checkout:",
|
|
29
46
|
"fields.legend": "Legend",
|
|
30
47
|
"fields.legend.default": "Queried when no fields are specified",
|
|
31
48
|
"fields.legend.ip_field": "Can be queried using CIDR notation",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"hit.details.actions.vote": "Vote",
|
|
144
144
|
"hit.details.actions.vote.novote": "No Vote",
|
|
145
145
|
"hit.details.asessments.ambiguous.description": "The nature of the hit was not successfully determined based on available data.",
|
|
146
|
-
"hit.details.asessments.attempt.description": "The hit
|
|
146
|
+
"hit.details.asessments.attempt.description": "The hit represents malicious activity attempting to compromise systems or data.",
|
|
147
147
|
"hit.details.asessments.compromise.description": "The hit was part of a successful compromise.",
|
|
148
148
|
"hit.details.asessments.development.description": "The hit is related to development activity and should be ignored.",
|
|
149
149
|
"hit.details.asessments.false-positive.description": "The hit is not related to the detection that created it.",
|
|
@@ -431,8 +431,13 @@
|
|
|
431
431
|
"route.advanced.query.yaml": "Sigma Rule",
|
|
432
432
|
"route.advanced.query.yaml.description": "Sigma is a generic and open signature format that allows you to describe relevant log events in a straightforward manner. The rule format is very flexible, easy to write and applicable to any type of log file.",
|
|
433
433
|
"route.advanced.result.title": "Howler Advanced Search",
|
|
434
|
-
"route.advanced.result.description": "
|
|
435
|
-
"route.advanced.type": "Rule Type",
|
|
434
|
+
"route.advanced.result.description": "Execute a query to show results here.",
|
|
435
|
+
"route.advanced.rule.type": "Rule Type",
|
|
436
|
+
"route.advanced.pivot.field": "Group By Field",
|
|
437
|
+
"route.advanced.query.type": "Query Type",
|
|
438
|
+
"route.advanced.query.type.default": "Default",
|
|
439
|
+
"route.advanced.query.type.facet": "Facet",
|
|
440
|
+
"route.advanced.query.type.groupby": "Group By",
|
|
436
441
|
"route.analytics": "Analytics",
|
|
437
442
|
"route.analytics.deleted": "Deleted Rule!",
|
|
438
443
|
"route.analytics.detections": "Detections:",
|
|
@@ -553,6 +558,7 @@
|
|
|
553
558
|
"route.home": "User Dashboard",
|
|
554
559
|
"route.login.button.oauth": "Sign in with",
|
|
555
560
|
"route.search": "Search",
|
|
561
|
+
"route.clear": "Clear query",
|
|
556
562
|
"route.history": "History mode: See all previous queries",
|
|
557
563
|
"route.templates": "Templates",
|
|
558
564
|
"route.templates.analytic": "Choose Analytic",
|
|
@@ -587,6 +593,8 @@
|
|
|
587
593
|
"route.overviews.manager.search": "Search Overviews",
|
|
588
594
|
"route.overviews.manager.delete": "Delete Overview",
|
|
589
595
|
"route.overviews.manager.delete.success": "Overview Removed.",
|
|
596
|
+
"route.overviews.theme.light": "Preview in Light Mode",
|
|
597
|
+
"route.overviews.theme.dark": "Preview in Dark Mode",
|
|
590
598
|
"route.dossiers": "Dossiers",
|
|
591
599
|
"route.dossiers.default": "Default",
|
|
592
600
|
"route.dossiers.search.prompt": "Search by title, query, or owner.",
|
|
@@ -639,6 +647,7 @@
|
|
|
639
647
|
"route.views.manager.search": "Search Views",
|
|
640
648
|
"route.views.manager.default": "Default View",
|
|
641
649
|
"route.views.name": "View Name",
|
|
650
|
+
"route.views.save": "Save Query as View",
|
|
642
651
|
"route.views.saved": "Pinned Views",
|
|
643
652
|
"route.views.search.prompt": "Search by name, query, or owner.",
|
|
644
653
|
"search.open": "Open Search",
|
|
@@ -26,6 +26,23 @@
|
|
|
26
26
|
"fields.idx_view": "Index des vues",
|
|
27
27
|
"fields.important": "Important",
|
|
28
28
|
"fields.important.text": "Lorsque vous utilisez le moteur de recherche dans l'interface utilisateur, votre requête de recherche est effectuée sur tous ces index en même temps, sauf si vous spécifiez explicitement l'index que vous souhaitez cibler.",
|
|
29
|
+
"fields.textvskeywords": "Texte vs mots-clés",
|
|
30
|
+
"fields.textvskeywords.description": "Le champ de texte et le champ de mot-clé dans Howler présentent des différences qui leur confèrent leurs propres cas d'utilisation. Ces différences sont présentées comme suit :",
|
|
31
|
+
"fields.textvskeywords.keywordfamily": "La famille de mots-clés est optimisée pour les champs qui représentent des données individuelles et comprend les types de champs suivants :",
|
|
32
|
+
"fields.textvskeywords.wildcard": "wildcard",
|
|
33
|
+
"fields.textvskeywords.wildcard.description": "Le type générique est optimisé pour les champs contenant des valeurs importantes ou présentant une cardinalité élevée. C'est le cas de la plupart des champs de télémétrie.",
|
|
34
|
+
"fields.textvskeywords.keyword": "keyword",
|
|
35
|
+
"fields.textvskeywords.keyword.description": "Utilisé pour le contenu structuré avec des valeurs définies, telles que les champs spécifiques à Howler. Ces champs ne prennent pas en charge les recherches avec caractères génériques.",
|
|
36
|
+
"fields.textvskeywords.constantkeyword": "constant_keyword",
|
|
37
|
+
"fields.textvskeywords.constantkeyword.description": "Pour les champs de mots-clés qui contiennent toujours la même valeur. Ce type devrait rarement être nécessaire.",
|
|
38
|
+
"fields.textvskeywords.keyword.more.info": "Pour plus d'informations sur le champ Keyword, veuillez consulter la documentation officielle d'Elasticsearch :",
|
|
39
|
+
"fields.textvskeywords.textfamily": "La famille Text est optimisée pour la recherche de contenu non structuré lisible par l'homme et comprend les types de champs suivants :",
|
|
40
|
+
"fields.textvskeywords.text": "text",
|
|
41
|
+
"fields.textvskeywords.text.description": "Type de champ traditionnel pour le contenu en texte intégral, tel que le corps d'un e-mail ou d'un document.",
|
|
42
|
+
"fields.textvskeywords.matchonlytext": "match_only_text",
|
|
43
|
+
"fields.textvskeywords.matchonlytext.description": "Une variante du texte optimisée pour l'espace qui désactive le scoring et est moins performante pour les requêtes nécessitant des positions. Elle est particulièrement adaptée à l'indexation des messages de journal.",
|
|
44
|
+
"fields.textvskeywords.text.more.info": "Pour plus d'informations concernant le champ Texte, veuillez consulter la documentation officielle d'Elasticsearch :",
|
|
45
|
+
"fields.textvskeywords.text.keyword.compare": "Pour obtenir la liste complète des différents cas d'utilisation des champs Mot-clé et Texte, veuillez consulter :",
|
|
29
46
|
"fields.legend": "Légende",
|
|
30
47
|
"fields.legend.default": "Interrogé lorsqu'aucun champ n'est spécifié",
|
|
31
48
|
"fields.legend.ip_field": "Peut être interrogé en utilisant la notation CIDR",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"hit.details.actions.vote": "Voter",
|
|
144
144
|
"hit.details.actions.vote.novote": "Pas de vote",
|
|
145
145
|
"hit.details.asessments.ambiguous.description": "La nature du hit n'a pas pu être déterminée avec succès sur la base des données disponibles.",
|
|
146
|
-
"hit.details.asessments.attempt.description": "Le hit
|
|
146
|
+
"hit.details.asessments.attempt.description": "Le hit représente une activité malveillante visant à compromettre des systèmes ou des données.",
|
|
147
147
|
"hit.details.asessments.compromise.description": "Le hit fait partie d'un compromis réussi.",
|
|
148
148
|
"hit.details.asessments.development.description": "Le hit est liée à une activité de développement et doit être ignorée.",
|
|
149
149
|
"hit.details.asessments.false-positive.description": "Le hit n'est pas lié à la détection qui l'a créé.",
|
|
@@ -431,8 +431,13 @@
|
|
|
431
431
|
"route.advanced.query.yaml": "Règle Sigma",
|
|
432
432
|
"route.advanced.query.yaml.description": "Sigma est un format de signature générique et ouvert qui vous permet de décrire des événements de journal pertinents de manière directe. Le format de règle est très flexible, facile à écrire et applicable à tout type de fichier journal.",
|
|
433
433
|
"route.advanced.result.title": "Recherche avancée de howler",
|
|
434
|
-
"route.advanced.result.description": "
|
|
435
|
-
"route.advanced.type": "Type de règle",
|
|
434
|
+
"route.advanced.result.description": "Exécutez une requête pour afficher les résultats ici.",
|
|
435
|
+
"route.advanced.rule.type": "Type de règle",
|
|
436
|
+
"route.advanced.pivot.field": "Champ à regrouper par",
|
|
437
|
+
"route.advanced.query.type": "Type de requête",
|
|
438
|
+
"route.advanced.query.type.default": "Défaut",
|
|
439
|
+
"route.advanced.query.type.facet": "Facette",
|
|
440
|
+
"route.advanced.query.type.groupby": "Regrouper",
|
|
436
441
|
"route.analytics": "Analyses",
|
|
437
442
|
"route.analytics.deleted": "Règle supprimée!",
|
|
438
443
|
"route.analytics.detections": "Détections:",
|
|
@@ -551,6 +556,7 @@
|
|
|
551
556
|
"route.home.title": "Howler",
|
|
552
557
|
"route.home": "Tableau de bord utilisateur",
|
|
553
558
|
"route.history": "Mode historique : Voir toutes les requêtes précédentes",
|
|
559
|
+
"route.clear": "Effacer la requête",
|
|
554
560
|
"route.help.hit.banner": "Documentation sur en-tête de hit",
|
|
555
561
|
"route.login.button.oauth": "Se connecter avec",
|
|
556
562
|
"route.templates": "Modèles",
|
|
@@ -585,7 +591,8 @@
|
|
|
585
591
|
"route.overviews.manager.search": "Rechercher les vues d'ensemble",
|
|
586
592
|
"route.overviews.manager.delete": "Supprimer la vue d'ensemble",
|
|
587
593
|
"route.overviews.manager.delete.success": "Vue d'ensemble Supprimée.",
|
|
588
|
-
"route.overviews.
|
|
594
|
+
"route.overviews.theme.light": "Prévoyez en mode clair",
|
|
595
|
+
"route.overviews.theme.dark": "Prévoyez en mode sombre",
|
|
589
596
|
"route.dossiers": "Dossiers",
|
|
590
597
|
"route.dossiers.default": "Défaut",
|
|
591
598
|
"route.dossiers.search.prompt": "Recherche par titre, requête ou propriétaire.",
|
|
@@ -635,6 +642,7 @@
|
|
|
635
642
|
"route.views.manager.readonly": "Intégré",
|
|
636
643
|
"route.views.manager.search": "Rechercher les vues",
|
|
637
644
|
"route.views.manager": "Gérer vue",
|
|
645
|
+
"route.views.save": "Enregistrer cette requête comme vue",
|
|
638
646
|
"route.views.saved": "Vues épinglées",
|
|
639
647
|
"route.views.show": "Voir les vues",
|
|
640
648
|
"route.views": "Vues",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Analytic } from './entities/generated/Analytic';
|
|
2
|
+
import type { Dossier } from './entities/generated/Dossier';
|
|
3
|
+
import type { Overview } from './entities/generated/Overview';
|
|
4
|
+
import type { Template } from './entities/generated/Template';
|
|
5
|
+
export type WithMetadata<T> = T & {
|
|
6
|
+
__analytic?: Analytic;
|
|
7
|
+
__overview?: Overview;
|
|
8
|
+
__template?: Template;
|
|
9
|
+
__dossiers?: Dossier[];
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -113,6 +113,7 @@ export interface APIConfiguration {
|
|
|
113
113
|
classification: string;
|
|
114
114
|
}[];
|
|
115
115
|
};
|
|
116
|
+
mapping: APIMappings;
|
|
116
117
|
features: {
|
|
117
118
|
borealis: boolean;
|
|
118
119
|
notebook: boolean;
|
|
@@ -257,9 +258,15 @@ export interface APIC12Ndef {
|
|
|
257
258
|
UNRESTRICTED: string;
|
|
258
259
|
RESTRICTED: string;
|
|
259
260
|
}
|
|
261
|
+
export interface APIMappings {
|
|
262
|
+
mapping: {
|
|
263
|
+
[index: string]: string;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
260
266
|
export interface ApiType {
|
|
261
267
|
indexes: APIIndexes;
|
|
262
268
|
lookups: APILookups;
|
|
263
269
|
configuration: APIConfiguration;
|
|
264
270
|
c12nDef: APIC12Ndef;
|
|
271
|
+
mapping: APIMappings;
|
|
265
272
|
}
|