diff --git a/.gitignore b/.gitignore index e9830578d..e8e188a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ src/analysis/signatures/ src/plugins/*/*/bin src/plugins/analysis/crypto_hints/signatures/crypto_signatures.yar src/plugins/analysis/cve_lookup/internal/database/cve_cpe.db +src/plugins/analysis/cve_lookup/internal/database/version.json src/plugins/analysis/qemu_exec/test/data/test_tmp_dir src/plugins/analysis/qemu_exec/test/data/test_tmp_dir_2 src/plugins/analysis/users_and_passwords/internal/passwords/10k-most-common.txt diff --git a/src/helperFunctions/fileSystem.py b/src/helperFunctions/fileSystem.py index 35572b0e7..26fd5c40f 100644 --- a/src/helperFunctions/fileSystem.py +++ b/src/helperFunctions/fileSystem.py @@ -21,6 +21,15 @@ def get_template_dir() -> Path: return Path(get_src_dir()) / 'web_interface' / 'templates' +def get_bin_dir() -> Path: + """ + Retrieves the absolute path of the bin directory. + + :return: The (absolute) path of the bin directory. + """ + return Path(get_src_dir()) / 'bin' + + def get_relative_object_path(path: Path, offset_path: Path) -> str: """ FACT extraction unpacks files into a temporary directory. These files have to be offset to get the path relative diff --git a/src/install/common.py b/src/install/common.py index c1c5ec5c3..06eb20cec 100644 --- a/src/install/common.py +++ b/src/install/common.py @@ -1,3 +1,4 @@ +import json import logging import subprocess from contextlib import suppress @@ -85,6 +86,7 @@ def _install_fw_magic(version: str = 'v0.2.2'): # compile the magic files (results in .mgc suffix) so that we don't get warnings when using them run_cmd_with_logging('file -C -m firmware') run_cmd_with_logging('file -C -m internal_symlink_magic') + Path('fw_magic_version.json').write_text(json.dumps({'version': version.lstrip('v')})) def _update_submodules(): diff --git a/src/plugins/analysis/cve_lookup/code/cve_lookup.py b/src/plugins/analysis/cve_lookup/code/cve_lookup.py index 139b73712..c58c9e6c2 100644 --- a/src/plugins/analysis/cve_lookup/code/cve_lookup.py +++ b/src/plugins/analysis/cve_lookup/code/cve_lookup.py @@ -1,6 +1,8 @@ from __future__ import annotations +import json import sys +from contextlib import suppress from pathlib import Path from typing import TYPE_CHECKING @@ -20,7 +22,9 @@ from database.db_connection import DbConnection from lookup import Lookup -DB_PATH = str(Path(__file__).parent / '../internal/database/cve_cpe.db') +DB_DIR = Path(__file__).parent.parent / 'internal/database' +DB_PATH = str(DB_DIR / 'cve_cpe.db') +VERSION_PATH = DB_DIR / 'version.json' class AnalysisPlugin(AnalysisBasePlugin): @@ -32,10 +36,12 @@ class AnalysisPlugin(AnalysisBasePlugin): DESCRIPTION = 'lookup CVE vulnerabilities' MIME_BLACKLIST = MIME_BLACKLIST_NON_EXECUTABLE DEPENDENCIES = ['software_components'] # noqa: RUF012 - VERSION = '0.2.0' + VERSION = '0.2.1' FILE = __file__ def additional_setup(self): + with suppress(json.JSONDecodeError, FileNotFoundError): + self.SYSTEM_VERSION = json.loads(VERSION_PATH.read_text()).get('version') self.min_crit_score = getattr(config.backend.plugin.get(self.NAME, {}), 'min-critical-score', 9.0) def process_object(self, file_object: FileObject) -> FileObject: diff --git a/src/plugins/analysis/cve_lookup/internal/data_parsing.py b/src/plugins/analysis/cve_lookup/internal/data_parsing.py index c743226a9..8531fb101 100644 --- a/src/plugins/analysis/cve_lookup/internal/data_parsing.py +++ b/src/plugins/analysis/cve_lookup/internal/data_parsing.py @@ -1,8 +1,11 @@ from __future__ import annotations +import datetime import json import lzma import re +from http import HTTPStatus +from pathlib import Path from typing import TYPE_CHECKING import requests @@ -14,7 +17,10 @@ from ..internal.helper_functions import CveEntry FILE_NAME = 'CVE-all.json.xz' -CVE_URL = f'https://github.com/fkie-cad/nvd-json-data-feeds/releases/latest/download/{FILE_NAME}' +VERSION_FILE = Path(__file__).parent / 'database' / 'version.json' +REPO = 'fkie-cad/nvd-json-data-feeds' +CVE_URL = f'https://github.com/{REPO}/releases/latest/download/{FILE_NAME}' +API_URL = f'https://api.github.com/repos/{REPO}/releases/latest' def _retrieve_url(download_url: str) -> Response: @@ -24,6 +30,22 @@ def _retrieve_url(download_url: str) -> Response: return session.get(download_url) +def _retrieve_latest_version() -> str | None: + response = requests.get(API_URL) + if response.status_code == HTTPStatus.OK: + data = response.json() + return data['tag_name'] + return None + + +def _store_release_data(): + data = { + 'version': _retrieve_latest_version(), + 'last_updated': datetime.datetime.now().isoformat(), + } + Path(VERSION_FILE).write_text(json.dumps(data)) + + def download_and_decompress_data() -> bytes: """ Downloads data from a URL, saves it to a file, decompresses it, and returns the decompressed data. @@ -93,3 +115,4 @@ def parse_data() -> list[CveEntry]: if __name__ == '__main__': parse_data() + _store_release_data() diff --git a/src/plugins/analysis/file_type/code/file_type.py b/src/plugins/analysis/file_type/code/file_type.py index 09fa0cd97..439988017 100644 --- a/src/plugins/analysis/file_type/code/file_type.py +++ b/src/plugins/analysis/file_type/code/file_type.py @@ -1,14 +1,18 @@ from __future__ import annotations +import json import typing +from pathlib import Path from typing import List import pydantic from pydantic import Field +from semver import Version from analysis.plugin import AnalysisPluginV0 from analysis.plugin.compat import AnalysisBasePluginAdapterMixin from helperFunctions import magic +from helperFunctions.fileSystem import get_bin_dir if typing.TYPE_CHECKING: import io @@ -24,11 +28,17 @@ class Schema(pydantic.BaseModel): ) def __init__(self): + try: + version_file = Path(get_bin_dir()) / 'version.json' + fw_magic_db_version = json.loads(version_file.read_text()).get('version') + except (json.JSONDecodeError, FileNotFoundError): + fw_magic_db_version = None super().__init__( metadata=AnalysisPluginV0.MetaData( name='file_type', description='identify the file type', - version='1.0.0', + version=Version(1, 0, 1), + system_version=fw_magic_db_version, Schema=AnalysisPlugin.Schema, ), ) diff --git a/src/test/unit/helperFunctions/test_file_system.py b/src/test/unit/helperFunctions/test_file_system.py index c83b08ce9..ac6b78288 100644 --- a/src/test/unit/helperFunctions/test_file_system.py +++ b/src/test/unit/helperFunctions/test_file_system.py @@ -5,6 +5,7 @@ from helperFunctions.fileSystem import ( file_is_empty, + get_bin_dir, get_config_dir, get_relative_object_path, get_src_dir, @@ -39,6 +40,11 @@ def test_get_template_dir(): assert '.html' in file_suffixes_in_template_dir +def test_get_bin_dir(): + bin_dir = get_bin_dir() + assert bin_dir.is_dir(), 'bin dir not found' + + @pytest.mark.parametrize( ('base', 'offset', 'result', 'message'), [