From 2f365e7c7a2f64d6067fe0418cc950ffd38a4548 Mon Sep 17 00:00:00 2001 From: sertic Date: Tue, 16 Feb 2016 18:13:49 +0100 Subject: [PATCH] Implemented #8 Add support for OWASP dependency check analysis --- README.md | 22 +- pom.xml | 5 + .../mavensonarsputnik/MavenEnvironment.java | 122 +++++++ .../MavenSonarSputnikMojo.java | 59 +--- .../mavensonarsputnik/SonarExecutor.java | 8 - .../SonarExecutorHelper.java | 21 -- .../touk/sputnik/engine/ProcessorBuilder.java | 4 + .../owasp/OWASPDependencyCheckProcessor.java | 323 ++++++++++++++++++ .../processor/pitest/PITestProcessor.java | 14 + .../processor/sonar/SonarProcessor.java | 47 ++- .../default-owaspdependencycheck.properties | 4 + src/main/resources/default-sputnik.properties | 2 + .../OWASPDependencyCheckProcessorTest.java | 17 + 13 files changed, 562 insertions(+), 86 deletions(-) create mode 100644 src/main/java/de/mirkosertic/mavensonarsputnik/MavenEnvironment.java delete mode 100644 src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutor.java delete mode 100644 src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutorHelper.java create mode 100644 src/main/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessor.java create mode 100644 src/main/resources/default-owaspdependencycheck.properties create mode 100644 src/test/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessorTest.java diff --git a/README.md b/README.md index 2187d93..9f15b74 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,22 @@ sonar.host.url= ## Advanced Reporting +### Mutation Testing + +This plugin can integrate Mutation Testing results based on PITest in the review. To enable this, +PITest must be executed as part of the Maven build. + +Additional goals and configuration: + +``` +mvn org.pitest:pitest-maven:1.1.9:scmMutationCoverage -DanalyseLastCommit=true +``` + +### OWASP Dependency Checks + +This plugin also runs a OWASP Dependency Check in case of any changes at the Maven project configuration, hence if a pom.xml is +part of the current patchset. + ### Automated Quality Feedback The Maven plugin can add reports to the review comments. For instance, a SonarQube Plugin can generate a simple text file containing statistics about the submitted change and how it affects SonarQube metrics. This file is stored by the Plugin and can be read and added as a review comment. @@ -78,8 +94,4 @@ SonarQube will place to files inside the .sonar/issues-report Directory of the w * issues-report-light.html contains only the new introduced and removed issues of the PatchSet * issues-report.html contains all issues of the PatchSet -These Reports can be easily integrated using the Publish HTML Post Build Action of Jenkins - - - - +These Reports can be easily integrated using the Publish HTML Post Build Action of Jenkins diff --git a/pom.xml b/pom.xml index 3ebe1d1..a2f6d18 100644 --- a/pom.xml +++ b/pom.xml @@ -124,6 +124,11 @@ sonar-maven-plugin 3.0.1 + + org.twdata.maven + mojo-executor + 2.2.0 + junit junit diff --git a/src/main/java/de/mirkosertic/mavensonarsputnik/MavenEnvironment.java b/src/main/java/de/mirkosertic/mavensonarsputnik/MavenEnvironment.java new file mode 100644 index 0000000..70ea8be --- /dev/null +++ b/src/main/java/de/mirkosertic/mavensonarsputnik/MavenEnvironment.java @@ -0,0 +1,122 @@ +package de.mirkosertic.mavensonarsputnik; + +import java.io.File; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactCollector; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.execution.RuntimeInformation; +import org.apache.maven.lifecycle.LifecycleExecutor; +import org.apache.maven.plugin.BuildPluginManager; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; +import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; + +public class MavenEnvironment { + + private final static ThreadLocal ENVIRONMENT = new ThreadLocal(); + + private final MavenSession mavenSession; + private final BuildPluginManager buildPluginManager; + private final Log log; + private final DependencyTreeBuilder dependencyTreeBuilder; + private final ArtifactRepository localRepository; + private final SecDispatcher securityDispatcher; + private final MavenProjectBuilder projectBuilder; + private final LifecycleExecutor lifecycleExecutor; + private final ArtifactFactory artifactFactory; + private final ArtifactMetadataSource artifactMetadataSource; + private final ArtifactCollector artifactCollector; + private final File sonarConfiguration; + private final RuntimeInformation runtimeInformation; + + public static void initialize(MavenSession aMavenSession, BuildPluginManager aBuildPluginManager, Log aLog, + DependencyTreeBuilder aDependencyTreeBuilder, ArtifactRepository aLocalRepository, + SecDispatcher aSecurityDispatcher, MavenProjectBuilder aProjectBuilder, + LifecycleExecutor aLifecycleExecutor, ArtifactFactory aArtifactFactory, + ArtifactMetadataSource aArtifactMetadataSource, ArtifactCollector aArtifactCollector, File aSonarConfiguration, RuntimeInformation aRuntimeInformation) { + ENVIRONMENT.set(new MavenEnvironment(aMavenSession, aBuildPluginManager, aLog, + aDependencyTreeBuilder, aLocalRepository, + aSecurityDispatcher, aProjectBuilder, + aLifecycleExecutor, aArtifactFactory, + aArtifactMetadataSource, aArtifactCollector, aSonarConfiguration, aRuntimeInformation)); + } + + public static MavenEnvironment get() { + return ENVIRONMENT.get(); + } + + public MavenEnvironment(MavenSession aMavenSession, BuildPluginManager aBuildPluginManager, Log aLog, + DependencyTreeBuilder aDependencyTreeBuilder, ArtifactRepository aLocalRepository, + SecDispatcher aSecurityDispatcher, MavenProjectBuilder aProjectBuilder, + LifecycleExecutor aLifecycleExecutor, ArtifactFactory aArtifactFactory, + ArtifactMetadataSource aArtifactMetadataSource, ArtifactCollector aArtifactCollector, File aSonarConfiguration, RuntimeInformation aRuntimeInformation) { + mavenSession = aMavenSession; + buildPluginManager = aBuildPluginManager; + log = aLog; + dependencyTreeBuilder = aDependencyTreeBuilder; + localRepository = aLocalRepository; + securityDispatcher = aSecurityDispatcher; + projectBuilder = aProjectBuilder; + lifecycleExecutor = aLifecycleExecutor; + artifactFactory = aArtifactFactory; + artifactMetadataSource = aArtifactMetadataSource; + artifactCollector = aArtifactCollector; + sonarConfiguration = aSonarConfiguration; + runtimeInformation = aRuntimeInformation; + } + + public MavenSession getMavenSession() { + return mavenSession; + } + + public BuildPluginManager getBuildPluginManager() { + return buildPluginManager; + } + + public Log getLog() { + return log; + } + + public DependencyTreeBuilder getDependencyTreeBuilder() { + return dependencyTreeBuilder; + } + + public ArtifactRepository getLocalRepository() { + return localRepository; + } + + public SecDispatcher getSecurityDispatcher() { + return securityDispatcher; + } + + public MavenProjectBuilder getProjectBuilder() { + return projectBuilder; + } + + public RuntimeInformation getRuntimeInformation() { + return runtimeInformation; + } + + public LifecycleExecutor getLifecycleExecutor() { + return lifecycleExecutor; + } + + public ArtifactFactory getArtifactFactory() { + return artifactFactory; + } + + public ArtifactMetadataSource getArtifactMetadataSource() { + return artifactMetadataSource; + } + + public ArtifactCollector getArtifactCollector() { + return artifactCollector; + } + + public File getSonarConfiguration() { + return sonarConfiguration; + } +} \ No newline at end of file diff --git a/src/main/java/de/mirkosertic/mavensonarsputnik/MavenSonarSputnikMojo.java b/src/main/java/de/mirkosertic/mavensonarsputnik/MavenSonarSputnikMojo.java index 59fc43a..d23b6d5 100644 --- a/src/main/java/de/mirkosertic/mavensonarsputnik/MavenSonarSputnikMojo.java +++ b/src/main/java/de/mirkosertic/mavensonarsputnik/MavenSonarSputnikMojo.java @@ -9,16 +9,13 @@ import org.apache.maven.execution.RuntimeInformation; import org.apache.maven.lifecycle.LifecycleExecutor; import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.*; import org.apache.maven.project.MavenProjectBuilder; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; -import org.sonar.runner.api.EmbeddedRunner; -import org.sonarsource.scanner.maven.DependencyCollector; -import org.sonarsource.scanner.maven.ExtensionsFactory; -import org.sonarsource.scanner.maven.bootstrap.*; import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; import pl.touk.sputnik.configuration.CliOption; @@ -50,6 +47,9 @@ public class MavenSonarSputnikMojo extends AbstractMojo { @Component private RuntimeInformation runtimeInformation; + @Component + private BuildPluginManager pluginManager; + /** * The gerrit change id. */ @@ -132,42 +132,11 @@ public void execute() throws MojoExecutionException, MojoFailureException { theSputnikProperties.setProperty(CliOption.CHANGE_ID.getKey(), theChangeID); theSputnikProperties.setProperty(CliOption.REVISION_ID.getKey(), theRevision); - SonarExecutor theExecutor = new SonarExecutor() { - @Override - public File executeSonar() throws Exception { - File theWorkingDirectory = MavenProjectConverter.getSonarWorkDir(mavenSession.getCurrentProject()); - theWorkingDirectory.mkdirs(); - - // This will switch the cache to the working directory - System.setProperty("SONAR_USER_HOME", theWorkingDirectory.toString()); - - ExtensionsFactory theExtensionsFactory = new ExtensionsFactory(getLog(), mavenSession, lifecycleExecutor, artifactFactory, localRepository, artifactMetadataSource, artifactCollector, - dependencyTreeBuilder, projectBuilder); - DependencyCollector theDependencyCollector = new DependencyCollector(dependencyTreeBuilder, localRepository); - MavenProjectConverter theMavenProjectConverter = new MavenProjectConverter(getLog(), theDependencyCollector); - LogHandler theLogHandler = new LogHandler(getLog()); - - PropertyDecryptor thePropertyDecryptor = new PropertyDecryptor(getLog(), securityDispatcher); - - RunnerFactory theRunnerFactory = new RunnerFactory(theLogHandler, getLog().isDebugEnabled(), runtimeInformation, mavenSession, thePropertyDecryptor); - - EmbeddedRunner theRunner = theRunnerFactory.create(); - - Properties theSonarConfigurationToAdd = new Properties(); - theSonarConfigurationToAdd.load(getClass().getResourceAsStream("/default-sonar.properties")); - try (InputStream theStream = new FileInputStream(sonarConfiguration)) { - theSonarConfigurationToAdd.load(theStream); - } - - theRunner.addGlobalProperties(theSonarConfigurationToAdd); - - new RunnerBootstrapper(getLog(), mavenSession, theRunner, theMavenProjectConverter, theExtensionsFactory, thePropertyDecryptor).execute(); - - return new File(theWorkingDirectory, "sonar-report.json"); - }; - }; - - SonarExecutorHelper.set(theExecutor); + MavenEnvironment.initialize(mavenSession, pluginManager, getLog(), + dependencyTreeBuilder, localRepository, + securityDispatcher, projectBuilder, + lifecycleExecutor, artifactFactory, + artifactMetadataSource, artifactCollector, sonarConfiguration, runtimeInformation); Configuration theConfiguration = ConfigurationBuilder.initFromProperties(theSputnikProperties); @@ -175,16 +144,14 @@ public File executeSonar() throws Exception { new Engine(facade, theConfiguration).run(); } catch (Exception e) { throw new MojoExecutionException("Error invoking sputnik", e); - } finally { - SonarExecutorHelper.remove(); } } - private static ConnectorFacade getConnectorFacade(Configuration configuration) { + private static ConnectorFacade getConnectorFacade(Configuration aConfiguration) { ConnectorType theConnectorType = ConnectorType - .getValidConnectorType(configuration.getProperty(GeneralOption.CONNECTOR_TYPE)); - ConnectorFacade theFacade = ConnectorFacadeFactory.INSTANCE.build(theConnectorType, configuration); - theFacade.validate(configuration); + .getValidConnectorType(aConfiguration.getProperty(GeneralOption.CONNECTOR_TYPE)); + ConnectorFacade theFacade = ConnectorFacadeFactory.INSTANCE.build(theConnectorType, aConfiguration); + theFacade.validate(aConfiguration); return theFacade; } } \ No newline at end of file diff --git a/src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutor.java b/src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutor.java deleted file mode 100644 index daa00ce..0000000 --- a/src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutor.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.mirkosertic.mavensonarsputnik; - -import java.io.File; - -public interface SonarExecutor { - - File executeSonar() throws Exception; -} diff --git a/src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutorHelper.java b/src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutorHelper.java deleted file mode 100644 index 5477b10..0000000 --- a/src/main/java/de/mirkosertic/mavensonarsputnik/SonarExecutorHelper.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.mirkosertic.mavensonarsputnik; - -public class SonarExecutorHelper { - - private static final ThreadLocal EXECUTOR = new ThreadLocal<>(); - - SonarExecutorHelper() { - } - - public static SonarExecutor get() { - return EXECUTOR.get(); - } - - public static void set(SonarExecutor aExecutor) { - EXECUTOR.set(aExecutor); - } - - public static void remove() { - EXECUTOR.remove(); - } -} diff --git a/src/main/java/pl/touk/sputnik/engine/ProcessorBuilder.java b/src/main/java/pl/touk/sputnik/engine/ProcessorBuilder.java index 0a82af7..13dbe44 100644 --- a/src/main/java/pl/touk/sputnik/engine/ProcessorBuilder.java +++ b/src/main/java/pl/touk/sputnik/engine/ProcessorBuilder.java @@ -8,6 +8,7 @@ import pl.touk.sputnik.processor.findbugs.FindBugsProcessor; import pl.touk.sputnik.processor.jshint.JsHintProcessor; import pl.touk.sputnik.processor.jslint.JsLintProcessor; +import pl.touk.sputnik.processor.owasp.OWASPDependencyCheckProcessor; import pl.touk.sputnik.processor.pitest.PITestProcessor; import pl.touk.sputnik.processor.pmd.PmdProcessor; import pl.touk.sputnik.processor.scalastyle.ScalastyleProcessor; @@ -49,6 +50,9 @@ public static List buildProcessors(Configuration configuration) if (Boolean.valueOf(configuration.getProperty(PITestProcessor.PITEST_ENABLED))) { processors.add(new PITestProcessor(configuration)); } + if (Boolean.valueOf(configuration.getProperty(OWASPDependencyCheckProcessor.OWASPDEPENDENCYCHECK_ENABLED))) { + processors.add(new OWASPDependencyCheckProcessor(configuration)); + } return processors; } } diff --git a/src/main/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessor.java b/src/main/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessor.java new file mode 100644 index 0000000..4bdda42 --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessor.java @@ -0,0 +1,323 @@ +package pl.touk.sputnik.processor.owasp; + +import de.mirkosertic.mavensonarsputnik.MavenEnvironment; +import lombok.extern.slf4j.Slf4j; +import pl.touk.sputnik.configuration.Configuration; +import pl.touk.sputnik.configuration.ConfigurationOption; +import pl.touk.sputnik.review.Review; +import pl.touk.sputnik.review.ReviewException; +import pl.touk.sputnik.review.ReviewFile; +import pl.touk.sputnik.review.ReviewProcessor; +import pl.touk.sputnik.review.ReviewResult; +import pl.touk.sputnik.review.Severity; +import pl.touk.sputnik.review.Violation; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.lang.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.Xpp3DomBuilder; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; +import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment; +import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; +import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; +import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; +import static org.twdata.maven.mojoexecutor.MojoExecutor.version; + +@Slf4j +public class OWASPDependencyCheckProcessor implements ReviewProcessor { + + public final static ConfigurationOption OWASPDEPENDENCYCHECK_ENABLED = new ConfigurationOption() { + @Override + public String getKey() { + return "owaspdependencycheck.enabled"; + } + + @Override + public String getDescription() { + return "OWASP Dependency Check enabled"; + } + + @Override + public String getDefaultValue() { + return "true"; + } + }; + + public final static ConfigurationOption OWASPDEPENDENCYCHECK_CONFIGURATION = new ConfigurationOption() { + @Override + public String getKey() { + return "owaspdependencycheck.configurationFile"; + } + + @Override + public String getDescription() { + return "OWASP Dependency check configuration file"; + } + + @Override + public String getDefaultValue() { + return ""; + } + }; + + public static class MavenIdentifier { + + private final String groupId; + private final String artifactId; + private final String version; + + public MavenIdentifier(String aIdentifierString) { + aIdentifierString = aIdentifierString.substring(1, aIdentifierString.length() - 1); + int p = aIdentifierString.indexOf(":"); + groupId = aIdentifierString.substring(0, p); + aIdentifierString = aIdentifierString.substring(p+1); + p = aIdentifierString.indexOf(":"); + artifactId = aIdentifierString.substring(0, p); + aIdentifierString = aIdentifierString.substring(p+1); + version = aIdentifierString; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getVersion() { + return version; + } + } + + private final Configuration configuration; + private final Properties properties; + private final Severity severity; + private final boolean report; + private final boolean reportTransitive; + + public OWASPDependencyCheckProcessor(Configuration aConfiguration) { + configuration = aConfiguration; + + properties = new Properties(); + try (InputStream theStream = getClass().getResourceAsStream("/default-owaspdependencycheck.properties")) { + properties.load(theStream); + } catch (Exception e) { + throw new RuntimeException("Error initializing OWASPDependencyCheck", e); + } + + String theConfiguration = aConfiguration.getProperty(OWASPDEPENDENCYCHECK_CONFIGURATION); + if (!StringUtils.isEmpty(theConfiguration)) { + try (InputStream theStream = new FileInputStream(theConfiguration)) { + properties.load(theStream); + } catch (Exception e) { + throw new RuntimeException("Error initializing OWASP Dependency Check", e); + } + } + + severity = Severity.valueOf(properties.getProperty("owaspdependencycheck.severity")); + report = Boolean.parseBoolean(properties.getProperty("owaspdependencycheck.report")); + reportTransitive = Boolean.parseBoolean(properties.getProperty("owaspdependencycheck.reporttransitive")); + } + + @NotNull @Override + public String getName() { + return "OWASP Dependency Check"; + } + + @Nullable @Override + public ReviewResult process(@NotNull Review aReview) { + ReviewResult theResult = new ReviewResult(); + for (ReviewFile theFile : aReview.getFiles()) { + File theIOFile = theFile.getIoFile(); + if (theIOFile != null && theIOFile.exists() && theIOFile.isFile() && theIOFile.getName().equals("pom.xml")) { + try { + processChangedMavenModule(theFile, theIOFile.getAbsoluteFile(), aReview, theResult); + } catch (Exception e) { + throw new ReviewException("Error invoking OWASP Dependency Checker for " + theIOFile); + } + } else { + log.debug("Ignoring {}", theIOFile); + } + } + return theResult; + } + + public static Xpp3Dom plainTextConfigurationFrom(String aXML) throws IOException, XmlPullParserException { + return Xpp3DomBuilder.build(new StringReader(aXML)); + } + + private void processChangedMavenModule(ReviewFile aReviewFile, File aPomXMLFile, Review aReview, ReviewResult aReviewResult) + throws IOException, XmlPullParserException, MojoExecutionException, ParserConfigurationException, SAXException { + + log.info("Processing changed maven module {}", aPomXMLFile); + + MavenEnvironment theEnvironment = MavenEnvironment.get(); + + MavenProject theProject = null; + for (MavenProject theSingleProject : theEnvironment.getMavenSession().getAllProjects()) { + if ((theSingleProject.getFile() != null) && (theSingleProject.getFile().equals(aPomXMLFile))) { + theProject = theSingleProject; + } + } + + if (theProject == null) { + throw new IllegalStateException("Cannot find Maven project for " + aPomXMLFile); + } + + File theOutputDirectory = new File(new File(aPomXMLFile.getParent(), "target"), "owasp-dependency-check"); + log.info("Writing reports to {}", theOutputDirectory); + + theOutputDirectory.mkdirs(); + + MavenProject theOldCurrent = theEnvironment.getMavenSession().getCurrentProject(); + try { + theEnvironment.getMavenSession().setCurrentProject(theProject); + + executeMojo( + plugin( + groupId("org.owasp"), + artifactId("dependency-check-maven"), + version(properties.getProperty("owaspdependencycheck.pluginversion")) + ), + goal("check"), + plainTextConfigurationFrom("trueALL" + + theOutputDirectory + ""), + executionEnvironment( + theProject, + theEnvironment.getMavenSession(), + theEnvironment.getBuildPluginManager() + ) + ); + } finally { + theEnvironment.getMavenSession().setCurrentProject(theOldCurrent); + } + + if (report) { + File theXMLReport = new File(theOutputDirectory, "dependency-check-report.xml"); + DocumentBuilderFactory theFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder theBuilder = theFactory.newDocumentBuilder(); + Document theDocument = theBuilder.parse(theXMLReport); + NodeList theDependencies = theDocument.getElementsByTagName("dependency"); + for (int i = 0; i < theDependencies.getLength(); i++) { + Element theDependency = (Element) theDependencies.item(i); + NodeList theIdentifiers = theDependency.getElementsByTagName("identifier"); + + NodeList theVulnerabilities = theDependency.getElementsByTagName("vulnerability"); + if (theVulnerabilities != null && theVulnerabilities.getLength() > 0 && theIdentifiers.getLength() > 0) { + for (int j = 0; j < theIdentifiers.getLength(); j++) { + Element theIdentifier = (Element) theIdentifiers.item(j); + if ("maven".equals(theIdentifier.getAttribute("type"))) { + NodeList theNames = theIdentifier.getElementsByTagName("name"); + for (int k = 0; k < theNames.getLength(); k++) { + Element theName = (Element) theNames.item(k); + processSingleDependency(aPomXMLFile, theVulnerabilities, aReviewResult, aReviewFile, + new MavenIdentifier(theName.getTextContent())); + } + } + } + } + } + } + } + + private String getElementContent(Element aElement, String aTagname) { + NodeList theChilds = aElement.getChildNodes(); + for (int i=0;i theLines = new ArrayList<>(); + try (BufferedReader theReader = new BufferedReader(new FileReader(aPomXML))) { + while(theReader.ready()) { + String theLine = theReader.readLine(); + if (theLine != null) { + theLines.add(theLine); + } + } + } + + for (int i=0;i 0) { + theReviewComment.append(theCWE); + theReviewComment.append(" : "); + } + theReviewComment.append(theDescription); + theReviewComment.append("\n\n"); + + NodeList theReferences = theSingleElement.getElementsByTagName("reference"); + for (int k=0;k" + aMavenIdentifier.getArtifactId() + " 0) { + theMessage.append("\n"); + theMessage.append("\n"); + theMessage.append("Related Unit Tests:"); + theMessage.append("\n"); + for (int j = 0; j < theTestInfos.getLength(); j++) { + Element theTestInfo = (Element) theTestInfos.item(j); + theMessage.append("*"); + theMessage.append(theTestInfo.getTextContent()); + theMessage.append("\n"); + } + } + Violation theViolation = new Violation(theFile.getReviewFilename(), theLineNumber, theMessage.toString(), severity); diff --git a/src/main/java/pl/touk/sputnik/processor/sonar/SonarProcessor.java b/src/main/java/pl/touk/sputnik/processor/sonar/SonarProcessor.java index dfc5624..5e62098 100644 --- a/src/main/java/pl/touk/sputnik/processor/sonar/SonarProcessor.java +++ b/src/main/java/pl/touk/sputnik/processor/sonar/SonarProcessor.java @@ -2,18 +2,25 @@ import java.io.File; import java.io.FileInputStream; -import java.util.HashSet; -import java.util.Set; +import java.io.InputStream; +import java.util.Properties; +import de.mirkosertic.mavensonarsputnik.MavenEnvironment; import de.mirkosertic.mavensonarsputnik.Options; -import de.mirkosertic.mavensonarsputnik.SonarExecutor; -import de.mirkosertic.mavensonarsputnik.SonarExecutorHelper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.sonar.runner.api.EmbeddedRunner; +import org.sonarsource.scanner.maven.DependencyCollector; +import org.sonarsource.scanner.maven.ExtensionsFactory; +import org.sonarsource.scanner.maven.bootstrap.LogHandler; +import org.sonarsource.scanner.maven.bootstrap.MavenProjectConverter; +import org.sonarsource.scanner.maven.bootstrap.PropertyDecryptor; +import org.sonarsource.scanner.maven.bootstrap.RunnerBootstrapper; +import org.sonarsource.scanner.maven.bootstrap.RunnerFactory; import com.google.common.annotations.VisibleForTesting; @@ -44,9 +51,37 @@ public ReviewResult process(@NotNull Review review) { } try { - SonarExecutor theExecutor = SonarExecutorHelper.get(); + MavenEnvironment theEnvironment = MavenEnvironment.get(); - File resultFile = theExecutor.executeSonar(); + File theWorkingDirectory = MavenProjectConverter.getSonarWorkDir(theEnvironment.getMavenSession().getCurrentProject()); + theWorkingDirectory.mkdirs(); + + // This will switch the cache to the working directory + System.setProperty("SONAR_USER_HOME", theWorkingDirectory.toString()); + + ExtensionsFactory theExtensionsFactory = new ExtensionsFactory(theEnvironment.getLog(), theEnvironment.getMavenSession(), theEnvironment.getLifecycleExecutor(), theEnvironment.getArtifactFactory(), theEnvironment.getLocalRepository(), theEnvironment.getArtifactMetadataSource(), theEnvironment.getArtifactCollector(), + theEnvironment.getDependencyTreeBuilder(), theEnvironment.getProjectBuilder()); + DependencyCollector theDependencyCollector = new DependencyCollector(theEnvironment.getDependencyTreeBuilder(), theEnvironment.getLocalRepository()); + MavenProjectConverter theMavenProjectConverter = new MavenProjectConverter(theEnvironment.getLog(), theDependencyCollector); + LogHandler theLogHandler = new LogHandler(theEnvironment.getLog()); + + PropertyDecryptor thePropertyDecryptor = new PropertyDecryptor(theEnvironment.getLog(), theEnvironment.getSecurityDispatcher()); + + RunnerFactory theRunnerFactory = new RunnerFactory(theLogHandler, theEnvironment.getLog().isDebugEnabled(), theEnvironment.getRuntimeInformation(), theEnvironment.getMavenSession(), thePropertyDecryptor); + + EmbeddedRunner theRunner = theRunnerFactory.create(); + + Properties theSonarConfigurationToAdd = new Properties(); + theSonarConfigurationToAdd.load(getClass().getResourceAsStream("/default-sonar.properties")); + try (InputStream theStream = new FileInputStream(theEnvironment.getSonarConfiguration())) { + theSonarConfigurationToAdd.load(theStream); + } + + theRunner.addGlobalProperties(theSonarConfigurationToAdd); + + new RunnerBootstrapper(theEnvironment.getLog(), theEnvironment.getMavenSession(), theRunner, theMavenProjectConverter, theExtensionsFactory, thePropertyDecryptor).execute(); + + File resultFile = new File(theWorkingDirectory, "sonar-report.json"); SonarResultParser parser = new SonarResultParser(resultFile); diff --git a/src/main/resources/default-owaspdependencycheck.properties b/src/main/resources/default-owaspdependencycheck.properties new file mode 100644 index 0000000..b5218fb --- /dev/null +++ b/src/main/resources/default-owaspdependencycheck.properties @@ -0,0 +1,4 @@ +owaspdependencycheck.pluginversion=1.3.4 +owaspdependencycheck.severity=WARNING +owaspdependencycheck.report=true +owaspdependencycheck.reporttransitive=false diff --git a/src/main/resources/default-sputnik.properties b/src/main/resources/default-sputnik.properties index 889ac8b..ce256a0 100644 --- a/src/main/resources/default-sputnik.properties +++ b/src/main/resources/default-sputnik.properties @@ -23,3 +23,5 @@ sonar.enabled=true sonar.verbose=false pitest.enabled=true pitest.configurationFile= +owaspdependencycheck.enabled=true +owaspdependencycheck.configurationFile= \ No newline at end of file diff --git a/src/test/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessorTest.java b/src/test/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessorTest.java new file mode 100644 index 0000000..ead03a7 --- /dev/null +++ b/src/test/java/pl/touk/sputnik/processor/owasp/OWASPDependencyCheckProcessorTest.java @@ -0,0 +1,17 @@ +package pl.touk.sputnik.processor.owasp; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class OWASPDependencyCheckProcessorTest { + + @Test + public void testMavenIdentifier() { + OWASPDependencyCheckProcessor.MavenIdentifier theIdentifier = new OWASPDependencyCheckProcessor.MavenIdentifier("(org.springframework.security.oauth:spring-security-oauth2:2.0.4.RELEASE)"); + assertEquals("org.springframework.security.oauth", theIdentifier.getGroupId()); + assertEquals("spring-security-oauth2", theIdentifier.getArtifactId()); + assertEquals("2.0.4.RELEASE", theIdentifier.getVersion()); + } + +} \ No newline at end of file