Skip to content

Commit

Permalink
[Cloud Security] Get rules state API
Browse files Browse the repository at this point in the history
  • Loading branch information
CohenIdo authored Dec 28, 2023
1 parent 445b757 commit 03726cf
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 20 deletions.
4 changes: 4 additions & 0 deletions x-pack/plugins/cloud_security_posture/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export const CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH =
'/internal/cloud_security_posture/rules/_bulk_action';
export const CSP_BENCHMARK_RULES_BULK_ACTION_API_CURRENT_VERSION = '1';

export const CSP_GET_BENCHMARK_RULES_STATE_ROUTE_PATH =
'/internal/cloud_security_posture/rules/_get_states';
export const CSP_GET_BENCHMARK_RULES_STATE_API_CURRENT_VERSION = '1';

export const GET_DETECTION_RULE_ALERTS_STATUS_PATH =
'/internal/cloud_security_posture/detection_engine_rules/alerts/_status';
export const DETECTION_RULE_ALERTS_STATUS_API_CURRENT_VERSION = '1';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,21 @@ export interface FindCspBenchmarkRuleResponse {

export type PageUrlParams = Record<'policyId' | 'packagePolicyId', string>;

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

export const cspBenchmarkRulesBulkActionRequestSchema = schema.object({
action: schema.oneOf([schema.literal('mute'), schema.literal('unmute')]),
rules: cspBenchmarkRules,
rules: rulesToUpdate,
});

export type CspBenchmarkRules = TypeOf<typeof cspBenchmarkRules>;
export type RulesToUpdate = TypeOf<typeof rulesToUpdate>;

export type CspBenchmarkRulesBulkActionRequestSchema = TypeOf<
typeof cspBenchmarkRulesBulkActionRequestSchema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import { bulkActionBenchmarkRulesHandler } from './v1';
action: 'mute' | 'unmute'; // Specify the bulk action type (mute or unmute)
rules: [
{
benchmark_id: string; // Identifier for the CSP benchmark
benchmark_version: string; // Version of the CSP benchmark
rule_number: string; // Rule number within the benchmark
rule_id: string; // Unique identifier for the rule
},
// ... (additional benchmark rules)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { FindResult, RulesClient } from '@kbn/alerting-plugin/server';
import type { RuleParams } from '@kbn/alerting-plugin/server/application/rule/types';
import type {
CspBenchmarkRule,
RulesToUpdate,
CspBenchmarkRulesStates,
CspSettings,
} from '../../../../common/types/rules/v3';
Expand Down Expand Up @@ -118,23 +119,22 @@ export const updateRulesStates = async (
export const setRulesStates = (
ruleIds: string[],
state: boolean,
benchmarkRules: CspBenchmarkRule[]
rulesToUpdate: RulesToUpdate
): CspBenchmarkRulesStates => {
const rulesStates: CspBenchmarkRulesStates = {};
ruleIds.forEach((ruleId, index) => {
const benchmarkRule = benchmarkRules[index];
const benchmarkRule = rulesToUpdate[index];
rulesStates[ruleId] = {
muted: state,
benchmark_id: benchmarkRule.metadata.benchmark.id,
benchmark_version: benchmarkRule.metadata.benchmark.version,
rule_number: benchmarkRule.metadata.benchmark.rule_number || '',
rule_id: benchmarkRule.metadata.id,
benchmark_id: benchmarkRule.benchmark_id,
benchmark_version: benchmarkRule.benchmark_version,
rule_number: benchmarkRule.rule_number,
rule_id: benchmarkRule.rule_id,
};
});
return rulesStates;
};

export const buildRuleKey = (benchmarkRule: CspBenchmarkRule) => {
const ruleNumber = benchmarkRule.metadata.benchmark.rule_number;
return `${benchmarkRule.metadata.benchmark.id};${benchmarkRule.metadata.benchmark.version};${ruleNumber}`;
export const buildRuleKey = (benchmarkId: string, benchmarkVersion: string, ruleNumber: string) => {
return `${benchmarkId};${benchmarkVersion};${ruleNumber}`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {
} from './utils';
import type {
BulkActionBenchmarkRulesResponse,
CspBenchmarkRule,
CspBenchmarkRules,
RulesToUpdate,
} from '../../../../common/types/rules/v3';

const muteStatesMap = {
Expand All @@ -29,7 +28,7 @@ export const bulkActionBenchmarkRulesHandler = async (
soClient: SavedObjectsClientContract,
encryptedSoClient: SavedObjectsClientContract,
detectionRulesClient: RulesClient,
rulesToUpdate: CspBenchmarkRules,
rulesToUpdate: RulesToUpdate,
action: 'mute' | 'unmute',
logger: Logger
): Promise<BulkActionBenchmarkRulesResponse> => {
Expand All @@ -39,13 +38,12 @@ export const bulkActionBenchmarkRulesHandler = async (
if (benchmarkRules.includes(undefined))
throw new Error('At least one of the provided benchmark rule IDs does not exist');

const rulesKeys = benchmarkRules.map((benchmarkRule) => buildRuleKey(benchmarkRule!));
const newRulesStates = setRulesStates(
rulesKeys,
muteStatesMap[action],
benchmarkRules as CspBenchmarkRule[]
const rulesKeys = rulesToUpdate.map((rule) =>
buildRuleKey(rule.benchmark_id, rule.benchmark_version, rule.rule_number)
);

const newRulesStates = setRulesStates(rulesKeys, muteStatesMap[action], rulesToUpdate);

const newCspSettings = await updateRulesStates(encryptedSoClient, newRulesStates);
const disabledRulesCounter =
action === 'mute' ? await muteDetectionRules(soClient, detectionRulesClient, rulesIds) : 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { transformError } from '@kbn/securitysolution-es-utils';
import { CspRouter } from '../../../types';
import { CSP_GET_BENCHMARK_RULES_STATE_ROUTE_PATH } from '../../../../common/constants';
import { CspBenchmarkRulesStates } from '../../../../common/types/rules/v3';
import { getCspBenchmarkRulesStatesHandler } from './v1';

export const defineGetCspBenchmarkRulesStatesRoute = (router: CspRouter) =>
router.versioned
.get({
access: 'internal',
path: CSP_GET_BENCHMARK_RULES_STATE_ROUTE_PATH,
})
.addVersion(
{
version: '1',
validate: {},
},
async (context, request, response) => {
if (!(await context.fleet).authz.fleet.all) {
return response.forbidden();
}
const cspContext = await context.csp;

try {
const encryptedSoClient = cspContext.encryptedSavedObjects;

const rulesStates: CspBenchmarkRulesStates = await getCspBenchmarkRulesStatesHandler(
encryptedSoClient
);

return response.ok({
body: rulesStates,
});
} catch (err) {
const error = transformError(err);

cspContext.logger.error(`Failed to fetch CSP benchmark rules state: ${error.message}`);
return response.customError({
body: { message: error.message },
statusCode: error.statusCode || 500, // Default to 500 if no specific status code is provided
});
}
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { CspBenchmarkRulesStates, CspSettings } from '../../../../common/types/rules/v3';
import {
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID,
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE,
} from '../../../../common/constants';

export const createCspSettingObject = async (soClient: SavedObjectsClientContract) => {
return soClient.create<CspSettings>(
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE,
{
rules: {},
},
{ id: INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID }
);
};

export const getCspBenchmarkRulesStatesHandler = async (
encryptedSoClient: SavedObjectsClientContract
): Promise<CspBenchmarkRulesStates> => {
try {
const getSoResponse = await encryptedSoClient.get<CspSettings>(
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE,
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID
);
return getSoResponse.attributes.rules;
} catch (err) {
const error = transformError(err);
if (error.statusCode === 404) {
const newCspSettings = await createCspSettingObject(encryptedSoClient);
return newCspSettings.attributes.rules;
}

throw new Error(
`An error occurred while trying to fetch csp settings: ${error.message}, ${error.statusCode}`
);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { defineGetCspStatusRoute } from './status/status';
import { defineFindCspBenchmarkRuleRoute } from './benchmark_rules/find/find';
import { defineGetDetectionEngineAlertsStatus } from './detection_engine/get_detection_engine_alerts_count_by_rule_tags';
import { defineBulkActionCspBenchmarkRulesRoute } from './benchmark_rules/bulk_action/bulk_action';
import { defineGetCspBenchmarkRulesStatesRoute } from './benchmark_rules/get_states/get_states';

/**
* 1. Registers routes
Expand All @@ -43,6 +44,7 @@ export async function setupRoutes({
defineFindCspBenchmarkRuleRoute(router);
defineGetDetectionEngineAlertsStatus(router);
defineBulkActionCspBenchmarkRulesRoute(router);
defineGetCspBenchmarkRulesStatesRoute(router);

core.http.registerRouteHandlerContext<CspRequestHandlerContext, typeof PLUGIN_ID>(
PLUGIN_ID,
Expand Down
1 change: 1 addition & 0 deletions x-pack/test/cloud_security_posture_api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
require.resolve('./routes/vulnerabilities_dashboard.ts'),
require.resolve('./routes/stats.ts'),
require.resolve('./routes/csp_benchmark_rules_bulk_update.ts'),
require.resolve('./routes/csp_benchmark_rules_get_states.ts'),
],
junit: {
reportName: 'X-Pack Cloud Security Posture API Tests',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,15 @@ export default function ({ getService }: FtrProviderContext) {
action: 'mute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule2.metadata.benchmark.id,
benchmark_version: rule2.metadata.benchmark.version,
rule_number: rule2.metadata.benchmark.rule_number || '',
rule_id: rule2.metadata.id,
},
],
Expand Down Expand Up @@ -162,9 +168,15 @@ export default function ({ getService }: FtrProviderContext) {
action: 'unmute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule2.metadata.benchmark.id,
benchmark_version: rule2.metadata.benchmark.version,
rule_number: rule2.metadata.benchmark.rule_number || '',
rule_id: rule2.metadata.id,
},
],
Expand Down Expand Up @@ -210,9 +222,15 @@ export default function ({ getService }: FtrProviderContext) {
action: 'unmute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule2.metadata.benchmark.id,
benchmark_version: rule2.metadata.benchmark.version,
rule_number: rule2.metadata.benchmark.rule_number || '',
rule_id: rule2.metadata.id,
},
],
Expand Down Expand Up @@ -252,9 +270,15 @@ export default function ({ getService }: FtrProviderContext) {
action: 'mute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule3.metadata.benchmark.id,
benchmark_version: rule3.metadata.benchmark.version,
rule_number: rule3.metadata.benchmark.rule_number || '',
rule_id: rule3.metadata.id,
},
],
Expand Down Expand Up @@ -299,6 +323,9 @@ export default function ({ getService }: FtrProviderContext) {
action: 'mute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
],
Expand All @@ -323,9 +350,15 @@ export default function ({ getService }: FtrProviderContext) {
action: 'mute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule2.metadata.benchmark.id,
benchmark_version: rule2.metadata.benchmark.version,
rule_number: rule2.metadata.benchmark.rule_number || '',
rule_id: rule2.metadata.id,
},
],
Expand All @@ -347,6 +380,9 @@ export default function ({ getService }: FtrProviderContext) {
action: 'foo',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
],
Expand Down
Loading

0 comments on commit 03726cf

Please sign in to comment.