mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Suricata Row Renders Upgrade (#31990)
* Fixed `event.severity` to be operational now that ECS has been updated * Added a Suricata SID database with references * Wired in the references * Created a tagging system for the UI * Added Suricata SID and made it draggable onto the filters of the timeline * Changed the hand rolled sushi 🍣 inline flex styles to EuiFlexItem, EuiGroupItem * Removed older CVE column renderer from the system * Wrote unit tests * https://github.com/elastic/ingest-dev/issues/175 * https://github.com/elastic/ingest-dev/issues/299
This commit is contained in:
parent
e9c1360dab
commit
90fd992c09
28 changed files with 7326 additions and 468 deletions
|
@ -58,6 +58,7 @@
|
|||
"x-pack/plugins/secops/public/components/flyout/index.tsx",
|
||||
"x-pack/plugins/secops/public/components/flyout/resize_handle.tsx",
|
||||
"x-pack/plugins/secops/public/components/notes/helpers.tsx",
|
||||
"x-pack/plugins/secops/public/components/timeline/body/renderers/suricata_rules_ref.ts",
|
||||
"x-pack/plugins/secops/public/components/timeline/properties/helpers.tsx",
|
||||
"x-pack/plugins/secops/public/components/timeline/properties/index.tsx",
|
||||
"x-pack/plugins/secops/public/components/timeline/search_or_filter/search_or_filter.tsx",
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"react-markdown": "^4.0.6",
|
||||
"lodash": "^4.17.10",
|
||||
"memoize-one": "^5.0.0",
|
||||
"apollo-link-error": "^1.1.7"
|
||||
"apollo-link-error": "^1.1.7",
|
||||
"suricata-sid-db": "^1.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,10 +79,6 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
"isInstance": [Function],
|
||||
"renderColumn": [Function],
|
||||
},
|
||||
Object {
|
||||
"isInstance": [Function],
|
||||
"renderColumn": [Function],
|
||||
},
|
||||
]
|
||||
}
|
||||
ecs={
|
||||
|
@ -127,7 +123,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="sc-jtRfpW erLKZH"
|
||||
className="sc-gxMtzJ gYJQDW"
|
||||
component="div"
|
||||
data-test-subj="data-driven-columns"
|
||||
direction="row"
|
||||
|
@ -137,7 +133,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
wrap={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive sc-jtRfpW erLKZH"
|
||||
className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive sc-gxMtzJ gYJQDW"
|
||||
data-test-subj="data-driven-columns"
|
||||
>
|
||||
<EuiFlexItem
|
||||
|
@ -155,7 +151,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ hUSEpG"
|
||||
className="sc-dfVpRl kAmxmB"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
@ -581,7 +577,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ gSTrkX"
|
||||
className="sc-dfVpRl gLUJqQ"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
@ -1007,7 +1003,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ hUSEpG"
|
||||
className="sc-dfVpRl kAmxmB"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
@ -1433,7 +1429,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ gSTrkX"
|
||||
className="sc-dfVpRl gLUJqQ"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
@ -1859,7 +1855,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ hUSEpG"
|
||||
className="sc-dfVpRl kAmxmB"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
@ -2285,7 +2281,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ gSTrkX"
|
||||
className="sc-dfVpRl gLUJqQ"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
@ -2711,7 +2707,7 @@ exports[`Columns it renders the expected columns 1`] = `
|
|||
minWidth="120px"
|
||||
>
|
||||
<div
|
||||
className="sc-kTUwUJ hUSEpG"
|
||||
className="sc-dfVpRl kAmxmB"
|
||||
data-test-subj="column"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
|
|
2532
x-pack/plugins/secops/public/components/timeline/body/renderers/__snapshots__/source_dest_ip.test.tsx.snap
generated
Normal file
2532
x-pack/plugins/secops/public/components/timeline/body/renderers/__snapshots__/source_dest_ip.test.tsx.snap
generated
Normal file
File diff suppressed because it is too large
Load diff
3369
x-pack/plugins/secops/public/components/timeline/body/renderers/__snapshots__/suricata_details.test.tsx.snap
generated
Normal file
3369
x-pack/plugins/secops/public/components/timeline/body/renderers/__snapshots__/suricata_details.test.tsx.snap
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,721 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SuricataSignature rendering it renders the default SuricataSignature 1`] = `
|
||||
<ThemeProvider
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"relative": Object {
|
||||
"days": Object {
|
||||
"units": "day",
|
||||
},
|
||||
"hours": Object {
|
||||
"units": "hour",
|
||||
},
|
||||
"minutes": Object {
|
||||
"units": "minute",
|
||||
},
|
||||
"months": Object {
|
||||
"units": "month",
|
||||
},
|
||||
"seconds": Object {
|
||||
"units": "second",
|
||||
},
|
||||
"years": Object {
|
||||
"units": "year",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": Symbol(react.fragment),
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
theme={[Function]}
|
||||
>
|
||||
<Provider
|
||||
store={
|
||||
Object {
|
||||
"dispatch": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
Symbol(observable): [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<DragDropContext
|
||||
onDragEnd={[Function]}
|
||||
>
|
||||
<pure(Component)
|
||||
id="doc-id-123"
|
||||
signature="ET SCAN ATTACK Hello"
|
||||
signatureId="id-123"
|
||||
>
|
||||
<Component
|
||||
id="doc-id-123"
|
||||
signature="ET SCAN ATTACK Hello"
|
||||
signatureId="id-123"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="none"
|
||||
justifyContent="center"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--justifyContentCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<pure(Component)
|
||||
id="doc-id-123"
|
||||
signatureId="id-123"
|
||||
>
|
||||
<Component
|
||||
id="doc-id-123"
|
||||
signatureId="id-123"
|
||||
>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="sc-jwKygS jynuQh"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero sc-jwKygS jynuQh"
|
||||
>
|
||||
<Connect(DraggableWrapperComponent)
|
||||
dataProvider={
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "suricata-doc-id-123-sig-id-123",
|
||||
"kqlQuery": "",
|
||||
"name": "id-123",
|
||||
"queryMatch": Object {
|
||||
"field": "suricata.eve.alert.signature_id",
|
||||
"value": "id-123",
|
||||
},
|
||||
}
|
||||
}
|
||||
render={[Function]}
|
||||
>
|
||||
<DraggableWrapperComponent
|
||||
dataProvider={
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "suricata-doc-id-123-sig-id-123",
|
||||
"kqlQuery": "",
|
||||
"name": "id-123",
|
||||
"queryMatch": Object {
|
||||
"field": "suricata.eve.alert.signature_id",
|
||||
"value": "id-123",
|
||||
},
|
||||
}
|
||||
}
|
||||
registerProvider={[Function]}
|
||||
render={[Function]}
|
||||
suricata-doc-id-123-sig-id-123={
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "suricata-doc-id-123-sig-id-123",
|
||||
"kqlQuery": "",
|
||||
"name": "id-123",
|
||||
"queryMatch": Object {
|
||||
"field": "suricata.eve.alert.signature_id",
|
||||
"value": "id-123",
|
||||
},
|
||||
}
|
||||
}
|
||||
unRegisterProvider={[Function]}
|
||||
>
|
||||
<div
|
||||
data-test-subj="draggableWrapperDiv"
|
||||
>
|
||||
<Connect(Droppable)
|
||||
direction="vertical"
|
||||
droppableId="droppableId.content.suricata-doc-id-123-sig-id-123"
|
||||
ignoreContainerClipping={false}
|
||||
isDropDisabled={true}
|
||||
type="DEFAULT"
|
||||
>
|
||||
<Droppable
|
||||
direction="vertical"
|
||||
dispatch={[Function]}
|
||||
draggingOverWith={null}
|
||||
droppableId="droppableId.content.suricata-doc-id-123-sig-id-123"
|
||||
ignoreContainerClipping={false}
|
||||
isDraggingOver={false}
|
||||
isDropDisabled={true}
|
||||
placeholder={null}
|
||||
type="DEFAULT"
|
||||
>
|
||||
<DroppableDimensionPublisher
|
||||
direction="vertical"
|
||||
droppableId="droppableId.content.suricata-doc-id-123-sig-id-123"
|
||||
getDroppableRef={[Function]}
|
||||
ignoreContainerClipping={false}
|
||||
isDropDisabled={true}
|
||||
type="DEFAULT"
|
||||
>
|
||||
<div
|
||||
data-react-beautiful-dnd-droppable="0"
|
||||
>
|
||||
<Connect(Draggable)
|
||||
disableInteractiveElementBlocking={false}
|
||||
draggableId="draggableId.content.suricata-doc-id-123-sig-id-123"
|
||||
index={0}
|
||||
isDragDisabled={false}
|
||||
key="suricata-doc-id-123-sig-id-123"
|
||||
>
|
||||
<Draggable
|
||||
dimension={null}
|
||||
disableInteractiveElementBlocking={false}
|
||||
draggableId="draggableId.content.suricata-doc-id-123-sig-id-123"
|
||||
draggingOver={null}
|
||||
drop={[Function]}
|
||||
dropAnimationFinished={[Function]}
|
||||
index={0}
|
||||
isDragDisabled={false}
|
||||
isDragging={false}
|
||||
isDropAnimating={false}
|
||||
lift={[Function]}
|
||||
move={[Function]}
|
||||
moveByWindowScroll={[Function]}
|
||||
moveDown={[Function]}
|
||||
moveLeft={[Function]}
|
||||
moveRight={[Function]}
|
||||
moveUp={[Function]}
|
||||
offset={
|
||||
Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
shouldAnimateDisplacement={true}
|
||||
shouldAnimateDragMovement={false}
|
||||
>
|
||||
<DraggableDimensionPublisher
|
||||
draggableId="draggableId.content.suricata-doc-id-123-sig-id-123"
|
||||
droppableId="droppableId.content.suricata-doc-id-123-sig-id-123"
|
||||
getDraggableRef={[Function]}
|
||||
index={0}
|
||||
key="draggableId.content.suricata-doc-id-123-sig-id-123"
|
||||
type="DEFAULT"
|
||||
>
|
||||
<Moveable
|
||||
destination={
|
||||
Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
onMoveEnd={[Function]}
|
||||
speed="INSTANT"
|
||||
>
|
||||
<Motion
|
||||
defaultStyle={
|
||||
Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
onRest={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<DoubleRenderBlocker
|
||||
change={
|
||||
Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<DragHandle
|
||||
callbacks={
|
||||
Object {
|
||||
"onCancel": [Function],
|
||||
"onDrop": [Function],
|
||||
"onLift": [Function],
|
||||
"onMove": [Function],
|
||||
"onMoveDown": [Function],
|
||||
"onMoveLeft": [Function],
|
||||
"onMoveRight": [Function],
|
||||
"onMoveUp": [Function],
|
||||
"onWindowScroll": [Function],
|
||||
}
|
||||
}
|
||||
canDragInteractiveElements={false}
|
||||
draggableId="draggableId.content.suricata-doc-id-123-sig-id-123"
|
||||
getDraggableRef={[Function]}
|
||||
isDragging={false}
|
||||
isDropAnimating={false}
|
||||
isEnabled={true}
|
||||
>
|
||||
<styled.div
|
||||
aria-roledescription="Draggable item. Press space bar to lift"
|
||||
data-react-beautiful-dnd-drag-handle="0"
|
||||
data-react-beautiful-dnd-draggable="0"
|
||||
data-test-subj="providerContainer"
|
||||
draggable={false}
|
||||
innerRef={[Function]}
|
||||
onBlur={[Function]}
|
||||
onDragStart={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"transform": null,
|
||||
"transition": null,
|
||||
"zIndex": 9000,
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
<div
|
||||
aria-roledescription="Draggable item. Press space bar to lift"
|
||||
className="sc-hzDkRC koNYSi"
|
||||
data-react-beautiful-dnd-drag-handle="0"
|
||||
data-react-beautiful-dnd-draggable="0"
|
||||
data-test-subj="providerContainer"
|
||||
draggable={false}
|
||||
onBlur={[Function]}
|
||||
onDragStart={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"transform": null,
|
||||
"transition": null,
|
||||
"zIndex": 9000,
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
<Styled(EuiBadge)
|
||||
color="hollow"
|
||||
iconType="number"
|
||||
>
|
||||
<EuiBadge
|
||||
className="sc-btzYZH jBzfTi"
|
||||
color="hollow"
|
||||
iconSide="left"
|
||||
iconType="number"
|
||||
>
|
||||
<span
|
||||
className="euiBadge euiBadge--hollow sc-btzYZH jBzfTi"
|
||||
style={null}
|
||||
>
|
||||
<span
|
||||
className="euiBadge__content"
|
||||
>
|
||||
<EuiIcon
|
||||
className="euiBadge__icon"
|
||||
size="s"
|
||||
type="number"
|
||||
>
|
||||
<number
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.808 10.197H6.796L5.859 13H4.485l.937-2.803H3.966l.219-1.25h1.647l.608-1.805H4.991l.226-1.251h1.64l.95-2.844h1.368l-.95 2.844h1.018l.95-2.844h1.374l-.95 2.844h1.51l-.218 1.25h-1.702l-.608 1.805h1.497l-.219 1.251H9.182L8.252 13H6.878l.93-2.803zm-.602-1.25h1.012l.615-1.805H7.814l-.608 1.804z"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</number>
|
||||
</EuiIcon>
|
||||
<span
|
||||
className="euiBadge__text"
|
||||
>
|
||||
id-123
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</EuiBadge>
|
||||
</Styled(EuiBadge)>
|
||||
</div>
|
||||
</styled.div>
|
||||
</DragHandle>
|
||||
</DoubleRenderBlocker>
|
||||
</Motion>
|
||||
</Moveable>
|
||||
</DraggableDimensionPublisher>
|
||||
</Draggable>
|
||||
</Connect(Draggable)>
|
||||
</div>
|
||||
</DroppableDimensionPublisher>
|
||||
</Droppable>
|
||||
</Connect(Droppable)>
|
||||
</div>
|
||||
</DraggableWrapperComponent>
|
||||
</Connect(DraggableWrapperComponent)>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexItem)>
|
||||
</Component>
|
||||
</pure(Component)>
|
||||
<pure(Component)
|
||||
tokens={
|
||||
Array [
|
||||
"ET",
|
||||
"SCAN",
|
||||
"ATTACK",
|
||||
]
|
||||
}
|
||||
>
|
||||
<Component
|
||||
tokens={
|
||||
Array [
|
||||
"ET",
|
||||
"SCAN",
|
||||
"ATTACK",
|
||||
]
|
||||
}
|
||||
>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={false}
|
||||
key="ET"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="sc-lhVmIH krgDqk"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero sc-lhVmIH krgDqk"
|
||||
>
|
||||
<EuiBadge
|
||||
color="hollow"
|
||||
iconSide="left"
|
||||
iconType="tag"
|
||||
>
|
||||
<span
|
||||
className="euiBadge euiBadge--hollow"
|
||||
style={null}
|
||||
>
|
||||
<span
|
||||
className="euiBadge__content"
|
||||
>
|
||||
<EuiIcon
|
||||
className="euiBadge__icon"
|
||||
size="s"
|
||||
type="tag"
|
||||
>
|
||||
<tag
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.254 14.97L.996 9.712c-.315-.316-.397-.463-.45-.64a.909.909 0 0 1 0-.534c.053-.177.135-.324.45-.64L7.43 1.466c.182-.183.252-.24.338-.293a.87.87 0 0 1 .273-.113c.099-.023.188-.032.446-.032h5.173c.445 0 .607.046.77.133.162.087.29.214.377.377.088.162.134.324.136.769l.015 5.15c0 .259-.009.348-.032.448a.87.87 0 0 1-.112.273c-.054.087-.111.157-.294.34L8.067 14.97c-.315.315-.462.396-.639.45a.909.909 0 0 1-.535 0c-.176-.054-.324-.135-.639-.45zm1.106-.707l6.453-6.453c.092-.092.126-.128.141-.147.003-.025.004-.074.004-.204l-.015-5.15c0-.181-.003-.245-.009-.275a2.247 2.247 0 0 0-.274-.007H8.487c-.13 0-.179.001-.203.004-.02.015-.055.05-.147.141L1.703 8.606a2.248 2.248 0 0 0-.189.2c.017.024.061.07.19.198l5.257 5.259c.128.128.175.171.2.188.024-.017.071-.06.2-.188zm4.972-10.607a2 2 0 1 1-2.828 2.828 2 2 0 0 1 2.828-2.828zm-.707.707a1 1 0 1 0-1.414 1.414 1 1 0 0 0 1.414-1.414zM6.807 11.28L4.686 9.159a.5.5 0 1 1 .707-.707l2.121 2.12a.5.5 0 1 1-.707.708zm1.414-1.414l-2.12-2.122a.5.5 0 1 1 .706-.707L8.928 9.16a.5.5 0 1 1-.707.707z"
|
||||
/>
|
||||
</svg>
|
||||
</tag>
|
||||
</EuiIcon>
|
||||
<span
|
||||
className="euiBadge__text"
|
||||
>
|
||||
ET
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</EuiBadge>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexItem)>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={false}
|
||||
key="SCAN"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="sc-lhVmIH krgDqk"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero sc-lhVmIH krgDqk"
|
||||
>
|
||||
<EuiBadge
|
||||
color="hollow"
|
||||
iconSide="left"
|
||||
iconType="tag"
|
||||
>
|
||||
<span
|
||||
className="euiBadge euiBadge--hollow"
|
||||
style={null}
|
||||
>
|
||||
<span
|
||||
className="euiBadge__content"
|
||||
>
|
||||
<EuiIcon
|
||||
className="euiBadge__icon"
|
||||
size="s"
|
||||
type="tag"
|
||||
>
|
||||
<tag
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.254 14.97L.996 9.712c-.315-.316-.397-.463-.45-.64a.909.909 0 0 1 0-.534c.053-.177.135-.324.45-.64L7.43 1.466c.182-.183.252-.24.338-.293a.87.87 0 0 1 .273-.113c.099-.023.188-.032.446-.032h5.173c.445 0 .607.046.77.133.162.087.29.214.377.377.088.162.134.324.136.769l.015 5.15c0 .259-.009.348-.032.448a.87.87 0 0 1-.112.273c-.054.087-.111.157-.294.34L8.067 14.97c-.315.315-.462.396-.639.45a.909.909 0 0 1-.535 0c-.176-.054-.324-.135-.639-.45zm1.106-.707l6.453-6.453c.092-.092.126-.128.141-.147.003-.025.004-.074.004-.204l-.015-5.15c0-.181-.003-.245-.009-.275a2.247 2.247 0 0 0-.274-.007H8.487c-.13 0-.179.001-.203.004-.02.015-.055.05-.147.141L1.703 8.606a2.248 2.248 0 0 0-.189.2c.017.024.061.07.19.198l5.257 5.259c.128.128.175.171.2.188.024-.017.071-.06.2-.188zm4.972-10.607a2 2 0 1 1-2.828 2.828 2 2 0 0 1 2.828-2.828zm-.707.707a1 1 0 1 0-1.414 1.414 1 1 0 0 0 1.414-1.414zM6.807 11.28L4.686 9.159a.5.5 0 1 1 .707-.707l2.121 2.12a.5.5 0 1 1-.707.708zm1.414-1.414l-2.12-2.122a.5.5 0 1 1 .706-.707L8.928 9.16a.5.5 0 1 1-.707.707z"
|
||||
/>
|
||||
</svg>
|
||||
</tag>
|
||||
</EuiIcon>
|
||||
<span
|
||||
className="euiBadge__text"
|
||||
>
|
||||
SCAN
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</EuiBadge>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexItem)>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={false}
|
||||
key="ATTACK"
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="sc-lhVmIH krgDqk"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero sc-lhVmIH krgDqk"
|
||||
>
|
||||
<EuiBadge
|
||||
color="hollow"
|
||||
iconSide="left"
|
||||
iconType="tag"
|
||||
>
|
||||
<span
|
||||
className="euiBadge euiBadge--hollow"
|
||||
style={null}
|
||||
>
|
||||
<span
|
||||
className="euiBadge__content"
|
||||
>
|
||||
<EuiIcon
|
||||
className="euiBadge__icon"
|
||||
size="s"
|
||||
type="tag"
|
||||
>
|
||||
<tag
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
className="euiIcon euiIcon--small euiBadge__icon"
|
||||
focusable="false"
|
||||
height="16"
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.254 14.97L.996 9.712c-.315-.316-.397-.463-.45-.64a.909.909 0 0 1 0-.534c.053-.177.135-.324.45-.64L7.43 1.466c.182-.183.252-.24.338-.293a.87.87 0 0 1 .273-.113c.099-.023.188-.032.446-.032h5.173c.445 0 .607.046.77.133.162.087.29.214.377.377.088.162.134.324.136.769l.015 5.15c0 .259-.009.348-.032.448a.87.87 0 0 1-.112.273c-.054.087-.111.157-.294.34L8.067 14.97c-.315.315-.462.396-.639.45a.909.909 0 0 1-.535 0c-.176-.054-.324-.135-.639-.45zm1.106-.707l6.453-6.453c.092-.092.126-.128.141-.147.003-.025.004-.074.004-.204l-.015-5.15c0-.181-.003-.245-.009-.275a2.247 2.247 0 0 0-.274-.007H8.487c-.13 0-.179.001-.203.004-.02.015-.055.05-.147.141L1.703 8.606a2.248 2.248 0 0 0-.189.2c.017.024.061.07.19.198l5.257 5.259c.128.128.175.171.2.188.024-.017.071-.06.2-.188zm4.972-10.607a2 2 0 1 1-2.828 2.828 2 2 0 0 1 2.828-2.828zm-.707.707a1 1 0 1 0-1.414 1.414 1 1 0 0 0 1.414-1.414zM6.807 11.28L4.686 9.159a.5.5 0 1 1 .707-.707l2.121 2.12a.5.5 0 1 1-.707.708zm1.414-1.414l-2.12-2.122a.5.5 0 1 1 .706-.707L8.928 9.16a.5.5 0 1 1-.707.707z"
|
||||
/>
|
||||
</svg>
|
||||
</tag>
|
||||
</EuiIcon>
|
||||
<span
|
||||
className="euiBadge__text"
|
||||
>
|
||||
ATTACK
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</EuiBadge>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexItem)>
|
||||
</Component>
|
||||
</pure(Component)>
|
||||
<pure(Component)
|
||||
link="ET SCAN ATTACK Hello"
|
||||
value="Hello"
|
||||
>
|
||||
<Component
|
||||
link="ET SCAN ATTACK Hello"
|
||||
value="Hello"
|
||||
>
|
||||
<Styled(EuiFlexItem)
|
||||
grow={false}
|
||||
>
|
||||
<EuiFlexItem
|
||||
className="sc-bYSBpT kqevpY"
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero sc-bYSBpT kqevpY"
|
||||
>
|
||||
<EuiLink
|
||||
color="primary"
|
||||
href="https://www.google.com/search?q=ET%20SCAN%20ATTACK%20Hello"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary"
|
||||
href="https://www.google.com/search?q=ET%20SCAN%20ATTACK%20Hello"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Hello
|
||||
</a>
|
||||
</EuiLink>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexItem)>
|
||||
</Component>
|
||||
</pure(Component)>
|
||||
</div>
|
||||
</EuiFlexGroup>
|
||||
</Component>
|
||||
</pure(Component)>
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
`;
|
|
@ -24,11 +24,9 @@ const allFieldsInSchemaByName = getAllFieldsInSchemaByMappedName(virtualEcsSchem
|
|||
|
||||
describe('get_column_renderer', () => {
|
||||
let nonSuricata: Ecs;
|
||||
let suricata: Ecs;
|
||||
|
||||
beforeEach(() => {
|
||||
nonSuricata = cloneDeep(mockEcsData[0]);
|
||||
suricata = cloneDeep(mockEcsData[2]);
|
||||
});
|
||||
|
||||
test('should render event id when dealing with data that is not suricata', () => {
|
||||
|
@ -52,18 +50,6 @@ describe('get_column_renderer', () => {
|
|||
expect(wrapper.text()).toEqual('1');
|
||||
});
|
||||
|
||||
test('should render CVE text as the event when dealing with a suricata event', () => {
|
||||
const columnName = 'event.id';
|
||||
const columnRenderer = getColumnRenderer(columnName, columnRenderers, suricata);
|
||||
const column = columnRenderer.renderColumn(
|
||||
columnName,
|
||||
suricata,
|
||||
allFieldsInSchemaByName[columnName]
|
||||
);
|
||||
const wrapper = mount(<span>{column}</span>);
|
||||
expect(wrapper.text()).toEqual('CVE-2016-10174');
|
||||
});
|
||||
|
||||
test('should render empty value when dealing with an empty value of user', () => {
|
||||
delete nonSuricata.user;
|
||||
const columnName = 'user.name';
|
||||
|
|
|
@ -19,9 +19,5 @@ export const getColumnRenderer = (
|
|||
const renderer = columnRenderers.find(columnRenderer =>
|
||||
columnRenderer.isInstance(columnName, ecs)
|
||||
);
|
||||
if (renderer == null) {
|
||||
return unhandledColumnRenderer();
|
||||
} else {
|
||||
return renderer;
|
||||
}
|
||||
return renderer != null ? renderer : unhandledColumnRenderer();
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@ describe('get_column_renderer', () => {
|
|||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toContain(
|
||||
'some child ET EXPLOIT NETGEAR WNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)'
|
||||
'some child 4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createLinkWithSignature, getSuricataCVEFromSignature } from '.';
|
||||
|
||||
describe('index', () => {
|
||||
describe('#getSuricataCVEFromSignature', () => {
|
||||
test('should parse a basic CVE string by its self', () => {
|
||||
const cve = getSuricataCVEFromSignature('CVE-123-123');
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string in the middle of a signature', () => {
|
||||
const cve = getSuricataCVEFromSignature(
|
||||
'I am a normal signature and this is CVE-123-123 for you'
|
||||
);
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string at the beginning of a signature', () => {
|
||||
const cve = getSuricataCVEFromSignature('CVE-123-123 I am a normal signature for you.');
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string at the end of a signature', () => {
|
||||
const cve = getSuricataCVEFromSignature('I am a normal signature for you. CVE-123-123');
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string with no spaces mixed in a signature at the end', () => {
|
||||
const cve = getSuricataCVEFromSignature('I am a normal signature for youCVE-123-123');
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string with no spaces mixed in a signature at the beginning', () => {
|
||||
const cve = getSuricataCVEFromSignature('CVE-123-123I am a normal signature for you');
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string with no spaces mixed in the middle of a word', () => {
|
||||
const cve = getSuricataCVEFromSignature('I am a normalCVE-123-123signature for you');
|
||||
expect(cve).toEqual('CVE-123-123');
|
||||
});
|
||||
|
||||
test('should return a null if a CVE is not present', () => {
|
||||
const cve = getSuricataCVEFromSignature('I am a normal signature for you');
|
||||
expect(cve).toBeNull();
|
||||
});
|
||||
|
||||
test('should return a null if the signature is empty', () => {
|
||||
const cve = getSuricataCVEFromSignature();
|
||||
expect(cve).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createLinkWithSignature', () => {
|
||||
test('should parse a basic CVE string by its self as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature('CVE-123-123');
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string in the middle of a signature as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature(
|
||||
'I am a normal signature and this is CVE-123-123 for you'
|
||||
);
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string at the beginning of a signature as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature('CVE-123-123 I am a normal signature for you.');
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string at the end of a signature as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature('I am a normal signature for you. CVE-123-123');
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string with no spaces mixed in a signature at the end as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature('I am a normal signature for youCVE-123-123');
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string with no spaces mixed in a signature at the beginning as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature('CVE-123-123I am a normal signature for you');
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a basic CVE string with no spaces mixed in the middle of a word as a hyper link to cve.mitre.org', () => {
|
||||
const cve = createLinkWithSignature('I am a normalCVE-123-123signature for you');
|
||||
expect(cve).toEqual('https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-123-123');
|
||||
});
|
||||
|
||||
test('should parse a link to google.com with no CVE in the signature', () => {
|
||||
const cve = createLinkWithSignature('I am a normal signature for you');
|
||||
expect(cve).toEqual(
|
||||
'https://www.google.com/search?q=I%20am%20a%20normal%20signature%20for%20you'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,7 +9,6 @@ import { emptyColumnRenderer } from './empty_column_renderer';
|
|||
import { plainColumnRenderer } from './plain_column_renderer';
|
||||
import { plainRowRenderer } from './plain_row_renderer';
|
||||
import { RowRenderer } from './row_renderer';
|
||||
import { suricataColumnRenderer } from './suricata_column_renderer';
|
||||
import { suricataRowRenderer } from './suricata_row_renderer';
|
||||
import { unknownColumnRenderer } from './unknown_column_renderer';
|
||||
|
||||
|
@ -20,34 +19,13 @@ export * from './get_row_renderer';
|
|||
export * from './get_column_renderer';
|
||||
export * from './plain_row_renderer';
|
||||
export * from './plain_column_renderer';
|
||||
export * from './suricata_column_renderer';
|
||||
export * from './suricata_row_renderer';
|
||||
export * from './unknown_column_renderer';
|
||||
|
||||
export const rowRenderers: RowRenderer[] = [suricataRowRenderer, plainRowRenderer];
|
||||
|
||||
export const columnRenderers: ColumnRenderer[] = [
|
||||
suricataColumnRenderer,
|
||||
plainColumnRenderer,
|
||||
emptyColumnRenderer,
|
||||
unknownColumnRenderer,
|
||||
];
|
||||
|
||||
export const getSuricataCVEFromSignature = (signature?: string): string | null => {
|
||||
const regex = /CVE-[0-9]*-[0-9]*/;
|
||||
const found = (signature && signature.match(regex)) || false;
|
||||
if (found) {
|
||||
return encodeURIComponent(found[0]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const createLinkWithSignature = (signature: string): string => {
|
||||
const cve = getSuricataCVEFromSignature(signature);
|
||||
if (cve != null) {
|
||||
return `https://cve.mitre.org/cgi-bin/cvename.cgi?name=${cve}`;
|
||||
} else {
|
||||
return `https://www.google.com/search?q=${encodeURIComponent(signature)}`;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import { noop } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { DragDropContext } from 'react-beautiful-dnd';
|
||||
import { Provider } from 'react-redux';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
|
||||
import { mockEcsData } from '../../../../mock';
|
||||
import { mockGlobalState } from '../../../../mock';
|
||||
import { createStore, State } from '../../../../store';
|
||||
import { SourceDest } from './source_dest_ip';
|
||||
|
||||
describe('SuricataDestIp', () => {
|
||||
const state: State = mockGlobalState;
|
||||
const theme = () => ({ eui: euiDarkVars, darkMode: true });
|
||||
let store = createStore(state);
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore(state);
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
test('it renders the default SuricataDestIp', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<SourceDest data={mockEcsData[2]} />
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Ecs } from '../../../../graphql/types';
|
||||
import {
|
||||
fieldExists,
|
||||
getAllFieldsInSchemaByMappedName,
|
||||
getMappedEcsValue,
|
||||
mappedEcsSchemaFieldNames,
|
||||
virtualEcsSchema,
|
||||
} from '../../../../lib/ecs';
|
||||
import { escapeQueryValue } from '../../../../lib/keury';
|
||||
import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper';
|
||||
import { escapeDataProviderId } from '../../../drag_and_drop/helpers';
|
||||
import { Provider } from '../../data_providers/provider';
|
||||
import { FormattedField } from './formatted_field';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const Label = styled.div`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const allFieldsInSchemaByName = getAllFieldsInSchemaByMappedName(virtualEcsSchema);
|
||||
|
||||
export const DraggableValue = pure<{ data: Ecs; fieldName: string }>(({ data, fieldName }) => {
|
||||
const itemDataProvider = {
|
||||
enabled: true,
|
||||
id: escapeDataProviderId(`row-render-value-for-${data._id}-${fieldName}`),
|
||||
name: `${fieldName}: ${getMappedEcsValue({
|
||||
data,
|
||||
fieldName,
|
||||
})}`,
|
||||
queryMatch: {
|
||||
field: getOr(fieldName, fieldName, mappedEcsSchemaFieldNames),
|
||||
value: escapeQueryValue(
|
||||
getMappedEcsValue({
|
||||
data,
|
||||
fieldName,
|
||||
})
|
||||
),
|
||||
},
|
||||
excluded: false,
|
||||
kqlQuery: '',
|
||||
and: [],
|
||||
};
|
||||
|
||||
const field = allFieldsInSchemaByName[fieldName];
|
||||
const fieldType = field != null ? field.type : '';
|
||||
|
||||
return { data, fieldName } ? (
|
||||
<DraggableWrapper
|
||||
key={`row-render-value-for-${data._id}-${fieldName}`}
|
||||
dataProvider={itemDataProvider}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
<DragEffects>
|
||||
<Provider dataProvider={dataProvider} />
|
||||
</DragEffects>
|
||||
) : (
|
||||
<FormattedField data={data} fieldName={fieldName} fieldType={fieldType} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export const SourceIp = pure(({ data }: { data: Ecs }) =>
|
||||
fieldExists({ data, fieldName: 'source.ip' }) ? (
|
||||
<>
|
||||
<EuiFlexItem>
|
||||
<Label>{i18n.SOURCE}</Label>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<DraggableValue
|
||||
data={data}
|
||||
data-test-subj="source-ip-and-port"
|
||||
fieldName={'source.ip'}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>:</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<DraggableValue data={data} fieldName={'source.port'} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
) : null
|
||||
);
|
||||
|
||||
export const DestinationIp = pure(({ data }: { data: Ecs }) =>
|
||||
fieldExists({ data, fieldName: 'destination.ip' }) ? (
|
||||
<>
|
||||
<EuiFlexItem>
|
||||
<Label>{i18n.DESTINATION}</Label>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<DraggableValue
|
||||
data={data}
|
||||
data-test-subj="destination-ip-and-port"
|
||||
fieldName={'destination.ip'}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>:</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<DraggableValue data={data} fieldName={'destination.port'} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
) : null
|
||||
);
|
||||
|
||||
export const SourceDest = pure(({ data }: { data: Ecs }) => (
|
||||
<EuiFlexGroup justifyContent="spaceEvenly">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup direction="column" alignItems="center" gutterSize="none">
|
||||
<SourceIp data={data} />
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup direction="column" alignItems="center" gutterSize="none">
|
||||
<DestinationIp data={data} />
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
));
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import { cloneDeep, set } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
|
||||
import { suricataColumnRenderer } from '.';
|
||||
import { Ecs } from '../../../../graphql/types';
|
||||
import { getAllFieldsInSchemaByMappedName, virtualEcsSchema } from '../../../../lib/ecs';
|
||||
import { mockEcsData } from '../../../../mock';
|
||||
import { getEmptyValue } from '../../../empty_value';
|
||||
|
||||
const allFieldsInSchemaByName = getAllFieldsInSchemaByMappedName(virtualEcsSchema);
|
||||
|
||||
describe('suricata_column_renderer', () => {
|
||||
let mockDatum: Ecs;
|
||||
beforeEach(() => {
|
||||
mockDatum = cloneDeep(mockEcsData[2]);
|
||||
});
|
||||
|
||||
test('should return isInstance of false if event is empty', () => {
|
||||
delete mockDatum.event;
|
||||
expect(suricataColumnRenderer.isInstance('event.id', mockDatum)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return isInstance of false if event module is empty', () => {
|
||||
delete mockDatum.event!.module;
|
||||
expect(suricataColumnRenderer.isInstance('event.id', mockDatum)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return isInstance of false if event module does not equal suricata', () => {
|
||||
mockDatum.event!.module = 'some other value';
|
||||
expect(suricataColumnRenderer.isInstance('event.id', mockDatum)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return isInstance true if event is NOT empty and module equals suricata', () => {
|
||||
expect(suricataColumnRenderer.isInstance('event.id', mockDatum)).toBe(true);
|
||||
});
|
||||
|
||||
test('should return isInstance true if event is NOT empty and module equals SurICaTA', () => {
|
||||
mockDatum.event!.module = 'SurICaTA';
|
||||
expect(suricataColumnRenderer.isInstance('event.id', mockDatum)).toBe(true);
|
||||
});
|
||||
|
||||
test('should return a value of the CVE if event has a valid suricata value and it is a CVE', () => {
|
||||
const column = suricataColumnRenderer.renderColumn(
|
||||
'event.id',
|
||||
mockDatum,
|
||||
allFieldsInSchemaByName['event.id']
|
||||
);
|
||||
const wrapper = mount(<span>{column}</span>);
|
||||
expect(wrapper.text()).toEqual('CVE-2016-10174');
|
||||
});
|
||||
|
||||
test('should return a value of the event id if no CVE is in the event', () => {
|
||||
const dataumWithValue = set(
|
||||
'suricata.eve.alert.signature',
|
||||
'Something without a CVE entry inside of it',
|
||||
mockDatum
|
||||
);
|
||||
const column = suricataColumnRenderer.renderColumn(
|
||||
'event.id',
|
||||
dataumWithValue,
|
||||
allFieldsInSchemaByName['event.id']
|
||||
);
|
||||
const wrapper = mount(<span>{column}</span>);
|
||||
expect(wrapper.text()).toEqual('4');
|
||||
});
|
||||
|
||||
test('should return a value of the empty if no CVE is in the event and the event does not have an id', () => {
|
||||
delete mockDatum.suricata!.eve!.alert!.signature;
|
||||
delete mockDatum.event!.id;
|
||||
const column = suricataColumnRenderer.renderColumn(
|
||||
'event.id',
|
||||
mockDatum,
|
||||
allFieldsInSchemaByName['event.id']
|
||||
);
|
||||
const wrapper = mount(<span>{column}</span>);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
});
|
||||
|
||||
test('should return a value if an unknown column name is sent in', () => {
|
||||
const column = suricataColumnRenderer.renderColumn(
|
||||
'made up column name',
|
||||
mockDatum,
|
||||
allFieldsInSchemaByName['made up column name']
|
||||
);
|
||||
const wrapper = mount(<span>{column}</span>);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
});
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
|
||||
import { ColumnRenderer, getSuricataCVEFromSignature } from '.';
|
||||
import { Ecs } from '../../../../graphql/types';
|
||||
import { getEmptyTagValue, getOrEmptyTag } from '../../../empty_value';
|
||||
|
||||
const suricataColumnsOverridden = ['event.id'];
|
||||
|
||||
export const suricataColumnRenderer: ColumnRenderer = {
|
||||
isInstance: (columnName: string, ecs: Ecs) => {
|
||||
if (
|
||||
suricataColumnsOverridden.includes(columnName) &&
|
||||
ecs &&
|
||||
ecs.event &&
|
||||
ecs.event.module &&
|
||||
ecs.event.module.toLowerCase() === 'suricata'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
renderColumn: (columnName: string, data: Ecs) => {
|
||||
switch (columnName) {
|
||||
case 'event.id':
|
||||
const signature: string = get('suricata.eve.alert.signature', data);
|
||||
const cve = getSuricataCVEFromSignature(signature);
|
||||
if (cve != null) {
|
||||
return <>{cve}</>;
|
||||
} else {
|
||||
return getOrEmptyTag('event.id', data);
|
||||
}
|
||||
default:
|
||||
// unknown column name
|
||||
return getEmptyTagValue();
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import { noop } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { DragDropContext } from 'react-beautiful-dnd';
|
||||
import { Provider } from 'react-redux';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
|
||||
import { mockEcsData } from '../../../../mock';
|
||||
import { mockGlobalState } from '../../../../mock';
|
||||
import { createStore, State } from '../../../../store';
|
||||
import { SuricataDetails } from './suricata_details';
|
||||
|
||||
describe('SuricataDetails', () => {
|
||||
const state: State = mockGlobalState;
|
||||
let store = createStore(state);
|
||||
const theme = () => ({ eui: euiDarkVars, darkMode: true });
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore(state);
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
test('it renders the default SuricataDetails', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<SuricataDetails data={mockEcsData[2]} />
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('it returns text if the data does contain suricata data', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<SuricataDetails data={mockEcsData[2]} />
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(
|
||||
'4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343'
|
||||
);
|
||||
});
|
||||
|
||||
test('it returns null for text if the data contains no suricata data', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<SuricataDetails data={mockEcsData[0]} />
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(null);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { Ecs } from '../../../../graphql/types';
|
||||
import { SourceDest } from './source_dest_ip';
|
||||
import { SuricataRefs } from './suricata_refs';
|
||||
import { SuricataSignature } from './suricata_signature';
|
||||
|
||||
const Details = styled.div`
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
export const SuricataDetails = pure(({ data }: { data: Ecs }) => {
|
||||
const signature: string | null = get('suricata.eve.alert.signature', data);
|
||||
const signatureId: string | null = get('suricata.eve.alert.signature_id', data);
|
||||
if (signatureId != null && signature != null) {
|
||||
return (
|
||||
<Details>
|
||||
<SuricataSignature id={data._id} signature={signature} signatureId={signatureId} />
|
||||
<SuricataRefs signatureId={signatureId} />
|
||||
<EuiSpacer size="s" />
|
||||
<SourceDest data={data} />
|
||||
</Details>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getBeginningTokens, getLinksFromSignature } from './suricata_links';
|
||||
|
||||
describe('SuricataLinks', () => {
|
||||
describe('#getLinksFromSignature', () => {
|
||||
test('it should return an empty array when the link does not exist', () => {
|
||||
const links = getLinksFromSignature('id-madeup-does-not-exist');
|
||||
expect(links).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should return a valid unique set of rules (if analyst has added duplicate refs)', () => {
|
||||
const links = getLinksFromSignature('2019415');
|
||||
expect(links).toEqual([
|
||||
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=2014-3566',
|
||||
'http://blog.fox-it.com/2014/10/15/poodle/',
|
||||
'http://www.openssl.org/~bodo/ssl-poodle.pdf',
|
||||
'http://askubuntu.com/questions/537196/how-do-i-patch-workaround-sslv3-poodle-vulnerability-cve-2014-3566',
|
||||
'http://www.imperialviolet.org/2014/10/14/poodle.html',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getBeginningTokens', () => {
|
||||
test('it should return valid tags of ET and PRO', () => {
|
||||
const tokens = getBeginningTokens('ET PRO Some Signature');
|
||||
expect(tokens).toEqual(['ET', 'PRO']);
|
||||
});
|
||||
|
||||
test('it should return valid tags of ET SCAN SHAZAM', () => {
|
||||
const tokens = getBeginningTokens('ET SCAN SHAZAM Some Signature');
|
||||
expect(tokens).toEqual(['ET', 'SCAN', 'SHAZAM']);
|
||||
});
|
||||
|
||||
test('it should return valid tag of GPL', () => {
|
||||
const tokens = getBeginningTokens('GPL YeT ANoTHER Signature');
|
||||
expect(tokens).toEqual(['GPL']);
|
||||
});
|
||||
|
||||
test('it should return valid tags with special characters', () => {
|
||||
const tokens = getBeginningTokens('ET IPv4 IPv6 SCAN Rebecca');
|
||||
expect(tokens).toEqual(['ET', 'IPv4', 'IPv6', 'SCAN']);
|
||||
});
|
||||
|
||||
test('it should NOT return multiple mixed tokens, but only the ones at the beginning', () => {
|
||||
const tokens = getBeginningTokens('EVAN BRADEN Hassanabad FRANK');
|
||||
expect(tokens).toEqual(['EVAN', 'BRADEN']);
|
||||
});
|
||||
|
||||
test('it should return empty tags if there are no tags', () => {
|
||||
const tokens = getBeginningTokens('No Tags Here');
|
||||
expect(tokens).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should return empty tags if any empty string is sent in', () => {
|
||||
const tokens = getBeginningTokens('');
|
||||
expect(tokens).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should return empty tags if a string of all spaces is sent in', () => {
|
||||
const tokens = getBeginningTokens(' ');
|
||||
expect(tokens).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should return empty tags if a signature has extra spaces at the start', () => {
|
||||
const tokens = getBeginningTokens(' Hello How are You?');
|
||||
expect(tokens).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should return empty tags if a signature has extra spaces at the end', () => {
|
||||
const tokens = getBeginningTokens('Hello How are You? ');
|
||||
expect(tokens).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should return valid tags if a signature has extra spaces at the start', () => {
|
||||
const tokens = getBeginningTokens(' HELLO HOW are You?');
|
||||
expect(tokens).toEqual(['HELLO', 'HOW']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { uniq } from 'lodash/fp';
|
||||
import { db } from 'suricata-sid-db';
|
||||
|
||||
export const getLinksFromSignature = (id: string): string[] => {
|
||||
const refs = db[id];
|
||||
if (refs != null) {
|
||||
return uniq(refs);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const specialTokenRules = ['IPv4', 'IPv6'];
|
||||
|
||||
export const getBeginningTokens = (signature: string): string[] => {
|
||||
const signatureSplit = signature.trim().split(' ');
|
||||
return signatureSplit.reduce<string[]>((accum, curr, index) => {
|
||||
if (
|
||||
(accum.length === index && curr === curr.toUpperCase() && curr !== '') ||
|
||||
specialTokenRules.includes(curr)
|
||||
) {
|
||||
accum = accum.concat(curr);
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink } from '@elastic/eui';
|
||||
import * as React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { getLinksFromSignature } from './suricata_links';
|
||||
|
||||
const Icon = styled(EuiIcon)`
|
||||
margin-left: 10px;
|
||||
margin-right: 3px;
|
||||
`;
|
||||
|
||||
const LinkEuiFlexItem = styled(EuiFlexItem)`
|
||||
display: inline;
|
||||
`;
|
||||
|
||||
export const SuricataRefs = pure(({ signatureId }: { signatureId: string }) => {
|
||||
const links = getLinksFromSignature(signatureId);
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="none" justifyContent="center" wrap>
|
||||
{links.map(link => (
|
||||
<LinkEuiFlexItem key={link} grow={false}>
|
||||
<Icon type="link" size="s" />
|
||||
<EuiLink href={link} color="subdued" target="_blank">
|
||||
{link}
|
||||
</EuiLink>
|
||||
</LinkEuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
});
|
|
@ -62,7 +62,7 @@ describe('suricata_row_renderer', () => {
|
|||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toContain(
|
||||
'some children ET EXPLOIT NETGEAR WNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)'
|
||||
'some children 4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -4,49 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { get, getOr } from 'lodash/fp';
|
||||
import { get } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { createLinkWithSignature, RowRenderer } from '.';
|
||||
import { RowRenderer } from '.';
|
||||
import { Ecs } from '../../../../graphql/types';
|
||||
import {
|
||||
getAllFieldsInSchemaByMappedName,
|
||||
getMappedEcsValue,
|
||||
mappedEcsSchemaFieldNames,
|
||||
virtualEcsSchema,
|
||||
} from '../../../../lib/ecs';
|
||||
import { escapeQueryValue } from '../../../../lib/keury';
|
||||
import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper';
|
||||
import { escapeDataProviderId } from '../../../drag_and_drop/helpers';
|
||||
import { Provider } from '../../data_providers/provider';
|
||||
import { FormattedField } from './formatted_field';
|
||||
import * as i18n from './translations';
|
||||
|
||||
export const dropInEffect = keyframes`
|
||||
0% {
|
||||
border: 1px solid;
|
||||
border-color: #d9d9d9;
|
||||
transform: scale(1.050);
|
||||
box-shadow: 0 2px 2px -1px rgba(153, 153, 153, 0.3), 0 1px 5px -2px rgba(153, 153, 153, 0.3);
|
||||
}
|
||||
|
||||
35%, 80% {
|
||||
border: 1px solid;
|
||||
border-color: #d9d9d9;
|
||||
transform: scale(1.010);
|
||||
box-shadow: 0 2px 2px -1px rgba(153, 153, 153, 0.3), 0 1px 5px -2px rgba(153, 153, 153, 0.3);
|
||||
}
|
||||
|
||||
100% {
|
||||
border-color: transparent;
|
||||
border-left: 2px solid #8ecce3;
|
||||
transform: scale(1);
|
||||
box-shadow: unset;
|
||||
}
|
||||
`;
|
||||
import { SuricataDetails } from './suricata_details';
|
||||
|
||||
const SuricataRow = styled.div`
|
||||
width: 100%;
|
||||
|
@ -56,138 +20,16 @@ const SuricataRow = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const SuricataSignature = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 5px;
|
||||
`;
|
||||
|
||||
const Details = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin: 5px;
|
||||
min-width: 340px;
|
||||
`;
|
||||
|
||||
const LabelValuePairContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const Label = styled.div`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const allFieldsInSchemaByName = getAllFieldsInSchemaByMappedName(virtualEcsSchema);
|
||||
|
||||
export const fieldExists = ({ data, fieldName }: { data: Ecs; fieldName: string }): boolean =>
|
||||
getMappedEcsValue({ data, fieldName }) != null;
|
||||
|
||||
const DraggableValue = pure<{ data: Ecs; fieldName: string }>(({ data, fieldName }) => {
|
||||
const itemDataProvider = {
|
||||
enabled: true,
|
||||
id: escapeDataProviderId(`id-suricata-row-render-value-for-${fieldName}-${data._id}`),
|
||||
name: `${fieldName}: ${getMappedEcsValue({
|
||||
data,
|
||||
fieldName,
|
||||
})}`,
|
||||
queryMatch: {
|
||||
field: getOr(fieldName, fieldName, mappedEcsSchemaFieldNames),
|
||||
value: escapeQueryValue(
|
||||
getMappedEcsValue({
|
||||
data,
|
||||
fieldName,
|
||||
})
|
||||
),
|
||||
},
|
||||
excluded: false,
|
||||
kqlQuery: '',
|
||||
and: [],
|
||||
};
|
||||
|
||||
const field = allFieldsInSchemaByName[fieldName];
|
||||
const fieldType = field != null ? field.type : '';
|
||||
|
||||
return fieldExists({ data, fieldName }) ? (
|
||||
<DraggableWrapper
|
||||
key={`suricata-row-render-value-for-${fieldName}-${data._id}`}
|
||||
dataProvider={itemDataProvider}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
<DragEffects>
|
||||
<Provider dataProvider={dataProvider} />
|
||||
</DragEffects>
|
||||
) : (
|
||||
<FormattedField data={data} fieldName={fieldName} fieldType={fieldType} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export const ValuesContainer = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const suricataRowRenderer: RowRenderer = {
|
||||
isInstance: (ecs: Ecs) => {
|
||||
if (ecs && ecs.event && ecs.event.module && ecs.event.module.toLowerCase() === 'suricata') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
const module: string | null = get('event.module', ecs);
|
||||
return module != null && module.toLowerCase() === 'suricata';
|
||||
},
|
||||
renderRow: (data: Ecs, children: React.ReactNode) => {
|
||||
const signature = get('suricata.eve.alert.signature', data) as string;
|
||||
return (
|
||||
<SuricataRow>
|
||||
{children}
|
||||
{signature != null ? (
|
||||
<SuricataSignature>
|
||||
<EuiButton
|
||||
key={data._id}
|
||||
fill
|
||||
size="s"
|
||||
href={createLinkWithSignature(signature)}
|
||||
target="_blank"
|
||||
>
|
||||
{signature}
|
||||
</EuiButton>
|
||||
<Details>
|
||||
{fieldExists({ data, fieldName: 'source.ip' }) ? (
|
||||
<LabelValuePairContainer>
|
||||
<Label>{i18n.SOURCE}</Label>
|
||||
<ValuesContainer>
|
||||
<DraggableValue
|
||||
data={data}
|
||||
data-test-subj="source-ip-and-port"
|
||||
fieldName={'source.ip'}
|
||||
/>
|
||||
{fieldExists({ data, fieldName: 'source.port' }) ? ':' : null}
|
||||
<DraggableValue data={data} fieldName={'source.port'} />
|
||||
</ValuesContainer>
|
||||
</LabelValuePairContainer>
|
||||
) : null}
|
||||
|
||||
{fieldExists({ data, fieldName: 'destination.ip' }) ? (
|
||||
<LabelValuePairContainer>
|
||||
<Label>{i18n.DESTINATION}</Label>
|
||||
<ValuesContainer>
|
||||
<DraggableValue
|
||||
data={data}
|
||||
data-test-subj="destination-ip-and-port"
|
||||
fieldName={'destination.ip'}
|
||||
/>
|
||||
{fieldExists({ data, fieldName: 'destination.port' }) ? ':' : null}
|
||||
<DraggableValue data={data} fieldName={'destination.port'} />
|
||||
</ValuesContainer>
|
||||
</LabelValuePairContainer>
|
||||
) : null}
|
||||
</Details>
|
||||
</SuricataSignature>
|
||||
) : null}
|
||||
<SuricataDetails data={data} />
|
||||
</SuricataRow>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import { noop } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
|
||||
import { DragDropContext } from 'react-beautiful-dnd';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { mockGlobalState } from '../../../../mock';
|
||||
import { createStore, State } from '../../../../store';
|
||||
import { DraggableSignatureId, GoogleLink, SuricataSignature, Tokens } from './suricata_signature';
|
||||
|
||||
describe('SuricataSignature', () => {
|
||||
const state: State = mockGlobalState;
|
||||
const theme = () => ({ eui: euiDarkVars, darkMode: true });
|
||||
let store = createStore(state);
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore(state);
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
test('it renders the default SuricataSignature', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<SuricataSignature
|
||||
id="doc-id-123"
|
||||
signatureId="id-123"
|
||||
signature="ET SCAN ATTACK Hello"
|
||||
/>
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GoogleLink', () => {
|
||||
test('it renders text passed in as value', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<GoogleLink link={'http:/example.com/'} value={'Example Link'} />
|
||||
);
|
||||
expect(wrapper.text()).toEqual('Example Link');
|
||||
});
|
||||
|
||||
test('it renders props passed in as link', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<GoogleLink link={'http:/example.com/'} value={'Example Link'} />
|
||||
);
|
||||
expect(wrapper.find('a').prop('href')).toEqual(
|
||||
'https://www.google.com/search?q=http:/example.com/'
|
||||
);
|
||||
});
|
||||
|
||||
test("it encodes <script>alert('XSS')</script>", () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<GoogleLink
|
||||
link={"http:/example.com?q=<script>alert('XSS')</script>"}
|
||||
value={'Example Link'}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('a').prop('href')).toEqual(
|
||||
"https://www.google.com/search?q=http:/example.com?q=%3Cscript%3Ealert('XSS')%3C/script%3E"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tokens', () => {
|
||||
test('should render empty if tokens are empty', () => {
|
||||
const wrapper = mountWithIntl(<Tokens tokens={[]} />);
|
||||
expect(wrapper.text()).toEqual(null);
|
||||
});
|
||||
|
||||
test('should render a single if it is present', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<div>
|
||||
<Tokens tokens={['ET']} />
|
||||
</div>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('ET');
|
||||
});
|
||||
|
||||
test('should render the multiple tokens if they are present', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<div>
|
||||
<Tokens tokens={['ET', 'SCAN']} />
|
||||
</div>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('ETSCAN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('DraggableSignatureId', () => {
|
||||
test('it renders the default SuricataSignature', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<ThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<DraggableSignatureId id="id-123" signatureId="signature-123" />
|
||||
</DragDropContext>
|
||||
</Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('signature-123');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
|
||||
import * as React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper';
|
||||
import { escapeDataProviderId } from '../../../drag_and_drop/helpers';
|
||||
import { Provider } from '../../../timeline/data_providers/provider';
|
||||
import { getBeginningTokens } from './suricata_links';
|
||||
|
||||
const SignatureFlexItem = styled(EuiFlexItem)`
|
||||
min-width: 77px;
|
||||
`;
|
||||
|
||||
const Badge = styled(EuiBadge)`
|
||||
vertical-align: top;
|
||||
`;
|
||||
|
||||
const TokensFlexItem = styled(EuiFlexItem)`
|
||||
margin-left: 3px;
|
||||
`;
|
||||
|
||||
const LinkFlexItem = styled(EuiFlexItem)`
|
||||
margin-left: 6px;
|
||||
`;
|
||||
|
||||
export const GoogleLink = pure(({ link, value }: { link: string; value: string }) => (
|
||||
<LinkFlexItem grow={false}>
|
||||
<EuiLink href={`https://www.google.com/search?q=${encodeURI(link)}`} target="_blank">
|
||||
{value}
|
||||
</EuiLink>
|
||||
</LinkFlexItem>
|
||||
));
|
||||
|
||||
export const Tokens = pure(({ tokens }: { tokens: string[] }) => (
|
||||
<>
|
||||
{tokens.map(token => (
|
||||
<TokensFlexItem key={token} grow={false}>
|
||||
<EuiBadge iconType="tag" color="hollow">
|
||||
{token}
|
||||
</EuiBadge>
|
||||
</TokensFlexItem>
|
||||
))}
|
||||
</>
|
||||
));
|
||||
|
||||
export const DraggableSignatureId = pure(
|
||||
({ id, signatureId }: { id: string; signatureId: string }) => (
|
||||
<SignatureFlexItem grow={false}>
|
||||
<DraggableWrapper
|
||||
dataProvider={{
|
||||
and: [],
|
||||
enabled: true,
|
||||
id: escapeDataProviderId(`suricata-${id}-sig-${signatureId}`),
|
||||
name: signatureId,
|
||||
excluded: false,
|
||||
kqlQuery: '',
|
||||
queryMatch: {
|
||||
field: 'suricata.eve.alert.signature_id',
|
||||
value: signatureId,
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
<DragEffects>
|
||||
<Provider dataProvider={dataProvider} />
|
||||
</DragEffects>
|
||||
) : (
|
||||
<Badge iconType="number" color="hollow">
|
||||
{signatureId}
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</SignatureFlexItem>
|
||||
)
|
||||
);
|
||||
|
||||
export const SuricataSignature = pure(
|
||||
({ id, signature, signatureId }: { id: string; signature: string; signatureId: string }) => {
|
||||
const tokens = getBeginningTokens(signature);
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="center" gutterSize="none">
|
||||
<DraggableSignatureId id={id} signatureId={signatureId} />
|
||||
<Tokens tokens={tokens} />
|
||||
<GoogleLink
|
||||
link={signature}
|
||||
value={signature
|
||||
.split(' ')
|
||||
.splice(tokens.length)
|
||||
.join(' ')}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -90,3 +90,6 @@ export const getPopulatedMappedFields = ({
|
|||
'name',
|
||||
getAllFieldsInSchema(schema).filter(f => getMappedEcsValue({ data, fieldName: f.name }) != null)
|
||||
);
|
||||
|
||||
export const fieldExists = ({ data, fieldName }: { data: Ecs; fieldName: string }): boolean =>
|
||||
getMappedEcsValue({ data, fieldName }) != null;
|
||||
|
|
|
@ -85,8 +85,7 @@ export const eventBaseFieldsMap: Readonly<Record<string, string>> = {
|
|||
// NOTE: This is only for the index filebeat. If you're using auditbeat, then this needs to be changed out for 'event.id': 'event.id'
|
||||
'event.id': 'suricata.eve.flow_id',
|
||||
'event.module': 'event.module',
|
||||
// NOTE: This is only for the index filebeat. If you're using auditbeat, this doesn't matter as auditbeat does not have severities yet.
|
||||
'event.severity': 'suricata.eve.alert.severity',
|
||||
'event.severity': 'event.severity',
|
||||
'event.type': 'event.type',
|
||||
};
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ describe('events elasticsearch_adapter', () => {
|
|||
module: 'event-module-1',
|
||||
type: 'event-type-1',
|
||||
category: 'event-category-1',
|
||||
severity: 1,
|
||||
},
|
||||
},
|
||||
sort: ['123567890', '1234'],
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -21178,6 +21178,13 @@ supports-color@^6.1.0:
|
|||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
suricata-sid-db@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/suricata-sid-db/-/suricata-sid-db-1.0.2.tgz#96ceda4db117a9f1282c8f9d785285e5ccf342b1"
|
||||
integrity sha512-13h9BiGWLQ2Ng5yrtVkwYudjo1yFalRIW63qsw7/awb8sJZjptmVSIg/iyfFAB/TV1Ru8Kgt9Q95CZwgUipF1w==
|
||||
dependencies:
|
||||
typescript "^3.3.3333"
|
||||
|
||||
svg-tags@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
|
||||
|
@ -22534,6 +22541,11 @@ typescript@^3.0.3:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8"
|
||||
integrity sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==
|
||||
|
||||
typescript@^3.3.3333:
|
||||
version "3.3.3333"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6"
|
||||
integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==
|
||||
|
||||
ua-parser-js@^0.7.18, ua-parser-js@^0.7.9:
|
||||
version "0.7.18"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue