Beispiel #1
0
    def test_runs_flag_for_human_review(self, flag_for_human_review_mock):
        self.scanner_rule.update(action=FLAG_FOR_HUMAN_REVIEW)

        ScannerResult.run_action(self.version)

        assert flag_for_human_review_mock.called
        flag_for_human_review_mock.assert_called_with(self.version)
Beispiel #2
0
    def test_returns_when_no_action_found(self, log_mock):
        self.scanner_rule.delete()

        ScannerResult.run_action(self.version)

        log_mock.assert_called_with('No action to execute for version %s.',
                                    self.version.id)
Beispiel #3
0
    def test_runs_no_action(self, no_action_mock):
        self.scanner_rule.update(action=NO_ACTION)

        ScannerResult.run_action(self.version)

        assert no_action_mock.called
        no_action_mock.assert_called_with(self.version)
Beispiel #4
0
    def test_runs_delay_auto_approval(self, _delay_auto_approval_mock):
        self.scanner_rule.update(action=DELAY_AUTO_APPROVAL)

        ScannerResult.run_action(self.version)

        assert _delay_auto_approval_mock.called
        _delay_auto_approval_mock.assert_called_with(self.version)
Beispiel #5
0
    def test_flags_for_human_review_by_mad_when_score_is_too_high(self):
        version = version_factory(addon=addon_factory())
        results = {'scanners': {'customs': {'score': 0.99}}}
        ScannerResult.objects.create(version=version,
                                     scanner=MAD,
                                     results=results)

        ScannerResult.run_action(version)

        assert version.reviewerflags.needs_human_review_by_mad
    def test_handle_revert_report(self):
        # Create one entry with matches
        rule = ScannerRule.objects.create(name='some-rule', scanner=YARA)
        result = ScannerResult(scanner=YARA,
                               version=version_factory(addon=addon_factory()))
        result.add_yara_result(rule=rule.name)
        result.state = TRUE_POSITIVE
        result.save()
        assert result.state == TRUE_POSITIVE

        response = self.client.post(
            reverse('admin:scanners_scannerresult_handlerevert',
                    args=[result.pk]),
            follow=True,
        )

        result.refresh_from_db()
        assert result.state == UNKNOWN
        # The action should send a redirect.
        last_url, status_code = response.redirect_chain[-1]
        assert status_code == 302
        # The action should redirect to the list view and the default list
        # filters should show the result (because its state is UNKNOWN again).
        html = pq(response.content)
        assert html('#result_list tbody tr').length == 1
        # A confirmation message should also appear.
        assert html('.messagelist .info').length == 1
Beispiel #7
0
    def test_does_not_flag_for_human_review_by_mad_when_score_is_okay(self):
        version = version_factory(addon=addon_factory())
        results = {'scanners': {'customs': {'score': 0.2}}}
        ScannerResult.objects.create(version=version,
                                     scanner=MAD,
                                     results=results)

        ScannerResult.run_action(version)

        with self.assertRaises(VersionReviewerFlags.DoesNotExist):
            version.reviewerflags
Beispiel #8
0
    def test_selects_the_action_with_the_highest_severity(
            self, flag_for_human_review_mock, no_action_mock):
        # Create another rule and add it to the current scanner result. This
        # rule is more severe than `rule-1` created in `setUp()`.
        rule = ScannerRule.objects.create(name='rule-2',
                                          scanner=self.scanner,
                                          action=FLAG_FOR_HUMAN_REVIEW)
        self.scanner_result.matched_rules.add(rule)

        ScannerResult.run_action(self.version)

        assert not no_action_mock.called
        assert flag_for_human_review_mock.called
