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

[Security Solution][Alerts] refactor rule execution tests #145406

Closed
vitaliidm opened this issue Nov 16, 2022 · 4 comments
Closed

[Security Solution][Alerts] refactor rule execution tests #145406

vitaliidm opened this issue Nov 16, 2022 · 4 comments
Labels
refactoring Team:Detection Alerts Security Detection Alerts Area Team Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.7.0 v8.8.0

Comments

@vitaliidm
Copy link
Contributor

vitaliidm commented Nov 16, 2022

Currently, functional rule executions tests use mock documents that stored in json and ingested into indices

Random test looks something like this:

    it('should generate 1 alert for unique combination of terms, one of which is a number', async () => {
      const rule: NewTermsRuleCreateProps = {
        ...getCreateNewTermsRulesSchemaMock('rule-1', true),
        index: ['new_terms'],
        new_terms_fields: ['user.name', 'user.id'],
        from: '2020-10-19T05:00:04.000Z',
        history_window_start: '2020-10-13T05:00:04.000Z',
      };

      const { previewId } = await previewRule({ supertest, rule });
      const previewAlerts = await getPreviewAlerts({ es, previewId });

      expect(previewAlerts.length).eql(1);
      expect(previewAlerts[0]._source?.['kibana.alert.new_terms']).eql(['user-0', '1']);
    });

For this test to work, particular document should exist that satisfy multiple conditions:

  • its timestamp should be greater than from value
  • should have unique combination of user.name and user.id in comparison with history window items
  • values 'user-0', '1' are hardcoded in mock and it's not easy to see why exactly these values are asserted
  • values 'user-0', '1' should be present in historical window, but not in combinaton with each other
  • should be only such document in the data-set.
  • Another document could either generate a new alert. Or if it has the same field values, test combination might not be a unique anymore

Given that a number of tests could be quite big, it becomes difficult to maintain data set and add new tests that could possibly break existing

Proposal

Provision test indices with documents during test run, so it will be easy to see how many documents are present and which fields will be asserted.

Possible solution could be something like this:

 it('should generate 1 alert for unique combination of terms, one of which is a number', async () => {
      const index = ['new_terms'];
      const date = moment();

      // documents in historical window
      addDocument({
        timestamp: date.subtract(3, 'hours').toISOString(),
        index,
        fields: {
          'user.name': 'user-0',
          'user.id': 0,
        },
      });

      addDocument({
        timestamp: date.subtract(4, 'hours').toISOString(),
        index,
        fields: {
          'user.name': 'user-1',
          'user.id': 1,
        },
      });

      // document ot generate alert
      addDocument({
        timestamp: date.toISOString(),
        index,
        fields: {
          'user.name': 'user-0',
          'user.id': 1,
        },
      });

      const rule: NewTermsRuleCreateProps = {
        ...getCreateNewTermsRulesSchemaMock('rule-1', true),
        index,
        new_terms_fields: ['user.name', 'user.id'],
        from: date.subtract(2, 'hours').toISOString(),
        history_window_start: date.subtract(6, 'hours').toISOString(),
      };

      const { previewId } = await previewRule({ supertest, rule });
      const previewAlerts = await getPreviewAlerts({ es, previewId });

      expect(previewAlerts.length).eql(1);
      expect(previewAlerts[0]._source?.['kibana.alert.new_terms']).eql(['user-0', '1']);
    });

Where addDocument could be helper method that would add document to index
Depends on implementation or performance, there could be different ways to setup indices

  • indices can be created in before/add hooks, and tests could target added items by some identifier
  • indices could be wiped out before each run
@vitaliidm vitaliidm added refactoring Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. Team:Detection Alerts Security Detection Alerts Area Team labels Nov 16, 2022
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-solution (Team: SecuritySolution)

@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detections-response (Team:Detections and Resp)

@vitaliidm
Copy link
Contributor Author

vitaliidm commented Feb 16, 2023

Example how test written in ticket can be formatted according to draft PR

Before

    it('should generate 1 alert for unique combination of terms, one of which is a number', async () => {
      const rule: NewTermsRuleCreateProps = {
        ...getCreateNewTermsRulesSchemaMock('rule-1', true),
        index: ['new_terms'],
        new_terms_fields: ['user.name', 'user.id'],
        from: '2020-10-19T05:00:04.000Z',
        history_window_start: '2020-10-13T05:00:04.000Z',
      };

      const { previewId } = await previewRule({ supertest, rule });
      const previewAlerts = await getPreviewAlerts({ es, previewId });

      expect(previewAlerts.length).eql(1);
      expect(previewAlerts[0]._source?.['kibana.alert.new_terms']).eql(['user-0', '1']);
    });

Aftert

