[Security Solution] Adds a version and OS check for Host Isolation (#103026)
This commit is contained in:
parent
e749fa62fa
commit
1ff2407b18
|
@ -102,7 +102,7 @@ export class BaseDataGenerator<GeneratedDoc extends {} = {}> {
|
|||
}
|
||||
|
||||
protected randomVersion(): string {
|
||||
return [6, ...this.randomNGenerator(10, 2)].map((x) => x.toString()).join('.');
|
||||
return [7, ...this.randomNGenerator(20, 2)].map((x) => x.toString()).join('.');
|
||||
}
|
||||
|
||||
protected randomChoice<T>(choices: T[]): T {
|
||||
|
|
|
@ -51,7 +51,7 @@ export const ANCESTRY_LIMIT: number = 2;
|
|||
|
||||
const Windows: OSFields[] = [
|
||||
{
|
||||
name: 'windows 10.0',
|
||||
name: 'Windows',
|
||||
full: 'Windows 10',
|
||||
version: '10.0',
|
||||
platform: 'Windows',
|
||||
|
@ -61,7 +61,7 @@ const Windows: OSFields[] = [
|
|||
},
|
||||
},
|
||||
{
|
||||
name: 'windows 10.0',
|
||||
name: 'Windows',
|
||||
full: 'Windows Server 2016',
|
||||
version: '10.0',
|
||||
platform: 'Windows',
|
||||
|
@ -71,7 +71,7 @@ const Windows: OSFields[] = [
|
|||
},
|
||||
},
|
||||
{
|
||||
name: 'windows 6.2',
|
||||
name: 'Windows',
|
||||
full: 'Windows Server 2012',
|
||||
version: '6.2',
|
||||
platform: 'Windows',
|
||||
|
@ -81,7 +81,7 @@ const Windows: OSFields[] = [
|
|||
},
|
||||
},
|
||||
{
|
||||
name: 'windows 6.3',
|
||||
name: 'Windows',
|
||||
full: 'Windows Server 2012R2',
|
||||
version: '6.3',
|
||||
platform: 'Windows',
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isVersionSupported, isOsSupported, isIsolationSupported } from './utils';
|
||||
|
||||
describe('Host Isolation utils isVersionSupported', () => {
|
||||
test.each`
|
||||
a | b | expected
|
||||
${'8.14.0'} | ${'7.13.0'} | ${true}
|
||||
${'7.14.0'} | ${'7.13.0'} | ${true}
|
||||
${'7.14.1'} | ${'7.14.0'} | ${true}
|
||||
${'8.14.0'} | ${'9.14.0'} | ${false}
|
||||
${'7.13.0'} | ${'7.14.0'} | ${false}
|
||||
${'7.14.0'} | ${'7.14.1'} | ${false}
|
||||
${'7.14.0'} | ${'7.14.0'} | ${true}
|
||||
${'7.14.0-SNAPSHOT'} | ${'7.14.0'} | ${true}
|
||||
${'7.14.0-SNAPSHOT-beta'} | ${'7.14.0'} | ${true}
|
||||
${'7.14.0-alpha'} | ${'7.14.0'} | ${true}
|
||||
`('should validate that version $a is compatible($expected) to $b', ({ a, b, expected }) => {
|
||||
expect(
|
||||
isVersionSupported({
|
||||
currentVersion: a,
|
||||
minVersionRequired: b,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Host Isolation utils isOsSupported', () => {
|
||||
test.each`
|
||||
a | b | expected
|
||||
${'linux'} | ${['macos', 'linux']} | ${true}
|
||||
${'linux'} | ${['macos', 'windows']} | ${false}
|
||||
`('should validate that os $a is compatible($expected) to $b', ({ a, b, expected }) => {
|
||||
expect(
|
||||
isOsSupported({
|
||||
currentOs: a,
|
||||
supportedOss: b,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Host Isolation utils isIsolationSupported', () => {
|
||||
test.each`
|
||||
a | b | expected
|
||||
${'windows'} | ${'7.14.0'} | ${true}
|
||||
${'linux'} | ${'7.13.0'} | ${false}
|
||||
${'linux'} | ${'7.14.0'} | ${false}
|
||||
${'macos'} | ${'7.13.0'} | ${false}
|
||||
`(
|
||||
'should validate that os $a and version $b supports hostIsolation($expected)',
|
||||
({ a, b, expected }) => {
|
||||
expect(
|
||||
isIsolationSupported({
|
||||
osName: a,
|
||||
version: b,
|
||||
})
|
||||
).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const isVersionSupported = ({
|
||||
currentVersion,
|
||||
minVersionRequired,
|
||||
}: {
|
||||
currentVersion: string;
|
||||
minVersionRequired: string;
|
||||
}) => {
|
||||
const parsedCurrentVersion = currentVersion.includes('-SNAPSHOT')
|
||||
? currentVersion.substring(0, currentVersion.indexOf('-'))
|
||||
: currentVersion;
|
||||
const tokenizedCurrent = parsedCurrentVersion
|
||||
.split('.')
|
||||
.map((token: string) => parseInt(token, 10));
|
||||
const tokenizedMin = minVersionRequired.split('.').map((token: string) => parseInt(token, 10));
|
||||
|
||||
const versionNotSupported = tokenizedCurrent.some((token: number, index: number) => {
|
||||
return token < tokenizedMin[index];
|
||||
});
|
||||
|
||||
return !versionNotSupported;
|
||||
};
|
||||
|
||||
export const isOsSupported = ({
|
||||
currentOs,
|
||||
supportedOss,
|
||||
}: {
|
||||
currentOs: string;
|
||||
supportedOss: string[];
|
||||
}) => {
|
||||
return supportedOss.some((os) => currentOs === os);
|
||||
};
|
||||
|
||||
export const isIsolationSupported = ({ osName, version }: { osName: string; version: string }) => {
|
||||
const normalizedOs = osName.toLowerCase();
|
||||
return (
|
||||
isOsSupported({ currentOs: normalizedOs, supportedOss: ['macos', 'windows'] }) &&
|
||||
isVersionSupported({ currentVersion: version, minVersionRequired: '7.14.0' })
|
||||
);
|
||||
};
|
|
@ -46,6 +46,10 @@ describe('When using the Endpoint Details Actions Menu', () => {
|
|||
// Safe to mutate this mocked data
|
||||
// @ts-ignore
|
||||
endpointHost.metadata.Endpoint.state.isolation = isolation;
|
||||
// @ts-ignore
|
||||
endpointHost.metadata.host.os.name = 'Windows';
|
||||
// @ts-ignore
|
||||
endpointHost.metadata.agent.version = '7.14.0';
|
||||
httpMocks.responseProvider.metadataDetails.mockReturnValue(endpointHost);
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import { useAppUrl } from '../../../../../common/lib/kibana/hooks';
|
|||
import { ContextMenuItemNavByRouterProps } from '../components/context_menu_item_nav_by_rotuer';
|
||||
import { isEndpointHostIsolated } from '../../../../../common/utils/validators';
|
||||
import { useLicense } from '../../../../../common/hooks/use_license';
|
||||
import { isIsolationSupported } from '../../../../../../common/endpoint/service/host_isolation/utils';
|
||||
|
||||
/**
|
||||
* Returns a list (array) of actions for an individual endpoint
|
||||
|
@ -37,6 +38,10 @@ export const useEndpointActionItems = (
|
|||
const endpointPolicyId = endpointMetadata.Endpoint.policy.applied.id;
|
||||
const endpointHostName = endpointMetadata.host.hostname;
|
||||
const fleetAgentId = endpointMetadata.elastic.agent.id;
|
||||
const isolationSupported = isIsolationSupported({
|
||||
osName: endpointMetadata.host.os.name,
|
||||
version: endpointMetadata.agent.version,
|
||||
});
|
||||
const {
|
||||
show,
|
||||
selected_endpoint: _selectedEndpoint,
|
||||
|
@ -73,7 +78,7 @@ export const useEndpointActionItems = (
|
|||
/>
|
||||
),
|
||||
});
|
||||
} else if (isPlatinumPlus) {
|
||||
} else if (isPlatinumPlus && isolationSupported) {
|
||||
// For Platinum++ licenses, users also have ability to isolate
|
||||
isolationActions.push({
|
||||
'data-test-subj': 'isolateLink',
|
||||
|
|
|
@ -1189,6 +1189,17 @@ describe('when on the endpoint list page', () => {
|
|||
isolation: false,
|
||||
},
|
||||
},
|
||||
host: {
|
||||
...hosts[0].metadata.host,
|
||||
os: {
|
||||
...hosts[0].metadata.host.os,
|
||||
name: 'Windows',
|
||||
},
|
||||
},
|
||||
agent: {
|
||||
...hosts[0].metadata.agent,
|
||||
version: '7.14.0',
|
||||
},
|
||||
},
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
} from '../../../../detections/components/host_isolation/translations';
|
||||
import { ALERT_DETAILS } from './translations';
|
||||
import { useIsolationPrivileges } from '../../../../common/hooks/endpoint/use_isolate_privileges';
|
||||
import { isIsolationSupported } from '../../../../../common/endpoint/service/host_isolation/utils';
|
||||
import { endpointAlertCheck } from '../../../../common/utils/endpoint_alert_check';
|
||||
import { useWithCaseDetailsRefresh } from '../../../../common/components/endpoint/host_isolation/endpoint_host_isolation_cases_context';
|
||||
|
||||
|
@ -102,6 +103,22 @@ const EventDetailsPanelComponent: React.FC<EventDetailsPanelProps> = ({
|
|||
return findAgentId ? findAgentId[0] : '';
|
||||
}, [detailsData]);
|
||||
|
||||
const hostOsFamily = useMemo(() => {
|
||||
const findOsName = find({ category: 'host', field: 'host.os.name' }, detailsData)?.values;
|
||||
return findOsName ? findOsName[0] : '';
|
||||
}, [detailsData]);
|
||||
|
||||
const agentVersion = useMemo(() => {
|
||||
const findAgentVersion = find({ category: 'agent', field: 'agent.version' }, detailsData)
|
||||
?.values;
|
||||
return findAgentVersion ? findAgentVersion[0] : '';
|
||||
}, [detailsData]);
|
||||
|
||||
const isolationSupported = isIsolationSupported({
|
||||
osName: hostOsFamily,
|
||||
version: agentVersion,
|
||||
});
|
||||
|
||||
const backToAlertDetailsLink = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
|
@ -164,15 +181,18 @@ const EventDetailsPanelComponent: React.FC<EventDetailsPanelProps> = ({
|
|||
/>
|
||||
)}
|
||||
</StyledEuiFlyoutBody>
|
||||
{isIsolationAllowed && isEndpointAlert && isHostIsolationPanelOpen === false && (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<TakeActionDropdown onChange={showHostIsolationPanel} agentId={agentId} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
)}
|
||||
{isIsolationAllowed &&
|
||||
isEndpointAlert &&
|
||||
isolationSupported &&
|
||||
isHostIsolationPanelOpen === false && (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<TakeActionDropdown onChange={showHostIsolationPanel} agentId={agentId} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
|
Loading…
Reference in a new issue