Beispiel #9
0
    def test_flags_for_human_review_by_mad_when_models_disagree(self):
        version = version_factory(addon=addon_factory())
        results = {
            'scanners': {
                'customs': {'result_details': {'models_agree': False}}
            }
        }
        ScannerResult.objects.create(
            version=version, scanner=MAD, results=results
        )

        ScannerResult.run_action(version)

        assert version.versionreviewerflags.needs_human_review_by_mad
    def test_handle_yara_false_positive(self):
        # Create one entry with matches
        rule = ScannerRule.objects.create(name='some-rule', scanner=YARA)
        result = ScannerResult(scanner=YARA)
        result.add_yara_result(rule=rule.name)
        result.save()
        assert result.state == UNKNOWN

        response = self.client.post(
            reverse(
                'admin:scanners_scannerresult_handlefalsepositive',
                args=[result.pk],
            ))

        result.refresh_from_db()
        assert result.state == FALSE_POSITIVE
        # This action should send a redirect to GitHub.
        assert response.status_code == 302
        # We create a GitHub issue draft by passing some query parameters to
        # GitHub.
        assert response['Location'].startswith(
            'https://github.com/git/repo/issues/new?')
        assert (urlencode({
            'title':
            'False positive report for '
            'ScannerResult {}'.format(result.pk)
        }) in response['Location'])
        assert urlencode({'body': '### Report'}) in response['Location']
        assert (urlencode({'labels': 'false positive report'})
                in response['Location'])
        assert 'Raw+scanner+results' in response['Location']
Beispiel #11
0
    def test_unlisted_channel(self):
        version = version_factory(
            addon=addon_factory(), channel=amo.RELEASE_CHANNEL_UNLISTED
        )
        result = ScannerResult(version=version)

        assert self.admin.channel(result) == 'Unlisted'
Beispiel #12
0
    def test_formatted_results(self):
        results = {'some': 'results'}
        result = ScannerResult(results=results)

        assert self.admin.formatted_results(result) == format_html(
            '<pre>{}</pre>', json.dumps(results, indent=2)
        )
Beispiel #13
0
    def test_selects_active_actions_only(self, flag_for_human_review_mock,
                                         no_action_mock):
        # Create another rule and add it to the current scanner result. This
        # rule is more severe than `rule-1` created in `setUp()`. In this test
        # case, we disable this rule, though.
        rule = ScannerRule.objects.create(
            name='rule-2',
            scanner=self.scanner,
            action=FLAG_FOR_HUMAN_REVIEW,
            is_active=False,
        )
        self.scanner_result.matched_rules.add(rule)

        ScannerResult.run_action(self.version)

        assert no_action_mock.called
        assert not flag_for_human_review_mock.called
 def test_handle_revert_report_and_non_admin_user(self):
     result = ScannerResult(scanner=CUSTOMS)
     user = user_factory()
     self.grant_permission(user, 'Admin:ScannersResultsView')
     self.client.login(email=user.email)
     response = self.client.post(
         reverse(
             'admin:scanners_scannerresult_handlerevert',
             args=[result.pk],
         ))
     assert response.status_code == 404
Beispiel #15
0
    def test_formatted_addon(self):
        addon = addon_factory()
        version = version_factory(addon=addon,
                                  channel=amo.RELEASE_CHANNEL_LISTED)
        result = ScannerResult(version=version)

        assert self.admin.formatted_addon(result) == (
            '<a href="{}">{} (version: {})</a>'.format(
                reverse('reviewers.review', args=[addon.slug]),
                addon.name,
                version.id,
            ))
    def test_formatted_listed_addon(self):
        addon = addon_factory()
        version = version_factory(addon=addon,
                                  channel=amo.RELEASE_CHANNEL_LISTED)
        result = ScannerResult(version=version)

        assert self.admin.formatted_addon(result) == (
            '<a href="{}">{} (version: {})</a>'.format(
                urljoin(
                    settings.EXTERNAL_SITE_URL,
                    reverse('reviewers.review', args=['listed', addon.id]),
                ),
                addon.name,
                version.version,
            ))
Beispiel #17
0
    def test_list_shows_matches_only_by_default(self):
        # Create one entry without matches
        ScannerResult.objects.create(scanner=YARA)
        # Create one entry with matches
        with_matches = ScannerResult(scanner=YARA)
        with_matches.add_match(rule='some-rule')
        with_matches.save()

        response = self.client.get(self.list_url)
        assert response.status_code == 200
        html = pq(response.content)
        assert html('#result_list tbody tr').length == 1
