[Security Solution] Adds a version and OS check for Host Isolation (#103026)

This commit is contained in:
Kevin Logan 2021-06-29 19:46:19 -04:00 committed by GitHub
parent e749fa62fa
commit 1ff2407b18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 167 additions and 15 deletions

View file

@ -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 {

View file

@ -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',

View file

@ -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);
}
);
});

View file

@ -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' })
);
};

View file

@ -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);
};

View file

@ -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',

View file

@ -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,
};

View file

@ -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>
)}
</>
) : (
<>