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

org.whitesource.agent.ProjectsSender Maven / Gradle / Ivy

Go to download

File System Agent is a simple java command line tool which extracts descriptive information from your open source libraries

There is a newer version: 18.9.1.1
Show newest version
/**
 * Copyright (C) 2014 WhiteSource Ltd.
 * 

* 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 *

* http://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.whitesource.agent; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import com.google.common.collect.Lists; import com.google.gson.Gson; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.whitesource.agent.api.dispatch.*; import org.whitesource.agent.api.model.AgentProjectInfo; import org.whitesource.agent.api.model.Coordinates; import org.whitesource.agent.api.model.DependencyInfo; import org.whitesource.agent.client.WhitesourceService; import org.whitesource.agent.client.WssServiceException; import org.whitesource.agent.report.OfflineUpdateRequest; import org.whitesource.agent.report.PolicyCheckReport; import org.whitesource.agent.utils.LoggerFactory; import org.whitesource.agent.utils.Pair; import org.whitesource.contracts.PluginInfo; import org.whitesource.fs.LogMapAppender; import org.whitesource.fs.Main; import org.whitesource.fs.ProjectsDetails; import org.whitesource.fs.StatusCode; import org.whitesource.fs.configuration.OfflineConfiguration; import org.whitesource.fs.configuration.RequestConfiguration; import org.whitesource.fs.configuration.SenderConfiguration; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collectors; /** * Class for sending projects for all WhiteSource command line agents. * * @author Itai Marko * @author tom.shapira * @author anna.rozin */ public class ProjectsSender { /* --- Static members --- */ private static final String DATE_FORMAT = "HH:mm:ss"; public static final String PROJECT_URL_PREFIX = "Wss/WSS.html#!project;id="; protected static final int MAX_LOG_EVENTS = 1000; /* --- Members --- */ private final Logger logger = LoggerFactory.getLogger(ProjectsSender.class); private final SenderConfiguration senderConfig; private final OfflineConfiguration offlineConfig; private final RequestConfiguration requestConfig; private final PluginInfo pluginInfo; protected StatusCode prepStepStatusCode = StatusCode.SUCCESS; /* --- Constructors --- */ public ProjectsSender(SenderConfiguration senderConfig, OfflineConfiguration offlineConfig, RequestConfiguration requestConfig, PluginInfo pluginInfo) { this.senderConfig = senderConfig; this.offlineConfig = offlineConfig; this.requestConfig = requestConfig; this.pluginInfo = pluginInfo; } /* --- Public methods --- */ public Pair sendRequest(ProjectsDetails projectsDetails) { // send request logger.info("Initializing WhiteSource Client"); Collection projects = projectsDetails.getProjects(); if (checkDependenciesUpbound(projects)) { return new Pair<>("Number of dependencies exceeded the maximum supported", StatusCode.SERVER_FAILURE); } WhitesourceService service = createService(); String resultInfo = Constants.EMPTY_STRING; if (offlineConfig.isOffline()) { resultInfo = offlineUpdate(service, projects); return new Pair<>(resultInfo, this.prepStepStatusCode); } else { // update type UpdateType updateType = UpdateType.OVERRIDE; String updateTypeValue = senderConfig.getUpdateTypeValue(); try { updateType = UpdateType.valueOf(updateTypeValue); } catch (Exception e) { logger.info("Invalid value {} for updateType, defaulting to {}", updateTypeValue, UpdateType.OVERRIDE); } logger.info("UpdateType set to {} ", updateTypeValue); StatusCode statusCode = StatusCode.SUCCESS; if (senderConfig.isEnableImpactAnalysis()) { runViaAnalysis(projectsDetails, service); } else if (!senderConfig.isEnableImpactAnalysis()) { //todo return logs when needed would be enabled for all WSE-342 } int retries = senderConfig.getConnectionRetries(); while (retries-- > -1) { try { statusCode = checkPolicies(service, projects); if (senderConfig.isUpdateInventory()) { if (statusCode == StatusCode.SUCCESS || (senderConfig.isForceUpdate() && senderConfig.isForceUpdateFailBuildOnPolicyViolation())) { resultInfo = update(service, projects); } } break; } catch (WssServiceException e) { if (e.getCause() != null && e.getCause().getClass().getCanonicalName().substring(0, e.getCause().getClass().getCanonicalName().lastIndexOf(Constants.DOT)).equals(Constants.JAVA_NETWORKING)) { statusCode = StatusCode.CONNECTION_FAILURE; logger.error("Trying " + (retries + 1) + " more time" + (retries != 0 ? "s" : Constants.EMPTY_STRING)); } else { statusCode = StatusCode.SERVER_FAILURE; retries = -1; } resultInfo = "Failed to send request to WhiteSource server: " + e.getMessage(); logger.error(resultInfo, e.getMessage()); logger.debug(resultInfo, e); if (retries > -1) { try { Thread.sleep(senderConfig.getConnectionRetriesIntervals()); } catch (InterruptedException e1) { logger.error("Failed to sleep while retrying to connect to server " + e1.getMessage(), e1); } } String requestToken = e.getRequestToken(); if (StringUtils.isNotBlank(requestToken)) { resultInfo += Constants.NEW_LINE + "Support token: " + requestToken; logger.info("Support token: {}", requestToken); } } } if (service != null) { service.shutdown(); } if (statusCode == StatusCode.SUCCESS) { return new Pair<>(resultInfo, this.prepStepStatusCode); } return new Pair<>(resultInfo, statusCode); } } private void runViaAnalysis(ProjectsDetails projectsDetails, WhitesourceService service) { try { Class vulnerabilitiesAnalysisClass = Class.forName("whitesource.analysis.vulnerabilities.VulnerabilitiesAnalysis"); Method getAnalysisMethod = vulnerabilitiesAnalysisClass.getMethod("getAnalysis", String.class, int.class); Object vulnerabilitiesAnalysis = null; for (AgentProjectInfo project : projectsDetails.getProjectToViaComponents().keySet()) { // check language for scan according to user file LinkedList viaComponentsList = projectsDetails.getProjectToViaComponents().get(project); if (viaComponentsList.size() == 0) { logger.error("Effective Usage Analysis will not run if 0 dependencies are found. Check that the -d parameter specifies a valid project path"); Main.exit(StatusCode.ERROR.getValue()); //// TODO: 8/28/2018 as a result of WSE-765 exit using function from main. function signature should be change to throw an exception } for (ViaComponents viaComponents : viaComponentsList) { logger.info("Starting VIA impact analysis"); checkDependenciesSha1(viaComponents); String appPath = viaComponents.getAppPath(); ViaLanguage language = viaComponents.getLanguage(); try { vulnerabilitiesAnalysis = getAnalysisMethod.invoke(null, language.toString(), requestConfig.getViaAnalysisLevel()); // set app path for java script if (language.equals(ViaLanguage.JAVA_SCRIPT)) { int lastIndex = appPath.lastIndexOf(Constants.BACK_SLASH) != -1 ? appPath.lastIndexOf(Constants.BACK_SLASH) : appPath.lastIndexOf(Constants.FORWARD_SLASH); appPath = appPath.substring(0, lastIndex); } if (vulnerabilitiesAnalysis != null) { AgentProjectInfo projectToServer = new AgentProjectInfo(); projectToServer.setDependencies(Lists.newArrayList(project.getDependencies())); projectToServer.setProjectSetupDescription(project.getProjectSetupDescription()); projectToServer.setCoordinates(project.getCoordinates()); projectToServer.setProjectToken(project.getProjectToken()); projectToServer.setProjectSetupStatus(project.getProjectSetupStatus()); projectToServer.setParentCoordinates(project.getParentCoordinates()); Class fsaAgentServerClass = Class.forName("whitesource.analysis.server.FSAgentServer"); Object server = fsaAgentServerClass.getConstructor(AgentProjectInfo.class, WhitesourceService.class, String.class, String.class).newInstance( projectToServer, service, requestConfig.getApiToken(), requestConfig.getUserKey()); logger.info("Starting analysis for: {}", appPath); Class serverClass = Class.forName("whitesource.analysis.server.Server"); Method runAnalysis = vulnerabilitiesAnalysisClass.getDeclaredMethod("runAnalysis", serverClass, String.class, Collection.class, Boolean.class); runAnalysis.invoke(vulnerabilitiesAnalysis, server, appPath, project.getDependencies(), Boolean.valueOf(requestConfig.getViaDebug())); logger.info("Got impact analysis result from server"); } } catch (InvocationTargetException e) { logger.error("Failed to run VIA impact analysis {}", e.getTargetException().getMessage()); } catch (Exception e) { logger.error("Failed to run VIA impact analysis {}", e.getMessage()); } } } } catch (NoSuchMethodException e) { logger.error("Failed to run VIA impact analysis, couldn't find method {}", e.getMessage()); } catch (ClassNotFoundException e) { logger.error("Failed to run VIA impact analysis, couldn't find class {}", e.getMessage()); } } private void checkDependenciesSha1(ViaComponents viaComponents) { Collection dependencyWithoutSha1 = new LinkedList<>(); int unknownDependencyCounter = 0; for (DependencyInfo dependencyInfo : viaComponents.getDependencies()) { if (StringUtils.isEmpty(dependencyInfo.getSha1())) { unknownDependencyCounter++; dependencyWithoutSha1.add(dependencyInfo); } } if (unknownDependencyCounter > 0) { if (requestConfig.isRequireKnownSha1()) { logger.warn("The system found {} with an unknown SHA1 value. Default processing was terminated.", unknownDependencyCounter); printDependenciesWithoutSha1(dependencyWithoutSha1); Main.exit(StatusCode.ERROR.getValue()); } else { logger.warn("The system found {} with an unknown SHA1 value. Default processing termination was overridden by parameter.", unknownDependencyCounter); printDependenciesWithoutSha1(dependencyWithoutSha1); } } } private void printDependenciesWithoutSha1(Collection dependencyWithoutSha1) { logger.warn("Found dependencies with an unknown SHA1 value:"); for (DependencyInfo dependencyInfo : dependencyWithoutSha1) { logger.warn(new Coordinates(dependencyInfo.getGroupId(), dependencyInfo.getArtifactId(), dependencyInfo.getVersion()).toString()); } } private boolean checkDependenciesUpbound(Collection projects) { int numberOfDependencies = projects.stream().map(x -> x.getDependencies()).mapToInt(x -> x.size()).sum(); if (numberOfDependencies > Constants.MAX_NUMBER_OF_DEPENDENCIES) { logger.warn("Number of dependencies: {} exceeded the maximum supported: {}", numberOfDependencies, Constants.MAX_NUMBER_OF_DEPENDENCIES); return true; } return false; } protected WhitesourceService createService() { logger.info("Service URL is " + senderConfig.getServiceUrl()); boolean setProxy = false; if (StringUtils.isNotBlank(senderConfig.getProxyHost()) || !offlineConfig.isOffline()) { setProxy = true; } int connectionTimeoutMinutes = senderConfig.getConnectionTimeOut(); final WhitesourceService service = new WhitesourceService(pluginInfo.getAgentType(), pluginInfo.getAgentVersion(), pluginInfo.getPluginVersion(), senderConfig.getServiceUrl(), setProxy, connectionTimeoutMinutes, senderConfig.isIgnoreCertificateCheck()); if (StringUtils.isNotBlank(senderConfig.getProxyHost())) { service.getClient().setProxy(senderConfig.getProxyHost(), senderConfig.getProxyPort(), senderConfig.getProxyUser(), senderConfig.getProxyPassword()); } return service; } private StatusCode checkPolicies(WhitesourceService service, Collection projects) throws WssServiceException { boolean policyCompliance = true; if (senderConfig.isCheckPolicies() || !senderConfig.isUpdateInventory()) { logger.info("Checking policies"); CheckPolicyComplianceResult checkPoliciesResult; if (senderConfig.isSendLogsToWss()) { String logData = getLogData(); checkPoliciesResult = service.checkPolicyCompliance(requestConfig.getApiToken(), requestConfig.getProductName(), requestConfig.getProductVersion(), projects, senderConfig.isForceCheckAllDependencies(), requestConfig.getUserKey(), requestConfig.getRequesterEmail(), logData, requestConfig.getProductToken()); } else { checkPoliciesResult = service.checkPolicyCompliance(requestConfig.getApiToken(), requestConfig.getProductName(), requestConfig.getProductVersion(), projects, senderConfig.isForceCheckAllDependencies(), requestConfig.getUserKey(), requestConfig.getRequesterEmail(), null, requestConfig.getProductToken()); } if (checkPoliciesResult.hasRejections()) { if (senderConfig.isForceUpdate() && senderConfig.isUpdateInventory()) { logger.info("Some dependencies violate open source policies, however all were force " + "updated to organization inventory."); if (senderConfig.isForceUpdateFailBuildOnPolicyViolation()) { policyCompliance = false; } } else if (!senderConfig.isUpdateInventory()) { logger.info("Some dependencies did not conform with open source policies, review report for details"); policyCompliance = false; } else { logger.info("Some dependencies did not conform with open source policies, review report for details"); logger.info("=== UPDATE ABORTED ==="); policyCompliance = false; } } else { logger.info("All dependencies conform with open source policies."); } String requestToken = checkPoliciesResult.getRequestToken(); if (StringUtils.isNotBlank(requestToken)) { logger.info("Check Policies Support Token: {}", requestToken); } try { // generate report PolicyCheckReport report = new PolicyCheckReport(checkPoliciesResult); File outputDir = new File(offlineConfig.getWhiteSourceFolderPath()); report.generate(outputDir, false); report.generateJson(outputDir); logger.info("Policies report generated successfully"); } catch (IOException e) { logger.error("Error generating check policies report: " + e.getMessage(), e); } } return policyCompliance ? StatusCode.SUCCESS : StatusCode.POLICY_VIOLATION; } protected String update(WhitesourceService service, Collection projects) throws WssServiceException { logger.info("Sending Update"); UpdateInventoryResult updateResult; //-------------------------------- if (requestConfig.getViaDebug().equals("SAVE") || Boolean.valueOf(requestConfig.getViaDebug())) { saveRequestToFile(projects); } //-------------------------------- if (senderConfig.isSendLogsToWss()) { String logData = getLogData(); updateResult = service.update(requestConfig.getApiToken(), requestConfig.getRequesterEmail(), UpdateType.valueOf(senderConfig.getUpdateTypeValue()), requestConfig.getProductName(), requestConfig.getProductVersion(), projects, requestConfig.getUserKey(), logData, requestConfig.getScanComment(), requestConfig.getProductToken()); } else { updateResult = service.update(requestConfig.getApiToken(), requestConfig.getRequesterEmail(), UpdateType.valueOf(senderConfig.getUpdateTypeValue()), requestConfig.getProductName(), requestConfig.getProductVersion(), projects, requestConfig.getUserKey(), null, requestConfig.getScanComment(), requestConfig.getProductToken()); } String resultInfo = logResult(updateResult); // remove line separators resultInfo = resultInfo.replace(System.lineSeparator(), Constants.EMPTY_STRING); return resultInfo; } private void saveRequestToFile(Collection projects) { String fileName = "jsonOut" + Constants.DASH + requestConfig.getProductName() + Constants.DASH + requestConfig.getProjectName() + ".json"; RequestFactory requestFactory = new RequestFactory(pluginInfo.getAgentType(), pluginInfo.getAgentVersion(), pluginInfo.getPluginVersion()); String updateJson = new Gson().toJson(requestFactory.newUpdateInventoryRequest(requestConfig.getApiToken(), UpdateType.valueOf(senderConfig.getUpdateTypeValue()), requestConfig.getRequesterEmail(), requestConfig.getProductName(), requestConfig.getProductVersion(), projects, requestConfig.getUserKey(), (String) null, (String) null, (String) requestConfig.getProductToken())); Path path = Paths.get(fileName); try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.write(updateJson); } catch (Exception e) { logger.debug("couldn't create via debug file {}", e.getMessage()); } } private String offlineUpdate(WhitesourceService service, Collection projects) { String resultInfo = Constants.EMPTY_STRING; logger.info("Generating offline update request"); // generate offline request UpdateInventoryRequest updateRequest = service.offlineUpdate(new UpdateInventoryRequest(requestConfig.getApiToken(), requestConfig.getProductName(), requestConfig.getProductVersion(), projects, requestConfig.getUserKey(), requestConfig.getScanComment())); if (senderConfig.isSendLogsToWss()) { updateRequest.setLogData(getLogData()); } updateRequest.setRequesterEmail(requestConfig.getRequesterEmail()); if (requestConfig.getProductToken() != null && !requestConfig.getProductToken().isEmpty()) { updateRequest.setProductToken(requestConfig.getProductToken()); } try { OfflineUpdateRequest offlineUpdateRequest = new OfflineUpdateRequest(updateRequest); UpdateType updateTypeFinal; // if the update type was forced by command or config -> set it if (StringUtils.isNotBlank(senderConfig.getUpdateTypeValue())) { try { updateTypeFinal = UpdateType.valueOf(senderConfig.getUpdateTypeValue()); } catch (Exception e) { logger.info("Invalid value {} for updateType, defaulting to {}", senderConfig.getUpdateTypeValue(), UpdateType.OVERRIDE); updateTypeFinal = UpdateType.OVERRIDE; } } else { // Otherwise use the parameter in the file updateTypeFinal = updateRequest.getUpdateType(); } logger.info("UpdateType offline set to {} ", updateTypeFinal); updateRequest.setUpdateType(updateTypeFinal); File outputDir = new File(offlineConfig.getWhiteSourceFolderPath()).getAbsoluteFile(); if (!outputDir.exists() && !outputDir.mkdir()) { throw new IOException("Unable to make output directory: " + outputDir); } File file = offlineUpdateRequest.generate(outputDir, offlineConfig.isZip(), offlineConfig.isPrettyJson()); resultInfo = "Offline request generated successfully at " + file.getPath(); logger.info(resultInfo); } catch (IOException e) { resultInfo = "Error generating offline update request: " + e.getMessage(); logger.error(resultInfo); } finally { if (service != null) { service.shutdown(); } } return resultInfo; } private String logResult(UpdateInventoryResult updateResult) { StringBuilder resultLogMsg = new StringBuilder("Inventory update results for ").append(updateResult.getOrganization()).append(Constants.NEW_LINE); logger.info("Inventory update results for {}", updateResult.getOrganization()); // newly created projects Collection createdProjects = updateResult.getCreatedProjects(); if (createdProjects.isEmpty()) { logger.info("No new projects found."); resultLogMsg.append("No new projects found.").append(Constants.NEW_LINE); } else { logger.info("Newly created projects:"); resultLogMsg.append("Newly created projects:").append(Constants.NEW_LINE); for (String projectName : createdProjects) { logger.info("# {}", projectName); resultLogMsg.append(projectName).append(Constants.NEW_LINE); } } // updated projects Collection updatedProjects = updateResult.getUpdatedProjects(); if (updatedProjects.isEmpty()) { logger.info("No projects were updated."); resultLogMsg.append("No projects were updated.").append(Constants.NEW_LINE); } else { logger.info("Updated projects:"); resultLogMsg.append("Updated projects:").append(Constants.NEW_LINE); for (String projectName : updatedProjects) { logger.info("# {}", projectName); resultLogMsg.append(projectName).append(Constants.NEW_LINE); } } // reading projects' URLs HashMap projectsUrls = updateResult.getProjectNamesToIds(); if (projectsUrls != null && !projectsUrls.isEmpty()) { for (String projectName : projectsUrls.keySet()) { String appUrl = senderConfig.getServiceUrl().replace("agent", Constants.EMPTY_STRING); String projectsUrl = appUrl + PROJECT_URL_PREFIX + projectsUrls.get(projectName); logger.info("Project name: {}, URL: {}", projectName, projectsUrl); resultLogMsg.append(Constants.NEW_LINE).append("Project name: ").append(projectName).append(", project URL:").append(projectsUrl); } } // support token String requestToken = updateResult.getRequestToken(); if (StringUtils.isNotBlank(requestToken)) { logger.info("Support Token: {}", requestToken); resultLogMsg.append(Constants.NEW_LINE).append("Support Token: ").append(requestToken); } return resultLogMsg.toString(); } private String getLogData() { String logs = Constants.EMPTY_STRING; ch.qos.logback.classic.Logger setLog = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(Constants.MAP_LOG_NAME); ConcurrentSkipListMap collectToSet = ((LogMapAppender) setLog.getAppender(Constants.MAP_APPENDER_NAME)).getLogEvents(); // going over all the collected events, filtering out the empty ones, and writing them to a long string SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT); List events = collectToSet.values().stream().filter(iLoggingEvent -> !iLoggingEvent.getMessage().isEmpty() && !iLoggingEvent.getMessage().equals(Constants.NEW_LINE)).collect(Collectors.toList()); if (events.size() > MAX_LOG_EVENTS) { events = events.stream().filter(iLoggingEvent -> iLoggingEvent.getLevel().levelInt >= Level.INFO.levelInt).collect(Collectors.toList()); } for (ILoggingEvent event : events) { logs = logs.concat("[" + event.getLevel() + "] " + simpleDateFormat.format(new Date(event.getTimeStamp())) + " - " + event.getFormattedMessage()).concat(Constants.NEW_LINE); } return logs; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy