All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metaeffekt.artifact.analysis.spdxbom.mapper.PackageFillers Maven / Gradle / Ivy

There is a newer version: 0.132.0
Show newest version
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * 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.
 */
package com.metaeffekt.artifact.analysis.spdxbom.mapper;

import com.metaeffekt.artifact.analysis.metascan.Constants;
import com.metaeffekt.artifact.analysis.spdxbom.context.SpdxDocumentContext;
import com.metaeffekt.artifact.analysis.spdxbom.holder.FillPackageHolder;
import com.metaeffekt.artifact.analysis.spdxbom.mapper.exception.PurlGenerationFailedException;
import org.apache.commons.lang3.StringUtils;
import org.metaeffekt.common.notice.model.ComponentDefinition;
import org.metaeffekt.common.notice.model.NoticeParameters;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spdx.library.InvalidSPDXAnalysisException;
import org.spdx.library.SpdxConstants;
import org.spdx.library.model.Checksum;
import org.spdx.library.model.ExternalRef;
import org.spdx.library.model.ReferenceType;
import org.spdx.library.model.SpdxDocument;
import org.spdx.library.model.enumerations.AnnotationType;
import org.spdx.library.model.enumerations.ChecksumAlgorithm;
import org.spdx.library.model.enumerations.ReferenceCategory;

import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.BiFunction;

import static com.metaeffekt.artifact.analysis.metascan.Constants.KEY_BINARY_ARTIFACT_HASH_SHA256;
import static com.metaeffekt.artifact.analysis.spdxbom.mapper.MappingUtils.getOverflowAnnotation;
import static org.metaeffekt.core.inventory.processor.model.Constants.KEY_HASH_SHA1;
import static org.metaeffekt.core.inventory.processor.model.Constants.KEY_HASH_SHA256;

/**
 * Contains static filler helpers for some of the default package fields.
* This helps avoid code duplication in the individual mapper classes. */ public class PackageFillers { private static final Logger LOG = LoggerFactory.getLogger(PackageFillers.class); public static void fillComment(FillPackageHolder fillHolder) { fillHolder.getPackageBuilder().setComment(fillHolder.getArtifact().getComment()); fillHolder.getAttributesWritten().add(Artifact.Attribute.COMMENT.getKey()); } public static void fillHomepageFromUrl(FillPackageHolder fillHolder) { final String url = fillHolder.getArtifact().getUrl(); if (StringUtils.isNotEmpty(url)) { try { // FIXME: beautify new URL(url); fillHolder.getPackageBuilder().setHomepage(url); fillHolder.getAttributesWritten().add(Artifact.Attribute.URL.getKey()); } catch (MalformedURLException e) { LOG.warn("URL value is not a valid URL: [{}]", url); } } } public static void fillVersionInfo(FillPackageHolder fillHolder) { if (fillHolder.getArtifact().getVersion() == null) { return; } fillHolder.getPackageBuilder().setVersionInfo(fillHolder.getArtifact().getVersion()); fillHolder.getAttributesWritten().add(Artifact.Attribute.VERSION.getKey()); } public static void fillChecksums(FillPackageHolder fillHolder, SpdxDocument document) throws InvalidSPDXAnalysisException { final Artifact artifact = fillHolder.getArtifact(); List checksums = new ArrayList<>(); // FIXME: consolidate names String checksum = artifact.getChecksum(); if (StringUtils.isBlank(checksum)) { checksum = artifact.get("Checksum (MD5)"); } if (StringUtils.isNotBlank(checksum)) { checksums.add(document.createChecksum(ChecksumAlgorithm.MD5, checksum)); fillHolder.getAttributesWritten().add(Artifact.Attribute.CHECKSUM.getKey()); fillHolder.getAttributesWritten().add("Checksum (MD5)"); } // FIXME: consolidate names String sha1 = artifact.get("Checksum (SHA-1)"); if (StringUtils.isBlank(sha1)) { sha1 = artifact.get(KEY_HASH_SHA1); } if (StringUtils.isNotBlank(sha1)) { checksums.add(document.createChecksum(ChecksumAlgorithm.SHA1, sha1)); fillHolder.getAttributesWritten().add("Checksum (SHA-1)"); fillHolder.getAttributesWritten().add(KEY_HASH_SHA1); } // FIXME: consolidate names String sha256 = artifact.get("Checksum (SHA-256)"); if (StringUtils.isBlank(sha256)) { sha256 = artifact.get("Hash (SHA-256)"); } if (StringUtils.isBlank(sha256)) { // the observed checksum is not available sha256 = artifact.get(KEY_BINARY_ARTIFACT_HASH_SHA256); } if (StringUtils.isNotBlank(sha256)) { checksums.add(document.createChecksum(ChecksumAlgorithm.SHA256, sha256)); fillHolder.getAttributesWritten().add("Checksum (SHA-256)"); fillHolder.getAttributesWritten().add(KEY_HASH_SHA256); fillHolder.getAttributesWritten().add(KEY_BINARY_ARTIFACT_HASH_SHA256); } if (!checksums.isEmpty()) { fillHolder.getPackageBuilder().setChecksums(checksums); } } public static void fillRequired(FillPackageHolder fillHolder) { // set filesAnalyzed to false. at the time of writing, spdx file output would require additional parsing. // TODO: consider outputting file-by-file outputs? fillHolder.getPackageBuilder().setFilesAnalyzed(false); fillHolder.getPackageBuilder().setDownloadLocation(SpdxConstants.NOASSERTION_VALUE); } public static void fillAuthors(FillPackageHolder fillHolder) { String authorsKey = "Extracted Authors (ScanCode-1)"; String authors = fillHolder.getArtifact().get(authorsKey); if (StringUtils.isNotBlank(authors)) { fillHolder.getPackageBuilder().setAttributionText(Arrays.asList(authors.split("\\|\n"))); } fillHolder.getAttributesWritten().add(authorsKey); // FIXME: fileContributors cannot be set via api (or was not found); use instead of setAttributionText(...) // what was meant by this? perhaps per-file outputs were confused with package fields? } public static void fillOverflowKeyValueAnnotation(FillPackageHolder fillHolder, SpdxDocumentContext documentContext, Collection approved) throws InvalidSPDXAnalysisException { Map validOverflowContent = new HashMap<>(); // iterate keys that were approved for inclusion by key-value map for (String key : approved) { if (fillHolder.getAttributesWritten().contains(key)) { // we already wrote this (possibly properly by mapper). do not output again continue; } // get the value for this particular attribute String value = fillHolder.getArtifact().get(key); // only add the attribute if the value is not empty if (StringUtils.isNotBlank(value)) { validOverflowContent.put(key, value); } // mark this attribute as "written" fillHolder.getAttributesWritten().add(key); } if (!validOverflowContent.isEmpty()) { fillHolder.getPackageBuilder().addAnnotation(getOverflowAnnotation(documentContext, validOverflowContent)); } } /** * Fills an external ref with a purl generated by purlGetter. * @param fillHolder helper object with required objects for fillers * @param document the document that this package is created from and added to * @param purlGetter a method parameter for generating the purl string from the artifact * @param referenceCategory a reference category for the "external reference" as defined by spdx * * @throws InvalidSPDXAnalysisException Throws InvalidSPDXAnalysisException. */ public static void fillExternalRefWithPurl(FillPackageHolder fillHolder, SpdxDocument document, BiFunction, String> purlGetter, ReferenceCategory referenceCategory) throws InvalidSPDXAnalysisException { final Artifact artifact = fillHolder.getArtifact(); try { // prepare purl to hold artifact information final String purlString = purlGetter.apply(artifact, fillHolder.getAttributesWritten()); final ReferenceType referenceType = new ReferenceType( new ReferenceType(SpdxConstants.SPDX_LISTED_REFERENCE_TYPES_PREFIX + "purl") ); final ExternalRef externalRef = document.createExternalRef(referenceCategory, referenceType, purlString, null); fillHolder.getPackageBuilder().addExternalRef(externalRef); } catch (PurlGenerationFailedException e) { LOG.warn("Purl generation failed for artifact [{}]. Leaving external reference empty.", artifact); } } /** * This method gets what we call "notes" from notice parameters and sets them.
* We may want to merge this with {@link PackageFillers#fillAuthors(FillPackageHolder)}, since the two are similar in nature. * Spdx also doesn't fully specify what's supposed to be in the attribution text field. * I think the notes might be a better fit than whatever authors field we had so far, but both qualify as correct. *
* Until we discuss changes, I'll be filling this information into an annotation so it's at least present SOMEWHERE. * @param fillHolder helper object with commonly required data for filling * @param context context required to build annotations using the document * @param noticeParam The {@link NoticeParameters} instance to use if available. * * @throws InvalidSPDXAnalysisException Thrown in case SPDX library subroutines fail. */ public static void fillNotesAnnotation(FillPackageHolder fillHolder, SpdxDocumentContext context, NoticeParameters noticeParam) throws InvalidSPDXAnalysisException { if (noticeParam == null) { // no need to get data that isn't there. return; } String notesAnnotationText = buildNotesAnnotation(noticeParam); if (StringUtils.isNotBlank(notesAnnotationText)) { fillHolder.getPackageBuilder().addAnnotation( context.getSpdxDocument().createAnnotation( "Tool: " + context.getSpdxDocumentSpec().getTool(), // storing additional data, so type is OTHER AnnotationType.OTHER, new SimpleDateFormat(SpdxConstants.SPDX_DATE_FORMAT).format(new Date()), notesAnnotationText ) ); } } private static String buildNotesAnnotation(NoticeParameters noticeParam) { StringJoiner annotationTextsJoiner = new StringJoiner("\n\n"); if (noticeParam.getComponent() != null && noticeParam.getComponent().getNote() != null ) { annotationTextsJoiner.add( "The component \"" + noticeParam.getComponent().getName() + "\" has notes:\n" + noticeParam.getComponent().getNote() ); } if (noticeParam.getSubcomponents() != null) { for (ComponentDefinition subcomp : noticeParam.getSubcomponents()) { if (subcomp.getNote() != null) { annotationTextsJoiner.add( "The subcomponent \"" + subcomp.getName() + "\" has notes:\n" + subcomp.getNote() ); } } } return annotationTextsJoiner.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy