public void perform()

in src/main/java/com/amazon/inspector/jenkins/amazoninspectorbuildstep/AmazonInspectorBuilder.java [169:389]


    public void perform(Run<?, ?> build, FilePath workspace, EnvVars env, Launcher launcher, TaskListener listener)
            throws IOException, InterruptedException {
        logger = listener.getLogger();

        File outFile = new File(build.getRootDir(), "out");
        this.job = build.getParent();

        PrintStream printStream = new PrintStream(outFile, StandardCharsets.UTF_8);
        try {
            Map<String, String> artifactMap = new HashMap<>();

            if (Jenkins.getInstanceOrNull() == null) {
                throw new RuntimeException("No Jenkins instance found.");
            }

            String activeArchiveType = archiveType;
            if (activeArchiveType == null || activeArchiveType.isEmpty()) {
                activeArchiveType = "container";
            }

            String sbomgenSelection = this.sbomgenSelection;

            String activeSbomgenPath;
            if ("automatic".equalsIgnoreCase(sbomgenSelection)) {
                logger.println("Automatic SBOMGen selected, downloading using default settings...");
                activeSbomgenPath = SbomgenDownloader.getBinary(workspace);
            } else if ("manual".equalsIgnoreCase(sbomgenSelection)) {
                if (sbomgenPath == null || sbomgenPath.isEmpty()) {
                    throw new IllegalArgumentException("Manual SBOMGen selected but no path provided.");
                }
                File sbomgenFile = new File(sbomgenPath);
                if (!sbomgenFile.exists() || !sbomgenFile.canExecute()) {
                    throw new IllegalArgumentException("Provided SBOMgen path is invalid or not executable: " + sbomgenPath);
                }
                logger.println("Manual SBOMGen selected, using provided path: " + sbomgenPath);
                activeSbomgenPath = sbomgenPath;
            } else {
                logger.println("Invalid SBOMGen selection. Defaulting to Automatic.");
                activeSbomgenPath = SbomgenDownloader.getBinary(workspace);
            }

            StandardUsernamePasswordCredentials credential = null;
            if (credentialId == null) {
                logger.println("Credential ID is null, this is not normal, please check your config. " +
                        "Continuing without docker credentials.");
            } else {
                credential = CredentialsProvider.findCredentialById(credentialId,
                        StandardUsernamePasswordCredentials.class, build);
            }
            String skipfiles = (sbomgenSkipFiles != null) ? sbomgenSkipFiles : "";
            String sbom;
            if (credential != null) {
                sbom = new SbomgenRunner(launcher, activeSbomgenPath, activeArchiveType, archivePath, credential.getUsername(),
                        credential.getPassword().getPlainText(),skipfiles).run();
            } else {
                sbom = new SbomgenRunner(launcher, activeSbomgenPath, activeArchiveType, archivePath, null, null, skipfiles).run();
            }

            JsonElement metadata = JsonParser.parseString(sbom).getAsJsonObject().get("metadata");
            JsonObject component = null;
            if (metadata != null && metadata.getAsJsonObject().get("component") != null) {
                component = metadata.getAsJsonObject().get("component").getAsJsonObject();
            }

            Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
            String imageSha = getImageSha(sbom);

            listener.getLogger().printf("Sending SBOM to Inspector for validation with info: credential:%s, role:%s, profile:%s",
                    awsCredentialId, iamRole, awsProfileName);
            AmazonWebServicesCredentials awsCredential = null;
            if (awsCredentialId != null) {
                awsCredential = CredentialsProvider.findCredentialById(awsCredentialId,
                        AmazonWebServicesCredentials.class, build);
            }
            listener.getLogger().print("\n");

            String workingOidcCredentialId = oidcCredentialId;
            if (workingOidcCredentialId == null) {
                workingOidcCredentialId = "";
            }

            IdTokenStringCredentials oidcStr = CredentialsProvider.findCredentialById(workingOidcCredentialId, IdTokenStringCredentials.class, build);
            IdTokenFileCredentials oidcFile = CredentialsProvider.findCredentialById(workingOidcCredentialId, IdTokenFileCredentials.class, build);
            String oidcToken = getOidcToken(oidcStr, oidcFile);

            String responseData = new SdkRequests(awsRegion, awsCredential, oidcToken, awsProfileName, iamRole).requestSbom(sbom);
            SbomData sbomData = SbomData.builder().sbom(gson.fromJson(responseData, Sbom.class)).build();

            String sbomFileName = String.format("%s-%s-sbom.json", reportArtifactName, build.getDisplayName()).replaceAll("[ #]", "");
            String sbomWorkspacePath = String.format("%s/%s", build.getId(), sbomFileName);

            FilePath sbomFile = workspace.child(sbomWorkspacePath);
            FilePath sbomFileParent = sbomFile.getParent();
            if (sbomFile == null || sbomFileParent == null) {
                throw new NullPointerException("SbomFile cannot be null.");
            }
            if (!sbomFileParent.exists()) {
                sbomFileParent.mkdirs();
            }

            sbomFile.write(gson.toJson(sbomData.getSbom()), "UTF-8");

            artifactMap.put(sbomFileName, sbomWorkspacePath);

            build.getArtifactManager().archive(workspace, launcher, new BuildListenerAdapter(listener), artifactMap);
            listener.getLogger().println("Artifact saved: " + sbomFile.getRemote());

            CsvConverter converter = new CsvConverter(sbomData);
            String csvVulnFileName = String.format("%s-%s-vuln.csv", build.getParent().getDisplayName(),
                    build.getDisplayName()).replaceAll("[ #]", "");
            String csvVulnWorkspacePath = String.format("%s/%s", build.getId(), csvVulnFileName);

            FilePath csvVulnFile = workspace.child(csvVulnWorkspacePath);

            String csvDockerFileName = String.format("%s-%s-docker.csv", build.getParent().getDisplayName(),
                    build.getDisplayName()).replaceAll("[ #]", "");
            String csvDockerWorkspacePath = String.format("%s/%s", build.getId(), csvDockerFileName);
            FilePath csvDockerFile = workspace.child(csvDockerWorkspacePath);
            logger.println("Converting SBOM Results to CSV.");

            SbomOutputParser parser = new SbomOutputParser(sbomData);
            parser.parseVulnCounts();

            String sanitizedArchiveName = null;
            String componentName = null;
            if (component != null && component.get("name") != null) {
                componentName = component.get("name").getAsString();
            }

            if (componentName != null && componentName.endsWith(".tar")) {
                sanitizedArchiveName = sanitizeFilePath("file://" + componentName);
            } else {
                sanitizedArchiveName = archivePath;
            }

            converter.routeVulnerabilities();
            String csvVulnContent = converter.convertVulnerabilities(sanitizedArchiveName, imageSha, build.getId(), SbomOutputParser.vulnCounts);
            if (csvVulnContent != null) {
                artifactMap.put(csvVulnFileName, csvVulnWorkspacePath);
                csvVulnFile.write(csvVulnContent, "UTF-8");
            }

            String csvDockerContent = converter.convertDocker(sanitizedArchiveName, imageSha, build.getId(), SbomOutputParser.dockerCounts);
            if (csvDockerContent != null) {
                artifactMap.put(csvDockerFileName, csvDockerWorkspacePath);
                csvDockerFile.write(csvDockerContent, "UTF-8");
            }

            String[] splitName = sanitizedArchiveName.split(":");
            String tag = null;
            if (splitName.length > 1) {
                tag = splitName[1];
            }

            @SuppressFBWarnings
            HtmlData htmlData = HtmlData.builder()
                    .artifactsPath(sanitizeUrl(env.get("RUN_ARTIFACTS_DISPLAY_URL"))) //jenkins specific
                    .updatedAt(new SimpleDateFormat("MM/dd/yyyy, hh:mm:ss aa").format(Calendar.getInstance().getTime()))
                    .imageMetadata(ImageMetadata.builder()
                            .id(splitName[0])
                            .tags(tag)
                            .sha(imageSha)
                            .build())
                    .docker(HtmlConversionUtils.convertDocker(sbomData.getSbom().getVulnerabilities(),
                            sbomData.getSbom().getComponents()))
                    .vulnerabilities(HtmlConversionUtils.convertVulnerabilities(sbomData.getSbom().getVulnerabilities(),
                            sbomData.getSbom().getComponents()))
                    .build();

            String reportData = gson.toJson(htmlData);

            String htmlJarPath = String.valueOf(new FilePath(new File(HtmlJarHandler.class.getProtectionDomain().getCodeSource().getLocation()
                    .toURI())));

            new HtmlJarHandler(htmlJarPath, reportData).copyHtmlToDir(workspace, build.getId());
            artifactMap.put("index.html", String.format("%s/%s", build.getId(), "index.html"));

            build.getArtifactManager().archive(workspace, launcher, new BuildListenerAdapter(listener), artifactMap);

            listener.getLogger().println("Build Artifacts: " + env.get("RUN_ARTIFACTS_DISPLAY_URL"));

            boolean doesBuildPass = !doesBuildFail(SbomOutputParser.aggregateCounts.getCounts());

            if (!isThresholdEnabled) {
                listener.getLogger().println("Thresholds disabled. Skipping all threshold/EPSS checks.");
                doesBuildPass = true;
            } else {
                if (epssThreshold != null) {
                    listener.getLogger().println("EPSS Threshold set to: " + epssThreshold);
                    boolean cvesExceedThreshold = assessCVEsAgainstEPSS(build, workspace, listener, epssThreshold, sbomWorkspacePath);
                    if (cvesExceedThreshold) {
                        doesBuildPass = false;
                    } else {
                        listener.getLogger().println("All CVEs are within the EPSS threshold of " + epssThreshold + ".");
                    }
                } else {
                    listener.getLogger().println("No EPSS Threshold specified. Skipping EPSS assessment.");
                }
            }

            if (doesBuildPass) {
                build.setResult(Result.SUCCESS);
            } else {
                build.setResult(Result.FAILURE);
            }

            listener.getLogger().println("Results: " + SbomOutputParser.aggregateCounts.toString());
            if (!isThresholdEnabled) {
                listener.getLogger().println("Ignoring results due to thresholds being disabled.");
            }

            listener.getLogger().println("Does Build Pass: " + doesBuildPass);
        } catch (Exception e) {
            listener.getLogger().println("Plugin execution ran into an error and is being aborted!");
            build.setResult(Result.ABORTED);
            listener.getLogger().println("Exception:" + e);
            e.printStackTrace(listener.getLogger());
        } finally {
            printStream.close();
        }
    }