Skip to content

Commit

Permalink
drop Python 3.7 support
Browse files Browse the repository at this point in the history
  • Loading branch information
gruebel committed Jul 21, 2024
1 parent cb86f91 commit 7f3065d
Show file tree
Hide file tree
Showing 35 changed files with 397 additions and 203 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

- name: Setup Python 3.7
- name: Setup Python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: '3.7'
python-version: '3.8'

- name: Install dependencies
run: |
Expand All @@ -42,10 +42,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Set up Python 3.7
- name: Set up Python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: '3.7'
python-version: '3.8'

- name: Install dependencies
run: |
Expand All @@ -69,10 +69,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Set up Python 3.7
- name: Set up Python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: '3.7'
python-version: '3.8'
- name: publish brew
run: |
sleep 5m
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-dependency-updater.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: '3.7'
python-version: '3.8'

- name: Run Pyup.io Dependency updater
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

- name: Setup Python 3.7
- name: Setup Python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: '3.7'
python-version: '3.8'

- name: Install dependencies
run: |
Expand All @@ -44,7 +44,7 @@ jobs:
strategy:
fail-fast: true
matrix:
python: ['3.8', '3.9', '3.10', '3.11', '3.12']
python: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-bundle-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup python
uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
with:
python-version: '3.7'
python-version: '3.8'

- name: Install dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion cloudsplaining/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@


def change_log_level(log_level: Union[int, str]) -> None:
""""Change log level of module logger"""
""" "Change log level of module logger"""
logger.setLevel(log_level)


Expand Down
4 changes: 3 additions & 1 deletion cloudsplaining/bin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ def cloudsplaining() -> None:


cloudsplaining.add_command(command.create_exclusions_file.create_exclusions_file)
cloudsplaining.add_command(command.create_multi_account_config_file.create_multi_account_config_file)
cloudsplaining.add_command(
command.create_multi_account_config_file.create_multi_account_config_file
)
cloudsplaining.add_command(command.expand_policy.expand_policy)
cloudsplaining.add_command(command.scan.scan)
cloudsplaining.add_command(command.scan_multi_account.scan_multi_account)
Expand Down
2 changes: 1 addition & 1 deletion cloudsplaining/bin/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# pylint: disable=missing-module-docstring
__version__ = '0.6.4'
__version__ = "0.7.0"
10 changes: 9 additions & 1 deletion cloudsplaining/command/create_exclusions_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Create YML Template files for the exclusions template command.
This way, users don't have to remember exactly how to phrase the yaml files, since this command creates it for them.
"""

# Copyright (c) 2020, salesforce.com, inc.
# All rights reserved.
# Licensed under the BSD 3-Clause license.
Expand All @@ -21,7 +22,14 @@
context_settings=dict(max_content_width=160),
short_help="Creates a YML file to be used as a custom exclusions template",
)
@click.option("-o", "--output-file", type=click.Path(exists=False), default=os.path.join(os.getcwd(), "exclusions.yml"), required=True, help="Relative path to output file where we want to store the exclusions template.")
@click.option(
"-o",
"--output-file",
type=click.Path(exists=False),
default=os.path.join(os.getcwd(), "exclusions.yml"),
required=True,
help="Relative path to output file where we want to store the exclusions template.",
)
@click.option("--verbose", "-v", "verbosity", count=True)
def create_exclusions_file(output_file: str, verbosity: int) -> None:
"""
Expand Down
11 changes: 10 additions & 1 deletion cloudsplaining/command/create_multi_account_config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Create YML Template files for the exclusions template command.
This way, users don't have to remember exactly how to phrase the yaml files, since this command creates it for them.
"""

# Copyright (c) 2020, salesforce.com, inc.
# All rights reserved.
# Licensed under the BSD 3-Clause license.
Expand All @@ -23,7 +24,15 @@
context_settings=dict(max_content_width=160),
short_help="Creates a YML file to be used for multi-account scanning",
)
@click.option("-o", "--output-file", "output_file", type=click.Path(exists=False), default=os.path.join(os.getcwd(), "multi-account-config.yml"), required=True, help="Relative path to output file where we want to store the multi account config template.")
@click.option(
"-o",
"--output-file",
"output_file",
type=click.Path(exists=False),
default=os.path.join(os.getcwd(), "multi-account-config.yml"),
required=True,
help="Relative path to output file where we want to store the multi account config template.",
)
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def create_multi_account_config_file(output_file: str, verbosity: int) -> None:
"""
Expand Down
27 changes: 23 additions & 4 deletions cloudsplaining/command/download.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Runs aws iam get-authorization-details on all accounts specified in the aws credentials file, and stores them in
account-alias.json """

# Copyright (c) 2020, salesforce.com, inc.
# All rights reserved.
# Licensed under the BSD 3-Clause license.
Expand Down Expand Up @@ -28,9 +29,27 @@
short_help="Runs aws iam get-authorization-details on all accounts specified in the aws credentials "
"file, and stores them in account-alias.json"
)
@click.option("-p", "--profile", type=str, required=False, envvar="AWS_DEFAULT_PROFILE", help="Specify 'all' to authenticate to AWS and scan from *all* AWS credentials profiles. Specify a non-default profile here. Defaults to the 'default' profile.")
@click.option("-o", "--output", type=click.Path(exists=True), default=os.getcwd(), help="Path to store the output. Defaults to current directory.")
@click.option("--include-non-default-policy-versions", is_flag=True, default=False, help="When downloading AWS managed policy documents, also include the non-default policy versions. Note that this will dramatically increase the size of the downloaded file.")
@click.option(
"-p",
"--profile",
type=str,
required=False,
envvar="AWS_DEFAULT_PROFILE",
help="Specify 'all' to authenticate to AWS and scan from *all* AWS credentials profiles. Specify a non-default profile here. Defaults to the 'default' profile.",
)
@click.option(
"-o",
"--output",
type=click.Path(exists=True),
default=os.getcwd(),
help="Path to store the output. Defaults to current directory.",
)
@click.option(
"--include-non-default-policy-versions",
is_flag=True,
default=False,
help="When downloading AWS managed policy documents, also include the non-default policy versions. Note that this will dramatically increase the size of the downloaded file.",
)
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def download(
profile: str, output: str, include_non_default_policy_versions: bool, verbosity: int
Expand Down Expand Up @@ -64,7 +83,7 @@ def get_account_authorization_details(
session_data: Dict[str, str], include_non_default_policy_versions: bool
) -> Dict[str, List[Any]]:
"""Runs aws-iam-get-account-authorization-details"""
session = boto3.Session(**session_data) # type:ignore[arg-type] # dynamically constructed
session = boto3.Session(**session_data) # type:ignore[arg-type]
config = Config(connect_timeout=5, retries={"max_attempts": 10})
iam_client: IAMClient = session.client("iam", config=config)

Expand Down
9 changes: 8 additions & 1 deletion cloudsplaining/command/expand_policy.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Expands the wildcards (*) on an IAM policy file so it is easier for a human to understand. Example: s3:g* vs s3:GetObject, s3:GetObjectAcl, etc.
"""

# Copyright (c) 2020, salesforce.com, inc.
# All rights reserved.
# Licensed under the BSD 3-Clause license.
Expand All @@ -18,7 +19,13 @@
@click.command(
short_help="Expand the * Actions in IAM policy files to improve readability"
)
@click.option("-i", "--input-file", type=click.Path(exists=True), required=True, help="Path to the JSON policy file.")
@click.option(
"-i",
"--input-file",
type=click.Path(exists=True),
required=True,
help="Path to the JSON policy file.",
)
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
def expand_policy(input_file: str, verbosity: int) -> None:
"""
Expand Down
2 changes: 2 additions & 0 deletions cloudsplaining/command/scan.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Scan a single account authorization file
"""

# Copyright (c) 2020, salesforce.com, inc.
# All rights reserved.
# Licensed under the BSD 3-Clause license.
Expand Down Expand Up @@ -41,6 +42,7 @@
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
@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))


# fmt: on
def scan(
input_file: str,
Expand Down
101 changes: 85 additions & 16 deletions cloudsplaining/command/scan_multi_account.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Scan multiple AWS accounts via AssumeRole"""

from __future__ import annotations

import logging
Expand Down Expand Up @@ -46,19 +47,82 @@ def _accounts(self) -> Dict[str, str]:


@click.command(short_help="Scan multiple AWS Accounts using a config file")
@click.option("--config", "-c", "config_file", type=click.Path(exists=True), required=True, help="Path of the multi-account config file")
@click.option("-p", "--profile", type=str, required=False, envvar="AWS_DEFAULT_PROFILE", help="Specify the AWS IAM profile")
@click.option("-r", "--role-name", "role_name", type=str, required=True, help="The name of the IAM role to assume in target accounts. Must be the same name in all target accounts.")
@click.option("-e", "--exclusions-file", "exclusions_file", help="A yaml file containing a list of policy names to exclude from the scan.", type=click.Path(exists=True), required=False, default=EXCLUSIONS_FILE)
@click.option(
"--config",
"-c",
"config_file",
type=click.Path(exists=True),
required=True,
help="Path of the multi-account config file",
)
@click.option(
"-p",
"--profile",
type=str,
required=False,
envvar="AWS_DEFAULT_PROFILE",
help="Specify the AWS IAM profile",
)
@click.option(
"-r",
"--role-name",
"role_name",
type=str,
required=True,
help="The name of the IAM role to assume in target accounts. Must be the same name in all target accounts.",
)
@click.option(
"-e",
"--exclusions-file",
"exclusions_file",
help="A yaml file containing a list of policy names to exclude from the scan.",
type=click.Path(exists=True),
required=False,
default=EXCLUSIONS_FILE,
)
@optgroup.group("Output Target Options", help="")
@optgroup.option("-o", "--output-directory", "output_directory", type=click.Path(exists=True), help="Output directory. Supply this and/or --bucket.")
@optgroup.option("-b", "--output-bucket", "output_bucket", type=str, help="The S3 bucket to save the results. Supply this and/or --output-directory.")
@optgroup.option(
"-o",
"--output-directory",
"output_directory",
type=click.Path(exists=True),
help="Output directory. Supply this and/or --bucket.",
)
@optgroup.option(
"-b",
"--output-bucket",
"output_bucket",
type=str,
help="The S3 bucket to save the results. Supply this and/or --output-directory.",
)
@optgroup.group("Other Options", help="")
@optgroup.option("-w", "--write-data-file", is_flag=True, required=False, default=False, help="Save the cloudsplaining JSON-formatted data results.")
@click.option("-aR", "--flag-all-risky-actions", required=False, default=False, is_flag=True, help="Flag all risky actions, regardless of whether resource ARN constraints or conditions are used.")
@optgroup.option(
"-w",
"--write-data-file",
is_flag=True,
required=False,
default=False,
help="Save the cloudsplaining JSON-formatted data results.",
)
@click.option(
"-aR",
"--flag-all-risky-actions",
required=False,
default=False,
is_flag=True,
help="Flag all risky actions, regardless of whether resource ARN constraints or conditions are used.",
)
@click.option("-v", "--verbose", "verbosity", help="Log verbosity level.", count=True)
@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))

@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
),
)
def scan_multi_account(
config_file: str,
profile: str,
Expand Down Expand Up @@ -98,7 +162,7 @@ def scan_multi_account(
write_data_file=write_data_file,
severity=severity,
flag_conditional_statements=flag_conditional_statements,
flag_resource_arn_statements=flag_resource_arn_statements
flag_resource_arn_statements=flag_resource_arn_statements,
)


Expand All @@ -112,7 +176,7 @@ def scan_accounts(
output_bucket: Optional[str] = None,
severity: List[str] | None = None,
flag_conditional_statements: bool = False,
flag_resource_arn_statements: bool = False
flag_resource_arn_statements: bool = False,
) -> None:
"""Use this method as a library to scan multiple accounts"""
# TODO: Speed improvements? Multithreading? This currently runs sequentially.
Expand All @@ -127,7 +191,7 @@ def scan_accounts(
profile=profile,
severity=severity,
flag_conditional_statements=flag_conditional_statements,
flag_resource_arn_statements=flag_resource_arn_statements
flag_resource_arn_statements=flag_resource_arn_statements,
)
html_report = HTMLReport(
account_id=target_account_id,
Expand All @@ -142,7 +206,10 @@ def scan_accounts(
"Please supply --output-bucket and/or --output-directory as arguments."
)
if output_bucket:
s3 = cast("S3ServiceResource", aws_login.get_boto3_resource(service="s3", profile=profile))
s3 = cast(
"S3ServiceResource",
aws_login.get_boto3_resource(service="s3", profile=profile),
)
# Write the HTML file
output_file = f"{target_account_name}.html"
s3.Object(output_bucket, output_file).put(
Expand All @@ -163,7 +230,9 @@ def scan_accounts(
)
if output_directory:
# Write the HTML file
html_output_file = os.path.join(output_directory, f"{target_account_name}.html")
html_output_file = os.path.join(
output_directory, f"{target_account_name}.html"
)
utils.write_file(html_output_file, rendered_report)
utils.print_green(
f"Saved the HTML report to: {os.path.relpath(html_output_file)}"
Expand All @@ -188,7 +257,7 @@ def scan_account(
profile: Optional[str] = None,
severity: List[str] | None = None,
flag_conditional_statements: bool = False,
flag_resource_arn_statements: bool = False
flag_resource_arn_statements: bool = False,
) -> Dict[str, Dict[str, Any]]:
"""Scan a target account in one shot"""
account_authorization_details = download_account_authorization_details(
Expand Down
Loading

0 comments on commit 7f3065d

Please sign in to comment.