Skip to content

Commit a3d0b71

Browse files
Fixes a bug where the the proxy settings were not being properly picked up And adds the last seen date as a String to the returned Application Data.
1 parent e95761a commit a3d0b71

File tree

8 files changed

+90
-35
lines changed

8 files changed

+90
-35
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,11 @@ When configuring in your config.json file, include the proxy settings in the arg
304304
### Docker
305305
When running the MCP server in Docker, you can configure the proxy by passing the relevant environment variables:
306306

307+
307308
```bash
308309
docker run \
309-
-e HTTP_PROXY="http://proxy.example.com:8080" \
310-
-e HTTPS_PROXY="http://proxy.example.com:8080" \
310+
-e http_proxy_host="proxy.example.com" \
311+
-e http_proxy_port="8080" \
311312
-e CONTRAST_HOST_NAME=example.contrastsecurity.com \
312313
-e CONTRAST_API_KEY=example \
313314
-e CONTRAST_SERVICE_KEY=example \
@@ -339,8 +340,8 @@ For VS Code configuration with Docker and proxy, modify the settings.json like t
339340
"CONTRAST_USERNAME",
340341
"-e",
341342
"CONTRAST_ORG_ID",
342-
"-e", "HTTP_PROXY=http://proxy.example.com:8080",
343-
"-e", "HTTPS_PROXY=http://proxy.example.com:8080",
343+
"-e", "http_proxy_host",
344+
"-e", "http_proxy_port",
344345
"-i",
345346
"--rm",
346347
"contrast/mcp-contrast:latest",
@@ -353,8 +354,8 @@ For VS Code configuration with Docker and proxy, modify the settings.json like t
353354
"CONTRAST_SERVICE_KEY": "example",
354355
"CONTRAST_USERNAME": "[email protected]",
355356
"CONTRAST_ORG_ID": "example",
356-
"HTTP_PROXY": "http://proxy.example.com:8080",
357-
"HTTP_PROXY": "http://proxy.example.com:8080"
357+
"http_proxy_host": "proxy.example.com",
358+
"http_proxy_port": "8080"
358359
}
359360
}
360361
}

src/main/java/com/contrast/labs/ai/mcp/contrast/ADRService.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class ADRService {
3232

3333
private static final Logger logger = LoggerFactory.getLogger(ADRService.class);
3434

35+
3536
@Value("${contrast.host-name:${CONTRAST_HOST_NAME:}}")
3637
private String hostName;
3738

@@ -47,13 +48,20 @@ public class ADRService {
4748
@Value("${contrast.org-id:${CONTRAST_ORG_ID:}}")
4849
private String orgID;
4950

51+
@Value("${http.proxy.host:${http_proxy_host:}}")
52+
private String httpProxyHost;
53+
54+
@Value("${http.proxy.port:${http_proxy_port:}}")
55+
private String httpProxyPort;
56+
57+
5058
@Tool(name = "get_ADR_Protect_Rules", description = "takes a application name and returns the protect / adr rules for the application")
5159
public ProtectData getProtectData(String applicationName) throws IOException {
5260
logger.info("Starting retrieval of protection rules for application: {}", applicationName);
5361
long startTime = System.currentTimeMillis();
5462

5563
try {
56-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
64+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
5765
logger.debug("ContrastSDK initialized successfully for application: {}", applicationName);
5866

5967
// Get application ID from name
@@ -90,7 +98,7 @@ public ProtectData getProtectDataByAppID(String appID) throws IOException {
9098

9199
try {
92100
// Initialize ContrastSDK
93-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
101+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
94102
logger.debug("ContrastSDK initialized successfully for application ID: {}", appID);
95103

96104
// Initialize SDK extension

src/main/java/com/contrast/labs/ai/mcp/contrast/AssessService.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@
4242
import org.springframework.stereotype.Service;
4343

4444
import java.io.IOException;
45-
import java.util.ArrayList;
46-
import java.util.HashSet;
47-
import java.util.List;
48-
import java.util.Optional;
49-
import java.util.Set;
45+
import java.util.*;
5046

5147
@Service
5248
public class AssessService {
@@ -69,11 +65,18 @@ public class AssessService {
6965
@Value("${contrast.org-id:${CONTRAST_ORG_ID:}}")
7066
private String orgID;
7167

68+
@Value("${http.proxy.host:${http_proxy_host:}}")
69+
private String httpProxyHost;
70+
71+
@Value("${http.proxy.port:${http_proxy_port:}}")
72+
private String httpProxyPort;
73+
74+
7275

7376
@Tool(name = "get_vulnerability_by_id", description = "takes a vulnerability ID ( vulnID ) and Application ID ( appID ) and returns details about the specific security vulnerability. If based on the stacktrace, the vulnerability looks like it is in code that is not in the codebase, the vulnerability may be in a 3rd party library, review the CVE data attached to that stackframe you believe the vulnerability exists in and if possible upgrade that library to the next non vulnerable version based on the remediation guidance.")
7477
public Vulnerability getVulnerabilityById(String vulnID, String appID) throws IOException {
7578
logger.info("Retrieving vulnerability details for vulnID: {} in application ID: {}", vulnID, appID);
76-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
79+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
7780
logger.debug("ContrastSDK initialized with host: {}", hostName);
7881

7982
try {
@@ -153,7 +156,7 @@ private Optional<LibraryLibraryObservation> findMatchingLibraryData(String stack
153156
@Tool(name = "get_vulnerability", description = "Takes a vulnerability ID (vulnID) and application name (app_name) and returns details about the specific security vulnerability. If based on the stacktrace, the vulnerability looks like it is in code that is not in the codebase, the vulnerability may be in a 3rd party library, review the CVE data attached to that stackframe you believe the vulnerability exists in and if possible upgrade that library to the next non vulnerable version based on the remediation guidance.")
154157
public Vulnerability getVulnerability(String vulnID, String app_name) throws IOException {
155158
logger.info("Retrieving vulnerability details for vulnID: {} in application: {}", vulnID, app_name);
156-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
159+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
157160
Optional<String> appID = Optional.empty();
158161
logger.debug("Searching for application ID matching name: {}", app_name);
159162

@@ -175,7 +178,7 @@ public Vulnerability getVulnerability(String vulnID, String app_name) throws IOE
175178
@Tool(name = "list_vulnerabilities_with_id", description = "Takes a Application ID ( appID ) and returns a list of vulnerabilities, please remember to include the vulnID in the response.")
176179
public List<VulnLight> listVulnsByAppId(String appID) throws IOException {
177180
logger.info("Listing vulnerabilities for application ID: {}", appID);
178-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
181+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
179182
try {
180183
List<Trace> traces = contrastSDK.getTraces(orgID, appID, new TraceFilterBody()).getTraces();
181184
logger.debug("Found {} vulnerability traces for application ID: {}", traces.size(), appID);
@@ -197,7 +200,7 @@ public List<VulnLight> listVulnsByAppId(String appID) throws IOException {
197200
@Tool(name = "list_vulnerabilities", description = "Takes an application name ( app_name ) and returns a list of vulnerabilities, please remember to include the vulnID in the response. ")
198201
public List<VulnLight> listVulnsInAppByName(String app_name) throws IOException {
199202
logger.info("Listing vulnerabilities for application: {}", app_name);
200-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
203+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
201204

202205
Optional<String> appID = Optional.empty();
203206
logger.debug("Searching for application ID matching name: {}", app_name);
@@ -226,15 +229,15 @@ public List<VulnLight> listVulnsInAppByName(String app_name) throws IOException
226229
@Tool(name = "list_applications", description = "Takes an application name (app_name) returns a list of active applications matching that name. Please remember to display the name, status and ID.")
227230
public List<ApplicationData> getApplications(String app_name) throws IOException {
228231
logger.info("Listing active applications matching name: {}", app_name);
229-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
232+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
230233
try {
231234
List<Application> applications = SDKHelper.getApplicationsWithCache(orgID, contrastSDK);
232235
logger.debug("Retrieved {} total applications from Contrast", applications.size());
233236

234237
List<ApplicationData> filteredApps = new ArrayList<>();
235238
for(Application app : applications) {
236239
if(app.getName().toLowerCase().contains(app_name.toLowerCase())) {
237-
filteredApps.add(new ApplicationData(app.getName(), app.getStatus(), app.getId(), app.getLastSeen(), app.getLanguage()));
240+
filteredApps.add(new ApplicationData(app.getName(), app.getStatus(), app.getId(), app.getLastSeen(), new Date(app.getLastSeen()).toString(), app.getLanguage()));
238241
logger.debug("Found matching application - ID: {}, Name: {}, Status: {}",
239242
app.getId(), app.getName(), app.getStatus());
240243
}
@@ -252,15 +255,15 @@ public List<ApplicationData> getApplications(String app_name) throws IOException
252255
@Tool(name = "list_all_applications", description = "Takes no argument and list all the applications")
253256
public List<ApplicationData> getAllApplications() throws IOException {
254257
logger.info("Listing all applications");
255-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
258+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
256259
try {
257260
List<Application> applications = SDKHelper.getApplicationsWithCache(orgID, contrastSDK);
258261
logger.debug("Retrieved {} total applications from Contrast", applications.size());
259262

260263
List<ApplicationData> returnedApps = new ArrayList<>();
261264
for(Application app : applications) {
262265
returnedApps.add(new ApplicationData(app.getName(), app.getStatus(), app.getId(),
263-
app.getLastSeen(), app.getLanguage()));
266+
app.getLastSeen(), new Date(app.getLastSeen()).toString(),app.getLanguage()));
264267
}
265268

266269
logger.info("Found {} applications", returnedApps.size());

src/main/java/com/contrast/labs/ai/mcp/contrast/RouteCoverageService.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class RouteCoverageService {
2222
private static final Logger logger = LoggerFactory.getLogger(RouteCoverageService.class);
2323

2424

25+
2526
@Value("${contrast.host-name:${CONTRAST_HOST_NAME:}}")
2627
private String hostName;
2728

@@ -37,11 +38,19 @@ public class RouteCoverageService {
3738
@Value("${contrast.org-id:${CONTRAST_ORG_ID:}}")
3839
private String orgID;
3940

41+
@Value("${http.proxy.host:${http_proxy_host:}}")
42+
private String httpProxyHost;
43+
44+
@Value("${http.proxy.port:${http_proxy_port:}}")
45+
private String httpProxyPort;
46+
47+
48+
4049
@Tool(name = "get_application_route_coverage", description = "takes a application name and return the route coverage data for that application. " +
4150
"If a route/endpoint is DISCOVERED, it means it has been found by Assess but that route has had no inbound http requests. If it is EXERCISED, it means it has had atleast one inbound http request to that route/endpoint.")
4251
public RouteCoverageResponse getRouteCoverage(String app_name) throws IOException {
4352
logger.info("Retrieving route coverage for application by name: {}", app_name);
44-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
53+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
4554
SDKExtension sdkExtension = new SDKExtension(contrastSDK);
4655
Optional<String> appID = Optional.empty();
4756
logger.debug("Searching for application ID matching name: {}", app_name);
@@ -78,7 +87,7 @@ public RouteCoverageResponse getRouteCoverage(String app_name) throws IOExceptio
7887
"If a route/endpoint is DISCOVERED, it means it has been found by Assess but that route has had no inbound http requests. If it is EXERCISED, it means it has had atleast one inbound http request to that route/endpoint.")
7988
public RouteCoverageResponse getRouteCoverageByAppID(String app_id) throws IOException {
8089
logger.info("Retrieving route coverage for application by ID: {}", app_id);
81-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
90+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
8291
SDKExtension sdkExtension = new SDKExtension(contrastSDK);
8392

8493
logger.debug("Fetching route coverage data for application ID: {}", app_id);

src/main/java/com/contrast/labs/ai/mcp/contrast/SCAService.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class SCAService {
3939

4040
private static final Logger logger = LoggerFactory.getLogger(SCAService.class);
4141

42+
4243
@Value("${contrast.host-name:${CONTRAST_HOST_NAME:}}")
4344
private String hostName;
4445

@@ -54,10 +55,17 @@ public class SCAService {
5455
@Value("${contrast.org-id:${CONTRAST_ORG_ID:}}")
5556
private String orgID;
5657

58+
@Value("${http.proxy.host:${http_proxy_host:}}")
59+
private String httpProxyHost;
60+
61+
@Value("${http.proxy.port:${http_proxy_port:}}")
62+
private String httpProxyPort;
63+
64+
5765
@Tool(name = "list_application_libraries_by_app_id", description = "takes a application ID and returns the libraries used in the application, note if class usage count is 0 the library is unlikely to be used")
5866
public List<LibraryExtended> getApplicationLibrariesByID(String appID) throws IOException {
5967
logger.info("Retrieving libraries for application id: {}", appID);
60-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
68+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
6169
logger.debug("ContrastSDK initialized with host: {}", hostName);
6270

6371
SDKExtension extendedSDK = new SDKExtension(contrastSDK);
@@ -69,7 +77,7 @@ public List<LibraryExtended> getApplicationLibrariesByID(String appID) throws IO
6977
@Tool(name = "list_application_libraries", description = "takes a application name and returns the libraries used in the application, note if class usage count is 0 the library is unlikely to be used")
7078
public List<LibraryExtended> getApplicationLibraries(String app_name) throws IOException {
7179
logger.info("Retrieving libraries for application: {}", app_name);
72-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
80+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
7381
logger.debug("ContrastSDK initialized with host: {}", hostName);
7482

7583
SDKExtension extendedSDK = new SDKExtension(contrastSDK);
@@ -94,7 +102,7 @@ public List<LibraryExtended> getApplicationLibraries(String app_name) throws IOE
94102
@Tool(name= "list_applications_vulnerable_to_cve", description = "takes a cve id and returns the applications and servers vulnerable to the cve. Please note if the application class usage is 0, its unlikely to be vulnerable")
95103
public CveData listCVESForApplication(String cveid) throws IOException {
96104
logger.info("Retrieving applications vulnerable to CVE: {}", cveid);
97-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
105+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
98106

99107
logger.debug("ContrastSDK initialized with host: {}", hostName);
100108
contrastSDK.getLibrariesWithFilter(orgID, new LibraryFilterForm());

src/main/java/com/contrast/labs/ai/mcp/contrast/SastService.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class SastService {
3737

3838
private static final Logger logger = LoggerFactory.getLogger(SastService.class);
3939

40+
4041
@Value("${contrast.host-name:${CONTRAST_HOST_NAME:}}")
4142
private String hostName;
4243

@@ -52,10 +53,18 @@ public class SastService {
5253
@Value("${contrast.org-id:${CONTRAST_ORG_ID:}}")
5354
private String orgID;
5455

56+
@Value("${http.proxy.host:${http_proxy_host:}}")
57+
private String httpProxyHost;
58+
59+
@Value("${http.proxy.port:${http_proxy_port:}}")
60+
private String httpProxyPort;
61+
62+
63+
5564
@Tool(name = "list_Scan_Project", description = "takes a scan project name and returns the project details")
5665
public Project getScanProject(String projectName) throws IOException {
5766
logger.info("Retrieving scan project details for project: {}", projectName);
58-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
67+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
5968
logger.debug("ContrastSDK initialized with host: {}", hostName);
6069

6170
try {
@@ -72,7 +81,7 @@ public Project getScanProject(String projectName) throws IOException {
7281
@Tool(name = "list_Scan_Results", description = "takes a scan project name and returns the latest results in Sarif format")
7382
public String getLatestScanResult(String projectName) throws IOException {
7483
logger.info("Retrieving latest scan results in SARIF format for project: {}", projectName);
75-
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName);
84+
ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName,httpProxyHost, httpProxyPort);
7685
logger.debug("ContrastSDK initialized with host: {}", hostName);
7786

7887
try {

src/main/java/com/contrast/labs/ai/mcp/contrast/data/ApplicationData.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@
1515
*/
1616
package com.contrast.labs.ai.mcp.contrast.data;
1717

18-
public record ApplicationData(String name, String status, String appID, long lastSeen, String language) {
18+
import java.util.Date;
19+
20+
public record ApplicationData(String name, String status, String appID, long lastSeen, String lastSeenDate, String language) {
1921
}

src/main/java/com/contrast/labs/ai/mcp/contrast/sdkexstension/SDKHelper.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.contrastsecurity.http.LibraryFilterForm;
2929

3030
import java.io.IOException;
31+
import java.net.Proxy;
3132
import java.util.ArrayList;
3233
import java.util.EnumSet;
3334
import java.util.List;
@@ -159,12 +160,26 @@ public static List<LibraryObservation> getLibraryObservationsWithCache(
159160

160161
// The withUserAgentProduct will generate a user agent header that looks like
161162
// User-Agent: contrast-mcp/1.0 contrast-sdk-java/3.4.2 Java/19.0.2+7
162-
public static ContrastSDK getSDK(String hostName, String apiKey, String serviceKey, String userName) {
163-
logger.info("Initializing ContrastSDK with username: {}, host: {}", userName, hostName);
164-
return new ContrastSDK.Builder(userName, serviceKey, apiKey)
165-
.withApiUrl( "https://" + hostName + "/Contrast/api")
166-
.withUserAgentProduct(UserAgentProduct.of(MCP_SERVER_NAME,MCP_VERSION))
167-
.build();
163+
public static ContrastSDK getSDK(String hostName, String apiKey, String serviceKey, String userName, String httpProxyHost, String httpProxyPort) {
164+
logger.info("Initializing ContrastSDK with username: {}, host: {}", userName, hostName);
165+
166+
ContrastSDK.Builder builder = new ContrastSDK.Builder(userName, serviceKey, apiKey)
167+
.withApiUrl("https://" + hostName + "/Contrast/api")
168+
.withUserAgentProduct(UserAgentProduct.of(MCP_SERVER_NAME, MCP_VERSION));
169+
170+
if (httpProxyHost != null && !httpProxyHost.isEmpty()) {
171+
int port = httpProxyPort != null && !httpProxyPort.isEmpty() ? Integer.parseInt(httpProxyPort) : 80;
172+
logger.debug("Configuring HTTP proxy: {}:{}", httpProxyHost, port);
173+
174+
java.net.Proxy proxy = new java.net.Proxy(
175+
java.net.Proxy.Type.HTTP,
176+
new java.net.InetSocketAddress(httpProxyHost, port)
177+
);
178+
179+
builder.withProxy(proxy);
180+
}
181+
182+
return builder.build();
168183
}
169184

170185
/**

0 commit comments

Comments
 (0)