From ed77ac88ab64620f8b3c5a3ed9e730e129f2f507 Mon Sep 17 00:00:00 2001 From: Bartosz Gardziejewski Date: Thu, 10 Dec 2020 09:55:20 +0100 Subject: Add signature and certificate for individual artifacts. Signed-off-by: Bartosz Gardziejewski Change-Id: Ifb9e06fffefea6b4d068a915b69b66c750ab02e8 Issue-ID: VNFSDK-714 --- Changelog.md | 4 + .../cvc/csar/cc/sol004/VTPValidateCSARR130206.java | 139 ++++++++++++--- .../org/onap/cvc/csar/parser/MetadataParser.java | 6 +- .../VTPValidateCSARR130206IntegrationTest.java | 190 +++++++++++++++------ .../org/onap/functional/CsarValidationUtility.java | 11 ++ .../functional/PnfValidationFunctionalTest.java | 65 +++++-- ...incorrect-signature-of-individual-artifact.csar | Bin 0 -> 28266 bytes ...y-signature-or-cert-of-individual-artifact.csar | Bin 0 -> 28261 bytes ...alid-with-signature-of-individual-artifact.csar | Bin 0 -> 28272 bytes ...g-path-to-signature-of-individual-artifact.csar | Bin 0 -> 28263 bytes ...-cms-with-signature-of-individual-artifact.csar | Bin 0 -> 4602 bytes ...with-tosca-cert-pointing-non-existing-cert.csar | Bin 114649 -> 116253 bytes 12 files changed, 333 insertions(+), 82 deletions(-) create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-incorrect-signature-of-individual-artifact.csar create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-only-signature-or-cert-of-individual-artifact.csar create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-signature-of-individual-artifact.csar create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-wrong-path-to-signature-of-individual-artifact.csar create mode 100644 csarvalidation/src/test/resources/pnf/r130206/csar-no-cms-with-signature-of-individual-artifact.csar diff --git a/Changelog.md b/Changelog.md index 001e931..c08f264 100644 --- a/Changelog.md +++ b/Changelog.md @@ -112,3 +112,7 @@ All notable changes to this project will be documented in this file. - https://jira.onap.org/browse/VNFSDK-713 ## [1.2.16] + +## Added +- Possibility to add certificate and signature per artifact in manifest file. + - https://jira.onap.org/browse/VNFSDK-714 diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java index 05feb54..6f1ff1f 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java @@ -57,7 +57,7 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { private static final String EMPTY_STRING = ""; private final ShaHashCodeGenerator shaHashCodeGenerator = new ShaHashCodeGenerator(); - private final ManifestFileSignatureValidator manifestFileSignatureValidator = new ManifestFileSignatureValidator(); + private final FileSignatureValidator fileSignatureValidator = new FileSignatureValidator(); public static class CSARErrorUnableToFindCertificate extends CSARArchive.CSARError { @@ -178,6 +178,54 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } } + public static class CSARErrorUnableToFindArtifactCertificate extends CSARArchive.CSARError { + + CSARErrorUnableToFindArtifactCertificate(String path) { + super("0x4015"); + this.message = String.format("Source '%s' has signature tag, but unable to find certificate tag!", path); + } + } + + public static class CSARErrorUnableToFindArtifactCertificateFile extends CSARArchive.CSARError { + + CSARErrorUnableToFindArtifactCertificateFile(String path) { + super("0x4016"); + this.message = String.format("Source '%s' has certificate tag, pointing to non existing file!", path); + } + } + + public static class CSARErrorUnableToFindArtifactSignature extends CSARArchive.CSARError { + + CSARErrorUnableToFindArtifactSignature(String path) { + super("0x4017"); + this.message = String.format("Source '%s' has certificate tag, but unable to find signature tag!", path); + } + } + + public static class CSARErrorUnableToFindArtifactSignatureFile extends CSARArchive.CSARError { + + CSARErrorUnableToFindArtifactSignatureFile(String path) { + super("0x4018"); + this.message = String.format("Source '%s' has signature tag, pointing to non existing file!", path); + } + } + + public static class CSARErrorFailToLoadArtifactSignature extends CSARArchive.CSARError { + + CSARErrorFailToLoadArtifactSignature(String path, String cms) { + super("0x4019"); + this.message = String.format("Fail to load signature '%s', for source '%s'!", path, cms); + } + } + + public static class CSARErrorIncorrectArtifactSignature extends CSARArchive.CSARError { + + CSARErrorIncorrectArtifactSignature(String path) { + super("0x4020"); + this.message = String.format("Source '%s' has incorrect signature!", path); + } + } + public static class CSARWarningNoSecurity extends CSARArchive.CSARErrorWarning { CSARWarningNoSecurity() { super(EMPTY_STRING, EMPTY_STRING, -1, EMPTY_STRING); @@ -207,9 +255,9 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { if (containsCms(csar.getManifest())) { validateCmsSignature(csar, csarRootDirectory); } else if ( - ( containsToscaMeta(csar) && containsCertificateInTosca(csar.getToscaMeta()) ) || + (containsToscaMeta(csar) && containsCertificateInTosca(csar.getToscaMeta())) || containsCertificateInRootCatalog(csar) || - containsHashOrAlgorithm(csar.getManifest())) { + containsPerArtifactSecurity(csar.getManifest())) { this.errors.add(new CSARErrorUnableToFindCms()); } else { this.errors.add(new CSARWarningNoSecurity()); @@ -218,7 +266,7 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { private void validateCmsSignature(CSARArchive csar, Path csarRootDirectory) throws NoSuchAlgorithmException, IOException { try { - CmsSignatureData signatureData = this.manifestFileSignatureValidator.createSignatureData(csar.getManifestMfFile()); + CmsSignatureData signatureData = this.fileSignatureValidator.createSignatureDataForManifestFile(csar.getManifestMfFile()); if (signatureData.getCertificate().isPresent()) { validateCertificationUsingCmsCertificate(signatureData, csar, csarRootDirectory); } else if (containsToscaMeta(csar)) { @@ -253,11 +301,13 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { return potentialCertificateFileInRootDirectory.exists(); } - private boolean containsHashOrAlgorithm(CSARArchive.Manifest manifest) { + private boolean containsPerArtifactSecurity(CSARArchive.Manifest manifest) { return manifest.getSources().stream().anyMatch( source -> !source.getAlgorithm().equals(EMPTY_STRING) || - !source.getHash().equals(EMPTY_STRING) + !source.getHash().equals(EMPTY_STRING) || + !source.getCertificate().equals(EMPTY_STRING) || + !source.getSignature().equals(EMPTY_STRING) ); } @@ -288,7 +338,7 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } private boolean loadCertificateFromTosca(CmsSignatureData signatureData, CSARArchive csar) { - if(csar.getToscaMeta().getEntryCertificate() != null) { + if (csar.getToscaMeta().getEntryCertificate() != null) { try { final Path absolutePathToEntryCertificate = csar.getFileFromCsar(csar.getToscaMeta().getEntryCertificate()).toPath(); signatureData.loadCertificate(absolutePathToEntryCertificate); @@ -368,7 +418,7 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { } private void validateFileSignature(CmsSignatureData signatureData) { - final boolean isValid = this.manifestFileSignatureValidator.isValid(signatureData); + final boolean isValid = this.fileSignatureValidator.isValid(signatureData); if (!isValid) { this.errors.add(new CSARErrorInvalidSignature()); } @@ -378,23 +428,27 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { throws NoSuchAlgorithmException, IOException { final List sources = manifest.getSources(); for (SourcesParser.Source source : sources) { - if (!source.getAlgorithm().isEmpty() || !source.getHash().isEmpty()) { - validateSource(csarRootDirectory, source); - } + validateSource(csarRootDirectory, source); } } private void validateSource(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { final Path sourcePath = csarRootDirectory.resolve(source.getValue()); - if (!sourcePath.toFile().exists()) { - this.errors.add(new CSARErrorUnableToFindSource(source.getValue())); + if (sourcePath.toFile().exists()) { + validateHashIfPresent(csarRootDirectory, source); + validateArtifactSignatureIfPresent(csarRootDirectory, source); } else { - if (!source.getAlgorithm().isEmpty()) { - validateSourceHashCode(csarRootDirectory, source); - } else if (source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()) { - this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue())); - } + this.errors.add(new CSARErrorUnableToFindSource(source.getValue())); + } + } + + private void validateHashIfPresent(Path csarRootDirectory, SourcesParser.Source source) + throws NoSuchAlgorithmException, IOException { + if (!source.getAlgorithm().isEmpty()) { + validateSourceHashCode(csarRootDirectory, source); + } else if (source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue())); } } @@ -420,19 +474,55 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { throw new UnsupportedOperationException(String.format("Algorithm '%s' is not supported!", algorithm)); } + private void validateArtifactSignatureIfPresent(Path csarRootDirectory, SourcesParser.Source source) + throws IOException { + if (!source.getSignature().isEmpty() && !source.getCertificate().isEmpty()) { + validateArtifactSignature(csarRootDirectory, source); + } else if (!source.getSignature().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindArtifactCertificate(source.getValue())); + } else if (!source.getCertificate().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindArtifactSignature(source.getValue())); + } + } + + private void validateArtifactSignature(Path csarRootDirectory, SourcesParser.Source source) + throws IOException { + final Path filePath = csarRootDirectory.resolve(source.getValue()); + final Path signaturePath = csarRootDirectory.resolve(source.getSignature()); + final Path certificatePath = csarRootDirectory.resolve(source.getCertificate()); + if (signaturePath.toFile().exists() && certificatePath.toFile().exists()) { + try { + CmsSignatureData signatureData = + fileSignatureValidator.createSignatureData(filePath, signaturePath, certificatePath); + if( !fileSignatureValidator.isValid(signatureData) ) { + this.errors.add(new CSARErrorIncorrectArtifactSignature(source.getValue())); + } + } catch (CmsSignatureLoadingException e) { + this.errors.add(new CSARErrorFailToLoadArtifactSignature(source.getValue(),source.getSignature())); + } + } else { + if (!signaturePath.toFile().exists()) { + this.errors.add(new CSARErrorUnableToFindArtifactSignatureFile(source.getValue())); + } + if (!certificatePath.toFile().exists()) { + this.errors.add(new CSARErrorUnableToFindArtifactCertificateFile(source.getValue())); + } + } + } + @Override protected String getVnfReqsNo() { return "R130206"; } - static class ManifestFileSignatureValidator { + static class FileSignatureValidator { private final ManifestFileSplitter manifestFileSplitter = new ManifestFileSplitter(); private final CmsSignatureValidator cmsSignatureValidator = new CmsSignatureValidator(); private final CmsSignatureDataFactory cmsSignatureDataFactory = new CmsSignatureDataFactory(); - CmsSignatureData createSignatureData(File manifestFile) throws CmsSignatureLoadingException { + CmsSignatureData createSignatureDataForManifestFile(File manifestFile) throws CmsSignatureLoadingException { ManifestFileModel mf = manifestFileSplitter.split(manifestFile); return cmsSignatureDataFactory.createForFirstSigner( toBytes(mf.getCMS(), mf.getNewLine()), @@ -440,6 +530,15 @@ public class VTPValidateCSARR130206 extends VTPValidateCSARBase { ); } + CmsSignatureData createSignatureData(Path filePath, Path cmsFilePath, Path certFilePath) throws CmsSignatureLoadingException, IOException { + CmsSignatureData signatureData = cmsSignatureDataFactory.createForFirstSigner( + Files.readAllBytes(cmsFilePath), + Files.readAllBytes(filePath) + ); + signatureData.loadCertificate(certFilePath); + return signatureData; + } + boolean isValid(CmsSignatureData signatureData) { try { return cmsSignatureValidator.verifySignedData(signatureData); diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java index f529c45..8e3a9b8 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java @@ -91,13 +91,15 @@ public class MetadataParser { private boolean isSourceSection(Pair data) { return data.getKey().equalsIgnoreCase(SOURCE_TAG_SECTION) || data.getKey().equalsIgnoreCase(ALGORITHM) - || data.getKey().equalsIgnoreCase(HASH); + || data.getKey().equalsIgnoreCase(HASH) + || data.getKey().equalsIgnoreCase(SIGNATURE) + || data.getKey().equalsIgnoreCase(CERTIFICATE); } private boolean isSectionSupported(String key) { return Lists.newArrayList( METADATA_SECTION_TAG_SECTION, - SOURCE_TAG_SECTION, ALGORITHM, HASH, + SOURCE_TAG_SECTION, ALGORITHM, HASH, SIGNATURE, CERTIFICATE, NON_MANO_ARTIFACT_SETS_TAG_SECTION).contains(key.toLowerCase()); } diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java index 2d6d058..7337a29 100644 --- a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java +++ b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java @@ -34,6 +34,7 @@ import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.convertToMessages public class VTPValidateCSARR130206IntegrationTest { private static final boolean IS_PNF = true; + public static final String VTP_VALIDATE_SCHEMA = "vtp-validate-csar-r130206.yaml"; private VTPValidateCSARR130206 testCase; @Before @@ -54,7 +55,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void manual_shouldValidateProperCsarWithCms() throws Exception { // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-valid.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-valid.csar"); // when testCase.execute(); @@ -72,7 +73,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void manual_shouldValidateCsarWithCertificateInToscaEtsiWithValidSignature() throws Exception { // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-tosca-valid.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-tosca-valid.csar"); // when testCase.execute(); @@ -86,7 +87,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReportWarningForMissingCertInCmsToscaMetaAndRootCatalogAndMissingHashCodesInManifest() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-not-secure-warning.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-not-secure-warning.csar"); // when testCase.execute(); @@ -94,7 +95,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Warning. Consider adding package integrity and authenticity assurance according to ETSI NFV-SOL 004 Security Option 1" ); } @@ -103,7 +104,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnNoErrorWhenCertIsOnlyInCmsAndAlgorithmAndHashesAreCorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms.csar"); // when testCase.execute(); @@ -111,7 +112,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "File has invalid signature!" ); } @@ -120,7 +121,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnNoErrorWhenCertIsOnlyInToscaAndAlgorithmAndHashesAreCorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-tosca.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-tosca.csar"); // when testCase.execute(); @@ -128,7 +129,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "File has invalid signature!" ); } @@ -137,7 +138,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCsarContainsToscaFileHoweverToscaDoesNotContainsCertEntryAndAlgorithmAndHashesAreCorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-with-tosca-no-cert-entry.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-with-tosca-no-cert-entry.csar"); // when testCase.execute(); @@ -145,7 +146,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Unable to find ETSI-Entry-Certificate in Tosca file" ); } @@ -154,7 +155,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertIsOnlyInCmsHoweverHashesAreIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-incorrect-hash.csar"); // when testCase.execute(); @@ -162,7 +163,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(2); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Source 'Artifacts/Other/my_script.csh' has wrong hash!", "File has invalid signature!" ); @@ -172,7 +173,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertIsOnlyInToscaHoweverHashesAreIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-tosca-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-tosca-incorrect-hash.csar"); // when testCase.execute(); @@ -180,7 +181,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(2); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Source 'Artifacts/Deployment/Measurements/PM_Dictionary.yml' has wrong hash!", "File has invalid signature!" ); @@ -190,7 +191,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertIsOnlyInRootDirectoryHoweverHashesAreIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-root-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-root-incorrect-hash.csar"); // when testCase.execute(); @@ -198,7 +199,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(3); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Source 'Artifacts/Deployment/Events/RadioNode_Pnf_v1.yaml' has wrong hash!", "Unable to find ETSI-Entry-Certificate in Tosca file", "Certificate present in root catalog despite the TOSCA.meta file" @@ -209,15 +210,14 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenToscaEtsiEntryCertificatePointToNotExistingFile() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-with-tosca-cert-pointing-non-existing-cert.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-with-tosca-cert-pointing-non-existing-cert.csar"); // when testCase.execute(); // then List errors = testCase.getErrors(); - assertThat(errors.size()).isEqualTo(2); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Unable to find cert file defined by ETSI-Entry-Certificate!", "Invalid value. Entry [Entry-Certificate]. Artifacts/sample-pnf.cert does not exist" ); @@ -227,7 +227,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInCmsAndInTosca() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-and-tosca.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-and-tosca.csar"); // when testCase.execute(); @@ -235,10 +235,10 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(3); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( + "File has invalid signature!", "ETSI-Entry-Certificate entry in Tosca.meta is defined despite the certificate is included in the signature container", - "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container", - "File has invalid signature!" + "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container" ); } @@ -246,7 +246,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInCmsAndInToscaAndHashIsIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-and-tosca-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-and-tosca-incorrect-hash.csar"); // when testCase.execute(); @@ -254,11 +254,11 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(4); - assertThat(convertToMessagesList(errors)).contains( - "ETSI-Entry-Certificate entry in Tosca.meta is defined despite the certificate is included in the signature container", - "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container", + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Source 'Artifacts/Informational/user_guide.txt' has wrong hash!", - "File has invalid signature!" + "File has invalid signature!", + "ETSI-Entry-Certificate entry in Tosca.meta is defined despite the certificate is included in the signature container", + "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container" ); } @@ -266,7 +266,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInCmsAndInToscaAndInRootDirectory() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-and-root-and-tosca.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-and-root-and-tosca.csar"); // when testCase.execute(); @@ -274,7 +274,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(4); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "ETSI-Entry-Certificate entry in Tosca.meta is defined despite the certificate is included in the signature container", "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container", "Certificate present in root catalog despite the certificate is included in the signature container", @@ -287,7 +287,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInCmsAndInToscaAndInRootDirectoryAndHashIsIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-and-root-and-tosca-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-and-root-and-tosca-incorrect-hash.csar"); // when testCase.execute(); @@ -295,7 +295,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(5); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "ETSI-Entry-Certificate entry in Tosca.meta is defined despite the certificate is included in the signature container", "ETSI-Entry-Certificate certificate present despite the certificate is included in the signature container", "Certificate present in root catalog despite the certificate is included in the signature container", @@ -308,7 +308,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInCmsAndInRootDirectory() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-and-root.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-and-root.csar"); // when testCase.execute(); @@ -316,7 +316,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(2); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Certificate present in root catalog despite the certificate is included in the signature container", "File has invalid signature!" ); @@ -326,7 +326,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInCmsAndInRootDirectoryAndHashIsIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-cms-and-root-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-and-root-incorrect-hash.csar"); // when testCase.execute(); @@ -334,7 +334,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(3); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Certificate present in root catalog despite the certificate is included in the signature container", "Source 'Artifacts/Informational/user_guide.txt' has wrong hash!", "File has invalid signature!" @@ -345,7 +345,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInToscaAndInRootDirectory() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-root-and-tosca.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-root-and-tosca.csar"); // when testCase.execute(); @@ -353,7 +353,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(2); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Certificate present in root catalog despite the TOSCA.meta file", "File has invalid signature!" ); @@ -363,7 +363,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInToscaAndInRootDirectoryAdnHashIsIncorrect() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-root-and-tosca-incorrect-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-root-and-tosca-incorrect-hash.csar"); // when testCase.execute(); @@ -371,7 +371,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(3); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Certificate present in root catalog despite the TOSCA.meta file", "Source 'Artifacts/Deployment/Yang_module/yang-module1.yang' has wrong hash!", "File has invalid signature!" @@ -382,7 +382,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnNoErrorWhenCertificateIsLocatedInToscaAndInRootDirectoryHoweverEtsiEntryIsPointingCertificateInRoot() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-root-pointed-by-tosca.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-root-pointed-by-tosca.csar"); // when testCase.execute(); @@ -390,7 +390,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "File has invalid signature!" ); } @@ -399,7 +399,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCertificateIsLocatedInToscaHoweverManifestDoesNotContainsCms() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-tosca-no-cms.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-tosca-no-cms.csar"); // when testCase.execute(); @@ -407,7 +407,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Unable to find cms signature!" ); } @@ -416,7 +416,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnErrorWhenCsarDoesNotContainsCmsAndCertsHoweverManifestContainsHash() throws Exception{ // given - configureTestCase(testCase, "pnf/r130206/csar-no-cms-no-cert-with-hash.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-no-cms-no-cert-with-hash.csar"); // when testCase.execute(); @@ -424,7 +424,7 @@ public class VTPValidateCSARR130206IntegrationTest { // then List errors = testCase.getErrors(); assertThat(errors.size()).isEqualTo(1); - assertThat(convertToMessagesList(errors)).contains( + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( "Unable to find cms signature!" ); } @@ -433,7 +433,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnNoCertificationErrorWhenCertIsOnlyInRoot() throws Exception { // given - configureTestCase(testCase, "pnf/r130206/csar-cert-in-root.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-root.csar"); // when testCase.execute(); @@ -452,7 +452,7 @@ public class VTPValidateCSARR130206IntegrationTest { public void shouldReturnCertificateNotFoundErrorWhenCertIsNotPresentInCmsInRootAndTocsaDirectoryIsMissing() throws Exception { // given - configureTestCase(testCase, "pnf/r130206/csar-no-cert-no-tosca-dir.csar", "vtp-validate-csar-r130206.yaml", IS_PNF); + configureTestCaseForRule130206("pnf/r130206/csar-no-cert-no-tosca-dir.csar"); // when testCase.execute(); @@ -466,4 +466,98 @@ public class VTPValidateCSARR130206IntegrationTest { "Unable to find cert file!" ); } + + @Test + public void shouldReturnNoErrorWhenCertIsPresentInCmsAndIndividualArtifactHaveCorrectSignature() throws Exception { + + // given + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-valid-with-signature-of-individual-artifact.csar"); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( + "File has invalid signature!" + ); + } + + @Test + public void shouldReturnErrorsWhenCertIsPresentInCmsAndIndividualArtifactHaveOnlySignatureOrCertificate() throws Exception { + + // given + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-valid-with-only-signature-or-cert-of-individual-artifact.csar"); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( + "Source 'Files/Scripts/my_script.sh' has certificate tag, but unable to find signature tag!", + "Source 'Files/pnf-sw-information/pnf-sw-information.yaml' has signature tag, but unable to find certificate tag!", + "File has invalid signature!" + ); + } + + @Test + public void shouldReturnErrorsWhenCertIsPresentInCmsAndIndividualArtifactHaveSignatureAndCertificateShowingIncorrectFiles() throws Exception { + + // given + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-valid-with-wrong-path-to-signature-of-individual-artifact.csar"); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( + "Source 'Files/Yang_module/mynetconf.yang' has signature tag, pointing to non existing file!", + "Source 'Files/Yang_module/mynetconf.yang' has certificate tag, pointing to non existing file!", + "File has invalid signature!" + ); + } + + @Test + public void shouldReturnErrorWhenCertIsPresentInCmsAndIndividualArtifactHaveIncorrectSignature() throws Exception { + + // given + configureTestCaseForRule130206("pnf/r130206/csar-cert-in-cms-valid-with-incorrect-signature-of-individual-artifact.csar"); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( + "Source 'Files/ChangeLog.txt' has incorrect signature!", + "File has invalid signature!" + ); + } + + @Test + public void shouldReturnErrorWhenOnlyIndividualArtifactHaveSignature() throws Exception { + + // given + configureTestCaseForRule130206("pnf/r130206/csar-no-cms-with-signature-of-individual-artifact.csar"); + + // when + testCase.execute(); + + // then + List errors = testCase.getErrors(); + + assertThat(convertToMessagesList(errors)).containsExactlyInAnyOrder( + "Unable to find cms signature!" + ); + } + + private void configureTestCaseForRule130206(String filePath) throws OnapCommandException, URISyntaxException { + configureTestCase(testCase, filePath, VTP_VALIDATE_SCHEMA, IS_PNF); + } } diff --git a/csarvalidation/src/test/java/org/onap/functional/CsarValidationUtility.java b/csarvalidation/src/test/java/org/onap/functional/CsarValidationUtility.java index efeeb5d..11292d8 100644 --- a/csarvalidation/src/test/java/org/onap/functional/CsarValidationUtility.java +++ b/csarvalidation/src/test/java/org/onap/functional/CsarValidationUtility.java @@ -35,6 +35,7 @@ public final class CsarValidationUtility { public static final String CERTIFICATION_RULE = "r130206"; public static final String PM_DICTIONARY_YAML_RULE = "r816745"; + public static final String MANIFEST_FILE_RULE = "r01123"; public static final String OPERATION_STATUS_FAILED = "FAILED"; public static final String OPERATION_STATUS_PASS = "PASS"; @@ -53,6 +54,16 @@ public final class CsarValidationUtility { ); } + public static OnapCliValidationResponseWrapper.ValidationResultWrapper.ValidationErrorWrapper createExpectedError( + String rule, String errorCode, String errorMessage, String file + ) { + return new OnapCliValidationResponseWrapper.ValidationResultWrapper.ValidationErrorWrapper( + rule.toUpperCase(), errorCode, + errorMessage, + file, UNKNOWN_LINE_NUMBER + ); + } + public static boolean ruleHaveOneOfCodes(String ruleCode, String... codes ) { return Arrays.asList(codes).contains(ruleCode); } diff --git a/csarvalidation/src/test/java/org/onap/functional/PnfValidationFunctionalTest.java b/csarvalidation/src/test/java/org/onap/functional/PnfValidationFunctionalTest.java index 4dec242..576f763 100644 --- a/csarvalidation/src/test/java/org/onap/functional/PnfValidationFunctionalTest.java +++ b/csarvalidation/src/test/java/org/onap/functional/PnfValidationFunctionalTest.java @@ -26,6 +26,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.absoluteFilePath; import static org.onap.functional.CsarValidationUtility.CERTIFICATION_RULE; +import static org.onap.functional.CsarValidationUtility.MANIFEST_FILE_RULE; import static org.onap.functional.CsarValidationUtility.OPERATION_STATUS_FAILED; import static org.onap.functional.CsarValidationUtility.OPERATION_STATUS_PASS; import static org.onap.functional.CsarValidationUtility.PM_DICTIONARY_YAML_RULE; @@ -53,13 +54,12 @@ public class PnfValidationFunctionalTest { assertThat(result.criteria).isEqualTo(OPERATION_STATUS_PASS); result.results.forEach((ruleValidationResult)->{ - assertThat(ruleValidationResult.errors).hasSize(0); + assertThat(ruleValidationResult.errors).isEmpty(); if (ruleValidationResult.vnfreqName.equals(CERTIFICATION_RULE)) { assertThat(ruleValidationResult.warnings) - .hasSize(1) - .containsOnly(expectedWarning); + .containsExactlyInAnyOrder(expectedWarning); } else { - assertThat(ruleValidationResult.warnings).hasSize(0); + assertThat(ruleValidationResult.warnings).isEmpty(); } }); verifyThatOperationFinishedWithoutAnyError(cli); @@ -106,13 +106,12 @@ public class PnfValidationFunctionalTest { assertThat(result.criteria).isEqualTo(OPERATION_STATUS_FAILED); result.results.forEach((ruleValidationResult)->{ - assertThat(ruleValidationResult.warnings).hasSize(0); + assertThat(ruleValidationResult.warnings).isEmpty(); if (ruleValidationResult.vnfreqName.equals(CERTIFICATION_RULE)) { assertThat(ruleValidationResult.errors) - .hasSize(5) - .containsAll(expectedErrors); + .containsExactlyInAnyOrderElementsOf(expectedErrors); } else { - assertThat(ruleValidationResult.errors).hasSize(0); + assertThat(ruleValidationResult.errors).isEmpty(); } }); verifyThatOperationFinishedWithoutAnyError(cli); @@ -152,8 +151,7 @@ public class PnfValidationFunctionalTest { assertThat(ruleValidationResult.warnings).isEmpty(); if (ruleValidationResult.vnfreqName.equals(CERTIFICATION_RULE)) { assertThat(ruleValidationResult.errors) - .hasSize(5) - .containsAll(expectedErrors); + .containsExactlyInAnyOrderElementsOf(expectedErrors); } else { assertThat(ruleValidationResult.errors).isEmpty(); } @@ -219,14 +217,57 @@ public class PnfValidationFunctionalTest { assertThat(ruleValidationResult.warnings).isEmpty(); if (ruleValidationResult.vnfreqName.equals(CERTIFICATION_RULE)) { assertThat(ruleValidationResult.errors) - .hasSize(5) - .containsAll(expectedErrors); + .containsExactlyInAnyOrderElementsOf(expectedErrors); } else { assertThat(ruleValidationResult.errors).isEmpty(); } }); verifyThatOperationFinishedWithoutAnyError(cli); } + + @Test + public void shouldReportThatIndividualArtifactHaveIncorrectCertificateAndCertificateAndSignatureAreNotPresentAsSources() throws URISyntaxException { + // given + List expectedCertificationErrors = + List.of( + createExpectedError(CERTIFICATION_RULE, "0x4020", + "Source 'Files/ChangeLog.txt' has incorrect signature!"), + createExpectedError(CERTIFICATION_RULE, "0x4007", + "File has invalid signature!") + ); + List expectedManifestErrors = + List.of( + createExpectedError(MANIFEST_FILE_RULE, "0x1001", + "file(s): [Files/pnf-sw-information/pnf-sw-information.cert, Files/pnf-sw-information/pnf-sw-information.sig.cms] available in CSAR, but cannot be found in Manifest as Source", + "TOSCA-Metadata" + ) + ); + OnapCliWrapper cli = new OnapCliWrapper(createPnfValidationRequestInfo( + "pnf/r130206/csar-cert-in-cms-valid-with-incorrect-signature-of-individual-artifact.csar" + )); + + // when + cli.handle(); + + // then + final OnapCliValidationResponseWrapper result = getCliCommandValidationResult(cli); + + assertThat(result.criteria).isEqualTo(OPERATION_STATUS_FAILED); + result.results.forEach((ruleValidationResult)->{ + assertThat(ruleValidationResult.warnings).isEmpty(); + if (ruleValidationResult.vnfreqName.equals(CERTIFICATION_RULE)) { + assertThat(ruleValidationResult.errors) + .containsAll(expectedCertificationErrors); + } else if (ruleValidationResult.vnfreqName.equals(MANIFEST_FILE_RULE)) { + assertThat(ruleValidationResult.errors) + .containsAll(expectedManifestErrors); + } else { + assertThat(ruleValidationResult.errors).isEmpty(); + } + }); + verifyThatOperationFinishedWithoutAnyError(cli); + } + private String[] createPnfValidationRequestInfo(String csarPath) throws URISyntaxException { return new String[]{ "--product", "onap-dublin", diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-incorrect-signature-of-individual-artifact.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-incorrect-signature-of-individual-artifact.csar new file mode 100644 index 0000000..8fd6159 Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-incorrect-signature-of-individual-artifact.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-only-signature-or-cert-of-individual-artifact.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-only-signature-or-cert-of-individual-artifact.csar new file mode 100644 index 0000000..07d595c Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-only-signature-or-cert-of-individual-artifact.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-signature-of-individual-artifact.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-signature-of-individual-artifact.csar new file mode 100644 index 0000000..5938326 Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-signature-of-individual-artifact.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-wrong-path-to-signature-of-individual-artifact.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-wrong-path-to-signature-of-individual-artifact.csar new file mode 100644 index 0000000..5fc4da3 Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-cert-in-cms-valid-with-wrong-path-to-signature-of-individual-artifact.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-no-cms-with-signature-of-individual-artifact.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-no-cms-with-signature-of-individual-artifact.csar new file mode 100644 index 0000000..9d48ede Binary files /dev/null and b/csarvalidation/src/test/resources/pnf/r130206/csar-no-cms-with-signature-of-individual-artifact.csar differ diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-with-tosca-cert-pointing-non-existing-cert.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-with-tosca-cert-pointing-non-existing-cert.csar index b392fac..7b211bf 100644 Binary files a/csarvalidation/src/test/resources/pnf/r130206/csar-with-tosca-cert-pointing-non-existing-cert.csar and b/csarvalidation/src/test/resources/pnf/r130206/csar-with-tosca-cert-pointing-non-existing-cert.csar differ -- cgit 1.2.3-korg