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

com.metaeffekt.mirror.contents.msrcdata.MsrcSupersedeNode 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.msrcdata;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.json.JSONObject;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MsrcSupersedeNode {
    private final String kbId;
    private final String articleUrl;
    private final String downloadUrl;
    private final Map productRelations = new HashMap<>();

    public MsrcSupersedeNode(String kbId) {
        this(kbId, null, null);
    }

    public MsrcSupersedeNode(String kbId, String articleUrl, String downloadUrl) {
        this.kbId = kbId;
        this.articleUrl = articleUrl;
        this.downloadUrl = downloadUrl;
    }

    public void addSupersededBy(String product, MsrcSupersedeNode node) {
        productRelations.computeIfAbsent(product, k -> new MsrcSupersedeNodeRelations()).addSupersededBy(node);
    }

    public void addSupersededBy(String product, Collection supersededBy) {
        productRelations.computeIfAbsent(product, k -> new MsrcSupersedeNodeRelations()).addSupersededBy(supersededBy);
    }

    public void addSupersedes(String product, MsrcSupersedeNode node) {
        productRelations.computeIfAbsent(product, k -> new MsrcSupersedeNodeRelations()).addSupersedes(node);
    }

    public void addSupersedes(String product, Collection superseded) {
        productRelations.computeIfAbsent(product, k -> new MsrcSupersedeNodeRelations()).addSupersedes(superseded);
    }

    public void addAffectsVulnerability(String product, String vulnerabilityId) {
        productRelations.computeIfAbsent(product, k -> new MsrcSupersedeNodeRelations()).addAffectsVulnerability(vulnerabilityId);
    }

    public void addAffectsVulnerability(String product, Collection vulnerabilityIds) {
        productRelations.computeIfAbsent(product, k -> new MsrcSupersedeNodeRelations()).addAffectsVulnerability(vulnerabilityIds);
    }

    public String getKbId() {
        return kbId;
    }

    public String getArticleUrl() {
        return articleUrl;
    }

    public String getDownloadUrl() {
        return downloadUrl;
    }

    public Map getProductRelations() {
        return productRelations;
    }

    public Set getProductNames() {
        return productRelations.keySet();
    }

    public MsrcSupersedeNodeRelations getProductRelationsByProduct(String product) {
        return productRelations.get(product);
    }

    public boolean containsVulnerability(String vulnerability) {
        return productRelations.values().stream().anyMatch(p -> p.getAffectsVulnerabilities().contains(vulnerability));
    }

    public boolean containsVulnerability(String vulnerability, String product) {
        return productRelations.containsKey(product) && productRelations.get(product).getAffectsVulnerabilities().contains(vulnerability);
    }

    public boolean containsSupersededKbId(String kbId) {
        return productRelations.values().stream().anyMatch(p -> p.getSupersedes().stream().anyMatch(s -> s.getKbId().equals(kbId)));
    }

    public boolean containsSupersededKbId(String kbId, String product) {
        return productRelations.containsKey(product) && productRelations.get(product).getSupersedes().stream().anyMatch(s -> s.getKbId().equals(kbId));
    }

    public boolean containsSupersededByKbId(String kbId) {
        return productRelations.values().stream().anyMatch(p -> p.getSupersededBy().stream().anyMatch(s -> s.getKbId().equals(kbId)));
    }

    public boolean containsSupersededByKbId(String kbId, String product) {
        return productRelations.containsKey(product) && productRelations.get(product).getSupersededBy().stream().anyMatch(s -> s.getKbId().equals(kbId));
    }

    @Override
    public String toString() {
        return kbId;
    }

    public JSONObject toJson() {
        return new JSONObject()
                .put("kbId", kbId)
                .put("articleUrl", articleUrl)
                .put("downloadUrl", downloadUrl)
                .put("rel", productRelations.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toJson())));
    }

    public Document toDocument() {
        final Document doc = new Document();

        doc.add(new TextField("kbId", kbId, Field.Store.YES));
        if (articleUrl != null) doc.add(new TextField("articleUrl", articleUrl, Field.Store.YES));
        if (downloadUrl != null) doc.add(new TextField("downloadUrl", downloadUrl, Field.Store.YES));

        final JSONObject relationJson = new JSONObject(productRelations.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toJson())));
        doc.add(new TextField("rel", relationJson.toString(), Field.Store.YES));

        return doc;
    }

    public static MsrcSupersedeNode fromJson(JSONObject json, Map nodes) {
        final MsrcSupersedeNode node = nodes.computeIfAbsent(json.getString("kbId"), kb -> {
            final String articleUrl = json.optString("articleUrl", null);
            final String downloadUrl = json.optString("downloadUrl", null);
            return new MsrcSupersedeNode(kb, articleUrl, downloadUrl);
        });

        final JSONObject productRelationsJson = json.getJSONObject("rel");
        for (String product : productRelationsJson.keySet()) {
            final MsrcSupersedeNodeRelations parsed = MsrcSupersedeNodeRelations.fromJson(productRelationsJson.getJSONObject(product), nodes);
            node.productRelations.put(product, parsed);
        }

        return node;
    }

    public static MsrcSupersedeNode fromDocument(Document document, Map nodes) {
        final MsrcSupersedeNode node;

        synchronized (nodes) { // see MsrcKbChainIndexQuery#findNodeByKbId
            node = nodes.computeIfAbsent(document.get("kbId"), kb -> {
                final String articleUrl = document.get("articleUrl");
                final String downloadUrl = document.get("downloadUrl");
                return new MsrcSupersedeNode(kb, articleUrl, downloadUrl);
            });
        }

        final JSONObject productRelationsJson = new JSONObject(document.get("rel"));
        for (String product : productRelationsJson.keySet()) {
            final MsrcSupersedeNodeRelations parsed = MsrcSupersedeNodeRelations.fromJson(productRelationsJson.getJSONObject(product), nodes);
            node.productRelations.put(product, parsed);
        }

        return node;
    }

    public static Map mergeNodes(Collection> nodeArrays) {
        final Map mergedNodes = new HashMap<>();

        for (Collection nodes : nodeArrays) {
            nodes.forEach(node -> mergedNodes.computeIfAbsent(node.getKbId(), cloneNodeWithBaseProperties(node)));
        }

        for (Collection nodes : nodeArrays) {
            MsrcSupersedeNode.mergeNodesDeepCopyAllNodes(mergedNodes, nodes);
        }

        return mergedNodes;
    }

    private static Function cloneNodeWithBaseProperties(MsrcSupersedeNode node) {
        return kb -> {
            final String articleUrl = node.getArticleUrl();
            final String downloadUrl = node.getDownloadUrl();
            return new MsrcSupersedeNode(kb, articleUrl, downloadUrl);
        };
    }

    private static void mergeNodesDeepCopyAllNodes(Map mergedNodes, Collection nodes) {
        for (MsrcSupersedeNode referenceNode : nodes) {
            final MsrcSupersedeNode node = mergedNodes.get(referenceNode.getKbId());

            for (String productName : referenceNode.getProductNames()) {
                final MsrcSupersedeNodeRelations relation = referenceNode.getProductRelationsByProduct(productName);

                for (String vulnerability : relation.getAffectsVulnerabilities()) {
                    node.addAffectsVulnerability(productName, vulnerability);
                }

                for (MsrcSupersedeNode supersededByNode : relation.getSupersededBy()) {
                    final MsrcSupersedeNode otherNode = mergedNodes.computeIfAbsent(supersededByNode.getKbId(), cloneNodeWithBaseProperties(supersededByNode));
                    node.addSupersededBy(productName, otherNode);
                    otherNode.addSupersedes(productName, node);
                }

                for (MsrcSupersedeNode supersededNode : relation.getSupersedes()) {
                    final MsrcSupersedeNode otherNode = mergedNodes.computeIfAbsent(supersededNode.getKbId(), cloneNodeWithBaseProperties(supersededNode));
                    node.addSupersedes(productName, otherNode);
                    otherNode.addSupersededBy(productName, node);
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy