org.owasp.dependencycheck.analyzer.NspAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for org.owasp.dependencycheck.analyzer.NspAnalyzer.java

Source

/*
 * This file is part of dependency-check-core.
 *
 * 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.
 *
 * Copyright (c) 2017 Steve Springett. All Rights Reserved.
 */
package org.owasp.dependencycheck.analyzer;

import org.apache.commons.io.FileUtils;
import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nsp.Advisory;
import org.owasp.dependencycheck.data.nsp.NspSearch;
import org.owasp.dependencycheck.data.nsp.SanitizePackage;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
import org.owasp.dependencycheck.utils.FileFilterBuilder;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
import javax.json.Json;
import javax.json.JsonException;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.URLConnectionFailureException;

/**
 * Used to analyze Node Package Manager (npm) package.json files via Node
 * Security Platform (nsp).
 *
 * @author Steve Springett
 */
@ThreadSafe
public class NspAnalyzer extends AbstractNpmAnalyzer {

    /**
     * The logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(NspAnalyzer.class);

    /**
     * The default URL to the NSP check API.
     */
    public static final String DEFAULT_URL = "https://api.nodesecurity.io/check";
    /**
     * A descriptor for the type of dependencies processed or added by this
     * analyzer.
     */
    public static final String DEPENDENCY_ECOSYSTEM = NPM_DEPENDENCY_ECOSYSTEM;
    /**
     * The file name to scan.
     */
    private static final String PACKAGE_JSON = "package.json";

    /**
     * Filter that detects files named "package.json".
     */
    private static final FileFilter PACKAGE_JSON_FILTER = FileFilterBuilder.newInstance().addFilenames(PACKAGE_JSON)
            .build();

    /**
     * The NSP Searcher.
     */
    private NspSearch searcher;

    /**
     * Returns the FileFilter
     *
     * @return the FileFilter
     */
    @Override
    protected FileFilter getFileFilter() {
        return PACKAGE_JSON_FILTER;
    }

    /**
     * Initializes the analyzer once before any analysis is performed.
     *
     * @param engine a reference to the dependency-check engine
     * @throws InitializationException if there's an error during initialization
     */
    @Override
    public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException {
        LOGGER.debug("Initializing {}", getName());
        try {
            searcher = new NspSearch(getSettings());
        } catch (MalformedURLException ex) {
            setEnabled(false);
            throw new InitializationException("The configured URL to Node Security Platform is malformed", ex);
        }
        try {
            final Settings settings = engine.getSettings();
            final boolean nodeEnabled = settings.getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED);
            if (!nodeEnabled) {
                LOGGER.warn("The Node Package Analyzer has been disabled; the resulting report will only "
                        + " contain the known vulnerable dependency - not a bill of materials for the node project.");
            }
        } catch (InvalidSettingException ex) {
            throw new InitializationException("Unable to read configuration settings", ex);
        }
    }

    /**
     * Returns the name of the analyzer.
     *
     * @return the name of the analyzer.
     */
    @Override
    public String getName() {
        return "Node Security Platform Analyzer";
    }

    /**
     * Returns the phase that the analyzer is intended to run in.
     *
     * @return the phase that the analyzer is intended to run in.
     */
    @Override
    public AnalysisPhase getAnalysisPhase() {
        return AnalysisPhase.FINDING_ANALYSIS;
    }

    /**
     * Returns the key used in the properties file to determine if the analyzer
     * is enabled.
     *
     * @return the enabled property setting key for the analyzer
     */
    @Override
    protected String getAnalyzerEnabledSettingKey() {
        return Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED;
    }

    @Override
    protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
        engine.removeDependency(dependency);
        final File file = dependency.getActualFile();
        if (!file.isFile() || file.length() == 0 || !shouldProcess(file)) {
            return;
        }

        try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) {

            // Retrieves the contents of package.json from the Dependency
            final JsonObject packageJson = jsonReader.readObject();

            // Create a sanitized version of the package.json
            final JsonObject sanitizedJson = SanitizePackage.sanitize(packageJson);

            // Create a new 'package' object that acts as a container for the sanitized package.json
            final JsonObjectBuilder builder = Json.createObjectBuilder();
            final JsonObject nspPayload = builder.add("package", sanitizedJson).build();

            // Submits the package payload to the nsp check service
            final List<Advisory> advisories = searcher.submitPackage(nspPayload);

            for (Advisory advisory : advisories) {
                /*
                 * Create a new vulnerability out of the advisory returned by nsp.
                 */
                final Vulnerability vuln = new Vulnerability();
                vuln.setCvssScore(advisory.getCvssScore());
                vuln.setDescription(advisory.getOverview());
                vuln.setName(String.valueOf(advisory.getId()));
                vuln.setSource(Vulnerability.Source.NSP);
                vuln.addReference("NSP", "Advisory " + advisory.getId() + ": " + advisory.getTitle(),
                        advisory.getAdvisory());

                /*
                 * Create a single vulnerable software object - these do not use CPEs unlike the NVD.
                 */
                final VulnerableSoftware vs = new VulnerableSoftware();
                //TODO consider changing this to available versions on the dependency
                //  - the update is a part of the version, not versions to update to
                //vs.setUpdate(advisory.getPatchedVersions());

                vs.setName(advisory.getModule() + ":" + advisory.getVulnerableVersions());
                vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs)));

                final Dependency existing = findDependency(engine, advisory.getModule(), advisory.getVersion());
                if (existing == null) {
                    final Dependency nodeModule = createDependency(dependency, advisory.getModule(),
                            advisory.getVersion(), "transitive");
                    nodeModule.addVulnerability(vuln);
                    engine.addDependency(nodeModule);
                } else {
                    existing.addVulnerability(vuln);
                }
            }
        } catch (URLConnectionFailureException e) {
            this.setEnabled(false);
            throw new AnalysisException(e.getMessage(), e);
        } catch (IOException e) {
            LOGGER.debug("Error reading dependency or connecting to Node Security Platform - check API", e);
            this.setEnabled(false);
            throw new AnalysisException(e.getMessage(), e);
        } catch (JsonException e) {
            throw new AnalysisException(String.format("Failed to parse %s file.", file.getPath()), e);
        }
    }
}