Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -510,14 +510,31 @@
"name":"com.fortify.cli.aviator.config.TagMappingConfig",
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setMapping","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Mapping"] }, {"name":"setTag_id","parameterTypes":["java.lang.String"] }]
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setMapping","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Mapping"] }, {"name":"setSuppression_exclusions","parameterTypes":["java.util.List"] }, {"name":"setTag_id","parameterTypes":["java.lang.String"] }]
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$Mapping",
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setTier_1","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Tier"] }, {"name":"setTier_2","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Tier"] }]
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusion",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allPublicFields": true,
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setCategories","parameterTypes":["java.util.List"] }]
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusionBeanInfo"
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusionCustomizer"
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$MappingBeanInfo"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,31 @@
"name":"com.fortify.cli.aviator.config.TagMappingConfig",
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setMapping","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Mapping"] }, {"name":"setTag_id","parameterTypes":["java.lang.String"] }]
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setMapping","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Mapping"] }, {"name":"setSuppression_exclusions","parameterTypes":["java.util.List"] }, {"name":"setTag_id","parameterTypes":["java.lang.String"] }]
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$Mapping",
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setTier_1","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Tier"] }, {"name":"setTier_2","parameterTypes":["com.fortify.cli.aviator.config.TagMappingConfig$Tier"] }]
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusion",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true,
"allPublicFields": true,
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setCategories","parameterTypes":["java.util.List"] }]
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusionBeanInfo"
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusionCustomizer"
},
{
"name":"com.fortify.cli.aviator.config.TagMappingConfig$MappingBeanInfo"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2021-2026 Open Text.
*
* The only warranties for products and services of Open Text
* and its affiliates and licensors ("Open Text") are as may
* be set forth in the express warranty statements accompanying
* such products and services. Nothing herein should be construed
* as constituting an additional warranty. Open Text shall not be
* liable for technical or editorial errors or omissions contained
* herein. The information contained herein is subject to change
* without notice.
*/
package com.fortify.cli;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.stream.StreamSupport;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

class NativeYamlReflectConfigTest {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final String TAG_MAPPING_CONFIG_CLASS = "com.fortify.cli.aviator.config.TagMappingConfig";
private static final List<String> TAG_MAPPING_NESTED_CLASSES = List.of(
"com.fortify.cli.aviator.config.TagMappingConfig$SuppressionExclusion",
"com.fortify.cli.aviator.config.TagMappingConfig$Mapping",
"com.fortify.cli.aviator.config.TagMappingConfig$Tier",
"com.fortify.cli.aviator.config.TagMappingConfig$Result");

@ParameterizedTest
@ValueSource(strings = {
"META-INF/native-image/fcli/fcli-app/yaml/reflect-config.json",
"META-INF/native-image/fcli/fcli-app/grpc/reflect-config.json"
})
void testTagMappingConfigNativeReflectConfigIncludesSuppressionExclusions(String resourcePath) throws Exception {
JsonNode reflectConfig = loadReflectConfig(resourcePath);
JsonNode tagMappingConfigEntry = getReflectConfigEntry(reflectConfig, TAG_MAPPING_CONFIG_CLASS);

assertTrue(tagMappingConfigEntry.path("allDeclaredFields").asBoolean(),
() -> "Expected allDeclaredFields for " + TAG_MAPPING_CONFIG_CLASS + " in " + resourcePath);
assertTrue(hasMethod(tagMappingConfigEntry, "setSuppression_exclusions"),
() -> "Expected setSuppression_exclusions metadata for " + TAG_MAPPING_CONFIG_CLASS + " in " + resourcePath);

TAG_MAPPING_NESTED_CLASSES.forEach(className -> assertTrue(hasReflectConfigEntry(reflectConfig, className),
() -> "Expected reflect-config entry for " + className + " in " + resourcePath));
}

private JsonNode loadReflectConfig(String resourcePath) throws IOException {
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath)) {
assertNotNull(inputStream, () -> "Missing native reflect-config resource: " + resourcePath);
return OBJECT_MAPPER.readTree(inputStream);
}
}

private JsonNode getReflectConfigEntry(JsonNode reflectConfig, String className) {
return StreamSupport.stream(reflectConfig.spliterator(), false)
.filter(node -> className.equals(node.path("name").asText()))
.findFirst()
.orElseThrow(() -> new AssertionError("Missing reflect-config entry for " + className));
}

