diff --git a/CHANGELOG.md b/CHANGELOG.md index 489cbb4..97963a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,16 @@ All notable changes to this project from version 0.9.3 onwards are documented in this file. -## 0.9.11 - 2024-03-XX +## 0.10.0 - 2024-04-11 + +### New features/enhancements + +- Add REST API endpoints for linting OCSP responses (#62 - implemented by @mans-andersson) ### Fixes -TBD +- Handle malformed inputs given via the CLI more gracefully (#63 - fixed by @ralienpp) +- Pin validators package version to work around issue in latest version (#65) ## 0.9.10 - 2024-03-04 diff --git a/pkilint/rest/__init__.py b/pkilint/rest/__init__.py index 9815a9d..b992f43 100644 --- a/pkilint/rest/__init__.py +++ b/pkilint/rest/__init__.py @@ -7,7 +7,7 @@ from pkilint.rest import model _PKILINT_VERSION = version('pkilint') -_API_VERSION = 'v1.1' +_API_VERSION = 'v1.2' app = FastAPI( title='pkilint API', @@ -15,7 +15,6 @@ description='HTTP interface for pkilint' ) - _CERTIFICATE_LINTER_GROUPS = [ cabf_smime.create_linter_group_instance(), cabf_serverauth.create_linter_group_instance(), @@ -96,12 +95,14 @@ def certificate_lint(linter_group_name: str, linter_name: str, doc: model.Certif return linter_instance.lint(parsed_doc) + @app.get('/ocsp/pkix') def ocsp_linter_validations() -> List[model.Validation]: """Returns the set of validations performed by the OCSP response linter""" return _OCSP_PKIX_LINTER.validations + @app.post('/ocsp/pkix') def ocsp_response_lint(doc: model.OcspResponseModel) -> model.LintResultList: """Lints the specified OCSP response""" diff --git a/pkilint/rest/model.py b/pkilint/rest/model.py index 242d850..d405785 100644 --- a/pkilint/rest/model.py +++ b/pkilint/rest/model.py @@ -129,6 +129,7 @@ def validate(self) -> 'CertificateModel': def parsed_document(self): return self._parsed_document + class OcspResponseModel(DocumentModel): _parsed_document = None @@ -153,6 +154,7 @@ def validate(self) -> 'OcspResponseModel': def parsed_document(self): return self._parsed_document + def create_unprocessable_entity_error_detail(message: str, error_type: str = 'value_error'): return [ { diff --git a/pkilint/rest/ocsp.py b/pkilint/rest/ocsp.py index f32f529..2a5ac1a 100644 --- a/pkilint/rest/ocsp.py +++ b/pkilint/rest/ocsp.py @@ -1,6 +1,7 @@ from pkilint.pkix import ocsp, create_attribute_decoder, create_extension_decoder, extension, name from pkilint.rest import model + def create_ocsp_response_linter(): return model.Linter( validator=ocsp.create_pkix_ocsp_response_validator_container( diff --git a/tests/test_server.py b/tests/test_server.py index b2acdc4..f2da58d 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -4,11 +4,11 @@ from fastapi.testclient import TestClient -from pkilint import report +from pkilint import report, pkix from pkilint.cabf import serverauth from pkilint.cabf.serverauth import serverauth_constants from pkilint.cabf.smime import smime_constants -from pkilint.pkix import certificate +from pkilint.pkix import certificate, ocsp, name, extension from pkilint.rest import app as web_app @@ -142,6 +142,28 @@ def test_version(client): ''' +_OCSP_RESPONSE = '''MIIDnwoBAKCCA5gwggOUBgkrBgEFBQcwAQEEggOFMIIDgTCBsKIWBBQK46D+ndQl +dpi163Lrygznvz318RgPMjAyNDA0MDIxMjM3NDdaMIGEMIGBMFkwDQYJYIZIAWUD +BAIBBQAEIDqZRndWgHOnB7/eUBhjReTNYTTbCF66odEEJfA7bwjqBCBHSmyjAfI9 +yff3B4cE4cf1/JbnFnX27YguerZcP1hFQwIEAarwDYAAGA8yMDI0MDQwMzEyMzc0 +N1qgERgPMjAyNDA0MTAxMjM3NDdaMAoGCCqGSM49BAMDA2kAMGYCMQDRmVmiIb4D +m9yEXiv2XtoeQi6ftpjLmlBqqRIi+3htfF/OyjdHnFuh38cQKYqqrWYCMQDKiPct +Vu7SQs587d2ZBEHQH20j5AFiGGsbI1b3+C9ZK6NIzgD6DnWlDwpSfilEarOgggJT +MIICTzCCAkswggGuoAMCAQICAQEwCgYIKoZIzj0EAwQwODELMAkGA1UEBhMCWFgx +FDASBgNVBAoMC0NlcnRzICdyIFVzMRMwEQYDVQQDDApJc3N1aW5nIENBMB4XDTI0 +MDQwMjEyMzc0N1oXDTI1MDQwMjEyMzc0N1owPDELMAkGA1UEBhMCWFgxFDASBgNV +BAoMC0NlcnRzICdyIFVzMRcwFQYDVQQDDA5PQ1NQIFJlc3BvbmRlcjB2MBAGByqG +SM49AgEGBSuBBAAiA2IABFsJAbiFIyluuRnVD/oanLN0vE1AlYYoK/7KEbHZWtu1 +RzSvVwv4K3IozyJrz0wl3bz+Oxo605Qw7/dj4daNLhUdkXILd5W1jaazRjlhOo+5 +tajaSMZ0cRf5kZ6EJPN+yKOBhzCBhDAdBgNVHQ4EFgQUCuOg/p3UJXaYtety68oM +57899fEwHwYDVR0jBBgwFoAUjsIUCWB26pA46TmuG21SxBd9n74wDAYDVR0TAQH/ +BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwkwDwYJKwYB +BQUHMAEFBAIFADAKBggqhkjOPQQDBAOBigAwgYYCQRQqjNYKbGXHdGXfEVvB//i+ +DiG02hraU9kGNKXeiQcPdZRajQsY/hdZPVyaykkAFVQGv29yWmTrEax+r4oZTtzG +AkFJCwtJpi7m00Qx9r/ugNWsnCFSiKUdxuvj7mg9lJtz0hexRJZKFODWJG5dUh// +Bc2w8vywgYYoduXu4QLcoP17CA==''' + + def _assert_validationerror_list_present(resp): j = resp.json() @@ -357,3 +379,32 @@ def test_validations_list(client): for actual, expected in zip(j, report.get_included_validations(v)): assert actual['code'] == expected.code assert actual['severity'] == str(expected.severity) + + +def test_ocsp_pkix_validations_list(client): + resp = client.get('/ocsp/pkix') + assert resp.status_code == HTTPStatus.OK + + j = resp.json() + + v = ocsp.create_pkix_ocsp_response_validator_container( + [ + ocsp.create_response_decoder(), + pkix.create_attribute_decoder(name.ATTRIBUTE_TYPE_MAPPINGS), + pkix.create_extension_decoder(extension.EXTENSION_MAPPINGS), + ], + [] + ) + + for actual, expected in zip(j, report.get_included_validations(v)): + assert actual['code'] == expected.code + assert actual['severity'] == str(expected.severity) + + +def test_ocsp_pkix_lint(client): + resp = client.post('/ocsp/pkix', json={'b64': _OCSP_RESPONSE}) + assert resp.status_code == HTTPStatus.OK + + j = resp.json() + + assert len(j['results']) == 0