Single test

    it('should generate 1 alert for unique combination of terms, one of which is a number', async () => {
      const historicalWindowStart = '2020-10-13T05:00:04.000Z';
      const ruleExecutionStart = '2020-10-19T05:00:04.000Z';
      const testId = 'test-1';

      // historical window documents
      const historicalDocuments = [
        { user: { name: 'user-0', id: 0 } },
        { user: { name: 'user-1', id: 1 } },
      ];

      await indexEnhancedDocuments({
        interval: [historicalWindowStart, ruleExecutionStart],
        id: testId,
        documents: historicalDocuments,
      });

      // rule execution documents
      const ruleExecutionDocuments = [{ user: { name: 'user-0', id: 1 } }];

      await indexEnhancedDocuments({
        id: testId,
        documents: ruleExecutionDocuments,
      });

      const rule: NewTermsRuleCreateProps = {
        ...getCreateNewTermsRulesSchemaMock('rule-1', true),
        index: ['new_terms_refactored'],
        new_terms_fields: ['user.name', 'user.id'],
        from:ruleExecutionStart,
        history_window_start: historicalWindowStart,
        query: `id: "${testId}"`,
      };

      const { previewId } = await previewRule({ supertest, rule });
      const previewAlerts = await getPreviewAlerts({ es, previewId });

      expect(previewAlerts.length).eql(1);
      expect(previewAlerts[0]._source?.['kibana.alert.new_terms']).eql(['user-0', '1']);
    });

Multiple tests

When multiple tests needed to be run, additional boilerplate can be reduced to something like this:

    it.only('should generate 1 alert for unique combination of terms, one of which is a number', async () => {
      const testId = 'test-2';
      const historicalDocuments = [
        { user: { name: 'user-0', id: 0 } },
        { user: { name: 'user-1', id: 1 } },
      ];
      const ruleExecutionDocuments = [{ user: { name: 'user-0', id: 1 } }];

      await newTermsTestExecutionSetup({
        historicalDocuments,
        ruleExecutionDocuments,
        testId,
      });

      const rule: NewTermsRuleCreateProps = {
        ...getCreateNewTermsRulesSchemaMock('rule-1', true),
        index: ['new_terms_refactored'],
        new_terms_fields: ['user.name', 'user.id'],
        from: ruleExecutionStart,
        history_window_start: historicalWindowStart,
        query: `id: "${testId}"`,
      };

      const { previewId } = await previewRule({ supertest, rule });
      const previewAlerts = await getPreviewAlerts({ es, previewId });

      expect(previewAlerts.length).eql(1);
      expect(previewAlerts[0]._source?.['kibana.alert.new_terms']).eql(['user-0', '1']);
    });

where newTermsTestExecutionSetup :

    const historicalWindowStart = '2020-10-13T05:00:04.000Z';
    const ruleExecutionStart = '2020-10-19T05:00:04.000Z';
    const newTermsTestExecutionSetup = async ({
      historicalDocuments,
      ruleExecutionDocuments,
      testId,
    }: {
      historicalDocuments: Array<Record<string, unknown>>;
      ruleExecutionDocuments: Array<Record<string, unknown>>;
      testId: string;
    }) => {
      await indexEnhancedDocuments({
        interval: [historicalWindowStart, ruleExecutionStart],
        id: testId,
        documents: historicalDocuments,
      });

      await indexEnhancedDocuments({
        id: testId,
        documents: ruleExecutionDocuments,
      });
    };

vitaliidm added a commit that referenced this issue Mar 6, 2023
…151192)

## Summary

- partially addresses #145406 by
adding dataGenerator
- once dataGenerator API agreed, we can proceed with tests refactoring
if needed
- added README how to use data generating tools -
https://github.com/vitaliidm/kibana/blob/alerts/functional-tests-data-generator/x-pack/test/detection_engine_api_integration/utils/data_generator/README.md
- partially migrated a few `new terms` tests, to give an example how to
use the created tool
@vitaliidm
Copy link
Contributor Author

vitaliidm commented Mar 6, 2023

further updates in tests can be done according to README after #151192 has been merged

bmorelli25 pushed a commit to bmorelli25/kibana that referenced this issue Mar 10, 2023
…lastic#151192)

## Summary

- partially addresses elastic#145406 by
adding dataGenerator
- once dataGenerator API agreed, we can proceed with tests refactoring
if needed
- added README how to use data generating tools -
https://github.com/vitaliidm/kibana/blob/alerts/functional-tests-data-generator/x-pack/test/detection_engine_api_integration/utils/data_generator/README.md
- partially migrated a few `new terms` tests, to give an example how to
use the created tool
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
refactoring Team:Detection Alerts Security Detection Alerts Area Team Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.7.0 v8.8.0
Projects
None yet
Development

No branches or pull requests

2 participants