Skip to content

Commit

Permalink
[Cloud Security] Mute detection rules
Browse files Browse the repository at this point in the history
  • Loading branch information
CohenIdo committed Dec 21, 2023
1 parent b793a2e commit faa3d8f
Show file tree
Hide file tree
Showing 14 changed files with 553 additions and 186 deletions.
15 changes: 11 additions & 4 deletions x-pack/plugins/cloud_security_posture/common/types/rules/v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { schema, TypeOf } from '@kbn/config-schema';

import type { SavedObjectsUpdateResponse } from '@kbn/core-saved-objects-api-server';
import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../constants';

const DEFAULT_BENCHMARK_RULES_PER_PAGE = 25;
Expand Down Expand Up @@ -132,9 +132,7 @@ export interface FindCspBenchmarkRuleResponse {

export const cspBenchmarkRules = schema.arrayOf(
schema.object({
benchmark_id: schema.string(),
benchmark_version: schema.string(),
rule_number: schema.string(),
rule_id: schema.string(),
})
);

Expand All @@ -153,6 +151,10 @@ const rulesStates = schema.recordOf(
schema.string(),
schema.object({
muted: schema.boolean(),
benchmark_id: schema.string(),
benchmark_version: schema.string(),
rule_number: schema.string(),
rule_id: schema.string(),
})
);

Expand All @@ -162,3 +164,8 @@ export const cspSettingsSchema = schema.object({

export type CspBenchmarkRulesStates = TypeOf<typeof rulesStates>;
export type CspSettings = TypeOf<typeof cspSettingsSchema>;

export interface BulkActionBenchmarkRulesResponse {
newCspSettings: SavedObjectsUpdateResponse<CspSettings>;
disabledRulesCounter: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* 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 { CspBenchmarkRuleMetadata } from '../types';
import {
convertRuleTagsToKQL,
generateBenchmarkRuleTags,
getFindingsDetectionRuleSearchTags,
} from './detection_rules';

describe('Detection rules utils', () => {
it('should convert tags to KQL format', () => {
const inputTags = ['tag1', 'tag2', 'tag3'];

const result = convertRuleTagsToKQL(inputTags);

const expectedKQL = 'alert.attributes.tags:("tag1" AND "tag2" AND "tag3")';
expect(result).toBe(expectedKQL);
});

it('Should convert tags to KQL format', () => {
const inputTags = [] as string[];

const result = convertRuleTagsToKQL(inputTags);

const expectedKQL = 'alert.attributes.tags:()';
expect(result).toBe(expectedKQL);
});

it('Should generate search tags for a CSP benchmark rule', () => {
const cspBenchmarkRule = {
benchmark: {
id: 'cis_gcp',
rule_number: '1.1',
},
} as unknown as CspBenchmarkRuleMetadata;

const result = getFindingsDetectionRuleSearchTags(cspBenchmarkRule);

const expectedTags = ['CIS', 'GCP', 'CIS GCP 1.1'];
expect(result).toEqual(expectedTags);
});

it('Should handle undefined benchmark object gracefully', () => {
const cspBenchmarkRule = { benchmark: {} } as any;
const expectedTags: string[] = [];
const result = getFindingsDetectionRuleSearchTags(cspBenchmarkRule);
expect(result).toEqual(expectedTags);
});

it('Should handle undefined rule number gracefully', () => {
const cspBenchmarkRule = {
benchmark: {
id: 'cis_gcp',
},
} as unknown as CspBenchmarkRuleMetadata;
const result = getFindingsDetectionRuleSearchTags(cspBenchmarkRule);
const expectedTags = ['CIS', 'GCP', 'CIS GCP'];
expect(result).toEqual(expectedTags);
});

it('Should generate tags for a CSPM benchmark rule', () => {
const cspBenchmarkRule = {
benchmark: {
id: 'cis_gcp',
rule_number: '1.1',
posture_type: 'cspm',
},
} as unknown as CspBenchmarkRuleMetadata;

const result = generateBenchmarkRuleTags(cspBenchmarkRule);

const expectedTags = [
'Cloud Security',
'Use Case: Configuration Audit',
'CIS',
'GCP',
'CIS GCP 1.1',
'CSPM',
'Data Source: CSPM',
'Domain: Cloud',
];
expect(result).toEqual(expectedTags);
});

it('Should generate tags for a KSPM benchmark rule', () => {
const cspBenchmarkRule = {
benchmark: {
id: 'cis_gcp',
rule_number: '1.1',
posture_type: 'kspm',
},
} as unknown as CspBenchmarkRuleMetadata;

const result = generateBenchmarkRuleTags(cspBenchmarkRule);

const expectedTags = [
'Cloud Security',
'Use Case: Configuration Audit',
'CIS',
'GCP',
'CIS GCP 1.1',
'KSPM',
'Data Source: KSPM',
'Domain: Container',
];
expect(result).toEqual(expectedTags);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 { CspBenchmarkRuleMetadata } from '../types/latest';

const CSP_RULE_TAG = 'Cloud Security';
const CSP_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit';
const CSP_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: ';

const STATIC_RULE_TAGS = [CSP_RULE_TAG, CSP_RULE_TAG_USE_CASE];

export const convertRuleTagsToKQL = (tags: string[]): string => {
const TAGS_FIELD = 'alert.attributes.tags';
return `${TAGS_FIELD}:(${tags.map((tag) => `"${tag}"`).join(' AND ')})`;
};

/*
* Returns an array of CspFinding tags that can be used to search and filter a detection rule
*/
export const getFindingsDetectionRuleSearchTags = (
cspBenchmarkRule: CspBenchmarkRuleMetadata
): string[] => {
if (!cspBenchmarkRule.benchmark || !cspBenchmarkRule.benchmark.id) {
// Return an empty array if benchmark ID is undefined
return [];
}

// ex: cis_gcp to ['CIS', 'GCP']
const benchmarkIdTags = cspBenchmarkRule.benchmark.id.split('_').map((tag) => tag.toUpperCase());

// ex: 'CIS GCP 1.1'
const benchmarkRuleNumberTag = cspBenchmarkRule.benchmark.rule_number
? `${cspBenchmarkRule.benchmark.id.replace('_', ' ').toUpperCase()} ${
cspBenchmarkRule.benchmark.rule_number
}`
: cspBenchmarkRule.benchmark.id.replace('_', ' ').toUpperCase();

return benchmarkIdTags.concat([benchmarkRuleNumberTag]);
};

export const generateBenchmarkRuleTags = (rule: CspBenchmarkRuleMetadata) => {
return [STATIC_RULE_TAGS]
.concat(getFindingsDetectionRuleSearchTags(rule))
.concat(
rule.benchmark.posture_type
? [
rule.benchmark.posture_type.toUpperCase(),
`${CSP_RULE_TAG_DATA_SOURCE_PREFIX}${rule.benchmark.posture_type.toUpperCase()}`,
]
: []
)
.concat(rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container'])
.flat();
};
3 changes: 2 additions & 1 deletion x-pack/plugins/cloud_security_posture/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"cloud",
"licensing",
"share",
"kibanaUtils"
"kibanaUtils",
"alerting"
],
"optionalPlugins": ["usageCollection"],
"requiredBundles": ["kibanaReact", "usageCollection"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useQuery } from '@tanstack/react-query';
import { DETECTION_RULE_RULES_API_CURRENT_VERSION } from '../../../common/constants';
import { RuleResponse } from '../types';
import { DETECTION_ENGINE_RULES_KEY } from '../constants';
import { convertRuleTagsToKQL } from '../../../common/utils/detection_rules';

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
Expand All @@ -26,16 +27,10 @@ export interface FetchRulesResponse {
data: RuleResponse[];
}

export const TAGS_FIELD = 'alert.attributes.tags';

const DETECTION_ENGINE_URL = '/api/detection_engine' as const;
const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules` as const;
export const DETECTION_ENGINE_RULES_URL_FIND = `${DETECTION_ENGINE_RULES_URL}/_find` as const;

export function convertRuleTagsToKQL(tags: string[]): string {
return `${TAGS_FIELD}:(${tags.map((tag) => `"${tag}"`).join(' AND ')})`;
}

export const useFetchDetectionRulesByTags = (tags: string[]) => {
const { http } = useKibana<CoreStart>().services;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,16 @@ import type { HttpSetup } from '@kbn/core/public';
import React from 'react';
import { CspFinding } from '../../../../common/schemas/csp_finding';
import { DetectionRuleCounter } from '../../../components/detection_rule_counter';
import {
createDetectionRuleFromFinding,
getFindingsDetectionRuleSearchTags,
} from '../utils/create_detection_rule_from_finding';
import { createDetectionRuleFromFinding } from '../utils/create_detection_rule_from_finding';
import { getFindingsDetectionRuleSearchTags } from '../../../../common/utils/detection_rules';

export const FindingsDetectionRuleCounter = ({ finding }: { finding: CspFinding }) => {
const createMisconfigurationRuleFn = async (http: HttpSetup) =>
await createDetectionRuleFromFinding(http, finding);

return (
<DetectionRuleCounter
tags={getFindingsDetectionRuleSearchTags(finding)}
tags={getFindingsDetectionRuleSearchTags(finding.rule)}
createRuleFn={createMisconfigurationRuleFn}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LATEST_FINDINGS_RETENTION_POLICY,
} from '../../../../common/constants';
import { createDetectionRule } from '../../../common/api/create_detection_rule';
import { generateBenchmarkRuleTags } from '../../../../common/utils/detection_rules';

const DEFAULT_RULE_RISK_SCORE = 0;
const DEFAULT_RULE_SEVERITY = 'low';
Expand Down Expand Up @@ -47,43 +48,6 @@ const convertReferencesLinksToArray = (input: string | undefined) => {
return matches.map((link) => link.replace(/^\d+\. /, '').replace(/\n/g, ''));
};

const CSP_RULE_TAG = 'Cloud Security';
const CSP_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit';
const CSP_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: ';

const STATIC_RULE_TAGS = [CSP_RULE_TAG, CSP_RULE_TAG_USE_CASE];

/*
* Returns an array of CspFinding tags that can be used to search and filter a detection rule
*/
export const getFindingsDetectionRuleSearchTags = ({ rule }: CspFinding) => {
// ex: cis_gcp to ['CIS', 'GCP']
const benchmarkIdTags = rule.benchmark.id.split('_').map((tag) => tag.toUpperCase());
// ex: 'CIS GCP 1.1'
const benchmarkRuleNumberTag = `${rule.benchmark.id.replace('_', ' ').toUpperCase()} ${
rule.benchmark.rule_number
}`;

return benchmarkIdTags.concat([benchmarkRuleNumberTag]);
};

const generateFindingsTags = (finding: CspFinding) => {
return [STATIC_RULE_TAGS]
.concat(getFindingsDetectionRuleSearchTags(finding))
.concat(
finding.rule.benchmark.posture_type
? [
finding.rule.benchmark.posture_type.toUpperCase(),
`${CSP_RULE_TAG_DATA_SOURCE_PREFIX}${finding.rule.benchmark.posture_type.toUpperCase()}`,
]
: []
)
.concat(
finding.rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container']
)
.flat();
};

const generateFindingsRuleQuery = (finding: CspFinding) => {
const currentTimestamp = new Date().toISOString();

Expand Down Expand Up @@ -128,7 +92,7 @@ export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: C
references: convertReferencesLinksToArray(finding.rule.references),
name: finding.rule.name,
description: finding.rule.rationale,
tags: generateFindingsTags(finding),
tags: generateBenchmarkRuleTags(finding.rule),
investigation_fields: DEFAULT_INVESTIGATION_FIELDS,
note: finding.rule.remediation,
},
Expand Down

This file was deleted.

Loading

0 comments on commit faa3d8f

Please sign in to comment.