diff options
| author | J08nY | 2024-02-13 17:11:58 +0100 |
|---|---|---|
| committer | J08nY | 2024-02-13 17:11:58 +0100 |
| commit | bb3b54d177797726d473f624be83c91266d612e9 (patch) | |
| tree | dc7bea6dfc6b1949f82bd4b978e6115a49aa8fec /src/sec_certs/sample | |
| parent | 4243fd6937bfdcab2dbcaa80e26fb8a03c579b0a (diff) | |
| download | sec-certs-bb3b54d177797726d473f624be83c91266d612e9.tar.gz sec-certs-bb3b54d177797726d473f624be83c91266d612e9.tar.zst sec-certs-bb3b54d177797726d473f624be83c91266d612e9.zip | |
Refactor document state in CC.
Diffstat (limited to 'src/sec_certs/sample')
| -rw-r--r-- | src/sec_certs/sample/cc.py | 342 |
1 files changed, 107 insertions, 235 deletions
diff --git a/src/sec_certs/sample/cc.py b/src/sec_certs/sample/cc.py index a4426d01..42cb15d4 100644 --- a/src/sec_certs/sample/cc.py +++ b/src/sec_certs/sample/cc.py @@ -5,7 +5,6 @@ import re from collections import Counter, defaultdict from dataclasses import dataclass, field from datetime import date, datetime -from enum import Enum from pathlib import Path from typing import Any, ClassVar from urllib.parse import unquote_plus, urlparse @@ -39,9 +38,61 @@ HEADERS = { } -class ReferenceType(Enum): - DIRECT = "direct" - INDIRECT = "indirect" +@dataclass +class CCDocumentState(ComplexSerializableType): + download_ok: bool = False # Whether download went OK + convert_garbage: bool = False # Whether initial conversion resulted in garbage + convert_ok: bool = False # Whether overall conversion went OK (either pdftotext or via OCR) + extract_ok: bool = False # Whether extraction went OK + + pdf_hash: str | None = None + txt_hash: str | None = None + + _pdf_path: Path | None = None + _txt_path: Path | None = None + + def is_ok_to_download(self, fresh: bool = True) -> bool: + return True if fresh else not self.download_ok + + def is_ok_to_convert(self, fresh: bool = True) -> bool: + return self.download_ok if fresh else self.download_ok and not self.convert_ok + + def is_ok_to_analyze(self, fresh: bool = True) -> bool: + if fresh: + return self.download_ok and self.convert_ok + else: + return self.download_ok and self.convert_ok and not self.extract_ok + + @property + def pdf_path(self) -> Path: + if not self._pdf_path: + raise ValueError(f"pdf_path not set on {type(self)}") + return self._pdf_path + + @pdf_path.setter + def pdf_path(self, pth: str | Path | None) -> None: + self._pdf_path = Path(pth) if pth else None + + @property + def txt_path(self) -> Path: + if not self._txt_path: + raise ValueError(f"txt_path not set on {type(self)}") + return self._txt_path + + @txt_path.setter + def txt_path(self, pth: str | Path | None) -> None: + self._txt_path = Path(pth) if pth else None + + @property + def serialized_attributes(self) -> list[str]: + return [ + "download_ok", + "convert_garbage", + "convert_ok", + "extract_ok", + "pdf_hash", + "txt_hash", + ] class CCCertificate( @@ -95,199 +146,20 @@ class CCCertificate( def __lt__(self, other): return self.maintenance_date < other.maintenance_date - @dataclass(init=False) + @dataclass class InternalState(ComplexSerializableType): """ Holds internal state of the certificate, whether downloads and converts of individual components succeeded. Also holds information about errors and paths to the files. """ - st_download_ok: bool # Whether target download went OK - report_download_ok: bool # Whether report download went OK - cert_download_ok: bool # Whether certificate download went OK - st_convert_garbage: bool # Whether initial target conversion resulted in garbage - report_convert_garbage: bool # Whether initial report conversion resulted in garbage - cert_convert_garbage: bool # Whether initial certificate conversion resulted in garbage - st_convert_ok: bool # Whether overall target conversion went OK (either pdftotext or via OCR) - report_convert_ok: bool # Whether overall report conversion went OK (either pdftotext or via OCR) - cert_convert_ok: bool # Whether overall certificate conversion went OK (either pdftotext or via OCR) - st_extract_ok: bool # Whether target extraction went OK - report_extract_ok: bool # Whether report extraction went OK - cert_extract_ok: bool # Whether certificate extraction went OK - - st_pdf_hash: str | None - report_pdf_hash: str | None - cert_pdf_hash: str | None - st_txt_hash: str | None - report_txt_hash: str | None - cert_txt_hash: str | None - - _st_pdf_path: Path | None = None - _report_pdf_path: Path | None = None - _cert_pdf_path: Path | None = None - _st_txt_path: Path | None = None - _report_txt_path: Path | None = None - _cert_txt_path: Path | None = None - - def __init__( - self, - st_download_ok: bool = False, - report_download_ok: bool = False, - cert_download_ok: bool = False, - st_convert_garbage: bool = False, - report_convert_garbage: bool = False, - cert_convert_garbage: bool = False, - st_convert_ok: bool = False, - report_convert_ok: bool = False, - cert_convert_ok: bool = False, - st_extract_ok: bool = False, - report_extract_ok: bool = False, - cert_extract_ok: bool = False, - st_pdf_hash: str | None = None, - report_pdf_hash: str | None = None, - cert_pdf_hash: str | None = None, - st_txt_hash: str | None = None, - report_txt_hash: str | None = None, - cert_txt_hash: str | None = None, - ): - super().__init__() - self.st_download_ok = st_download_ok - self.report_download_ok = report_download_ok - self.cert_download_ok = cert_download_ok - self.st_convert_garbage = st_convert_garbage - self.report_convert_garbage = report_convert_garbage - self.cert_convert_garbage = cert_convert_garbage - self.st_convert_ok = st_convert_ok - self.report_convert_ok = report_convert_ok - self.cert_convert_ok = cert_convert_ok - self.st_extract_ok = st_extract_ok - self.report_extract_ok = report_extract_ok - self.cert_extract_ok = cert_extract_ok - self.st_pdf_hash = st_pdf_hash - self.report_pdf_hash = report_pdf_hash - self.cert_pdf_hash = cert_pdf_hash - self.st_txt_hash = st_txt_hash - self.report_txt_hash = report_txt_hash - self.cert_txt_hash = cert_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 cert_pdf_path(self) -> Path: - if not self._cert_pdf_path: - raise ValueError(f"cert_pdf_path not set on {type(self)}") - return self._cert_pdf_path - - @cert_pdf_path.setter - def cert_pdf_path(self, pth: str | Path | None) -> None: - self._cert_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 cert_txt_path(self) -> Path: - if not self._cert_txt_path: - raise ValueError(f"cert_txt_path not set on {type(self)}") - return self._cert_txt_path - - @cert_txt_path.setter - def cert_txt_path(self, pth: str | Path | None) -> None: - self._cert_txt_path = Path(pth) if pth else None + report: CCDocumentState = field(default_factory=CCDocumentState) + st: CCDocumentState = field(default_factory=CCDocumentState) + cert: CCDocumentState = field(default_factory=CCDocumentState) @property def serialized_attributes(self) -> list[str]: - return [ - "st_download_ok", - "report_download_ok", - "cert_download_ok", - "st_convert_garbage", - "report_convert_garbage", - "cert_convert_garbage", - "st_convert_ok", - "report_convert_ok", - "cert_convert_ok", - "st_extract_ok", - "report_extract_ok", - "cert_extract_ok", - "st_pdf_hash", - "report_pdf_hash", - "cert_pdf_hash", - "st_txt_hash", - "report_txt_hash", - "cert_txt_hash", - ] - - def report_is_ok_to_download(self, fresh: bool = True) -> bool: - return True if fresh else not self.report_download_ok - - def st_is_ok_to_download(self, fresh: bool = True) -> bool: - return True if fresh else not self.st_download_ok - - def cert_is_ok_to_download(self, fresh: bool = True) -> bool: - return True if fresh else not self.st_download_ok - - def report_is_ok_to_convert(self, fresh: bool = True) -> bool: - return self.report_download_ok if fresh else self.report_download_ok and not self.report_convert_ok - - def st_is_ok_to_convert(self, fresh: bool = True) -> bool: - return self.st_download_ok if fresh else self.st_download_ok and not self.st_convert_ok - - def cert_is_ok_to_convert(self, fresh: bool = True) -> bool: - return self.cert_download_ok if fresh else self.cert_download_ok and not self.cert_convert_ok - - def report_is_ok_to_analyze(self, fresh: bool = True) -> bool: - if fresh is True: - return self.report_download_ok and self.report_convert_ok - else: - return self.report_download_ok and self.report_convert_ok and not self.report_extract_ok - - def st_is_ok_to_analyze(self, fresh: bool = True) -> bool: - if fresh is True: - return self.st_download_ok and self.st_convert_ok - else: - return self.st_download_ok and self.st_convert_ok and not self.st_extract_ok - - def cert_is_ok_to_analyze(self, fresh: bool = True) -> bool: - if fresh is True: - return self.cert_download_ok and self.cert_convert_ok - else: - return self.cert_download_ok and self.cert_convert_ok and not self.cert_extract_ok + return ["report", "st", "cert"] @dataclass class PdfData(BasePdfData, ComplexSerializableType): @@ -892,17 +764,17 @@ class CCCertificate( :param Optional[Union[str, Path]] cert_txt_dir: Directory where txtcertificates shall be stored """ if report_pdf_dir: - self.state.report_pdf_path = Path(report_pdf_dir) / (self.dgst + ".pdf") + self.state.report.pdf_path = Path(report_pdf_dir) / (self.dgst + ".pdf") if st_pdf_dir: - self.state.st_pdf_path = Path(st_pdf_dir) / (self.dgst + ".pdf") + self.state.st.pdf_path = Path(st_pdf_dir) / (self.dgst + ".pdf") if cert_pdf_dir: - self.state.cert_pdf_path = Path(cert_pdf_dir) / (self.dgst + ".pdf") + self.state.cert.pdf_path = Path(cert_pdf_dir) / (self.dgst + ".pdf") if report_txt_dir: - self.state.report_txt_path = Path(report_txt_dir) / (self.dgst + ".txt") + self.state.report.txt_path = Path(report_txt_dir) / (self.dgst + ".txt") if st_txt_dir: - self.state.st_txt_path = Path(st_txt_dir) / (self.dgst + ".txt") + self.state.st.txt_path = Path(st_txt_dir) / (self.dgst + ".txt") if cert_txt_dir: - self.state.cert_txt_path = Path(cert_txt_dir) / (self.dgst + ".txt") + self.state.cert.txt_path = Path(cert_txt_dir) / (self.dgst + ".txt") @staticmethod def download_pdf_report(cert: CCCertificate) -> CCCertificate: @@ -916,14 +788,14 @@ class CCCertificate( if not cert.report_link: exit_code = "No link" else: - exit_code = helpers.download_file(cert.report_link, cert.state.report_pdf_path) + exit_code = helpers.download_file(cert.report_link, cert.state.report.pdf_path) 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 + 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.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 @@ -936,16 +808,16 @@ class CCCertificate( :return CCCertificate: returns the modified certificate with updated state """ exit_code: str | int = ( - helpers.download_file(cert.st_link, cert.state.st_pdf_path) if cert.st_link else "No link" + 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) - cert.state.st_download_ok = False + 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.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 @@ -958,16 +830,16 @@ class CCCertificate( :return CCCertificate: returns the modified certificate with updated state """ exit_code: str | int = ( - helpers.download_file(cert.cert_link, cert.state.cert_pdf_path) if cert.cert_link else "No link" + helpers.download_file(cert.cert_link, cert.state.cert.pdf_path) if cert.cert_link else "No link" ) if exit_code != requests.codes.ok: error_msg = f"failed to download certificate from {cert.cert_link}, code: {exit_code}" logger.error(f"Cert dgst: {cert.dgst} " + error_msg) - cert.state.cert_download_ok = False + 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.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]) return cert @@ -980,17 +852,17 @@ class CCCertificate( :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 + 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 + cert.state.report.convert_garbage = ocr_done # And put the whole result into convert_ok - cert.state.report_convert_ok = ok_result + 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) + cert.state.report.txt_hash = helpers.get_sha256_filepath(cert.state.report.txt_path) return cert @staticmethod @@ -1001,16 +873,16 @@ 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) + 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 + cert.state.st.convert_garbage = ocr_done # And put the whole result into convert_ok - cert.state.st_convert_ok = ok_result + 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) + cert.state.st.txt_hash = helpers.get_sha256_filepath(cert.state.st.txt_path) return cert @staticmethod @@ -1021,16 +893,16 @@ 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) + 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 + cert.state.cert.convert_garbage = ocr_done # And put the whole result into convert_ok - cert.state.cert_convert_ok = ok_result + 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) + cert.state.cert.txt_hash = helpers.get_sha256_filepath(cert.state.cert.txt_path) return cert @staticmethod @@ -1041,11 +913,11 @@ 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) + 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 + cert.state.report.extract_ok = False else: - cert.state.report_extract_ok = True + cert.state.report.extract_ok = True return cert @staticmethod @@ -1056,11 +928,11 @@ 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) + 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 + cert.state.st.extract_ok = False else: - cert.state.st_extract_ok = True + cert.state.st.extract_ok = True return cert @staticmethod @@ -1071,11 +943,11 @@ 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) + 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 + cert.state.cert.extract_ok = False else: - cert.state.cert_extract_ok = True + cert.state.cert.extract_ok = True return cert @staticmethod @@ -1089,10 +961,10 @@ class CCCertificate( cert.pdf_data.report_frontpage = {} for header_type, associated_header_func in HEADERS.items(): - response, cert.pdf_data.report_frontpage[header_type] = associated_header_func(cert.state.report_txt_path) + response, cert.pdf_data.report_frontpage[header_type] = associated_header_func(cert.state.report.txt_path) if response != constants.RETURNCODE_OK: - cert.state.report_extract_ok = False + cert.state.report.extract_ok = False return cert @staticmethod @@ -1104,9 +976,9 @@ 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) + 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 + cert.state.report.extract_ok = False else: cert.pdf_data.report_keywords = report_keywords return cert @@ -1120,9 +992,9 @@ 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) + 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 + cert.state.st.extract_ok = False else: cert.pdf_data.st_keywords = st_keywords return cert @@ -1136,9 +1008,9 @@ 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) + 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 + cert.state.cert.extract_ok = False else: cert.pdf_data.cert_keywords = cert_keywords return cert |
