AttestFacade.java

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
Location : prepare
Killed by : none
replaced return value with null for org/egothor/methodatlas/attest/AttestFacade::prepare → NO_COVERAGE

84

1.1
Location : asSink
Killed by : none
replaced return value with null for org/egothor/methodatlas/attest/AttestFacade$Handle::asSink → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.30.1 support