clair-cicd 1.0.2
Clair CI/CD
License: MIT
Keywords: development,tools

clair-cicd
==========

|Maintained| |MIT license| |PythonVersions| |status| |PyPI|
|Requirements| |CircleCI| |codecov|
|docker-simonsdave/clair-cicd-database|
|docker-simonsdave/clair-cicd-clair|

`Clair <https://github.com/coreos/clair>`__, `released by CoreOS in Nov
’16 <https://coreos.com/blog/vulnerability-analysis-for-containers/>`__,
is a very effective tool for statically analyzing docker images to
determine which known vulnerabilities exist in the images. Integrating
Clair into a CI/CD pipeline:

1. is complex (believe this is partly a documentation challenge)
2. creates performance problems (building the Clair required Postgres
   database of vulnerabilities is slow)
3. in and of itself is insufficient from a risk assessment point of view
   because once vulnerabilities are identified there’s a lack of
   prescriptive guidance on how to act on the identified vulnerabilities

This repo was created to address the above challenges.

Background
----------

The roots of this repo center around the following beliefs:

-  when inserted into a CI/CD pipeline
   `Clair <https://github.com/coreos/clair>`__ can be a very effective
   foundation for the automated assessment of Docker image vulnerability
   risk
-  services should be run in Docker containers and thus CI/CD pipelines
   should be focused on the automated generation, assessment and
   ultimately deployment of Docker images
-  understanding and assessing the risk profile of services is important
   ie. security is important
-  Docker images should **not** be pushed to a Docker registry until
   their risk profile is understood (this is an important one)
-  the CI/CD pipeline has to be fast. how fast? ideally < 5 minutes
   between code commit and automated (CD) deployment begins rolling out
   a change
-  there should be a clear division of responsibilities between those
   who create a docker image (service engineer) and those who determine
   the risk of vulnerabilities in a docker image (security analyst)
-  the risk assessment process should generate evidence which can be
   used to understand the risk assessment decision

Key Participants
----------------

-  service engineer - responsible for implementing a service that is
   packaged in a docker image
-  security analyst - responsible for defining whitelists which are
   consumed by ``clair_cicd`` to influence Docker image risk assessment
   decisions

How to Use
----------

Getting Started
~~~~~~~~~~~~~~~

To start using ``clair-cicd``, a service engineer inserts a single line
of code into a service’s CI pipeline. The single line of code runs the
shell script
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__.
Part of the CI pipeline’s responsibility is to build the docker image
and then push that docker image to a docker registry. The single line of
``clair-cicd`` code should appear after the docker image is built and
tested but before the docker image is pushed to a docker registry.

In this simple case,
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
returns a zero exit status if the docker image contains no known
vulnerabilities above a medium severity. If the docker image contains
any known vulnerabilities with a severity higher than medium,
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
returns a non-zero exit status and the build fails ie. the build should
fail before the docker image is pushed to a docker registry.

The example illustrates what’s described about for the `alpine:3.4
docker image <https://hub.docker.com/_/alpine?tab=tags>`__.

.. code:: bash

   ~> curl -s -L \
     https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
     bash -s -- alpine:3.4
   ~> echo $?
   0
   ~>

Understanding the Risk Assessment Decision
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To understand how ``assess-image-risk.sh`` is making its risk assessment
decision try using the ``-v`` flag.

