mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
## Summary [Internal link](https://github.com/elastic/security-team/issues/10820) to the feature details Part of https://github.com/elastic/security-team/issues/11232 This PR covers SIEM Migrations GET API (route: `/internal/siem_migrations/rules/{migration_id}`) integration test: * Basic calls * Filtering * Sorting * Pagination
This commit is contained in:
parent
baadf59aa2
commit
526ff0516e
2 changed files with 749 additions and 43 deletions
|
@ -8,9 +8,17 @@
|
|||
import expect from 'expect';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import {
|
||||
RuleTranslationResult,
|
||||
SiemMigrationStatus,
|
||||
} from '@kbn/security-solution-plugin/common/siem_migrations/constants';
|
||||
import {
|
||||
RuleMigrationDocument,
|
||||
createMigrationRules,
|
||||
defaultElasticRule,
|
||||
defaultOriginalRule,
|
||||
deleteAllMigrationRules,
|
||||
getMigrationRuleDocument,
|
||||
getMigrationRuleDocuments,
|
||||
migrationRulesRouteHelpersFactory,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
@ -25,18 +33,652 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await deleteAllMigrationRules(es);
|
||||
});
|
||||
|
||||
it('should fetch existing rules within specified migration', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
const migrationRuleDocument = getMigrationRuleDocument({ migration_id: migrationId });
|
||||
await createMigrationRules(es, [migrationRuleDocument]);
|
||||
describe('Basic', () => {
|
||||
it('should fetch existing rules within specified migration', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
const migrationRuleDocument = getMigrationRuleDocument({ migration_id: migrationId });
|
||||
await createMigrationRules(es, [migrationRuleDocument]);
|
||||
|
||||
const { '@timestamp': timestamp, updated_at: updatedAt, ...rest } = migrationRuleDocument;
|
||||
const { '@timestamp': timestamp, updated_at: updatedAt, ...rest } = migrationRuleDocument;
|
||||
|
||||
// fetch migration rule
|
||||
const response = await migrationRulesRoutes.get(migrationId);
|
||||
expect(response.body.total).toEqual(1);
|
||||
expect(response.body.data).toEqual(expect.arrayContaining([expect.objectContaining(rest)]));
|
||||
// fetch migration rule
|
||||
const response = await migrationRulesRoutes.get({ migrationId });
|
||||
expect(response.body.total).toEqual(1);
|
||||
expect(response.body.data).toEqual(expect.arrayContaining([expect.objectContaining(rest)]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filtering', () => {
|
||||
it('should fetch rules filtered by `searchTerm`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const title = `${index < 5 ? 'Elastic' : 'Splunk'} rule - ${index}`;
|
||||
const originalRule = { ...defaultOriginalRule, title };
|
||||
const elasticRule = { ...defaultElasticRule, title };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
original_rule: originalRule,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// Search by word `Elastic`
|
||||
let expectedRuleDocuments = expect.arrayContaining(
|
||||
migrationRuleDocuments
|
||||
.slice(0, 5)
|
||||
.map(({ '@timestamp': timestamp, updated_at: updatedAt, ...rest }) =>
|
||||
expect.objectContaining(rest)
|
||||
)
|
||||
);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { searchTerm: 'Elastic' },
|
||||
});
|
||||
expect(response.body.total).toEqual(5);
|
||||
expect(response.body.data).toEqual(expectedRuleDocuments);
|
||||
|
||||
// Search by word `Splunk`
|
||||
expectedRuleDocuments = expect.arrayContaining(
|
||||
migrationRuleDocuments
|
||||
.slice(5)
|
||||
.map(({ '@timestamp': timestamp, updated_at: updatedAt, ...rest }) =>
|
||||
expect.objectContaining(rest)
|
||||
)
|
||||
);
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { searchTerm: 'Splunk' },
|
||||
});
|
||||
expect(response.body.total).toEqual(5);
|
||||
expect(response.body.data).toEqual(expectedRuleDocuments);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `ids`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, () => ({
|
||||
migration_id: migrationId,
|
||||
}));
|
||||
const createdDocumentIds = await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
const expectedIds = createdDocumentIds.slice(0, 3).sort();
|
||||
|
||||
// fetch migration rules by existing ids
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { ids: expectedIds },
|
||||
});
|
||||
expect(response.body.total).toEqual(3);
|
||||
expect(response.body.data.map(({ id }) => id).sort()).toEqual(expectedIds);
|
||||
|
||||
// fetch migration rules by non-existing id
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { ids: [uuidv4()] },
|
||||
});
|
||||
expect(response.body.total).toEqual(0);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `prebuilt`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const prebuiltRuleId = index < 3 ? uuidv4() : undefined;
|
||||
const elasticRule = { ...defaultElasticRule, prebuilt_rule_id: prebuiltRuleId };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules matched Elastic prebuilt rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { prebuilt: true },
|
||||
});
|
||||
expect(response.body.total).toEqual(3);
|
||||
|
||||
// fetch custom translated migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { prebuilt: false },
|
||||
});
|
||||
expect(response.body.total).toEqual(7);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `installed`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const installedRuleId = index < 2 ? uuidv4() : undefined;
|
||||
const elasticRule = { ...defaultElasticRule, id: installedRuleId };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch installed migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { installed: true },
|
||||
});
|
||||
expect(response.body.total).toEqual(2);
|
||||
|
||||
// fetch non-installed migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { installed: false },
|
||||
});
|
||||
expect(response.body.total).toEqual(8);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `failed`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const status = index < 4 ? SiemMigrationStatus.FAILED : SiemMigrationStatus.COMPLETED;
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
status,
|
||||
};
|
||||
};
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch failed migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { failed: true },
|
||||
});
|
||||
expect(response.body.total).toEqual(4);
|
||||
|
||||
// fetch non-failed migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { failed: false },
|
||||
});
|
||||
expect(response.body.total).toEqual(6);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `fullyTranslated`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const translationResult =
|
||||
index < 6
|
||||
? RuleTranslationResult.FULL
|
||||
: index < 8
|
||||
? RuleTranslationResult.PARTIAL
|
||||
: RuleTranslationResult.UNTRANSLATABLE;
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
translation_result: translationResult,
|
||||
};
|
||||
};
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch failed migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { fullyTranslated: true },
|
||||
});
|
||||
expect(response.body.total).toEqual(6);
|
||||
|
||||
// fetch non-failed migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { fullyTranslated: false },
|
||||
});
|
||||
expect(response.body.total).toEqual(4);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `partiallyTranslated`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const translationResult =
|
||||
index < 4
|
||||
? RuleTranslationResult.FULL
|
||||
: index < 8
|
||||
? RuleTranslationResult.PARTIAL
|
||||
: RuleTranslationResult.UNTRANSLATABLE;
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
translation_result: translationResult,
|
||||
};
|
||||
};
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch failed migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { partiallyTranslated: true },
|
||||
});
|
||||
expect(response.body.total).toEqual(4);
|
||||
|
||||
// fetch non-failed migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { partiallyTranslated: false },
|
||||
});
|
||||
expect(response.body.total).toEqual(6);
|
||||
});
|
||||
|
||||
it('should fetch rules filtered by `untranslatable`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const translationResult =
|
||||
index < 3
|
||||
? RuleTranslationResult.FULL
|
||||
: index < 5
|
||||
? RuleTranslationResult.PARTIAL
|
||||
: RuleTranslationResult.UNTRANSLATABLE;
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
translation_result: translationResult,
|
||||
};
|
||||
};
|
||||
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(10, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch failed migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { untranslatable: true },
|
||||
});
|
||||
expect(response.body.total).toEqual(5);
|
||||
|
||||
// fetch non-failed migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
filters: { untranslatable: false },
|
||||
});
|
||||
expect(response.body.total).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sorting', () => {
|
||||
it('should fetch rules sorted by `title`', async () => {
|
||||
const titles = ['Elastic 1', 'Windows', 'Linux', 'Elastic 2'];
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const title = titles[index];
|
||||
const originalRule = { ...defaultOriginalRule, title };
|
||||
const elasticRule = { ...defaultElasticRule, title };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
original_rule: originalRule,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(titles.length, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.title',
|
||||
sortDirection: 'asc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.title)).toEqual(titles.sort());
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.title',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.title)).toEqual(
|
||||
titles.sort().reverse()
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch rules sorted by `severity`', async () => {
|
||||
const severities = ['critical', 'low', 'medium', 'low', 'critical'];
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const severity = severities[index];
|
||||
const elasticRule = { ...defaultElasticRule, severity };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(
|
||||
severities.length,
|
||||
overrideCallback
|
||||
);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.severity',
|
||||
sortDirection: 'asc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.severity)).toEqual([
|
||||
'low',
|
||||
'low',
|
||||
'medium',
|
||||
'critical',
|
||||
'critical',
|
||||
]);
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.severity',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.severity)).toEqual([
|
||||
'critical',
|
||||
'critical',
|
||||
'medium',
|
||||
'low',
|
||||
'low',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fetch rules sorted by `risk_score`', async () => {
|
||||
const riskScores = [55, 0, 100, 23];
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const riskScore = riskScores[index];
|
||||
const elasticRule = { ...defaultElasticRule, risk_score: riskScore };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(
|
||||
riskScores.length,
|
||||
overrideCallback
|
||||
);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.risk_score',
|
||||
sortDirection: 'asc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.risk_score)).toEqual(
|
||||
riskScores.sort((a, b) => {
|
||||
return a - b;
|
||||
})
|
||||
);
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.risk_score',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.risk_score)).toEqual(
|
||||
riskScores
|
||||
.sort((a, b) => {
|
||||
return a - b;
|
||||
})
|
||||
.reverse()
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch rules sorted by `prebuilt_rule_id`', async () => {
|
||||
const prebuiltRuleIds = ['rule-1', undefined, undefined, 'rule-2', undefined];
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const prebuiltRuleId = prebuiltRuleIds[index];
|
||||
const elasticRule = { ...defaultElasticRule, prebuilt_rule_id: prebuiltRuleId };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(
|
||||
prebuiltRuleIds.length,
|
||||
overrideCallback
|
||||
);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.prebuilt_rule_id',
|
||||
sortDirection: 'asc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.prebuilt_rule_id)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
'rule-1',
|
||||
'rule-2',
|
||||
]);
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'elastic_rule.prebuilt_rule_id',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.prebuilt_rule_id)).toEqual([
|
||||
'rule-2',
|
||||
'rule-1',
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fetch rules sorted by `translation_result`', async () => {
|
||||
const translationResults = [
|
||||
RuleTranslationResult.UNTRANSLATABLE,
|
||||
RuleTranslationResult.FULL,
|
||||
RuleTranslationResult.PARTIAL,
|
||||
];
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
translation_result: translationResults[index],
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(
|
||||
translationResults.length,
|
||||
overrideCallback
|
||||
);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'translation_result',
|
||||
sortDirection: 'asc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.translation_result)).toEqual([
|
||||
RuleTranslationResult.UNTRANSLATABLE,
|
||||
RuleTranslationResult.PARTIAL,
|
||||
RuleTranslationResult.FULL,
|
||||
]);
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'translation_result',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.translation_result)).toEqual([
|
||||
RuleTranslationResult.FULL,
|
||||
RuleTranslationResult.PARTIAL,
|
||||
RuleTranslationResult.UNTRANSLATABLE,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fetch rules sorted by `updated_at`', async () => {
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
// Creating documents separately to have different `update_at` timestamps
|
||||
await createMigrationRules(es, [getMigrationRuleDocument({ migration_id: migrationId })]);
|
||||
await createMigrationRules(es, [getMigrationRuleDocument({ migration_id: migrationId })]);
|
||||
await createMigrationRules(es, [getMigrationRuleDocument({ migration_id: migrationId })]);
|
||||
await createMigrationRules(es, [getMigrationRuleDocument({ migration_id: migrationId })]);
|
||||
await createMigrationRules(es, [getMigrationRuleDocument({ migration_id: migrationId })]);
|
||||
|
||||
// fetch migration rules
|
||||
let response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'updated_at',
|
||||
sortDirection: 'asc',
|
||||
});
|
||||
const ascSorted = response.body.data.map((rule) => rule.updated_at);
|
||||
|
||||
// fetch migration rules
|
||||
response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
sortField: 'updated_at',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
const descSorted = response.body.data.map((rule) => rule.updated_at);
|
||||
|
||||
expect(ascSorted).toEqual(descSorted.reverse());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pagination', () => {
|
||||
it('should fetch rules within specific page', async () => {
|
||||
const titles = Array.from({ length: 50 }, (_, index) => `Migration rule - ${index}`);
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const title = titles[index];
|
||||
const originalRule = { ...defaultOriginalRule, title };
|
||||
const elasticRule = { ...defaultElasticRule, title };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
original_rule: originalRule,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(titles.length, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
const response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
page: 3,
|
||||
perPage: 7,
|
||||
});
|
||||
const start = 3 * 7;
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.title)).toEqual(
|
||||
titles.slice(start, start + 7)
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch rules within very first page if `perPage` is not specified', async () => {
|
||||
const titles = Array.from({ length: 50 }, (_, index) => `Migration rule - ${index}`);
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const title = titles[index];
|
||||
const originalRule = { ...defaultOriginalRule, title };
|
||||
const elasticRule = { ...defaultElasticRule, title };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
original_rule: originalRule,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(titles.length, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
const response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
page: 3,
|
||||
});
|
||||
const defaultSize = 10;
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.title)).toEqual(
|
||||
titles.slice(0, defaultSize)
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch rules within very first page of a specified size if `perPage` is specified', async () => {
|
||||
const titles = Array.from({ length: 50 }, (_, index) => `Migration rule - ${index}`);
|
||||
|
||||
// create a document
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
const title = titles[index];
|
||||
const originalRule = { ...defaultOriginalRule, title };
|
||||
const elasticRule = { ...defaultElasticRule, title };
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
original_rule: originalRule,
|
||||
elastic_rule: elasticRule,
|
||||
};
|
||||
};
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(titles.length, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
// fetch migration rules
|
||||
const response = await migrationRulesRoutes.get({
|
||||
migrationId,
|
||||
perPage: 18,
|
||||
});
|
||||
expect(response.body.data.map((rule) => rule.elastic_rule?.title)).toEqual(
|
||||
titles.slice(0, 18)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -10,32 +10,51 @@ import type { Client } from '@elastic/elasticsearch';
|
|||
import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common';
|
||||
import { replaceParams } from '@kbn/openapi-common/shared';
|
||||
|
||||
import { RuleMigration } from '@kbn/security-solution-plugin/common/siem_migrations/model/rule_migration.gen';
|
||||
import {
|
||||
ElasticRule,
|
||||
OriginalRule,
|
||||
RuleMigration,
|
||||
} from '@kbn/security-solution-plugin/common/siem_migrations/model/rule_migration.gen';
|
||||
import { INDEX_PATTERN as SIEM_MIGRATIONS_INDEX_PATTERN } from '@kbn/security-solution-plugin/server/lib/siem_migrations/rules/data/rule_migrations_data_service';
|
||||
import { SIEM_RULE_MIGRATION_PATH } from '@kbn/security-solution-plugin/common/siem_migrations/constants';
|
||||
import { GetRuleMigrationResponse } from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { generateAssistantComment } from '@kbn/security-solution-plugin/server/lib/siem_migrations/rules/task/util/comments';
|
||||
import { RuleMigrationFilters } from '@kbn/security-solution-plugin/common/siem_migrations/types';
|
||||
|
||||
const SIEM_MIGRATIONS_RULES_INDEX_PATTERN = `${SIEM_MIGRATIONS_INDEX_PATTERN}-rules-default`;
|
||||
|
||||
export type RuleMigrationDocument = Omit<RuleMigration, 'id'>;
|
||||
|
||||
const migrationRuleDocument: RuleMigrationDocument = {
|
||||
export const defaultOriginalRule: OriginalRule = {
|
||||
id: 'https://127.0.0.1:8089/servicesNS/nobody/SA-AccessProtection/saved/searches/Access%20-%20Default%20Account%20Usage%20-%20Rule',
|
||||
vendor: 'splunk',
|
||||
title: 'Access - Default Account Usage - Rule',
|
||||
description:
|
||||
'Discovers use of default accounts (such as admin, administrator, etc.). Default accounts have default passwords and are therefore commonly targeted by attackers using brute force attack tools.',
|
||||
query:
|
||||
'| from datamodel:"Authentication"."Successful_Default_Authentication" | stats max("_time") as "lastTime",values("tag") as "tag",count by "dest","user","app"',
|
||||
query_language: 'spl',
|
||||
annotations: {
|
||||
mitre_attack: ['T1078'],
|
||||
},
|
||||
};
|
||||
|
||||
export const defaultElasticRule: ElasticRule = {
|
||||
severity: 'low',
|
||||
risk_score: 21,
|
||||
integration_ids: [''],
|
||||
query:
|
||||
'FROM [indexPattern]\n| STATS lastTime = max(_time), tag = values(tag), count BY dest, user, app',
|
||||
description:
|
||||
'Discovers use of default accounts (such as admin, administrator, etc.). Default accounts have default passwords and are therefore commonly targeted by attackers using brute force attack tools.',
|
||||
query_language: 'esql',
|
||||
title: 'Access - Default Account Usage - Rule',
|
||||
};
|
||||
|
||||
const defaultMigrationRuleDocument: RuleMigrationDocument = {
|
||||
'@timestamp': '2025-01-13T15:17:43.571Z',
|
||||
migration_id: '25a24356-3aab-401b-a73c-905cb8bf7a6d',
|
||||
original_rule: {
|
||||
id: 'https://127.0.0.1:8089/servicesNS/nobody/SA-AccessProtection/saved/searches/Access%20-%20Default%20Account%20Usage%20-%20Rule',
|
||||
vendor: 'splunk',
|
||||
title: 'Access - Default Account Usage - Rule',
|
||||
description:
|
||||
'Discovers use of default accounts (such as admin, administrator, etc.). Default accounts have default passwords and are therefore commonly targeted by attackers using brute force attack tools.',
|
||||
query:
|
||||
'| from datamodel:"Authentication"."Successful_Default_Authentication" | stats max("_time") as "lastTime",values("tag") as "tag",count by "dest","user","app"',
|
||||
query_language: 'spl',
|
||||
annotations: {
|
||||
mitre_attack: ['T1078'],
|
||||
},
|
||||
},
|
||||
original_rule: defaultOriginalRule,
|
||||
status: 'completed',
|
||||
created_by: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
updated_by: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
|
@ -50,32 +69,34 @@ const migrationRuleDocument: RuleMigrationDocument = {
|
|||
),
|
||||
],
|
||||
translation_result: 'partial',
|
||||
elastic_rule: {
|
||||
severity: 'low',
|
||||
risk_score: 21,
|
||||
integration_ids: [''],
|
||||
query:
|
||||
'FROM [indexPattern]\n| STATS lastTime = max(_time), tag = values(tag), count BY dest, user, app',
|
||||
description:
|
||||
'Discovers use of default accounts (such as admin, administrator, etc.). Default accounts have default passwords and are therefore commonly targeted by attackers using brute force attack tools.',
|
||||
query_language: 'esql',
|
||||
title: 'Access - Default Account Usage - Rule',
|
||||
},
|
||||
elastic_rule: defaultElasticRule,
|
||||
};
|
||||
|
||||
export const getMigrationRuleDocument = (
|
||||
overrideParams: Partial<RuleMigrationDocument>
|
||||
): RuleMigrationDocument => ({
|
||||
...migrationRuleDocument,
|
||||
...defaultMigrationRuleDocument,
|
||||
...overrideParams,
|
||||
});
|
||||
|
||||
export const getMigrationRuleDocuments = (
|
||||
count: number,
|
||||
overrideCallback: (index: number) => Partial<RuleMigrationDocument>
|
||||
): RuleMigrationDocument[] => {
|
||||
const docs: RuleMigrationDocument[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const overrideParams = overrideCallback(i);
|
||||
docs.push(getMigrationRuleDocument(overrideParams));
|
||||
}
|
||||
return docs;
|
||||
};
|
||||
|
||||
export const createMigrationRules = async (
|
||||
es: Client,
|
||||
rules: RuleMigrationDocument[]
|
||||
): Promise<void> => {
|
||||
): Promise<string[]> => {
|
||||
const createdAt = new Date().toISOString();
|
||||
await es.bulk({
|
||||
const res = await es.bulk({
|
||||
refresh: 'wait_for',
|
||||
operations: rules.flatMap((ruleMigration) => [
|
||||
{ create: { _index: SIEM_MIGRATIONS_RULES_INDEX_PATTERN } },
|
||||
|
@ -86,6 +107,13 @@ export const createMigrationRules = async (
|
|||
},
|
||||
]),
|
||||
});
|
||||
const ids = res.items.reduce((acc, item) => {
|
||||
if (item.create?._id) {
|
||||
acc.push(item.create._id);
|
||||
}
|
||||
return acc;
|
||||
}, [] as string[]);
|
||||
return ids;
|
||||
};
|
||||
|
||||
export const deleteAllMigrationRules = async (es: Client): Promise<void> => {
|
||||
|
@ -109,14 +137,50 @@ const assertStatusCode = (statusCode: number, response: SuperTest.Response) => {
|
|||
}
|
||||
};
|
||||
|
||||
export interface GetRuleMigrationParams {
|
||||
/** `id` of the migration to get rules documents for */
|
||||
migrationId: string;
|
||||
/** Optional page number to retrieve */
|
||||
page?: number;
|
||||
/** Optional number of documents per page to retrieve */
|
||||
perPage?: number;
|
||||
/** Optional field of the rule migration object to sort results by */
|
||||
sortField?: string;
|
||||
/** Optional direction to sort results by */
|
||||
sortDirection?: 'asc' | 'desc';
|
||||
/** Optional parameter to filter documents */
|
||||
filters?: RuleMigrationFilters;
|
||||
/** Optional expected status code parameter */
|
||||
expectStatusCode?: number;
|
||||
}
|
||||
|
||||
export const migrationRulesRouteHelpersFactory = (supertest: SuperTest.Agent) => {
|
||||
return {
|
||||
get: async (
|
||||
migrationId: string,
|
||||
expectStatusCode: number = 200
|
||||
): Promise<{ body: GetRuleMigrationResponse }> => {
|
||||
get: async ({
|
||||
migrationId,
|
||||
page,
|
||||
perPage,
|
||||
sortField,
|
||||
sortDirection,
|
||||
filters,
|
||||
expectStatusCode = 200,
|
||||
}: GetRuleMigrationParams): Promise<{ body: GetRuleMigrationResponse }> => {
|
||||
const response = await supertest
|
||||
.get(replaceParams(SIEM_RULE_MIGRATION_PATH, { migration_id: migrationId }))
|
||||
.query({
|
||||
page,
|
||||
per_page: perPage,
|
||||
sort_field: sortField,
|
||||
sort_direction: sortDirection,
|
||||
search_term: filters?.searchTerm,
|
||||
ids: filters?.ids,
|
||||
is_prebuilt: filters?.prebuilt,
|
||||
is_installed: filters?.installed,
|
||||
is_fully_translated: filters?.fullyTranslated,
|
||||
is_partially_translated: filters?.partiallyTranslated,
|
||||
is_untranslatable: filters?.untranslatable,
|
||||
is_failed: filters?.failed,
|
||||
})
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '1')
|
||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue