Skip to content

Commit

Permalink
[Cloud Security][CIS GCP]cis gcp now use updated gcp field name + sma…
Browse files Browse the repository at this point in the history
…ll last minute changes (#164792)

This PR is for updating GCP forms 
- Manifest for GCP has been changed (this PR updates the field name that
was updated on the manifest)
- EuiCodeBlock for CloudShell is interactive now
- Static hover name for AWS and GCP icon on dashboard
- MIN_VERSION for CIS GCP has now been update to 1.5.2 from 1.5.0

<img width="739" alt="Screenshot 2023-08-24 at 10 58 44 PM"
src="https://github.com/elastic/kibana/assets/8703149/b88f4787-8396-4913-a475-5a381c802021">
## Summary

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
animehart and kibanamachine authored Aug 28, 2023
1 parent e574281 commit 145fefe
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 66 deletions.
12 changes: 11 additions & 1 deletion x-pack/plugins/cloud_security_posture/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* 2.0.
*/

import { PostureTypes, VulnSeverity, AwsCredentialsTypeFieldMap } from './types';
import {
PostureTypes,
VulnSeverity,
AwsCredentialsTypeFieldMap,
GcpCredentialsTypeFieldMap,
} from './types';

export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status';
export const STATUS_API_CURRENT_VERSION = '1';
Expand Down Expand Up @@ -143,3 +148,8 @@ export const SETUP_ACCESS_CLOUD_SHELL = 'google_cloud_shell';
export const SETUP_ACCESS_MANUAL = 'manual';

export const DETECTION_ENGINE_ALERTS_INDEX_DEFAULT = '.alerts-security.alerts-default';

export const GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP: GcpCredentialsTypeFieldMap = {
'credentials-file': ['gcp.credentials.file'],
'credentials-json': ['gcp.credentials.json'],
};
6 changes: 6 additions & 0 deletions x-pack/plugins/cloud_security_posture/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export type AwsCredentialsTypeFieldMap = {
[key in AwsCredentialsType]: string[];
};

export type GcpCredentialsType = 'credentials-file' | 'credentials-json';

export type GcpCredentialsTypeFieldMap = {
[key in GcpCredentialsType]: string[];
};

export type Evaluation = 'passed' | 'failed' | 'NA';

export type PostureTypes = 'cspm' | 'kspm' | 'vuln_mgmt' | 'all';
Expand Down
104 changes: 104 additions & 0 deletions x-pack/plugins/cloud_security_posture/common/utils/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,109 @@ describe('test helper methods', () => {
shared_credential_file: { value: undefined, type: 'text' },
});
});

it('when aws credential type is undefined, return unchanged policy', () => {
const mockPackagePolicy = createPackagePolicyMock();
mockPackagePolicy.inputs = [
{
type: 'cloudbeat/cis_eks',
enabled: true,
streams: [
{
id: 'findings',
enabled: true,
data_stream: {
dataset: 'cloud_security_posture.findings',
type: 'logs',
},
vars: {
'aws.credentials.type': { value: undefined },
access_key_id: { value: 'used', type: 'text' },
credential_profile_name: { value: 'unused', type: 'text' },
role_arn: { value: 'unused' },
secret_access_key: { value: 'used', type: 'text' },
session_token: { value: 'unused', type: 'text' },
shared_credential_file: { value: 'unused', type: 'text' },
},
},
],
},
];

const cleanedPackage = cleanupCredentials(mockPackagePolicy);
expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({
'aws.credentials.type': { value: undefined },
access_key_id: { value: 'used', type: 'text' },
credential_profile_name: { value: 'unused', type: 'text' },
role_arn: { value: 'unused' },
secret_access_key: { value: 'used', type: 'text' },
session_token: { value: 'unused', type: 'text' },
shared_credential_file: { value: 'unused', type: 'text' },
});
});

