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

com.metaeffekt.mirror.contents.base.DataSourceIndicator Maven / Gradle / Ivy

The 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.mirror.contents.base;

import com.metaeffekt.artifact.analysis.vulnerability.CommonEnumerationUtil;
import com.metaeffekt.mirror.contents.msrcdata.MsrcProduct;
import com.metaeffekt.mirror.contents.store.*;
import com.metaeffekt.mirror.contents.vulnerability.Vulnerability;
import lombok.Getter;
import org.json.JSONArray;
import org.json.JSONObject;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.springett.parsers.cpe.Cpe;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * A data structure to represent the matching source of a vulnerability or advisory.
 */
@Getter
public class DataSourceIndicator {

    private final static Logger LOG = LoggerFactory.getLogger(DataSourceIndicator.class);

    private final ContentIdentifierStore.ContentIdentifier dataSource;
    private final Reason matchReason;

    public DataSourceIndicator(ContentIdentifierStore.ContentIdentifier dataSource, Reason matchReason) {
        this.dataSource = dataSource;
        this.matchReason = matchReason;
    }

    public JSONObject toJson() {
        return new JSONObject()
                .put("source", dataSource.name())
                .put("implementation", dataSource.getImplementation())
                .put("matches", matchReason.toJson());
    }

    public static DataSourceIndicator fromJson(JSONObject json) {
        final String source = json.getString("source");
        final String implementation = json.optString("implementation", null);
        final ContentIdentifierStore.ContentIdentifier parsedSource = sourceFromSourceAndImplementation(source, implementation);

        return new DataSourceIndicator(
                parsedSource,
                Reason.fromJson(json.getJSONObject("matches"))
        );
    }

    public static List fromJson(JSONArray json) {
        final List result = new ArrayList<>();
        for (int i = 0; i < json.length(); i++) {
            final Object o = json.get(i);
            if (o instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) o;
                DataSourceIndicator fromJson = fromJson(jsonObject);
                result.add(fromJson);
            } else {
                LOG.warn("Unexpected JSON object in array on [{}#fromJson(JSONArray)]: {}", DataSourceIndicator.class, o);
            }
        }
        return result;
    }

    public static JSONArray toJson(Collection indicators) {
        if (indicators == null || indicators.isEmpty()) {
            return new JSONArray();
        }
        try {
            return new JSONArray(
                    indicators.stream()
                            .filter(Objects::nonNull)
                            .map(DataSourceIndicator::toJson)
                            .collect(Collectors.toList())
            );
        } catch (Exception e) {
            LOG.error("Failed to convert indicators to JSON: {}", indicators, e);
            return new JSONArray();
        }
    }

    @Override
    public String toString() {
        return "DataSourceIndicator[" + dataSource + " --> " + (matchReason == null ? "unspecified" : matchReason.toJson()) + "]";
    }

    public static DataSourceIndicator msrcProduct(Artifact artifact, MsrcProduct msrcProduct, String... kbIds) {
        return new DataSourceIndicator(AdvisoryTypeStore.MSRC, new MsrcProductReason(artifact, msrcProduct.getId(), kbIds));
    }

    public static DataSourceIndicator msrcProduct(Artifact artifact, MsrcProduct msrcProduct, Collection kbIds) {
        return msrcProduct(artifact, msrcProduct, kbIds.toArray(new String[0]));
    }

    public static DataSourceIndicator cpe(Artifact artifact, ContentIdentifierStore.ContentIdentifier source, Cpe matchingCpe, String configuration) {
        return new DataSourceIndicator(source, new ArtifactCpeReason(artifact, CommonEnumerationUtil.toCpe22UriOrFallbackToCpe23FS(matchingCpe), configuration));
    }

    public static DataSourceIndicator vulnerability(Vulnerability vulnerability) {
        return new DataSourceIndicator(VulnerabilityTypeStore.CVE, new VulnerabilityReason(vulnerability));
    }

    public static DataSourceIndicator ghsa(Artifact artifact, String coordinates) {
        return new DataSourceIndicator(AdvisoryTypeStore.GHSA, new ArtifactGhsaReason(artifact, coordinates));
    }

    public static DataSourceIndicator assessmentStatus(File originFile) {
        return new DataSourceIndicator(OtherTypeStore.ASSESSMENT_STATUS, new AssessmentStatusReason(originFile == null ? "no-file" : originFile.getAbsolutePath()));
    }

    public static DataSourceIndicator sourcedArtifact(Artifact artifact, String reason) {
        return new DataSourceIndicator(VulnerabilityTypeStore.CVE, new AnyArtifactOverwriteSourceReason(artifact, reason));
    }

    private static ContentIdentifierStore.ContentIdentifier sourceFromSourceAndImplementation(String source, String implementation) {
        final AdvisoryTypeIdentifier advisoryTypeIdentifier = AdvisoryTypeStore.get().fromNameAndImplementationWithoutCreation(source, implementation);
        if (advisoryTypeIdentifier != null) {
            return advisoryTypeIdentifier;
        }

        final VulnerabilityTypeIdentifier vulnerabilityTypeIdentifier = VulnerabilityTypeStore.get().fromNameAndImplementationWithoutCreation(source, implementation);
        if (vulnerabilityTypeIdentifier != null) {
            return vulnerabilityTypeIdentifier;
        }

        final OtherTypeIdentifier otherTypeIdentifier = OtherTypeStore.get().fromNameAndImplementationWithoutCreation(source, implementation);
        if (otherTypeIdentifier != null) {
            return otherTypeIdentifier;
        }

        return AdvisoryTypeStore.get().fromNameAndImplementationWithoutCreation(source, implementation);
    }

    @Getter
    public static class AssessmentStatusReason extends Reason {
        public final static String TYPE = "assessment-status";

        private final String originFile;

        public AssessmentStatusReason(String originFile) {
            super(TYPE);
            this.originFile = originFile;
        }

        public String getOriginFileName() {
            if (originFile == null || originFile.isEmpty() || originFile.equals("no-file")) {
                return "no-file";
            }
            return new File(originFile).getName();
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("originFile", originFile);
        }
    }

    @Getter
    public static class VulnerabilityReason extends Reason {
        public final static String TYPE = "vulnerability";

        private final String id;

        public VulnerabilityReason(String id) {
            super(TYPE);
            this.id = id;
        }

        public VulnerabilityReason(Vulnerability vulnerability) {
            super(TYPE);
            this.id = vulnerability.getId();
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("id", id);
        }
    }

    @Getter
    public static class ArtifactGhsaReason extends ArtifactReason {
        public final static String TYPE = "artifact-ghsa";

        private final String coordinates;

        public ArtifactGhsaReason(Artifact artifact, String coordinates) {
            super(TYPE, artifact);
            this.coordinates = coordinates;
        }

        protected ArtifactGhsaReason(JSONObject artifactData, String coordinates) {
            super(TYPE, artifactData);
            this.coordinates = coordinates;
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("coordinates", coordinates);
        }
    }

    @Getter
    public static class ArtifactCpeReason extends ArtifactReason {
        public final static String TYPE = "artifact-cpe";

        private final String cpe;
        private final String configuration;

        public ArtifactCpeReason(Artifact artifact, String cpe) {
            super(TYPE, artifact);
            this.cpe = cpe;
            this.configuration = null;
        }

        protected ArtifactCpeReason(JSONObject artifactData, String cpe) {
            super(TYPE, artifactData);
            this.cpe = cpe;
            this.configuration = null;
        }

        public ArtifactCpeReason(Artifact artifact, String cpe, String configuration) {
            super(TYPE, artifact);
            this.cpe = cpe;
            this.configuration = configuration;
        }

        protected ArtifactCpeReason(JSONObject artifactData, String cpe, String configuration) {
            super(TYPE, artifactData);
            this.cpe = cpe;
            this.configuration = configuration;
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("cpe", cpe)
                    .put("configuration", configuration);
        }
    }

    @Getter
    public static class MsrcProductReason extends ArtifactReason {
        public final static String TYPE = "msrc-product";

        private final String msrcProductId;
        private final String[] kbIds;

        public MsrcProductReason(Artifact artifact, String msrcProductId, String[] kbIds) {
            super(TYPE, artifact);
            this.msrcProductId = msrcProductId;
            this.kbIds = kbIds;
        }

        protected MsrcProductReason(JSONObject artifactData, String msrcProductId, String[] kbIds) {
            super(TYPE, artifactData);
            this.msrcProductId = msrcProductId;
            this.kbIds = kbIds;
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("msrcProductId", msrcProductId)
                    .put("kbIds", kbIds);
        }
    }

    @Getter
    public static class AnyReason extends Reason {
        public final static String TYPE = "any";

        private final String description;

        public AnyReason(String description) {
            super(TYPE);
            this.description = description;
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("description", description);
        }
    }

    @Getter
    public static class AnyArtifactOverwriteSourceReason extends ArtifactReason {
        public final static String TYPE = "any-artifact-overwrite-source";

        private final String source;

        public AnyArtifactOverwriteSourceReason(Artifact artifact, String source) {
            super(TYPE, artifact);
            this.source = source;
        }

        protected AnyArtifactOverwriteSourceReason(JSONObject artifactData) {
            super(TYPE, artifactData);
            this.source = artifactData.optString("source", null);
        }

        @Override
        public String overwriteSource() {
            return source;
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("source", source);
        }
    }

    public static class AnyArtifactReason extends ArtifactReason {
        public final static String TYPE = "any-artifact";

        public AnyArtifactReason(Artifact artifact) {
            super(TYPE, artifact);
        }

        protected AnyArtifactReason(JSONObject artifactData) {
            super(TYPE, artifactData);
        }

        @Override
        public JSONObject toJson() {
            return super.toJson();
        }
    }

    @Getter
    public abstract static class ArtifactReason extends Reason {
        protected final Artifact artifact;

        protected final String artifactId;
        protected final String artifactComponent;
        protected final String artifactVersion;

        protected ArtifactReason(String type, Artifact artifact) {
            super(type);
            if (artifact == null) {
                LOG.warn("Artifact is null in [{}#ArtifactReason(String, Artifact)], using empty artifact", DataSourceIndicator.class);
                artifact = new Artifact();
            }
            this.artifact = artifact;
            this.artifactId = artifact.getId();
            this.artifactComponent = artifact.getComponent();
            this.artifactVersion = artifact.getVersion();
        }

        protected ArtifactReason(String type, JSONObject artifactData) {
            super(type);
            this.artifact = null;
            this.artifactId = artifactData.optString("artifactId", null);
            this.artifactComponent = artifactData.optString("artifactComponent", null);
            this.artifactVersion = artifactData.optString("artifactVersion", null);
        }

        public boolean hasArtifact() {
            return artifact != null;
        }

        @Override
        public JSONObject toJson() {
            return super.toJson()
                    .put("artifactId", artifactId)
                    .put("artifactComponent", artifactComponent)
                    .put("artifactVersion", artifactVersion);
        }

        public Artifact findArtifact(Set artifacts) {
            if (artifact != null) {
                return artifact;
            }
            return artifacts.stream()
                    .filter(this::isArtifact)
                    .findFirst()
                    .orElse(null);
        }

        public boolean isArtifact(Artifact artifact) {
            if (artifact == null) {
                return false;
            }
            if (this.artifact != null) {
                return this.artifact.equals(artifact);
            }
            return Objects.equals(artifactId, artifact.getId()) &&
                    Objects.equals(artifactComponent, artifact.getComponent()) &&
                    Objects.equals(artifactVersion, artifact.getVersion());
        }
    }

    @Getter
    public abstract static class Reason {
        protected final String type;

        protected Reason(String type) {
            this.type = type;
        }

        public JSONObject toJson() {
            return new JSONObject().put("type", type);
        }

        public String overwriteSource() {
            return null;
        }

        @Override
        public String toString() {
            return toJson().toString();
        }

        public static Reason fromJson(JSONObject json) {
            if (!json.has("type")) throw new IllegalArgumentException("Missing type attribute in reason JSON: " + json);
            final String type = json.getString("type");
            switch (type) {
                case VulnerabilityReason.TYPE:
                    return new VulnerabilityReason(json.getString("id"));
                case ArtifactCpeReason.TYPE:
                    return new ArtifactCpeReason(
                            json,
                            json.optString("cpe", null),
                            json.optString("configuration", null)
                    );
                case MsrcProductReason.TYPE:
                    return new MsrcProductReason(
                            json,
                            json.optString("msrcProductId", null),
                            json.getJSONArray("kbIds").toList().stream().map(Object::toString).toArray(String[]::new)
                    );
                case ArtifactGhsaReason.TYPE:
                    return new ArtifactGhsaReason(
                            json,
                            json.optString("coordinates", null)
                    );
                case AnyReason.TYPE:
                    return new AnyReason(json.optString("description", null));
                case AnyArtifactReason.TYPE:
                    return new AnyArtifactReason(json);
                case AssessmentStatusReason.TYPE:
                    return new AssessmentStatusReason(json.optString("originFile", null));
                case AnyArtifactOverwriteSourceReason.TYPE:
                    return new AnyArtifactOverwriteSourceReason(json);
                default:
                    throw new IllegalArgumentException("Unknown reason type: " + type + "\nIn reason JSON:" + json);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy