Ejemplo n.º 1
0
    def test_print_stats_failed_conditions_zero_and_multiple_secrets_per_condition(
        self,
        capsys,
        live_secrets_fixture,
        unaudited_secrets_fixture,
        audited_real_secrets_fixture,
    ):
        unaudited_secrets_fixture = audited_real_secrets_fixture = []

        with self.mock_env():
            print_stats(
                live_secrets_fixture,
                unaudited_secrets_fixture,
                audited_real_secrets_fixture,
                baseline_filename,
                True,
                True,
                True,
            )
            secrets = audit.get_secrets_list_from_file(baseline_filename)

        captured = capsys.readouterr()

        assert captured.out == '\n{} potential secrets in {} were reviewed.'.format(
            colorize(len(secrets), AnsiColor.BOLD),
            colorize(baseline_filename, AnsiColor.BOLD),
        ) + ' Found {} live secret, {} unaudited secrets'.format(
            colorize(len(live_secrets_fixture), AnsiColor.BOLD),
            colorize(len(unaudited_secrets_fixture), AnsiColor.BOLD),
        ) + ' and {} secrets that were audited as real.\n\n'.format(
            colorize(len(audited_real_secrets_fixture), AnsiColor.BOLD), )
Ejemplo n.º 2
0
    def test_print_summary_all_failed_conditions(self, capsys):
        unaudited_return_code = live_return_code = audited_real_return_code = 1

        print_summary(
            unaudited_return_code,
            live_return_code,
            audited_real_return_code,
            baseline_filename,
            True,
            True,
            True,
            False,
        )

        captured = capsys.readouterr()

        assert captured.out == '\nFailed conditions:\n\n{}\n{}\n{}\n{}\n{}\n{}{}{}\n{}\n'.format(
            colorize('\t- Unaudited secrets were found', AnsiColor.BOLD),
            '\n\t\tRun detect-secrets audit {}, and audit all potential secrets.\n'
            .format(baseline_filename, ),
            colorize('\t- Live secrets were found', AnsiColor.BOLD),
            '\n\t\tRevoke all live secrets and remove them from the codebase.'
            ' Afterwards, run detect-secrets scan --update {} to re-scan.\n'.
            format(baseline_filename, ),
            colorize('\t- Audited true secrets were found', AnsiColor.BOLD),
            '\n\t\tIf any active secrets meet this condition, revoke them.',
            ' Then, remove secrets that were audited as real from the codebase and',
            ' run detect-secrets scan --update {} to re-scan.\n'.format(
                baseline_filename, ),
            'For additional help, run detect-secrets audit --help.\n',
        )
Ejemplo n.º 3
0
    def test_print_stats_only_audited_real(
        self,
        capsys,
        live_secrets_fixture,
        unaudited_secrets_fixture,
        audited_real_secrets_fixture,
    ):
        unaudited_secrets_fixture = live_secrets_fixture = []

        with self.mock_env():
            print_stats(
                live_secrets_fixture,
                unaudited_secrets_fixture,
                audited_real_secrets_fixture,
                baseline_filename,
                False,
                False,
                True,
            )
            secrets = audit.get_secrets_list_from_file(baseline_filename)

        captured = capsys.readouterr()

        assert captured.out == '\n{} potential secrets in {} were reviewed.'.format(
            colorize(len(secrets), AnsiColor.BOLD),
            colorize(baseline_filename, AnsiColor.BOLD),
        ) + ' Found {} secret that was audited as real.\n\n'.format(
            colorize(len(audited_real_secrets_fixture), AnsiColor.BOLD), )
