Skip to content

Commit 996ef75

Browse files
author
Melânia Pereira
authored
Feature/added filter by severity option (#295)
* severity field on json output and option to filter results by severity * fix type in option filter severity * generate new example report * add new dependencies to setup * -f option on scan_policy_file and scan_multi_account and "critical" and "none" as options * remove added dependencies * removed js dependencies, fixed unit tests for new output format * revert package-lock and fix mypy issues * fix typo
1 parent 2be95c0 commit 996ef75

31 files changed

+53920
-18721
lines changed

Dockerfile

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
FROM --platform=linux/amd64 alpine:3.17.2
2+
3+
COPY . ./app
4+
5+
WORKDIR /app
6+
### install prerequesites
7+
8+
RUN apk add --update --no-cache python3==3.10.11-r0 && ln -sf python3 /usr/bin/python
9+
RUN python3 -m ensurepip
10+
RUN pip3 install --no-cache --upgrade pip setuptools==67.6.0
11+
RUN apk add nodejs==18.16.0-r0
12+
RUN apk add npm==9.1.2-r0
13+
RUN apk add make==4.3-r1
14+
###### install requirements.txt
15+
RUN python3 -m pip install -r requirements.txt
16+
### remove unnecassary
17+
RUN rm -rf dist/
18+
RUN rm -rf build/
19+
RUN rm -rf *.egg-info
20+
RUN find . -name '*.pyc' -delete
21+
RUN find . -name '*.pyo' -delete
22+
RUN find . -name '*.egg-link' -delete
23+
RUN find . -name '*.pyc' -exec rm --force {} +
24+
RUN find . -name '*.pyo' -exec rm --force {} +
25+
#### seorate requirements
26+
RUN python3 -m pip install --upgrade setuptools==67.6.0 wheel==0.40.0
27+
RUN python3 -m setup -q sdist bdist_wheel
28+
RUN python3 -m pip install -q ./dist/cloudsplaining*.tar.gz
29+
######## NPM installation
30+
RUN npm install
31+
32+
ENTRYPOINT ["cloudsplaining"]

cloudsplaining/command/scan.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
@click.option("-m", "--minimize", required=False, default=False, is_flag=True, help="Reduce the size of the HTML Report by pulling the Cloudsplaining Javascript code over the internet.")
3838
@click.option("-aR", "--flag-all-risky-actions", is_flag=True, help="Flag all risky actions, regardless of whether resource ARN constraints or conditions are used.")
3939
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
40+
@click.option("-f", "--filter-severity", "severity", help="Filter the severity of findings to be reported.", multiple=True,type=click.Choice(['CRITICAL','HIGH', 'MEDIUM','LOW','NONE'], case_sensitive=False))
41+
4042
# fmt: on
4143
def scan(
4244
input_file: str,
@@ -46,6 +48,7 @@ def scan(
4648
minimize: bool,
4749
flag_all_risky_actions: bool,
4850
verbosity: int,
51+
severity: List[str],
4952
) -> None: # pragma: no cover
5053
"""
5154
Given the path to account authorization details files and the exclusions config file, scan all inline and
@@ -85,6 +88,7 @@ def scan(
8588
minimize=minimize,
8689
flag_conditional_statements=flag_conditional_statements,
8790
flag_resource_arn_statements=flag_resource_arn_statements,
91+
severity=severity,
8892
)
8993
html_output_file = os.path.join(output, f"iam-report-{account_name}.html")
9094
logger.info("Saving the report to %s", html_output_file)
@@ -122,6 +126,7 @@ def scan(
122126
output,
123127
write_data_files=True,
124128
minimize=minimize,
129+
severity=severity,
125130
)
126131
html_output_file = os.path.join(output, f"iam-report-{account_name}.html")
127132
logger.info("Saving the report to %s", html_output_file)
@@ -153,6 +158,7 @@ def scan_account_authorization_details(
153158
return_json_results: bool = False,
154159
flag_conditional_statements: bool = False,
155160
flag_resource_arn_statements: bool = False,
161+
severity: List[str] = [],
156162
) -> Any: # pragma: no cover
157163
"""
158164
Given the path to account authorization details files and the exclusions config file, scan all inline and
@@ -165,9 +171,11 @@ def scan_account_authorization_details(
165171
)
166172
check_authorization_details_schema(account_authorization_details_cfg)
167173
authorization_details = AuthorizationDetails(
168-
account_authorization_details_cfg, exclusions=exclusions,
174+
account_authorization_details_cfg,
175+
exclusions=exclusions,
169176
flag_conditional_statements=flag_conditional_statements,
170-
flag_resource_arn_statements=flag_resource_arn_statements
177+
flag_resource_arn_statements=flag_resource_arn_statements,
178+
severity=severity,
171179
)
172180
results = authorization_details.results
173181

@@ -207,9 +215,9 @@ def scan_account_authorization_details(
207215

208216
if return_json_results:
209217
return {
210-
"iam_results" : authorization_details.results,
211-
"iam_findings" : results,
212-
"rendered_report" : rendered_report
218+
"iam_results": authorization_details.results,
219+
"iam_findings": results,
220+
"rendered_report": rendered_report,
213221
}
214222
else:
215223
return rendered_report
@@ -233,3 +241,8 @@ def get_authorization_files_in_directory(
233241
if valid_schema:
234242
new_file_list.append(str(file))
235243
return new_file_list
244+
245+
246+
@click.pass_context
247+
def getSeverity(context: Any) -> Any:
248+
return context.params["severity"]

cloudsplaining/command/scan_multi_account.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def _accounts(self) -> Dict[str, str]:
5151
@optgroup.group("Other Options", help="")
5252
@optgroup.option("-w", "--write-data-file", is_flag=True, required=False, default=False, help="Save the cloudsplaining JSON-formatted data results.")
5353
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
54+
@click.option("-f", "--filter-severity", "severity", help="Filter the severity of findings to be reported.", multiple=True,type=click.Choice(['CRITICAL','HIGH', 'MEDIUM','LOW','NONE'], case_sensitive=False))
55+
5456
def scan_multi_account(
5557
config_file: str,
5658
profile: str,
@@ -60,6 +62,7 @@ def scan_multi_account(
6062
output_bucket: str,
6163
write_data_file: bool,
6264
verbosity: int,
65+
severity: List[str],
6366
) -> None:
6467
"""Scan multiple accounts via AssumeRole"""
6568
set_log_level(verbosity)
@@ -79,6 +82,7 @@ def scan_multi_account(
7982
output_directory=output_directory,
8083
output_bucket=output_bucket,
8184
write_data_file=write_data_file,
85+
severity=severity,
8286
)
8387

8488

@@ -90,6 +94,7 @@ def scan_accounts(
9094
profile: Optional[str] = None,
9195
output_directory: Optional[str] = None,
9296
output_bucket: Optional[str] = None,
97+
severity: List[str] = [],
9398
) -> None:
9499
"""Use this method as a library to scan multiple accounts"""
95100
# TODO: Speed improvements? Multithreading? This currently runs sequentially.
@@ -102,12 +107,14 @@ def scan_accounts(
102107
target_role_name=role_name,
103108
exclusions=exclusions,
104109
profile=profile,
110+
severity=severity,
105111
)
106112
html_report = HTMLReport(
107113
account_id=target_account_id,
108114
account_name=target_account_name,
109115
results=results,
110-
minimize=True,
116+
## minimize has to be false because changes were made on javascript code so it cannot be pulled over the internet, unless these changes are updated on the internet code
117+
minimize=False,
111118
)
112119
rendered_report = html_report.get_html_report()
113120
if not output_directory and not output_bucket:
@@ -159,6 +166,7 @@ def scan_account(
159166
target_role_name: str,
160167
exclusions: Exclusions,
161168
profile: Optional[str] = None,
169+
severity: List[str] = [],
162170
) -> Dict[str, Dict[str, Any]]:
163171
"""Scan a target account in one shot"""
164172
account_authorization_details = download_account_authorization_details(
@@ -167,7 +175,7 @@ def scan_account(
167175
profile=profile,
168176
)
169177
check_authorization_details_schema(account_authorization_details)
170-
authorization_details = AuthorizationDetails(account_authorization_details, exclusions)
178+
authorization_details = AuthorizationDetails(account_authorization_details, exclusions=exclusions,severity=severity)
171179
results = authorization_details.results
172180
return results
173181

@@ -211,3 +219,8 @@ def get_exclusions(exclusions_file: Optional[str] = None) -> Exclusions:
211219
else:
212220
exclusions = DEFAULT_EXCLUSIONS
213221
return exclusions
222+
223+
224+
@click.pass_context
225+
def getSeverity(context: Any) -> Any:
226+
return context.params["severity"]

0 commit comments

Comments
 (0)