diff options
Diffstat (limited to 'csarvalidation/src/main')
13 files changed, 872 insertions, 191 deletions
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java index 6438306..f8e36d1 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java @@ -16,6 +16,7 @@ package org.onap.cvc.csar; import org.apache.commons.lang3.tuple.Pair; +import org.onap.cvc.csar.parser.SourcesParser; import java.io.IOException; import java.util.ArrayList; @@ -37,7 +38,8 @@ public class PnfCSARArchive extends CSARArchive { ); Pair<Manifest.Metadata, List<CSARError>> metadataData = pnfManifestParser.fetchMetadata(); - Pair<List<String>, List<CSARError>> sourcesSectionData = pnfManifestParser.fetchSourcesSection(); + Pair<List<SourcesParser.Source>, List<CSARError>> sourcesSectionData = pnfManifestParser.fetchSourcesSection(); + Pair<String, List<CSARError>> cmsSectionData = pnfManifestParser.fetchCMS(); Optional<Pair<Map<String, Map<String, List<String>>>, List<CSARError>>> nonManoArtifactsData = pnfManifestParser.fetchNonManoArtifacts(); PnfManifest manifest = (PnfManifest) this.getManifest(); @@ -47,6 +49,9 @@ public class PnfCSARArchive extends CSARArchive { manifest.setSources(sourcesSectionData.getKey()); this.getErrors().addAll(sourcesSectionData.getValue()); + manifest.setCms(cmsSectionData.getKey()); + this.getErrors().addAll(cmsSectionData.getValue()); + if(nonManoArtifactsData.isPresent()){ manifest.setNonMano(nonManoArtifactsData.get().getKey()); this.getErrors().addAll(nonManoArtifactsData.get().getValue()); @@ -65,14 +70,23 @@ public class PnfCSARArchive extends CSARArchive { } public static class PnfManifest extends Manifest { - private List<String> sources = new ArrayList<>(); + private List<SourcesParser.Source> sources = new ArrayList<>(); + private String cms; - public List<String> getSources() { + public List<SourcesParser.Source> getSources() { return Collections.unmodifiableList(sources); } - public void setSources(List<String> sources) { + void setSources(List<SourcesParser.Source> sources) { this.sources.addAll(sources); } + + public String getCms() { + return this.cms; + } + + public void setCms(String cms) { + this.cms = cms; + } } } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfManifestParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfManifestParser.java index 8831082..250aa4f 100644 --- a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfManifestParser.java +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfManifestParser.java @@ -15,18 +15,16 @@ */ package org.onap.cvc.csar; -import com.google.common.collect.Lists; import org.apache.commons.lang3.tuple.Pair; -import org.onap.cvc.csar.PnfCSARError.PnfCSARErrorEntryMissing; -import org.onap.cvc.csar.PnfCSARError.PnfCSARErrorInvalidEntry; -import org.onap.cvc.csar.PnfCSARError.PnfCSARErrorWarning; +import org.onap.cvc.csar.parser.CmsParser; +import org.onap.cvc.csar.parser.MetadataParser; +import org.onap.cvc.csar.parser.NonManoArtifactsParser; +import org.onap.cvc.csar.parser.SourcesParser; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -35,21 +33,20 @@ import java.util.stream.Stream; class PnfManifestParser { + private final List<String> lines; - private static final String METADATA_SECTION_TAG_SECTION = "metadata"; - private static final String SOURCE_TAG_SECTION = "source"; - private static final String NON_MANO_ARTIFACT_SETS_TAG_SECTION = "non_mano_artifact_sets"; - private static final String PRODUCT_NAME = "pnfd_name"; - private static final String PROVIDER_ID = "pnfd_provider"; - private static final String VERSION = "pnfd_archive_version"; - private static final String RELEASE_DATE_TIME = "pnfd_release_date_time"; + private final MetadataParser metadataParser; + private final SourcesParser sourcesParser; + private final NonManoArtifactsParser nonManoArtifactsParser; + private final CmsParser cmsParser; - private final List<String> lines; - private final String fileName; - PnfManifestParser(List<String> lines, String fileName) { + PnfManifestParser(List<String> lines, MetadataParser metadataParser, SourcesParser sourcesParser, NonManoArtifactsParser nonManoArtifactsParser, CmsParser cmsParser) { this.lines = lines; - this.fileName = fileName; + this.metadataParser = metadataParser; + this.sourcesParser = sourcesParser; + this.nonManoArtifactsParser = nonManoArtifactsParser; + this.cmsParser = cmsParser; } static PnfManifestParser getInstance(File pnfManifestFile) throws IOException { @@ -59,188 +56,28 @@ class PnfManifestParser { .map(String::trim) .collect(Collectors.toList()); - return new PnfManifestParser(lines, pnfManifestFile.getName()); + final String pnfManifestFileName = pnfManifestFile.getName(); + return new PnfManifestParser(lines, new MetadataParser(pnfManifestFileName), new SourcesParser(pnfManifestFileName), new NonManoArtifactsParser(), new CmsParser(pnfManifestFileName)); } } Pair<CSARArchive.Manifest.Metadata, List<CSARArchive.CSARError>> fetchMetadata() { - CSARArchive.Manifest.Metadata metadata = new CSARArchive.Manifest.Metadata(); - List<CSARArchive.CSARError> errors = new ArrayList<>(); - - boolean isMetadataSectionAvailable = false; - - for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { - String line = lines.get(lineNumber); - Pair<String, String> data = parseLine(line); - - if(data.getKey().toLowerCase().equals(METADATA_SECTION_TAG_SECTION)) { - isMetadataSectionAvailable = true; - }else if (isMetadataSectionAvailable && !isLineExcluded(line)) { - - if (shouldStopProcessing(data, errors, lineNumber)) { - break; - } - - handleMetadataLine(metadata, errors, lineNumber, data); - } - } - - if (!isMetadataSectionAvailable) { - errors.add(new PnfCSARErrorEntryMissing(METADATA_SECTION_TAG_SECTION, this.fileName, -1)); - } - - return Pair.of(metadata, errors); - - } - - Pair<List<String>, List<CSARArchive.CSARError>> fetchSourcesSection() { - List<String> sources = new ArrayList<>(); - List<CSARArchive.CSARError> errors = new ArrayList<>(); - boolean isSpecialTagReached = false; - boolean sourceSectionParsing = false; - for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { - String line = lines.get(lineNumber); - if (sourceSectionParsing && (startsWith(line, METADATA_SECTION_TAG_SECTION) || startsWith(line, NON_MANO_ARTIFACT_SETS_TAG_SECTION))) { - isSpecialTagReached = true; - }else if (!isSpecialTagReached && startsWith(line, SOURCE_TAG_SECTION)) { - sourceSectionParsing = true; - Pair<String, String> data = parseLine(line); - - String value = data.getValue(); - if (value.isEmpty()) { - errors.add(new PnfCSARErrorWarning(data.getKey(), this.fileName, lineNumber)); - break; - } else { - sources.add(value); - } - } - } - - return Pair.of(sources, errors); - } - - Optional<Pair<Map<String, Map<String, List<String>>>, List<CSARArchive.CSARError>>> fetchNonManoArtifacts() { - Map<String, Map<String, List<String>>> nonManoArtifacts = new HashMap<>(); - List<CSARArchive.CSARError> errors = new ArrayList<>(); - - boolean isNonManoArtifactsSectionAvailable = false; - String attributeName = null; - - for (String line : lines) { - - if (startsWith(line, NON_MANO_ARTIFACT_SETS_TAG_SECTION)) { - isNonManoArtifactsSectionAvailable = true; - } else if (isNonManoArtifactsSectionAvailable) { - Pair<String, String> data = parseLine(line); + return this.metadataParser.parse(this.lines); - if (isNewSection(data)) { - attributeName = data.getKey(); - nonManoArtifacts.put(attributeName, new HashMap<>()); - continue; - } - - handleNonManoArtifactLine(nonManoArtifacts, attributeName, data); - } - } - - if (!isNonManoArtifactsSectionAvailable) { - return Optional.empty(); - } - - return Optional.of(Pair.of(nonManoArtifacts, errors)); - } - - private boolean isLineExcluded(String line) { - return line.trim().isEmpty() - || startsWith(line, "#") - || startsWith(line,SOURCE_TAG_SECTION); - } - - private boolean shouldStopProcessing(Pair<String, String> data, List<CSARArchive.CSARError> errors, int lineNumber) { - if (isNewSection(data) || data.getKey().toLowerCase().equals(SOURCE_TAG_SECTION)) { - if(!isSectionSupported(data.getKey())) { - errors.add(new PnfCSARErrorWarning(data.getKey(), this.fileName, lineNumber)); - } - return true; - } - return false; - } - - private boolean startsWith(String line, String word){ - return line.trim().toLowerCase().startsWith(word); } - private void handleMetadataLine( - CSARArchive.Manifest.Metadata metadata, - List<CSARArchive.CSARError> errors, - int lineNumber, - Pair<String, String> data) { - - String paramName = data.getKey(); - String value = data.getValue(); - - switch (paramName) { - case PRODUCT_NAME: - metadata.setProductName(value); - break; - case PROVIDER_ID: - metadata.setProviderId(value); - break; - case VERSION: - metadata.setPackageVersion(value); - break; - case RELEASE_DATE_TIME: - metadata.setReleaseDateTime(value); - break; - default: - errors.add(new PnfCSARErrorInvalidEntry( - paramName, - this.fileName, - lineNumber)); - break; - } + Pair<List<SourcesParser.Source>, List<CSARArchive.CSARError>> fetchSourcesSection() { + return this.sourcesParser.parse(this.lines); } - private void handleNonManoArtifactLine( - Map<String, Map<String, List<String>>> nonManoArtifacts, - String attributeName, - Pair<String, String> data) { - - String key = data.getKey(); - String value = data.getValue(); - Map<String, List<String>> attributeWithValues = nonManoArtifacts.getOrDefault(attributeName, new HashMap<>()); - List<String> values = attributeWithValues.getOrDefault(key, new ArrayList<>()); - values.add(value); - attributeWithValues.put(key, values); - nonManoArtifacts.put(attributeName, attributeWithValues); - } - - private boolean isSectionSupported(String key) { - return Lists.newArrayList( - METADATA_SECTION_TAG_SECTION, - SOURCE_TAG_SECTION, - NON_MANO_ARTIFACT_SETS_TAG_SECTION).contains(key.toLowerCase()); + Optional<Pair<Map<String, Map<String, List<String>>>, List<CSARArchive.CSARError>>> fetchNonManoArtifacts() { + return this.nonManoArtifactsParser.parse(this.lines); } - private boolean isNewSection(Pair<String, String> data) { - String key = data.getKey().trim(); - String value = data.getValue().trim(); - return key.matches("[a-zA-z_0-9]+") && (value.isEmpty() || startsWith(value,"#")); + Pair<String, List<CSARArchive.CSARError>> fetchCMS() { + return this.cmsParser.parse(this.lines); } - - private Pair<String, String> parseLine(String line) { - String[] elements = line.split(": "); - if (elements.length == 2) - return Pair.of(elements[0], elements[1]); - - if (line.endsWith(":")) - return Pair.of(line.substring(0, line.length() - 1), ""); - else - return Pair.of(line, ""); - - - } } diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java new file mode 100644 index 0000000..2be0db8 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java @@ -0,0 +1,157 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.cc.sol004; + + +import org.onap.cli.fw.error.OnapCommandException; +import org.onap.cli.fw.schema.OnapCommandSchema; +import org.onap.cvc.csar.CSARArchive; +import org.onap.cvc.csar.FileArchive; +import org.onap.cvc.csar.PnfCSARArchive; +import org.onap.cvc.csar.cc.VTPValidateCSARBase; +import org.onap.cvc.csar.parser.SourcesParser; +import org.onap.cvc.csar.security.ShaHashCodeGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Optional; + +@OnapCommandSchema(schema = "vtp-validate-csar-r787966.yaml") +public class VTPValidateCSARR787966 extends VTPValidateCSARBase { + + private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR787966.class); + private static final String SHA_256 = "SHA-256"; + private static final String SHA_512 = "SHA-512"; + + private final ShaHashCodeGenerator shaHashCodeGenerator = new ShaHashCodeGenerator(); + + + public static class CSARErrorUnableToFindCertificate extends CSARArchive.CSARError { + CSARErrorUnableToFindCertificate() { + super("0x4001"); + this.message = "Unable to find cert file defined by Entry-Certificate!"; + } + } + + public static class CSARErrorUnableToFindCmsSection extends CSARArchive.CSARError { + CSARErrorUnableToFindCmsSection() { + super("0x4002"); + this.message = "Unable to find CMS section in manifest!"; + } + } + + public static class CSARErrorUnableToFindCsarContent extends CSARArchive.CSARError { + CSARErrorUnableToFindCsarContent() { + super("0x4003"); + this.message = "Unable to find csar content!"; + } + } + + public static class CSARErrorWrongHashCode extends CSARArchive.CSARError { + CSARErrorWrongHashCode(String path) { + super("0x4004"); + this.message = String.format("Source '%s' has wrong hash!", path); + } + } + + public static class CSARErrorUnableToFindAlgorithm extends CSARArchive.CSARError { + CSARErrorUnableToFindAlgorithm(String path) { + super("0x4005"); + this.message = String.format("Source '%s' has hash, but unable to find algorithm tag!", path); + } + } + + @Override + protected void validateCSAR(CSARArchive csar) throws OnapCommandException { + + try { + FileArchive.Workspace workspace = csar.getWorkspace(); + final Optional<Path> pathToCsarFolder = workspace.getPathToCsarFolder(); + if(pathToCsarFolder.isPresent()) { + validate(csar, pathToCsarFolder.get()); + } else { + this.errors.add(new CSARErrorUnableToFindCsarContent()); + } + } catch (Exception e) { + LOG.error("Internal VTPValidateCSARR787966 command error", e); + throw new OnapCommandException("0x3000", "Internal VTPValidateCSARR787966 command error. See logs."); + } + + } + + private void validate(CSARArchive csar, Path csarRootDirectory ) throws IOException, NoSuchAlgorithmException { + + final PnfCSARArchive.PnfManifest manifest = (PnfCSARArchive.PnfManifest) csar.getManifest(); + final CSARArchive.TOSCAMeta toscaMeta = csar.getToscaMeta(); + validateSecurityStructure(toscaMeta, csarRootDirectory, manifest); + validateSources(csarRootDirectory, manifest); + } + + private void validateSecurityStructure(CSARArchive.TOSCAMeta toscaMeta , Path csarRootDirectory, PnfCSARArchive.PnfManifest manifest) { + final File entryCertificate = csarRootDirectory.resolve(toscaMeta.getEntryCertificate()).toFile(); + if (!entryCertificate.exists() && !manifest.getCms().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindCertificate()); + } else if (entryCertificate.exists() && manifest.getCms().isEmpty()) { + this.errors.add(new CSARErrorUnableToFindCmsSection()); + } + } + + private void validateSources(Path csarRootDirectory, PnfCSARArchive.PnfManifest manifest) throws NoSuchAlgorithmException, IOException { + final List<SourcesParser.Source> sources = manifest.getSources(); + for (SourcesParser.Source source: sources){ + if(!source.getAlgorithm().isEmpty()) { + validateSourceHashCode(csarRootDirectory, source); + } else if(source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()){ + this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue())); + } + } + } + + private void validateSourceHashCode(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { + String hashCode = generateHashCode(csarRootDirectory, source); + if (!hashCode.equals(source.getHash())) { + this.errors.add(new CSARErrorWrongHashCode(source.getValue())); + } + } + + private String generateHashCode(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException { + final byte[] sourceData = Files.readAllBytes(csarRootDirectory.resolve(source.getValue())); + final String algorithm = source.getAlgorithm(); + + if(algorithm.equalsIgnoreCase(SHA_256)) { + return this.shaHashCodeGenerator.generateSha256(sourceData); + } else if(algorithm.equalsIgnoreCase(SHA_512)){ + return this.shaHashCodeGenerator.generateSha512(sourceData); + } + + throw new UnsupportedOperationException(String.format("Algorithm '%s' is not supported!", algorithm)); + } + + @Override + protected String getVnfReqsNo() { + return "R787966"; + } + + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/CmsParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/CmsParser.java new file mode 100644 index 0000000..b1bf4b4 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/CmsParser.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import org.apache.commons.lang3.tuple.Pair; +import org.onap.cvc.csar.CSARArchive; +import org.onap.cvc.csar.PnfCSARError; + +import java.util.ArrayList; +import java.util.List; + +import static org.onap.cvc.csar.parser.ManifestConsts.*; + + +public class CmsParser { + + + private final String fileName; + + public CmsParser(String fileName) { + this.fileName = fileName; + } + + public Pair<String, List<CSARArchive.CSARError>> parse(List<String> lines){ + StringBuilder buf = new StringBuilder(); + List<CSARArchive.CSARError> errors = new ArrayList<>(); + + boolean isSpecialTagReached = false; + boolean cmsSectionParsing = false; + boolean endCmsMarkerReached = false; + boolean atEndFile = true; + + + for (String line : lines) { + ManifestLine manifestLine = ManifestLine.of(line); + if (cmsSectionParsing && (manifestLine.startsWith(METADATA_SECTION_TAG_SECTION) + || manifestLine.startsWith(NON_MANO_ARTIFACT_SETS_TAG_SECTION) + || manifestLine.startsWith(SOURCE_TAG_SECTION))) { + isSpecialTagReached = true; + } else if (!isSpecialTagReached && line.contains(BEGIN_CMS_SECTION)) { + cmsSectionParsing = true; + } else if (!isSpecialTagReached && line.contains(END_CMS_SECTION)) { + if(!cmsSectionParsing){ + errors.add(new PnfCSARError.PnfCSARErrorInvalidEntry("Unable to find BEGIN CMS marker!", this.fileName, -1)); + break; + } + cmsSectionParsing = false; + endCmsMarkerReached = true; + } else if (cmsSectionParsing){ + buf.append(line); + } else if(endCmsMarkerReached) { + atEndFile = false; + } + } + + if(!atEndFile){ + errors.add(new PnfCSARError.PnfCSARErrorInvalidEntry("CMS section is not at the end of file!", this.fileName, -1)); + } + + return constructResponse(buf, errors, cmsSectionParsing, endCmsMarkerReached); + } + + private Pair<String, List<CSARArchive.CSARError>> constructResponse(StringBuilder buf, List<CSARArchive.CSARError> errors, boolean cmsSectionParsing, boolean endCmsMarkerReached) { + if(endCmsMarkerReached) { + return Pair.of(buf.toString(), errors); + } else if(cmsSectionParsing) { + errors.add(new PnfCSARError.PnfCSARErrorInvalidEntry("Unable to find END CMS marker!", this.fileName, -1)); + return Pair.of("",errors); + } else { + return Pair.of("",errors); + } + } + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestConsts.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestConsts.java new file mode 100644 index 0000000..da17317 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestConsts.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + + +final class ManifestConsts { + + private ManifestConsts(){} + + static final String METADATA_SECTION_TAG_SECTION = "metadata"; + static final String SOURCE_TAG_SECTION = "source"; + static final String ALGORITHM = "algorithm"; + static final String HASH = "hash"; + static final String NON_MANO_ARTIFACT_SETS_TAG_SECTION = "non_mano_artifact_sets"; + static final String PRODUCT_NAME = "pnfd_name"; + static final String PROVIDER_ID = "pnfd_provider"; + static final String VERSION = "pnfd_archive_version"; + static final String RELEASE_DATE_TIME = "pnfd_release_date_time"; + static final String CMS = "CMS"; + static final String BEGIN_CMS_SECTION = "BEGIN CMS"; + static final String END_CMS_SECTION = "END CMS"; + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java new file mode 100644 index 0000000..390c534 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestLine.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import org.apache.commons.lang3.tuple.Pair; + +public class ManifestLine { + + private final String line; + + private ManifestLine(String line) { + this.line = line; + } + + public static ManifestLine of(String line) { + return new ManifestLine(line); + } + + public Pair<String, String> parse() { + String[] elements = line.split(": "); + if (elements.length == 2) { + return Pair.of(elements[0], elements[1]); + } + + if (line.endsWith(":")) { + return Pair.of(line.substring(0, line.length() - 1), ""); + } else { + return Pair.of(line, ""); + } + + + } + + boolean startsWith(String word) { + return line.trim().toLowerCase().startsWith(word); + } + + boolean isEmpty() { + return line.trim().isEmpty(); + } + +} 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 new file mode 100644 index 0000000..b0c06ee --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/MetadataParser.java @@ -0,0 +1,135 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import com.google.common.collect.Lists; +import org.apache.commons.lang3.tuple.Pair; +import org.onap.cvc.csar.CSARArchive; +import org.onap.cvc.csar.PnfCSARError; + +import java.util.ArrayList; +import java.util.List; + +import static org.onap.cvc.csar.parser.ManifestConsts.*; + +public class MetadataParser { + + private final String fileName; + + public MetadataParser(String fileName) { + this.fileName = fileName; + } + + public Pair<CSARArchive.Manifest.Metadata, List<CSARArchive.CSARError>> parse(List<String> lines) { + CSARArchive.Manifest.Metadata metadata = new CSARArchive.Manifest.Metadata(); + List<CSARArchive.CSARError> errors = new ArrayList<>(); + + boolean isMetadataSectionAvailable = false; + + for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { + String line = lines.get(lineNumber); + ManifestLine manifestLine = ManifestLine.of(line); + Pair<String, String> data = manifestLine.parse(); + + if (data.getKey().equalsIgnoreCase(METADATA_SECTION_TAG_SECTION)) { + isMetadataSectionAvailable = true; + } else if (isMetadataSectionAvailable && !isLineExcluded(manifestLine)) { + + if (shouldStopProcessing(data, errors, lineNumber)) { + break; + } + + handleMetadataLine(metadata, errors, lineNumber, data); + } + } + + if (!isMetadataSectionAvailable) { + errors.add(new PnfCSARError.PnfCSARErrorEntryMissing(METADATA_SECTION_TAG_SECTION, this.fileName, -1)); + } + + return Pair.of(metadata, errors); + + } + + private boolean isLineExcluded(ManifestLine line) { + return line.isEmpty() + || line.startsWith("#") + || line.startsWith(SOURCE_TAG_SECTION); + } + + private boolean shouldStopProcessing(Pair<String, String> data, List<CSARArchive.CSARError> errors, int lineNumber) { + if (isNewSection(data) || isSourceSection(data)) { + if (!isSectionSupported(data.getKey())) { + errors.add(new PnfCSARError.PnfCSARErrorWarning(data.getKey(), this.fileName, lineNumber)); + } + return true; + } + return false; + } + + private boolean isNewSection(Pair<String, String> data) { + String key = data.getKey().trim(); + String value = data.getValue().trim(); + return key.matches("[a-zA-z_0-9]+") && (value.isEmpty() || ManifestLine.of(value).startsWith("#")); + } + + private boolean isSourceSection(Pair<String, String> data) { + return data.getKey().equalsIgnoreCase(SOURCE_TAG_SECTION) + || data.getKey().equalsIgnoreCase(ALGORITHM) + || data.getKey().equalsIgnoreCase(HASH); + } + + private boolean isSectionSupported(String key) { + return Lists.newArrayList( + METADATA_SECTION_TAG_SECTION, + SOURCE_TAG_SECTION, ALGORITHM, HASH, + NON_MANO_ARTIFACT_SETS_TAG_SECTION).contains(key.toLowerCase()); + } + + private void handleMetadataLine( + CSARArchive.Manifest.Metadata metadata, + List<CSARArchive.CSARError> errors, + int lineNumber, + Pair<String, String> data) { + + String paramName = data.getKey(); + String value = data.getValue(); + + switch (paramName) { + case PRODUCT_NAME: + metadata.setProductName(value); + break; + case PROVIDER_ID: + metadata.setProviderId(value); + break; + case VERSION: + metadata.setPackageVersion(value); + break; + case RELEASE_DATE_TIME: + metadata.setReleaseDateTime(value); + break; + default: + errors.add(new PnfCSARError.PnfCSARErrorInvalidEntry( + paramName, + this.fileName, + lineNumber)); + break; + } + } + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java new file mode 100644 index 0000000..d27ef68 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/NonManoArtifactsParser.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import org.apache.commons.lang3.tuple.Pair; +import org.onap.cvc.csar.CSARArchive; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.onap.cvc.csar.parser.ManifestConsts.NON_MANO_ARTIFACT_SETS_TAG_SECTION; + +public class NonManoArtifactsParser { + + public Optional<Pair<Map<String, Map<String, List<String>>>, List<CSARArchive.CSARError>>> parse(List<String> lines) { + Map<String, Map<String, List<String>>> nonManoArtifacts = new HashMap<>(); + List<CSARArchive.CSARError> errors = new ArrayList<>(); + + boolean isNonManoArtifactsSectionAvailable = false; + String attributeName = null; + + for (String line : lines) { + ManifestLine manifestLine = ManifestLine.of(line); + if (manifestLine.startsWith(NON_MANO_ARTIFACT_SETS_TAG_SECTION)) { + isNonManoArtifactsSectionAvailable = true; + } else if (isNonManoArtifactsSectionAvailable) { + Pair<String, String> data = manifestLine.parse(); + + if (isNewSection(data)) { + attributeName = data.getKey(); + nonManoArtifacts.put(attributeName, new HashMap<>()); + continue; + } + + handleNonManoArtifactLine(nonManoArtifacts, attributeName, data); + } + } + + if (!isNonManoArtifactsSectionAvailable) { + return Optional.empty(); + } + + return Optional.of(Pair.of(nonManoArtifacts, errors)); + } + + private boolean isNewSection(Pair<String, String> data) { + String key = data.getKey().trim(); + String value = data.getValue().trim(); + return key.matches("[a-zA-z_0-9]+") && (value.isEmpty() || ManifestLine.of(value).startsWith("#")); + } + + private void handleNonManoArtifactLine( + Map<String, Map<String, List<String>>> nonManoArtifacts, + String attributeName, + Pair<String, String> data) { + + String key = data.getKey(); + String value = data.getValue(); + + Map<String, List<String>> attributeWithValues = nonManoArtifacts.getOrDefault(attributeName, new HashMap<>()); + List<String> values = attributeWithValues.getOrDefault(key, new ArrayList<>()); + values.add(value); + attributeWithValues.put(key, values); + nonManoArtifacts.put(attributeName, attributeWithValues); + } + +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/SourcesParser.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/SourcesParser.java new file mode 100644 index 0000000..5f3f0d7 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/SourcesParser.java @@ -0,0 +1,165 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.parser; + +import org.apache.commons.lang3.tuple.Pair; +import org.onap.cvc.csar.CSARArchive; +import org.onap.cvc.csar.PnfCSARError; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static org.onap.cvc.csar.parser.ManifestConsts.*; + +public class SourcesParser { + + private final String fileName; + + public SourcesParser(String fileName) { + this.fileName = fileName; + } + + public Pair<List<Source>, List<CSARArchive.CSARError>> parse(List<String> lines) { + List<Source> sources = new ArrayList<>(); + List<CSARArchive.CSARError> errors = new ArrayList<>(); + boolean isSpecialTagReached = false; + boolean sourceSectionParsing = false; + Source source = null; + + for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) { + String line = lines.get(lineNumber); + ManifestLine manifestLine = ManifestLine.of(line); + if (sourceSectionParsing && (manifestLine.startsWith(METADATA_SECTION_TAG_SECTION) + || manifestLine.startsWith(NON_MANO_ARTIFACT_SETS_TAG_SECTION) + || line.contains(CMS))) { + isSpecialTagReached = true; + } else if (!isSpecialTagReached && manifestLine.startsWith(SOURCE_TAG_SECTION)) { + sourceSectionParsing = true; + source = handleSourceLine(sources, errors, lineNumber, manifestLine); + } else if (!isSpecialTagReached && manifestLine.startsWith(ALGORITHM)) { + handleAlgorithmLine(errors, source, lineNumber, manifestLine); + } else if (!isSpecialTagReached && manifestLine.startsWith(HASH)) { + handleHashLine(errors, source, lineNumber, manifestLine); + } + } + + return Pair.of(sources, errors); + } + + private Source handleSourceLine(List<Source> sources, List<CSARArchive.CSARError> errors, int lineNumber, ManifestLine manifestLine) { + Source source; + String value = parseSourceSectionLine(manifestLine, lineNumber, errors); + if (!value.isEmpty()) { + source = new Source(value); + sources.add(source); + } else { + source = null; + } + return source; + } + + private void handleAlgorithmLine(List<CSARArchive.CSARError> errors, Source source, int lineNumber, ManifestLine manifestLine) { + String algorithm = parseSourceSectionLine(manifestLine, lineNumber, errors); + if (source != null) + source.setAlgorithm(algorithm); + } + + private void handleHashLine(List<CSARArchive.CSARError> errors, Source source, int lineNumber, ManifestLine manifestLine) { + String hash = parseSourceSectionLine(manifestLine, lineNumber, errors); + if (source != null) + source.setHash(hash); + } + + private String parseSourceSectionLine(ManifestLine line, int lineNumber, List<CSARArchive.CSARError> errors) { + String retVal = ""; + Pair<String, String> data = line.parse(); + + String value = data.getValue(); + if (value.isEmpty()) { + errors.add(new PnfCSARError.PnfCSARErrorWarning(data.getKey(), this.fileName, lineNumber)); + } else { + retVal = value; + } + + return retVal; + } + + public static class Source { + + + private final String value; + private String algorithm; + private String hash; + + public Source(String value, String algorithm, String hash) { + + this.value = value; + this.algorithm = algorithm; + this.hash = hash; + } + + public Source(String source) { + this(source, "", ""); + } + + public String getValue() { + return value; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Source source1 = (Source) o; + return Objects.equals(value, source1.value) && + Objects.equals(algorithm, source1.algorithm) && + Objects.equals(hash, source1.hash); + } + + @Override + public int hashCode() { + return Objects.hash(value, algorithm, hash); + } + + @Override + public String toString() { + return "Source{" + + "value='" + value + '\'' + + ", algorithm='" + algorithm + '\'' + + ", hash='" + hash + '\'' + + '}'; + } + } +} diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/ShaHashCodeGenerator.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/ShaHashCodeGenerator.java new file mode 100644 index 0000000..5b6b474 --- /dev/null +++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/ShaHashCodeGenerator.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019 Nokia + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.onap.cvc.csar.security; + +import org.bouncycastle.util.encoders.Hex; + + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class ShaHashCodeGenerator { + + public String generateSha256(byte[] source) throws NoSuchAlgorithmException { + final String algorithm = "SHA-256"; + return generateHashCode(source, algorithm); + } + + public String generateSha512(byte[] source) throws NoSuchAlgorithmException { + final String algorithm = "SHA-512"; + return generateHashCode(source, algorithm); + } + + private String generateHashCode(byte[] source, String algorithm) throws NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance(algorithm); + byte[] hash = digest.digest(source); + return new String(Hex.encode(hash)); + } +} diff --git a/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand b/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand index 21c6af1..c6f2934 100644 --- a/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand +++ b/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand @@ -47,4 +47,5 @@ org.onap.cvc.csar.cc.sol004.VTPValidateCSARR293901 org.onap.cvc.csar.cc.sol004.VTPValidateCSARR146092 org.onap.cvc.csar.cc.sol004.VTPValidateCSARR57019 org.onap.cvc.csar.cc.sol004.VTPValidateCSARR787965 +org.onap.cvc.csar.cc.sol004.VTPValidateCSARR787966 diff --git a/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml new file mode 100644 index 0000000..3b039a1 --- /dev/null +++ b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml @@ -0,0 +1,60 @@ +# Copyright 2019 Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +open_cli_schema_version: 1.0 + +name: csar-validate-r787966 + +description: | + The VNF/PNF package shall contain a Digest (a.k.a. hash) for each of the components of the VNF/PNF package. The table of hashes is included in the manifest file, which is signed with the VNF provider private key. In addition, the VNF provider shall include a signing certificate that includes the VNF provider public key, following a pre-defined naming convention and located either at the root of the archive or in a predefined location (e.g. directory). + +info: + product: onap-vtp + version: 1.0 + service: validation + author: ONAP VTP Team onap-discuss@lists.onap.org + +parameters: + - name: csar + description: CSAR file path + long_option: csar + short_option: b + type: binary + is_optional: false + - name: pnf + description: CSAR file contains PNF + long_option: pnf + short_option: p + type: bool + is_optional: true + default_value: true +results: + direction: landscape + attributes: + - name: code + description: Error code + scope: short + type: string + - name: message + description: Error message + scope: short + type: string + - name: file + description: File in which error occured + scope: short + type: string + - name: line-no + description: Line no at which error occured + scope: short + type: string diff --git a/csarvalidation/src/main/resources/vnfreqs.properties b/csarvalidation/src/main/resources/vnfreqs.properties index cbb681d..b2ae957 100644 --- a/csarvalidation/src/main/resources/vnfreqs.properties +++ b/csarvalidation/src/main/resources/vnfreqs.properties @@ -1,5 +1,5 @@ vnfreqs.enabled=r02454,r04298,r07879,r09467,r13390,r23823,r26881,r27310,r35851,r40293,r43958,r66070,r77707,r77786,r87234,r10087,r21322,r26885,r40820,r35854,r65486,r17852,r46527,r15837,r54356,r67895,r95321,r32155,r01123,r51347,r787965 -pnfreqs.enabled=r10087,r87234,r35854,r15837,r17852,r293901,r146092,r57019,r787965 +pnfreqs.enabled=r10087,r87234,r35854,r15837,r17852,r293901,r146092,r57019,r787965,r787966 # ignored all chef and ansible related tests vnferrors.ignored=0x1005,0x1006,r07879-0x1000,r13390-0x1000,r27310-0x1000,r40293-0x1000,r77786-0x1000,r04298-0x1000,r07879-0x1000,r10087-0x1000,r13390-0x1000,r23823-0x1000,r26881-0x1000,r40820-0x1000,r35851-0x1000,r32155-0x1000,r54356-0x1000,r67895-0x1000,r95321-0x1000,r46527-0x1000,r02454-0x1000 pnferrors.ignored=
\ No newline at end of file |