Beispiel #18
0
    def test_list_can_show_all_entries(self):
        # Create one entry without matches
        ScannerResult.objects.create(scanner=YARA)
        # Create one entry with matches
        with_matches = ScannerResult(scanner=YARA)
        with_matches.add_match(rule='some-rule')
        with_matches.save()

        response = self.client.get(self.list_url,
                                   {MatchesFilter.parameter_name: 'all'})
        assert response.status_code == 200
        html = pq(response.content)
        expected_length = ScannerResult.objects.count()
        assert html('#result_list tbody tr').length == expected_length
 def test_change_view_contains_link_to_results(self):
     rule = ScannerRule.objects.create(name='bar', scanner=YARA)
     result = ScannerResult(scanner=YARA)
     result.add_yara_result(rule=rule.name)
     result.save()
     ScannerResult.objects.create(scanner=YARA)  # Doesn't match
     url = reverse('admin:scanners_scannerrule_change', args=(rule.pk, ))
     response = self.client.get(url)
     assert response.status_code == 200
     doc = pq(response.content)
     link = doc('.field-matched_results_link a')
     assert link
     results_list_url = reverse('admin:scanners_scannerresult_changelist')
     expected_href = (
         f'{results_list_url}?matched_rules__id__exact={rule.pk}'
         f'&has_version=all&state=all&scanner={rule.scanner}')
     assert link.attr('href') == expected_href
     assert link.text() == '1'
    def test_handle_customs_false_positive(self):
        # Create one entry with matches
        rule = ScannerRule.objects.create(name='some-rule', scanner=CUSTOMS)
        result = ScannerResult(scanner=CUSTOMS,
                               results={'matchedRules': [rule.name]})
        result.save()
        assert result.state == UNKNOWN

        response = self.client.post(
            reverse(
                'admin:scanners_scannerresult_handlefalsepositive',
                args=[result.pk],
            ))

        result.refresh_from_db()
        assert result.state == FALSE_POSITIVE
        # This action should send a redirect to GitHub.
        assert response.status_code == 302
        assert 'Raw+scanner+results' not in response['Location']
    def test_channel_without_version(self):
        result = ScannerResult(version=None)

        assert self.admin.channel(result) == '-'
    def test_guid(self):
        version = version_factory(addon=addon_factory())
        result = ScannerResult(version=version)

        assert self.admin.guid(result) == version.addon.guid
    def test_guid_without_version(self):
        result = ScannerResult(version=None)

        assert self.admin.guid(result) == '-'
Beispiel #24
0
    def test_raise_when_action_is_invalid(self):
        # `12345` is an invalid action ID
        self.scanner_rule.update(action=12345)

        with pytest.raises(Exception, match='invalid action 12345'):
            ScannerResult.run_action(self.version)
    def test_list_can_show_all_entries(self):
        # Create one entry without matches
        ScannerResult.objects.create(scanner=YARA)
        # Create one entry with matches
        rule = ScannerRule.objects.create(name='some-rule', scanner=YARA)
        with_matches = ScannerResult(scanner=YARA)
        with_matches.add_yara_result(rule=rule.name)
        with_matches.save()
        # Create a false positive
        false_positive = ScannerResult(scanner=YARA, state=FALSE_POSITIVE)
        false_positive.add_yara_result(rule=rule.name)
        false_positive.save()
        # Create an entry without a version
        without_version = ScannerResult(scanner=YARA)
        without_version.add_yara_result(rule=rule.name)
        without_version.save()

        response = self.client.get(
            self.list_url,
            {
                MatchesFilter.parameter_name: 'all',
                StateFilter.parameter_name: 'all',
                WithVersionFilter.parameter_name: 'all',
            },
        )
        assert response.status_code == 200
        html = pq(response.content)
        expected_length = ScannerResult.objects.count()
        assert html('#result_list tbody tr').length == expected_length
    def test_list_default(self):
        # Create one entry without matches, it will not be shown by default
        ScannerResult.objects.create(
            scanner=YARA,
            version=version_factory(addon=addon_factory()),
        )
        # Create one entry with matches, it will be shown by default
        rule = ScannerRule.objects.create(name='some-rule', scanner=YARA)
        with_matches = ScannerResult(
            scanner=YARA,
            version=version_factory(addon=addon_factory()),
        )
        with_matches.add_yara_result(rule=rule.name)
        with_matches.save()
        # Create a false positive, it will not be shown by default
        false_positive = ScannerResult(
            scanner=YARA,
            state=FALSE_POSITIVE,
            version=version_factory(addon=addon_factory()),
        )
        false_positive.add_yara_result(rule=rule.name)
        false_positive.save()
        # Create an entry without a version, it will not be shown by default
        without_version = ScannerResult(scanner=YARA)
        without_version.add_yara_result(rule=rule.name)
        without_version.save()

        response = self.client.get(self.list_url)
        assert response.status_code == 200
        html = pq(response.content)
        assert html('#result_list tbody tr').length == 1
    def test_list_filter_matched_rules(self):
        rule_bar = ScannerRule.objects.create(name='bar', scanner=YARA)
        rule_hello = ScannerRule.objects.create(name='hello', scanner=YARA)
        rule_foo = ScannerRule.objects.create(name='foo', scanner=CUSTOMS)
        with_bar_matches = ScannerResult(scanner=YARA)
        with_bar_matches.add_yara_result(rule=rule_bar.name)
        with_bar_matches.add_yara_result(rule=rule_hello.name)
        with_bar_matches.save()
        ScannerResult.objects.create(scanner=CUSTOMS,
                                     results={'matchedRules': [rule_foo.name]})
        with_hello_match = ScannerResult(scanner=YARA)
        with_hello_match.add_yara_result(rule=rule_hello.name)

        response = self.client.get(
            self.list_url, {
                'matched_rules__id__exact': rule_bar.pk,
                WithVersionFilter.parameter_name: 'all',
            })
        assert response.status_code == 200
        doc = pq(response.content)
        assert doc('#result_list tbody tr').length == 1
        assert doc('.field-formatted_matched_rules').text() == 'bar, hello'
    def test_formatted_results_without_results(self):
        result = ScannerResult()

        assert self.admin.formatted_results(result) == '<pre>[]</pre>'