Ejemplo n.º 4
0
    def test_print_stats_no_failed_conditions(
        self,
        capsys,
        live_secrets_fixture,
        unaudited_secrets_fixture,
        audited_real_secrets_fixture,
    ):
        live_secrets_fixture = unaudited_secrets_fixture = audited_real_secrets_fixture = []

        with self.mock_env():
            print_stats(
                live_secrets_fixture,
                unaudited_secrets_fixture,
                audited_real_secrets_fixture,
                baseline_filename,
                True,
                True,
                True,
            )
            secrets = audit.get_secrets_list_from_file(baseline_filename)

        captured = capsys.readouterr()

        assert captured.out == ('\n{} potential secrets in {} were reviewed.'
                                ' All checks have passed.\n\n'.format(
                                    colorize(len(secrets), AnsiColor.BOLD),
                                    colorize(baseline_filename,
                                             AnsiColor.BOLD),
                                ))
Ejemplo n.º 5
0
def print_line(name, time, baseline, timeout):
    """
    :type name: str

    :type time: float
    :param time: seconds it took to execute

    :type baseline: float
    :param baseline: expected seconds to execute

    :type timeout: float
    :param timeout: used to calculate difference when either current
        execution or baseline execution exceeds timeout.
    """
    if not time:
        time_string = 'Timeout exceeded!'
    else:
        time_string = '{}s'.format(str(time))

    if baseline is not None:
        if time and baseline:
            difference = round(baseline - time, 2)
        elif time:
            # This handles the case when the baseline execution exceeds timeout
            difference = round(timeout - time, 2)
        elif baseline:
            # This handles the case when this current execution exceeds timeout
            difference = round(timeout - baseline, 2)
        else:
            # They both failed.
            difference = 0

        if difference > 0:
            difference_string = colorize(
                '▲  {}'.format(difference),
                AnsiColor.LIGHT_GREEN,
            )
            difference_string = '{:>22s}'.format(difference_string)
        elif difference < 0:
            difference_string = colorize(
                '▼ {}'.format(difference),
                AnsiColor.RED,
            )
            difference_string = '{:>22s}'.format(difference_string)
        else:
            difference_string = '{:>10s}'.format('-')

        print(
            '{:<25s}{:^20s}{}'.format(
                name,
                time_string,
                difference_string,
            ),
        )
    else:
        print('{:<25s}{:>20s}'.format(name, time_string))
Ejemplo n.º 6
0
def print_stats(
    live_secrets: List,
    unaudited_secrets: List,
    audited_real_secrets: List,
    baseline_filename: str,
    fail_on_live: bool,
    fail_on_unaudited: bool,
    fail_on_audited_real: bool,
) -> None:
    """
    Given lists of secrets which failed certain conditions and a baseline file name,
    print a sentence summarizing aggregate stats.
    """
    secrets = audit.get_secrets_list_from_file(baseline_filename)

    secrets_failing_conditions = live_secrets + unaudited_secrets + audited_real_secrets

    if len(secrets_failing_conditions) == 0:
        print(
            '\n{} potential secrets in {} were reviewed.'
            ' All checks have passed.\n'.format(
                colorize(len(secrets), AnsiColor.BOLD),
                colorize(baseline_filename, AnsiColor.BOLD),
            ), )
        return

    stats = '\n{} potential secrets in {} were reviewed. Found'.format(
        colorize(len(secrets), AnsiColor.BOLD),
        colorize(baseline_filename, AnsiColor.BOLD),
    )

    if fail_on_live:
        stats += ' {} live secret{}{}'.format(
            colorize(len(live_secrets), AnsiColor.BOLD),
            's' if len(live_secrets) > 1 or len(live_secrets) == 0 else '',
            '.\n'
            if not fail_on_unaudited and not fail_on_audited_real else '',
        )

    if fail_on_unaudited:
        stats += '{}{} {} unaudited secret{}{}'.format(
            ',' if fail_on_audited_real and fail_on_live else '',
            ' and' if not fail_on_audited_real and fail_on_live else '',
            colorize(len(unaudited_secrets), AnsiColor.BOLD),
            's' if len(unaudited_secrets) > 1 or len(unaudited_secrets) == 0
            else '',
            '.\n' if not fail_on_audited_real else '',
        )

    if fail_on_audited_real:
        stats += ' {}{} secret{} that {} audited as real.\n'.format(
            'and ' if fail_on_live or fail_on_unaudited else '',
            colorize(len(audited_real_secrets), AnsiColor.BOLD),
            's' if len(audited_real_secrets) > 1
            or len(audited_real_secrets) == 0 else '',
            'were' if len(audited_real_secrets) > 1
            or len(audited_real_secrets) == 0 else 'was',
        )

    print(stats)
Ejemplo n.º 7
0
    def test_print_summary_all_failed_conditions_omit_instructions(
            self, capsys):
        unaudited_return_code = live_return_code = audited_real_return_code = 1

        print_summary(
            unaudited_return_code,
            live_return_code,
            audited_real_return_code,
            baseline_filename,
            True,
            True,
            True,
            True,
        )

        captured = capsys.readouterr()

        assert captured.out == '\nFailed conditions:\n\n{}\n\n{}\n\n{}\n\n'.format(
            colorize('\t- Unaudited secrets were found', AnsiColor.BOLD),
            colorize('\t- Live secrets were found', AnsiColor.BOLD),
            colorize('\t- Audited true secrets were found', AnsiColor.BOLD),
        )
Ejemplo n.º 8
0
    def test_print_summary_no_failed_conditions(self, capsys):
        unaudited_return_code = live_return_code = audited_real_return_code = 0

        print_summary(
            unaudited_return_code,
            live_return_code,
            audited_real_return_code,
            baseline_filename,
            True,
            True,
            True,
            True,
        )

        captured = capsys.readouterr()

        assert captured.out == '{}\n\n{}\n\n{}\n\n'.format(
            colorize('\t- No unaudited secrets were found', AnsiColor.BOLD),
            colorize('\t- No live secrets were found', AnsiColor.BOLD),
            colorize('\t- No secrets that were audited as real were found',
                     AnsiColor.BOLD),
        )
Ejemplo n.º 9
0
    def test_print_summary_only_unaudited_pass(self, capsys):
        unaudited_return_code = live_return_code = audited_real_return_code = 0

        print_summary(
            unaudited_return_code,
            live_return_code,
            audited_real_return_code,
            baseline_filename,
            False,
            True,
            False,
            False,
        )

        captured = capsys.readouterr()

        assert captured.out == '{}\n\n'.format(
            colorize('\t- No unaudited secrets were found', AnsiColor.BOLD), )
Ejemplo n.º 10
0
    def test_print_summary_only_unaudited_fail(self, capsys):
        unaudited_return_code = live_return_code = audited_real_return_code = 1

        print_summary(
            unaudited_return_code,
            live_return_code,
            audited_real_return_code,
            baseline_filename,
            False,
            True,
            False,
            False,
        )

        captured = capsys.readouterr()

        assert captured.out == '\nFailed conditions:\n\n{}\n{}\n{}\n'.format(
            colorize('\t- Unaudited secrets were found', AnsiColor.BOLD),
            '\n\t\tRun detect-secrets audit {}, and audit all potential secrets.'
            .format(baseline_filename, ),
            '\nFor additional help, run detect-secrets audit --help.\n',
        )
Ejemplo n.º 11
0
    def test_print_summary_only_live_fail(self, capsys):
        unaudited_return_code = live_return_code = audited_real_return_code = 1

        print_summary(
            unaudited_return_code,
            live_return_code,
            audited_real_return_code,
            baseline_filename,
            True,
            False,
            False,
            False,
        )

        captured = capsys.readouterr()

        assert captured.out == '\nFailed conditions:\n\n{}\n{}\n{}\n'.format(
            colorize('\t- Live secrets were found', AnsiColor.BOLD),
            '\n\t\tRevoke all live secrets and remove them from the codebase.'
            ' Afterwards, run detect-secrets scan --update {} to re-scan.'.
            format(baseline_filename, ),
            '\nFor additional help, run detect-secrets audit --help.\n',
        )
Ejemplo n.º 12
0
    def test_print_stats_failed_conditions_multiple_secrets_per_condition(
        self,
        capsys,
        live_secrets_fixture,
        unaudited_secrets_fixture,
        audited_real_secrets_fixture,
    ):
        modified_baseline = deepcopy(self.baseline)
        modified_baseline['results']['filenameA'][0]['is_secret'] = True
        modified_baseline['results']['filenameA'][1]['is_secret'] = None
        modified_baseline['results']['filenameA'].append(
            {
                'hashed_secret': 'd',
                'line_number': 150,
                'type': 'Private key',
                'is_secret': None,
            }, )
        modified_baseline['results']['filenameB'][0]['is_verified'] = True
        modified_baseline['results']['filenameB'].append(
            {
                'hashed_secret': 'e',
                'line_number': 185,
                'type': 'Hex High Entropy String',
                'is_verified': True,
            }, )
        modified_baseline['results']['filenameB'].append(
            {
                'hashed_secret': 'f',
                'line_number': 200,
                'type': 'Hex High Entropy String',
                'is_secret': True,
            }, )

        live_secrets_fixture.append(
            {
                'failed_condition': ReportSecretType.LIVE.value,
                'filename': baseline_filename,
                'line': 180,
                'type': 'Private key',
            }, )
        unaudited_secrets_fixture.append(
            {
                'failed_condition': ReportSecretType.UNAUDITED.value,
                'filename': baseline_filename,
                'line': 150,
                'type': 'Hex High Entropy String',
            }, )
        audited_real_secrets_fixture.append(
            {
                'failed_condition': ReportSecretType.AUDITED_REAL.value,
                'filename': baseline_filename,
                'line': 200,
                'type': 'Hex High Entropy String',
            }, )

        with self.mock_env(baseline=modified_baseline):
            print_stats(
                live_secrets_fixture,
                unaudited_secrets_fixture,
                audited_real_secrets_fixture,
                baseline_filename,
                True,
                True,
                True,
            )
            secrets = audit.get_secrets_list_from_file(baseline_filename)

        captured = capsys.readouterr()

        assert captured.out == '\n{} potential secrets in {} were reviewed.'.format(
            colorize(len(secrets), AnsiColor.BOLD),
            colorize(baseline_filename, AnsiColor.BOLD),
        ) + ' Found {} live secrets, {} unaudited secrets'.format(
            colorize(len(live_secrets_fixture), AnsiColor.BOLD),
            colorize(len(unaudited_secrets_fixture), AnsiColor.BOLD),
        ) + ' and {} secrets that were audited as real.\n\n'.format(
            colorize(len(audited_real_secrets_fixture), AnsiColor.BOLD), )
Ejemplo n.º 13
0
def _get_secret_with_context(
    filename,
    secret,
    plugins_used,
    custom_plugin_paths,
    lines_of_context=5,
    force_line_printing=False,
):
    """
    Displays the secret, with surrounding lines of code for better context.

    :type filename: str
    :param filename: filename where secret resides in

    :type secret: dict, PotentialSecret.json() format
    :param secret: the secret listed in baseline

    :type plugins_used: list
    :param plugins_used: output of "plugins_used" in baseline. e.g.
        >>> [
        ...     {
        ...         'name': 'Base64HighEntropyString',
        ...         'base64_limit': 4.5,
        ...     },
        ... ]

    :type custom_plugin_paths: Tuple[str]
    :param custom_plugin_paths: possibly empty tuple of paths that have custom plugins.

    :type lines_of_context: int
    :param lines_of_context: number of lines displayed before and after
        secret.

    :type force_line_printing: bool
    :param force_line_printing: if True, will print the lines of code even if it doesn't
        find the secret expected

    :raises: SecretNotFoundOnSpecifiedLineError
    """

    try:
        file_content = _open_file_with_cache(filename)
        if not file_content:
            raise SecretNotFoundOnSpecifiedLineError(secret['line_number'])

        file_lines = file_content.splitlines()

        snippet = CodeSnippetHighlighter().get_code_snippet(
            file_lines,
            secret['line_number'],
            lines_of_context=lines_of_context,
        )

        raw_secret_value = get_raw_secret_value(
            secret=secret,
            plugins_used=plugins_used,
            custom_plugin_paths=custom_plugin_paths,
            file_handle=io.StringIO(file_content),
            filename=filename,
        )

        try:
            snippet.highlight_line(raw_secret_value)
        except ValueError:
            raise SecretNotFoundOnSpecifiedLineError(secret['line_number'])
    except SecretNotFoundOnSpecifiedLineError:
        if not force_line_printing:
            raise

        snippet.target_line = colorize(
            snippet.target_line,
            AnsiColor.BOLD,
        )

    return snippet.add_line_numbers()
