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

com.capitalone.dashboard.collector.DefaultWhiteSourceClient Maven / Gradle / Ivy

package com.capitalone.dashboard.collector;

import com.capitalone.dashboard.client.RestClient;
import com.capitalone.dashboard.misc.HygieiaException;
import com.capitalone.dashboard.model.Build;
import com.capitalone.dashboard.model.BuildStatus;
import com.capitalone.dashboard.model.Collector;
import com.capitalone.dashboard.model.CollectorItem;
import com.capitalone.dashboard.model.LibraryPolicyResult;
import com.capitalone.dashboard.model.LibraryPolicyThreatDisposition;
import com.capitalone.dashboard.model.LibraryPolicyThreatLevel;
import com.capitalone.dashboard.model.LibraryPolicyType;
import com.capitalone.dashboard.model.LicensePolicyType;
import com.capitalone.dashboard.model.WhiteSourceChangeRequest;
import com.capitalone.dashboard.model.WhiteSourceComponent;
import com.capitalone.dashboard.model.WhiteSourceProduct;
import com.capitalone.dashboard.model.WhiteSourceProjectVital;
import com.capitalone.dashboard.model.WhiteSourceRequest;
import com.capitalone.dashboard.model.WhitesourceOrg;
import com.capitalone.dashboard.repository.BuildRepository;
import com.capitalone.dashboard.repository.CollectorItemRepository;
import com.capitalone.dashboard.repository.CollectorRepository;
import com.capitalone.dashboard.repository.LibraryPolicyResultsRepository;
import com.capitalone.dashboard.repository.WhiteSourceComponentRepository;
import com.capitalone.dashboard.settings.WhiteSourceServerSettings;
import com.capitalone.dashboard.settings.WhiteSourceSettings;
import com.capitalone.dashboard.util.HygieiaUtils;
import com.capitalone.dashboard.utils.Constants;
import com.capitalone.dashboard.utils.DateTimeUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.capitalone.dashboard.utils.Constants.DEFAULT_WHITESOURCE_TIMEZONE;
import static com.capitalone.dashboard.utils.Constants.yyyy_MM_dd_HH_mm_ss;
import static com.capitalone.dashboard.utils.Constants.yyyy_MM_dd_HH_mm_ss_z;

@Component
public class DefaultWhiteSourceClient implements WhiteSourceClient {
    private static final Log LOG = LogFactory.getLog(DefaultWhiteSourceClient.class);
    private static final String API_URL = "/api/v1.3";
    private final RestClient restClient;
    private final WhiteSourceSettings whiteSourceSettings;
    private final CollectorItemRepository collectorItemRepository;
    private final LibraryPolicyResultsRepository libraryPolicyResultsRepository;
    private final CollectorRepository collectorRepository;
    private final BuildRepository buildRepository;
    private final WhiteSourceComponentRepository whiteSourceComponentRepository;


    @Autowired
    public DefaultWhiteSourceClient(RestClient restClient, WhiteSourceSettings settings,
                                    WhiteSourceComponentRepository whiteSourceComponentRepository,
                                    CollectorItemRepository collectorItemRepository,
                                    LibraryPolicyResultsRepository libraryPolicyResultsRepository,
                                    CollectorRepository collectorRepository,
                                    BuildRepository buildRepository) {
        this.restClient = restClient;
        this.whiteSourceSettings = settings;
        this.collectorItemRepository = collectorItemRepository;
        this.whiteSourceComponentRepository = whiteSourceComponentRepository;
        this.libraryPolicyResultsRepository = libraryPolicyResultsRepository;
        this.collectorRepository = collectorRepository;
        this.buildRepository = buildRepository;
    }


    /*  A note about whitesouce date time stamps - they are all over the place.
     * Whitesource server is UTC timezone.
     *
     * This client class need to make sure that the date times are correctly
     * translated back and forth.
     *
     * For saving to Hygieia everything is converted into local timestamp millis
     * For querying Whitesource, timestamps must be formatted accordingly and in UTC timezone
     *
     * Whitesource date time formats in different apis response
     * In Change Log: "startDateTime": "2020-12-18 19:21:33",
     * In Project Vitals:
     *              "creationDate": "2020-12-21 15:37:48 +0000",
     *             "lastUpdatedDate": "2020-12-21 16:45:11 +0000"
     * In alerts:
     *             "date": "2020-12-21",
     *             "modifiedDate": "2020-12-21",
     *             "time": 1608559754000,
     *             "creation_date": "2020-12-21",
     *
     *
     * Whitesource date time formats in different apis request
     * {
     *     "requestType" : "getOrganizationAlertsByType",
     *     "userKey": "user_key",
     *     "alertType" : "alert_type",
     *     "orgToken" : "organization_api_key",
     *     "fromDate" : "2016-01-01 10:00:00",
     *     "toDate" : "2016-01-02 10:00:00"
     * }
     *
     */


    /**
     * Get Whitesource Org Details
     *
     * @param serverSettings Whitesource Server Setting
     * @return Whitesource Org
     * @throws HygieiaException Hygieia Exception
     */
    @Override
    public WhitesourceOrg getOrgDetails(WhiteSourceServerSettings serverSettings) throws HygieiaException {
        // Name will be filled up via the api call
        WhitesourceOrg whitesourceOrg = new WhitesourceOrg("", serverSettings.getOrgToken());
        try {
            JSONObject jsonObject = makeRestCall(
                    Constants.RequestType.getOrganizationDetails, whitesourceOrg,
                    null, null, null, null, serverSettings);
            String name = (String) jsonObject.get(Constants.ORG_NAME);
            return new WhitesourceOrg(name, whitesourceOrg.getToken());
            //TODO: Refactor Exception Handling
        } catch (Exception e) {
            throw new HygieiaException("Exception occurred while calling getOrgDetails", e.getCause(), HygieiaException.BAD_DATA);
        }
    }

