1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 from __future__
import absolute_import
, print_function
, unicode_literals
11 from mozbuild
.base
import (
15 from mach
.decorators
import (
21 import mozpack
.path
as mozpath
22 from mozpack
.files
import FileFinder
24 from mozlog
import commandline
26 here
= os
.path
.abspath(os
.path
.dirname(__file__
))
30 class MachCommands(MachCommandBase
):
31 @Command('python-safety', category
='testing',
32 description
='Run python requirements safety checks')
33 @CommandArgument('--python',
35 help='Version of Python for Pipenv to use. When given a '
36 'Python version, Pipenv will automatically scan your '
37 'system for a Python that matches that given version.')
38 def python_safety(self
, python
=None, **kwargs
):
39 self
.logger
= commandline
.setup_logging(
40 "python-safety", {"raw": sys
.stdout
})
42 self
.activate_pipenv(pipfile
=os
.path
.join(here
, 'Pipfile'), python
=python
, populate
=True)
44 pattern
= '**/*requirements*.txt'
45 path
= mozpath
.normsep(os
.path
.dirname(os
.path
.dirname(here
)))
46 finder
= FileFinder(path
)
47 files
= [os
.path
.join(path
, p
) for p
, _
in finder
.find(pattern
)]
51 self
.logger
.suite_start(tests
=files
)
52 for filepath
in files
:
53 self
._run
_python
_safety
(filepath
)
55 self
.logger
.suite_end()
58 def _run_python_safety(self
, test_path
):
59 from mozprocess
import ProcessHandler
62 self
.logger
.test_start(test_path
)
64 def _line_handler(line
):
65 output
.append(line
.decode('UTF-8'))
67 cmd
= ['safety', 'check', '--cache', '--json', '-r', test_path
]
68 env
= os
.environ
.copy()
69 env
['PYTHONDONTWRITEBYTECODE'] = '1'
71 proc
= ProcessHandler(
72 cmd
, env
=env
, processOutputLine
=_line_handler
, storeOutput
=False)
75 return_code
= proc
.wait()
78 Example output for an error in json.
83 "Heap-based buffer overflow in the ALGnew...",
87 # Warnings are currently interleaved with json, see
88 # https://github.com/pyupio/safety/issues/133
89 for warning
in output
:
90 if warning
.startswith('Warning'):
91 self
.logger
.warning(warning
)
92 output
= [line
for line
in output
if not line
.startswith('Warning')]
94 output_json
= json
.loads("".join(output
))
96 for entry
in output_json
:
97 affected
.add(entry
[0])
98 message
= "{0} installed:{2} affected:{1} description:{3}\n".format(
100 self
.logger
.test_status(test
=test_path
,
110 self
.logger
.test_end(test_path
, status
=status
,
111 expected
='PASS', message
=" ".join(affected
))