diff --git a/lang-java/src/main/resources/vulas-java.properties b/lang-java/src/main/resources/vulas-java.properties index bf46f8d8d..7ed59bf54 100644 --- a/lang-java/src/main/resources/vulas-java.properties +++ b/lang-java/src/main/resources/vulas-java.properties @@ -49,7 +49,7 @@ vulas.core.instr.blacklist.jars.ignoreScopes = test, provided # Constructs of dependencies whose filename matches one of the following regular expressions will not be instrumented (multiple ones to be separated by comma) # Default: lang-java-.*\.jar,vulas-core-.*\.jar,surefire-.*\.jar,junit-.*\.jar -vulas.core.instr.blacklist.jars = lang-java-.*\.jar,vulas-core-.*\.jar,surefire-.*\.jar,junit-.*\.jar +vulas.core.instr.blacklist.jars = lang-java-.*\.jar,vulas-core-.*\.jar,surefire-.*\.jar,junit-.*\.jar,org.jacoco.agent.*\.jar # User-provided blacklist: Constructs of dependencies whose filename matches one of the following regular expressions will not be instrumented (multiple ones to be separated by comma) # Default: - @@ -63,7 +63,7 @@ vulas.core.instr.blacklist.classes.jre = java.,sun.,com.sun.,org.xml.,org.ietf., # Other Java packages whose constructs are not instrumented (e.g., from an app container or the JUnit framework) (multiple ones to be separated by comma) # Default: org.apache.maven.surefire,org.junit,com.sap.psr.vulas,javassist.,org.apache.commons.logging.,org.apache.log4j.,com.fasterxml.jackson. # Note: "org.apache.juli", "org.apache.tomcat", "org.apache.catalina" or relevant in the context of Tomcat instrumentation -vulas.core.instr.blacklist.classes = org.apache.maven.surefire,org.junit,com.sap.psr.vulas,javassist.,org.apache.commons.logging.,org.apache.log4j.,com.fasterxml.jackson. +vulas.core.instr.blacklist.classes = org.apache.maven.surefire,org.junit,com.sap.psr.vulas,javassist.,org.apache.commons.logging.,org.apache.log4j.,com.fasterxml.jackson.,org.jacoco. # User-provided Java packages whose constructs are not instrumented (multiple ones to be separated by comma) # Default: - @@ -82,7 +82,7 @@ vulas.core.instr.writeCode = false # JARs for which no traces and no archive information will be uploaded (e.g., from Vulas itself) # Multiple entries are separated by comma, each entry is a regex -vulas.core.monitor.blacklist.jars = lang-java-.*\.jar,vulas-core-.*\.jar,surefire-.*\.jar,junit-.*\.jar +vulas.core.monitor.blacklist.jars = lang-java-.*\.jar,vulas-core-.*\.jar,surefire-.*\.jar,junit-.*\.jar,org.jacoco.agent.*\.jar # Enables or disables the periodic upload of collected traces to the backend # Default: true diff --git a/plugin-maven/pom.xml b/plugin-maven/pom.xml index 9cfe868f5..e36343943 100644 --- a/plugin-maven/pom.xml +++ b/plugin-maven/pom.xml @@ -1,125 +1,229 @@ - 4.0.0 - - - com.sap.research.security.vulas - root - 3.0.10-SNAPSHOT - - plugin-maven - maven-plugin - - Plugin for Maven - - - - - com.sap.research.security.vulas - lang - ${project.version} - compile - - - com.sap.research.security.vulas - lang-java-reach - ${project.version} - compile - - - - com.sap.research.security.vulas - lang-java-reach-wala - ${project.version} - runtime - - - com.google.code.gson - gson - - - org.apache.maven - maven-core - - - org.apache.maven - maven-plugin-api - - - org.apache.maven.plugin-tools - maven-plugin-annotations - 3.5.2 - provided - - - org.codehaus.plexus - plexus-utils - 3.1.0 - - - - - - - - org.apache.maven.plugins - maven-plugin-plugin - 3.5.2 - - vulas - true - - - - help-goal - - helpmojo - - - - - - - - - run-its - - - - - org.apache.maven.plugins - maven-invoker-plugin - 1.7 - - true - ${project.build.directory}/it - - */pom.xml - - verify - ${project.build.directory}/local-repo - src/it/settings.xml - - clean - test-compile - - - - - integration-test - - install - integration-test - verify - - - - - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + + com.sap.research.security.vulas + root + 3.0.10-SNAPSHOT + + plugin-maven + maven-plugin + + Plugin for Maven + + + + + 3.5.4 + + + + + com.sap.research.security.vulas + lang + ${project.version} + compile + + + com.sap.research.security.vulas + lang-java-reach + ${project.version} + compile + + + + com.sap.research.security.vulas + lang-java-reach-wala + ${project.version} + runtime + + + com.google.code.gson + gson + + + org.apache.maven + maven-core + ${maven.version} + + + org.apache.maven + maven-plugin-api + ${maven.version} + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.5.2 + provided + + + org.codehaus.plexus + plexus-utils + 3.1.0 + + + + org.apache.ant + ant + 1.10.5 + + + + + com.sap.research.security.vulas + lang-java + ${project.version} + jar-with-dependencies + + + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 3.3.0 + test + + + org.apache.maven + maven-compat + ${maven.version} + test + + + org.apache.maven + maven-aether-provider + 3.3.9 + test + + + org.sonatype.aether + aether-api + 1.13.1 + test + + + org.apache.maven.shared + maven-verifier + 1.2 + test + + + + + com.xebialabs.restito + restito + test + + + com.jayway.restassured + rest-assured + test + + + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5.2 + + vulas + true + + + + help-goal + + helpmojo + + + + + + + + + + + + run-its + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.5 + + + add-test-source + process-resources + + add-test-source + + + + src/it/java + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.1 + + + + integration-test + verify + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + 1.7 + + true + ${project.build.directory}/it + + */pom.xml + + verify + ${project.build.directory}/local-repo + src/it/settings.xml + + clean + test-compile + + + + + integration-test + + install + integration-test + verify + + + + + + + + + + diff --git a/plugin-maven/src/it/java/StubServerSetup.java b/plugin-maven/src/it/java/StubServerSetup.java new file mode 100644 index 000000000..f45171bc6 --- /dev/null +++ b/plugin-maven/src/it/java/StubServerSetup.java @@ -0,0 +1,141 @@ +import com.jayway.restassured.RestAssured; +import com.sap.psr.vulas.core.util.CoreConfiguration; +import com.sap.psr.vulas.shared.connectivity.PathBuilder; +import com.sap.psr.vulas.shared.connectivity.Service; +import com.sap.psr.vulas.shared.json.JacksonUtil; +import com.sap.psr.vulas.shared.json.model.Application; +import com.sap.psr.vulas.shared.json.model.Space; +import com.sap.psr.vulas.shared.json.model.Tenant; +import com.sap.psr.vulas.shared.util.StringUtil; +import com.sap.psr.vulas.shared.util.VulasConfiguration; +import com.xebialabs.restito.server.StubServer; +import org.glassfish.grizzly.http.util.HttpStatus; + +import static com.jayway.restassured.RestAssured.expect; +import static com.jayway.restassured.RestAssured.options; +import static com.xebialabs.restito.builder.stub.StubHttp.whenHttp; +import static com.xebialabs.restito.semantics.Action.*; +import static com.xebialabs.restito.semantics.Action.status; +import static com.xebialabs.restito.semantics.Condition.post; +import static com.xebialabs.restito.semantics.Condition.get; +import static com.xebialabs.restito.semantics.Condition.uri; + + +public class StubServerSetup { + + public StubServer server; + + protected Tenant testTenant; + protected Space testSpace; + protected Application testApp; + + + private String backendURL; + + public StubServerSetup(String group, String artifact, String version) { + server = new StubServer().run(); + RestAssured.port = server.getPort(); + + testTenant = this.buildTestTenant(); + testSpace = this.buildTestSpace(); + testApp = this.buildTestApplication(group, artifact, version); + + // App context + System.setProperty(CoreConfiguration.APP_CTX_GROUP, testApp.getMvnGroup()); + System.setProperty(CoreConfiguration.APP_CTX_ARTIF, testApp.getArtifact()); + System.setProperty(CoreConfiguration.APP_CTX_VERSI, testApp.getVersion()); + + // Identify app code + System.setProperty(CoreConfiguration.BACKEND_CONNECT, CoreConfiguration.ConnectType.READ_WRITE.toString()); + + // Identify app code + System.setProperty(CoreConfiguration.APP_PREFIXES, "com.acme"); + } + + public void stop() { + server.stop(); + System.out.println("Stopped Server:" + this.backendURL); + + } + + protected Tenant buildTestTenant() { + final String rnd = StringUtil.getRandonString(6); + return new Tenant("tenant-token-" + rnd, "tenant-name-" + rnd); + } + + protected Space buildTestSpace() { + final String rnd = StringUtil.getRandonString(6); + return new Space("space-token-" + rnd, "space-name-" + rnd, "space-description"); + } + + + protected Application buildTestApplication(String group, String artifact, String version) { + return new Application(group, artifact, version); + } + + protected void configureBackendServiceUrl(StubServer _ss) { + final StringBuffer b = new StringBuffer(); + b.append("http://localhost:").append(_ss.getPort()).append("/backend"); + this.backendURL = b.toString(); + VulasConfiguration.getGlobal().setProperty(VulasConfiguration.getServiceUrlKey(Service.BACKEND), this.backendURL); + System.out.println("Started Server:" + this.backendURL); + + } + + public String getBackendURL() { + return backendURL; + } + + + /** + * App creation results in the following two HTTP calls. + * + * @param _a TODO + */ + public void setupMockServices(Application _a) { + final String s_json = JacksonUtil.asJsonString(_a); + whenHttp(server). + match(post("/backend" + PathBuilder.apps())). + then( + stringContent(s_json), + contentType("application/json"), + charset("UTF-8"), + status(HttpStatus.CREATED_201)); + + expect() + .statusCode(201). + when() + .post("/backend" + PathBuilder.apps()); + + whenHttp(server). + match(post("/backend" + PathBuilder.goalExcecutions(null, null, _a))). + then( + stringContent(s_json), + contentType("application/json"), + charset("UTF-8"), + status(HttpStatus.CREATED_201)); + + expect() + .statusCode(201). + when() + .post("/backend" + PathBuilder.goalExcecutions(null, null, _a)); + + whenHttp(server). + match(uri("/backend" + PathBuilder.app(testApp))). + then( + stringContent(s_json), + contentType("application/json"), + charset("UTF-8"), + status(HttpStatus.OK_200)); + + whenHttp(server). + match(uri("/backend" + PathBuilder.app(testApp)+"/deps")). + then( + stringContent("[]"), + contentType("application/json"), + charset("UTF-8"), + status(HttpStatus.OK_200)); + } + + +} diff --git a/plugin-maven/src/it/java/VulasMavenPluginTests.java b/plugin-maven/src/it/java/VulasMavenPluginTests.java new file mode 100644 index 000000000..4989834e2 --- /dev/null +++ b/plugin-maven/src/it/java/VulasMavenPluginTests.java @@ -0,0 +1,122 @@ +import com.sap.psr.vulas.shared.connectivity.Service; +import com.sap.psr.vulas.shared.util.VulasConfiguration; +import org.apache.maven.it.Verifier; +import org.apache.maven.it.util.ResourceExtractor; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +public class VulasMavenPluginTests { + + + private static StubServerSetup stubServer; + + @BeforeClass + public static void startServer() { + // setup the stubserver to simulate communication with vulas's backend + stubServer = new StubServerSetup("foo.bar", "sampletest", "1.0.0"); + stubServer.configureBackendServiceUrl(stubServer.server); + stubServer.setupMockServices(stubServer.testApp); + } + + + @AfterClass + public static void stopServer() { + // stop the stubserver + stubServer.stop(); + } + + + public Verifier testPlugin(String pomFileName) + throws Exception { + + // set the maven project to test + File testDir = ResourceExtractor.simpleExtractResources(getClass(), "/testproject"); + + Verifier verifier = new Verifier(testDir.getAbsolutePath()); + + // remove artifacts created by this test from the m2 repository + verifier.deleteArtifact("foo.bar", "sampletest", "1.0.0", "pom"); + + // execute the goals + List cliOptions = new ArrayList(); + // pass the backendURL to the mvn invoke command + Properties properties = new Properties(); + properties.setProperty(VulasConfiguration.getServiceUrlKey(Service.BACKEND), stubServer.getBackendURL()); + verifier.setSystemProperties(properties); + + // do not recurse into sub-projects + cliOptions.add("-N"); + cliOptions.add("-f=" + pomFileName); + verifier.setCliOptions(cliOptions); + List goals = new ArrayList(); + goals.add("clean"); + goals.add("compile"); + goals.add("vulas:app"); + goals.add("test"); + + verifier.executeGoals(goals); + + //check if vulas has been executed + verifier.assertFilePresent("target/vulas/tmp"); + + verifier.verifyErrorFreeLog(); + + return verifier; + + + } + + @Test + public void prepareGoalTest() throws Exception { + Verifier verifier = testPlugin("pom.xml"); + // check prepare-vulas-agent has been executed + verifier.verifyTextInLog("prepare-vulas-agent"); + // check if jacoco has been executed + verifier.assertFilePresent("target/jacoco.exec"); + + } + + + @Test + public void backwardsCompabilityTest() throws Exception { + String pomFileName = "backwardComppom.xml"; + Path pomFilePath = Paths.get("target", "test-classes", "testproject", pomFileName); + // Since environment variables and system properties are not passed to the forked vm + // write the backendURL directly into the pom file + String content = new String(Files.readAllBytes(pomFilePath), Charset.defaultCharset()); + content = content.replaceAll("REPLACE_WITH_BACKENDURL", stubServer.getBackendURL()); + Files.write(pomFilePath, content.getBytes(Charset.defaultCharset())); + + Verifier verifier = testPlugin(pomFileName); + verifier.verifyTextInLog("/vulas/lib/vulas-core-latest-jar-with-dependencies.jar"); + } + + + @Test + public void mixedConfigurationTest() throws Exception { + String pomFileName = "mixedpom.xml"; + Path pomFilePath = Paths.get("target", "test-classes", "testproject", pomFileName); + // Since environment variables and system properties are not passed to the forked vm + // write the backendURL directly into the pom file + String content = new String(Files.readAllBytes(pomFilePath), Charset.defaultCharset()); + content = content.replaceAll("REPLACE_WITH_BACKENDURL", stubServer.getBackendURL()); + Files.write(pomFilePath, content.getBytes(Charset.defaultCharset())); + + Verifier verifier = testPlugin(pomFileName); + // in the mixed setting: the manual javaagent is always executed + // all argLine arguments set by any maven plugin are ignored see https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html + verifier.verifyTextInLog("/vulas/lib/vulas-core-latest-jar-with-dependencies.jar"); + } + + +} diff --git a/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/AbstractVulasMojo.java b/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/AbstractVulasMojo.java index d5724d65f..61036c62b 100755 --- a/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/AbstractVulasMojo.java +++ b/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/AbstractVulasMojo.java @@ -37,183 +37,188 @@ public abstract class AbstractVulasMojo extends AbstractMojo { - private static final String INCLUDES = "vulas.maven.includes"; - - private static final String EXCLUDES = "vulas.maven.excludes"; - - private static final String IGNORE_POMS = "vulas.maven.ignorePoms"; - - @Parameter(defaultValue = "${project}", property = "project", required = true, readonly = true) - protected MavenProject project; - - @Parameter(defaultValue = "${session}", property = "session", required = true, readonly = true) - protected MavenSession session; - - /** - * All plugin configuration settings of the element are put in this {@link Map}. - */ - @Parameter - private Map layeredConfiguration; - - protected AbstractAppGoal goal = null; - - private StringList includeArtifacts = null; - private StringList excludeArtifacts = null; - private boolean ignorePoms = false; - - /** - * Puts the plugin configuration element as a new layer into {@link VulasConfiguration}. - * If no such element exists, e.g., because the POM file does not contain a plugin section for Vulas, default settings - * are established using {@link MavenProject} and {@link VulasConfiguration#setPropertyIfEmpty(String, Object)}. - * @throws Exception - */ - public final void prepareConfiguration() throws Exception { - - // Delete any transient settings that remaining from a previous goal execution (if any) - final boolean contained_values = VulasConfiguration.getGlobal().clearTransientProperties(); - if(contained_values) - getLog().info("Transient configuration settings deleted"); - - // Get the configuration layer from the plugin configuration (can be null) - VulasConfiguration.getGlobal().addAfterSystemProperties("Plugin configuration", this.layeredConfiguration, null, true); - - // Check whether the application context can be established - Application app = null; - try { - app = CoreConfiguration.getAppContext(); - } - // In case the plugin is called w/o using the Vulas profile, project-specific settings are not set - // Set them using the project member - catch (ConfigurationException e) { - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_CTX_GROUP, this.project.getGroupId()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_CTX_ARTIF, this.project.getArtifactId()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_CTX_VERSI, this.project.getVersion()); - app = CoreConfiguration.getAppContext(); - } - - // Set defaults for all the paths - VulasConfiguration.getGlobal().setPropertyIfEmpty(VulasConfiguration.TMP_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "tmp").toString()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.UPLOAD_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "upload").toString()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_SRC_DIR, Paths.get(this.project.getBuild().getDirectory()).toString()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_TARGET_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "target").toString()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_INCLUDE_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "include").toString()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_LIB_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "lib").toString()); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.REP_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "report").toString()); - - // Read app constructs from src/main/java and target/classes - final String p = Paths.get(this.project.getBuild().getOutputDirectory()).toString() + "," + Paths.get(this.project.getBuild().getSourceDirectory()).toString(); - VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_DIRS, p); - - // Test how-to get the reactor POM in a reliable manner - // The following method call fails if Maven is called with option -pl - getLog().info("Top level project: " + this.session.getTopLevelProject()); - getLog().info("Execution root dir: " + this.session.getExecutionRootDirectory()); - - // Includes, excludes and ignorePoms - this.includeArtifacts = new StringList(VulasConfiguration.getGlobal().getStringArray(INCLUDES, null)); - this.excludeArtifacts = new StringList(VulasConfiguration.getGlobal().getStringArray(EXCLUDES, null)); - this.ignorePoms = VulasConfiguration.getGlobal().getConfiguration().getBoolean(IGNORE_POMS, false); - } - - /** - * This method, called by Maven, first invokes {@link AbstractVulasMojo#createGoal()} and then {@link AbstractVulasMojo#executeGoal()}. - */ - public final void execute() throws MojoExecutionException, MojoFailureException { - try { - this.prepareConfiguration(); - - final boolean do_process = this instanceof MvnPluginReport || this.isPassingFilter(this.project); - if(do_process) { - // Create the goal - this.createGoal(); - this.goal.setGoalClient(GoalClient.MAVEN_PLUGIN); - this.goal.setConfiguration(VulasConfiguration.getGlobal()); - - // Set the application paths - this.goal.addAppPaths(FileUtil.getPaths(VulasConfiguration.getGlobal().getStringArray(CoreConfiguration.APP_DIRS, null))); - - // Set the dependency paths - this.setKnownDependencies(); - - // Execute the goal - this.executeGoal(); - } - } - // Expected problems will be passed on as is - catch (MojoFailureException mfe) { - throw mfe; - } - // Unexpected problems (the goal execution terminates abnormally/unexpectedly) - catch (GoalExecutionException gee) { - throw new MojoExecutionException(gee.getMessage(), gee); - } - // Every other exception results in a MojoExecutionException (= unexpected) - catch(Exception e) { - throw new MojoExecutionException("Error during Vulas goal execution " + this.goal + ": ", e ); - } - } - - /** - * Evaluates the configuration settings {@link AbstractVulasMojo#INCLUDES}, {@link AbstractVulasMojo#EXCLUDES} and {@link #IGNORE_POMS} to - * determine whether the given {@link MavenProject} shall be processed. - * @param _prj - * @return - */ - private boolean isPassingFilter(MavenProject _prj) { - boolean do_process = true; - - // Only included ones - if(!this.includeArtifacts.isEmpty()) { - do_process = this.includeArtifacts.contains(_prj.getArtifactId(), ComparisonMode.EQUALS, CaseSensitivity.CASE_INSENSITIVE); - if(do_process) - this.getLog().info("Artifact [" + _prj.getArtifactId() + "] explicitly included for processing via configuration parameter [" + INCLUDES + "]"); - else - this.getLog().warn("Artifact [" + _prj.getArtifactId() + "] will NOT be processed, it is not among those explicitly included for processing via configuration parameter [" + INCLUDES + "]"); - } - - // Excluded (explicitly or through packaging) - else { - if(!this.excludeArtifacts.isEmpty() && this.excludeArtifacts.contains(_prj.getArtifactId(), ComparisonMode.EQUALS, CaseSensitivity.CASE_INSENSITIVE)) { - this.getLog().warn("Artifact [" + _prj.getArtifactId() + "] explicitly excluded from processing via configuration parameter [" + EXCLUDES + "]"); - do_process = false; - } - if(do_process && this.ignorePoms && "POM".equalsIgnoreCase(_prj.getPackaging())) { - this.getLog().warn("Artifact [" + _prj.getArtifactId() + "] excluded from processing via configuration parameter [" + IGNORE_POMS + "]"); - do_process = false; - } - } - - return do_process; - } - - /** - * Creates the respective goal. - * - * MUST be overridden by subclasses. - * @return - */ - protected abstract void createGoal(); - - /** - * Simply calls {@AbstractAnalysisGoal#execute}. - * - * CAN be overridden by subclasses. - * @return - */ - protected void executeGoal() throws Exception { - this.goal.executeSync(); - } - - /** - * Identifies known dependencies and passes them to Mojos inheriting from {@link AbstractAppGoal}. - * Note that such subclasses should be annotated with "requiresDependencyResolution = ResolutionScope.TEST". - * - * @throws DependencyResolutionRequiredException - */ - private final void setKnownDependencies() throws DependencyResolutionRequiredException { - if(this.goal!=null && this.goal instanceof AbstractAppGoal) { - - // ---- Determine dependencies (Vulas 2.x) + private static final String INCLUDES = "vulas.maven.includes"; + + private static final String EXCLUDES = "vulas.maven.excludes"; + + private static final String IGNORE_POMS = "vulas.maven.ignorePoms"; + + @Parameter(defaultValue = "${project}", property = "project", required = true, readonly = true) + protected MavenProject project; + + + @Parameter(defaultValue = "${session}", property = "session", required = true, readonly = true) + protected MavenSession session; + + /** + * All plugin configuration settings of the element are put in this {@link Map}. + */ + @Parameter + private Map layeredConfiguration; + + protected AbstractAppGoal goal = null; + + private StringList includeArtifacts = null; + private StringList excludeArtifacts = null; + private boolean ignorePoms = false; + + /** + * Puts the plugin configuration element as a new layer into {@link VulasConfiguration}. + * If no such element exists, e.g., because the POM file does not contain a plugin section for Vulas, default settings + * are established using {@link MavenProject} and {@link VulasConfiguration#setPropertyIfEmpty(String, Object)}. + * + * @throws Exception + */ + public final void prepareConfiguration() throws Exception { + + // Delete any transient settings that remaining from a previous goal execution (if any) + final boolean contained_values = VulasConfiguration.getGlobal().clearTransientProperties(); + if (contained_values) + getLog().info("Transient configuration settings deleted"); + + // Get the configuration layer from the plugin configuration (can be null) + VulasConfiguration.getGlobal().addAfterSystemProperties("Plugin configuration", this.layeredConfiguration, null, true); + + // Check whether the application context can be established + Application app = null; + try { + app = CoreConfiguration.getAppContext(); + } + // In case the plugin is called w/o using the Vulas profile, project-specific settings are not set + // Set them using the project member + catch (ConfigurationException e) { + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_CTX_GROUP, this.project.getGroupId()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_CTX_ARTIF, this.project.getArtifactId()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_CTX_VERSI, this.project.getVersion()); + app = CoreConfiguration.getAppContext(); + } + + // Set defaults for all the paths + VulasConfiguration.getGlobal().setPropertyIfEmpty(VulasConfiguration.TMP_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "tmp").toString()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.UPLOAD_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "upload").toString()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_SRC_DIR, Paths.get(this.project.getBuild().getDirectory()).toString()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_TARGET_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "target").toString()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_INCLUDE_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "include").toString()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_LIB_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "lib").toString()); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.REP_DIR, Paths.get(this.project.getBuild().getDirectory(), "vulas", "report").toString()); + + // Read app constructs from src/main/java and target/classes + final String p = Paths.get(this.project.getBuild().getOutputDirectory()).toString() + "," + Paths.get(this.project.getBuild().getSourceDirectory()).toString(); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.APP_DIRS, p); + + // Test how-to get the reactor POM in a reliable manner + // The following method call fails if Maven is called with option -pl + getLog().info("Top level project: " + this.session.getTopLevelProject()); + getLog().info("Execution root dir: " + this.session.getExecutionRootDirectory()); + + // Includes, excludes and ignorePoms + this.includeArtifacts = new StringList(VulasConfiguration.getGlobal().getStringArray(INCLUDES, null)); + this.excludeArtifacts = new StringList(VulasConfiguration.getGlobal().getStringArray(EXCLUDES, null)); + this.ignorePoms = VulasConfiguration.getGlobal().getConfiguration().getBoolean(IGNORE_POMS, false); + } + + /** + * This method, called by Maven, first invokes {@link AbstractVulasMojo#createGoal()} and then {@link AbstractVulasMojo#executeGoal()}. + */ + public void execute() throws MojoExecutionException, MojoFailureException { + try { + this.prepareConfiguration(); + + final boolean do_process = this instanceof MvnPluginReport || this.isPassingFilter(this.project); + if (do_process) { + // Create the goal + this.createGoal(); + this.goal.setGoalClient(GoalClient.MAVEN_PLUGIN); + this.goal.setConfiguration(VulasConfiguration.getGlobal()); + + // Set the application paths + this.goal.addAppPaths(FileUtil.getPaths(VulasConfiguration.getGlobal().getStringArray(CoreConfiguration.APP_DIRS, null))); + + // Set the dependency paths + this.setKnownDependencies(); + + // Execute the goal + this.executeGoal(); + } + } + // Expected problems will be passed on as is + catch (MojoFailureException mfe) { + throw mfe; + } + // Unexpected problems (the goal execution terminates abnormally/unexpectedly) + catch (GoalExecutionException gee) { + throw new MojoExecutionException(gee.getMessage(), gee); + } + // Every other exception results in a MojoExecutionException (= unexpected) + catch (Exception e) { + throw new MojoExecutionException("Error during Vulas goal execution " + this.goal + ": ", e); + } + } + + /** + * Evaluates the configuration settings {@link AbstractVulasMojo#INCLUDES}, {@link AbstractVulasMojo#EXCLUDES} and {@link #IGNORE_POMS} to + * determine whether the given {@link MavenProject} shall be processed. + * + * @param _prj + * @return + */ + private boolean isPassingFilter(MavenProject _prj) { + boolean do_process = true; + + // Only included ones + if (!this.includeArtifacts.isEmpty()) { + do_process = this.includeArtifacts.contains(_prj.getArtifactId(), ComparisonMode.EQUALS, CaseSensitivity.CASE_INSENSITIVE); + if (do_process) + this.getLog().info("Artifact [" + _prj.getArtifactId() + "] explicitly included for processing via configuration parameter [" + INCLUDES + "]"); + else + this.getLog().warn("Artifact [" + _prj.getArtifactId() + "] will NOT be processed, it is not among those explicitly included for processing via configuration parameter [" + INCLUDES + "]"); + } + + // Excluded (explicitly or through packaging) + else { + if (!this.excludeArtifacts.isEmpty() && this.excludeArtifacts.contains(_prj.getArtifactId(), ComparisonMode.EQUALS, CaseSensitivity.CASE_INSENSITIVE)) { + this.getLog().warn("Artifact [" + _prj.getArtifactId() + "] explicitly excluded from processing via configuration parameter [" + EXCLUDES + "]"); + do_process = false; + } + if (do_process && this.ignorePoms && "POM".equalsIgnoreCase(_prj.getPackaging())) { + this.getLog().warn("Artifact [" + _prj.getArtifactId() + "] excluded from processing via configuration parameter [" + IGNORE_POMS + "]"); + do_process = false; + } + } + + return do_process; + } + + /** + * Creates the respective goal. + *

+ * MUST be overridden by subclasses. + * + * @return + */ + protected abstract void createGoal(); + + /** + * Simply calls {@AbstractAnalysisGoal#execute}. + *

+ * CAN be overridden by subclasses. + * + * @return + */ + protected void executeGoal() throws Exception { + this.goal.executeSync(); + } + + /** + * Identifies known dependencies and passes them to Mojos inheriting from {@link AbstractAppGoal}. + * Note that such subclasses should be annotated with "requiresDependencyResolution = ResolutionScope.TEST". + * + * @throws DependencyResolutionRequiredException + */ + private final void setKnownDependencies() throws DependencyResolutionRequiredException { + if (this.goal != null && this.goal instanceof AbstractAppGoal) { + + // ---- Determine dependencies (Vulas 2.x) /*final Set runtime_system_classpath = new HashSet(); runtime_system_classpath.addAll(project.getRuntimeClasspathElements()); @@ -223,54 +228,53 @@ private final void setKnownDependencies() throws DependencyResolutionRequiredExc //this.goal.addDepPath(Paths.get(resource)); getLog().info("Dependency [" + ++i + "]: " + resource); }*/ - - // ---- Determine dependencies (Vulas 3.x) - - // Not sure why this line existed, let's see what happens if I comment it out :) - //final ClassLoader originalContextClassLoader = currentThread().getContextClassLoader(); - - // Old way of learning about dependencies (getArtifacts seems much better) - //for (final String resource : project.getRuntimeClasspathElements()) { - - // Path to dependency info - final Map dep_for_path = new HashMap(); - - // Dependencies (direct and transitive), including Maven ID and file system path - @SuppressWarnings("deprecation") - final Set direct_artifacts = project.getDependencyArtifacts(); // The artifact class does not seem to tell whether it is a direct or transitive dependency (hence we keep the call and suppress the warning) - final Set artifacts = project.getArtifacts(); - int count = 0; - Dependency dep = null; - Library lib = null; - for(Artifact a: artifacts) { - // Create lib w/o SHA1 - lib = new Library(); - lib.setLibraryId(new LibraryId(a.getGroupId(), a.getArtifactId(), a.getVersion())); - - // Create dependency and put into map - dep = new Dependency(this.goal.getGoalContext().getApplication(), lib, Scope.valueOf(a.getScope().toUpperCase()), !direct_artifacts.contains(a), null, a.getFile().toPath().toString()); - dep_for_path.put(a.getFile().toPath(), dep); - - getLog().info("Dependency [" + StringUtil.padLeft(++count, 4) + "]: Dependency [libid=" + dep.getLib().getLibraryId() + ", path " + a.getFile().getPath() + ", direct=" + direct_artifacts.contains(a) + ", scope=" + dep.getScope() + "] created for Maven artifact [g=" + a.getGroupId() + ", a=" + a.getArtifactId() + ", base version=" + a.getBaseVersion() +", version=" + a.getVersion() + ", classifier=" + a.getClassifier() + "]"); - getLog().info(" " + this.trailToString(a.getDependencyTrail(), " => ")); - } - - //TODO: Is it necessary to check whether the above dependency (via getArtifacts) is actually the one added to the classpath (via project.getRuntimeClasspathElements())? - //TODO: It may be that a different version (file) is chosen due to conflict resolution. Still, those cases should also be visible in the frontend (archive view). - - ((AbstractAppGoal)this.goal).setKnownDependencies(dep_for_path); - } - } - - private final String trailToString(List _trail, String _sep) { - final StringBuffer b = new StringBuffer(); - if(_trail!=null) { - for(int i=0; i<_trail.size(); i++) { - if(i>0) - b.append(_sep); - b.append(_trail.get(i)); - } - } - return b.toString(); - } + + // ---- Determine dependencies (Vulas 3.x) + + // Not sure why this line existed, let's see what happens if I comment it out :) + //final ClassLoader originalContextClassLoader = currentThread().getContextClassLoader(); + + // Old way of learning about dependencies (getArtifacts seems much better) + //for (final String resource : project.getRuntimeClasspathElements()) { + + // Path to dependency info + final Map dep_for_path = new HashMap(); + + // Dependencies (direct and transitive), including Maven ID and file system path + @SuppressWarnings("deprecation") final Set direct_artifacts = project.getDependencyArtifacts(); // The artifact class does not seem to tell whether it is a direct or transitive dependency (hence we keep the call and suppress the warning) + final Set artifacts = project.getArtifacts(); + int count = 0; + Dependency dep = null; + Library lib = null; + for (Artifact a : artifacts) { + // Create lib w/o SHA1 + lib = new Library(); + lib.setLibraryId(new LibraryId(a.getGroupId(), a.getArtifactId(), a.getVersion())); + + // Create dependency and put into map + dep = new Dependency(this.goal.getGoalContext().getApplication(), lib, Scope.valueOf(a.getScope().toUpperCase()), !direct_artifacts.contains(a), null, a.getFile().toPath().toString()); + dep_for_path.put(a.getFile().toPath(), dep); + + getLog().info("Dependency [" + StringUtil.padLeft(++count, 4) + "]: Dependency [libid=" + dep.getLib().getLibraryId() + ", path " + a.getFile().getPath() + ", direct=" + direct_artifacts.contains(a) + ", scope=" + dep.getScope() + "] created for Maven artifact [g=" + a.getGroupId() + ", a=" + a.getArtifactId() + ", base version=" + a.getBaseVersion() + ", version=" + a.getVersion() + ", classifier=" + a.getClassifier() + "]"); + getLog().info(" " + this.trailToString(a.getDependencyTrail(), " => ")); + } + + //TODO: Is it necessary to check whether the above dependency (via getArtifacts) is actually the one added to the classpath (via project.getRuntimeClasspathElements())? + //TODO: It may be that a different version (file) is chosen due to conflict resolution. Still, those cases should also be visible in the frontend (archive view). + + ((AbstractAppGoal) this.goal).setKnownDependencies(dep_for_path); + } + } + + private final String trailToString(List _trail, String _sep) { + final StringBuffer b = new StringBuffer(); + if (_trail != null) { + for (int i = 0; i < _trail.size(); i++) { + if (i > 0) + b.append(_sep); + b.append(_trail.get(i)); + } + } + return b.toString(); + } } diff --git a/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/VulasAgentMojo.java b/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/VulasAgentMojo.java new file mode 100644 index 000000000..5daa8b930 --- /dev/null +++ b/plugin-maven/src/main/java/com/sap/psr/vulas/mvn/VulasAgentMojo.java @@ -0,0 +1,271 @@ +package com.sap.psr.vulas.mvn; + + +import com.sap.psr.vulas.core.util.CoreConfiguration; +import com.sap.psr.vulas.shared.connectivity.Service; +import com.sap.psr.vulas.shared.util.StringUtil; +import com.sap.psr.vulas.shared.util.VulasConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Environment; + +import java.io.File; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.String.format; + +@Mojo(name = "prepare-vulas-agent", defaultPhase = LifecyclePhase.INITIALIZE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true) +public class VulasAgentMojo extends AbstractVulasMojo { + + + @Parameter(property = "vulas.agent.propertyName") + private String propertyName; + + @Parameter(property = "plugin.artifactMap", required = true, readonly = true) + private Map pluginArtifactMap; + + + private static final String ECLIPSE_TEST_PLUGIN = "eclipse-test-plugin"; + + + private static final String VULAS_AGENT_ARTIFACT_NAME = "com.sap.research.security.vulas:lang-java"; + private static final String VULAS_AGENT_ARTIFACT_CLASSIFIER = "jar-with-dependencies"; + + /** + * Name of the property used in maven-osgi-test-plugin. + */ + private static final String TYCHO_ARG_LINE = "tycho.testArgLine"; + /** + * Name of the property used in maven-surefire-plugin. + */ + private static final String SUREFIRE_ARG_LINE = "argLine"; + + + protected final class VulasAgentOptions { + + private final HashMap agentOptions = new HashMap<>(); + + /** + * Creates the options for Vuals' Java Agent and initializes them with reasonable defaults. + */ + public VulasAgentOptions() { + + //prepare vulas configuration + try { + VulasAgentMojo.this.prepareConfiguration(); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.MONI_PERIODIC_UPL_ENABLED, false); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_WRITE_CODE, false); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_MAX_STACKTRACES, 10); + VulasConfiguration.getGlobal().setPropertyIfEmpty(CoreConfiguration.INSTR_CHOOSEN_INSTR, "com.sap.psr.vulas.monitor.trace.SingleStackTraceInstrumentor"); + + } catch (Exception e) { + e.printStackTrace(); + } + + Configuration configuration = VulasConfiguration.getGlobal().getConfiguration(); + + //shared properties + setTmpDir(configuration.getString(VulasConfiguration.TMP_DIR)); + + //backend + setBackendURL(VulasConfiguration.getGlobal().getServiceUrl(Service.BACKEND)); + setBackendConnect(CoreConfiguration.ConnectType.READ_ONLY); + + //core properties + setVulasDummy("dummy"); + setUploadDir(configuration.getString(CoreConfiguration.UPLOAD_DIR)); + setPeriodicUpload(configuration.getBoolean(CoreConfiguration.MONI_PERIODIC_UPL_ENABLED)); + + //space token + setSpaceToken(configuration.getString(CoreConfiguration.SPACE_TOKEN)); + + //app agentOptions + setAppCtxGroup(configuration.getString(CoreConfiguration.APP_CTX_GROUP)); + setAppCtxArtifact(configuration.getString(CoreConfiguration.APP_CTX_ARTIF)); + setAppCtxVersion(configuration.getString(CoreConfiguration.APP_CTX_VERSI)); + //instrumentation config + setWriteCode(configuration.getBoolean(CoreConfiguration.INSTR_WRITE_CODE)); + setMaxStackTraces(configuration.getInt(CoreConfiguration.INSTR_MAX_STACKTRACES)); + setInstrumentors(configuration.getStringArray(CoreConfiguration.INSTR_CHOOSEN_INSTR)); + + } + + void setVulasDummy(String dummy) { + this.agentOptions.put("vulas.core.dummy", dummy); + } + + void setSpaceToken(String spaceToken) { + this.agentOptions.put(CoreConfiguration.SPACE_TOKEN, spaceToken); + } + + void setBackendURL(String backendURL) { + this.agentOptions.put("vulas.shared.backend.serviceUrl", backendURL); + } + + void setBackendConnect(CoreConfiguration.ConnectType connectType) { + this.agentOptions.put(CoreConfiguration.BACKEND_CONNECT, connectType.toString()); + } + + void setAppCtxGroup(String appCtxGroup) { + this.agentOptions.put(CoreConfiguration.APP_CTX_GROUP, appCtxGroup); + } + + void setAppCtxArtifact(String appCtxArtifact) { + this.agentOptions.put(CoreConfiguration.APP_CTX_ARTIF, appCtxArtifact); + } + + void setAppCtxVersion(String appCtxVersion) { + this.agentOptions.put(CoreConfiguration.APP_CTX_VERSI, appCtxVersion); + } + + void setPeriodicUpload(boolean enabled) { + this.agentOptions.put(CoreConfiguration.MONI_PERIODIC_UPL_ENABLED, String.valueOf(enabled)); + } + + + void setUploadDir(String uploadDir) { + this.agentOptions.put(CoreConfiguration.UPLOAD_DIR, uploadDir); + } + + void setTmpDir(String tmpDir) { + this.agentOptions.put(VulasConfiguration.TMP_DIR, tmpDir); + } + + void setWriteCode(boolean enable) { + this.agentOptions.put(CoreConfiguration.INSTR_WRITE_CODE, String.valueOf(enable)); + } + + void setMaxStackTraces(int maxNumberOfStackTraces) { + this.agentOptions.put(CoreConfiguration.INSTR_MAX_STACKTRACES, String.valueOf(maxNumberOfStackTraces)); + } + + void setInstrumentors(String[] vulasInstr) { + String selectedInstr = StringUtil.join(vulasInstr, ","); + this.agentOptions.put(CoreConfiguration.INSTR_CHOOSEN_INSTR, selectedInstr); + } + + + public String prependVMArguments(final String arguments, final File agentJarFile) { + + CommandlineJava commandlineJava = new CommandlineJava() { + @Override + public void setVm(String vm) { + //do not set "**/java" as the first command + } + }; + + //add the javaagent + commandlineJava.createVmArgument().setLine(format("-javaagent:%s", agentJarFile)); + + //remove any javaagent with the same file name from the arguments + final String[] args = Commandline.translateCommandline(arguments); + // the new javaagent, as used by the prepare-vulas-agent goal + final String regexForCurrentVulasAgent = format("-javaagent:(\"?)%s(\"?)", Pattern.quote(agentJarFile.toString())); + // the default name of the original javaagent + final String regexForOldVulasAgent = format("-javaagent:(\"?).*%s(\"?)", Pattern.quote("/vulas/lib/vulas-core-latest-jar-with-dependencies.jar")); + + ArrayList patterns = new ArrayList<>(); + patterns.add(regexForCurrentVulasAgent); + patterns.add(regexForOldVulasAgent); + // go through the arguments to check for existing javaagents + argprocess: for (String arg : args) { + + // check if one of vulas's agents is already defined as an arg + // if yes ignore the arg + for (String regExExp : patterns) { + if (arg.matches(regExExp)) { + continue argprocess; + } + + } + commandlineJava.createArgument().setLine(arg); + + } + //add my properties + for (final String key : this.agentOptions.keySet()) { + final String value = this.agentOptions.get(key); + if (value != null && !value.isEmpty()) { + Environment.Variable variable = new Environment.Variable(); + variable.setKey(key); + variable.setValue(value); + commandlineJava.addSysproperty(variable); + } + } + + //add -noverify + commandlineJava.createVmArgument().setValue("-noverify"); + + return commandlineJava.toString(); + } + + + } + + @Override + public void execute() throws MojoExecutionException { + + final String name = getEffectivePropertyName(); + final Properties projectProperties = this.project.getProperties(); + final String oldValue = projectProperties.getProperty(name); + + VulasAgentOptions vulasAgentOptions = createVulasAgentConfig(); + final String newValue = vulasAgentOptions.prependVMArguments( + oldValue, getAgentJarFile()); + getLog().info(name + " set to " + newValue); + projectProperties.setProperty(name, newValue); + } + + + File getAgentJarFile() throws MojoExecutionException { + final Artifact vulasAgentArtifact = pluginArtifactMap + .get(VULAS_AGENT_ARTIFACT_NAME); + if (vulasAgentArtifact == null || !vulasAgentArtifact.hasClassifier() || !vulasAgentArtifact.getClassifier().equals(VULAS_AGENT_ARTIFACT_CLASSIFIER)) { + throw new MojoExecutionException("Could not found " + VULAS_AGENT_ARTIFACT_NAME + ":" + VULAS_AGENT_ARTIFACT_CLASSIFIER); + } + return vulasAgentArtifact.getFile(); + } + + + @Override + protected void createGoal() { + throw new RuntimeException("Create Goal not valid for Mojo: " + this.getClass().getName()); + + } + + private VulasAgentOptions createVulasAgentConfig() { + return new VulasAgentOptions(); + } + + /** + * Same as done in jacoco + * checks which property should be modified + * + * @return the name of the property to modify + */ + private String getEffectivePropertyName() { + if (isPropertyNameSpecified()) { + return propertyName; + } + if (isEclipseTestPluginPackaging()) { + return TYCHO_ARG_LINE; + } + return SUREFIRE_ARG_LINE; + } + + private boolean isPropertyNameSpecified() { + return propertyName != null && !propertyName.isEmpty(); + } + + private boolean isEclipseTestPluginPackaging() { + return ECLIPSE_TEST_PLUGIN.equals(project.getPackaging()); + } +} diff --git a/plugin-maven/src/test/java/com/sap/psr/vulas/mvn/TestProjectStub.java b/plugin-maven/src/test/java/com/sap/psr/vulas/mvn/TestProjectStub.java new file mode 100644 index 000000000..e92cd3627 --- /dev/null +++ b/plugin-maven/src/test/java/com/sap/psr/vulas/mvn/TestProjectStub.java @@ -0,0 +1,91 @@ +package com.sap.psr.vulas.mvn; + + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Build; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; +import org.apache.maven.shared.utils.ReaderFactory; + +import java.io.File; +import java.util.*; + +public class TestProjectStub + extends MavenProjectStub { + private final String projectPath; + private Map/**/pluginArtifactMap = new HashMap/**/(); + + /** + * Default constructor + */ + public TestProjectStub(String projectPath, String pomFile) { + this.projectPath = projectPath; + + MavenXpp3Reader pomReader = new MavenXpp3Reader(); + Model model; + try { + model = pomReader.read(ReaderFactory.newXmlReader(new File(getBasedir(), pomFile))); + setModel(model); + } catch (Exception e) { + throw new RuntimeException(e); + } + + setGroupId(model.getGroupId()); + setArtifactId(model.getArtifactId()); + setVersion(model.getVersion()); + setName(model.getName()); + setUrl(model.getUrl()); + setPackaging(model.getPackaging()); + + + Build build = new Build(); + build.setFinalName(model.getArtifactId()); + build.setDirectory(getBasedir() + "/target"); + build.setSourceDirectory(getBasedir() + "/src/main/java"); + build.setOutputDirectory(getBasedir() + "/target/classes"); + build.setTestSourceDirectory(getBasedir() + "/src/test/java"); + build.setTestOutputDirectory(getBasedir() + "/target/test-classes"); + build.setPlugins(model.getBuild().getPlugins()); + build.setExtensions(model.getBuild().getExtensions()); + setBuild(build); + + + List compileSourceRoots = new ArrayList(); + compileSourceRoots.add(getBasedir() + "/src/main/java"); + setCompileSourceRoots(compileSourceRoots); + + List testCompileSourceRoots = new ArrayList(); + testCompileSourceRoots.add(getBasedir() + "/src/test/java"); + setTestCompileSourceRoots(testCompileSourceRoots); + + + + } + + + /** + * {@inheritDoc} + */ + public File getBasedir() { + return new File(super.getBasedir() + this.projectPath); + } + + + @Override + public Properties getProperties() { + return this.getModel().getProperties(); + } + + public Map/**/getPluginArtifactMap() { + return pluginArtifactMap; + } + + public void setPluginArtifactMap(Map/**/pluginArtifactMap) { + this.pluginArtifactMap = pluginArtifactMap; + } +} + diff --git a/plugin-maven/src/test/java/com/sap/psr/vulas/mvn/VulasAgentOptionsTests.java b/plugin-maven/src/test/java/com/sap/psr/vulas/mvn/VulasAgentOptionsTests.java new file mode 100644 index 000000000..e3a65f899 --- /dev/null +++ b/plugin-maven/src/test/java/com/sap/psr/vulas/mvn/VulasAgentOptionsTests.java @@ -0,0 +1,113 @@ +package com.sap.psr.vulas.mvn; + + +import com.sap.psr.vulas.mvn.VulasAgentMojo; +import com.sap.psr.vulas.shared.util.VulasConfiguration; +import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import org.apache.maven.plugin.testing.MojoRule; +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.configuration.PlexusConfiguration; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test for the AgentOptions standalone + */ +public class VulasAgentOptionsTests { + @Rule + public MojoRule rule = new MojoRule(); + + @Test + public void testCreateCommandLineArgs() throws Exception { + + TestProjectStub stub = new TestProjectStub("/target/test-classes/unitTestPom/", "pom.xml"); + + VulasAgentMojo myMojo = (VulasAgentMojo) rule.lookupConfiguredMojo(stub, "prepare-vulas-agent"); + assertNotNull(myMojo); + + + VulasAgentMojo.VulasAgentOptions vulasAgentOptions = myMojo.new VulasAgentOptions(); + + assertNotNull(vulasAgentOptions); + String finalArg = vulasAgentOptions.prependVMArguments("", new File("myaggent")); + System.out.println(finalArg); + assertTrue(finalArg.contains("-javaagent:myaggent")); + assertTrue(finalArg.contains("-noverify")); + + } + + @Test + public void testPrependCommandLineArgs() throws Exception { + + TestProjectStub stub = new TestProjectStub("/target/test-classes/unitTestPom/", "pom2.xml"); + + VulasAgentMojo myMojo = (VulasAgentMojo) rule.lookupConfiguredMojo(stub, "prepare-vulas-agent"); + assertNotNull(myMojo); + + + VulasAgentMojo.VulasAgentOptions vulasAgentOptions = myMojo.new VulasAgentOptions(); + + assertNotNull(vulasAgentOptions); + String finalArg = vulasAgentOptions.prependVMArguments("-Djava.security.manager -Djava.security.policy=${basedir}/src/test/resources/java.policy", new File("myaggent")); + System.out.println(finalArg); + assertTrue(finalArg.contains("-javaagent:myaggent")); + assertTrue(finalArg.contains("-noverify")); + assertTrue(finalArg.contains("-Djava.security.manager")); + assertTrue(finalArg.contains("-Djava.security.policy=${basedir}/src/test/resources/java.policy")); + + } + + + @Test + public void testRemoveDuplicateAgentCommandLineArgs() throws Exception { + + TestProjectStub stub = new TestProjectStub("/target/test-classes/unitTestPom/", "pom2.xml"); + + VulasAgentMojo myMojo = (VulasAgentMojo) rule.lookupConfiguredMojo(stub, "prepare-vulas-agent"); + assertNotNull(myMojo); + + VulasAgentMojo.VulasAgentOptions vulasAgentOptions = myMojo.new VulasAgentOptions(); + assertNotNull(vulasAgentOptions); + + String finalArg = vulasAgentOptions.prependVMArguments("-javaagent:myaggent", new File("myaggent")); + + assertNotNull(finalArg); + + assertTrue(finalArg.contains("-javaagent:myaggent")); + assertTrue(finalArg.contains("-noverify")); + + + } + + @Test + public void testRemoveOriginalAgentCommandLineArgs() throws Exception { + + TestProjectStub stub = new TestProjectStub("/target/test-classes/unitTestPom/", "pom2.xml"); + + VulasAgentMojo myMojo = (VulasAgentMojo) rule.lookupConfiguredMojo(stub, "prepare-vulas-agent"); + assertNotNull(myMojo); + + VulasAgentMojo.VulasAgentOptions vulasAgentOptions = myMojo.new VulasAgentOptions(); + assertNotNull(vulasAgentOptions); + + String finalArg = vulasAgentOptions.prependVMArguments("-javaagent:/mnt/c/Users/myuser/testproject/vulas/lib/vulas-core-latest-jar-with-dependencies.jar -DfooProp=bar", new File("myaggent")); + + assertNotNull(finalArg); + + assertTrue(finalArg.contains("-javaagent:myaggent")); + assertTrue(!finalArg.contains("/vulas/lib/vulas-core-latest-jar-with-dependencies.jar")); + + assertTrue(finalArg.contains("-noverify")); + + + } + + + + +} diff --git a/plugin-maven/src/test/resources/testproject/backwardComppom.xml b/plugin-maven/src/test/resources/testproject/backwardComppom.xml new file mode 100644 index 000000000..0ef38c67f --- /dev/null +++ b/plugin-maven/src/test/resources/testproject/backwardComppom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + foo.bar + sampletest + 1.0.0 + + + + 3.0.10-SNAPSHOT + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + junit + junit + 4.11 + test + + + + + org.apache.httpcomponents + httpclient + 4.1.3 + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy-vulas + generate-test-resources + + copy + + + + + com.sap.research.security.vulas + lang-java + ${vulas.version} + jar + jar-with-dependencies + ${project.build.directory}/vulas/lib + vulas-core-latest-jar-with-dependencies.jar + + + com.sap.research.security.vulas + lang-java + ${vulas.version} + jar + jar-with-dependencies + ${project.build.directory}/vulas/include + vulas-core-latest-jar-with-dependencies.jar + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.21.0 + + + 1 + 0 + + -javaagent:"${project.build.directory}/vulas/lib/vulas-core-latest-jar-with-dependencies.jar" + -Dvulas.shared.tmpDir=${project.build.directory}/vulas/tmp + -Dvulas.core.dummy=dummy + -Dvulas.core.backendConnection=READ_ONLY + -Dvulas.core.uploadDir=${project.build.directory}/vulas/upload + -Dvulas.core.monitor.periodicUpload.enabled=false + -Dvulas.core.appContext.group=${vulas.core.appContext.group} + -Dvulas.core.appContext.artifact=${vulas.core.appContext.artifact} + -Dvulas.core.appContext.version=${vulas.core.appContext.version} + -Dvulas.shared.backend.serviceUrl=REPLACE_WITH_BACKENDURL + -Dvulas.core.instr.writeCode=false + -Dvulas.core.instr.maxStacktraces=10 + -Dvulas.core.instr.instrumentorsChoosen=com.sap.psr.vulas.monitor.trace.StackTraceInstrumentor,com.sap.psr.vulas.monitor.touch.TouchPointInstrumentor + -noverify + + + + + + + + com.sap.research.security.vulas + plugin-maven + 3.0.10-SNAPSHOT + + + + wala + ${project.build.directory}/vulas/tmp + ${vulas.core.appContext.group} + ${vulas.core.appContext.artifact} + + ${vulas.core.appContext.version} + 1 + + ${project.build.directory}/vulas/upload + + + ${project.build.directory}/classes,${project.basedir}/src/main/java,${project.basedir}/src/main/python + + + ${project.build.directory} + ${project.build.directory}/vulas/target + ${project.build.directory}/vulas/include + + ${project.build.directory}/vulas/lib + false + + com.sap.psr.vulas.monitor.trace.StackTraceInstrumentor,com.sap.psr.vulas.monitor.touch.TouchPointInstrumentor + + false + + NO_FLOW_TO_CASTS_NO_METHOD_INVOKE + + 15 + + + ${project.build.directory}/vulas/report + + + + + + + + + + + diff --git a/plugin-maven/src/test/resources/testproject/mixedpom.xml b/plugin-maven/src/test/resources/testproject/mixedpom.xml new file mode 100644 index 000000000..c3f274474 --- /dev/null +++ b/plugin-maven/src/test/resources/testproject/mixedpom.xml @@ -0,0 +1,161 @@ + + + 4.0.0 + + foo.bar + sampletest + 1.0.0 + + + + 3.0.10-SNAPSHOT + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + junit + junit + 4.11 + test + + + + + org.apache.httpcomponents + httpclient + 4.1.3 + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy-vulas + generate-test-resources + + copy + + + + + com.sap.research.security.vulas + lang-java + ${vulas.version} + jar + jar-with-dependencies + ${project.build.directory}/vulas/lib + vulas-core-latest-jar-with-dependencies.jar + + + com.sap.research.security.vulas + lang-java + ${vulas.version} + jar + jar-with-dependencies + ${project.build.directory}/vulas/include + vulas-core-latest-jar-with-dependencies.jar + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + 1 + 0 + + -javaagent:"${project.build.directory}/vulas/lib/vulas-core-latest-jar-with-dependencies.jar" + -Dvulas.shared.backend.serviceUrl=REPLACE_WITH_BACKENDURL + -Dvulas.shared.tmpDir=${project.build.directory}/vulas/tmp + -Dvulas.core.dummy=dummy + -Dvulas.core.backendConnection=READ_ONLY + -Dvulas.core.uploadDir=${project.build.directory}/vulas/upload + -Dvulas.core.monitor.periodicUpload.enabled=false + -Dvulas.core.appContext.group=${project.groupId} + -Dvulas.core.appContext.artifact=${project.artifactId} + -Dvulas.core.appContext.version=${project.version} + -Dvulas.core.instr.writeCode=false + -Dvulas.core.instr.maxStacktraces=10 + -Dvulas.core.instr.instrumentorsChoosen=com.sap.psr.vulas.monitor.trace.StackTraceInstrumentor,com.sap.psr.vulas.monitor.touch.TouchPointInstrumentor + -noverify + + + + + + + + com.sap.research.security.vulas + plugin-maven + 3.0.10-SNAPSHOT + + + pre-unit-test + + prepare-vulas-agent + + + + + + + wala + ${project.build.directory}/vulas/tmp + ${vulas.core.appContext.group} + ${vulas.core.appContext.artifact} + + ${vulas.core.appContext.version} + 1 + + ${project.build.directory}/vulas/upload + + + ${project.build.directory}/classes,${project.basedir}/src/main/java,${project.basedir}/src/main/python + + + ${project.build.directory} + ${project.build.directory}/vulas/target + ${project.build.directory}/vulas/include + + ${project.build.directory}/vulas/lib + false + + com.sap.psr.vulas.monitor.trace.StackTraceInstrumentor,com.sap.psr.vulas.monitor.touch.TouchPointInstrumentor + + false + + NO_FLOW_TO_CASTS_NO_METHOD_INVOKE + + 15 + + + ${project.build.directory}/vulas/report + + + + + + + + + + + diff --git a/plugin-maven/src/test/resources/testproject/pom.xml b/plugin-maven/src/test/resources/testproject/pom.xml new file mode 100644 index 000000000..208fac7ae --- /dev/null +++ b/plugin-maven/src/test/resources/testproject/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + foo.bar + sampletest + 1.0.0 + + + + 3.0.10-SNAPSHOT + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + junit + junit + 4.11 + test + + + + + org.apache.httpcomponents + httpclient + 4.1.3 + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.3-SNAPSHOT + + + + prepare-agent + + + + + + + com.sap.research.security.vulas + plugin-maven + 3.0.10-SNAPSHOT + + + pre-unit-test + + prepare-vulas-agent + + + + + + + wala + ${project.build.directory}/vulas/tmp + ${vulas.core.appContext.group} + ${vulas.core.appContext.artifact} + + ${vulas.core.appContext.version} + 1 + + ${project.build.directory}/vulas/upload + + + ${project.build.directory}/classes,${project.basedir}/src/main/java,${project.basedir}/src/main/python + + + ${project.build.directory} + ${project.build.directory}/vulas/target + ${project.build.directory}/vulas/include + + ${project.build.directory}/vulas/lib + false + + com.sap.psr.vulas.monitor.trace.StackTraceInstrumentor,com.sap.psr.vulas.monitor.touch.TouchPointInstrumentor + + false + + NO_FLOW_TO_CASTS_NO_METHOD_INVOKE + + 15 + + + ${project.build.directory}/vulas/report + + + + + + + + + + + diff --git a/plugin-maven/src/test/resources/testproject/src/main/java/com/acme/Simple.java b/plugin-maven/src/test/resources/testproject/src/main/java/com/acme/Simple.java new file mode 100644 index 000000000..0395bb38b --- /dev/null +++ b/plugin-maven/src/test/resources/testproject/src/main/java/com/acme/Simple.java @@ -0,0 +1,75 @@ +package com.acme; + +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; + + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import javax.security.cert.CertificateException; +import javax.security.cert.X509Certificate; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.impl.client.DefaultHttpClient; + +public class Simple { + + public void callHttpClient(String _url) throws Exception { + + //build a trust manager, which checks if given certificates are valid. Here accept all certificates, never throw an exception + X509TrustManager tm = new X509TrustManager() { + + /** Same method name and signature as below, only difference is that argument type belongs to package javax.security.cert and the other one to java.security.cert. */ + public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + /** Same method name and signature as below, only difference is that argument type belongs to package javax.security.cert and the other one to java.security.cert. */ + public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + public void checkClientTrusted( + java.security.cert.X509Certificate[] chain, String authType) + throws java.security.cert.CertificateException { + // TODO Auto-generated method stub + + } + + public void checkServerTrusted( + java.security.cert.X509Certificate[] chain, String authType) + throws java.security.cert.CertificateException { + // TODO Auto-generated method stub + + } + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + // TODO Auto-generated method stub + return null; + } + }; + + + SSLContext sslcontext = SSLContext.getInstance("SSL"); + sslcontext.init(null, new TrustManager[]{tm}, null); + + SSLSocketFactory sslsf = new SSLSocketFactory(sslcontext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + SchemeRegistry schemeRegistry = new SchemeRegistry(); + schemeRegistry.register(new Scheme("http", new PlainSocketFactory(), 80)); + schemeRegistry.register(new Scheme("https", sslsf, 443)); + + ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(schemeRegistry); + HttpClient client = new DefaultHttpClient(manager); + HttpGet httpGet = new HttpGet(_url); + try { + client.execute(httpGet); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + +} \ No newline at end of file diff --git a/plugin-maven/src/test/resources/testproject/src/test/java/SimpleTest.java b/plugin-maven/src/test/resources/testproject/src/test/java/SimpleTest.java new file mode 100644 index 000000000..ee2323605 --- /dev/null +++ b/plugin-maven/src/test/resources/testproject/src/test/java/SimpleTest.java @@ -0,0 +1,24 @@ +import static org.junit.Assert.assertEquals; + +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.nio.file.Paths; + +import org.junit.Test; + +import com.acme.Simple; + +public class SimpleTest { + + @Test + public void callHttpClientTest() { + Simple p = null; + try { + p = new Simple(); + p.callHttpClient("https://example.com"); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/plugin-maven/src/test/resources/unitTestPom/pom.xml b/plugin-maven/src/test/resources/unitTestPom/pom.xml new file mode 100644 index 000000000..305e4f2c7 --- /dev/null +++ b/plugin-maven/src/test/resources/unitTestPom/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + foo.bar + vulas-testpom + 1.0.0 + pom + + + + 3.0.10-SNAPSHOT + + + + + + com.sap.research.security.vulas + plugin-maven + ${vulas.version} + + + + + + diff --git a/plugin-maven/src/test/resources/unitTestPom/pom2.xml b/plugin-maven/src/test/resources/unitTestPom/pom2.xml new file mode 100644 index 000000000..cef43cf94 --- /dev/null +++ b/plugin-maven/src/test/resources/unitTestPom/pom2.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + foo.bar + vulas-testpom + 1.0.0 + pom + + + + 3.0.10-SNAPSHOT + -Djava.security.manager -Djava.security.policy=${basedir}/src/test/resources/java.policy + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + + + com.sap.research.security.vulas + plugin-maven + ${vulas.version} + + + + + +