    /**
     * Gets products for a whitesource org
     *
     * @param whitesourceOrg whitesource org
     * @param serverSettings whitesource server settings
     * @return List of Whitesource products
     * @throws HygieiaException Hygieia Exception
     */
    @Override
    public List getProducts(WhitesourceOrg whitesourceOrg, WhiteSourceServerSettings serverSettings) throws HygieiaException {
        long timeToGetProducts = System.currentTimeMillis();
        List whiteSourceProducts = new ArrayList<>();
        try {
            JSONObject jsonObject = makeRestCall(Constants.RequestType.getAllProducts, whitesourceOrg, null, null, null, null, serverSettings);
            if (Objects.isNull(jsonObject)) return new ArrayList<>();
            JSONArray jsonArray = (JSONArray) jsonObject.get(Constants.PRODUCTS);
            if (CollectionUtils.isEmpty(jsonArray)) return new ArrayList<>();
            for (Object product : jsonArray) {
                JSONObject wsProduct = (JSONObject) product;
                WhiteSourceProduct whiteSourceProduct = new WhiteSourceProduct();
                whiteSourceProduct.setProductId(getLongValue(wsProduct, Constants.PRODUCT_ID));
                whiteSourceProduct.setProductName(getStringValue(wsProduct, Constants.PRODUCT_NAME));
                whiteSourceProduct.setProductToken(getStringValue(wsProduct, Constants.PRODUCT_TOKEN));
                whiteSourceProducts.add(whiteSourceProduct);
            }
            //TODO: Refactor Exception Handling
        } catch (Exception e) {
            throw new HygieiaException("Exception occurred while retrieving getAllProducts for orgName=" + whitesourceOrg.getName(), e.getCause(), HygieiaException.BAD_DATA);
        }
        timeToGetProducts = System.currentTimeMillis() - timeToGetProducts;
        LOG.info("WhitesourceClient: Time to get products =" + timeToGetProducts);
        return whiteSourceProducts;
    }

    /**
     * Gets all projects for a given product
     *
     * @param whitesourceOrg Whitesource Org
     * @param product        Whitesource Product
     * @param serverSettings Whitesource Server Settings
     * @return List of Whitesource Component
     */
    @Override
    public List getAllProjectsForProduct(WhitesourceOrg whitesourceOrg, WhiteSourceProduct product, WhiteSourceServerSettings serverSettings) {
        List whiteSourceProjects = new ArrayList<>();
        try {
            JSONObject jsonObject = makeRestCall(Constants.RequestType.getAllProjects, whitesourceOrg, product.getProductToken(), null, null, null, serverSettings);
            if (Objects.isNull(jsonObject)) return whiteSourceProjects;
            JSONArray jsonArray = (JSONArray) jsonObject.get(Constants.PROJECTS);
            if (jsonArray == null) return whiteSourceProjects;
            for (Object project : jsonArray) {
                JSONObject wsProject = (JSONObject) project;
                WhiteSourceComponent whiteSourceProject = new WhiteSourceComponent();
                whiteSourceProject.setProjectName(getStringValue(wsProject, Constants.PROJECT_NAME));
                whiteSourceProject.setProjectToken(getStringValue(wsProject, Constants.PROJECT_TOKEN));
                whiteSourceProject.setProductToken(product.getProductToken());
                whiteSourceProject.setProductName(product.getProductName());
                whiteSourceProject.setOrgName(whitesourceOrg.getName());
                whiteSourceProjects.add(whiteSourceProject);
            }
            //TODO: Refactor Exception Handling
        } catch (Exception e) {
            LOG.error("Exception occurred while retrieving getAllProjectsForProduct for productName=" + product.getProductName(), e);
        }
        return whiteSourceProjects;
    }


    /**
     * Gets a set of project tokens that are in Org Alerts By Policy Violations
     *
     * @param whitesourceOrg   Whitesource Org
     * @param historyTimestamp Time to go back
     * @param serverSettings   Whitesource Server Setting
     * @return Set of Project Tokens
     */
    @Override
    public Set getAffectedProjectsForOrganization(WhitesourceOrg whitesourceOrg, long historyTimestamp, WhiteSourceServerSettings serverSettings) {
        historyTimestamp = Math.max(historyTimestamp, System.currentTimeMillis() - whiteSourceSettings.getMaxOrgLevelQueryTimeWindow());
        Set affectedProjectTokens = getAffectedProjectTokens(whitesourceOrg, Constants.REJECTED_BY_POLICY, historyTimestamp, serverSettings);
        affectedProjectTokens.addAll(getAffectedProjectTokens(whitesourceOrg, Constants.SECURITY_VULNERABILITY, historyTimestamp, serverSettings));
        return affectedProjectTokens;
    }

    /**
     * Gets a set of project tokens By Policy Violations
     *
     * @param whitesourceOrg   Whitesource Org
     * @param alertType        Alert Type
     * @param historyTimestamp Time to go back
     * @param serverSettings   Whitesource Server Setting
     * @return Set of Project Tokens
     */

