diff --git a/.github/workflows/checkers-action.yml b/.github/workflows/checkers-action.yml new file mode 100644 index 0000000000..3888dffd26 --- /dev/null +++ b/.github/workflows/checkers-action.yml @@ -0,0 +1,75 @@ +name: Checkers-Action + +on: + push: + branches: [ main ] + paths: + - 'cve_bin_tool/checkers/**/*.py' + pull_request: + branches: [ main ] + paths: + - 'cve_bin_tool/checkers/**/*.py' + +jobs: + run-script: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Get date + id: get-date + run: | + echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT + echo "yesterday=$(/bin/date -d "-1 day" -u "+%Y%m%d")" >> $GITHUB_OUTPUT + + - name: Print Cache Keys + run: | + echo "Today's Cache Key: Linux-cve-bin-tool-${{ steps.get-date.outputs.date }}" + echo "Yesterday's Cache Key: Linux-cve-bin-tool-${{ steps.get-date.outputs.yesterday }}" + + - name: Get today's cached database + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 + id: todays-cache + with: + path: cache + key: Linux-cve-bin-tool-${{ steps.get-date.outputs.date }} + + - name: Get yesterday's cached database if today's is not available + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 + if: steps.todays-cache.outputs.cache-hit != 'true' + with: + path: cache + key: Linux-cve-bin-tool-${{ steps.get-date.outputs.yesterday }} + + - name: Install cve-bin-tool + if: env.sbom != 'true' + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade setuptools + python -m pip install --upgrade wheel + python -m pip install --upgrade -r dev-requirements.txt + python -m pip install --upgrade . + + - name: Try single CLI run of tool + if: env.sbom != 'true' + run: | + [[ -e cache ]] && mkdir -p .cache && mv cache ~/.cache/cve-bin-tool + NO_EXIT_CVE_NUM=1 python -m cve_bin_tool.cli test/assets/test-kerberos-5-1.15.1.out + cp -r ~/.cache/cve-bin-tool cache + + - name: Get changed files in checkers directory + id: changed-files + run: | + files=$(git diff --name-only ${{ github.sha }} ${{ github.event.before }} | grep '^cve_bin_tool/checkers/' | xargs) + echo "::set-output name=files::$files" + shell: bash + + - name: Run Python script + run: | + IFS=' ' read -r -a files <<< "${{ steps.changed-files.outputs.files }}" + for file in "${files[@]}"; do + python cve_bin_tool/ci_pre_checker.py "$file" + done + shell: bash + diff --git a/cve_bin_tool/checkers/README.md b/cve_bin_tool/checkers/README.md index 688b8dbb71..e8d7a71918 100644 --- a/cve_bin_tool/checkers/README.md +++ b/cve_bin_tool/checkers/README.md @@ -16,6 +16,7 @@ - [Running tests](#running-tests) - [How it works](#how-it-works) - [Updating checker table](#updating-checker-table) + - [Help, my checker PR fails `checkers-action`](#help-my-checker-pr-fails-the-checkers-action-in-github-ci) - [Pull Request Template](#pull-request-template) ## Requirements @@ -534,6 +535,10 @@ the product. We have done this in the checkers of `python` and`sqlite`. You do not need to run format_checkers.py to update the checker table in documentation. A pull request with updated checker table is created automatically when a new checker is merged. +## Help, my checker PR fails the `checkers-action` in github CI. + +CVE Binary Tool has a action named `checkers-action` in CI. If it fails, that means every {vendor,product} pair in the VENDOR_PRODUCT of the checker does not have a reported or associated CVE. This action triggers if any changes like addition/modification is done to the `checkers` directory. + ## Pull Request Template When you are ready to share your code, you can go to [our pull request page](https://github.com/intel/cve-bin-tool/pulls) to make a new pull request from the web interface and to use the guided template for new checker, click on the `Compare & pull request` button and add `?template=new_checker.md` at the end of the url. diff --git a/cve_bin_tool/checkers/test_pre_checker.py b/cve_bin_tool/checkers/test_pre_checker.py new file mode 100644 index 0000000000..6ff5e3dd65 --- /dev/null +++ b/cve_bin_tool/checkers/test_pre_checker.py @@ -0,0 +1,23 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: GPL-3.0-or-later + +""" + +Test for checker-action github action. +Below code is meant to mimic a checker, except it contains bogus VENDOR_PRODUCT. +The test in the CI should fail. + + +-- Joydeep Tripathy (joydeep049) +""" + +from __future__ import annotations + +from cve_bin_tool.checkers import Checker + + +class TestPreCheckerChecker(Checker): + CONTAINS_PATTERNS: list[str] = [] + FILENAME_PATTERNS: list[str] = [] + VERSION_PATTERNS = [] + VENDOR_PRODUCT = [("apc", "something")] diff --git a/cve_bin_tool/ci_pre_checker.py b/cve_bin_tool/ci_pre_checker.py new file mode 100644 index 0000000000..07dbb9db21 --- /dev/null +++ b/cve_bin_tool/ci_pre_checker.py @@ -0,0 +1,54 @@ +# Copyright (C) 2021 Intel Corporation +# SPDX-License-Identifier: GPL-3.0-or-later +"""Testing script for checkers-action.yml""" +import ast +import sqlite3 +import sys +from pathlib import Path + +OLD_CACHE_DIR = Path("~").expanduser() / ".cache" / "cve-bin-tool" / "cve.db" + + +def extract_vendor_product(file_path): + """Extract {vendor,product} pairs from given checker file""" + vendor_product = None + print(file_path) + with open(file_path) as file: + inside_vendor_product = False + vendor_product_str = "" + for line in file: + if "VENDOR_PRODUCT" in line: + inside_vendor_product = True + if inside_vendor_product: + print("inside_vendor_product") + vendor_product_str += line.strip() + if line.strip().endswith("]"): + break + if vendor_product_str: + print(vendor_product_str) + vendor_product = ast.literal_eval(vendor_product_str.split("=")[1].strip()) + return vendor_product + + +def query_database(file_path): + """Query the database and check whether all the {vendor,product} pairs have associated CVEs""" + vendor_product = extract_vendor_product(file_path) + dbcon = sqlite3.connect(OLD_CACHE_DIR) + cursor = dbcon.cursor() + for vendor, product in vendor_product: + cursor.execute( + "SELECT count(*) FROM cve_range WHERE vendor = ? AND product = ?", + (vendor, product), + ) + result = cursor.fetchall() + # Failing Workflow + if result[0] == 0: + sys.exit(1) + # Indicate Success + sys.exit(0) + + +# Caller Code +file_path = sys.argv[1] +print(OLD_CACHE_DIR) +query_database(file_path)