.. code:: bash

   ~> curl -s -L \
     https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
     bash -s -- -v alpine:3.4
   2020-01-12 16:43:35 pulling clair database image 'simonsdave/clair-cicd-database:latest'
   2020-01-12 16:44:17 successfully pulled clair database image
   2020-01-12 16:44:17 starting clair database container 'clair-db-c1dbb5f93ae98755'
   2020-01-12 16:44:23 waiting for database server in container 'clair-db-c1dbb5f93ae98755' to start ...........................
   2020-01-12 16:44:54 successfully started clair database container
   2020-01-12 16:44:54 clair configuration in '/var/folders/7x/rr443kj575s8zz54jrbrp4jc0000gn/T/tmp.ElAlhGNl'
   2020-01-12 16:44:59 pulling clair image 'simonsdave/clair-cicd-clair:latest'
   2020-01-12 16:45:13 successfully pulled clair image 'simonsdave/clair-cicd-clair:latest'
   2020-01-12 16:45:13 starting clair container 'clair-e9573ae537134fa0'
   2020-01-12 16:45:15 successfully started clair container 'clair-e9573ae537134fa0'
   2020-01-12 16:45:15 saving docker image 'alpine:3.4' to '/tmp/tmp.IaNHCH'
   2020-01-12 16:45:16 successfully saved docker image 'alpine:3.4'
   2020-01-12 16:45:16 starting to create clair layers
   2020-01-12 16:45:16 creating clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:45:16 successfully created clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:45:16 done creating clair layers
   2020-01-12 16:45:16 starting to get vulnerabilities for clair layers
   2020-01-12 16:45:16 saving vulnerabilities to directory '/tmp/tmp.MDncHN'
   2020-01-12 16:45:16 getting vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:45:16 successfully got vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:45:16 done getting vulnerabilities for clair layers
   2020-01-12 21:45:17 INFO io:89 Looking for vulnerabilities in directory '/tmp/tmp.MDncHN'
   2020-01-12 21:45:17 INFO io:95 Found 1 files with vulnerabilities in directory '/tmp/tmp.MDncHN'
   2020-01-12 21:45:17 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.MDncHN/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
   2020-01-12 21:45:17 INFO io:122 Found 0 vulnerabilities in '/tmp/tmp.MDncHN/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
   2020-01-12 21:45:17 INFO io:133 Found 0 vulnerabilities in 1 files in directory '/tmp/tmp.MDncHN'
   2020-01-12 21:45:17 INFO assessor:19 Assessment starts
   2020-01-12 21:45:17 INFO assessor:26 Assessment ends - pass
   ~> echo $?
   0
   ~>

Adding a Vulnerability Whitelist
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In the above examples a default vulnerability whitelist was used. When
specified as a JSON doc, this whitelist would be:

.. code:: json

   {
     "ignoreSevertiesAtOrBelow": "medium"
   }

By default,
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
returns a non-zero exit status if any vulnerabilities are identified in
the image with a severity higher than medium. The medium is derived from
the default vulnerability whitelist.

The example below illustrate how to specify a vulnerability whitelist
and with a severity other than medium. Note the use of the ``json://``
prefix to indicate this is an inline whitelist.