    public Set getAffectedProjectTokens(WhitesourceOrg whitesourceOrg, String alertType, long historyTimestamp, WhiteSourceServerSettings serverSettings) {
        Set affectedProjectTokens = new HashSet<>();
        String startDateTime = DateTimeUtils.timeFromLongToString(historyTimestamp, serverSettings.getTimeZone(), yyyy_MM_dd_HH_mm_ss);
        try {
            // Get policy alerts
            JSONObject jsonObject = makeRestCall(Constants.RequestType.getOrganizationAlertsByType, whitesourceOrg, null, null, alertType, startDateTime, serverSettings);
            JSONArray alerts = (JSONArray) Objects.requireNonNull(jsonObject).get(Constants.ALERTS);
            if (!CollectionUtils.isEmpty(alerts)) {
                for (Object a : alerts) {
                    JSONObject alert = (JSONObject) a;
                    String projectToken = getStringValue(alert, Constants.PROJECT_TOKEN);
                    affectedProjectTokens.add(projectToken);
                }
            }
            //TODO: Refactor Exception Handling
        } catch (Exception e) {
            LOG.info("Exception occurred while calling getAffectedProjectsForOrganization for orgToken =" + whitesourceOrg.getToken() , e);
        }
        return affectedProjectTokens;
    }

    /**
     * Gets product alerts
     *
     * @param productToken    Product Token
     * @param enabledProjects
     * @param projectVitalMap Product Vitals Map
     * @param serverSettings  Whitesource Server Setting
     * @return Map or Project Token and Corresponding Library Policy Result
     */
    @Override
    public Map getProductAlerts(String productToken, Set enabledProjects, Map projectVitalMap, WhiteSourceServerSettings serverSettings) {
        Map libraryPolicyResultMap = new HashMap<>();
        try {
            JSONObject jsonObject = makeRestCall(Constants.RequestType.getProductAlerts, null, productToken, null, null, null, serverSettings);
            JSONArray alerts = (JSONArray) Objects.requireNonNull(jsonObject).get(Constants.ALERTS);
            if (!CollectionUtils.isEmpty(alerts)) {
                libraryPolicyResultMap = transformProductAlerts(alerts, enabledProjects, projectVitalMap, serverSettings);
            }
            //TODO: Refactor Exception Handling
        } catch (Exception e) {
            LOG.info("Exception occurred while calling getProductAlerts for productToken =" + productToken ,e);
            return libraryPolicyResultMap;
        }

        // Now create skeleton library policy results for projects that didn't show up in the above response. These projects
        // does not have scan results for whatever reasons
        Map finalLibraryPolicyResultMap = libraryPolicyResultMap;
        Map emptyLibraryPolicyMap = enabledProjects.stream()
                .filter(p-> productToken.equals(p.getProductToken()))
                .filter(p -> !finalLibraryPolicyResultMap.containsKey(p.getProjectToken()))
                .filter(p -> Objects.nonNull(projectVitalMap.get(p.getProjectToken())))
                .collect(Collectors.toMap(WhiteSourceComponent::getProjectToken, p -> getEmptyProjectAlert(p, projectVitalMap.get(p.getProjectToken()), serverSettings), (a, b) -> b));
        libraryPolicyResultMap.putAll(emptyLibraryPolicyMap);
        return libraryPolicyResultMap;
    }


    /**
     * Get an empty library policy object
     * @param project whitesource project
     * @param projectVital project vital
     * @param serverSettings whitesource server setting
     * @return Library Policy object
     */
    public static LibraryPolicyResult getEmptyProjectAlert(WhiteSourceComponent project, WhiteSourceProjectVital projectVital, WhiteSourceServerSettings serverSettings) {
        LibraryPolicyResult libraryPolicyResult = new LibraryPolicyResult();
        setEvaluationTimeStampAndReportUrl(libraryPolicyResult, projectVital, serverSettings);
        libraryPolicyResult.setCollectorItemId(project.getId());
        return libraryPolicyResult;
    }

    /**
     * Gets Project Alerts
     *
     * @param project Whitesource Component
     * @param projectVital         Project Vitals Map
     * @param serverSettings       Whitesource Server Setting
     * @return Library Policy Result
     */

    @Override
    public LibraryPolicyResult getProjectAlerts(WhiteSourceComponent project, WhiteSourceProjectVital projectVital, WhiteSourceServerSettings serverSettings, Boolean[] failed) {
        LibraryPolicyResult libraryPolicyResult = new LibraryPolicyResult();
        try {
            JSONObject jsonObject = makeRestCall(Constants.RequestType.getProjectAlerts, null, null, project.getProjectToken(), null, null, serverSettings);
            JSONArray alerts = (JSONArray) Objects.requireNonNull(jsonObject).get(Constants.ALERTS);
            if (projectVital == null) {
                JSONObject projectVitalsObject = makeRestCall(Constants.RequestType.getProjectVitals, null, null, project.getProjectToken(), null, null, serverSettings);
                setEvaluationTimeStampAndReportUrl(libraryPolicyResult, projectVitalsObject, serverSettings);
            } else {
                setEvaluationTimeStampAndReportUrl(libraryPolicyResult, projectVital, serverSettings);
            }
            libraryPolicyResult.setCollectorItemId(project.getId());
            if (!CollectionUtils.isEmpty(alerts)) {
                transformAlerts(libraryPolicyResult, alerts);
            }
        } catch (Exception e) {
            LOG.info("Exception occurred while calling getProjectAlerts for projectName=" + project.getProjectName() , e);
            if(Objects.nonNull(failed)){failed[0] = true;}
        }
        return libraryPolicyResult;
    }


