Skip to content

Commit 108af71

Browse files
authored
Impact Findings Improvements (#74)
* correct-status-format * move_findings_under_impact_key
1 parent afb9fa6 commit 108af71

File tree

4 files changed

+65
-43
lines changed

4 files changed

+65
-43
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,15 @@ The **Context** module has the capability to retrieve information from the affec
7373

7474
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`.
7575

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

7878
## Associations
7979

8080
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.
8181

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

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

8686
## Tags
8787

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

205205
## Owner
206206

207-
**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.
208-
209-
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.
207+
**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)).
210208

211209
| **Possible Statuses** | **Value** | **Description** |
212210
| --------------------- | :-------: | ------------------------------------------- |

lib/html/template.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@
244244
var value = cell.getValue();
245245
var color;
246246
var emoji;
247-
if (value === "not-attached" || value === "not-running") {
247+
if (value === "not-attached" || value === "not-running" || value === "not-enabled") {
248248
color = "green";
249249
emoji = "🟢";
250-
} else if (value === "attached" || value === "running") {
250+
} else if (value === "attached" || value === "running" || value === "enabled") {
251251
color = "orange";
252252
emoji = "🟠";
253253
} else if (value === "unknown") {

lib/impact/findings.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from lib.config.configuration import findings_severity_value
2+
3+
4+
class Findings:
5+
def __init__(self, logger):
6+
self.logger = logger
7+
8+
def get_findings_score(self, resource_arn, resource_values):
9+
self.logger.info("Calculating impact findings score for resource")
10+
11+
# Initialize the findings score to zero
12+
findings_score = 0
13+
count_active_findings = {
14+
"CRITICAL": 0,
15+
"HIGH": 0,
16+
"MEDIUM": 0,
17+
"LOW": 0,
18+
"INFORMATIONAL": 0,
19+
"UNDEFINED": 0,
20+
}
21+
22+
# Iterate through each finding in the resource
23+
for f in resource_values["findings"]:
24+
for k, v in f.items():
25+
# Check if the finding is active
26+
if v.get("RecordState") == "ACTIVE":
27+
# Get the severity value for the finding
28+
single_finding_severity = findings_severity_value.get(
29+
v.get("SeverityLabel")
30+
)
31+
# Get the single finding score
32+
single_finding_score = single_finding_severity / max(
33+
findings_severity_value.values()
34+
)
35+
# Sum the single finding score to the findings score
36+
findings_score += single_finding_score
37+
# Count the number of active findings per severity
38+
count_active_findings[v.get("SeverityLabel")] += 1
39+
40+
# Ensure the findings score does not exceed 1
41+
if findings_score > 1:
42+
findings_score = 1
43+
44+
return {findings_score: {"findings": count_active_findings}}

lib/impact/impact.py

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
import yaml
44

5-
from lib.config.configuration import findings_severity_value, path_yaml_impact
5+
from lib.config.configuration import path_yaml_impact
66
from lib.impact.access import Access
77
from lib.impact.application import Application
88
from lib.impact.encryption import Encryption
99
from lib.impact.environment import Environment
1010
from lib.impact.exposure import Exposure
11+
from lib.impact.findings import Findings
1112
from lib.impact.owner import Owner
1213
from lib.impact.status import Status
1314

@@ -54,33 +55,6 @@ def validate_config(self, config):
5455
return False
5556
return True
5657

57-
def get_findings_score(self, resource_values):
58-
self.logger.info("Calculating impact findings score for resource")
59-
60-
# Initialize the findings score to zero
61-
findings_score = 0
62-
63-
# Iterate through each finding in the resource
64-
for f in resource_values["findings"]:
65-
for k, v in f.items():
66-
# Check if the finding is active
67-
if v.get("RecordState") == "ACTIVE":
68-
# Get the severity value for the finding
69-
single_finding_severity = findings_severity_value.get(
70-
v.get("SeverityLabel")
71-
)
72-
# Get the single finding score
73-
single_finding_score = single_finding_severity / max(
74-
findings_severity_value.values()
75-
)
76-
# Sum the single finding score to the findings score
77-
findings_score += single_finding_score
78-
79-
# Ensure the findings score does not exceed 1
80-
if findings_score > 1:
81-
findings_score = 1
82-
return findings_score
83-
8458
def check_property_values_with_resource(
8559
self, property_name, property_values, resource_values
8660
):
@@ -93,8 +67,8 @@ def check_property_values_with_resource(
9367
return value_key, value_data["score"]
9468
return False
9569

96-
def get_meta_score(self, resource_values):
97-
self.logger.info("Calculating impact meta score for resource")
70+
def calculate_properties_score(self, resource_values):
71+
self.logger.info("Calculating impact properties score for resource")
9872

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

153127
# Calculate the findings score using the calculate_findings_score method
154-
findings_score = self.get_findings_score(resource_values)
155-
# Calculate the meta score using the get_meta_score method
156-
meta_score = self.get_meta_score(resource_values)
128+
findings_score = Findings(self.logger).get_findings_score(
129+
resource_arn, resource_values
130+
)
131+
findings_score = [str(key) for key in findings_score.keys()]
132+
findings_score = float(findings_score[0])
133+
# Calculate the impact properties score
134+
meta_score = self.calculate_properties_score(resource_values)
157135

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

177155
# Return the dictionary containing impact scores
178-
return {
179-
impact_score: {"findings_score": findings_score, "meta_score": meta_score}
180-
}
156+
return {impact_score: {}}
181157

182158
def generate_impact_checks(self, resource_arn, resource_values):
183159
self.logger.info("Executing Impact Module")
@@ -189,6 +165,7 @@ def generate_impact_checks(self, resource_arn, resource_values):
189165
"environment": {},
190166
"application": {},
191167
"owner": {},
168+
"findings": {},
192169
"score": {},
193170
}
194171
impact_dict["exposure"].update(
@@ -212,4 +189,7 @@ def generate_impact_checks(self, resource_arn, resource_values):
212189
impact_dict["owner"].update(
213190
Owner(self.logger).get_owner(resource_arn, resource_values)
214191
)
192+
impact_dict["findings"].update(
193+
Findings(self.logger).get_findings_score(resource_arn, resource_values)
194+
)
215195
return impact_dict

0 commit comments

Comments
 (0)