Skip to content

Commit

Permalink
Add schema support for Correlation Rules (#416)
Browse files Browse the repository at this point in the history
  • Loading branch information
zacbrown authored Nov 29, 2023
1 parent 30f45fd commit 060070b
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 14 deletions.
2 changes: 2 additions & 0 deletions panther_analysis_tool/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from schema import Schema

from panther_analysis_tool.schemas import (
CORRELATION_RULE_SCHEMA,
DATA_MODEL_SCHEMA,
DERIVED_SCHEMA,
GLOBAL_SCHEMA,
Expand Down Expand Up @@ -79,6 +80,7 @@ class AnalysisTypes:
AnalysisTypes.RULE: RULE_SCHEMA,
AnalysisTypes.DERIVED: DERIVED_SCHEMA,
AnalysisTypes.SCHEDULED_RULE: RULE_SCHEMA,
AnalysisTypes.CORRELATION_RULE: CORRELATION_RULE_SCHEMA,
}

SET_FIELDS = [
Expand Down
232 changes: 218 additions & 14 deletions panther_analysis_tool/detection_schemas/analysis_config_schema.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/panther-labs/panther/pkg/detections/sdl/schema/simple-detection-schema",
"$ref": "#/$defs/SimpleDetectionSchema",
"$defs": {
"AssociatedLogType": {
Expand Down Expand Up @@ -563,6 +564,17 @@
"type": "object",
"required": ["OnlyOne"]
},
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"None": {
"$ref": "#/$defs/MatchExpressions"
}
},
"additionalProperties": false,
"type": "object",
"required": ["None"]
},
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
Expand Down Expand Up @@ -899,6 +911,185 @@
"additionalProperties": false,
"type": "object",
"required": ["Condition", "Values"]
},
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"EventEvaluationOrder": {
"type": "string",
"enum": ["Chronological", "ReverseChronological"]
},
"LookbackWindowMinutes": {
"type": "integer"
},
"Schedule": {
"properties": {
"CronExpression": {
"type": "string"
},
"RateMinutes": {
"type": "integer"
},
"TimeoutMinutes": {
"type": "integer"
}
},
"additionalProperties": false,
"type": "object",
"required": ["TimeoutMinutes"]
},
"Sequence": {
"items": {
"properties": {
"ID": {
"type": "string"
},
"RuleID": {
"type": "string"
},
"SignalMatch": {
"type": "string"
},
"MinMatchCount": {
"type": "integer"
},
"MaxMatchCount": {
"type": "integer"
},
"Absence": {
"type": "boolean"
}
},
"additionalProperties": false,
"type": "object"
},
"type": "array"
},
"Transitions": {
"items": {
"properties": {
"ID": {
"type": "string"
},
"From": {
"type": "string"
},
"To": {
"type": "string"
},
"WithinTimeFrameMinutes": {
"type": "integer"
},
"Match": {
"items": {
"properties": {
"On": {
"type": "string"
},
"From": {
"type": "string"
},
"To": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"type": "array",
"maxItems": 1,
"minItems": 1
}
},
"additionalProperties": false,
"type": "object",
"required": ["From", "To"]
},
"type": "array"
}
},
"additionalProperties": false,
"type": "object",
"required": ["Schedule", "Sequence"]
},
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"EventEvaluationOrder": {
"type": "string",
"enum": ["Chronological", "ReverseChronological"]
},
"LookbackWindowMinutes": {
"type": "integer"
},
"Schedule": {
"properties": {
"CronExpression": {
"type": "string"
},
"RateMinutes": {
"type": "integer"
},
"TimeoutMinutes": {
"type": "integer"
}
},
"additionalProperties": false,
"type": "object",
"required": ["TimeoutMinutes"]
},
"Group": {
"items": {
"properties": {
"ID": {
"type": "string"
},
"RuleID": {
"type": "string"
},
"SignalMatch": {
"type": "string"
},
"MinMatchCount": {
"type": "integer"
},
"MaxMatchCount": {
"type": "integer"
},
"Absence": {
"type": "boolean"
}
},
"additionalProperties": false,
"type": "object"
},
"type": "array"
},
"MatchCriteria": {
"patternProperties": {
".*": {
"items": {
"properties": {
"GroupID": {
"type": "string"
},
"Match": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object",
"required": ["GroupID", "Match"]
},
"type": "array"
}
},
"type": "object"
}
},
"additionalProperties": false,
"type": "object",
"required": ["Schedule", "Group"]
}
],
"type": "object"
Expand Down Expand Up @@ -1020,7 +1211,8 @@
"scheduled_rule",
"scheduled_query",
"lookup_table",
"saved_query"
"saved_query",
"correlation_rule"
]
},
"Description": {
Expand Down Expand Up @@ -1054,7 +1246,19 @@
"type": "string"
},
"Severity": {
"type": "string"
"type": "string",
"enum": [
"Info",
"INFO",
"Low",
"LOW",
"Medium",
"MEDIUM",
"High",
"HIGH",
"Critical",
"CRITICAL"
]
},
"Tags": {
"items": {
Expand Down Expand Up @@ -1155,12 +1359,12 @@
"Schema": {
"type": "string"
},
"PackDefinition": {
"$ref": "#/$defs/PackDefinition"
},
"PackID": {
"type": "string"
},
"PackDefinition": {
"$ref": "#/$defs/PackDefinition"
},
"Detection": {
"$ref": "#/$defs/MatchExpressions"
},
Expand Down Expand Up @@ -1195,27 +1399,27 @@
},
"Test": {
"properties": {
"Name": {
"type": "string"
},
"ExpectedResult": {
"type": "boolean"
},
"Log": true,
"LogType": {
"type": "string"
},
"ResourceType": {
"type": "string"
},
"Mocks": {
"$ref": "#/$defs/Mocks"
},
"Name": {
"type": "string"
},
"Resource": true,
"ResourceType": {
"type": "string"
}
"Log": true,
"Resource": true
},
"additionalProperties": false,
"type": "object",
"required": ["ExpectedResult", "Name"]
"required": ["Name", "ExpectedResult"]
}
}
}
7 changes: 7 additions & 0 deletions panther_analysis_tool/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
BackendNotFoundException,
add_path_to_filename,
convert_unicode,
is_correlation_rule,
is_derived_detection,
is_simple_detection,
)
Expand Down Expand Up @@ -911,6 +912,12 @@ def setup_run_tests( # pylint: disable=too-many-locals,too-many-arguments
filters=analysis_spec.get(BACKEND_FILTERS_ANALYSIS_SPEC_KEY) or None,
)

if is_correlation_rule(analysis_spec):
logging.warning(
"Skipping Correlation Rule '%s', testing not supported", analysis_spec.get("RuleID")
)
continue

if is_simple_detection(analysis_spec) or is_derived_detection(analysis_spec):
# skip tests when the body is empty
if not analysis_spec.get("body"):
Expand Down
39 changes: 39 additions & 0 deletions panther_analysis_tool/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def validate(
TYPE_SCHEMA = Schema(
{
"AnalysisType": Or(
"correlation_rule",
"datamodel",
"global",
"pack",
Expand Down Expand Up @@ -221,6 +222,44 @@ def validate(
ignore_extra_keys=False,
)

CORRELATION_RULE_SCHEMA = Schema(
{
"AnalysisType": "correlation_rule",
"RuleID": And(str, NAME_ID_VALIDATION_REGEX),
"Enabled": bool,
"Detection": object,
"Severity": Or("Info", "Low", "Medium", "High", "Critical"),
Optional("Description"): str,
Optional("DedupPeriodMinutes"): int,
Optional("InlineFilters"): object,
Optional("DisplayName"): And(str, NAME_ID_VALIDATION_REGEX),
Optional("OnlyUseBaseRiskScore"): bool,
Optional("OutputIds"): [str],
Optional("Reference"): str,
Optional("Runbook"): str,
Optional("SummaryAttributes"): [str],
Optional("Threshold"): int,
Optional("Tags"): [str],
Optional("Reports"): {str: list},
Optional("Tests"): [
{
"Name": str,
Optional(
"LogType"
): str, # Not needed anymore, optional for backwards compatibility
"ExpectedResult": bool,
"Log": object,
Optional("Mocks"): [MOCK_SCHEMA],
}
],
Optional("DynamicSeverities"): object,
Optional("AlertTitle"): str,
Optional("AlertContext"): object,
Optional("GroupBy"): object,
},
ignore_extra_keys=False,
)

SAVED_QUERY_SCHEMA = Schema(
{
"AnalysisType": Or("saved_query"),
Expand Down

0 comments on commit 060070b

Please sign in to comment.