    /**
     * Gets Orgnization Change Request Log
     *
     * @param whitesourceOrg   Whitesource Org
     * @param historyTimestamp Start time
     * @param serverSettings   Whitesource Server Settings
     * @return List of Whitesource Change Request
     */
    @Override
    public List getChangeRequestLog(WhitesourceOrg whitesourceOrg, long historyTimestamp, WhiteSourceServerSettings serverSettings) throws HygieiaException {
        long timeGetChangeRequestLog = System.currentTimeMillis();
        String startDateT = DateTimeUtils.timeFromLongToString(historyTimestamp, serverSettings.getTimeZone(), yyyy_MM_dd_HH_mm_ss);
        JSONObject jsonObject = makeRestCall(Constants.RequestType.getChangesReport, whitesourceOrg, null, null, null, startDateT, serverSettings);
        JSONArray changes = (JSONArray) Objects.requireNonNull(jsonObject).get(Constants.CHANGES);
        List changeRequests = new ArrayList<>();
        if (CollectionUtils.isEmpty(changes)) {
            return changeRequests;
        } else {
            for (Object c : changes) {
                JSONObject change = (JSONObject) c;
                WhiteSourceChangeRequest whiteSourceChangeRequest = new WhiteSourceChangeRequest();
                whiteSourceChangeRequest.setScope(getStringValue(change, Constants.SCOPE));
                whiteSourceChangeRequest.setStartDateTime(DateTimeUtils.timeFromStringToMillis(getStringValue(change, Constants.START_DATE_TIME), serverSettings.getTimeZone(), yyyy_MM_dd_HH_mm_ss));
                whiteSourceChangeRequest.setChangeAspect(getStringValue(change, Constants.CHANGE_ASPECT));
                whiteSourceChangeRequest.setChangeCategory(getStringValue(change, Constants.CHANGE_CATEGORY));
                whiteSourceChangeRequest.setChangeClass(getStringValue(change, Constants.CHANGE_CLASS));
                whiteSourceChangeRequest.setOrgName(getStringValue(change, Constants.ORG_NAME));
                whiteSourceChangeRequest.setProductName(getStringValue(change, Constants.PRODUCT_NAME));
                whiteSourceChangeRequest.setProjectName(getStringValue(change, Constants.PROJECT_NAME));
                whiteSourceChangeRequest.setScopeName(getStringValue(change, Constants.SCOPE_NAME));
                whiteSourceChangeRequest.setChangeScopeId(getLongValue(change, Constants.CHANGE_SCOPE_ID));
                whiteSourceChangeRequest.setOperator(getStringValue(change, Constants.OPERATOR));
                whiteSourceChangeRequest.setUserEmail(getStringValue(change, Constants.USER_EMAIL));
                whiteSourceChangeRequest.setBeforeChange(getStringList(change, Constants.BEFORE_CHANGE));
                whiteSourceChangeRequest.setAfterChange(getStringList(change, Constants.AFTER_CHANGE));
                changeRequests.add(whiteSourceChangeRequest);
            }
        }
        timeGetChangeRequestLog = System.currentTimeMillis() - timeGetChangeRequestLog;
        LOG.info("WhitesourceCilent: Time to get all change request logs: " + timeGetChangeRequestLog);
        return changeRequests;
    }

    /**
     * Gets org level project vitals
     *
     * @param whitesourceOrg Whitesource Org
     * @param serverSettings Whitesource Server Setting
     * @return Map of project token and project vital
     */
    @Override
    public Map getOrgProjectVitals(WhitesourceOrg whitesourceOrg, WhiteSourceServerSettings serverSettings) throws HygieiaException {
        long timeGetProjectVitals = System.currentTimeMillis();
        Map projectVitalMap = new HashMap<>();
        JSONObject jsonObject = makeRestCall(Constants.RequestType.getOrganizationProjectVitals, whitesourceOrg, null, null, null, null, serverSettings);
        JSONArray vitals = (JSONArray) Objects.requireNonNull(jsonObject).get(Constants.PROJECT_VITALS);
        for (Object v : vitals) {
            JSONObject vital = (JSONObject) v;
            WhiteSourceProjectVital whiteSourceProjectVital = new WhiteSourceProjectVital();
            whiteSourceProjectVital.setName(getStringValue(vital, Constants.NAME));
            whiteSourceProjectVital.setId(getLongValue(vital, Constants.ID));
            String token = getStringValue(vital, Constants.TOKEN);
            whiteSourceProjectVital.setToken(token);
            whiteSourceProjectVital.setLastUpdateDate(DateTimeUtils.timeFromStringToMillis(getStringValue(vital, Constants.LAST_UPDATED_DATE), serverSettings.getTimeZone(), yyyy_MM_dd_HH_mm_ss_z));
            whiteSourceProjectVital.setCreationDate(DateTimeUtils.timeFromStringToMillis(getStringValue(vital, Constants.CREATIONDATE), serverSettings.getTimeZone(), yyyy_MM_dd_HH_mm_ss_z));
            projectVitalMap.put(token, whiteSourceProjectVital);
        }
        timeGetProjectVitals = System.currentTimeMillis() - timeGetProjectVitals;
        LOG.info("WhitesourceClient: Time to get all project vitals: " + timeGetProjectVitals);
        return projectVitalMap;
    }


    @Override
    public HashMap refresh (String projectToken, String altIdentifier){
        List components = getWhiteSourceComponents(projectToken, altIdentifier);

        HashMap resultsMap = new HashMap<>();
        resultsMap.put("passed",0);
        resultsMap.put("failed",0);

        for (WhiteSourceComponent component: components){
            Boolean[] failed = {false};
            LibraryPolicyResult libraryPolicyResult = getProjectAlerts(component, null, whiteSourceSettings.getWhiteSourceServerSettings().get(0), failed);
            if (Objects.nonNull(libraryPolicyResult)) {
                libraryPolicyResult.setCollectorItemId(component.getId());
                LibraryPolicyResult libraryPolicyResultExisting = getLibraryPolicyData(component, libraryPolicyResult);
                if (Objects.nonNull(libraryPolicyResultExisting)) {
                    libraryPolicyResult.setId(libraryPolicyResultExisting.getId());
                }
                libraryPolicyResultsRepository.save(libraryPolicyResult);
            }
            if(failed[0]){resultsMap.put("failed",resultsMap.get("failed")+1);}
            else{resultsMap.put("passed",resultsMap.get("passed")+1);}
        }
        return resultsMap;
    }

