-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Cloud Security] [Findings] [Vulnerabilities] [Alerts] - Create detection rule #163545
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
Changes from all commits
131e72b
641f3eb
9fe7364
4efc103
a62ba3d
485175f
4a93ec8
3b6b2a1
99cca48
1d2f484
237a085
b305b12
cc804c3
7bec744
6c18daf
d791b61
3902398
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| /* | ||
| * 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. | ||
| */ | ||
|
|
||
| // TODO: this needs to be defined in a versioned schema | ||
| import type { EcsEvent } from '@kbn/ecs'; | ||
| import { VulnSeverity } from '../types'; | ||
|
|
||
| export interface CspVulnerabilityFinding { | ||
| '@timestamp': string; | ||
| resource?: { | ||
| id: string; | ||
| name: string; | ||
| }; | ||
| event: EcsEvent; | ||
| vulnerability: Vulnerability; | ||
| ecs: { | ||
| version: string; | ||
| }; | ||
| host: { | ||
| os: { | ||
| name: string; | ||
| kernel: string; | ||
| codename: string; | ||
| type: string; | ||
| platform: string; | ||
| version: string; | ||
| family: string; | ||
| }; | ||
| id: string; | ||
| name: string; | ||
| containerized: boolean; | ||
| ip: string[]; | ||
| mac: string[]; | ||
| hostname: string; | ||
| architecture: string; | ||
| }; | ||
| agent: { | ||
| ephemeral_id: string; | ||
| id: string; | ||
| name: string; | ||
| type: string; | ||
| version: string; | ||
| }; | ||
| cloud: { | ||
| image?: { | ||
| id: string; | ||
| }; | ||
| provider?: string; | ||
| instance?: { | ||
| id: string; | ||
| }; | ||
| machine?: { | ||
| type: string; | ||
| }; | ||
| region: string; | ||
| availability_zone?: string; | ||
| service?: { | ||
| name: string; | ||
| }; | ||
| account?: { | ||
| id: string; | ||
| }; | ||
| }; | ||
| cloudbeat: { | ||
| version: string; | ||
| commit_sha: string; | ||
| commit_time: string; | ||
| }; | ||
| } | ||
|
|
||
| export interface Vulnerability { | ||
| published_date: string; | ||
| score: { | ||
| version: string; | ||
| base: number; | ||
| }; | ||
| cwe: string[]; | ||
| id: string; | ||
| title: string; | ||
| reference: string; | ||
| severity: VulnSeverity; | ||
| cvss: { | ||
| nvd: VectorScoreBase; | ||
| redhat?: VectorScoreBase; | ||
| ghsa?: VectorScoreBase; | ||
| }; | ||
| data_source: { | ||
| ID: string; | ||
| Name: string; | ||
| URL: string; | ||
| }; | ||
| enumeration: string; | ||
| description: string; | ||
| classification: string; | ||
| scanner: { | ||
| vendor: string; | ||
| }; | ||
| package: { | ||
| version: string; | ||
| name: string; | ||
| fixed_version?: string; | ||
| }; | ||
| } | ||
|
|
||
| export interface VectorScoreBase { | ||
| V3Score?: number; | ||
| V3Vector?: string; | ||
| V2Score?: number; | ||
| V2Vector?: string; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /* | ||
| * 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 type { CspVulnerabilityFinding } from '../../../common/schemas'; | ||
|
|
||
| export const getVulnerabilityReferenceUrl = ( | ||
| finding: CspVulnerabilityFinding | ||
| ): string | undefined => { | ||
| const nvdDomain = 'https://nvd'; | ||
| const nvdWebsite = `${nvdDomain}.nist.gov/vuln/detail/${finding?.vulnerability?.id}`; | ||
|
|
||
| const vulnerabilityReference = finding.vulnerability?.cvss?.nvd | ||
| ? nvdWebsite | ||
| : finding.vulnerability?.reference; | ||
|
|
||
| return vulnerabilityReference; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,14 +7,18 @@ | |
|
|
||
| import { HttpSetup } from '@kbn/core/public'; | ||
| import type { CspFinding } from '../../../../common/schemas/csp_finding'; | ||
| import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants'; | ||
| import { | ||
| FINDINGS_INDEX_PATTERN, | ||
| LATEST_FINDINGS_RETENTION_POLICY, | ||
| } from '../../../../common/constants'; | ||
| import { createDetectionRule } from '../../../common/api/create_detection_rule'; | ||
|
|
||
| const DEFAULT_RULE_RISK_SCORE = 0; | ||
| const DEFAULT_RULE_SEVERITY = 'low'; | ||
| const DEFAULT_RULE_ENABLED = true; | ||
| const DEFAULT_RULE_AUTHOR = 'Elastic'; | ||
| const DEFAULT_RULE_LICENSE = 'Elastic License v2'; | ||
| const DEFAULT_MAX_ALERTS_PER_RULE = 100; | ||
| const ALERT_SUPPRESSION_FIELD = 'resource.id'; | ||
| const ALERT_TIMESTAMP_FIELD = 'event.ingested'; | ||
|
|
||
|
|
@@ -40,23 +44,36 @@ const convertReferencesLinksToArray = (input: string | undefined) => { | |
| return matches.map((link) => link.replace(/^\d+\. /, '').replace(/\n/g, '')); | ||
| }; | ||
|
|
||
| const STATIC_RULE_TAGS = ['Elastic', 'Cloud Security']; | ||
| const CSP_RULE_TAG = 'Cloud Security'; | ||
| const CSP_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit'; | ||
| const CSP_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: '; | ||
|
|
||
| const generateMisconfigurationsTags = (finding: CspFinding) => { | ||
| const STATIC_RULE_TAGS = [CSP_RULE_TAG, CSP_RULE_TAG_USE_CASE]; | ||
|
|
||
| const generateFindingsTags = (finding: CspFinding) => { | ||
| return [STATIC_RULE_TAGS] | ||
| .concat(finding.rule.tags) | ||
| .concat( | ||
| finding.rule.benchmark.posture_type ? [finding.rule.benchmark.posture_type.toUpperCase()] : [] | ||
| finding.rule.benchmark.posture_type | ||
| ? [ | ||
| finding.rule.benchmark.posture_type.toUpperCase(), | ||
| `${CSP_RULE_TAG_DATA_SOURCE_PREFIX}${finding.rule.benchmark.posture_type.toUpperCase()}`, | ||
| ] | ||
| : [] | ||
| ) | ||
| .concat( | ||
| finding.rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container'] | ||
| ) | ||
| .flat(); | ||
| }; | ||
|
|
||
| const generateMisconfigurationsRuleQuery = (finding: CspFinding) => { | ||
| return ` | ||
| rule.benchmark.rule_number: "${finding.rule.benchmark.rule_number}" | ||
| const generateFindingsRuleQuery = (finding: CspFinding) => { | ||
| const currentTimestamp = new Date().toISOString(); | ||
|
|
||
| return `rule.benchmark.rule_number: "${finding.rule.benchmark.rule_number}" | ||
| AND rule.benchmark.id: "${finding.rule.benchmark.id}" | ||
| AND result.evaluation: "failed" | ||
| `; | ||
| AND event.ingested >= "${currentTimestamp}"`; | ||
| }; | ||
|
|
||
| /* | ||
|
|
@@ -78,8 +95,9 @@ export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: C | |
| severity_mapping: [], | ||
| threat: [], | ||
| interval: '1h', | ||
| from: 'now-7200s', | ||
| from: `now-${LATEST_FINDINGS_RETENTION_POLICY}`, | ||
| to: 'now', | ||
| max_signals: DEFAULT_MAX_ALERTS_PER_RULE, | ||
| timestamp_override: ALERT_TIMESTAMP_FIELD, | ||
| timestamp_override_fallback_disabled: false, | ||
| actions: [], | ||
|
|
@@ -88,12 +106,12 @@ export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: C | |
| group_by: [ALERT_SUPPRESSION_FIELD], | ||
| missing_fields_strategy: AlertSuppressionMissingFieldsStrategy.Suppress, | ||
| }, | ||
| index: [LATEST_FINDINGS_INDEX_DEFAULT_NS], | ||
| query: generateMisconfigurationsRuleQuery(finding), | ||
| index: [FINDINGS_INDEX_PATTERN], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just wondering, why are we not using the latest-findings/vuln?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. great question, as we plan to deprecate the transforms, having existing rules using the transformed index would make it harder for us to apply backward compatibility in the future. Our transforms routines run every 5 minutes with a 60 seconds delay, and the rule routine runs every hour, there's a small possibility that during this 4 minutes window where data came right after the transform runs, and the rule routine runs, an alert can be triggered before the finding can be seen in the UI, but that's just a couple of minutes delay and worth not having to deal with Backward compatibility later. |
||
| query: generateFindingsRuleQuery(finding), | ||
| references: convertReferencesLinksToArray(finding.rule.references), | ||
| name: finding.rule.name, | ||
| description: finding.rule.rationale, | ||
| tags: generateMisconfigurationsTags(finding), | ||
| tags: generateFindingsTags(finding), | ||
| }, | ||
| }); | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.