.. code:: bash

   ~> curl -s -L \
     https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
     bash -s -- -v --whitelist 'json://{"ignoreSevertiesAtOrBelow": "negligible"}' ubuntu:18.04
   2020-01-12 16:46:56 pulling clair database image 'simonsdave/clair-cicd-database:latest'
   2020-01-12 16:46:58 successfully pulled clair database image
   2020-01-12 16:46:58 starting clair database container 'clair-db-3b0811925f7e8bc2'
   2020-01-12 16:46:59 waiting for database server in container 'clair-db-3b0811925f7e8bc2' to start .............................
   2020-01-12 16:47:32 successfully started clair database container
   2020-01-12 16:47:32 clair configuration in '/var/folders/7x/rr443kj575s8zz54jrbrp4jc0000gn/T/tmp.BXCs3Giy'
   2020-01-12 16:47:34 pulling clair image 'simonsdave/clair-cicd-clair:latest'
   2020-01-12 16:47:36 successfully pulled clair image 'simonsdave/clair-cicd-clair:latest'
   2020-01-12 16:47:36 starting clair container 'clair-fc579c71e7daba57'
   2020-01-12 16:47:38 successfully started clair container 'clair-fc579c71e7daba57'
   2020-01-12 16:47:38 saving docker image 'ubuntu:18.04' to '/tmp/tmp.lPDhNd'
   2020-01-12 16:47:43 successfully saved docker image 'ubuntu:18.04'
   2020-01-12 16:47:43 starting to create clair layers
   2020-01-12 16:47:43 creating clair layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
   2020-01-12 16:47:43 successfully created clair layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
   2020-01-12 16:47:43 creating clair layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
   2020-01-12 16:47:44 successfully created clair layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
   2020-01-12 16:47:44 creating clair layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
   2020-01-12 16:47:44 successfully created clair layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
   2020-01-12 16:47:44 creating clair layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
   2020-01-12 16:47:44 successfully created clair layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
   2020-01-12 16:47:44 done creating clair layers
   2020-01-12 16:47:44 starting to get vulnerabilities for clair layers
   2020-01-12 16:47:44 saving vulnerabilities to directory '/tmp/tmp.dkfgmI'
   2020-01-12 16:47:44 getting vulnerabilities for layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
   2020-01-12 16:47:44 successfully got vulnerabilities for layer 'cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd'
   2020-01-12 16:47:44 getting vulnerabilities for layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
   2020-01-12 16:47:44 successfully got vulnerabilities for layer '27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54'
   2020-01-12 16:47:44 getting vulnerabilities for layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
   2020-01-12 16:47:44 successfully got vulnerabilities for layer 'd80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e'
   2020-01-12 16:47:44 getting vulnerabilities for layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
   2020-01-12 16:47:44 successfully got vulnerabilities for layer '1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d'
   2020-01-12 16:47:44 done getting vulnerabilities for clair layers
   2020-01-12 21:47:45 INFO io:89 Looking for vulnerabilities in directory '/tmp/tmp.dkfgmI'
   2020-01-12 21:47:45 INFO io:95 Found 4 files with vulnerabilities in directory '/tmp/tmp.dkfgmI'
   2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54.json'
   2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/27a911bb510bf1e9458437f0f44216fd38fd08c462ed7aa026d91aab8c054e54.json'
   2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd.json'
   2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/cc59b0ca1cf21d77c81a98138703008daa167b1ab1a115849d498dba64e738dd.json'
   2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d.json'
   2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/1ee34a985f7aef86436a5519f5ad83f866a74c7d9a0c22e47c4213ee9cb64e6d.json'
   2020-01-12 21:47:45 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.dkfgmI/d80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e.json'
   2020-01-12 21:47:45 INFO io:122 Found 33 vulnerabilities in '/tmp/tmp.dkfgmI/d80735acaa72040a0a98ca3ae6891f9abb4e2f5d627b4099c4fefdc3ce1e696e.json'
   2020-01-12 21:47:45 INFO io:133 Found 33 vulnerabilities in 4 files in directory '/tmp/tmp.dkfgmI'
   2020-01-12 21:47:45 INFO assessor:19 Assessment starts
   2020-01-12 21:47:45 INFO assessor:34 Assessing vulnerability CVE-2018-11236 - start
   2020-01-12 21:47:45 INFO assessor:52 Vulnerability CVE-2018-11236 @ severity medium greater than whitelist severity @ negligible - fail
   2020-01-12 21:47:45 INFO assessor:36 Assessing vulnerability CVE-2018-11236 - finish
   2020-01-12 21:47:45 INFO assessor:23 Assessment ends - fail
   ~> echo $?
   1
   ~>

The above is an example of an inline whitelist. It’s also possible to
specify a whitelist in a file. The example below illustrates the usage.
Note use of the ``file://`` prefix to indicate the whitelist is
contained in a file.