    ////////////////////////////////////////////       Helper and private methods below /////////////////////////////////////////


    /**
     * Transforms all product alerts into corresponding Library Policy Results and returns in a Map of Project Token
     * and Library Policy Result
     *
     * @param alerts project alerts
     * @param enabledProjects
     * @param projectVitalMap project vital map
     * @param serverSettings whitesource server setting
     * @return Map of project token and LibraryPolicyResult
     */
    private Map transformProductAlerts(JSONArray alerts, Set enabledProjects, Map projectVitalMap, WhiteSourceServerSettings serverSettings) throws HygieiaException {
        Map libraryPolicyResultMap = new HashMap<>();
        for (Object alert : alerts) {
            String projectToken = getStringValue((JSONObject) alert, Constants.PROJECT_TOKEN);
            WhiteSourceComponent project = enabledProjects.stream().filter(p->p.getProjectToken().equals(projectToken)).findFirst().orElse(null);

            LibraryPolicyResult libraryPolicyResult = libraryPolicyResultMap.get(projectToken);
            if (libraryPolicyResult == null) {
                libraryPolicyResult = new LibraryPolicyResult();
            }
            if (project != null) {
                libraryPolicyResult.setCollectorItemId(project.getCollectorId());
            }
            translateAlertJSON((JSONObject) alert, libraryPolicyResult);
            WhiteSourceProjectVital projectVital = projectVitalMap.get(projectToken);
            if (projectVital == null) {
                try {
                    JSONObject projectVitalsObject = makeRestCall(Constants.RequestType.getProjectVitals, null, null, projectToken, null, null, serverSettings);
                    setEvaluationTimeStampAndReportUrl(libraryPolicyResult, projectVitalsObject, serverSettings);
                } catch (Exception e) {
                    LOG.error("Exception occurred while fetching ProjectVitals for projectToken : "+ projectToken);
                }
            } else {
                setEvaluationTimeStampAndReportUrl(libraryPolicyResult, projectVital, serverSettings);
            }
            libraryPolicyResultMap.put(projectToken, libraryPolicyResult);
        }
        return libraryPolicyResultMap;
    }


    /**
     * Transforms alerts into Library Policy Fields
     *
     * @param libraryPolicyResult Library Policy Result that needs to be enriched with alerts data
     * @param alerts              project alerts
     */
    private void transformAlerts(LibraryPolicyResult libraryPolicyResult, JSONArray alerts) {
        for (Object alert : alerts) {
            translateAlertJSON((JSONObject) alert, libraryPolicyResult);
        }
    }


    /**
     * Helper method to Translates alert json object
     *
     * @param alert               JSONObject of project alert
     * @param libraryPolicyResult Library Policy Result that need to be enriched
     */
    private void translateAlertJSON(JSONObject alert, LibraryPolicyResult libraryPolicyResult) {
        String alertType = getStringValue(alert, Constants.TYPE);
        JSONObject library = (JSONObject) Objects.requireNonNull(alert).get(Constants.LIBRARY);
        long creationDateTimeStamp = getLongValue(alert, Constants.TIME);
        String description = (StringUtils.isNotEmpty(getStringValue(alert, Constants.DESCRIPTION))) ? getStringValue(alert, Constants.DESCRIPTION) : "None";
        String componentName = getStringValue(library, Constants.FILENAME);
        // add threat for license
        setAllLibraryLicensesAlerts(libraryPolicyResult, componentName, String.valueOf(DateTimeUtils.getDays(creationDateTimeStamp)), getLicenseThreatLevel(alertType, description), description);
        // add threat for Security vulns
        JSONObject vulns = (JSONObject) Objects.requireNonNull(alert).get(Constants.VULNERABILITY);
        if (Objects.nonNull(vulns) && !vulns.isEmpty()) {
            setSecurityVulns(vulns, libraryPolicyResult, componentName, String.valueOf(DateTimeUtils.getDays(creationDateTimeStamp)), description);
            setCVSS3SecurityVulns(vulns, libraryPolicyResult, componentName, String.valueOf(DateTimeUtils.getDays(creationDateTimeStamp)), description);
        }
        libraryPolicyResult.setTimestamp(System.currentTimeMillis());
    }

    /**
     * Generic helper method to execute rest call
     *
     * @param requestType    Request Type
     * @param whitesourceOrg Whitesource Org
     * @param productToken   product token
     * @param projectToken   project token
     * @param alertType      alert type
     * @param startDateTime  start date time
     * @param serverSettings server settings
     * @return JSON Object
     */
    private JSONObject makeRestCall(Constants.RequestType requestType, WhitesourceOrg whitesourceOrg, String productToken,
                                    String projectToken, String alertType, String startDateTime,
                                    WhiteSourceServerSettings serverSettings) {
        String orgToken =  whitesourceOrg != null ? whitesourceOrg.getToken() : null;
        JSONObject requestJSON = getRequest(requestType, orgToken, productToken, projectToken, startDateTime, serverSettings, alertType);
        JSONParser parser = new JSONParser();
        try {
            ResponseEntity response = restClient.makeRestCallPost(getApiBaseUrl(serverSettings.getInstanceUrl()), new HttpHeaders(), requestJSON);
            if ((response == null) || (response.toString().isEmpty())) return new JSONObject();
            return (JSONObject) parser.parse(response.getBody());
        } catch (ParseException e) {
            LOG.error("Exception occurred while parsing json object", e);
        }
        return new JSONObject();
    }


    // Gets project evaluation time stamp
    public static void setEvaluationTimeStampAndReportUrl(LibraryPolicyResult libraryPolicyResult, JSONObject projectVitalsObject, WhiteSourceServerSettings serverSettings) throws HygieiaException {
        JSONArray projectVitals = (JSONArray) Objects.requireNonNull(projectVitalsObject).get(Constants.PROJECT_VITALS);
        if (!CollectionUtils.isEmpty(projectVitals)) {
            //There is just 1 of them!
            for (Object projectVital : projectVitals) {
                JSONObject projectVitalObject = (JSONObject) projectVital;
                libraryPolicyResult.setEvaluationTimestamp(DateTimeUtils.timeFromStringToMillis(getStringValue(projectVitalObject, Constants.LAST_UPDATED_DATE), serverSettings.getTimeZone(), yyyy_MM_dd_HH_mm_ss_z));
                Long projectId = getLongValue(projectVitalObject, Constants.ID);
                libraryPolicyResult.setReportUrl(String.format(serverSettings.getDeeplink(), projectId));
            }
        }
    }

    // Gets evaluation time stamp
    public static void setEvaluationTimeStampAndReportUrl(LibraryPolicyResult libraryPolicyResult, WhiteSourceProjectVital projectVital, WhiteSourceServerSettings serverSettings) {
        if (projectVital == null) return;
        libraryPolicyResult.setEvaluationTimestamp(projectVital.getLastUpdateDate());
        Long projectId = projectVital.getId();
        libraryPolicyResult.setReportUrl(String.format(serverSettings.getDeeplink(), projectId));
    }


