Skip to content

Commit d841035

Browse files
authored
Support for Generic Custom STIX Object (#98)
* Add initial working sample * Update java doc for better usage * Clenaup imports for usage * fix generic parser to have better usage * Add tests for Custom Object
1 parent 4106b42 commit d841035

File tree

9 files changed

+171
-3
lines changed

9 files changed

+171
-3
lines changed

src/main/java/io/digitalstate/stix/bundle/BundleableObject.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.annotation.JsonTypeInfo;
44
import io.digitalstate.stix.common.Stix;
5+
import io.digitalstate.stix.custom.objects.CustomObject;
56
import io.digitalstate.stix.datamarkings.GranularMarkingDm;
67
import io.digitalstate.stix.datamarkings.MarkingDefinitionDm;
78

@@ -13,7 +14,7 @@
1314
* Thus the name "BundleableObject". A Bundleable Object by STIX standard is: SDO, SRO, and Marking Definition.
1415
* The Type field is used to determine the sub-types as registered in the {@link io.digitalstate.stix.json.StixParsers}
1516
*/
16-
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY)
17+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true, defaultImpl = CustomObject.class)
1718
public interface BundleableObject extends Serializable, Stix {
1819

1920
String getType();

src/main/java/io/digitalstate/stix/common/StixCommonProperties.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY;
2929

3030
/**
31-
* Base interface used by Immutable STIX Domain Objects
31+
* Base interface used by Immutable STIX Bundleable Objects
3232
*/
3333
@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE)
3434
public interface StixCommonProperties extends StixSpecVersion, SdoDefaultValidator, BundleableObject {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.digitalstate.stix.common;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
5+
import io.digitalstate.stix.redaction.Redactable;
6+
import io.digitalstate.stix.validation.contraints.startswith.StartsWith;
7+
import io.digitalstate.stix.validation.groups.ValidateIdOnly;
8+
import org.immutables.value.Value;
9+
10+
import javax.validation.constraints.NotBlank;
11+
import javax.validation.constraints.NotNull;
12+
import javax.validation.constraints.Pattern;
13+
import javax.validation.groups.Default;
14+
15+
/**
16+
*
17+
*/
18+
@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE)
19+
public interface StixCustomObjectId {
20+
21+
@JsonProperty("id")
22+
@JsonPropertyDescription("Represents identifiers across the CTI specifications. The format consists of the name of the top-level object being identified, followed by two dashes (--), followed by a UUIDv4.")
23+
@Pattern(regexp = "^[a-z][a-z-]+[a-z]--[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
24+
@NotBlank(groups = {Default.class, ValidateIdOnly.class}, message = "Id is required")
25+
@StartsWith("x-")
26+
String getId();
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.digitalstate.stix.common;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
5+
import io.digitalstate.stix.validation.contraints.startswith.StartsWith;
6+
import io.digitalstate.stix.validation.groups.ValidateIdOnly;
7+
import org.immutables.value.Value;
8+
9+
import javax.validation.constraints.NotBlank;
10+
import javax.validation.constraints.Pattern;
11+
import javax.validation.constraints.Size;
12+
import javax.validation.groups.Default;
13+
14+
/**
15+
*
16+
*/
17+
@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE)
18+
public interface StixCustomObjectType {
19+
20+
@JsonProperty("type")
21+
@JsonPropertyDescription("The type property identifies the type of STIX Object (SDO, Relationship Object, etc). The value of the type field MUST be one of the types defined by a STIX Object (e.g., indicator).")
22+
@Pattern(regexp = "^\\-?[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\-?$")
23+
@Size(min = 3, max = 250)
24+
@NotBlank(groups = {Default.class, ValidateIdOnly.class}, message = "Type is required1")
25+
@StartsWith("x-")
26+
String getType();
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.digitalstate.stix.custom;
2+
3+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonUnwrapped;
6+
import io.digitalstate.stix.common.*;
7+
import io.digitalstate.stix.validation.contraints.startswith.StartsWith;
8+
import org.hibernate.validator.constraints.Length;
9+
10+
import java.util.Map;
11+
12+
/**
13+
* Provides a Generic STIX Custom Object to use for Bundleable objects when the object is not included in the mappings.
14+
* Note: Custom properties (x_) are included in the CustomObjectProperties
15+
*/
16+
public interface StixCustomObject extends
17+
StixCommonProperties,
18+
StixLabels,
19+
StixModified,
20+
StixRevoked{
21+
22+
@Override
23+
@StartsWith("x-")
24+
String getType();
25+
26+
@Override
27+
@StartsWith("x-")
28+
String getId();
29+
30+
//@TODO Future enhancement to create a custom deserializer that will support the difference between x_ props and the CustomObjectProperties()
31+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
32+
@JsonUnwrapped @JsonAnyGetter
33+
Map<@Length(min = 3,
34+
max = 250,
35+
message = "STIX Custom Properties must have a min key length of 3 and max of 250")
36+
String, Object> getCustomObjectProperties();
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.digitalstate.stix.custom.objects;
2+
3+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
6+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
7+
import io.digitalstate.stix.bundle.BundleableObject;
8+
import io.digitalstate.stix.custom.StixCustomObject;
9+
import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue;
10+
import io.digitalstate.stix.validation.groups.DefaultValuesProcessor;
11+
import org.immutables.serial.Serial;
12+
import org.immutables.value.Value;
13+
14+
15+
@Value.Immutable @Serial.Version(1L)
16+
//@DefaultTypeValue(value = "", groups = {DefaultValuesProcessor.class})
17+
@Value.Style(typeAbstract="Generic*", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true)
18+
@JsonSerialize(as = CustomObject.class) @JsonDeserialize(builder = CustomObject.Builder.class)
19+
@JsonPropertyOrder({"type", "id", "created_by_ref", "created",
20+
"modified", "revoked", "labels", "external_references",
21+
"object_marking_refs", "granular_markings"})
22+
public interface GenericCustomObject extends StixCustomObject {
23+
24+
25+
}

src/main/java/io/digitalstate/stix/json/StixParsers.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.digitalstate.stix.json;
22

3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.fasterxml.jackson.databind.JavaType;
35
import com.fasterxml.jackson.databind.ObjectMapper;
46
import com.fasterxml.jackson.databind.module.SimpleModule;
57
import com.fasterxml.jackson.datatype.guava.GuavaModule;
@@ -141,7 +143,7 @@ public static BundleableObject parseObject(String objectJsonString) throws IOExc
141143
}
142144
}
143145

144-
public static <T extends Stix> Stix parse(String bundleJsonString, Class<T> stixClass) throws IOException, StixParserValidationException {
146+
public static <T extends Stix> T parse(String bundleJsonString, Class<T> stixClass) throws IOException, StixParserValidationException {
145147
try {
146148
return getJsonMapper().readValue(bundleJsonString, stixClass);
147149
} catch (IOException ex) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package stix.custom
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper
4+
import io.digitalstate.stix.bundle.BundleableObject
5+
import io.digitalstate.stix.custom.StixCustomObject
6+
import io.digitalstate.stix.custom.objects.CustomObject
7+
import io.digitalstate.stix.json.StixParsers
8+
import spock.lang.Shared
9+
import spock.lang.Specification
10+
import spock.lang.Unroll
11+
12+
class CustomObjectSpec extends Specification {
13+
14+
@Shared ObjectMapper mapper = new ObjectMapper()
15+
16+
@Unroll
17+
def "Generic Object Test 1"() {
18+
when: "Attempt to Parse a custom object"
19+
String jsonString = getClass().getResource("/stix/custom/custom_object_1.json").getText("UTF-8")
20+
21+
then:
22+
StixCustomObject originalObject = (StixCustomObject)StixParsers.parseObject(jsonString)
23+
StixCustomObject originalObjectGeneric = StixParsers.parse(jsonString, CustomObject.class)
24+
BundleableObject bundleableObject = StixParsers.parse(jsonString, BundleableObject.class)
25+
// println originalObject
26+
// println originalObjectGeneric
27+
// println bundleableObject
28+
// println "********"
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"external_references": [
3+
{
4+
"external_id": "TA0006",
5+
"source_name": "mitre-attack",
6+
"url": "https://attack.mitre.org/tactics/TA0006"
7+
}
8+
],
9+
"id": "x-mitre-tactic--2558fd61-8c75-4730-94c4-11926db2a263",
10+
"name": "Credential Access",
11+
"created": "2018-10-17T00:14:20.652Z",
12+
"modified": "2018-10-17T00:14:20.652Z",
13+
"type": "x-mitre-tactic",
14+
"description": "Credential access represents techniques resulting in access to or control over system, domain, or service credentials that are used within an enterprise environment. Adversaries will likely attempt to obtain legitimate credentials from users or administrator accounts (local system administrator or domain users with administrator access) to use within the network. This allows the adversary to assume the identity of the account, with all of that account's permissions on the system and network, and makes it harder for defenders to detect the adversary. With sufficient access within a network, an adversary can create accounts for later use within the environment.",
15+
"x_mitre_shortname": "credential-access"
16+
}

0 commit comments

Comments
 (0)