Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2c1ce22
feat: add --issue-ids filtering to Aviator SSC/FoD apply-remediations
kireetivar Jul 2, 2026
a5a051c
chore: correct file path syntax in remediation test
kireetivar Jul 6, 2026
64d2d7f
feat: add SSC/FoD remediations FPR download and local apply flows
kireetivar Jul 13, 2026
30b8b21
chore: update output structure for download remediations FPR command
kireetivar Jul 15, 2026
3225a4c
chore: replace remediations FPR download with cache zip workflow
kireetivar Jul 17, 2026
1569055
chore: add missing descrption key
kireetivar Jul 17, 2026
5a0b2d5
chore: handle artifacts not containing remediations.xml
kireetivar Jul 17, 2026
ae5417f
chore: refactor remediations cache handling and improve logging
kireetivar Jul 17, 2026
3aa7789
chore: refactor remediations cache selectors and cache zip I/O
kireetivar Jul 20, 2026
06dd250
chore: clean up remediations cache helpers and shared selection messages
kireetivar Jul 20, 2026
3fa6c5a
chore: reuse FoD release resolver for remediations cache commands
kireetivar Jul 20, 2026
17a8729
chore: stream remediations cache into ZipFS and extract apply helpers
kireetivar Jul 21, 2026
5f0b8bf
chore: add progress messages for FoD download-remediations-cache
kireetivar Jul 21, 2026
1a6b608
chore: share remediations apply sources and harden cache write/publish
kireetivar Jul 22, 2026
bbd5676
chore: refactor source argument group for apply-remediations to use r…
kireetivar Jul 23, 2026
95870a5
chore: simplify remediations cache writer and product entry APIs
kireetivar Jul 23, 2026
c75e7fd
chore: nest remediations cache product data and simplify apply results
kireetivar Jul 24, 2026
0d81857
chore: fix file names
kireetivar Jul 24, 2026
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
@@ -0,0 +1,82 @@
/*
* 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.aviator._common.remediations_cache;

import java.nio.file.Path;
import java.util.List;

import com.fortify.cli.aviator._common.remediations_cache.RemediationsCacheReader.ResolvedFpr;
import com.fortify.cli.common.exception.FcliBugException;

/**
* Offline remediations source: ordered FPR paths from a remediations cache zip.
* Product identity (and thus artifact vs release id) comes from the validated entry model.
*/
public final class CacheRemediationsFprSource implements IRemediationsFprSource {
private final RemediationsCacheReader reader;

private CacheRemediationsFprSource(RemediationsCacheReader reader) {
this.reader = reader;
}

public static CacheRemediationsFprSource open(Path cacheZip, String expectedProduct) {
RemediationsCacheReader reader = RemediationsCacheReader.open(cacheZip);
try {
reader.requireProduct(expectedProduct);
return new CacheRemediationsFprSource(reader);
} catch (RuntimeException e) {
reader.close();
throw e;
}
}

public RemediationsCacheReader reader() {
return reader;
}

@Override
public void forEachEntry(EntryAction action) {
List<ResolvedFpr> units = reader.getOrderedResolvedFprs();
int total = units.size();
for (int i = 0; i < total; i++) {
ResolvedFpr unit = units.get(i);
RemediationsCacheEntry entry = unit.entry();
String label = entry.getPath() != null
? entry.getPath()
: unit.fprPath().getFileName().toString();
if (!action.accept(unit.fprPath(), label, productId(entry), i + 1, total)) {
break;
}
}
}

/**
* Id for progress/result lists: artifactId (SSC) or releaseId (FoD).
* Entries are validated before resolve, so exactly one product block is present.
*/
private static String productId(RemediationsCacheEntry entry) {
if (entry.getSscData() != null) {
return entry.getSscData().getArtifactId();
}
if (entry.getFodData() != null) {
return entry.getFodData().getReleaseId();
}
throw new FcliBugException(
"Remediations cache entry missing product data after validation: " + entry.getPath());
}

@Override
public void close() {
reader.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.aviator._common.remediations_cache;

import java.nio.file.Path;

/**
* Source of audited FPR files for apply-remediations (cache zip or online download).
* Implementations own any resources (zip FS, temp files) until {@link #close()}.
*
* <p>Paths passed to {@link EntryAction#accept} are valid only for the duration of that call
* (online sources may download to a temp file and delete it afterward).
*/
public interface IRemediationsFprSource extends AutoCloseable {

/**
* Invokes {@code action} for each FPR in order. Returning {@code false} from the action
* stops iteration early (for example when an issue-id filter is exhausted).
*/
void forEachEntry(EntryAction action);

@Override
void close();

@FunctionalInterface
interface EntryAction {
/**
* @param fprPath host-readable FPR path (zip entry or temp file)
* @param label human-readable label for progress/logs
* @param id artifact id, release id, or empty
* @param index 1-based index
* @param total total entries
* @return {@code false} to stop processing further entries
*/
boolean accept(Path fprPath, String label, String id, int index, int total);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* 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.aviator._common.remediations_cache;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;

import com.fortify.cli.aviator._common.exception.AviatorSimpleException;
import com.fortify.cli.aviator._common.util.AviatorRemediationMetricsHelper;
import com.fortify.cli.aviator.applyRemediation.ApplyAutoRemediationOnSource;
import com.fortify.cli.aviator.config.IAviatorLogger;
import com.fortify.cli.aviator.fpr.processor.RemediationProcessor.RemediationMetric;
import com.fortify.cli.aviator.util.FprHandle;
import com.fortify.cli.common.exception.FcliTechnicalException;

/**
* Single apply-remediations loop for any {@link IRemediationsFprSource}
* (cache zip entries or online downloads). Soft-skips on {@link AviatorSimpleException}.
*/
public final class RemediationsApplyHelper {
private RemediationsApplyHelper() {}

public record ApplyResult(
List<String> processedEntries,
List<String> processedIds,
int skipped,
List<RemediationMetric> metrics) {}

/**
* Applies remediations for each source entry until done or the issue-id filter is exhausted.
* Caller owns {@code source} lifecycle (try-with-resources).
*/
public static ApplyResult apply(
IRemediationsFprSource source,
String sourceCodeDirectory,
IAviatorLogger logger,
Set<String> issueIdFilter,
Logger skipLog) {
Accumulator acc = new Accumulator(issueIdFilter);
source.forEachEntry((fprPath, label, id, index, total) -> {
if (acc.remaining != null && acc.remaining.isEmpty()) {
return false;
}
RemediationMetric metric = applyOne(
fprPath, label, index, total, sourceCodeDirectory, logger, acc.remaining, skipLog);
if (metric == null) {
acc.skipped++;
} else {
acc.metrics.add(metric);
acc.processedEntries.add(label);
acc.processedIds.add(id != null ? id : "");
acc.remaining = AviatorRemediationMetricsHelper.getRemainingIssueIds(acc.remaining, metric);
}
return true;
});
return acc.toResult();
}

public static String actionLabel(RemediationMetric metric) {
return metric != null && metric.appliedRemediations() > 0 ? "Remediation-Applied" : "No-Remediation-Applied";
}

private static RemediationMetric applyOne(
Path fprPath,
String entryLabel,
int index,
int total,
String sourceCodeDirectory,
IAviatorLogger logger,
Set<String> issueFilter,
Logger skipLog) {
logger.progress("Processing FPR " + index + "/" + total + " (" + entryLabel + ")");
logger.progress("Status: Processing FPR with Aviator for Applying Auto Remediations");
try (FprHandle fprHandle = new FprHandle(fprPath)) {
return ApplyAutoRemediationOnSource.applyRemediations(fprHandle, sourceCodeDirectory, logger, issueFilter);
} catch (AviatorSimpleException e) {
skipLog.warn("Skipping entry {} as {}", entryLabel, e.getMessage());
return null;
} catch (IOException e) {
throw new FcliTechnicalException("Failed to close FPR handle for entry " + entryLabel, e);
}
}

/** Sequential-loop state (not concurrent — avoids Atomic* only to satisfy lambda capture rules). */
private static final class Accumulator {
private final List<RemediationMetric> metrics = new ArrayList<>();
private final List<String> processedEntries = new ArrayList<>();
private final List<String> processedIds = new ArrayList<>();
private int skipped;
private Set<String> remaining;

private Accumulator(Set<String> issueIdFilter) {
this.remaining = issueIdFilter == null ? null : new LinkedHashSet<>(issueIdFilter);
}

private ApplyResult toResult() {
return new ApplyResult(
List.copyOf(processedEntries),
List.copyOf(processedIds),
skipped,
List.copyOf(metrics));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.aviator._common.remediations_cache;

public final class RemediationsCacheConstants {
public static final int SCHEMA_VERSION = 1;
public static final String KIND = "aviator-remediations-cache";
public static final String MANIFEST_ENTRY = "manifest.json";
public static final String FPRS_DIR = "fprs";
public static final String PRODUCT_SSC = "ssc";
public static final String PRODUCT_FOD = "fod";

private RemediationsCacheConstants() {}
}
Loading
Loading