Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEST] Security Solution Redeploy #199056

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/
import { pickBy } from 'lodash';
import { withSyncSecuritySpan } from '../../../../../utils/with_security_span';
import type { PromisePoolError } from '../../../../../utils/promise_pool';
import {
PickVersionValuesEnum,
Expand Down Expand Up @@ -36,77 +37,82 @@ export const createModifiedPrebuiltRuleAssets = ({
upgradeableRules,
requestBody,
}: CreateModifiedPrebuiltRuleAssetsProps) => {
const { pick_version: globalPickVersion = PickVersionValuesEnum.MERGED, mode } = requestBody;

const { modifiedPrebuiltRuleAssets, processingErrors } = upgradeableRules.reduce<ProcessedRules>(
(processedRules, upgradeableRule) => {
const targetRuleType = upgradeableRule.target.type;
const ruleId = upgradeableRule.target.rule_id;
const fieldNames = FIELD_NAMES_BY_RULE_TYPE_MAP.get(targetRuleType);

try {
if (fieldNames === undefined) {
throw new Error(`Unexpected rule type: ${targetRuleType}`);
}

const { current, target } = upgradeableRule;
if (current.type !== target.type) {
assertPickVersionIsTarget({ ruleId, requestBody });
}

const calculatedRuleDiff = calculateRuleFieldsDiff({
base_version: upgradeableRule.base
? convertRuleToDiffable(convertPrebuiltRuleAssetToRuleResponse(upgradeableRule.base))
: MissingVersion,
current_version: convertRuleToDiffable(upgradeableRule.current),
target_version: convertRuleToDiffable(
convertPrebuiltRuleAssetToRuleResponse(upgradeableRule.target)
),
}) as AllFieldsDiff;

if (mode === 'ALL_RULES' && globalPickVersion === 'MERGED') {
const fieldsWithConflicts = Object.keys(getFieldsDiffConflicts(calculatedRuleDiff));
if (fieldsWithConflicts.length > 0) {
// If the mode is ALL_RULES, no fields can be overriden to any other pick_version
// than "MERGED", so throw an error for the fields that have conflicts.
throw new Error(
`Merge conflicts found in rule '${ruleId}' for fields: ${fieldsWithConflicts.join(
', '
)}. Please resolve the conflict manually or choose another value for 'pick_version'`
);
return withSyncSecuritySpan('createModifiedPrebuiltRuleAssets', () => {
const { pick_version: globalPickVersion = PickVersionValuesEnum.MERGED, mode } = requestBody;

const { modifiedPrebuiltRuleAssets, processingErrors } =
upgradeableRules.reduce<ProcessedRules>(
(processedRules, upgradeableRule) => {
const targetRuleType = upgradeableRule.target.type;
const ruleId = upgradeableRule.target.rule_id;
const fieldNames = FIELD_NAMES_BY_RULE_TYPE_MAP.get(targetRuleType);

try {
if (fieldNames === undefined) {
throw new Error(`Unexpected rule type: ${targetRuleType}`);
}

const { current, target } = upgradeableRule;
if (current.type !== target.type) {
assertPickVersionIsTarget({ ruleId, requestBody });
}

const calculatedRuleDiff = calculateRuleFieldsDiff({
base_version: upgradeableRule.base
? convertRuleToDiffable(
convertPrebuiltRuleAssetToRuleResponse(upgradeableRule.base)
)
: MissingVersion,
current_version: convertRuleToDiffable(upgradeableRule.current),
target_version: convertRuleToDiffable(
convertPrebuiltRuleAssetToRuleResponse(upgradeableRule.target)
),
}) as AllFieldsDiff;

if (mode === 'ALL_RULES' && globalPickVersion === 'MERGED') {
const fieldsWithConflicts = Object.keys(getFieldsDiffConflicts(calculatedRuleDiff));
if (fieldsWithConflicts.length > 0) {
// If the mode is ALL_RULES, no fields can be overriden to any other pick_version
// than "MERGED", so throw an error for the fields that have conflicts.
throw new Error(
`Merge conflicts found in rule '${ruleId}' for fields: ${fieldsWithConflicts.join(
', '
)}. Please resolve the conflict manually or choose another value for 'pick_version'`
);
}
}

const modifiedPrebuiltRuleAsset = createModifiedPrebuiltRuleAsset({
upgradeableRule,
fieldNames,
requestBody,
globalPickVersion,
calculatedRuleDiff,
});

processedRules.modifiedPrebuiltRuleAssets.push(modifiedPrebuiltRuleAsset);

return processedRules;
} catch (err) {
processedRules.processingErrors.push({
error: err,
item: { rule_id: ruleId },
});

return processedRules;
}
},
{
modifiedPrebuiltRuleAssets: [],
processingErrors: [],
}
);

const modifiedPrebuiltRuleAsset = createModifiedPrebuiltRuleAsset({
upgradeableRule,
fieldNames,
requestBody,
globalPickVersion,
calculatedRuleDiff,
});

processedRules.modifiedPrebuiltRuleAssets.push(modifiedPrebuiltRuleAsset);

return processedRules;
} catch (err) {
processedRules.processingErrors.push({
error: err,
item: { rule_id: ruleId },
});

return processedRules;
}
},
{
modifiedPrebuiltRuleAssets: [],
processingErrors: [],
}
);

return {
modifiedPrebuiltRuleAssets,
processingErrors,
};
return {
modifiedPrebuiltRuleAssets,
processingErrors,
};
});
};

interface CreateModifiedPrebuiltRuleAssetParams {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { withSyncSecuritySpan } from '../../../../../utils/with_security_span';
import type {
RuleResponse,
RuleUpgradeSpecifier,
Expand All @@ -26,58 +26,60 @@ export const getUpgradeableRules = ({
versionSpecifiers?: RuleUpgradeSpecifier[];
mode: Mode;
}) => {
const upgradeableRules = new Map(
rawUpgradeableRules.map((_rule) => [_rule.current.rule_id, _rule])
);
const fetchErrors: Array<PromisePoolError<{ rule_id: string }, Error>> = [];
const skippedRules: SkippedRuleUpgrade[] = [];
return withSyncSecuritySpan('getUpgradeableRules', () => {
const upgradeableRules = new Map(
rawUpgradeableRules.map((_rule) => [_rule.current.rule_id, _rule])
);
const fetchErrors: Array<PromisePoolError<{ rule_id: string }, Error>> = [];
const skippedRules: SkippedRuleUpgrade[] = [];

if (mode === ModeEnum.SPECIFIC_RULES) {
const installedRuleIds = new Set(currentRules.map((rule) => rule.rule_id));
const upgradeableRuleIds = new Set(rawUpgradeableRules.map(({ current }) => current.rule_id));
versionSpecifiers?.forEach((rule) => {
// Check that the requested rule was found
if (!installedRuleIds.has(rule.rule_id)) {
fetchErrors.push({
error: new Error(
`Rule with rule_id "${rule.rule_id}" and version "${rule.version}" not found`
),
item: rule,
});
return;
}
if (mode === ModeEnum.SPECIFIC_RULES) {
const installedRuleIds = new Set(currentRules.map((rule) => rule.rule_id));
const upgradeableRuleIds = new Set(rawUpgradeableRules.map(({ current }) => current.rule_id));
versionSpecifiers?.forEach((rule) => {
// Check that the requested rule was found
if (!installedRuleIds.has(rule.rule_id)) {
fetchErrors.push({
error: new Error(
`Rule with rule_id "${rule.rule_id}" and version "${rule.version}" not found`
),
item: rule,
});
return;
}

// Check that the requested rule is upgradeable
if (!upgradeableRuleIds.has(rule.rule_id)) {
skippedRules.push({
rule_id: rule.rule_id,
reason: SkipRuleUpgradeReasonEnum.RULE_UP_TO_DATE,
});
return;
}
// Check that the requested rule is upgradeable
if (!upgradeableRuleIds.has(rule.rule_id)) {
skippedRules.push({
rule_id: rule.rule_id,
reason: SkipRuleUpgradeReasonEnum.RULE_UP_TO_DATE,
});
return;
}

// Check that rule revisions match (no update slipped in since the user reviewed the list)
const currentRevision = currentRules.find(
(currentRule) => currentRule.rule_id === rule.rule_id
)?.revision;
if (rule.revision !== currentRevision) {
fetchErrors.push({
error: new Error(
`Revision mismatch for rule_id ${rule.rule_id}: expected ${currentRevision}, got ${rule.revision}`
),
item: rule,
});
// Remove the rule from the list of upgradeable rules
if (upgradeableRules.has(rule.rule_id)) {
upgradeableRules.delete(rule.rule_id);
// Check that rule revisions match (no update slipped in since the user reviewed the list)
const currentRevision = currentRules.find(
(currentRule) => currentRule.rule_id === rule.rule_id
)?.revision;
if (rule.revision !== currentRevision) {
fetchErrors.push({
error: new Error(
`Revision mismatch for rule_id ${rule.rule_id}: expected ${currentRevision}, got ${rule.revision}`
),
item: rule,
});
// Remove the rule from the list of upgradeable rules
if (upgradeableRules.has(rule.rule_id)) {
upgradeableRules.delete(rule.rule_id);
}
}
}
});
}
});
}

return {
upgradeableRules: Array.from(upgradeableRules.values()),
fetchErrors,
skippedRules,
};
return {
upgradeableRules: Array.from(upgradeableRules.values()),
fetchErrors,
skippedRules,
};
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import type { SpanOptions } from '@kbn/apm-utils';
import { withSpan } from '@kbn/apm-utils';
import type agent from 'elastic-apm-node';
import agent from 'elastic-apm-node';
import { APP_ID } from '../../common/constants';

type Span = Exclude<typeof agent.currentSpan, undefined | null>;
Expand Down Expand Up @@ -35,3 +35,16 @@ export const withSecuritySpan = <T>(
},
cb
);

export const withSyncSecuritySpan = <T>(name: string, fn: (span: Span | null) => T): T => {
const span = agent.startSpan(name, APP_ID);

try {
const result = fn(span);
return result;
} finally {
if (span) {
span.end();
}
}
};