Skip to content

Commit

Permalink
IOC data model and DTO. (opensearch-project#1029)
Browse files Browse the repository at this point in the history
* Rough draft of IOC data model.

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>

* Changed IOC value from a list to a string.

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>

* Added validation for IOC type, value, and feedId fields.

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>

* Refactored IocType to for ipv4, and ipv6.

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>

* Refactored IocType.

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>

* Added unit tests.

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>

---------

Signed-off-by: AWSHurneyt <hurneyt@amazon.com>
  • Loading branch information
AWSHurneyt authored and jowg-amazon committed Jun 3, 2024
1 parent 78f696e commit 0c8e5d3
Show file tree
Hide file tree
Showing 6 changed files with 719 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import java.util.Optional;
Expand Down Expand Up @@ -61,6 +62,7 @@
import org.opensearch.securityanalytics.mapper.IndexTemplateManager;
import org.opensearch.securityanalytics.mapper.MapperService;
import org.opensearch.securityanalytics.model.CustomLogType;
import org.opensearch.securityanalytics.model.IocDao;
import org.opensearch.securityanalytics.model.ThreatIntelFeedData;
import org.opensearch.securityanalytics.resthandler.*;
import org.opensearch.securityanalytics.threatIntel.service.DetectorThreatIntelService;
Expand Down Expand Up @@ -103,10 +105,17 @@ public class SecurityAnalyticsPlugin extends Plugin implements ActionPlugin, Map
public static final String FINDINGS_CORRELATE_URI = FINDINGS_BASE_URI + "/correlate";
public static final String LIST_CORRELATIONS_URI = PLUGINS_BASE_URI + "/correlations";
public static final String CORRELATION_RULES_BASE_URI = PLUGINS_BASE_URI + "/correlation/rules";
public static final String IOC_BASE_URI = PLUGINS_BASE_URI + "/ioc";
public static final String IOC_FETCH_BASE_URI = IOC_BASE_URI + "/fetch";

public static final String CUSTOM_LOG_TYPE_URI = PLUGINS_BASE_URI + "/logtype";
public static final String JOB_INDEX_NAME = ".opensearch-sap--job";
public static final Map<String, Object> TIF_JOB_INDEX_SETTING = Map.of(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1, IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-all", IndexMetadata.SETTING_INDEX_HIDDEN, true);
public static final String IOC_INDEX_NAME_BASE = ".opensearch-sap-ioc";
public static final String IOC_ALL_INDEX_PATTERN = IOC_INDEX_NAME_BASE + "-*";
public static final String IOC_DOMAIN_INDEX_NAME = IOC_INDEX_NAME_BASE + IocDao.IocType.DOMAIN.name().toLowerCase(Locale.ROOT);
public static final String IOC_HASH_INDEX_NAME = IOC_INDEX_NAME_BASE + IocDao.IocType.HASH.name().toLowerCase(Locale.ROOT);
public static final String IOC_IP_INDEX_NAME = IOC_INDEX_NAME_BASE + IocDao.IocType.IP.name().toLowerCase(Locale.ROOT);

private CorrelationRuleIndices correlationRuleIndices;

Expand Down
334 changes: 334 additions & 0 deletions src/main/java/org/opensearch/securityanalytics/model/IocDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.securityanalytics.model;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.core.xcontent.XContentParserUtils;

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

import static org.opensearch.securityanalytics.SecurityAnalyticsPlugin.IOC_DOMAIN_INDEX_NAME;
import static org.opensearch.securityanalytics.SecurityAnalyticsPlugin.IOC_HASH_INDEX_NAME;
import static org.opensearch.securityanalytics.SecurityAnalyticsPlugin.IOC_IP_INDEX_NAME;

public class IocDao implements Writeable, ToXContentObject {
private static final Logger logger = LogManager.getLogger(IocDao.class);

public static final String NO_ID = "";

static final String ID_FIELD = "id";
static final String NAME_FIELD = "name";
static final String TYPE_FIELD = "type";
static final String VALUE_FIELD = "value";
static final String SEVERITY_FIELD = "severity";
static final String SPEC_VERSION_FIELD = "spec_version";
static final String CREATED_FIELD = "created";
static final String MODIFIED_FIELD = "modified";
static final String DESCRIPTION_FIELD = "description";
static final String LABELS_FIELD = "labels";
static final String FEED_ID_FIELD = "feed_id";

private String id;
private String name;
private IocType type;
private String value;
private String severity;
private String specVersion;
private Instant created;
private Instant modified;
private String description;
private List<String> labels;
private String feedId;

public IocDao(
String id,
String name,
IocType type,
String value,
String severity,
String specVersion,
Instant created,
Instant modified,
String description,
List<String> labels,
String feedId
) {
this.id = id == null ? NO_ID : id;
this.name = name;
this.type = type;
this.value = value;
this.severity = severity;
this.specVersion = specVersion;
this.created = created;
this.modified = modified;
this.description = description;
this.labels = labels == null ? Collections.emptyList() : labels;
this.feedId = feedId;
validate();
}

public IocDao(StreamInput sin) throws IOException {
this(
sin.readString(), // id
sin.readString(), // name
sin.readEnum(IocType.class), // type
sin.readString(), // value
sin.readString(), // severity
sin.readString(), // specVersion
sin.readInstant(), // created
sin.readInstant(), // modified
sin.readString(), // description
sin.readStringList(), // labels
sin.readString() // feedId
);
}

public IocDao(IocDto iocDto) {
this(
iocDto.getId(),
iocDto.getName(),
iocDto.getType(),
iocDto.getValue(),
iocDto.getSeverity(),
iocDto.getSpecVersion(),
iocDto.getCreated(),
iocDto.getModified(),
iocDto.getDescription(),
iocDto.getLabels(),
iocDto.getFeedId()
);
}

public static IocDao readFrom(StreamInput sin) throws IOException {
return new IocDao(sin);
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(id);
out.writeString(name);
out.writeEnum(type);
out.writeString(value);
out.writeString(severity);
out.writeString(specVersion);
out.writeInstant(created);
out.writeInstant(modified);
out.writeString(description);
out.writeStringCollection(labels);
out.writeString(feedId);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field(ID_FIELD, id)
.field(NAME_FIELD, name)
.field(TYPE_FIELD, type)
.field(VALUE_FIELD, value)
.field(SEVERITY_FIELD, severity)
.field(SPEC_VERSION_FIELD, specVersion)
.timeField(CREATED_FIELD, created)
.timeField(MODIFIED_FIELD, modified)
.field(DESCRIPTION_FIELD, description)
.field(LABELS_FIELD, labels)
.field(FEED_ID_FIELD, feedId)
.endObject();
}

public static IocDao parse(XContentParser xcp, String id) throws IOException {
if (id == null) {
id = NO_ID;
}

String name = null;
IocType type = null;
String value = null;
String severity = null;
String specVersion = null;
Instant created = null;
Instant modified = null;
String description = null;
List<String> labels = Collections.emptyList();
String feedId = null;

XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp);
while (xcp.nextToken() != XContentParser.Token.END_OBJECT) {
String fieldName = xcp.currentName();
xcp.nextToken();

switch (fieldName) {
case NAME_FIELD:
name = xcp.text();
break;
case TYPE_FIELD:
type = IocType.valueOf(xcp.text().toUpperCase(Locale.ROOT));
break;
case VALUE_FIELD:
value = xcp.text();
break;
case SEVERITY_FIELD:
severity = xcp.text();
break;
case SPEC_VERSION_FIELD:
specVersion = xcp.text();
break;
case CREATED_FIELD:
if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) {
created = null;
} else if (xcp.currentToken().isValue()) {
created = Instant.ofEpochMilli(xcp.longValue());
} else {
XContentParserUtils.throwUnknownToken(xcp.currentToken(), xcp.getTokenLocation());
created = null;
}
break;
case MODIFIED_FIELD:
if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) {
modified = null;
} else if (xcp.currentToken().isValue()) {
modified = Instant.ofEpochMilli(xcp.longValue());
} else {
XContentParserUtils.throwUnknownToken(xcp.currentToken(), xcp.getTokenLocation());
modified = null;
}
break;
case DESCRIPTION_FIELD:
description = xcp.text();
break;
case LABELS_FIELD:
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp);
while (xcp.nextToken() != XContentParser.Token.END_ARRAY) {
String entry = xcp.textOrNull();
if (entry != null) {
labels.add(entry);
}
}
break;
case FEED_ID_FIELD:
feedId = xcp.text();
break;
default:
xcp.skipChildren();
}
}

