com.metaeffekt.artifact.enrichment.other.EolEnrichment 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.artifact.enrichment.other;
import com.metaeffekt.artifact.analysis.utils.StringUtils;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.InventoryAttribute;
import com.metaeffekt.artifact.enrichment.InventoryEnricher;
import com.metaeffekt.artifact.enrichment.configurations.EolEnrichmentConfiguration;
import com.metaeffekt.mirror.contents.eol.EolCycle;
import com.metaeffekt.mirror.contents.eol.EolLifecycle;
import com.metaeffekt.mirror.contents.eol.export.CycleStateScenario;
import com.metaeffekt.mirror.contents.eol.export.ExportedCycleState;
import com.metaeffekt.mirror.download.documentation.DocRelevantMethods;
import com.metaeffekt.mirror.download.documentation.EnricherMetadata;
import com.metaeffekt.mirror.download.documentation.InventoryEnrichmentPhase;
import com.metaeffekt.mirror.query.EolIndexQuery;
import lombok.Setter;
import org.apache.commons.lang3.ObjectUtils;
import org.json.JSONArray;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import org.metaeffekt.core.inventory.processor.model.Inventory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Interpreting the data
* The 6 values we actually use from the data source can either be a Boolean or a date:
*
* - EOL (End of Life): Generally, whether something has reached the end-of-life state, i.e., both support and extended
* support have expired.
* - Support: Whether the basic support is still valid.
* - Extended Support: Something like security support or similar, is usually either not set or equals the EOL date.
* - LTS (Long Term Support): If Boolean, whether the cycle is LTS; if Date, when the cycle becomes LTS.
* - Discontinued: Whether the cycle is explicitly marked as discontinued, is often not set.
* - Technical Guidance: When the technical guidance support ends, is often not set.
*
* If the value ist a date, it always indicates when this state will be reached. Except for LTS, reaching this date is
* always something negative.
* We can differentiate the lifecycle state of a product based on whether extended support is available or not. Here are
* the two scenarios:
* Scenario 1: Extended Support is Available
*
* - Green: EOL has not been reached, both support and extended support are still valid.
* - Light Green: Support ends in less than X months (configurable, default is 6 months).
* - Yellow: Support is no longer valid, but extended support is still in effect.
* - Orange: Extended support ends in less than X months (configurable, default is 6 months).
* - Red: EOL has been reached, extended support is no longer valid.
*
* Scenario 2: Extended Support is Not Available
* In this case, phases 3 and 4 are skipped and the cycle immediately reaches the end of life.
*
* - Green: EOL has not been reached, support is still valid.
* - Orange: Support ends in less than X months (configurable, default is 6 months).
* - Red: EOL has been reached, support is no longer valid.
*
*/
@EnricherMetadata(
name = "EOL Date", phase = InventoryEnrichmentPhase.VULNERABILITY_PRIORITIZATION,
intermediateFileSuffix = "eol-date", mavenPropertyName = "eolEnrichment"
)
public class EolEnrichment extends InventoryEnricher {
private static final Logger LOG = LoggerFactory.getLogger(EolEnrichment.class);
@Setter
private EolEnrichmentConfiguration configuration = new EolEnrichmentConfiguration();
private final EolIndexQuery eolIndexQuery;
public EolEnrichment(File baseMirrorDirectory) {
this.eolIndexQuery = new EolIndexQuery(baseMirrorDirectory);
}
@Override
public EolEnrichmentConfiguration getConfiguration() {
return configuration;
}
@Override
@DocRelevantMethods({"EolEnrichment#performEnrichmentOnSingleArtifact"})
protected void performEnrichment(Inventory inventory) {
int count = 0;
final Map> matchedEolCyclesTracker = new HashMap<>();
for (Artifact artifact : inventory.getArtifacts()) {
final Map> result = performEnrichmentOnSingleArtifact(artifact);
if (result == null) continue;
matchedEolCyclesTracker.putAll(result);
count++;
}
LOG.info("Added EOL Information to [{}] artifacts for products: {}",
count,
matchedEolCyclesTracker.entrySet().stream().map(e -> e.getKey() + " (" + e.getValue().stream().map(EolCycle::getCycle).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(", ")));
}
public Map> performEnrichmentOnSingleArtifact(Artifact artifact) {
if (!artifact.has(InventoryAttribute.EOL_ID.getKey())) {
return null;
}
final String[] eolIds = artifact.get(InventoryAttribute.EOL_ID.getKey()).split(", ");
final Map> matchedEolCyclesTracker = new HashMap<>();
for (String eolId : eolIds) {
final EolLifecycle lifecycle = this.eolIndexQuery.findCyclesByProduct(eolId);
if (lifecycle == null || lifecycle.getCycles().isEmpty()) {
LOG.warn("No EOL information found for product: {}", eolId);
continue;
}
final String cycleQueryVersion = ObjectUtils.firstNonNull(
artifact.get(InventoryAttribute.EOL_OVERWRITE_CYCLE_QUERY_VERSION),
artifact.get(Artifact.Attribute.VERSION)
);
final String latestVersionQueryVersion = ObjectUtils.firstNonNull(
artifact.get(InventoryAttribute.EOL_OVERWRITE_LATEST_VERSION_QUERY_VERSION),
artifact.get(Artifact.Attribute.VERSION)
);
final EolCycle currentCycle = lifecycle.findCycleFromVersion(cycleQueryVersion);
if (currentCycle == null) {
if (cycleQueryVersion == null) {
LOG.warn("Lifecycle found for product [{}], artifact query version [{}] [{}] is null on artifact [{}]", eolId, cycleQueryVersion, latestVersionQueryVersion, artifact.get(Artifact.Attribute.ID));
} else {
LOG.warn("Lifecycle found for product [{}], artifact query version [{}] [{}] on artifact [{}] cannot be found in cycle data", eolId, cycleQueryVersion, latestVersionQueryVersion, artifact.get(Artifact.Attribute.ID));
}
continue;
}
this.performEnrichmentOnArtifactAndCycle(lifecycle, artifact, currentCycle, latestVersionQueryVersion);
matchedEolCyclesTracker.computeIfAbsent(eolId, k -> new HashSet<>()).add(currentCycle);
}
return matchedEolCyclesTracker;
}
private void performEnrichmentOnArtifactAndCycle(EolLifecycle lifecycle, Artifact artifact, EolCycle cycle, String latestVersionQueryVersion) {
final ExportedCycleState state = ExportedCycleState.from(cycle, artifact, latestVersionQueryVersion, lifecycle, configuration.getWarningThresholdMillisSupport(), configuration.getWarningThresholdMillisExtendedSupport());
if (StringUtils.isEmpty(artifact.get(InventoryAttribute.EOL_FULL_STATE.getKey()))) {
artifact.set(InventoryAttribute.EOL_FULL_STATE.getKey(), new JSONArray().put(state.toJson()).toString());
} else {
final JSONArray existing = new JSONArray(artifact.get(InventoryAttribute.EOL_FULL_STATE.getKey()));
existing.put(state.toJson());
artifact.set(InventoryAttribute.EOL_FULL_STATE.getKey(), existing.toString());
}
artifact.set(InventoryAttribute.EOL_RECOMMENDED_CYCLE_VERSION.getKey(), state.getLatestCycleVersion());
artifact.set(InventoryAttribute.EOL_RECOMMENDED_LIFECYCLE_VERSION.getKey(), state.getLatestLifecycleVersion());
artifact.set(InventoryAttribute.EOL_RECOMMENDED_NEXT_SUPPORTED_VERSION.getKey(), state.getNextSupportedVersion());
artifact.set(InventoryAttribute.EOL_RECOMMENDED_NEXT_SUPPORTED_EXTENDED_VERSION.getKey(), state.getNextSupportedExtendedVersion());
artifact.set(InventoryAttribute.EOL_RECOMMENDED_CLOSEST_SUPPORTED_LTS_VERSION.getKey(), state.getClosestActiveLtsVersion());
artifact.set(InventoryAttribute.EOL_RECOMMENDED_LATEST_SUPPORTED_LTS_VERSION.getKey(), state.getLatestActiveLtsVersion());
artifact.set(InventoryAttribute.EOL_IS_EOL.getKey(), String.valueOf(state.isEol()));
artifact.set(InventoryAttribute.EOL_IS_SUPPORTED.getKey(), String.valueOf(state.isSupport()));
artifact.set(InventoryAttribute.EOL_IS_SUPPORTED_EXTENDED.getKey(), String.valueOf(state.isExtendedSupport()));
artifact.set(InventoryAttribute.EOL_RATING.getKey(),
String.valueOf(state.getCycleStateScenario() == CycleStateScenario.EXTENDED_SUPPORT_NOT_PRESENT
? state.getCycleStateExtendedSupportUnavailable().getRating().getRating()
: state.getCycleStateExtendedSupportAvailable().getRating().getRating()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy