-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JAVA-3738 add Download and Attach Agent Task
- Loading branch information
1 parent
e47365f
commit 30a76df
Showing
5 changed files
with
278 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Contrast Gradle Plugin | ||
|
||
Gradle plugin for including the Contrast Security analysis in Java web applications | ||
|
||
## Building | ||
Requires Java 21 to build | ||
|
||
Use `./gradlew build` to build the plugin | ||
|
||
## Publishing to MavenLocal | ||
To publish this plugin to your mavenLocal apply the `maven-publish` plugin to this project's `build.gradle` file and run: | ||
|
||
|
||
```shell | ||
./gradlew publishToMavenLocal | ||
``` | ||
|
||
## Coniguration | ||
Attaching the Java agent with this plugin relies on your API credentials being set in the following env variables: | ||
```shell | ||
export CONTRAST__API__URL=https://app.contrastsecurity.com/Contrast/api | ||
export CONTRAST__API__USER_NAME=<your-user-name> | ||
export CONTRAST__API__API_KEY=<your-api-key> | ||
export CONTRAST__API__SERVICE_KEY=<your-service-key> | ||
export CONTRAST__API__ORGANIZATION_ID=<your-organization-id> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
...ugin/src/main/java/com/contrastsecurity/gradle/plugin/ContrastConfigurationExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package com.contrastsecurity.gradle.plugin; | ||
|
||
/** Extension for configuring TeamServer API Credentials for downloading agent */ | ||
public class ContrastConfigurationExtension { | ||
final String username; | ||
final String apiKey; | ||
final String serviceKey; | ||
final String apiUrl; | ||
final String orgUuid; | ||
final String appName; | ||
final String serverName; | ||
final String jarPath; | ||
final String appVersion; | ||
|
||
// default constructor with null values | ||
// this is shit figure out what gradle wants | ||
public ContrastConfigurationExtension() { | ||
this.username = null; | ||
this.apiKey = null; | ||
this.serviceKey = null; | ||
this.apiUrl = null; | ||
this.orgUuid = null; | ||
this.appName = null; | ||
this.serverName = null; | ||
this.jarPath = null; | ||
this.appVersion = null; | ||
} | ||
|
||
public ContrastConfigurationExtension( | ||
final String username, | ||
final String apiKey, | ||
final String serviceKey, | ||
final String apiUrl, | ||
final String orgUuid, | ||
final String appName, | ||
final String serverName, | ||
final String jarPath, | ||
final String appVersion) { | ||
this.username = username; | ||
this.apiKey = apiKey; | ||
this.serviceKey = serviceKey; | ||
this.apiUrl = apiUrl; | ||
this.orgUuid = orgUuid; | ||
this.appName = appName; | ||
this.serverName = serverName; | ||
this.jarPath = jarPath; | ||
this.appVersion = appVersion; | ||
} | ||
|
||
public String getUsername() { | ||
return username; | ||
} | ||
|
||
public String getApiKey() { | ||
return apiKey; | ||
} | ||
|
||
public String getServiceKey() { | ||
return serviceKey; | ||
} | ||
|
||
public String getApiUrl() { | ||
return apiUrl; | ||
} | ||
|
||
public String getOrgUuid() { | ||
return orgUuid; | ||
} | ||
|
||
public String getAppName() { | ||
return appName; | ||
} | ||
|
||
public String getServerName() { | ||
return serverName; | ||
} | ||
|
||
public String getJarPath() { | ||
return jarPath; | ||
} | ||
|
||
public String getAppVersion() { | ||
return appVersion; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
gradle-plugin/src/main/java/com/contrastsecurity/gradle/plugin/InstallAgentTask.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package com.contrastsecurity.gradle.plugin; | ||
|
||
import static com.contrastsecurity.gradle.plugin.ContrastGradlePlugin.EXTENSION_NAME; | ||
|
||
import com.contrastsecurity.exceptions.UnauthorizedException; | ||
import com.contrastsecurity.models.AgentType; | ||
import com.contrastsecurity.sdk.ContrastSDK; | ||
import com.contrastsecurity.sdk.UserAgentProduct; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.nio.file.StandardOpenOption; | ||
import java.text.SimpleDateFormat; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Date; | ||
import org.gradle.api.DefaultTask; | ||
import org.gradle.api.tasks.JavaExec; | ||
import org.gradle.api.tasks.TaskAction; | ||
import org.gradle.internal.impldep.org.apache.commons.io.FileUtils; | ||
|
||
/** | ||
* Downloads the current java agent from TeamServer using Credentials provided by the | ||
* AgentCredentialsExtension | ||
*/ | ||
public class InstallAgentTask extends DefaultTask { | ||
|
||
final ContrastConfigurationExtension config = | ||
(ContrastConfigurationExtension) getProject().getExtensions().getByName(EXTENSION_NAME); | ||
|
||
@TaskAction | ||
void installAgent() { | ||
|
||
// create sdk object for connecting to Contrast | ||
final ContrastSDK sdk = connectToContrast(); | ||
|
||
// get agent, either from configured jar path or from TS | ||
final Path agent = retrieveAgent(sdk); | ||
|
||
attachAgentToTasks(agent.toAbsolutePath()); | ||
} | ||
|
||
/** Configures JavaExec tasks to run with the agent attached */ | ||
private void attachAgentToTasks(final Path agentPath) { | ||
getProject() | ||
.getTasks() | ||
.withType(JavaExec.class) | ||
.configureEach( | ||
task -> { | ||
task.jvmArgs(createContrastArgs(agentPath)); | ||
}); | ||
} | ||
|
||
/** | ||
* Creates the jvmArgs for running the java agent against JavaExec tasks | ||
* | ||
* @param agentPath preconfigured path to an agent defined by the ContrastConfigurationExtension | ||
* @return Set of arguments | ||
*/ | ||
private Collection<String> createContrastArgs(final Path agentPath) { | ||
final Collection<String> args = Collections.emptySet(); | ||
args.add("-javaagent:" + agentPath.toAbsolutePath()); | ||
args.add("-Dcontrast.override.appname=" + config.appName); | ||
args.add("-Dcontrast.server=" + config.serverName); | ||
args.add("-Dcontrast.env=qa"); | ||
|
||
String appVersion = config.getAppVersion(); | ||
if (appVersion == null) { | ||
appVersion = computeAppVersion(); | ||
} | ||
|
||
args.add("-Dcontrast.override.appversion=" + appVersion); | ||
|
||
return args; | ||
} | ||
|
||
/** | ||
* Shamelessly stolen from the maven plugin TODO check if we still want to do this based on travis | ||
* or circle build number | ||
* | ||
* @return computed AppVersion | ||
*/ | ||
private String computeAppVersion() { | ||
final Date currentDate = new Date(); | ||
String travisBuildNumber = System.getenv("TRAVIS_BUILD_NUMBER"); | ||
String circleBuildNum = System.getenv("CIRCLE_BUILD_NUM"); | ||
|
||
final String appVersionQualifier; | ||
if (travisBuildNumber != null) { | ||
appVersionQualifier = travisBuildNumber; | ||
} else if (circleBuildNum != null) { | ||
appVersionQualifier = circleBuildNum; | ||
} else { | ||
appVersionQualifier = new SimpleDateFormat("yyyyMMddHHmmss").format(currentDate); | ||
} | ||
return config.getAppName() + "-" + appVersionQualifier; | ||
} | ||
|
||
/** Use ContrastSDK to download agent and return the path where agent jar is stored */ | ||
private Path retrieveAgent(final ContrastSDK connection) { | ||
// Initially attempt to run agent from the previously configured location | ||
final String jarPath = config.getJarPath(); | ||
if (jarPath != null) { | ||
final Path agent = Paths.get(jarPath); | ||
if (!Files.exists(agent)) { | ||
throw new RuntimeException("Unable to find java agent at " + jarPath); | ||
} | ||
return agent; | ||
} | ||
|
||
// If no jar is provided, and no jarpath configured, attempt to retrieve the agent from TS | ||
final byte[] bytes; | ||
try { | ||
bytes = connection.getAgent(AgentType.JAVA, config.getOrgUuid()); | ||
} catch (IOException e) { | ||
throw new RuntimeException("Failed to retrieve Contrast Java Agent: " + e); | ||
} catch (UnauthorizedException e) { | ||
throw new RuntimeException( | ||
"\nWe contacted Contrast successfully but couldn't authorize with the credentials you provided. The error is:", | ||
e); | ||
} | ||
|
||
// Save the jar to the 'target' directory | ||
final Path target = Paths.get(getProject().getProjectDir().getPath()); | ||
try { | ||
FileUtils.forceMkdir(target.toFile()); | ||
} catch (final IOException e) { | ||
throw new RuntimeException("Unable to create directory " + target, e); | ||
} | ||
|
||
final Path agent = target.resolve(AGENT_NAME); | ||
try { | ||
Files.write(agent, bytes, StandardOpenOption.CREATE, StandardOpenOption.WRITE); | ||
} catch (final IOException e) { | ||
throw new RuntimeException("Unable to save the latest java agent.", e); | ||
} | ||
return agent; | ||
} | ||
|
||
/** Use ContrastSDK to download agent creds for running the agent */ | ||
private void downloadAgentCredentials(final ContrastSDK connection) {} | ||
|
||
/** Create ContrastSDK for connecting to TeamServer */ | ||
private ContrastSDK connectToContrast() { | ||
// TODO get plugin version for this as well | ||
final UserAgentProduct gradle = UserAgentProduct.of("contrast-gradle-plugin"); | ||
return new ContrastSDK.Builder(config.getUsername(), config.getServiceKey(), config.getApiKey()) | ||
.withApiUrl(config.getApiUrl()) | ||
// TODO figure out how to define this proxy | ||
// .withProxy(proxy) //with proxy? | ||
.withUserAgentProduct(gradle) | ||
.build(); | ||
} | ||
|
||
private static final String AGENT_NAME = "contrast.jar"; | ||
} |