Skip to content

Commit

Permalink
Add server side license check for alert suppression
Browse files Browse the repository at this point in the history
  • Loading branch information
marshallmain committed Nov 9, 2022
1 parent 079e934 commit 1b45b4b
Show file tree
Hide file tree
Showing 29 changed files with 309 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ export const AlertSuppression = t.exact(
group_by: AlertSuppressionGroupBy,
})
);

export const minimumLicenseForSuppression = 'platinum';
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { DocLink } from '../../../../common/components/links_to_docs/doc_link';
import { defaultCustomQuery } from '../../../pages/detection_engine/rules/utils';
import { getIsRulePreviewDisabled } from '../rule_preview/helpers';
import { GroupByFields } from '../group_by_fields';
import { useLicense } from '../../../../common/hooks/use_license';

const CommonUseField = getUseField({ component: Field });

Expand Down Expand Up @@ -134,6 +135,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
const [indexModified, setIndexModified] = useState(false);
const [threatIndexModified, setThreatIndexModified] = useState(false);
const [dataViewTitle, setDataViewTitle] = useState<string>();
const license = useLicense();

const { form } = useForm<DefineStepRule>({
defaultValue: initialState,
Expand Down Expand Up @@ -763,7 +765,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
}}
/>
</RuleTypeEuiFormRow>
<RuleTypeEuiFormRow $isVisible={isQueryRule(ruleType)} fullWidth>
<RuleTypeEuiFormRow $isVisible={license.isPlatinumPlus()}>
<UseField
path="groupByFields"
component={GroupByFields}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,19 @@ export const getPackagePolicyCreateCallback = (
request
);

