Skip to content
This repository has been archived by the owner on Aug 25, 2024. It is now read-only.

Commit

Permalink
docs: example: shouldi
Browse files Browse the repository at this point in the history
Fixes: #84

Signed-off-by: John Andersen <[email protected]>
  • Loading branch information
pdxjohnny committed Jun 7, 2019
1 parent b141cf0 commit 7729643
Show file tree
Hide file tree
Showing 21 changed files with 657 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
inputs and outputs for operation implementations.
- Async helpers got an `aenter_stack` method which creates and returns and
`contextlib.AsyncExitStack` after entering all the context's passed to it.
- Example of how to use Data Flow Facilitator / Orchestrator / Operations by
writing a Python meta static analysis tool,
[shouldi](https://pypi.org/project/shouldi/)
### Changed
- OperationImplementation add_label and add_orig_label methods now use op.name
instead of ENTRY_POINT_ORIG_LABEL and ENTRY_POINT_NAME.
Expand Down
2 changes: 1 addition & 1 deletion dffml/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
Version of DFFML
"""
VERSION = "0.2.0"
VERSION = "0.2.1"
278 changes: 278 additions & 0 deletions docs/usage/operations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
Example Data Flow Usage
=======================

This example will show you how to generate a dataset using operations.

Operations are the core of DFFML, they have inputs and outputs, are configurable
and are run by the Data Flow Facilitator in what amounts to a large event loop.
The events in the event loop are pieces of data entering the network. When a
piece of data which matches the data types of one of the operations inputs
enters the network, that operation is then run.

We're going to write a few operations which will run some Python static analysis
tools. With the goal being to create a command line utility called ``shouldi``
which will provide us with the information we need to make the decision, should
I install Python package X? When it's done it'll look like this

.. code-block:: console
$ shouldi install insecure-package bandit
bandit is okay to install
Do not install insecure-package! {'safety_check_number_of_issues': 1}
Creating our Package
--------------------

Clone a copy of DFFML and navigate the top of the source directory.

Create a new package using the create script.

.. code-block:: console
$ ./scripts/create.sh operations shouldi
You can now move this to another directory if you wish (the copy for this
example is located under ``examples/shouldi``.

.. code-block:: console
$ mv operations/shouldi ../shouldi
$ cd ../shouldi
We're going to change the name of the package to ``shouldi`` instead of the
default, ``dffml_operations_shouldi``.

**setup.py**

.. code-block:: python
NAME = "shouldi"
We need to rename the directory as well.

.. code-block:: console
$ mv dffml_operations_shouldi shouldi
And the directory within the coveragerc file

**.coveragerc**

.. code-block:: python
source =
shouldi
tests
Now install your freshly renamed module!

.. code-block:: console
$ python3.7 -m pip install -e .
Installing Static Analysis Tools
--------------------------------

For simplicities sake the beginning of this example will use subprocesses to
interact with command line Python static analysis tools. Let's install them all
via ``pip``.

.. code-block:: console
$ python3.7 -m pip install -U safety pylint bandit
We need to make http requests so let's install ``aiohttp``.

**setup.py**

.. code-block:: python
INSTALL_REQUIRES = [
"aiohttp>=3.5.4"
]
Our Zeroth Operation
--------------------

We'll write an operation to check for CVEs in a package by using ``safety``.

Safety uses the package name and version to tell us if there are any security
issues in the package for that version.

To use safety, we have to have the version of the package we want to check.

Let's write an operation to grab the version of a package.

.. literalinclude:: /../examples/shouldi/shouldi/pypi.py

Write a test for it

.. literalinclude:: /../examples/shouldi/tests/test_pypi.py

Run the tests

.. code-block:: console
$ python3.7 setup.py test -s tests.test_pypi
Safety Operation
----------------

The output of the last operation will automatticly be combined with the package
name to create a call you our new operation, ``SafetyCheck``.

This is how running safety on the command line works.

.. code-block:: console
$ echo insecure-package==0.1.0 | safety check --stdin
╒══════════════════════════════════════════════════════════════════════════════╕
│ │
│ /$$$$$$ /$$ │
│ /$$__ $$ | $$ │
│ /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ │
│ /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ │
│ | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ │
│ \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ │
│ /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ │
│ |_______/ \_______/|__/ \_______/ \___/ \____ $$ │
│ /$$ | $$ │
│ | $$$$$$/ │
│ by pyup.io \______/ │
│ │
╞══════════════════════════════════════════════════════════════════════════════╡
│ REPORT │
│ checked 1 packages, using default DB │
╞════════════════════════════╤═══════════╤══════════════════════════╤══════════╡
│ package │ installed │ affected │ ID │
╞════════════════════════════╧═══════════╧══════════════════════════╧══════════╡
│ insecure-package │ 0.1.0 │ <0.2.0 │ 25853 │
╘══════════════════════════════════════════════════════════════════════════════╛
We want parsable output, so let's try it with the ``--json`` flag.

.. code-block:: console
$ echo insecure-package==0.1.0 | safety check --stdin --json
[
[
"insecure-package",
"<0.2.0",
"0.1.0",
"This is an insecure package with lots of exploitable security vulnerabilities.",
"25853"
]
]
Let's now write the operation to call ``safety`` via a subprocess.

.. literalinclude:: /../examples/shouldi/shouldi/safety.py

Write a test for it

.. literalinclude:: /../examples/shouldi/tests/test_safety.py

Run the tests

.. code-block:: console
$ python3.7 setup.py test -s tests.test_safety
.. TODO Add they operations to setup.py entry_points
.. TODO Add bandit
.. TODO Add pylint
CLI
---

Writing the CLI is as simple as importing our operations and having the memory
orchestrator run them. DFFML also provides a quick and dirty CLI abstraction
based on :py:mod:`argparse` which will speed things up.

.. TODO explain more about writing the CLI and the orchestrator
**shouldi/cli.py**

.. literalinclude:: /../examples/shouldi/shouldi/cli.py

Let's test out the code in ``shouldi.cli`` before making it accessable via the
command line.

.. literalinclude:: /../examples/shouldi/tests/test_cli.py

Run the all the tests this time

.. code-block:: console
$ python3.7 setup.py test
If you have coverage installed (``pip install coverage``) you can also check the
code coverage.

.. code-block:: console
$ python3.7 -m coverage run setup.py test
running test
running egg_info
writing shouldi.egg-info/PKG-INFO
writing dependency_links to shouldi.egg-info/dependency_links.txt
writing entry points to shouldi.egg-info/entry_points.txt
writing requirements to shouldi.egg-info/requires.txt
writing top-level names to shouldi.egg-info/top_level.txt
reading manifest file 'shouldi.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'shouldi.egg-info/SOURCES.txt'
running build_ext
test_install (tests.test_cli.TestCLI) ... ok
test_run (tests.test_safety.TestSafetyCheck) ... ok
test_run (tests.test_pypi.TestPyPiLatestPackageVersion) ... ok
----------------------------------------------------------------------
Ran 3 tests in 2.314s
OK
$ python3.7 -m coverage report -m
Name Stmts Miss Branch BrPart Cover Missing
--------------------------------------------------------------------
shouldi/__init__.py 0 0 0 0 100%
shouldi/cli.py 30 0 11 0 100%
shouldi/definitions.py 5 0 2 0 100%
shouldi/pypi.py 12 0 2 0 100%
shouldi/safety.py 18 0 0 0 100%
shouldi/version.py 1 0 0 0 100%
tests/__init__.py 0 0 0 0 100%
tests/test_cli.py 11 0 0 0 100%
tests/test_pypi.py 9 0 0 0 100%
tests/test_safety.py 9 0 0 0 100%
--------------------------------------------------------------------
TOTAL 95 0 15 0 100%
We want this to be usable as a command line utility, Python's
:py:mod:`setuptools` allows us to define console ``entry_points``. All we have
to do is tell :py:mod:`setuptools` what Python function we want it to call when
a user runs a given command line application. The name of our CLI is ``shouldi``
and the function we want to run is ``main`` in the ``ShouldI`` class which is in
the ``shouldi.cli`` module.

**setup.py**

.. code-block:: python
entry_points={"console_scripts": ["shouldi = shouldi.cli:ShouldI.main"]},
Re-install the package via pip

.. code-block:: console
$ python3.7 -m pip install -e .
Now we should be able to run our new tool via the CLI! (Provided your ``$PATH``
is set up correctly.

.. code-block:: console
$ shouldi install insecure-package bandit
bandit is okay to install
Do not install insecure-package! {'safety_check_number_of_issues': 1}
13 changes: 13 additions & 0 deletions examples/shouldi/.coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[run]
source =
shouldi
tests
branch = True

[report]
exclude_lines =
no cov
no qa
noqa
pragma: no cover
if __name__ == .__main__.:
20 changes: 20 additions & 0 deletions examples/shouldi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
*.log
*.pyc
.cache/
.coverage
.idea/
.vscode/
*.egg-info/
build/
dist/
docs/build/
venv/
wheelhouse/
*.egss
.mypy_cache/
*.swp
.venv/
.eggs/
*.modeldir
*.db
htmlcov/
21 changes: 21 additions & 0 deletions examples/shouldi/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Copyright (c) 2017-2019 Intel

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2 changes: 2 additions & 0 deletions examples/shouldi/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include README.md
include LICENSE
38 changes: 38 additions & 0 deletions examples/shouldi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# shouldi

![shouldi](https://github.com/intel/dffml/raw/master/examples/shouldi/shouldi.jpg)

## Usage

```console
$ shouldi install insecure-package bandit
bandit is okay to install
Do not install insecure-package! {'safety_check_number_of_issues': 1}
```

## Dependencies

`shouldi` depends on safety, pylint, and bandit being installed separately.

```console
$ python3.7 -m pip install -U safety pylint bandit
```

## WTF is this

`shouldi` is a tool that runs static analysis tools to let you know if there are
any issues in any of the python packages you were thinking of installing.

`shouldi` is similar to things like [Go Report Card](https://goreportcard.com/).

Right now `shouldi` runs the following static analysis tools and complains if:

- [safety](https://pyup.io/safety/)
- Any issues are found
- TODO: [bandit](https://pypi.org/project/bandit/)
- TODO: [pylint](https://pypi.org/project/pylint/)
- TDB (something about the number of errors)

## License

shouldi is distributed under the [MIT License](LICENSE).
Loading

0 comments on commit 7729643

Please sign in to comment.