Exemplo n.º 1
0
 def resolve_findings(self, findings):
     rids = set()
     for f in findings:
         for r in f['Resources']:
             # Security hub invented some new arn format for a few resources...
             # detect that and normalize to something sane.
             if r['Id'].startswith(
                     'AWS') and r['Type'] == 'AwsIamAccessKey':
                 if 'PrincipalName' in r['Details']['AwsIamAccessKey']:
                     label = r['Details']['AwsIamAccessKey'][
                         'PrincipalName']
                 else:
                     label = r['Details']['AwsIamAccessKey']['UserName']
                 rids.add('arn:{}:iam::{}:user/{}'.format(
                     get_partition(r['Region']), f['AwsAccountId'], label))
             elif not r['Id'].startswith('arn'):
                 if r['Type'] == 'AwsEc2Instance':
                     rids.add('arn:{}:ec2:{}:{}:instance/{}'.format(
                         get_partition(r['Region']), r['Region'],
                         f['AwsAccountId'], r['Id']))
                 else:
                     log.warning("security hub unknown id:%s rtype:%s",
                                 r['Id'], r['Type'])
             else:
                 rids.add(r['Id'])
     return rids
Exemplo n.º 2
0
 def format_envelope(self, r):
     details = {}
     envelope = filter_empty({
         'Id': self.manager.get_arns([r])[0],
         'Region': self.manager.config.region,
         'Tags': {t['Key']: t['Value'] for t in r.get('Tags', [])},
         'Partition': get_partition(self.manager.config.region),
         'Details': {self.resource_type: details},
         'Type': self.resource_type
     })
     return envelope, details
Exemplo n.º 3
0
    def get_variables(self, variables=None):
        """Get runtime variables for policy interpolation.

        Runtime variables are merged with the passed in variables
        if any.
        """
        # Global policy variable expansion, we have to carry forward on
        # various filter/action local vocabularies. Where possible defer
        # by using a format string.
        #
        # See https://github.com/cloud-custodian/cloud-custodian/issues/2330
        if not variables:
            variables = {}

        partition = utils.get_partition(self.options.region)
        if 'mode' in self.data:
            if 'role' in self.data['mode'] and not self.data['mode'][
                    'role'].startswith("arn:aws"):
                self.data['mode']['role'] = "arn:%s:iam::%s:role/%s" % \
                    (partition, self.options.account_id, self.data['mode']['role'])

        variables.update({
            # standard runtime variables for interpolation
            'account': '{account}',
            'account_id': self.options.account_id,
            'partition': partition,
            'region': self.options.region,
            # non-standard runtime variables from local filter/action vocabularies
            #
            # notify action
            'policy': self.data,
            'event': '{event}',
            # mark for op action
            'op': '{op}',
            'action_date': '{action_date}',
            # tag action pyformat-date handling
            'now': utils.FormatDate(datetime.utcnow()),
            # account increase limit action
            'service': '{service}',
            # s3 set logging action :-( see if we can revisit this one.
            'bucket_region': '{bucket_region}',
            'bucket_name': '{bucket_name}',
            'source_bucket_name': '{source_bucket_name}',
            'source_bucket_region': '{source_bucket_region}',
            'target_bucket_name': '{target_bucket_name}',
            'target_prefix': '{target_prefix}',
            'LoadBalancerName': '{LoadBalancerName}'
        })
        return variables
Exemplo n.º 4
0
    def process(self, resources, event=None):
        alias = utils.get_account_alias_from_sts(
            utils.local_session(self.manager.session_factory))
        partition = utils.get_partition(self.manager.config.region)
        message = {
            'event': event,
            'account_id': self.manager.config.account_id,
            'partition': partition,
            'account': alias,
            'version': version,
            'region': self.manager.config.region,
            'execution_id': self.manager.ctx.execution_id,
            'execution_start': self.manager.ctx.start_time,
            'policy': self.manager.data}
        message['action'] = self.expand_variables(message)

        for batch in utils.chunks(resources, self.batch_size):
            message['resources'] = self.prepare_resources(batch)
            receipt = self.send_data_message(message)
            self.log.info("sent message:%s policy:%s template:%s count:%s" % (
                receipt, self.manager.data['name'],
                self.data.get('template', 'default'), len(batch)))