.. code:: bash

   ~> cat whitelist.json
   {
     "ignoreSevertiesAtOrBelow": "medium"
   }
   ~> curl -s -L \
     https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
     bash -s -- -v --whitelist file://whitelist.json alpine:3.4
   2020-01-12 16:48:41 pulling clair database image 'simonsdave/clair-cicd-database:latest'
   2020-01-12 16:48:42 successfully pulled clair database image
   2020-01-12 16:48:42 starting clair database container 'clair-db-191152e37b864e4b'
   2020-01-12 16:48:43 waiting for database server in container 'clair-db-191152e37b864e4b' to start .............................
   2020-01-12 16:49:16 successfully started clair database container
   2020-01-12 16:49:16 clair configuration in '/var/folders/7x/rr443kj575s8zz54jrbrp4jc0000gn/T/tmp.GdlBNmiG'
   2020-01-12 16:49:19 pulling clair image 'simonsdave/clair-cicd-clair:latest'
   2020-01-12 16:49:20 successfully pulled clair image 'simonsdave/clair-cicd-clair:latest'
   2020-01-12 16:49:20 starting clair container 'clair-747d1c50606fba7e'
   2020-01-12 16:49:21 successfully started clair container 'clair-747d1c50606fba7e'
   2020-01-12 16:49:22 saving docker image 'alpine:3.4' to '/tmp/tmp.Eldkbe'
   2020-01-12 16:49:23 successfully saved docker image 'alpine:3.4'
   2020-01-12 16:49:23 starting to create clair layers
   2020-01-12 16:49:23 creating clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:49:23 successfully created clair layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:49:23 done creating clair layers
   2020-01-12 16:49:23 starting to get vulnerabilities for clair layers
   2020-01-12 16:49:23 saving vulnerabilities to directory '/tmp/tmp.pCOhlL'
   2020-01-12 16:49:23 getting vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:49:23 successfully got vulnerabilities for layer '378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d'
   2020-01-12 16:49:23 done getting vulnerabilities for clair layers
   2020-01-12 21:49:23 INFO io:89 Looking for vulnerabilities in directory '/tmp/tmp.pCOhlL'
   2020-01-12 21:49:23 INFO io:95 Found 1 files with vulnerabilities in directory '/tmp/tmp.pCOhlL'
   2020-01-12 21:49:23 INFO io:104 Looking for vulnerabilities in '/tmp/tmp.pCOhlL/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
   2020-01-12 21:49:23 INFO io:122 Found 0 vulnerabilities in '/tmp/tmp.pCOhlL/378cb6b4a17e08c366cebd813d218f60889848387fa61a56ac054ca027a4890d.json'
   2020-01-12 21:49:23 INFO io:133 Found 0 vulnerabilities in 1 files in directory '/tmp/tmp.pCOhlL'
   2020-01-12 21:49:23 INFO assessor:19 Assessment starts
   2020-01-12 21:49:23 INFO assessor:26 Assessment ends - pass
   ~> echo $?
   0
   ~>

Whitelists can also contain vulnerabilities. The example below
illustrates this capability. If you add the ``-v`` (verbose) flag to
``assess-image-risk.sh`` you see exactly how the vulnerabilities impact
the risk assessment with statements like
``Vulnerability CVE-2019-13627 in whitelist - pass``

.. code:: bash

   ~> curl -s -L \
     https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
     bash -s -- --whitelist 'json://{"ignoreSevertiesAtOrBelow":"low"}' ubuntu:18.04
   ~> echo $?
   1
   ~> cat whitelist.json
   {
     "ignoreSevertiesAtOrBelow": "low",
     "vulnerabilities": [
       { "cveId": "CVE-2018-20839", "rationale": "reason #1" },
       { "cveId": "CVE-2019-5188", "rationale": "reason #2" },
       { "cveId": "CVE-2018-11236", "rationale": "reason #3" },
       { "cveId": "CVE-2019-13627", "rationale": "reason #4" },
       { "cveId": "CVE-2019-13050", "rationale": "reason #5" },
       { "cveId": "CVE-2018-11237", "rationale": "reason #6" },
       { "cveId": "CVE-2018-19591", "rationale": "reason #7" }
     ]
   }
   ~> curl -s -L \
     https://raw.githubusercontent.com/simonsdave/clair-cicd/master/bin/assess-image-risk.sh | \
     bash -s -- --whitelist 'file://whitelist.json' ubuntu:18.04
   ~> echo $?
   0
   ~>