Ejemplo n.º 14
0
def _print_context(  # pragma: no cover
    filename,
    secret,
    count,
    total,
    plugins_used,
    custom_plugin_paths,
    additional_header_lines=None,
    force_line_printing=False,
):
    """
    :type filename: str
    :param filename: the file currently scanned.

    :type secret: dict, in PotentialSecret.json() format
    :param secret: the secret, represented in the baseline file.

    :type count: int
    :param count: current count of secrets scanned so far

    :type total: int
    :param total: total number of secrets in baseline

    :type plugins_used: list
    :param plugins_used: output of "plugins_used" in baseline. e.g.
        >>> [
        ...     {
        ...         'name': 'Base64HighEntropyString',
        ...         'base64_limit': 4.5,
        ...     },
        ... ]

    :type custom_plugin_paths: Tuple[str]
    :param custom_plugin_paths: possibly empty tuple of paths that have custom plugins.

    :type additional_header_lines: str
    :param additional_header_lines: any additional lines to add to the
        header of the interactive audit display.

    :type force_line_printing: bool
    :param force_line_printing: if True, will print the lines of code even if it doesn't
        find the secret expected

    :raises: SecretNotFoundOnSpecifiedLineError
    """
    print(
        '{} {} {} {}\n{} {}\n{} {}'.format(
            colorize('Secret:     ', AnsiColor.BOLD),
            colorize(str(count), AnsiColor.PURPLE),
            colorize('of', AnsiColor.BOLD),
            colorize(str(total), AnsiColor.PURPLE),
            colorize('Filename:   ', AnsiColor.BOLD),
            colorize(filename, AnsiColor.PURPLE),
            colorize('Secret Type:', AnsiColor.BOLD),
            colorize(secret['type'], AnsiColor.PURPLE),
        ),
    )
    if additional_header_lines:
        print(additional_header_lines)

    print('-' * 10)

    error_obj = None
    try:
        secret_with_context = _get_secret_with_context(
            filename=filename,
            secret=secret,
            plugins_used=plugins_used,
            custom_plugin_paths=custom_plugin_paths,
            force_line_printing=force_line_printing,
        )
        print(secret_with_context)
    except SecretNotFoundOnSpecifiedLineError as e:
        error_obj = e
        print(e)

    print('-' * 10)

    if error_obj:
        raise error_obj
