-
Notifications
You must be signed in to change notification settings - Fork 153
Dlp #1909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dlp #1909
Changes from 30 commits
bcc7dcb
497a43c
f0119d3
5f84eb6
e2536fe
6ebb9c4
9d2e722
699b226
7ffee70
559a62b
de3ed95
4c3ff8f
8cfb161
b17b14a
099ec70
02d75ec
2be464b
450204b
75b56d9
8adedb9
e7cc895
0f673bc
6a0bed1
469256e
53bc2f8
7d04071
ff15443
ea1e179
dce7806
e2eaa48
13711b1
7aa7e95
60a13df
0bf1268
21c3489
20ed318
c20c70d
528cc14
8d75f7c
c3ec071
e9a6286
8b1a278
77022dd
5008ee7
e58e11d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import com.predic8.membrane.annot.MCAttribute; | ||
|
|
||
| public abstract class Action { | ||
|
|
||
| private String field; | ||
|
|
||
| public abstract String apply(String json); | ||
|
|
||
| public String getField() { | ||
| return field; | ||
| } | ||
|
|
||
| @MCAttribute | ||
| public void setField(String field) { | ||
| this.field = field; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import java.io.BufferedReader; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.io.InputStreamReader; | ||
| import java.util.*; | ||
|
|
||
| /** | ||
| * Loads field risk mappings from CSV file with format: | ||
| * field_name,description,risk_level | ||
| * where risk_level should be one of: high, medium, low, unclassified | ||
| */ | ||
| public class CsvFieldConfiguration implements FieldConfiguration { | ||
|
|
||
| private static final Logger log = LoggerFactory.getLogger(CsvFieldConfiguration.class); | ||
|
|
||
| // Optional: define allowed risk levels | ||
| private static final Set<String> ALLOWED_RISK_LEVELS = Set.of("high", "medium", "low", "unclassified"); | ||
|
|
||
| @Override | ||
| public Map<String, String> getFields(String fileName) { | ||
| try (InputStream inputStream = CsvFieldConfiguration.class.getClassLoader().getResourceAsStream(fileName)) { | ||
| if (inputStream == null) { | ||
| log.error("Could not find file: {}", fileName); | ||
| throw new NullPointerException("InputStream is null. File not found: " + fileName); | ||
| } | ||
|
|
||
| Map<String, String> riskDict = new HashMap<>(); | ||
| BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); | ||
| String line; | ||
| boolean isHeader = true; | ||
|
|
||
| while ((line = reader.readLine()) != null) { | ||
| line = line.trim(); | ||
| if (isHeader) { | ||
| isHeader = false; | ||
| continue; | ||
| } | ||
| if (line.isEmpty() || line.startsWith("#")) continue; | ||
|
|
||
| String[] parts = line.split(",", -1); | ||
| if (parts.length >= 2) { | ||
| String field = parts[0].trim().toLowerCase(Locale.ROOT); | ||
| String riskLevel = parts[parts.length - 1].trim().toLowerCase(Locale.ROOT); | ||
|
|
||
| if (!ALLOWED_RISK_LEVELS.contains(riskLevel)) { | ||
| log.warn("Unknown risk level '{}' for field '{}'", riskLevel, field); | ||
| } | ||
|
|
||
| riskDict.put(field, riskLevel); | ||
| } else { | ||
| log.warn("Invalid CSV line (too few columns): {}", line); | ||
| } | ||
| } | ||
|
|
||
| return riskDict; | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Failed to load risk data from " + fileName, e); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonFactory; | ||
| import com.fasterxml.jackson.core.JsonFactoryBuilder; | ||
| import com.fasterxml.jackson.core.StreamReadConstraints; | ||
| import com.fasterxml.jackson.core.json.JsonReadFeature; | ||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.predic8.membrane.core.http.Message; | ||
|
|
||
| import java.io.InputStream; | ||
| import java.util.*; | ||
|
|
||
| public class DLPAnalyzer { | ||
|
|
||
| private static final JsonFactory JSON_FACTORY = new JsonFactoryBuilder() | ||
| .configure(JsonReadFeature.ALLOW_TRAILING_COMMA, true) | ||
| .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS, false) | ||
| .streamReadConstraints(StreamReadConstraints.builder() | ||
| .maxNestingDepth(64) | ||
| .maxStringLength(16 * 1024) | ||
| .build()) | ||
| .build(); | ||
|
|
||
| private static final ObjectMapper MAPPER = new ObjectMapper(JSON_FACTORY); | ||
|
|
||
| private final Map<String, String> riskDict; | ||
|
|
||
| public DLPAnalyzer(Map<String, String> riskDict) { | ||
| this.riskDict = Map.copyOf(riskDict); | ||
| } | ||
|
|
||
| public RiskReport analyze(Message msg) { | ||
| try (InputStream is = msg.getBodyAsStreamDecoded()) { | ||
| RiskReport report = new RiskReport(); | ||
| traverse(MAPPER.readTree(is), new ArrayDeque<>(), report); | ||
| return report; | ||
| } catch (Exception e) { | ||
| throw new RuntimeException("Failed to analyse message", e); | ||
| } | ||
| } | ||
|
|
||
| private void traverse(JsonNode node, Deque<String> path, RiskReport report) { | ||
| if (node.isObject()) { | ||
| node.fieldNames().forEachRemaining(fieldName -> { | ||
| path.addLast(fieldName); | ||
| traverse(node.get(fieldName), path, report); | ||
| path.removeLast(); | ||
| }); | ||
| } else if (node.isArray()) { | ||
| for (JsonNode child : node) { | ||
| traverse(child, path, report); | ||
| } | ||
| } else { | ||
| String fullPath = String.join(".", path).toLowerCase(Locale.ROOT); | ||
| String simpleName = path.isEmpty() ? "" : path.getLast().toLowerCase(Locale.ROOT); | ||
| report.recordField(fullPath, classify(fullPath, simpleName)); | ||
| } | ||
| } | ||
|
|
||
| private String classify(String fullPath, String simpleName) { | ||
| return Optional.ofNullable(riskDict.get(fullPath)) | ||
| .or(() -> Optional.ofNullable(riskDict.get(simpleName))) | ||
| .orElse("unclassified") | ||
| .toLowerCase(Locale.ROOT); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,119 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.predic8.membrane.core.interceptor.dlp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.annot.MCAttribute; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.annot.MCChildElement; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.annot.MCElement; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.core.exchange.Exchange; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.core.http.Message; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.core.interceptor.AbstractInterceptor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.core.interceptor.Outcome; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.slf4j.Logger; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.slf4j.LoggerFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.nio.charset.StandardCharsets; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @MCElement(name = "dlp") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class DLPInterceptor extends AbstractInterceptor { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final Logger log = LoggerFactory.getLogger(DLPInterceptor.class); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private DLPAnalyzer dlpAnalyzer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private String fieldsConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private List<Mask> masks = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private List<Filter> filters = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private List<Report> reports = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void init() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (fieldsConfig != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dlpAnalyzer = new DLPAnalyzer(new CsvFieldConfiguration().getFields(fieldsConfig)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dlpAnalyzer = new DLPAnalyzer(java.util.Map.of()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super.init(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Outcome handleRequest(Exchange exc) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return handleInternal(exc.getRequest()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Outcome handleResponse(Exchange exc) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return handleInternal(exc.getResponse()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Outcome handleInternal(Message msg) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("DLP Risk Analysis: {}", dlpAnalyzer.analyze(msg).getLogReport()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!masks.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (Mask mask : masks) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg.setBodyContent(mask.apply(msg.getBodyAsStringDecoded()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .getBytes(StandardCharsets.UTF_8)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!filters.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (Filter filter : filters) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg.setBodyContent(filter.apply(msg.getBodyAsStringDecoded()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .getBytes(StandardCharsets.UTF_8)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!reports.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (Report report : reports) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg.setBodyContent(report.apply(msg.getBodyAsStringDecoded(), dlpAnalyzer.analyze(msg)).getBytes(StandardCharsets.UTF_8)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return CONTINUE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.error("Exception in DLPInterceptor handleInternal: ", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Outcome.ABORT; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Outcome handleInternal(Message msg) { | |
| try { | |
| log.info("DLP Risk Analysis: {}", dlpAnalyzer.analyze(msg).getLogReport()); | |
| if (!masks.isEmpty()) { | |
| for (Mask mask : masks) { | |
| msg.setBodyContent(mask.apply(msg.getBodyAsStringDecoded()) | |
| .getBytes(StandardCharsets.UTF_8)); | |
| } | |
| } | |
| if (!filters.isEmpty()) { | |
| for (Filter filter : filters) { | |
| msg.setBodyContent(filter.apply(msg.getBodyAsStringDecoded()) | |
| .getBytes(StandardCharsets.UTF_8)); | |
| } | |
| } | |
| if (!reports.isEmpty()) { | |
| for (Report report : reports) { | |
| msg.setBodyContent(report.apply(msg.getBodyAsStringDecoded(), dlpAnalyzer.analyze(msg)).getBytes(StandardCharsets.UTF_8)); | |
| } | |
| } | |
| return CONTINUE; | |
| } catch (Exception e) { | |
| log.error("Exception in DLPInterceptor handleInternal: ", e); | |
| return Outcome.ABORT; | |
| } | |
| } | |
| public Outcome handleInternal(Message msg) { | |
| + // input validation | |
| + if (msg == null || msg.getBodyAsStringDecoded() == null || msg.getBodyAsStringDecoded().isEmpty()) { | |
| + return CONTINUE; | |
| + } | |
| try { | |
| - log.info("DLP Risk Analysis: {}", dlpAnalyzer.analyze(msg).getLogReport()); | |
| + // analyze once | |
| + RiskReport riskReport = dlpAnalyzer.analyze(msg); | |
| + log.info("DLP Risk Analysis: {}", riskReport.getLogReport()); | |
| + | |
| + // work off a single mutable body string | |
| + String body = msg.getBodyAsStringDecoded(); | |
| if (!masks.isEmpty()) { | |
| for (Mask mask : masks) { | |
| - msg.setBodyContent(mask.apply(msg.getBodyAsStringDecoded()) | |
| - .getBytes(StandardCharsets.UTF_8)); | |
| + body = mask.apply(body); | |
| } | |
| } | |
| if (!filters.isEmpty()) { | |
| for (Filter filter : filters) { | |
| - msg.setBodyContent(filter.apply(msg.getBodyAsStringDecoded()) | |
| - .getBytes(StandardCharsets.UTF_8)); | |
| + body = filter.apply(body); | |
| } | |
| } | |
| if (!reports.isEmpty()) { | |
| for (Report report : reports) { | |
| - msg.setBodyContent(report.apply(msg.getBodyAsStringDecoded(), dlpAnalyzer.analyze(msg)).getBytes(StandardCharsets.UTF_8)); | |
| + body = report.apply(body, riskReport); | |
| } | |
| } | |
| + | |
| + // apply all transformations at once | |
| + msg.setBodyContent(body.getBytes(StandardCharsets.UTF_8)); | |
| return CONTINUE; | |
| } catch (Exception e) { | |
| log.error("Exception in DLPInterceptor handleInternal: ", e); | |
| return Outcome.ABORT; | |
| } | |
| } |
🤖 Prompt for AI Agents
In
core/src/main/java/com/predic8/membrane/core/interceptor/dlp/DLPInterceptor.java
between lines 49 and 78, optimize by calling dlpAnalyzer.analyze(msg) once and
reusing its result to avoid redundant processing. Add validation to check if the
message body is non-null and non-empty before applying masks, filters, or
reports. Instead of repeatedly decoding and re-encoding the message body for
each transformation, decode once, apply all transformations sequentially on the
decoded string, then encode and set the body content once at the end.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import com.predic8.membrane.annot.MCAttribute; | ||
| import com.predic8.membrane.annot.MCElement; | ||
|
|
||
| @MCElement(name = "field") | ||
| public class Field { | ||
|
|
||
| private String jsonpath; | ||
|
|
||
| public String getJsonpath() { | ||
| return jsonpath; | ||
| } | ||
|
|
||
| @MCAttribute | ||
| public void setJsonpath(String jsonpath) { | ||
| this.jsonpath = jsonpath; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package com.predic8.membrane.core.interceptor.dlp; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.annot.MCAttribute; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.annot.MCElement; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import com.predic8.membrane.core.http.Message; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import org.slf4j.Logger; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import org.slf4j.LoggerFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Locale; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Objects; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.regex.Pattern; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @MCElement(name = "field") | ||||||||||||||||||||||||||||||||||||||||||||||||
| public class Field { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| private static final Logger log = LoggerFactory.getLogger(Field.class); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| private String name; | ||||||||||||||||||||||||||||||||||||||||||||||||
| private Action action = Action.REPORT; | ||||||||||||||||||||||||||||||||||||||||||||||||
| private Pattern compiled = Pattern.compile(".*"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @MCAttribute | ||||||||||||||||||||||||||||||||||||||||||||||||
| public void setName(String name) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.name = Objects.requireNonNull(name, "field name must not be null"); | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.compiled = Pattern.compile(name, Pattern.CASE_INSENSITIVE); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @MCAttribute | ||||||||||||||||||||||||||||||||||||||||||||||||
| public void setAction(String action) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.action = Action.valueOf(action.toUpperCase(Locale.ROOT)); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public void setAction(String action) { | |
| this.action = Action.valueOf(action.toUpperCase(Locale.ROOT)); | |
| } | |
| import java.util.Arrays; | |
| @MCAttribute | |
| public void setAction(String action) { | |
| Objects.requireNonNull(action, "action must not be null"); | |
| try { | |
| this.action = Action.valueOf(action.toUpperCase(Locale.ROOT)); | |
| } catch (IllegalArgumentException e) { | |
| throw new IllegalArgumentException( | |
| "Invalid action: " + action + | |
| ". Valid actions are: " + | |
| String.join(", ", | |
| Arrays.stream(Action.values()) | |
| .map(Enum::name) | |
| .toArray(String[]::new) | |
| ), | |
| e | |
| ); | |
| } | |
| } |
🤖 Prompt for AI Agents
In core/src/main/java/com/predic8/membrane/core/interceptor/dlp/Field.txt around
lines 29 to 31, the setAction method converts the input string to an enum
without validation, risking IllegalArgumentException. To fix this, validate the
input string against the Action enum values before conversion. If the string is
invalid, handle it gracefully by throwing a custom exception or logging an
error. Also, add the necessary import for Locale if not already present.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import com.predic8.membrane.core.http.Message; | ||
|
|
||
| import java.util.Locale; | ||
| import java.util.regex.Pattern; | ||
|
|
||
| public interface FieldActionStrategy { | ||
| void apply(Message msg, Pattern pattern); | ||
|
|
||
| static FieldActionStrategy of(String action) { | ||
| return switch (action.toLowerCase(Locale.ROOT)) { | ||
| case "mask" -> new MaskField(); | ||
| case "filter" -> new FilterField(); | ||
| case "report" -> new ReportField(); | ||
| default -> throw new IllegalArgumentException("Unknown action: " + action); | ||
| }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| public interface FieldConfiguration { | ||
| Map<String, String> getFields(String fileName); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.predic8.membrane.core.interceptor.dlp; | ||
|
|
||
| import com.predic8.membrane.annot.MCChildElement; | ||
| import com.predic8.membrane.annot.MCElement; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| @MCElement(name = "fields") | ||
| public class Fields { | ||
|
|
||
| private List<Field> fields = new ArrayList<>(); | ||
|
|
||
| @MCChildElement | ||
| public Fields setFields(List<Field> fields) { | ||
| this.fields = fields; | ||
| return this; | ||
| } | ||
|
|
||
| public List<Field> getFields() { | ||
| return fields; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for the field parameter.
The setter should validate that the field is not empty and is a valid JSONPath expression to prevent runtime errors.
@MCAttribute public void setField(String field) { + if (field != null && field.trim().isEmpty()) { + throw new IllegalArgumentException("field cannot be empty"); + } this.field = field; }🤖 Prompt for AI Agents