Skip to content

Commit

Permalink
Impact Findings Improvements (#74)
Browse files Browse the repository at this point in the history
* correct-status-format

* move_findings_under_impact_key
  • Loading branch information
gabrielsoltz committed Dec 9, 2023
1 parent afb9fa6 commit 108af71
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 43 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ The **Context** module has the capability to retrieve information from the affec

Under the `config` key, you can find anyting related to the configuration of the affected resource. For example, if the affected resource is an EC2 Instance, you will see keys like `private_ip`, `public_ip`, or `instance_profile`.

You can filter your findings based on Config outputs using the option: `--mh-filters-config <key> {True/False}`. See [Config Filtering](#config-filtering).
You can filter your findings based on Config outputs using the option: `--mh-filters-config <key> {True/False}`. See [Config Filters](#config-filters).

## Associations

Under the `associations` key, you will find all the associated resources of the affected resource. For example, if the affected resource is an EC2 Instance, you will find resources like: Security Groups, IAM Roles, Volumes, VPC, Subnets, Auto Scaling Groups, etc. Each time MetaHub finds an association, it will connect to the associated resource again and fetch its own context.

Associations are key to understanding the context and impact of your security findings as their exposure.

You can filter your findings based on Associations outputs using the option: `--mh-filters-config <key> {True/False}`. See [Config Filtering](#config-filtering).
You can filter your findings based on Associations outputs using the option: `--mh-filters-config <key> {True/False}`. See [Config Filters](#config-filters).

## Tags

Expand Down Expand Up @@ -204,9 +204,7 @@ The formula for getting the impact score include the following criteria:

## Owner

**Owner** focuses on ownership detection. It can determine the owner of the affected resource in various ways. This information can be used to automatically assign a security finding to the correct owner, escalate it, or make decisions based on this information.

An automated way to determine the owner of a resource is critical for security teams. It allows them to focus on the most critical issues and escalate them to the right people in automated workflows. But automating workflows this way, it is only viable if you have a reliable way to define the impact of a finding, which is why MetaHub also focuses on impact.
**Owner** focuses on ownership detection. It can determine the owner of the affected resource in various ways. This information can be used to automatically assign a security finding to the correct owner, escalate it, or make decisions based on this information. An automated way to determine the owner of a resource is critical for security teams. It allows them to focus on the most critical issues and assign them as fast as possible to the right people in automated workflows. You can define your owner definitions and strategy in the configuration file (See [Customizing Configuration](#customizing-configuration)).

| **Possible Statuses** | **Value** | **Description** |
| --------------------- | :-------: | ------------------------------------------- |
Expand Down
4 changes: 2 additions & 2 deletions lib/html/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,10 @@
var value = cell.getValue();
var color;
var emoji;
if (value === "not-attached" || value === "not-running") {
if (value === "not-attached" || value === "not-running" || value === "not-enabled") {
color = "green";
emoji = "🟢";
} else if (value === "attached" || value === "running") {
} else if (value === "attached" || value === "running" || value === "enabled") {
color = "orange";
emoji = "🟠";
} else if (value === "unknown") {
Expand Down
44 changes: 44 additions & 0 deletions lib/impact/findings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from lib.config.configuration import findings_severity_value


class Findings:
def __init__(self, logger):
self.logger = logger

def get_findings_score(self, resource_arn, resource_values):
self.logger.info("Calculating impact findings score for resource")

# Initialize the findings score to zero
findings_score = 0
count_active_findings = {
"CRITICAL": 0,
"HIGH": 0,
"MEDIUM": 0,
"LOW": 0,
"INFORMATIONAL": 0,
"UNDEFINED": 0,
}

# Iterate through each finding in the resource
for f in resource_values["findings"]:
for k, v in f.items():
# Check if the finding is active
if v.get("RecordState") == "ACTIVE":
# Get the severity value for the finding
single_finding_severity = findings_severity_value.get(
v.get("SeverityLabel")
)
# Get the single finding score
single_finding_score = single_finding_severity / max(
findings_severity_value.values()
)
# Sum the single finding score to the findings score
findings_score += single_finding_score
# Count the number of active findings per severity
count_active_findings[v.get("SeverityLabel")] += 1

# Ensure the findings score does not exceed 1
if findings_score > 1:
findings_score = 1

return {findings_score: {"findings": count_active_findings}}
52 changes: 16 additions & 36 deletions lib/impact/impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import yaml

from lib.config.configuration import findings_severity_value, path_yaml_impact
from lib.config.configuration import path_yaml_impact
from lib.impact.access import Access
from lib.impact.application import Application
from lib.impact.encryption import Encryption
from lib.impact.environment import Environment
from lib.impact.exposure import Exposure
from lib.impact.findings import Findings
from lib.impact.owner import Owner
from lib.impact.status import Status

Expand Down Expand Up @@ -54,33 +55,6 @@ def validate_config(self, config):
return False
return True

def get_findings_score(self, resource_values):
self.logger.info("Calculating impact findings score for resource")

# Initialize the findings score to zero
findings_score = 0

# Iterate through each finding in the resource
for f in resource_values["findings"]:
for k, v in f.items():
# Check if the finding is active
if v.get("RecordState") == "ACTIVE":
# Get the severity value for the finding
single_finding_severity = findings_severity_value.get(
v.get("SeverityLabel")
)
# Get the single finding score
single_finding_score = single_finding_severity / max(
findings_severity_value.values()
)
# Sum the single finding score to the findings score
findings_score += single_finding_score

# Ensure the findings score does not exceed 1
if findings_score > 1:
findings_score = 1
return findings_score

def check_property_values_with_resource(
self, property_name, property_values, resource_values
):
Expand All @@ -93,8 +67,8 @@ def check_property_values_with_resource(
return value_key, value_data["score"]
return False

def get_meta_score(self, resource_values):
self.logger.info("Calculating impact meta score for resource")
def calculate_properties_score(self, resource_values):
self.logger.info("Calculating impact properties score for resource")

# Initialize variables to track the meta score details and context
meta_score_details = {}
Expand Down Expand Up @@ -151,9 +125,13 @@ def generate_impact_scoring(self, resource_arn, resource_values):
return False

# Calculate the findings score using the calculate_findings_score method
findings_score = self.get_findings_score(resource_values)
# Calculate the meta score using the get_meta_score method
meta_score = self.get_meta_score(resource_values)
findings_score = Findings(self.logger).get_findings_score(
resource_arn, resource_values
)
findings_score = [str(key) for key in findings_score.keys()]
findings_score = float(findings_score[0])
# Calculate the impact properties score
meta_score = self.calculate_properties_score(resource_values)

# Check if the meta score is not "n/a" (i.e., there's context)
if meta_score != "n/a" and meta_score != 0:
Expand All @@ -175,9 +153,7 @@ def generate_impact_scoring(self, resource_arn, resource_values):
impact_score = int(impact_score) # Return the integer part

# Return the dictionary containing impact scores
return {
impact_score: {"findings_score": findings_score, "meta_score": meta_score}
}
return {impact_score: {}}

def generate_impact_checks(self, resource_arn, resource_values):
self.logger.info("Executing Impact Module")
Expand All @@ -189,6 +165,7 @@ def generate_impact_checks(self, resource_arn, resource_values):
"environment": {},
"application": {},
"owner": {},
"findings": {},
"score": {},
}
impact_dict["exposure"].update(
Expand All @@ -212,4 +189,7 @@ def generate_impact_checks(self, resource_arn, resource_values):
impact_dict["owner"].update(
Owner(self.logger).get_owner(resource_arn, resource_values)
)
impact_dict["findings"].update(
Findings(self.logger).get_findings_score(resource_arn, resource_values)
)
return impact_dict

0 comments on commit 108af71

Please sign in to comment.