Beispiel #29
0
    def process(self, version):
        """Process a single version, figuring out if it should be auto-approved
        and calling the approval code if necessary."""
        already_locked = AutoApprovalSummary.check_is_locked(version)
        if not already_locked:
            # Lock the addon for ourselves if possible. Even though
            # AutoApprovalSummary.create_summary_for_version() will do
            # call check_is_locked() again later when calculating the verdict,
            # we have to do it now to prevent overwriting an existing lock with
            # our own.
            set_reviewing_cache(version.addon.pk, settings.TASK_USER_ID)
        try:
            with transaction.atomic():
                log.info('Processing %s version %s...',
                         str(version.addon.name), str(version.version))

                if waffle.switch_is_active('run-action-in-auto-approve'):
                    # We want to execute `run_action()` only once.
                    summary_exists = AutoApprovalSummary.objects.filter(
                        version=version).exists()
                    if summary_exists:
                        log.info('Not running run_action() because it has '
                                 'already been executed')
                    else:
                        ScannerResult.run_action(version)

                summary, info = AutoApprovalSummary.create_summary_for_version(
                    version, dry_run=self.dry_run)
                self.stats.update({k: int(v) for k, v in info.items()})
                if summary.verdict == self.successful_verdict:
                    if summary.verdict == amo.AUTO_APPROVED:
                        self.approve(version)
                    self.stats['auto_approved'] += 1
                    verdict_string = summary.get_verdict_display()
                else:
                    verdict_string = '%s (%s)' % (
                        summary.get_verdict_display(), ', '.join(
                            summary.verdict_info_prettifier(info)))
                log.info('Auto Approval for %s version %s: %s',
                         str(version.addon.name), str(version.version),
                         verdict_string)

        # At this point, any exception should have rolled back the transaction,
        # so even if we did create/update an AutoApprovalSummary instance that
        # should have been rolled back. This ensures that, for instance, a
        # signing error doesn't leave the version and its autoapprovalsummary
        # in conflicting states.
        except (AutoApprovalNotEnoughFilesError,
                AutoApprovalNoValidationResultError):
            log.info(
                'Version %s was skipped either because it had no '
                'files or because it had no validation attached.', version)
            self.stats['error'] += 1
        except SigningError:
            statsd.incr('reviewers.auto_approve.approve.failure')
            log.info('Version %s was skipped because of a signing error',
                     version)
            self.stats['error'] += 1
        finally:
            # Always clear our own lock no matter what happens (but only ours).
            if not already_locked:
                clear_reviewing_cache(version.addon.pk)
    def test_formatted_addon_without_version(self):
        result = ScannerResult(version=None)

        assert self.admin.formatted_addon(result) == '-'