|
| 1 | +Example Data Flow Usage |
| 2 | +======================= |
| 3 | + |
| 4 | +This example will show you how to generate a dataset using operations. |
| 5 | + |
| 6 | +Operations are the core of DFFML, they have inputs and outputs, are configurable |
| 7 | +and are run by the Data Flow Facilitator in what amounts to a large event loop. |
| 8 | +The events in the event loop are pieces of data entering the network. When a |
| 9 | +piece of data which matches the data types of one of the operations inputs |
| 10 | +enters the network, that operation is then run. |
| 11 | + |
| 12 | +We're going to write a few operations which will run some Python static analysis |
| 13 | +tools. With the goal being to create a command line utility called ``shouldi`` |
| 14 | +which will provide us with the information we need to make the decision, should |
| 15 | +I install Python package X? When it's done it'll look like this |
| 16 | + |
| 17 | +.. code-block:: console |
| 18 | +
|
| 19 | + $ shouldi install insecure-package bandit |
| 20 | + bandit is okay to install |
| 21 | + Do not install insecure-package! {'safety_check_number_of_issues': 1} |
| 22 | +
|
| 23 | +Creating our Package |
| 24 | +-------------------- |
| 25 | + |
| 26 | +Clone a copy of DFFML and navigate the top of the source directory. |
| 27 | + |
| 28 | +Create a new package using the create script. |
| 29 | + |
| 30 | +.. code-block:: console |
| 31 | +
|
| 32 | + $ ./scripts/create.sh operations shouldi |
| 33 | +
|
| 34 | +You can now move this to another directory if you wish (the copy for this |
| 35 | +example is located under ``examples/shouldi``. |
| 36 | + |
| 37 | +.. code-block:: console |
| 38 | +
|
| 39 | + $ mv operations/shouldi ../shouldi |
| 40 | + $ cd ../shouldi |
| 41 | +
|
| 42 | +We're going to change the name of the package to ``shouldi`` instead of the |
| 43 | +default, ``dffml_operations_shouldi``. |
| 44 | + |
| 45 | +**setup.py** |
| 46 | + |
| 47 | +.. code-block:: python |
| 48 | +
|
| 49 | + NAME = "shouldi" |
| 50 | +
|
| 51 | +We need to rename the directory as well. |
| 52 | + |
| 53 | +.. code-block:: console |
| 54 | +
|
| 55 | + $ mv dffml_operations_shouldi shouldi |
| 56 | +
|
| 57 | +And the directory within the coveragerc file |
| 58 | + |
| 59 | +**.coveragerc** |
| 60 | + |
| 61 | +.. code-block:: python |
| 62 | +
|
| 63 | + source = |
| 64 | + shouldi |
| 65 | + tests |
| 66 | +
|
| 67 | +Now install your freshly renamed module! |
| 68 | + |
| 69 | +.. code-block:: console |
| 70 | +
|
| 71 | + $ python3.7 -m pip install -e . |
| 72 | +
|
| 73 | +Installing Static Analysis Tools |
| 74 | +-------------------------------- |
| 75 | + |
| 76 | +For simplicities sake the beginning of this example will use subprocesses to |
| 77 | +interact with command line Python static analysis tools. Let's install them all |
| 78 | +via ``pip``. |
| 79 | + |
| 80 | +.. code-block:: console |
| 81 | +
|
| 82 | + $ python3.7 -m pip install -U safety pylint bandit |
| 83 | +
|
| 84 | +We need to make http requests so let's install ``aiohttp``. |
| 85 | + |
| 86 | +**setup.py** |
| 87 | + |
| 88 | +.. code-block:: python |
| 89 | +
|
| 90 | + INSTALL_REQUIRES = [ |
| 91 | + "aiohttp>=3.5.4" |
| 92 | + ] |
| 93 | +
|
| 94 | +Our Zeroth Operation |
| 95 | +-------------------- |
| 96 | + |
| 97 | +We'll write an operation to check for CVEs in a package by using ``safety``. |
| 98 | + |
| 99 | +Safety uses the package name and version to tell us if there are any security |
| 100 | +issues in the package for that version. |
| 101 | + |
| 102 | +To use safety, we have to have the version of the package we want to check. |
| 103 | + |
| 104 | +Let's write an operation to grab the version of a package. |
| 105 | + |
| 106 | +.. literalinclude:: /../examples/shouldi/shouldi/pypi.py |
| 107 | + |
| 108 | +Write a test for it |
| 109 | + |
| 110 | +.. literalinclude:: /../examples/shouldi/tests/test_pypi.py |
| 111 | + |
| 112 | +Run the tests |
| 113 | + |
| 114 | +.. code-block:: console |
| 115 | +
|
| 116 | + $ python3.7 setup.py test -s tests.test_pypi |
| 117 | +
|
| 118 | +Safety Operation |
| 119 | +---------------- |
| 120 | + |
| 121 | +The output of the last operation will automatticly be combined with the package |
| 122 | +name to create a call you our new operation, ``SafetyCheck``. |
| 123 | + |
| 124 | +This is how running safety on the command line works. |
| 125 | + |
| 126 | +.. code-block:: console |
| 127 | +
|
| 128 | + $ echo insecure-package==0.1.0 | safety check --stdin |
| 129 | + ╒══════════════════════════════════════════════════════════════════════════════╕ |
| 130 | + │ │ |
| 131 | + │ /$$$$$$ /$$ │ |
| 132 | + │ /$$__ $$ | $$ │ |
| 133 | + │ /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ │ |
| 134 | + │ /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ │ |
| 135 | + │ | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ │ |
| 136 | + │ \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ │ |
| 137 | + │ /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ │ |
| 138 | + │ |_______/ \_______/|__/ \_______/ \___/ \____ $$ │ |
| 139 | + │ /$$ | $$ │ |
| 140 | + │ | $$$$$$/ │ |
| 141 | + │ by pyup.io \______/ │ |
| 142 | + │ │ |
| 143 | + ╞══════════════════════════════════════════════════════════════════════════════╡ |
| 144 | + │ REPORT │ |
| 145 | + │ checked 1 packages, using default DB │ |
| 146 | + ╞════════════════════════════╤═══════════╤══════════════════════════╤══════════╡ |
| 147 | + │ package │ installed │ affected │ ID │ |
| 148 | + ╞════════════════════════════╧═══════════╧══════════════════════════╧══════════╡ |
| 149 | + │ insecure-package │ 0.1.0 │ <0.2.0 │ 25853 │ |
| 150 | + ╘══════════════════════════════════════════════════════════════════════════════╛ |
| 151 | +
|
| 152 | +We want parsable output, so let's try it with the ``--json`` flag. |
| 153 | + |
| 154 | +.. code-block:: console |
| 155 | +
|
| 156 | + $ echo insecure-package==0.1.0 | safety check --stdin --json |
| 157 | + [ |
| 158 | + [ |
| 159 | + "insecure-package", |
| 160 | + "<0.2.0", |
| 161 | + "0.1.0", |
| 162 | + "This is an insecure package with lots of exploitable security vulnerabilities.", |
| 163 | + "25853" |
| 164 | + ] |
| 165 | + ] |
| 166 | +
|
| 167 | +Let's now write the operation to call ``safety`` via a subprocess. |
| 168 | + |
| 169 | +.. literalinclude:: /../examples/shouldi/shouldi/safety.py |
| 170 | + |
| 171 | +Write a test for it |
| 172 | + |
| 173 | +.. literalinclude:: /../examples/shouldi/tests/test_safety.py |
| 174 | + |
| 175 | +Run the tests |
| 176 | + |
| 177 | +.. code-block:: console |
| 178 | +
|
| 179 | + $ python3.7 setup.py test -s tests.test_safety |
| 180 | +
|
| 181 | +.. TODO Add they operations to setup.py entry_points |
| 182 | +
|
| 183 | +.. TODO Add bandit |
| 184 | +
|
| 185 | +.. TODO Add pylint |
| 186 | +
|
| 187 | +CLI |
| 188 | +--- |
| 189 | + |
| 190 | +Writing the CLI is as simple as importing our operations and having the memory |
| 191 | +orchestrator run them. DFFML also provides a quick and dirty CLI abstraction |
| 192 | +based on :py:mod:`argparse` which will speed things up. |
| 193 | + |
| 194 | +.. TODO explain more about writing the CLI and the orchestrator |
| 195 | +
|
| 196 | +**shouldi/cli.py** |
| 197 | + |
| 198 | +.. literalinclude:: /../examples/shouldi/shouldi/cli.py |
| 199 | + |
| 200 | +Let's test out the code in ``shouldi.cli`` before making it accessable via the |
| 201 | +command line. |
| 202 | + |
| 203 | +.. literalinclude:: /../examples/shouldi/tests/test_cli.py |
| 204 | + |
| 205 | +Run the all the tests this time |
| 206 | + |
| 207 | +.. code-block:: console |
| 208 | +
|
| 209 | + $ python3.7 setup.py test |
| 210 | +
|
| 211 | +If you have coverage installed (``pip install coverage``) you can also check the |
| 212 | +code coverage. |
| 213 | + |
| 214 | +.. code-block:: console |
| 215 | +
|
| 216 | + $ python3.7 -m coverage run setup.py test |
| 217 | + running test |
| 218 | + running egg_info |
| 219 | + writing shouldi.egg-info/PKG-INFO |
| 220 | + writing dependency_links to shouldi.egg-info/dependency_links.txt |
| 221 | + writing entry points to shouldi.egg-info/entry_points.txt |
| 222 | + writing requirements to shouldi.egg-info/requires.txt |
| 223 | + writing top-level names to shouldi.egg-info/top_level.txt |
| 224 | + reading manifest file 'shouldi.egg-info/SOURCES.txt' |
| 225 | + reading manifest template 'MANIFEST.in' |
| 226 | + writing manifest file 'shouldi.egg-info/SOURCES.txt' |
| 227 | + running build_ext |
| 228 | + test_install (tests.test_cli.TestCLI) ... ok |
| 229 | + test_run (tests.test_safety.TestSafetyCheck) ... ok |
| 230 | + test_run (tests.test_pypi.TestPyPiLatestPackageVersion) ... ok |
| 231 | +
|
| 232 | + ---------------------------------------------------------------------- |
| 233 | + Ran 3 tests in 2.314s |
| 234 | +
|
| 235 | + OK |
| 236 | + $ python3.7 -m coverage report -m |
| 237 | + Name Stmts Miss Branch BrPart Cover Missing |
| 238 | + -------------------------------------------------------------------- |
| 239 | + shouldi/__init__.py 0 0 0 0 100% |
| 240 | + shouldi/cli.py 30 0 11 0 100% |
| 241 | + shouldi/definitions.py 5 0 2 0 100% |
| 242 | + shouldi/pypi.py 12 0 2 0 100% |
| 243 | + shouldi/safety.py 18 0 0 0 100% |
| 244 | + shouldi/version.py 1 0 0 0 100% |
| 245 | + tests/__init__.py 0 0 0 0 100% |
| 246 | + tests/test_cli.py 11 0 0 0 100% |
| 247 | + tests/test_pypi.py 9 0 0 0 100% |
| 248 | + tests/test_safety.py 9 0 0 0 100% |
| 249 | + -------------------------------------------------------------------- |
| 250 | + TOTAL 95 0 15 0 100% |
| 251 | +
|
| 252 | +We want this to be usable as a command line utility, Python's |
| 253 | +:py:mod:`setuptools` allows us to define console ``entry_points``. All we have |
| 254 | +to do is tell :py:mod:`setuptools` what Python function we want it to call when |
| 255 | +a user runs a given command line application. The name of our CLI is ``shouldi`` |
| 256 | +and the function we want to run is ``main`` in the ``ShouldI`` class which is in |
| 257 | +the ``shouldi.cli`` module. |
| 258 | + |
| 259 | +**setup.py** |
| 260 | + |
| 261 | +.. code-block:: python |
| 262 | +
|
| 263 | + entry_points={"console_scripts": ["shouldi = shouldi.cli:ShouldI.main"]}, |
| 264 | +
|
| 265 | +Re-install the package via pip |
| 266 | + |
| 267 | +.. code-block:: console |
| 268 | +
|
| 269 | + $ python3.7 -m pip install -e . |
| 270 | +
|
| 271 | +Now we should be able to run our new tool via the CLI! (Provided your ``$PATH`` |
| 272 | +is set up correctly. |
| 273 | + |
| 274 | +.. code-block:: console |
| 275 | +
|
| 276 | + $ shouldi install insecure-package bandit |
| 277 | + bandit is okay to install |
| 278 | + Do not install insecure-package! {'safety_check_number_of_issues': 1} |
0 commit comments