const license = licenseService.getLicenseInformation();
// perform these operations in parallel in order to help in not delaying the API response too much
const [, manifestValue] = await Promise.all([
// Install Detection Engine prepackaged rules
exceptionsClient &&
license &&
installPrepackagedRules({
logger,
context: securitySolutionContext,
request,
alerts,
exceptionsClient,
license,
}),

// create the Artifact Manifest for this policy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import type { ILicense } from '@kbn/licensing-plugin/server';
import type { KibanaRequest, Logger } from '@kbn/core/server';
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
import type { PluginStartContract as AlertsStartContract } from '@kbn/alerting-plugin/server';
Expand All @@ -18,6 +19,7 @@ export interface InstallPrepackagedRulesProps {
request: KibanaRequest;
alerts: AlertsStartContract;
exceptionsClient: ExceptionListClient;
license: ILicense;
}

/**
Expand All @@ -30,6 +32,7 @@ export const installPrepackagedRules = async ({
request,
alerts,
exceptionsClient,
license,
}: InstallPrepackagedRulesProps): Promise<void> => {
// Create detection index & rules (if necessary). move past any failure, this is just a convenience
try {
Expand All @@ -48,6 +51,7 @@ export const installPrepackagedRules = async ({
await createPrepackagedRules(
context,
alerts.getRulesClientWithRequest(request),
license,
exceptionsClient
);
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
import {
getEmptyFindResult,
addPrepackagedRulesRequest,
Expand Down Expand Up @@ -84,6 +85,9 @@ describe('add_prepackaged_rules_route', () => {
let server: ReturnType<typeof serverMock.create>;
let { clients, context } = requestContextMock.createTools();
let mockExceptionsClient: ExceptionListClient;
const basicLicense = licensingMock.createLicense({
license: { status: 'active', type: 'basic' },
});

beforeEach(() => {
server = serverMock.create();
Expand Down Expand Up @@ -245,6 +249,7 @@ describe('add_prepackaged_rules_route', () => {
await createPrepackagedRules(
context.securitySolution,
clients.rulesClient,
basicLicense,
mockExceptionsClient
);

Expand All @@ -258,6 +263,7 @@ describe('add_prepackaged_rules_route', () => {
await createPrepackagedRules(
context.securitySolution,
clients.rulesClient,
basicLicense,
mockExceptionsClient
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import moment from 'moment';
import type { ILicense } from '@kbn/licensing-plugin/server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { validate } from '@kbn/securitysolution-io-ts-utils';
import type { RulesClient } from '@kbn/alerting-plugin/server';
Expand Down Expand Up @@ -53,11 +54,14 @@ export const installPrebuiltRulesAndTimelinesRoute = (router: SecuritySolutionPl
const siemResponse = buildSiemResponse(response);

try {
const rulesClient = (await context.alerting).getRulesClient();
const ctx = await context.resolve(['core', 'securitySolution', 'licensing', 'alerting']);
const rulesClient = ctx.alerting.getRulesClient();
const license = ctx.licensing.license;

const validated = await createPrepackagedRules(
await context.securitySolution,
ctx.securitySolution,
rulesClient,
license,
undefined
);
return response.ok({ body: validated ?? {} });
Expand All @@ -83,6 +87,7 @@ export class PrepackagedRulesError extends Error {
export const createPrepackagedRules = async (
context: SecuritySolutionApiRequestHandlerContext,
rulesClient: RulesClient,
license: ILicense,
exceptionsClient?: ExceptionListClient
): Promise<InstallPrebuiltRulesAndTimelinesResponse | null> => {
const config = context.getConfig();
Expand Down Expand Up @@ -116,7 +121,7 @@ export const createPrepackagedRules = async (
const rulesToInstall = getRulesToInstall(latestPrepackagedRulesMap, installedPrePackagedRules);
const rulesToUpdate = getRulesToUpdate(latestPrepackagedRulesMap, installedPrePackagedRules);

await createPrebuiltRules(rulesClient, rulesToInstall);
await createPrebuiltRules(rulesClient, rulesToInstall, license);

const timeline = await installPrepackagedTimelines(
maxTimelineImportExportSize,
Expand All @@ -132,7 +137,8 @@ export const createPrepackagedRules = async (
rulesClient,
savedObjectsClient,
rulesToUpdate,
context.getRuleExecutionLog()
context.getRuleExecutionLog(),
license
);

const prepackagedRulesOutput: InstallPrebuiltRulesAndTimelinesResponse = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
*/

import type { RulesClient } from '@kbn/alerting-plugin/server';
import type { ILicense } from '@kbn/licensing-plugin/server';
import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../common/constants';
import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules';
import { initPromisePool } from '../../../../utils/promise_pool';
import { withSecuritySpan } from '../../../../utils/with_security_span';
import { createRules } from '../../rule_management/logic/crud/create_rules';

export const createPrebuiltRules = (rulesClient: RulesClient, rules: PrebuiltRuleToInstall[]) =>
export const createPrebuiltRules = (
rulesClient: RulesClient,
rules: PrebuiltRuleToInstall[],
license: ILicense
) =>
withSecuritySpan('createPrebuiltRules', async () => {
const result = await initPromisePool({
concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL,
Expand All @@ -23,6 +28,7 @@ export const createPrebuiltRules = (rulesClient: RulesClient, rules: PrebuiltRul
params: rule,
immutable: true,
defaultEnabled: false,
license,
});
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks';
import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { getRuleMock, getFindResultWithSingleHit } from '../../routes/__mocks__/request_responses';
import { updatePrebuiltRules } from './update_prebuilt_rules';
Expand Down Expand Up @@ -34,6 +35,9 @@ describe('updatePrebuiltRules', () => {
let rulesClient: ReturnType<typeof rulesClientMock.create>;
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let ruleExecutionLog: ReturnType<typeof ruleExecutionLogMock.forRoutes.create>;
const basicLicense = licensingMock.createLicense({
license: { status: 'active', type: 'basic' },
});

beforeEach(() => {
rulesClient = rulesClientMock.create();
Expand All @@ -59,7 +63,8 @@ describe('updatePrebuiltRules', () => {
rulesClient,
savedObjectsClient,
[{ ...prepackagedRule, actions }],
ruleExecutionLog
ruleExecutionLog,
basicLicense
);

expect(patchRules).toHaveBeenCalledWith(
Expand Down Expand Up @@ -96,7 +101,8 @@ describe('updatePrebuiltRules', () => {
rulesClient,
savedObjectsClient,
[{ ...prepackagedRule, ...updatedThreatParams }],
ruleExecutionLog
ruleExecutionLog,
basicLicense
);

expect(patchRules).toHaveBeenCalledWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { chunk } from 'lodash/fp';
import type { SavedObjectsClientContract } from '@kbn/core/server';
import type { RulesClient, PartialRule } from '@kbn/alerting-plugin/server';
import type { ILicense } from '@kbn/licensing-plugin/server';

import type { PrebuiltRuleToInstall } from '../../../../../common/detection_engine/prebuilt_rules';
import { transformAlertToRuleAction } from '../../../../../common/detection_engine/transform_actions';
Expand Down Expand Up @@ -36,15 +37,17 @@ export const updatePrebuiltRules = async (
rulesClient: RulesClient,
savedObjectsClient: SavedObjectsClientContract,
rules: PrebuiltRuleToInstall[],
ruleExecutionLog: IRuleExecutionLogForRoutes
ruleExecutionLog: IRuleExecutionLogForRoutes,
license: ILicense
): Promise<void> => {
const ruleChunks = chunk(MAX_RULES_TO_UPDATE_IN_PARALLEL, rules);
for (const ruleChunk of ruleChunks) {
const rulePromises = createPromises(
rulesClient,
savedObjectsClient,
ruleChunk,
ruleExecutionLog
ruleExecutionLog,
license
);
await Promise.all(rulePromises);
}
Expand All @@ -61,7 +64,8 @@ const createPromises = (
rulesClient: RulesClient,
savedObjectsClient: SavedObjectsClientContract,
rules: PrebuiltRuleToInstall[],
ruleExecutionLog: IRuleExecutionLogForRoutes
ruleExecutionLog: IRuleExecutionLogForRoutes,
license: ILicense
): Array<Promise<PartialRule<RuleParams> | null>> => {
return rules.map(async (rule) => {
const existingRule = await readRules({
Expand Down Expand Up @@ -99,6 +103,7 @@ const createPromises = (
enabled: migratedRule.enabled,
actions: migratedRule.actions.map(transformAlertToRuleAction),
},
license,
});
} else {
return patchRules({
Expand All @@ -110,6 +115,7 @@ const createPromises = (
enabled: undefined,
actions: undefined,
},
license,
});
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { fold } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';
import { identity } from 'fp-ts/lib/function';

import type { ILicense } from '@kbn/licensing-plugin/server';
import { transformError } from '@kbn/securitysolution-es-utils';
import type {
CreateExceptionListSchema,
Expand Down Expand Up @@ -77,6 +78,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter)
]);
const rulesClient = ctx.alerting.getRulesClient();
const listsClient = ctx.securitySolution.getExceptionListClient();
const license = ctx.licensing.license;

const { items } = request.body;
const { id: ruleId } = request.params;
Expand Down Expand Up @@ -131,6 +133,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter)
rulesClient,
listsClient,
removeOldAssociation: true,
license,
});

createdItems = await createExceptionListItems({ items, defaultList, listsClient });
Expand All @@ -141,6 +144,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter)
rulesClient,
listsClient,
removeOldAssociation: false,
license,
});

createdItems = await createExceptionListItems({ items, defaultList, listsClient });
Expand Down Expand Up @@ -196,11 +200,13 @@ export const createAndAssociateDefaultExceptionList = async ({
listsClient,
rulesClient,
removeOldAssociation,
license,
}: {
rule: SanitizedRule<RuleParams>;
listsClient: ExceptionListClient | null;
rulesClient: RulesClient;
removeOldAssociation: boolean;
license: ILicense;
}): Promise<ExceptionListSchema> => {
const exceptionList: CreateExceptionListSchema = {
description: `Exception list containing exceptions for rule with id: ${rule.id}`,
Expand Down Expand Up @@ -272,6 +278,7 @@ export const createAndAssociateDefaultExceptionList = async ({
},
],
},
license,
});

return exceptionListAssociatedToRule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ export const bulkCreateRulesRoute = (

const rulesClient = ctx.alerting.getRulesClient();
const savedObjectsClient = ctx.core.savedObjects.client;
const license = ctx.licensing.license;

const mlAuthz = buildMlAuthz({
license: ctx.licensing.license,
license,
ml,
request,
savedObjectsClient,
Expand Down Expand Up @@ -104,6 +105,7 @@ export const bulkCreateRulesRoute = (
const createdRule = await createRules({
rulesClient,
params: payloadRule,
license,
});

return transformValidateBulkError(createdRule.params.ruleId, createdRule, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ export const bulkPatchRulesRoute = (
const rulesClient = ctx.alerting.getRulesClient();
const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog();
const savedObjectsClient = ctx.core.savedObjects.client;
const license = ctx.licensing.license;

const mlAuthz = buildMlAuthz({
license: ctx.licensing.license,
license,
ml,
request,
savedObjectsClient,
Expand Down Expand Up @@ -93,6 +94,7 @@ export const bulkPatchRulesRoute = (
existingRule: migratedRule,
rulesClient,
nextParams: payloadRule,
license,
});
if (rule != null && rule.enabled != null && rule.name != null) {
const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id);
Expand Down
Loading

0 comments on commit 1b45b4b

Please sign in to comment.