aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJán Jančár2025-02-14 16:41:07 +0100
committerGitHub2025-02-14 16:41:07 +0100
commita8cba36b9e3d2711c7f7c4a7c07d1cbe299cd1d7 (patch)
tree2241537ca6e3168ca83a64ff9d90681d4f9739b9
parent2a2b60565df9904d913cd830e7675af91749c5c1 (diff)
parentbf27325735c0a68f480ccb6b7f2ea2265ab6124c (diff)
downloadsec-certs-a8cba36b9e3d2711c7f7c4a7c07d1cbe299cd1d7.tar.gz
sec-certs-a8cba36b9e3d2711c7f7c4a7c07d1cbe299cd1d7.tar.zst
sec-certs-a8cba36b9e3d2711c7f7c4a7c07d1cbe299cd1d7.zip
Merge pull request #477 from crocs-muni/fix/no-more-retcode
Get rid of RETURNCODE_OK. This is not C99.
-rw-r--r--src/sec_certs/constants.py3
-rw-r--r--src/sec_certs/dataset/auxiliary_dataset_handling.py4
-rw-r--r--src/sec_certs/dataset/cpe.py3
-rw-r--r--src/sec_certs/dataset/cve.py3
-rw-r--r--src/sec_certs/dataset/dataset.py3
-rw-r--r--src/sec_certs/dataset/fips_algorithm.py11
-rw-r--r--src/sec_certs/sample/cc.py186
-rw-r--r--src/sec_certs/sample/fips.py16
-rw-r--r--src/sec_certs/sample/protection_profile.py33
-rw-r--r--src/sec_certs/utils/extract.py30
-rw-r--r--src/sec_certs/utils/helpers.py8
-rw-r--r--src/sec_certs/utils/nvd_dataset_builder.py6
-rw-r--r--src/sec_certs/utils/pdf.py9
13 files changed, 129 insertions, 186 deletions
diff --git a/src/sec_certs/constants.py b/src/sec_certs/constants.py
index 125c2265..ccc3887e 100644
--- a/src/sec_certs/constants.py
+++ b/src/sec_certs/constants.py
@@ -10,9 +10,6 @@ REF_EMBEDDING_METHOD = Literal["tf_idf", "transformer"]
# This stupid thing should die in a fire...
DUMMY_NONEXISTING_PATH = Path("/this/is/dummy/nonexisting/path")
-RESPONSE_OK = 200
-RETURNCODE_OK = "ok"
-RETURNCODE_NOK = "nok"
REQUEST_TIMEOUT = 20
INCREMENTAL_NVD_UPDATE_MAX_INTERVAL_DAYS: Final[int] = 120
diff --git a/src/sec_certs/dataset/auxiliary_dataset_handling.py b/src/sec_certs/dataset/auxiliary_dataset_handling.py
index 69d5e837..d89f6520 100644
--- a/src/sec_certs/dataset/auxiliary_dataset_handling.py
+++ b/src/sec_certs/dataset/auxiliary_dataset_handling.py
@@ -8,6 +8,8 @@ from collections.abc import Iterable
from pathlib import Path
from typing import Any, ClassVar
+import requests
+
from sec_certs import constants
from sec_certs.configuration import config
from sec_certs.dataset.cc_scheme import CCSchemeDataset
@@ -138,7 +140,7 @@ class CPEMatchDictHandler(AuxiliaryDatasetHandler):
dset_path,
progress_bar_desc="Downloading CPE Match feed from web",
)
- == constants.RESPONSE_OK
+ == requests.codes.ok
):
raise RuntimeError(f"Could not download CPE Match feed from {config.cpe_match_latest_snapshot}.")
with gzip.open(str(dset_path)) as handle:
diff --git a/src/sec_certs/dataset/cpe.py b/src/sec_certs/dataset/cpe.py
index 56e6ac5d..3257d2a4 100644
--- a/src/sec_certs/dataset/cpe.py
+++ b/src/sec_certs/dataset/cpe.py
@@ -8,6 +8,7 @@ from pathlib import Path
from typing import Any
import pandas as pd
+import requests
from sec_certs import constants
from sec_certs.configuration import config
@@ -86,7 +87,7 @@ class CPEDataset(JSONPathDataset, ComplexSerializableType):
dset_path,
progress_bar_desc="Downloading CPEDataset from web",
)
- == constants.RESPONSE_OK
+ == requests.codes.ok
):
raise RuntimeError(f"Could not download CPEDataset from {config.cpe_latest_snapshot}.")
dset = cls.from_json(dset_path, is_compressed=True)
diff --git a/src/sec_certs/dataset/cve.py b/src/sec_certs/dataset/cve.py
index 6314040c..f76211ee 100644
--- a/src/sec_certs/dataset/cve.py
+++ b/src/sec_certs/dataset/cve.py
@@ -9,6 +9,7 @@ from typing import Any, ClassVar
import numpy as np
import pandas as pd
+import requests
import sec_certs.configuration as config_module
from sec_certs import constants
@@ -82,7 +83,7 @@ class CVEDataset(JSONPathDataset, ComplexSerializableType):
dset_path,
progress_bar_desc="Downloading CVEDataset from web",
)
- == constants.RESPONSE_OK
+ == requests.codes.ok
):
raise RuntimeError(f"Could not download CVEDataset from {config_module.config.cve_latest_snapshot}.")
dset = cls.from_json(dset_path, is_compressed=True)
diff --git a/src/sec_certs/dataset/dataset.py b/src/sec_certs/dataset/dataset.py
index 446cc5ff..c17109f3 100644
--- a/src/sec_certs/dataset/dataset.py
+++ b/src/sec_certs/dataset/dataset.py
@@ -12,6 +12,7 @@ from pathlib import Path
from typing import Any, ClassVar, Generic, TypeVar, cast
import pandas as pd
+import requests
from pydantic import AnyHttpUrl
from sec_certs import constants
@@ -192,7 +193,7 @@ class Dataset(Generic[CertSubType], ComplexSerializableType, ABC):
show_progress_bar=True,
progress_bar_desc=progress_bar_desc,
)
- if res != constants.RESPONSE_OK:
+ if res != requests.codes.ok:
raise ValueError(f"Download failed: {res}")
with tarfile.open(dset_path, "r:gz") as tar:
tar.extractall(str(path))
diff --git a/src/sec_certs/dataset/fips_algorithm.py b/src/sec_certs/dataset/fips_algorithm.py
index 56163078..ee7d06a1 100644
--- a/src/sec_certs/dataset/fips_algorithm.py
+++ b/src/sec_certs/dataset/fips_algorithm.py
@@ -8,6 +8,7 @@ from pathlib import Path
from tempfile import TemporaryDirectory
import pandas as pd
+import requests
from bs4 import BeautifulSoup
from sec_certs import constants
@@ -64,9 +65,9 @@ class FIPSAlgorithmDataset(JSONPathDataset, ComplexSerializableType):
ITEMS_PER_PAGE = "ipp=250"
res = helpers.download_file(constants.FIPS_ALG_SEARCH_URL + "1&" + ITEMS_PER_PAGE, first_page_path)
- if res != constants.RESPONSE_OK:
+ if res != requests.codes.ok:
res = helpers.download_file(constants.FIPS_ALG_SEARCH_URL + "1&" + ITEMS_PER_PAGE, first_page_path)
- if res != constants.RESPONSE_OK:
+ if res != requests.codes.ok:
logger.error(f"Could not build Algorithm dataset, got server response: {res}")
raise ValueError(f"Could not build Algorithm dataset, got server response: {res}")
@@ -76,13 +77,11 @@ class FIPSAlgorithmDataset(JSONPathDataset, ComplexSerializableType):
paths = [output_dir / f"page{i}.html" for i in range(2, n_pages + 1)]
responses = helpers.download_parallel(urls, paths, progress_bar_desc="Downloading FIPS Algorithm HTMLs")
- failed_tuples = [
- (url, path) for url, path, resp in zip(urls, paths, responses) if resp != constants.RESPONSE_OK
- ]
+ failed_tuples = [(url, path) for url, path, resp in zip(urls, paths, responses) if resp != requests.codes.ok]
if failed_tuples:
failed_urls, failed_paths = zip(*failed_tuples)
responses = helpers.download_parallel(failed_urls, failed_paths)
- if any(x != constants.RESPONSE_OK for x in responses):
+ if any(x != requests.codes.ok for x in responses):
raise ValueError("Failed to download the algorithms HTML data, the dataset won't be constructed.")
return paths
diff --git a/src/sec_certs/sample/cc.py b/src/sec_certs/sample/cc.py
index ca096a6c..e43333e9 100644
--- a/src/sec_certs/sample/cc.py
+++ b/src/sec_certs/sample/cc.py
@@ -7,15 +7,13 @@ from collections import Counter, defaultdict
from dataclasses import dataclass, field
from datetime import date, datetime
from pathlib import Path
-from typing import Any, ClassVar
+from typing import Any, ClassVar, Literal
from urllib.parse import unquote_plus, urlparse
import numpy as np
import requests
from bs4 import Tag
-import sec_certs.utils.extract
-import sec_certs.utils.pdf
from sec_certs import constants
from sec_certs.cert_rules import SARS_IMPLIED_FROM_EAL, cc_rules, rules
from sec_certs.configuration import config
@@ -28,7 +26,8 @@ from sec_certs.sample.sar import SAR
from sec_certs.serialization.json import ComplexSerializableType
from sec_certs.serialization.pandas import PandasSerializableType
from sec_certs.utils import helpers, sanitization
-from sec_certs.utils.extract import normalize_match_string, scheme_frontpage_functions
+from sec_certs.utils.extract import extract_keywords, normalize_match_string, scheme_frontpage_functions
+from sec_certs.utils.pdf import convert_pdf_file, extract_pdf_metadata
class CCCertificate(
@@ -662,7 +661,7 @@ class CCCertificate(
:param Optional[Union[str, Path]] cert_pdf_dir: Directory where pdf certificates shall be stored
: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
- :param Optional[Union[str, Path]] cert_txt_dir: Directory where txtcertificates shall be stored
+ :param Optional[Union[str, Path]] cert_txt_dir: Directory where txt certificates shall be stored
"""
if report_pdf_dir:
self.state.report.pdf_path = Path(report_pdf_dir) / (self.dgst + ".pdf")
@@ -678,6 +677,22 @@ class CCCertificate(
self.state.cert.txt_path = Path(cert_txt_dir) / (self.dgst + ".txt")
@staticmethod
+ def _download_pdf(cert: CCCertificate, doc_type: Literal["report", "st", "cert"]):
+ link = getattr(cert, f"{doc_type}_link")
+ doc_state = getattr(cert.state, doc_type)
+ exit_code = helpers.download_file(link, doc_state.pdf_path, proxy=config.cc_use_proxy) if link else "No link"
+
+ if exit_code != requests.codes.ok:
+ error_msg = f"failed to download {doc_type} from {link}, code: {exit_code}"
+ logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
+ doc_state.download_ok = False
+ else:
+ doc_state.download_ok = True
+ doc_state.pdf_hash = helpers.get_sha256_filepath(doc_state.pdf_path)
+ setattr(cert.pdf_data, f"{doc_type}_filename", unquote_plus(str(urlparse(link).path).split("/")[-1]))
+ return cert
+
+ @staticmethod
def download_pdf_report(cert: CCCertificate) -> CCCertificate:
"""
Downloads pdf of certification report given the certificate. Staticmethod to allow for parallelization.
@@ -685,20 +700,7 @@ class CCCertificate(
:param CCCertificate cert: cert to download the pdf report for
:return CCCertificate: returns the modified certificate with updated state
"""
- exit_code: str | int
- if not cert.report_link:
- exit_code = "No link"
- else:
- exit_code = helpers.download_file(cert.report_link, cert.state.report.pdf_path, proxy=config.cc_use_proxy)
- if exit_code != requests.codes.ok:
- error_msg = f"failed to download report from {cert.report_link}, code: {exit_code}"
- logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
- cert.state.report.download_ok = False
- else:
- cert.state.report.download_ok = True
- cert.state.report.pdf_hash = helpers.get_sha256_filepath(cert.state.report.pdf_path)
- cert.pdf_data.report_filename = unquote_plus(str(urlparse(cert.report_link).path).split("/")[-1])
- return cert
+ return CCCertificate._download_pdf(cert, "report")
@staticmethod
def download_pdf_st(cert: CCCertificate) -> CCCertificate:
@@ -708,21 +710,7 @@ 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 = (
- helpers.download_file(cert.st_link, cert.state.st.pdf_path, proxy=config.cc_use_proxy)
- 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)
- cert.state.st.download_ok = False
- else:
- cert.state.st.download_ok = True
- cert.state.st.pdf_hash = helpers.get_sha256_filepath(cert.state.st.pdf_path)
- cert.pdf_data.st_filename = unquote_plus(str(urlparse(cert.st_link).path).split("/")[-1])
- return cert
+ return CCCertificate._download_pdf(cert, "st")
@staticmethod
def download_pdf_cert(cert: CCCertificate) -> CCCertificate:
@@ -732,20 +720,21 @@ class CCCertificate(
:param CCCertificate cert: cert to download the pdf of
:return CCCertificate: returns the modified certificate with updated state
"""
- exit_code: str | int = (
- helpers.download_file(cert.cert_link, cert.state.cert.pdf_path, proxy=config.cc_use_proxy)
- if cert.cert_link
- else "No link"
- )
+ return CCCertificate._download_pdf(cert, "cert")
- if exit_code != requests.codes.ok:
- error_msg = f"failed to download certificate from {cert.cert_link}, code: {exit_code}"
+ @staticmethod
+ def _convert_pdf(cert: CCCertificate, doc_type: Literal["report", "st", "cert"]) -> CCCertificate:
+ doc_state = getattr(cert.state, doc_type)
+ ocr_done, ok_result = convert_pdf_file(doc_state.pdf_path, doc_state.txt_path)
+ # If OCR was done the result was garbage
+ doc_state.convert_garbage = ocr_done
+ # And put the whole result into convert_ok
+ doc_state.convert_ok = ok_result
+ if not ok_result:
+ error_msg = f"failed to convert {doc_type} pdf->txt"
logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
- cert.state.cert.download_ok = False
else:
- cert.state.cert.download_ok = True
- cert.state.cert.pdf_hash = helpers.get_sha256_filepath(cert.state.cert.pdf_path)
- cert.pdf_data.cert_filename = unquote_plus(str(urlparse(cert.cert_link).path).split("/")[-1])
+ doc_state.txt_hash = helpers.get_sha256_filepath(doc_state.txt_path)
return cert
@staticmethod
@@ -756,19 +745,7 @@ class CCCertificate(
:param CCCertificate cert: cert to convert the pdf report for
:return CCCertificate: the modified certificate with updated state
"""
- ocr_done, ok_result = sec_certs.utils.pdf.convert_pdf_file(
- cert.state.report.pdf_path, cert.state.report.txt_path
- )
- # If OCR was done the result was garbage
- cert.state.report.convert_garbage = ocr_done
- # And put the whole result into convert_ok
- cert.state.report.convert_ok = ok_result
- if not ok_result:
- error_msg = "failed to convert report pdf->txt"
- logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
- else:
- cert.state.report.txt_hash = helpers.get_sha256_filepath(cert.state.report.txt_path)
- return cert
+ return CCCertificate._convert_pdf(cert, "report")
@staticmethod
def convert_st_pdf(cert: CCCertificate) -> CCCertificate:
@@ -778,17 +755,7 @@ class CCCertificate(
:param CCCertificate cert: cert to convert the pdf security target for
:return CCCertificate: the modified certificate with updated state
"""
- ocr_done, ok_result = sec_certs.utils.pdf.convert_pdf_file(cert.state.st.pdf_path, cert.state.st.txt_path)
- # If OCR was done the result was garbage
- cert.state.st.convert_garbage = ocr_done
- # And put the whole result into convert_ok
- cert.state.st.convert_ok = ok_result
- if not ok_result:
- error_msg = "failed to convert security target pdf->txt"
- logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
- else:
- cert.state.st.txt_hash = helpers.get_sha256_filepath(cert.state.st.txt_path)
- return cert
+ return CCCertificate._convert_pdf(cert, "st")
@staticmethod
def convert_cert_pdf(cert: CCCertificate) -> CCCertificate:
@@ -798,16 +765,17 @@ class CCCertificate(
:param CCCertificate cert: cert to convert the certificate for
:return CCCertificate: the modified certificate with updated state
"""
- ocr_done, ok_result = sec_certs.utils.pdf.convert_pdf_file(cert.state.cert.pdf_path, cert.state.cert.txt_path)
- # If OCR was done the result was garbage
- cert.state.cert.convert_garbage = ocr_done
- # And put the whole result into convert_ok
- cert.state.cert.convert_ok = ok_result
- if not ok_result:
- error_msg = "failed to convert security target pdf->txt"
- logger.error(f"Cert dgst: {cert.dgst} " + error_msg)
- else:
- cert.state.cert.txt_hash = helpers.get_sha256_filepath(cert.state.cert.txt_path)
+ return CCCertificate._convert_pdf(cert, "cert")
+
+ @staticmethod
+ def _extract_pdf_metadata(cert: CCCertificate, doc_type: Literal["report", "st", "cert"]) -> CCCertificate:
+ doc_state = getattr(cert.state, doc_type)
+ try:
+ metadata = extract_pdf_metadata(doc_state.pdf_path)
+ setattr(cert.pdf_data, f"{doc_type}_metadata", metadata)
+ doc_state.extract_ok = True
+ except ValueError:
+ doc_state.extract_ok = False
return cert
@staticmethod
@@ -818,12 +786,7 @@ class CCCertificate(
:param CCCertificate cert: cert to extract the metadata for.
:return CCCertificate: the modified certificate with updated state
"""
- response, cert.pdf_data.report_metadata = sec_certs.utils.pdf.extract_pdf_metadata(cert.state.report.pdf_path)
- if response != constants.RETURNCODE_OK:
- cert.state.report.extract_ok = False
- else:
- cert.state.report.extract_ok = True
- return cert
+ return CCCertificate._extract_pdf_metadata(cert, "report")
@staticmethod
def extract_st_pdf_metadata(cert: CCCertificate) -> CCCertificate:
@@ -833,12 +796,7 @@ class CCCertificate(
:param CCCertificate cert: cert to extract the metadata for.
:return CCCertificate: the modified certificate with updated state
"""
- response, cert.pdf_data.st_metadata = sec_certs.utils.pdf.extract_pdf_metadata(cert.state.st.pdf_path)
- if response != constants.RETURNCODE_OK:
- cert.state.st.extract_ok = False
- else:
- cert.state.st.extract_ok = True
- return cert
+ return CCCertificate._extract_pdf_metadata(cert, "st")
@staticmethod
def extract_cert_pdf_metadata(cert: CCCertificate) -> CCCertificate:
@@ -848,12 +806,7 @@ class CCCertificate(
:param CCCertificate cert: cert to extract the metadata for.
:return CCCertificate: the modified certificate with updated state
"""
- response, cert.pdf_data.cert_metadata = sec_certs.utils.pdf.extract_pdf_metadata(cert.state.cert.pdf_path)
- if response != constants.RETURNCODE_OK:
- cert.state.cert.extract_ok = False
- else:
- cert.state.cert.extract_ok = True
- return cert
+ return CCCertificate._extract_pdf_metadata(cert, "cert")
@staticmethod
def extract_report_pdf_frontpage(cert: CCCertificate) -> CCCertificate:
@@ -867,12 +820,26 @@ class CCCertificate(
if cert.scheme in scheme_frontpage_functions:
header_func = scheme_frontpage_functions[cert.scheme]
- response, cert.pdf_data.report_frontpage[cert.scheme] = header_func(cert.state.report.txt_path)
- if response != constants.RETURNCODE_OK:
+ try:
+ cert.pdf_data.report_frontpage[cert.scheme] = header_func(cert.state.report.txt_path)
+ except ValueError:
cert.state.report.extract_ok = False
return cert
@staticmethod
+ def _extract_pdf_keywords(cert: CCCertificate, doc_type: Literal["report", "st", "cert"]) -> CCCertificate:
+ doc_state = getattr(cert.state, doc_type)
+ try:
+ keywords = extract_keywords(doc_state.txt_path, cc_rules)
+ if keywords is None:
+ doc_state.extract_ok = False
+ else:
+ setattr(cert.pdf_data, f"{doc_type}_keywords", keywords)
+ except ValueError:
+ doc_state.extract_ok = False
+ return cert
+
+ @staticmethod
def extract_report_pdf_keywords(cert: CCCertificate) -> CCCertificate:
"""
Matches regular expressions in txt obtained from certification report and extracts the matches into attribute.
@@ -881,12 +848,7 @@ class CCCertificate(
:param CCCertificate cert: certificate to extract the keywords for.
:return CCCertificate: the modified certificate with extracted keywords.
"""
- report_keywords = sec_certs.utils.extract.extract_keywords(cert.state.report.txt_path, cc_rules)
- if report_keywords is None:
- cert.state.report.extract_ok = False
- else:
- cert.pdf_data.report_keywords = report_keywords
- return cert
+ return CCCertificate._extract_pdf_keywords(cert, "report")
@staticmethod
def extract_st_pdf_keywords(cert: CCCertificate) -> CCCertificate:
@@ -897,12 +859,7 @@ class CCCertificate(
:param CCCertificate cert: certificate to extract the keywords for.
:return CCCertificate: the modified certificate with extracted keywords.
"""
- st_keywords = sec_certs.utils.extract.extract_keywords(cert.state.st.txt_path, cc_rules)
- if st_keywords is None:
- cert.state.st.extract_ok = False
- else:
- cert.pdf_data.st_keywords = st_keywords
- return cert
+ return CCCertificate._extract_pdf_keywords(cert, "st")
@staticmethod
def extract_cert_pdf_keywords(cert: CCCertificate) -> CCCertificate:
@@ -913,12 +870,7 @@ class CCCertificate(
:param CCCertificate cert: certificate to extract the keywords for.
:return CCCertificate: the modified certificate with extracted keywords.
"""
- cert_keywords = sec_certs.utils.extract.extract_keywords(cert.state.cert.txt_path, cc_rules)
- if cert_keywords is None:
- cert.state.cert.extract_ok = False
- else:
- cert.pdf_data.cert_keywords = cert_keywords
- return cert
+ return CCCertificate._extract_pdf_keywords(cert, "cert")
def compute_heuristics_cert_versions(self, cert_ids: dict[str, CertificateId | None]) -> None: # noqa: C901
"""
diff --git a/src/sec_certs/sample/fips.py b/src/sec_certs/sample/fips.py
index ce2e6907..7480991c 100644
--- a/src/sec_certs/sample/fips.py
+++ b/src/sec_certs/sample/fips.py
@@ -23,8 +23,9 @@ from sec_certs.sample.certificate import PdfData as BasePdfData
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 import extract, helpers, tables
from sec_certs.utils.helpers import fips_dgst
+from sec_certs.utils.pdf import convert_pdf_file, extract_pdf_metadata, repair_pdf
class FIPSHTMLParser:
@@ -590,7 +591,7 @@ class FIPSCertificate(
"""
Converts policy pdf -> txt
"""
- ocr_done, ok_result = pdf.convert_pdf_file(cert.state.policy_pdf_path, cert.state.policy_txt_path)
+ ocr_done, ok_result = 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
@@ -610,12 +611,9 @@ class FIPSCertificate(
"""
Extract the PDF metadata from the security policy.
"""
- _, metadata = pdf.extract_pdf_metadata(cert.state.policy_pdf_path)
-
- if metadata:
- cert.pdf_data.policy_metadata = metadata
- else:
- cert.pdf_data.policy_metadata = {}
+ try:
+ cert.pdf_data.policy_metadata = extract_pdf_metadata(cert.state.policy_pdf_path)
+ except ValueError:
cert.state.policy_extract_ok = False
return cert
@@ -640,7 +638,7 @@ class FIPSCertificate(
from tabula import read_pdf
if table_rich_page_numbers := tables.find_pages_with_tables(cert.state.policy_txt_path):
- pdf.repair_pdf(cert.state.policy_pdf_path)
+ repair_pdf(cert.state.policy_pdf_path)
try:
tabular_data = read_pdf(cert.state.policy_pdf_path, pages=list(table_rich_page_numbers), silent=True)
cert.heuristics.algorithms |= set(
diff --git a/src/sec_certs/sample/protection_profile.py b/src/sec_certs/sample/protection_profile.py
index 35eab2dc..87565c40 100644
--- a/src/sec_certs/sample/protection_profile.py
+++ b/src/sec_certs/sample/protection_profile.py
@@ -9,8 +9,6 @@ from urllib.parse import unquote_plus, urlparse
import requests
from bs4 import Tag
-import sec_certs.utils.extract
-import sec_certs.utils.pdf
from sec_certs import constants
from sec_certs.cert_rules import cc_rules
from sec_certs.configuration import config
@@ -20,6 +18,8 @@ from sec_certs.sample.certificate import PdfData as BasePdfData
from sec_certs.sample.document_state import DocumentState
from sec_certs.serialization.json import ComplexSerializableType
from sec_certs.utils import cc_html_parsing, helpers, sanitization
+from sec_certs.utils.extract import extract_keywords
+from sec_certs.utils.pdf import convert_pdf_file, extract_pdf_metadata
class ProtectionProfile(
@@ -259,7 +259,7 @@ class ProtectionProfile(
"""
Downloads pdf of certification report for the given protection profile.
"""
- exit_code: str | int
+ exit_code: str | int | None
if not cert.web_data.report_link:
exit_code = "No link"
else:
@@ -281,7 +281,7 @@ class ProtectionProfile(
"""
Downloads actual pdf of the given protection profile.
"""
- exit_code: str | int
+ exit_code: str | int | None
if not cert.web_data.pp_link:
exit_code = "No link"
else:
@@ -301,9 +301,7 @@ class ProtectionProfile(
"""
Converts certification reports from pdf to txt.
"""
- ocr_done, ok_result = sec_certs.utils.pdf.convert_pdf_file(
- cert.state.report.pdf_path, cert.state.report.txt_path
- )
+ ocr_done, ok_result = convert_pdf_file(cert.state.report.pdf_path, cert.state.report.txt_path)
cert.state.report.convert_garbage = ocr_done
cert.state.report.convert_ok = ok_result
if not ok_result:
@@ -317,7 +315,7 @@ class ProtectionProfile(
"""
Converts the actual protection profile from pdf to txt.
"""
- ocr_done, ok_result = sec_certs.utils.pdf.convert_pdf_file(cert.state.pp.pdf_path, cert.state.pp.txt_path)
+ ocr_done, ok_result = convert_pdf_file(cert.state.pp.pdf_path, cert.state.pp.txt_path)
cert.state.pp.convert_garbage = ocr_done
cert.state.pp.convert_ok = ok_result
if not ok_result:
@@ -331,8 +329,11 @@ class ProtectionProfile(
"""
Extracts various pdf metadata from the certification report.
"""
- response, cert.pdf_data.report_metadata = sec_certs.utils.pdf.extract_pdf_metadata(cert.state.report.pdf_path)
- cert.state.report.extract_ok = response == constants.RETURNCODE_OK
+ try:
+ cert.pdf_data.report_metadata = extract_pdf_metadata(cert.state.report.pdf_path)
+ cert.state.report.extract_ok = True
+ except ValueError:
+ cert.state.report.extract_ok = False
return cert
@staticmethod
@@ -340,8 +341,12 @@ class ProtectionProfile(
"""
Extracts various pdf metadata from the actual protection profile.
"""
- response, cert.pdf_data.pp_metadata = sec_certs.utils.pdf.extract_pdf_metadata(cert.state.pp.pdf_path)
- cert.state.pp.extract_ok = response == constants.RETURNCODE_OK
+ try:
+ cert.pdf_data.pp_metadata = extract_pdf_metadata(cert.state.pp.pdf_path)
+ cert.state.pp.extract_ok = True
+ except ValueError:
+ cert.state.pp.extract_ok = False
+
return cert
@staticmethod
@@ -349,7 +354,7 @@ class ProtectionProfile(
"""
Extracts keywords using regexes from the certification report.
"""
- report_keywords = sec_certs.utils.extract.extract_keywords(cert.state.report.txt_path, cc_rules)
+ report_keywords = extract_keywords(cert.state.report.txt_path, cc_rules)
if report_keywords is None:
cert.state.report.extract_ok = False
else:
@@ -361,7 +366,7 @@ class ProtectionProfile(
"""
Extracts keywords using regexes from the actual protection profile.
"""
- pp_keywords = sec_certs.utils.extract.extract_keywords(cert.state.pp.txt_path, cc_rules)
+ pp_keywords = extract_keywords(cert.state.pp.txt_path, cc_rules)
if pp_keywords is None:
cert.state.pp.extract_ok = False
else:
diff --git a/src/sec_certs/utils/extract.py b/src/sec_certs/utils/extract.py
index 6af7f4cc..35361e94 100644
--- a/src/sec_certs/utils/extract.py
+++ b/src/sec_certs/utils/extract.py
@@ -267,19 +267,9 @@ def search_only_headers_anssi(filepath: Path): # noqa: C901
relative_filepath = "/".join(str(filepath).split("/")[-4:])
error_msg = f"Failed to parse ANSSI frontpage headers from {relative_filepath}; {e}"
logger.error(error_msg)
- return error_msg, None
+ raise ValueError(error_msg) from e
- # if True:
- # print('# hits for rule')
- # sorted_rules = sorted(num_rules_hits.items(),
- # key=operator.itemgetter(1), reverse=True)
- # used_rules = []
- # for rule in sorted_rules:
- # print('{:4d} : {}'.format(rule[1], rule[0]))
- # if rule[1] > 0:
- # used_rules.append(rule[0])
-
- return constants.RETURNCODE_OK, items_found
+ return items_found
def search_only_headers_bsi(filepath: Path): # noqa: C901
@@ -376,9 +366,9 @@ def search_only_headers_bsi(filepath: Path): # noqa: C901
relative_filepath = "/".join(str(filepath).split("/")[-4:])
error_msg = f"Failed to parse BSI headers from frontpage: {relative_filepath}; {e}"
logger.error(error_msg)
- return error_msg, None
+ raise ValueError(error_msg) from e
- return constants.RETURNCODE_OK, items_found
+ return items_found
def search_only_headers_nscib(filepath: Path): # noqa: C901
@@ -456,9 +446,9 @@ def search_only_headers_nscib(filepath: Path): # noqa: C901
except Exception as e:
error_msg = f"Failed to parse NSCIB headers from frontpage: {filepath}; {e}"
logger.error(error_msg)
- return error_msg, None
+ raise ValueError(error_msg) from e
- return constants.RETURNCODE_OK, items_found
+ return items_found
def search_only_headers_niap(filepath: Path):
@@ -507,9 +497,9 @@ def search_only_headers_niap(filepath: Path):
except Exception as e:
error_msg = f"Failed to parse NIAP headers from frontpage: {filepath}; {e}"
logger.error(error_msg)
- return error_msg, None
+ raise ValueError(error_msg) from e
- return constants.RETURNCODE_OK, items_found
+ return items_found
def search_only_headers_canada(filepath: Path): # noqa: C901
@@ -580,9 +570,9 @@ def search_only_headers_canada(filepath: Path): # noqa: C901
except Exception as e:
error_msg = f"Failed to parse Canada headers from frontpage: {filepath}; {e}"
logger.error(error_msg)
- return error_msg, None
+ raise ValueError(error_msg) from e
- return constants.RETURNCODE_OK, items_found
+ return items_found
def flatten_matches(dct: dict) -> dict:
diff --git a/src/sec_certs/utils/helpers.py b/src/sec_certs/utils/helpers.py
index 63de6fd9..4f0e4136 100644
--- a/src/sec_certs/utils/helpers.py
+++ b/src/sec_certs/utils/helpers.py
@@ -102,8 +102,7 @@ def download_file( # noqa: C901
show_progress_bar: bool = False,
progress_bar_desc: str | None = None,
proxy: bool = False,
-) -> str | int:
- """Download a file from a URL to a local path."""
+) -> int | None:
try:
proxied = False
if proxy:
@@ -142,13 +141,12 @@ def download_file( # noqa: C901
if show_progress_bar:
pbar.update(len(data))
- return r.status_code
+ return r.status_code
except requests.exceptions.Timeout:
return requests.codes.timeout
except Exception as e:
logger.error(f"Failed to download from {url}; {e}")
- return constants.RETURNCODE_NOK
- return constants.RETURNCODE_NOK
+ return None
def download_parallel(
diff --git a/src/sec_certs/utils/nvd_dataset_builder.py b/src/sec_certs/utils/nvd_dataset_builder.py
index 4e7162eb..bffde677 100644
--- a/src/sec_certs/utils/nvd_dataset_builder.py
+++ b/src/sec_certs/utils/nvd_dataset_builder.py
@@ -192,7 +192,7 @@ class NvdDatasetBuilder(Generic[DatasetType], ABC):
if response.status_code == 404:
# This is likely due to no CPEs to update, incremental update very soon.
return 0
- if response.status_code != constants.RESPONSE_OK:
+ if response.status_code != requests.codes.ok:
if fresh:
logger.warning(
f"Error when attempting to fetch number of pages to get from NVD API {self._ENDPOINT} endpoint, sleeping 6 seconds and repeating."
@@ -227,9 +227,9 @@ class NvdDatasetBuilder(Generic[DatasetType], ABC):
"""
Will fetch successfull responses into self._ok_responses and prune self.requests_to_process accordingly
"""
- response_is_nok = np.array([x.status_code != constants.RESPONSE_OK for x in responses])
+ response_is_nok = np.array([x.status_code != requests.codes.ok for x in responses])
nok_indices = np.where(response_is_nok == True)[0] # noqa E712, doesn't work with `is True`
- currently_ok = [x for x in responses if x.status_code == constants.RESPONSE_OK]
+ currently_ok = [x for x in responses if x.status_code == requests.codes.ok]
logger.info(
f"Attempt {self.max_attempts - self._attempts_left}/{self.max_attempts}: Successfully processed {len(currently_ok)}/{len(self._requests_to_process)} requests."
diff --git a/src/sec_certs/utils/pdf.py b/src/sec_certs/utils/pdf.py
index 3195e67e..39046717 100644
--- a/src/sec_certs/utils/pdf.py
+++ b/src/sec_certs/utils/pdf.py
@@ -13,7 +13,6 @@ import pikepdf
import pytesseract
from PIL import Image
-from sec_certs import constants
from sec_certs.constants import (
GARBAGE_ALPHA_CHARS_THRESHOLD,
GARBAGE_AVG_LLEN_THRESHOLD,
@@ -151,7 +150,7 @@ def parse_pdf_date(dateval: bytes | None) -> datetime | None:
return None
-def extract_pdf_metadata(filepath: Path) -> tuple[str, dict[str, Any] | None]: # noqa: C901
+def extract_pdf_metadata(filepath: Path) -> dict[str, Any]: # noqa: C901
"""
Extract PDF metadata, such as the number of pages, author, title, etc.
@@ -237,9 +236,9 @@ def extract_pdf_metadata(filepath: Path) -> tuple[str, dict[str, Any] | None]:
relative_filepath = "/".join(str(filepath).split("/")[-4:])
error_msg = f"Failed to read metadata of {relative_filepath}, error: {e}"
logger.error(error_msg)
- return error_msg, None
+ raise ValueError(error_msg)
- return constants.RETURNCODE_OK, metadata
+ return metadata
def text_is_garbage(text: str) -> bool:
@@ -274,7 +273,7 @@ def text_is_garbage(text: str) -> bool:
# If the average length of a line is small, this is garbage.
if avg_line_len < GARBAGE_AVG_LLEN_THRESHOLD:
return True
- # If there a small amount of lines that have more than one character at every second character, this is garbage.
+ # If there is a small amount of lines that have more than one character at every second character, this is garbage.
# This detects the ANSSI spacing issues.
if every_second < GARBAGE_EVERY_SECOND_CHAR_THRESHOLD:
return True