|
| 1 | +package com.contrast.labs.ai.mcp.contrast; |
| 2 | + |
| 3 | +import com.contrast.labs.ai.mcp.contrast.sdkexstension.SDKExtension; |
| 4 | +import com.contrast.labs.ai.mcp.contrast.sdkexstension.SDKHelper; |
| 5 | +import com.contrast.labs.ai.mcp.contrast.sdkexstension.data.routecoverage.Route; |
| 6 | +import com.contrast.labs.ai.mcp.contrast.sdkexstension.data.routecoverage.RouteCoverageResponse; |
| 7 | +import com.contrast.labs.ai.mcp.contrast.sdkexstension.data.routecoverage.RouteDetailsResponse; |
| 8 | +import com.contrastsecurity.models.Application; |
| 9 | +import com.contrastsecurity.sdk.ContrastSDK; |
| 10 | +import org.slf4j.Logger; |
| 11 | +import org.slf4j.LoggerFactory; |
| 12 | +import org.springframework.ai.tool.annotation.Tool; |
| 13 | +import org.springframework.beans.factory.annotation.Value; |
| 14 | +import org.springframework.stereotype.Service; |
| 15 | + |
| 16 | +import java.io.IOException; |
| 17 | +import java.util.Optional; |
| 18 | + |
| 19 | +@Service |
| 20 | +public class RouteCoverageService { |
| 21 | + |
| 22 | + private static final Logger logger = LoggerFactory.getLogger(RouteCoverageService.class); |
| 23 | + |
| 24 | + |
| 25 | + @Value("${contrast.host-name:${CONTRAST_HOST_NAME:}}") |
| 26 | + private String hostName; |
| 27 | + |
| 28 | + @Value("${contrast.api-key:${CONTRAST_API_KEY:}}") |
| 29 | + private String apiKey; |
| 30 | + |
| 31 | + @Value("${contrast.service-key:${CONTRAST_SERVICE_KEY:}}") |
| 32 | + private String serviceKey; |
| 33 | + |
| 34 | + @Value("${contrast.username:${CONTRAST_USERNAME:}}") |
| 35 | + private String userName; |
| 36 | + |
| 37 | + @Value("${contrast.org-id:${CONTRAST_ORG_ID:}}") |
| 38 | + private String orgID; |
| 39 | + |
| 40 | + @Tool(name = "get_application_route_coverage", description = "takes a application name and return the route coverage data for that application. " + |
| 41 | + "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.") |
| 42 | + public RouteCoverageResponse getRouteCoverage(String app_name) throws IOException { |
| 43 | + logger.info("Retrieving route coverage for application by name: {}", app_name); |
| 44 | + ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName); |
| 45 | + SDKExtension sdkExtension = new SDKExtension(contrastSDK); |
| 46 | + Optional<String> appID = Optional.empty(); |
| 47 | + logger.debug("Searching for application ID matching name: {}", app_name); |
| 48 | + |
| 49 | + for(Application app : SDKHelper.getApplicationsWithCache(orgID, contrastSDK)) { |
| 50 | + if(app.getName().toLowerCase().contains(app_name.toLowerCase())) { |
| 51 | + appID = Optional.of(app.getId()); |
| 52 | + logger.debug("Found matching application with ID: {}", appID.get()); |
| 53 | + break; |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + if (!appID.isPresent()) { |
| 58 | + logger.error("Application not found: {}", app_name); |
| 59 | + throw new IOException("Application not found: " + app_name); |
| 60 | + } |
| 61 | + |
| 62 | + logger.debug("Fetching route coverage data for application ID: {}", appID.get()); |
| 63 | + RouteCoverageResponse response = sdkExtension.getRouteCoverage(orgID, appID.get(), null); |
| 64 | + logger.debug("Found {} routes for application", response.getRoutes().size()); |
| 65 | + |
| 66 | + logger.debug("Retrieving route details for each route"); |
| 67 | + for(Route route : response.getRoutes()) { |
| 68 | + logger.trace("Fetching details for route: {}", route.getSignature()); |
| 69 | + RouteDetailsResponse routeDetailsResponse = sdkExtension.getRouteDetails(orgID, appID.get(), route.getRouteHash()); |
| 70 | + route.setRouteDetailsResponse(routeDetailsResponse); |
| 71 | + } |
| 72 | + |
| 73 | + logger.info("Successfully retrieved route coverage for application: {}", app_name); |
| 74 | + return response; |
| 75 | + } |
| 76 | + |
| 77 | + @Tool(name = "get_application_route_coverage_by_app_id", description = "takes a application id and return the route coverage data for that application. " + |
| 78 | + "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.") |
| 79 | + public RouteCoverageResponse getRouteCoverageByAppID(String app_id) throws IOException { |
| 80 | + logger.info("Retrieving route coverage for application by ID: {}", app_id); |
| 81 | + ContrastSDK contrastSDK = SDKHelper.getSDK(hostName, apiKey, serviceKey, userName); |
| 82 | + SDKExtension sdkExtension = new SDKExtension(contrastSDK); |
| 83 | + |
| 84 | + logger.debug("Fetching route coverage data for application ID: {}", app_id); |
| 85 | + RouteCoverageResponse response = sdkExtension.getRouteCoverage(orgID, app_id, null); |
| 86 | + logger.debug("Found {} routes for application", response.getRoutes().size()); |
| 87 | + |
| 88 | + logger.debug("Retrieving route details for each route"); |
| 89 | + for(Route route : response.getRoutes()) { |
| 90 | + logger.trace("Fetching details for route: {}", route.getSignature()); |
| 91 | + RouteDetailsResponse routeDetailsResponse = sdkExtension.getRouteDetails(orgID, app_id, route.getRouteHash()); |
| 92 | + route.setRouteDetailsResponse(routeDetailsResponse); |
| 93 | + } |
| 94 | + |
| 95 | + logger.info("Successfully retrieved route coverage for application ID: {}", app_id); |
| 96 | + return response; |
| 97 | + } |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | +} |
0 commit comments