Ejemplo n.º 15
0
def compare_baselines(old_baseline_filename, new_baseline_filename):
    """
    This function enables developers to more easily configure plugin
    settings, by comparing two generated baselines and highlighting
    their differences.

    For effective use, a few assumptions are made:
        1. Baselines are sorted by (filename, line_number, hash).
           This allows for a deterministic order, when doing a side-by-side
           comparison.

        2. Baselines are generated for the same codebase snapshot.
           This means that we won't have cases where secrets are moved around;
           only added or removed.

    Note: We don't want to do a version check, because we want to be able to
    use this functionality across versions (to see how the new version fares
    compared to the old one).
    """
    if old_baseline_filename == new_baseline_filename:
        raise RedundantComparisonError

    old_baseline = _get_baseline_from_file(old_baseline_filename)
    new_baseline = _get_baseline_from_file(new_baseline_filename)

    _remove_nonexistent_files_from_baseline(old_baseline)
    _remove_nonexistent_files_from_baseline(new_baseline)

    # We aggregate the secrets first, so that we can display a total count.
    secrets_to_compare = _get_secrets_to_compare(old_baseline, new_baseline)
    total_reviews = len(secrets_to_compare)
    current_index = 0

    secret_iterator = BidirectionalIterator(secrets_to_compare)
    for filename, secret, is_removed in secret_iterator:
        _clear_screen()
        current_index += 1

        header = '{}      {}'
        if is_removed:
            plugins_used = old_baseline['plugins_used']
            custom_plugin_paths = old_baseline['custom_plugin_paths']
            header = header.format(
                colorize('Status:', AnsiColor.BOLD),
                '>> {} <<'.format(
                    colorize('REMOVED', AnsiColor.RED),
                ),
            )
        else:
            plugins_used = new_baseline['plugins_used']
            custom_plugin_paths = new_baseline['custom_plugin_paths']
            header = header.format(
                colorize('Status:', AnsiColor.BOLD),
                '>> {} <<'.format(
                    colorize('ADDED', AnsiColor.LIGHT_GREEN),
                ),
            )

        try:
            _print_context(
                filename=filename,
                secret=secret,
                count=current_index,
                total=total_reviews,
                plugins_used=plugins_used,
                custom_plugin_paths=custom_plugin_paths,
                additional_header_lines=header,
                force_line_printing=is_removed,
            )
            decision = _get_user_decision(
                can_step_back=secret_iterator.can_step_back(),
                prompt_secret_decision=False,
            )
        except SecretNotFoundOnSpecifiedLineError:
            decision = _get_user_decision(prompt_secret_decision=False)

        if decision == 'q':
            print('Quitting...')
            break

        if decision == 'b':  # pragma: no cover
            current_index -= 2
            secret_iterator.step_back_on_next_iteration()
Ejemplo n.º 16
0
def print_summary(
    unaudited_return_code: int,
    live_return_code: int,
    audited_real_return_code: int,
    baseline_filename: str,
    fail_on_live: bool,
    fail_on_unaudited: bool,
    fail_on_audited_real: bool,
    omit_instructions=False,
) -> None:
    """
    Prints information about failed checks in a report,
    as well as how to remediate them.

    Instructions can optionally be omitted.
    """

    if unaudited_return_code == live_return_code == audited_real_return_code == 0:
        if fail_on_unaudited:
            print(
                '{}\n'.format(
                    colorize('\t- No unaudited secrets were found',
                             AnsiColor.BOLD), ), )

        if fail_on_live:
            print(
                '{}\n'.format(
                    colorize('\t- No live secrets were found',
                             AnsiColor.BOLD), ), )

        if fail_on_audited_real:
            print(
                '{}\n'.format(
                    colorize(
                        '\t- No secrets that were audited as real were found',
                        AnsiColor.BOLD), ), )
        return

    print('\nFailed conditions:\n')

    if fail_on_unaudited and unaudited_return_code != 0:
        print(
            '{}\n'.format(
                colorize('\t- Unaudited secrets were found',
                         AnsiColor.BOLD), ), )
        if omit_instructions is False:
            print(
                '\t\tRun detect-secrets audit {}, and audit all potential secrets.\n'
                .format(baseline_filename, ), )
    if fail_on_live and live_return_code != 0:
        print(
            '{}\n'.format(
                colorize('\t- Live secrets were found', AnsiColor.BOLD), ), )
        if omit_instructions is False:
            print(
                '\t\tRevoke all live secrets and remove them from the codebase.'
                ' Afterwards, run detect-secrets scan --update {} to re-scan.\n'
                .format(baseline_filename, ), )

    if fail_on_audited_real and audited_real_return_code != 0:
        print(
            '{}\n'.format(
                colorize('\t- Audited true secrets were found',
                         AnsiColor.BOLD), ), )
        if omit_instructions is False:
            print(
                '\t\tIf any active secrets meet this condition, revoke them.'
                ' Then, remove secrets that were audited as real from the codebase'
                ' and run detect-secrets scan --update {} to re-scan.\n'.
                format(baseline_filename), )

    if omit_instructions is False:
        print('For additional help, run detect-secrets audit --help.\n')