private boolean hasReflectConfigEntry(JsonNode reflectConfig, String className) {
return StreamSupport.stream(reflectConfig.spliterator(), false)
.anyMatch(node -> className.equals(node.path("name").asText()));
}

private boolean hasMethod(JsonNode reflectConfigEntry, String methodName) {
return StreamSupport.stream(reflectConfigEntry.path("methods").spliterator(), false)
.anyMatch(node -> methodName.equals(node.path("name").asText()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.fortify.cli.aviator.audit;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -54,6 +55,9 @@ public static FPRAuditResult auditFPR(AuditFprOptions options)
// --- STAGE 1: PARSING ---
ParsedFprData parsedData = prepareAndParseFpr(options.getFprHandle());
TagMappingConfig tagMappingConfig = loadTagMappingConfig(options.getTagMappingPath());
Map<String, String> issueCategoryLookup = tagMappingConfig.requiresCategoryForSuppressionEvaluation()
? buildIssueCategoryLookup(parsedData.vulnerabilities)
: Map.of();

// --- STAGE 2: FILTER SELECTION (DELEGATED) ---
FilterSelection filterSelection = FilterSetSelector.select(
Expand All @@ -71,7 +75,7 @@ public static FPRAuditResult auditFPR(AuditFprOptions options)
// --- STAGE 4: FINALIZATION ---
return finalizeFprAudit(
auditOutcome, auditResponses, parsedData.auditProcessor,
tagMappingConfig, parsedData.fprInfo
tagMappingConfig, issueCategoryLookup, parsedData.fprInfo
);
}

Expand All @@ -95,13 +99,28 @@ private static ParsedFprData prepareAndParseFpr(FprHandle fprHandle) {
}

private static TagMappingConfig loadTagMappingConfig(String tagMappingFilePath) {
TagMappingConfig tagMappingConfig;
if (tagMappingFilePath != null && !tagMappingFilePath.trim().isEmpty()) {
LOG.info("Loading user-provided tag mapping from: {}", tagMappingFilePath);
return ResourceUtil.loadYamlFile(new File(tagMappingFilePath), TagMappingConfig.class);
tagMappingConfig = ResourceUtil.loadYamlFile(new File(tagMappingFilePath), TagMappingConfig.class);
} else {
LOG.info("Using default tag mapping configuration.");
return AviatorConfigManager.getInstance().getDefaultTagMappingConfig();
tagMappingConfig = AviatorConfigManager.getInstance().getDefaultTagMappingConfig();
}

tagMappingConfig.validate();
return tagMappingConfig;
}

private static Map<String, String> buildIssueCategoryLookup(List<Vulnerability> vulnerabilities) {
Map<String, String> issueCategoryLookup = new HashMap<>();
for (Vulnerability vulnerability : vulnerabilities) {
String instanceId = vulnerability.getInstanceID();
if (instanceId != null && !instanceId.isBlank()) {
issueCategoryLookup.putIfAbsent(instanceId, vulnerability.getCategory());
}
}
return issueCategoryLookup;
}

private static AuditOutcome performAviatorAudit(
Expand All @@ -128,7 +147,7 @@ private static AuditOutcome performAviatorAudit(
private static FPRAuditResult finalizeFprAudit(
AuditOutcome auditOutcome, Map<String, AuditResponse> auditResponses,
AuditProcessor auditProcessor, TagMappingConfig tagMappingConfig,
FPRInfo fprInfo) {
Map<String, String> issueCategoryLookup, FPRInfo fprInfo) {

int totalIssuesToAudit = auditOutcome.getTotalIssuesToAudit();
if (auditResponses.isEmpty()) {
Expand Down Expand Up @@ -168,7 +187,8 @@ private static FPRAuditResult finalizeFprAudit(

File updatedFile = null;
if (issuesSuccessfullyAudited > 0) {
updatedFile = auditProcessor.updateAndSaveAuditAndRemediationsXml(auditResponses, tagMappingConfig, fprInfo);
updatedFile = auditProcessor.updateAndSaveAuditAndRemediationsXml(
auditResponses, tagMappingConfig, issueCategoryLookup, fprInfo);
}

LOG.info("FPR audit process completed with status: {}", status);
Expand Down
Loading
Loading