awsfindingsmanagerlib 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- awsfindingsmanagerlib/__init__.py +37 -0
- awsfindingsmanagerlib/aws_lambda/__init__.py +1 -0
- awsfindingsmanagerlib/aws_lambda/clients.py +28 -0
- awsfindingsmanagerlib/aws_lambda/events.py +74 -0
- awsfindingsmanagerlib/aws_lambda/logging.py +36 -0
- awsfindingsmanagerlib/aws_lambda/rules_backend.py +61 -0
- awsfindingsmanagerlib/aws_lambda/scheduled.py +42 -0
- awsfindingsmanagerlib/aws_lambda/trigger.py +48 -0
- awsfindingsmanagerlib/aws_lambda/worker.py +60 -0
- awsfindingsmanagerlib/awsfindingsmanagerlib.py +75 -0
- awsfindingsmanagerlib/boto_utils.py +215 -0
- awsfindingsmanagerlib/filters/__init__.py +42 -0
- awsfindingsmanagerlib/filters/cidr.py +42 -0
- awsfindingsmanagerlib/filters/date.py +68 -0
- awsfindingsmanagerlib/filters/filters_factory.py +113 -0
- awsfindingsmanagerlib/filters/filters_interface.py +70 -0
- awsfindingsmanagerlib/filters/map.py +70 -0
- awsfindingsmanagerlib/filters/number.py +89 -0
- awsfindingsmanagerlib/filters/regex_string.py +40 -0
- awsfindingsmanagerlib/filters/string.py +100 -0
- awsfindingsmanagerlib/manager.py +196 -0
- awsfindingsmanagerlib/note_text_config.py +52 -0
- awsfindingsmanagerlib/py.typed +0 -0
- awsfindingsmanagerlib/rule.py +339 -0
- awsfindingsmanagerlib/utils.py +139 -0
- awsfindingsmanagerlib-2.0.0.dist-info/METADATA +108 -0
- awsfindingsmanagerlib-2.0.0.dist-info/RECORD +28 -0
- awsfindingsmanagerlib-2.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""The main package of awsfindingsmanagerlib."""
|
|
2
|
+
|
|
3
|
+
from .awsfindingsmanagerlib import validate_filters, validate_updates
|
|
4
|
+
from .boto_utils import (
|
|
5
|
+
BotoStubCall,
|
|
6
|
+
get_values_by_boto_argument,
|
|
7
|
+
stub_boto_client,
|
|
8
|
+
validate_call_params,
|
|
9
|
+
)
|
|
10
|
+
from .manager import Manager, MatchAndUpdateResult
|
|
11
|
+
from .note_text_config import NoteTextConfig
|
|
12
|
+
from .rule import Rule
|
|
13
|
+
from .utils import (
|
|
14
|
+
TimeRange,
|
|
15
|
+
are_keys_in_collection,
|
|
16
|
+
are_keys_in_dataclass_fields,
|
|
17
|
+
is_valid_against_reference,
|
|
18
|
+
parse_timestamp_str_if_set,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"BotoStubCall",
|
|
23
|
+
"Manager",
|
|
24
|
+
"MatchAndUpdateResult",
|
|
25
|
+
"NoteTextConfig",
|
|
26
|
+
"Rule",
|
|
27
|
+
"TimeRange",
|
|
28
|
+
"are_keys_in_collection",
|
|
29
|
+
"are_keys_in_dataclass_fields",
|
|
30
|
+
"get_values_by_boto_argument",
|
|
31
|
+
"is_valid_against_reference",
|
|
32
|
+
"parse_timestamp_str_if_set",
|
|
33
|
+
"stub_boto_client",
|
|
34
|
+
"validate_call_params",
|
|
35
|
+
"validate_filters",
|
|
36
|
+
"validate_updates",
|
|
37
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AWS Lambda entry points for running awsfindingsmanagerlib as part of an event-driven pipeline."""
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Lazily-created, cached boto3 clients shared across Lambda handlers.
|
|
2
|
+
|
|
3
|
+
Creating these lazily (rather than as module-level constants) means importing a handler module
|
|
4
|
+
never requires AWS credentials or network access, which keeps the handlers unit-testable.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from functools import cache
|
|
8
|
+
|
|
9
|
+
import boto3
|
|
10
|
+
from botocore.client import BaseClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@cache
|
|
14
|
+
def get_securityhub_client() -> BaseClient:
|
|
15
|
+
"""Return a cached boto3 Security Hub client, created on first use."""
|
|
16
|
+
return boto3.client("securityhub")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@cache
|
|
20
|
+
def get_s3_client() -> BaseClient:
|
|
21
|
+
"""Return a cached boto3 S3 client, created on first use."""
|
|
22
|
+
return boto3.client("s3")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@cache
|
|
26
|
+
def get_sqs_client() -> BaseClient:
|
|
27
|
+
"""Return a cached boto3 SQS client, created on first use."""
|
|
28
|
+
return boto3.client("sqs")
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""EventBridge-triggered Lambda handler.
|
|
2
|
+
|
|
3
|
+
Suppresses findings carried by a "Security Hub Findings - Imported" EventBridge event.
|
|
4
|
+
|
|
5
|
+
Returns `{"finding_state": "suppressed"}` or `{"finding_state": "skipped"}` and never raises, so
|
|
6
|
+
that a Step Function orchestrating this handler ahead of a ticketing integration can branch on
|
|
7
|
+
the result: `terraform-aws-mcaf-securityhub-findings-manager` relies on exactly this contract to
|
|
8
|
+
decide whether a finding still needs a Jira ticket after this handler has run.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from awsfindingsmanagerlib import Manager
|
|
14
|
+
from awsfindingsmanagerlib.aws_lambda.clients import (
|
|
15
|
+
get_s3_client,
|
|
16
|
+
get_securityhub_client,
|
|
17
|
+
)
|
|
18
|
+
from awsfindingsmanagerlib.aws_lambda.logging import get_logger
|
|
19
|
+
from awsfindingsmanagerlib.aws_lambda.rules_backend import load_rules
|
|
20
|
+
|
|
21
|
+
LOGGER = get_logger()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@LOGGER.inject_lambda_context()
|
|
25
|
+
def lambda_handler(event: dict[str, Any], _context: object) -> dict[str, str]:
|
|
26
|
+
"""Suppress the findings carried by an EventBridge "Security Hub Findings - Imported" event.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
event : dict[str, Any]
|
|
31
|
+
The EventBridge event. `event["detail"]["findings"]` is expected to be a list of Security
|
|
32
|
+
Hub finding dicts.
|
|
33
|
+
_context : object
|
|
34
|
+
The Lambda context object (unused).
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
dict[str, str]
|
|
39
|
+
`{"finding_state": "suppressed"}` if at least one finding matched a rule and all matching
|
|
40
|
+
updates were processed successfully, `{"finding_state": "skipped"}` otherwise (no rule
|
|
41
|
+
matched, the manager could not be initialized, or an update failed).
|
|
42
|
+
"""
|
|
43
|
+
findings = event.get("detail", {}).get("findings", [])
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
manager = Manager.from_rules_document(
|
|
47
|
+
load_rules(s3_client=get_s3_client()), get_securityhub_client()
|
|
48
|
+
)
|
|
49
|
+
except Exception:
|
|
50
|
+
LOGGER.exception("Failed to initialize the findings manager; skipping.")
|
|
51
|
+
return {"finding_state": "skipped"}
|
|
52
|
+
|
|
53
|
+
matched_any = False
|
|
54
|
+
processed_all = True
|
|
55
|
+
|
|
56
|
+
for finding in findings:
|
|
57
|
+
try:
|
|
58
|
+
result = manager.process_finding(finding)
|
|
59
|
+
except Exception:
|
|
60
|
+
LOGGER.exception("Failed to process a finding; skipping it.")
|
|
61
|
+
processed_all = False
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
matched_any = matched_any or result.matched_rules > 0
|
|
65
|
+
processed_all = processed_all and result.all_processed
|
|
66
|
+
|
|
67
|
+
if matched_any and processed_all:
|
|
68
|
+
LOGGER.info("Successfully suppressed matching finding(s).")
|
|
69
|
+
return {"finding_state": "suppressed"}
|
|
70
|
+
|
|
71
|
+
LOGGER.info(
|
|
72
|
+
"No finding(s) were suppressed (no rule matched, or an update could not be processed)."
|
|
73
|
+
)
|
|
74
|
+
return {"finding_state": "skipped"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Shared Lambda logging setup, built on aws-lambda-powertools.
|
|
2
|
+
|
|
3
|
+
aws-lambda-powertools is an optional dependency (the 'lambda' extra), so importing this module
|
|
4
|
+
without it installed raises a clear, actionable error instead of a bare ModuleNotFoundError.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from aws_lambda_powertools import Logger
|
|
9
|
+
from aws_lambda_powertools.logging import utils
|
|
10
|
+
except ImportError as error:
|
|
11
|
+
msg = (
|
|
12
|
+
"aws-lambda-powertools is required to use awsfindingsmanagerlib.aws_lambda handlers. "
|
|
13
|
+
"Install it with the 'lambda' extra, e.g. `uv add 'awsfindingsmanagerlib[lambda]'` "
|
|
14
|
+
"or `pip install 'awsfindingsmanagerlib[lambda]'`."
|
|
15
|
+
)
|
|
16
|
+
raise ImportError(msg) from error
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_logger() -> Logger:
|
|
20
|
+
"""Create a powertools Logger and propagate its configuration to other registered loggers.
|
|
21
|
+
|
|
22
|
+
The service name is taken from the `POWERTOOLS_SERVICE_NAME` environment variable, as usual
|
|
23
|
+
for aws-lambda-powertools. Handlers built on this logger call `inject_lambda_context()`
|
|
24
|
+
without an explicit `log_event` argument, so whether the incoming event is logged (which can
|
|
25
|
+
include full Security Hub finding payloads) is controlled entirely by the
|
|
26
|
+
`POWERTOOLS_LOGGER_LOG_EVENT` environment variable (defaults to `false`); passing
|
|
27
|
+
`log_event=True`/`False` explicitly at the call site would silently override that setting.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
Logger
|
|
32
|
+
A ready-to-use powertools Logger.
|
|
33
|
+
"""
|
|
34
|
+
logger = Logger()
|
|
35
|
+
utils.copy_config_to_registered_loggers(source_logger=logger)
|
|
36
|
+
return logger
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Load suppression rules for AWS Lambda use."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from botocore.client import BaseClient
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from yaml import safe_load
|
|
11
|
+
except ImportError as error:
|
|
12
|
+
msg = (
|
|
13
|
+
"pyyaml is required to use awsfindingsmanagerlib.aws_lambda handlers. "
|
|
14
|
+
"Install it with the 'lambda' extra, e.g. `uv add 'awsfindingsmanagerlib[lambda]'` "
|
|
15
|
+
"or `pip install 'awsfindingsmanagerlib[lambda]'`."
|
|
16
|
+
)
|
|
17
|
+
raise ImportError(msg) from error
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def load_rules(s3_client: BaseClient | None = None) -> dict[str, Any]:
|
|
21
|
+
"""Load the rules document, from S3 if configured, otherwise from a local file.
|
|
22
|
+
|
|
23
|
+
If both the `S3_BUCKET_NAME` and `S3_OBJECT_NAME` environment variables are set, the rules
|
|
24
|
+
document is loaded from that S3 object. Otherwise, it is loaded from the local file at
|
|
25
|
+
`RULES_PATH` (defaults to `rules.yaml`).
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
s3_client : BaseClient, optional
|
|
30
|
+
The boto3 S3 client to use when loading rules from S3. Only used (and required) when
|
|
31
|
+
`S3_BUCKET_NAME` and `S3_OBJECT_NAME` are both set. Accepting it as a parameter, rather
|
|
32
|
+
than creating one internally, keeps this function free of AWS calls in unit tests.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
dict[str, Any]
|
|
37
|
+
The parsed rules document, expected to contain a top-level `Rules` key and optionally a
|
|
38
|
+
`ManagerConfig` key. See `awsfindingsmanagerlib.Manager.from_rules_document` for turning this into a
|
|
39
|
+
ready-to-use `Manager`.
|
|
40
|
+
|
|
41
|
+
Raises
|
|
42
|
+
------
|
|
43
|
+
ValueError
|
|
44
|
+
If S3 loading is configured but no `s3_client` was provided.
|
|
45
|
+
"""
|
|
46
|
+
bucket_name = os.environ.get("S3_BUCKET_NAME")
|
|
47
|
+
object_name = os.environ.get("S3_OBJECT_NAME")
|
|
48
|
+
|
|
49
|
+
if bucket_name and object_name:
|
|
50
|
+
if s3_client is None:
|
|
51
|
+
msg = (
|
|
52
|
+
"S3_BUCKET_NAME and S3_OBJECT_NAME are set, but no s3_client was provided "
|
|
53
|
+
"to load_rules()."
|
|
54
|
+
)
|
|
55
|
+
raise ValueError(msg)
|
|
56
|
+
response = s3_client.get_object(Bucket=bucket_name, Key=object_name)
|
|
57
|
+
return safe_load(response["Body"].read())
|
|
58
|
+
|
|
59
|
+
rules_path = os.environ.get("RULES_PATH", "rules.yaml")
|
|
60
|
+
with Path(rules_path).open() as file:
|
|
61
|
+
return safe_load(file)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Standalone scheduled Lambda handler.
|
|
2
|
+
|
|
3
|
+
Loads the configured rules (from S3, or a local file bundled in the deployment package) and
|
|
4
|
+
applies all of them against every currently matching Security Hub finding. Intended to be run on
|
|
5
|
+
a schedule (e.g. via an EventBridge rule) rather than triggered by individual finding or rules
|
|
6
|
+
events; see `awsfindingsmanagerlib.aws_lambda.events`, `.trigger` and `.worker` for the event-driven pipeline
|
|
7
|
+
used by `terraform-aws-mcaf-securityhub-findings-manager`.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from awsfindingsmanagerlib import Manager
|
|
13
|
+
from awsfindingsmanagerlib.aws_lambda.clients import (
|
|
14
|
+
get_s3_client,
|
|
15
|
+
get_securityhub_client,
|
|
16
|
+
)
|
|
17
|
+
from awsfindingsmanagerlib.aws_lambda.logging import get_logger
|
|
18
|
+
from awsfindingsmanagerlib.aws_lambda.rules_backend import load_rules
|
|
19
|
+
|
|
20
|
+
LOGGER = get_logger()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@LOGGER.inject_lambda_context()
|
|
24
|
+
def lambda_handler(_event: dict[str, Any], _context: object) -> None:
|
|
25
|
+
"""Apply all configured suppression rules to every currently matching Security Hub finding.
|
|
26
|
+
|
|
27
|
+
Raises
|
|
28
|
+
------
|
|
29
|
+
RuntimeError
|
|
30
|
+
If not all matched findings could be successfully processed. Check the logged warnings
|
|
31
|
+
above the raised error for which finding(s)/rule(s) were affected.
|
|
32
|
+
"""
|
|
33
|
+
manager = Manager.from_rules_document(
|
|
34
|
+
load_rules(s3_client=get_s3_client()), get_securityhub_client()
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if not manager.get_and_update_all():
|
|
38
|
+
msg = (
|
|
39
|
+
"Not all findings were processed successfully; "
|
|
40
|
+
"see the warnings logged above for details."
|
|
41
|
+
)
|
|
42
|
+
raise RuntimeError(msg)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""S3-triggered Lambda handler.
|
|
2
|
+
|
|
3
|
+
Fired when the rules document is uploaded to S3. Loads the rules and fans each one out as its
|
|
4
|
+
own message on SQS, carrying the shared `ManagerConfig` alongside it, so that the worker Lambda
|
|
5
|
+
(`awsfindingsmanagerlib.aws_lambda.worker`) can build an equivalent single-rule Manager for it.
|
|
6
|
+
|
|
7
|
+
Unlike `events.lambda_handler`, failures here are raised rather than swallowed: there is no
|
|
8
|
+
downstream Step Function branching on this handler's result, and a failure to queue rules should
|
|
9
|
+
be visible (e.g. via a Lambda error metric alarm) rather than silently skipped.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from awsfindingsmanagerlib.aws_lambda.clients import get_s3_client, get_sqs_client
|
|
17
|
+
from awsfindingsmanagerlib.aws_lambda.logging import get_logger
|
|
18
|
+
from awsfindingsmanagerlib.aws_lambda.rules_backend import load_rules
|
|
19
|
+
|
|
20
|
+
LOGGER = get_logger()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@LOGGER.inject_lambda_context()
|
|
24
|
+
def lambda_handler(_event: dict[str, Any], _context: object) -> None:
|
|
25
|
+
"""Load the configured rules and place each one on SQS for the worker Lambda to apply.
|
|
26
|
+
|
|
27
|
+
Reads the destination queue URL from the `SQS_QUEUE_URL` environment variable.
|
|
28
|
+
|
|
29
|
+
Raises
|
|
30
|
+
------
|
|
31
|
+
Exception
|
|
32
|
+
Re-raises any failure to load the rules or send a message to SQS, after logging it.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
rules = load_rules(s3_client=get_s3_client())
|
|
36
|
+
manager_config = rules.get("ManagerConfig", {})
|
|
37
|
+
queue_url = os.environ.get("SQS_QUEUE_URL")
|
|
38
|
+
sqs = get_sqs_client()
|
|
39
|
+
|
|
40
|
+
for rule in rules.get("Rules", []):
|
|
41
|
+
message_body = json.dumps(
|
|
42
|
+
{"ManagerConfig": manager_config, "Rules": [rule]}
|
|
43
|
+
)
|
|
44
|
+
LOGGER.info("Putting rule on SQS. Rule details: %s", message_body)
|
|
45
|
+
sqs.send_message(QueueUrl=queue_url, MessageBody=message_body)
|
|
46
|
+
except Exception:
|
|
47
|
+
LOGGER.exception("Failed putting rule(s) on SQS.")
|
|
48
|
+
raise
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""SQS-triggered Lambda handler.
|
|
2
|
+
|
|
3
|
+
Each SQS record carries a single-rule rules document (as produced by
|
|
4
|
+
`awsfindingsmanagerlib.aws_lambda.trigger`): applies that rule against all currently matching Security Hub
|
|
5
|
+
findings.
|
|
6
|
+
|
|
7
|
+
Failures are reported per record via SQS's partial batch response contract
|
|
8
|
+
(`{"batchItemFailures": [...]}`), not by raising: a failed record becomes visible again after the
|
|
9
|
+
queue's visibility timeout and is retried (and eventually sent to the dead-letter queue per the
|
|
10
|
+
queue's redrive policy) without the whole batch, including already-successful records, being
|
|
11
|
+
reprocessed. This requires `function_response_types = ["ReportBatchItemFailures"]` to be set on
|
|
12
|
+
the event source mapping; without it, Lambda ignores this return value and deletes the entire
|
|
13
|
+
batch regardless of these results.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from awsfindingsmanagerlib import Manager
|
|
20
|
+
from awsfindingsmanagerlib.aws_lambda.clients import get_securityhub_client
|
|
21
|
+
from awsfindingsmanagerlib.aws_lambda.logging import get_logger
|
|
22
|
+
|
|
23
|
+
LOGGER = get_logger()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@LOGGER.inject_lambda_context()
|
|
27
|
+
def lambda_handler(
|
|
28
|
+
event: dict[str, Any], _context: object
|
|
29
|
+
) -> dict[str, list[dict[str, str]]]:
|
|
30
|
+
"""Apply the suppression rule carried by each SQS record to matching Security Hub findings.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
event : dict[str, Any]
|
|
35
|
+
The SQS event. Each `event["Records"]` entry's `body` is expected to be a JSON-encoded
|
|
36
|
+
rules document with exactly one rule in its `Rules` list.
|
|
37
|
+
_context : object
|
|
38
|
+
The Lambda context object (unused).
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
dict[str, list[dict[str, str]]]
|
|
43
|
+
`{"batchItemFailures": [{"itemIdentifier": <messageId>}, ...]}`, naming only the records
|
|
44
|
+
that failed to process. Requires `function_response_types = ["ReportBatchItemFailures"]`
|
|
45
|
+
on the SQS event source mapping to take effect.
|
|
46
|
+
"""
|
|
47
|
+
batch_item_failures: list[dict[str, str]] = []
|
|
48
|
+
|
|
49
|
+
for record in event.get("Records", []):
|
|
50
|
+
try:
|
|
51
|
+
rule = json.loads(record["body"])
|
|
52
|
+
manager = Manager.from_rules_document(rule, get_securityhub_client())
|
|
53
|
+
manager.get_and_update_all()
|
|
54
|
+
except Exception:
|
|
55
|
+
LOGGER.exception(
|
|
56
|
+
"Failed to process rule from SQS record %s.", record.get("messageId")
|
|
57
|
+
)
|
|
58
|
+
batch_item_failures.append({"itemIdentifier": record["messageId"]})
|
|
59
|
+
|
|
60
|
+
return {"batchItemFailures": batch_item_failures}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""The main module of awsfindingsmanagerlib."""
|
|
2
|
+
|
|
3
|
+
import botocore.session
|
|
4
|
+
from botocore.client import BaseClient
|
|
5
|
+
|
|
6
|
+
from .boto_utils import (
|
|
7
|
+
BotoStubCall,
|
|
8
|
+
validate_call_params,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _validate_call_params(
|
|
13
|
+
stub_responses: list[BotoStubCall],
|
|
14
|
+
session_client: BaseClient | None = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
if not session_client:
|
|
17
|
+
session_client = botocore.session.get_session().create_client("securityhub")
|
|
18
|
+
|
|
19
|
+
validate_call_params(stub_responses, session_client)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def validate_filters(filters: dict, session_client: BaseClient | None = None) -> None:
|
|
23
|
+
"""Validate AWS SecurityHub filters to get findings.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
filters : dict
|
|
28
|
+
The filters to validate
|
|
29
|
+
session_client : BaseClient, optional
|
|
30
|
+
A boto session BaseClient for AWS SecurityHub
|
|
31
|
+
Tries to create one if not provided
|
|
32
|
+
|
|
33
|
+
Raises
|
|
34
|
+
------
|
|
35
|
+
botocore.exceptions.ParamValidationError
|
|
36
|
+
If the filters contain invalid values
|
|
37
|
+
"""
|
|
38
|
+
_validate_call_params(
|
|
39
|
+
[
|
|
40
|
+
BotoStubCall(
|
|
41
|
+
method="get_findings",
|
|
42
|
+
service_response={"Findings": []},
|
|
43
|
+
expected_params={"Filters": filters},
|
|
44
|
+
)
|
|
45
|
+
],
|
|
46
|
+
session_client,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def validate_updates(updates: dict, session_client: BaseClient | None = None) -> None:
|
|
51
|
+
"""Validate AWS SecurityHub updates to findings.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
updates : dict
|
|
56
|
+
The updates to make to (a set of) findings
|
|
57
|
+
session_client : BaseClient, optional
|
|
58
|
+
A boto session BaseClient for AWS SecurityHub
|
|
59
|
+
Tries to create one if not provided
|
|
60
|
+
|
|
61
|
+
Raises
|
|
62
|
+
------
|
|
63
|
+
botocore.exceptions.ParamValidationError
|
|
64
|
+
If the updates contain invalid values
|
|
65
|
+
"""
|
|
66
|
+
_validate_call_params(
|
|
67
|
+
[
|
|
68
|
+
BotoStubCall(
|
|
69
|
+
method="batch_update_findings",
|
|
70
|
+
service_response={"ProcessedFindings": [], "UnprocessedFindings": []},
|
|
71
|
+
expected_params=updates,
|
|
72
|
+
)
|
|
73
|
+
],
|
|
74
|
+
session_client,
|
|
75
|
+
)
|