aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGeorgeFI2023-02-20 15:33:46 +0100
committerGeorgeFI2023-02-20 15:33:46 +0100
commit21ee2d787fa6279acc6672fbe223717b928aa039 (patch)
treee17206b354b272dcc2e1a36233c417204297b2fe
parent599734647e9595e00f67349c8d4b42cb603f306c (diff)
parentc0084cff4161df63d6b372f0ceda4d12b5bbb787 (diff)
downloadsec-certs-21ee2d787fa6279acc6672fbe223717b928aa039.tar.gz
sec-certs-21ee2d787fa6279acc6672fbe223717b928aa039.tar.zst
sec-certs-21ee2d787fa6279acc6672fbe223717b928aa039.zip
merge: merged main into feature branch
-rw-r--r--.flake820
-rw-r--r--.github/workflows/lint.yml66
-rw-r--r--.gitignore8
-rw-r--r--.pre-commit-config.yaml23
-rw-r--r--CONTRIBUTING.md14
-rw-r--r--README.md12
-rw-r--r--data/reference_annotations_split/test.json780
-rw-r--r--data/reference_annotations_split/train.json466
-rw-r--r--data/reference_annotations_split/valid.json312
-rw-r--r--docs/_static/logo.pngbin0 -> 41751 bytes
-rw-r--r--docs/_static/logo_badge.pngbin0 -> 18611 bytes
-rw-r--r--docs/_static/logolink_OP_VVV_hor_barva_eng.jpgbin0 -> 363422 bytes
-rw-r--r--docs/conf.py8
-rw-r--r--docs/index.md7
-rw-r--r--notebooks/cc/reference_annotations/train_validation_test_split.ipynb81
-rw-r--r--pyproject.toml52
-rw-r--r--requirements/dev_requirements.txt40
-rw-r--r--requirements/requirements.txt14
-rw-r--r--requirements/test_requirements.txt14
-rw-r--r--src/sec_certs/cert_rules.py20
-rw-r--r--src/sec_certs/cli.py119
-rw-r--r--src/sec_certs/config/settings-schema.json2
-rw-r--r--src/sec_certs/config/settings.yaml4
-rw-r--r--src/sec_certs/constants.py5
-rw-r--r--src/sec_certs/dataset/__init__.py2
-rw-r--r--src/sec_certs/dataset/cc.py108
-rw-r--r--src/sec_certs/dataset/cpe.py18
-rw-r--r--src/sec_certs/dataset/cve.py14
-rw-r--r--src/sec_certs/dataset/dataset.py101
-rw-r--r--src/sec_certs/dataset/fips.py49
-rw-r--r--src/sec_certs/dataset/fips_algorithm.py12
-rw-r--r--src/sec_certs/dataset/protection_profile.py2
-rw-r--r--src/sec_certs/model/cpe_matching.py23
-rw-r--r--src/sec_certs/model/evaluation.py2
-rw-r--r--src/sec_certs/model/reference_finder.py2
-rw-r--r--src/sec_certs/model/sar_transformer.py11
-rw-r--r--src/sec_certs/model/transitive_vulnerability_finder.py2
-rw-r--r--src/sec_certs/sample/cc.py78
-rw-r--r--src/sec_certs/sample/cc_maintenance_update.py2
-rw-r--r--src/sec_certs/sample/certificate.py3
-rw-r--r--src/sec_certs/sample/fips.py96
-rw-r--r--src/sec_certs/sample/fips_iut.py5
-rw-r--r--src/sec_certs/sample/fips_mip.py20
-rw-r--r--src/sec_certs/sample/protection_profile.py2
-rw-r--r--src/sec_certs/sample/sar.py2
-rw-r--r--src/sec_certs/serialization/json.py16
-rw-r--r--src/sec_certs/utils/extract.py51
-rw-r--r--src/sec_certs/utils/helpers.py22
-rw-r--r--src/sec_certs/utils/pandas.py2
-rw-r--r--src/sec_certs/utils/parallel_processing.py7
-rw-r--r--src/sec_certs/utils/pdf.py38
-rw-r--r--src/sec_certs/utils/sanitization.py4
-rw-r--r--tests/cc/test_cc_analysis.py6
-rw-r--r--tests/cc/test_cc_certificate.py2
-rw-r--r--tests/cc/test_cc_dataset.py2
-rw-r--r--tests/cc/test_cc_maintenance_updates.py10
-rw-r--r--tests/data/cc/analysis/auxiliary_datasets/cpe_dataset.json (renamed from tests/data/cc/analysis/auxillary_datasets/cpe_dataset.json)0
-rw-r--r--tests/data/cc/analysis/auxiliary_datasets/cve_dataset.json (renamed from tests/data/cc/analysis/auxillary_datasets/cve_dataset.json)0
-rw-r--r--tests/data/cc/analysis/auxiliary_datasets/cve_dset_with_cpe_configs.json (renamed from tests/data/cc/analysis/auxillary_datasets/cve_dset_with_cpe_configs.json)0
-rw-r--r--tests/data/cc/analysis/cc_full_dataset.json2
-rw-r--r--tests/data/cc/analysis/vulnerable_dataset.json2
-rw-r--r--tests/data/cc/dataset/auxiliary_datasets/maintenances/maintenance_updates.json (renamed from tests/data/cc/dataset/auxillary_datasets/maintenances/maintenance_updates.json)2
-rw-r--r--tests/data/cc/dataset/toy_dataset.json2
-rw-r--r--tests/data/fips/certificate/fictional_cert.json2
-rw-r--r--tests/data/fips/dataset/toy_dataset.json46
-rw-r--r--tests/fips/test_fips_algorithm_dataset.py2
-rw-r--r--tests/fips/test_fips_analysis.py8
-rw-r--r--tests/fips/test_fips_certificate.py2
-rw-r--r--tests/fips/test_fips_dataset.py5
-rw-r--r--tests/fips/test_fips_iut.py2
-rw-r--r--tests/fips/test_fips_mip.py2
-rw-r--r--tests/test_common.py2
-rw-r--r--tests/test_cpe.py8
-rw-r--r--tests/test_cve.py4
74 files changed, 2245 insertions, 627 deletions
diff --git a/.flake8 b/.flake8
deleted file mode 100644
index 2d24fa82..00000000
--- a/.flake8
+++ /dev/null
@@ -1,20 +0,0 @@
-[flake8]
-max-line-length = 120
-exclude =
- .git,
- __pycache__,
- build,
- dist,
- venv,
- certsvenv,
- .eggs,
- scratches,
-max-complexity = 10
-
-ignore =
- # line length, should be handleded by black
- E501,
- # line break before binary operator, depracated
- W503,
- # whitespace before :, not PEP8 compliant
- E203,
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 95de7f7b..9cb2236f 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,66 +1,44 @@
-name: Lint (MyPy, Black, isort, Flake8)
+name: Lint (MyPy, Black, Ruff)
on:
push:
workflow_dispatch:
jobs:
- mypy:
+ black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - name: Setup python
- uses: actions/setup-python@v4
+ - uses: actions/setup-python@v4
with:
- python-version: "3.9"
- - name: Install external dependencies
- run: sudo apt-get install build-essential libpoppler-cpp-dev pkg-config python3-dev -y
- - name: Install python dependencies
+ python-version: "3.8"
+ - name: Install dependencies
run: |
- pip install -r requirements/requirements.txt
+ sudo apt-get install build-essential libpoppler-cpp-dev pkg-config python3-dev -y
pip install -r requirements/dev_requirements.txt
- - name: Run mypy
- run: mypy .
- black:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: psf/black@stable
- isort:
+ - name: Run Black
+ run: black . --check --target-version py38
+ ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
- python-version: 3.8
- - uses: isort/isort-action@master
- with:
- requirementsFiles: "requirements/requirements.txt requirements/dev_requirements.txt"
- pyupgrade:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - name: Setup python
- uses: actions/setup-python@v4
- with:
python-version: "3.8"
- - name: Install external dependencies
- run: sudo apt-get install build-essential libpoppler-cpp-dev pkg-config python3-dev -y
- - name: Install python dependencies
+ - name: Install dependencies
run: |
- pip install -r requirements/requirements.txt
+ sudo apt-get install build-essential libpoppler-cpp-dev pkg-config python3-dev -y
pip install -r requirements/dev_requirements.txt
- - name: Run pyupgrade
- run: pre-commit run pyupgrade --all-files
- flake8-lint:
+ - name: Run Ruff
+ run: ruff . --format=github
+ mypy:
runs-on: ubuntu-latest
- name: Flake8
steps:
- - name: Check out source repository
- uses: actions/checkout@v3
- - name: Set up Python environment
- uses: actions/setup-python@v4
+ - uses: actions/checkout@v3
+ - uses: actions/setup-python@v4
with:
python-version: "3.8"
- - name: flake8 Lint
- uses: py-actions/flake8@v2
- with:
- plugins: "flake8-future-annotations"
+ - name: Install dependencies
+ run: |
+ sudo apt-get install build-essential libpoppler-cpp-dev pkg-config python3-dev -y
+ pip install -r requirements/dev_requirements.txt
+ - name: Run Mypy
+ run: mypy .
diff --git a/.gitignore b/.gitignore
index d64264b0..42b98cd1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,9 +116,15 @@ virt/
# mypy
.mypy_cache/
+# ruff
+.ruff_cache
+
# log
cert_processing_log.txt
./cc_processing_log.txt
# Experiment results produced by notebooks
-notebooks/cc/results/ \ No newline at end of file
+notebooks/cc/results/
+
+# Default directory for dataset
+/dataset
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7e4da57e..7725ce34 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,21 +1,16 @@
repos:
- - repo: https://github.com/asottile/pyupgrade
- rev: v3.2.3
- hooks:
- - id: pyupgrade
- args: ["--py38-plus"]
- repo: https://github.com/psf/black
- rev: 22.6.0
+ rev: 23.1.0
hooks:
- id: black
args: ["--check", "--target-version", "py38"]
- - repo: https://github.com/pycqa/isort
- rev: 5.10.1
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
+ # Ruff version.
+ rev: "v0.0.239"
hooks:
- - id: isort
- args: ["--check-only"]
+ - id: ruff
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: "v0.982"
+ rev: "v1.0.0"
hooks:
- id: mypy
additional_dependencies:
@@ -23,9 +18,3 @@ repos:
- "types-PyYAML"
- "types-python-dateutil"
- "types-requests"
- - repo: https://github.com/pycqa/flake8
- rev: "4.0.1"
- hooks:
- - id: flake8
- additional_dependencies:
- - "flake8-future-annotations"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dd00667f..32f2f0ab 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,8 +33,9 @@ Note on single-sourcing the package version: More can be read [here](https://pac
### Currently, the release process is as follows
-1. Create a release from GitHub UI. Include release notes, add proper version tag and publish the release (or create it from scratch with new tag).
-2. This will automatically update PyPi and DockerHub packages.
+1. Update dependencies with `pre-commit autoupdate`, pin new versions of linters into `pyproject.toml` and run `cd requirements && ./compile.sh`.
+2. Create a release from GitHub UI. Include release notes, add proper version tag and publish the release (or create it from scratch with new tag).
+3. This will automatically update PyPi and DockerHub packages.
## Quality assurance
@@ -43,11 +44,10 @@ All commits shall pass the lint pipeline of the following tools:
- Mypy (see [pyproject.toml](https://github.com/crocs-muni/sec-certs/blob/main/pyproject.toml) for settings)
- Black (see [pyproject.toml](https://github.com/crocs-muni/sec-certs/blob/main/pyproject.toml) for settings)
-- isort (see [pyproject.toml](https://github.com/crocs-muni/sec-certs/blob/main/pyproject.toml) for settings)
-- Flake8 (see [.flake8](https://github.com/crocs-muni/sec-certs/blob/main/.flake8) for settings)
+- Ruff (see [pyproject.toml](https://github.com/crocs-muni/sec-certs/blob/main/pyproject.toml) for settings)
- PyUpgrade
-These tools can be installed via [dev_requirements.txt](https://github.com/crocs-muni/sec-certs/blob/main/dev_requirements.txt) You can use [pre-commit](https://pre-commit.com/) tool register git hook that will evalute these checks prior to any commit and abort the commit for you. Note that the pre-commit is not meant to automatically fix the issues, just warn you.
+These tools can be installed via [dev_requirements.txt](https://github.com/crocs-muni/sec-certs/blob/main/dev_requirements.txt) You can use [pre-commit](https://pre-commit.com/) tool to register git hook that will evalute these checks prior to any commit and abort the commit for you. Note that the pre-commit is not meant to automatically fix the issues, just warn you.
It should thus suffice to:
@@ -60,9 +60,7 @@ pre-commit run --all-files
To ivoke the tools manually, you can, in the repository root, use:
- Mypy: `mypy .`
- Black: `black --check .` (without the flag to reformat)
-- isort: `isort --check-only .` (without the flag to actually fix the issue)
-- Flake8: `flake8 .`
-- PyUpgrade: `pyupgrade --py38-plus 'find ./sec_certs/ -name "*.py" -type f'`
+- Ruff: `ruff ." (or with `--fix` flag to apply fixes)
## Documentation
diff --git a/README.md b/README.md
index a02348f1..bf6a58ea 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,15 @@
# Sec-certs
-![](docs/_static/logo.svg)
+![](docs/_static/logo.png)
A tool for data scraping and analysis of security certificates from Common Criteria and FIPS 140-2/3 frameworks. This project is developed by the [Centre for Research On Cryptography and Security](https://crocs.fi.muni.cz) at Masaryk University, Czech Republic.
[![Website](https://img.shields.io/website?down_color=red&down_message=offline&style=flat-square&up_color=SpringGreen&up_message=online&url=https%3A%2F%2Fseccerts.org)](https://seccerts.org)
[![Website](https://img.shields.io/website?label=docs&down_color=red&down_message=offline&style=flat-square&up_color=SpringGreen&up_message=online&url=https%3A%2F%2Fseccerts.org/docs/index.html)](https://seccerts.org/docs/index.html)
[![PyPI](https://img.shields.io/pypi/v/sec-certs?style=flat-square)](https://pypi.org/project/sec-certs/)
+[![DockerHub](https://img.shields.io/docker/v/seccerts/sec-certs/latest?label=DockerHub&style=flat-square)](https://hub.docker.com/r/seccerts/sec-certs/tags)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sec-certs?label=Python%20versions&style=flat-square)](https://pypi.org/project/sec-certs/)
-[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/crocs-muni/sec-certs/tests?label=tests&style=flat-square)](https://github.com/crocs-muni/sec-certs/actions/workflows/tests.yml)
-[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/crocs-muni/sec-certs/Docker%20Image%20CI?label=Docker%20build&style=flat-square)](https://hub.docker.com/repository/docker/seccerts/sec-certs)
+[![Tests](https://img.shields.io/github/actions/workflow/status/crocs-muni/sec-certs/.github/workflows/tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/crocs-muni/sec-certs/actions/workflows/tests.yml?query=branch%3Amain)
[![Codecov](https://img.shields.io/codecov/c/github/crocs-muni/sec-certs?style=flat-square)](https://app.codecov.io/gh/crocs-muni/sec-certs)
## Installation
@@ -57,3 +57,9 @@ df_2015_and_newer = df.loc[df.year_from > 2014]
# Plot distribution of years of certification
df.year_from.value_counts().sort_index().plot.line()
```
+
+## Authors
+
+This work is being done at [CRoCS MUNI](https://crocs.fi.muni.cz/) by Adam Janovsky, Jan Jancar, Petr Svenda, Jiri Michalik, Lukasz Chmielewski and other contributors. This work was supported by the Internal grant agency of Masaryk University, CZ.02.2.69/0.0/0.0/19_073/0016943.
+
+![](docs/_static/logolink_OP_VVV_hor_barva_eng.jpg) \ No newline at end of file
diff --git a/data/reference_annotations_split/test.json b/data/reference_annotations_split/test.json
new file mode 100644
index 00000000..41ac06ee
--- /dev/null
+++ b/data/reference_annotations_split/test.json
@@ -0,0 +1,780 @@
+[
+ "11a98c5d5ff6f40f",
+ "6dde54b8c6d592ef",
+ "054bd8196e015197",
+ "c902a788d699fe4a",
+ "5d674c06a559a5e7",
+ "9f8c9a3aa64a9f8e",
+ "9545f2b0b8b92c8d",
+ "56ceb1a9c003ac3f",
+ "9299f6cd8a266541",
+ "d1fe94ee32cbbf05",
+ "6313a92bf426d2bb",
+ "e8a37e48a8d34410",
+ "f34df0dd7011366e",
+ "c23218b044706800",
+ "3f4b6e4f245f6fab",
+ "5f99b00bf258f32f",
+ "4f3bf0c433b7f54f",
+ "587055293e9fa51d",
+ "da2d3567a814d2d7",
+ "915e919390e6f58b",
+ "a5e8267322d0d7a6",
+ "82dce1546c69369d",
+ "7d4585a4b5b6e873",
+ "736ede0289146b59",
+ "0cbcee9f1cde47a8",
+ "4bf7d6ef245dd393",
+ "3817c0aca007989e",
+ "baead73ecc6e4a33",
+ "19d3185c40be008d",
+ "8a7731e9eb363fb4",
+ "78a279b07a6d3367",
+ "47913a485c3c8a18",
+ "5f259ce91f0234a0",
+ "d4a14ea7adb375ed",
+ "994a2bf994b4facd",
+ "ac1257069b2f4afd",
+ "b5985efd378af972",
+ "bc1452fff95744b9",
+ "4f953ae00a24eb85",
+ "a35ed74b7ea3ec4b",
+ "654554952ecb20ac",
+ "157b741a0f529a0a",
+ "30ae700d801bd41c",
+ "2f9feaf4121720da",
+ "e0283686d26420d9",
+ "1b2764a62ffe86a2",
+ "b47b457f73c42c60",
+ "ff45605151ec4eb1",
+ "5346d5a4418466ab",
+ "e5b04a0a3872954f",
+ "981f7ea91d7bdcb6",
+ "f229a8078cb4ff82",
+ "731aac44217611ec",
+ "4e29916c366fe997",
+ "5efe98a1ba4df4d7",
+ "ffd213e55eee8cc8",
+ "7e24168630d5124e",
+ "00ad121fa63e5dfb",
+ "173097995b7a7f12",
+ "4b2f963b48e0f954",
+ "5e4c757231135ba1",
+ "aed7a612d966a786",
+ "1468948b4f0509ab",
+ "4d885e54f1361d04",
+ "8a5f8686ab9de19e",
+ "37d28734245a3bb1",
+ "80fe3ba3d9c9bf64",
+ "b1b385d5bad724ed",
+ "c4ca6565698f0e49",
+ "d9bfffa3cc6d1c53",
+ "d32e5d4d5ff8b871",
+ "8865aed38c79d3fc",
+ "5bf118ffa29520c0",
+ "6136acacc653106c",
+ "05d96aada7bb839c",
+ "0aad0176e35b3749",
+ "64ed7e55bdc50177",
+ "3976c9e492193315",
+ "65fba51313f65938",
+ "55e52bcaab69d573",
+ "4dd0daec78093d31",
+ "d67edade78d25033",
+ "71eae9be60b7fb6c",
+ "3d149fe7c08bfc58",
+ "1d9ae732c5dec242",
+ "6bdca9f13e19572e",
+ "d6ade0a63d0f9130",
+ "da1139ecdc3512da",
+ "637657613004dbbd",
+ "29fb07d8f74e734b",
+ "075ddd00e07fa7ed",
+ "ce92195cf4a19084",
+ "68875c05e00bd1aa",
+ "61e862ed07d3cb03",
+ "cdbbbad526f0aa13",
+ "1d1df0fb541e49b8",
+ "5d4aae047020163b",
+ "c67724216ed5a318",
+ "10229cdd92c1989f",
+ "4dbb108406acb1a5",
+ "9292ab2134e8a712",
+ "84501b4439b66fd9",
+ "35efe13fa9e93a68",
+ "5c5431cca026fb3e",
+ "e3a743ef04ecfb64",
+ "89d1f7ab8b77e41d",
+ "70908a4cfdc5585c",
+ "8035f242b12cec6d",
+ "67240adc18be338d",
+ "3e08a27e9d9c9b1e",
+ "5eb91b620bbf5bfc",
+ "7e92dcac20e009a5",
+ "49e6665ee56d3bfc",
+ "54fada9909a1edb3",
+ "8ae9a7fbbb50b6ed",
+ "61e7ec1c62704f39",
+ "f3bedaa5ffeeadb1",
+ "a3d152e25f8ce6c9",
+ "de2ce0c1b72d5c90",
+ "92ae986997c1d45c",
+ "29cc495628e76ffa",
+ "83bb8420c3cccf85",
+ "ddd9691cb063b07d",
+ "693325ca20a6325b",
+ "37b94c08f8e25249",
+ "26c7b173668167d8",
+ "60ec8738947badb0",
+ "37fe6036c2ac932b",
+ "d69309b0a27c929c",
+ "01e5c6d31f763f56",
+ "adc6971cdb0613b0",
+ "ffc97a8fff838fa8",
+ "9ac6a8bda4fb7dc4",
+ "e247b50a301ce26c",
+ "e359ab2e436adf36",
+ "723ba544431f7874",
+ "fff289a66cb9ce4b",
+ "583c12b5bf2423c6",
+ "98a578f7758bb518",
+ "10233188161519ee",
+ "123c94418ddf1ad8",
+ "0bd651fcbb86f2a9",
+ "6a46ff1d1a5154b0",
+ "a5898123ec124b34",
+ "6fe5828936663aed",
+ "5506b5232076d409",
+ "1ea31bb5f6a15995",
+ "45098872448f5816",
+ "112fe9f123e277ee",
+ "09220515a7edf3f9",
+ "114e39b4da949e6d",
+ "621335449637e335",
+ "b82b48b0e915aaac",
+ "db9267bdc717c801",
+ "195366bfba7213a5",
+ "177dee4c1051e612",
+ "dcd67613012044b3",
+ "60f0dd83c8f32b8c",
+ "bff1330c13ccd99b",
+ "c06c7eb3afa4aea0",
+ "9ae7edbd0444982d",
+ "ec21d3c76c5ef65d",
+ "449c74a92ebb61a4",
+ "23a85a7f9c07412f",
+ "7942e835fb18c4ac",
+ "51c589803c349e44",
+ "f577607298c4b574",
+ "8944c4f14bbcaa74",
+ "f1a73b2232787b26",
+ "96c904c81aa2a241",
+ "0ef56cba2362e463",
+ "b965c3b6a03aefc4",
+ "35b9be2203d2b9e6",
+ "da6845ee99cfa275",
+ "977b04d04def60a7",
+ "7d3546a4000b5b75",
+ "82a2bd8fe4ed2087",
+ "135ca3602f0005bf",
+ "95774db1eb19b767",
+ "83020f91ce1aaf68",
+ "950ba1c7368c397f",
+ "98642c6f99438194",
+ "b7209eec8fe7fc58",
+ "b21be5dd6fdec83b",
+ "60f08c85a518de1c",
+ "62eb991153211de6",
+ "f9d9fe0dd9f7f110",
+ "017f3cea8275a3e1",
+ "b644b78aafc367b0",
+ "808de32aa819821d",
+ "cc99b5d3806b1e97",
+ "d07b6e3036abb8f1",
+ "db28c1d7f201af1a",
+ "997281c99ac93b3b",
+ "a596f2412c0f8fe2",
+ "dc51cf4e51f01086",
+ "692e91451741ef49",
+ "d3d257648d4c3105",
+ "d6d129c94c368155",
+ "f9a3871824bcd47a",
+ "d063a93beba2dd65",
+ "e4dc90de190437b8",
+ "3feda0b8b5637540",
+ "8da3e1e348d36e07",
+ "d905d620783d3768",
+ "bb509db67c18bc40",
+ "902dbd430adb77a5",
+ "cb9a110799bc37f8",
+ "41bd2924e9afced5",
+ "a2cbd066271b6b3b",
+ "6587b26878da24b5",
+ "5498bfcecf19dfa3",
+ "2e997ba7beaed34d",
+ "0208e6b60a0b1a32",
+ "17bfc3d0570ab3d0",
+ "c459c68e95d54f87",
+ "0a6525cdbeb38df6",
+ "eac2041efb4cf9f7",
+ "a9f542d8e2be69fd",
+ "be9ca77bec616fe5",
+ "09f539aedefa9756",
+ "86e9da10bc176267",
+ "ad2351479ff6ee84",
+ "6b605ff743c859d0",
+ "59820117bb7fa240",
+ "f5a53fa6d2c3d9b2",
+ "9cc6c08cbaa694f2",
+ "5ee9898421c06859",
+ "93f14cad1cb201fa",
+ "183a5cd3e2aaa0ec",
+ "53bba35f7ea6edc9",
+ "6eeed6fefb1f0243",
+ "8b0204e041dd3eb8",
+ "7386d04b20f354f4",
+ "a57845715134651a",
+ "2a91f42389fda90d",
+ "f7cbb33cc639b5b5",
+ "b5b8bd0cb8bb7658",
+ "86462bcec4492bfa",
+ "75c08d873d0dcc06",
+ "6d9c2bab8cc8d28c",
+ "102bbdfcd696d7a9",
+ "bbc9a3a1d685d5f1",
+ "ce22219c2049985c",
+ "e2a45ce49bb2b731",
+ "21cffadbbb87c205",
+ "b4768c31e98b2170",
+ "16b0a0811bc4fe82",
+ "0ee132dec087b772",
+ "8bc3658bc958f81f",
+ "18b7e5bdf459ca13",
+ "f2516946c797b1a0",
+ "e56d5d27cb2b3e6f",
+ "0a6e50445a906052",
+ "33cada8c95bf55c5",
+ "ecee66b37d7576db",
+ "bc25a76284e7c7ce",
+ "e9c5d02aae54cdc5",
+ "b3855f3a6c06c01c",
+ "b24a14935edd51ad",
+ "69b877d99c360f97",
+ "f731aaaab07bdca2",
+ "7980d204dbc3ecee",
+ "8c6c4b4606608dfe",
+ "6e9496b7254e02e2",
+ "aadbb9992a672a1b",
+ "ca1fa7c3f26bb3c5",
+ "27ccec0740bb7915",
+ "ee4e169753085913",
+ "ea4f0c0a7b82f526",
+ "16f1a518cd842fcb",
+ "83c1ee8ab43ea00a",
+ "76d52f916b4222b1",
+ "4d2a177384b23fd6",
+ "43a4ca62d0c0b0da",
+ "e23f9c02819688f6",
+ "16b24ff1bf3c079b",
+ "cd72fc2b62780a43",
+ "6eacef3b2f976ce9",
+ "fbdaf22c4f50391c",
+ "107c348f4ba0a41c",
+ "fbe41655a05a01ff",
+ "39c07ebbca541145",
+ "173704f0d2b8a02f",
+ "096b05114ee5e09f",
+ "a289b9d43310182b",
+ "cc6918e95b803734",
+ "5f84cd800ee96468",
+ "07ce7e4844f62c5c",
+ "22ede463dbf1a105",
+ "8ee7145a1b48b578",
+ "85fa17a19251a2ea",
+ "58ece75f7f2b5099",
+ "420876ec1e5ba657",
+ "2d0b4100c3ead88a",
+ "42f53e51476f1a3c",
+ "96c685a3cdd9ed59",
+ "eacc2cf0173eb438",
+ "9938be53082a02e6",
+ "d75ff490c057793c",
+ "7dfcefd1ffeaa941",
+ "131ec924c42ef90b",
+ "d3323745a51a604d",
+ "2eee0fdd5cdaf565",
+ "bdf8d5624e230986",
+ "7096b882b315807a",
+ "84b1520706c3d784",
+ "b49efd086851f84d",
+ "e56e14e8cbf89e3c",
+ "3183669bf78db43b",
+ "c28289a45fc012a6",
+ "a14f221cf1761a8c",
+ "e7a197d6aefb66df",
+ "0349c893b81f7c1a",
+ "84ff17001564ea47",
+ "b1401488f998c1f0",
+ "e367ecaf05dacedb",
+ "6f90b0bf6bbe884b",
+ "4abc83fee59586b2",
+ "e64266a9c13fc74e",
+ "37c8d23c44b95833",
+ "5edefd19948ad581",
+ "932cef2b9698dde8",
+ "158950ed4bc35274",
+ "4dc023ea2e3c4115",
+ "8bbbd89d09bb82ab",
+ "ac4687f4b15507b6",
+ "3392558f04e04663",
+ "64e80c255d51ff37",
+ "78ef46252d580af0",
+ "4b5521f85ab3fff7",
+ "6c3e0fe95400f1f5",
+ "988394e53c8e52d9",
+ "983d431922822165",
+ "0319b03323f257d1",
+ "3a3b0a9113835307",
+ "91108cfcecb98297",
+ "b883e389d34b1bb1",
+ "af58ba642f4fc3d2",
+ "b0fd694500bfffab",
+ "d7501476836cf315",
+ "1cc05dbb992431b9",
+ "d4a1feebc1e1cb5d",
+ "ea316d47c03f9fdf",
+ "781c243d0021d4c5",
+ "aa3f466daa34d3ff",
+ "568c473e6e5e95c7",
+ "cdecc0eedcf05d2c",
+ "d3987535a95fbffd",
+ "f4da9e13977580f4",
+ "c483baec90d76587",
+ "6becc4c21730d391",
+ "7c04881c9a46b337",
+ "a93edea77d9f3338",
+ "c1e14f36f031a342",
+ "b12b4e4eacad1497",
+ "fd583001a87023fd",
+ "63e55780fa1766de",
+ "58ef8ca403038960",
+ "b9a6922899b66b8b",
+ "0ff82b1cc24b3105",
+ "0c9e215997d96f44",
+ "f0bd6a29eee94a2c",
+ "4f8db13958be0f75",
+ "c4f5a7748428e7d1",
+ "adc9df2489ce090a",
+ "e3231fac88242d81",
+ "c62b878b1ac43df9",
+ "a6c1dd30884a197c",
+ "5469d2b3e5fb8323",
+ "f588fbe6fd36bc6b",
+ "04c5a5a66fb7f89a",
+ "34ed76f0c32d9e93",
+ "6dfa24c82e89436b",
+ "d12a50d33b1c4253",
+ "ab88698a0c6ee1bb",
+ "13e5b88ad3becb77",
+ "cce214002aca738f",
+ "b6c3a9d53b9e784a",
+ "b1b04d862f4c32af",
+ "dbbb56b48acbaf22",
+ "adb678f7143014a0",
+ "e0999b85d2f77733",
+ "438b59086f6ebd64",
+ "6700028a309c3f7f",
+ "9337ab9573b366c1",
+ "4c0e2794cb729825",
+ "6f480809fc73b635",
+ "2760aeedce0b79db",
+ "bec58e970e8cd6c8",
+ "d3420c2bf8974a5d",
+ "57e929939f1fd1c0",
+ "65a47f93451f13de",
+ "b168db2651cbc1d6",
+ "75609423e1f40c07",
+ "0d4a95a8b52b787b",
+ "296618f10b019d9c",
+ "fce7e01f2cd0cac0",
+ "36641ce7192b5f92",
+ "4d21561351ecd00a",
+ "06b24bc51eb69188",
+ "dbe20d7c305b24eb",
+ "dbcf1311b3d95cdc",
+ "51a4bea9a77a55cf",
+ "9f400f89490fb8d5",
+ "55669bcb3a9e09c0",
+ "6337d3dcc5362dc6",
+ "55f9f2fedfc7f069",
+ "3d756c83419bba28",
+ "8f3037c89c498e88",
+ "2be10f342e68a89e",
+ "e501cafa3b025fb9",
+ "42bc15eb26cf2eec",
+ "05822f92f304fcd6",
+ "44becd7e128f4ba0",
+ "696c193b3d13924e",
+ "c5bc8b961a199646",
+ "2a07b85f61ac08b0",
+ "5cfe68ed449e0478",
+ "02c6fbcd90dcf05a",
+ "4285d9b580f8a2a6",
+ "16513a1bff79b46a",
+ "a334142059d865a2",
+ "a67919286833a7df",
+ "2078424be58e4db1",
+ "0f684159ad31f883",
+ "a76054a284f0ce61",
+ "8870ddf70bf57890",
+ "15b121492722bffb",
+ "a781ac2579798523",
+ "bf1338e9abe85c39",
+ "86f1afdbbd29e48c",
+ "afcf1165734847de",
+ "f569fb2a6ddae452",
+ "882a9d016c7e1f36",
+ "b584e424a8b3dabe",
+ "d80c26c2484dc87d",
+ "30e9ce0969941ffb",
+ "8cfcf8dc64485a74",
+ "d1df1bf926a69ca9",
+ "f621ca835cf98a6d",
+ "3519c14e4114d93d",
+ "db28c203846775d8",
+ "2d046178aa118fff",
+ "40fc6ad0aed92913",
+ "48d897f17754c7ce",
+ "b5968cd571a14cf3",
+ "31fd07069aebf013",
+ "c18dc9f29967fe7f",
+ "afe441f40a42865e",
+ "f8a3fde3a557f44d",
+ "c8e381408b191a02",
+ "f9e9366287e79e4d",
+ "cc64f3b8666b17bf",
+ "e535b7a4343399e7",
+ "01ce6f422bec823d",
+ "e64eedbb40d09a50",
+ "ca88a34b0fcc5b0e",
+ "bd346dd3e46dff68",
+ "966a03f9f111aa35",
+ "b95f2b009d70fad1",
+ "337ece90615ed69d",
+ "f14484e630df1dec",
+ "10af4a6378d0fb7a",
+ "5cec11774890d61e",
+ "b99c572f58cfb19e",
+ "b6c15035f759ec50",
+ "975dae6e02765a90",
+ "c96ac4c4015414ad",
+ "ea1b5ef156502d98",
+ "082380caaa153ee6",
+ "1ff08f79cb89c1de",
+ "87abd998f3b7e26f",
+ "13d2556ef392ed41",
+ "00a2b91e5b58cef1",
+ "cc5a3d2a52f99ab3",
+ "7a5a4a9578503a55",
+ "59b026d889aa0309",
+ "157f811757a0b29e",
+ "8825e2c99223c084",
+ "ed511caf8988dc45",
+ "04cb360502da6492",
+ "d317c9c2e53ce427",
+ "f9fb8170cecedc23",
+ "e3129aad84d5c40c",
+ "bb21cecb8bbfdf63",
+ "46459cfb9c1045d8",
+ "102707a8c4d8a1b0",
+ "2bd9661657e78278",
+ "a191b2da409f3518",
+ "62313881916550ca",
+ "e5863682239df24b",
+ "c572cacbeb840621",
+ "e93cb94a06c6957e",
+ "cc5e2f58b681e555",
+ "259e0ab1c4b690cf",
+ "fb2cfdfef4f93536",
+ "abef2b316c54f9e7",
+ "7f02a580f5219377",
+ "819d92a6492415d3",
+ "dd4cfd03d9b3f25f",
+ "996691f0e03e303d",
+ "5e4e16da9d0630dd",
+ "5162627872566ada",
+ "a465a767ceb7fd30",
+ "cce1a95634c6972b",
+ "7a26d92281df3e8e",
+ "109eb2158ca6a2f9",
+ "743318f3a2b4e281",
+ "06998c36415036aa",
+ "a49706f436a6873d",
+ "75ce95f62fc65fac",
+ "43d9cb9d3fe30ff2",
+ "4871a084d74a4bc0",
+ "8ade0910a89717a8",
+ "00b539d629e60965",
+ "74d685c3723bdf92",
+ "d08b5ddedd34448d",
+ "6999db84bd629b32",
+ "f7941427d5fa40ea",
+ "92c7355f5ea28e52",
+ "0f7ce013d15f6ff5",
+ "7b63c31cfd0eb65f",
+ "15fae84c1e6c6c57",
+ "4eb33712d4b16f43",
+ "67e75c6c6538110e",
+ "8817adf6eaf4f06c",
+ "97f302708066dc04",
+ "02cb3c385865ad2c",
+ "c6c291cd525225b7",
+ "f2e7c6dfd6a431c8",
+ "8820852f4a1163b3",
+ "ec8a2a25cdbda634",
+ "5bfbc4a6cbab9eb1",
+ "491c766e35ae9f6e",
+ "3f22bd3eaef5d64d",
+ "c497da3fdb2f5027",
+ "b6e60b9025d116e4",
+ "e89a1a7817dae7e4",
+ "15ae64b85b3e28d7",
+ "7e8763f553e43d12",
+ "691b730a7bbb28b0",
+ "8d92202f35f15712",
+ "d34363168b1590bd",
+ "e6aaffd588f2832e",
+ "57e108dcd6ee072b",
+ "6529877d9799822a",
+ "384dca297bd4f1ca",
+ "b764f5d5391e431e",
+ "b5b8de155e4e40e6",
+ "d4647f5217d84ba3",
+ "dbd62ef3bd952bad",
+ "8dcdbce3262bfcd6",
+ "835211383d281d89",
+ "b7132fe23fbdf9e1",
+ "de50abaf95e79d34",
+ "c621d1371b314913",
+ "6ff89f3123a6a98f",
+ "5caf1c90ec7001b7",
+ "596a0fd33f70da59",
+ "40fa0a4cb5193977",
+ "d3c2a59a2cffa30c",
+ "9ba1fa1ae8c3ae67",
+ "598e2a9978d3c79c",
+ "08bff656ff61e949",
+ "0160f6becf137107",
+ "f67f3737a3b47208",
+ "c3dc8cfd97735115",
+ "541da571bac5e279",
+ "55925bc39df20912",
+ "af78198822da03a7",
+ "6f13e8b708b43383",
+ "5787ff8207fd751d",
+ "424f6d496746258b",
+ "2c41f9dfcd5014e6",
+ "e6dd3d435bd1b82e",
+ "a29d46ab6efee468",
+ "f4b4b026e87b5086",
+ "ae175aa839dbf692",
+ "97eb8171b51f9aad",
+ "a2a5ccdf0846ce67",
+ "aeb79c7e573de0e5",
+ "42d2e68c29d9eef7",
+ "f1300cb717ea9335",
+ "9231adbf747903df",
+ "e9845b4128afd538",
+ "e0be32a08b66d264",
+ "8f7e22207cb3dd8b",
+ "f68006f6c05e1211",
+ "40f62cd468a546cc",
+ "56ebfd7a918891c6",
+ "cf1c544add4bf860",
+ "c1754dc2e7d666d8",
+ "a4d0e44f4527180f",
+ "3a6a5f536bfbd4d4",
+ "d07cacdb732c0b0f",
+ "9973eb31c46f8637",
+ "925da3b468003e72",
+ "7c7e48e3aed8bfd4",
+ "24624a4e60ddedd4",
+ "3e182053b03f1faf",
+ "7a059d2754e088a3",
+ "e6959c2b66202cb8",
+ "b252ff42ce0b7443",
+ "5f4cd1504f8b18e3",
+ "be080cda1829826e",
+ "0f1b31ac93459d1f",
+ "ea17788d9a9d12eb",
+ "5103648897ba1b3a",
+ "b887b7dcd6f1fdbb",
+ "1494cff85b3dc2bb",
+ "2adb03f5294c31c2",
+ "5cbd46653c3bea1a",
+ "259df98e7476a843",
+ "c4e6a655a15144dd",
+ "9b30bcdbccfd4c68",
+ "84444ea4fabcc66c",
+ "01dc77d7f8f94d97",
+ "3ea02a62856c752a",
+ "e8adf99d340489ce",
+ "65af80e5c6538215",
+ "a00e93f2981ef418",
+ "ef02bc67ebead2a2",
+ "9be76c10474e0c80",
+ "54f754dc95137c47",
+ "a0621c7e36e9ecb1",
+ "797ea41cc2e95f08",
+ "b4fc6a7b6c200b1b",
+ "fca98ecd003e1b82",
+ "a5451ba38050a44d",
+ "58688dfc2c59d3ab",
+ "f2327302df1aa660",
+ "30c6777901ae346c",
+ "6fd73557577cf9ed",
+ "96b0895b9f712d2f",
+ "e92ba97c2742e63d",
+ "fb1fff87c87b82d6",
+ "122245dd15683e26",
+ "34506b8fab6dd7c3",
+ "8cfd0c9f4bcd21b8",
+ "5b1b55f1997a9439",
+ "09b07fbbb24e1b04",
+ "f2eb0ef953f3f147",
+ "7e9fc9f948e5cb17",
+ "4893140ae5daadaf",
+ "f2fd56c195593dd8",
+ "953e132dd43ec03f",
+ "1c8567a1b1a6c12a",
+ "48a41f3f5110db1f",
+ "032faef0207b715f",
+ "d2bece2b0eebce5b",
+ "8d2c50e41a1a0061",
+ "a9004216663480de",
+ "8f7cc0f456186028",
+ "10b17081dd7cad8f",
+ "6ae356d8abbfdeb9",
+ "1bcfaefe46abccf0",
+ "8f81bb35e37f1b4c",
+ "51768783ab3e67f3",
+ "b6a96b0dcf6eadf0",
+ "5b67a83de2aaa93a",
+ "4c90a96fb1ee09f6",
+ "cecb6a5d441fba98",
+ "48fd3d84b1a0bc68",
+ "00eb3e5c7a4537b1",
+ "3f188bbf2af01b25",
+ "7e37ff763a97bc72",
+ "3ba9f330ce93636b",
+ "21951e191e55b66e",
+ "ba521df2c728c5a2",
+ "ffe4328dcdc6b538",
+ "ae9574428bd713e6",
+ "ff0ffb9a32873433",
+ "56a56d39dbfe3516",
+ "a925d8f1adc5cfe2",
+ "7c708f62e3280ec3",
+ "aa0213dd9f727219",
+ "3334d22b6559d0e2",
+ "b2436dcf1c8e5dc5",
+ "8d68ee5c283ca6b0",
+ "4057f5dbe4ffa4cd",
+ "18a515a0fff0c0c3",
+ "4832a44c0df0bad2",
+ "c8fc487eec95c21e",
+ "8782c7c292ef2759",
+ "36df04ab9c978ab9",
+ "06e70fa70393d60f",
+ "559e0907fef71cf6",
+ "af373587f2c57a24",
+ "3746512864b941d4",
+ "1999d4ed82b3188e",
+ "793075287bf70a81",
+ "878af76320373ba0",
+ "ca63425a882729f3",
+ "95c52fa37ef1eb3b",
+ "d2d43918e5447c09",
+ "ab769495f97aab9c",
+ "2b0342d5b70b7a78",
+ "ee3a996c18ace68e",
+ "9d36f8ab4e1b45dc",
+ "3d8083b1e6c7b336",
+ "fa8384342c25ec59",
+ "373e7b1bc9066563",
+ "26c4912b140fde9f",
+ "d71d0b082233758e",
+ "9be0c86102117436",
+ "f70d226e1598ac41",
+ "328fee1e52f7ac82",
+ "70f75a14390522b6",
+ "1188aebd8681e4fe",
+ "08408cbc394e7421",
+ "cc932dbc293df39e",
+ "d5187fc1cd590073",
+ "0b0648a9b9a26747",
+ "0cd406e7c5013b74",
+ "25c07c5d0a05d86c",
+ "9e55246befe96fb4",
+ "a0fff0700364df49",
+ "f9d96d0eb2bc85cc",
+ "5eda77c801e2df86",
+ "ab8cf6af80a5c0e0",
+ "73dbc35c2b91c4fd",
+ "8e713eb632acc712",
+ "56bca69ce012d4c1",
+ "bc715a45d7584d95",
+ "bb02df07a0d95e77",
+ "5423483cd3c3b75a",
+ "6df8f86449e56c05",
+ "3e345d3c6002ad55",
+ "29f2dd90f57ea948",
+ "ee6718f7a7e3d30b",
+ "ea330145d3ba738d",
+ "fb8010928a4bb56b",
+ "921ecc7c3e5f4884",
+ "940a18f62c79ffae",
+ "83b4d7d35a747d1b",
+ "b15c099c68671895",
+ "f1b9158cc2b98388",
+ "8b7145c13f36c324",
+ "37e0d3cb098458d0",
+ "080aa6966d757f05",
+ "ee35d872179a1397",
+ "6abef2102c86c0fc",
+ "096ccb6bcb3d2731",
+ "71d3720cad53b289",
+ "15ee7651b7ad610c",
+ "0da9d81c9f563df5",
+ "6ef82dead8e3437f",
+ "fd966f807d3ba9fe",
+ "b804c318fc0c1e9b",
+ "4750f5114dcaa60d",
+ "9e7c513fdd35df17",
+ "b2ba4d07e03b47ea",
+ "cbe5be4b63677c25",
+ "53fe111411edfa45",
+ "87a80325171d8add",
+ "3f8475de6ea558ac",
+ "a64e6bf63ab6a299",
+ "c10a3a62bf9fe782",
+ "711a7b593ed41b02",
+ "5a1764ef77908457",
+ "6c2fc8308efeccb4",
+ "1ac8de53c0894d18",
+ "bf0f48ffd0dff903",
+ "a851c3b4377f4923",
+ "f96bfb09bb1bd640",
+ "fd3f988b7a505c45",
+ "84c8bf1f809380bc",
+ "c16f3dafab92df61",
+ "55e85aac6594c2fc",
+ "6cef8442092da1da",
+ "1172a88e009dc248",
+ "af4c9b6e93062fde",
+ "96106c2d31eea8b2",
+ "c518dd59d12daf17",
+ "871066e9e2eaba52",
+ "acee5f619f09bf79",
+ "0fe0bb1054f49b7f"
+] \ No newline at end of file
diff --git a/data/reference_annotations_split/train.json b/data/reference_annotations_split/train.json
new file mode 100644
index 00000000..b2e65750
--- /dev/null
+++ b/data/reference_annotations_split/train.json
@@ -0,0 +1,466 @@
+[
+ "412316e54e63beef",
+ "ce3ee1fa409676b3",
+ "cb862d0020c32547",
+ "bda89f3b2429ab6a",
+ "388653542ec9fb6c",
+ "7f1f7bffe46d4c85",
+ "0f1bd208c27d202c",
+ "b8370ceababbca40",
+ "12e20466a0f08342",
+ "9b8b9b7fcd97bc60",
+ "32e083734907ad15",
+ "294ef6cb28408ec9",
+ "f17fe9ceea628f62",
+ "6f38b9bce48590e3",
+ "b61363e51ed90c7a",
+ "9328c028356ab522",
+ "11687f8008b8198d",
+ "12c3b8b612c581cb",
+ "db3b58c1836b220e",
+ "f87b5bc03e18d010",
+ "a3de319045809828",
+ "ed22d4c0f09c3e3a",
+ "09b1192474bc8b27",
+ "d4e1874210b91dd4",
+ "c9b0cae8b5784d2d",
+ "dfe9de5d26e08949",
+ "97d00a9f198a6e57",
+ "d75282fdda80b6fd",
+ "6d41460480570d22",
+ "a7666a5157a31d32",
+ "5c3806bf79eeab7f",
+ "d2d0b2521a1f186d",
+ "6dfe436482047387",
+ "7e426c62da58175b",
+ "9dbb21687510991c",
+ "2b2dd14fee46498d",
+ "cf32640f396edb87",
+ "4d5cb991d2675a6a",
+ "751a324c3f87f58a",
+ "8bb25a14b21fcce6",
+ "243291b6c7ecde61",
+ "13a92431f9683226",
+ "7f4d3b659fc17c09",
+ "dba4b5a166f9456a",
+ "768dda4099b6f2d9",
+ "668b5c95eb0229c1",
+ "1b797b27b981aafe",
+ "17b5257b3755a07c",
+ "1c34d31598257da8",
+ "775b1fdecba5cb81",
+ "e0265f0fb8e196c0",
+ "161216fcc9eb8d34",
+ "7574381766e293c7",
+ "9529eb793550093c",
+ "4960693ee45f2ae6",
+ "cacdfdafce47678c",
+ "60f3bd10ee9be85b",
+ "4f5f41ecf7517e63",
+ "c7fc146c5ccf4821",
+ "2477ebdeb9bcf4aa",
+ "6ca1063d82ab795a",
+ "d2a4b8cb9ae7fe8f",
+ "c2024b43eeaa96de",
+ "a6fe8fe0aaf2fa92",
+ "815d67f8292ad7c3",
+ "19c60923e31777e7",
+ "97d5f9e6e0a11e37",
+ "c39e6ef4060594e2",
+ "a76d1c0d9964d583",
+ "a52f807db0eb7fdb",
+ "6e4de0c194952a21",
+ "317713948b4473b3",
+ "73e4f65789159996",
+ "54375fc2889bdfb4",
+ "2731d8ddba404fad",
+ "617810f97f3f0b4f",
+ "e8e5f42a4e4005c7",
+ "03aded94fb04c62e",
+ "1fb1564dfb0f0b04",
+ "e7e366f3619b5953",
+ "cd43abdf668dbb23",
+ "1fa2284c3dcf136a",
+ "c96343baf608174d",
+ "c32378a010479b33",
+ "99223aca5d9eb3b3",
+ "678b2b51e429444a",
+ "727bce62137f97b7",
+ "62bf5de838cd3a7c",
+ "732271484650d27a",
+ "b42009eb34746731",
+ "2b944ab6ce8fe8e1",
+ "8ff91b407ccd4b18",
+ "8fbe28bc70a821bd",
+ "86255c61e33c2caf",
+ "49bf2cf97dd4ca29",
+ "9776cff7d7ead7bb",
+ "69347104bc7e740a",
+ "818f62b997d143e3",
+ "bc17c54936484a90",
+ "824a8da5564344a3",
+ "fe223a9f01625d62",
+ "4c3282bd1640fcbc",
+ "ee1c6dd97918d74a",
+ "ace99e269d2a1bd9",
+ "4f3bf2d41fc130df",
+ "ca5da2fe138af656",
+ "41fb9717f70d3037",
+ "de9a98c311683ef3",
+ "bc260c38202644f4",
+ "30120e4f3aa2f30a",
+ "e66a9b22df7fe32d",
+ "559a5c3c4c23a9d3",
+ "3515801dee00995f",
+ "f20be2311365981c",
+ "e10bc3422afaec8a",
+ "5734ff91ea85611b",
+ "dabbb27e6faa13d5",
+ "7a8d4ed693d443d7",
+ "d94b13d95fd1c12a",
+ "e2e705cabd42e40e",
+ "b434dea9be2868db",
+ "e9e12b044b495c6f",
+ "a07548a64fac5794",
+ "5df7b3241b307f8b",
+ "18e68a485e648ddc",
+ "01e8805514b4ef67",
+ "13d95239226aa537",
+ "3f87aa90c0aaf83b",
+ "f2dc875bd399a806",
+ "a83fe0ea2f391ee8",
+ "7793c695818500d2",
+ "a38fbb576027f6c6",
+ "2f418fb3ced3820a",
+ "b5b0e3205923e723",
+ "627bfd69c2a831cf",
+ "e0b122da55f1f002",
+ "f6579adcbf5faa99",
+ "b33eaac3aae2f1e7",
+ "e6e8add5e4db2d9d",
+ "b0b7b073ca2dfe5f",
+ "fa67fbbcfc0b9442",
+ "af61a31e3fd0d6f0",
+ "fe445dc1c13738d6",
+ "0d08a211bd29f963",
+ "f02cf5078e4548e0",
+ "a276bd58a15d5be6",
+ "9d0a023ecd4ad7c5",
+ "0ff9337564f4f742",
+ "fbb507bc72376d36",
+ "6ddd0d9c37f27706",
+ "6a2323c5267ae0b7",
+ "744a7a202d909323",
+ "714559f7e1bf956c",
+ "c92a196d0408a5ff",
+ "f869956d14ea1694",
+ "34370b67b5e675c3",
+ "388b6899fb38b762",
+ "dd81eae9478576f3",
+ "8975cbb58ab67846",
+ "055985699aee1e09",
+ "1416afc51d8ce022",
+ "eea87713e11a55c2",
+ "44f1f74c09e634cb",
+ "598b6c745cdcd404",
+ "e9b7e07dc5598afe",
+ "0b3e1cdf3ef9413d",
+ "610e118d804e4076",
+ "fb9ec9e846ea4e87",
+ "aa096ffde94b953b",
+ "887afbb0843425b6",
+ "e2275acc4f1c2d0d",
+ "b3b7ac7aae87793d",
+ "95b2a54b81b0bc6c",
+ "77acb3a4a3f58e0b",
+ "ecfb020c517d37c1",
+ "9302a95ca3efba5c",
+ "ced6075dd1813122",
+ "ab3af998dff7a2ef",
+ "0f3900cdcd0c7f3e",
+ "d699ed2b1adc6be4",
+ "1e40bd733acd56bc",
+ "aea11fd4d2a7709d",
+ "cf7705e45048032e",
+ "1d89cece70c6cb64",
+ "f73d08f8cdfe276d",
+ "cfdacd53c732343c",
+ "9a94525307a1b13d",
+ "543d800fd468f0fc",
+ "25eade3365f92578",
+ "e886bfa3a4f4ffd4",
+ "b8437dbfeeaf0f04",
+ "05c03233d710cf54",
+ "18bb29b147e80caa",
+ "10107b83e7393c50",
+ "65ab2fd5467ef78d",
+ "10868774b23c5730",
+ "253e4d53f952a882",
+ "a47713a44e4c656b",
+ "f2e00a1fee27bdd7",
+ "4783477a22587a6e",
+ "15d935e36152eb15",
+ "4a6100f1ddaef93b",
+ "680aeb0a20a9fed3",
+ "14df54305cad83b4",
+ "188d3cda4e6f2d5d",
+ "fc373293fb7c56ac",
+ "79f48541164e16ff",
+ "e9fc2f8b110e71a0",
+ "ce14eb1b2318f054",
+ "12fd6b1b6c4aecf3",
+ "5f4b39feb2a82dac",
+ "0f040d1ec658b8a5",
+ "09be49631855ae19",
+ "c8ff17063166050d",
+ "76b289d10702db4d",
+ "eac7be8886cb755b",
+ "84c868322995b179",
+ "a515a81bc4be3cd2",
+ "b2bab86575d21b44",
+ "0eff4063e68d7cd6",
+ "32431779b1c54c0a",
+ "c118ee1ff2d1bede",
+ "1b0a6ecee1f830fc",
+ "cd052c606f2f9124",
+ "5deb0179858d78a1",
+ "dd1472476dfe4a8b",
+ "1a191f3535ccaea3",
+ "a9d336b90e8b94a2",
+ "6c222ea916b4f5e1",
+ "c5a60dbdb668bc10",
+ "f3a461d6bf980196",
+ "977477f3f0bf1bfc",
+ "1ee7ecee9e7e131c",
+ "d1a87288683080c4",
+ "185aa4f76ff15d62",
+ "23ce630f1ab642b8",
+ "7e9dd6cb86b58f95",
+ "b30ef2c1aaec9236",
+ "a154a01ff4cf04c7",
+ "79c729d80cb73939",
+ "a9639521d8a5ab09",
+ "f32a1e7e2843c096",
+ "8c21cafc192eeea5",
+ "59f15180eddee8b2",
+ "2ebb5bdbe7e00a78",
+ "bd1a21ea3d047c14",
+ "dbf15cc4f389196f",
+ "98f6f9dbb1540a31",
+ "cb203cf5d91b1ae3",
+ "f921d39906e6d93d",
+ "6c14f733fe7aa28e",
+ "f71fca5fc684df8b",
+ "7e82a1ceb29e4019",
+ "54273dd266fce692",
+ "536aac44b608f951",
+ "921e042759b30033",
+ "0167c92c0d8c8b47",
+ "90581f56f6846be5",
+ "a91ca8347f800ee7",
+ "dc278166b5d14512",
+ "90c290ed4ded479d",
+ "cee70006132a6f46",
+ "1357546ec01b1a65",
+ "5a66027d34aafc52",
+ "2c4caf71ca3735a8",
+ "0baac042dac42abc",
+ "54dd28994604ee35",
+ "60b45a1573fc53e9",
+ "6a999675c9422dfb",
+ "0d164fb0d72dfa00",
+ "b24f997d9358b324",
+ "df6728b8420ab3b4",
+ "d50ab8fd0c085fb6",
+ "9ae8bef18938d2df",
+ "19d14dec2d11fd96",
+ "f0ce40dfe333e918",
+ "3da6e0f0f97b3d2f",
+ "0d730f18ae9d5694",
+ "ecba01acba8df2ec",
+ "17a23970b35a4f44",
+ "1b8705a0486b3be1",
+ "1ca9b79a98ef45fa",
+ "66e47f5d28e2f95b",
+ "5f1bc82727f54376",
+ "9743174f44c5e2c9",
+ "ec84060a194fd9cc",
+ "845bb039719ac5d8",
+ "ea6d47427a2bb349",
+ "27d11629261d8806",
+ "6a5cb89e80f86ce9",
+ "29b0321ec6b75ebd",
+ "d355c778fcfee3a9",
+ "0ce083454814c1ba",
+ "c06d86db280f9b2b",
+ "b0e95af7c706ffa0",
+ "b9a973281ea252b3",
+ "d0705c9e6fbaeba3",
+ "a3210e75e10a3ba8",
+ "61832cb4291c343f",
+ "22dd52fbce13f27e",
+ "985ffde955eee01a",
+ "882df28aee338320",
+ "b23babf61349db1a",
+ "1c9bd4db3d8388f7",
+ "1ff5d648a625f3c7",
+ "691119fe8a2f8e1c",
+ "c30de3192d2e8ec2",
+ "5bf4e37462a1b715",
+ "de23f9f77f9b49f0",
+ "dadd7dd2d4247627",
+ "36ed04f4b45e3ab9",
+ "6b249c38b49ec9f0",
+ "a69ec271f8651d1b",
+ "7f9eb1b8217d15ad",
+ "6b003152c5459126",
+ "6eb6b29c45f6355f",
+ "502645d74f607b06",
+ "6d6ade44dcc497dd",
+ "3ebe69af702fa116",
+ "29c65bb00fb9aa6c",
+ "b4a2999b439b1ba8",
+ "3b0c5666e54a2394",
+ "8bdb610131555c12",
+ "b10ee0bb166cfd86",
+ "e676143c80802a59",
+ "15d68159595eae09",
+ "06b1a65aa87ffa78",
+ "5ee0fb9826fec164",
+ "2c15dfc106ebf8d8",
+ "bab97726875c0f14",
+ "a867d281d34d34b7",
+ "eeb2e28f54484dc9",
+ "d753660b3c2bdd84",
+ "5f7760ca5dd8b8ef",
+ "8ba22f6c9651edc3",
+ "9650ee69410c68ec",
+ "608fe49756dc68cc",
+ "e7bfc160fd3c0d5d",
+ "335800e03030fb93",
+ "446aa68e0c4c5083",
+ "1e58fd6b31d72e2a",
+ "4c1ccde478ce063a",
+ "5cc6ace5ef0d7525",
+ "09b17cb9b3c8b1bb",
+ "bbf0ceb1e0e4c6ee",
+ "c78ba3edcbd57788",
+ "ead1076787fdd7e4",
+ "8ecc2cf269498bee",
+ "d6d6b7c041820ab8",
+ "d837b4fde544a9f2",
+ "3ef6d59aeaa6d26e",
+ "fca2af05ee78ac5c",
+ "cf5cfcae47e5e3a1",
+ "baa8b8c5f52810af",
+ "3f45ee333ce457bb",
+ "7e24a5a087db6814",
+ "30f71b100c5cebae",
+ "9a22a2bb323cfd0f",
+ "84a75b6fc33b669e",
+ "21d71bac070403e0",
+ "0d4385899adc0781",
+ "05726637bd47a762",
+ "7a9f073396eaec67",
+ "5cdef03a3004a6ff",
+ "7d8dc0575933f142",
+ "890f788a91fc2fa1",
+ "5682e68143deea61",
+ "6dc47e3f5d35aedd",
+ "16bdbde359584f99",
+ "0bf7a19b22163465",
+ "bdd16afe92166295",
+ "9ad46ff913bb805f",
+ "b07adba37aa1b7e2",
+ "3cdca4cc05854ade",
+ "41148b8edaf1106e",
+ "85054d3f47bee56c",
+ "aabc3155ba9aec95",
+ "dbaf9789160ca414",
+ "463ecd64b7506048",
+ "cef1c9957f4ff612",
+ "1ee8a6a4d8f26d65",
+ "a92a031f28de3544",
+ "c1a87780d88d85d9",
+ "659f501baa5997da",
+ "95c69029642bdb41",
+ "e10d907c64d75e3a",
+ "4fd2ed9e6088176e",
+ "1eabf957a9401fab",
+ "cf8ac5972027dda5",
+ "623dee827372d496",
+ "f041e5b526e79ef4",
+ "0832672073492f50",
+ "0213bdb5ebb8dc19",
+ "d49988efd778ca9d",
+ "c301c38902477230",
+ "376b8eb06fc0b40a",
+ "037577fc2019fcfa",
+ "14e053dfe9057aa8",
+ "28e70f46ba4394dd",
+ "b9482c3bd09d03dd",
+ "d2b0cb5a911b8ef8",
+ "05a8a84b60a30ceb",
+ "63d0bacbab451d2d",
+ "5f3c4c189362c2e5",
+ "c7d162e62f174cd5",
+ "1f9f4b843070fc1b",
+ "850f5b2c312299d5",
+ "b9d8c4cb5cf8a4d6",
+ "f6147b5c0a147123",
+ "3db023c27932ad93",
+ "42a062fe7f1ab253",
+ "a904d6cb272e8533",
+ "e86db251be2c8a60",
+ "5e5d4b87195388c7",
+ "9ef6037ebd29a9be",
+ "242fdec5d0ed5010",
+ "0bd759633c0ceec6",
+ "6f8d7a6a1dea6a3a",
+ "a55ace0305e39654",
+ "19f06ccd7136a2b4",
+ "2e2d56abe9dd6e22",
+ "c385c0ffaa17bd29",
+ "c38734a9eeea0ff8",
+ "3897d8d16ba333e8",
+ "d7467eab308fb4a6",
+ "4761b0a9b046cc66",
+ "0ac0120f667a8dcf",
+ "ed10036ddfbda912",
+ "00efeb17bcaafce6",
+ "61bff064f593ecc0",
+ "c3549567b1ad7bdf",
+ "371dca18821f7714",
+ "a6cdbfd0a7bef417",
+ "c175537338906951",
+ "c7f6cb518fb19151",
+ "6291bcc06184d125",
+ "06e505abb8dad1b8",
+ "82b7b915b7af9c17",
+ "725b6b95cd47b601",
+ "4bede5d2af4cb11f",
+ "600e2a1d0f05afd9",
+ "03281d5d8a77be56",
+ "27b4366a0f1fcdef",
+ "42799014c183e1b3",
+ "9c6440ecec046cc0",
+ "b26c9f300c14239d",
+ "366786e98797ebf3",
+ "454f266801ae2ae0",
+ "fdbbd2b53cb2e1e3",
+ "76610bf4610246c7",
+ "09963e79b21cde12",
+ "457fcec1ad31841f",
+ "6962629b904be54d",
+ "a7d6e1aeb5e009f0",
+ "2674228d88c4f3d6",
+ "2bb4685a03857b2c",
+ "4d776db86abecdda",
+ "21c3236216f620ff",
+ "5a12431e371f3ce9",
+ "2d2ce200fea72359",
+ "9aa4b513807ea22c",
+ "4526aa14337a2b0c",
+ "f659401e91439524",
+ "dbbf02a1cd0ad33b"
+] \ No newline at end of file
diff --git a/data/reference_annotations_split/valid.json b/data/reference_annotations_split/valid.json
new file mode 100644
index 00000000..939924f0
--- /dev/null
+++ b/data/reference_annotations_split/valid.json
@@ -0,0 +1,312 @@
+[
+ "84d6a370e4f38ddc",
+ "7482381a8f5cf6b8",
+ "d5074e4b714ac9e7",
+ "d11406732e3bea76",
+ "b865bde82d06ebb3",
+ "e56a494dd7eca1bd",
+ "0bae4647efd39e51",
+ "81273108dd167b98",
+ "85328590c0d7273f",
+ "dd000f356b48904d",
+ "e35db152221bc857",
+ "d44641bd5ae5278f",
+ "5bf14a907f9a4c65",
+ "32d4bf275131aa15",
+ "78bb780793fbb176",
+ "e5f9f083c2d03d82",
+ "7623950728f52a71",
+ "625b243397001f01",
+ "59f7dd56d9e1b0a8",
+ "16abf8ee0697e64f",
+ "66faa094a9333d1a",
+ "97cc58c1e41f99b0",
+ "5f3c9c6bf76ba72d",
+ "0041baf85c9ca3ec",
+ "ab4814d2b8c742a0",
+ "a21d17fd2d8b8edc",
+ "26351f87a8ee7708",
+ "4b3f577896c80f8a",
+ "d96438a7165e5d82",
+ "50aef770cb4f028f",
+ "4ea21dc6d8ed8838",
+ "1d800e909e6141d4",
+ "f817fca54b13831d",
+ "22388445fe620ac0",
+ "f6a89befd13a643f",
+ "0372b733c28f305a",
+ "3f2a6d4893c18634",
+ "350581534e265186",
+ "88902efe4a3194b0",
+ "63c1045055ec3b58",
+ "dbefd21e0104bf7c",
+ "67c8e242329ba986",
+ "4d983357fd464bb5",
+ "340a7fd84bf76abf",
+ "d314fa7ee445bd1d",
+ "fd1a78c4cfc3ce73",
+ "0ea6796582fa5732",
+ "4489bfc781a82281",
+ "8927428c96c0b59a",
+ "cf103f7882a05deb",
+ "b19a4e8667817aa3",
+ "7f29aa0291e79ae3",
+ "7e58bfc14edf68e4",
+ "d472c3dfb2f2a810",
+ "c2dc665e589f3592",
+ "83bc5fbf2b54e83b",
+ "73a8da91bc0e9ef3",
+ "f419a1217eb38de5",
+ "7f776a2d7992cecd",
+ "c3d0eacd0639efb5",
+ "83e9484cfd868dd3",
+ "833ff87d69cff5db",
+ "60dad220f2ab3639",
+ "b402cb61be362e5f",
+ "19c7a1b1a2df87d7",
+ "a17918ec81a36da6",
+ "120a49c0aa284f68",
+ "c18fea56107696a8",
+ "37a37d1a4986527a",
+ "234da54efbcf734b",
+ "459539da9315ccac",
+ "646d03428a882c1b",
+ "682ae6d18001b529",
+ "8c92d4b773f61ca8",
+ "a6fac58198296194",
+ "2f0be9733140e2c6",
+ "edb3056f1838e6ae",
+ "fc875e0381a28435",
+ "647d17d44745a532",
+ "2fb4fdf2a570c366",
+ "b1ef468658a9a0c1",
+ "cb20c04ba174a26c",
+ "629372a8091eafae",
+ "2f43fe24bd8e91e1",
+ "c1d88ce9dadd7d2d",
+ "2f3c913f6a97964d",
+ "8928e191ee2fd3b9",
+ "f6934fa14d46e748",
+ "36b156f7888a84cf",
+ "afa1af9f34e28040",
+ "60f347ddb0b38299",
+ "c7c1215819127a1c",
+ "ee0a5dc41c7621a8",
+ "3e077f85d5a6c833",
+ "b00bb452e7a8176c",
+ "425f13fb7e94add5",
+ "8e93586c1ec97514",
+ "1681e7b35fd0cbf9",
+ "0fe8df0e85116b61",
+ "b88f4271f7200c7d",
+ "be874afd46c1e39a",
+ "bcac7e3f9f614ec6",
+ "ce504241f1c04e66",
+ "9664c0f0ec6401b9",
+ "92f3743c5ec7a199",
+ "994ac7d986a99e50",
+ "96d69242a699323d",
+ "a5adb726852f5cc5",
+ "82c717a9a379b75c",
+ "03fad65e5af65088",
+ "c63e878cc60a8682",
+ "2c2244c35d126bfb",
+ "5a5aa275eb4ec7b5",
+ "69b15e884cc13c70",
+ "ec1d79d2314e86c1",
+ "861434b03ddcac64",
+ "f5035bed07944920",
+ "37bae9eaf342542b",
+ "0dc53ac916660b90",
+ "a0c38b4389ad7cf7",
+ "f00a01940be6481d",
+ "fcfd47d66ec6e37d",
+ "3cb89a7fc7672364",
+ "28c5a30fd4464675",
+ "b3b547454a63153b",
+ "c37a9b7ae53093d8",
+ "a7840a951b1de6c2",
+ "e1b08870ad5117a8",
+ "798f2870a2d9dcd0",
+ "bcd8763845d1b292",
+ "81cbadb9d7a6993c",
+ "d77bee5cc3a0378d",
+ "c23bb542201aa511",
+ "f4d510a39278e687",
+ "293d35fb3749487b",
+ "0eefe40a0baa2971",
+ "0767ea7f695f5166",
+ "720aab4f782f4c60",
+ "610581cb76d7590d",
+ "928a4c5185e10dbd",
+ "8424207a5053802b",
+ "3fb4f28492730d2e",
+ "1e23ae421d7a2d01",
+ "01bb9c4c07d29bff",
+ "b8875373ee66e841",
+ "7537ed41507e461b",
+ "8d89e926a34fee1b",
+ "db7627e56b8621b1",
+ "92aa6b2fcbfc7722",
+ "d3b779682ee988c8",
+ "611a1cc1a2d405d3",
+ "3a3ae16e94bc7aba",
+ "f654c82bca57cf62",
+ "2b1f098200b44adf",
+ "8d4a6cefea9d8f17",
+ "f357d2d64485b9c9",
+ "9f0de9801a38d664",
+ "7ce2c3dbaa47508a",
+ "0e22fe4e4e58faf4",
+ "0694786160024e30",
+ "0f53257eb74bee54",
+ "94fb86d31446e531",
+ "031667f4e242da61",
+ "d696a9daccdb2a9f",
+ "4c017087976d05eb",
+ "8037b4b4bc8d26c2",
+ "73e0b884cd04d9bc",
+ "c46a9d4c505a95d1",
+ "13c393fa406a72cb",
+ "89a05fc32f2f5834",
+ "d4a83d8a8bd26bab",
+ "fbc78e2407910a4b",
+ "37411c359cd8cc6c",
+ "4ef66341677f73f5",
+ "48fbeab82953a1e8",
+ "43109d44111df5d3",
+ "cfffcf0b8f0516a5",
+ "cefb1202ed3cd997",
+ "e0a244a6765fd08f",
+ "5926a9aa89511b26",
+ "59ecc4b560f48ba8",
+ "559b3c20886ed742",
+ "c8cc75b95f0c8047",
+ "1e7b90184a127017",
+ "2d835bff00514238",
+ "e76ba28e58604c26",
+ "2cbc543a7c7de8b5",
+ "aabd0ff0d82fff72",
+ "37c937488a4647a8",
+ "5a7c1a87df0e05ed",
+ "657f8d0cc39bbcdc",
+ "1816ddecd7432cf9",
+ "bad93fb821395db2",
+ "0bb56716a1fbef1e",
+ "ef9e2e35160ff85e",
+ "a6dc46792d4e41c8",
+ "238f8edc5eda1358",
+ "3357c28c91cc2ed1",
+ "f05de4db911ec8af",
+ "c55f7ae8ec2a2b95",
+ "21f23f5e8c270deb",
+ "265e576fd3517f91",
+ "1a27e38bd8df6232",
+ "cb1947cca068e463",
+ "c030c8ed94582dce",
+ "46c90dce23083cfe",
+ "d185510cb8fd18f9",
+ "9d69024aca1c4854",
+ "0d2cab8061464438",
+ "141b99fabc179550",
+ "74ceffa7e539fa48",
+ "6ed3cd21c6a2c9d0",
+ "dc45c7a6ef17f8b5",
+ "f0d94de8beabbd84",
+ "5d13cfce75a45cd1",
+ "38e7d62918a4f9b6",
+ "d7c8920acdf478ea",
+ "1b71508a0007113e",
+ "077c28a0185d07ab",
+ "b1f2aa8f6f76b30c",
+ "8dd850c81b49a1f9",
+ "09c97bb07b943fc5",
+ "844703f5c2809777",
+ "5e731c2ba80088f0",
+ "2748f920f4df8eaf",
+ "35482ae218713d54",
+ "87286bdc06061018",
+ "0c7ef6c32cbdee47",
+ "537154d2c4344d48",
+ "187d237ce11c036e",
+ "f96206c3f5234d23",
+ "edfe491576c461c4",
+ "4edd58bd918ce969",
+ "fe55e99f4f3dc195",
+ "6a85bb1d21a35e7d",
+ "774cb0f28f7900f9",
+ "df9ccdffca7c28c8",
+ "f48b3738091c9424",
+ "b664311c69cff92d",
+ "22d6578b30f5a227",
+ "1657515edd068cd6",
+ "710c8d7f75c0a710",
+ "4d38305c4a2ee035",
+ "cae968d4926648bd",
+ "dc562be6acc8930e",
+ "d211465bc603f6af",
+ "0efe2627f1ce8ac2",
+ "8da263e23f332322",
+ "bbc41d7d09e40c0c",
+ "9c662ed62bfd5d2b",
+ "48e6abb1bafdff36",
+ "34de7910ace6d2e9",
+ "e6057ffc5085a192",
+ "adca05bec28db483",
+ "011796336c7b94de",
+ "1754df1f2e3d5adb",
+ "ef339ad918652d91",
+ "4fa8af0edccb7ada",
+ "8532a6232360aff8",
+ "6d1d34e4c80a3368",
+ "3983cd9d80386fb4",
+ "d55eb8c97d71e843",
+ "000176284683faa2",
+ "328058c83791df93",
+ "ebc77980250ee68f",
+ "cc533c087f06ad0d",
+ "7b1efef5807fbf94",
+ "a3c99646acb0bb6f",
+ "f6317f134f532696",
+ "67a79bfc84b4071b",
+ "d7e408fcb899dade",
+ "8344cee00da693fb",
+ "fb6b381d9ec012fd",
+ "bbcfe0aee7ea78ea",
+ "5dd0014fd38a553d",
+ "2793414918738c7f",
+ "983d16512ae92d46",
+ "1a5f86cb1d942c37",
+ "991790f44a825778",
+ "7a12bbde6a73c9af",
+ "1e545ff455f7ea5d",
+ "0d94556c4f9d05ad",
+ "0b36924a005c2ae2",
+ "6785be05d030779b",
+ "5140d3b40ac28386",
+ "7941d384f7b9cde1",
+ "a67614bd685d7dfc",
+ "24de96ea505af909",
+ "38f79f1a48e7844f",
+ "c24f7253b10c0356",
+ "13da66c1d4719bbb",
+ "5c06e4fb1887fdc7",
+ "2532fe8c7214c485",
+ "4962c0d8106b8b5e",
+ "4ef5adf3069403e9",
+ "e9ed6f2d52911d5c",
+ "fd34fcd8a64e1253",
+ "eea7fe4f5d754792",
+ "a4bbf07046109fc9",
+ "29964f32c68b0ce8",
+ "078becead8a70053",
+ "d0055474970a4f5c",
+ "24363a67840112f4",
+ "0656984a0d34dbdd",
+ "e3b6fe6756441ee1",
+ "1b20b1629ba7d3a6",
+ "5cf51622fafaf868",
+ "ba69597434946260",
+ "2fdab67916d89c9b",
+ "14b5a3a574c90154"
+] \ No newline at end of file
diff --git a/docs/_static/logo.png b/docs/_static/logo.png
new file mode 100644
index 00000000..3667dcf6
--- /dev/null
+++ b/docs/_static/logo.png
Binary files differ
diff --git a/docs/_static/logo_badge.png b/docs/_static/logo_badge.png
new file mode 100644
index 00000000..de2ea153
--- /dev/null
+++ b/docs/_static/logo_badge.png
Binary files differ
diff --git a/docs/_static/logolink_OP_VVV_hor_barva_eng.jpg b/docs/_static/logolink_OP_VVV_hor_barva_eng.jpg
new file mode 100644
index 00000000..fdbb9ea6
--- /dev/null
+++ b/docs/_static/logolink_OP_VVV_hor_barva_eng.jpg
Binary files differ
diff --git a/docs/conf.py b/docs/conf.py
index 996e8521..562491f9 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,7 +18,7 @@ from importlib.metadata import version as get_version
# -- Project information -----------------------------------------------------
project = "sec-certs"
-copyright = "2020-2022"
+copyright = "CRoCS MUNI | 2020-2023"
# Note thas this inference won't work from Docker: https://github.com/pypa/setuptools_scm/#usage-from-docker
release = ".".join(get_version("sec-certs").split(".")[:3])
@@ -59,11 +59,11 @@ html_theme = "sphinx_book_theme"
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
-html_logo = "_static/logo.svg"
-html_favicon = "_static/logo_badge.svg"
+html_logo = "_static/logo.png"
+html_favicon = "_static/logo_badge.png"
html_theme_options = {
- "repository_url": "https://anonymous.4open.science/r/sec-certs-7A92",
+ "repository_url": "https://github.com/crocs-muni/sec-certs",
"repository_branch": "main",
"launch_buttons": {"binderhub_url": "https://mybinder.org"},
"use_fullscreen_button": False,
diff --git a/docs/index.md b/docs/index.md
index 0b0ba966..1baac8e6 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,10 +1,5 @@
# Sec-certs documentation
-```{warning}
-This documentation was anonymized but may still contain occasional links back to the non-anonymous GitHub repository.
-```
-
-
Welcome to the technical documentation of *sec-certs* tool for the data analysis of products certified with Common Criteria or FIPS 140 frameworks. If you're looking for general description of the tool, its use cases and capabilites, we refer you to [sec-certs homepage](https://seccerts.org/). If you are looking for more advanced knowledge, e.g. how to mine your own data, how to extend the tool, and so forth, this is the right place.
There are three main parts of this documentation. *User's guide* describes high-level use of our tool. Driven by this knowledge, you can progress to *Notebook examples* that showcase some of the API that we use in the form of Jupyter notebooks. The documentation also contains some of the modules documented with `autodoc`, see *API reference*. Still, some dark corners of our codebase are not documented. To inspect the code directly, see the [sec_certs](https://github.com/crocs-muni/sec-certs/tree/main/src/sec_certs) module. If you want, you can run the notebooks as they are stored in the [project repository](https://github.com/crocs-muni/sec-certs/tree/main/notebooks). If you are interested in contributing to our project or in other aspects of our development, you can consult the relevant *GitHub artifacts*
@@ -26,7 +21,7 @@ Each of the notebooks can be launched interactively in MyBinder by clicking on ð
:maxdepth: 1
Seccerts homepage <https://seccerts.org/>
Seccerts docs <https://seccerts.org/docs>
-GitHub repo <https://anonymous.4open.science/r/sec-certs-7A92/>
+GitHub repo <https://github.com/crocs-muni/sec-certs>
Seccerts PyPi <https://pypi.org/project/sec-certs/>
```
diff --git a/notebooks/cc/reference_annotations/train_validation_test_split.ipynb b/notebooks/cc/reference_annotations/train_validation_test_split.ipynb
new file mode 100644
index 00000000..840adb46
--- /dev/null
+++ b/notebooks/cc/reference_annotations/train_validation_test_split.ipynb
@@ -0,0 +1,81 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from sec_certs.dataset import CCDataset\n",
+ "from sec_certs.sample import CCCertificate\n",
+ "import pandas as pd\n",
+ "from sklearn.model_selection import train_test_split\n",
+ "import json"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dset = CCDataset.from_web_latest()\n",
+ "df = dset.to_pandas()\n",
+ "reference_rich_certs = {x.dgst for x in dset if (x.heuristics.st_references.directly_referencing and x.state.st_txt_path) or (x.heuristics.report_references.directly_referencing and x.state.report_txt_path)}\n",
+ "df = df.loc[df.index.isin(reference_rich_certs)]\n",
+ "\n",
+ "# The following certs go straight to the test set as they represent super rare categories that we cannot split\n",
+ "certs_from_rare_categories = df.loc[df.category.isin({\"Multi-Function Devices\", \"Mobility\", \"Data Protection\"})].index.tolist()\n",
+ "df = df.loc[~df.index.isin(certs_from_rare_categories)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# This splits 30/20/50 (train, valid, test)\n",
+ "x_train, x_test = train_test_split(df.index, test_size=0.5, shuffle=True, stratify=df.category)\n",
+ "x_train, x_valid = train_test_split(x_train, test_size=0.4, shuffle=True, stratify=df.loc[df.index.isin(x_train)].category)\n",
+ "x_test = list(x_test) + list(certs_from_rare_categories)\n",
+ "\n",
+ "with open(\"../../../data/reference_annotations_split/train.json\", \"w\") as handle:\n",
+ " json.dump(x_train.tolist(), handle, indent=4)\n",
+ "\n",
+ "with open(\"../../../data/reference_annotations_split/valid.json\", \"w\") as handle:\n",
+ " json.dump(x_valid.tolist(), handle, indent=4)\n",
+ "\n",
+ "with open(\"../../../data/reference_annotations_split/test.json\", \"w\") as handle:\n",
+ " json.dump(x_test, handle, indent=4) "
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.13"
+ },
+ "orig_nbformat": 4,
+ "vscode": {
+ "interpreter": {
+ "hash": "a5b8c5b127d2cfe5bc3a1c933e197485eb9eba25154c3661362401503b4ef9d4"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/pyproject.toml b/pyproject.toml
index 5948f79a..0c7f33d0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,7 +41,7 @@
"pdftotext",
"pikepdf",
"Pillow>=9.2.0",
- "PyPDF2",
+ "pypdf[crypto]>=3.1.0",
"python-dateutil",
"PyYAML",
"rapidfuzz",
@@ -62,7 +62,9 @@
[project.optional-dependencies]
dev = [
- "mypy",
+ "black==23.1.0",
+ "ruff==0.0.239",
+ "mypy==1.0.0",
"types-PyYAML",
"types-python-dateutil",
"types-requests",
@@ -70,9 +72,6 @@
"pytest-cov",
"pytest-monitor",
"pytest-profiling",
- "black",
- "isort",
- "flake8",
"pre-commit",
"pip-tools",
"sphinx",
@@ -80,8 +79,6 @@
"sphinx-book-theme",
"sphinx-design",
"sphinx-copybutton",
- "pyupgrade",
- "flake8-future-annotations",
"ipython!=8.7.0",
]
test = ["pytest", "coverage", "pytest-cov"]
@@ -94,8 +91,30 @@
[project.scripts]
sec-certs = "sec_certs.cli:main"
-[tool.setuptools.package-data]
- "*" = ["*.yaml", "*.json"]
+[tool.ruff]
+ select = [
+ "I", # isort
+ "E", # pycodestyle
+ "W", # pycodestyle
+ "F", # pyflakes
+ "C90", # mccabe
+ "UP", # pyupgrade
+ "PTH", # enforce pathlib usage
+ "C4", # comprehensions
+ "SIM",
+ ]
+ ignore = [
+ "E501", # line-length, should be handled by black
+ ]
+ src = ["src", "tests"]
+ line-length = 120
+ target-version = "py38"
+
+ [tool.ruff.mccabe]
+ max-complexity = 10
+
+ [tool.setuptools.package-data]
+ "*" = ["*.yaml", "*.json"]
[tool.setuptools_scm]
@@ -104,31 +123,24 @@
[tool.black]
line-length = 120
- exclude = '''
+ force-exclude = '''
/(
\.git
| \.mypy_cache
| \.tox
| venv
- | certsvenv
| \.venv
| _build
| buck-out
| build
| dist
| src/sec_certs/_version.py
+ | docs
+ | .ipynb_checkpoints
+ | .eggs
)/
'''
-[tool.isort]
- multi_line_output = 3
- include_trailing_comma = true
- force_grid_wrap = 0
- use_parentheses = true
- ensure_newline_before_comments = true
- line_length = 120
- skip = ["certsvenv", "build"]
-
[tool.mypy]
plugins = ["numpy.typing.mypy_plugin"]
ignore_missing_imports = true
diff --git a/requirements/dev_requirements.txt b/requirements/dev_requirements.txt
index e174d11b..a44ecdba 100644
--- a/requirements/dev_requirements.txt
+++ b/requirements/dev_requirements.txt
@@ -21,7 +21,7 @@ beautifulsoup4==4.11.1
# sec-certs (./../pyproject.toml)
billiard==4.0.2
# via sec-certs (./../pyproject.toml)
-black==22.10.0
+black==23.1.0
# via sec-certs (./../pyproject.toml)
blis==0.7.9
# via thinc
@@ -75,8 +75,6 @@ docutils==0.17.1
# myst-parser
# pydata-sphinx-theme
# sphinx
-en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1.tar.gz
- # via sec-certs (./../pyproject.toml)
entrypoints==0.4
# via jupyter-client
exceptiongroup==1.0.4
@@ -87,12 +85,6 @@ fastjsonschema==2.16.2
# via nbformat
filelock==3.8.2
# via virtualenv
-flake8==6.0.0
- # via
- # flake8-future-annotations
- # sec-certs (./../pyproject.toml)
-flake8-future-annotations==1.0.0
- # via sec-certs (./../pyproject.toml)
fonttools==4.38.0
# via matplotlib
gprof2dot==2022.7.29
@@ -121,7 +113,7 @@ ipykernel==6.19.1
# ipywidgets
# myst-nb
# sec-certs (./../pyproject.toml)
-ipython==8.6.0
+ipython==8.10.0
# via
# ipykernel
# ipywidgets
@@ -129,8 +121,6 @@ ipython==8.6.0
# sec-certs (./../pyproject.toml)
ipywidgets==8.0.3
# via sec-certs (./../pyproject.toml)
-isort==5.10.1
- # via sec-certs (./../pyproject.toml)
jedi==0.18.2
# via ipython
jinja2==3.1.2
@@ -179,8 +169,6 @@ matplotlib-inline==0.1.6
# via
# ipykernel
# ipython
-mccabe==0.7.0
- # via flake8
mdit-py-plugins==0.3.3
# via myst-parser
mdurl==0.1.2
@@ -192,7 +180,7 @@ murmurhash==1.0.9
# preshed
# spacy
# thinc
-mypy==0.991
+mypy==1.0.0
# via sec-certs (./../pyproject.toml)
mypy-extensions==0.4.3
# via
@@ -236,6 +224,7 @@ numpy==1.23.5
# thinc
packaging==22.0
# via
+ # black
# build
# deprecation
# ipykernel
@@ -303,8 +292,8 @@ ptyprocess==0.7.0
# via pexpect
pure-eval==0.2.2
# via stack-data
-pycodestyle==2.10.0
- # via flake8
+pycryptodome==3.16.0
+ # via pypdf
pydantic==1.10.2
# via
# confection
@@ -312,15 +301,13 @@ pydantic==1.10.2
# thinc
pydata-sphinx-theme==0.8.1
# via sphinx-book-theme
-pyflakes==3.0.1
- # via flake8
pygments==2.13.0
# via
# ipython
# sphinx
pyparsing==3.0.9
# via matplotlib
-pypdf2==2.11.2
+pypdf[crypto]==3.2.1
# via sec-certs (./../pyproject.toml)
pyrsistent==0.19.2
# via jsonschema
@@ -348,8 +335,6 @@ pytz==2022.6
# via
# babel
# pandas
-pyupgrade==3.3.1
- # via sec-certs (./../pyproject.toml)
pyyaml==6.0
# via
# jupyter-cache
@@ -370,6 +355,8 @@ requests==2.28.1
# sec-certs (./../pyproject.toml)
# spacy
# sphinx
+ruff==0.0.239
+ # via sec-certs (./../pyproject.toml)
scikit-learn==1.2.0
# via sec-certs (./../pyproject.toml)
scipy==1.9.3
@@ -395,9 +382,7 @@ snowballstemmer==2.2.0
soupsieve==2.3.2.post1
# via beautifulsoup4
spacy==3.4.3
- # via
- # en-core-web-sm
- # sec-certs (./../pyproject.toml)
+ # via sec-certs (./../pyproject.toml)
spacy-legacy==3.0.10
# via spacy
spacy-loggers==1.0.4
@@ -446,8 +431,6 @@ thinc==8.1.5
# via spacy
threadpoolctl==3.1.0
# via scikit-learn
-tokenize-rt==5.0.0
- # via pyupgrade
toml==0.10.2
# via pre-commit
tomli==2.0.1
@@ -492,11 +475,12 @@ types-urllib3==1.26.25.4
# via types-requests
typing-extensions==4.4.0
# via
+ # black
# mypy
# myst-nb
# myst-parser
# pydantic
- # pypdf2
+ # pypdf
# setuptools-scm
urllib3==1.26.13
# via requests
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 91411c35..67d80236 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -48,8 +48,6 @@ deprecation==2.1.0
# via pikepdf
distro==1.8.0
# via tabula-py
-en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1.tar.gz
- # via sec-certs (./../pyproject.toml)
entrypoints==0.4
# via jupyter-client
executing==1.2.0
@@ -66,7 +64,7 @@ ipykernel==6.19.1
# via
# ipywidgets
# sec-certs (./../pyproject.toml)
-ipython==8.7.0
+ipython==8.10.0
# via
# ipykernel
# ipywidgets
@@ -179,6 +177,8 @@ ptyprocess==0.7.0
# via pexpect
pure-eval==0.2.2
# via stack-data
+pycryptodome==3.16.0
+ # via pypdf
pydantic==1.10.2
# via
# confection
@@ -188,7 +188,7 @@ pygments==2.13.0
# via ipython
pyparsing==3.0.9
# via matplotlib
-pypdf2==2.11.2
+pypdf[crypto]==3.2.1
# via sec-certs (./../pyproject.toml)
pyrsistent==0.19.2
# via jsonschema
@@ -236,9 +236,7 @@ smart-open==6.2.0
soupsieve==2.3.2.post1
# via beautifulsoup4
spacy==3.4.3
- # via
- # en-core-web-sm
- # sec-certs (./../pyproject.toml)
+ # via sec-certs (./../pyproject.toml)
spacy-legacy==3.0.10
# via spacy
spacy-loggers==1.0.4
@@ -282,7 +280,7 @@ typer==0.7.0
typing-extensions==4.4.0
# via
# pydantic
- # pypdf2
+ # pypdf
# setuptools-scm
urllib3==1.26.13
# via requests
diff --git a/requirements/test_requirements.txt b/requirements/test_requirements.txt
index f60c03ca..43373faf 100644
--- a/requirements/test_requirements.txt
+++ b/requirements/test_requirements.txt
@@ -54,8 +54,6 @@ deprecation==2.1.0
# via pikepdf
distro==1.8.0
# via tabula-py
-en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1.tar.gz
- # via sec-certs (./../pyproject.toml)
entrypoints==0.4
# via jupyter-client
exceptiongroup==1.0.4
@@ -76,7 +74,7 @@ ipykernel==6.19.1
# via
# ipywidgets
# sec-certs (./../pyproject.toml)
-ipython==8.7.0
+ipython==8.10.0
# via
# ipykernel
# ipywidgets
@@ -192,6 +190,8 @@ ptyprocess==0.7.0
# via pexpect
pure-eval==0.2.2
# via stack-data
+pycryptodome==3.16.0
+ # via pypdf
pydantic==1.10.2
# via
# confection
@@ -201,7 +201,7 @@ pygments==2.13.0
# via ipython
pyparsing==3.0.9
# via matplotlib
-pypdf2==2.11.2
+pypdf[crypto]==3.2.1
# via sec-certs (./../pyproject.toml)
pyrsistent==0.19.2
# via jsonschema
@@ -255,9 +255,7 @@ smart-open==6.2.0
soupsieve==2.3.2.post1
# via beautifulsoup4
spacy==3.4.3
- # via
- # en-core-web-sm
- # sec-certs (./../pyproject.toml)
+ # via sec-certs (./../pyproject.toml)
spacy-legacy==3.0.10
# via spacy
spacy-loggers==1.0.4
@@ -304,7 +302,7 @@ typer==0.7.0
typing-extensions==4.4.0
# via
# pydantic
- # pypdf2
+ # pypdf
# setuptools-scm
urllib3==1.26.13
# via requests
diff --git a/src/sec_certs/cert_rules.py b/src/sec_certs/cert_rules.py
index 145566f7..c126434b 100644
--- a/src/sec_certs/cert_rules.py
+++ b/src/sec_certs/cert_rules.py
@@ -205,21 +205,19 @@ def _load():
script_dir = Path(__file__).parent
filepath = script_dir / "rules.yaml"
with Path(filepath).open("r") as file:
- loaded = yaml.load(file, Loader=yaml.FullLoader)
- return loaded
+ return yaml.load(file, Loader=yaml.FullLoader)
-def _process(obj):
+def _process(obj: dict | list):
if isinstance(obj, dict):
return {k: _process(v) for k, v in obj.items()}
- elif isinstance(obj, list):
- return [
- re.compile(
- REGEXEC_SEP_START + MATCH_START + rule + MATCH_END + REGEXEC_SEP_END,
- re.MULTILINE,
- )
- for rule in obj
- ]
+ return [
+ re.compile(
+ REGEXEC_SEP_START + MATCH_START + rule + MATCH_END + REGEXEC_SEP_END,
+ re.MULTILINE,
+ )
+ for rule in obj
+ ]
rules = _load()
diff --git a/src/sec_certs/cli.py b/src/sec_certs/cli.py
index a25b55ee..08ef6abd 100644
--- a/src/sec_certs/cli.py
+++ b/src/sec_certs/cli.py
@@ -10,9 +10,9 @@ from typing import Callable
import click
-from sec_certs import constants
from sec_certs.config.configuration import config
from sec_certs.dataset import CCDataset, FIPSDataset
+from sec_certs.dataset.dataset import Dataset
from sec_certs.utils.helpers import warn_if_missing_poppler, warn_if_missing_tesseract
logger = logging.getLogger(__name__)
@@ -20,6 +20,8 @@ logger = logging.getLogger(__name__)
EXIT_CODE_NOK: int = 1
EXIT_CODE_OK: int = 0
+DEFAULT_OUTPUTPATH: Path = Path("./dataset").resolve()
+
@dataclass
class ProcessingStep:
@@ -29,6 +31,11 @@ class ProcessingStep:
precondition_error_msg: str | None = field(default=None)
pre_callback_func: Callable | None = field(default=None)
+ def __post_init__(self) -> None:
+ for condition in self.preconditions:
+ if not hasattr(Dataset.DatasetInternalState, condition):
+ raise ValueError(f"Precondition attribute {condition} is not member of `Dataset.DatasetInternalState`.")
+
def run(self, dset: CCDataset | FIPSDataset) -> None:
for condition in self.preconditions:
if not getattr(dset.state, condition):
@@ -54,12 +61,14 @@ def build_or_load_dataset(
framework: str,
inputpath: Path | None,
to_build: bool,
- outputpath: Path = constants.DUMMY_NONEXISTING_PATH,
+ outputpath: Path | None,
) -> CCDataset | FIPSDataset:
constructor: type[CCDataset] | type[FIPSDataset] = CCDataset if framework == "cc" else FIPSDataset
dset: CCDataset | FIPSDataset
if to_build:
+ if not outputpath:
+ outputpath = DEFAULT_OUTPUTPATH
if inputpath:
print(
f"Warning: you wanted to build a dataset but you provided one in JSON -- that will be ignored. New one will be constructed at: {outputpath}"
@@ -72,23 +81,56 @@ def build_or_load_dataset(
)
dset.get_certs_from_web()
else:
- if inputpath:
- dset = constructor.from_json(inputpath)
- if outputpath and dset.root_dir != outputpath:
- print(
- "Warning: you provided both input and output paths. The dataset from input path will get copied to output path."
- )
- dset.copy_dataset(outputpath)
- else:
+ if not inputpath:
click.echo(
"Error: If you do not use 'build' action, you must provide --input parameter to point to an existing dataset.",
err=True,
)
sys.exit(EXIT_CODE_NOK)
+ dset = constructor.from_json(inputpath)
+
+ if outputpath and dset.root_dir != outputpath:
+ print(
+ "Warning: you provided both input and output paths. The dataset from input path will get copied to output path."
+ )
+ dset.copy_dataset(outputpath)
+
return dset
+steps = [
+ ProcessingStep(
+ "process-aux-dsets",
+ "process_auxiliary_datasets",
+ preconditions=["meta_sources_parsed"],
+ precondition_error_msg="Error: You want to process the auxiliary datasets, but the data from cert. framework website was not parsed. You must use 'build' action first.",
+ pre_callback_func=None,
+ ),
+ ProcessingStep(
+ "download",
+ "download_all_artifacts",
+ preconditions=["meta_sources_parsed"],
+ precondition_error_msg="Error: You want to download all artifacts, but the data from the cert. framework website was not parsed. You must use 'build' action first.",
+ pre_callback_func=None,
+ ),
+ ProcessingStep(
+ "convert",
+ "convert_all_pdfs",
+ preconditions=["artifacts_downloaded"],
+ precondition_error_msg="Error: You want to convert pdfs -> txt, but the pdfs were not downloaded. You must use 'download' action first.",
+ pre_callback_func=warn_missing_libs,
+ ),
+ ProcessingStep(
+ "analyze",
+ "analyze_certificates",
+ preconditions=["pdfs_converted", "auxiliary_datasets_processed"],
+ precondition_error_msg="Error: You want to process txt documents of certificates, but pdfs were not converted. You must use 'convert' action first.",
+ pre_callback_func=None,
+ ),
+]
+
+
@click.command()
@click.argument(
"framework",
@@ -105,10 +147,9 @@ def build_or_load_dataset(
@click.option(
"-o",
"--output",
+ "outputpath",
type=click.Path(file_okay=False, dir_okay=True, writable=True, readable=True, resolve_path=True),
help="Path where the output of the experiment will be stored. May overwrite existing content.",
- default=Path("./dataset/"),
- show_default=True,
)
@click.option(
"-c",
@@ -129,10 +170,10 @@ def build_or_load_dataset(
def main(
framework: str,
actions: list[str],
- outputpath: Path,
- configpath: str | None,
+ outputpath: Path | None,
+ configpath: Path | None,
inputpath: Path | None,
- silent: bool,
+ quiet: bool,
):
try:
file_handler = logging.FileHandler(config.log_filepath)
@@ -140,13 +181,13 @@ def main(
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
file_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
- handlers: list[logging.StreamHandler] = [file_handler] if silent else [file_handler, stream_handler]
+ handlers: list[logging.StreamHandler] = [file_handler] if quiet else [file_handler, stream_handler]
logging.basicConfig(level=logging.INFO, handlers=handlers)
start = datetime.now()
if configpath:
try:
- config.load(Path(configpath))
+ config.load(configpath)
except FileNotFoundError:
click.echo("Error: Bad path to configuration file", err=True)
sys.exit(EXIT_CODE_NOK)
@@ -155,54 +196,24 @@ def main(
sys.exit(EXIT_CODE_NOK)
actions_set = (
- {"build", "process-aux-dsets", "download", "convert", "analyze", "maintenances"}
- if "all" in actions
- else set(actions)
+ {"build", "process-aux-dsets", "download", "convert", "analyze"} if "all" in actions else set(actions)
)
dset = build_or_load_dataset(framework, inputpath, "build" in actions_set, outputpath)
aux_dsets_to_handle = "PP, Maintenance updates" if framework == "cc" else "Algorithms"
aux_dsets_to_handle += "CPE, CVE"
- analysis_pre_callback = None
-
- steps = [
- ProcessingStep(
- "process-aux-dsets",
- "process_auxillary_datasets",
- preconditions=["meta_sources_parsed"],
- precondition_error_msg=f"Error: You want to process the auxillary datasets: {aux_dsets_to_handle} , but the data from cert. framework website was not parsed. You must use 'build' action first.",
- pre_callback_func=None,
- ),
- ProcessingStep(
- "download",
- "download_all_artifacts",
- preconditions=["meta_sources_parsed"],
- precondition_error_msg="Error: You want to download all artifacts, but the data from the cert. framework website was not parsed. You must use 'build' action first.",
- pre_callback_func=None,
- ),
- ProcessingStep(
- "convert",
- "convert_all_pdfs",
- preconditions=["pdfs_downloaded"],
- precondition_error_msg="Error: You want to convert pdfs -> txt, but the pdfs were not downloaded. You must use 'download' action first.",
- pre_callback_func=warn_missing_libs,
- ),
- ProcessingStep(
- "analyze",
- "analyze_certificates",
- preconditions=["pdfs_converted", "auxillary_datasets_processed"],
- precondition_error_msg="Error: You want to process txt documents of certificates, but pdfs were not converted. You must use 'convert' action first.",
- pre_callback_func=analysis_pre_callback,
- ),
- ]
processing_step: ProcessingStep
- for processing_step in [x for x in steps if x in actions_set]:
+ for processing_step in [x for x in steps if x.name in actions_set]:
processing_step.run(dset)
end = datetime.now()
logger.info(f"The computation took {(end-start)} seconds.")
- except Exception:
+ except Exception as e:
+ click.echo(
+ f"Unhandled exception: {e}",
+ err=True,
+ )
return EXIT_CODE_NOK
return EXIT_CODE_OK
diff --git a/src/sec_certs/config/settings-schema.json b/src/sec_certs/config/settings-schema.json
index a31f6b3f..2d5c17a6 100644
--- a/src/sec_certs/config/settings-schema.json
+++ b/src/sec_certs/config/settings-schema.json
@@ -96,7 +96,7 @@
{
"properties": {
"value": {
- "minimum": 1
+ "minimum": -1
}
}
}
diff --git a/src/sec_certs/config/settings.yaml b/src/sec_certs/config/settings.yaml
index 8d645758..c69f7cdf 100644
--- a/src/sec_certs/config/settings.yaml
+++ b/src/sec_certs/config/settings.yaml
@@ -13,8 +13,8 @@ year_difference_between_validations:
difference higher than _this_
value: 7
n_threads:
- description: How many threads to use for parallel computations
- value: 8
+ description: How many threads to use for parallel computations. Set to -1 to use all cores (*2 with multithreading).
+ value: -1
cpe_matching_threshold:
description: Level of required string similarity between CPE and certificate name on CC CPE matching, 0-100. Lower values yield more false negatives, higher values more false positives
value: 92
diff --git a/src/sec_certs/constants.py b/src/sec_certs/constants.py
index f71bb3c3..684662d0 100644
--- a/src/sec_certs/constants.py
+++ b/src/sec_certs/constants.py
@@ -74,7 +74,10 @@ CC_CANADA_INEVAL_URL = "https://www.cyber.gc.ca/en/tools-services/common-criteri
CC_ANSSI_BASE_URL = "https://www.ssi.gouv.fr"
CC_ANSSI_CERTIFIED_URL = CC_ANSSI_BASE_URL + "/en/products/certified-products/"
CC_BSI_BASE_URL = "https://www.bsi.bund.de/"
-CC_BSI_CERTIFIED_URL = CC_BSI_BASE_URL + "EN2021/Topics/Certification/certified_products/certified_products_node.html"
+CC_BSI_CERTIFIED_URL = (
+ CC_BSI_BASE_URL
+ + "EN/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Zertifizierung-und-Anerkennung/Listen/Zertifizierte-Produkte-nach-CC/zertifizierte-produkte-nach-cc_node.html"
+)
CC_INDIA_CERTIFIED_URL = "https://www.commoncriteria-india.gov.in/product-certified"
CC_INDIA_ARCHIVED_URL = "https://www.commoncriteria-india.gov.in/archived-prod-cer"
CC_ITALY_BASE_URL = "https://www.ocsi.gov.it"
diff --git a/src/sec_certs/dataset/__init__.py b/src/sec_certs/dataset/__init__.py
index d5f93c78..c557c630 100644
--- a/src/sec_certs/dataset/__init__.py
+++ b/src/sec_certs/dataset/__init__.py
@@ -1,4 +1,4 @@
-"""This package exposes Datasets of various Samples, both primary (Common Criteria, FIPS) and auxillary (CVEs, CPEs, ...)"""
+"""This package exposes Datasets of various Samples, both primary (Common Criteria, FIPS) and auxiliary (CVEs, CPEs, ...)"""
from sec_certs.dataset.cc import CCDataset, CCDatasetMaintenanceUpdates, CCSchemeDataset
from sec_certs.dataset.cpe import CPEDataset
diff --git a/src/sec_certs/dataset/cc.py b/src/sec_certs/dataset/cc.py
index 9894bd48..c2849772 100644
--- a/src/sec_certs/dataset/cc.py
+++ b/src/sec_certs/dataset/cc.py
@@ -21,7 +21,7 @@ from sec_certs import constants
from sec_certs.config.configuration import config
from sec_certs.dataset.cpe import CPEDataset
from sec_certs.dataset.cve import CVEDataset
-from sec_certs.dataset.dataset import AuxillaryDatasets, Dataset, logger
+from sec_certs.dataset.dataset import AuxiliaryDatasets, Dataset, logger
from sec_certs.dataset.protection_profile import ProtectionProfileDataset
from sec_certs.model.reference_finder import ReferenceFinder
from sec_certs.model.sar_transformer import SARTransformer
@@ -31,20 +31,20 @@ from sec_certs.sample.cc_certificate_id import CertificateId
from sec_certs.sample.cc_maintenance_update import CCMaintenanceUpdate
from sec_certs.sample.protection_profile import ProtectionProfile
from sec_certs.serialization.json import ComplexSerializableType, CustomJSONDecoder, serialize
-from sec_certs.utils import helpers as helpers
+from sec_certs.utils import helpers
from sec_certs.utils import parallel_processing as cert_processing
from sec_certs.utils.sanitization import sanitize_navigable_string as sns
@dataclass
-class CCAuxillaryDatasets(AuxillaryDatasets):
+class CCAuxiliaryDatasets(AuxiliaryDatasets):
cpe_dset: CPEDataset | None = None
cve_dset: CVEDataset | None = None
pp_dset: ProtectionProfileDataset | None = None
mu_dset: CCDatasetMaintenanceUpdates | None = None
-class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializableType):
+class CCDataset(Dataset[CCCertificate, CCAuxiliaryDatasets], ComplexSerializableType):
"""
Class that holds CCCertificate. Serializable into json, pandas, dictionary. Conveys basic certificate manipulations
and dataset transformations. Many private methods that perform internal operations, feel free to exploit them.
@@ -52,12 +52,12 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
def __init__(
self,
- certs: dict[str, CCCertificate] = dict(),
+ certs: dict[str, CCCertificate] = {},
root_dir: str | Path = constants.DUMMY_NONEXISTING_PATH,
name: str | None = None,
description: str = "",
state: Dataset.DatasetInternalState | None = None,
- auxillary_datasets: CCAuxillaryDatasets | None = None,
+ auxiliary_datasets: CCAuxiliaryDatasets | None = None,
):
self.certs = certs
self.timestamp = datetime.now()
@@ -66,8 +66,8 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
self.description = description if description else datetime.now().strftime("%d/%m/%Y %H:%M:%S")
self.state = state if state else self.DatasetInternalState()
- self.auxillary_datasets: CCAuxillaryDatasets = (
- auxillary_datasets if auxillary_datasets else CCAuxillaryDatasets()
+ self.auxiliary_datasets: CCAuxiliaryDatasets = (
+ auxiliary_datasets if auxiliary_datasets else CCAuxiliaryDatasets()
)
self.root_dir = Path(root_dir)
@@ -144,14 +144,14 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
"""
Returns directory that holds files associated with Protection profiles
"""
- return self.auxillary_datasets_dir / "pp_dataset.json"
+ return self.auxiliary_datasets_dir / "pp_dataset.json"
@property
def mu_dataset_dir(self) -> Path:
"""
Returns directory that holds dataset of maintenance updates
"""
- return self.auxillary_datasets_dir / "maintenances"
+ return self.auxiliary_datasets_dir / "maintenances"
@property
def mu_dataset_path(self) -> Path:
@@ -220,15 +220,15 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
def _set_local_paths(self):
super()._set_local_paths()
- if self.auxillary_datasets.pp_dset:
- self.auxillary_datasets.pp_dset.json_path = self.pp_dataset_path
+ if self.auxiliary_datasets.pp_dset:
+ self.auxiliary_datasets.pp_dset.json_path = self.pp_dataset_path
- if self.auxillary_datasets.mu_dset:
- self.auxillary_datasets.mu_dset.root_dir = self.mu_dataset_dir
+ if self.auxiliary_datasets.mu_dset:
+ self.auxiliary_datasets.mu_dset.root_dir = self.mu_dataset_dir
for cert in self:
cert.set_local_paths(self.reports_pdf_dir, self.targets_pdf_dir, self.reports_txt_dir, self.targets_txt_dir)
- # TODO: This forgets to set local paths for other auxillary datasets
+ # TODO: This forgets to set local paths for other auxiliary datasets
def _merge_certs(self, certs: dict[str, CCCertificate], cert_source: str | None = None) -> None:
"""
@@ -324,13 +324,9 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
return CCDataset.BASE_URL + relative_path
def _get_primary_key_str(row: Tag):
- prim_key = row["category"] + row["cert_name"] + row["report_link"]
- return prim_key
+ return row["category"] + row["cert_name"] + row["report_link"]
- if "active" in str(file):
- cert_status = "active"
- else:
- cert_status = "archived"
+ cert_status = "active" if "active" in str(file) else "archived"
csv_header = [
"category",
@@ -394,7 +390,7 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
)
)
- certs = {
+ return {
x.dgst: CCCertificate(
cert_status,
x.category,
@@ -416,7 +412,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
)
for x in df_base.itertuples()
}
- return certs
def _get_all_certs_from_html(self, get_active: bool, get_archived: bool) -> dict[str, CCCertificate]:
"""
@@ -483,10 +478,7 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
return table_certs
- if "active" in str(file):
- cert_status = "active"
- else:
- cert_status = "archived"
+ cert_status = "active" if "active" in str(file) else "archived"
cc_cat_abbreviations = ["AC", "BP", "DP", "DB", "DD", "IC", "KM", "MD", "MF", "NS", "OS", "OD", "DG", "TC"]
cc_table_ids = ["tbl" + x for x in cc_cat_abbreviations]
@@ -535,7 +527,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
cert_processing.process_parallel(
CCCertificate.download_pdf_report,
certs_to_process,
- config.n_threads,
progress_bar_desc="Downloading PDFs of CC certification reports",
)
@@ -553,7 +544,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
cert_processing.process_parallel(
CCCertificate.download_pdf_st,
certs_to_process,
- config.n_threads,
progress_bar_desc="Downloading PDFs of CC security targets",
)
@@ -571,7 +561,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
cert_processing.process_parallel(
CCCertificate.convert_report_pdf,
certs_to_process,
- config.n_threads,
progress_bar_desc="Converting PDFs of certification reports to txt",
)
@@ -589,7 +578,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
cert_processing.process_parallel(
CCCertificate.convert_st_pdf,
certs_to_process,
- config.n_threads,
progress_bar_desc="Converting PDFs of security targets to txt",
)
@@ -603,7 +591,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
processed_certs = cert_processing.process_parallel(
CCCertificate.extract_report_pdf_metadata,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting report metadata",
)
@@ -615,7 +602,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
processed_certs = cert_processing.process_parallel(
CCCertificate.extract_st_pdf_metadata,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting target metadata",
)
@@ -631,7 +617,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
processed_certs = cert_processing.process_parallel(
CCCertificate.extract_report_pdf_frontpage,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting report frontpages",
)
@@ -643,7 +628,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
processed_certs = cert_processing.process_parallel(
CCCertificate.extract_st_pdf_frontpage,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting target frontpages",
)
@@ -659,7 +643,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
processed_certs = cert_processing.process_parallel(
CCCertificate.extract_report_pdf_keywords,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting report keywords",
)
@@ -671,7 +654,6 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
processed_certs = cert_processing.process_parallel(
CCCertificate.extract_st_pdf_keywords,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting target keywords",
)
@@ -729,7 +711,7 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
return set()
res = set()
for scheme, matches in kws["cc_cert_id"].items():
- for match in matches.keys():
+ for match in matches:
try:
canonical = CertificateId(scheme, match).canonical
res.add(canonical)
@@ -750,16 +732,16 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
for dgst in self.certs:
setattr(self.certs[dgst].heuristics, dep_attr, finder.predict_single_cert(dgst, keep_unknowns=False))
- def process_auxillary_datasets(self, download_fresh: bool = False) -> None:
+ @serialize
+ def process_auxiliary_datasets(self, download_fresh: bool = False) -> None:
"""
- Processes all auxillary datasets needed during computation. On top of base-class processing,
+ Processes all auxiliary datasets needed during computation. On top of base-class processing,
CC handles protection profiles and maintenance updates.
"""
- super().process_auxillary_datasets(download_fresh)
- self.auxillary_datasets.pp_dset = self.process_protection_profiles(to_download=download_fresh)
- self.auxillary_datasets.mu_dset = self.process_maintenance_updates(to_download=download_fresh)
+ super().process_auxiliary_datasets(download_fresh)
+ self.auxiliary_datasets.pp_dset = self.process_protection_profiles(to_download=download_fresh)
+ self.auxiliary_datasets.mu_dset = self.process_maintenance_updates(to_download=download_fresh)
- @serialize
def process_protection_profiles(
self, to_download: bool = True, keep_metadata: bool = True
) -> ProtectionProfileDataset:
@@ -773,7 +755,7 @@ class CCDataset(Dataset[CCCertificate, CCAuxillaryDatasets], ComplexSerializable
"""
logger.info("Processing protection profiles.")
- self.auxillary_datasets_dir.mkdir(parents=True, exist_ok=True)
+ self.auxiliary_datasets_dir.mkdir(parents=True, exist_ok=True)
if to_download or not self.pp_dataset_path.exists():
pp_dataset = ProtectionProfileDataset.from_web(self.pp_dataset_path)
@@ -829,7 +811,7 @@ class CCDatasetMaintenanceUpdates(CCDataset, ComplexSerializableType):
# Quite difficult to achieve correct behaviour with MyPy here, opting for ignore
def __init__(
self,
- certs: dict[str, CCMaintenanceUpdate] = dict(), # type: ignore
+ certs: dict[str, CCMaintenanceUpdate] = {}, # type: ignore
root_dir: Path = constants.DUMMY_NONEXISTING_PATH,
name: str = "dataset name",
description: str = "dataset_description",
@@ -851,7 +833,7 @@ class CCDatasetMaintenanceUpdates(CCDataset, ComplexSerializableType):
def compute_related_cves(self) -> None:
raise NotImplementedError
- def process_auxillary_datasets(self, download_fresh: bool = False) -> None:
+ def process_auxiliary_datasets(self, download_fresh: bool = False) -> None:
raise NotImplementedError
def analyze_certificates(self) -> None:
@@ -876,9 +858,7 @@ class CCDatasetMaintenanceUpdates(CCDataset, ComplexSerializableType):
df.index.name = "dgst"
df.maintenance_date = pd.to_datetime(df.maintenance_date, infer_datetime_format=True)
- df = df.fillna(value=np.nan)
-
- return df
+ return df.fillna(value=np.nan)
@classmethod
def from_web_latest(cls) -> CCDatasetMaintenanceUpdates:
@@ -913,11 +893,8 @@ class CCDatasetMaintenanceUpdates(CCDataset, ComplexSerializableType):
class CCSchemeDataset:
@staticmethod
def _download_page(url, session=None):
- if session:
- conn = session
- else:
- conn = requests
- resp = conn.get(url, headers={"User-Agent": "seccerts.org"})
+ conn = session if session else requests
+ resp = conn.get(url, headers={"User-Agent": "seccerts.org"}, verify=False)
if resp.status_code != requests.codes.ok:
raise ValueError(f"Unable to download: status={resp.status_code}")
return BeautifulSoup(resp.content, "html5lib")
@@ -1056,14 +1033,14 @@ class CCSchemeDataset:
pager = soup.find("ul", class_="pager")
for li in pager.find_all("li"):
try:
- new_page = int(li.text)
+ new_page = int(li.text) - 1
except Exception:
continue
if new_page not in seen_pages:
pages.add(new_page)
# Parse table
- tbody = soup.find("div", class_="content").find("table").find("tbody")
+ tbody = soup.find("div", class_="view-content").find("table").find("tbody")
for tr in tbody.find_all("tr"):
tds = tr.find_all("td")
if not tds:
@@ -1100,16 +1077,17 @@ class CCSchemeDataset:
# Update pages
pager = soup.find("ul", class_="pager")
- for li in pager.find_all("li"):
- try:
- new_page = int(li.text)
- except Exception:
- continue
- if new_page not in seen_pages:
- pages.add(new_page)
+ if pager:
+ for li in pager.find_all("li"):
+ try:
+ new_page = int(li.text) - 1
+ except Exception:
+ continue
+ if new_page not in seen_pages:
+ pages.add(new_page)
# Parse table
- tbody = soup.find("div", class_="content").find("table").find("tbody")
+ tbody = soup.find("div", class_="view-content").find("table").find("tbody")
for tr in tbody.find_all("tr"):
tds = tr.find_all("td")
if not tds:
diff --git a/src/sec_certs/dataset/cpe.py b/src/sec_certs/dataset/cpe.py
index 927ce674..1a20c71e 100644
--- a/src/sec_certs/dataset/cpe.py
+++ b/src/sec_certs/dataset/cpe.py
@@ -40,9 +40,9 @@ class CPEDataset(JSONPathDataset, ComplexSerializableType):
self.cpes = cpes
self.json_path = Path(json_path)
- self.vendor_to_versions: dict[str, set[str]] = dict()
- self.vendor_version_to_cpe: dict[tuple[str, str], set[CPE]] = dict()
- self.title_to_cpes: dict[str, set[CPE]] = dict()
+ self.vendor_to_versions: dict[str, set[str]] = {}
+ self.vendor_version_to_cpe: dict[tuple[str, str], set[CPE]] = {}
+ self.title_to_cpes: dict[str, set[CPE]] = {}
self.vendors: set[str] = set()
self.build_lookup_dicts()
@@ -62,7 +62,7 @@ class CPEDataset(JSONPathDataset, ComplexSerializableType):
def __contains__(self, item: CPE) -> bool:
if not isinstance(item, CPE):
raise ValueError(f"{item} is not of CPE class")
- return item.uri in self.cpes.keys() and self.cpes[item.uri] == item
+ return item.uri in self.cpes and self.cpes[item.uri] == item
def __eq__(self, other: object) -> bool:
return isinstance(other, CPEDataset) and self.cpes == other.cpes
@@ -77,8 +77,8 @@ class CPEDataset(JSONPathDataset, ComplexSerializableType):
"""
logger.info("CPE dataset: building lookup dictionaries.")
self.vendor_to_versions = {x.vendor: set() for x in self}
- self.vendor_version_to_cpe = dict()
- self.title_to_cpes = dict()
+ self.vendor_version_to_cpe = {}
+ self.title_to_cpes = {}
self.vendors = set(self.vendor_to_versions.keys())
for cpe in self:
self.vendor_to_versions[cpe.vendor].add(cpe.version)
@@ -141,9 +141,7 @@ class CPEDataset(JSONPathDataset, ComplexSerializableType):
:return pd.DataFrame: the resulting DataFrame
"""
- df = pd.DataFrame([x.pandas_tuple for x in self], columns=CPE.pandas_columns)
- df = df.set_index("uri")
- return df
+ return pd.DataFrame([x.pandas_tuple for x in self], columns=CPE.pandas_columns).set_index("uri")
@serialize
def enhance_with_cpes_from_cve_dataset(self, cve_dset: CVEDataset | str | Path) -> None:
@@ -165,7 +163,7 @@ class CPEDataset(JSONPathDataset, ComplexSerializableType):
and (considered_cpe.vendor, considered_cpe.item_name) not in vndr_item_lookup
):
return True
- elif (
+ if (
considered_cpe.version != constants.CPE_VERSION_NA
and (considered_cpe.vendor, considered_cpe.item_name, considered_cpe.version)
not in vndr_item_version_lookup
diff --git a/src/sec_certs/dataset/cve.py b/src/sec_certs/dataset/cve.py
index 9a36fa6b..65d141c3 100644
--- a/src/sec_certs/dataset/cve.py
+++ b/src/sec_certs/dataset/cve.py
@@ -14,13 +14,12 @@ from typing import ClassVar
import numpy as np
import pandas as pd
-import sec_certs.constants as constants
-import sec_certs.utils.helpers as helpers
-from sec_certs.config.configuration import config
+from sec_certs import constants
from sec_certs.dataset.json_path_dataset import JSONPathDataset
from sec_certs.sample.cpe import CPE, cached_cpe
from sec_certs.sample.cve import CVE
from sec_certs.serialization.json import ComplexSerializableType
+from sec_certs.utils import helpers
from sec_certs.utils.parallel_processing import process_parallel
from sec_certs.utils.tqdm import tqdm
@@ -34,7 +33,7 @@ class CVEDataset(JSONPathDataset, ComplexSerializableType):
def __init__(self, cves: dict[str, CVE], json_path: str | Path = constants.DUMMY_NONEXISTING_PATH):
self.cves = cves
self.json_path = Path(json_path)
- self.cpe_to_cve_ids_lookup: dict[str, set[str]] = dict()
+ self.cpe_to_cve_ids_lookup: dict[str, set[str]] = {}
self.cves_with_vulnerable_configurations: list[CVE] = []
@property
@@ -72,7 +71,7 @@ class CVEDataset(JSONPathDataset, ComplexSerializableType):
- CPE(uri='cpe:2.3:a:bayashi:dopvcomet\\*:0009:b:*:*:*:*:*:*', title=None, version='0009', vendor='bayashi', item_name='dopvcomet\\*', start_version=None, end_version=None)
- CPE(uri='cpe:2.3:a:bayashi:dopvstar\\*:0091:*:*:*:*:*:*:*', title=None, version='0091', vendor='bayashi', item_name='dopvstar\\*', start_version=None, end_version=None)
"""
- self.cpe_to_cve_ids_lookup = dict()
+ self.cpe_to_cve_ids_lookup = {}
self.cves = {x.cve_id.upper(): x for x in self}
logger.info("Getting CPE matching dictionary from NIST.gov")
@@ -134,12 +133,11 @@ class CVEDataset(JSONPathDataset, ComplexSerializableType):
cls.download_cves(tmp_dir, start_year, end_year)
json_files = glob.glob(tmp_dir + "/*.json")
- all_cves = dict()
+ all_cves = {}
logger.info("Downloaded required resources. Building CVEDataset from jsons.")
results = process_parallel(
cls.from_nist_json,
json_files,
- config.n_threads,
use_threading=False,
progress_bar_desc="Building CVEDataset from jsons",
)
@@ -247,7 +245,7 @@ class CVEDataset(JSONPathDataset, ComplexSerializableType):
with input_filepath.open("r") as handle:
match_data = json.load(handle)
- mapping_dict = dict()
+ mapping_dict = {}
for match in tqdm(match_data["matches"], desc="parsing cpe matching (by NIST) dictionary"):
key = parse_key_cpe(match)
value = parse_values_cpe(match)
diff --git a/src/sec_certs/dataset/dataset.py b/src/sec_certs/dataset/dataset.py
index a7120cef..9481d9f7 100644
--- a/src/sec_certs/dataset/dataset.py
+++ b/src/sec_certs/dataset/dataset.py
@@ -14,8 +14,7 @@ from typing import Any, Generic, Iterator, TypeVar, cast
import pandas as pd
-import sec_certs.constants as constants
-import sec_certs.utils.helpers as helpers
+from sec_certs import constants
from sec_certs.config.configuration import config
from sec_certs.dataset.cpe import CPEDataset
from sec_certs.dataset.cve import CVEDataset
@@ -23,23 +22,24 @@ from sec_certs.model.cpe_matching import CPEClassifier
from sec_certs.sample.certificate import Certificate
from sec_certs.sample.cpe import CPE
from sec_certs.serialization.json import ComplexSerializableType, get_class_fullname, serialize
+from sec_certs.utils import helpers
from sec_certs.utils.tqdm import tqdm
logger = logging.getLogger(__name__)
@dataclass
-class AuxillaryDatasets:
+class AuxiliaryDatasets:
cpe_dset: CPEDataset | None = None
cve_dset: CVEDataset | None = None
CertSubType = TypeVar("CertSubType", bound=Certificate)
-AuxillaryDatasetsSubType = TypeVar("AuxillaryDatasetsSubType", bound=AuxillaryDatasets)
+AuxiliaryDatasetsSubType = TypeVar("AuxiliaryDatasetsSubType", bound=AuxiliaryDatasets)
DatasetSubType = TypeVar("DatasetSubType", bound="Dataset")
-class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializableType, ABC):
+class Dataset(Generic[CertSubType, AuxiliaryDatasetsSubType], ComplexSerializableType, ABC):
"""
Base class for dataset of certificates from CC and FIPS 140 schemes. Layouts public
functions, the processing pipeline and common operations on the dataset and certs.
@@ -50,20 +50,17 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
meta_sources_parsed: bool = False
artifacts_downloaded: bool = False
pdfs_converted: bool = False
- auxillary_datasets_processed: bool = False
+ auxiliary_datasets_processed: bool = False
certs_analyzed: bool = False
- def __bool__(self):
- return any(vars(self))
-
def __init__(
self,
- certs: dict[str, CertSubType] = dict(),
+ certs: dict[str, CertSubType] = {},
root_dir: str | Path = constants.DUMMY_NONEXISTING_PATH,
name: str | None = None,
description: str = "",
state: DatasetInternalState | None = None,
- auxillary_datasets: AuxillaryDatasetsSubType | None = None,
+ auxiliary_datasets: AuxiliaryDatasetsSubType | None = None,
):
self.certs = certs
@@ -73,10 +70,10 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
self.description = description if description else "No description provided"
self.state = state if state else self.DatasetInternalState()
- if not auxillary_datasets:
- self.auxillary_datasets = AuxillaryDatasets()
+ if not auxiliary_datasets:
+ self.auxiliary_datasets = AuxiliaryDatasets()
else:
- self.auxillary_datasets = auxillary_datasets
+ self.auxiliary_datasets = auxiliary_datasets
self.root_dir = Path(root_dir)
@@ -108,11 +105,11 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
return self.root_dir / "web"
@property
- def auxillary_datasets_dir(self) -> Path:
+ def auxiliary_datasets_dir(self) -> Path:
"""
- Path to directory with auxillary datasets.
+ Path to directory with auxiliary datasets.
"""
- return self.root_dir / "auxillary_datasets"
+ return self.root_dir / "auxiliary_datasets"
@property
def certs_dir(self) -> Path:
@@ -123,15 +120,15 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
@property
def cpe_dataset_path(self) -> Path:
- return self.auxillary_datasets_dir / "cpe_dataset.json"
+ return self.auxiliary_datasets_dir / "cpe_dataset.json"
@property
def cve_dataset_path(self) -> Path:
- return self.auxillary_datasets_dir / "cve_dataset.json"
+ return self.auxiliary_datasets_dir / "cve_dataset.json"
@property
def nist_cve_cpe_matching_dset_path(self) -> Path:
- return self.auxillary_datasets_dir / "nvdcpematch-1.0.json"
+ return self.auxiliary_datasets_dir / "nvdcpematch-1.0.json"
@property
def json_path(self) -> Path:
@@ -205,10 +202,10 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
return dset
def _set_local_paths(self) -> None:
- if self.auxillary_datasets.cpe_dset:
- self.auxillary_datasets.cpe_dset.json_path = self.cpe_dataset_path
- if self.auxillary_datasets.cve_dset:
- self.auxillary_datasets.cve_dset.json_path = self.cve_dataset_path
+ if self.auxiliary_datasets.cpe_dset:
+ self.auxiliary_datasets.cpe_dset.json_path = self.cpe_dataset_path
+ if self.auxiliary_datasets.cve_dset:
+ self.auxiliary_datasets.cve_dset.json_path = self.cve_dataset_path
def move_dataset(self, new_root_dir: str | Path) -> None:
"""
@@ -249,15 +246,15 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
@serialize
@abstractmethod
- def process_auxillary_datasets(self, download_fresh: bool = False) -> None:
+ def process_auxiliary_datasets(self, download_fresh: bool = False) -> None:
"""
- Processes all auxillary datasets (CPE, CVE, ...) that are required during computation.
+ Processes all auxiliary datasets (CPE, CVE, ...) that are required during computation.
"""
- logger.info("Processing auxillary datasets.")
- self.auxillary_datasets_dir.mkdir(parents=True, exist_ok=True)
- self.auxillary_datasets.cpe_dset = self._prepare_cpe_dataset(download_fresh)
- self.auxillary_datasets.cve_dset = self._prepare_cve_dataset(download_fresh_cves=download_fresh)
- self.state.auxillary_datasets_processed = True
+ logger.info("Processing auxiliary datasets.")
+ self.auxiliary_datasets_dir.mkdir(parents=True, exist_ok=True)
+ self.auxiliary_datasets.cpe_dset = self._prepare_cpe_dataset(download_fresh)
+ self.auxiliary_datasets.cve_dset = self._prepare_cve_dataset(download_fresh_cves=download_fresh)
+ self.state.auxiliary_datasets_processed = True
@serialize
def download_all_artifacts(self, fresh: bool = True) -> None:
@@ -311,9 +308,9 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
"Attempting run analysis of txt files while not having the pdf->txt conversion done. Returning."
)
return
- if not self.state.auxillary_datasets_processed:
+ if not self.state.auxiliary_datasets_processed:
logger.info(
- "Attempting to run analysis of certifies while not having the auxillary datasets processed. Returning."
+ "Attempting to run analysis of certifies while not having the auxiliary datasets processed. Returning."
)
logger.info("Analyzing certificates.")
@@ -345,8 +342,8 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
def _prepare_cpe_dataset(self, download_fresh_cpes: bool = False) -> CPEDataset:
logger.info("Preparing CPE dataset.")
- if not self.auxillary_datasets_dir.exists():
- self.auxillary_datasets_dir.mkdir(parents=True)
+ if not self.auxiliary_datasets_dir.exists():
+ self.auxiliary_datasets_dir.mkdir(parents=True)
if not self.cpe_dataset_path.exists() or download_fresh_cpes is True:
cpe_dataset = CPEDataset.from_web(self.cpe_dataset_path)
@@ -360,8 +357,8 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
self, download_fresh_cves: bool = False, use_nist_cpe_matching_dict: bool = True
) -> CVEDataset:
logger.info("Preparing CVE dataset.")
- if not self.auxillary_datasets_dir.exists():
- self.auxillary_datasets_dir.mkdir(parents=True)
+ if not self.auxiliary_datasets_dir.exists():
+ self.auxiliary_datasets_dir.mkdir(parents=True)
if not self.cve_dataset_path.exists() or download_fresh_cves is True:
cve_dataset = CVEDataset.from_web(json_path=self.cve_dataset_path)
@@ -392,32 +389,32 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
and not any(char.isdigit() for char in cpe.title)
):
return False
- elif (
+ if (
not cpe.title
and cpe.item_name
and (cpe.version == "-" or cpe.version == "*")
and not any(char.isdigit() for char in cpe.item_name)
):
return False
- elif re.match(constants.RELEASE_CANDIDATE_REGEX, cpe.update):
+ if re.match(constants.RELEASE_CANDIDATE_REGEX, cpe.update):
return False
- elif cpe in WINDOWS_WEAK_CPES:
+ if cpe in WINDOWS_WEAK_CPES:
return False
return True
logger.info("Computing heuristics: Finding CPE matches for certificates")
- if not self.auxillary_datasets.cpe_dset or download_fresh_cpes:
- self.auxillary_datasets.cpe_dset = self._prepare_cpe_dataset(download_fresh_cpes)
+ if not self.auxiliary_datasets.cpe_dset or download_fresh_cpes:
+ self.auxiliary_datasets.cpe_dset = self._prepare_cpe_dataset(download_fresh_cpes)
# Temporarily disabled, see: https://github.com/crocs-muni/sec-certs/issues/173
# if not cpe_dset.was_enhanced_with_vuln_cpes:
- # self.auxillary_datasets.cve_dset = self._prepare_cve_dataset(download_fresh_cves=False)
- # self.auxillary_datasets.cpe_dset.enhance_with_cpes_from_cve_dataset(cve_dset) # this also calls build_lookup_dicts() on cpe_dset
+ # self.auxiliary_datasets.cve_dset = self._prepare_cve_dataset(download_fresh_cves=False)
+ # self.auxiliary_datasets.cpe_dset.enhance_with_cpes_from_cve_dataset(cve_dset) # this also calls build_lookup_dicts() on cpe_dset
# else:
- # self.auxillary_datasets.cpe_dset.build_lookup_dicts()
+ # self.auxiliary_datasets.cpe_dset.build_lookup_dicts()
clf = CPEClassifier(config.cpe_matching_threshold, config.cpe_n_max_matches)
- clf.fit([x for x in self.auxillary_datasets.cpe_dset if filter_condition(x)])
+ clf.fit([x for x in self.auxiliary_datasets.cpe_dset if filter_condition(x)])
cert: CertSubType
for cert in tqdm(self, desc="Predicting CPE matches with the classifier"):
@@ -456,9 +453,7 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
logger.info("Translating label studio matches into their CPE representations and assigning to certificates.")
for annotation in tqdm(data, desc="Translating label studio matches"):
- cpe_candidate_keys = {
- key for key in annotation.keys() if "option_" in key and annotation[key] != "No good match"
- }
+ cpe_candidate_keys = {key for key in annotation if "option_" in key and annotation[key] != "No good match"}
if "verified_cpe_match" not in annotation:
incorrect_keys: set[str] = set()
@@ -517,8 +512,8 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
Computes CVEs for the certificates, given their CPE matches.
"""
logger.info("Retrieving related CVEs to verified CPE matches")
- if download_fresh_cves or not self.auxillary_datasets.cve_dset:
- self.auxillary_datasets.cve_dset = self._prepare_cve_dataset(
+ if download_fresh_cves or not self.auxiliary_datasets.cve_dset:
+ self.auxiliary_datasets.cve_dset = self._prepare_cve_dataset(
download_fresh_cves, use_nist_cpe_matching_dict
)
@@ -533,12 +528,12 @@ class Dataset(Generic[CertSubType, AuxillaryDatasetsSubType], ComplexSerializabl
return
relevant_cpes = set(itertools.chain.from_iterable(x.heuristics.cpe_matches for x in cpe_rich_certs))
- self.auxillary_datasets.cve_dset.filter_related_cpes(relevant_cpes)
+ self.auxiliary_datasets.cve_dset.filter_related_cpes(relevant_cpes)
cert: Certificate
for cert in tqdm(cpe_rich_certs, desc="Computing related CVES"):
if cert.heuristics.cpe_matches:
- related_cves = self.auxillary_datasets.cve_dset.get_cves_from_matched_cpes(cert.heuristics.cpe_matches)
+ related_cves = self.auxiliary_datasets.cve_dset.get_cves_from_matched_cpes(cert.heuristics.cpe_matches)
if related_cves:
cert.heuristics.related_cves = related_cves
diff --git a/src/sec_certs/dataset/fips.py b/src/sec_certs/dataset/fips.py
index 14571f93..ce7563a0 100644
--- a/src/sec_certs/dataset/fips.py
+++ b/src/sec_certs/dataset/fips.py
@@ -15,7 +15,7 @@ from sec_certs import constants
from sec_certs.config.configuration import config
from sec_certs.dataset.cpe import CPEDataset
from sec_certs.dataset.cve import CVEDataset
-from sec_certs.dataset.dataset import AuxillaryDatasets, Dataset
+from sec_certs.dataset.dataset import AuxiliaryDatasets, Dataset
from sec_certs.dataset.fips_algorithm import FIPSAlgorithmDataset
from sec_certs.model.reference_finder import ReferenceFinder
from sec_certs.model.transitive_vulnerability_finder import TransitiveVulnerabilityFinder
@@ -28,25 +28,25 @@ from sec_certs.utils.helpers import fips_dgst
logger = logging.getLogger(__name__)
-class FIPSAuxillaryDatasets(AuxillaryDatasets):
+class FIPSAuxiliaryDatasets(AuxiliaryDatasets):
cpe_dset: CPEDataset | None = None
cve_dset: CVEDataset | None = None
algorithm_dset: FIPSAlgorithmDataset | None = None
-class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerializableType):
+class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxiliaryDatasets], ComplexSerializableType):
"""
Class for processing of FIPSCertificate samples. Inherits from `ComplexSerializableType` and base abstract `Dataset` class.
"""
def __init__(
self,
- certs: dict[str, FIPSCertificate] = dict(),
+ certs: dict[str, FIPSCertificate] = {},
root_dir: str | Path = constants.DUMMY_NONEXISTING_PATH,
name: str | None = None,
description: str = "",
state: Dataset.DatasetInternalState | None = None,
- auxillary_datasets: FIPSAuxillaryDatasets | None = None,
+ auxiliary_datasets: FIPSAuxiliaryDatasets | None = None,
):
self.certs = certs
self.timestamp = datetime.datetime.now()
@@ -54,8 +54,8 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
self.name = name if name else type(self).__name__ + " dataset"
self.description = description if description else datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
self.state = state if state else self.DatasetInternalState()
- self.auxillary_datasets: FIPSAuxillaryDatasets = (
- auxillary_datasets if auxillary_datasets else FIPSAuxillaryDatasets()
+ self.auxiliary_datasets: FIPSAuxiliaryDatasets = (
+ auxiliary_datasets if auxiliary_datasets else FIPSAuxiliaryDatasets()
)
self.root_dir = Path(root_dir)
@@ -84,7 +84,7 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
@property
def algorithm_dataset_path(self) -> Path:
- return self.auxillary_datasets_dir / "algorithms.json"
+ return self.auxiliary_datasets_dir / "algorithms.json"
def __getitem__(self, item: str) -> FIPSCertificate:
try:
@@ -102,7 +102,6 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
processed_certs = cert_processing.process_parallel(
FIPSCertificate.parse_html_module,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting data from html modules",
)
@@ -126,7 +125,6 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
processed_certs = cert_processing.process_parallel(
FIPSCertificate.extract_policy_pdf_keywords,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting keywords from policy pdfs",
)
@@ -148,7 +146,6 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
cert_processing.process_parallel(
FIPSCertificate.download_module,
certs_to_process,
- config.n_threads,
progress_bar_desc="Downloading HTML modules",
)
@@ -164,7 +161,6 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
cert_processing.process_parallel(
FIPSCertificate.download_policy,
certs_to_process,
- config.n_threads,
progress_bar_desc="Downloading PDF security policies",
)
@@ -185,25 +181,24 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
cert_processing.process_parallel(
FIPSCertificate.convert_policy_pdf,
certs_to_process,
- config.n_threads,
progress_bar_desc="Converting policies to pdf",
)
def _download_html_resources(self) -> None:
logger.info("Downloading HTML files that list FIPS certificates.")
html_urls = list(FIPSDataset.LIST_OF_CERTS_HTML.values())
- html_paths = [self.web_dir / x for x in FIPSDataset.LIST_OF_CERTS_HTML.keys()]
+ html_paths = [self.web_dir / x for x in FIPSDataset.LIST_OF_CERTS_HTML]
helpers.download_parallel(html_urls, html_paths)
def _get_all_certs_from_html_sources(self) -> list[FIPSCertificate]:
return list(
itertools.chain.from_iterable(
- self._get_certificates_from_html(self.web_dir / x) for x in self.LIST_OF_CERTS_HTML.keys()
+ self._get_certificates_from_html(self.web_dir / x) for x in self.LIST_OF_CERTS_HTML
)
)
def _get_certificates_from_html(self, html_file: Path) -> list[FIPSCertificate]:
- with open(html_file, encoding="utf-8") as handle:
+ with html_file.open("r", encoding="utf-8") as handle:
html = BeautifulSoup(handle.read(), "html5lib")
table = [x for x in html.find(id="searchResultsTable").tbody.contents if x != "\n"]
@@ -216,7 +211,7 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
if cert_id not in cert_ids:
cert_ids.add(cert_id)
- return [FIPSCertificate(cert_id) for cert_id in cert_ids]
+ return [FIPSCertificate(int(cert_id)) for cert_id in cert_ids]
@classmethod
def from_web_latest(cls) -> FIPSDataset:
@@ -227,8 +222,8 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
def _set_local_paths(self) -> None:
super()._set_local_paths()
- if self.auxillary_datasets.algorithm_dset:
- self.auxillary_datasets.algorithm_dset.json_path = self.algorithm_dataset_path
+ if self.auxiliary_datasets.algorithm_dset:
+ self.auxiliary_datasets.algorithm_dset.json_path = self.algorithm_dataset_path
cert: FIPSCertificate
for cert in self.certs.values():
@@ -252,9 +247,9 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
self.state.meta_sources_parsed = True
@serialize
- def process_auxillary_datasets(self, download_fresh: bool = False) -> None:
- super().process_auxillary_datasets(download_fresh)
- self.auxillary_datasets.algorithm_dset = self._prepare_algorithm_dataset(download_fresh)
+ def process_auxiliary_datasets(self, download_fresh: bool = False) -> None:
+ super().process_auxiliary_datasets(download_fresh)
+ self.auxiliary_datasets.algorithm_dset = self._prepare_algorithm_dataset(download_fresh)
def _prepare_algorithm_dataset(self, download_fresh_algs: bool = False) -> FIPSAlgorithmDataset:
logger.info("Preparing FIPSAlgorithm dataset.")
@@ -272,7 +267,6 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
cert_processing.process_parallel(
FIPSCertificate.get_algorithms_from_policy_tables,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting Algorithms from policy tables",
)
@@ -283,7 +277,6 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
processed_certs = cert_processing.process_parallel(
FIPSCertificate.extract_policy_pdf_metadata,
certs_to_process,
- config.n_threads,
use_threading=False,
progress_bar_desc="Extracting security policy metadata",
)
@@ -291,7 +284,7 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
def _compute_transitive_vulnerabilities(self) -> None:
logger.info("Computing heuristics: Computing transitive vulnerabilities in referenc(ed/ing) certificates.")
- transitive_cve_finder = TransitiveVulnerabilityFinder(lambda cert: cert.cert_id)
+ transitive_cve_finder = TransitiveVulnerabilityFinder(lambda cert: str(cert.cert_id))
transitive_cve_finder.fit(self.certs, lambda cert: cert.heuristics.policy_processed_references)
for dgst in self.certs:
@@ -304,7 +297,7 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
cert.prune_referenced_cert_ids()
# Previously, a following procedure was used to prune reference_candidates:
- # - A set of algorithms was obtained via self.auxillary_datasets.algorithm_dset.get_algorithms_by_id(reference_candidate)
+ # - A set of algorithms was obtained via self.auxiliary_datasets.algorithm_dset.get_algorithms_by_id(reference_candidate)
# - If any of these algorithms had the same vendor as the reference_candidate, the candidate was rejected
# - The rationale is that if an ID appears in a certificate s.t. an algorithm with the same ID was produced by the same vendor, the reference likely refers to alg.
# - Such reference should then be discarded.
@@ -316,12 +309,12 @@ class FIPSDataset(Dataset[FIPSCertificate, FIPSAuxillaryDatasets], ComplexSerial
policy_reference_finder = ReferenceFinder()
policy_reference_finder.fit(
- self.certs, lambda cert: cert.cert_id, lambda cert: cert.heuristics.policy_prunned_references
+ self.certs, lambda cert: str(cert.cert_id), lambda cert: cert.heuristics.policy_prunned_references
)
module_reference_finder = ReferenceFinder()
module_reference_finder.fit(
- self.certs, lambda cert: cert.cert_id, lambda cert: cert.heuristics.module_prunned_references
+ self.certs, lambda cert: str(cert.cert_id), lambda cert: cert.heuristics.module_prunned_references
)
for cert in self:
diff --git a/src/sec_certs/dataset/fips_algorithm.py b/src/sec_certs/dataset/fips_algorithm.py
index c48cff07..df113381 100644
--- a/src/sec_certs/dataset/fips_algorithm.py
+++ b/src/sec_certs/dataset/fips_algorithm.py
@@ -20,12 +20,10 @@ logger = logging.getLogger(__name__)
class FIPSAlgorithmDataset(JSONPathDataset, ComplexSerializableType):
- def __init__(
- self, algs: dict[str, FIPSAlgorithm] = dict(), json_path: str | Path = constants.DUMMY_NONEXISTING_PATH
- ):
+ def __init__(self, algs: dict[str, FIPSAlgorithm] = {}, json_path: str | Path = constants.DUMMY_NONEXISTING_PATH):
self.algs = algs
self.json_path = Path(json_path)
- self.alg_number_to_algs: dict[str, set[FIPSAlgorithm]] = dict()
+ self.alg_number_to_algs: dict[str, set[FIPSAlgorithm]] = {}
self._build_lookup_dicts()
@@ -48,7 +46,7 @@ class FIPSAlgorithmDataset(JSONPathDataset, ComplexSerializableType):
def __contains__(self, item: FIPSAlgorithm) -> bool:
if not isinstance(item, FIPSAlgorithm):
raise ValueError(f"{item} is not of FIPSAlgorithm class")
- return item.dgst in self.algs.keys() and self.algs[item.dgst] == item
+ return item.dgst in self.algs and self.algs[item.dgst] == item
def __eq__(self, other: object) -> bool:
return isinstance(other, FIPSAlgorithmDataset) and self.algs == other.algs
@@ -109,9 +107,7 @@ class FIPSAlgorithmDataset(JSONPathDataset, ComplexSerializableType):
return set(df["alg"])
def to_pandas(self) -> pd.DataFrame:
- df = pd.DataFrame([x.pandas_tuple for x in self], columns=FIPSAlgorithm.pandas_columns)
- df = df.set_index("dgst")
- return df
+ return pd.DataFrame([x.pandas_tuple for x in self], columns=FIPSAlgorithm.pandas_columns).set_index("dgst")
def _build_lookup_dicts(self) -> None:
for alg in self:
diff --git a/src/sec_certs/dataset/protection_profile.py b/src/sec_certs/dataset/protection_profile.py
index e4f9f4b8..9730a477 100644
--- a/src/sec_certs/dataset/protection_profile.py
+++ b/src/sec_certs/dataset/protection_profile.py
@@ -7,11 +7,11 @@ import tempfile
from dataclasses import dataclass
from pathlib import Path
-import sec_certs.utils.helpers as helpers
from sec_certs import constants
from sec_certs.config.configuration import config
from sec_certs.sample.protection_profile import ProtectionProfile
from sec_certs.serialization.json import get_class_fullname
+from sec_certs.utils import helpers
logger = logging.getLogger(__name__)
diff --git a/src/sec_certs/model/cpe_matching.py b/src/sec_certs/model/cpe_matching.py
index 0febea5d..5d08d7af 100644
--- a/src/sec_certs/model/cpe_matching.py
+++ b/src/sec_certs/model/cpe_matching.py
@@ -8,7 +8,6 @@ from typing import Pattern
import spacy
from rapidfuzz import fuzz
-from sklearn.base import BaseEstimator
from sec_certs import cert_rules, constants
from sec_certs.sample.cpe import CPE
@@ -17,10 +16,10 @@ from sec_certs.utils.tqdm import tqdm
logger = logging.getLogger(__name__)
-class CPEClassifier(BaseEstimator):
+class CPEClassifier:
"""
Class that can predict CPE matches for certificate instances.
- Adheres to sklearn BaseEstimator interface.
+ Adheres to sklearn `sklearn.base.BaseEstimator` interface.
Fit method is called on list of CPEs and build two look-up dictionaries, see description of attributes.
"""
@@ -66,7 +65,7 @@ class CPEClassifier(BaseEstimator):
sufficiently_long_cpes = self._filter_short_cpes(X)
self.vendor_to_versions_ = {x.vendor: set() for x in sufficiently_long_cpes}
self.vendors_ = set(self.vendor_to_versions_.keys())
- self.vendor_version_to_cpe_ = dict()
+ self.vendor_version_to_cpe_ = {}
for cpe in tqdm(sufficiently_long_cpes, desc="Fitting the CPE classifier"):
self.vendor_to_versions_[cpe.vendor].add(cpe.version)
@@ -148,7 +147,7 @@ class CPEClassifier(BaseEstimator):
def filter_condition(regex: Pattern, cpe: CPE, min_value: int, soft: bool = True):
if matches := re.findall(regex, cpe.update):
return int(re.findall(r"\d+", matches[0])[0]) >= min_value
- return True if soft else False
+ return soft
update_regexes = [cert_rules.SERVICE_PACK_RE, cert_rules.RELEASE_RE]
@@ -161,7 +160,7 @@ class CPEClassifier(BaseEstimator):
return cpes
def _filter_candidates_by_platform(self, cpes: list[CPE], cert_title: str) -> list[CPE]:
- def filter_condition(cpe: CPE, cert_platforms: set[str]):
+ def filter_condition(cpe: CPE, cert_platforms: set[str]) -> bool:
if not cert_platforms and cpe.target_hw == "*":
return True
if cert_platforms and cpe.target_hw == "*":
@@ -180,8 +179,9 @@ class CPEClassifier(BaseEstimator):
)
if not target_hw_platforms:
return can_return_true
- else:
- return can_return_true and target_hw_platforms[0] in cert_platforms
+
+ return can_return_true and target_hw_platforms[0] in cert_platforms
+ return True
crt_platforms = {
platform for platform, regex in cert_rules.PLATFORM_REGEXES.items() if re.search(regex, cert_title)
@@ -323,7 +323,7 @@ class CPEClassifier(BaseEstimator):
itertools.chain.from_iterable([x.strip() for x in manufacturer.split(s)] for s in splits)
)
result_aux = [self._get_candidate_list_of_vendors(x) for x in vendor_tokens]
- result_used = set(set(itertools.chain.from_iterable(x for x in result_aux if x)))
+ result_used = set(itertools.chain.from_iterable(x for x in result_aux if x))
return result_used if result_used else set()
if manufacturer in self.vendors_:
@@ -347,10 +347,7 @@ class CPEClassifier(BaseEstimator):
def simple_startswith(seeked_version: str, checked_string: str) -> bool:
if seeked_version == checked_string:
return True
- else:
- return (
- checked_string.startswith(seeked_version) and not checked_string[len(seeked_version)].isdigit()
- )
+ return checked_string.startswith(seeked_version) and not checked_string[len(seeked_version)].isdigit()
if not cpe_version:
return False
diff --git a/src/sec_certs/model/evaluation.py b/src/sec_certs/model/evaluation.py
index 4d0243f6..6f14fda5 100644
--- a/src/sec_certs/model/evaluation.py
+++ b/src/sec_certs/model/evaluation.py
@@ -6,11 +6,11 @@ from pathlib import Path
import numpy as np
-import sec_certs.utils.helpers as helpers
from sec_certs.dataset.cpe import CPEDataset
from sec_certs.sample.cc import CCCertificate
from sec_certs.sample.fips import FIPSCertificate
from sec_certs.serialization.json import CustomJSONEncoder
+from sec_certs.utils import helpers
logger = logging.getLogger(__name__)
diff --git a/src/sec_certs/model/reference_finder.py b/src/sec_certs/model/reference_finder.py
index 117a8b9d..94a3b29f 100644
--- a/src/sec_certs/model/reference_finder.py
+++ b/src/sec_certs/model/reference_finder.py
@@ -23,7 +23,7 @@ class ReferenceFinder:
The fit is called on a dictionary of certificates, builds a hashmap of references, and assigns references for each certificate in the dictionary.
"""
- def __init__(self):
+ def __init__(self: ReferenceFinder) -> None:
self.references: ReferencesType = {}
self.id_mapping: IDMapping = {}
self._fitted: bool = False
diff --git a/src/sec_certs/model/sar_transformer.py b/src/sec_certs/model/sar_transformer.py
index 45c4f7d5..a60f7495 100644
--- a/src/sec_certs/model/sar_transformer.py
+++ b/src/sec_certs/model/sar_transformer.py
@@ -3,8 +3,6 @@ from __future__ import annotations
import logging
from typing import Dict, Iterable, cast
-from sklearn.base import BaseEstimator, TransformerMixin
-
from sec_certs.sample.cc import CCCertificate
from sec_certs.sample.sar import SAR, SAR_DICT_KEY
@@ -12,10 +10,10 @@ logger = logging.getLogger(__name__)
# TODO: Right now we ignore number of ocurrences for final SAR selection. If we keep it this way, we can discard that variable
-class SARTransformer(BaseEstimator, TransformerMixin):
+class SARTransformer:
"""
Class for transforming SARs defined in st_keywords and report_keywords dictionaries into SAR objects.
- This class implements sklearn transformer interface, so fit_transform() can be called on it.
+ This class implements `sklearn.base.Transformer` interface, so fit_transform() can be called on it.
"""
def fit(self, certificates: Iterable[CCCertificate]) -> SARTransformer:
@@ -27,6 +25,9 @@ class SARTransformer(BaseEstimator, TransformerMixin):
"""
return self
+ def fit_transform(self, X, y=None, **fit_params):
+ return self.fit(X).transform(X)
+
def transform(self, certificates: Iterable[CCCertificate]) -> list[set[SAR] | None]:
"""
Just a wrapper around transform_single_cert() called on an iterable of CCCertificate.
@@ -128,7 +129,7 @@ class SARTransformer(BaseEstimator, TransformerMixin):
:param dgst: DIgest of the processed certificate.
:return: _description_
"""
- sars: dict[str, tuple[SAR, int]] = dict()
+ sars: dict[str, tuple[SAR, int]] = {}
for sar_class, class_matches in dct.items():
for sar_string, n_occurences in class_matches.items():
try:
diff --git a/src/sec_certs/model/transitive_vulnerability_finder.py b/src/sec_certs/model/transitive_vulnerability_finder.py
index de733481..5eafea93 100644
--- a/src/sec_certs/model/transitive_vulnerability_finder.py
+++ b/src/sec_certs/model/transitive_vulnerability_finder.py
@@ -97,7 +97,7 @@ class TransitiveVulnerabilityFinder:
thrown_away_cert_counter += 1
continue
- self.vulnerabilities[cert.dgst] = dict()
+ self.vulnerabilities[cert.dgst] = {}
self.vulnerabilities[cert.dgst][ReferenceType.DIRECT.value] = self._get_cert_transitive_cves(
cert, ReferenceType.DIRECT, ref_func
)
diff --git a/src/sec_certs/sample/cc.py b/src/sec_certs/sample/cc.py
index 603c29f2..9f73aefa 100644
--- a/src/sec_certs/sample/cc.py
+++ b/src/sec_certs/sample/cc.py
@@ -17,13 +17,12 @@ from bs4 import Tag
import sec_certs.utils.extract
import sec_certs.utils.pdf
import sec_certs.utils.sanitization
-from sec_certs import constants as constants
+from sec_certs import constants
from sec_certs.cert_rules import SARS_IMPLIED_FROM_EAL, cc_rules, rules, security_level_csv_scan
from sec_certs.sample.cc_certificate_id import canonicalize
-from sec_certs.sample.certificate import Certificate
+from sec_certs.sample.certificate import Certificate, References, logger
from sec_certs.sample.certificate import Heuristics as BaseHeuristics
from sec_certs.sample.certificate import PdfData as BasePdfData
-from sec_certs.sample.certificate import References, logger
from sec_certs.sample.protection_profile import ProtectionProfile
from sec_certs.sample.sar import SAR
from sec_certs.serialization.json import ComplexSerializableType
@@ -117,10 +116,10 @@ class CCCertificate(
st_txt_hash: str | None
report_txt_hash: str | None
- st_pdf_path: Path
- report_pdf_path: Path
- st_txt_path: Path
- report_txt_path: Path
+ _st_pdf_path: Path | None = None
+ _report_pdf_path: Path | None = None
+ _st_txt_path: Path | None = None
+ _report_txt_path: Path | None = None
def __init__(
self,
@@ -152,6 +151,46 @@ class CCCertificate(
self.report_txt_hash = report_txt_hash
@property
+ def st_pdf_path(self) -> Path:
+ if not self._st_pdf_path:
+ raise ValueError(f"st_pdf_path not set on {type(self)}")
+ return self._st_pdf_path
+
+ @st_pdf_path.setter
+ def st_pdf_path(self, pth: str | Path | None) -> None:
+ self._st_pdf_path = Path(pth) if pth else None
+
+ @property
+ def report_pdf_path(self) -> Path:
+ if not self._report_pdf_path:
+ raise ValueError(f"report_pdf_path not set on {type(self)}")
+ return self._report_pdf_path
+
+ @report_pdf_path.setter
+ def report_pdf_path(self, pth: str | Path | None) -> None:
+ self._report_pdf_path = Path(pth) if pth else None
+
+ @property
+ def st_txt_path(self) -> Path:
+ if not self._st_txt_path:
+ raise ValueError(f"st_txt_path not set on {type(self)}")
+ return self._st_txt_path
+
+ @st_txt_path.setter
+ def st_txt_path(self, pth: str | Path | None) -> None:
+ self._st_txt_path = Path(pth) if pth else None
+
+ @property
+ def report_txt_path(self) -> Path:
+ if not self._report_txt_path:
+ raise ValueError(f"report_txt_path not set on {type(self)}")
+ return self._report_txt_path
+
+ @report_txt_path.setter
+ def report_txt_path(self, pth: str | Path | None) -> None:
+ self._report_txt_path = Path(pth) if pth else None
+
+ @property
def serialized_attributes(self) -> list[str]:
return [
"st_download_ok",
@@ -472,9 +511,9 @@ class CCCertificate(
self.manufacturer_web = sec_certs.utils.sanitization.sanitize_link(manufacturer_web)
self.protection_profiles = protection_profiles
self.maintenance_updates = maintenance_updates
- self.state = self.InternalState() if not state else state
- self.pdf_data = self.PdfData() if not pdf_data else pdf_data
- self.heuristics: CCCertificate.Heuristics = self.Heuristics() if not heuristics else heuristics
+ self.state = state if state else self.InternalState()
+ self.pdf_data = pdf_data if pdf_data else self.PdfData()
+ self.heuristics: CCCertificate.Heuristics = heuristics if heuristics else self.Heuristics()
@property
def dgst(self) -> str:
@@ -507,7 +546,7 @@ class CCCertificate(
Computes actual SARs. First, SARs implied by EAL are computed. Then, these are augmented with heuristically extracted SARs
:return Optional[Set[SAR]]: Set of actual SARs of a certificate, None if empty
"""
- sars = dict()
+ sars = {}
if self.eal:
sars = {x[0]: SAR(x[0], x[1]) for x in SARS_IMPLIED_FROM_EAL[self.eal[:4]]}
@@ -755,13 +794,13 @@ class CCCertificate(
:param Optional[Union[str, Path]] report_txt_dir: Directory where txt reports shall be stored
:param Optional[Union[str, Path]] st_txt_dir: Directory where txt security targets shall be stored
"""
- if report_pdf_dir is not None:
+ if report_pdf_dir:
self.state.report_pdf_path = Path(report_pdf_dir) / (self.dgst + ".pdf")
- if st_pdf_dir is not None:
+ if st_pdf_dir:
self.state.st_pdf_path = Path(st_pdf_dir) / (self.dgst + ".pdf")
- if report_txt_dir is not None:
+ if report_txt_dir:
self.state.report_txt_path = Path(report_txt_dir) / (self.dgst + ".txt")
- if st_txt_dir is not None:
+ if st_txt_dir:
self.state.st_txt_path = Path(st_txt_dir) / (self.dgst + ".txt")
@staticmethod
@@ -795,11 +834,10 @@ class CCCertificate(
:param CCCertificate cert: cert to download the pdf security target for
:return CCCertificate: returns the modified certificate with updated state
"""
- exit_code: str | int
- if not cert.st_link:
- exit_code = "No link"
- else:
- exit_code = helpers.download_file(cert.st_link, cert.state.st_pdf_path)
+ exit_code: str | int = (
+ helpers.download_file(cert.st_link, cert.state.st_pdf_path) if cert.st_link else "No link"
+ )
+
if exit_code != requests.codes.ok:
error_msg = f"failed to download ST from {cert.st_link}, code: {exit_code}"
logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
diff --git a/src/sec_certs/sample/cc_maintenance_update.py b/src/sec_certs/sample/cc_maintenance_update.py
index d78359fc..4273b25e 100644
--- a/src/sec_certs/sample/cc_maintenance_update.py
+++ b/src/sec_certs/sample/cc_maintenance_update.py
@@ -4,9 +4,9 @@ import logging
from datetime import date
from typing import ClassVar
-import sec_certs.utils.helpers as helpers
from sec_certs.sample.cc import CCCertificate
from sec_certs.serialization.json import ComplexSerializableType
+from sec_certs.utils import helpers
logger = logging.getLogger(__name__)
diff --git a/src/sec_certs/sample/certificate.py b/src/sec_certs/sample/certificate.py
index bb49c0df..5dab9cef 100644
--- a/src/sec_certs/sample/certificate.py
+++ b/src/sec_certs/sample/certificate.py
@@ -25,6 +25,9 @@ class References(ComplexSerializableType):
directly_referencing: set[str] | None = field(default=None)
indirectly_referencing: set[str] | None = field(default=None)
+ def __bool__(self):
+ return any(getattr(self, x) for x in vars(self))
+
class Heuristics:
cpe_matches: set[str] | None
diff --git a/src/sec_certs/sample/fips.py b/src/sec_certs/sample/fips.py
index aec791d4..b3ff8ba6 100644
--- a/src/sec_certs/sample/fips.py
+++ b/src/sec_certs/sample/fips.py
@@ -14,21 +14,16 @@ import requests
from bs4 import BeautifulSoup, Tag
from tabula import read_pdf
-import sec_certs.constants as constants
-import sec_certs.utils.extract
-import sec_certs.utils.helpers as helpers
-import sec_certs.utils.pdf
-import sec_certs.utils.pdf as pdf
-import sec_certs.utils.tables as tables
+from sec_certs import constants
from sec_certs.cert_rules import FIPS_ALGS_IN_TABLE, fips_rules
from sec_certs.config.configuration import config
-from sec_certs.sample.certificate import Certificate
+from sec_certs.sample.certificate import Certificate, References, logger
from sec_certs.sample.certificate import Heuristics as BaseHeuristics
from sec_certs.sample.certificate import PdfData as BasePdfData
-from sec_certs.sample.certificate import References, logger
from sec_certs.sample.cpe import CPE
from sec_certs.serialization.json import ComplexSerializableType
from sec_certs.serialization.pandas import PandasSerializableType
+from sec_certs.utils import extract, helpers, pdf, tables
from sec_certs.utils.helpers import fips_dgst
@@ -69,7 +64,7 @@ class FIPSHTMLParser:
[x.find("div", class_="col-md-3") for x in entries], [x.find("div", class_="col-md-9") for x in entries]
)
entries = [(FIPSHTMLParser.normalize_string(key.text), entry) for key, entry in entries]
- entries = [parse_single_detail_entry(*x) for x in entries if x[0] in DETAILS_KEY_NORMALIZATION_DICT.keys()]
+ entries = [parse_single_detail_entry(*x) for x in entries if x[0] in DETAILS_KEY_NORMALIZATION_DICT]
entries = {x: y for x, y in entries}
if "caveat" in entries:
@@ -130,11 +125,22 @@ class FIPSHTMLParser:
@staticmethod
def parse_algorithms(algorithms_div: Tag) -> dict[str, set[str]]:
- rows = algorithms_div.find("tbody").find_all("tr")
- dct: dict[str, set[str]] = dict()
- for row in rows:
- cells = row.find_all("td")
- dct[cells[0].text] = {m.group() for m in re.finditer(FIPS_ALGS_IN_TABLE, cells[1].text)}
+ dct: dict[str, set[str]] = {}
+ table = algorithms_div.find("tbody")
+ # Two types of organization on the CMVP website:
+ # - One is a table with algo references in text
+ # - Other is just divs for rows, one per algo
+ if table:
+ rows = table.find_all("tr")
+ for row in rows:
+ cells = row.find_all("td")
+ dct[str(cells[0].text)] = {m.group() for m in re.finditer(FIPS_ALGS_IN_TABLE, cells[1].text)}
+ else:
+ rows = algorithms_div.find_all("div", class_="col-md-12")
+ for row in rows:
+ dct[str(row.find("div", class_="col-md-3").text)] = {
+ str(row.find("div", class_="col-md-4").text).strip()
+ }
return dct
@staticmethod
@@ -144,7 +150,7 @@ class FIPSHTMLParser:
@staticmethod
def parse_tested_configurations(tested_configurations: Tag) -> list[str] | None:
configurations = [y.text for y in tested_configurations.find_all("li")]
- return configurations if not configurations == ["N/A"] else None
+ return None if configurations == ["N/A"] else configurations
@staticmethod
def normalize_embodiment(embodiment_element: Tag) -> str:
@@ -248,9 +254,9 @@ class FIPSCertificate(
policy_pdf_hash: str | None
policy_txt_hash: str | None
- policy_pdf_path: Path
- policy_txt_path: Path
- module_html_path: Path
+ _policy_pdf_path: Path | None = None
+ _policy_txt_path: Path | None = None
+ _module_html_path: Path | None = None
def __init__(
self,
@@ -273,6 +279,36 @@ class FIPSCertificate(
self.policy_txt_hash = policy_txt_hash
@property
+ def policy_pdf_path(self) -> Path:
+ if not self._policy_pdf_path:
+ raise ValueError(f"policy_pdf_path not set on {type(self)}")
+ return self._policy_pdf_path
+
+ @policy_pdf_path.setter
+ def policy_pdf_path(self, pth: str | Path | None) -> None:
+ self._policy_pdf_path = Path(pth) if pth else None
+
+ @property
+ def policy_txt_path(self) -> Path:
+ if not self._policy_txt_path:
+ raise ValueError(f"policy_txt_path not set on {type(self)}")
+ return self._policy_txt_path
+
+ @policy_txt_path.setter
+ def policy_txt_path(self, pth: str | Path | None) -> None:
+ self._policy_txt_path = Path(pth) if pth else None
+
+ @property
+ def module_html_path(self) -> Path:
+ if not self._module_html_path:
+ raise ValueError(f"module_html_path not set on {type(self)}")
+ return self._module_html_path
+
+ @module_html_path.setter
+ def module_html_path(self, pth: str | Path | None) -> None:
+ self._module_html_path = Path(pth) if pth else None
+
+ @property
def serialized_attributes(self) -> list[str]:
return [
"module_download_ok",
@@ -389,8 +425,8 @@ class FIPSCertificate(
def certlike_algorithm_numbers(self) -> set[str]:
"""Returns numbers of certificates from keywords["fips_certlike"]["Certlike"]"""
if self.keywords and "fips_certlike" in self.keywords:
- fips_certlike = self.keywords["fips_certlike"].get("Certlike", dict())
- matches = {re.search(r"#\s{0,1}\d{1,4}", x) for x in fips_certlike.keys()}
+ fips_certlike = self.keywords["fips_certlike"].get("Certlike", {})
+ matches = {re.search(r"#\s{0,1}\d{1,4}", x) for x in fips_certlike}
return {"".join([x for x in match.group() if x.isdigit()]) for match in matches if match}
else:
return set()
@@ -463,7 +499,7 @@ class FIPSCertificate(
def __init__(
self,
- cert_id: str,
+ cert_id: int,
web_data: FIPSCertificate.WebData | None = None,
pdf_data: FIPSCertificate.PdfData | None = None,
heuristics: FIPSCertificate.Heuristics | None = None,
@@ -543,9 +579,7 @@ class FIPSCertificate(
"""
Converts policy pdf -> txt
"""
- ocr_done, ok_result = sec_certs.utils.pdf.convert_pdf_file(
- cert.state.policy_pdf_path, cert.state.policy_txt_path
- )
+ ocr_done, ok_result = pdf.convert_pdf_file(cert.state.policy_pdf_path, cert.state.policy_txt_path)
# If OCR was done and the result was garbage
cert.state.policy_convert_garbage = ocr_done
@@ -565,12 +599,12 @@ class FIPSCertificate(
"""
Extract the PDF metadata from the security policy.
"""
- _, metadata = sec_certs.utils.pdf.extract_pdf_metadata(cert.state.policy_pdf_path)
+ _, metadata = pdf.extract_pdf_metadata(cert.state.policy_pdf_path)
if metadata:
cert.pdf_data.policy_metadata = metadata
else:
- cert.pdf_data.policy_metadata = dict()
+ cert.pdf_data.policy_metadata = {}
cert.state.policy_extract_ok = False
return cert
@@ -579,7 +613,7 @@ class FIPSCertificate(
"""
Extract keywords from policy document
"""
- keywords = sec_certs.utils.extract.extract_keywords(cert.state.policy_txt_path, fips_rules)
+ keywords = extract.extract_keywords(cert.state.policy_txt_path, fips_rules)
if not keywords:
cert.state.policy_extract_ok = False
else:
@@ -618,7 +652,7 @@ class FIPSCertificate(
self.heuristics.module_prunned_references = self._prune_reference_ids_variable(html_module_ids)
if self.pdf_data.keywords:
- pdf_policy_ids = set(self.pdf_data.keywords["fips_cert_id"].get("Cert", dict()).keys())
+ pdf_policy_ids = set(self.pdf_data.keywords["fips_cert_id"].get("Cert", {}).keys())
pdf_policy_ids = {"".join([y for y in x if y.isdigit()]) for x in pdf_policy_ids}
else:
pdf_policy_ids = set()
@@ -646,9 +680,7 @@ class FIPSCertificate(
2. Consider only ids s.t. they don't appear in self.heuristics.algorithms
3. Consider only ids s.t. they don't appear in self.pdf_data.keywords["fips_certlike"]["Certlike"]
"""
- prunned = {x for x in attribute_to_prune if x != self.cert_id}
+ prunned = {x for x in attribute_to_prune if x != str(self.cert_id)}
prunned = {x for x in prunned if int(x) > config.always_false_positive_fips_cert_id_threshold}
prunned = {x for x in prunned if x not in self.heuristics.algorithm_numbers}
- prunned = {x for x in prunned if x not in self.pdf_data.certlike_algorithm_numbers}
-
- return prunned
+ return {x for x in prunned if x not in self.pdf_data.certlike_algorithm_numbers}
diff --git a/src/sec_certs/sample/fips_iut.py b/src/sec_certs/sample/fips_iut.py
index cb521ee4..f6010346 100644
--- a/src/sec_certs/sample/fips_iut.py
+++ b/src/sec_certs/sample/fips_iut.py
@@ -85,10 +85,11 @@ class IUTSnapshot(ComplexSerializableType):
last_updated_elem = next(
filter(
- lambda e: isinstance(e, Tag) and e.name == "p",
+ lambda e: isinstance(e, Tag) and e.name == "p" and "Last Updated" in str(e.string),
soup.find(id="content").next_siblings,
)
)
+
last_updated_text = str(last_updated_elem.string).strip()
last_updated = datetime.strptime(last_updated_text, "Last Updated: %m/%d/%Y").date()
table = tables[0].find("tbody")
@@ -100,7 +101,7 @@ class IUTSnapshot(ComplexSerializableType):
str(line[2].string),
datetime.strptime(str(line[3].string), "%m/%d/%Y").date(),
)
- for line in map(lambda tr: tr.find_all("td"), lines)
+ for line in (tr.find_all("td") for tr in lines)
}
# Parse footer
diff --git a/src/sec_certs/sample/fips_mip.py b/src/sec_certs/sample/fips_mip.py
index 6918d2aa..7e0ddff0 100644
--- a/src/sec_certs/sample/fips_mip.py
+++ b/src/sec_certs/sample/fips_mip.py
@@ -114,7 +114,7 @@ class MIPSnapshot(ComplexSerializableType):
MIPEntry(
str(line[0].string), str(line[1].string), str(line[2].string), MIPStatus(str(line[3].string)), None
)
- for line in map(lambda tr: tr.find_all("td"), lines)
+ for line in (tr.find_all("td") for tr in lines)
}
@classmethod
@@ -128,14 +128,14 @@ class MIPSnapshot(ComplexSerializableType):
MIPStatus(str(line[3].string)),
None,
)
- for line in map(lambda tr: tr.find_all("td"), lines)
+ for line in (tr.find_all("td") for tr in lines)
}
@classmethod
def _extract_entries_4(cls, lines):
"""Works now."""
entries = set()
- for line in map(lambda tr: tr.find_all("td"), lines):
+ for line in (tr.find_all("td") for tr in lines):
module_name = str(line[0].string)
vendor_name = str(" ".join(line[1].find_all(text=True, recursive=False)).strip())
standard = str(line[2].string)
@@ -150,14 +150,12 @@ class MIPSnapshot(ComplexSerializableType):
@classmethod
def _extract_entries(cls, lines, snapshot_date):
if snapshot_date <= datetime(2020, 10, 28):
- entries = cls._extract_entries_1(lines)
- elif snapshot_date <= datetime(2021, 4, 20):
- entries = cls._extract_entries_2(lines)
- elif snapshot_date <= datetime(2022, 3, 23):
- entries = cls._extract_entries_3(lines)
- else:
- entries = cls._extract_entries_4(lines)
- return entries
+ return cls._extract_entries_1(lines)
+ if snapshot_date <= datetime(2021, 4, 20):
+ return cls._extract_entries_2(lines)
+ if snapshot_date <= datetime(2022, 3, 23):
+ return cls._extract_entries_3(lines)
+ return cls._extract_entries_4(lines)
@classmethod
def from_page(cls, content: bytes, snapshot_date: datetime) -> MIPSnapshot:
diff --git a/src/sec_certs/sample/protection_profile.py b/src/sec_certs/sample/protection_profile.py
index b7c2ec34..4c26a1c7 100644
--- a/src/sec_certs/sample/protection_profile.py
+++ b/src/sec_certs/sample/protection_profile.py
@@ -5,8 +5,8 @@ import logging
from dataclasses import dataclass
from typing import Any
-import sec_certs.utils.sanitization as sanitization
from sec_certs.serialization.json import ComplexSerializableType
+from sec_certs.utils import sanitization
logger = logging.getLogger(__name__)
diff --git a/src/sec_certs/sample/sar.py b/src/sec_certs/sample/sar.py
index 31359299..8f48f417 100644
--- a/src/sec_certs/sample/sar.py
+++ b/src/sec_certs/sample/sar.py
@@ -18,7 +18,7 @@ SAR_CLASS_MAPPING = {
"ACO": "Comoposition",
}
-SAR_CLASSES = {x for x in SAR_CLASS_MAPPING}
+SAR_CLASSES = set(SAR_CLASS_MAPPING)
SAR_DICT_KEY = "cc_sar"
diff --git a/src/sec_certs/serialization/json.py b/src/sec_certs/serialization/json.py
index 69bbc2ff..7b523b9b 100644
--- a/src/sec_certs/serialization/json.py
+++ b/src/sec_certs/serialization/json.py
@@ -67,8 +67,7 @@ class ComplexSerializableType:
def from_json(cls: type[T], input_path: str | Path) -> T:
input_path = Path(input_path)
with input_path.open("r") as handle:
- obj = json.load(handle, cls=CustomJSONDecoder)
- return obj
+ return json.load(handle, cls=CustomJSONDecoder)
# Decorator for serialization
@@ -95,10 +94,7 @@ def serialize(func: Callable):
def get_class_fullname(obj: Any) -> str:
- if isinstance(obj, type):
- klass = obj
- else:
- klass = obj.__class__
+ klass = obj if isinstance(obj, type) else obj.__class__
module = klass.__module__
if module == "builtins":
return klass.__qualname__
@@ -112,9 +108,9 @@ class CustomJSONEncoder(json.JSONEncoder):
if isinstance(obj, dict):
return obj
if isinstance(obj, set):
- return {"_type": "Set", "elements": sorted(list(obj))}
+ return {"_type": "Set", "elements": sorted(obj)}
if isinstance(obj, frozenset):
- return sorted(list(obj))
+ return sorted(obj)
if isinstance(obj, date):
return str(obj)
if isinstance(obj, Path):
@@ -136,10 +132,10 @@ class CustomJSONDecoder(json.JSONDecoder):
def object_hook(self, obj):
if "_type" in obj and obj["_type"] == "Set":
return set(obj["elements"])
- if "_type" in obj and obj["_type"] in self.serializable_complex_types.keys():
+ if "_type" in obj and obj["_type"] in self.serializable_complex_types:
complex_type = obj.pop("_type")
return self.serializable_complex_types[complex_type].from_dict(obj)
- elif "_type" in obj:
+ if "_type" in obj:
raise SerializationError(f"JSONDecoder doesn't know how to handle {obj}")
return obj
diff --git a/src/sec_certs/utils/extract.py b/src/sec_certs/utils/extract.py
index 09460fb7..933db4b8 100644
--- a/src/sec_certs/utils/extract.py
+++ b/src/sec_certs/utils/extract.py
@@ -6,11 +6,11 @@ import re
from collections import Counter
from enum import Enum
from pathlib import Path
-from typing import Any, Iterator
+from typing import Any
import numpy as np
-from sec_certs import constants as constants
+from sec_certs import constants
from sec_certs.cert_rules import REGEXEC_SEP, cc_rules
from sec_certs.constants import FILE_ERRORS_STRATEGY, LINE_SEPARATOR, MAX_ALLOWED_MATCH_LENGTH
@@ -585,11 +585,6 @@ def search_only_headers_canada(filepath: Path): # noqa: C901
return constants.RETURNCODE_OK, items_found
-def search_files(folder: str | Path) -> Iterator[str]:
- for root, _, files in os.walk(str(folder)):
- yield from [os.path.join(root, x) for x in files]
-
-
def flatten_matches(dct: dict) -> dict:
"""
Function to flatten dictionary of matches.
@@ -663,13 +658,13 @@ def extract_keywords(filepath: Path, search_rules) -> dict[str, dict[str, int]]
def extract(rules):
if isinstance(rules, dict):
return {k: extract(v) for k, v in rules.items()}
- elif isinstance(rules, list):
+ if isinstance(rules, list):
matches = [extract(rule) for rule in rules]
c = Counter()
for match_list in matches:
c += Counter(match_list)
return dict(c)
- elif isinstance(rules, re.Pattern):
+ if isinstance(rules, re.Pattern):
rule = rules
matches = []
for match in rule.finditer(whole_text):
@@ -719,7 +714,7 @@ def load_text_file(
logger.warning("UnicodeDecodeError, opening as utf8")
if was_unicode_decode_error:
- with open(file_name, encoding="utf8", errors=FILE_ERRORS_STRATEGY) as f2:
+ with Path(file_name).open("r", encoding="utf8", errors=FILE_ERRORS_STRATEGY) as f2:
# coding failure, try line by line
line = " "
while line:
@@ -746,23 +741,31 @@ def load_text_file(
return whole_text, whole_text_with_newlines, was_unicode_decode_error
-def load_cert_html_file(file_name: str) -> str:
- with open(file_name, errors=FILE_ERRORS_STRATEGY) as f:
- try:
- return f.read()
- except UnicodeDecodeError:
- logger.warning("UnicodeDecodeError, opening as utf8")
+def rules_get_subset(desired_path: str) -> dict:
+ """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- with open(file_name, encoding="utf8", errors=FILE_ERRORS_STRATEGY) as f2:
- try:
- return f2.read()
- except UnicodeDecodeError:
- logger.error(f"Failed to read file {file_name}")
- return ""
-def rules_get_subset(desired_path: str) -> dict:
- """
Recursively applies cc_certs.get(key) on tokens from desired_path,
returns the keys of the inner-most layer.
"""
diff --git a/src/sec_certs/utils/helpers.py b/src/sec_certs/utils/helpers.py
index 302f4e6a..596ecf62 100644
--- a/src/sec_certs/utils/helpers.py
+++ b/src/sec_certs/utils/helpers.py
@@ -14,8 +14,7 @@ import numpy as np
import pkgconfig
import requests
-import sec_certs.constants as constants
-from sec_certs.config.configuration import config
+from sec_certs import constants
from sec_certs.utils import parallel_processing
from sec_certs.utils.tqdm import tqdm
@@ -45,12 +44,11 @@ def download_file(
ctx = nullcontext
if r.status_code == requests.codes.ok:
- with ctx() as pbar:
- with output.open("wb") as f:
- for data in r.iter_content(1024):
- f.write(data)
- if show_progress_bar:
- pbar.update(len(data))
+ with ctx() as pbar, output.open("wb") as f:
+ for data in r.iter_content(1024):
+ f.write(data)
+ if show_progress_bar:
+ pbar.update(len(data))
return r.status_code
except requests.exceptions.Timeout:
@@ -65,7 +63,7 @@ def download_parallel(
urls: Collection[str], paths: Collection[Path], progress_bar_desc: str | None = None
) -> list[int]:
exit_codes = parallel_processing.process_parallel(
- download_file, list(zip(urls, paths)), config.n_threads, unpack=True, progress_bar_desc=progress_bar_desc
+ download_file, list(zip(urls, paths)), unpack=True, progress_bar_desc=progress_bar_desc
)
n_successful = len([e for e in exit_codes if e == requests.codes.ok])
logger.info(f"Successfully downloaded {n_successful} files, {len(exit_codes) - n_successful} failed.")
@@ -98,8 +96,7 @@ def to_utc(timestamp: datetime) -> datetime:
if offset is None:
return timestamp
timestamp -= offset
- timestamp = timestamp.replace(tzinfo=None)
- return timestamp
+ return timestamp.replace(tzinfo=None)
def is_in_dict(target_dict: dict, path: str) -> bool:
@@ -107,8 +104,7 @@ def is_in_dict(target_dict: dict, path: str) -> bool:
for item in path:
if item not in current_level:
return False
- else:
- current_level = current_level[item]
+ current_level = current_level[item]
return True
diff --git a/src/sec_certs/utils/pandas.py b/src/sec_certs/utils/pandas.py
index b61d11d7..749292e3 100644
--- a/src/sec_certs/utils/pandas.py
+++ b/src/sec_certs/utils/pandas.py
@@ -140,7 +140,7 @@ def get_sar_level_from_set(sars: set[SAR], sar_family: str) -> int | None:
"""
Given a set of SARs and a family name, will return level of the seeked SAR from the set.
"""
- family_sars_dict = {x.family: x for x in sars} if (sars and not pd.isnull(sars)) else dict()
+ family_sars_dict = {x.family: x for x in sars} if (sars and not pd.isnull(sars)) else {}
if sar_family not in family_sars_dict.keys():
return None
return family_sars_dict[sar_family].level
diff --git a/src/sec_certs/utils/parallel_processing.py b/src/sec_certs/utils/parallel_processing.py
index 1101035f..b3016695 100644
--- a/src/sec_certs/utils/parallel_processing.py
+++ b/src/sec_certs/utils/parallel_processing.py
@@ -1,24 +1,29 @@
from __future__ import annotations
import time
+from multiprocessing import cpu_count
from multiprocessing.pool import ThreadPool
from typing import Any, Callable, Iterable
from billiard.pool import Pool
+from sec_certs.config.configuration import config
from sec_certs.utils.tqdm import tqdm
def process_parallel(
func: Callable,
items: Iterable,
- max_workers: int,
+ max_workers: int = config.n_threads,
callback: Callable | None = None,
use_threading: bool = True,
progress_bar: bool = True,
unpack: bool = False,
progress_bar_desc: str | None = None,
) -> list[Any]:
+ if max_workers == -1:
+ max_workers = cpu_count()
+
pool: Pool | ThreadPool = ThreadPool(max_workers) if use_threading else Pool(max_workers)
results = (
[pool.apply_async(func, (*i,), callback=callback) for i in items]
diff --git a/src/sec_certs/utils/pdf.py b/src/sec_certs/utils/pdf.py
index d97dff95..edda0570 100644
--- a/src/sec_certs/utils/pdf.py
+++ b/src/sec_certs/utils/pdf.py
@@ -11,10 +11,10 @@ from typing import Any
import pdftotext
import pikepdf
-from PyPDF2 import PdfFileReader
-from PyPDF2.generic import BooleanObject, ByteStringObject, FloatObject, IndirectObject, NumberObject, TextStringObject
+from pypdf import PdfReader
+from pypdf.generic import BooleanObject, ByteStringObject, FloatObject, IndirectObject, NumberObject, TextStringObject
-from sec_certs import constants as constants
+from sec_certs import constants
from sec_certs.constants import (
GARBAGE_ALPHA_CHARS_THRESHOLD,
GARBAGE_AVG_LLEN_THRESHOLD,
@@ -24,6 +24,7 @@ from sec_certs.constants import (
)
logger = logging.getLogger(__name__)
+logging.getLogger("pypdf").setLevel(logging.ERROR)
def repair_pdf(file: Path) -> None:
@@ -165,7 +166,7 @@ def extract_pdf_metadata(filepath: Path) -> tuple[str, dict[str, Any] | None]:
val = int(val)
elif isinstance(val, IndirectObject) and not nope_out:
# Let's make sure to nope out in case of cycles
- val = map_metadata_value(val.getObject(), nope_out=True)
+ val = map_metadata_value(val.get_object(), nope_out=True)
elif isinstance(val, TextStringObject):
val = str(val)
elif isinstance(val, ByteStringObject):
@@ -180,27 +181,26 @@ def extract_pdf_metadata(filepath: Path) -> tuple[str, dict[str, Any] | None]:
def resolve_indirect(val, bound=10):
if isinstance(val, list) and bound:
return [resolve_indirect(v, bound - 1) for v in val]
- elif isinstance(val, IndirectObject) and bound:
- return resolve_indirect(val.getObject(), bound - 1)
- else:
- return val
+ if isinstance(val, IndirectObject) and bound:
+ return resolve_indirect(val.get_object(), bound - 1)
+ return val
- metadata: dict[str, Any] = dict()
+ metadata: dict[str, Any] = {}
try:
metadata["pdf_file_size_bytes"] = filepath.stat().st_size
with filepath.open("rb") as handle:
- pdf = PdfFileReader(handle, strict=False)
- metadata["pdf_is_encrypted"] = pdf.getIsEncrypted()
+ pdf = PdfReader(handle, strict=False)
+ metadata["pdf_is_encrypted"] = pdf.is_encrypted
# see https://stackoverflow.com/questions/26242952/pypdf-2-decrypt-not-working
if metadata["pdf_is_encrypted"]:
pikepdf.open(filepath, allow_overwriting_input=True).save()
with filepath.open("rb") as handle:
- pdf = PdfFileReader(handle, strict=False)
- metadata["pdf_number_of_pages"] = pdf.getNumPages()
- pdf_document_info = pdf.getDocumentInfo()
+ pdf = PdfReader(handle, strict=False)
+ metadata["pdf_number_of_pages"] = len(pdf.pages)
+ pdf_document_info = pdf.metadata
if pdf_document_info is None:
raise ValueError("PDF metadata unavailable")
@@ -251,14 +251,8 @@ def text_is_garbage(text: str) -> bool:
if len(set(line[1::2])) > 1:
every_second += 1
- if lines:
- avg_line_len = content_len / lines
- else:
- avg_line_len = 0
- if size:
- alpha = alpha_len / size
- else:
- alpha = 0
+ avg_line_len = content_len / lines if lines else 0
+ alpha = alpha_len / size if size else 0
# If number of lines is small, this is garbage.
if lines < GARBAGE_LINES_THRESHOLD:
diff --git a/src/sec_certs/utils/sanitization.py b/src/sec_certs/utils/sanitization.py
index 2f9cd046..3563e1f4 100644
--- a/src/sec_certs/utils/sanitization.py
+++ b/src/sec_certs/utils/sanitization.py
@@ -26,9 +26,9 @@ def sanitize_link(record: str | None) -> str | None:
def sanitize_date(record: pd.Timestamp | date | np.datetime64) -> date | None:
if pd.isnull(record):
return None
- elif isinstance(record, pd.Timestamp):
+ if isinstance(record, pd.Timestamp):
return record.date()
- elif isinstance(record, (date, type(None))):
+ if isinstance(record, (date, type(None))):
return record
raise ValueError("Unsupported type given as input")
diff --git a/tests/cc/test_cc_analysis.py b/tests/cc/test_cc_analysis.py
index a61c7e67..6d0f4cfe 100644
--- a/tests/cc/test_cc_analysis.py
+++ b/tests/cc/test_cc_analysis.py
@@ -4,8 +4,8 @@ import shutil
from pathlib import Path
import pytest
-
import tests.data.cc.analysis
+
from sec_certs.cert_rules import SARS_IMPLIED_FROM_EAL
from sec_certs.dataset import CCDataset
from sec_certs.dataset.cpe import CPEDataset
@@ -91,7 +91,7 @@ def cc_dset(data_dir: Path, cve_dset: CVEDataset, tmp_path_factory) -> CCDataset
cc_dset = CCDataset.from_json(tmp_dir / "vulnerable_dataset.json")
cc_dset.process_protection_profiles()
cc_dset.extract_data()
- cc_dset.auxillary_datasets.cve_dset = cve_dset
+ cc_dset.auxiliary_datasets.cve_dset = cve_dset
cc_dset._compute_heuristics()
return cc_dset
@@ -104,7 +104,7 @@ def cc_config_dset(data_dir: Path, cve_config_dset: CVEDataset, tmp_path_factory
cc_config_dset = CCDataset.from_json(tmp_dir / "vulnerable_dataset.json")
cc_config_dset.process_protection_profiles()
cc_config_dset.extract_data()
- cc_config_dset.auxillary_datasets.cve_dset = cve_config_dset
+ cc_config_dset.auxiliary_datasets.cve_dset = cve_config_dset
cc_config_dset._compute_heuristics()
return cc_config_dset
diff --git a/tests/cc/test_cc_certificate.py b/tests/cc/test_cc_certificate.py
index 2234ee11..2a5ff222 100644
--- a/tests/cc/test_cc_certificate.py
+++ b/tests/cc/test_cc_certificate.py
@@ -4,9 +4,9 @@ from datetime import date
from pathlib import Path
import pytest
-
import tests.data.cc.analysis
import tests.data.cc.certificate
+
from sec_certs.dataset import CCDataset
from sec_certs.sample import CCCertificate
from sec_certs.sample.protection_profile import ProtectionProfile
diff --git a/tests/cc/test_cc_dataset.py b/tests/cc/test_cc_dataset.py
index 5d0b8fc5..2b37c2f8 100644
--- a/tests/cc/test_cc_dataset.py
+++ b/tests/cc/test_cc_dataset.py
@@ -5,8 +5,8 @@ from pathlib import Path
from tempfile import TemporaryDirectory
import pytest
-
import tests.data.cc.dataset
+
from sec_certs import constants
from sec_certs.dataset import CCDataset
from sec_certs.sample.cc import CCCertificate
diff --git a/tests/cc/test_cc_maintenance_updates.py b/tests/cc/test_cc_maintenance_updates.py
index 833391ef..254df45c 100644
--- a/tests/cc/test_cc_maintenance_updates.py
+++ b/tests/cc/test_cc_maintenance_updates.py
@@ -2,8 +2,8 @@ import json
from pathlib import Path
import pytest
-
import tests.data.cc.dataset
+
from sec_certs.dataset import CCDataset, CCDatasetMaintenanceUpdates
from sec_certs.sample.cc_maintenance_update import CCMaintenanceUpdate
@@ -21,7 +21,7 @@ def cc_dset(data_dir: Path) -> CCDataset:
@pytest.fixture
def mu_dset(data_dir: Path, tmp_path_factory) -> CCDatasetMaintenanceUpdates:
tmp_dir = tmp_path_factory.mktemp("mu_dset")
- dset = CCDatasetMaintenanceUpdates.from_json(data_dir / "auxillary_datasets/maintenances/maintenance_updates.json")
+ dset = CCDatasetMaintenanceUpdates.from_json(data_dir / "auxiliary_datasets/maintenances/maintenance_updates.json")
dset.copy_dataset(tmp_dir)
return dset
@@ -33,7 +33,7 @@ def test_methods_not_meant_to_be_implemented():
with pytest.raises(NotImplementedError):
dset._compute_heuristics()
with pytest.raises(NotImplementedError):
- dset.process_auxillary_datasets()
+ dset.process_auxiliary_datasets()
with pytest.raises(NotImplementedError):
dset.compute_related_cves()
with pytest.raises(NotImplementedError):
@@ -58,7 +58,7 @@ def test_dataset_to_json(mu_dset: CCDatasetMaintenanceUpdates, data_dir: Path, t
with (tmp_path / "dset.json").open("r") as handle:
data = json.load(handle)
- with (data_dir / "auxillary_datasets/maintenances/maintenance_updates.json").open("r") as handle:
+ with (data_dir / "auxiliary_datasets/maintenances/maintenance_updates.json").open("r") as handle:
template_data = json.load(handle)
del template_data["timestamp"]
@@ -68,7 +68,7 @@ def test_dataset_to_json(mu_dset: CCDatasetMaintenanceUpdates, data_dir: Path, t
def test_dataset_from_json(mu_dset: CCDatasetMaintenanceUpdates, data_dir: Path):
assert mu_dset == CCDatasetMaintenanceUpdates.from_json(
- data_dir / "auxillary_datasets/maintenances/maintenance_updates.json"
+ data_dir / "auxiliary_datasets/maintenances/maintenance_updates.json"
)
diff --git a/tests/data/cc/analysis/auxillary_datasets/cpe_dataset.json b/tests/data/cc/analysis/auxiliary_datasets/cpe_dataset.json
index 559a3b57..559a3b57 100644
--- a/tests/data/cc/analysis/auxillary_datasets/cpe_dataset.json
+++ b/tests/data/cc/analysis/auxiliary_datasets/cpe_dataset.json
diff --git a/tests/data/cc/analysis/auxillary_datasets/cve_dataset.json b/tests/data/cc/analysis/auxiliary_datasets/cve_dataset.json
index 4caf45d1..4caf45d1 100644
--- a/tests/data/cc/analysis/auxillary_datasets/cve_dataset.json
+++ b/tests/data/cc/analysis/auxiliary_datasets/cve_dataset.json
diff --git a/tests/data/cc/analysis/auxillary_datasets/cve_dset_with_cpe_configs.json b/tests/data/cc/analysis/auxiliary_datasets/cve_dset_with_cpe_configs.json
index 32d188f5..32d188f5 100644
--- a/tests/data/cc/analysis/auxillary_datasets/cve_dset_with_cpe_configs.json
+++ b/tests/data/cc/analysis/auxiliary_datasets/cve_dset_with_cpe_configs.json
diff --git a/tests/data/cc/analysis/cc_full_dataset.json b/tests/data/cc/analysis/cc_full_dataset.json
index d12a9456..05e75c55 100644
--- a/tests/data/cc/analysis/cc_full_dataset.json
+++ b/tests/data/cc/analysis/cc_full_dataset.json
@@ -5,7 +5,7 @@
"meta_sources_parsed": true,
"artifacts_downloaded": true,
"pdfs_converted": true,
- "auxillary_datasets_processed": true,
+ "auxiliary_datasets_processed": true,
"certs_analyzed": false
},
"timestamp": "2022-11-04 16:28:04.282938",
diff --git a/tests/data/cc/analysis/vulnerable_dataset.json b/tests/data/cc/analysis/vulnerable_dataset.json
index d433fac1..32ec6ce7 100644
--- a/tests/data/cc/analysis/vulnerable_dataset.json
+++ b/tests/data/cc/analysis/vulnerable_dataset.json
@@ -5,7 +5,7 @@
"meta_sources_parsed": true,
"artifacts_downloaded": false,
"pdfs_converted": false,
- "auxillary_datasets_processed": true,
+ "auxiliary_datasets_processed": true,
"certs_analyzed": false
},
"timestamp": "2021-04-16 15:05:18.386794",
diff --git a/tests/data/cc/dataset/auxillary_datasets/maintenances/maintenance_updates.json b/tests/data/cc/dataset/auxiliary_datasets/maintenances/maintenance_updates.json
index 21567fe4..fd21471b 100644
--- a/tests/data/cc/dataset/auxillary_datasets/maintenances/maintenance_updates.json
+++ b/tests/data/cc/dataset/auxiliary_datasets/maintenances/maintenance_updates.json
@@ -5,7 +5,7 @@
"meta_sources_parsed": true,
"artifacts_downloaded": true,
"pdfs_converted": false,
- "auxillary_datasets_processed": false,
+ "auxiliary_datasets_processed": false,
"certs_analyzed": false
},
"timestamp": "2022-11-10 13:44:35.171285",
diff --git a/tests/data/cc/dataset/toy_dataset.json b/tests/data/cc/dataset/toy_dataset.json
index bd674311..dec802f7 100644
--- a/tests/data/cc/dataset/toy_dataset.json
+++ b/tests/data/cc/dataset/toy_dataset.json
@@ -5,7 +5,7 @@
"meta_sources_parsed": true,
"artifacts_downloaded": false,
"pdfs_converted": false,
- "auxillary_datasets_processed": false,
+ "auxiliary_datasets_processed": false,
"certs_analyzed": false
},
"timestamp": "2020-11-16 17:04:14.770153",
diff --git a/tests/data/fips/certificate/fictional_cert.json b/tests/data/fips/certificate/fictional_cert.json
index a1dbec54..9ec36238 100644
--- a/tests/data/fips/certificate/fictional_cert.json
+++ b/tests/data/fips/certificate/fictional_cert.json
@@ -1,7 +1,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "184097a88a9b4ad9",
- "cert_id": "3518",
+ "cert_id": 3518,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
diff --git a/tests/data/fips/dataset/toy_dataset.json b/tests/data/fips/dataset/toy_dataset.json
index 37853ff9..97edf45e 100644
--- a/tests/data/fips/dataset/toy_dataset.json
+++ b/tests/data/fips/dataset/toy_dataset.json
@@ -5,7 +5,7 @@
"meta_sources_parsed": true,
"artifacts_downloaded": true,
"pdfs_converted": true,
- "auxillary_datasets_processed": false,
+ "auxiliary_datasets_processed": false,
"certs_analyzed": false
},
"timestamp": "2022-11-30 16:38:51.953055",
@@ -17,7 +17,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "69d9200c309f5f97",
- "cert_id": "2590",
+ "cert_id": 2590,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -101,7 +101,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "3b05cf67849835e3",
- "cert_id": "2997",
+ "cert_id": 2997,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -185,7 +185,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "618102f275d5649a",
- "cert_id": "3495",
+ "cert_id": 3495,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -269,7 +269,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "5b00a7bb92c1cb19",
- "cert_id": "2721",
+ "cert_id": 2721,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -353,7 +353,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "e1ff7d9997726782",
- "cert_id": "3090",
+ "cert_id": 3090,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -437,7 +437,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "ed16d272044ed81f",
- "cert_id": "2860",
+ "cert_id": 2860,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -521,7 +521,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "73e7f3f802b0e919",
- "cert_id": "3176",
+ "cert_id": 3176,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -605,7 +605,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "756a6524cdb2d4ea",
- "cert_id": "3850",
+ "cert_id": 3850,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -689,7 +689,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "0e8b7a7f79b3098b",
- "cert_id": "2665",
+ "cert_id": 2665,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -773,7 +773,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "f7df34ce0a0ad316",
- "cert_id": "3093",
+ "cert_id": 3093,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -857,7 +857,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "0df59fd6c41cadd2",
- "cert_id": "3711",
+ "cert_id": 3711,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -941,7 +941,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "24a05f4d1305d667",
- "cert_id": "3493",
+ "cert_id": 3493,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1025,7 +1025,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "51c94a510b50e68f",
- "cert_id": "3141",
+ "cert_id": 3141,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1109,7 +1109,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "75ca28395bfc9ad1",
- "cert_id": "2630",
+ "cert_id": 2630,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1193,7 +1193,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "cd0666cdd7ce0244",
- "cert_id": "3095",
+ "cert_id": 3095,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1277,7 +1277,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "ceab64951a30f684",
- "cert_id": "3197",
+ "cert_id": 3197,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1361,7 +1361,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "0ab77c56eece2d24",
- "cert_id": "3651",
+ "cert_id": 3651,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1445,7 +1445,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "9ca7b8be590113b8",
- "cert_id": "2711",
+ "cert_id": 2711,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1529,7 +1529,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "184097a88a9b4ad9",
- "cert_id": "3518",
+ "cert_id": 3518,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1613,7 +1613,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "c333a75353784177",
- "cert_id": "2779",
+ "cert_id": 2779,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1697,7 +1697,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "ad1e075215267a28",
- "cert_id": "2441",
+ "cert_id": 2441,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
@@ -1781,7 +1781,7 @@
{
"_type": "sec_certs.sample.fips.FIPSCertificate",
"dgst": "2aec3694418f35d8",
- "cert_id": "3488",
+ "cert_id": 3488,
"web_data": {
"_type": "sec_certs.sample.fips.FIPSCertificate.WebData",
"module_name": null,
diff --git a/tests/fips/test_fips_algorithm_dataset.py b/tests/fips/test_fips_algorithm_dataset.py
index 88016f4f..1d45e67c 100644
--- a/tests/fips/test_fips_algorithm_dataset.py
+++ b/tests/fips/test_fips_algorithm_dataset.py
@@ -4,8 +4,8 @@ from pathlib import Path
from typing import Any
import pytest
-
import tests.data.fips.dataset
+
from sec_certs.dataset.fips_algorithm import FIPSAlgorithmDataset
from sec_certs.sample.fips_algorithm import FIPSAlgorithm
from sec_certs.serialization.json import SerializationError
diff --git a/tests/fips/test_fips_analysis.py b/tests/fips/test_fips_analysis.py
index ffd78a3f..6c5b032e 100644
--- a/tests/fips/test_fips_analysis.py
+++ b/tests/fips/test_fips_analysis.py
@@ -3,8 +3,8 @@ from __future__ import annotations
from pathlib import Path
import pytest
-
import tests.data.fips.dataset
+
from sec_certs.dataset import CPEDataset, CVEDataset
from sec_certs.dataset.fips import FIPSDataset
from sec_certs.sample.cpe import CPE, CPEConfiguration
@@ -147,8 +147,8 @@ def processed_dataset(
toy_static_dataset.extract_data()
toy_static_dataset._compute_references(keep_unknowns=True)
- toy_static_dataset.auxillary_datasets.cpe_dset = cpe_dataset
- toy_static_dataset.auxillary_datasets.cve_dset = cve_dataset
+ toy_static_dataset.auxiliary_datasets.cpe_dset = cpe_dataset
+ toy_static_dataset.auxiliary_datasets.cve_dset = cve_dataset
toy_static_dataset.compute_cpe_heuristics()
toy_static_dataset.compute_related_cves()
toy_static_dataset._compute_transitive_vulnerabilities()
@@ -271,7 +271,7 @@ def test_find_related_cves_for_cpe_configuration(
cve_dataset.cves = {ibm_xss_cve.cve_id: ibm_xss_cve}
cert = processed_dataset["2441"]
cert.heuristics.cpe_matches = {cve.uri for cve in cpes_ibm_websphere_app_with_platform}
- processed_dataset.auxillary_datasets.cve_dset = cve_dataset
+ processed_dataset.auxiliary_datasets.cve_dset = cve_dataset
processed_dataset.compute_related_cves()
assert {ibm_xss_cve.cve_id} == cert.heuristics.related_cves
diff --git a/tests/fips/test_fips_certificate.py b/tests/fips/test_fips_certificate.py
index b612834c..8f68238e 100644
--- a/tests/fips/test_fips_certificate.py
+++ b/tests/fips/test_fips_certificate.py
@@ -3,9 +3,9 @@ import shutil
from pathlib import Path
import pytest
-
import tests.data.fips.certificate
import tests.data.fips.dataset
+
from sec_certs.dataset.fips import FIPSDataset
from sec_certs.sample.fips import FIPSCertificate
diff --git a/tests/fips/test_fips_dataset.py b/tests/fips/test_fips_dataset.py
index b5f09aa9..99dc57af 100644
--- a/tests/fips/test_fips_dataset.py
+++ b/tests/fips/test_fips_dataset.py
@@ -4,9 +4,9 @@ from pathlib import Path
from tempfile import TemporaryDirectory
import pytest
-
-import sec_certs.constants as constants
import tests.data.fips.dataset
+
+from sec_certs import constants
from sec_certs.dataset.fips import FIPSDataset
from sec_certs.sample.fips import FIPSCertificate
@@ -70,6 +70,7 @@ def test_build_dataset(data_dir: Path, toy_dataset: FIPSDataset):
def test_download_meta_html_files():
with TemporaryDirectory() as tmp_dir:
dset = FIPSDataset(root_dir=Path(tmp_dir))
+ dset.web_dir.mkdir()
dset._download_html_resources()
assert (dset.web_dir / "fips_modules_active.html").exists()
diff --git a/tests/fips/test_fips_iut.py b/tests/fips/test_fips_iut.py
index 5a478a20..1dca086c 100644
--- a/tests/fips/test_fips_iut.py
+++ b/tests/fips/test_fips_iut.py
@@ -1,8 +1,8 @@
from pathlib import Path
import pytest
-
import tests.data.fips.iut
+
from sec_certs.dataset import IUTDataset
from sec_certs.sample import IUTSnapshot
diff --git a/tests/fips/test_fips_mip.py b/tests/fips/test_fips_mip.py
index 8748bf23..4918a4c6 100644
--- a/tests/fips/test_fips_mip.py
+++ b/tests/fips/test_fips_mip.py
@@ -1,8 +1,8 @@
from pathlib import Path
import pytest
-
import tests.data.fips.mip
+
from sec_certs.dataset import MIPDataset
from sec_certs.sample import MIPSnapshot
diff --git a/tests/test_common.py b/tests/test_common.py
index d5e62cc4..fa7a3775 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -6,4 +6,4 @@ def test_rules():
assert "fips_cert_id" in fips_rules
for rule_group in rules:
if rule_group not in ("cc_rules", "fips_rules"):
- assert rule_group in cc_rules.keys() or rule_group in fips_rules.keys()
+ assert rule_group in cc_rules or rule_group in fips_rules
diff --git a/tests/test_cpe.py b/tests/test_cpe.py
index 3be2c1a6..9cee5459 100644
--- a/tests/test_cpe.py
+++ b/tests/test_cpe.py
@@ -5,7 +5,7 @@ from typing import Any
import pytest
-import tests.data.cc.analysis.auxillary_datasets
+import tests.data.cc.analysis.auxiliary_datasets
from sec_certs import constants
from sec_certs.dataset import CPEDataset, CVEDataset
from sec_certs.sample import CPE, CPEConfiguration
@@ -14,12 +14,12 @@ from sec_certs.serialization.json import SerializationError
@pytest.fixture(scope="module")
def cpe_dset_path() -> Path:
- return Path(tests.data.cc.analysis.auxillary_datasets.__path__[0]) / "cpe_dataset.json"
+ return Path(tests.data.cc.analysis.auxiliary_datasets.__path__[0]) / "cpe_dataset.json"
@pytest.fixture(scope="module")
def cve_dset_with_cpe_configs_path() -> Path:
- return Path(tests.data.cc.analysis.auxillary_datasets.__path__[0]) / "cve_dset_with_cpe_configs.json"
+ return Path(tests.data.cc.analysis.auxiliary_datasets.__path__[0]) / "cve_dset_with_cpe_configs.json"
@pytest.fixture(scope="module")
@@ -145,7 +145,7 @@ def test_to_pandas(cpe_dset: CPEDataset):
def test_serialization_missing_path():
- dummy_dset = CPEDataset(False, dict())
+ dummy_dset = CPEDataset(False, {})
with pytest.raises(SerializationError):
dummy_dset.to_json()
diff --git a/tests/test_cve.py b/tests/test_cve.py
index 20c04147..0382974a 100644
--- a/tests/test_cve.py
+++ b/tests/test_cve.py
@@ -5,7 +5,7 @@ from typing import Any
import pytest
-import tests.data.cc.analysis.auxillary_datasets
+import tests.data.cc.analysis.auxiliary_datasets
from sec_certs.dataset import CVEDataset
from sec_certs.sample import CVE
from sec_certs.sample.cpe import CPE
@@ -24,7 +24,7 @@ def test_from_web():
@pytest.fixture(scope="module")
def cve_dataset_path() -> Path:
- return Path(tests.data.cc.analysis.auxillary_datasets.__path__[0]) / "cve_dataset.json"
+ return Path(tests.data.cc.analysis.auxiliary_datasets.__path__[0]) / "cve_dataset.json"
@pytest.fixture(scope="module")