return new IocDao(
id,
name,
type,
value,
severity,
specVersion,
created,
modified,
description,
labels,
feedId
);
}

/**
* Validates required fields.
* @throws IllegalArgumentException
*/
public void validate() throws IllegalArgumentException {
if (type == null) {
throw new IllegalArgumentException(String.format("[%s] is required.", TYPE_FIELD));
} else if (!Arrays.asList(IocType.values()).contains(type)) {
logger.debug("Unsupported IocType: {}", type);
throw new IllegalArgumentException(String.format("[%s] is not supported.", TYPE_FIELD));
}

if (value == null || value.isEmpty()) {
throw new IllegalArgumentException(String.format("[%s] is required.", VALUE_FIELD));
}

if (feedId == null || feedId.isEmpty()) {
throw new IllegalArgumentException(String.format("[%s] is required.", FEED_ID_FIELD));
}
}

public String getId() {
return id;
}

public String getName() {
return name;
}

public IocType getType() {
return type;
}

public String getValue() {
return value;
}

public String getSeverity() {
return severity;
}

public String getSpecVersion() {
return specVersion;
}

public Instant getCreated() {
return created;
}

public Instant getModified() {
return modified;
}

public String getDescription() {
return description;
}

public List<String> getLabels() {
return labels;
}

public String getFeedId() {
return feedId;
}

public enum IocType {
DOMAIN("domain") {
@Override
public String getSystemIndexName() {
return IOC_DOMAIN_INDEX_NAME;
}
},
HASH("hash") { // TODO placeholder
@Override
public String getSystemIndexName() {
return IOC_HASH_INDEX_NAME;
}
},
IP("ip") {
@Override
public String getSystemIndexName() {
return IOC_IP_INDEX_NAME;
}
};

IocType(String type) {}

public abstract String getSystemIndexName();
}
}
Loading

0 comments on commit 0c8e5d3

Please sign in to comment.