1. Core Signal Sciences Client
// SignalSciencesClient.java package com.signalsciences; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.util.*; public class SignalSciencesClient { private final String baseUrl; private final String email; private final String apiToken; private final String corpName; private final CloseableHttpClient httpClient; private final ObjectMapper objectMapper; public SignalSciencesClient(String baseUrl, String email, String apiToken, String corpName) { this.baseUrl = baseUrl; this.email = email; this.apiToken = apiToken; this.corpName = corpName; this.httpClient = HttpClients.createDefault(); this.objectMapper = new ObjectMapper(); } // Corporation Management public List<Corporation> getCorporations() throws SignalSciencesException { return executeGet("/v0/corps", objectMapper.getTypeFactory().constructCollectionType(List.class, Corporation.class)); } public Corporation getCorporation(String corpName) throws SignalSciencesException { return executeGet("/v0/corps/" + corpName, Corporation.class); } // Site Management public List<Site> getSites() throws SignalSciencesException { return executeGet("/v0/corps/" + corpName + "/sites", objectMapper.getTypeFactory().constructCollectionType(List.class, Site.class)); } public Site getSite(String siteName) throws SignalSciencesException { return executeGet("/v0/corps/" + corpName + "/sites/" + siteName, Site.class); } public Site createSite(SiteRequest siteRequest) throws SignalSciencesException { return executePost("/v0/corps/" + corpName + "/sites", siteRequest, Site.class); } // Threat Intelligence public List<Threat> getRecentThreats(String siteName, Integer limit) throws SignalSciencesException { String url = "/v0/corps/" + corpName + "/sites/" + siteName + "/threats"; if (limit != null) { url += "?limit=" + limit; } return executeGet(url, objectMapper.getTypeFactory().constructCollectionType(List.class, Threat.class)); } public ThreatFeed getThreatFeed(String siteName) throws SignalSciencesException { return executeGet("/v0/corps/" + corpName + "/sites/" + siteName + "/feed/threats", ThreatFeed.class); } // Requests and Analytics public RequestAnalytics getRequestAnalytics(String siteName, AnalyticsQuery query) throws SignalSciencesException { String url = buildAnalyticsUrl("/v0/corps/" + corpName + "/sites/" + siteName + "/requests", query); return executeGet(url, RequestAnalytics.class); } public TimeseriesData getTimeseriesData(String siteName, AnalyticsQuery query) throws SignalSciencesException { String url = buildAnalyticsUrl("/v0/corps/" + corpName + "/sites/" + siteName + "/timeseries", query); return executeGet(url, TimeseriesData.class); } // Alerts and Notifications public List<Alert> getAlerts(String siteName, AlertQuery query) throws SignalSciencesException { String url = buildAlertUrl("/v0/corps/" + corpName + "/sites/" + siteName + "/alerts", query); return executeGet(url, objectMapper.getTypeFactory().constructCollectionType(List.class, Alert.class)); } // Custom Rules and Signals public List<CustomRule> getCustomRules(String siteName) throws SignalSciencesException { return executeGet("/v0/corps/" + corpName + "/sites/" + siteName + "/rules", objectMapper.getTypeFactory().constructCollectionType(List.class, CustomRule.class)); } public CustomRule createCustomRule(String siteName, CustomRule rule) throws SignalSciencesException { return executePost("/v0/corps/" + corpName + "/sites/" + siteName + "/rules", rule, CustomRule.class); } public void deleteCustomRule(String siteName, String ruleId) throws SignalSciencesException { executeDelete("/v0/corps/" + corpName + "/sites/" + siteName + "/rules/" + ruleId); } // IP Lists public List<IPList> getIPLists(String siteName) throws SignalSciencesException { return executeGet("/v0/corps/" + corpName + "/sites/" + siteName + "/ip-lists", objectMapper.getTypeFactory().constructCollectionType(List.class, IPList.class)); } public IPList createIPList(String siteName, IPList ipList) throws SignalSciencesException { return executePost("/v0/corps/" + corpName + "/sites/" + siteName + "/ip-lists", ipList, IPList.class); } // Private methods for HTTP operations private <T> T executeGet(String path, Class<T> responseType) throws SignalSciencesException { try { String url = baseUrl + path; HttpGet request = new HttpGet(url); addAuthHeaders(request); HttpResponse response = httpClient.execute(request); String responseBody = EntityUtils.toString(response.getEntity()); if (response.getStatusLine().getStatusCode() != 200) { throw new SignalSciencesException("GET request failed: " + responseBody); } return objectMapper.readValue(responseBody, responseType); } catch (IOException e) { throw new SignalSciencesException("GET request failed", e); } } private <T> T executeGet(String path, com.fasterxml.jackson.core.type.TypeReference<T> typeReference) throws SignalSciencesException { try { String url = baseUrl + path; HttpGet request = new HttpGet(url); addAuthHeaders(request); HttpResponse response = httpClient.execute(request); String responseBody = EntityUtils.toString(response.getEntity()); if (response.getStatusLine().getStatusCode() != 200) { throw new SignalSciencesException("GET request failed: " + responseBody); } return objectMapper.readValue(responseBody, typeReference); } catch (IOException e) { throw new SignalSciencesException("GET request failed", e); } } private <T> T executePost(String path, Object requestBody, Class<T> responseType) throws SignalSciencesException { try { String url = baseUrl + path; HttpPost request = new HttpPost(url); addAuthHeaders(request); String jsonBody = objectMapper.writeValueAsString(requestBody); StringEntity entity = new StringEntity(jsonBody); request.setEntity(entity); request.setHeader("Content-type", "application/json"); HttpResponse response = httpClient.execute(request); String responseBody = EntityUtils.toString(response.getEntity()); if (response.getStatusLine().getStatusCode() != 200) { throw new SignalSciencesException("POST request failed: " + responseBody); } return objectMapper.readValue(responseBody, responseType); } catch (IOException e) { throw new SignalSciencesException("POST request failed", e); } } private void executeDelete(String path) throws SignalSciencesException { try { String url = baseUrl + path; org.apache.http.client.methods.HttpDelete request = new org.apache.http.client.methods.HttpDelete(url); addAuthHeaders(request); HttpResponse response = httpClient.execute(request); if (response.getStatusLine().getStatusCode() != 204) { String responseBody = EntityUtils.toString(response.getEntity()); throw new SignalSciencesException("DELETE request failed: " + responseBody); } } catch (IOException e) { throw new SignalSciencesException("DELETE request failed", e); } } private void addAuthHeaders(org.apache.http.client.methods.HttpRequestBase request) { String auth = Base64.getEncoder().encodeToString((email + ":" + apiToken).getBytes()); request.setHeader("Authorization", "Basic " + auth); } private String buildAnalyticsUrl(String basePath, AnalyticsQuery query) { StringBuilder url = new StringBuilder(basePath); url.append("?from=").append(query.getFrom()); url.append("&until=").append(query.getUntil()); if (query.getInterval() != null) { url.append("&interval=").append(query.getInterval()); } if (query.getLimit() != null) { url.append("&limit=").append(query.getLimit()); } if (query.getFilters() != null && !query.getFilters().isEmpty()) { query.getFilters().forEach((key, value) -> url.append("&").append(key).append("=").append(value)); } return url.toString(); } private String buildAlertUrl(String basePath, AlertQuery query) { StringBuilder url = new StringBuilder(basePath); boolean hasParams = false; if (query.getSince() != null) { url.append("?since=").append(query.getSince()); hasParams = true; } if (query.getUntil() != null) { url.append(hasParams ? "&" : "?").append("until=").append(query.getUntil()); hasParams = true; } if (query.getLimit() != null) { url.append(hasParams ? "&" : "?").append("limit=").append(query.getLimit()); } return url.toString(); } public void close() throws IOException { httpClient.close(); } } 2. Data Models
// Signal Sciences Data Models package com.signalsciences; import java.util.*; public class Corporation { private String name; private String displayName; private Date createdAt; private Date updatedAt; private List<Site> sites; // Constructors, getters, and setters public Corporation() {} public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Date getUpdatedAt() { return updatedAt; } public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; } public List<Site> getSites() { return sites; } public void setSites(List<Site> sites) { this.sites = sites; } } public class Site { private String name; private String displayName; private String agentCode; private String agentKey; private Date createdAt; private Date updatedAt; private Map<String, Object> config; private List<CustomRule> rules; private List<IPList> ipLists; // Constructors, getters, and setters public Site() {} public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getAgentCode() { return agentCode; } public void setAgentCode(String agentCode) { this.agentCode = agentCode; } public String getAgentKey() { return agentKey; } public void setAgentKey(String agentKey) { this.agentKey = agentKey; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Date getUpdatedAt() { return updatedAt; } public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; } public Map<String, Object> getConfig() { return config; } public void setConfig(Map<String, Object> config) { this.config = config; } public List<CustomRule> getRules() { return rules; } public void setRules(List<CustomRule> rules) { this.rules = rules; } public List<IPList> getIpLists() { return ipLists; } public void setIpLists(List<IPList> ipLists) { this.ipLists = ipLists; } } public class SiteRequest { private String name; private String displayName; private Map<String, Object> config; public SiteRequest(String name, String displayName) { this.name = name; this.displayName = displayName; this.config = new HashMap<>(); } // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public Map<String, Object> getConfig() { return config; } public void setConfig(Map<String, Object> config) { this.config = config; } } public class Threat { private String id; private String source; private String type; private String severity; private String description; private Date timestamp; private String clientIP; private String country; private String userAgent; private String path; private String method; private Map<String, String> tags; private List<String> indicators; // Constructors, getters, and setters public Threat() {} public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public String getClientIP() { return clientIP; } public void setClientIP(String clientIP) { this.clientIP = clientIP; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getUserAgent() { return userAgent; } public void setUserAgent(String userAgent) { this.userAgent = userAgent; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public Map<String, String> getTags() { return tags; } public void setTags(Map<String, String> tags) { this.tags = tags; } public List<String> getIndicators() { return indicators; } public void setIndicators(List<String> indicators) { this.indicators = indicators; } } public class ThreatFeed { private List<Threat> threats; private Date generatedAt; private int totalCount; private Map<String, Integer> severityBreakdown; // Constructors, getters, and setters public ThreatFeed() {} public List<Threat> getThreats() { return threats; } public void setThreats(List<Threat> threats) { this.threats = threats; } public Date getGeneratedAt() { return generatedAt; } public void setGeneratedAt(Date generatedAt) { this.generatedAt = generatedAt; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public Map<String, Integer> getSeverityBreakdown() { return severityBreakdown; } public void setSeverityBreakdown(Map<String, Integer> severityBreakdown) { this.severityBreakdown = severityBreakdown; } } public class RequestAnalytics { private Date from; private Date until; private int totalRequests; private int blockedRequests; private int allowedRequests; private Map<String, Integer> attackTypes; private Map<String, Integer> topIPs; private Map<String, Integer> topPaths; private List<RequestDetail> recentRequests; // Constructors, getters, and setters public RequestAnalytics() {} public Date getFrom() { return from; } public void setFrom(Date from) { this.from = from; } public Date getUntil() { return until; } public void setUntil(Date until) { this.until = until; } public int getTotalRequests() { return totalRequests; } public void setTotalRequests(int totalRequests) { this.totalRequests = totalRequests; } public int getBlockedRequests() { return blockedRequests; } public void setBlockedRequests(int blockedRequests) { this.blockedRequests = blockedRequests; } public int getAllowedRequests() { return allowedRequests; } public void setAllowedRequests(int allowedRequests) { this.allowedRequests = allowedRequests; } public Map<String, Integer> getAttackTypes() { return attackTypes; } public void setAttackTypes(Map<String, Integer> attackTypes) { this.attackTypes = attackTypes; } public Map<String, Integer> getTopIPs() { return topIPs; } public void setTopIPs(Map<String, Integer> topIPs) { this.topIPs = topIPs; } public Map<String, Integer> getTopPaths() { return topPaths; } public void setTopPaths(Map<String, Integer> topPaths) { this.topPaths = topPaths; } public List<RequestDetail> getRecentRequests() { return recentRequests; } public void setRecentRequests(List<RequestDetail> recentRequests) { this.recentRequests = recentRequests; } } public class RequestDetail { private String id; private Date timestamp; private String clientIP; private String country; private String method; private String path; private String userAgent; private String action; // ALLOW, BLOCK, RATE_LIMIT private List<String> tags; private Map<String, String> headers; // Constructors, getters, and setters public RequestDetail() {} public String getId() { return id; } public void setId(String id) { this.id = id; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public String getClientIP() { return clientIP; } public void setClientIP(String clientIP) { this.clientIP = clientIP; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getUserAgent() { return userAgent; } public void setUserAgent(String userAgent) { this.userAgent = userAgent; } public String getAction() { return action; } public void setAction(String action) { this.action = action; } public List<String> getTags() { return tags; } public void setTags(List<String> tags) { this.tags = tags; } public Map<String, String> getHeaders() { return headers; } public void setHeaders(Map<String, String> headers) { this.headers = headers; } } public class TimeseriesData { private Date from; private Date until; private String interval; private List<DataPoint> data; // Constructors, getters, and setters public TimeseriesData() {} public Date getFrom() { return from; } public void setFrom(Date from) { this.from = from; } public Date getUntil() { return until; } public void setUntil(Date until) { this.until = until; } public String getInterval() { return interval; } public void setInterval(String interval) { this.interval = interval; } public List<DataPoint> getData() { return data; } public void setData(List<DataPoint> data) { this.data = data; } } public class DataPoint { private Date timestamp; private int totalRequests; private int blockedRequests; private int attackRequests; private Map<String, Integer> attackBreakdown; // Constructors, getters, and setters public DataPoint() {} public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public int getTotalRequests() { return totalRequests; } public void setTotalRequests(int totalRequests) { this.totalRequests = totalRequests; } public int getBlockedRequests() { return blockedRequests; } public void setBlockedRequests(int blockedRequests) { this.blockedRequests = blockedRequests; } public int getAttackRequests() { return attackRequests; } public void setAttackRequests(int attackRequests) { this.attackRequests = attackRequests; } public Map<String, Integer> getAttackBreakdown() { return attackBreakdown; } public void setAttackBreakdown(Map<String, Integer> attackBreakdown) { this.attackBreakdown = attackBreakdown; } } public class Alert { private String id; private String type; private String severity; private String description; private Date createdAt; private Date resolvedAt; private String status; private Map<String, Object> metadata; private List<String> affectedSites; // Constructors, getters, and setters public Alert() {} public String getId() { return id; } public void setId(String id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Date getResolvedAt() { return resolvedAt; } public void setResolvedAt(Date resolvedAt) { this.resolvedAt = resolvedAt; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Map<String, Object> getMetadata() { return metadata; } public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; } public List<String> getAffectedSites() { return affectedSites; } public void setAffectedSites(List<String> affectedSites) { this.affectedSites = affectedSites; } } public class CustomRule { private String id; private String name; private String description; private String condition; private List<String> actions; private List<String> tags; private boolean enabled; private Date createdAt; private Date updatedAt; // Constructors, getters, and setters public CustomRule() {} public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getCondition() { return condition; } public void setCondition(String condition) { this.condition = condition; } public List<String> getActions() { return actions; } public void setActions(List<String> actions) { this.actions = actions; } public List<String> getTags() { return tags; } public void setTags(List<String> tags) { this.tags = tags; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Date getUpdatedAt() { return updatedAt; } public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; } } public class IPList { private String id; private String name; private String description; private List<String> entries; private String type; // WHITELIST, BLACKLIST private Date createdAt; private Date updatedAt; // Constructors, getters, and setters public IPList() {} public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List<String> getEntries() { return entries; } public void setEntries(List<String> entries) { this.entries = entries; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Date getUpdatedAt() { return updatedAt; } public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; } } // Query Models public class AnalyticsQuery { private Date from; private Date until; private String interval; private Integer limit; private Map<String, String> filters; public AnalyticsQuery(Date from, Date until) { this.from = from; this.until = until; this.filters = new HashMap<>(); } // Getters and setters public Date getFrom() { return from; } public void setFrom(Date from) { this.from = from; } public Date getUntil() { return until; } public void setUntil(Date until) { this.until = until; } public String getInterval() { return interval; } public void setInterval(String interval) { this.interval = interval; } public Integer getLimit() { return limit; } public void setLimit(Integer limit) { this.limit = limit; } public Map<String, String> getFilters() { return filters; } public void setFilters(Map<String, String> filters) { this.filters = filters; } public void addFilter(String key, String value) { filters.put(key, value); } } public class AlertQuery { private Date since; private Date until; private Integer limit; // Getters and setters public Date getSince() { return since; } public void setSince(Date since) { this.since = since; } public Date getUntil() { return until; } public void setUntil(Date until) { this.until = until; } public Integer getLimit() { return limit; } public void setLimit(Integer limit) { this.limit = limit; } } public class SignalSciencesException extends Exception { public SignalSciencesException(String message) { super(message); } public SignalSciencesException(String message, Throwable cause) { super(message, cause); } } 3. Real-time Threat Monitoring
// ThreatMonitor.java package com.signalsciences.monitoring; import com.signalsciences.*; import java.util.*; import java.util.concurrent.*; public class ThreatMonitor { private final SignalSciencesClient client; private final String siteName; private final ScheduledExecutorService scheduler; private final List<ThreatListener> listeners; private final Map<String, Threat> recentThreats; private final int maxRecentThreats; private ScheduledFuture<?> monitoringTask; public ThreatMonitor(SignalSciencesClient client, String siteName) { this.client = client; this.siteName = siteName; this.scheduler = Executors.newScheduledThreadPool(2); this.listeners = new CopyOnWriteArrayList<>(); this.recentThreats = new ConcurrentHashMap<>(); this.maxRecentThreats = 1000; } public void startMonitoring(long intervalSeconds) { if (monitoringTask != null && !monitoringTask.isCancelled()) { throw new IllegalStateException("Monitor is already running"); } monitoringTask = scheduler.scheduleAtFixedRate(() -> { try { checkForNewThreats(); } catch (Exception e) { notifyListenersOfError(e); } }, 0, intervalSeconds, TimeUnit.SECONDS); System.out.println("Started threat monitoring for site: " + siteName); } public void stopMonitoring() { if (monitoringTask != null) { monitoringTask.cancel(true); monitoringTask = null; } System.out.println("Stopped threat monitoring for site: " + siteName); } public void addListener(ThreatListener listener) { listeners.add(listener); } public void removeListener(ThreatListener listener) { listeners.remove(listener); } private void checkForNewThreats() throws SignalSciencesException { List<Threat> threats = client.getRecentThreats(siteName, 50); for (Threat threat : threats) { if (!recentThreats.containsKey(threat.getId())) { recentThreats.put(threat.getId(), threat); notifyListeners(threat); // Maintain recent threats size if (recentThreats.size() > maxRecentThreats) { String oldestKey = recentThreats.keySet().iterator().next(); recentThreats.remove(oldestKey); } } } } private void notifyListeners(Threat threat) { for (ThreatListener listener : listeners) { try { listener.onThreatDetected(threat); } catch (Exception e) { System.err.println("Error notifying threat listener: " + e.getMessage()); } } } private void notifyListenersOfError(Exception error) { for (ThreatListener listener : listeners) { try { listener.onMonitoringError(error); } catch (Exception e) { System.err.println("Error notifying error listener: " + e.getMessage()); } } } public ThreatStatistics getStatistics() { Map<String, Integer> severityCount = new HashMap<>(); Map<String, Integer> typeCount = new HashMap<>(); Map<String, Integer> countryCount = new HashMap<>(); for (Threat threat : recentThreats.values()) { severityCount.merge(threat.getSeverity(), 1, Integer::sum); typeCount.merge(threat.getType(), 1, Integer::sum); countryCount.merge(threat.getCountry(), 1, Integer::sum); } return new ThreatStatistics( recentThreats.size(), severityCount, typeCount, countryCount, new Date() ); } public List<Threat> getRecentThreats(int limit) { return recentThreats.values().stream() .sorted((t1, t2) -> t2.getTimestamp().compareTo(t1.getTimestamp())) .limit(limit) .toList(); } public void shutdown() { stopMonitoring(); scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } } } // Threat Listener Interface package com.signalsciences.monitoring; import com.signalsciences.Threat; public interface ThreatListener { void onThreatDetected(Threat threat); void onMonitoringError(Exception error); } // Threat Statistics package com.signalsciences.monitoring; import java.util.Date; import java.util.Map; public class ThreatStatistics { private final int totalThreats; private final Map<String, Integer> severityBreakdown; private final Map<String, Integer> typeBreakdown; private final Map<String, Integer> countryBreakdown; private final Date generatedAt; public ThreatStatistics(int totalThreats, Map<String, Integer> severityBreakdown, Map<String, Integer> typeBreakdown, Map<String, Integer> countryBreakdown, Date generatedAt) { this.totalThreats = totalThreats; this.severityBreakdown = severityBreakdown; this.typeBreakdown = typeBreakdown; this.countryBreakdown = countryBreakdown; this.generatedAt = generatedAt; } // Getters public int getTotalThreats() { return totalThreats; } public Map<String, Integer> getSeverityBreakdown() { return severityBreakdown; } public Map<String, Integer> getTypeBreakdown() { return typeBreakdown; } public Map<String, Integer> getCountryBreakdown() { return countryBreakdown; } public Date getGeneratedAt() { return generatedAt; } public int getCriticalThreats() { return severityBreakdown.getOrDefault("CRITICAL", 0) + severityBreakdown.getOrDefault("HIGH", 0); } public String getTopThreatType() { return typeBreakdown.entrySet().stream() .max(Map.Entry.comparingByValue()) .map(Map.Entry::getKey) .orElse("NONE"); } public String getTopCountry() { return countryBreakdown.entrySet().stream() .max(Map.Entry.comparingByValue()) .map(Map.Entry::getKey) .orElse("UNKNOWN"); } } 4. Spring Boot Integration
// SignalSciencesConfig.java package com.signalsciences.config; import com.signalsciences.SignalSciencesClient; import com.signalsciences.monitoring.ThreatMonitor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SignalSciencesConfig { @Value("${signalsciences.baseUrl:https://dashboard.signalsciences.net}") private String baseUrl; @Value("${signalsciences.email}") private String email; @Value("${signalsciences.apiToken}") private String apiToken; @Value("${signalsciences.corpName}") private String corpName; @Value("${signalsciences.siteName}") private String siteName; @Value("${signalsciences.monitoring.enabled:true}") private boolean monitoringEnabled; @Value("${signalsciences.monitoring.interval:60}") private long monitoringInterval; @Bean public SignalSciencesClient signalSciencesClient() { return new SignalSciencesClient(baseUrl, email, apiToken, corpName); } @Bean public ThreatMonitor threatMonitor(SignalSciencesClient client) { ThreatMonitor monitor = new ThreatMonitor(client, siteName); if (monitoringEnabled) { monitor.startMonitoring(monitoringInterval); // Add default listener for logging monitor.addListener(new LoggingThreatListener()); } return monitor; } } // LoggingThreatListener.java package com.signalsciences.config; import com.signalsciences.Threat; import com.signalsciences.monitoring.ThreatListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingThreatListener implements ThreatListener { private static final Logger logger = LoggerFactory.getLogger(LoggingThreatListener.class); @Override public void onThreatDetected(Threat threat) { logger.warn("THREAT DETECTED - Type: {}, Severity: {}, IP: {}, Path: {}", threat.getType(), threat.getSeverity(), threat.getClientIP(), threat.getPath()); if ("CRITICAL".equals(threat.getSeverity()) || "HIGH".equals(threat.getSeverity())) { logger.error("CRITICAL THREAT - Immediate attention required: {}", threat.getDescription()); } } @Override public void onMonitoringError(Exception error) { logger.error("Threat monitoring error: {}", error.getMessage(), error); } } // SignalSciencesController.java package com.signalsciences.web; import com.signalsciences.*; import com.signalsciences.monitoring.ThreatMonitor; import com.signalsciences.monitoring.ThreatStatistics; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Date; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/security") public class SignalSciencesController { @Autowired private SignalSciencesClient client; @Autowired private ThreatMonitor threatMonitor; @GetMapping("/threats/recent") public ResponseEntity<List<Threat>> getRecentThreats(@RequestParam(defaultValue = "50") int limit) { try { List<Threat> threats = client.getRecentThreats("default", limit); return ResponseEntity.ok(threats); } catch (SignalSciencesException e) { return ResponseEntity.internalServerError().build(); } } @GetMapping("/analytics/requests") public ResponseEntity<RequestAnalytics> getRequestAnalytics( @RequestParam Long from, @RequestParam Long until, @RequestParam(required = false) String interval) { try { AnalyticsQuery query = new AnalyticsQuery(new Date(from), new Date(until)); if (interval != null) { query.setInterval(interval); } RequestAnalytics analytics = client.getRequestAnalytics("default", query); return ResponseEntity.ok(analytics); } catch (SignalSciencesException e) { return ResponseEntity.internalServerError().build(); } } @GetMapping("/monitoring/stats") public ResponseEntity<ThreatStatistics> getMonitoringStats() { ThreatStatistics stats = threatMonitor.getStatistics(); return ResponseEntity.ok(stats); } @GetMapping("/alerts") public ResponseEntity<List<Alert>> getAlerts( @RequestParam(required = false) Long since, @RequestParam(required = false) Long until, @RequestParam(defaultValue = "50") int limit) { try { AlertQuery query = new AlertQuery(); if (since != null) query.setSince(new Date(since)); if (until != null) query.setUntil(new Date(until)); query.setLimit(limit); List<Alert> alerts = client.getAlerts("default", query); return ResponseEntity.ok(alerts); } catch (SignalSciencesException e) { return ResponseEntity.internalServerError().build(); } } @PostMapping("/rules") public ResponseEntity<CustomRule> createCustomRule(@RequestBody CustomRule rule) { try { CustomRule created = client.createCustomRule("default", rule); return ResponseEntity.ok(created); } catch (SignalSciencesException e) { return ResponseEntity.internalServerError().build(); } } @GetMapping("/health") public ResponseEntity<Map<String, Object>> health() { try { List<Site> sites = client.getSites(); boolean healthy = !sites.isEmpty(); return ResponseEntity.ok(Map.of( "status", healthy ? "healthy" : "degraded", "sitesCount", sites.size(), "timestamp", System.currentTimeMillis() )); } catch (SignalSciencesException e) { return ResponseEntity.ok(Map.of( "status", "unhealthy", "error", e.getMessage(), "timestamp", System.currentTimeMillis() )); } } } 5. Web Application Firewall Filter
// WAFRequestFilter.java package com.signalsciences.filter; import com.signalsciences.SignalSciencesClient; import com.signalsciences.Threat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; @Component public class WAFRequestFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(WAFRequestFilter.class); @Autowired private SignalSciencesClient client; private final Set<String> suspiciousUserAgents = Set.of( "sqlmap", "nikto", "metasploit", "nmap", "burpsuite", "w3af" ); private final Set<String> blockedIPs = Collections.synchronizedSet(new HashSet<>()); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String clientIP = getClientIP(httpRequest); String userAgent = httpRequest.getHeader("User-Agent"); String path = httpRequest.getRequestURI(); String method = httpRequest.getMethod(); // Check if IP is blocked if (blockedIPs.contains(clientIP)) { logger.warn("Blocked request from blacklisted IP: {}", clientIP); httpResponse.setStatus(403); httpResponse.getWriter().write("Access denied"); return; } // Analyze request for threats ThreatAnalysis analysis = analyzeRequest(httpRequest); if (analysis.isSuspicious()) { logger.warn("Suspicious request detected from {}: {}", clientIP, analysis.getReason()); // Log threat for monitoring logThreat(clientIP, userAgent, path, method, analysis); // Block critical threats if (analysis.isCritical()) { blockedIPs.add(clientIP); httpResponse.setStatus(403); httpResponse.getWriter().write("Request blocked by WAF"); return; } } chain.doFilter(request, response); } private String getClientIP(HttpServletRequest request) { String xForwardedFor = request.getHeader("X-Forwarded-For"); if (xForwardedFor != null && !xForwardedFor.isEmpty()) { return xForwardedFor.split(",")[0].trim(); } return request.getRemoteAddr(); } private ThreatAnalysis analyzeRequest(HttpServletRequest request) { ThreatAnalysis analysis = new ThreatAnalysis(); String clientIP = getClientIP(request); String userAgent = request.getHeader("User-Agent"); String path = request.getRequestURI(); String queryString = request.getQueryString(); // Check for suspicious user agents if (userAgent != null) { String lowerUserAgent = userAgent.toLowerCase(); for (String suspicious : suspiciousUserAgents) { if (lowerUserAgent.contains(suspicious)) { analysis.addSuspicion("Suspicious User-Agent: " + userAgent); analysis.setCritical(true); } } } // Check for SQL injection patterns if (containsSQLInjection(path) || (queryString != null && containsSQLInjection(queryString))) { analysis.addSuspicion("SQL injection attempt detected"); analysis.setCritical(true); } // Check for path traversal if (containsPathTraversal(path)) { analysis.addSuspicion("Path traversal attempt detected"); analysis.setCritical(true); } // Check for XSS attempts if (containsXSS(path) || (queryString != null && containsXSS(queryString))) { analysis.addSuspicion("XSS attempt detected"); analysis.setCritical(true); } // Rate limiting (simplified) if (isRateLimited(clientIP)) { analysis.addSuspicion("Rate limit exceeded"); analysis.setCritical(true); } return analysis; } private boolean containsSQLInjection(String input) { if (input == null) return false; String[] sqlPatterns = { "union.*select", "select.*from", "insert.*into", "update.*set", "delete.*from", "drop.*table", "1=1", "or.*1=1", "';", "--" }; String lowerInput = input.toLowerCase(); return Arrays.stream(sqlPatterns) .anyMatch(pattern -> lowerInput.matches(".*" + pattern + ".*")); } private boolean containsPathTraversal(String input) { if (input == null) return false; return input.contains("../") || input.contains("..\\") || input.contains("/etc/passwd"); } private boolean containsXSS(String input) { if (input == null) return false; String[] xssPatterns = { "<script>", "javascript:", "onload=", "onerror=", "onclick=", "alert\\(", "document\\.cookie", "<iframe>" }; String lowerInput = input.toLowerCase(); return Arrays.stream(xssPatterns) .anyMatch(pattern -> lowerInput.contains(pattern)); } private boolean isRateLimited(String clientIP) { // Simplified rate limiting - in production, use Redis or similar // This is just a basic example return false; } private void logThreat(String clientIP, String userAgent, String path, String method, ThreatAnalysis analysis) { // In production, this would send data to Signal Sciences logger.warn("THREAT: IP={}, Method={}, Path={}, UserAgent={}, Reason={}", clientIP, method, path, userAgent, analysis.getReason()); } @Override public void init(FilterConfig filterConfig) throws ServletException { logger.info("WAF Request Filter initialized"); } @Override public void destroy() { logger.info("WAF Request Filter destroyed"); } } // ThreatAnalysis.java package com.signalsciences.filter; import java.util.ArrayList; import java.util.List; public class ThreatAnalysis { private List<String> suspicions; private boolean critical; public ThreatAnalysis() { this.suspicions = new ArrayList<>(); this.critical = false; } public void addSuspicion(String suspicion) { suspicions.add(suspicion); } public boolean isSuspicious() { return !suspicions.isEmpty(); } public boolean isCritical() { return critical; } public void setCritical(boolean critical) { this.critical = critical; } public String getReason() { return String.join("; ", suspicions); } public List<String> getSuspicions() { return new ArrayList<>(suspicions); } } 6. Security Dashboard Service
// SecurityDashboardService.java package com.signalsciences.dashboard; import com.signalsciences.*; import com.signalsciences.monitoring.ThreatMonitor; import com.signalsciences.monitoring.ThreatStatistics; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Service public class SecurityDashboardService { @Autowired private SignalSciencesClient client; @Autowired private ThreatMonitor threatMonitor; private final Map<String, DashboardMetrics> metricsCache = new ConcurrentHashMap<>(); private final int CACHE_TTL_MINUTES = 5; public SecurityDashboard getDashboard() throws SignalSciencesException { SecurityDashboard dashboard = new SecurityDashboard(); dashboard.setGeneratedAt(new Date()); // Get real-time threats ThreatStatistics threatStats = threatMonitor.getStatistics(); dashboard.setThreatStatistics(threatStats); // Get request analytics for last 24 hours Date now = new Date(); Date yesterday = new Date(now.getTime() - (24 * 60 * 60 * 1000)); AnalyticsQuery query = new AnalyticsQuery(yesterday, now); RequestAnalytics requestAnalytics = client.getRequestAnalytics("default", query); dashboard.setRequestAnalytics(requestAnalytics); // Get recent alerts AlertQuery alertQuery = new AlertQuery(); alertQuery.setSince(yesterday); alertQuery.setLimit(20); List<Alert> alerts = client.getAlerts("default", alertQuery); dashboard.setRecentAlerts(alerts); // Get top threats List<Threat> topThreats = threatMonitor.getRecentThreats(10); dashboard.setTopThreats(topThreats); // Calculate security score double securityScore = calculateSecurityScore(threatStats, requestAnalytics); dashboard.setSecurityScore(securityScore); dashboard.setSecurityLevel(getSecurityLevel(securityScore)); return dashboard; } public ThreatReport generateThreatReport(Date from, Date until) throws SignalSciencesException { ThreatReport report = new ThreatReport(); report.setFrom(from); report.setUntil(until); // Get threats in time range AnalyticsQuery query = new AnalyticsQuery(from, until); RequestAnalytics analytics = client.getRequestAnalytics("default", query); // Analyze threat patterns Map<String, ThreatPattern> patterns = analyzeThreatPatterns(analytics); report.setThreatPatterns(patterns); // Generate recommendations List<SecurityRecommendation> recommendations = generateRecommendations(analytics, patterns); report.setRecommendations(recommendations); // Calculate metrics ThreatMetrics metrics = calculateThreatMetrics(analytics); report.setMetrics(metrics); return report; } @Scheduled(fixedRate = 300000) // Update every 5 minutes public void updateMetricsCache() { try { SecurityDashboard dashboard = getDashboard(); DashboardMetrics metrics = new DashboardMetrics(dashboard); metricsCache.put("current", metrics); } catch (SignalSciencesException e) { System.err.println("Failed to update metrics cache: " + e.getMessage()); } } private double calculateSecurityScore(ThreatStatistics threatStats, RequestAnalytics analytics) { double score = 100.0; // Deduct for critical threats score -= threatStats.getCriticalThreats() * 5; // Deduct for high blocked request rate if (analytics.getTotalRequests() > 0) { double blockRate = (double) analytics.getBlockedRequests() / analytics.getTotalRequests(); if (blockRate > 0.1) { // More than 10% blocked score -= 20; } } // Deduct for high attack rate if (analytics.getTotalRequests() > 0) { double attackRate = analytics.getAttackTypes().values().stream() .mapToInt(Integer::intValue).sum() / (double) analytics.getTotalRequests(); if (attackRate > 0.05) { // More than 5% attacks score -= 15; } } return Math.max(score, 0); } private String getSecurityLevel(double score) { if (score >= 90) return "EXCELLENT"; if (score >= 80) return "GOOD"; if (score >= 70) return "FAIR"; if (score >= 60) return "POOR"; return "CRITICAL"; } private Map<String, ThreatPattern> analyzeThreatPatterns(RequestAnalytics analytics) { Map<String, ThreatPattern> patterns = new HashMap<>(); // Analyze by attack type analytics.getAttackTypes().forEach((type, count) -> { ThreatPattern pattern = new ThreatPattern(); pattern.setType("ATTACK_TYPE"); pattern.setValue(type); pattern.setCount(count); pattern.setSeverity(calculateAttackSeverity(type, count)); patterns.put("ATTACK_" + type, pattern); }); // Analyze by IP analytics.getTopIPs().forEach((ip, count) -> { if (count > 10) { // Only consider IPs with significant activity ThreatPattern pattern = new ThreatPattern(); pattern.setType("SUSPICIOUS_IP"); pattern.setValue(ip); pattern.setCount(count); pattern.setSeverity(count > 50 ? "HIGH" : "MEDIUM"); patterns.put("IP_" + ip, pattern); } }); return patterns; } private String calculateAttackSeverity(String attackType, int count) { if (count > 100) return "CRITICAL"; if (count > 50) return "HIGH"; if (count > 20) return "MEDIUM"; return "LOW"; } private List<SecurityRecommendation> generateRecommendations( RequestAnalytics analytics, Map<String, ThreatPattern> patterns) { List<SecurityRecommendation> recommendations = new ArrayList<>(); // Check for SQL injection patterns if (patterns.containsKey("ATTACK_sql_injection")) { recommendations.add(new SecurityRecommendation( "SQL_INJECTION_PROTECTION", "Implement additional SQL injection protection rules", "HIGH", "Add custom rules to block common SQL injection patterns" )); } // Check for XSS patterns if (patterns.containsKey("ATTACK_xss")) { recommendations.add(new SecurityRecommendation( "XSS_PROTECTION", "Enhance XSS protection", "HIGH", "Update WAF rules to detect advanced XSS attempts" )); } // Check for high block rate if (analytics.getTotalRequests() > 0) { double blockRate = (double) analytics.getBlockedRequests() / analytics.getTotalRequests(); if (blockRate > 0.15) { recommendations.add(new SecurityRecommendation( "FALSE_POSITIVES", "Review false positives", "MEDIUM", "High block rate may indicate false positives. Review blocked requests." )); } } return recommendations; } private ThreatMetrics calculateThreatMetrics(RequestAnalytics analytics) { ThreatMetrics metrics = new ThreatMetrics(); metrics.setTotalRequests(analytics.getTotalRequests()); metrics.setBlockedRequests(analytics.getBlockedRequests()); metrics.setAttackRequests(analytics.getAttackTypes().values().stream() .mapToInt(Integer::intValue).sum()); // Calculate rates if (analytics.getTotalRequests() > 0) { metrics.setBlockRate((double) analytics.getBlockedRequests() / analytics.getTotalRequests()); metrics.setAttackRate((double) metrics.getAttackRequests() / analytics.getTotalRequests()); } return metrics; } } // Dashboard Models package com.signalsciences.dashboard; import com.signalsciences.*; import com.signalsciences.monitoring.ThreatStatistics; import java.util.*; public class SecurityDashboard { private Date generatedAt; private double securityScore; private String securityLevel; private ThreatStatistics threatStatistics; private RequestAnalytics requestAnalytics; private List<Alert> recentAlerts; private List<Threat> topThreats; // Constructors, getters, and setters public SecurityDashboard() {} public Date getGeneratedAt() { return generatedAt; } public void setGeneratedAt(Date generatedAt) { this.generatedAt = generatedAt; } public double getSecurityScore() { return securityScore; } public void setSecurityScore(double securityScore) { this.securityScore = securityScore; } public String getSecurityLevel() { return securityLevel; } public void setSecurityLevel(String securityLevel) { this.securityLevel = securityLevel; } public ThreatStatistics getThreatStatistics() { return threatStatistics; } public void setThreatStatistics(ThreatStatistics threatStatistics) { this.threatStatistics = threatStatistics; } public RequestAnalytics getRequestAnalytics() { return requestAnalytics; } public void setRequestAnalytics(RequestAnalytics requestAnalytics) { this.requestAnalytics = requestAnalytics; } public List<Alert> getRecentAlerts() { return recentAlerts; } public void setRecentAlerts(List<Alert> recentAlerts) { this.recentAlerts = recentAlerts; } public List<Threat> getTopThreats() { return topThreats; } public void setTopThreats(List<Threat> topThreats) { this.topThreats = topThreats; } } public class ThreatReport { private Date from; private Date until; private Map<String, ThreatPattern> threatPatterns; private List<SecurityRecommendation> recommendations; private ThreatMetrics metrics; // Constructors, getters, and setters public ThreatReport() {} public Date getFrom() { return from; } public void setFrom(Date from) { this.from = from; } public Date getUntil() { return until; } public void setUntil(Date until) { this.until = until; } public Map<String, ThreatPattern> getThreatPatterns() { return threatPatterns; } public void setThreatPatterns(Map<String, ThreatPattern> threatPatterns) { this.threatPatterns = threatPatterns; } public List<SecurityRecommendation> getRecommendations() { return recommendations; } public void setRecommendations(List<SecurityRecommendation> recommendations) { this.recommendations = recommendations; } public ThreatMetrics getMetrics() { return metrics; } public void setMetrics(ThreatMetrics metrics) { this.metrics = metrics; } } public class ThreatPattern { private String type; private String value; private int count; private String severity; // Constructors, getters, and setters public ThreatPattern() {} public String getType() { return type; } public void setType(String type) { this.type = type; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } } public class SecurityRecommendation { private String id; private String title; private String severity; private String description; private String action; public SecurityRecommendation(String id, String title, String severity, String description) { this.id = id; this.title = title; this.severity = severity; this.description = description; } // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getAction() { return action; } public void setAction(String action) { this.action = action; } } public class ThreatMetrics { private int totalRequests; private int blockedRequests; private int attackRequests; private double blockRate; private double attackRate; // Constructors, getters, and setters public ThreatMetrics() {} public int getTotalRequests() { return totalRequests; } public void setTotalRequests(int totalRequests) { this.totalRequests = totalRequests; } public int getBlockedRequests() { return blockedRequests; } public void setBlockedRequests(int blockedRequests) { this.blockedRequests = blockedRequests; } public int getAttackRequests() { return attackRequests; } public void setAttackRequests(int attackRequests) { this.attackRequests = attackRequests; } public double getBlockRate() { return blockRate; } public void setBlockRate(double blockRate) { this.blockRate = blockRate; } public double getAttackRate() { return attackRate; } public void setAttackRate(double attackRate) { this.attackRate = attackRate; } } public class DashboardMetrics { private SecurityDashboard dashboard; private Date cachedAt; public DashboardMetrics(SecurityDashboard dashboard) { this.dashboard = dashboard; this.cachedAt = new Date(); } // Getters and setters public SecurityDashboard getDashboard() { return dashboard; } public void setDashboard(SecurityDashboard dashboard) { this.dashboard = dashboard; } public Date getCachedAt() { return cachedAt; } public void setCachedAt(Date cachedAt) { this.cachedAt = cachedAt; } } 7. Application Configuration
# application.yml spring: application: name: signal-sciences-integration signalsciences: baseUrl: https://dashboard.signalsciences.net email: ${SIGSCI_EMAIL:} apiToken: ${SIGSCI_API_TOKEN:} corpName: ${SIGSCI_CORP_NAME:} siteName: ${SIGSCI_SITE_NAME:default} monitoring: enabled: true interval: 60 server: port: 8080 management: endpoints: web: exposure: include: health,metrics,info endpoint: health: show-details: always logging: level: com.signalsciences: DEBUG Key Features:
- Complete API Integration: Full Signal Sciences REST API client
- Real-time Threat Monitoring: Continuous monitoring with event listeners
- Spring Boot Integration: Seamless framework integration
- WAF Request Filter: In-application security filtering
- Security Dashboard: Comprehensive security analytics and reporting
- Custom Rules Management: Dynamic rule creation and management
- IP List Management: Whitelist/blacklist functionality
- Alert System: Real-time security alerts and notifications
This implementation provides enterprise-grade Signal Sciences WAF integration for Java applications, enabling comprehensive web application security monitoring, threat detection, and real-time protection.