Exemplo n.º 5
0
    def format_resource(self, r):
        details = {}
        for k in r:
            if isinstance(k, (list, dict)):
                continue
            details[k] = r[k]

        for f in self.fields:
            value = jmespath.search(f['expr'], r)
            if not value:
                continue
            details[f['key']] = value

        for k, v in details.items():
            if isinstance(v, datetime):
                v = v.isoformat()
            elif isinstance(v, (list, dict)):
                v = dumps(v)
            elif isinstance(v, (int, float, bool)):
                v = str(v)
            else:
                continue
            details[k] = v[:SECHUB_VALUE_SIZE_LIMIT]

        details['c7n:resource-type'] = self.manager.type
        other = {
            'Type': self.resource_type,
            'Id': self.manager.get_arns([r])[0],
            'Region': self.manager.config.region,
            'Partition': get_partition(self.manager.config.region),
            'Details': {
                self.resource_type: filter_empty(details)
            }
        }
        tags = {t['Key']: t['Value'] for t in r.get('Tags', [])}
        if tags:
            other['Tags'] = tags
        return other
Exemplo n.º 6
0
    def get_finding(self, resources, existing_finding_id, created_at, updated_at):
        policy = self.manager.ctx.policy
        model = self.manager.resource_type
        region = self.data.get('region', self.manager.config.region)

        if existing_finding_id:
            finding_id = existing_finding_id
        else:
            finding_id = '{}/{}/{}/{}'.format(  # nosec
                self.manager.config.region,
                self.manager.config.account_id,
                hashlib.md5(json.dumps(  # nosemgrep
                    policy.data).encode('utf8')).hexdigest(),
                hashlib.md5(json.dumps(list(sorted(  # nosemgrep
                    [r[model.id] for r in resources]))).encode(
                        'utf8')).hexdigest())
        finding = {
            "SchemaVersion": self.FindingVersion,
            "ProductArn": "arn:{}:securityhub:{}::product/cloud-custodian/cloud-custodian".format(
                get_partition(self.manager.config.region),
                region
            ),
            "AwsAccountId": self.manager.config.account_id,
            # Long search chain for description values, as this was
            # made required long after users had policies deployed, so
            # use explicit description, or policy description, or
            # explicit title, or policy name, in that order.
            "Description": self.data.get(
                "description", policy.data.get(
                    "description",
                    self.data.get('title', policy.name))).strip(),
            "Title": self.data.get("title", policy.name),
            'Id': finding_id,
            "GeneratorId": policy.name,
            'CreatedAt': created_at,
            'UpdatedAt': updated_at,
            "RecordState": "ACTIVE",
        }

        severity = {'Product': 0, 'Normalized': 0, 'Label': 'INFORMATIONAL'}
        if self.data.get("severity") is not None:
            severity["Product"] = self.data["severity"]
        if self.data.get("severity_label") is not None:
            severity["Label"] = self.data["severity_label"]
        # severity_normalized To be deprecated per https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format.html#asff-severity # NOQA
        if self.data.get("severity_normalized") is not None:
            severity["Normalized"] = self.data["severity_normalized"]
        if severity:
            finding["Severity"] = severity

        recommendation = {}
        if self.data.get("recommendation"):
            recommendation["Text"] = self.data["recommendation"]
        if self.data.get("recommendation_url"):
            recommendation["Url"] = self.data["recommendation_url"]
        if recommendation:
            finding["Remediation"] = {"Recommendation": recommendation}

        if "confidence" in self.data:
            finding["Confidence"] = self.data["confidence"]
        if "criticality" in self.data:
            finding["Criticality"] = self.data["criticality"]
        if "compliance_status" in self.data:
            finding["Compliance"] = {"Status": self.data["compliance_status"]}
        if "record_state" in self.data:
            finding["RecordState"] = self.data["record_state"]

        fields = {
            'resource': policy.resource_type,
            'ProviderName': 'CloudCustodian',
            'ProviderVersion': version
        }

        if "fields" in self.data:
            fields.update(self.data["fields"])
        else:
            tags = {}
            for t in policy.tags:
                if ":" in t:
                    k, v = t.split(":", 1)
                else:
                    k, v = t, ""
                tags[k] = v
            fields.update(tags)
        if fields:
            finding["ProductFields"] = fields

        finding_resources = []
        for r in resources:
            finding_resources.append(self.format_resource(r))
        finding["Resources"] = finding_resources
        finding["Types"] = list(self.data["types"])

        return filter_empty(finding)