    // Decodes json payload
    private static Object decodeJsonPayload(String payload) throws HygieiaException {
        if (payload == null || StringUtils.isEmpty(payload)) {
            throw new HygieiaException("WhiteSource request is not a valid json.", HygieiaException.JSON_FORMAT_ERROR);
        }
        byte[] decodedBytes = Base64.getDecoder().decode(payload);
        String decodedPayload = new String(decodedBytes);
        JSONParser parser = new JSONParser();
        Object obj = null;
        try {
            obj = parser.parse(decodedPayload);

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return obj;
    }


    // Get library Policy Data from repository
    public LibraryPolicyResult getLibraryPolicyData(CollectorItem component, LibraryPolicyResult libraryPolicyResult) {
        return libraryPolicyResultsRepository.findByCollectorItemIdAndEvaluationTimestamp(
                component.getId(), libraryPolicyResult.getEvaluationTimestamp());
    }

    public String process(WhiteSourceRequest whiteSourceRequest) throws HygieiaException {
        long startTime = System.currentTimeMillis();
        JSONObject projectVital = (JSONObject) decodeJsonPayload(whiteSourceRequest.getProjectVitals());
        JSONArray alerts = (JSONArray) decodeJsonPayload(whiteSourceRequest.getAlerts());
        String orgName = whiteSourceRequest.getOrgName();
        String clientReference = whiteSourceRequest.getClientReference();
        if (projectVital == null) {
            throw new HygieiaException("WhiteSource request : Project Vital is null for project with correlation_id="+clientReference, HygieiaException.BAD_DATA);
        }
        String token = (String) projectVital.get("token");

        String lastUpdatedDate = (String) projectVital.get("lastUpdatedDate");
        long timestamp = DateTimeUtils.timeFromStringToMillis(lastUpdatedDate, DEFAULT_WHITESOURCE_TIMEZONE, yyyy_MM_dd_HH_mm_ss_z);
        List collectorItems = whiteSourceComponentRepository.findByProjectToken(token);
        if (CollectionUtils.isEmpty(collectorItems)) {
            throw new HygieiaException(String.format("WhiteSource request : Invalid Whitesource project projectToken=%s correlation_id=%s", token, clientReference), HygieiaException.BAD_DATA);
        }

        // add reportUrls to the result
        Long projectId = (Long) projectVital.get("id");
        List reportURLs = whiteSourceSettings.getWhiteSourceServerSettings().stream().map(s -> String.format(s.getDeeplink(), projectId)).collect(Collectors.toList());
        String reportUrl = String.join(",", reportURLs);

        List lpIds = new ArrayList<>();
        for (CollectorItem collectorItem: collectorItems) {
            LOG.info("WhiteSourceRequest collecting  analysis for projectToken=" + token + " timestamp=" + timestamp + " correlation_id=" + clientReference);
            if (collectorItem == null) continue;
            LibraryPolicyResult libraryPolicyResult = new LibraryPolicyResult();
            libraryPolicyResult.setEvaluationTimestamp(timestamp);
            LibraryPolicyResult existingLp = getLibraryPolicyData(collectorItem, libraryPolicyResult);
            if (existingLp != null) {
                // vulnerability dis-positioning not updating the evaluation timestamp in the latest report. so, the sent report takes precedence over existing
                libraryPolicyResult.setId(existingLp.getId());
                String lpIdStr = libraryPolicyResult.getId().toString();
                LOG.info(String.format("process(): existing library_policy is updated with the pushed library_policy_id=%s evaluationTimeStamp=%d correlation_id=%s",
                        lpIdStr, libraryPolicyResult.getEvaluationTimestamp(), clientReference));
            }
            transformAlerts(libraryPolicyResult, alerts);
            libraryPolicyResult.setReportUrl(reportUrl);
            libraryPolicyResult.setCollectorItemId(collectorItem.getId());
            libraryPolicyResult.setBuildUrl(whiteSourceRequest.getBuildUrl());
            //associate build to LibraryPolicyResult
            associateBuildToLibraryPolicy(whiteSourceRequest.getBuildUrl(), whiteSourceRequest.getClientReference(), libraryPolicyResult);
            libraryPolicyResult.setClientReference(whiteSourceRequest.getClientReference());
            libraryPolicyResult = libraryPolicyResultsRepository.save(libraryPolicyResult);
            if (!collectorItem.isEnabled()) {
                collectorItem.setEnabled(true);
                collectorItemRepository.save(collectorItem);
            }
            LOG.info(String.format("process(): successfully updated library_policy_result=%s  for correlation_id=%s ", libraryPolicyResult.getId(), clientReference));
            lpIds.add(libraryPolicyResult.getId().toString());
        }
        String changedIds = String.join(",", lpIds);
        LOG.info(String.format("process(): completed updating library_policy_results=[%s] for correlation_id=%s duration=%d", changedIds, clientReference, (System.currentTimeMillis() - startTime) / 1000));
        return String.format("Successfully updated library policy result : [%s] for correlation_id=%s ", changedIds, clientReference);
    }

    private void associateBuildToLibraryPolicy(String buildUrl, String clientReference, LibraryPolicyResult libraryPolicyResult){
        if(Objects.isNull(buildUrl)) return;
            Build build = buildRepository.findByBuildUrl(buildUrl);
            if(Objects.nonNull(build)){
                build.setClientReference(clientReference);
                libraryPolicyResult.setBuildId(build.getId());
                buildRepository.save(build);
            }else{
                Build baseBuild = new Build();
                baseBuild.setBuildUrl(buildUrl);
                String trailedBuildUrl = HygieiaUtils.normalizeJobUrl(buildUrl);
                Collector collector = collectorRepository.findByName(whiteSourceSettings.getBuildCollectorName());
                CollectorItem buildCollectorItem = collectorItemRepository.findByJobUrl(collector.getId(), trailedBuildUrl);
                if(Objects.nonNull(buildCollectorItem)){
                    baseBuild.setCollectorItemId(buildCollectorItem.getId());
                }
                baseBuild.setBuildStatus(BuildStatus.InProgress);
                baseBuild.setClientReference(clientReference);
                baseBuild = buildRepository.save(baseBuild);
                libraryPolicyResult.setBuildId(baseBuild.getId());
            }
    }


    public List getWhiteSourceComponents(String projectToken, String altIdentifier) {
        Collector collector = collectorRepository.findByName(Constants.WHITE_SOURCE);
        Iterable collectorItems = new ArrayList<>();

        if (StringUtils.isNotEmpty(projectToken)) {
            collectorItems = whiteSourceComponentRepository.findByCollectorIdAndProjectToken(collector.getId(), projectToken);
        }
        else if (StringUtils.isNotEmpty(altIdentifier)) {
            collectorItems = whiteSourceComponentRepository.findByCollectorIdAndAltIdentifier(collector.getId(), altIdentifier);
        }


        List whiteSourceComponents = new ArrayList<>();
        for (CollectorItem collectorItem : collectorItems) {
            whiteSourceComponents.add(buildWhiteSourceComponent(collectorItem));
        }
        return whiteSourceComponents;
    }

    private static WhiteSourceComponent buildWhiteSourceComponent(CollectorItem collectorItem) {
        WhiteSourceComponent whiteSourceComponent = new WhiteSourceComponent();
        whiteSourceComponent.setOrgName((String) collectorItem.getOptions().get(Constants.ORG_NAME));
        whiteSourceComponent.setProductName((String) collectorItem.getOptions().get(Constants.PRODUCT_NAME));
        whiteSourceComponent.setProjectName((String) collectorItem.getOptions().get(Constants.PROJECT_NAME));
        whiteSourceComponent.setProjectToken((String) collectorItem.getOptions().get(Constants.PROJECT_TOKEN));
        whiteSourceComponent.setProductToken((String) collectorItem.getOptions().get(Constants.PRODUCT_TOKEN));
        whiteSourceComponent.setId(collectorItem.getId());
        whiteSourceComponent.setCollectorId(collectorItem.getCollectorId());
        whiteSourceComponent.setEnabled(collectorItem.isEnabled());
        whiteSourceComponent.setLastUpdated(collectorItem.getLastUpdated());
        return whiteSourceComponent;
    }


    private static void setAllLibraryLicensesAlerts(LibraryPolicyResult libraryPolicyResult, String componentName, String age, LibraryPolicyThreatLevel severity, String policyName) {
        libraryPolicyResult.addThreat(LibraryPolicyType.License, severity, LibraryPolicyThreatDisposition.Open, Constants.OPEN, componentName, age, Constants.ZERO, policyName);
    }

    private static void setSecurityVulns(JSONObject vuln, LibraryPolicyResult libraryPolicyResult, String componentName, String age, String policyName) {
        libraryPolicyResult.addThreat(LibraryPolicyType.Security, LibraryPolicyThreatLevel.fromString(getSecurityVulnSeverity(vuln)), LibraryPolicyThreatDisposition.Open, Constants.OPEN, componentName, age, getScore(vuln), policyName);
    }

    private static void setCVSS3SecurityVulns(JSONObject vuln, LibraryPolicyResult libraryPolicyResult, String componentName, String age, String policyName) {
        libraryPolicyResult.addThreat(LibraryPolicyType.Security_cvss3, LibraryPolicyThreatLevel.fromString(getCVSS3SecurityVulnSeverity(vuln)), LibraryPolicyThreatDisposition.Open, Constants.OPEN, componentName, age, getCVSS3Score(vuln), policyName);
    }

    private LibraryPolicyThreatLevel getLicenseThreatLevel(String alertType, String description) {
        if (!CollectionUtils.isEmpty(whiteSourceSettings.getCriticalLicensePolicyTypes()) && getLicenseSeverity(whiteSourceSettings.getCriticalLicensePolicyTypes(), alertType, description)) {
            return LibraryPolicyThreatLevel.Critical;
        }
        if (!CollectionUtils.isEmpty(whiteSourceSettings.getHighLicensePolicyTypes()) && getLicenseSeverity(whiteSourceSettings.getHighLicensePolicyTypes(), alertType, description)) {
            return LibraryPolicyThreatLevel.High;
        }
        if (!CollectionUtils.isEmpty(whiteSourceSettings.getMediumLicensePolicyTypes()) && getLicenseSeverity(whiteSourceSettings.getMediumLicensePolicyTypes(), alertType, description)) {
            return LibraryPolicyThreatLevel.Medium;
        }
        if (!CollectionUtils.isEmpty(whiteSourceSettings.getLowLicensePolicyTypes()) && getLicenseSeverity(whiteSourceSettings.getLowLicensePolicyTypes(), alertType, description)) {
            return LibraryPolicyThreatLevel.Low;
        }
        return LibraryPolicyThreatLevel.None;
    }

    private static boolean getLicenseSeverity(List licensePolicyTypes, String alertType, String description) {
        return licensePolicyTypes.stream().anyMatch(licensePolicyType -> licensePolicyType.getPolicyName().equalsIgnoreCase(alertType)
                && licensePolicyType.getDescriptions().stream().anyMatch(description::contains));
    }

    /**
     * Gets api base url
     * @param instanceUrl whitesource instance url
     * @return string base url
     */
    private static String getApiBaseUrl(String instanceUrl) {
        return instanceUrl + API_URL;
    }

    /**
     * Builds rest request body
     * @param requestType post request type
     * @param orgToken org token
     * @param productToken product token
     * @param projectToken project token
     * @param startDateTime start date time
     * @param serverSettings server setting
     * @param alertType alert type
     * @return json body that can be posted
     */
    private static JSONObject getRequest(Constants.RequestType requestType, String orgToken, String productToken, String projectToken, String startDateTime, WhiteSourceServerSettings serverSettings, String alertType) {
        JSONObject requestJSON = new JSONObject();
        requestJSON.put(Constants.REQUEST_TYPE, requestType.toString());
        requestJSON.put(Constants.USER_KEY, serverSettings.getUserKey());
        switch (requestType) {
            case getProjectInventory:
            case getProjectAlerts:
            case getProjectVitals:
                requestJSON.put(Constants.PROJECT_TOKEN, projectToken);
                return requestJSON;
            case getAllProjects:
            case getProductAlerts:
                requestJSON.put(Constants.PRODUCT_TOKEN, productToken);
                return requestJSON;
            case getAllProducts:
            case getOrganizationDetails:
            case getOrganizationProjectVitals:
                requestJSON.put(Constants.ORG_TOKEN, orgToken);
                return requestJSON;
            case getChangesReport:
                requestJSON.put(Constants.START_DATE_TIME, startDateTime);
                requestJSON.put(Constants.ORG_TOKEN, orgToken);
//                requestJSON.put(Constants.SCOPE, Constants.PROJECT); This does not do anything
                return requestJSON;
            case getOrganizationAlertsByType:
                requestJSON.put(Constants.ORG_TOKEN, orgToken);
                requestJSON.put(Constants.ALERT_TYPE, alertType);
                requestJSON.put(Constants.FROM_DATE, startDateTime);
                return requestJSON;
            default:
                return requestJSON;
        }
    }

    // Helper methods to extract Json elements ///

    private static String getStringValue(JSONObject jsonObject, String key) {
        return Objects.toString(jsonObject.get(key), "");
    }

    private static List getStringList(JSONObject jsonObject, String key) {
        if ((jsonObject == null || jsonObject.get(key) == null)) {
            return Collections.emptyList();
        }
        JSONArray jsonArray = (JSONArray) jsonObject.get(key);
        Stream stringStream = jsonArray.stream();
        return stringStream.collect(Collectors.toList());
    }

    private static Long getLongValue(JSONObject jsonObject, String key) {
        Object obj = jsonObject.get(key);
        return obj == null ? 0L : (Long) obj;
    }

    private static Double toDouble(JSONObject jsonObject, String key) {
        Object obj = jsonObject.get(key);
        return obj == null ? 0 : (Double) obj;
    }

    private static String getScore(JSONObject vuln) {
        Double score = toDouble(vuln, Constants.SCORE1);
        return score.toString();
    }

    private static String getSecurityVulnSeverity(JSONObject vuln) {
        String severity = getStringValue(vuln, Constants.SEVERITY);
        if (Objects.nonNull(severity)) return severity;
        return Constants.NONE;
    }


    private static String getCVSS3Score(JSONObject vuln) {
        Double cvss3Score = toDouble(vuln, Constants.CVSS_3_SCORE);
        return cvss3Score.toString();
    }

    private static String getCVSS3SecurityVulnSeverity(JSONObject vuln) {
        String cvss3_severity = getStringValue(vuln, Constants.CVSS_3_SEVERITY);
        if (Objects.nonNull(cvss3_severity)) return cvss3_severity;
        return Constants.NONE;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy