diff options
| author | Ján Jančár | 2025-02-14 16:41:07 +0100 |
|---|---|---|
| committer | GitHub | 2025-02-14 16:41:07 +0100 |
| commit | a8cba36b9e3d2711c7f7c4a7c07d1cbe299cd1d7 (patch) | |
| tree | 2241537ca6e3168ca83a64ff9d90681d4f9739b9 | |
| parent | 2a2b60565df9904d913cd830e7675af91749c5c1 (diff) | |
| parent | bf27325735c0a68f480ccb6b7f2ea2265ab6124c (diff) | |
| download | sec-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.py | 3 | ||||
| -rw-r--r-- | src/sec_certs/dataset/auxiliary_dataset_handling.py | 4 | ||||
| -rw-r--r-- | src/sec_certs/dataset/cpe.py | 3 | ||||
| -rw-r--r-- | src/sec_certs/dataset/cve.py | 3 | ||||
| -rw-r--r-- | src/sec_certs/dataset/dataset.py | 3 | ||||
| -rw-r--r-- | src/sec_certs/dataset/fips_algorithm.py | 11 | ||||
| -rw-r--r-- | src/sec_certs/sample/cc.py | 186 | ||||
| -rw-r--r-- | src/sec_certs/sample/fips.py | 16 | ||||
| -rw-r--r-- | src/sec_certs/sample/protection_profile.py | 33 | ||||
| -rw-r--r-- | src/sec_certs/utils/extract.py | 30 | ||||
| -rw-r--r-- | src/sec_certs/utils/helpers.py | 8 | ||||
| -rw-r--r-- | src/sec_certs/utils/nvd_dataset_builder.py | 6 | ||||
| -rw-r--r-- | src/sec_certs/utils/pdf.py | 9 |
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 |