Responsibilities
^^^^^^^^^^^^^^^^

-  maintained by security analyst **not** service engineer
-  checked into source code control and appropriate change management
   processes are used to make changes (code reviews, feature branches,
   etc)

How it Works + Requirements/Assumptions
---------------------------------------

There are 3 moving pieces:

1. `assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
   is bash script which does the heavy lifting to co-ordinate the
   interaction of the 2 other moving pieces
2. `Clair’s <https://github.com/coreos/clair>`__ vulnerability database
   which is packaged inside the docker image
   `simonsdave/clair-database <https://hub.docker.com/r/simonsdave/clair-database/>`__
   - a `CircleCI <https://circleci.com/>`__ cron job is used to rebuild
   `simonsdave/clair-database <https://hub.docker.com/r/simonsdave/clair-database/>`__
   3 days per week to ensure the vulnerability database is current
3. a set of Python and Bash risk assessment scripts packaged in the
   `simonsdave/clair-cicd-clair <https://hub.docker.com/r/simonsdave/clair-cicd-clair/>`__
   docker image which is based on the docker image
   `quay.io/coreos/clair <https://quay.io/repository/coreos/clair>`__
   which packages up `Clair <https://github.com/coreos/clair>`__

From the samples at the start of this doc you’ll see the approach of
curl’ing the latest release of
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
into a localy run bash shell.
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
then spins up a container using the
`simonsdave/clair-database <https://hub.docker.com/r/simonsdave/clair-database/>`__.
Another container is then run using
`simonsdave/clair-cicd-clair <https://hub.docker.com/r/simonsdave/clair-cicd-clair/>`__
with the
`simonsdave/clair-cicd-clair <https://hub.docker.com/r/simonsdave/clair-cicd-clair/>`__
container being able to talk with the
`simonsdave/clair-database <https://hub.docker.com/r/simonsdave/clair-database/>`__
container. Once the
`simonsdave/clair-cicd-clair <https://hub.docker.com/r/simonsdave/clair-cicd-clair/>`__
container is running,
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
docker exec’s `this bash
script <dockerfiles/clair/assess-image-risk.sh>`__ which does the actual
risk assessment.

Armed with the understanding of how ``clair-cicd`` works you’ll
appreciate that the ability to execute
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
is what defines the requirements for the execution environment.
`assess-image-risk.sh <https://github.com/simonsdave/clair-cicd/blob/release-1.0.2/bin/assess-image-risk.sh>`__
is a bash script used to launch the risk assessment process and as such
it’s this script which defines the bulk of the assumptions/requirements
for ``clair-cicd`` - the script uses docker, sed and openssl so all
these need to be available in the environment running ``clair-cicd``

References
----------

-  `29 Jan ’20 - What Is DevSecOps, and How Is It Different from
   DevOps? <https://research.g2.com/insights/what-is-devsecops-and-how-is-it-different-from-devops>`__
-  `1 Oct ’18 - Baking Compliance in your CI/CD
   Pipeline <https://thenewstack.io/baking-compliance-in-your-ci-cd-pipeline>`__
-  `11 Sep ’18 - What is
   DevSecOps? <https://medium.com/@aditi.chaudhry92/what-is-devsecops-cb14cfd457b2>`__
-  `20 Jun ’18 - Where in the DevOps cycle do you do
   security? <https://opensource.com/article/18/6/where-cycle-security-devops>`__
-  `8 May ’18 - DevSecOps: 7 habits of strong security
   organizations <https://enterprisersproject.com/article/2018/5/devsecops-7-habits-strong-security-organizations>`__
-  `18 Apr ’18 - The Cloudcast #343 - Container Vulnerability
   Scanning <http://www.thecloudcast.net/2018/04/the-cloudcast-343-container.html>`__
-  `21 Feb ’18 - Automated Compliance Testing Tool Accelerates
   DevSecOps <https://www.securityweek.com/automated-compliance-testing-tool-accelerates-devsecops>`__
-  `20 Feb ’18 - 6 Requirements for Achieving
   DevSecOps <https://securityboulevard.com/2018/02/6-requirements-for-achieving-devsecops/>`__
-  `22 Jan ’18 - DevOps and Security: How to Overcome Cultural
   Challenges and Transform to True
   DevSecOps <https://thenewstack.io/devops-security-overcome-cultural-challenges-transform-true-devsecops/>`__
-  `15 Jan ’18 - Why DevSecOps matters to IT
   leaders <https://enterprisersproject.com/article/2018/1/why-devsecops-matters-it-leaders>`__
-  `27 Nov ’17 - What is vulnerability management? Processes and
   software for prioritizing
   threats <https://www.csoonline.com/article/3238080/vulnerabilities/what-is-vulnerability-management-processes-and-software-for-prioritizing-threats.html>`__
-  `23 Oct ’17 - The Ten Cybersecurity
   Commandments <http://www.securityweek.com/ten-cybersecurity-commandments>`__
-  `9 Oct ’17 - 10 layers of Linux container
   security <https://opensource.com/article/17/10/10-layers-container-security>`__
-  `5 Oct ’17 - How to Maintain Security when Rolling out
   DevOps <https://www.informationweek.com/devops/how-to-maintain-security-when-rolling-out-devops/a/d-id/1330047?imm_mid=0f71d7&cmp=em-webops-na-na-newsltr_security_20171010_length_control>`__
-  `26 Jan ’17 - DevOps and Separation of
   Duties <https://www.newcontext.com/devops-and-separation-of-duties/>`__
-  `26 Jul ’16 - Injecting security into Continuous
   Delivery <https://www.oreilly.com/learning/injecting-security-into-continuous-delivery>`__
-  `5 Jun ’16 - <— Shifting Security to the
   Left <http://www.devsecops.org/blog/2016/5/20/-security>`__
-  `Five Secrets and Two Common “Gotchas” of Vulnerability
   Scanning <https://www.kennasecurity.com/resources/secrets-gotchas-of-vuln-scanning>`__

.. |Maintained| image:: https://img.shields.io/maintenance/yes/2020.svg
.. |MIT license| image:: http://img.shields.io/badge/license-MIT-brightgreen.svg
   :target: http://opensource.org/licenses/MIT
.. |PythonVersions| image:: https://img.shields.io/pypi/pyversions/clair-cicd.svg?style=flat
.. |status| image:: https://img.shields.io/pypi/status/clair-cicd.svg?style=flat
.. |PyPI| image:: https://img.shields.io/pypi/v/clair-cicd.svg?style=flat
   :target: https://pypi.python.org/pypi/clair-cicd
.. |Requirements| image:: https://requires.io/github/simonsdave/clair-cicd/requirements.svg?branch=release-1.0.2
   :target: https://requires.io/github/simonsdave/clair-cicd/requirements/?branch=release-1.0.2
.. |CircleCI| image:: https://circleci.com/gh/simonsdave/clair-cicd/tree/release-1.0.2.svg?style=shield
   :target: https://circleci.com/gh/simonsdave/clair-cicd/tree/release-1.0.2
.. |codecov| image:: https://codecov.io/gh/simonsdave/clair-cicd/branch/release-1.0.2/graph/badge.svg
   :target: https://codecov.io/gh/simonsdave/clair-cicd/branch/release-1.0.2
.. |docker-simonsdave/clair-cicd-database| image:: https://img.shields.io/badge/docker-simonsdave%2Fclair%20cicd%20database-blue.svg
   :target: https://hub.docker.com/r/simonsdave/clair-cicd-database/
.. |docker-simonsdave/clair-cicd-clair| image:: https://img.shields.io/badge/docker-simonsdave%2Fclair%20cicd%20clair-blue.svg
   :target: https://hub.docker.com/r/simonsdave/clair-cicd-clair/