├── tests ├── dummy_data │ ├── rpm │ │ ├── packages │ │ ├── cve-2013-4459.nopatch │ │ ├── package-1.-5.src.rpm │ │ ├── package2-1.-5.src.rpm │ │ ├── package2.spec │ │ ├── package.spec │ │ └── CVE-2014-5461.patch │ ├── cve.ini │ ├── eopkg │ │ └── files │ │ │ └── security │ │ │ ├── cve-2013-4459.nopatch │ │ │ └── cve-2014-5461.patch │ ├── pkgbuild │ │ ├── PKGBUILD │ │ └── cve-2014-5461.patch │ └── plugins │ │ └── jira │ │ ├── cve-check-tool.conf │ │ └── jira_test_issues.json ├── check-database.c ├── check-core.c ├── check-packaging.c ├── check-jira-plugin.c ├── check-hashmap.c └── check-template.c ├── update_format.sh ├── AUTHORS ├── autogen.sh ├── src ├── plugins │ ├── output │ │ ├── csv │ │ │ ├── Makefile.am │ │ │ └── csv.c │ │ ├── html │ │ │ └── Makefile.am │ │ └── cli │ │ │ ├── Makefile.am │ │ │ ├── cli.h │ │ │ └── cli.c │ ├── packaging │ │ ├── faux │ │ │ ├── Makefile.am │ │ │ ├── faux.h │ │ │ └── faux.c │ │ ├── eopkg │ │ │ ├── Makefile.am │ │ │ ├── eopkg.h │ │ │ └── eopkg.c │ │ ├── pkgbuild │ │ │ ├── Makefile.am │ │ │ ├── pkgbuild.h │ │ │ └── pkgbuild.c │ │ └── rpm │ │ │ ├── Makefile.am │ │ │ ├── rpm.h │ │ │ ├── rpm_common.c │ │ │ └── srpm.c │ └── Makefile.am ├── update.h ├── Makefile.am ├── library │ ├── fetch.h │ ├── cve-check-tool.h │ ├── cve-string.c │ ├── cve-db-lock.h │ ├── common.h │ ├── template.h │ ├── cve-db-lock.c │ ├── fetch.c │ ├── cve-string.h │ ├── util.c │ ├── hashmap.h │ └── util.h ├── plugin-manager.h ├── plugin.h ├── core.h ├── update-main.c └── plugin-manager.c ├── test-init.sh ├── .gitignore ├── common.mk ├── .travis.yml ├── TODO ├── data ├── cve-check-tool.conf └── packages.template ├── README.md ├── .clang-format ├── docs └── cve-check-tool.1 ├── Makefile.am ├── configure.ac ├── HACKING └── m4 └── ax_valgrind_check.m4 /tests/dummy_data/rpm/packages: -------------------------------------------------------------------------------- 1 | package 2 | -------------------------------------------------------------------------------- /tests/dummy_data/rpm/cve-2013-4459.nopatch: -------------------------------------------------------------------------------- 1 | ## Ignore me ## -------------------------------------------------------------------------------- /tests/dummy_data/cve.ini: -------------------------------------------------------------------------------- 1 | product = dummy-product 2 | vendor = dummy-vendor 3 | -------------------------------------------------------------------------------- /tests/dummy_data/eopkg/files/security/cve-2013-4459.nopatch: -------------------------------------------------------------------------------- 1 | ## Ignore me ## -------------------------------------------------------------------------------- /update_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang-format -i $(find . -name '*.[ch]') 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Ikey Doherty 2 | John L. Whiteman 3 | -------------------------------------------------------------------------------- /tests/dummy_data/rpm/package-1.-5.src.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/cve-check-tool/master/tests/dummy_data/rpm/package-1.-5.src.rpm -------------------------------------------------------------------------------- /tests/dummy_data/rpm/package2-1.-5.src.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/cve-check-tool/master/tests/dummy_data/rpm/package2-1.-5.src.rpm -------------------------------------------------------------------------------- /tests/dummy_data/pkgbuild/PKGBUILD: -------------------------------------------------------------------------------- 1 | pkgname=my-test-package 2 | pkgver=1.0.3 3 | pkgrel=10 4 | pkgdesc="My Test Package" 5 | arch=('i686' 'x86_64') 6 | url="https://github.com" 7 | license=('GPLv2') 8 | 9 | build() { 10 | } 11 | 12 | check() { 13 | } 14 | 15 | package() { 16 | } 17 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | autoreconf --force --install --symlink --warnings=all 6 | 7 | args="\ 8 | --sysconfdir=/etc \ 9 | --localstatedir=/var \ 10 | --prefix=/usr \ 11 | --enable-silent-rules" 12 | 13 | ./configure CFLAGS="-g -O1 $CFLAGS" $args "$@" 14 | make clean 15 | -------------------------------------------------------------------------------- /src/plugins/output/csv/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | csv.la 3 | 4 | csv_la_SOURCES = \ 5 | src/plugins/output/csv/csv.c 6 | 7 | csv_la_LIBADD = \ 8 | $(MODULE_COMMON_LIBS) \ 9 | libcve.la 10 | 11 | csv_la_CFLAGS = \ 12 | $(MODULE_COMMON_CFLAGS) \ 13 | $(AM_CFLAGS) 14 | 15 | csv_la_LDFLAGS = \ 16 | $(MODULE_FLAGS) 17 | -------------------------------------------------------------------------------- /src/plugins/output/html/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | html.la 3 | 4 | html_la_SOURCES = \ 5 | src/plugins/output/html/html.c 6 | 7 | html_la_LIBADD = \ 8 | $(MODULE_COMMON_LIBS) \ 9 | libcve.la 10 | 11 | html_la_CFLAGS = \ 12 | $(MODULE_COMMON_CFLAGS) \ 13 | $(AM_CFLAGS) 14 | 15 | html_la_LDFLAGS = \ 16 | $(MODULE_FLAGS) 17 | -------------------------------------------------------------------------------- /test-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | top_srcdir="${1}" 5 | top_builddir="${2}" 6 | 7 | 8 | if [[ -d "${top_builddir}/tests/dummy_install/" ]]; then 9 | rm -rf "${top_builddir}/tests/dummy_install/" 10 | fi 11 | 12 | mkdir -p "${top_builddir}/tests/dummy_install/" 13 | 14 | cp ${top_builddir}/.libs/*.so "${top_builddir}/tests/dummy_install/." 15 | -------------------------------------------------------------------------------- /src/plugins/output/cli/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | cli.la 3 | 4 | cli_la_SOURCES = \ 5 | src/plugins/output/cli/cli.c \ 6 | src/plugins/output/cli/cli.h 7 | 8 | cli_la_LIBADD = \ 9 | $(MODULE_COMMON_LIBS) \ 10 | libcve.la 11 | 12 | cli_la_CFLAGS = \ 13 | $(MODULE_COMMON_CFLAGS) \ 14 | $(AM_CFLAGS) 15 | 16 | cli_la_LDFLAGS = \ 17 | $(MODULE_FLAGS) 18 | -------------------------------------------------------------------------------- /src/plugins/packaging/faux/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | faux.la 3 | 4 | faux_la_SOURCES = \ 5 | src/plugins/packaging/faux/faux.c \ 6 | src/plugins/packaging/faux/faux.h 7 | 8 | faux_la_LIBADD = \ 9 | $(MODULE_COMMON_LIBS) \ 10 | libcve.la 11 | 12 | faux_la_CFLAGS = \ 13 | $(MODULE_COMMON_CFLAGS) \ 14 | $(AM_CFLAGS) 15 | 16 | faux_la_LDFLAGS = \ 17 | $(MODULE_FLAGS) 18 | -------------------------------------------------------------------------------- /src/plugins/packaging/eopkg/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | eopkg.la 3 | 4 | eopkg_la_SOURCES = \ 5 | src/plugins/packaging/eopkg/eopkg.c \ 6 | src/plugins/packaging/eopkg/eopkg.h 7 | 8 | eopkg_la_LIBADD = \ 9 | $(MODULE_COMMON_LIBS) \ 10 | libcve.la 11 | 12 | eopkg_la_CFLAGS = \ 13 | $(MODULE_COMMON_CFLAGS) \ 14 | $(AM_CFLAGS) 15 | 16 | eopkg_la_LDFLAGS = \ 17 | $(MODULE_FLAGS) 18 | -------------------------------------------------------------------------------- /src/plugins/packaging/pkgbuild/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | pkgbuild.la 3 | 4 | pkgbuild_la_SOURCES = \ 5 | src/plugins/packaging/pkgbuild/pkgbuild.c \ 6 | src/plugins/packaging/pkgbuild/pkgbuild.h 7 | 8 | pkgbuild_la_LIBADD = \ 9 | $(MODULE_COMMON_LIBS) \ 10 | libcve.la 11 | 12 | pkgbuild_la_CFLAGS = \ 13 | $(MODULE_COMMON_CFLAGS) \ 14 | $(AM_CFLAGS) 15 | 16 | pkgbuild_la_LDFLAGS = \ 17 | $(MODULE_FLAGS) 18 | -------------------------------------------------------------------------------- /src/plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES = 2 | 3 | # Output plugins 4 | include src/plugins/output/cli/Makefile.am 5 | include src/plugins/output/csv/Makefile.am 6 | include src/plugins/output/html/Makefile.am 7 | 8 | # Packaging plugins 9 | include src/plugins/packaging/eopkg/Makefile.am 10 | include src/plugins/packaging/faux/Makefile.am 11 | include src/plugins/packaging/pkgbuild/Makefile.am 12 | include src/plugins/packaging/rpm/Makefile.am 13 | -------------------------------------------------------------------------------- /tests/dummy_data/rpm/package2.spec: -------------------------------------------------------------------------------- 1 | # Mostly ensuring demacro performs correctly 2 | %define sub package 3 | %define patch 0 4 | %define minor 1 5 | %define suffix %{minor}.%{patch} 6 | 7 | # test-package 8 | Name : test-%{sub} 9 | # 1.1.0 10 | Version : 1.%{suffix} 11 | Release : 5 12 | Patch : cve-2014-5161.patch 13 | Patch2 : cve-2014-5461.patch 14 | 15 | %description 16 | Invalid RPM spec package, provided solely for testing 17 | 18 | %patch 19 | %patch2 20 | -------------------------------------------------------------------------------- /tests/dummy_data/rpm/package.spec: -------------------------------------------------------------------------------- 1 | # Mostly ensuring demacro performs correctly 2 | %define sub package 3 | %define patch 0 4 | %define minor 1 5 | %define suffix %{minor}.%{patch} 6 | 7 | # test-package 8 | Name : test-%{sub} 9 | Summary : foo 10 | License : GPL-2.0 11 | # 1.1.0 12 | Version : 1.%{suffix} 13 | Release : 5 14 | Patch : cve-2014-5461.patch 15 | Patch1 : cve-2013-4459.nopatch 16 | 17 | %description 18 | Invalid RPM spec package, provided solely for testing 19 | 20 | %setup 21 | %patch0 22 | -------------------------------------------------------------------------------- /tests/dummy_data/rpm/CVE-2014-5461.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/ldo.c b/src/ldo.c 2 | index d1bf786..d43f611 100644 3 | --- a/src/ldo.c 4 | +++ b/src/ldo.c 5 | @@ -217,7 +217,7 @@ static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { 6 | int nvar = actual - nfixargs; /* number of extra arguments */ 7 | lua_assert(p->is_vararg & VARARG_HASARG); 8 | luaC_checkGC(L); 9 | - luaD_checkstack(L, p->maxstacksize); 10 | + luaD_checkstack(L, p->maxstacksize + p->numparams); 11 | htab = luaH_new(L, nvar, 1); /* create `arg' table */ 12 | for (i=0; itop - nvar + i); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | m4/libtool.m4 4 | m4/lt~obsolete.m4 5 | m4/ltoptions.m4 6 | m4/ltsugar.m4 7 | m4/ltversion.m4 8 | 9 | config.* 10 | ltmain.sh 11 | libtool 12 | install-sh 13 | stamp-* 14 | depcomp 15 | missing 16 | aclocal.m4 17 | .deps 18 | compile 19 | configure 20 | *.o 21 | autom4te.cache 22 | cve-check-tool 23 | cve-check-update 24 | report.html 25 | test-driver 26 | 27 | # Checks 28 | check_core 29 | check_database 30 | check_*.log 31 | check_*.trs 32 | test-*.log 33 | .dirstamp 34 | check_jira_plugin 35 | check_hashmap 36 | check_packaging 37 | check_template 38 | 39 | coverage/ 40 | 41 | *.gcno 42 | *.gcda 43 | 44 | *.lo 45 | .libs 46 | *.la 47 | 48 | /tests/dummy_install/ 49 | -------------------------------------------------------------------------------- /tests/dummy_data/pkgbuild/cve-2014-5461.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/ldo.c b/src/ldo.c 2 | index d1bf786..d43f611 100644 3 | --- a/src/ldo.c 4 | +++ b/src/ldo.c 5 | @@ -217,7 +217,7 @@ static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { 6 | int nvar = actual - nfixargs; /* number of extra arguments */ 7 | lua_assert(p->is_vararg & VARARG_HASARG); 8 | luaC_checkGC(L); 9 | - luaD_checkstack(L, p->maxstacksize); 10 | + luaD_checkstack(L, p->maxstacksize + p->numparams); 11 | htab = luaH_new(L, nvar, 1); /* create `arg' table */ 12 | for (i=0; itop - nvar + i); 14 | -------------------------------------------------------------------------------- /tests/dummy_data/eopkg/files/security/cve-2014-5461.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/ldo.c b/src/ldo.c 2 | index d1bf786..d43f611 100644 3 | --- a/src/ldo.c 4 | +++ b/src/ldo.c 5 | @@ -217,7 +217,7 @@ static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { 6 | int nvar = actual - nfixargs; /* number of extra arguments */ 7 | lua_assert(p->is_vararg & VARARG_HASARG); 8 | luaC_checkGC(L); 9 | - luaD_checkstack(L, p->maxstacksize); 10 | + luaD_checkstack(L, p->maxstacksize + p->numparams); 11 | htab = luaH_new(L, nvar, 1); /* create `arg' table */ 12 | for (i=0; itop - nvar + i); 14 | -------------------------------------------------------------------------------- /src/plugins/packaging/rpm/Makefile.am: -------------------------------------------------------------------------------- 1 | pkglib_LTLIBRARIES += \ 2 | rpm.la \ 3 | srpm.la 4 | 5 | rpm_la_SOURCES = \ 6 | src/plugins/packaging/rpm/rpm.c \ 7 | src/plugins/packaging/rpm/rpm.h 8 | 9 | rpm_la_LIBADD = \ 10 | $(MODULE_COMMON_LIBS) \ 11 | libcve.la 12 | 13 | rpm_la_CFLAGS = \ 14 | $(MODULE_COMMON_CFLAGS) \ 15 | $(AM_CFLAGS) 16 | 17 | rpm_la_LDFLAGS = \ 18 | $(MODULE_FLAGS) 19 | 20 | srpm_la_SOURCES = \ 21 | src/plugins/packaging/rpm/srpm.c 22 | 23 | srpm_la_LIBADD = \ 24 | $(MODULE_COMMON_LIBS) \ 25 | libcve.la 26 | 27 | srpm_la_CFLAGS = \ 28 | $(MODULE_COMMON_CFLAGS) \ 29 | $(AM_CFLAGS) 30 | 31 | srpm_la_LDFLAGS = \ 32 | $(MODULE_FLAGS) 33 | 34 | EXTRA_DIST += \ 35 | src/plugins/packaging/rpm/rpm_common.c 36 | -------------------------------------------------------------------------------- /src/update.h: -------------------------------------------------------------------------------- 1 | /* 2 | * update.c - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #include "cve-string.h" 13 | 14 | cve_string *get_db_path(const char *path); 15 | 16 | int update_required(const char *db_file); 17 | 18 | bool update_db(bool quiet, const char *db_file, const char *cacert_file); 19 | 20 | /* 21 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 22 | * 23 | * Local variables: 24 | * c-basic-offset: 8 25 | * tab-width: 8 26 | * indent-tabs-mode: nil 27 | * End: 28 | * 29 | * vi: set shiftwidth=8 tabstop=8 expandtab: 30 | * :indentSize=8:tabSize=8:noTabs=true: 31 | */ 32 | -------------------------------------------------------------------------------- /src/plugins/packaging/faux/faux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * faux.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "cve-check-tool.h" 15 | 16 | struct FauxData { 17 | gchar **patched; 18 | gchar **ignored; 19 | }; 20 | 21 | bool faux_is_patched(struct source_package_t *pkg, char *id); 22 | bool faux_is_ignored(struct source_package_t *pkg, char *id); 23 | 24 | bool faux_is_package(const char *filename); 25 | 26 | /* 27 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 28 | * 29 | * Local variables: 30 | * c-basic-offset: 8 31 | * tab-width: 8 32 | * indent-tabs-mode: nil 33 | * End: 34 | * 35 | * vi: set shiftwidth=8 tabstop=8 expandtab: 36 | * :indentSize=8:tabSize=8:noTabs=true: 37 | */ 38 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -fstack-protector -Wall -pedantic \ 2 | -Wstrict-prototypes -Wundef -fno-common \ 3 | -Werror-implicit-function-declaration \ 4 | -Wformat -Wformat-security -Werror=format-security \ 5 | -Wno-conversion -Wunused-variable -Wunreachable-code \ 6 | -Wall -W -D_FORTIFY_SOURCE=2 \ 7 | -std=c99 -Wno-error 8 | 9 | AM_CPPFLAGS = \ 10 | -I $(top_srcdir)/src \ 11 | -I $(top_srcdir)/src/packaging \ 12 | -I $(top_srcdir)/src/library \ 13 | -I $(top_srcdir)/src/output \ 14 | -I $(top_srcdir)/src/plugins/packaging \ 15 | -DSITE_PATH=\"$(sysconfdir)\" \ 16 | -DTOP_DIR=\"$(abs_top_srcdir)\" \ 17 | -DTOP_BUILD_DIR=\"$(abs_top_builddir)\" 18 | 19 | if INTREE 20 | AM_CPPFLAGS += \ 21 | -DMODULE_DIR=\"$(abs_top_srcdir)/src/.libs\" \ 22 | -DDATA_DIRECTORY=\"$(abs_top_srcdir)/data\" 23 | else 24 | AM_CPPFLAGS += \ 25 | -DMODULE_DIR=\"$(pkglibdir)\" \ 26 | -DDATA_DIRECTORY=\"$(datadir)/cve-check-tool\" 27 | endif 28 | 29 | AUTOMAKE_OPTIONS = color-tests parallel-tests 30 | 31 | MODULE_FLAGS = \ 32 | $(AM_LDFLAGS) \ 33 | -fvisibility=hidden \ 34 | -module \ 35 | -avoid-version 36 | -------------------------------------------------------------------------------- /src/plugins/output/cli/cli.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cli.h - cve-check-tool helpers 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | 14 | #include "config.h" 15 | #include "cve-check-tool.h" 16 | #include "util.h" 17 | 18 | #define COL "\x1B[" 19 | #define COLOR(x) COL "3" #x "m" 20 | 21 | #define C_RESET "\033[0m" 22 | #define C_RED COLOR(1) 23 | #define C_GREEN COLOR(2) 24 | #define C_YELLOW COLOR(3) 25 | #define C_BLUE COLOR(4) 26 | #define C_MAGENTA COLOR(5) 27 | #define C_CYAN COLOR(6) 28 | #define C_WHITE COLOR(7) 29 | 30 | /* 31 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 32 | * 33 | * Local variables: 34 | * c-basic-offset: 8 35 | * tab-width: 8 36 | * indent-tabs-mode: nil 37 | * End: 38 | * 39 | * vi: set shiftwidth=8 tabstop=8 expandtab: 40 | * :indentSize=8:tabSize=8:noTabs=true: 41 | */ 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | compiler: 9 | - gcc 10 | 11 | os: 12 | - linux 13 | 14 | before_script: 15 | ./autogen.sh CC="gcc-5" 16 | 17 | language: c 18 | 19 | addons: 20 | apt: 21 | sources: 22 | - llvm-toolchain-precise-3.8 23 | - llvm-toolchain-precise 24 | - ubuntu-toolchain-r-test 25 | packages: 26 | - check 27 | - clang-3.8 28 | - clang-format-3.8 29 | - gcc-5 30 | - libblkid-dev 31 | - lcov 32 | - llvm-3.8 33 | - valgrind 34 | 35 | 36 | before_install: 37 | - gem install coveralls-lcov 38 | 39 | script: 40 | - lcov --version | grep "1.10" 41 | - export CC="gcc-5" 42 | - ./configure --enable-coverage && make && make check && make distcheck 43 | - clang-format-3.8 -i $(find . -name '*.[ch]') && git diff --exit-code 44 | 45 | 46 | after_success: 47 | - cd ${TRAVIS_BUILD_DIR} 48 | - lcov --compat-libtool --directory . --capture --output-file coverage.info 49 | - lcov --remove coverage.info 'tests/*' '/usr/*' --output-file coverage.info 50 | - coveralls-lcov coverage.info 51 | -------------------------------------------------------------------------------- /src/plugins/packaging/pkgbuild/pkgbuild.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pkgbuild.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "cve-check-tool.h" 15 | 16 | /** 17 | * Inspect an Arch Linux PKGBUILD file 18 | * 19 | * @param filename Path to the PKGBUILD file 20 | * @return a struct source_package_t if successful, otherwise NULL 21 | */ 22 | struct source_package_t *pkgbuild_inspect_spec(const char *filename); 23 | 24 | bool pkgbuild_is_patched(struct source_package_t *pkg, char *id); 25 | 26 | void pkgbuild_locate_sources(const char *directory, bool recurse, cve_add_callback cb); 27 | 28 | bool pkgbuild_is_package(const char *filename); 29 | 30 | /* 31 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 32 | * 33 | * Local variables: 34 | * c-basic-offset: 8 35 | * tab-width: 8 36 | * indent-tabs-mode: nil 37 | * End: 38 | * 39 | * vi: set shiftwidth=8 tabstop=8 expandtab: 40 | * :indentSize=8:tabSize=8:noTabs=true: 41 | */ 42 | -------------------------------------------------------------------------------- /src/plugins/packaging/eopkg/eopkg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * eopkg.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "cve-check-tool.h" 15 | 16 | /** 17 | * Inspect an Solus Operating System pspec.xml file (eopkg packaging) 18 | * 19 | * @param filename Path to the pspec.xml file 20 | * @return a struct source_package_t if successful, otherwise NULL 21 | */ 22 | struct source_package_t *eopkg_inspect_pspec(const char *filename); 23 | 24 | bool eopkg_is_patched(struct source_package_t *pkg, char *id); 25 | bool eopkg_is_ignored(struct source_package_t *pkg, char *id); 26 | 27 | void eopkg_locate_sources(const char *directory, bool recurse, cve_add_callback cb); 28 | 29 | bool eopkg_is_package(const char *filename); 30 | 31 | /* 32 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 33 | * 34 | * Local variables: 35 | * c-basic-offset: 8 36 | * tab-width: 8 37 | * indent-tabs-mode: nil 38 | * End: 39 | * 40 | * vi: set shiftwidth=8 tabstop=8 expandtab: 41 | * :indentSize=8:tabSize=8:noTabs=true: 42 | */ 43 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = \ 2 | cve-check-tool \ 3 | cve-check-update 4 | 5 | noinst_LTLIBRARIES = \ 6 | libcve.la 7 | 8 | libcve_la_SOURCES = \ 9 | src/library/common.h \ 10 | src/library/cve-check-tool.h \ 11 | src/library/cve-string.h \ 12 | src/library/cve-string.c \ 13 | src/library/hashmap.h \ 14 | src/library/hashmap.c \ 15 | src/library/cve-db-lock.h \ 16 | src/library/cve-db-lock.c \ 17 | src/library/fetch.c \ 18 | src/library/fetch.h \ 19 | src/library/template.c \ 20 | src/library/template.h \ 21 | src/library/util.c \ 22 | src/library/util.h \ 23 | src/plugins/packaging/faux/faux.h \ 24 | src/core.h \ 25 | src/core.c 26 | 27 | libcve_la_CFLAGS = \ 28 | $(CVE_CHECK_TOOL_CFLAGS) \ 29 | $(AM_CFLAGS) 30 | 31 | libcve_la_LIBADD = \ 32 | $(CVE_CHECK_TOOL_LIBS) 33 | 34 | cve_check_tool_SOURCES = \ 35 | src/main.c \ 36 | src/update.h \ 37 | src/update.c \ 38 | src/plugin-manager.c \ 39 | src/plugin-manager.h \ 40 | src/plugin.h 41 | 42 | 43 | cve_check_tool_CFLAGS = \ 44 | $(CVE_CHECK_TOOL_CFLAGS) \ 45 | $(AM_CFLAGS) 46 | 47 | 48 | cve_check_tool_LDADD = \ 49 | $(CVE_CHECK_TOOL_LIBS) \ 50 | libcve.la \ 51 | -ldl 52 | 53 | cve_check_update_SOURCES = \ 54 | src/update.c \ 55 | src/update.h \ 56 | src/update-main.c 57 | 58 | 59 | cve_check_update_CFLAGS = \ 60 | $(CVE_CHECK_TOOL_CFLAGS) \ 61 | $(AM_CFLAGS) 62 | 63 | 64 | cve_check_update_LDADD = \ 65 | $(CVE_CHECK_TOOL_LIBS) \ 66 | libcve.la \ 67 | -ldl 68 | 69 | include src/plugins/Makefile.am 70 | -------------------------------------------------------------------------------- /src/library/fetch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * fetch.h - cve-check-tool helpers 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | typedef enum { FETCH_STATUS_FAIL, FETCH_STATUS_UPDATE, FETCH_STATUS_OK } FetchStatus; 17 | 18 | /** 19 | * Fetch the given URI to the target directory, only if it is newer than 20 | * its counterpart on disk, or the counterpart does not yet exist. 21 | * 22 | * @param uri URI to fetch 23 | * @param target Target filename 24 | * @param verbose Whether to be verbose 25 | * @return A FetchStatus, indicating the operation taken 26 | */ 27 | FetchStatus fetch_uri(const char *uri, const char *target, bool verbose, const char *cacert_file); 28 | 29 | /** 30 | * Attempt to extract the given gzipped file 31 | * 32 | * @param filename Path to file 33 | * @return a boolean value, indicating success of the operation 34 | */ 35 | bool gunzip_file(const char *path); 36 | 37 | /* 38 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 39 | * 40 | * Local variables: 41 | * c-basic-offset: 8 42 | * tab-width: 8 43 | * indent-tabs-mode: nil 44 | * End: 45 | * 46 | * vi: set shiftwidth=8 tabstop=8 expandtab: 47 | * :indentSize=8:tabSize=8:noTabs=true: 48 | */ 49 | -------------------------------------------------------------------------------- /src/plugin-manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * plugin-manager.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #include "plugin.h" 13 | #include 14 | 15 | /** 16 | * Initialise the plugin manager, loading all available plugins 17 | */ 18 | void cve_plugin_manager_init(void); 19 | 20 | /** 21 | * Destroy the plugin manager - unloading any loaded plugins 22 | */ 23 | void cve_plugin_manager_destroy(void); 24 | 25 | /** 26 | * Get a plugin with a given name if it exists 27 | * 28 | * @param name Name of the plugin to load 29 | * @return A matching CvePlugin, or NULL if not found 30 | */ 31 | CvePlugin *cve_plugin_get_by_name(const char *name); 32 | 33 | /** 34 | * Get a list of plugins for a given capability 35 | * 36 | * @note The allocated list must be freed, but not its contents 37 | * 38 | * @param cap Capibility as exposed via CvePluginType flags 39 | * @return a list of matching plugins, or NULL 40 | */ 41 | GList *cve_plugin_get_by_cap(int cap); 42 | 43 | /* 44 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 45 | * 46 | * Local variables: 47 | * c-basic-offset: 8 48 | * tab-width: 8 49 | * indent-tabs-mode: nil 50 | * End: 51 | * 52 | * vi: set shiftwidth=8 tabstop=8 expandtab: 53 | * :indentSize=8:tabSize=8:noTabs=true: 54 | */ 55 | -------------------------------------------------------------------------------- /src/plugins/packaging/rpm/rpm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * rpm.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "cve-check-tool.h" 15 | 16 | /** 17 | * Inspect an RPM spec file 18 | * 19 | * @param filename Path to the .spec file 20 | * @return a struct source_package_t if successful, otherwise NULL 21 | */ 22 | struct source_package_t *rpm_inspect_spec(const char *filename); 23 | struct source_package_t *srpm_examine(const char *filename); 24 | 25 | /** 26 | * Inspect a src.rpm 27 | * 28 | * @param dir Directory where srpm is expected to be located 29 | * @param name Package name 30 | * @param version Package version 31 | * @param release Package release 32 | * @return a struct source_package_t if successful, otherwise NULL 33 | */ 34 | 35 | struct source_package_t *rpm_inspect_srpm(const char *dir, const char *name, const char *version, const char *release); 36 | 37 | bool srpm_is_patched(struct source_package_t *t, char *id); 38 | bool srpm_is_ignored(struct source_package_t *t, char *id); 39 | bool rpm_is_patched(struct source_package_t *pkg, char *id); 40 | bool rpm_is_ignored(struct source_package_t *pkg, char *id); 41 | 42 | void rpm_locate_sources(const char *directory, bool recurse, cve_add_callback cb); 43 | void srpm_locate_sources(const char *directory, bool recurse, cve_add_callback cb); 44 | 45 | bool rpm_is_package(const char *filename); 46 | bool srpm_is_package(const char *filename); 47 | 48 | /* 49 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 50 | * 51 | * Local variables: 52 | * c-basic-offset: 8 53 | * tab-width: 8 54 | * indent-tabs-mode: nil 55 | * End: 56 | * 57 | * vi: set shiftwidth=8 tabstop=8 expandtab: 58 | * :indentSize=8:tabSize=8:noTabs=true: 59 | */ 60 | -------------------------------------------------------------------------------- /tests/dummy_data/plugins/jira/cve-check-tool.conf: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Use this section to define your global JIRA configuration parameters 3 | ###################################################################### 4 | [JIRA] 5 | # Sets the JIRA server's REST API URL 6 | # e.g., https:///rest/api/2 7 | url=http://127.0.0.1 8 | 9 | # Sets the JIRA login user name 10 | user=myuser 11 | 12 | # Sets the JIRA login user name's password if any 13 | password=mypassword 14 | 15 | # Sets the timeout in seconds in case server hangs. 0 means wait forver 16 | timeout_secs=60 17 | 18 | # Sets to true to show more output, good for debugging 19 | verbose=false 20 | 21 | ###################################################################### 22 | # Use this section to define the default fields and values for adding 23 | # a new JIRA issue. Make sure that they exist with proper permissions. 24 | # You can add if required. If your JIRA schema is very complicated 25 | # with many custom fields and options then consider using template instead. 26 | # 27 | # =value # For simple fields like summary and description 28 | # .=value # For fields that have attributes, issuetype 29 | # .[] # For fields that arrays of attributes, components 30 | ###################################################################### 31 | [JIRA-New-Issue] 32 | components.[name]=YOURCOMPONENT 33 | issuetype.name=Bug 34 | project.key=YOURPROJECT 35 | assignee.name=YOURUSERNAME 36 | 37 | ###################################################################### 38 | # Use this section to define your JIRA search criteria 39 | ###################################################################### 40 | [JIRA-Search-Issues] 41 | 42 | # Literal JIRA jql search criteria 43 | # See https://confluence.atlassian.com/display/JIRA/Advanced+Searching 44 | jql="project=MYPROJECT AND component=MYCOMPONENT","maxResults":10000 45 | 46 | 47 | # Set search token for summary fields. Globbing with wildcards ok. 48 | search_filter=CVE-* 49 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO List 2 | --------- 3 | 4 | cve-check-tool has achieved minimal functionality, which as come at a 5 | cost. A refactor will be needed to bring performance and flexibility 6 | up to par. 7 | 8 | In essence cve-check-tool is an aggregator of data sources, performing 9 | a cross-reference to determine whether the final user (distribution) is 10 | affected by a CVE. 11 | 12 | Redesign overview: 13 | 14 | The current primary data source is the NVD DB. However due to the fact 15 | it is in XML it is extremely expensive to work with. We mitigate this 16 | for the most part by interweaving cross-reference logic with data checking. 17 | 18 | Going forward, data sources will be separate plugins to feed the primary 19 | database. This database will most likely be sqlite3, offering high performance 20 | read operations. 21 | 22 | cve-check-tool will pull read-only from its database, cross-referencing 23 | distro data (whether previously imported or dynamic at runtime) - improving 24 | performance and vastly reducing code complexity. 25 | 26 | Data sources and parsers are to be separate from reporters (such as JIRA, 27 | HTML, CSV) - removing the current interweaving issue. 28 | 29 | This means splitting the data-import from the cross-check runs: 30 | 31 | 32 | .---------------------. 33 | | Package/distro data | 34 | '---------------------' 35 | | .------. 36 | v | CSV | 37 | (cve-check-tool binary) '------' 38 | ******************* .------. 39 | * Cross-reference * ----> | HTML | Output 40 | ******************* '------' 41 | ^ .------. 42 | Data sources | | JIRA | 43 | | '------' 44 | .-----. .----------------------. 45 | | NVD | -- -> | CVE/Central database | 46 | '-----' '----------------------' 47 | .-----. ^ 48 | | USN | ---------| 49 | '-----' 50 | 51 | 52 | This has many benefits, as the majority of queries we are interested in 53 | are best supported by database solutions, i.e. 54 | SELECT * FROM vulnerabilities WHERE product ... 55 | -------------------------------------------------------------------------------- /src/library/cve-check-tool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cve-check-tool.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "common.h" 19 | 20 | #include "core.h" 21 | 22 | /** 23 | * Distro implementations need to add packages to the interest list 24 | * before we will check them for CVEs. This will actually call back to 25 | * the self->examine function, and add the parsed package into the 26 | * current list. 27 | * 28 | * @param path Full legal path to the source package 29 | */ 30 | 31 | typedef void (*cve_add_callback)(const char *); 32 | 33 | /** 34 | * Instance tracking 35 | */ 36 | typedef struct CveCheckTool { 37 | int64_t modified; /**/rest/api/2 11 | url= 12 | 13 | # Sets the JIRA login user name 14 | user= 15 | 16 | # Sets the JIRA login user name's password if any 17 | password= 18 | 19 | # Sets the timeout in seconds in case server hangs. 0 means wait forever 20 | timeout_secs=60 21 | 22 | # Sets to true to show more output, good for debugging 23 | verbose=false 24 | 25 | ###################################################################### 26 | # Use this section to define the default fields and values for adding 27 | # a new JIRA issue. Make sure that they exist with proper permissions. 28 | # If your JIRA schema is more complicated with custom fields and options 29 | # beyond the defaults shown here then consider using a template instead. 30 | # 31 | # =value # For simple fields, like summary 32 | # .=value # For fields that have attributes, like issuetype 33 | # .[] # For fields that have arrays of attributes, like components 34 | ###################################################################### 35 | [JIRA-New-Issue] 36 | components.[name]=YOURCOMPONENT 37 | issuetype.name=Bug 38 | project.key=YOURPROJECT 39 | assignee.name=YOURUSERNAME 40 | 41 | ###################################################################### 42 | # Use this section to define your JIRA search criteria 43 | ###################################################################### 44 | [JIRA-Search-Issues] 45 | 46 | # Literal JIRA jql search criteria 47 | # Note: maxResults should be set to a high enough value to return 48 | # all records that match your criteria. By default, JIRA 49 | # servers may limit the number of rows returned. 50 | # See https://confluence.atlassian.com/display/JIRA/Advanced+Searching 51 | jql="project=MYPROJECT AND component=MYCOMPONENT","maxResults":100000 52 | 53 | # Set search token for summary fields. Globbing with wildcards ok. 54 | search_filter=CVE-* 55 | -------------------------------------------------------------------------------- /src/plugins/packaging/rpm/rpm_common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * rpm.c - Generic RPM support 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "cve-check-tool.h" 20 | #include "rpm.h" 21 | #include "util.h" 22 | 23 | static inline gchar *firstword(gchar *input) 24 | { 25 | autofree(gstrv) *splits = NULL; 26 | 27 | splits = g_strsplit(input, " ", 2); 28 | return g_strdup(splits[0]); 29 | } 30 | 31 | /** 32 | * Determine if strings are equal, but ignore case sensitivity. 33 | */ 34 | static inline bool str_iequal(const gchar *inp, const gchar *cmp) 35 | { 36 | autofree(gchar) *left = g_ascii_strdown(inp, -1); 37 | autofree(gchar) *right = g_ascii_strdown(cmp, -1); 38 | 39 | if (g_str_equal(left, right)) { 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | /** 46 | * Utility method: Determine if string has the given prefix, case insensitive. 47 | */ 48 | static inline bool str_has_iprefix(const gchar *inp, const gchar *cmp) 49 | { 50 | autofree(gchar) *left = g_ascii_strdown(inp, -1); 51 | autofree(gchar) *right = g_ascii_strdown(cmp, -1); 52 | 53 | if (g_str_has_prefix(left, right)) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | /** 60 | * Utility method: Determine if string has the given suffix, case insensitive. 61 | */ 62 | static inline bool str_has_isuffix(const gchar *inp, const gchar *cmp) 63 | { 64 | autofree(gchar) *left = g_ascii_strdown(inp, -1); 65 | autofree(gchar) *right = g_ascii_strdown(cmp, -1); 66 | 67 | if (g_str_has_suffix(left, right)) { 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | /* 74 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 75 | * 76 | * Local variables: 77 | * c-basic-offset: 8 78 | * tab-width: 8 79 | * indent-tabs-mode: nil 80 | * End: 81 | * 82 | * vi: set shiftwidth=8 tabstop=8 expandtab: 83 | * :indentSize=8:tabSize=8:noTabs=true: 84 | */ 85 | -------------------------------------------------------------------------------- /src/library/cve-string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cve-string.c - string management 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "cve-string.h" 20 | 21 | cve_string *cve_string_dup(const char *str) 22 | { 23 | if (!str) { 24 | return NULL; 25 | } 26 | struct cve_string_t *st = calloc(1, sizeof(struct cve_string_t)); 27 | if (!st) { 28 | return NULL; 29 | } 30 | st->len = asprintf(&st->str, "%s", str); 31 | if (st->len < 0 || !st->str) { 32 | free(st); 33 | return NULL; 34 | } 35 | return st; 36 | } 37 | 38 | cve_string *cve_string_dup_printf(const char *ptn, ...) 39 | { 40 | if (!ptn) { 41 | return NULL; 42 | } 43 | 44 | struct cve_string_t *st = calloc(1, sizeof(struct cve_string_t)); 45 | if (!st) { 46 | return NULL; 47 | } 48 | 49 | va_list va; 50 | va_start(va, ptn); 51 | 52 | st->len = vasprintf(&st->str, ptn, va); 53 | if (st->len < 0 || !st->str) { 54 | free(st); 55 | st = NULL; 56 | goto end; 57 | } 58 | end: 59 | va_end(va); 60 | 61 | return st; 62 | } 63 | 64 | bool cve_string_cat(cve_string *s, const char *append) 65 | { 66 | char *p = NULL; 67 | int len = 0; 68 | 69 | if (!s || !append) { 70 | return false; 71 | } 72 | if (!s->str) { 73 | return false; 74 | } 75 | len = asprintf(&p, "%s%s", s->str, append); 76 | if (!p || len < s->len) { 77 | return false; 78 | } 79 | free(s->str); 80 | s->str = p; 81 | s->len = len; 82 | return true; 83 | } 84 | 85 | /* 86 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 87 | * 88 | * Local variables: 89 | * c-basic-offset: 8 90 | * tab-width: 8 91 | * indent-tabs-mode: nil 92 | * End: 93 | * 94 | * vi: set shiftwidth=8 tabstop=8 expandtab: 95 | * :indentSize=8:tabSize=8:noTabs=true: 96 | */ 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cve-check-tool 2 | ============== 3 | 4 | [![Build Status](https://travis-ci.org/ikeydoherty/cve-check-tool.svg?branch=master)](https://travis-ci.org/ikeydoherty/cve-check-tool) 5 | [![Coverage Status](https://coveralls.io/repos/github/ikeydoherty/cve-check-tool/badge.png?branch=master)](https://coveralls.io/github/ikeydoherty/cve-check-tool?branch=master) 6 | 7 | 8 | cve-check-tool, as its name suggests, is a tool for checking known 9 | (public) CVEs. The tool will identify potentially vunlnerable software 10 | packages within Linux distributions through version matching. Where 11 | possible it will also seek to determine (through a distribution 12 | implemention) if a vulnerability has been addressed by way of a patch. 13 | 14 | CVEs are only ever *potential* - due to the various policies of various 15 | distributions, and indeed semantics in versioning within various projects, 16 | it is expected that the tool may generate false positives. 17 | 18 | The tool is designed to integrate with a locally cached copy of the 19 | National Vulnerability Database, which should be updated every 3-4 20 | hours. Correctly integrated within the workflow of a distribution, 21 | and indeed with the correct bug report tool, this yields a minimum 22 | 4 hour turnaround on all disclosed CVEs (non-embargoed) 23 | 24 | Data Usage 25 | ---------- 26 | cve-check-tool downloads the NVD in its entirety, from 2002 until the 27 | current moment. The decompressed XML database is in excess of 550MB, 28 | so this should be taken into account before running the tool. From then 29 | on, only the *changed* database segments are fetched. Therefore it is 30 | advisable to use cve-check-tool on a machine that has sufficient space 31 | and internet connection. 32 | 33 | On a fairly modern machine, it should only take around 10 seconds to 34 | consume the databases. Note however that when the tool runs, it will 35 | use a lot of resources to ensure it is fast (it needs to go through over 36 | 7 million lines of XML, for one.) 37 | 38 | CLI usage: 39 | ---------- 40 | 41 | Most common usage, automatically determine package type and scan for the 42 | packages in the given package list file: 43 | 44 | cve-check-tool ../packages 45 | 46 | Recurse a directory structure, with the predetermined type of eopkg: 47 | 48 | cve-check-tool -t eopkg . 49 | 50 | Check a single RPM source package, ignoring patched issues: 51 | 52 | cve-check-tool -n readline.spec 53 | 54 | Flags can be combined, check `-h` for details. An example to recurse all 55 | directories, finding .spec RPM files, and ignoring patched issues: 56 | 57 | cve-check-tool -n -t rpm . 58 | 59 | 60 | License 61 | -------- 62 | 63 | cve-check-tool is available under the terms of the GNU General Public License, 64 | Version 2. Please check the LICENSE file for further details. 65 | 66 | Copyright (C) 2015 Intel Corporation 67 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: 0 3 | AlignAfterOpenBracket: true 4 | AlignConsecutiveAssignments: false 5 | #uncomment for clang 3.9 6 | #AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: None 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | # AlwaysBreakAfterDefinitionReturnType: None 17 | #uncomment for clang 3.9 18 | #AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: false 22 | BinPackParameters: true 23 | # BraceWrapping: (not set since BreakBeforeBraces is not Custom) 24 | BreakBeforeBinaryOperators: None 25 | # BreakAfterJavaFieldAnnotations: (not java) 26 | BreakBeforeBinaryOperators: None 27 | BreakBeforeBraces: Linux 28 | BreakBeforeTernaryOperators: true 29 | BreakConstructorInitializersBeforeComma: false 30 | #uncomment for clang 3.9 31 | #BreakStringLiterals: false 32 | ColumnLimit: 120 33 | CommentPragmas: '\*\<' 34 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 35 | ConstructorInitializerIndentWidth: 4 36 | ContinuationIndentWidth: 4 37 | Cpp11BracedListStyle: false 38 | DerivePointerAlignment: false 39 | DisableFormat: false 40 | ExperimentalAutoDetectBinPacking: false 41 | ForEachMacros: [ NC_LIST_FOREACH ] 42 | #Uncomment for clang 3.9 43 | #IncludeCategories: 44 | # - Regex: '^"' 45 | # Priority: 1 46 | # IncludeIsMainRegex: (project doesn't use a main includes that can add other includes via regex) 47 | IndentCaseLabels: false 48 | IndentWidth: 8 49 | IndentWrappedFunctionNames: false 50 | # JavaScriptQuotes: (not javascript) 51 | KeepEmptyLinesAtTheStartOfBlocks: false 52 | Language: Cpp 53 | MacroBlockBegin: '' 54 | MacroBlockEnd: '' 55 | MaxEmptyLinesToKeep: 1 56 | NamespaceIndentation: None 57 | # ObjCBlockIndentWidth: (not objc) 58 | # ObjCSpaceAfterProperty: (not objc) 59 | # ObjCSpaceBeforeProtocolList: (not objc) 60 | PenaltyBreakBeforeFirstCallParameter: 400 61 | PenaltyBreakComment: 0 62 | # PenaltyBreakFirstLessLess: (not cpp) 63 | PenaltyBreakString: 500 64 | PenaltyExcessCharacter: 10000 65 | PenaltyReturnTypeOnItsOwnLine: 600 66 | PointerAlignment: Right 67 | #uncomment for clang 3.9 68 | #ReflowComments: true 69 | #uncomment for clang 3.9 70 | #SortIncludes: true 71 | SpaceAfterCStyleCast: false 72 | SpaceBeforeAssignmentOperators: true 73 | SpaceBeforeParens: ControlStatements 74 | SpaceInEmptyParentheses: false 75 | SpacesBeforeTrailingComments: 1 76 | SpacesInAngles: false 77 | SpacesInCStyleCastParentheses: false 78 | # SpacesInContainerLiterals: (not objc or javascript) 79 | SpacesInParentheses: false 80 | SpacesInSquareBrackets: false 81 | Standard: Cpp11 82 | TabWidth: 8 83 | UseTab: Never 84 | ... 85 | -------------------------------------------------------------------------------- /tests/dummy_data/plugins/jira/jira_test_issues.json: -------------------------------------------------------------------------------- 1 | {"expand":"schema,names","startAt":0,"maxResults":10,"total":10,"issues":[{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"123456","self":"http://127.0.0.1/rest/api/2/issue/123456","key":"MYPROJECT-21","fields":{"summary":"CVE-1999-0095","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0095","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"234567","self":"http://127.0.0.1/rest/api/2/issue/234567","key":"MYPROJECT-111","fields":{"summary":"CVE-1999-1123","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-1123","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"256789","self":"http://127.0.0.1/rest/api/2/issue/256789","key":"MYPROJECT-256","fields":{"summary":"CVE-1999-0082","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0082","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"312321","self":"http://127.0.0.1/rest/api/2/issue/312321","key":"MYPROJECT-321","fields":{"summary":"CVE-1999-0097","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0097","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"421345","self":"http://127.0.0.1/rest/api/2/issue/421345","key":"MYPROJECT-443","fields":{"summary":"CVE-1999-0181","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0181","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"543212","self":"http://127.0.0.1/rest/api/2/issue/543212","key":"MYPROJECT-589","fields":{"summary":"CVE-1999-1193","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-1193","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"654321","self":"http://127.0.0.1/rest/api/2/issue/654321","key":"MYPROJECT-609","fields":{"summary":"CVE-1999-0019","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0019","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"765432","self":"http://127.0.0.1/rest/api/2/issue/765432","key":"MYPROJECT-701","fields":{"summary":"CVE-1999-0211","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0211","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"890765","self":"http://127.0.0.1/rest/api/2/issue/890765","key":"MYPROJECT-867","fields":{"summary":"CVE-1999-0626","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-0626","status":{"name":"New"}}},{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"921345","self":"http://127.0.0.1/rest/api/2/issue/921345","key":"MYPROJECT-988","fields":{"summary":"CVE-1999-1385","description":"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-1999-1385","status":{"name":"New"}}}]} 2 | -------------------------------------------------------------------------------- /src/plugin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * plugin.h 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #include "common.h" 13 | #include "cve-check-tool.h" 14 | #include 15 | 16 | #pragma once 17 | 18 | typedef enum { 19 | PLUGIN_TYPE_MIN = 1 << 0, 20 | PLUGIN_TYPE_PACKAGE = 1 << 1, /**. 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #include 13 | 14 | /** 15 | * Enumerate to represent possible locking types to imply on the 16 | * database file 17 | */ 18 | typedef enum { 19 | LT_READ = 0, /** 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "cve-check-tool.h" 19 | #include "faux.h" 20 | #include "plugin.h" 21 | #include "util.h" 22 | 23 | void faux_free(struct source_package_t *pkg) 24 | { 25 | struct FauxData *d = NULL; 26 | 27 | if (pkg && pkg->extra) { 28 | d = pkg->extra; 29 | if (d->patched) { 30 | g_strfreev(d->patched); 31 | } 32 | if (d->ignored) { 33 | g_strfreev(d->ignored); 34 | } 35 | free(pkg->extra); 36 | pkg->extra = NULL; 37 | } 38 | } 39 | 40 | static bool faux_patch_check(struct source_package_t *t, char *id, bool ignore) 41 | { 42 | struct FauxData *d = NULL; 43 | gchar **list = NULL; 44 | 45 | if (!t->extra) { 46 | return false; 47 | } 48 | 49 | d = t->extra; 50 | list = ignore ? d->ignored : d->patched; 51 | if (!list) { 52 | return false; 53 | } 54 | 55 | for (uint i = 0; i < g_strv_length(list); i++) { 56 | list[i] = g_strchomp(list[i]); 57 | if (g_str_equal(list[i], "")) { 58 | continue; 59 | } 60 | if (g_str_equal(id, list[i])) { 61 | return true; 62 | } 63 | } 64 | 65 | return false; 66 | } 67 | 68 | bool faux_is_patched(struct source_package_t *t, char *id) 69 | { 70 | return faux_patch_check(t, id, false); 71 | } 72 | 73 | bool faux_is_ignored(struct source_package_t *t, char *id) 74 | { 75 | return faux_patch_check(t, id, true); 76 | } 77 | 78 | bool faux_is_package(const char *filename) 79 | { 80 | return g_str_has_suffix((const gchar *)filename, ".csv"); 81 | } 82 | 83 | _module_export_ bool cve_plugin_module_init(CvePlugin *self) 84 | { 85 | self->flags = PLUGIN_TYPE_PACKAGE; 86 | self->name = "faux"; 87 | self->is_ignored = faux_is_ignored; 88 | self->is_patched = faux_is_patched; 89 | self->is_package = faux_is_package; 90 | self->scan_package = NULL; 91 | self->free_package = faux_free; 92 | return true; 93 | } 94 | 95 | /* 96 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 97 | * 98 | * Local variables: 99 | * c-basic-offset: 8 100 | * tab-width: 8 101 | * indent-tabs-mode: nil 102 | * End: 103 | * 104 | * vi: set shiftwidth=8 tabstop=8 expandtab: 105 | * :indentSize=8:tabSize=8:noTabs=true: 106 | */ 107 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * core.h - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | #pragma once 12 | 13 | #include "common.h" 14 | #include 15 | 16 | extern const char *nvd_file; /* nvd.db */ 17 | extern const char *nvd_dir; /* NVDS */ 18 | extern bool use_frac_compare; 19 | 20 | /** 21 | * A CVE Database, using the National Vulnerability Database as its source 22 | */ 23 | typedef struct CveDB CveDB; 24 | 25 | typedef enum { 26 | CVE_DB_MIN = 1 << 0, 27 | CVE_DB_ID = 1 << 1, 28 | CVE_DB_PRODUCT = 1 << 2, 29 | CVE_DB_VENDOR = 1 << 3, 30 | CVE_DB_MODIFIED = 1 << 4, 31 | CVE_DB_LIKE = 1 << 5, 32 | CVE_DB_MAX = 1 << 6 33 | } SearchType; 34 | 35 | /** 36 | * Construct a new CveDB 37 | */ 38 | CveDB *cve_db_new(const char *path); 39 | 40 | /** 41 | * Free an allocated CveDB 42 | * 43 | * @param db A valid CveDB instance 44 | */ 45 | void cve_db_free(CveDB *db); 46 | 47 | /** 48 | * Finalize the DB initialisation 49 | */ 50 | bool cve_db_finalize(CveDB *db); 51 | 52 | /** 53 | * Begin the DB initialisation 54 | */ 55 | bool cve_db_begin(CveDB *db); 56 | 57 | /** 58 | * Load the NVD XML file into this DB 59 | * 60 | * @param db A valid CveDB instance 61 | * @param file Path to the NVD XML data 62 | * 63 | * @return a boolean value, indicating success 64 | */ 65 | bool cve_db_load(CveDB *db, const char *file); 66 | 67 | /** 68 | * Get CVE data for the given ID 69 | * 70 | * @param db A valid CveDB instance 71 | * @param id The CVE-XXXX-XXXX ID 72 | * 73 | * @return A newly allocated data structure, or NULL if the CVE is not found 74 | */ 75 | struct cve_entry_t *cve_db_get_cve(CveDB *db, char *id); 76 | 77 | /** 78 | * Retrieve a list of CVEs for the given parameters 79 | * 80 | * @param db A valid CveDB instance 81 | * @param product The CPE product name 82 | * @param version A version string to match against 83 | * 84 | * @return A newly allocated list of strings if found, otherwise NULL 85 | */ 86 | GList *cve_db_get_issues(CveDB *db, char *product, char *version); 87 | GList *cve_db_get_issues_frac_compare(CveDB *db, char *product, char *version); 88 | 89 | /** 90 | * Util to free a struct cve_entry_t 91 | */ 92 | static inline void cve_free(void *v) 93 | { 94 | if (!v) { 95 | return; 96 | } 97 | struct cve_entry_t *t = v; 98 | if (t->uris) { 99 | g_list_free_full(t->uris, xmlFree); 100 | } 101 | if (t->score) { 102 | g_free(t->score); 103 | } 104 | if (t->vector) { 105 | g_free(t->vector); 106 | } 107 | g_free(t->id); 108 | g_free(t->summary); 109 | g_free(t); 110 | } 111 | 112 | /* 113 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 114 | * 115 | * Local variables: 116 | * c-basic-offset: 8 117 | * tab-width: 8 118 | * indent-tabs-mode: nil 119 | * End: 120 | * 121 | * vi: set shiftwidth=8 tabstop=8 expandtab: 122 | * :indentSize=8:tabSize=8:noTabs=true: 123 | */ 124 | -------------------------------------------------------------------------------- /src/library/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common.h - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | struct source_package_t; 19 | 20 | /** 21 | * Resolution status that should be implemented by the bug plugin 22 | */ 23 | typedef enum { 24 | REPORT_STATUS_MIN, 25 | REPORT_STATUS_OPEN, /** 13 | #include 14 | #include 15 | 16 | #include "core.c" 17 | #include "cve-string.c" 18 | #include "hashmap.c" 19 | #include "util.c" 20 | #include "util.h" 21 | 22 | #include "config.h" 23 | 24 | START_TEST(cve_database_new) 25 | { 26 | CveDB *db = NULL; 27 | 28 | db = cve_db_new(":memory:"); 29 | fail_if(!db, "Failed to create CveDB"); 30 | cve_db_free(db); 31 | } 32 | END_TEST 33 | 34 | START_TEST(cve_database_load) 35 | { 36 | CveDB *db = NULL; 37 | 38 | db = cve_db_new(":memory:"); 39 | fail_if(!db, "Failed to create CveDB"); 40 | 41 | fail_if(!cve_db_load(db, TOP_DIR "/tests/dummy_data/nvdcve-2.0-2002.xml"), "Failed to load database"); 42 | 43 | cve_db_free(db); 44 | } 45 | END_TEST 46 | 47 | START_TEST(cve_database_fetch) 48 | { 49 | CveDB *db = NULL; 50 | GList *ret = NULL; 51 | __attribute__((unused)) gchar *cve = NULL; 52 | struct cve_entry_t *t = NULL; 53 | 54 | db = cve_db_new(":memory:"); 55 | fail_if(!db, "Failed to create CveDB"); 56 | 57 | fail_if(!cve_db_load(db, TOP_DIR "/tests/dummy_data/nvdcve-2.0-2002.xml"), "Failed to load database"); 58 | 59 | ret = cve_db_get_issues(db, "ssleay", "0.9"); 60 | fail_if(!ret, "Failed to get issues list for ssleay"); 61 | 62 | fail_if(g_list_length(ret) != 2, "Incorrect issue count for ssleay"); 63 | 64 | cve = ret->data; 65 | t = cve_db_get_cve(db, cve); 66 | fail_if(!t, "Failed to retrieve CVE entry"); 67 | fail_if(!g_str_equal(t->id, cve), "Mismatched CVE return"); 68 | cve_free(t); 69 | t = NULL; 70 | 71 | cve = ret->next->data; 72 | t = cve_db_get_cve(db, cve); 73 | fail_if(!t, "Failed to retrieve CVE entry"); 74 | fail_if(!g_str_equal(t->id, cve), "Mismatched CVE return"); 75 | cve_free(t); 76 | t = NULL; 77 | 78 | g_list_free_full(ret, g_free); 79 | 80 | cve_db_free(db); 81 | } 82 | END_TEST 83 | 84 | static Suite *core_suite(void) 85 | { 86 | Suite *s = NULL; 87 | TCase *tc = NULL; 88 | 89 | s = suite_create("cve_database"); 90 | tc = tcase_create("cve_dtabase_functions"); 91 | tcase_set_timeout(tc, 60); 92 | tcase_add_test(tc, cve_database_new); 93 | tcase_add_test(tc, cve_database_load); 94 | tcase_add_test(tc, cve_database_fetch); 95 | suite_add_tcase(s, tc); 96 | 97 | return s; 98 | } 99 | 100 | int main(void) 101 | { 102 | Suite *s; 103 | SRunner *sr; 104 | int fail; 105 | 106 | s = core_suite(); 107 | sr = srunner_create(s); 108 | srunner_run_all(sr, CK_VERBOSE); 109 | fail = srunner_ntests_failed(sr); 110 | srunner_free(sr); 111 | 112 | xmlCleanupParser(); 113 | 114 | if (fail > 0) { 115 | return EXIT_FAILURE; 116 | } 117 | 118 | return EXIT_SUCCESS; 119 | } 120 | 121 | /* 122 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 123 | * 124 | * Local variables: 125 | * c-basic-offset: 8 126 | * tab-width: 8 127 | * indent-tabs-mode: nil 128 | * End: 129 | * 130 | * vi: set shiftwidth=8 tabstop=8 expandtab: 131 | * :indentSize=8:tabSize=8:noTabs=true: 132 | */ 133 | -------------------------------------------------------------------------------- /src/library/template.h: -------------------------------------------------------------------------------- 1 | /* 2 | * template.h - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | 17 | #include "util.h" 18 | 19 | struct TemplateContext; 20 | 21 | typedef struct TemplateContext TemplateContext; 22 | 23 | /** 24 | * Short hand method to construct a TemplateContext from a GHashTable, 25 | * run all input through template_context_process_line, and return the 26 | * allocated string. 27 | * 28 | * @param original The string to perform replacements on 29 | * @param keys A string->string kv GHashTable 30 | * @return A newly allocated string with all replacements performed 31 | */ 32 | cve_string *template_string(const char *original, GHashTable *keys); 33 | 34 | /** 35 | * Replace all {{style}} variables in a string with the string values 36 | * in the given TemplateContext, and child contexts 37 | * This method only supports string->string right now, however in future 38 | * support for lists will added. 39 | * 40 | * @note Section rendering is done on a second pass once the section has 41 | * been correctly built. 42 | * 43 | * @param original The string to perform replacements on 44 | * @param keys An initialised TemplateContext 45 | * @param skip Skip section expansion/rendering, default should be false. 46 | * @return A newly allocated string with all replacements performed 47 | */ 48 | cve_string *template_context_process_line(TemplateContext *context, const char *original, bool skip); 49 | 50 | /** 51 | * Create a new TemplateContext 52 | */ 53 | TemplateContext *template_context_new(void); 54 | 55 | /** 56 | * Free any resources used by a TemplateContext 57 | * 58 | * @param context Valid TemplateContext instance 59 | */ 60 | void template_context_free(TemplateContext *context); 61 | 62 | /** 63 | * Add a key->value string pair to this context 64 | * 65 | * @param key Identifying string 66 | * @param val Value to pair with key 67 | * 68 | * return True if the operation succeeded 69 | */ 70 | bool template_context_add_string(TemplateContext *context, const char *key, char *val); 71 | 72 | /** 73 | * Add a key->value string pair to this context 74 | * 75 | * @param key Identifying string 76 | * @param val Boolean value 77 | * 78 | * return True if the operation succeeded 79 | */ 80 | bool template_context_add_bool(TemplateContext *context, const char *key, bool value); 81 | 82 | /** 83 | * Append to a named list 84 | * 85 | * @param key Identifying string 86 | * @param val Seeded TemplateContext 87 | * 88 | * return True if the operation succeeded 89 | */ 90 | bool template_context_add_list(TemplateContext *context, const char *key, TemplateContext *value); 91 | 92 | /** 93 | * Add a named subcontext to this TemplateContext, enabling conditional 94 | * sections. 95 | * 96 | * @param ctx Root TemplateContext instance 97 | * @param key Unique name for the section and subcontext 98 | * @param child Child to add as a new subcontext 99 | */ 100 | void template_context_add_subcontext(TemplateContext *context, const char *key, TemplateContext *child); 101 | 102 | DEF_AUTOFREE(TemplateContext, template_context_free) 103 | 104 | /* 105 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 106 | * 107 | * Local variables: 108 | * c-basic-offset: 8 109 | * tab-width: 8 110 | * indent-tabs-mode: nil 111 | * End: 112 | * 113 | * vi: set shiftwidth=8 tabstop=8 expandtab: 114 | * :indentSize=8:tabSize=8:noTabs=true: 115 | */ 116 | -------------------------------------------------------------------------------- /src/update-main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * update.c - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "cve-check-tool.h" 22 | 23 | #include "config.h" 24 | #include "core.h" 25 | #include "cve-db-lock.h" 26 | #include "cve-string.h" 27 | #include "util.h" 28 | 29 | #include "update.h" 30 | 31 | static void show_version(void) 32 | { 33 | const gchar *msg = 34 | "\ 35 | " PACKAGE " " PACKAGE_VERSION 36 | "\n\ 37 | Copyright (C) 2015 Intel Corporation\n\ 38 | " PACKAGE_NAME 39 | " is free software; you can redistribute it and/or modify\n\ 40 | it under the terms of the GNU General Public License as published by\n\ 41 | the Free Software Foundation; either version 2 of the License, or\n\ 42 | (at your option) any later version."; 43 | fprintf(stderr, "%s\n", msg); 44 | } 45 | 46 | static gchar *nvds = NULL; 47 | static bool _show_version = false; 48 | static bool _quiet = false; 49 | static const char *_cacert_file = NULL; 50 | 51 | static GOptionEntry _entries[] = 52 | { { "nvd-dir", 'd', 0, G_OPTION_ARG_STRING, &nvds, "NVD directory in filesystem", NULL }, 53 | { "version", 'v', 0, G_OPTION_ARG_NONE, &_show_version, "Show version", NULL }, 54 | { "quiet", 'q', 0, G_OPTION_ARG_NONE, &_quiet, "Run silently", NULL }, 55 | { "cacert", 56 | 'C', 57 | 0, 58 | G_OPTION_ARG_STRING, 59 | &_cacert_file, 60 | "Path to the combined SSL certificates file (system default is used if not set)", 61 | NULL }, 62 | {.short_name = 0 } }; 63 | 64 | /** 65 | * Main entry. 66 | */ 67 | int main(int argc, char **argv) 68 | { 69 | autofree(GError) *error = NULL; 70 | autofree(GOptionContext) *context = NULL; 71 | autofree(cve_string) *db_path = NULL; 72 | int ret = EXIT_FAILURE; 73 | bool db_locked; 74 | 75 | LIBXML_TEST_VERSION 76 | context = g_option_context_new(" - cve update"); 77 | g_option_context_add_main_entries(context, _entries, NULL); 78 | if (!g_option_context_parse(context, &argc, &argv, &error)) { 79 | g_printerr("Invalid options: %s\n", error->message); 80 | goto end; 81 | } 82 | 83 | if (_show_version) { 84 | show_version(); 85 | ret = EXIT_SUCCESS; 86 | goto end; 87 | } 88 | 89 | db_path = get_db_path(nvds); 90 | if (!db_path) { 91 | fprintf(stderr, "main(): Can't get db path\n"); 92 | goto end; 93 | } 94 | 95 | db_locked = cve_db_lock_init(db_path->str); 96 | if (!db_locked) { 97 | fputs("Not continuing without a database lock\n", stderr); 98 | goto end; 99 | } 100 | 101 | if (update_db(_quiet, db_path->str, _cacert_file)) { 102 | ret = EXIT_SUCCESS; 103 | } else { 104 | fprintf(stderr, "Failed to update database\n"); 105 | } 106 | 107 | cve_db_lock_fini(); 108 | end: 109 | xmlCleanupParser(); 110 | return ret; 111 | } 112 | 113 | /* 114 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 115 | * 116 | * Local variables: 117 | * c-basic-offset: 8 118 | * tab-width: 8 119 | * indent-tabs-mode: nil 120 | * End: 121 | * 122 | * vi: set shiftwidth=8 tabstop=8 expandtab: 123 | * :indentSize=8:tabSize=8:noTabs=true: 124 | */ 125 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([cve-check-tool], 5.6.4, [michael.i.doherty@intel.com], [cve-check-tool], [https://github.com/ikeydoherty/cve-check-tool]) 2 | AM_INIT_AUTOMAKE([-Wno-portability no-dist-gzip dist-xz foreign subdir-objects]) 3 | AC_PROG_CC 4 | AC_PROG_CC_STDC 5 | LT_PREREQ(2.2) 6 | AC_CONFIG_HEADERS([config.h]) 7 | AC_PREFIX_DEFAULT(/usr/local) 8 | AM_SILENT_RULES([yes]) 9 | LT_INIT([disable-static]) 10 | AC_CONFIG_MACRO_DIR([m4]) 11 | AX_VALGRIND_CHECK 12 | 13 | m4_define([glib_required_version], [2.36.0]) 14 | m4_define([libxml2_required_version], [2.9.1]) 15 | m4_define([curl_required_version], [7.29.0]) 16 | m4_define([gobject_required_version], [2.0]) 17 | m4_define([check_required_version], [0.9]) 18 | m4_define([json_required_version], [0.16.0]) 19 | m4_define([openssl_required_version],[1.0.0]) 20 | # TODO: Set minimum sqlite 21 | 22 | AC_CHECK_FUNCS_ONCE(malloc_trim) 23 | 24 | PKG_CHECK_MODULES(CVE_CHECK_TOOL, 25 | [ 26 | glib-2.0 >= glib_required_version, 27 | gio-2.0 >= glib_required_version, 28 | libxml-2.0 >= libxml2_required_version, 29 | libcurl >= curl_required_version, 30 | gobject-2.0 >= gobject_required_version, 31 | sqlite3, 32 | openssl >= openssl_required_version 33 | ]) 34 | 35 | PKG_CHECK_MODULES(MODULE_COMMON, 36 | [ 37 | libxml-2.0 >= libxml2_required_version, 38 | glib-2.0 >= glib_required_version 39 | ]) 40 | 41 | PKG_CHECK_MODULES(CHECK, 42 | check >= check_required_version, 43 | [have_check=yes], [have_check=no] 44 | ) 45 | 46 | AM_CONDITIONAL(ENABLE_UNIT_TESTS, test x$have_check = xyes) 47 | 48 | have_coverage=no 49 | AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable test coverage])) 50 | if test "x$enable_coverage" = "xyes" ; then 51 | AC_CHECK_PROG(lcov_found, [lcov], [yes], [no]) 52 | if test "x$lcov_found" = xno ; then 53 | AC_MSG_ERROR([*** lcov support requested but the program was not found]) 54 | else 55 | lcov_version_major="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 1`" 56 | lcov_version_minor="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 2`" 57 | if test "$lcov_version_major" -eq 1 -a "$lcov_version_minor" -lt 10; then 58 | AC_MSG_ERROR([*** lcov version is too old. 1.10 required]) 59 | else 60 | have_coverage=yes 61 | AC_DEFINE([COVERAGE], [1], [Coverage enabled]) 62 | fi 63 | fi 64 | fi 65 | AM_CONDITIONAL([COVERAGE], [test "$have_coverage" = "yes"]) 66 | 67 | AC_ARG_ENABLE(run_in_tree, AS_HELP_STRING([--enable-run-in-tree], [enable running from git tree])) 68 | if test "x$enable_run_in_tree" = "xyes" ; then 69 | AC_DEFINE([INTREE], [1], [In-tree usage enabled]) 70 | else 71 | enable_run_in_tree="no" 72 | fi 73 | 74 | AM_CONDITIONAL([INTREE], [test "$enable_run_in_tree" = "yes"]) 75 | 76 | 77 | AC_ARG_ENABLE(relative_plugins, AS_HELP_STRING([--enable-relative-plugins], [enable relative plugin location lookup])) 78 | if test "x$enable_relative_plugins" = "xyes" ; then 79 | AC_DEFINE([RELATIVE_PLUGINS], [1], [Relative plugin location lookup]) 80 | else 81 | enable_relative_plugins="no" 82 | fi 83 | 84 | AM_CONDITIONAL([RELATIVE_PLUGINS], [test "$enable_relative_plugins" = "yes"]) 85 | 86 | AC_CONFIG_FILES([Makefile]) 87 | 88 | AC_OUTPUT 89 | 90 | AC_MSG_RESULT([ 91 | cve-check-tool $VERSION 92 | ======== 93 | 94 | prefix: ${prefix} 95 | libdir: ${libdir} 96 | sysconfdir: ${sysconfdir} 97 | exec_prefix: ${exec_prefix} 98 | bindir: ${bindir} 99 | datarootdir: ${datarootdir} 100 | 101 | coverage: ${have_coverage} 102 | unit-tests: ${have_check} 103 | run-in-tree: ${enable_run_in_tree} 104 | 105 | compiler: ${CC} 106 | cflags: ${CFLAGS} 107 | ldflags: ${LDFLAGS} 108 | 109 | Relative plugins: ${enable_relative_plugins} 110 | ]) 111 | -------------------------------------------------------------------------------- /data/packages.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{affected_string}} 4 | 116 | 117 | 118 |

{{affected_string}}

119 |
120 | 121 |
122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | {{#bugs_enabled}} 132 | {{/bugs_enabled}} 133 | 134 | 135 | 136 | {{#packages}} 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | {{#bugs_enabled}} 146 | 147 | {{/bugs_enabled}} 148 | 149 | {{/packages}} 150 | 151 |
CVEPackageVersionDescriptionCVSS (Score)Access VectorStatusReport Status
{{cve}}{{package_name}}{{package_version}}{{desc}}{{score}}{{vector}}{{status_string}}{{report_status}}
152 |
153 | 154 | 155 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | Hacking cve-check-tool 2 | ---------------------- 3 | 4 | Indentation is strictly 8 spaces (set tab stops to 8 spaces) - No tabs 5 | No spaces between function name and parentheses. Only use space after 6 | "if (", etc. 7 | 8 | Curly braces must be on the same line (i.e. expressions) unless it 9 | is the function declaration: 10 | 11 | Acceptable: 12 | int main(int argc, char **argv) 13 | { 14 | if (someThingOrOther) { 15 | // Do something 16 | } 17 | return 0; 18 | } 19 | 20 | Unacceptable: 21 | int main(int argc, char **argv) { 22 | 23 | if(someThingOrOther) 24 | { 25 | // Do something 26 | } 27 | return 0; 28 | } 29 | 30 | When appropriate remember to declare your function first! It helps when 31 | looking back through the file. 32 | 33 | Use consistent pointers! "*" should prefix your variable name. Also ensure 34 | your pointers (where appropriate) are not left uninitialized, resulting 35 | in broken g_free() calls. 36 | 37 | Acceptable: 38 | char *something = NULL; 39 | doFunc(&something); 40 | 41 | Unacceptable: 42 | char* something; 43 | doFunc(&someThing); 44 | 45 | Minimise your use of "goto"'s, and test every code path is reached. Also 46 | ensure *every* if/else, etc, even if single line, is wrapped in curly braces. 47 | 48 | Memory management: 49 | ------------------ 50 | cve-check-tool prefers a scope-based approach to memory management, 51 | employing a RAII-like system for allocations. Where possible, avoid 52 | explicit free's and reuse of unrelated variables. 53 | 54 | util.h defines a number of autofree()-ready types. These are implemented 55 | using __attribute__ ((cleanup(x))), available in GCC and Clang. Thus, 56 | MSVC (and potentially other compilers) are not supported. 57 | 58 | An autofree variable is declared using the autofree() macro, which is 59 | primarily provided for syntatical sugar. Here is an example of a 60 | variable that is automatically reaped/freed when it goes out of scope: 61 | 62 | autofree(GError) *error = NULL; 63 | 64 | Remember that these *are* scope sensitive, so the following would result 65 | in undefined behaviour: 66 | 67 | gchar *somestr = NULL; 68 | { 69 | autofree(gchar) *str = g_strdup_printf("Scoped string\n"); 70 | somestr = str; 71 | } 72 | printf("%s: %d\n", somestr, strlen(somestr)); 73 | 74 | At this point, 'str' has been freed, and somestr still points to the 75 | memory that has now been freed. 76 | 77 | As a rule of thumb, if you find yourself in an instance where you have 78 | used an explicit free/unref in a fashion that could be automated, you 79 | should define the cleanup function in util.h (see DEF_AUTOFREE) 80 | 81 | C99 vs GLibisms 82 | --------------- 83 | cve-check-tool is implemented using C99 (not GNU C99) and as such must 84 | build with either GCC or Clang using C99 mode (-std=c99). We use the 85 | stdbool type "bool", and public methods should use these in place of 86 | GLib's "gboolean" (which is an integer) 87 | 88 | Unless absolutely required, prefer non GIO/GLib methods, as they are 89 | known to cause leaks, and introduce non-portable changes for a high 90 | cost. An example of this is gio leaking both memory and file descriptors 91 | on exit. 92 | 93 | Currently there is an ongoing effort to strip all of GIO/GLib usage from 94 | cve-check-tool, so when writing new code please avoid using these libraries. 95 | 96 | String Handling 97 | --------------- 98 | Internally some code paths may still make use of g_strdup, strdup, asprintf, 99 | etc. Note that the use of the 'strlen' function is not permitted within 100 | cve-check-tool. If you require access to a string length, then please use 101 | the safe cve_string functions: 102 | 103 | /* duplicate a string and compare it*/ 104 | cve_string *str = cve_string_dup("some text"); 105 | if (cve_string_const_equal(str, "some text")) { 106 | printf("Equal\n"); 107 | } 108 | cve_string_free(str); 109 | 110 | /* printf style dup */ 111 | cve_string *str = cve_string_dup_printf("X is: %d", x); 112 | 113 | /* Access internal string and length */ 114 | printf("String (len: %d): %s\n", str->len, str->str); 115 | 116 | Also note scope based autofree helpers exist for cve_string, so you should 117 | only rarely need to manually free a cve_string: 118 | 119 | { 120 | autofree(cve_string) *str = NULL; 121 | str = cve_string_dup("some text"); 122 | } /* Out of scope, now freed */ 123 | 124 | Pull Requests/commiting: 125 | ------------------------ 126 | Commits should clearly define the purpose in less than 80 columns in 127 | the first line. Futher expansion, if needed, should be provided in a 128 | following paragraph, separated by one blank line. 129 | -------------------------------------------------------------------------------- /src/library/cve-db-lock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cve-check-tool.c 3 | * 4 | * Copyright (C) 2015-2016 Sergey Popovich . 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "core.h" 27 | #include "cve-db-lock.h" 28 | #include "cve-string.h" 29 | #include "util.h" 30 | 31 | static const short int locktype2l_type[LT_MAX + 1] = { 32 | [LT_READ] = F_RDLCK, [LT_WRITE] = F_WRLCK, 33 | }; 34 | 35 | static const char locktype2string[LT_MAX + 1][sizeof("write")] = { 36 | [LT_READ] = "read", [LT_WRITE] = "write", 37 | }; 38 | 39 | static int db_lock_fd = -1; 40 | static cve_string *db_lock_fname; 41 | 42 | #ifndef O_NOFOLLOW 43 | #define O_NOFOLLOW 0 44 | #endif 45 | 46 | bool cve_db_lock_init(const char *db_path) 47 | { 48 | const int flags = O_RDWR | O_CREAT | O_NONBLOCK | O_NOFOLLOW; 49 | const mode_t mode = S_IRUSR | S_IWUSR; 50 | 51 | assert(db_lock_fd < 0); 52 | assert(db_lock_fname == NULL); 53 | assert(db_path != NULL); 54 | 55 | db_lock_fname = make_db_dot_fname(db_path, "cve.lock"); 56 | if (!db_lock_fname) { 57 | return false; 58 | } 59 | 60 | db_lock_fd = open(db_lock_fname->str, flags, mode); 61 | if (db_lock_fd < 0) { 62 | cve_string_free(db_lock_fname); 63 | db_lock_fname = NULL; 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | 70 | void cve_db_lock_fini(void) 71 | { 72 | assert(db_lock_fd >= 0); 73 | assert(db_lock_fname != NULL); 74 | 75 | close(db_lock_fd); 76 | db_lock_fd = -1; 77 | 78 | unlink(db_lock_fname->str); 79 | cve_string_free(db_lock_fname); 80 | db_lock_fname = NULL; 81 | } 82 | 83 | bool cve_db_lock(locktype lt, int wait) 84 | { 85 | const char *lt_str; 86 | unsigned int waited; 87 | 88 | assert(lt < LT_MAX + 1); 89 | assert(db_lock_fd >= 0); 90 | 91 | lt_str = locktype2string[lt]; 92 | 93 | if (wait < 0) { 94 | waited = wait = 2; 95 | } else { 96 | waited = 0; 97 | } 98 | 99 | do { 100 | struct flock fl = { 101 | .l_type = locktype2l_type[lt], .l_whence = SEEK_SET, 102 | }; 103 | int ret; 104 | 105 | ret = fcntl(db_lock_fd, F_SETLK, &fl); 106 | if (!ret) { 107 | return true; 108 | } 109 | if (errno != EAGAIN && errno != EACCES) { 110 | fprintf(stderr, "Error acquiring database lock: %s\n", strerror(errno)); 111 | break; 112 | } 113 | 114 | if (waited % 2) { 115 | goto sleep; 116 | } 117 | fputs("Another app holds the lock on database", stderr); 118 | if (wait) { 119 | int remaining = wait - waited; 120 | if (remaining <= 0) { 121 | fprintf(stderr, "; %s lock is not acquired\n", lt_str); 122 | break; 123 | } 124 | fprintf(stderr, "; acquiring %s lock within %ds ...", lt_str, remaining); 125 | } else { 126 | fputs("; waiting indefinitely", stderr); 127 | } 128 | fputc('\n', stderr); 129 | sleep: 130 | sleep(1); 131 | waited++; 132 | if (wait && waited >= (unsigned int)wait) { 133 | /* last round: make it even */ 134 | waited = (wait + 1) & ~1; 135 | } 136 | } while (1); 137 | 138 | return false; 139 | } 140 | 141 | void cve_db_unlock(void) 142 | { 143 | struct flock fl = { 144 | .l_type = F_UNLCK, .l_whence = SEEK_SET, 145 | }; 146 | int ret; 147 | 148 | ret = fcntl(db_lock_fd, F_SETLK, &fl); 149 | 150 | assert(ret == 0); 151 | } 152 | 153 | /* 154 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 155 | * 156 | * Local variables: 157 | * c-basic-offset: 8 158 | * tab-width: 8 159 | * indent-tabs-mode: nil 160 | * End: 161 | * 162 | * vi: set shiftwidth=8 tabstop=8 expandtab: 163 | * :indentSize=8:tabSize=8:noTabs=true: 164 | */ 165 | -------------------------------------------------------------------------------- /src/plugins/packaging/pkgbuild/pkgbuild.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pkgbuild.c - Arch Linux specific 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "pkgbuild.h" 20 | #include "plugin.h" 21 | #include "util.h" 22 | 23 | struct source_package_t *pkgbuild_inspect_spec(const char *filename) 24 | { 25 | struct source_package_t *t = NULL; 26 | autofree(GFile) *fi = g_file_new_for_path(filename); 27 | autofree(GError) *error = NULL; 28 | if (!fi) { 29 | return NULL; 30 | } 31 | autofree(GFileInputStream) *fis = g_file_read(fi, NULL, &error); 32 | if (error) { 33 | g_printerr("Unable to read: %s\n", error->message); 34 | return NULL; 35 | } 36 | 37 | autofree(GDataInputStream) *dis = g_data_input_stream_new(G_INPUT_STREAM(fis)); 38 | char *read = NULL; 39 | char *fpath = NULL; 40 | autofree(gchar) *name = NULL; 41 | autofree(gchar) *version = NULL; 42 | autofree(gchar) *release = NULL; 43 | 44 | while ((read = g_data_input_stream_read_line(dis, NULL, NULL, NULL)) != NULL) { 45 | autofree(gstrv) *strv = NULL; 46 | const gchar *key = NULL; 47 | autofree(gchar) *value = NULL; 48 | 49 | read = g_strstrip(read); 50 | 51 | if (!strchr(read, '=')) { 52 | goto clean; 53 | } 54 | 55 | strv = g_strsplit(read, "=", -1); 56 | if (g_strv_length(strv) < 2) { 57 | goto clean; 58 | } 59 | key = g_strstrip(strv[0]); 60 | value = g_strjoinv("=", strv + 1); 61 | value = g_strstrip(value); 62 | 63 | if (g_str_equal(key, "pkgname")) { 64 | name = g_strdup(value); 65 | } else if (g_str_equal(key, "pkgver")) { 66 | version = g_strdup(value); 67 | } else if (g_str_equal(key, "pkgrel")) { 68 | release = g_strdup(value); 69 | } 70 | 71 | if (name && version && release) { 72 | g_free(read); 73 | break; 74 | } 75 | clean: 76 | g_free(read); 77 | } 78 | 79 | if (!name || !version || !release) { 80 | return NULL; 81 | } 82 | 83 | fpath = cve_get_file_parent(filename); 84 | if (!fpath) { 85 | return NULL; 86 | } 87 | 88 | t = calloc(1, sizeof(struct source_package_t)); 89 | if (!t) { 90 | free(fpath); 91 | return NULL; 92 | } 93 | t->name = g_strdup(name); 94 | t->version = g_strdup(version); 95 | t->release = atoi(release); 96 | t->path = fpath; 97 | t->type = PACKAGE_TYPE_PKGBUILD; 98 | return t; 99 | } 100 | 101 | bool pkgbuild_is_patched(struct source_package_t *pkg, char *id) 102 | { 103 | /* Determine if its patched. */ 104 | autofree(gchar) *pnamet = g_ascii_strdown((gchar *)id, -1); 105 | autofree(gchar) *pnamel = g_strdup_printf("%s/%s.patch", pkg->path, pnamet); 106 | autofree(gchar) *pname = g_strdup_printf("%s/%s.patch", pkg->path, id); 107 | 108 | if (g_file_test(pname, G_FILE_TEST_EXISTS) || g_file_test(pnamel, G_FILE_TEST_EXISTS)) { 109 | return true; 110 | } 111 | 112 | return false; 113 | } 114 | 115 | bool pkgbuild_is_package(const char *filename) 116 | { 117 | return g_str_has_suffix((const gchar *)filename, "PKGBUILD"); 118 | } 119 | 120 | void pkgbuild_locate_sources(const char *directory, bool recurse, cve_add_callback cb) 121 | { 122 | find_sources(directory, &pkgbuild_is_package, recurse, cb); 123 | } 124 | 125 | _module_export_ bool cve_plugin_module_init(CvePlugin *self) 126 | { 127 | self->flags = PLUGIN_TYPE_PACKAGE; 128 | self->name = "pkgbuild"; 129 | self->is_ignored = NULL; 130 | self->is_patched = pkgbuild_is_patched; 131 | self->is_package = pkgbuild_is_package; 132 | self->scan_package = pkgbuild_inspect_spec; 133 | return true; 134 | } 135 | 136 | /* 137 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 138 | * 139 | * Local variables: 140 | * c-basic-offset: 8 141 | * tab-width: 8 142 | * indent-tabs-mode: nil 143 | * End: 144 | * 145 | * vi: set shiftwidth=8 tabstop=8 expandtab: 146 | * :indentSize=8:tabSize=8:noTabs=true: 147 | */ 148 | -------------------------------------------------------------------------------- /src/library/fetch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * fetch.c - cve-check-tool helpers 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "fetch.h" 22 | #include "util.h" 23 | 24 | struct fetch_t { 25 | FILE *f; 26 | const char *target; 27 | }; 28 | 29 | static size_t write_func(void *ptr, size_t size, size_t nmemb, struct fetch_t *f) 30 | { 31 | if (!f->f) { 32 | f->f = fopen(f->target, "wb"); 33 | } 34 | if (!f->f) { 35 | return -1; 36 | } 37 | return fwrite(ptr, size, nmemb, f->f); 38 | } 39 | 40 | FetchStatus fetch_uri(const char *uri, const char *target, bool verbose, const char *cacert_file) 41 | { 42 | FetchStatus ret = FETCH_STATUS_FAIL; 43 | CURLcode res; 44 | struct stat st; 45 | CURL *curl = NULL; 46 | struct fetch_t *f = NULL; 47 | 48 | curl = curl_easy_init(); 49 | if (!curl) { 50 | return ret; 51 | } 52 | 53 | if (cacert_file) { 54 | res = curl_easy_setopt(curl, CURLOPT_CAINFO, cacert_file); 55 | if (res != CURLE_OK) { 56 | goto bail; 57 | } 58 | } 59 | 60 | if (stat(target, &st) == 0) { 61 | res = curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); 62 | if (res != CURLE_OK) { 63 | goto bail; 64 | } 65 | res = curl_easy_setopt(curl, CURLOPT_TIMEVALUE, st.st_mtime); 66 | if (res != CURLE_OK) { 67 | goto bail; 68 | } 69 | } 70 | 71 | res = curl_easy_setopt(curl, CURLOPT_URL, uri); 72 | if (res != CURLE_OK) { 73 | goto bail; 74 | } 75 | if (verbose) { 76 | (void)curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); 77 | } 78 | res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)write_func); 79 | if (res != CURLE_OK) { 80 | goto bail; 81 | } 82 | f = calloc(1, sizeof(struct fetch_t)); 83 | if (!f) { 84 | goto bail; 85 | } 86 | f->target = target; 87 | res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, f); 88 | if (res != CURLE_OK) { 89 | goto bail; 90 | } 91 | 92 | /* Handle future redirects from NVD */ 93 | res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 94 | if (res != CURLE_OK) { 95 | goto bail; 96 | } 97 | res = curl_easy_perform(curl); 98 | if (res != CURLE_OK) { 99 | goto bail; 100 | } 101 | ret = f->f ? FETCH_STATUS_UPDATE : FETCH_STATUS_OK; 102 | 103 | bail: 104 | 105 | if (f) { 106 | if (f->f) { 107 | fclose(f->f); 108 | } 109 | free(f); 110 | } 111 | 112 | if (curl) { 113 | curl_easy_cleanup(curl); 114 | } 115 | 116 | return ret; 117 | } 118 | 119 | bool gunzip_file(const char *path) 120 | { 121 | GFile *in = NULL, *out = NULL; 122 | autofree(gchar) *newpath = NULL; 123 | autofree(GFileInputStream) *fis = NULL; 124 | autofree(GFileOutputStream) *fos = NULL; 125 | autofree(GOutputStream) *cos = NULL; 126 | autofree(GZlibDecompressor) *conv = NULL; 127 | gsize ret; 128 | 129 | newpath = g_strdup(path); 130 | 131 | if (g_str_has_suffix(newpath, ".gz")) { 132 | newpath = str_replace(newpath, ".gz", ""); 133 | } 134 | 135 | in = g_file_new_for_path(path); 136 | out = g_file_new_for_path(newpath); 137 | 138 | fis = g_file_read(in, NULL, NULL); 139 | if (!fis) { 140 | return NULL; 141 | } 142 | fos = g_file_replace(out, NULL, FALSE, 0, NULL, NULL); 143 | if (!fos) { 144 | return NULL; 145 | } 146 | 147 | conv = g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP); 148 | cos = g_converter_output_stream_new(G_OUTPUT_STREAM(fos), G_CONVERTER(conv)); 149 | if (!cos) { 150 | return NULL; 151 | } 152 | ret = g_output_stream_splice(cos, G_INPUT_STREAM(fis), G_OUTPUT_STREAM_SPLICE_NONE, NULL, NULL); 153 | return (ret > 0 ? true : false); 154 | } 155 | 156 | /* 157 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 158 | * 159 | * Local variables: 160 | * c-basic-offset: 8 161 | * tab-width: 8 162 | * indent-tabs-mode: nil 163 | * End: 164 | * 165 | * vi: set shiftwidth=8 tabstop=8 expandtab: 166 | * :indentSize=8:tabSize=8:noTabs=true: 167 | */ 168 | -------------------------------------------------------------------------------- /src/plugins/output/cli/cli.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cli.c - cve-check-tool helpers 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | 14 | #include 15 | #include 16 | 17 | #include "cli.h" 18 | #include "config.h" 19 | #include "cve-check-tool.h" 20 | #include "plugin.h" 21 | #include "util.h" 22 | 23 | static bool cli_write_report(CveCheckTool *self) 24 | { 25 | GHashTableIter iter; 26 | gchar *key = NULL; 27 | struct source_package_t *v = NULL; 28 | GList *c = NULL, *t = NULL; 29 | struct cve_entry_t *entry = NULL; 30 | FILE *fd = NULL; 31 | bool ret = false; 32 | 33 | if (self->output_file) { 34 | fd = fopen(self->output_file, "w"); 35 | if (!fd) { 36 | fprintf(stderr, "Unable to open %s for writing: %s\n", self->output_file, strerror(errno)); 37 | return false; 38 | } 39 | } else { 40 | fd = stdout; 41 | } 42 | 43 | g_hash_table_iter_init(&iter, self->db); 44 | while (g_hash_table_iter_next(&iter, (void **)&key, (void **)&v)) { 45 | if (!v->issues && !v->patched && !self->show_unaffected) { 46 | continue; 47 | } 48 | if (!v->issues && self->hide_patched) { 49 | continue; 50 | } 51 | if (fprintf(fd, 52 | "%s %s (%u patched, %u issues)\n" C_WHITE "------------" C_RESET "\n", 53 | key, 54 | (char *)v->version, 55 | g_list_length(v->patched), 56 | g_list_length(v->issues)) < 0) { 57 | goto io_error; 58 | } 59 | for (c = v->issues; c; c = c->next) { 60 | entry = cve_db_get_cve(self->cve_db, (gchar *)c->data); 61 | if (self->modified > 0 && entry->modified > self->modified) { 62 | cve_free(entry); 63 | continue; 64 | } 65 | if (fprintf(fd, " * " C_RED "%s" C_RESET " : %s\n\n", (char *)c->data, entry->summary) < 0) { 66 | goto io_error; 67 | } 68 | /* Print links.. */ 69 | bool p = false; 70 | for (t = entry->uris; t; t = t->next) { 71 | if (fprintf(fd, " - %s\n", (char *)t->data) < 0) { 72 | goto io_error; 73 | } 74 | p = true; 75 | } 76 | if (p) { 77 | if (fprintf(fd, "\n") < 0) { 78 | goto io_error; 79 | } 80 | } 81 | cve_free(entry); 82 | } 83 | if (!self->hide_patched) { 84 | for (c = v->patched; c; c = c->next) { 85 | entry = cve_db_get_cve(self->cve_db, (gchar *)c->data); 86 | if (self->modified > 0 && entry->modified > self->modified) { 87 | cve_free(entry); 88 | continue; 89 | } 90 | if (fprintf(fd, 91 | " * " C_BLUE "%s [PATCHED]" C_RESET " : %s\n\n", 92 | (char *)c->data, 93 | entry->summary) < 0) { 94 | goto io_error; 95 | } 96 | cve_free(entry); 97 | } 98 | } 99 | fputc('\n', fd); 100 | ret = true; 101 | goto success; 102 | } 103 | /* Nothing to write */ 104 | ret = true; 105 | goto success; 106 | 107 | io_error: 108 | fprintf(stderr, "Error writing to file: %s\n", strerror(errno)); 109 | success: 110 | if (fd != stdout && self->output_file) { 111 | fclose(fd); 112 | } 113 | 114 | return ret; 115 | } 116 | 117 | _module_export_ bool cve_plugin_module_init(CvePlugin *self) 118 | { 119 | self->report = cli_write_report; 120 | self->flags = PLUGIN_TYPE_REPORT; 121 | self->name = "cli"; 122 | return true; 123 | } 124 | 125 | /* 126 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 127 | * 128 | * Local variables: 129 | * c-basic-offset: 8 130 | * tab-width: 8 131 | * indent-tabs-mode: nil 132 | * End: 133 | * 134 | * vi: set shiftwidth=8 tabstop=8 expandtab: 135 | * :indentSize=8:tabSize=8:noTabs=true: 136 | */ 137 | -------------------------------------------------------------------------------- /src/plugins/packaging/rpm/srpm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * rpm.c - Generic RPM support 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "cve-check-tool.h" 20 | #include "plugin.h" 21 | #include "rpm.h" 22 | #include "rpm_common.c" 23 | #include "util.h" 24 | 25 | static bool srpm_patch_check(struct source_package_t *t, char *id, bool ignore) 26 | { 27 | if (!t->extra) { 28 | return false; 29 | } 30 | 31 | autofree(gchar) *pnamet = g_ascii_strdown((gchar *)id, -1); 32 | autofree(gchar) *pname = g_strdup_printf(ignore ? "%s.nopatch" : "%s.patch", pnamet); 33 | gchar **list = t->extra; 34 | 35 | for (uint i = 0; i < g_strv_length(list); i++) { 36 | autofree(gchar) *comp = g_strchomp(g_ascii_strdown((gchar *)list[i], -1)); 37 | if (g_str_equal(comp, "")) { 38 | continue; 39 | } 40 | if (g_str_equal(pname, comp)) { 41 | return true; 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | 48 | void srpm_extra_free(struct source_package_t *pkg) 49 | { 50 | if (pkg && pkg->extra) { 51 | g_strfreev(pkg->extra); 52 | pkg->extra = NULL; 53 | } 54 | } 55 | 56 | struct source_package_t *srpm_examine(const char *filename) 57 | { 58 | struct source_package_t *t = NULL; 59 | autofree(gchar) *cmdline = NULL; 60 | autofree(GError) *error = NULL; 61 | autofree(gstrv) *splits = NULL; 62 | int exit = 0; 63 | autofree(gchar) *output = NULL; 64 | int len = 0; 65 | 66 | t = calloc(1, sizeof(struct source_package_t)); 67 | if (!t) { 68 | return NULL; 69 | } 70 | 71 | if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { 72 | fprintf(stderr, "Required source rpm not present: %s\n", filename); 73 | free(t); 74 | return NULL; 75 | } 76 | cmdline = g_strdup_printf("rpm -qp --queryformat \'%%{NAME}\t%%{VERSION}\t[%%{PATCH}\t]\n\' %s", filename); 77 | if (!cmdline) { 78 | free(t); 79 | return NULL; 80 | } 81 | if (!g_spawn_command_line_sync(cmdline, &output, NULL, &exit, &error)) { 82 | g_printerr("Unable to run command: %s\n", error->message); 83 | free(t); 84 | return NULL; 85 | } 86 | if (exit != 0) { 87 | fprintf(stderr, "Abnormal exit code for package %s: %s\n", filename, output); 88 | } 89 | splits = g_strsplit(output, "\t", -1); 90 | if ((len = g_strv_length(splits)) < 2) { 91 | fprintf(stderr, "Invalid output from rpm\n"); 92 | return NULL; 93 | } 94 | 95 | t->name = g_strdup(splits[0]); 96 | t->version = g_strdup(splits[1]); 97 | t->type = PACKAGE_TYPE_SRPM; 98 | if (len > 2) { 99 | /* TODO: De-glibificate */ 100 | GPtrArray *arr = g_ptr_array_new_with_free_func(NULL); 101 | for (int i = 0; i < len - 2; i++) { 102 | g_ptr_array_add(arr, g_strdup(splits[i + 2])); 103 | } 104 | g_ptr_array_add(arr, NULL); 105 | t->extra = (gchar **)arr->pdata; 106 | g_ptr_array_free(arr, FALSE); 107 | } 108 | 109 | return t; 110 | } 111 | 112 | struct source_package_t *srpm_scan_archive(const char *dir, const char *name, const char *version, const char *release) 113 | { 114 | autofree(gchar) *path = NULL; 115 | 116 | path = g_strdup_printf("%s%s%s-%s-%s.src.rpm", dir, G_DIR_SEPARATOR_S, name, version, release); 117 | 118 | if (!path) { 119 | return NULL; 120 | } 121 | 122 | return srpm_examine(path); 123 | } 124 | 125 | bool srpm_is_patched(struct source_package_t *t, char *id) 126 | { 127 | return srpm_patch_check(t, id, false); 128 | } 129 | 130 | bool srpm_is_ignored(struct source_package_t *t, char *id) 131 | { 132 | return srpm_patch_check(t, id, true); 133 | } 134 | 135 | bool srpm_is_package(const char *filename) 136 | { 137 | return g_str_has_suffix((const gchar *)filename, ".src.rpm"); 138 | } 139 | 140 | _module_export_ bool cve_plugin_module_init(CvePlugin *self) 141 | { 142 | self->flags = PLUGIN_TYPE_PACKAGE; 143 | self->name = "srpm"; 144 | self->is_ignored = srpm_is_ignored; 145 | self->is_patched = srpm_is_patched; 146 | self->is_package = srpm_is_package; 147 | self->scan_archive = srpm_scan_archive; 148 | self->scan_package = srpm_examine; 149 | self->free_package = srpm_extra_free; 150 | return true; 151 | } 152 | 153 | /* 154 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 155 | * 156 | * Local variables: 157 | * c-basic-offset: 8 158 | * tab-width: 8 159 | * indent-tabs-mode: nil 160 | * End: 161 | * 162 | * vi: set shiftwidth=8 tabstop=8 expandtab: 163 | * :indentSize=8:tabSize=8:noTabs=true: 164 | */ 165 | -------------------------------------------------------------------------------- /tests/check-core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cve-check-tool 3 | * Copyright (C) 2015 Intel Corporation 4 | * 5 | * cve-check-tool is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | */ 10 | 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | 16 | #include "cve-string.c" 17 | #include "hashmap.c" 18 | #include "util.c" 19 | #include "util.h" 20 | 21 | #include "config.h" 22 | 23 | const char *nvd_file = "nvd.db"; 24 | 25 | /** 26 | * Ensure parse_xml_date works 27 | */ 28 | START_TEST(cve_date_function) 29 | { 30 | const char *date = "2015-03-10T08:24:10.220-05:00"; 31 | int64_t t1, t2; 32 | 33 | t1 = parse_xml_date(date); 34 | fail_if(t1 < 0, "Failed to parse XML date"); 35 | 36 | date = "2015-03-10T08:34:10.220-05:00"; 37 | t2 = parse_xml_date(date); 38 | fail_if(t2 < 0, "Failed to parse second XML date"); 39 | 40 | fail_if(t1 > t2, "XML Date 1 should be less than Date 2"); 41 | } 42 | END_TEST 43 | 44 | /** 45 | * Ensure scope based management is functional 46 | */ 47 | static bool reclaimed = false; 48 | typedef char memtestchar; 49 | 50 | static void reaper(void *v) 51 | { 52 | free(v); 53 | v = NULL; 54 | fprintf(stderr, "Freeing tmp var\n"); 55 | reclaimed = true; 56 | } 57 | DEF_AUTOFREE(memtestchar, reaper) 58 | 59 | START_TEST(cve_memory_test) 60 | { 61 | { 62 | autofree(memtestchar) *tmp = NULL; 63 | 64 | if (!asprintf(&tmp, "Allocation test")) { 65 | fail("Unable to allocate memory"); 66 | } 67 | } 68 | fail_if(!reclaimed, "Scope based tmp var was not reclaimed!"); 69 | } 70 | END_TEST 71 | 72 | START_TEST(cve_string_test) 73 | { 74 | cve_string *str = NULL, *str2 = NULL; 75 | char *c = NULL; 76 | 77 | str = cve_string_dup_printf("Test String #%d", 1); 78 | fail_if(!str, "Failed to allocate string"); 79 | fail_if(!g_str_equal(str->str, "Test String #1"), "Invalid formatted string"); 80 | fail_if(cstrlen(str) != 14, "Incorrect string length"); 81 | 82 | fail_if(!cve_string_has_suffix(str, "g #1"), "String has incorrect suffix"); 83 | fail_if(!cve_string_has_prefix(str, "T"), "String has incorrect prefix"); 84 | 85 | fail_if(!cve_string_cat(str, "append"), "Failed to append string"); 86 | fail_if(cstrlen(str) != 20, "Incorrect string length after append"); 87 | 88 | str2 = cve_string_dup(str->str); 89 | fail_if(!g_str_equal(str->str, str2->str), "Strings do not match"); 90 | fail_if(cstrlen(str) != cstrlen(str2), "Invalid string length after dup"); 91 | 92 | cve_string_free(str); 93 | cve_string_free(str2); 94 | 95 | str = cve_string_dup(" spacey text "); 96 | c = cve_string_strip(str); 97 | fail_if(!c, "Failed to strip string"); 98 | fail_if(!streq(c, "spacey text"), "Failed to match stripped string"); 99 | 100 | str = cve_string_dup_printf("Test String #%d", 2); 101 | fail_if(!str, "Failed to allocate string"); 102 | fail_if(!cve_string_const_equal(str, "Test String #2"), "Const String Compare failed"); 103 | /* Ensure its not failing completely. */ 104 | fail_if(cve_string_const_equal(str, "Test String #1"), "Const String Compare should not match"); 105 | 106 | str2 = cve_string_dup(str->str); 107 | fail_if(!str2, "Failed to dup string"); 108 | fail_if(!cve_string_equal(str, str2), "Identical strings not matching"); 109 | 110 | fail_if(!cve_string_const_equal(str2, (const char *)str->str), "Direct const compare fail!"); 111 | 112 | cve_string_free(str); 113 | cve_string_free(str2); 114 | 115 | fail_if(cve_string_dup(NULL), "dup on NULL string"); 116 | fail_if(cve_string_dup_printf(NULL), "dup_printf on NULL string"); 117 | fail_if(cve_string_cat(NULL, NULL), "cat on NULL string"); 118 | 119 | fail_if(cve_string_equal(NULL, NULL), "equal on NULL string"); 120 | fail_if(cve_string_const_equal(NULL, NULL), "const_equal on NULL string"); 121 | 122 | /* Forced empty ->str tests */ 123 | cve_string st = {.len = 0 }; 124 | fail_if(cve_string_equal(&st, &st), "equal on NULL ->str"); 125 | fail_if(cve_string_const_equal(&st, "TEST"), "const_equal on NULL ->str"); 126 | } 127 | END_TEST 128 | 129 | static Suite *core_suite(void) 130 | { 131 | Suite *s = NULL; 132 | TCase *tc = NULL; 133 | 134 | s = suite_create("cve_core"); 135 | tc = tcase_create("cve_core_functions"); 136 | tcase_add_test(tc, cve_date_function); 137 | tcase_add_test(tc, cve_memory_test); 138 | tcase_add_test(tc, cve_string_test); 139 | suite_add_tcase(s, tc); 140 | 141 | return s; 142 | } 143 | 144 | int main(void) 145 | { 146 | Suite *s; 147 | SRunner *sr; 148 | int fail; 149 | 150 | s = core_suite(); 151 | sr = srunner_create(s); 152 | srunner_run_all(sr, CK_VERBOSE); 153 | fail = srunner_ntests_failed(sr); 154 | srunner_free(sr); 155 | 156 | if (fail > 0) { 157 | return EXIT_FAILURE; 158 | } 159 | 160 | return EXIT_SUCCESS; 161 | } 162 | 163 | /* 164 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 165 | * 166 | * Local variables: 167 | * c-basic-offset: 8 168 | * tab-width: 8 169 | * indent-tabs-mode: nil 170 | * End: 171 | * 172 | * vi: set shiftwidth=8 tabstop=8 expandtab: 173 | * :indentSize=8:tabSize=8:noTabs=true: 174 | */ 175 | -------------------------------------------------------------------------------- /src/plugins/output/csv/csv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * csv.c - CSV output 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | 14 | #include 15 | #include 16 | 17 | #include "config.h" 18 | #include "cve-check-tool.h" 19 | #include "plugin.h" 20 | #include "util.h" 21 | 22 | static inline bool filter_item(CveCheckTool *self, __attribute__((unused)) GList *item) 23 | { 24 | struct cve_entry_t *c_entry = cve_db_get_cve(self->cve_db, (gchar *)item->data); 25 | bool ret = false; 26 | if (self->modified > 0 && c_entry->modified > self->modified) { 27 | ret = true; 28 | } 29 | cve_free(c_entry); 30 | return ret; 31 | } 32 | 33 | static gchar *list_as_string(CveCheckTool *self, GList *list) 34 | { 35 | GList *it = NULL; 36 | gchar *ret = NULL; 37 | if (!list) { 38 | return NULL; 39 | } 40 | 41 | if (!filter_item(self, list)) { 42 | ret = g_strdup_printf("%s", (gchar *)list->data); 43 | } 44 | 45 | if (!list->next) { 46 | return ret; 47 | } 48 | 49 | for (it = list->next; it; it = it->next) { 50 | if (filter_item(self, it)) { 51 | continue; 52 | } 53 | gchar *next = NULL; 54 | next = g_strdup_printf("%s %s", ret, (gchar *)it->data); 55 | if (ret) { 56 | g_free(ret); 57 | } 58 | ret = next; 59 | } 60 | return ret; 61 | } 62 | 63 | static int get_open_bug_count(CveCheckTool *self, const gchar *cves) 64 | { 65 | int bug_count = 0; 66 | gchar **ids, **id = NULL; 67 | ReportStatus report_status = REPORT_STATUS_UNREPORTED; 68 | 69 | if (cves == NULL) { 70 | return bug_count; 71 | } 72 | ids = g_strsplit(cves, " ", -1); 73 | for (id = ids; *id; id++) { 74 | if (g_hash_table_contains(self->bdb, *id)) { 75 | report_status = *((ReportStatus *)g_hash_table_lookup(self->bdb, *id)); 76 | if (report_status == REPORT_STATUS_OPEN) { 77 | ++bug_count; 78 | } 79 | } 80 | } 81 | g_strfreev(ids); 82 | return bug_count; 83 | } 84 | 85 | static bool csv_write_report(CveCheckTool *self) 86 | { 87 | GHashTableIter iter; 88 | gchar *key = NULL; 89 | struct source_package_t *v = NULL; 90 | 91 | FILE *fd = NULL; 92 | bool ret = false; 93 | 94 | if (self->output_file) { 95 | fd = fopen(self->output_file, "w"); 96 | if (!fd) { 97 | fprintf(stderr, "Unable to open %s for writing: %s\n", self->output_file, strerror(errno)); 98 | return false; 99 | } 100 | } else { 101 | fd = stdout; 102 | } 103 | 104 | /* package,version,unpatched CVE numbers space delimited,patched CVE numbers space delimited, open bug count */ 105 | g_hash_table_iter_init(&iter, self->db); 106 | while (g_hash_table_iter_next(&iter, (void **)&key, (void **)&v)) { 107 | autofree(gchar) *issues = NULL; 108 | autofree(gchar) *patched = NULL; 109 | autofree(gchar) *open_bug_count = NULL; 110 | gchar *is = NULL, *pa = NULL; 111 | 112 | if (!v->issues && !v->patched && !self->show_unaffected) { 113 | continue; 114 | } 115 | if (!v->issues && self->hide_patched) { 116 | continue; 117 | } 118 | is = issues = list_as_string(self, v->issues); 119 | if (!is) { 120 | is = ""; 121 | } 122 | pa = patched = list_as_string(self, v->patched); 123 | if (!pa) { 124 | pa = ""; 125 | } 126 | if (!self->show_unaffected && !issues && !patched) { 127 | continue; 128 | } 129 | if (self->bdb) { 130 | open_bug_count = 131 | g_strdup_printf("%i", get_open_bug_count(self, is) + get_open_bug_count(self, pa)); 132 | } else { 133 | open_bug_count = g_strdup("0"); 134 | } 135 | if (fprintf(fd, "%s,%s,%s,%s,%s\n", key, (char *)v->version, is, pa, open_bug_count) < 0) { 136 | goto io_error; 137 | } 138 | } 139 | 140 | ret = true; 141 | goto success; 142 | 143 | io_error: 144 | fprintf(stderr, "Error writing to file: %s\n", strerror(errno)); 145 | success: 146 | ret = true; 147 | if (fd != stdout && self->output_file) { 148 | fclose(fd); 149 | } 150 | 151 | return ret; 152 | } 153 | 154 | _module_export_ bool cve_plugin_module_init(CvePlugin *self) 155 | { 156 | self->report = csv_write_report; 157 | self->flags = PLUGIN_TYPE_REPORT; 158 | self->name = "csv"; 159 | return true; 160 | } 161 | 162 | /* 163 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 164 | * 165 | * Local variables: 166 | * c-basic-offset: 8 167 | * tab-width: 8 168 | * indent-tabs-mode: nil 169 | * End: 170 | * 171 | * vi: set shiftwidth=8 tabstop=8 expandtab: 172 | * :indentSize=8:tabSize=8:noTabs=true: 173 | */ 174 | -------------------------------------------------------------------------------- /src/library/cve-string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cve-string.h - string management 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #define _GNU_SOURCE 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /** 22 | * Safely represent and store a buffer as a string 23 | */ 24 | typedef struct cve_string_t { 25 | char *str; /**str) { 58 | free(str->str); 59 | } 60 | free(str); 61 | } 62 | 63 | /** 64 | * Append the contents of 'append' into the given cve_string 65 | * 66 | * @param str Pointer to a valid cve_string 67 | * @param append Text to append into the cve_string 68 | * 69 | * @return a boolean value indicating success 70 | */ 71 | bool cve_string_cat(cve_string *str, const char *append); 72 | 73 | /** 74 | * Determine if string A is equal to string B 75 | * 76 | * @note This function will not check beyond the length of string A 77 | * 78 | * @param a string to check 79 | * @param b string to check against 80 | * @return a boolean value, true if the strings match, otherwise false 81 | */ 82 | static inline bool cve_string_equal(cve_string *a, cve_string *b) 83 | { 84 | if (!a || !b) { 85 | return false; 86 | } 87 | if (!a->str || !b->str) { 88 | return false; 89 | } 90 | return strncmp(a->str, b->str, a->len) == 0; 91 | } 92 | 93 | /** 94 | * Determine if string A is equal to string B 95 | * 96 | * @note This function will not check beyond the length of string A 97 | * 98 | * @param a string to check 99 | * @param b const char* string to check against 100 | * @return a boolean value, true if the strings match, otherwise false 101 | */ 102 | static inline bool cve_string_const_equal(cve_string *a, const char *b) 103 | { 104 | if (!a || !b) { 105 | return false; 106 | } 107 | if (!a->str) { 108 | return false; 109 | } 110 | return strncmp(a->str, b, a->len) == 0; 111 | } 112 | 113 | /** 114 | * Convenience wrapper to obtain string length 115 | * 116 | * @note This will assert the string is not NULL, ensuring a termination. 117 | * 118 | * @return length of the given string 119 | */ 120 | static inline int cstrlen(cve_string *str) 121 | { 122 | assert(str != NULL); 123 | return str->len; 124 | } 125 | 126 | /** 127 | * Determine if a string has the given suffix 128 | * 129 | * @param str Valid cve_string 130 | * @param suffix A suffix to check 131 | * @param len Length of the suffix 132 | * 133 | * @return True if @str has the given suffix 134 | */ 135 | static inline bool cve_string_has_suffix_const(cve_string *str, char *suffix, ssize_t len) 136 | { 137 | if (!str || !suffix) { 138 | return false; 139 | } 140 | if (len > str->len || len <= 0) { 141 | return false; 142 | } 143 | return (strncmp(str->str + (str->len - (len)), suffix, len) == 0); 144 | } 145 | 146 | /** 147 | * Determine if a string has the given prefix 148 | * 149 | * @param str Valid cve_string 150 | * @param prefix A prefix to check 151 | * @param len Length of the prefix 152 | * 153 | * @return True if @str has the given prefix 154 | */ 155 | static inline bool cve_string_has_prefix_const(cve_string *str, char *prefix, ssize_t len) 156 | { 157 | if (!str || !prefix) { 158 | return false; 159 | } 160 | if (len > str->len || len <= 0) { 161 | return false; 162 | } 163 | return (strncmp(str->str, prefix, len) == 0); 164 | } 165 | 166 | /** 167 | * Perform inplace modification of a strip to return 168 | * a stripped pointer, that is, without start and end whitespace. 169 | * 170 | * This does not modify the length of the string. 171 | */ 172 | static inline char *cve_string_strip(cve_string *str) 173 | { 174 | int i; 175 | 176 | if (!str || !str->str) { 177 | return NULL; 178 | } 179 | for (i = 0; i < str->len; i++) { 180 | if (str->str[i] != ' ') { 181 | break; 182 | } 183 | } 184 | for (int j = str->len - 1; j > i; j--) { 185 | if (str->str[j] != ' ') { 186 | str->str[j + 1] = '\0'; 187 | break; 188 | } 189 | } 190 | 191 | return str->str + i; 192 | } 193 | /** 194 | * To be used only with compile time constants. 195 | */ 196 | #define cve_string_has_suffix(a, suff) cve_string_has_suffix_const(a, suff, (sizeof(suff) - 1)) 197 | #define cve_string_has_prefix(a, pref) cve_string_has_prefix_const(a, pref, (sizeof(pref) - 1)) 198 | 199 | /* 200 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 201 | * 202 | * Local variables: 203 | * c-basic-offset: 8 204 | * tab-width: 8 205 | * indent-tabs-mode: nil 206 | * End: 207 | * 208 | * vi: set shiftwidth=8 tabstop=8 expandtab: 209 | * :indentSize=8:tabSize=8:noTabs=true: 210 | */ 211 | -------------------------------------------------------------------------------- /src/plugin-manager.c: -------------------------------------------------------------------------------- 1 | /* 2 | * plugin-manager.c 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include "config.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "plugin-manager.h" 22 | #include "plugin.h" 23 | #include "util.h" 24 | 25 | static CveHashmap *_plugins; 26 | 27 | static char *get_plugins_path(void) 28 | { 29 | #ifdef TEST_SUITE_BUILD 30 | return strdup(TOP_BUILD_DIR "/tests/dummy_install"); 31 | #elif defined(INTREE) 32 | return strdup(TOP_BUILD_DIR "/src/plugins/.libs/"); 33 | #elif !defined(RELATIVE_PLUGINS) 34 | /* Return built in path when relative plugins disabled */ 35 | return strdup(MODULE_DIR); 36 | #endif 37 | autofree(char) *binname = NULL; 38 | 39 | binname = realpath("/proc/self/exe", NULL); 40 | 41 | if (!binname) { 42 | return strdup(MODULE_DIR); 43 | } 44 | 45 | /* relative plugins */ 46 | const char *libpaths[] = { "lib/cve-check-tool", "lib64/cve-check-tool" }; 47 | 48 | autofree(char) *cp = strdup(binname); 49 | if (!cp) { 50 | return NULL; 51 | } 52 | char *d = dirname(cp); 53 | if (!d) { 54 | return NULL; 55 | } 56 | 57 | for (uint i = 0; i < sizeof(libpaths) / sizeof(libpaths[0]); i++) { 58 | autofree(char) *pot = NULL; 59 | 60 | if (!asprintf(&pot, "%s/../%s", d, libpaths[i])) { 61 | fprintf(stderr, "get_plugins_path(): Out of memory\n"); 62 | abort(); 63 | } 64 | if (access(pot, F_OK) == 0) { 65 | return strdup(pot); 66 | } 67 | } 68 | 69 | /* Still didn't find it, return baked in path */ 70 | return strdup(MODULE_DIR); 71 | } 72 | 73 | static char *build_module_path(const char *name) 74 | { 75 | autofree(char) *dir = get_plugins_path(); 76 | char *p = NULL; 77 | 78 | if (!dir) { 79 | fprintf(stderr, "build_module_path(): Failed to determine module path!\n"); 80 | return NULL; 81 | } 82 | 83 | if (!asprintf(&p, "%s/%s", dir, name)) { 84 | fprintf(stderr, "build_module_path(): Out of memory\n"); 85 | return NULL; 86 | } 87 | 88 | return p; 89 | } 90 | 91 | static void load_module(const char *name) 92 | { 93 | void *handle, *cast; 94 | autofree(char) *path = NULL; 95 | char *error = NULL; 96 | cve_plugin_init init_func; 97 | CvePlugin *plugin = NULL; 98 | 99 | path = build_module_path(name); 100 | if (!path) { 101 | fprintf(stderr, "Out of memory\n"); 102 | return; 103 | } 104 | handle = dlopen(path, RTLD_LAZY); 105 | if (!handle) { 106 | fprintf(stderr, "Unable to load module: %s\n", dlerror()); 107 | return; 108 | } 109 | dlerror(); 110 | cast = dlsym(handle, "cve_plugin_module_init"); 111 | if (!cast || (error = dlerror()) != NULL) { 112 | fprintf(stderr, "Cannot load module: %s\n", error); 113 | abort(); 114 | } 115 | dlerror(); 116 | memcpy(&init_func, &cast, sizeof(init_func)); 117 | 118 | plugin = calloc(1, sizeof(struct CvePlugin)); 119 | if (!plugin) { 120 | fprintf(stderr, "Unable to allocate memory\n"); 121 | abort(); 122 | } 123 | if (!init_func(plugin)) { 124 | fprintf(stderr, "Plugin initialisation failed\n"); 125 | abort(); 126 | } 127 | if (!plugin->name) { 128 | fprintf(stderr, "Plugin %s does not set a name - aborting\n", name); 129 | abort(); 130 | } 131 | plugin->handle = handle; 132 | cve_hashmap_put(_plugins, (char *)plugin->name, plugin); 133 | } 134 | 135 | static void destroy_plugin(CvePlugin *plugin) 136 | { 137 | if (plugin->destroy) { 138 | plugin->destroy(plugin); 139 | } 140 | if (plugin->handle) { 141 | dlclose(plugin->handle); 142 | } 143 | free(plugin); 144 | } 145 | 146 | void cve_plugin_manager_init() 147 | { 148 | DIR *dir = NULL; 149 | struct dirent *ent = NULL; 150 | autofree(char) *mod_path = NULL; 151 | 152 | if (_plugins) { 153 | return; 154 | } 155 | _plugins = cve_hashmap_new_full(string_hash, string_compare, NULL, (hash_free_func)destroy_plugin); 156 | 157 | mod_path = get_plugins_path(); 158 | if (!mod_path) { 159 | fprintf(stderr, "Unable to determine module path\n"); 160 | return; 161 | } 162 | 163 | if (!(dir = opendir(mod_path))) { 164 | fprintf(stderr, "Unable to list modules: %s\n", strerror(errno)); 165 | return; 166 | } 167 | 168 | while ((ent = readdir(dir))) { 169 | if (g_str_has_suffix(ent->d_name, ".so")) { 170 | load_module(ent->d_name); 171 | } 172 | } 173 | if (dir) { 174 | closedir(dir); 175 | } 176 | } 177 | 178 | void cve_plugin_manager_destroy() 179 | { 180 | if (!_plugins) { 181 | return; 182 | } 183 | cve_hashmap_free(_plugins); 184 | _plugins = NULL; 185 | } 186 | 187 | CvePlugin *cve_plugin_get_by_name(const char *name) 188 | { 189 | CvePlugin *ret = NULL; 190 | 191 | if (!_plugins) { 192 | return NULL; 193 | } 194 | ret = cve_hashmap_get(_plugins, name); 195 | return ret; 196 | } 197 | 198 | GList *cve_plugin_get_by_cap(int cap) 199 | { 200 | GList *ret = NULL; 201 | CveHashmapIter iter; 202 | CvePlugin *plug = NULL; 203 | 204 | if (!_plugins) { 205 | return NULL; 206 | } 207 | 208 | cve_hashmap_iter_init(_plugins, &iter); 209 | while (cve_hashmap_iter_next(&iter, NULL, (void **)&plug)) { 210 | if (plug->flags & cap) { 211 | ret = g_list_append(ret, plug); 212 | } 213 | } 214 | return ret; 215 | } 216 | 217 | /* 218 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 219 | * 220 | * Local variables: 221 | * c-basic-offset: 8 222 | * tab-width: 8 223 | * indent-tabs-mode: nil 224 | * End: 225 | * 226 | * vi: set shiftwidth=8 tabstop=8 expandtab: 227 | * :indentSize=8:tabSize=8:noTabs=true: 228 | */ 229 | -------------------------------------------------------------------------------- /m4/ax_valgrind_check.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_VALGRIND_CHECK() 8 | # 9 | # DESCRIPTION 10 | # 11 | # Checks whether Valgrind is present and, if so, allows running `make 12 | # check` under a variety of Valgrind tools to check for memory and 13 | # threading errors. 14 | # 15 | # Defines VALGRIND_CHECK_RULES which should be substituted in your 16 | # Makefile; and $enable_valgrind which can be used in subsequent configure 17 | # output. VALGRIND_ENABLED is defined and substituted, and corresponds to 18 | # the value of the --enable-valgrind option, which defaults to being 19 | # enabled if Valgrind is installed and disabled otherwise. 20 | # 21 | # If unit tests are written using a shell script and automake's 22 | # LOG_COMPILER system, the $(VALGRIND) variable can be used within the 23 | # shell scripts to enable Valgrind, as described here: 24 | # 25 | # https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html 26 | # 27 | # Usage example: 28 | # 29 | # configure.ac: 30 | # 31 | # AX_VALGRIND_CHECK 32 | # 33 | # Makefile.am: 34 | # 35 | # @VALGRIND_CHECK_RULES@ 36 | # VALGRIND_SUPPRESSIONS_FILES = my-project.supp 37 | # EXTRA_DIST = my-project.supp 38 | # 39 | # This results in a "check-valgrind" rule being added to any Makefile.am 40 | # which includes "@VALGRIND_CHECK_RULES@" (assuming the module has been 41 | # configured with --enable-valgrind). Running `make check-valgrind` in 42 | # that directory will run the module's test suite (`make check`) once for 43 | # each of the available Valgrind tools (out of memcheck, helgrind, drd and 44 | # sgcheck), and will output results to test-suite-$toolname.log for each. 45 | # The target will succeed if there are zero errors and fail otherwise. 46 | # 47 | # The macro supports running with and without libtool. 48 | # 49 | # LICENSE 50 | # 51 | # Copyright (c) 2014, 2015 Philip Withnall 52 | # 53 | # Copying and distribution of this file, with or without modification, are 54 | # permitted in any medium without royalty provided the copyright notice 55 | # and this notice are preserved. This file is offered as-is, without any 56 | # warranty. 57 | 58 | #serial 3 59 | 60 | AC_DEFUN([AX_VALGRIND_CHECK],[ 61 | dnl Check for --enable-valgrind 62 | AC_MSG_CHECKING([whether to enable Valgrind on the unit tests]) 63 | AC_ARG_ENABLE([valgrind], 64 | [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], 65 | [enable_valgrind=$enableval],[enable_valgrind=]) 66 | 67 | # Check for Valgrind. 68 | AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) 69 | 70 | AS_IF([test "$enable_valgrind" = "yes" -a "$VALGRIND" = ""],[ 71 | AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) 72 | ]) 73 | AS_IF([test "$enable_valgrind" != "no"],[enable_valgrind=yes]) 74 | 75 | AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) 76 | AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) 77 | AC_MSG_RESULT([$enable_valgrind]) 78 | 79 | # Check for Valgrind tools we care about. 80 | m4_define([valgrind_tool_list],[[memcheck], [helgrind], [drd], [exp-sgcheck]]) 81 | 82 | AS_IF([test "$VALGRIND" != ""],[ 83 | m4_foreach([vgtool],[valgrind_tool_list],[ 84 | m4_define([vgtooln],AS_TR_SH(vgtool)) 85 | m4_define([ax_cv_var],[ax_cv_valgrind_tool_]vgtooln) 86 | AC_CACHE_CHECK([for Valgrind tool ]vgtool,ax_cv_var,[ 87 | ax_cv_var= 88 | AS_IF([`$VALGRIND --tool=vgtool --help 2&>/dev/null`],[ 89 | ax_cv_var="vgtool" 90 | ]) 91 | ]) 92 | 93 | AC_SUBST([VALGRIND_HAVE_TOOL_]vgtooln,[$ax_cv_var]) 94 | ]) 95 | ]) 96 | 97 | VALGRIND_CHECK_RULES=' 98 | # Valgrind check 99 | # 100 | # Optional: 101 | # - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions 102 | # files to load. (Default: empty) 103 | # - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. 104 | # (Default: --num-callers=30) 105 | # - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: 106 | # memcheck, helgrind, drd, sgcheck). (Default: various) 107 | 108 | # Optional variables 109 | VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) 110 | VALGRIND_FLAGS ?= --num-callers=30 111 | VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no 112 | VALGRIND_helgrind_FLAGS ?= --history-level=approx 113 | VALGRIND_drd_FLAGS ?= 114 | VALGRIND_sgcheck_FLAGS ?= 115 | 116 | # Internal use 117 | valgrind_tools = memcheck helgrind drd sgcheck 118 | valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) 119 | 120 | valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) 121 | valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) 122 | valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) 123 | valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) 124 | 125 | valgrind_quiet = $(valgrind_quiet_$(V)) 126 | valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) 127 | valgrind_quiet_0 = --quiet 128 | 129 | # Support running with and without libtool. 130 | ifneq ($(LIBTOOL),) 131 | valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute 132 | else 133 | valgrind_lt = 134 | endif 135 | 136 | # Use recursive makes in order to ignore errors during check 137 | check-valgrind: 138 | ifeq ($(VALGRIND_ENABLED),yes) 139 | -$(foreach tool,$(valgrind_tools), \ 140 | $(if $(VALGRIND_HAVE_TOOL_$(tool))$(VALGRIND_HAVE_TOOL_exp_$(tool)), \ 141 | $(MAKE) $(AM_MAKEFLAGS) -k check-valgrind-tool VALGRIND_TOOL=$(tool); \ 142 | ) \ 143 | ) 144 | else 145 | @echo "Need to reconfigure with --enable-valgrind" 146 | endif 147 | 148 | # Valgrind running 149 | VALGRIND_TESTS_ENVIRONMENT = \ 150 | $(TESTS_ENVIRONMENT) \ 151 | env VALGRIND=$(VALGRIND) \ 152 | G_SLICE=always-malloc,debug-blocks \ 153 | G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly 154 | 155 | VALGRIND_LOG_COMPILER = \ 156 | $(valgrind_lt) \ 157 | $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) 158 | 159 | check-valgrind-tool: 160 | ifeq ($(VALGRIND_ENABLED),yes) 161 | $(MAKE) check-TESTS \ 162 | TESTS_ENVIRONMENT="$(VALGRIND_TESTS_ENVIRONMENT)" \ 163 | LOG_COMPILER="$(VALGRIND_LOG_COMPILER)" \ 164 | LOG_FLAGS="$(valgrind_$(VALGRIND_TOOL)_flags)" \ 165 | TEST_SUITE_LOG=test-suite-$(VALGRIND_TOOL).log 166 | else 167 | @echo "Need to reconfigure with --enable-valgrind" 168 | endif 169 | 170 | DISTCHECK_CONFIGURE_FLAGS ?= 171 | DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind 172 | 173 | MOSTLYCLEANFILES ?= 174 | MOSTLYCLEANFILES += $(valgrind_log_files) 175 | 176 | .PHONY: check-valgrind check-valgrind-tool 177 | ' 178 | 179 | AC_SUBST([VALGRIND_CHECK_RULES]) 180 | m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) 181 | ]) 182 | -------------------------------------------------------------------------------- /src/library/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * util.c - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "cve-check-tool.h" 25 | #include "cve-string.h" 26 | #include "util.h" 27 | 28 | bool find_sources(const char *path, package_match_func match, bool recurse, cve_add_callback cb) 29 | { 30 | struct stat st = {.st_ino = 0 }; 31 | bool ret = false; 32 | DIR *dir = NULL; 33 | struct dirent *ent = NULL; 34 | char *fullp = NULL; 35 | 36 | if (!cb) { 37 | return false; 38 | } 39 | 40 | if (!match) { 41 | return false; 42 | } 43 | 44 | if (lstat(path, &st) != 0) { 45 | goto end; 46 | } 47 | 48 | if (S_ISLNK(st.st_mode)) { 49 | ret = false; 50 | goto end; 51 | } else if (S_ISDIR(st.st_mode)) { 52 | if (!(dir = opendir(path))) { 53 | goto end; 54 | } 55 | while ((ent = readdir(dir))) { 56 | if (!streq(ent->d_name, ".") && !streq(ent->d_name, "..")) { 57 | if (!asprintf(&fullp, "%s/%s", path, ent->d_name)) { 58 | goto end; 59 | } 60 | if (!(cve_is_dir(fullp) && !recurse)) { 61 | find_sources(fullp, match, recurse, cb); 62 | } 63 | free(fullp); 64 | } 65 | } 66 | } else if (S_ISREG(st.st_mode)) { 67 | if (match(path)) { 68 | cb(path); 69 | } 70 | } 71 | 72 | ret = true; 73 | end: 74 | if (dir) { 75 | closedir(dir); 76 | } 77 | return ret; 78 | } 79 | 80 | bool is_package_list(cve_string *path) 81 | { 82 | if (cve_string_has_suffix(path, "packages") || cve_string_has_suffix(path, "packages-nvr")) { 83 | return cve_file_exists(path->str); 84 | } 85 | return false; 86 | } 87 | 88 | gchar *demacro(CveHashmap *macros, gchar *str) 89 | { 90 | gchar *key = NULL, *value = NULL; 91 | 92 | if (!macros) { 93 | return str; 94 | } 95 | 96 | while (true) { 97 | bool hit = false; 98 | CveHashmapIter iter; 99 | 100 | cve_hashmap_iter_init(macros, &iter); 101 | while (cve_hashmap_iter_next(&iter, (void **)&key, (void **)&value)) { 102 | if (str_contains(str, key)) { 103 | hit = true; 104 | str = str_replace(str, key, value); 105 | } 106 | } 107 | if (!hit) { 108 | break; 109 | } 110 | } 111 | return str; 112 | } 113 | 114 | int64_t parse_xml_date(const char *date) 115 | { 116 | autofree(cve_string) *tmp = cve_string_dup(date); 117 | autofree(GTimeZone) *tz = NULL; 118 | autofree(GDateTime) *t = NULL, *t2 = NULL; 119 | char *c = NULL; 120 | int64_t ret = -1; 121 | 122 | /* Example XML string: 123 | * 2015-03-05T08:24:10.220-05:00 124 | */ 125 | if (!tmp) { 126 | return -1; 127 | } 128 | if (!(c = memchr(tmp->str, 'T', tmp->len))) { 129 | return -1; 130 | } 131 | if (!(c = memchr(c, '-', tmp->len - (tmp->str - c)))) { 132 | return -1; 133 | } 134 | gint y, m, d, h, min, s; 135 | if (sscanf(date, "%4d-%2d-%2dT%2d:%2d:%2d", &y, &m, &d, &h, &min, &s) != 6) { 136 | return -1; 137 | } 138 | tz = g_time_zone_new(c); 139 | if (!tz) { 140 | return -1; 141 | } 142 | 143 | t = g_date_time_new(tz, y, m, d, h, min, (gdouble)s); 144 | if (!t) { 145 | return -1; 146 | } 147 | t2 = g_date_time_to_local(t); 148 | 149 | ret = (int64_t)g_date_time_to_unix(t2); 150 | 151 | return ret; 152 | } 153 | 154 | gchar *str_replace(gchar *source, const gchar *word, const gchar *replace) 155 | { 156 | autofree(gstrv) *splits = NULL; 157 | gchar *ret = NULL; 158 | splits = g_strsplit(source, word, -1); 159 | ret = g_strjoinv(replace, splits); 160 | g_free(source); 161 | return ret; 162 | } 163 | 164 | /* File functions */ 165 | 166 | bool cve_file_exists(const char *p) 167 | { 168 | struct stat st = {.st_ino = 0 }; 169 | return (stat(p, &st) == 0); 170 | } 171 | 172 | bool cve_is_dir(const char *p) 173 | { 174 | struct stat st = {.st_ino = 0 }; 175 | if (stat(p, &st) != 0) { 176 | return false; 177 | } 178 | return S_ISDIR(st.st_mode); 179 | } 180 | 181 | char *cve_get_file_parent(const char *p) 182 | { 183 | autofree(char) *d = strdup(p); 184 | char *r = realpath(dirname(d), NULL); 185 | return r; 186 | } 187 | 188 | bool cve_file_set_text(const char *path, char *text) 189 | { 190 | FILE *fp = NULL; 191 | bool ret = false; 192 | 193 | fp = fopen(path, "w"); 194 | 195 | if (!fp) { 196 | goto end; 197 | } 198 | 199 | if (fprintf(fp, "%s", text) < 0) { 200 | goto end; 201 | } 202 | ret = true; 203 | end: 204 | if (fp) { 205 | fclose(fp); 206 | } 207 | 208 | return ret; 209 | } 210 | 211 | cve_string *make_db_dot_fname(const char *db_path, const char *suffix) 212 | { 213 | autofree(char) *path = NULL; 214 | const char *dir; 215 | char *file; 216 | 217 | path = strdup(db_path); 218 | if (!path) { 219 | return NULL; 220 | } 221 | 222 | file = strrchr(path, '/'); 223 | if (file) { 224 | *file++ = '\0'; 225 | if (!*file) { 226 | file = (char *)nvd_file; 227 | } 228 | dir = *path ? path : "."; 229 | } else { 230 | file = path; 231 | dir = "."; 232 | } 233 | 234 | return cve_string_dup_printf("%s/.%s.%s", dir, file, suffix); 235 | } 236 | 237 | /* 238 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 239 | * 240 | * Local variables: 241 | * c-basic-offset: 8 242 | * tab-width: 8 243 | * indent-tabs-mode: nil 244 | * End: 245 | * 246 | * vi: set shiftwidth=8 tabstop=8 expandtab: 247 | * :indentSize=8:tabSize=8:noTabs=true: 248 | */ 249 | -------------------------------------------------------------------------------- /src/library/hashmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashmap.h - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This is a chained Hashmap implementation. It focuses on being clean 12 | * and efficient, and is comparable (at -O2) to open addressing hashmaps 13 | * up until around 105,000 elements, where open addressing will begin 14 | * to come out in front. At 1 million elements, open addressing is a clear 15 | * winner, however currently we only need a maximum of 70k elements in 16 | * our implementation. 17 | * 18 | * Given the memory required to even begin to deal with 1 million elements, 19 | * it becomes very questionable whether a hashmap should have even been 20 | * used in the first place (mmap'd db?) 21 | */ 22 | 23 | #pragma once 24 | 25 | #define _GNU_SOURCE 26 | 27 | #include 28 | #include 29 | 30 | /* Convert between uint and void* */ 31 | #define HASH_KEY(x) ((void *)((uintptr_t)(x))) 32 | #define HASH_VALUE(x) HASH_KEY(x) 33 | #define UNHASH_KEY(x) ((unsigned int)((uintptr_t)(x))) 34 | #define UNHASH_VALUE(x) UNHASH_KEY(x) 35 | 36 | typedef struct CveHashmap CveHashmap; 37 | 38 | /** 39 | * Iteration object 40 | */ 41 | typedef struct CveHashmapIter { 42 | int n0; 43 | void *n1; 44 | void *n2; 45 | } CveHashmapIter; 46 | 47 | /** 48 | * Hash comparison function definition 49 | * 50 | * @param l First value to compare 51 | * @param r Second value to compare 52 | * 53 | * @return true if l and r both match, otherwise false 54 | */ 55 | typedef bool (*hash_compare_func)(const void *l, const void *r); 56 | 57 | /** 58 | * Hash creation function definition 59 | * 60 | * @param key Key to generate a hash for 61 | * 62 | * @return an unsigned integer hash result 63 | */ 64 | typedef unsigned (*hash_create_func)(const void *key); 65 | 66 | /** 67 | * Callback definition to free keys and values 68 | * 69 | * @param p Single-depth pointer to either a key or value that should be freed 70 | */ 71 | typedef void (*hash_free_func)(void *p); 72 | 73 | /** 74 | * Default hash/comparison functions 75 | * 76 | * @note These are only used for comparison of *keys*, not values. Unless 77 | * explicitly using string keys, you should most likely stick with the default 78 | * simple_hash and simple_compare functions 79 | */ 80 | 81 | /* Default string hash */ 82 | static inline unsigned string_hash(const void *key) 83 | { 84 | unsigned hash = 5381; 85 | const signed char *c; 86 | 87 | /* DJB's hash function */ 88 | for (c = key; *c != '\0'; c++) { 89 | hash = (hash << 5) + hash + (unsigned)*c; 90 | } 91 | return hash; 92 | } 93 | 94 | /** 95 | * Trivial pointer->uint hash 96 | */ 97 | static inline unsigned simple_hash(const void *source) 98 | { 99 | return UNHASH_KEY(source); 100 | } 101 | 102 | /** 103 | * Comparison of string keys 104 | */ 105 | static inline bool string_compare(const void *l, const void *r) 106 | { 107 | if (!l || !r) { 108 | return false; 109 | } 110 | return (strcmp(l, r) == 0); 111 | } 112 | 113 | /** 114 | * Trivial pointer comparison 115 | */ 116 | static inline bool simple_compare(const void *l, const void *r) 117 | { 118 | return (l == r); 119 | } 120 | 121 | /** 122 | * Create a new CveHashmap 123 | * 124 | * @param hash Hash creation function 125 | * @param compare Key comparison function 126 | * 127 | * @return A newly allocated CveHashmap 128 | */ 129 | CveHashmap *cve_hashmap_new(hash_create_func hash, hash_compare_func compare); 130 | 131 | /** 132 | * Create a new CveHashmap with cleanup functions 133 | * 134 | * @param hash Hash creation function 135 | * @param compare Key comparison function 136 | * @param key_free Function to free keys when removed/destroyed 137 | * @param value_free Function to free values when removed/destroyed 138 | * 139 | * @return A newly allocated CveHashmap 140 | */ 141 | CveHashmap *cve_hashmap_new_full(hash_create_func hash, hash_compare_func compare, hash_free_func key_free, 142 | hash_free_func value_free); 143 | 144 | /** 145 | * Store a key/value pair in the hashmap 146 | * 147 | * @note This will displace duplicate keys, and may free both the key 148 | * and value if key_free and value_free are non null 149 | * 150 | * @param key Key to store in the hashmap 151 | * @param value Value to be associated with the key 152 | * 153 | * @return true if the operation succeeded. 154 | */ 155 | bool cve_hashmap_put(CveHashmap *map, const void *key, void *value); 156 | 157 | /** 158 | * Get the value associated with the unique key 159 | * 160 | * @param key Unique key to obtain a value for 161 | * @return The associated value if it exists, otherwise NULL 162 | */ 163 | void *cve_hashmap_get(CveHashmap *map, const void *key); 164 | 165 | /** 166 | * Determine if the key has an associated value in the CveHashmap 167 | * 168 | * @param key Unique key to check value for 169 | * @return True if the key exists in the hashmap 170 | */ 171 | bool cve_hashmap_contains(CveHashmap *self, const void *key); 172 | 173 | /** 174 | * Free the given CveHashmap, and all keys/values if appropriate 175 | */ 176 | void cve_hashmap_free(CveHashmap *map); 177 | 178 | /** 179 | * Remove the value and key identified by key. 180 | * 181 | * @note If key_free or value_free are non-NULL, they will be invoked 182 | * for both the key and value being removed 183 | * 184 | * @param key The unique key to remove 185 | * @return true if the key/value pair were removed 186 | */ 187 | bool cve_hashmap_remove(CveHashmap *map, const void *key); 188 | 189 | /** 190 | * Remove the value and key identified by key without freeing them 191 | * 192 | * @return true if the key/value pair were stolen 193 | */ 194 | bool cve_hashmap_steal(CveHashmap *map, const void *key); 195 | 196 | /** 197 | * Return the current size of the hashmap 198 | * 199 | * @return size of the current hashmap (element count) 200 | */ 201 | int cve_hashmap_size(CveHashmap *map); 202 | 203 | /** 204 | * Initialise a CveHashmapIter for iteration purposes 205 | * 206 | * @note The iter *must* be re-inited to re-use, or if it becomes 207 | * exhausted from a previous iteration run 208 | * 209 | * @param iter pointer to a CveHashmapIter 210 | */ 211 | void cve_hashmap_iter_init(CveHashmap *map, CveHashmapIter *iter); 212 | 213 | /** 214 | * Iterate every key/value pair in the hashmap 215 | * 216 | * @param iter A correctly initialised CveHashmapIter 217 | * @param key Pointers to store the key in, may be NULL to skip 218 | * @param value Pointer to store the value in, may be NULL to skip 219 | * 220 | * @return true if it's possible to iterate 221 | */ 222 | bool cve_hashmap_iter_next(CveHashmapIter *iter, void **key, void **value); 223 | 224 | /* 225 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 226 | * 227 | * Local variables: 228 | * c-basic-offset: 8 229 | * tab-width: 8 230 | * indent-tabs-mode: nil 231 | * End: 232 | * 233 | * vi: set shiftwidth=8 tabstop=8 expandtab: 234 | * :indentSize=8:tabSize=8:noTabs=true: 235 | */ 236 | -------------------------------------------------------------------------------- /src/plugins/packaging/eopkg/eopkg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * eopkg.c - Solus Operating System specific 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "eopkg.h" 21 | #include "plugin.h" 22 | #include "util.h" 23 | 24 | #define PATCH_PREFIX "files/security/" 25 | 26 | struct source_package_t *eopkg_inspect_pspec(const char *filename) 27 | { 28 | xmlDocPtr doc = NULL; 29 | xmlNodePtr root = NULL; 30 | xmlNodePtr nxt = NULL; 31 | xmlNodePtr child = NULL; 32 | xmlChar *tmp = NULL; 33 | struct source_package_t *t = NULL; 34 | 35 | xmlChar *source_name = NULL; 36 | int release = -1; 37 | xmlChar *version = NULL; 38 | char *fpath = NULL; 39 | 40 | doc = xmlReadFile(filename, NULL, 0); 41 | if (!doc) { 42 | return NULL; 43 | } 44 | 45 | root = xmlDocGetRootElement(doc); 46 | if (!root) { 47 | goto clean; 48 | } 49 | 50 | if (!xmlStrEqual(root->name, BAD_CAST "PISI")) { 51 | fprintf(stderr, "Invalid root node\n"); 52 | goto clean; 53 | } 54 | 55 | for (nxt = root->children; nxt; nxt = nxt->next) { 56 | if (nxt->type != XML_ELEMENT_NODE) { 57 | continue; 58 | } 59 | if (xmlStrEqual(nxt->name, BAD_CAST "Source")) { 60 | /* Grab child with "Source" name */ 61 | for (child = nxt->children; child; child = child->next) { 62 | if (!(child->type == XML_ELEMENT_NODE && xmlStrEqual(child->name, BAD_CAST "Name"))) { 63 | continue; 64 | } 65 | if (!child->children) { 66 | /* bail */ 67 | break; 68 | } 69 | if (child->children->type != XML_TEXT_NODE) { 70 | /* bail */ 71 | break; 72 | } 73 | source_name = child->children->content; 74 | break; 75 | } 76 | } else if (xmlStrEqual(nxt->name, BAD_CAST "History")) { 77 | for (child = nxt->children; child; child = child->next) { 78 | if (!(child->type == XML_ELEMENT_NODE && xmlStrEqual(child->name, BAD_CAST "Update"))) { 79 | continue; 80 | } 81 | tmp = xmlGetProp(child, BAD_CAST "release"); 82 | if (!tmp) { 83 | g_warning("Missing release property"); 84 | continue; 85 | } 86 | int t_release = atoi((const char *)tmp); 87 | if (t_release > release) { 88 | release = t_release; 89 | if (version) { 90 | version = NULL; 91 | } 92 | for (xmlNodePtr sub = child->children; sub; sub = sub->next) { 93 | if (!(sub->type == XML_ELEMENT_NODE && 94 | xmlStrEqual(sub->name, BAD_CAST "Version"))) { 95 | continue; 96 | } 97 | if (sub->children && sub->children->type == XML_TEXT_NODE) { 98 | version = sub->children->content; 99 | } 100 | break; 101 | } 102 | } 103 | xmlFree(tmp); 104 | } 105 | } 106 | } 107 | 108 | if (!version || !source_name) { 109 | goto clean; 110 | } 111 | 112 | fpath = cve_get_file_parent(filename); 113 | if (!fpath) { 114 | goto clean; 115 | } 116 | 117 | t = calloc(1, sizeof(struct source_package_t)); 118 | if (!t) { 119 | free(fpath); 120 | goto clean; 121 | } 122 | t->name = xmlStrdup(source_name); 123 | t->version = xmlStrdup(version); 124 | t->release = release; 125 | t->path = fpath; 126 | t->xml = true; /* Ensure xmlFree is used */ 127 | t->type = PACKAGE_TYPE_EOPKG; 128 | 129 | clean: 130 | xmlFreeDoc(doc); 131 | return t; 132 | } 133 | 134 | bool eopkg_is_patched(struct source_package_t *pkg, char *id) 135 | { 136 | /* Determine if its patched. */ 137 | autofree(gchar) *pnamet = g_ascii_strdown((gchar *)id, -1); 138 | autofree(gchar) *pnamel = g_strdup_printf("%s/%s/%s.patch", pkg->path, PATCH_PREFIX, pnamet); 139 | autofree(gchar) *pname = g_strdup_printf("%s/%s/%s.patch", pkg->path, PATCH_PREFIX, id); 140 | 141 | if (g_file_test(pname, G_FILE_TEST_EXISTS) || g_file_test(pnamel, G_FILE_TEST_EXISTS)) { 142 | return true; 143 | } 144 | 145 | return false; 146 | } 147 | 148 | bool eopkg_is_ignored(struct source_package_t *pkg, char *id) 149 | { 150 | /* Determine if its patched. */ 151 | autofree(gchar) *pnamet = g_ascii_strdown((gchar *)id, -1); 152 | autofree(gchar) *pnamel = g_strdup_printf("%s/%s/%s.nopatch", pkg->path, PATCH_PREFIX, pnamet); 153 | autofree(gchar) *pname = g_strdup_printf("%s/%s/%s.nopatch", pkg->path, PATCH_PREFIX, id); 154 | 155 | if (g_file_test(pname, G_FILE_TEST_EXISTS) || g_file_test(pnamel, G_FILE_TEST_EXISTS)) { 156 | return true; 157 | } 158 | 159 | return false; 160 | } 161 | 162 | bool eopkg_is_package(const char *filename) 163 | { 164 | return g_str_has_suffix((const gchar *)filename, "pspec.xml") || 165 | g_str_has_suffix((const gchar *)filename, "pspec_x86_64.xml"); 166 | } 167 | 168 | void eopkg_locate_sources(const char *directory, bool recurse, cve_add_callback cb) 169 | { 170 | find_sources(directory, &eopkg_is_package, recurse, cb); 171 | } 172 | 173 | _module_export_ bool cve_plugin_module_init(CvePlugin *self) 174 | { 175 | self->flags = PLUGIN_TYPE_PACKAGE; 176 | self->name = "eopkg"; 177 | self->is_ignored = eopkg_is_ignored; 178 | self->is_patched = eopkg_is_patched; 179 | self->is_package = eopkg_is_package; 180 | self->scan_package = eopkg_inspect_pspec; 181 | return true; 182 | } 183 | /* 184 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 185 | * 186 | * Local variables: 187 | * c-basic-offset: 8 188 | * tab-width: 8 189 | * indent-tabs-mode: nil 190 | * End: 191 | * 192 | * vi: set shiftwidth=8 tabstop=8 expandtab: 193 | * :indentSize=8:tabSize=8:noTabs=true: 194 | */ 195 | -------------------------------------------------------------------------------- /tests/check-packaging.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cve-check-tool 3 | * Copyright (C) 2015 Intel Corporation 4 | * 5 | * cve-check-tool is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | */ 10 | 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | 16 | #include "cve-string.c" 17 | #include "hashmap.c" 18 | #include "util.c" 19 | #include "util.h" 20 | 21 | #define TEST_SUITE_BUILD 1 22 | 23 | #include "plugin-manager.c" 24 | 25 | #include "config.h" 26 | 27 | const char *nvd_file = "nvd.db"; 28 | 29 | static int add_count = 0; 30 | 31 | void cve_add_package(__attribute__((unused)) const char *path) 32 | { 33 | add_count++; 34 | } 35 | 36 | static inline void package_free(void *p, CvePlugin *pkg_plugin) 37 | { 38 | if (!p) { 39 | return; 40 | } 41 | struct source_package_t *t = p; 42 | 43 | if (t->extra && pkg_plugin && pkg_plugin->free_package) { 44 | pkg_plugin->free_package(t); 45 | t->extra = NULL; 46 | } 47 | 48 | if (t->issues) { /* bless you */ 49 | g_list_free_full(t->issues, xmlFree); 50 | } 51 | if (t->patched) { 52 | g_list_free_full(t->patched, xmlFree); 53 | } 54 | if (t->path) { 55 | free(t->path); 56 | } 57 | if (t->xml) { 58 | xmlFree((xmlChar *)t->name); 59 | xmlFree((xmlChar *)t->version); 60 | } else { 61 | g_free((gchar *)t->name); 62 | g_free((gchar *)t->version); 63 | } 64 | 65 | free(t); 66 | } 67 | 68 | /** 69 | * General RPM test 70 | */ 71 | START_TEST(cve_rpm_test) 72 | { 73 | CvePlugin *plugin = cve_plugin_get_by_name("rpm"); 74 | struct source_package_t *pkg = NULL; 75 | 76 | fail_if(!plugin, "rpm plugin not found!"); 77 | 78 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/rpm/package_does_not_exist.spec"); 79 | fail_if(pkg, "Incorrectly succeeded on non-existing spec!"); 80 | 81 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/rpm/package2.spec"); 82 | fail_if(!pkg, "Failed to inspect RPM spec!"); 83 | package_free(pkg, plugin); 84 | pkg = NULL; 85 | 86 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/rpm/package.spec"); 87 | fail_if(!pkg, "Failed to inspect RPM spec!"); 88 | 89 | fail_if(!g_str_equal(pkg->name, "test-package"), "Invalid RPM package name"); 90 | fail_if(!g_str_equal(pkg->version, "1.1.0"), "Invalid RPM package version"); 91 | fail_if(pkg->release != 5, "Invalid RPM package release"); 92 | 93 | fail_if(!plugin->is_patched(pkg, "CVE-2014-5461"), "Failed to detect RPM CVE patch"); 94 | 95 | fail_if(!plugin->is_ignored(pkg, "CVE-2013-4459"), "Failed to detect ignored CVE"); 96 | fail_if(plugin->is_ignored(pkg, "CVE-2013-0012"), "Incorrectly detected non-ignored CVE"); 97 | 98 | package_free(pkg, plugin); 99 | } 100 | END_TEST 101 | 102 | START_TEST(cve_srpm_test) 103 | { 104 | gchar *c = NULL; 105 | CvePlugin *plugin = NULL; 106 | struct source_package_t *pkg = NULL; 107 | 108 | if (!(c = g_find_program_in_path("srpm"))) { 109 | fprintf(stderr, "Unable to perform SRPM tests!"); 110 | return; 111 | } 112 | g_free(c); 113 | 114 | plugin = cve_plugin_get_by_name("srpm"); 115 | 116 | fail_if(!plugin, "srpm plugin not found!"); 117 | 118 | pkg = plugin->scan_archive(TOP_DIR "/tests/dummy_data/rpm/", "package", "1.", "5"); 119 | fail_if(!pkg, "Failed to inspect source RPM!"); 120 | fail_if(!plugin->is_patched(pkg, "CVE-2014-5461"), "SRPM patch test failed"); 121 | fail_if(!g_str_equal(pkg->name, "test-package"), "Invalid SRPM package name"); 122 | fail_if(!g_str_equal(pkg->version, "1."), "Invalid SRPM package version"); 123 | 124 | fail_if(plugin->is_ignored(pkg, "CVE-2013-0012"), "Incorrectly detected non-ignored CVE"); 125 | 126 | package_free(pkg, plugin); 127 | 128 | pkg = plugin->scan_archive(TOP_DIR "/tests/dummy_data/rpm/", "invalid_package", "1.", "5"); 129 | fail_if(pkg, "Incorrectly succeeded at missing source RPM!"); 130 | } 131 | END_TEST 132 | 133 | /** 134 | * Solus eopkg test 135 | */ 136 | START_TEST(cve_eopkg_test) 137 | { 138 | CvePlugin *plugin = cve_plugin_get_by_name("eopkg"); 139 | struct source_package_t *pkg = NULL; 140 | 141 | fail_if(!plugin, "eopkg plugin not found!"); 142 | 143 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/eopkg/not-exist"); 144 | fail_if(pkg, "Non-existent eopkg should be NULL"); 145 | 146 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/eopkg/pspec.xml"); 147 | fail_if(!pkg, "Failed to inspect eopkg spec!"); 148 | 149 | fail_if(!g_str_equal(pkg->name, "budgie-desktop"), "Invalid eopkg package name"); 150 | fail_if(!g_str_equal(pkg->version, "8.1"), "Invalid eopkg package version"); 151 | fail_if(pkg->release != 41, "Invalid eopkg package release"); 152 | 153 | fail_if(!plugin->is_patched(pkg, "CVE-2014-5461"), "Failed to detect eopkg CVE patch"); 154 | 155 | fail_if(!plugin->is_ignored(pkg, "CVE-2013-4459"), "Failed to detect ignored CVE"); 156 | fail_if(plugin->is_ignored(pkg, "CVE-2013-0012"), "Incorrectly detected non-ignored CVE"); 157 | 158 | package_free(pkg, plugin); 159 | } 160 | END_TEST 161 | 162 | /** 163 | * Arch Linux pkgbuild test 164 | */ 165 | START_TEST(cve_pkgbuild_test) 166 | { 167 | CvePlugin *plugin = cve_plugin_get_by_name("pkgbuild"); 168 | struct source_package_t *pkg = NULL; 169 | 170 | fail_if(!plugin, "pkgbuild plugin not found!"); 171 | 172 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/pkgbuild/not-exist"); 173 | fail_if(pkg, "Non-existent PKGBUILD should be NULL"); 174 | 175 | pkg = plugin->scan_package(TOP_DIR "/tests/dummy_data/pkgbuild/PKGBUILD"); 176 | fail_if(!pkg, "Failed to inspect PKGBUILD spec!"); 177 | 178 | fail_if(!g_str_equal(pkg->name, "my-test-package"), "Invalid PKGBUILD package name"); 179 | fail_if(!g_str_equal(pkg->version, "1.0.3"), "Invalid PKGBUILD package version"); 180 | fail_if(pkg->release != 10, "Invalid PKGBUILD package release"); 181 | 182 | fail_if(!plugin->is_patched(pkg, "CVE-2014-5461"), "Failed to detect pkgbuild CVE patch"); 183 | 184 | package_free(pkg, plugin); 185 | } 186 | END_TEST 187 | 188 | static Suite *core_suite(void) 189 | { 190 | Suite *s = NULL; 191 | TCase *tc = NULL; 192 | 193 | s = suite_create("cve_packaging"); 194 | tc = tcase_create("cve_packaging_functions"); 195 | tcase_add_test(tc, cve_rpm_test); 196 | tcase_add_test(tc, cve_srpm_test); 197 | tcase_add_test(tc, cve_eopkg_test); 198 | tcase_add_test(tc, cve_pkgbuild_test); 199 | suite_add_tcase(s, tc); 200 | 201 | return s; 202 | } 203 | 204 | int main(void) 205 | { 206 | Suite *s; 207 | SRunner *sr; 208 | int fail; 209 | 210 | cve_plugin_manager_init(); 211 | s = core_suite(); 212 | sr = srunner_create(s); 213 | srunner_run_all(sr, CK_VERBOSE); 214 | fail = srunner_ntests_failed(sr); 215 | srunner_free(sr); 216 | 217 | cve_plugin_manager_destroy(); 218 | 219 | if (fail > 0) { 220 | return EXIT_FAILURE; 221 | } 222 | 223 | return EXIT_SUCCESS; 224 | } 225 | 226 | /* 227 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 228 | * 229 | * Local variables: 230 | * c-basic-offset: 8 231 | * tab-width: 8 232 | * indent-tabs-mode: nil 233 | * End: 234 | * 235 | * vi: set shiftwidth=8 tabstop=8 expandtab: 236 | * :indentSize=8:tabSize=8:noTabs=true: 237 | */ 238 | -------------------------------------------------------------------------------- /src/library/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * util.h - cve-check-tool 3 | * 4 | * Copyright (C) 2015 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #pragma once 13 | 14 | #define _GNU_SOURCE 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "cve-check-tool.h" 27 | #include "cve-string.h" 28 | #include "hashmap.h" 29 | 30 | /** 31 | * Determine if a given file is actually a list of packages.. 32 | */ 33 | bool is_package_list(cve_string *path); 34 | 35 | /** 36 | * Determine if a file is one the enumerator is interested in 37 | * 38 | * @note Paths are relative basename, not absolute 39 | */ 40 | typedef bool (*package_match_func)(const gchar *); 41 | 42 | /** 43 | * Recursively replace macros within a string 44 | * 45 | * @note Original string will be free'd, only perform on allocated 46 | * strings! 47 | * 48 | * @param macros Populated macro table (key->value) 49 | * @param str String to perform replacement on 50 | * 51 | * @return newly allocated string after replacements are performed 52 | */ 53 | gchar *demacro(CveHashmap *macros, gchar *str); 54 | 55 | /** 56 | * Convert an XML formatted date into unix seconds 57 | * 58 | * @param date XML input 59 | * @return int64_t unix timestamp, or -1 if it doesn't parse 60 | */ 61 | int64_t parse_xml_date(const char *date); 62 | 63 | /** 64 | * Search the repo source directory for all matching sources 65 | * 66 | * @param directory Base directory to recurse 67 | * @param match A function to determine "matching" source packages 68 | * @param recurse Whether we can recurse the given directory 69 | * @param cb A callback to execute when we encounter a matching package 70 | */ 71 | bool find_sources(const char *directory, package_match_func match, bool recurse, cve_add_callback cb); 72 | 73 | /** 74 | * Implemented in a *similar* fashion to how g_autoptr is intended to 75 | * work in future, but without the concerns of MSVC, etc.. 76 | */ 77 | #define DEF_AUTOFREE(N, C) \ 78 | static inline void _autofree_func_##N(void *p) \ 79 | { \ 80 | if (p && *(N **)p) { \ 81 | /* To debug: printf("Freeing %s\n", #N); */ \ 82 | C(*(N **)p); \ 83 | (*(void **)p) = NULL; \ 84 | } \ 85 | } 86 | 87 | #define autofree(N) __attribute__((cleanup(_autofree_func_##N))) N 88 | 89 | /** 90 | * Enable easier integration with autofree. Note this is still a single 91 | * pointer, you need to use it like: gstrv*, NOT gstrv. 92 | */ 93 | typedef gchar *gstrv; 94 | 95 | /** 96 | * Autofree helper: Cleanup a GFileEnumerator 97 | * 98 | * @param enu A valid GFileEnumerator pointer 99 | */ 100 | static inline void cve_io_enum_close(GFileEnumerator *enu) 101 | { 102 | if (!enu) { 103 | return; 104 | } 105 | g_file_enumerator_close(enu, NULL, NULL); 106 | g_object_unref(enu); 107 | } 108 | 109 | /** 110 | * We don't allow direct use, as this handles multiple GInputStream 111 | * subclasses.. */ 112 | static inline void cve_io_close(void *stream) 113 | { 114 | if (!stream) { 115 | return; 116 | } 117 | g_input_stream_close(stream, NULL, NULL); 118 | g_object_unref(stream); 119 | } 120 | static inline void cve_io_close_out(void *stream) 121 | { 122 | if (!stream) { 123 | return; 124 | } 125 | g_output_stream_close(stream, NULL, NULL); 126 | g_object_unref(stream); 127 | } 128 | 129 | DEF_AUTOFREE(GDateTime, g_date_time_unref) 130 | DEF_AUTOFREE(GOptionContext, g_option_context_free) 131 | DEF_AUTOFREE(gchar, g_free) 132 | DEF_AUTOFREE(GError, g_error_free) 133 | DEF_AUTOFREE(gstrv, g_strfreev) 134 | DEF_AUTOFREE(GFileEnumerator, cve_io_enum_close) 135 | DEF_AUTOFREE(GFile, g_object_unref) 136 | DEF_AUTOFREE(GFileInputStream, cve_io_close) 137 | DEF_AUTOFREE(GFileOutputStream, cve_io_close_out) 138 | DEF_AUTOFREE(GOutputStream, cve_io_close_out) 139 | DEF_AUTOFREE(GDataInputStream, cve_io_close) 140 | DEF_AUTOFREE(GHashTable, g_hash_table_unref) 141 | DEF_AUTOFREE(GTimeZone, g_time_zone_unref) 142 | DEF_AUTOFREE(GZlibDecompressor, g_object_unref) 143 | DEF_AUTOFREE(GKeyFile, g_key_file_unref) 144 | DEF_AUTOFREE(cve_string, cve_string_free) 145 | DEF_AUTOFREE(CveDB, cve_db_free) 146 | DEF_AUTOFREE(char, free) 147 | DEF_AUTOFREE(CveHashmap, cve_hashmap_free) 148 | 149 | /** 150 | * Suger-utility: Determine if a string contains a certain word 151 | * 152 | * @param word String to check 153 | * @param needle "word" to search for 154 | * 155 | * @return a boolean value, true if the string contains the word 156 | */ 157 | static inline bool str_contains(const gchar *word, const gchar *needle) 158 | { 159 | return strstr(word, needle) != NULL; 160 | } 161 | 162 | /** 163 | * Utility to replace one word with another in a string 164 | * 165 | * @note Only use on allocated strings, as this will free the original 166 | * string! 167 | * 168 | * @param source (Allocated) source string to perform replacement on 169 | * @param word Word to replace 170 | * @param replace Replacement word 171 | * 172 | * @return A newly allocated string with the replacement performed 173 | */ 174 | gchar *str_replace(gchar *source, const gchar *word, const gchar *replace); 175 | 176 | /** 177 | * Determine if two null terminated strings are equal 178 | * 179 | * @param s1 String compare against s2 180 | * @param s2 String compare against s1 181 | * @return a boolean value, true if two strings are equal 182 | */ 183 | static inline bool streq(const char *s1, const char *s2) 184 | { 185 | return !strcmp(s1, s2); 186 | } 187 | 188 | /** 189 | * Determine if the given path exists on disk 190 | * 191 | * @param p Path for the file to test 192 | * @return a boolean value, true if the condition is met 193 | */ 194 | bool cve_file_exists(const char *p); 195 | 196 | /** 197 | * Determine if the given path exists, and is a directory 198 | * 199 | * @param p Path to the directory 200 | * @return a boolean value, true if the condition is met 201 | */ 202 | bool cve_is_dir(const char *p); 203 | 204 | /** 205 | * Return the parent path for a given file 206 | * 207 | * @note This is an allocated string, and must be freed by the caller 208 | * @param p Path to file 209 | * @return a newly allocated string 210 | */ 211 | char *cve_get_file_parent(const char *p); 212 | 213 | /** 214 | * Return absolute path to the dot file in format .basename(db_path).suffix 215 | * 216 | * @note This is an allocated string, and must be freed by the caller 217 | * @param db_path Path to the database file 218 | * @param suffix Suffix of the database file 219 | * @return a newly allocated string or NULL in case of error 220 | */ 221 | cve_string *make_db_dot_fname(const char *db_path, const char *suffix); 222 | 223 | /** 224 | * Quick utility function to write small text files 225 | * 226 | * @note This will _always_ overwrite an existing file 227 | * 228 | * @param path Path of the file to be written 229 | * @param text Contents of the new file 230 | * 231 | * @return True if this succeeded 232 | */ 233 | bool cve_file_set_text(const char *path, char *text); 234 | 235 | /* 236 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 237 | * 238 | * Local variables: 239 | * c-basic-offset: 8 240 | * tab-width: 8 241 | * indent-tabs-mode: nil 242 | * End: 243 | * 244 | * vi: set shiftwidth=8 tabstop=8 expandtab: 245 | * :indentSize=8:tabSize=8:noTabs=true: 246 | */ 247 | -------------------------------------------------------------------------------- /tests/check-jira-plugin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cve-check-tool 3 | * Copyright (C) 2015 Intel Corporation 4 | * 5 | * cve-check-tool is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | */ 10 | 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | 16 | #include "cve-string.c" 17 | #include "util.c" 18 | #include "util.h" 19 | 20 | #include "config.h" 21 | #include "hashmap.c" 22 | #include "plugins/jira/jira.c" 23 | #include "plugins/jira/jira.h" 24 | #include "template.c" 25 | 26 | const char *nvd_file = "nvd.db"; 27 | 28 | #define DEFAULT_CONFIG_FILE DEFAULT_PATH "/cve-check-tool.conf" 29 | #define TEST_CONFIG_FILE TOP_DIR "/tests/dummy_data/plugins/jira/cve-check-tool.conf" 30 | #define TEST_JSON_FILE TOP_DIR "/tests/dummy_data/plugins/jira/jira_test_issues.json" 31 | 32 | /* Check the plugin init function which includes parsing the config file */ 33 | START_TEST(cve_jira_plugin_init_function) 34 | { 35 | bool ret = false; 36 | ret = init_jira_plugin(NULL, TEST_CONFIG_FILE); 37 | fail_if(!ret, "Failed to initialize JIRA plugin"); 38 | destroy_jira_plugin(); 39 | } 40 | END_TEST 41 | 42 | /* Check the JIRA issues family of functions */ 43 | START_TEST(cve_jira_plugin_jira_issues_functions) 44 | { 45 | bool ret = false; 46 | int count = 0; 47 | GSList *jira_issues = NULL; 48 | ret = init_jira_plugin(NULL, TEST_CONFIG_FILE); 49 | fail_if(!ret, "Failed to initialize JIRA plugin"); 50 | ret = get_jira_issues_file(TEST_JSON_FILE, &jira_issues); 51 | fail_if(!ret, "Failed load JIRA issues from file"); 52 | count = get_jira_issues_count(jira_issues); 53 | fail_if(count != 10, "Failed expected count of 10"); 54 | show_jira_issues(jira_issues); 55 | free_jira_issues(&jira_issues); 56 | free_jira_issues(NULL); 57 | destroy_jira_plugin(); 58 | } 59 | END_TEST 60 | 61 | /* No explicit get fuctions to verify, but let's still exercise the code */ 62 | START_TEST(cve_jira_plugin_set_functions) 63 | { 64 | bool ret = false; 65 | int i = 0; 66 | ret = init_jira_plugin(NULL, TEST_CONFIG_FILE); 67 | fail_if(!ret, "Failed to initialize JIRA plugin"); 68 | for (i = 0; i < 2; i++) { 69 | if (i == 1) { 70 | destroy_jira_plugin(); 71 | } 72 | set_timeout_secs(-1); 73 | set_timeout_secs(0); 74 | set_timeout_secs(123); 75 | set_timeout_secs(3.14); 76 | set_url("http://foo"); 77 | set_url(""); 78 | set_url(NULL); 79 | set_user("someuser"); 80 | set_user(""); 81 | set_user(NULL); 82 | set_verbose(true); 83 | set_verbose(false); 84 | } 85 | destroy_jira_plugin(); 86 | } 87 | END_TEST 88 | 89 | /* Check the save functions */ 90 | START_TEST(cve_jira_plugin_save_functions) 91 | { 92 | bool ret = false; 93 | int count = 0; 94 | GSList *jira_issues = NULL; 95 | ret = init_jira_plugin(NULL, TEST_CONFIG_FILE); 96 | fail_if(!ret, "Failed to initialize JIRA plugin"); 97 | ret = get_jira_issues_file(TEST_JSON_FILE, &jira_issues); 98 | fail_if(!ret, "Failed load JIRA issues from file"); 99 | count = get_jira_issues_count(jira_issues); 100 | fail_if(count != 10, "Failed expected count of 10"); 101 | ret = save_jira_issues_csv(jira_issues, "test_jira_issues.csv"); 102 | if (!ret || !cve_file_exists("test_jira_issues.csv")) { 103 | fail("Failed to save CSV to a file"); 104 | } else { 105 | remove("test_jira_issues.csv"); 106 | } 107 | ret = save_jira_issues_xml(jira_issues, "test_jira_issues.xml"); 108 | if (!ret || !cve_file_exists("test_jira_issues.xml")) { 109 | fail("Failed to save XML to a file"); 110 | } else { 111 | remove("test_jira_issues.xml"); 112 | } 113 | ret = save_jira_issues_csv(jira_issues, ""); 114 | fail_if(ret, "Negative save to CSV file test should have failed"); 115 | ret = save_jira_issues_csv(jira_issues, NULL); 116 | fail_if(ret, "Negative save to CSV file test should have failed"); 117 | ret = save_jira_issues_csv(NULL, ""); 118 | fail_if(ret, "Negative save to CSV file test should have failed"); 119 | ret = save_jira_issues_csv(NULL, NULL); 120 | fail_if(ret, "Negative save to CSV file test should have failed"); 121 | ret = save_jira_issues_csv(NULL, "test_jira_issues.csv"); 122 | fail_if(ret, "Negative save to CSV file test should have failed"); 123 | if (cve_file_exists("test_jira_issues.csv")) { 124 | remove("test_jira_issues.csv"); 125 | fail("Negative save to CSV file test should not have not create file"); 126 | } 127 | ret = save_jira_issues_xml(jira_issues, ""); 128 | fail_if(ret, "Negative save to XML file test should have failed"); 129 | ret = save_jira_issues_xml(jira_issues, NULL); 130 | fail_if(ret, "Negative save to XML file test should have failed"); 131 | ret = save_jira_issues_xml(NULL, ""); 132 | fail_if(ret, "Negative save to XML file test should have failed"); 133 | ret = save_jira_issues_xml(NULL, NULL); 134 | fail_if(ret, "Negative save to XML file test should have failed"); 135 | ret = save_jira_issues_xml(NULL, "test_jira_issues.xml"); 136 | fail_if(ret, "Negative save to XML file test should have failed"); 137 | if (cve_file_exists("test_jira_issues.xml")) { 138 | remove("test_jira_issues.xml"); 139 | fail("Negative save to XML file test should not have not create file"); 140 | } 141 | ret = save("fleischkuekle", "test_jira_save.txt"); 142 | if (!ret || !cve_file_exists("test_jira_save.txt")) { 143 | fail("Failed to save text to a file"); 144 | } else { 145 | remove("test_jira_save.txt"); 146 | } 147 | ret = save("", ""); 148 | fail_if(ret, "Negative save to text file test should have failed"); 149 | ret = save(NULL, ""); 150 | fail_if(ret, "Negative save to text file test should have failed"); 151 | ret = save("", NULL); 152 | fail_if(ret, "Negative save to text file test should have failed"); 153 | ret = save("", "test_jira_save.txt"); 154 | if (cve_file_exists("test_jira_save.txt")) { 155 | remove("test_jira_save.txt"); 156 | fail("Negative save to text file test should not have not create file"); 157 | } 158 | ret = save(NULL, "test_jira_save.txt"); 159 | if (cve_file_exists("test_jira_save.txt")) { 160 | remove("test_jira_save.txt"); 161 | fail("Negative save to text file test should not have not create file"); 162 | } 163 | destroy_jira_plugin(); 164 | } 165 | END_TEST 166 | 167 | static Suite *core_suite(void) 168 | { 169 | Suite *s = NULL; 170 | TCase *tc = NULL; 171 | 172 | s = suite_create("cve_jira_plugin"); 173 | tc = tcase_create("cve_jira_plugin_functions"); 174 | tcase_add_test(tc, cve_jira_plugin_init_function); 175 | tcase_add_test(tc, cve_jira_plugin_jira_issues_functions); 176 | tcase_add_test(tc, cve_jira_plugin_set_functions); 177 | tcase_add_test(tc, cve_jira_plugin_save_functions); 178 | suite_add_tcase(s, tc); 179 | return s; 180 | } 181 | 182 | int main(void) 183 | { 184 | Suite *s; 185 | SRunner *sr; 186 | int fail; 187 | 188 | s = core_suite(); 189 | sr = srunner_create(s); 190 | srunner_run_all(sr, CK_VERBOSE); 191 | fail = srunner_ntests_failed(sr); 192 | srunner_free(sr); 193 | 194 | if (fail > 0) { 195 | return EXIT_FAILURE; 196 | } 197 | 198 | return EXIT_SUCCESS; 199 | } 200 | 201 | /* 202 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 203 | * 204 | * Local variables: 205 | * c-basic-offset: 8 206 | * tab-width: 8 207 | * indent-tabs-mode: nil 208 | * End: 209 | * 210 | * vi: set shiftwidth=8 tabstop=8 expandtab: 211 | * :indentSize=8:tabSize=8:noTabs=true: 212 | */ 213 | -------------------------------------------------------------------------------- /tests/check-hashmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * check-hashmap.c - cve-check-tool 3 | * 4 | * Copyright (C) 2016 Intel Corporation 5 | * 6 | * cve-check-tool is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include "hashmap.c" 14 | #include 15 | #include 16 | 17 | START_TEST(cve_hashmap_new_check) 18 | { 19 | CveHashmap *map = NULL; 20 | 21 | map = cve_hashmap_new(NULL, NULL); 22 | fail_if(!map, "Failed to allocate new hashmap"); 23 | fail_if(cve_hashmap_size(map) != 0, "Hashmap size is not 0 after new"); 24 | cve_hashmap_free(map); 25 | } 26 | END_TEST 27 | 28 | START_TEST(cve_hashmap_simple_check) 29 | { 30 | CveHashmap *map = NULL; 31 | bool b; 32 | void *val = NULL; 33 | 34 | map = cve_hashmap_new(simple_hash, simple_compare); 35 | fail_if(!map, "Failed to allocate new hashmap"); 36 | 37 | for (int i = 0; i < 1000; i++) { 38 | b = cve_hashmap_put(map, HASH_KEY(i), HASH_VALUE(i)); 39 | fail_if(!b, "Failed to add integer to hashmap"); 40 | } 41 | fail_if(cve_hashmap_size(map) != 1000, "Hashmap size invalid after 1000 elements"); 42 | 43 | for (int i = 300; i < 700; i++) { 44 | b = cve_hashmap_remove(map, HASH_KEY(i)); 45 | fail_if(!b, "Failed to remove known integer from from hashmap"); 46 | } 47 | 48 | fail_if(cve_hashmap_size(map) != 600, "Hashmap size invalid after 400 removals"); 49 | 50 | val = cve_hashmap_get(map, HASH_KEY(302)); 51 | fail_if(val, "Value should not be returned from hashmap after removal"); 52 | 53 | val = cve_hashmap_get(map, HASH_KEY(802)); 54 | fail_if(!val, "Value should be returned from hashmap for known key"); 55 | fail_if(UNHASH_VALUE(val) != 802, "Value returned from hashmap was incorrect"); 56 | 57 | cve_hashmap_free(map); 58 | map = NULL; 59 | 60 | fail_if(cve_hashmap_size(map) >= 0, "Incorrect size returned for NULL hashmap"); 61 | } 62 | END_TEST 63 | 64 | START_TEST(cve_hashmap_string_check) 65 | { 66 | CveHashmap *map = NULL; 67 | bool b; 68 | void *val = NULL; 69 | 70 | map = cve_hashmap_new(string_hash, string_compare); 71 | fail_if(!map, "Failed to allocate new hashmap"); 72 | 73 | b = cve_hashmap_put(map, "John", HASH_VALUE(12)); 74 | fail_if(!b, "Failed to put entry into map"); 75 | b = cve_hashmap_put(map, "Lucy", HASH_VALUE(42)); 76 | fail_if(!b, "Failed to put entry into map"); 77 | b = cve_hashmap_put(map, "Bob", HASH_VALUE(19012)); 78 | fail_if(!b, "Failed to put entry into map"); 79 | b = cve_hashmap_put(map, "Sarah", HASH_VALUE(83)); 80 | fail_if(!b, "Failed to put entry into map"); 81 | 82 | val = cve_hashmap_get(map, "John"); 83 | fail_if(!val, "Failed to get known key from hashmap"); 84 | fail_if(UNHASH_VALUE(val) != 12, "Failed to get correct value from hashmap"); 85 | 86 | val = cve_hashmap_get(map, "Lucy"); 87 | fail_if(!val, "Failed to get known key from hashmap"); 88 | fail_if(UNHASH_VALUE(val) != 42, "Failed to get correct value from hashmap"); 89 | 90 | val = cve_hashmap_get(map, "Bob"); 91 | fail_if(!val, "Failed to get known key from hashmap"); 92 | fail_if(UNHASH_VALUE(val) != 19012, "Failed to get correct value from hashmap"); 93 | 94 | val = cve_hashmap_get(map, "Sarah"); 95 | fail_if(!val, "Failed to get known key from hashmap"); 96 | fail_if(UNHASH_VALUE(val) != 83, "Failed to get correct value from hashmap"); 97 | 98 | cve_hashmap_free(map); 99 | } 100 | END_TEST 101 | 102 | START_TEST(cve_hashmap_iter_check) 103 | { 104 | CveHashmap *map = NULL; 105 | CveHashmapIter iter; 106 | bool b; 107 | int count = 0; 108 | void *key = NULL; 109 | void *value = NULL; 110 | 111 | map = cve_hashmap_new(NULL, NULL); 112 | fail_if(!map, "Failed to allocate new hashmap"); 113 | 114 | for (int i = 0; i < 5000; i++) { 115 | b = cve_hashmap_put(map, HASH_KEY(i), HASH_KEY(i)); 116 | fail_if(!b, "Failed to insert key into hashmap"); 117 | } 118 | 119 | fail_if(cve_hashmap_size(map) != 5000, "Invalid hashmap size after 5000 elements"); 120 | 121 | cve_hashmap_iter_init(map, &iter); 122 | while (cve_hashmap_iter_next(&iter, (void **)&key, (void **)&value)) { 123 | fail_if(UNHASH_KEY(key) != UNHASH_VALUE(value), "Mismatched key/value pair in iteration"); 124 | ++count; 125 | } 126 | fail_if(count != 5000, "Did not iterate all hashmap elements"); 127 | 128 | count = 0; 129 | for (int i = 2000; i < 4000; i++) { 130 | b = cve_hashmap_remove(map, HASH_KEY(i)); 131 | fail_if(!b, "Failed to remove known integer key from hashmap"); 132 | } 133 | 134 | cve_hashmap_iter_init(map, &iter); 135 | key = value = NULL; 136 | while (cve_hashmap_iter_next(&iter, &key, &value)) { 137 | unsigned int k = UNHASH_KEY(key); 138 | fail_if(UNHASH_VALUE(value) != k, "Mismatched post-removal key/value pair in iteration"); 139 | fail_if(k >= 2000 && k < 4000, "Key/value not removed from hashtable"); 140 | ++count; 141 | } 142 | fail_if(count != 3000, "Did not iterate all 2000 elements"); 143 | fail_if(cve_hashmap_size(map) != 3000, "Invalid hashmap size after removals"); 144 | 145 | cve_hashmap_free(map); 146 | } 147 | END_TEST 148 | 149 | static int free_count = 0; 150 | 151 | static inline void free_helper(void *p) 152 | { 153 | free((void *)p); 154 | ++free_count; 155 | } 156 | 157 | START_TEST(cve_hashmap_alloc_check) 158 | { 159 | CveHashmap *map = NULL; 160 | char *str = NULL; 161 | void *val = NULL; 162 | bool b; 163 | 164 | map = cve_hashmap_new_full(string_hash, string_compare, free_helper, NULL); 165 | fail_if(!map, "Failed to allocate new hashmap"); 166 | 167 | fail_if(!(str = strdup("Key 1"), "Allocation problem")); 168 | fail_if(!cve_hashmap_put(map, str, str), "Failed to insert into hashmap"); 169 | 170 | fail_if(!(str = strdup("Key 2"), "Allocation problem")); 171 | fail_if(!cve_hashmap_put(map, str, str), "Failed to insert into hashmap"); 172 | 173 | val = cve_hashmap_get(map, "Key 1"); 174 | fail_if(!val, "Failed to get known value from hashmap"); 175 | fail_if(strcmp(val, "Key 1") != 0, "Key 1 does not match"); 176 | val = NULL; 177 | 178 | val = cve_hashmap_get(map, "Key 2"); 179 | fail_if(!val, "Failed to get known value from hashmap"); 180 | fail_if(strcmp(val, "Key 2") != 0, "Key 2 does not match"); 181 | 182 | b = cve_hashmap_remove(map, "Key 2"); 183 | fail_if(!b, "Failed to remove known key from hashmap"); 184 | fail_if(free_count != 1, "Failed to free element from hashmap"); 185 | 186 | cve_hashmap_free(map); 187 | fail_if(free_count != 2, "Failed to free last element from hashmap"); 188 | } 189 | END_TEST 190 | 191 | static Suite *cve_hashmap_suite(void) 192 | { 193 | Suite *s = NULL; 194 | TCase *tc = NULL; 195 | 196 | s = suite_create("cve_array"); 197 | tc = tcase_create("cve_hashmap_functions"); 198 | tcase_add_test(tc, cve_hashmap_new_check); 199 | tcase_add_test(tc, cve_hashmap_simple_check); 200 | tcase_add_test(tc, cve_hashmap_string_check); 201 | tcase_add_test(tc, cve_hashmap_iter_check); 202 | tcase_add_test(tc, cve_hashmap_alloc_check); 203 | suite_add_tcase(s, tc); 204 | 205 | return s; 206 | } 207 | 208 | int main(void) 209 | { 210 | int number_failed; 211 | Suite *s; 212 | SRunner *sr; 213 | 214 | s = cve_hashmap_suite(); 215 | sr = srunner_create(s); 216 | srunner_run_all(sr, CK_VERBOSE); 217 | number_failed = srunner_ntests_failed(sr); 218 | srunner_free(sr); 219 | 220 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 221 | } 222 | 223 | /* 224 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 225 | * 226 | * Local variables: 227 | * c-basic-offset: 8 228 | * tab-width: 8 229 | * indent-tabs-mode: nil 230 | * End: 231 | * 232 | * vi: set shiftwidth=8 tabstop=8 expandtab: 233 | * :indentSize=8:tabSize=8:noTabs=true: 234 | */ 235 | -------------------------------------------------------------------------------- /tests/check-template.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of cve-check-tool 3 | * Copyright (C) 2015 Intel Corporation 4 | * 5 | * cve-check-tool is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | */ 10 | 11 | #define _GNU_SOURCE 12 | #include 13 | #include 14 | #include 15 | 16 | #include "cve-string.c" 17 | #include "hashmap.c" 18 | #include "template.c" 19 | 20 | #include "config.h" 21 | 22 | START_TEST(cve_template_basic) 23 | { 24 | cve_string *ret = NULL; 25 | GHashTable *table = NULL; 26 | 27 | const char *unchanged = "I have no {{hashtable}}"; 28 | ret = template_string(unchanged, NULL); 29 | fail_if(!ret, "No return string"); 30 | fail_if(!cve_string_const_equal(ret, unchanged), "Differing string for no hashtable"); 31 | cve_string_free(ret); 32 | 33 | table = g_hash_table_new(g_str_hash, g_str_equal); 34 | fail_if(!table, "Unable to allocate table"); 35 | 36 | g_hash_table_insert(table, "key1", "Frodo"); 37 | g_hash_table_insert(table, "key2", " Baggins"); 38 | g_hash_table_insert(table, "key3", "Hobbit"); 39 | g_hash_table_insert(table, "location", "Mordor"); 40 | 41 | const char *str = 42 | "{{key1}}{{key2}} was a curious {{key3}}. With hindsight it is likely he would have avoided {{location}}."; 43 | const char *exp = 44 | "Frodo Baggins was a curious Hobbit. With hindsight it is likely he would have avoided Mordor."; 45 | ret = template_string(str, table); 46 | fail_if(!ret, "Unable to allocate string"); 47 | fail_if(!cve_string_const_equal(ret, exp), "Built string does not match expected template output"); 48 | cve_string_free(ret); 49 | 50 | g_hash_table_unref(table); 51 | } 52 | END_TEST 53 | 54 | START_TEST(cve_template_context) 55 | { 56 | TemplateContext *top = NULL; 57 | TemplateContext *child = NULL; 58 | cve_string *ret = NULL; 59 | 60 | top = template_context_new(); 61 | fail_if(!top, "Failed to allocate TemplateContext"); 62 | template_context_add_string(top, "name", "correctname"); 63 | 64 | child = template_context_new(); 65 | fail_if(!child, "Failed to allocate TemplateContext"); 66 | template_context_add_string(child, "number", "42"); 67 | template_context_add_subcontext(top, "section", child); 68 | 69 | const char *input = 70 | "The name is {{name}}{{#norender}}Output{{/norender}}{{#section}} {{name}} {{number}}{{/section}}"; 71 | const char *exp = "The name is correctname correctname 42"; 72 | 73 | ret = template_context_process_line(top, input, false); 74 | fail_if(!ret, "Failed to get return fro template_context_process_line"); 75 | fail_if(!cve_string_const_equal(ret, exp), "Returned context string does not match expected template output"); 76 | cve_string_free(ret); 77 | 78 | /* HTMLish */ 79 | input = "table a:visited { color: #999999 ; }"; 80 | ret = template_context_process_line(top, input, false); 81 | fail_if(!cve_string_const_equal(ret, input), 82 | "Returned context string does not match expected Style template output"); 83 | cve_string_free(ret); 84 | 85 | template_context_free(top); 86 | 87 | /* bool checks */ 88 | top = template_context_new(); 89 | input = 90 | "{{#bool1}}Hello Universe{{/bool1}}{{#bool2}}Hello World{{/bool2}}{{#ignored}}Should not emit{{/ignored}}"; 91 | exp = "Hello World"; 92 | template_context_add_bool(top, "bool1", false); 93 | template_context_add_bool(top, "bool2", true); 94 | ret = template_context_process_line(top, input, false); 95 | fail_if(!cve_string_const_equal(ret, exp), "Return context (boolean) string does not match expected output"); 96 | cve_string_free(ret); 97 | 98 | template_context_free(top); 99 | } 100 | END_TEST 101 | 102 | START_TEST(cve_template_fails) 103 | { 104 | cve_string *ret = NULL; 105 | GHashTable *table = NULL; 106 | 107 | table = g_hash_table_new(g_str_hash, g_str_equal); 108 | fail_if(!table, "Unable to allocate table"); 109 | 110 | g_hash_table_insert(table, "failure", "onoes"); 111 | 112 | const char *msg = "This is {{failure 1"; 113 | /* Expected this will be stripped */ 114 | const char *exp = "This is {{failure 1"; 115 | ret = template_string(msg, table); 116 | fail_if(!ret, "Unable to allocate string"); 117 | fail_if(!cve_string_const_equal(ret, exp), "Differing return string"); 118 | cve_string_free(ret); 119 | 120 | msg = "This is {{failure }2"; 121 | exp = "This is {{failure }2"; 122 | ret = template_string(msg, table); 123 | fail_if(!ret, "Unable to allocate string"); 124 | fail_if(!cve_string_const_equal(ret, exp), "Differing return string #2"); 125 | cve_string_free(ret); 126 | 127 | msg = "This is {{failure}} {{test}}!"; 128 | exp = "This is onoes !"; 129 | ret = template_string(msg, table); 130 | fail_if(!ret, "Unable to allocate string"); 131 | fail_if(!cve_string_const_equal(ret, exp), "Unknown value not collapsed"); 132 | cve_string_free(ret); 133 | 134 | g_hash_table_unref(table); 135 | } 136 | END_TEST 137 | 138 | START_TEST(cve_template_list) 139 | { 140 | TemplateContext *top = NULL; 141 | TemplateContext *node = NULL; 142 | const char *exp = NULL; 143 | cve_string *ret = NULL; 144 | 145 | top = template_context_new(); 146 | fail_if(!top, "Failed to allocate TemplateContext"); 147 | 148 | node = template_context_new(); 149 | template_context_add_string(node, "key1", "val1"); 150 | fail_if(!template_context_add_list(top, "list", node), "Failed to add node to list"); 151 | 152 | node = template_context_new(); 153 | template_context_add_string(node, "key1", "val2"); 154 | fail_if(!template_context_add_list(top, "list", node), "Failed to add node to list"); 155 | 156 | node = template_context_new(); 157 | template_context_add_string(node, "key1", "val3"); 158 | fail_if(!template_context_add_list(top, "list", node), "Failed to add node to list"); 159 | 160 | exp = "list items: val1 val2 val3 "; 161 | ret = template_context_process_line(top, "list items: {{#list}}{{key1}} {{/list}}", false); 162 | fail_if(!ret, "No return from template_context_process_line"); 163 | fail_if(!cve_string_const_equal(ret, exp), "Returned list string does not match expected output"); 164 | cve_string_free(ret); 165 | 166 | template_context_free(top); 167 | } 168 | END_TEST 169 | 170 | START_TEST(cve_template_bool) 171 | { 172 | TemplateContext *top = NULL; 173 | const char *exp = NULL; 174 | cve_string *ret = NULL; 175 | 176 | top = template_context_new(); 177 | fail_if(!top, "Failed to allocate TemplateContext"); 178 | 179 | template_context_add_bool(top, "conditiontrue", true); 180 | template_context_add_string(top, "name", "Frodo"); 181 | 182 | exp = "His name was Frodo: true"; 183 | ret = template_context_process_line( 184 | top, 185 | "His name was {{#conditiontrue}}{{name}}{{/conditiontrue}}{{#nocond}}Jimbob{{/nocond}}: {{conditiontrue}}", 186 | false); 187 | fail_if(!ret, "No return from template_context_process_line"); 188 | fail_if(!cve_string_const_equal(ret, exp), "Returned bool-test string does not match expected output"); 189 | cve_string_free(ret); 190 | 191 | template_context_free(top); 192 | } 193 | END_TEST 194 | static Suite *core_suite(void) 195 | { 196 | Suite *s = NULL; 197 | TCase *tc = NULL; 198 | 199 | s = suite_create("cve_template"); 200 | tc = tcase_create("cve_template_functions"); 201 | tcase_add_test(tc, cve_template_basic); 202 | tcase_add_test(tc, cve_template_fails); 203 | tcase_add_test(tc, cve_template_context); 204 | tcase_add_test(tc, cve_template_list); 205 | tcase_add_test(tc, cve_template_bool); 206 | suite_add_tcase(s, tc); 207 | 208 | return s; 209 | } 210 | 211 | int main(void) 212 | { 213 | Suite *s; 214 | SRunner *sr; 215 | int fail; 216 | 217 | s = core_suite(); 218 | sr = srunner_create(s); 219 | srunner_run_all(sr, CK_VERBOSE); 220 | fail = srunner_ntests_failed(sr); 221 | srunner_free(sr); 222 | 223 | if (fail > 0) { 224 | return EXIT_FAILURE; 225 | } 226 | 227 | return EXIT_SUCCESS; 228 | } 229 | 230 | /* 231 | * Editor modelines - https://www.wireshark.org/tools/modelines.html 232 | * 233 | * Local variables: 234 | * c-basic-offset: 8 235 | * tab-width: 8 236 | * indent-tabs-mode: nil 237 | * End: 238 | * 239 | * vi: set shiftwidth=8 tabstop=8 expandtab: 240 | * :indentSize=8:tabSize=8:noTabs=true: 241 | */ 242 | --------------------------------------------------------------------------------