def delete_vulnerability(finding_id: str, vuln_id: str, justification: str,
                         user_email: str) -> bool:
    vulnerability = vuln_dal.get(finding_id, uuid=vuln_id)
    success = False

    if vulnerability and vulnerability[0].get('historic_state'):
        all_states = cast(List[Dict[str, str]],
                          vulnerability[0].get('historic_state'))
        current_state = all_states[-1].get('state')
        if current_state == 'open':
            tzn = pytz.timezone(settings.TIME_ZONE)  # type: ignore
            current_day = datetime.now(
                tz=tzn).today().strftime('%Y-%m-%d %H:%M:%S')
            new_state = {
                'date': current_day,
                'state': 'DELETED',
                'justification': justification,
                'analyst': user_email
            }
            success = vuln_dal.update_state(
                finding_id, str(vulnerability[0].get('UUID', '')),
                'historic_state', [new_state],
                cast(List[Dict[str, str]], vulnerability))

    return success
    def test_get_open_vulnerability_date(self):
        closed_vulnerability = {
            'specific': 'phone',
            'finding_id': '422286126',
            'UUID': '80d6a69f-a376-46be-98cd-2fdedcffdcc0',
            'treatment_manager': '*****@*****.**',
            'historic_state': [{
                'date': '2019-01-08 16:01:26',
                'state': 'closed'
            }],
            'treatment_justification': 'Test 123',
            'vuln_type': 'inputs',
            'treatment': 'IN PROGRESS',
            'where': 'https://example.com',
            'analyst': '*****@*****.**'
        }

        open_vulnerability = vuln_dal.get(
            finding_id='422286126',
            vuln_type='inputs',
            where='https://example.com',
            uuid='80d6a69f-a376-46be-98cd-2fdedcffdcc0')[0]

        test_data = get_open_vulnerability_date(open_vulnerability)
        expected_output = datetime(2018, 9, 28).date()
        assert test_data == expected_output

        test_data = get_open_vulnerability_date(closed_vulnerability)
        assert test_data is None
def add_vuln_to_dynamo(item: Dict[str, str], specific: str, vuln: str,
                       finding_id: str, info) -> bool:
    """Add vulnerability to dynamo."""
    historic_state = []
    last_finding_state = cast(
        List[Dict[str, str]],
        finding_dal.get_finding(finding_id)['historic_treatment'])[-1]
    where = item.get('where', '')
    vulnerability = cast(
        List[Dict[str, str]],
        vuln_dal.get(finding_id,
                     vuln_type=vuln,
                     where=where,
                     specific=specific))
    response = False
    tzn = pytz.timezone(settings.TIME_ZONE)  # type: ignore
    current_day = datetime.now(tz=tzn).today().strftime('%Y-%m-%d %H:%M:%S')
    user_data = cast(UserType, util.get_jwt_content(info.context))
    email = str(user_data['user_email'])
    if vulnerability:
        response = update_vuln_state(info, vulnerability, item, finding_id,
                                     current_day)
    else:
        data: Dict[str, FindingType] = {}
        data['vuln_type'] = vuln
        data['where'] = where
        data['specific'] = specific
        data['finding_id'] = finding_id
        data['UUID'] = str(uuid.uuid4())
        data['treatment'] = 'NEW'
        if last_finding_state['treatment'] != 'NEW':
            data['treatment_manager'] = last_finding_state.get('user', '')
        if item.get('state'):
            if util.is_api_token(user_data):
                historic_state.append({
                    'date':
                    current_day,
                    'state':
                    item.get('state', ''),
                    'origin':
                    item.get('origin', ''),
                    'approval_status':
                    'PENDING',
                    'analyst':
                    'api-{email}'.format(email=email)
                })
            else:
                historic_state.append({
                    'date': current_day,
                    'state': item.get('state', ''),
                    'analyst': email
                })

            data['historic_state'] = historic_state
            response = vuln_dal.create(data)
        else:
            util.cloudwatch_log(
                info.context,
                'Security: Attempted to add vulnerability without state')
    return response
def update_approval_status(finding_id: str, vuln_id: str,
                           approval_status: str) -> bool:
    """ Update vulnerability approval state """
    vulnerability = cast(List[Dict[str, str]],
                         vuln_dal.get(finding_id, uuid=vuln_id))
    response = False

    if vulnerability:
        historic_state = cast(List[Dict[str, str]],
                              vulnerability[0].get('historic_state'))
        last_state = historic_state[-1]
        del vulnerability[0]['historic_state']

        if last_state.get('approval_status') == 'PENDING':
            if approval_status:
                response = \
                    approve_vulnerability(finding_id, historic_state,
                                          last_state, vulnerability)
            else:
                response = reject_vulnerability(finding_id, historic_state,
                                                vulnerability, vuln_id)
        else:
            # vuln approval_status is different than pending
            pass
    else:
        # vuln not found
        pass

    return response
def get(finding_id: str, vuln_id: str):
    vuln = cast(List[Dict[str, FindingType]],
                vuln_dal.get(finding_id, uuid=vuln_id))
    first_vuln = cast(Dict[str, List[Dict[str, str]]], vuln[0])
    if not vuln:
        raise VulnNotFound()
    if first_vuln.get('historic_state', [{}])[-1].get('state',
                                                      '') == 'DELETED':
        raise VulnNotFound()
    return vuln[0]
    def test_is_vulnerability_closed(self):
        closed_vulnerability = {
            'specific':
            'phone',
            'finding_id':
            '422286126',
            'UUID':
            '80d6a69f-a376-46be-98cd-2fdedcffdcc0',
            'treatment_manager':
            '*****@*****.**',
            'historic_state': [{
                'date': '2018-09-28 10:32:58',
                'state': 'open'
            }, {
                'date': '2019-01-08 16:01:26',
                'state': 'closed'
            }],
            'treatment_justification':
            'Test 123',
            'vuln_type':
            'inputs',
            'treatment':
            'IN PROGRESS',
            'where':
            'https://example.com',
            'analyst':
            '*****@*****.**'
        }

        open_vulnerability = vuln_dal.get(
            finding_id='422286126',
            vuln_type='inputs',
            where='https://example.com',
            uuid='80d6a69f-a376-46be-98cd-2fdedcffdcc0')[0]

        assert is_vulnerability_closed(closed_vulnerability)
        assert not is_vulnerability_closed(open_vulnerability)
def update_treatment_vuln(vulnerabilities: List[str], finding_id: str,
                          updated_values: Dict[str,
                                               FindingType], info) -> bool:
    if updated_values.get('acceptance_date'):
        del updated_values['acceptance_date']
    del updated_values['finding_id']
    user_email = util.get_jwt_content(info.context)['user_email']
    updated_vuln_description = []
    for vulnerability in vulnerabilities:
        vuln_info = cast(List[Dict[str, FindingType]],
                         vuln_dal.get(finding_id, uuid=vulnerability))
        new_info = copy.copy(updated_values)
        if new_info.get('tag'):
            new_info['tag'] = cast(List[str], vuln_info[0].get('tag', []))
            for tag in str(updated_values['tag']).split(','):
                validations.validate_field(cast(List[str], tag))
                if tag.strip():
                    cast(List[str], new_info['tag']).append(tag.strip())
            new_info['tag'] = cast(
                # conflict between mypy and pylint -> 'github.com/PyCQA/pylint/issues/2377'
                # pylint: disable=unsubscriptable-object
                List[str],
                list(set(cast(Iterable[Collection[str]], new_info['tag']))))
            new_info['tag'] = [
                html.unescape(tag) for tag in cast(List[str], new_info['tag'])
            ]
        new_info = {
            key: None if not value else value
            for key, value in new_info.items()
        }
        new_info = {
            util.camelcase_to_snakecase(k): new_info.get(k)
            for k in new_info
        }
        result_update_vuln = \
            vuln_dal.update(finding_id,
                            vulnerability,
                            new_info)
        if 'lines' in str(vuln_info[0]['vuln_type']):
            where = 'Path'
            specific = 'Line'
        elif 'ports' in str(vuln_info[0]['vuln_type']):
            where = 'Host'
            specific = 'Port'
        else:
            where = 'URL'
            specific = 'Field'
        mail_description =\
            "<b>{where}:</b>{where_info}&nbsp;&nbsp;&nbsp;\
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>{specific}:</b> {specific_info}"\
            .format(where=where, where_info=vuln_info[0]['where'],
                    specific_info=vuln_info[0]['specific'], specific=specific)
        updated_vuln_description.append(
            {'updated_vuln_description': mail_description})
        if not result_update_vuln:
            util.cloudwatch_log(
                info.context, 'Security: Attempted to update vulnerability\
            :{id} from finding:{finding}'.format(id=vulnerability,
                                                 finding=finding_id))
            return False
        util.cloudwatch_log(
            info.context, 'Security: Updated vulnerability:\
        {id} from finding:{finding} succesfully'.format(id=vulnerability,
                                                        finding=finding_id))
    if updated_values.get('treatment') != 'NEW':
        send_updated_vuln_email(finding_id, user_email,
                                cast(Dict[str, str], updated_values),
                                str(updated_vuln_description))

    return True