예제 #1
0
    def _update_statements(self, statements: list, is_cf=False) -> list:
        """
        Replace partition, region and account ID placeholders in resources
        with specific values.
        """

        if not statements:
            return []

        updated = []
        for statement in statements:
            resources = statement.get("Resource")
            if not resources:
                resources = "*"
            resources = make_list(resources)

            statement["Resource"] = []

            for resource in resources:
                if is_cf:
                    if "AWS::" in resource:
                        resource = {"Fn::Sub": resource}
                else:
                    resource = (resource.replace("${AWS::Partition}",
                                                 self.partition).replace(
                                                     "${AWS::Region}",
                                                     self.region).replace(
                                                         "${AWS::AccountId}",
                                                         self.account_id))
                statement["Resource"].append(resource)
            updated.append(statement)

        return updated
예제 #2
0
 def principal_service(self) -> list:
     """
     Return any service principals from the role
     """
     principal = self.role_dict.get("settings", {}).get("principal_service")
     if not principal:
         return []
     return make_list(principal)
예제 #3
0
def is_finding_filtered(finding, minimum_severity="LOW"):
    # Return True if the finding should be filtered (ie. return False if it should be displayed)
    minimum_severity = minimum_severity.upper()
    severity_choices = ["MUTE", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"]
    if severity_choices.index(
            finding.severity) < severity_choices.index(minimum_severity):
        return True

    if finding.ignore_locations:
        # The ignore_locations element looks like this:
        #
        # ignore_locations:
        # - filepath: "test.json"
        #   action: "s3:GetObject"
        #   resource:
        #   - "a"
        #   - "b"
        # - action: "s3:GetObject"
        #   resource:
        #    - "c.*"
        #
        # Assuming the finding has these types of values in the `location` element,
        # this will ignore any finding that matches the filepath to "test.json"
        # AND action to "s3:GetObject"
        # AND the resource to "a" OR "b"
        # It will also ignore a resource that matches "c.*".

        for ignore_location in finding.ignore_locations:
            all_match = True
            for location_type, locations_to_ignore in ignore_location.items():
                has_array_match = False
                for location_to_ignore in make_list(locations_to_ignore):
                    if re.fullmatch(
                            location_to_ignore.lower(),
                            str(finding.location.get(location_type,
                                                     "")).lower(),
                    ):
                        has_array_match = True
                if not has_array_match:
                    all_match = False
            if all_match:
                return True
    return False
예제 #4
0
def audit(policy):
    services = set()
    for stmt in policy.statements:
        actions = make_list(stmt.stmt["Action"])
        for action in actions:
            prefix = action.split(":")[0]
            if prefix == "*":
                continue
            services.add(prefix.lower())

    print(f"SERVICES: {services}")

    denied_services_in_policy = []
    for service in services:
        if service in DENIED_SERVICES:
            denied_services_in_policy.append(service)

    if denied_services_in_policy:
        policy.add_finding(
            "DENIED_SERVICE",
            "Services are not approved for use",
            location={"Action": denied_services_in_policy},
        )
예제 #5
0
def simulate_statement(client, account_id: str, statement: object) -> list:
    """
    Simulate a policy statement using the SimulateCustomPolicy API
    """

    all_actions = set()
    actions = make_list(statement.stmt.get("Action", []))
    for action in actions:
        expanded_actions = expand_action(action, raise_exceptions=False)
        for action_struct in expanded_actions:
            all_actions.add(action_struct["service"] + ":" +
                            action_struct["action"])

    resources = make_list(statement.stmt.get("Resource", []))
    if not resources:
        resources = ["*"]
    if len(resources) > 1 and "*" in resources:
        resources.remove("*")

    policies = [
        json.dumps({
            "Version": "2012-10-17",
            "Statement": statement.stmt
        })
    ]

    response = client.simulate_custom_policy(
        PolicyInputList=policies,
        ActionNames=sorted(all_actions),
        ResourceArns=resources,
        ResourceOwner=f"arn:aws:iam::{account_id}:root",
    )

    LOGGER.info(f"SimulateResponse = {response}")

    findings = []

    for result in response.get("EvaluationResults", []):

        action = result.get("EvalActionName")
        is_action_denied = result.get("EvalDecision",
                                      "implicitDeny") in DENY_RESULT
        is_org_allowed = result.get("OrganizationDecisionDetail",
                                    {}).get("AllowedByOrganizations")
        is_boundary_allowed = result.get(
            "PermissionsBoundaryDecisionDetail",
            {}).get("AllowedByPermissionsBoundary")

        denied_resources = [
            resource for resource in result.get("ResourceSpecificResults", [])
            if resource.get("EvalResourceDecision", "implicitDeny") in
            DENY_RESULT
        ]

        if denied_resources:
            resource_names = [
                resource.get("EvalResourceName")
                for resource in denied_resources
            ]

            findings.append(
                Finding(
                    "DENIED_POLICY",
                    detail=
                    f"API {action} was denied because the API is approved but not for the following resources requested:",
                    location={"Resource": resource_names},
                ))

        elif is_action_denied:
            if is_org_allowed is False:
                findings.append(
                    Finding(
                        "DENIED_POLICY",
                        detail=
                        f"API {action} was denied by an organizational policy",
                        location={"Action": action},
                    ))
            elif is_boundary_allowed is False:
                findings.append(
                    Finding(
                        "DENIED_POLICY",
                        detail=
                        f"API {action} was denied by a permission boundary policy",
                        location={"Action": action},
                    ))
            else:
                findings.append(
                    Finding(
                        "DENIED_POLICY",
                        detail=
                        f"API {action} was denied because it is not in the approved API list",
                        location={"Action": action},
                    ))
            continue

    return findings