All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jreleaser.catalogers.SyftSbomCatalogerProcessor Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright 2020-2024 The JReleaser 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
 *
 *     https://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 org.jreleaser.catalogers;

import org.jreleaser.bundle.RB;
import org.jreleaser.model.api.catalog.sbom.SyftSbomCataloger;
import org.jreleaser.model.internal.JReleaserContext;
import org.jreleaser.model.internal.common.Artifact;
import org.jreleaser.model.spi.catalog.CatalogProcessingException;
import org.jreleaser.model.spi.catalog.sbom.AbstractSbomCatalogerProcessor;
import org.jreleaser.sdk.command.CommandException;
import org.jreleaser.sdk.tool.Syft;
import org.jreleaser.sdk.tool.ToolException;
import org.jreleaser.util.FileUtils;
import org.jreleaser.version.SemanticVersion;

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

import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.jreleaser.util.ComparatorUtils.greaterThanOrEqualTo;
import static org.jreleaser.util.StringUtils.getFilename;

/**
 * @author Andres Almiray
 * @since 1.5.0
 */
public class SyftSbomCatalogerProcessor extends AbstractSbomCatalogerProcessor {
    private org.jreleaser.model.internal.catalog.sbom.SyftSbomCataloger cataloger;

    public SyftSbomCatalogerProcessor(JReleaserContext context) {
        super(context);
    }

    @Override
    public org.jreleaser.model.internal.catalog.sbom.SyftSbomCataloger getCataloger() {
        return cataloger;
    }

    @Override
    public void setCataloger(org.jreleaser.model.internal.catalog.sbom.SyftSbomCataloger cataloger) {
        this.cataloger = cataloger;
    }

    @Override
    public String getType() {
        return org.jreleaser.model.api.catalog.sbom.SyftSbomCataloger.TYPE;
    }

    @Override
    public Result catalog() throws CatalogProcessingException {
        Set artifacts = collectArtifactsSelf(context);

        if (artifacts.isEmpty()) {
            context.getLogger().info(RB.$("catalog.no.artifacts"));
            return Result.NO_ARTIFACTS;
        }

        Syft syft = setupSyft(context);

        boolean executed = false;
        for (Artifact artifact : artifacts) {
            if (generateSbom(syft, artifact)) executed = true;
        }

        Path archivePath = cataloger.resolveArchivePath(context);
        if (executed || !Files.exists(archivePath)) {
            pack(artifacts, archivePath);
        }

        return executed ? Result.EXECUTED : Result.UPTODATE;
    }

    private void pack(Set candidates, Path archivePath) throws CatalogProcessingException {
        if (!cataloger.getPack().isEnabled()) return;

        context.getLogger().info(RB.$("catalog.sbom.pack", archivePath.getFileName()));

        String archiveFileName = getFilename(archivePath.getFileName().toString());

        try {
            Path tmp = Files.createTempDirectory(getType());
            Path workingDirectory = tmp.resolve(archiveFileName);
            Files.createDirectories(workingDirectory);
            for (Artifact artifact : doResolveArtifacts(candidates)) {
                Files.copy(artifact.getEffectivePath(), workingDirectory.resolve(artifact.getEffectivePath().getFileName()),
                    REPLACE_EXISTING, COPY_ATTRIBUTES);
            }
            FileUtils.zip(workingDirectory, archivePath);
        } catch (IOException e) {
            throw new CatalogProcessingException(RB.$("ERROR_catalog_unexpected_error_packing", archivePath.getFileName()), e);
        }
    }

    private Set doResolveArtifacts(Set candidates) {
        Set artifacts = new LinkedHashSet<>();

        Path catalogDirectory = resolveCatalogDirectory();

        for (org.jreleaser.model.api.catalog.sbom.SyftSbomCataloger.Format format : cataloger.getFormats()) {
            for (Artifact artifact : candidates) {
                String artifactFile = artifact.getEffectivePath().getFileName().toString();
                Path targetPath = catalogDirectory.resolve(artifactFile + format.extension());
                artifacts.add(Artifact.of(targetPath));
            }
        }

        return artifacts;
    }

    private Syft setupSyft(JReleaserContext context) throws CatalogProcessingException {
        Syft syft = new Syft(context.asImmutable(), cataloger.getVersion());
        try {
            if (!syft.setup()) {
                throw new CatalogProcessingException(RB.$("tool_unavailable", "syft"));
            }
        } catch (ToolException e) {
            throw new CatalogProcessingException(e.getMessage(), e);
        }

        return syft;
    }

    private boolean generateSbom(Syft syft, Artifact artifact) throws CatalogProcessingException {
        Path catalogDirectory = resolveCatalogDirectory();

        try {
            Files.createDirectories(catalogDirectory);
        } catch (IOException e) {
            throw new CatalogProcessingException(RB.$("ERROR_assembler_create_directories"), e);
        }

        Path artifactPath = artifact.getEffectivePath();
        String artifactFile = artifactPath.getFileName().toString();

        SemanticVersion syftVersion = SemanticVersion.of(syft.getVersion());
        SemanticVersion syft99 = SemanticVersion.of("0.99.0");
        boolean deprecations = greaterThanOrEqualTo(syftVersion, syft99);

        boolean executed = false;
        for (SyftSbomCataloger.Format format : cataloger.getFormats()) {
            Path targetPath = catalogDirectory.resolve(artifactFile + format.extension());

            List args = new ArrayList<>();
            if (deprecations) {
                args.add("--source-name");
                args.add(artifactFile);
                args.add("--output");
                args.add(format + "=" + targetPath.toAbsolutePath());
            } else {
                args.add("--output");
                args.add(format.toString());
                args.add("--name");
                args.add(artifactFile);
                args.add("--file");
                args.add(targetPath.toAbsolutePath().toString());
            }
            args.add(artifactFile);

            if (!Files.exists(targetPath)) {
                context.getLogger().debug(RB.$("catalog.sbom.not.exist"), context.relativizeToBasedir(targetPath));
                generateSbom(syft, artifactPath.getParent(), targetPath.getFileName(), args);
                executed = true;
            } else if (artifactPath.toFile().lastModified() > targetPath.toFile().lastModified()) {
                context.getLogger().debug(RB.$("checksum.file.newer"),
                    context.relativizeToBasedir(artifactPath),
                    context.relativizeToBasedir(targetPath));
                generateSbom(syft, artifactPath.getParent(), targetPath.getFileName(), args);
                executed = true;
            }
        }

        return executed;
    }

    private Path resolveCatalogDirectory() {
        return context.getCatalogsDirectory()
            .resolve("sbom").resolve(cataloger.getType());
    }

    private void generateSbom(Syft syft, Path executionDirectory, Path filename, List args) throws CatalogProcessingException {
        context.getLogger().info(" - {}", filename);

        try {
            syft.invoke(executionDirectory, args);
        } catch (CommandException e) {
            throw new CatalogProcessingException(RB.$("ERROR_unexpected_error"), e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy