From f0faed09819de9b45d748757d75bbf7680a69bce Mon Sep 17 00:00:00 2001
From: Bertrand Goareguer <bertrand.goareguer@gmail.com>
Date: Mon, 20 Dec 2021 08:14:28 +0000
Subject: [PATCH] feat: add Trivy dependency scanner

---
 README.md                      | 15 +++++++++++
 kicker.json                    | 14 ++++++++++
 templates/gitlab-ci-python.yml | 48 ++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)

diff --git a/README.md b/README.md
index 4195de8..fae581d 100644
--- a/README.md
+++ b/README.md
@@ -228,6 +228,21 @@ It is bound to the `test` stage, and uses the following variables:
 
 This job outputs a **textual report** in the console, and in case of failure also exports a JSON report in the `reports/`
 directory _(relative to project root dir)_.
+
+### `py-trivy` job (dependency check)
+
+This job is **disabled by default** and performs a dependency check analysis using [Trivy](https://github.com/aquasecurity/trivy/).
+
+It is bound to the `test` stage, and uses the following variables:
+
+| Name             | description                                                             | default value     |
+| ---------------- | ----------------------------------------------------------------------- | ----------------- |
+| `PYTHON_TRIVY_ENABLED` | Set to `true` to enable Trivy job                                           | _none_ (disabled) |
+| `PYTHON_TRIVY_ARGS`    | Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/) | `--vuln-type library`   |
+
+This job outputs a **textual report** in the console, and in case of failure also exports a JSON report in the `reports/`
+directory _(relative to project root dir)_.
+
 ### Package jobs
 
 #### `py-package` job
diff --git a/kicker.json b/kicker.json
index 008b443..5983e8b 100644
--- a/kicker.json
+++ b/kicker.json
@@ -147,6 +147,20 @@
         }
       ]
     },
+    {
+      "id": "trivy",
+      "name": "Trivy",
+      "description": "Detect security vulnerabilities with [Trivy](https://github.com/aquasecurity/trivy/) (dependencies analysis)",
+      "enable_with": "PYTHON_TRIVY_ENABLED",
+      "variables": [
+        {
+          "name": "PYTHON_TRIVY_ARGS",
+          "description": "Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/)",
+          "default": "--vuln-type library",
+          "advanced": true
+        }
+      ]
+    },
     {
       "id": "package",
       "name": "package",
diff --git a/templates/gitlab-ci-python.yml b/templates/gitlab-ci-python.yml
index 95fa0dc..92a5ff0 100644
--- a/templates/gitlab-ci-python.yml
+++ b/templates/gitlab-ci-python.yml
@@ -36,6 +36,11 @@ variables:
   # Safety tool
   SAFETY_ARGS: "--full-report"
 
+  # Trivy tool
+  PYTHON_TRIVY_IMAGE: aquasec/trivy:latest
+  PYTHON_TRIVY_ARGS: "--vuln-type library"
+
+
   # Docs
   DOCS_REQUIREMENTS_FILE: docs-requirements.txt
   DOCS_DIRECTORY: docs
@@ -583,6 +588,49 @@ py-safety:
     - if: '$SAFETY_ENABLED == "true"'
       when: manual
       allow_failure: true
+
+# Trivy (dependency check)
+# Trivy only works if all dependencies are pinned to specific versions (e.g. with a poetry.lock file or a requirements.txt with all versions pinned)
+py-trivy:
+  extends: .python-base
+  image:
+    name: $PYTHON_TRIVY_IMAGE
+    entrypoint: [""]
+  stage: test
+  # force no dependencies
+  dependencies: []
+  script:
+    - mkdir -p reports
+    - chmod o+rwx reports
+    - |
+      if [ $(trivy fs ${PYTHON_TRIVY_ARGS} --format table --exit-code 0 $PYTHON_PROJECT_DIR | grep -c "Number of language-specific files: 0") -eq 1 ]; then
+        log_error "Could not find a file listing all dependencies with their versions."
+        exit 1
+      fi
+      trivy fs ${PYTHON_TRIVY_ARGS} --format table --exit-code 0 $PYTHON_PROJECT_DIR
+      trivy fs ${PYTHON_TRIVY_ARGS} --format json --output reports/trivy-python.json --exit-code 1 $PYTHON_PROJECT_DIR
+  
+  artifacts:
+    name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
+    expire_in: 1 day
+    when: always
+    paths:
+      - $PYTHON_PROJECT_DIR/reports/
+  rules:
+    # exclude merge requests
+    - if: $CI_MERGE_REQUEST_ID
+      when: never
+    # on production branch(es): if $TRIVY_ENABLED is set
+    # exclude if $PYTHON_TRIVY_ENABLED not set
+    - if: '$PYTHON_TRIVY_ENABLED != "true"'
+      when: never
+    # on production or integration branches: auto
+    - if: '$CI_COMMIT_REF_NAME =~ $PROD_REF || $CI_COMMIT_REF_NAME =~ $INTEG_REF'
+    # on non-production, non-integration branches: manual & non-blocking
+    - if: '$PYTHON_TRIVY_ENABLED == "true"' # useless but prevents GitLab warning
+      when: manual
+      allow_failure: true
+
 ###############################################################################################
 #                                      package stage                                           #
 ###############################################################################################
-- 
GitLab