WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 31716b2

Browse files
committed
Call run_action() even on initial match when scanning for narc outside auto_approve (#23820)
This ensures we trigger actions from NARC matches that happen outside auto-approve, even if they are the first time NARC is called for that version, for instance because it was already approved before we enabled NARC.
1 parent 773fdbe commit 31716b2

File tree

4 files changed

+128
-15
lines changed

4 files changed

+128
-15
lines changed

src/olympia/reviewers/management/commands/auto_approve.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,10 @@ def process(self, version_id):
117117
# attached to the upload at that point.
118118
# This needs to be run before run_action() and before
119119
# auto-approval is attempted, and has to be triggered
120-
# synchronously (no .delay()).
121-
run_narc_on_version(version.pk)
120+
# synchronously (no .delay()). In this case we pass
121+
# run_action_on_match=False since we're going to call
122+
# ScannerResult.run_action() below.
123+
run_narc_on_version(version.pk, run_action_on_match=False)
122124

123125
if waffle.switch_is_active('run-action-in-auto-approve'):
124126
if summary_exists:

src/olympia/reviewers/tests/test_commands.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,8 @@ def test_executes_run_narc_on_version_when_switch_is_active(self, run_narc_mock)
609609
call_command('auto_approve')
610610

611611
assert run_narc_mock.call_count == 1
612-
run_narc_mock.assert_called_with(self.version.pk)
612+
assert run_narc_mock.call_args[0] == (self.version.pk,)
613+
assert run_narc_mock.call_args[1] == {'run_action_on_match': False}
613614

614615
@mock.patch(
615616
'olympia.reviewers.management.commands.auto_approve.run_narc_on_version'
@@ -622,7 +623,8 @@ def test_only_executes_run_narc_on_version_once(
622623
call_command('auto_approve')
623624

624625
assert run_narc_mock.call_count == 1
625-
run_narc_mock.assert_called_with(self.version.pk)
626+
assert run_narc_mock.call_args[0] == (self.version.pk,)
627+
assert run_narc_mock.call_args[1] == {'run_action_on_match': False}
626628

627629
run_narc_mock.reset_mock()
628630
call_command('auto_approve')

src/olympia/scanners/tasks.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,12 @@ def run_customs(results, upload_pk):
163163

164164
@task
165165
@use_primary_db
166-
def run_narc_on_version(version_pk):
166+
def run_narc_on_version(version_pk, *, run_action_on_match=True):
167167
log.info('Starting narc task for Version %s.', version_pk)
168168
try:
169169
version = Version.unfiltered.get(pk=version_pk)
170170
with statsd.timer('devhub.narc'):
171-
_run_narc(version=version)
171+
_run_narc(version=version, run_action_on_match=run_action_on_match)
172172
except Exception as exc:
173173
statsd.incr('devhub.narc.failure')
174174
log.exception(
@@ -181,7 +181,7 @@ def run_narc_on_version(version_pk):
181181
log.info('Ending scanner "narc" task for Version %s.', version_pk)
182182

183183

184-
def _run_narc(*, version):
184+
def _run_narc(*, version, run_action_on_match):
185185
scanner_result, initial_run = ScannerResult.objects.get_or_create(
186186
scanner=NARC, version=version
187187
)
@@ -242,14 +242,13 @@ def _run_narc(*, version):
242242
)
243243
)
244244

245-
run_action = False
245+
has_new_matches = False
246246
new_results = [json.loads(result) for result in sorted(results)]
247-
if not initial_run and new_results != scanner_result.results:
248-
# Scanner actions are normally triggered by auto-approve, but here
249-
# we're re running the scanner on a version and found more results than
250-
# when it ran before. We don't know whether the action has already been
251-
# triggered but in case it has we need to do it again.
252-
run_action = True
247+
if new_results != scanner_result.results:
248+
# Either we're scanning this version for the first time, or it's a new
249+
# run following some change, but in any case we found new results. We
250+
# might need to run ScannerResult.run_action() as a result, see below.
251+
has_new_matches = True
253252
scanner_result.results = new_results
254253
scanner_result.save()
255254
if initial_run:
@@ -260,8 +259,9 @@ def _run_narc(*, version):
260259
statsd.incr(f'devhub.narc{statsd_suffix}.has_matches')
261260
for scanner_rule in scanner_result.matched_rules.all():
262261
statsd.incr(f'devhub.narc{statsd_suffix}.rule.{scanner_rule.id}.match')
263-
if version and run_action:
262+
if not initial_run and has_new_matches:
264263
statsd.incr(f'devhub.narc{statsd_suffix}.results_differ')
264+
if run_action_on_match and has_new_matches:
265265
ScannerResult.run_action(version, check_mad_results=False)
266266
return scanner_result
267267

src/olympia/scanners/tests/test_tasks.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,115 @@ def test_run_on_version_no_new_result(self, run_action_mock, incr_mock):
848848
# We didn't retrigger the action since the first run (no new matches).
849849
assert run_action_mock.call_count == 0
850850

851+
@mock.patch('olympia.scanners.tasks.statsd.incr')
852+
@mock.patch.object(ScannerResult, 'run_action')
853+
def test_run_action_initial_run(self, run_action_mock, incr_mock):
854+
rule = ScannerRule.objects.create(
855+
name='always_match_rule',
856+
scanner=NARC,
857+
definition='^My WebExtension.*$',
858+
)
859+
860+
run_narc_on_version(self.version.pk)
861+
862+
scanner_results = ScannerResult.objects.all()
863+
assert len(scanner_results) == 1
864+
narc_result = scanner_results[0]
865+
assert narc_result.scanner == NARC
866+
assert narc_result.upload is None
867+
assert narc_result.version == self.version
868+
assert narc_result.has_matches
869+
assert list(narc_result.matched_rules.all()) == [rule]
870+
assert len(narc_result.results) == 1
871+
assert narc_result.results == [
872+
{
873+
'meta': {
874+
'span': [0, 21],
875+
'locale': None,
876+
'source': 'xpi',
877+
'string': 'My WebExtension Addon',
878+
'pattern': '^My WebExtension.*$',
879+
},
880+
'rule': 'always_match_rule',
881+
},
882+
]
883+
assert incr_mock.called
884+
assert incr_mock.call_count == 3
885+
incr_mock.assert_has_calls(
886+
[
887+
mock.call('devhub.narc.has_matches'),
888+
mock.call(f'devhub.narc.rule.{rule.id}.match'),
889+
mock.call('devhub.narc.success'),
890+
]
891+
)
892+
893+
assert run_action_mock.call_count == 1
894+
assert run_action_mock.call_args[0] == (self.version,)
895+
assert run_action_mock.call_args[1] == {'check_mad_results': False}
896+
897+
@mock.patch('olympia.scanners.tasks.statsd.incr')
898+
@mock.patch.object(ScannerResult, 'run_action')
899+
def test_no_run_action_if_no_results(self, run_action_mock, incr_mock):
900+
run_narc_on_version(self.version.pk)
901+
902+
scanner_results = ScannerResult.objects.all()
903+
assert len(scanner_results) == 1
904+
narc_result = scanner_results[0]
905+
assert narc_result.scanner == NARC
906+
assert narc_result.upload is None
907+
assert narc_result.version == self.version
908+
assert not narc_result.has_matches
909+
assert not narc_result.matched_rules.all().exists()
910+
assert narc_result.results == []
911+
assert incr_mock.call_count == 1
912+
assert incr_mock.call_args[0] == ('devhub.narc.success',)
913+
914+
assert run_action_mock.call_count == 0
915+
916+
@mock.patch('olympia.scanners.tasks.statsd.incr')
917+
@mock.patch.object(ScannerResult, 'run_action')
918+
def test_no_run_action_if_parameter_is_passed(self, run_action_mock, incr_mock):
919+
rule = ScannerRule.objects.create(
920+
name='always_match_rule',
921+
scanner=NARC,
922+
definition='^My WebExtension.*$',
923+
)
924+
925+
run_narc_on_version(self.version.pk, run_action_on_match=False)
926+
927+
scanner_results = ScannerResult.objects.all()
928+
assert len(scanner_results) == 1
929+
narc_result = scanner_results[0]
930+
assert narc_result.scanner == NARC
931+
assert narc_result.upload is None
932+
assert narc_result.version == self.version
933+
assert narc_result.has_matches
934+
assert list(narc_result.matched_rules.all()) == [rule]
935+
assert len(narc_result.results) == 1
936+
assert narc_result.results == [
937+
{
938+
'meta': {
939+
'span': [0, 21],
940+
'locale': None,
941+
'source': 'xpi',
942+
'string': 'My WebExtension Addon',
943+
'pattern': '^My WebExtension.*$',
944+
},
945+
'rule': 'always_match_rule',
946+
},
947+
]
948+
assert incr_mock.called
949+
assert incr_mock.call_count == 3
950+
incr_mock.assert_has_calls(
951+
[
952+
mock.call('devhub.narc.has_matches'),
953+
mock.call(f'devhub.narc.rule.{rule.id}.match'),
954+
mock.call('devhub.narc.success'),
955+
]
956+
)
957+
958+
assert run_action_mock.call_count == 0
959+
851960

852961
class TestRunYara(UploadMixin, TestCase):
853962
def setUp(self):

0 commit comments

Comments
 (0)