| 1 | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | // Copyright 2026 Egothor | |
| 3 | // Copyright 2026 Accenture | |
| 4 | package org.egothor.methodatlas.attest; | |
| 5 | ||
| 6 | import java.io.IOException; | |
| 7 | import java.nio.file.Path; | |
| 8 | import java.util.List; | |
| 9 | import java.util.stream.Collectors; | |
| 10 | ||
| 11 | import org.egothor.methodatlas.emit.TestMethodSink; | |
| 12 | ||
| 13 | /** | |
| 14 | * Single public entry point used by {@code MethodAtlasApp} to drive | |
| 15 | * {@code -attest}. | |
| 16 | * | |
| 17 | * <p> | |
| 18 | * The package's data records, collector, and writer are all package-private so | |
| 19 | * they remain implementation details. The facade exposes only the operations | |
| 20 | * the CLI needs: | |
| 21 | * </p> | |
| 22 | * <ol> | |
| 23 | * <li>prepare a collector (returning a handle that must be threaded through | |
| 24 | * the orchestrator as an extra sink);</li> | |
| 25 | * <li>after the scan, ask the same handle to write the attestation | |
| 26 | * manifest.</li> | |
| 27 | * </ol> | |
| 28 | * | |
| 29 | * <p> | |
| 30 | * The manifest is a compact JSON file listing every AI-classified | |
| 31 | * security-relevant test method, the commit SHA (resolved from CI environment | |
| 32 | * variables), and optional content hashes. It is designed to be signed | |
| 33 | * externally with {@code cosign sign-blob} or GPG and attached to a release as | |
| 34 | * a tamper-evident record of which security tests existed at that point. | |
| 35 | * </p> | |
| 36 | */ | |
| 37 | public final class AttestFacade { | |
| 38 | ||
| 39 | /** | |
| 40 | * Default filename used when {@code -attest-file} is not supplied. | |
| 41 | */ | |
| 42 | public static final String DEFAULT_ATTEST_FILENAME = "security-tests.attestation.json"; | |
| 43 | ||
| 44 | private AttestFacade() { | |
| 45 | // Utility class. | |
| 46 | } | |
| 47 | ||
| 48 | /** | |
| 49 | * Prepares an attestation collector. | |
| 50 | * | |
| 51 | * @param minConfidence AI minimum-confidence threshold; AI-only evidence below | |
| 52 | * this value is excluded from the manifest | |
| 53 | * @param scanRoots scan root paths written to the manifest header | |
| 54 | * @return handle ready for use by the orchestrator and writer | |
| 55 | */ | |
| 56 | public static Handle prepare(double minConfidence, List<Path> scanRoots) { | |
| 57 | List<String> rootStrings = scanRoots.stream() | |
| 58 | .map(Path::toString) | |
| 59 | .collect(Collectors.toUnmodifiableList()); | |
| 60 | AttestCollector collector = new AttestCollector(minConfidence, rootStrings); | |
| 61 |
1
1. prepare : replaced return value with null for org/egothor/methodatlas/attest/AttestFacade::prepare → NO_COVERAGE |
return new Handle(collector); |
| 62 | } | |
| 63 | ||
| 64 | /** | |
| 65 | * Opaque handle returned by {@link #prepare(double, List)}. Carries the | |
| 66 | * collector through the scan and yields the manifest when the scan is done. | |
| 67 | */ | |
| 68 | public static final class Handle { | |
| 69 | ||
| 70 | /** Backing collector — owned by this handle, never exposed directly. */ | |
| 71 | private final AttestCollector collector; | |
| 72 | ||
| 73 | private Handle(AttestCollector collector) { | |
| 74 | this.collector = collector; | |
| 75 | } | |
| 76 | ||
| 77 | /** | |
| 78 | * Returns the {@link TestMethodSink} that the orchestrator must receive | |
| 79 | * every per-method record on. | |
| 80 | * | |
| 81 | * @return collector as a sink | |
| 82 | */ | |
| 83 | public TestMethodSink asSink() { | |
| 84 |
1
1. asSink : replaced return value with null for org/egothor/methodatlas/attest/AttestFacade$Handle::asSink → NO_COVERAGE |
return collector; |
| 85 | } | |
| 86 | ||
| 87 | /** | |
| 88 | * Builds the attestation manifest and writes it to {@code outputFile}. | |
| 89 | * | |
| 90 | * @param toolVersion resolved tool version string | |
| 91 | * @param outputFile destination path | |
| 92 | * @throws IOException if the file cannot be written | |
| 93 | */ | |
| 94 | public void write(String toolVersion, Path outputFile) throws IOException { | |
| 95 | AttestReport report = collector.buildReport(toolVersion); | |
| 96 | AttestWriter.write(report, outputFile); | |
| 97 | } | |
| 98 | } | |
| 99 | } | |
Mutations | ||
| 61 |
1.1 |
|
| 84 |
1.1 |