Skip to content

Commit

Permalink
fix detector writeTo() method missing fields (#695)
Browse files Browse the repository at this point in the history
* fix detector writeTo() method missing fields

Signed-off-by: Surya Sashank Nistala <snistala@amazon.com>

* fix test

Signed-off-by: Surya Sashank Nistala <snistala@amazon.com>

---------

Signed-off-by: Surya Sashank Nistala <snistala@amazon.com>
# Conflicts:
#	src/main/java/org/opensearch/securityanalytics/model/Detector.java
  • Loading branch information
eirsep authored and riysaxen-amzn committed Mar 18, 2024
1 parent e44e5d4 commit 33b1fdb
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 22 deletions.
107 changes: 85 additions & 22 deletions src/main/java/org/opensearch/securityanalytics/model/Detector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,28 @@
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.common.io.stream.Writeable;
import org.opensearch.common.xcontent.XContentParserUtils;
import org.opensearch.commons.alerting.model.CronSchedule;
import org.opensearch.commons.alerting.model.Schedule;
import org.opensearch.commons.authuser.User;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.common.io.stream.Writeable;
import org.opensearch.core.xcontent.XContentParserUtils;
import org.opensearch.core.ParseField;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.commons.alerting.model.CronSchedule;
import org.opensearch.commons.alerting.model.Schedule;
import org.opensearch.commons.authuser.User;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

import java.util.stream.Collectors;

public class Detector implements Writeable, ToXContentObject {

private static final Logger log = LogManager.getLogger(Detector.class);
Expand All @@ -50,8 +48,11 @@ public class Detector implements Writeable, ToXContentObject {
public static final String TRIGGERS_FIELD = "triggers";
public static final String LAST_UPDATE_TIME_FIELD = "last_update_time";
public static final String ENABLED_TIME_FIELD = "enabled_time";
public static final String THREAT_INTEL_ENABLED_FIELD = "threat_intel_enabled";
public static final String ALERTING_MONITOR_ID = "monitor_id";

public static final String ALERTING_WORKFLOW_ID = "workflow_ids";

public static final String BUCKET_MONITOR_ID_RULE_ID = "bucket_monitor_id_rule_id";
private static final String RULE_TOPIC_INDEX = "rule_topic_index";

Expand Down Expand Up @@ -79,6 +80,8 @@ public class Detector implements Writeable, ToXContentObject {

private String name;

private Boolean threatIntelEnabled;

private Boolean enabled;

private Schedule schedule;
Expand All @@ -99,6 +102,8 @@ public class Detector implements Writeable, ToXContentObject {

private Map<String, String> ruleIdMonitorIdMap;

private List<String> workflowIds;

private String ruleIndex;

private String alertsIndex;
Expand All @@ -117,7 +122,8 @@ public Detector(String id, Long version, String name, Boolean enabled, Schedule
Instant lastUpdateTime, Instant enabledTime, String logType,
User user, List<DetectorInput> inputs, List<DetectorTrigger> triggers, List<String> monitorIds,
String ruleIndex, String alertsIndex, String alertsHistoryIndex, String alertsHistoryIndexPattern,
String findingsIndex, String findingsIndexPattern, Map<String, String> rulePerMonitor) {
String findingsIndex, String findingsIndexPattern, Map<String, String> rulePerMonitor,
List<String> workflowIds, Boolean threatIntelEnabled) {
this.type = DETECTOR_TYPE;

this.id = id != null ? id : NO_ID;
Expand All @@ -139,6 +145,8 @@ public Detector(String id, Long version, String name, Boolean enabled, Schedule
this.findingsIndexPattern = findingsIndexPattern;
this.ruleIdMonitorIdMap = rulePerMonitor;
this.logType = logType;
this.workflowIds = workflowIds != null ? workflowIds : null;
this.threatIntelEnabled = threatIntelEnabled != null && threatIntelEnabled;

if (enabled) {
Objects.requireNonNull(enabledTime);
Expand All @@ -159,13 +167,15 @@ public Detector(StreamInput sin) throws IOException {
sin.readList(DetectorInput::readFrom),
sin.readList(DetectorTrigger::readFrom),
sin.readStringList(),
sin.readString(),
sin.readString(),
sin.readString(),
sin.readString(),
sin.readString(),
sin.readString(),
sin.readMap(StreamInput::readString, StreamInput::readString)
sin.readOptionalString(),
sin.readOptionalString(),
sin.readOptionalString(),
sin.readOptionalString(),
sin.readOptionalString(),
sin.readOptionalString(),
sin.readMap(StreamInput::readString, StreamInput::readString),
sin.readStringList(),
sin.readBoolean()
);
}

Expand Down Expand Up @@ -197,9 +207,18 @@ public void writeTo(StreamOutput out) throws IOException {
it.writeTo(out);
}
out.writeStringCollection(monitorIds);
out.writeString(ruleIndex);

out.writeOptionalString(ruleIndex);
out.writeOptionalString(alertsIndex);
out.writeOptionalString(alertsHistoryIndex);
out.writeOptionalString(alertsHistoryIndexPattern);
out.writeOptionalString(findingsIndex);
out.writeOptionalString(findingsIndexPattern);
out.writeMap(ruleIdMonitorIdMap, StreamOutput::writeString, StreamOutput::writeString);

if (workflowIds != null) {
out.writeStringCollection(workflowIds);
}
out.writeBoolean(threatIntelEnabled);
}

public XContentBuilder toXContentWithUser(XContentBuilder builder, Params params) throws IOException {
Expand Down Expand Up @@ -228,6 +247,7 @@ private XContentBuilder createXContentBuilder(XContentBuilder builder, ToXConten
}
}

builder.field(THREAT_INTEL_ENABLED_FIELD, threatIntelEnabled);
builder.field(ENABLED_FIELD, enabled);

if (enabledTime == null) {
Expand All @@ -253,6 +273,14 @@ private XContentBuilder createXContentBuilder(XContentBuilder builder, ToXConten
}

builder.field(ALERTING_MONITOR_ID, monitorIds);

if (workflowIds == null) {
builder.nullField(ALERTING_WORKFLOW_ID);
} else {
builder.field(ALERTING_WORKFLOW_ID, workflowIds);
}


builder.field(BUCKET_MONITOR_ID_RULE_ID, ruleIdMonitorIdMap);
builder.field(RULE_TOPIC_INDEX, ruleIndex);
builder.field(ALERTS_INDEX, alertsIndex);
Expand All @@ -261,7 +289,6 @@ private XContentBuilder createXContentBuilder(XContentBuilder builder, ToXConten
builder.field(FINDINGS_INDEX, findingsIndex);
builder.field(FINDINGS_INDEX_PATTERN, findingsIndexPattern);


if (params.paramAsBoolean("with_type", false)) {
builder.endObject();
}
Expand Down Expand Up @@ -299,6 +326,7 @@ public static Detector parse(XContentParser xcp, String id, Long version) throws
List<DetectorInput> inputs = new ArrayList<>();
List<DetectorTrigger> triggers = new ArrayList<>();
List<String> monitorIds = new ArrayList<>();
List<String> workflowIds = new ArrayList<>();
Map<String, String> rulePerMonitor = new HashMap<>();

String ruleIndex = null;
Expand All @@ -307,6 +335,7 @@ public static Detector parse(XContentParser xcp, String id, Long version) throws
String alertsHistoryIndexPattern = null;
String findingsIndex = null;
String findingsIndexPattern = null;
Boolean enableThreatIntel = false;

XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp);
while (xcp.nextToken() != XContentParser.Token.END_OBJECT) {
Expand All @@ -330,6 +359,9 @@ public static Detector parse(XContentParser xcp, String id, Long version) throws
case ENABLED_FIELD:
enabled = xcp.booleanValue();
break;
case THREAT_INTEL_ENABLED_FIELD:
enableThreatIntel = xcp.booleanValue();
break;
case SCHEDULE_FIELD:
schedule = Schedule.parse(xcp);
break;
Expand Down Expand Up @@ -374,6 +406,15 @@ public static Detector parse(XContentParser xcp, String id, Long version) throws
monitorIds.add(monitorId);
}
break;
case ALERTING_WORKFLOW_ID:
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp);
while (xcp.nextToken() != XContentParser.Token.END_ARRAY) {
String workflowId = xcp.textOrNull();
if (workflowId != null) {
workflowIds.add(workflowId);
}
}
break;
case BUCKET_MONITOR_ID_RULE_ID:
rulePerMonitor= xcp.mapStrings();
break;
Expand Down Expand Up @@ -429,7 +470,9 @@ public static Detector parse(XContentParser xcp, String id, Long version) throws
alertsHistoryIndexPattern,
findingsIndex,
findingsIndexPattern,
rulePerMonitor
rulePerMonitor,
workflowIds,
enableThreatIntel
);
}

Expand Down Expand Up @@ -566,10 +609,30 @@ public void setRuleIdMonitorIdMap(Map<String, String> ruleIdMonitorIdMap) {
this.ruleIdMonitorIdMap = ruleIdMonitorIdMap;
}

public void setWorkflowIds(List<String> workflowIds) {
this.workflowIds = workflowIds;
}

public void setThreatIntelEnabled(boolean threatIntelEnabled) {
this.threatIntelEnabled = threatIntelEnabled;
}

public List<String> getWorkflowIds() {
return workflowIds;
}

public String getDocLevelMonitorId() {
return ruleIdMonitorIdMap.get(DOC_LEVEL_MONITOR);
}

public boolean isWorkflowSupported() {
return workflowIds != null && !workflowIds.isEmpty();
}

public Boolean getThreatIntelEnabled() {
return threatIntelEnabled;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,42 @@
import java.io.IOException;
import java.util.List;

import static org.opensearch.securityanalytics.TestHelpers.parser;
import static org.opensearch.securityanalytics.TestHelpers.randomDetector;
import static org.opensearch.securityanalytics.TestHelpers.randomUser;
import static org.opensearch.securityanalytics.TestHelpers.randomUserEmpty;
import static org.opensearch.securityanalytics.TestHelpers.toJsonStringWithUser;

public class WriteableTests extends OpenSearchTestCase {

public void testDetectorAsStream() throws IOException {
Detector detector = randomDetector(List.of());
detector.setInputs(List.of(new DetectorInput("", List.of(), List.of(), List.of())));
logger.error(toJsonStringWithUser(detector));
BytesStreamOutput out = new BytesStreamOutput();
detector.writeTo(out);
StreamInput sin = StreamInput.wrap(out.bytes().toBytesRef().bytes);
Detector newDetector = new Detector(sin);
Assert.assertEquals("Round tripping Detector doesn't work", detector, newDetector);
}

public void testDetector() throws IOException { // an edge case of detector serialization that failed testDetectorAsAStream() intermittently
String detectorString = "{\"type\":\"detector\",\"name\":\"MczAuRCrve\",\"detector_type\":\"test_windows\"," +
"\"user\":{\"name\":\"QhKrfthgxw\",\"backend_roles\":[\"uYvGLCPhfX\",\"fOLkcRxMWR\"],\"roles\"" +
":[\"YuucNpVzTm\",\"all_access\"],\"custom_attribute_names\":[\"test_attr=test\"]," +
"\"user_requested_tenant\":null},\"threat_intel_enabled\":false,\"enabled\":false,\"enabled_time\"" +
":null,\"schedule\":{\"period\":{\"interval\":5,\"unit\":\"MINUTES\"}},\"inputs\":[{\"detector_input\"" +
":{\"description\":\"\",\"indices\":[],\"custom_rules\":[],\"pre_packaged_rules\":[]}}],\"triggers\"" +
":[{\"id\":\"SiWfaosBBiNA8if0E1bC\",\"name\":\"windows-trigger\",\"severity\":\"1\",\"types\"" +
":[\"test_windows\"],\"ids\":[\"QuarksPwDump Clearing Access History\"],\"sev_levels\":[\"high\"]," +
"\"tags\":[\"T0008\"],\"actions\":[],\"detection_types\":[\"rules\"]}],\"last_update_time\":" +
"1698300892093,\"monitor_id\":[\"\"],\"workflow_ids\":[],\"bucket_monitor_id_rule_id\"" +
":{},\"rule_topic_index\":\"\",\"alert_index\":\"\",\"alert_history_index\":\"\"," +
"\"alert_history_index_pattern\":\"\",\"findings_index\":\"\",\"findings_index_pattern\":\"\"}";
Detector detector = Detector.parse(parser(detectorString), null, null);
// Detector detector = randomDetector(List.of());
// detector.setInputs(List.of(new DetectorInput("", List.of(), List.of(), List.of())));
// logger.error(toJsonStringWithUser(detector));
BytesStreamOutput out = new BytesStreamOutput();
detector.writeTo(out);
StreamInput sin = StreamInput.wrap(out.bytes().toBytesRef().bytes);
Expand Down

0 comments on commit 33b1fdb

Please sign in to comment.