it('cleans unused gcp credential methods, when using credentials-file method ', () => {
const mockPackagePolicy = createPackagePolicyMock();
mockPackagePolicy.inputs = [
{
type: 'cloudbeat/cis_gcp',
enabled: true,
streams: [
{
id: 'findings',
enabled: true,
data_stream: {
dataset: 'cloud_security_posture.findings',
type: 'logs',
},
vars: {
'gcp.credentials.type': { value: 'credentials-file' },
'gcp.credentials.file': { value: 'used' },
'gcp.credentials.json': { value: 'unused' },
},
},
],
},
];

const cleanedPackage = cleanupCredentials(mockPackagePolicy);
expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({
'gcp.credentials.type': { value: 'credentials-file' },
'gcp.credentials.file': { value: 'used' },
'gcp.credentials.json': { value: undefined },
});
});

it('when gcp credential type is undefined, return unchanged policy', () => {
const mockPackagePolicy = createPackagePolicyMock();
mockPackagePolicy.inputs = [
{
type: 'cloudbeat/cis_gcp',
enabled: true,
streams: [
{
id: 'findings',
enabled: true,
data_stream: {
dataset: 'cloud_security_posture.findings',
type: 'logs',
},
vars: {
'gcp.credentials.type': { value: undefined },
'gcp.credentials.file': { value: 'used' },
'gcp.credentials.json': { value: 'unused' },
},
},
],
},
];

const cleanedPackage = cleanupCredentials(mockPackagePolicy);
expect(cleanedPackage.inputs[0].streams[0].vars).toEqual({
'gcp.credentials.type': { value: undefined },
'gcp.credentials.file': { value: 'used' },
'gcp.credentials.json': { value: 'unused' },
});
});
});
});
28 changes: 22 additions & 6 deletions x-pack/plugins/cloud_security_posture/common/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@ import {
CLOUDBEAT_VANILLA,
CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE,
AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP,
GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP,
} from '../constants';
import type { BenchmarkId, Score, BaseCspSetupStatus, AwsCredentialsType } from '../types';
import type {
BenchmarkId,
Score,
BaseCspSetupStatus,
AwsCredentialsType,
GcpCredentialsType,
} from '../types';

/**
* @example
Expand Down Expand Up @@ -103,12 +110,21 @@ export const getStatusForIndexName = (indexName: string, status?: BaseCspSetupSt

export const cleanupCredentials = (packagePolicy: NewPackagePolicy | UpdatePackagePolicy) => {
const enabledInput = packagePolicy.inputs.find((i) => i.enabled);
const credentialType: AwsCredentialsType | undefined =
const awsCredentialType: AwsCredentialsType | undefined =
enabledInput?.streams?.[0].vars?.['aws.credentials.type']?.value;

if (credentialType) {
const credsToKeep = AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP[credentialType];
const credFields = Object.values(AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat();
const gcpCredentialType: GcpCredentialsType | undefined =
enabledInput?.streams?.[0].vars?.['gcp.credentials.type']?.value;

if (awsCredentialType || gcpCredentialType) {
let credsToKeep: string[] = [' '];
let credFields: string[] = [' '];
if (awsCredentialType) {
credsToKeep = AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP[awsCredentialType];
credFields = Object.values(AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat();
} else if (gcpCredentialType) {
credsToKeep = GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP[gcpCredentialType];
credFields = Object.values(GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP).flat();
}

if (credsToKeep) {
// we need to return a copy of the policy with the unused
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export const cloudPostureIntegrations: CloudPostureIntegrations = {
},
};
export const FINDINGS_DOCS_URL = 'https://ela.st/findings';
export const MIN_VERSION_GCP_CIS = '1.5.0';
export const MIN_VERSION_GCP_CIS = '1.5.2';

export const NO_FINDINGS_STATUS_REFRESH_INTERVAL_MS = 10000;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export const AccountsEvaluatedWidget = ({
const cisAwsClusterAmount = filterClustersById(CIS_AWS).length;
const cisGcpClusterAmount = filterClustersById(CIS_GCP).length;

const cisAwsBenchmarkName = filterClustersById(CIS_AWS)[0]?.meta.benchmark.name || '';
const cisGcpBenchmarkName = filterClustersById(CIS_GCP)[0]?.meta.benchmark.name || '';
const cisAwsBenchmarkName = 'Amazon Web Services (AWS)';
const cisGcpBenchmarkName = 'Google Cloud Platform (GCP)';

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,56 +110,58 @@ const GoogleCloudShellSetup = () => {
);
};

type GcpFields = Record<string, { label: string; type?: 'password' | 'text' }>;
const credentialOptionsList = [
{
text: i18n.translate('xpack.csp.gcpIntegration.credentialsFileOption', {
defaultMessage: 'Credentials File',
}),
value: 'credentials-file',
},
{
text: i18n.translate('xpack.csp.gcpIntegration.credentialsJsonOption', {
defaultMessage: 'Credentials JSON',
}),
value: 'credentials-json',
},
];

type GcpFields = Record<string, { label: string; type?: 'password' | 'text'; value?: string }>;
interface GcpInputFields {
fields: GcpFields;
}

export const gcpField: GcpInputFields = {
fields: {
project_id: {
'gcp.project_id': {
label: i18n.translate('xpack.csp.gcpIntegration.projectidFieldLabel', {
defaultMessage: 'Project ID',
}),
type: 'text',
},
credentials_file: {
'gcp.credentials.file': {
label: i18n.translate('xpack.csp.findings.gcpIntegration.gcpInputText.credentialFileText', {
defaultMessage: 'Path to JSON file containing the credentials and key used to subscribe',
}),
type: 'text',
},
credentials_json: {
'gcp.credentials.json': {
label: i18n.translate('xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText', {
defaultMessage: 'JSON blob containing the credentials and key used to subscribe',
}),
type: 'text',
},
credentials_type: {
'gcp.credentials.type': {
label: i18n.translate(
'xpack.csp.findings.gcpIntegration.gcpInputText.credentialSelectBoxTitle',
{ defaultMessage: 'Credential' }
{
defaultMessage: 'Credential',
}
),
type: 'text',
},
},
};

const credentialOptionsList = [
{
text: i18n.translate('xpack.csp.gcpIntegration.credentialsFileOption', {
defaultMessage: 'Credentials File',
}),
value: 'credentials-file',
},
{
text: i18n.translate('xpack.csp.gcpIntegration.credentialsJsonOption', {
defaultMessage: 'Credentials JSON',
}),
value: 'credentials-json',
},
];

const getSetupFormatOptions = (): Array<{
id: SetupFormatGCP;
label: string;
Expand Down Expand Up @@ -193,10 +195,7 @@ interface GcpFormProps {
onChange: any;
}

const getInputVarsFields = (
input: NewPackagePolicyInput,
fields: GcpInputFields[keyof GcpInputFields]
) =>
const getInputVarsFields = (input: NewPackagePolicyInput, fields: GcpFields) =>
Object.entries(input.streams[0].vars || {})
.filter(([id]) => id in fields)
.map(([id, inputVar]) => {
Expand Down Expand Up @@ -348,7 +347,7 @@ export const GcpCredentialsForm = ({
// Integration is Invalid IF Version is not at least 1.5.0 OR Setup Access is manual but Project ID is empty
useEffect(() => {
const isProjectIdEmpty =
setupFormat === SETUP_ACCESS_MANUAL && !getFieldById('project_id')?.value;
setupFormat === SETUP_ACCESS_MANUAL && !getFieldById('gcp.project_id')?.value;
const isInvalidPolicy = isInvalid || isProjectIdEmpty;

setIsValid(!isInvalidPolicy);
Expand Down Expand Up @@ -411,19 +410,21 @@ const GcpInputVarFields = ({
const getFieldById = (id: keyof GcpInputFields['fields']) => {
return fields.find((element) => element.id === id);
};
const projectIdFields = getFieldById('project_id');
const credentialsTypeFields = getFieldById('credentials_type') || credentialOptionsList[0];
const credentialFilesFields = getFieldById('credentials_file');
const credentialJSONFields = getFieldById('credentials_json');
const projectIdFields = getFieldById('gcp.project_id');
const credentialsTypeFields = getFieldById('gcp.credentials.type');
const credentialFilesFields = getFieldById('gcp.credentials.file');
const credentialJSONFields = getFieldById('gcp.credentials.json');

const credentialFieldValue = credentialOptionsList[0].value;
const credentialJSONValue = credentialOptionsList[1].value;

const credentialsTypeValue = credentialsTypeFields?.value || credentialOptionsList[0].value;

return (
<div>
<EuiForm component="form">
{projectIdFields && (
<EuiFormRow fullWidth label={gcpField.fields.project_id.label}>
<EuiFormRow fullWidth label={gcpField.fields['gcp.project_id'].label}>
<EuiFieldText
data-test-subj={CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.PROJECT_ID}
id={projectIdFields.id}
Expand All @@ -433,22 +434,21 @@ const GcpInputVarFields = ({
/>
</EuiFormRow>
)}
{credentialFilesFields && credentialJSONFields && (
<EuiFormRow fullWidth label={gcpField.fields.credentials_type.label}>
{credentialsTypeFields && credentialFilesFields && credentialJSONFields && (
<EuiFormRow fullWidth label={gcpField.fields['gcp.credentials.type'].label}>
<EuiSelect
data-test-subj={CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_TYPE}
fullWidth
options={credentialOptionsList}
value={credentialsTypeFields?.value || credentialOptionsList[0].value}
onChange={(optionElem) => {
onChange('credentials_type', optionElem.target.value);
onChange(credentialsTypeFields?.id, optionElem.target.value);
}}
/>
</EuiFormRow>
)}

{credentialsTypeFields.value === credentialFieldValue && credentialFilesFields && (
<EuiFormRow fullWidth label={gcpField.fields.credentials_file.label}>
{credentialsTypeValue === credentialFieldValue && credentialFilesFields && (
<EuiFormRow fullWidth label={gcpField.fields['gcp.credentials.file'].label}>
<EuiFieldText
data-test-subj={CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_FILE}
id={credentialFilesFields.id}
Expand All @@ -458,8 +458,8 @@ const GcpInputVarFields = ({
/>
</EuiFormRow>
)}
{credentialsTypeFields?.value === credentialJSONValue && credentialJSONFields && (
<EuiFormRow fullWidth label={gcpField.fields.credentials_json.label}>
{credentialsTypeValue === credentialJSONValue && credentialJSONFields && (
<EuiFormRow fullWidth label={gcpField.fields['gcp.credentials.json'].label}>
<EuiTextArea
data-test-subj={CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_JSON}
id={credentialJSONFields.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const getMockPackageInfoCspmAWS = (packageVersion = '1.5.0') => {
} as PackageInfo;
};

export const getMockPackageInfoCspmGCP = (packageVersion = '1.5.0') => {
export const getMockPackageInfoCspmGCP = (packageVersion = '1.5.2') => {
return {
version: packageVersion,
name: 'cspm',
Expand Down Expand Up @@ -130,9 +130,10 @@ const getPolicyMock = (
};

const gcpVarsMock = {
project_id: { type: 'text' },
credentials_file: { type: 'text' },
credentials_json: { type: 'text' },
'gcp.project_id': { type: 'text' },
'gcp.credentials.file': { type: 'text' },
'gcp.credentials.json': { type: 'text' },
'gcp.credentials.type': { type: 'text' },
};

const dataStream = { type: 'logs', dataset: 'cloud_security_posture.findings' };
Expand Down
Loading

0 comments on commit 145fefe

Please sign in to comment.