From 2fe9d4e6ad819d7b7e6c13326890ab2a4c39b14c Mon Sep 17 00:00:00 2001 From: Adam Janovsky Date: Mon, 16 Nov 2020 19:49:08 +0100 Subject: Added basic OOP tests for CC dataset and cert --- sec_certs/dataset.py | 7 +- sec_certs/helpers.py | 4 +- test/data/test_cc_oop/cc_products_active.csv | 3 + test/data/test_cc_oop/cc_products_active.html | 654 ++++++++++++++++++++++++++ test/data/test_cc_oop/fictional_cert.json | 30 ++ test/data/test_cc_oop/toy_dataset.json | 50 ++ test/test_cc_oop.py | 127 +++++ 7 files changed, 872 insertions(+), 3 deletions(-) create mode 100644 test/data/test_cc_oop/cc_products_active.csv create mode 100644 test/data/test_cc_oop/cc_products_active.html create mode 100644 test/data/test_cc_oop/fictional_cert.json create mode 100644 test/data/test_cc_oop/toy_dataset.json create mode 100644 test/test_cc_oop.py diff --git a/sec_certs/dataset.py b/sec_certs/dataset.py index b50285c5..7493f615 100644 --- a/sec_certs/dataset.py +++ b/sec_certs/dataset.py @@ -241,7 +241,6 @@ class CCDataset(Dataset): html_sources = [x for x in html_sources if 'active' not in x or get_active] html_sources = [x for x in html_sources if 'archived' not in x or get_archived] - new_certs = {} for file in html_sources: partial_certs = self.parse_single_html(self.web_dir / file) @@ -264,7 +263,11 @@ class CCDataset(Dataset): def parse_table(soup: BeautifulSoup, table_id: str, category_string: str) -> Dict[str, 'CommonCriteriaCert']: tables = soup.find_all('table', id=table_id) - assert len(tables) == 1 + assert len(tables) <= 1 + + if not tables: + return {} + table = tables[0] rows = list(table.find_all('tr')) header, footer, body = rows[0], rows[1], rows[2:] diff --git a/sec_certs/helpers.py b/sec_certs/helpers.py index 35ae843d..da5e73d7 100644 --- a/sec_certs/helpers.py +++ b/sec_certs/helpers.py @@ -58,7 +58,9 @@ def sanitize_string(record: str) -> Union[str, None]: return None else: # TODO: There is a certificate with name 'ATMEL Secure Microcontroller AT90SC12872RCFT / AT90SC12836RCFT rev. I &#38; J' that has to be unescaped twice - return html.unescape(html.unescape(record)).replace('\r\n', ' ').replace('\n', '') + string = html.unescape(html.unescape(record)).replace('\n', '') + return ' '.join(string.split()) + def sanitize_security_levels(record: Union[str, set]) -> set: diff --git a/test/data/test_cc_oop/cc_products_active.csv b/test/data/test_cc_oop/cc_products_active.csv new file mode 100644 index 00000000..2561a372 --- /dev/null +++ b/test/data/test_cc_oop/cc_products_active.csv @@ -0,0 +1,3 @@ +Category,Name,Manufacturer,Scheme,Assurance Level,Protection Profile(s),Certification Date,Archived Date,Certification Report URL,Security Target URL,Maintenance Date,Maintenance Title,Maintenance Report,Maintenance ST +Access Control Devices and Systems,NetIQ Identity Manager 4.7,NetIQ Corporation,SE,"EAL3+,ALC_FLR.2",,06/15/2020,06/15/2025,http://www.commoncriteriaportal.org:443/files/epfiles/Certification Report - NetIQ® Identity Manager 4.7.pdf,http://www.commoncriteriaportal.org:443/files/epfiles/ST - NetIQ Identity Manager 4.7.pdf,,,, +Access Control Devices and Systems,Magic SSO V4.0,"Dreamsecurity Co., Ltd.",KR,None,KECS-PP-0822-2017 SSO V1.0,11/15/2019,11/15/2024,http://www.commoncriteriaportal.org:443/files/epfiles/KECS-CR-19-70 Magic SSO V4.0(eng) V1.0.pdf,http://www.commoncriteriaportal.org:443/files/epfiles/Magic_SSO_V4.0-ST-v1.4_EN.pdf,,,, diff --git a/test/data/test_cc_oop/cc_products_active.html b/test/data/test_cc_oop/cc_products_active.html new file mode 100644 index 00000000..463fc51a --- /dev/null +++ b/test/data/test_cc_oop/cc_products_active.html @@ -0,0 +1,654 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Certified Products : New CC Portal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+ + + +
+ + +
+ +
+ +
+ + +
+ + +

Certified Products

+ + + + + + + + + + + + + + + +

+ The Common Criteria Recognition Arrangement covers certificates with claims of compliance against Common + Criteria assurance components of either:  +

+
    +
  1. a collaborative Protection Profile (cPP), developed and maintained in accordance with CCRA Annex K, + with assurance activities selected from Evaluation Assurance Levels up to and including level 4 and ALC_FLR, + developed through an International Technical Community endorsed by the Management Committee; or +
  2. Evaluation Assurance Levels 1 through 2 and ALC_FLR.  +
+

+ Where a CC certificate claims compliance to Evaluation Assurance Level 3 or higher, but does not claim + compliance to a collaborative Protection Profile, then for purposes of mutual recognition under the CCRA, + the CC certificate should be treated as equivalent to Evaluation Assurance Level 2.  +

+

+ The CCDB has approved a resolution to limit the validity of mutually recognized CC certificates over + time.  + Certificates will remain on the CPL for five years.  + Effective 1 June 2019, certificates with an expired validity period (that is, 5 years or more from the date + of certificate issuance) will be moved to an Archive list on the CCRA portal, unless the validity period has + been extended using the appropriate procedures.  +

+ + + + + + + +

+ expand/collapse all categories +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductVendor + Product
Certificate
+ Date Certificate
Issued
+ Certificate
Validity
Expiration
Date
ComplianceScheme
+ This list was generated on Wednesday, November 11, 2020, at 10:47 AM +
+ Access Control Devices and + Systems – 27 Certified Products +
+ + NetIQ Identity Manager 4.7   +
+ +
+ + + + + + +
+
+ NetIQ Corporation + + + CCRA Certificate + + 2020-06-152025-06-15 + + + EAL3+ +
ALC_FLR.2 +
+ + SE – Swedish Certification Body for IT Security FMV/CSEC
SE
+
+ + Magic SSO V4.0   + + + Dreamsecurity Co., Ltd. + + +   + + 2019-11-152024-11-15 + + + PP Compliant + + + KR – IT Security Certification Center(ITSCC)
KR
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + \ No newline at end of file diff --git a/test/data/test_cc_oop/fictional_cert.json b/test/data/test_cc_oop/fictional_cert.json new file mode 100644 index 00000000..44a422db --- /dev/null +++ b/test/data/test_cc_oop/fictional_cert.json @@ -0,0 +1,30 @@ +{ + "category": "Sample category", + "name": "Sample certificate name", + "manufacturer": "Sample manufacturer", + "scheme": "Sample scheme", + "security_level": [ + "Sample security level" + ], + "not_valid_before": "1900-01-02", + "not_valid_after": "1900-01-03", + "report_link": "http://path.to/report/link", + "st_link": "http://path.to/st/link", + "src": "custom", + "cert_link": "http://path.to/cert/link", + "manufacturer_web": "http://path.to/manufacturer/web", + "protection_profiles": [ + { + "pp_name": "sample_pp", + "pp_link": "http://sample.pp" + } + ], + "maintainance_updates": [ + { + "maintainance_date": "1900-01-01", + "maintainance_title": "Sample maintainance", + "maintainance_report_link": "https://maintainance.up", + "maintainance_st_link": "https://maintainance.up" + } + ] +} \ No newline at end of file diff --git a/test/data/test_cc_oop/toy_dataset.json b/test/data/test_cc_oop/toy_dataset.json new file mode 100644 index 00000000..49d612b5 --- /dev/null +++ b/test/data/test_cc_oop/toy_dataset.json @@ -0,0 +1,50 @@ +{ + "root_dir": "/fictional/path/to/dataset", + "timestamp": "2020-11-16 17:04:14.770153", + "sha256_digest": "not implemented", + "name": "toy dataset", + "description": "toy dataset description", + "n_certs": 2, + "certs": [ + { + "category": "Access Control Devices and Systems", + "name": "NetIQ Identity Manager 4.7", + "manufacturer": "NetIQ Corporation", + "scheme": "SE", + "security_level": [ + "ALC_FLR.2", + "EAL3+" + ], + "not_valid_before": "2020-06-15", + "not_valid_after": "2025-06-15", + "report_link": "http://www.commoncriteriaportal.org/files/epfiles/Certification%20Report%20-%20NetIQ\u00ae%20Identity%20Manager%204.7.pdf", + "st_link": "http://www.commoncriteriaportal.org/files/epfiles/ST%20-%20NetIQ%20Identity%20Manager%204.7.pdf", + "src": "csv + html", + "cert_link": "http://www.commoncriteriaportal.org/files/epfiles/Certifikat%20CCRA%20-%20NetIQ%20Identity%20Manager%204.7_signed.pdf", + "manufacturer_web": "https://www.netiq.com/", + "protection_profiles": [], + "maintainance_updates": [] + }, + { + "category": "Access Control Devices and Systems", + "name": "Magic SSO V4.0", + "manufacturer": "Dreamsecurity Co., Ltd.", + "scheme": "KR", + "security_level": [], + "not_valid_before": "2019-11-15", + "not_valid_after": "2024-11-15", + "report_link": "http://www.commoncriteriaportal.org/files/epfiles/KECS-CR-19-70%20Magic%20SSO%20V4.0(eng)%20V1.0.pdf", + "st_link": "http://www.commoncriteriaportal.org/files/epfiles/Magic_SSO_V4.0-ST-v1.4_EN.pdf", + "src": "csv + html", + "cert_link": null, + "manufacturer_web": "https://www.dreamsecurity.com/", + "protection_profiles": [ + { + "pp_name": "Korean National Protection Profile for Single Sign On V1.0", + "pp_link": "http://www.commoncriteriaportal.org/files/ppfiles/KECS-PP-0822-2017%20Korean%20National%20PP%20for%20Single%20Sign%20On%20V1.0(eng).pdf" + } + ], + "maintainance_updates": [] + } + ] +} \ No newline at end of file diff --git a/test/test_cc_oop.py b/test/test_cc_oop.py new file mode 100644 index 00000000..c29e90e3 --- /dev/null +++ b/test/test_cc_oop.py @@ -0,0 +1,127 @@ +from unittest import TestCase +from pathlib import Path +from tempfile import TemporaryDirectory, mkstemp +from datetime import date, datetime +import json +import filecmp +import shutil +import os + +from sec_certs.dataset import CCDataset, DatasetJSONDecoder, DatasetJSONEncoder +from sec_certs.certificate import CommonCriteriaCert + + +class TestCommonCriteriaOOP(TestCase): + def setUp(self): + self.test_data_dir = Path(__file__).parent / 'data' / 'test_cc_oop' + self.crt_one = CommonCriteriaCert('Access Control Devices and Systems', + 'NetIQ Identity Manager 4.7', + 'NetIQ Corporation', + 'SE', + {'ALC_FLR.2', + 'EAL3+'}, + date(2020, 6, 15), + date(2025, 6, 15), + 'http://www.commoncriteriaportal.org/files/epfiles/Certification%20Report%20-%20NetIQ\u00ae%20Identity%20Manager%204.7.pdf', + 'http://www.commoncriteriaportal.org/files/epfiles/ST%20-%20NetIQ%20Identity%20Manager%204.7.pdf', + 'csv + html', + 'http://www.commoncriteriaportal.org/files/epfiles/Certifikat%20CCRA%20-%20NetIQ%20Identity%20Manager%204.7_signed.pdf', + 'https://www.netiq.com/', + set(), + set()) + + self.crt_two = CommonCriteriaCert('Access Control Devices and Systems', + 'Magic SSO V4.0', + 'Dreamsecurity Co., Ltd.', + 'KR', + set(), + date(2019, 11, 15), + date(2024, 11, 15), + 'http://www.commoncriteriaportal.org/files/epfiles/KECS-CR-19-70%20Magic%20SSO%20V4.0(eng)%20V1.0.pdf', + 'http://www.commoncriteriaportal.org/files/epfiles/Magic_SSO_V4.0-ST-v1.4_EN.pdf', + 'csv + html', + None, + 'https://www.dreamsecurity.com/', + {CommonCriteriaCert.ProtectionProfile('Korean National Protection Profile for Single Sign On V1.0', + 'http://www.commoncriteriaportal.org/files/ppfiles/KECS-PP-0822-2017%20Korean%20National%20PP%20for%20Single%20Sign%20On%20V1.0(eng).pdf')}, + set()) + + pp = CommonCriteriaCert.ProtectionProfile('sample_pp', 'http://sample.pp') + update = CommonCriteriaCert.MaintainanceReport(date(1900, 1, 1), 'Sample maintainance', 'https://maintainance.up', 'https://maintainance.up') + self.fictional_cert = CommonCriteriaCert('Sample category', + 'Sample certificate name', + 'Sample manufacturer', + 'Sample scheme', + {'Sample security level'}, + date(1900, 1, 2), + date(1900, 1, 3), + 'http://path.to/report/link', + 'http://path.to/st/link', + 'custom', + 'http://path.to/cert/link', + 'http://path.to/manufacturer/web', + {pp}, + {update}) + self.template_dataset = CCDataset({self.crt_one.dgst: self.crt_one, self.crt_two.dgst: self.crt_two}, Path('/fictional/path/to/dataset'), 'toy dataset', 'toy dataset description') + self.template_dataset.timestamp = datetime(2020, 11, 16, hour=17, minute=4, second=14, microsecond=770153) + + def test_certificate_input_sanity(self): + self.assertEqual(self.crt_one.report_link, + 'http://www.commoncriteriaportal.org/files/epfiles/Certification%20Report%20-%20NetIQ®%20Identity%20Manager%204.7.pdf', + 'Report link contains some improperly escaped characters.') + + @staticmethod + def equal_to_json(referential_path, obj): + fd, path = mkstemp() + try: + with os.fdopen(fd, 'w') as handle: + json.dump(obj, handle, cls=DatasetJSONEncoder, indent=4) + + return filecmp.cmp(referential_path, path) + finally: + os.remove(path) + + @staticmethod + def equal_from_json(referential_path, obj): + with open(referential_path, 'r') as handle: + new_obj = json.load(handle, cls=DatasetJSONDecoder) + return obj == new_obj + + def test_cert_to_json(self): + self.assertTrue(self.equal_to_json(self.test_data_dir / 'fictional_cert.json', self.fictional_cert), + 'The certificate serialized to json differs from a template.') + + def test_dataset_to_json(self): + self.assertTrue(self.equal_to_json(self.test_data_dir / 'toy_dataset.json', self.template_dataset), + 'The dataset serialized to json differs from a template.') + + def test_cert_from_json(self): + self.assertTrue(self.equal_from_json(self.test_data_dir / 'fictional_cert.json', self.fictional_cert), + 'The certificate serialized from json differs from a template.') + + def test_dataset_from_json(self): + self.assertTrue(self.equal_from_json(self.test_data_dir / 'toy_dataset.json', self.template_dataset), + 'The dataset serialized from json differs from a template.') + + def test_build_empty_dataset(self): + with TemporaryDirectory() as tmp_dir: + dset = CCDataset({}, Path(tmp_dir), 'sample_dataset', 'sample dataset description') + dset.get_certs_from_web(to_download=False, get_archived=False, get_active=False) + self.assertEqual(len(dset), 0, 'The dataset should contain 0 files.') + + def test_build_dataset(self): + with TemporaryDirectory() as tmp_dir: + dataset_path = Path(tmp_dir) + os.mkdir(dataset_path / 'web') + shutil.copyfile(self.test_data_dir / 'cc_products_active.csv', dataset_path / 'web' / 'cc_products_active.csv') + shutil.copyfile(self.test_data_dir / 'cc_products_active.html', dataset_path / 'web' / 'cc_products_active.html') + + dset = CCDataset({}, dataset_path, 'sample_dataset', 'sample dataset description') + dset.get_certs_from_web(keep_metadata=False, to_download=False, get_archived=False, get_active=True) + + self.assertEqual(len(os.listdir(dataset_path)), 0, + 'Meta files (csv, html) were not deleted properly albeit this was explicitly required.') + + self.assertEqual(len(dset), 2, 'The dataset should contain 2 files.') + self.assertTrue(self.crt_one in dset, 'The dataset does not contain the template certificate.') + self.assertEqual(dset, self.template_dataset, 'The loaded dataset does not match the template dataset.') -- cgit v1.3.1