├── docs ├── BPMN │ ├── report_review.png │ ├── course_of_action_1.png │ ├── sample_processes_1.png │ ├── stix_data_translation.png │ ├── cyber-observable-processing-1.png │ └── taxii_collection_propagation.png ├── Diagrams │ └── Generic-Data-Flow.png ├── images │ └── stix-graph-example1.png ├── Common_Code_Snippets.md └── stix-java-notes.md ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── src ├── main │ └── java │ │ └── io │ │ └── digitalstate │ │ └── stix │ │ ├── validation │ │ ├── groups │ │ │ ├── NoValidation.java │ │ │ ├── DefaultValuesProcessor.java │ │ │ └── ValidateIdOnly.java │ │ ├── GenericValidation.java │ │ ├── sequences │ │ │ ├── SequenceDefault.java │ │ │ └── SequenceValidationIdOnly.java │ │ ├── contraints │ │ │ ├── coo │ │ │ │ ├── allowedparents │ │ │ │ │ ├── AllowedParents.java │ │ │ │ │ └── ValidateExtensions.java │ │ │ │ └── validateextensions │ │ │ │ │ └── ValidateReferences.java │ │ │ ├── startswith │ │ │ │ ├── StixStartsWithValidatorString.java │ │ │ │ └── StartsWith.java │ │ │ ├── relationship │ │ │ │ ├── RelationshipLimit.java │ │ │ │ ├── RelationshipTypeLimit.java │ │ │ │ └── StixRelationshipTypeLimitValidator.java │ │ │ ├── hashingvocab │ │ │ │ ├── HashingVocab.java │ │ │ │ └── StixHashingVocabValidatorString.java │ │ │ ├── vocab │ │ │ │ ├── Vocab.java │ │ │ │ ├── StixVocabValidatorCollection.java │ │ │ │ ├── StixVocabValidatorString.java │ │ │ │ └── StixVocabValidatorOptionalString.java │ │ │ ├── markingdefinitiontype │ │ │ │ └── MarkingDefinitionTypeLimit.java │ │ │ ├── defaulttypevalue │ │ │ │ └── DefaultTypeValue.java │ │ │ └── businessrule │ │ │ │ └── BusinessRule.java │ │ └── SdoDefaultValidator.java │ │ ├── graph │ │ ├── elements │ │ │ ├── GraphElement.java │ │ │ ├── Node.java │ │ │ ├── Edge.java │ │ │ └── NodeData.java │ │ ├── GraphGenerator.java │ │ ├── bundle │ │ │ ├── BundleObjectGraphGenerator.java │ │ │ └── BundleableObjectGraphGenerator.java │ │ ├── StixGraphGenerator.java │ │ ├── datamarkings │ │ │ └── MarkingDefinitionGraphGenerator.java │ │ ├── sro │ │ │ └── RelationshipSroGraphGenerator.java │ │ ├── sdo │ │ │ ├── DomainObjectGraphGenerator.java │ │ │ └── ObservedDataGraphGenerator.java │ │ └── coo │ │ │ └── CyberObservableGraphGenerator.java │ │ ├── helpers │ │ ├── StixSpecVersion.java │ │ ├── StixCustomPropertiesConfig.java │ │ └── StixDataFormats.java │ │ ├── datamarkings │ │ ├── StixMarkingObject.java │ │ ├── objects │ │ │ ├── StatementMarkingObject.java │ │ │ └── TlpMarkingObject.java │ │ └── GranularMarkingDm.java │ │ ├── common │ │ ├── Stix.java │ │ ├── StixSpecVersion.java │ │ ├── StixLabels.java │ │ ├── StixRevoked.java │ │ ├── StixModified.java │ │ ├── StixCustomProperties.java │ │ ├── StixCustomObjectType.java │ │ ├── StixCustomObjectId.java │ │ └── StixBoolean.java │ │ ├── sro │ │ └── RelationshipObject.java │ │ ├── vocabulary │ │ ├── StixVocabulary.java │ │ └── vocabularies │ │ │ ├── WindowsPeBinaryTypes.java │ │ │ ├── TlpLevels.java │ │ │ ├── AccountTypes.java │ │ │ ├── IndicatorLabels.java │ │ │ ├── NetworkSocketTypes.java │ │ │ ├── IdentityClasses.java │ │ │ ├── WindowsServiceTypes.java │ │ │ ├── AttackResourceLevels.java │ │ │ ├── NetworkSocketAddressFamilies.java │ │ │ ├── WindowsServiceStartTypes.java │ │ │ ├── ThreatActorSophistication.java │ │ │ ├── HashingAlgorithms.java │ │ │ ├── ThreatActorRoles.java │ │ │ ├── WindowsServiceStatuses.java │ │ │ ├── AttackMotivations.java │ │ │ ├── ToolLabels.java │ │ │ ├── ReportLabels.java │ │ │ ├── EncryptionAlgorithms.java │ │ │ ├── ThreatActorLabels.java │ │ │ ├── WindowsRegistryValueDataTypes.java │ │ │ ├── MalwareLabels.java │ │ │ ├── RelationshipTypes.java │ │ │ ├── NetworkSocketProtocolFamilies.java │ │ │ └── IndustrySectors.java │ │ ├── coo │ │ ├── CyberObservableObject.java │ │ ├── extension │ │ │ ├── CyberObservableExtension.java │ │ │ ├── CyberObservableExtensionCommonProperties.java │ │ │ └── types │ │ │ │ ├── IcmpExtensionExt.java │ │ │ │ ├── ArchiveFileExtensionExt.java │ │ │ │ └── NtfsFileExtenstionExt.java │ │ ├── json │ │ │ ├── extension │ │ │ │ ├── CyberObservableExtensionsFieldSerializer.java │ │ │ │ └── CyberObservableExtensionsFieldDeserializer.java │ │ │ └── observables │ │ │ │ ├── CyberObservableSetFieldSerializer.java │ │ │ │ └── CyberObservableSetFieldDeserializer.java │ │ ├── objects │ │ │ ├── MutexCoo.java │ │ │ ├── DomainNameCoo.java │ │ │ ├── MacAddressCoo.java │ │ │ ├── AutonomousSystemCoo.java │ │ │ ├── EmailAddressCoo.java │ │ │ └── UrlCoo.java │ │ └── types │ │ │ ├── WindowsRegistryValueObj.java │ │ │ └── NtfsAlternateDataStreamObj.java │ │ ├── json │ │ ├── StixInstantSerializer.java │ │ ├── StixInstantDeserializer.java │ │ ├── StixBooleanDeserializer.java │ │ ├── StixParserValidationException.java │ │ ├── StixBooleanSerializer.java │ │ └── converters │ │ │ └── dehydrated │ │ │ ├── DomainObjectConverter.java │ │ │ ├── DomainObjectOptionalConverter.java │ │ │ ├── BundleableObjectConverter.java │ │ │ ├── MarkingDefinitionConverter.java │ │ │ ├── MarkingDefinitionSetConverter.java │ │ │ └── BundleableObjectSetConverter.java │ │ ├── sdo │ │ ├── DomainObject.java │ │ ├── objects │ │ │ ├── VulnerabilitySdo.java │ │ │ └── AttackPatternSdo.java │ │ └── types │ │ │ └── KillChainPhaseType.java │ │ ├── redaction │ │ └── Redactable.java │ │ ├── bundle │ │ └── BundleableObject.java │ │ └── custom │ │ ├── objects │ │ └── GenericCustomObject.java │ │ └── StixCustomObject.java └── test │ ├── groovy │ ├── faker │ │ └── configs │ │ │ └── sdo │ │ │ └── observeddata │ │ │ ├── ObservedData_FileCooConfig.groovy │ │ │ ├── ObservedData_UrlCooConfig.groovy │ │ │ ├── ObservedData_MutexCooConfig.groovy │ │ │ ├── ObservedData_ArtifactCooConfig.groovy │ │ │ ├── ObservedData_DirectoryCooConfig.groovy │ │ │ ├── ObservedData_ProcessCooConfig.groovy │ │ │ ├── ObservedData_SoftwareCooConfig.groovy │ │ │ ├── ObservedData_DomainNameCooConfig.groovy │ │ │ ├── ObservedData_Ipv4AddressCooConfig.groovy │ │ │ ├── ObservedData_Ipv6AddressCooConfig.groovy │ │ │ ├── ObservedData_MacAddressCooConfig.groovy │ │ │ ├── ObservedData_ObjectMarkingsConfig.groovy │ │ │ ├── ObservedData_UserAccountCooConfig.groovy │ │ │ ├── ObservedData_EmailAddressCooConfig.groovy │ │ │ ├── ObservedData_EmailMessageCooConfig.groovy │ │ │ ├── ObservedData_GranularMarkingConfig.groovy │ │ │ ├── ObservedData_NetworkTrafficCooConfig.groovy │ │ │ ├── ObservedData_AutonomousSystemCooConfig.groovy │ │ │ ├── ObservedData_ExternalReferencesConfig.groovy │ │ │ ├── ObservedData_X509CertificateCooConfig.groovy │ │ │ └── ObservedData_WindowsRegistryKeyCooConfig.groovy │ └── stix │ │ ├── custom │ │ └── CustomObjectSpec.groovy │ │ ├── sdo │ │ ├── MalwareSpec.groovy │ │ ├── CampaignSpec.groovy │ │ ├── ToolSpec.groovy │ │ ├── IdentitySpec.groovy │ │ ├── IndicatorSpec.groovy │ │ ├── ReportSpec.groovy │ │ ├── IntrusionSetSpec.groovy │ │ ├── AttackPatternSpec.groovy │ │ ├── CourseOfActionSpec.groovy │ │ ├── ThreatActorSpec.groovy │ │ ├── ObservedDataSpec.groovy │ │ └── VulnerabilitySpec.groovy │ │ ├── sro │ │ ├── SightingSpec.groovy │ │ └── RelationshipSpec.groovy │ │ └── datamarkings │ │ └── MarkingDefinitionSpec.groovy │ └── resources │ └── stix │ ├── baseline │ ├── README.md │ └── json │ │ └── sdo │ │ └── indicator │ │ └── indicators.json │ └── custom │ └── custom_object_1.json ├── .travis.yml ├── .gitignore └── LICENSE /docs/BPMN/report_review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/BPMN/report_review.png -------------------------------------------------------------------------------- /docs/BPMN/course_of_action_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/BPMN/course_of_action_1.png -------------------------------------------------------------------------------- /docs/BPMN/sample_processes_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/BPMN/sample_processes_1.png -------------------------------------------------------------------------------- /docs/BPMN/stix_data_translation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/BPMN/stix_data_translation.png -------------------------------------------------------------------------------- /docs/Diagrams/Generic-Data-Flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/Diagrams/Generic-Data-Flow.png -------------------------------------------------------------------------------- /docs/images/stix-graph-example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/images/stix-graph-example1.png -------------------------------------------------------------------------------- /docs/BPMN/cyber-observable-processing-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/BPMN/cyber-observable-processing-1.png -------------------------------------------------------------------------------- /docs/BPMN/taxii_collection_propagation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenOTT/STIX-Java/HEAD/docs/BPMN/taxii_collection_propagation.png -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/groups/NoValidation.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.groups; 2 | 3 | public interface NoValidation { 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | - openjdk8 6 | 7 | sudo: false 8 | install: true 9 | 10 | cache: 11 | directories: 12 | - $HOME/.m2 -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/groups/DefaultValuesProcessor.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.groups; 2 | 3 | public interface DefaultValuesProcessor { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/elements/GraphElement.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.elements; 2 | 3 | import java.util.Set; 4 | 5 | public interface GraphElement { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/groups/ValidateIdOnly.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.groups; 2 | 3 | /** 4 | * Validate ID Only Group used by javax.validation 5 | */ 6 | 7 | public interface ValidateIdOnly { 8 | } 9 | -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_FileCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_FileCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UrlCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_UrlCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MutexCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_MutexCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/helpers/StixSpecVersion.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.helpers; 2 | 3 | 4 | public class StixSpecVersion { 5 | 6 | /** 7 | * STIX spec version number 8 | */ 9 | public static final String SPECVERSION = "2.0"; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ArtifactCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_ArtifactCooConfig{ 4 | int occurrence_probability = 100 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DirectoryCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_DirectoryCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ProcessCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_ProcessCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_SoftwareCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_SoftwareCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DomainNameCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_DomainNameCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv4AddressCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_Ipv4AddressCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv6AddressCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_Ipv6AddressCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MacAddressCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_MacAddressCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ObjectMarkingsConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_ObjectMarkingsConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UserAccountCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_UserAccountCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /docs/Common_Code_Snippets.md: -------------------------------------------------------------------------------- 1 | 2 | #Hashes 3 | 4 | ```java 5 | @JsonProperty("hashes") @JsonInclude(NON_EMPTY) 6 | @Size(min = 1, message = "Must have at least 1 hash value") 7 | Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); 8 | ``` -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailAddressCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_EmailAddressCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailMessageCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_EmailMessageCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_GranularMarkingConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_GranularMarkingConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_NetworkTrafficCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_NetworkTrafficCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/GraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph; 2 | 3 | import io.digitalstate.stix.graph.elements.GraphElement; 4 | 5 | import java.util.Set; 6 | 7 | public interface GraphGenerator { 8 | 9 | Set process(); 10 | } 11 | -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_AutonomousSystemCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_AutonomousSystemCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ExternalReferencesConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_ExternalReferencesConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_X509CertificateCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_X509CertificateCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/test/groovy/faker/configs/sdo/observeddata/ObservedData_WindowsRegistryKeyCooConfig.groovy: -------------------------------------------------------------------------------- 1 | package faker.configs.sdo.observeddata 2 | 3 | class ObservedData_WindowsRegistryKeyCooConfig { 4 | int occurrence_probability = 10 5 | int occurs_count_lower = 1 6 | int occurs_count_upper = 5 7 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/datamarkings/StixMarkingObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.datamarkings; 2 | 3 | import io.digitalstate.stix.common.StixCustomProperties; 4 | 5 | import java.io.Serializable; 6 | 7 | public interface StixMarkingObject extends StixCustomProperties, Serializable { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/GenericValidation.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation; 2 | 3 | import org.immutables.value.Value; 4 | 5 | public interface GenericValidation extends SdoDefaultValidator { 6 | 7 | @Value.Check 8 | default public void validateEntity(){ 9 | this.validate(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/resources/stix/baseline/README.md: -------------------------------------------------------------------------------- 1 | # Baseline JSON file 2 | 3 | This package contains a set of baseline JSON files that contain "happy state"/valid STIX data. 4 | 5 | The goal is to have a consistent set of files that represent basic valid json for each of the STIX related objects. 6 | 7 | This JSON files are used in tests to validate serialization capabilities. -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/Stix.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 2 | 3 | /** 4 | * Base generic interface used for indentifying that a object is a Stix based object (a Bundle or Bundleable object) 5 | * Used mainly for generics and parsing. 6 | * May be used in the future as a entry point for STIX instance creation 7 | */ 8 | public interface Stix { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/sro/RelationshipObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.sro; 2 | 3 | import io.digitalstate.stix.common.*; 4 | 5 | import java.io.Serializable; 6 | 7 | public interface RelationshipObject extends Serializable, 8 | StixCommonProperties, 9 | StixCustomProperties, 10 | StixLabels, 11 | StixModified, 12 | StixRevoked { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/sequences/SequenceDefault.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.sequences; 2 | 3 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 4 | 5 | import javax.validation.GroupSequence; 6 | import javax.validation.groups.Default; 7 | 8 | @GroupSequence({DefaultValuesProcessor.class, Default.class}) 9 | public interface SequenceDefault { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/sequences/SequenceValidationIdOnly.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.sequences; 2 | 3 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 4 | import io.digitalstate.stix.validation.groups.ValidateIdOnly; 5 | 6 | import javax.validation.GroupSequence; 7 | 8 | @GroupSequence({DefaultValuesProcessor.class, ValidateIdOnly.class}) 9 | public interface SequenceValidationIdOnly { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/StixVocabulary.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary; 2 | 3 | import java.util.Set; 4 | 5 | public interface StixVocabulary { 6 | 7 | /** 8 | * Get all default terms 9 | * @return 10 | */ 11 | Set getAllTerms(); 12 | 13 | /** 14 | * Get all default terms and append some additional terms 15 | * @param terms 16 | * @return 17 | */ 18 | Set getAllTermsWithAdditional(String[] terms); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/CyberObservableObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.digitalstate.stix.common.StixCustomProperties; 5 | import io.digitalstate.stix.validation.contraints.coo.allowedparents.ValidateExtensions; 6 | 7 | import java.io.Serializable; 8 | 9 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) 10 | @ValidateExtensions 11 | public interface CyberObservableObject extends Serializable, 12 | CyberObservableObjectCommonProperties, 13 | StixCustomProperties { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/stix/baseline/json/sdo/indicator/indicators.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "bundle", 3 | "id": "bundle--6fab341c-36c5-4a7f-be29-0a6e3b85e7b0", 4 | "spec_version": "2.0", 5 | "objects": [ 6 | { 7 | "type": "indicator", 8 | "id": "indicator--59ccb738-921a-4941-8ab2-33da522bd4e1", 9 | "created": "2019-04-08T22:59:50.0Z", 10 | "modified": "2019-04-08T22:59:50.00Z", 11 | "revoked": false, 12 | "labels": [ 13 | "malicious-activity" 14 | ], 15 | "name": "128.0.0.1", 16 | "pattern": "[ipv4-addr:value = '128.0.0.1']", 17 | "valid_from": "2019-04-08T22:59:50.000Z" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixSpecVersion.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import org.immutables.value.Value; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | public interface StixSpecVersion { 9 | 10 | /** 11 | * Helper attribute to track the STIX Spec Version that was used for this object. 12 | * @return String of STIX Spec Version, example: "2.0" 13 | */ 14 | @NotBlank 15 | @JsonIgnore 16 | @Value.Lazy 17 | default String getSpecVersion(){ 18 | return io.digitalstate.stix.helpers.StixSpecVersion.SPECVERSION; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/elements/Node.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import javax.validation.constraints.NotNull; 6 | 7 | public class Node implements GraphElement { 8 | 9 | private NodeData data; 10 | 11 | public Node(@NotNull String id, String type, String parent, Object jsonData) { 12 | this.data = new NodeData(id, type, parent, jsonData); 13 | } 14 | 15 | @JsonProperty("data") 16 | public NodeData getData() { 17 | return data; 18 | } 19 | 20 | public void setData(NodeData data) { 21 | this.data = data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtension.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.extension; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.digitalstate.stix.common.StixCustomProperties; 5 | import io.digitalstate.stix.validation.GenericValidation; 6 | 7 | 8 | /** 9 | * Interface to tag Cyber Observable Extension classes 10 | * 11 | */ 12 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) 13 | public interface CyberObservableExtension extends 14 | CyberObservableExtensionCommonProperties, 15 | GenericValidation, 16 | StixCustomProperties { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/elements/Edge.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import javax.validation.constraints.NotNull; 6 | 7 | public class Edge implements GraphElement { 8 | 9 | private EdgeData data; 10 | 11 | public Edge(@NotNull String id, String type, String source, String target, Object jsonData) { 12 | this.data = new EdgeData(id, type, source, target, jsonData); 13 | } 14 | 15 | @JsonProperty("data") 16 | public EdgeData getData() { 17 | return data; 18 | } 19 | 20 | public void setData(EdgeData data) { 21 | this.data = data; 22 | } 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/AllowedParents.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.coo.allowedparents; 2 | 3 | import io.digitalstate.stix.coo.CyberObservableObject; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 11 | import static java.lang.annotation.ElementType.TYPE; 12 | 13 | /** 14 | * Values must be Array of Cyber Observable Object Interfaces!. 15 | */ 16 | @Documented 17 | @Target( { ANNOTATION_TYPE, TYPE }) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | public @interface AllowedParents { 20 | Class[] value(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/StixInstantSerializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.SerializerProvider; 5 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 6 | import io.digitalstate.stix.common.StixInstant; 7 | 8 | import java.io.IOException; 9 | 10 | public class StixInstantSerializer extends StdSerializer { 11 | 12 | public StixInstantSerializer() { 13 | this(null); 14 | } 15 | 16 | public StixInstantSerializer(Class t) { 17 | super(t); 18 | } 19 | 20 | @Override 21 | public void serialize(final StixInstant value, JsonGenerator gen, SerializerProvider provider) throws IOException { 22 | gen.writeString(value.toString()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtensionCommonProperties.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.extension; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import org.immutables.value.Value; 6 | 7 | import javax.validation.constraints.NotBlank; 8 | import javax.validation.constraints.Size; 9 | 10 | @Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) 11 | public interface CyberObservableExtensionCommonProperties { 12 | 13 | /** 14 | * This property is used for generation of the dictionary during serialization, and used as the "Type" mapping value for polymorphic when deserializing. 15 | * @return 16 | */ 17 | @NotBlank 18 | @JsonIgnore 19 | @JsonProperty("type") 20 | @Size(min = 3, max = 250) 21 | String getType(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | target/ 26 | pom.xml.tag 27 | pom.xml.releaseBackup 28 | pom.xml.versionsBackup 29 | pom.xml.next 30 | release.properties 31 | dependency-reduced-pom.xml 32 | buildNumber.properties 33 | .mvn/timing.properties 34 | 35 | .settings 36 | bin/ 37 | build-reports/ 38 | 39 | # Eclipse 40 | .classpath 41 | .factorypath 42 | .project 43 | .settings/ 44 | 45 | # Intellij 46 | .idea/ 47 | *.iws 48 | *.iml 49 | 50 | # Mac 51 | .DS_Store 52 | 53 | # Maven 54 | log/ -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/StixInstantDeserializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 7 | import io.digitalstate.stix.common.StixInstant; 8 | 9 | import java.io.IOException; 10 | 11 | public class StixInstantDeserializer extends StdDeserializer { 12 | 13 | public StixInstantDeserializer(){ 14 | this(null); 15 | } 16 | 17 | public StixInstantDeserializer(Class vc) { 18 | super(vc); 19 | } 20 | 21 | @Override 22 | public StixInstant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 23 | return StixInstant.parse(p.getText()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/StixBooleanDeserializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 7 | import io.digitalstate.stix.common.StixBoolean; 8 | 9 | import java.io.IOException; 10 | 11 | public class StixBooleanDeserializer extends StdDeserializer { 12 | 13 | public StixBooleanDeserializer(){ 14 | this(null); 15 | } 16 | 17 | public StixBooleanDeserializer(Class vc) { 18 | super(vc); 19 | } 20 | 21 | @Override 22 | public StixBoolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 23 | return new StixBoolean(p.getBooleanValue()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/bundle/BundleObjectGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.bundle; 2 | 3 | import io.digitalstate.stix.bundle.BundleObject; 4 | import io.digitalstate.stix.graph.GraphGenerator; 5 | import io.digitalstate.stix.graph.elements.GraphElement; 6 | 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | public class BundleObjectGraphGenerator implements GraphGenerator { 11 | 12 | private BundleObject object; 13 | 14 | public BundleObjectGraphGenerator(BundleObject object) { 15 | this.object = object; 16 | } 17 | 18 | public BundleObject getObject() { 19 | return object; 20 | } 21 | 22 | public Set process(){ 23 | Set items = new HashSet<>(); 24 | 25 | object.getObjects().forEach(o->{ 26 | items.addAll(new BundleableObjectGraphGenerator(o).process()); 27 | }); 28 | return items; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixLabels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; 6 | import io.digitalstate.stix.redaction.Redactable; 7 | import org.hibernate.validator.constraints.Length; 8 | import org.immutables.value.Value; 9 | 10 | import javax.validation.constraints.NotNull; 11 | import java.util.Set; 12 | 13 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 14 | 15 | /** 16 | * 17 | */ 18 | @Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) 19 | public interface StixLabels { 20 | 21 | @NotNull 22 | @JsonProperty("labels") @JsonInclude(NON_EMPTY) 23 | @JsonPropertyDescription("The labels property specifies a set of classifications.") 24 | @Redactable 25 | Set<@Length(min = 1) String> getLabels(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsPeBinaryTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class WindowsPeBinaryTypes implements StixVocabulary { 13 | 14 | @JsonProperty("windows-pebinary-type-ov") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "exe", "dll", "sys" 17 | )); 18 | 19 | @Override 20 | public Set getAllTerms() { 21 | return terms; 22 | } 23 | 24 | @Override 25 | public Set getAllTermsWithAdditional(String[] terms) { 26 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 27 | .collect(Collectors.toCollection(HashSet::new)); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixRevoked.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; 6 | import io.digitalstate.stix.redaction.Redactable; 7 | import org.immutables.value.Value; 8 | 9 | import javax.validation.constraints.NotNull; 10 | 11 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 12 | 13 | /** 14 | * 15 | */ 16 | @Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) 17 | public interface StixRevoked { 18 | 19 | @NotNull 20 | @JsonProperty("revoked") 21 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 22 | @JsonPropertyDescription("The revoked property indicates whether the object has been revoked.") 23 | @Value.Default 24 | @Redactable 25 | default StixBoolean getRevoked(){ 26 | return new StixBoolean(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/TlpLevels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class TlpLevels implements StixVocabulary { 13 | 14 | @JsonProperty("tlp_levels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "white", "green", "amber", "red")); 17 | 18 | // 19 | // Getters and Setters 20 | // 21 | 22 | @Override 23 | public Set getAllTerms() { 24 | return terms; 25 | } 26 | 27 | @Override 28 | public Set getAllTermsWithAdditional(String[] terms) { 29 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 30 | .collect(Collectors.toCollection(HashSet::new)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AccountTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class AccountTypes implements StixVocabulary { 13 | @JsonProperty("account-type-ov") 14 | private Set terms = new HashSet<>(Arrays.asList( 15 | "unix", "windows local", "windows domain", "ldap", "tacacs", "radius", 16 | "nis", "openid", "facebook", "skype", "twitter", "kavi" 17 | )); 18 | 19 | @Override 20 | public Set getAllTerms() { 21 | return terms; 22 | } 23 | 24 | @Override 25 | public Set getAllTermsWithAdditional(String[] terms) { 26 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 27 | .collect(Collectors.toCollection(HashSet::new)); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndicatorLabels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class IndicatorLabels implements StixVocabulary { 13 | 14 | @JsonProperty("indicator_labels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "anomalous-activity", "anonymization", "benign", 17 | "compromised", "malicious-activity", "attribution")); 18 | 19 | 20 | @Override 21 | public Set getAllTerms() { 22 | return terms; 23 | } 24 | 25 | @Override 26 | public Set getAllTermsWithAdditional(String[] terms) { 27 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 28 | .collect(Collectors.toCollection(HashSet::new)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class NetworkSocketTypes implements StixVocabulary { 13 | 14 | @JsonProperty("network-socket-type-enum") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "SOCK_STREAM", "SOCK_DGRAM", 17 | "SOCK_RAW", "SOCK_RDM", 18 | "SOCK_SEQPACKET" 19 | )); 20 | 21 | @Override 22 | public Set getAllTerms() { 23 | return terms; 24 | } 25 | 26 | @Override 27 | public Set getAllTermsWithAdditional(String[] terms) { 28 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 29 | .collect(Collectors.toCollection(HashSet::new)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/ValidateExtensions.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.coo.allowedparents; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 11 | import static java.lang.annotation.ElementType.TYPE; 12 | 13 | /** 14 | * Used to Validation Cyber Observable Extensions 15 | * Should only be placed on Cyber Observable Object classes. 16 | */ 17 | @Documented 18 | @Constraint(validatedBy = {StixValidateParentCooValidator.class}) 19 | @Target( { ANNOTATION_TYPE, TYPE }) 20 | @Retention(RetentionPolicy.RUNTIME) 21 | public @interface ValidateExtensions { 22 | String message() default "{io.digitalstate.stix.validation.contraints.coo.allowedparents.ValidateReferences}"; 23 | Class[] groups() default {}; 24 | Class[] payload() default {}; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IdentityClasses.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class IdentityClasses implements StixVocabulary { 13 | 14 | @JsonProperty("identity_classes_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "individual", "group", "organization", 17 | "class", "unknown")); 18 | 19 | // 20 | // Getters and Setters 21 | // 22 | 23 | @Override 24 | public Set getAllTerms() { 25 | return terms; 26 | } 27 | 28 | @Override 29 | public Set getAllTermsWithAdditional(String[] terms) { 30 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 31 | .collect(Collectors.toCollection(HashSet::new)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/sdo/DomainObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.sdo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import io.digitalstate.stix.common.*; 5 | import io.digitalstate.stix.sro.objects.RelationshipSro; 6 | 7 | import javax.validation.constraints.NotNull; 8 | import java.io.Serializable; 9 | import java.util.Set; 10 | 11 | /** 12 | * Base interface used by Immutable STIX Domain Objects 13 | */ 14 | public interface DomainObject extends Serializable, 15 | StixCommonProperties, 16 | StixCustomProperties, 17 | StixLabels, 18 | StixModified, 19 | StixRevoked{ 20 | 21 | /** 22 | * This is used with the SROs. The SRO interface enforces what relationships can be created. The Relationships can then be stored in the Domain object if they choose. 23 | * Otherwise you would typically add these Relationship SROs that are specific to SDOs, can be grabbed during bundle creation. 24 | * @return Set of Relationship SROs 25 | */ 26 | @NotNull 27 | @JsonIgnore 28 | Set getRelationships(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/StixParserValidationException.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json; 2 | 3 | import javax.validation.ConstraintViolation; 4 | import javax.validation.ConstraintViolationException; 5 | import javax.validation.ValidationException; 6 | import java.util.Collections; 7 | import java.util.Set; 8 | 9 | public class StixParserValidationException extends RuntimeException { 10 | 11 | public StixParserValidationException(ValidationException exceptionCause) { 12 | super(exceptionCause); 13 | } 14 | 15 | /** 16 | * If Cause is ${@link ConstraintViolationException}, 17 | * then returns the set of constraint validations, or else returns null. 18 | * @return Set of Constraint Violations 19 | */ 20 | public Set> getConstraintValidations() { 21 | if (getCause().getClass().equals(ConstraintViolationException.class)) { 22 | return ((ConstraintViolationException) getCause()).getConstraintViolations(); 23 | } else { 24 | return Collections.emptySet(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/startswith/StixStartsWithValidatorString.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.startswith; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | 6 | public class StixStartsWithValidatorString implements ConstraintValidator { 7 | 8 | private String prefix; 9 | 10 | @Override 11 | public void initialize(StartsWith startsWithConstraint) { 12 | prefix = startsWithConstraint.value(); 13 | } 14 | 15 | @Override 16 | public boolean isValid(String value, ConstraintValidatorContext cxt) { 17 | if (value.startsWith(prefix)){ 18 | return true; 19 | } else{ 20 | cxt.disableDefaultConstraintViolation(); 21 | String violationMessage = "StartsWith violation: string must start with value: " 22 | + prefix + ", but provided value: " + value; 23 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 24 | return false; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class WindowsServiceTypes implements StixVocabulary { 13 | 14 | @JsonProperty("windows-service-type-enum") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "SERVICE_KERNEL_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER", 17 | "SERVICE_WIN32_OWN_PROCESS", "SERVICE_WIN32_SHARE_PROCESS" 18 | )); 19 | 20 | @Override 21 | public Set getAllTerms() { 22 | return terms; 23 | } 24 | 25 | @Override 26 | public Set getAllTermsWithAdditional(String[] terms) { 27 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 28 | .collect(Collectors.toCollection(HashSet::new)); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackResourceLevels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class AttackResourceLevels implements StixVocabulary { 13 | 14 | @JsonProperty("attack_resource_levels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "individual", "club", "content", 17 | "team", "organization", "government")); 18 | 19 | // 20 | // Getters and Setters 21 | // 22 | 23 | @Override 24 | public Set getAllTerms() { 25 | return terms; 26 | } 27 | 28 | @Override 29 | public Set getAllTermsWithAdditional(String[] terms) { 30 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 31 | .collect(Collectors.toCollection(HashSet::new)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketAddressFamilies.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class NetworkSocketAddressFamilies implements StixVocabulary { 13 | 14 | @JsonProperty("network-socket-address-family-enum") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "AF_UNSPEC", "AF_INET", "AF_IPX", "AF_APPLETALK", 17 | "AF_NETBIOS", "AF_INET_6", "AF_IRDA", "AF_BTH" 18 | )); 19 | 20 | @Override 21 | public Set getAllTerms() { 22 | return terms; 23 | } 24 | 25 | @Override 26 | public Set getAllTermsWithAdditional(String[] terms) { 27 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 28 | .collect(Collectors.toCollection(HashSet::new)); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStartTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class WindowsServiceStartTypes implements StixVocabulary { 13 | 14 | @JsonProperty("windows-service-start-type-enum") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "SERVICE_AUTO_START", "SERVICE_BOOT_START", "SERVICE_DEMAND_START", 17 | "SERVICE_DISABLED", "SERVICE_SYSTEM_ALERT" 18 | )); 19 | 20 | @Override 21 | public Set getAllTerms() { 22 | return terms; 23 | } 24 | 25 | @Override 26 | public Set getAllTermsWithAdditional(String[] terms) { 27 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 28 | .collect(Collectors.toCollection(HashSet::new)); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 https://github.com/StephenOTT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/StixBooleanSerializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.SerializerProvider; 5 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 6 | import io.digitalstate.stix.common.StixBoolean; 7 | 8 | import java.io.IOException; 9 | 10 | public class StixBooleanSerializer extends StdSerializer { 11 | 12 | public StixBooleanSerializer() { 13 | this(null); 14 | } 15 | 16 | public StixBooleanSerializer(Class t) { 17 | super(t); 18 | } 19 | 20 | @Override 21 | public boolean isEmpty(SerializerProvider provider, StixBoolean value) { 22 | return !value.isdefinedValue(); 23 | //@TODO Future enhancement: Make this a configuration so the impl can decide if non defined values should still be serialized as their default values 24 | } 25 | 26 | @Override 27 | public void serialize(final StixBoolean value, JsonGenerator gen, SerializerProvider provider) throws IOException { 28 | gen.writeBoolean(value.getStixBooleanValue()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixModified.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | import io.digitalstate.stix.json.StixInstantDeserializer; 8 | import io.digitalstate.stix.json.StixInstantSerializer; 9 | import io.digitalstate.stix.redaction.Redactable; 10 | import org.immutables.value.Value; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import java.time.Instant; 14 | 15 | /** 16 | * 17 | */ 18 | @Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) 19 | public interface StixModified { 20 | 21 | @NotNull 22 | @JsonProperty("modified") 23 | @JsonPropertyDescription("The modified property represents the time that this particular version of the object was created. The timstamp value MUST be precise to the nearest millisecond.") 24 | @Value.Default 25 | @Redactable 26 | default StixInstant getModified(){ 27 | return new StixInstant(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorSophistication.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class ThreatActorSophistication implements StixVocabulary { 13 | 14 | @JsonProperty("threat_actor_sophistication_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "none", "minimal", "intermediate", 17 | "advanced", "expert", "innovator", 18 | "strategic")); 19 | 20 | // 21 | // Getters and Setters 22 | // 23 | 24 | @Override 25 | public Set getAllTerms() { 26 | return terms; 27 | } 28 | 29 | @Override 30 | public Set getAllTermsWithAdditional(String[] terms) { 31 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 32 | .collect(Collectors.toCollection(HashSet::new)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/resources/stix/custom/custom_object_1.json: -------------------------------------------------------------------------------- 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 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/HashingAlgorithms.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class HashingAlgorithms implements StixVocabulary { 13 | 14 | @JsonProperty("hashing_algorithms_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "MD5", "MD6", "RIPEMD-160", "SHA-1", 17 | "SHA-224", "SHA-256", "SHA-384", "SHA-512", 18 | "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", 19 | "SSDEEP", "WHIRLPOOL")); 20 | 21 | 22 | @Override 23 | public Set getAllTerms() { 24 | return terms; 25 | } 26 | 27 | @Override 28 | public Set getAllTermsWithAdditional(String[] terms) { 29 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 30 | .collect(Collectors.toCollection(HashSet::new)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorRoles.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class ThreatActorRoles implements StixVocabulary { 13 | 14 | @JsonProperty("threat_actor_roles_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "agent", "director", "independent", 17 | "infrastructure-architect", "infrastructure-operator", "malware-author", 18 | "sponsor")); 19 | 20 | // 21 | // Getters and Setters 22 | // 23 | 24 | @Override 25 | public Set getAllTerms() { 26 | return terms; 27 | } 28 | 29 | @Override 30 | public Set getAllTermsWithAdditional(String[] terms) { 31 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 32 | .collect(Collectors.toCollection(HashSet::new)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStatuses.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class WindowsServiceStatuses implements StixVocabulary { 13 | 14 | @JsonProperty("windows-service-status-enum") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "SERVICE_CONTINUE_PENDING", "SERVICE_PAUSE_PENDING", "SERVICE_PAUSED", 17 | "SERVICE_RUNNING", "SERVICE_START_PENDING", "SERVICE_STOP_PENDING", 18 | "SERVICE_STOPPED" 19 | )); 20 | 21 | @Override 22 | public Set getAllTerms() { 23 | return terms; 24 | } 25 | 26 | @Override 27 | public Set getAllTermsWithAdditional(String[] terms) { 28 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 29 | .collect(Collectors.toCollection(HashSet::new)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackMotivations.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class AttackMotivations implements StixVocabulary { 13 | 14 | @JsonProperty("attack_motivations_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "accidental", "coercion", "dominance", 17 | "ideology", "notoriety", "organizational-gain", 18 | "personal-gain", "personal-satisfaction", "revenge", 19 | "unpredictable")); 20 | 21 | 22 | @Override 23 | public Set getAllTerms() { 24 | return terms; 25 | } 26 | 27 | @Override 28 | public Set getAllTermsWithAdditional(String[] terms) { 29 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 30 | .collect(Collectors.toCollection(HashSet::new)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ToolLabels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class ToolLabels implements StixVocabulary { 13 | 14 | @JsonProperty("tool_labels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "denial-of-service", "exploitation", "information-gathering", 17 | "network-capture", "credential-exploitation", "remote-access", 18 | "vulnerability-scanning")); 19 | 20 | // 21 | // Getters and Setters 22 | // 23 | 24 | @Override 25 | public Set getAllTerms() { 26 | return terms; 27 | } 28 | 29 | @Override 30 | public Set getAllTermsWithAdditional(String[] terms) { 31 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 32 | .collect(Collectors.toCollection(HashSet::new)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixCustomProperties.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 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.validation.contraints.startswith.StartsWith; 7 | import org.hibernate.validator.constraints.Length; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * Stix Custom Properties 13 | */ 14 | public interface StixCustomProperties { 15 | 16 | /** 17 | * Custom Properties for STIX Objects. 18 | * Any object that supports custom properties will have a validation of the custom property prefix (typically "x_"). 19 | * If the additional property in the JSON does not meet the StartsWith condition, then the JSON will be rejected. 20 | * @return Map of custom properties {@code Map} 21 | */ 22 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) 23 | @JsonUnwrapped @JsonAnyGetter 24 | Map<@StartsWith() @Length(min = 3, max = 250, message = "STIX Custom Properties must have a min key length of 3 and max of 250") String, Object> getCustomProperties(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ReportLabels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class ReportLabels implements StixVocabulary { 13 | 14 | @JsonProperty("report_labels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "threat-report", "attack-pattern", "campaign", 17 | "identity", "indicator", "malware", 18 | "observed-data", "threat-actor", "tool", 19 | "vulnerability")); 20 | 21 | // 22 | // Getters and Setters 23 | // 24 | 25 | @Override 26 | public Set getAllTerms() { 27 | return terms; 28 | } 29 | 30 | @Override 31 | public Set getAllTermsWithAdditional(String[] terms) { 32 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 33 | .collect(Collectors.toCollection(HashSet::new)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/redaction/Redactable.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.redaction; 2 | 3 | import org.immutables.annotate.InjectAnnotation; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.*; 11 | 12 | /** 13 | * Marker to indicate which classes and methods are Redactable. 14 | * Redaction is the modification of JSON properties or removal of properties and 15 | * entire objects during serialization based on STIX Marking Definitions and Granular Markings. 16 | */ 17 | @Documented 18 | @Target( { ANNOTATION_TYPE, TYPE, METHOD }) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @InjectAnnotation(target = {InjectAnnotation.Where.ACCESSOR, InjectAnnotation.Where.IMMUTABLE_TYPE}, code = "([[*]])", type = Redactable.class) 21 | public @interface Redactable { 22 | 23 | boolean useMask() default false; 24 | String redactionMask() default ""+ ((char)0x2588) + ((char)0x2588) + "REDACTED" + ((char)0x2588) + ((char)0x2588); 25 | 26 | //@TODO: 27 | // Add Masking config 28 | // Add Conditional 29 | // Add object Refs 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/EncryptionAlgorithms.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class EncryptionAlgorithms implements StixVocabulary { 13 | 14 | @JsonProperty("encryption-algo-ov") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "AES128-ECB", "AES128-CBC", "AES128-CFB", "AES128-COFB", 17 | "AES128-CTR", "AES128-XTS", "AES128-GCM", 18 | "Salsa20", "Salsa8B", "ChaCha20-Poly1305", "ChaCha20", 19 | "DES-CBC", "3DES-CBC", "DES-EBC", "3DES-EBC", 20 | "CAST128-CBC", "CAST256-CBC", "RSA", "DSA" 21 | )); 22 | 23 | 24 | @Override 25 | public Set getAllTerms() { 26 | return terms; 27 | } 28 | 29 | @Override 30 | public Set getAllTermsWithAdditional(String[] terms) { 31 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 32 | .collect(Collectors.toCollection(HashSet::new)); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixCustomObjectType.java: -------------------------------------------------------------------------------- 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 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorLabels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class ThreatActorLabels implements StixVocabulary { 13 | 14 | @JsonProperty("threat_actor_labels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "activist", "competitor", "crime-syndicate", 17 | "criminal", "hacker", "insider-accidental", 18 | "insider-disgruntled", "nation-state", "sensationalist", 19 | "spy", "terrorist")); 20 | 21 | // 22 | // Getters and Setters 23 | // 24 | 25 | @Override 26 | public Set getAllTerms() { 27 | return terms; 28 | } 29 | 30 | @Override 31 | public Set getAllTermsWithAdditional(String[] terms) { 32 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 33 | .collect(Collectors.toCollection(HashSet::new)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/bundle/BundleableObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.bundle; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import io.digitalstate.stix.common.Stix; 5 | import io.digitalstate.stix.custom.objects.CustomObject; 6 | import io.digitalstate.stix.datamarkings.GranularMarkingDm; 7 | import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; 8 | 9 | import java.io.Serializable; 10 | import java.util.Set; 11 | 12 | /** 13 | * This interface is typically inherited by other interfaces that are considered "objects" that are part of a Bundle. 14 | * Thus the name "BundleableObject". A Bundleable Object by STIX standard is: SDO, SRO, and Marking Definition. 15 | * The Type field is used to determine the sub-types as registered in the {@link io.digitalstate.stix.json.StixParsers} 16 | */ 17 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true, defaultImpl = CustomObject.class) 18 | public interface BundleableObject extends Serializable, Stix { 19 | 20 | String getType(); 21 | String getId(); 22 | Set getObjectMarkingRefs(); 23 | Set getGranularMarkings(); 24 | boolean getHydrated(); 25 | String toJsonString(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipLimit.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.relationship; 2 | 3 | import io.digitalstate.stix.sdo.DomainObject; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 10 | import static java.lang.annotation.ElementType.TYPE; 11 | 12 | @Documented 13 | @Constraint(validatedBy = {StixRelationshipLimitValidator.class}) 14 | @Target( { ANNOTATION_TYPE, TYPE }) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Repeatable(RelationshipLimit.List.class) 17 | public @interface RelationshipLimit { 18 | String message() default "{io.digitalstate.stix.validation.contraints.relationship.RelationshipLimit}"; 19 | Class[] groups() default {}; 20 | Class[] payload() default {}; 21 | 22 | Class source(); 23 | String relationshipType(); 24 | Class[] target(); 25 | boolean classEquality() default false; 26 | 27 | @Documented 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target( { ANNOTATION_TYPE, TYPE }) 30 | @interface List { 31 | RelationshipLimit[] value(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/startswith/StartsWith.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.startswith; 2 | 3 | import io.digitalstate.stix.helpers.StixCustomPropertiesConfig; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 13 | import static java.lang.annotation.ElementType.TYPE_USE; 14 | 15 | /** 16 | *

Provides a Starts With validator of String values.

17 | *
18 | *

Defaults to {@link StixCustomPropertiesConfig#DEFAULT_CUSTOM_PROPERTY_PREFIX}

19 | */ 20 | @Documented 21 | @Constraint(validatedBy = {StixStartsWithValidatorString.class}) 22 | @Target( { ANNOTATION_TYPE, TYPE_USE }) 23 | @Retention(RetentionPolicy.RUNTIME) 24 | public @interface StartsWith { 25 | String message() default "{io.digitalstate.stix.validation.contraints.startswith.StartsWith}"; 26 | Class[] groups() default {}; 27 | Class[] payload() default {}; 28 | 29 | String value() default StixCustomPropertiesConfig.DEFAULT_CUSTOM_PROPERTY_PREFIX; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/HashingVocab.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.hashingvocab; 2 | 3 | import io.digitalstate.stix.vocabulary.StixVocabulary; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 13 | import static java.lang.annotation.ElementType.TYPE_USE; 14 | 15 | /** 16 | *

A modified version of the @StartsWith annotation, where this modified version 17 | * provides a parameter level control over Map keys, that meet the Hashes spec, 18 | * and allows x_ key names in addition

19 | */ 20 | @Documented 21 | @Constraint(validatedBy = {StixHashingVocabValidatorString.class}) 22 | @Target( { ANNOTATION_TYPE, TYPE_USE }) 23 | @Retention(RetentionPolicy.RUNTIME) 24 | public @interface HashingVocab { 25 | String message() default "{io.digitalstate.stix.validation.contraints.hashingvocab}"; 26 | Class[] groups() default {}; 27 | Class[] payload() default {}; 28 | 29 | Class value(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/groovy/stix/custom/CustomObjectSpec.groovy: -------------------------------------------------------------------------------- 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 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/StixGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import io.digitalstate.stix.bundle.BundleObject; 6 | import io.digitalstate.stix.graph.bundle.BundleObjectGraphGenerator; 7 | import io.digitalstate.stix.graph.elements.GraphElement; 8 | import io.digitalstate.stix.json.StixParsers; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | public class StixGraphGenerator implements GraphGenerator { 14 | 15 | private BundleObject bundle; 16 | private ObjectMapper jsonMapper = StixParsers.getJsonMapper(); 17 | 18 | public StixGraphGenerator(BundleObject bundle) { 19 | this.bundle = bundle; 20 | } 21 | 22 | @Override 23 | public Set process(){ 24 | Set elements = new HashSet<>(); 25 | 26 | elements.addAll(new BundleObjectGraphGenerator(bundle).process()); 27 | 28 | return elements; 29 | } 30 | 31 | public String toJson(){ 32 | try { 33 | return jsonMapper.writeValueAsString(process()); 34 | } catch (JsonProcessingException e) { 35 | throw new IllegalStateException(e); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/SdoDefaultValidator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation; 2 | 3 | import io.digitalstate.stix.validation.sequences.SequenceDefault; 4 | import io.digitalstate.stix.validation.sequences.SequenceValidationIdOnly; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.ConstraintViolationException; 8 | import javax.validation.Validation; 9 | import javax.validation.Validator; 10 | import java.util.Set; 11 | 12 | public interface SdoDefaultValidator { 13 | 14 | Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); 15 | 16 | default void validate() throws ConstraintViolationException{ 17 | Set> violations = VALIDATOR.validate(this, SequenceDefault.class); 18 | if (!violations.isEmpty()) { 19 | throw new ConstraintViolationException(violations); 20 | } 21 | } 22 | 23 | default void validateOnlyId() throws ConstraintViolationException{ 24 | Set> violations = VALIDATOR.validate(this, SequenceValidationIdOnly.class); 25 | if (!violations.isEmpty()) { 26 | throw new ConstraintViolationException(violations); 27 | } 28 | } 29 | } 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsRegistryValueDataTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class WindowsRegistryValueDataTypes implements StixVocabulary { 13 | 14 | @JsonProperty("data-types") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", 17 | "REG_BINARY", "REG_DWORD", "REG_DWORD_BIG_ENDIAN", 18 | "REG_LINK", "REG_MULTI_SZ", "REG_RESOURCE_LIST", 19 | "REG_FULL_RESOURCE_DESCRIPTION", "REG_RESOURCE_REQUIREMENTS_LIST", "REG_QWORD", 20 | "REG_INVALID_TYPE" 21 | )); 22 | 23 | @Override 24 | public Set getAllTerms() { 25 | return terms; 26 | } 27 | 28 | @Override 29 | public Set getAllTermsWithAdditional(String[] terms) { 30 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 31 | .collect(Collectors.toCollection(HashSet::new)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/datamarkings/objects/StatementMarkingObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.datamarkings.objects; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 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.datamarkings.StixMarkingObject; 8 | import io.digitalstate.stix.redaction.Redactable; 9 | import io.digitalstate.stix.validation.GenericValidation; 10 | import org.hibernate.validator.constraints.Length; 11 | import org.immutables.serial.Serial; 12 | import org.immutables.value.Value; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | 16 | @Value.Immutable @Serial.Version(1L) 17 | @Value.Style(typeImmutable = "Statement", additionalJsonAnnotations = {JsonTypeName.class}, validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) 18 | @JsonSerialize(as = Statement.class) @JsonDeserialize(builder = Statement.Builder.class) 19 | @Redactable 20 | @JsonTypeName("statement") 21 | public interface StatementMarkingObject extends GenericValidation, StixMarkingObject { 22 | 23 | @NotBlank 24 | @JsonProperty("statement") 25 | @Length(min = 1) String getStatement(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixCustomObjectId.java: -------------------------------------------------------------------------------- 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 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/ValidateReferences.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.coo.validateextensions; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 11 | import static java.lang.annotation.ElementType.TYPE; 12 | 13 | /** 14 | * Used to Validation Cyber Observable Extensions 15 | * Should only be placed on Cyber Observable Object classes. 16 | * This annotation will ensure that the extension is only used on the classes that implement {@link io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents} with the proper configuration. 17 | */ 18 | @Documented 19 | @Constraint(validatedBy = {StixValidateParentCooValidator.class}) 20 | @Target( { ANNOTATION_TYPE, TYPE }) 21 | @Retention(RetentionPolicy.RUNTIME) 22 | public @interface ValidateReferences { 23 | String message() default "{io.digitalstate.stix.validation.contraints.coo.allowedparents.ValidateReferences}"; 24 | Class[] groups() default {}; 25 | Class[] payload() default {}; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/MalwareLabels.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class MalwareLabels implements StixVocabulary { 13 | 14 | @JsonProperty("malware_labels_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "adware", "backdoor", "bot", 17 | "ddos", "dropper", "exploit-kit", 18 | "keylogger", "ransomware", "remote-access-trojan", 19 | "resource-exploitation", "rogue-security-software", "rootkit", 20 | "screen-capture", "spyware", "trojan", 21 | "virus", "worm")); 22 | 23 | // 24 | // Getters and Setters 25 | // 26 | 27 | @Override 28 | public Set getAllTerms() { 29 | return terms; 30 | } 31 | 32 | @Override 33 | public Set getAllTermsWithAdditional(String[] terms) { 34 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 35 | .collect(Collectors.toCollection(HashSet::new)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/datamarkings/objects/TlpMarkingObject.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.datamarkings.objects; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 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.datamarkings.StixMarkingObject; 8 | import io.digitalstate.stix.redaction.Redactable; 9 | import io.digitalstate.stix.validation.GenericValidation; 10 | import io.digitalstate.stix.validation.contraints.vocab.Vocab; 11 | import io.digitalstate.stix.vocabulary.vocabularies.TlpLevels; 12 | import org.immutables.serial.Serial; 13 | import org.immutables.value.Value; 14 | 15 | import javax.validation.constraints.NotNull; 16 | 17 | @Value.Immutable @Serial.Version(1L) 18 | @Value.Style(typeImmutable = "Tlp", additionalJsonAnnotations = {JsonTypeName.class}, validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) 19 | @JsonSerialize(as = Tlp.class) @JsonDeserialize(builder = Tlp.Builder.class) 20 | @Redactable 21 | @JsonTypeName("tlp") 22 | public interface TlpMarkingObject extends GenericValidation, StixMarkingObject { 23 | 24 | @NotNull 25 | @JsonProperty("tlp") 26 | @Vocab(TlpLevels.class) 27 | String getTlp(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/RelationshipTypes.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import io.digitalstate.stix.vocabulary.StixVocabulary; 4 | 5 | import java.util.Arrays; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | public class RelationshipTypes implements StixVocabulary { 12 | 13 | private static Set terms = new HashSet<>(Arrays.asList( 14 | "targets", 15 | "uses", 16 | "attributed-to", 17 | "mitigates", 18 | "indicates", 19 | "variant-of", 20 | "impersonates")); 21 | 22 | private static Set commonTerms = new HashSet<>(Arrays.asList( 23 | "duplicate-of", 24 | "derived-from", 25 | "related-to")); 26 | 27 | @Override 28 | public Set getAllTerms() { 29 | return Stream.concat(terms.stream(), commonTerms.stream()) 30 | .collect(Collectors.toCollection(HashSet::new)); 31 | } 32 | 33 | @Override 34 | public Set getAllTermsWithAdditional(String[] terms) { 35 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 36 | .collect(Collectors.toCollection(HashSet::new)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/custom/objects/GenericCustomObject.java: -------------------------------------------------------------------------------- 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 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/vocab/Vocab.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.vocab; 2 | 3 | import io.digitalstate.stix.vocabulary.StixVocabulary; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | import static java.lang.annotation.ElementType.*; 13 | 14 | /** 15 | *

Provides STIX Vocabulary validation for {@code String} and {@code Set} fields.

16 | *
17 | *

Value is the vocabulary class to be used for validation. 18 | * The class must implement {@link StixVocabulary} interface.

19 | *
20 | *

Example usage: {@code @HashingVocab(AttackMotivations.class)}

21 | */ 22 | @Documented 23 | @Constraint(validatedBy = { 24 | StixVocabValidatorString.class, 25 | StixVocabValidatorCollection.class 26 | }) 27 | @Target( { METHOD, FIELD, TYPE_USE, ANNOTATION_TYPE, PARAMETER }) 28 | @Retention(RetentionPolicy.RUNTIME) 29 | public @interface Vocab { 30 | String message() default "{io.digitalstate.stix.validation.contraints.VocabContains}"; 31 | Class[] groups() default {}; 32 | Class[] payload() default {}; 33 | 34 | Class value(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/datamarkings/MarkingDefinitionGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.datamarkings; 2 | 3 | import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; 4 | import io.digitalstate.stix.graph.GraphGenerator; 5 | import io.digitalstate.stix.graph.elements.GraphElement; 6 | import io.digitalstate.stix.graph.elements.Node; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class MarkingDefinitionGraphGenerator implements GraphGenerator{ 12 | 13 | private MarkingDefinitionDm object; 14 | 15 | public MarkingDefinitionGraphGenerator(MarkingDefinitionDm object) { 16 | this.object = object; 17 | } 18 | 19 | public MarkingDefinitionDm getObject() { 20 | return object; 21 | } 22 | 23 | public Set process(){ 24 | Set elements = new HashSet<>(); 25 | 26 | elements.add(generateNode()); 27 | // elements.addAll(generateEdges()); 28 | 29 | return elements; 30 | } 31 | 32 | private Node generateNode(){ 33 | return new Node(object.getId(), object.getType(), null, object); 34 | } 35 | 36 | // private Set generateEdges() { 37 | // Set edges = new HashSet<>(); 38 | // 39 | // edges.addAll(generateObjectMarkingRefEdges(object.getObjectMarkingRefs())); 40 | // 41 | // return edges; 42 | // } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketProtocolFamilies.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class NetworkSocketProtocolFamilies implements StixVocabulary { 13 | 14 | @JsonProperty("network-socket-protocol-family-enum") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "PF_INET", "PF_AX25", "PF_IPX", "PF_INET_6", 17 | "PF_APPLETALK", "PF_NETROM", "PF_BRIDGE", "PF_ATMPVC", 18 | "PF_X25", "PF_ROSE", "PF_DECNET", "PF_NETBEUI", 19 | "PF_SECURITY", "PF_KEY", "PF_NETLINK", "PF_ROUTE", 20 | "PF_PACKET", "PF_ASH", "PF_ECONET", "PF_ATMSVC", 21 | "PF_SNA", "PF_IRDA", "PF_PPPOX", "PF_WANPIPE", 22 | "PF_BLUETOOTH" 23 | )); 24 | 25 | @Override 26 | public Set getAllTerms() { 27 | return terms; 28 | } 29 | 30 | @Override 31 | public Set getAllTermsWithAdditional(String[] terms) { 32 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 33 | .collect(Collectors.toCollection(HashSet::new)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/custom/StixCustomObject.java: -------------------------------------------------------------------------------- 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 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/sro/RelationshipSroGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.sro; 2 | 3 | import io.digitalstate.stix.graph.GraphGenerator; 4 | import io.digitalstate.stix.graph.elements.Edge; 5 | import io.digitalstate.stix.graph.elements.GraphElement; 6 | import io.digitalstate.stix.sro.objects.RelationshipSro; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class RelationshipSroGraphGenerator implements GraphGenerator { 12 | 13 | private RelationshipSro object; 14 | 15 | public RelationshipSroGraphGenerator(RelationshipSro object) { 16 | this.object = object; 17 | } 18 | 19 | public RelationshipSro getObject() { 20 | return object; 21 | } 22 | 23 | @Override 24 | public Set process() { 25 | Set elements = new HashSet<>(); 26 | elements.add(generateEdge()); 27 | return elements; 28 | } 29 | 30 | private Edge generateEdge() { 31 | Edge edge = new Edge(object.getId(), 32 | object.getType(), 33 | object.getSourceRef().getId(), 34 | object.getTargetRef().getId(), 35 | object); 36 | 37 | edge.getData().setEdgeLabel(object.getRelationshipType()); 38 | 39 | edge.getData() 40 | .getAdditionalProperties() 41 | .put("relationship_type", object.getRelationshipType()); 42 | 43 | return edge; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/helpers/StixCustomPropertiesConfig.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.helpers; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * STIX Custom properties configuration 8 | */ 9 | public class StixCustomPropertiesConfig { 10 | 11 | /** 12 | * Default Custom Property Prefix: {@code x_ }. 13 | */ 14 | public static final String DEFAULT_CUSTOM_PROPERTY_PREFIX = "x_"; 15 | 16 | private static Set additionalPropertyPrefixes = new HashSet<>(); 17 | 18 | /** 19 | * Get Additional STIX Custom Property prefixes. 20 | */ 21 | public static Set getAdditionalPropertyPrefixes() { 22 | return additionalPropertyPrefixes; 23 | } 24 | 25 | /** 26 | * Set Additional STIX Custom Property prefixes. 27 | */ 28 | public static void setAdditionalPropertyPrefixes(Set additionalPropertyPrefixes) { 29 | StixCustomPropertiesConfig.additionalPropertyPrefixes = additionalPropertyPrefixes; 30 | } 31 | 32 | /** 33 | * Returns a aggregate of The Default Custom Property Prefix and any defined additional customer property prefixes. 34 | * @return Set of allowed custom property prefixes 35 | */ 36 | public static Set getAllCustomPropertyPrefixes(){ 37 | Set allPrefixes = new HashSet<>(getAdditionalPropertyPrefixes()); 38 | allPrefixes.add(DEFAULT_CUSTOM_PROPERTY_PREFIX); 39 | return allPrefixes; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldSerializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.json.extension; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.SerializerProvider; 5 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 6 | import io.digitalstate.stix.coo.extension.CyberObservableExtension; 7 | 8 | import java.io.IOException; 9 | import java.util.Set; 10 | 11 | public class CyberObservableExtensionsFieldSerializer extends StdSerializer> { 12 | 13 | public CyberObservableExtensionsFieldSerializer() { 14 | this(null); 15 | } 16 | 17 | protected CyberObservableExtensionsFieldSerializer(Class> t) { 18 | super(t); 19 | } 20 | 21 | @Override 22 | public boolean isEmpty(SerializerProvider provider, Set value) { 23 | return value.isEmpty(); 24 | } 25 | 26 | @Override 27 | public void serialize(Set value, JsonGenerator gen, SerializerProvider provider) throws IOException { 28 | gen.writeStartObject(); 29 | value.forEach(ext -> { 30 | try { 31 | gen.writeObjectField(ext.getType(), ext); 32 | } catch (IOException e) { 33 | throw new IllegalStateException("Unable to serialize COO Extension:", e); 34 | } 35 | }); 36 | gen.writeEndObject(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldSerializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.json.observables; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.SerializerProvider; 5 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | 8 | import java.io.IOException; 9 | import java.util.Set; 10 | 11 | public class CyberObservableSetFieldSerializer extends StdSerializer> { 12 | 13 | public CyberObservableSetFieldSerializer() { 14 | this(null); 15 | } 16 | 17 | protected CyberObservableSetFieldSerializer(Class> t) { 18 | super(t); 19 | } 20 | 21 | @Override 22 | public boolean isEmpty(SerializerProvider provider, Set value) { 23 | return value.isEmpty(); 24 | } 25 | 26 | @Override 27 | public void serialize(Set values, JsonGenerator gen, SerializerProvider provider) throws IOException { 28 | gen.writeStartObject(); 29 | values.forEach(observableObject -> { 30 | try { 31 | gen.writeObjectField(observableObject.getObservableObjectKey(), observableObject); 32 | } catch (IOException e) { 33 | throw new IllegalStateException("Unable to serialize object", e); 34 | } 35 | }); 36 | gen.writeEndObject(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/sdo/DomainObjectGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.sdo; 2 | 3 | import io.digitalstate.stix.graph.GraphGenerator; 4 | import io.digitalstate.stix.graph.elements.GraphElement; 5 | import io.digitalstate.stix.graph.elements.Node; 6 | import io.digitalstate.stix.sdo.DomainObject; 7 | import io.digitalstate.stix.sdo.objects.ObservedDataSdo; 8 | 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | public class DomainObjectGraphGenerator implements GraphGenerator { 13 | 14 | private DomainObject object; 15 | 16 | public DomainObjectGraphGenerator(DomainObject object) { 17 | this.object = object; 18 | } 19 | 20 | @Override 21 | public Set process() { 22 | Set elements = new HashSet<>(); 23 | 24 | if (ObservedDataSdo.class.isAssignableFrom(object.getClass())){ 25 | elements.addAll(generateObservedDataGraphElements((ObservedDataSdo)object)); 26 | } else { 27 | //@TODO Add various support for the other SDOs 28 | elements.add(generateNode()); 29 | } 30 | 31 | return elements; 32 | } 33 | 34 | public DomainObject getObject() { 35 | return object; 36 | } 37 | 38 | private Node generateNode() { 39 | return new Node(object.getId(), object.getType(), null, object); 40 | } 41 | 42 | private Set generateObservedDataGraphElements(ObservedDataSdo observedDataSdo){ 43 | return new ObservedDataGraphGenerator(observedDataSdo).process(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/MarkingDefinitionTypeLimit.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.markingdefinitiontype; 2 | 3 | import io.digitalstate.stix.datamarkings.StixMarkingObject; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 10 | import static java.lang.annotation.ElementType.TYPE; 11 | 12 | /** 13 | * To only be used on STIX MarkingDefinition class. 14 | * The annotation provides a Javax Validation that enforces Marking Definition Types based on the actual definition being used. 15 | * This annotation enforces the STIX Relationship Type restrictions for each SDO. 16 | */ 17 | @Documented 18 | @Constraint(validatedBy = {StixMarkingDefinitionTypeLimitValidator.class}) 19 | @Target( { ANNOTATION_TYPE, TYPE }) 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Repeatable(MarkingDefinitionTypeLimit.List.class) 22 | public @interface MarkingDefinitionTypeLimit { 23 | String message() default "{io.digitalstate.stix.validation.contraints.markingdefinitiontype.MarkingDefinitionTypeLimit}"; 24 | Class[] groups() default {}; 25 | Class[] payload() default {}; 26 | 27 | Class markingObject(); 28 | String markingDefinitionType(); 29 | 30 | @Documented 31 | @Retention(RetentionPolicy.RUNTIME) 32 | @Target( { ANNOTATION_TYPE, TYPE }) 33 | @interface List { 34 | MarkingDefinitionTypeLimit[] value(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/objects/MutexCoo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 8 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 9 | import org.immutables.serial.Serial; 10 | import org.immutables.value.Value; 11 | 12 | import javax.validation.constraints.NotNull; 13 | 14 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 15 | 16 | /** 17 | * mutex 18 | *

19 | * The Mutex Object represents the properties of a mutual exclusion (mutex) object. 20 | * 21 | */ 22 | @Value.Immutable @Serial.Version(1L) 23 | @DefaultTypeValue(value = "mutex", groups = {DefaultValuesProcessor.class}) 24 | @Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 25 | @JsonTypeName("mutex") 26 | @JsonSerialize(as = Mutex.class) @JsonDeserialize(builder = Mutex.Builder.class) 27 | @JsonPropertyOrder({"type", "extensions", "name"}) 28 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 29 | public interface MutexCoo extends CyberObservableObject { 30 | 31 | @JsonProperty("name") 32 | @JsonPropertyDescription("Specifies the name of the mutex object.") 33 | @NotNull 34 | String getName(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndustrySectors.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.vocabulary.vocabularies; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class IndustrySectors implements StixVocabulary { 13 | 14 | @JsonProperty("industry_sectors_vocabulary") 15 | private Set terms = new HashSet<>(Arrays.asList( 16 | "agriculture", "aerospace", "automotive", 17 | "communications", "construction", "defence", 18 | "education", "energy", "entertainment", 19 | "financial-services", "government-national", "government-regional", 20 | "government-local", "government-public-services", "healthcare", 21 | "hospitality-leisure", "infrastructure", "insurance", 22 | "manufacturing", "mining", "non-profit", 23 | "pharmaceuticals", "retail", "technology", 24 | "telecommunications", "transportation", "utilities")); 25 | 26 | // 27 | // Getters and Setters 28 | // 29 | 30 | @Override 31 | public Set getAllTerms() { 32 | return terms; 33 | } 34 | 35 | @Override 36 | public Set getAllTermsWithAdditional(String[] terms) { 37 | return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) 38 | .collect(Collectors.toCollection(HashSet::new)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/DefaultTypeValue.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.defaulttypevalue; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 11 | import static java.lang.annotation.ElementType.TYPE; 12 | 13 | 14 | /** 15 | *

Sets the default value of the {@code type} field for a Bundleable object. 16 | * This annotation must only be applied on a Class level of a class that extends from Bundleable object 17 | * (Typically SDOs, SROs, COOs, COO-Extensions, and Marking Definitions).

18 | *
19 | *

This annotation will also validate a manually set {@code type} attribute. 20 | * If the {@code type} attribute does not equal the value set in this annotation, then validation will fail.

21 | */ 22 | @Documented 23 | @Constraint(validatedBy = {StixDefaultTypeValueBundleableValidator.class, StixDefaultTypeValueBundleObjectValidator.class, StixDefaultTypeValueCyberObservableValidator.class, StixDefaultTypeValueCyberObservableExtensionValidator.class}) 24 | @Target( { ANNOTATION_TYPE, TYPE }) 25 | @Retention(RetentionPolicy.RUNTIME) 26 | public @interface DefaultTypeValue { 27 | String message() default "{io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue}"; 28 | Class[] groups() default {}; 29 | Class[] payload() default {}; 30 | 31 | String value(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipTypeLimit.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.relationship; 2 | 3 | import io.digitalstate.stix.sdo.DomainObject; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.*; 8 | 9 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 10 | import static java.lang.annotation.ElementType.TYPE; 11 | 12 | /** 13 | * To only be used on STIX Relationship class. 14 | * The annotation provides a Javax Validation that looks at the RelationshipType being used. 15 | * This annotation enforces the STIX Relationship Type restrictions for each SDO. 16 | */ 17 | @Documented 18 | @Constraint(validatedBy = {StixRelationshipTypeLimitValidator.class}) 19 | @Target( { ANNOTATION_TYPE, TYPE }) 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Repeatable(RelationshipTypeLimit.List.class) 22 | public @interface RelationshipTypeLimit { 23 | String message() default "{io.digitalstate.stix.validation.contraints.relationship.DefaultTypeValue}"; 24 | Class[] groups() default {}; 25 | Class[] payload() default {}; 26 | 27 | boolean enforceCommonRelationshipTypes() default true; 28 | // @TODO look to use reflection to populate the Common Relationship Types default values. 29 | String[] commonRelationshipTypes() default {"duplicate-of", "derived-from", "related-to"}; 30 | Class source(); 31 | String[] relationshipTypes(); 32 | 33 | @Documented 34 | @Retention(RetentionPolicy.RUNTIME) 35 | @Target( { ANNOTATION_TYPE, TYPE }) 36 | @interface List { 37 | RelationshipTypeLimit[] value(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectConverter.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json.converters.dehydrated; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.node.ObjectNode; 6 | import com.fasterxml.jackson.databind.util.StdConverter; 7 | import io.digitalstate.stix.json.StixParsers; 8 | import io.digitalstate.stix.sdo.DomainObject; 9 | 10 | /** 11 | * Generates a Dehydrated Domain Object based on a ID. 12 | */ 13 | public class DomainObjectConverter extends StdConverter { 14 | 15 | @Override 16 | public DomainObject convert(String value) { 17 | String[] parsedValue = value.split("--"); 18 | 19 | if (parsedValue.length == 2){ 20 | ObjectMapper mapper = StixParsers.getJsonMapper(); 21 | ObjectNode node = mapper.createObjectNode(); 22 | 23 | node.put("type", parsedValue[0]); 24 | node.put("id", value); 25 | node.put("hydrated", false); 26 | 27 | 28 | try { 29 | DomainObject domainObject = mapper.treeToValue(node, DomainObject.class); 30 | //@TODO add more logic 31 | return domainObject; 32 | 33 | } catch (JsonProcessingException e) { 34 | e.printStackTrace(); 35 | throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); 36 | } 37 | 38 | } else { 39 | throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectOptionalConverter.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json.converters.dehydrated; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.node.ObjectNode; 6 | import com.fasterxml.jackson.databind.util.StdConverter; 7 | import io.digitalstate.stix.json.StixParsers; 8 | import io.digitalstate.stix.sdo.DomainObject; 9 | 10 | import java.util.Optional; 11 | 12 | /** 13 | * Generates a Dehydrated Domain Object based on a ID. 14 | */ 15 | public class DomainObjectOptionalConverter extends StdConverter> { 16 | 17 | @Override 18 | public Optional convert(String value) { 19 | String[] parsedValue = value.split("--"); 20 | 21 | if (parsedValue.length == 2){ 22 | ObjectMapper mapper = StixParsers.getJsonMapper(); 23 | ObjectNode node = mapper.createObjectNode(); 24 | 25 | node.put("type", parsedValue[0]); 26 | node.put("id", value); 27 | node.put("hydrated", false); 28 | 29 | 30 | try { 31 | return Optional.ofNullable(mapper.treeToValue(node, DomainObject.class)); 32 | //@TODO add more logic 33 | 34 | } catch (JsonProcessingException e) { 35 | e.printStackTrace(); 36 | throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); 37 | } 38 | 39 | } else { 40 | throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectConverter.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json.converters.dehydrated; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.node.ObjectNode; 6 | import com.fasterxml.jackson.databind.util.StdConverter; 7 | import io.digitalstate.stix.bundle.BundleableObject; 8 | import io.digitalstate.stix.json.StixParsers; 9 | 10 | /** 11 | * Generates a Dehydrated Bundleable Object based on a ID. 12 | */ 13 | public class BundleableObjectConverter extends StdConverter { 14 | 15 | @Override 16 | public BundleableObject convert(String value) { 17 | String[] parsedValue = value.split("--"); 18 | 19 | if (parsedValue.length == 2){ 20 | ObjectMapper mapper = StixParsers.getJsonMapper(); 21 | ObjectNode node = mapper.createObjectNode(); 22 | 23 | node.put("type", parsedValue[0]); 24 | node.put("id", value); 25 | node.put("hydrated", false); 26 | 27 | 28 | try { 29 | BundleableObject bundleableObject = mapper.treeToValue(node, BundleableObject.class); 30 | //@TODO add more logic 31 | return bundleableObject; 32 | 33 | } catch (JsonProcessingException e) { 34 | e.printStackTrace(); 35 | throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); 36 | } 37 | 38 | } else { 39 | throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/common/StixBoolean.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.common; 2 | 3 | /** 4 | * Wrapper for boolean that is used in Stix to track if the boolean value was User provided. 5 | * Custom class is used instead of a Boolean in order to provide a more robust api 6 | */ 7 | public class StixBoolean { 8 | 9 | private boolean stixBooleanValue; 10 | private boolean isDefinedValue; 11 | 12 | public StixBoolean(boolean stixBooleanValue) { 13 | this.stixBooleanValue = stixBooleanValue; 14 | this.isDefinedValue = true; 15 | } 16 | 17 | public StixBoolean(boolean stixBooleanValue, boolean isDefinedValue) { 18 | this.stixBooleanValue = stixBooleanValue; 19 | this.isDefinedValue = isDefinedValue; 20 | } 21 | 22 | /** 23 | * Defaults to StixBoolean value to false. Sets isDefinedValue to false 24 | */ 25 | public StixBoolean() { 26 | this.stixBooleanValue = false; 27 | isDefinedValue = false; 28 | } 29 | 30 | public StixBoolean(String booleanString){ 31 | this.stixBooleanValue = Boolean.valueOf(booleanString); 32 | this.isDefinedValue = true; 33 | } 34 | 35 | public boolean getStixBooleanValue() { 36 | return stixBooleanValue; 37 | } 38 | 39 | /** 40 | * Indicates that the boolean value was explicitly defined, even if the value was false, 41 | * and the original object's property defaults to false if no value is provided. 42 | * @return boolean indicating if the value was defined. 43 | */ 44 | public boolean isdefinedValue() { 45 | return isDefinedValue; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return String.valueOf(getStixBooleanValue()); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionConverter.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json.converters.dehydrated; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.node.ObjectNode; 6 | import com.fasterxml.jackson.databind.util.StdConverter; 7 | import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; 8 | import io.digitalstate.stix.json.StixParsers; 9 | 10 | /** 11 | * Generates a Dehydrated Domain Object based on a ID. 12 | */ 13 | public class MarkingDefinitionConverter extends StdConverter { 14 | 15 | @Override 16 | public MarkingDefinitionDm convert(String value) { 17 | String[] parsedValue = value.split("--"); 18 | 19 | if (parsedValue.length == 2){ 20 | ObjectMapper mapper = StixParsers.getJsonMapper(); 21 | ObjectNode node = mapper.createObjectNode(); 22 | 23 | node.put("type", parsedValue[0]); 24 | node.put("id", value); 25 | node.put("hydrated", false); 26 | 27 | 28 | try { 29 | MarkingDefinitionDm domainObject = mapper.treeToValue(node, MarkingDefinitionDm.class); 30 | //@TODO add more logic 31 | return domainObject; 32 | 33 | } catch (JsonProcessingException e) { 34 | e.printStackTrace(); 35 | throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); 36 | } 37 | 38 | } else { 39 | throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/coo/CyberObservableGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.coo; 2 | 3 | import io.digitalstate.stix.coo.CyberObservableObject; 4 | import io.digitalstate.stix.graph.GraphGenerator; 5 | import io.digitalstate.stix.graph.elements.GraphElement; 6 | import io.digitalstate.stix.graph.elements.Node; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | import java.util.UUID; 11 | 12 | /** 13 | * Generally used by the Observed Data SDO Graph Generator 14 | */ 15 | public class CyberObservableGraphGenerator implements GraphGenerator { 16 | 17 | private CyberObservableObject object; 18 | private String observedDataObjectId; 19 | 20 | public CyberObservableGraphGenerator(String observedDataObjectId, CyberObservableObject object) { 21 | this.object = object; 22 | this.observedDataObjectId = observedDataObjectId; 23 | } 24 | 25 | public CyberObservableObject getObject() { 26 | return object; 27 | } 28 | 29 | public String getObservedDataObjectId() { 30 | return observedDataObjectId; 31 | } 32 | 33 | public Set process(){ 34 | Set elements = new HashSet<>(); 35 | elements.add(generateNode()); 36 | 37 | return elements; 38 | } 39 | 40 | // Is public to support custom usage by Observed Data Graph Generator 41 | public Node generateNode(){ 42 | String uuid = object.getObservableObjectKey() + "--" + UUID.randomUUID().toString(); 43 | String type = "coo-" + object.getType(); 44 | 45 | return new Node(uuid, type, null, object); 46 | //@TODO Refactor to support the parent node prob for sub graph node support 47 | // return new Node(uuid, type, observedDataObjectId, object); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldDeserializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.json.extension; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.core.TreeNode; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 8 | import com.fasterxml.jackson.databind.node.ObjectNode; 9 | import io.digitalstate.stix.coo.extension.CyberObservableExtension; 10 | 11 | import java.io.IOException; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | public class CyberObservableExtensionsFieldDeserializer extends StdDeserializer> { 16 | 17 | public CyberObservableExtensionsFieldDeserializer() { 18 | this(null); 19 | } 20 | 21 | protected CyberObservableExtensionsFieldDeserializer(Class vc) { 22 | super(vc); 23 | } 24 | 25 | @Override 26 | public Set deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 27 | Set extensions = new HashSet<>(); 28 | 29 | TreeNode tree = p.readValueAsTree(); 30 | 31 | tree.fieldNames().forEachRemaining(f -> { 32 | ObjectNode node = (ObjectNode) tree.get(f); 33 | node.put("type", f); 34 | try { 35 | CyberObservableExtension extension = node.traverse(p.getCodec()).readValueAs(CyberObservableExtension.class); 36 | extensions.add(extension); 37 | } catch (IOException e) { 38 | throw new IllegalStateException("Cannot deserialize COO extension: ", e); 39 | } 40 | }); 41 | 42 | return extensions; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/datamarkings/GranularMarkingDm.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.datamarkings; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 | import com.fasterxml.jackson.annotation.JsonIdentityReference; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.annotation.ObjectIdGenerators; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import io.digitalstate.stix.common.StixCustomProperties; 10 | import io.digitalstate.stix.json.converters.dehydrated.MarkingDefinitionConverter; 11 | import io.digitalstate.stix.redaction.Redactable; 12 | import io.digitalstate.stix.validation.SdoDefaultValidator; 13 | import org.immutables.serial.Serial; 14 | import org.immutables.value.Value; 15 | 16 | import javax.validation.constraints.NotNull; 17 | import javax.validation.constraints.Size; 18 | import java.io.Serializable; 19 | import java.util.Set; 20 | 21 | @Value.Immutable @Serial.Version(1L) 22 | @Value.Style(typeAbstract="*Dm", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) 23 | @JsonSerialize(as = GranularMarking.class) @JsonDeserialize(builder = GranularMarking.Builder.class) 24 | @Redactable 25 | public interface GranularMarkingDm extends StixCustomProperties, SdoDefaultValidator, Serializable { 26 | 27 | @NotNull 28 | @JsonProperty("marking_ref") 29 | @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") 30 | @JsonIdentityReference(alwaysAsId=true) 31 | @JsonDeserialize(converter = MarkingDefinitionConverter.class) 32 | MarkingDefinitionDm getMarkingRef(); 33 | 34 | @Size(min = 1, message = "Must have as least 1 selector") 35 | @JsonProperty("selectors") 36 | Set getSelectors(); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/bundle/BundleableObjectGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.bundle; 2 | 3 | import io.digitalstate.stix.bundle.BundleableObject; 4 | import io.digitalstate.stix.graph.sdo.DomainObjectGraphGenerator; 5 | import io.digitalstate.stix.graph.GraphGenerator; 6 | import io.digitalstate.stix.graph.sro.RelationshipSroGraphGenerator; 7 | import io.digitalstate.stix.graph.sro.SightingSroGraphGenerator; 8 | import io.digitalstate.stix.graph.elements.GraphElement; 9 | import io.digitalstate.stix.sdo.DomainObject; 10 | import io.digitalstate.stix.sro.objects.RelationshipSro; 11 | import io.digitalstate.stix.sro.objects.SightingSro; 12 | 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | public class BundleableObjectGraphGenerator implements GraphGenerator { 17 | 18 | private BundleableObject object; 19 | 20 | public BundleableObjectGraphGenerator(BundleableObject object) { 21 | this.object = object; 22 | } 23 | 24 | public BundleableObject getObject() { 25 | return object; 26 | } 27 | 28 | public Set process(){ 29 | Set items = new HashSet<>(); 30 | 31 | Class objectClass = object.getClass(); 32 | 33 | if (DomainObject.class.isAssignableFrom(objectClass)){ 34 | items.addAll(new DomainObjectGraphGenerator((DomainObject)object).process()); 35 | 36 | } else if (RelationshipSro.class.isAssignableFrom(objectClass)){ 37 | items.addAll(new RelationshipSroGraphGenerator((RelationshipSro)object).process()); 38 | 39 | } else if (SightingSro.class.isAssignableFrom(objectClass)){ 40 | items.addAll( 41 | new SightingSroGraphGenerator((SightingSro) object).process() 42 | ); 43 | } 44 | 45 | return items; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldDeserializer.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.json.observables; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.core.TreeNode; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 8 | import com.fasterxml.jackson.databind.node.ObjectNode; 9 | import io.digitalstate.stix.coo.CyberObservableObject; 10 | 11 | import java.io.IOException; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | public class CyberObservableSetFieldDeserializer extends StdDeserializer> { 16 | 17 | public CyberObservableSetFieldDeserializer() { 18 | this(null); 19 | } 20 | 21 | protected CyberObservableSetFieldDeserializer(Class vc) { 22 | super(vc); 23 | } 24 | 25 | @Override 26 | public Set deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 27 | Set objects = new HashSet<>(); 28 | TreeNode tree = p.readValueAsTree(); 29 | 30 | tree.fieldNames().forEachRemaining(f->{ 31 | ObjectNode node = (ObjectNode)tree.get(f); 32 | node.put("observable_object_key", f); 33 | // System.out.println(Node.toString()); 34 | try { 35 | CyberObservableObject object = node.traverse(p.getCodec()) 36 | .readValueAs(CyberObservableObject.class); 37 | objects.add(object); 38 | 39 | } catch (IOException e) { 40 | throw new IllegalStateException("Unable to deserialize cyber observable object", e); 41 | } 42 | }); 43 | 44 | return objects; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/objects/DomainNameCoo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 8 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 9 | import org.immutables.serial.Serial; 10 | import org.immutables.value.Value; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import java.util.Set; 14 | 15 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 16 | 17 | /** 18 | * domain-name 19 | *

20 | * The Domain Name represents the properties of a network domain name. 21 | * 22 | */ 23 | @Value.Immutable @Serial.Version(1L) 24 | @DefaultTypeValue(value = "domain-name", groups = {DefaultValuesProcessor.class}) 25 | @Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 26 | @JsonTypeName("domain-name") 27 | @JsonSerialize(as = DomainName.class) @JsonDeserialize(builder = DomainName.Builder.class) 28 | @JsonPropertyOrder({"type", "extensions", "value", "resolves_to_refs"}) 29 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 30 | public interface DomainNameCoo extends CyberObservableObject { 31 | 32 | @JsonProperty("value") 33 | @JsonPropertyDescription("Specifies the value of the domain name.") 34 | @NotNull 35 | String getValue(); 36 | 37 | @JsonProperty("resolves_to_refs") 38 | @JsonPropertyDescription("Specifies a list of references to one or more IP addresses or domain names that the domain name resolves to.") 39 | Set getResolvesToRefs(); 40 | } 41 | -------------------------------------------------------------------------------- /docs/stix-java-notes.md: -------------------------------------------------------------------------------- 1 | # STIX Java library Notes 2 | 3 | # Relation.class 4 | 5 | The _Relation_ class is a wrapper class for _Stix Relationships Objects_ (SROs) (Relationship.class and Sighting.class). 6 | 7 | Relation.class allows the wrapping of a String Id or a Bundleable Object. This means SDOs, SROs, and Data Markings 8 | (Marking Definitions). 9 | 10 | When a object is converted into a JSON string, any relationships that are within the Java object 11 | (attributes that link to another object, whether a list of 1-1) are converted to a String ID. 12 | When a JSON string is parsed back into a Java Object, there is no guarantee that the parsed object will contain 13 | the other objects to complete the relationship chain. Therefore the Relation.class wrapper is used. 14 | 15 | When any Object is converted into JSON String, it will convert all attributes that are relations into the String ID 16 | of the related object. 17 | 18 | When any object is parsed from JSON into a Java object, all relation fields will be parsed into a Relation 19 | object that contains only the ID. 20 | If you wish to "hydrate" the objects' relations with the pointers to the actual Objects, and thus make them navigable, 21 | you can use the "hydrate" method helpers found in each object. 22 | 23 | # Object Hydration 24 | 25 | The process of providing a list of bundleable objects (``) to which the object to whihc you call 26 | the hydration method on, will search through the list and create pointers to the real object and update the 27 | Relation Wrapper from being String ID to using the actual Object. 28 | ... 29 | 30 | # Bundle Auto Population 31 | 32 | The process of a bundle performing a recursive search on all objects in the bundle, and finding any nested objects 33 | that should be added into the bundle as "Bundle Objects". This allows someone to generate STIX objects in a fluent 34 | and nested fashion, and not have to worry about remembering to add all objects into the bundle. 35 | ... -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/objects/MacAddressCoo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 8 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 9 | import org.immutables.serial.Serial; 10 | import org.immutables.value.Value; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.Pattern; 14 | 15 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 16 | 17 | /** 18 | * mac-addr 19 | *

20 | * The MAC Address Object represents a single Media Access Control (MAC) address. 21 | * 22 | */ 23 | @Value.Immutable @Serial.Version(1L) 24 | @DefaultTypeValue(value = "mac-addr", groups = {DefaultValuesProcessor.class}) 25 | @Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 26 | @JsonTypeName("mac-addr") 27 | @JsonSerialize(as = MacAddress.class) @JsonDeserialize(builder = MacAddress.Builder.class) 28 | @JsonPropertyOrder({"type", "extensions", "value"}) 29 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 30 | public interface MacAddressCoo extends CyberObservableObject { 31 | 32 | /** 33 | * The MAC address value MUST be represented as a single colon-delimited, lowercase MAC-48 address, 34 | * which MUST include leading zeros for each octet. 35 | * (Required) 36 | * 37 | */ 38 | @JsonProperty("value") 39 | @JsonPropertyDescription("Specifies one or more mac addresses expressed using CIDR notation.") 40 | @Pattern(regexp="^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$") 41 | @NotNull 42 | String getValue(); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionSetConverter.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json.converters.dehydrated; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.node.ObjectNode; 6 | import com.fasterxml.jackson.databind.util.StdConverter; 7 | import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; 8 | import io.digitalstate.stix.json.StixParsers; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * Generates a Dehydrated Marking Definition Set used for Deserialization 15 | */ 16 | public class MarkingDefinitionSetConverter extends StdConverter, Set> { 17 | 18 | @Override 19 | public Set convert(Set values) { 20 | Set markDefSet = new HashSet<>(); 21 | values.forEach(v -> { 22 | String[] parsedValue = v.split("--"); 23 | 24 | if (parsedValue.length == 2) { 25 | ObjectMapper mapper = StixParsers.getJsonMapper(); 26 | ObjectNode node = mapper.createObjectNode(); 27 | 28 | node.put("type", parsedValue[0]); 29 | node.put("id", v); 30 | node.put("hydrated", false); 31 | 32 | try { 33 | MarkingDefinitionDm markingDef = mapper.treeToValue(node, MarkingDefinitionDm.class); 34 | //@TODO add more logic 35 | markDefSet.add(markingDef); 36 | 37 | } catch (JsonProcessingException e) { 38 | e.printStackTrace(); 39 | throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); 40 | } 41 | 42 | } else { 43 | throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + v); 44 | } 45 | }); 46 | return markDefSet; 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectSetConverter.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.json.converters.dehydrated; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.node.ObjectNode; 6 | import com.fasterxml.jackson.databind.util.StdConverter; 7 | import io.digitalstate.stix.bundle.BundleableObject; 8 | import io.digitalstate.stix.json.StixParsers; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * Generates a Dehydrated Bundleable Object based on a ID from a Set of BundleableObjects. 15 | */ 16 | public class BundleableObjectSetConverter extends StdConverter, Set> { 17 | 18 | @Override 19 | public Set convert(Set values) { 20 | Set bundleableObjectSet = new HashSet<>(); 21 | values.forEach(v -> { 22 | String[] parsedValue = v.split("--"); 23 | 24 | if (parsedValue.length == 2) { 25 | ObjectMapper mapper = StixParsers.getJsonMapper(); 26 | ObjectNode node = mapper.createObjectNode(); 27 | 28 | node.put("type", parsedValue[0]); 29 | node.put("id", v); 30 | node.put("hydrated", false); 31 | 32 | 33 | try { 34 | BundleableObject bundleableObject = mapper.treeToValue(node, BundleableObject.class); 35 | //@TODO add more logic 36 | bundleableObjectSet.add(bundleableObject); 37 | 38 | } catch (JsonProcessingException e) { 39 | e.printStackTrace(); 40 | throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); 41 | } 42 | 43 | } else { 44 | throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + v); 45 | } 46 | }); 47 | return bundleableObjectSet; 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/objects/AutonomousSystemCoo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 8 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 9 | import org.immutables.serial.Serial; 10 | import org.immutables.value.Value; 11 | 12 | import java.util.Optional; 13 | 14 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 15 | 16 | /** 17 | * autonomous-system 18 | *

19 | * The AS object represents the properties of an Autonomous Systems (AS). 20 | * 21 | */ 22 | @Value.Immutable @Serial.Version(1L) 23 | @DefaultTypeValue(value = "autonomous-system", groups = {DefaultValuesProcessor.class}) 24 | @Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 25 | @JsonTypeName("autonomous-system") 26 | @JsonSerialize(as = AutonomousSystem.class) @JsonDeserialize(builder = AutonomousSystem.Builder.class) 27 | @JsonPropertyOrder({"type", "extensions", "number", "name", "rir"}) 28 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 29 | public interface AutonomousSystemCoo extends CyberObservableObject { 30 | 31 | @JsonProperty("number") 32 | @JsonPropertyDescription("Specifies the number assigned to the AS. Such assignments are typically performed by a Regional Internet Registries (RIR)") 33 | Long getNumber(); 34 | 35 | @JsonProperty("name") 36 | @JsonPropertyDescription("Specifies the name of the AS.") 37 | Optional getName(); 38 | 39 | @JsonProperty("rir") 40 | @JsonPropertyDescription("Specifies the name of the Regional Internet Registry (RIR) that assigned the number to the AS.") 41 | Optional getRir(); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/helpers/StixDataFormats.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.helpers; 2 | 3 | import java.time.ZoneId; 4 | import java.time.format.DateTimeFormatter; 5 | import java.time.format.DateTimeFormatterBuilder; 6 | import java.time.temporal.ChronoField; 7 | 8 | public class StixDataFormats { 9 | 10 | /** 11 | * Default pattern for deserialization of date/times into a STIX compliant timestamp. 12 | */ 13 | public static final String TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]X"; 14 | 15 | /** 16 | * Default Timezone used for Serialization and Deserialization. 17 | */ 18 | public static final String TIMEZONE = "UTC"; 19 | 20 | /** 21 | * Supports 0-9 digits of Sub-Second precision storage. (Nano Second Support) 22 | * @return 23 | */ 24 | public static DateTimeFormatter getReaderStixDateTimeFormatter() { 25 | DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder() 26 | .appendPattern("yyyy-MM-dd'T'HH:mm:ss") 27 | .optionalStart() 28 | .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) 29 | .optionalEnd() 30 | .appendPattern("X"); 31 | 32 | return formatterBuilder.toFormatter().withZone(ZoneId.of("UTC")); 33 | } 34 | 35 | public static DateTimeFormatter getWriterStixDateTimeFormatter(int subSecondPrecision) { 36 | if (subSecondPrecision > 9){ 37 | throw new IllegalArgumentException("Sub-Second Precision can only be from 0 to 9 digits"); 38 | } 39 | DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder(); 40 | 41 | formatterBuilder.appendPattern("yyyy-MM-dd'T'HH:mm:ss"); 42 | 43 | if (subSecondPrecision > 0){ 44 | formatterBuilder.appendFraction(ChronoField.NANO_OF_SECOND,subSecondPrecision, subSecondPrecision, true); 45 | } 46 | 47 | formatterBuilder.appendPattern("X"); 48 | 49 | return formatterBuilder.toFormatter().withZone(ZoneId.of("UTC")); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/sdo/objects/VulnerabilitySdo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.sdo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.redaction.Redactable; 7 | import io.digitalstate.stix.sdo.DomainObject; 8 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 9 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 10 | import org.immutables.serial.Serial; 11 | import org.immutables.value.Value; 12 | 13 | import javax.validation.constraints.NotBlank; 14 | import java.util.Optional; 15 | 16 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 17 | 18 | /** 19 | * vulnerability 20 | *

21 | * A Vulnerability is a mistake in software that can be directly used by a hacker to gain access to a system or network. 22 | * 23 | */ 24 | @Value.Immutable @Serial.Version(1L) 25 | @Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 26 | @JsonTypeName("vulnerability") 27 | @DefaultTypeValue(value = "vulnerability", groups = {DefaultValuesProcessor.class}) 28 | @JsonSerialize(as = Vulnerability.class) @JsonDeserialize(builder = Vulnerability.Builder.class) 29 | @JsonPropertyOrder({"type", "id", "created_by_ref", "created", 30 | "modified", "revoked", "labels", "external_references", 31 | "object_marking_refs", "granular_markings", "name", "description"}) 32 | @Redactable 33 | public interface VulnerabilitySdo extends DomainObject { 34 | 35 | @NotBlank 36 | @JsonProperty("name") 37 | @JsonPropertyDescription("The name used to identify the Vulnerability.") 38 | @Redactable(useMask = true) 39 | String getName(); 40 | 41 | @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 42 | @JsonPropertyDescription("Provides more context and details about the Vulnerability.") 43 | @Redactable 44 | Optional getDescription(); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/elements/NodeData.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.elements; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.annotation.JsonUnwrapped; 7 | 8 | import java.util.HashMap; 9 | 10 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 11 | 12 | public class NodeData { 13 | 14 | private String id; 15 | private String type; 16 | private String parent; 17 | private Object jsonData; 18 | private HashMap additionalProperties = new HashMap<>(); 19 | 20 | public NodeData(String id, String type, String parent, Object jsonData) { 21 | this.id = id; 22 | this.type = type; 23 | this.parent = parent; 24 | this.jsonData = jsonData; 25 | } 26 | 27 | @JsonProperty("id") 28 | public String getId() { 29 | return id; 30 | } 31 | 32 | public void setId(String id) { 33 | this.id = id; 34 | } 35 | 36 | @JsonProperty("type") 37 | public String getType() { 38 | return type; 39 | } 40 | 41 | public void setType(String type) { 42 | this.type = type; 43 | } 44 | 45 | @JsonProperty("parent") 46 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 47 | public String getParent() { 48 | return parent; 49 | } 50 | 51 | public void setParent(String parent) { 52 | this.parent = parent; 53 | } 54 | 55 | @JsonProperty("stix") 56 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 57 | public Object getJsonData() { 58 | return jsonData; 59 | } 60 | 61 | public void setJsonData(Object jsonData) { 62 | this.jsonData = jsonData; 63 | } 64 | 65 | @JsonUnwrapped 66 | @JsonAnyGetter 67 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 68 | public HashMap getAdditionalProperties() { 69 | return additionalProperties; 70 | } 71 | 72 | public void setAdditionalProperties(HashMap additionalProperties) { 73 | this.additionalProperties = additionalProperties; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/extension/types/IcmpExtensionExt.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.extension.types; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.extension.CyberObservableExtension; 7 | import io.digitalstate.stix.coo.objects.NetworkTrafficCoo; 8 | import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; 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 | import javax.validation.constraints.NotNull; 15 | import javax.validation.constraints.Pattern; 16 | 17 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 18 | 19 | /** 20 | * icmp-ext 21 | *

22 | * The ICMP extension specifies a default extension for capturing network 23 | * traffic properties specific to ICMP. 24 | * 25 | */ 26 | @Value.Immutable @Serial.Version(1L) 27 | @DefaultTypeValue(value = "icmp-ext", groups = {DefaultValuesProcessor.class}) 28 | @Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) 29 | @JsonSerialize(as = IcmpExtension.class) @JsonDeserialize(builder = IcmpExtension.Builder.class) 30 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 31 | @JsonPropertyOrder({ "icmp_type_hex", "icmp_code_hex" }) 32 | @JsonTypeName("icmp-ext") 33 | @AllowedParents({NetworkTrafficCoo.class}) 34 | public interface IcmpExtensionExt extends CyberObservableExtension { 35 | 36 | @JsonProperty("icmp_type_hex") 37 | @JsonPropertyDescription("Specifies the ICMP type byte.") 38 | @Pattern(regexp = "^([a-fA-F0-9]{2})+$") 39 | @NotNull 40 | String getOcmpTypeHex(); 41 | 42 | @JsonProperty("icmp_code_hex") 43 | @JsonPropertyDescription("Specifies the ICMP code byte.") 44 | @Pattern(regexp = "^([a-fA-F0-9]{2})+$") 45 | @NotNull 46 | String getIcmpCodeHex(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/MalwareSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Malware 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class MalwareSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Malware Data: Run: '#i'"() { 21 | when: "Generating Malware Data" 22 | Malware originalMalware = stixMockDataGenerator.mockMalware() 23 | // println "Original Object: ${originalMalware.toString()}" 24 | 25 | then: "Convert Malware to Json" 26 | JsonNode originalJson = mapper.readTree(originalMalware.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Malware Object" 31 | Malware parsedMalware = (Malware)StixParsers.parseObject(originalJsonString) 32 | Malware parsedMalwareGeneric = StixParsers.parse(originalJsonString, Malware.class) 33 | // println "Parsed Object: ${parsedMalware}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Malware back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedMalware.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/graph/sdo/ObservedDataGraphGenerator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.graph.sdo; 2 | 3 | import io.digitalstate.stix.graph.GraphGenerator; 4 | import io.digitalstate.stix.graph.coo.CyberObservableGraphGenerator; 5 | import io.digitalstate.stix.graph.elements.Edge; 6 | import io.digitalstate.stix.graph.elements.GraphElement; 7 | import io.digitalstate.stix.graph.elements.Node; 8 | import io.digitalstate.stix.sdo.objects.ObservedDataSdo; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.UUID; 13 | 14 | public class ObservedDataGraphGenerator implements GraphGenerator { 15 | 16 | private ObservedDataSdo object; 17 | 18 | public ObservedDataGraphGenerator(ObservedDataSdo object) { 19 | this.object = object; 20 | } 21 | 22 | @Override 23 | public Set process() { 24 | Set elements = new HashSet<>(); 25 | 26 | elements.add(generateNode()); 27 | elements.addAll(generateCooElements()); 28 | 29 | return elements; 30 | } 31 | 32 | private Node generateNode() { 33 | return new Node(object.getId(), object.getType(), null, object); 34 | } 35 | 36 | public ObservedDataSdo getObject() { 37 | return object; 38 | } 39 | 40 | private Set generateCooElements(){ 41 | Set elements = new HashSet<>(); 42 | 43 | String observedDataId = object.getId(); 44 | 45 | object.getObjects().forEach(coo -> { 46 | 47 | // Generate the Cyber Observable node 48 | Node cooGraphNode = new CyberObservableGraphGenerator(observedDataId, coo).generateNode(); 49 | elements.add(cooGraphNode); 50 | 51 | String uuidPrefix = "ref"; 52 | String uuid = uuidPrefix + "-" + UUID.randomUUID().toString(); 53 | // Use the Cyber Observable node's ID as the target: 54 | Edge edge = new Edge(uuid, uuidPrefix, observedDataId, cooGraphNode.getData().getId(), null); 55 | 56 | edge.getData().setEdgeLabel(coo.getType()); 57 | edge.getData().getAdditionalProperties().put("ref_type", "cyber_observable"); 58 | 59 | elements.add(edge); 60 | }); 61 | 62 | return elements; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/businessrule/BusinessRule.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.businessrule; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.*; 6 | 7 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 8 | import static java.lang.annotation.ElementType.TYPE; 9 | 10 | /** 11 | * The interface Business rule. 12 | */ 13 | @Documented 14 | @Constraint(validatedBy = {StixValidateBusinessRuleValidator.class}) 15 | @Target( { ANNOTATION_TYPE, TYPE }) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Repeatable(BusinessRule.List.class) 18 | public @interface BusinessRule { 19 | 20 | /** 21 | * Message string. Not Used. The Error Message is used instead. 22 | * 23 | * @return the string 24 | */ 25 | String message() default "An business rule failed to validate"; 26 | 27 | /** 28 | * Groups class [ ]. 29 | * 30 | * @return the class [ ] 31 | */ 32 | Class[] groups() default {}; 33 | 34 | /** 35 | * Payload class [ ]. 36 | * 37 | * @return the class [ ] 38 | */ 39 | Class[] payload() default {}; 40 | 41 | /** 42 | * If exp string. 43 | * 44 | * @return the string 45 | */ 46 | String ifExp(); 47 | 48 | /** 49 | * Then exp string. 50 | * 51 | * @return the string 52 | */ 53 | //@TODO look into making the thenExp param into a array. So you can provide multiple independent conditions that all must result in the ExpectedResult value 54 | String thenExp(); 55 | 56 | /** 57 | * Error message string. 58 | * 59 | * @return the string 60 | */ 61 | String errorMessage(); 62 | 63 | /** 64 | * Expected result boolean. 65 | * 66 | * @return the boolean 67 | */ 68 | boolean expectedResult() default true; 69 | 70 | /** 71 | * The interface List. 72 | */ 73 | @Documented 74 | @Retention(RetentionPolicy.RUNTIME) 75 | @Target( { ANNOTATION_TYPE, TYPE }) 76 | @interface List { 77 | /** 78 | * Value business rule [ ]. 79 | * 80 | * @return the business rule [ ] 81 | */ 82 | BusinessRule[] value(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/types/WindowsRegistryValueObj.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.types; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.common.StixCustomProperties; 7 | import io.digitalstate.stix.validation.GenericValidation; 8 | import io.digitalstate.stix.validation.contraints.vocab.Vocab; 9 | import io.digitalstate.stix.vocabulary.vocabularies.WindowsRegistryValueDataTypes; 10 | import org.immutables.serial.Serial; 11 | import org.immutables.value.Value; 12 | 13 | import javax.validation.constraints.NotNull; 14 | import java.io.Serializable; 15 | import java.util.Optional; 16 | 17 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 18 | 19 | 20 | /** 21 | * The Windows Registry Value type captures the properties of a Windows Registry Key Value. 22 | */ 23 | @Value.Immutable @Serial.Version(1L) 24 | //@DefaultTypeValue(value = "windows-registry-value-type", groups = {DefaultValuesProcessor.class}) 25 | @Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 26 | @JsonSerialize(as = WindowsRegistryValue.class) @JsonDeserialize(builder = WindowsRegistryValue.Builder.class) 27 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 28 | @JsonPropertyOrder({ "name", "data", "data_type" }) 29 | //@JsonTypeName("windows-registry-value-type") 30 | public interface WindowsRegistryValueObj extends GenericValidation, StixCustomProperties, Serializable { 31 | 32 | @JsonProperty("name") 33 | @JsonPropertyDescription("Specifies the name of the registry value. For specifying the default value in a registry key, an empty string MUST be used.") 34 | @NotNull 35 | String getName(); 36 | 37 | @JsonProperty("data") 38 | @JsonPropertyDescription("Specifies the data contained in the registry value.") 39 | Optional getData(); 40 | 41 | @JsonProperty("data_type") 42 | @JsonPropertyDescription("Specifies the registry (REG_*) data type used in the registry value.") 43 | Optional<@Vocab(WindowsRegistryValueDataTypes.class) String> getDataType(); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/objects/EmailAddressCoo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 8 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 9 | import org.immutables.serial.Serial; 10 | import org.immutables.value.Value; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.Pattern; 14 | import java.util.Optional; 15 | 16 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 17 | 18 | /** 19 | * email-addr 20 | *

21 | * The Email Address Object represents a single email address. 22 | * 23 | */ 24 | @Value.Immutable @Serial.Version(1L) 25 | @DefaultTypeValue(value = "email-addr", groups = {DefaultValuesProcessor.class}) 26 | @Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 27 | @JsonTypeName("email-addr") 28 | @JsonSerialize(as = EmailAddress.class) @JsonDeserialize(builder = EmailAddress.Builder.class) 29 | @JsonPropertyOrder({"type", "extensions", "value", "display_name", "belongs_to_ref"}) 30 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 31 | public interface EmailAddressCoo extends CyberObservableObject { 32 | 33 | @JsonProperty("value") 34 | @JsonPropertyDescription("Specifies a single email address. This MUST not include the display name.") 35 | @Pattern(regexp="(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)") 36 | @NotNull 37 | String getValue(); 38 | 39 | @JsonProperty("display_name") 40 | @JsonPropertyDescription("Specifies a single email display name, i.e., the name that is displayed to the human user of a mail application.") 41 | Optional getDisplayName(); 42 | 43 | @JsonProperty("belongs_to_ref") 44 | @JsonPropertyDescription("Specifies the user account that the email address belongs to, as a reference to a User Account Object.") 45 | Optional getBelongsToRef(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/CampaignSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Campaign 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class CampaignSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Campaign Data: Run: '#i'"() { 21 | when: "Generating Campaign Data" 22 | Campaign originalCampaign = stixMockDataGenerator.mockCampaign() 23 | // println "Original Object: ${originalCampaign.toString()}" 24 | 25 | then: "Convert Campaign to Json" 26 | JsonNode originalJson = mapper.readTree(originalCampaign.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Campaign Object" 31 | Campaign parsedCampaign = (Campaign)StixParsers.parseObject(originalJsonString) 32 | Campaign parsedCampaignGeneric = StixParsers.parse(originalJsonString, Campaign.class) 33 | // println "Parsed Object: ${parsedCampaign}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Identity Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedCampaign.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/ToolSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Tool 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class ToolSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Tool Data: Run: '#i'"() { 21 | when: "Generating Tool Data" 22 | Tool originalTool = stixMockDataGenerator.mockTool() 23 | // println "Original Object: ${originalTool.toString()}" 24 | 25 | then: "Convert Tool to Json" 26 | JsonNode originalJson = mapper.readTree(originalTool.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Tool Object" 31 | Tool parsedTool = (Tool)StixParsers.parseObject(originalJsonString) 32 | Tool parsedToolGeneric = StixParsers.parse(originalJsonString, Tool.class) 33 | // println "Parsed Object: ${parsedTool}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Tool back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedTool.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sro/SightingSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sro 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sro.objects.Sighting 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class SightingSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Sighting SRO Data: Run: '#i'"() { 21 | when: "Generating Sighting SRO Data" 22 | Sighting originalSighting = stixMockDataGenerator.mockSighting() 23 | // println "Original Object: ${originalSighting.toString()}" 24 | 25 | then: "Convert Sighting to Json" 26 | JsonNode originalJson = mapper.readTree(originalSighting.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Sighting Object" 31 | Sighting parsedSighting = (Sighting)StixParsers.parseObject(originalJsonString) 32 | Sighting parsedSightingGeneric = StixParsers.parse(originalJsonString, Sighting.class) 33 | // println "Parsed Object: ${parsedSighting}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalSighting == parsedMarkingDefinition 38 | 39 | then: "Convert Parsed Sighting Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedSighting.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/IdentitySpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Identity 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class IdentitySpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Identity Data: Run: '#i'"() { 21 | when: "Generating Identity Data" 22 | Identity originalIdentity = stixMockDataGenerator.mockIdentity() 23 | // println "Original Object: ${originalIdentity.toString()}" 24 | 25 | then: "Convert Identity to Json" 26 | JsonNode originalJson = mapper.readTree(originalIdentity.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Attack Pattern Object" 31 | Identity parsedIdentity = (Identity)StixParsers.parseObject(originalJsonString) 32 | Identity parsedIdentityGeneric = StixParsers.parse(originalJsonString, Identity.class) 33 | // println "Parsed Object: ${parsedIdentity}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Identity Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedIdentity.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorCollection.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.vocab; 2 | 3 | import com.google.common.collect.Sets; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class StixVocabValidatorCollection implements ConstraintValidator> { 12 | 13 | private Class vocabulary; 14 | 15 | @Override 16 | public void initialize(Vocab vocabConstraint) { 17 | vocabulary = vocabConstraint.value(); 18 | } 19 | 20 | @Override 21 | public boolean isValid(Set vocab, 22 | ConstraintValidatorContext cxt) { 23 | try { 24 | Set vocabTerms = vocabulary.newInstance().getAllTerms(); 25 | boolean evalContains = vocabTerms.containsAll(vocab); 26 | if (!evalContains){ 27 | cxt.disableDefaultConstraintViolation(); 28 | Sets.SetView difference = Sets.difference(new HashSet<>(vocab), vocabTerms); 29 | 30 | String violationMessage = "Items: " + difference.toString() + " are not found in class " + vocabulary.getCanonicalName(); 31 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 32 | } 33 | 34 | return evalContains; 35 | 36 | } catch (InstantiationException e) { 37 | cxt.disableDefaultConstraintViolation(); 38 | String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); 39 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 40 | e.printStackTrace(); 41 | return false; 42 | 43 | } catch (IllegalAccessException e) { 44 | cxt.disableDefaultConstraintViolation(); 45 | String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); 46 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 47 | e.printStackTrace(); 48 | return false; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/IndicatorSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Indicator 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class IndicatorSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Indicator Data: Run: '#i'"() { 21 | when: "Generating Indicator Data" 22 | Indicator originalIndicator = stixMockDataGenerator.mockIndicator() 23 | // println "Original Object: ${originalIndicator.toString()}" 24 | 25 | then: "Convert Indicator to Json" 26 | JsonNode originalJson = mapper.readTree(originalIndicator.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Indicator Object" 31 | Indicator parsedIndicator = (Indicator)StixParsers.parseObject(originalJsonString) 32 | Indicator parsedIndicatorGeneric = StixParsers.parse(originalJsonString, Indicator.class) 33 | // println "Parsed Object: ${parsedIndicator}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Indicator back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedIndicator.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/ReportSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Report 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class ReportSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Report Data: Run: '#i'"() { 21 | when: "Generating Report Data" 22 | Report originalReport = stixMockDataGenerator.mockReport() 23 | // println "Original Object: ${originalReport.toString()}" 24 | 25 | then: "Convert Report to Json" 26 | JsonNode originalJson = mapper.readTree(originalReport.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Report Object" 31 | Report parsedReport = (Report)StixParsers.parseObject(originalJsonString) 32 | Report parsedReportGeneric = StixParsers.parse(originalJsonString, Report.class) 33 | // println "Parsed Object: ${parsedReport}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Report back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedReport.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..500) // More tests are run because of the large variation of probabilities and number of combinations 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/IntrusionSetSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.IntrusionSet 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class IntrusionSetSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Intrusion Set Data: Run: '#i'"() { 21 | when: "Generating Intrusion Set Data" 22 | IntrusionSet originalIntrusionSet = stixMockDataGenerator.mockIntrusionSet() 23 | // println "Original Object: ${originalIntrusionSet.toString()}" 24 | 25 | then: "Convert Intrusion Set to Json" 26 | JsonNode originalJson = mapper.readTree(originalIntrusionSet.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Intrusion Set Object" 31 | IntrusionSet parsedIntrusionSet = (IntrusionSet)StixParsers.parseObject(originalJsonString) 32 | IntrusionSet parsedIntrusionSetGeneric = StixParsers.parse(originalJsonString, IntrusionSet.class) 33 | // println "Parsed Object: ${parsedIntrusionSet}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Intrusion Set back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedIntrusionSet.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sro/RelationshipSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sro 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sro.objects.Relationship 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class RelationshipSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Relationship SRO Data: Run: '#i'"() { 21 | when: "Generating Relationship SRO Data" 22 | Relationship originalRelationship = stixMockDataGenerator.mockRelationship() 23 | // println "Original Object: ${originalRelationship.toString()}" 24 | 25 | then: "Convert Relationship to Json" 26 | JsonNode originalJson = mapper.readTree(originalRelationship.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Relationship Object" 31 | Relationship parsedRelationship = (Relationship)StixParsers.parseObject(originalJsonString) 32 | Relationship parsedRelationshipGeneric = StixParsers.parse(originalJsonString, Relationship.class) 33 | // println "Parsed Object: ${parsedRelationship}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalRelationship == parsedMarkingDefinition 38 | 39 | then: "Convert Parsed Relationship Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedRelationship.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/AttackPatternSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.AttackPattern 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class AttackPatternSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Attack Pattern Data: Run: '#i'"() { 21 | when: "Generating Attack Pattern Data" 22 | AttackPattern originalAttackPattern = stixMockDataGenerator.mockAttackPattern() 23 | // println "Original Object: ${originalAttackPattern.toString()}" 24 | 25 | then: "Convert Attack Pattern to Json" 26 | JsonNode originalJson = mapper.readTree(originalAttackPattern.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Attack Pattern Object" 31 | AttackPattern parsedAttackPattern = (AttackPattern)StixParsers.parseObject(originalJsonString) 32 | AttackPattern parsedAttackPatternGeneric = StixParsers.parse(originalJsonString, AttackPattern.class) 33 | // println "Parsed Object: ${parsedAttackPattern}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Attack Pattern Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedAttackPattern.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/CourseOfActionSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.CourseOfAction 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class CourseOfActionSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Course of Action Data: Run: '#i'"() { 21 | when: "Generating Identity Data" 22 | CourseOfAction originalCourseOfAction = stixMockDataGenerator.mockCourseOfAction() 23 | // println "Original Object: ${originalCourseOfAction.toString()}" 24 | 25 | then: "Convert Course of Action to Json" 26 | JsonNode originalJson = mapper.readTree(originalCourseOfAction.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Course of Action Object" 31 | CourseOfAction parsedCourseOfAction = (CourseOfAction)StixParsers.parseObject(originalJsonString) 32 | CourseOfAction parsedCourseOfActionGeneric = StixParsers.parse(originalJsonString, CourseOfAction.class) 33 | // println "Parsed Object: ${parsedCourseOfAction}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Identity Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedCourseOfAction.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipTypeLimitValidator.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.relationship; 2 | 3 | import io.digitalstate.stix.sdo.DomainObject; 4 | import io.digitalstate.stix.sro.objects.RelationshipSro; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class StixRelationshipTypeLimitValidator implements ConstraintValidator { 12 | 13 | private Class source; 14 | private String[] relationshipTypes; 15 | private String[] commonRelationshipsTypes; 16 | private boolean enforceCommonRelationshipTypes; 17 | 18 | @Override 19 | public void initialize(RelationshipTypeLimit relationshipTypeLimitConstraint) { 20 | source = relationshipTypeLimitConstraint.source(); 21 | relationshipTypes = relationshipTypeLimitConstraint.relationshipTypes(); 22 | commonRelationshipsTypes = relationshipTypeLimitConstraint.commonRelationshipTypes(); 23 | enforceCommonRelationshipTypes = relationshipTypeLimitConstraint.enforceCommonRelationshipTypes(); 24 | } 25 | 26 | @Override 27 | public boolean isValid(RelationshipSro relationshipSro, 28 | ConstraintValidatorContext cxt) { 29 | if (source.isAssignableFrom(relationshipSro.getSourceRef().getClass())){ 30 | List typesList = Arrays.asList(relationshipTypes); 31 | List commonTypesList = Arrays.asList(commonRelationshipsTypes); 32 | if (typesList.contains(relationshipSro.getRelationshipType())){ 33 | return true; 34 | } else if (enforceCommonRelationshipTypes && commonTypesList.contains(relationshipSro.getRelationshipType())){ 35 | return true; 36 | }else { 37 | cxt.disableDefaultConstraintViolation(); 38 | String violationMessage = "Relationship Type: '" + relationshipSro.getRelationshipType() + 39 | "' is not supported with class " + relationshipSro.getClass().getCanonicalName(); 40 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 41 | return false; 42 | } 43 | } else { 44 | return true; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/ThreatActorSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.ThreatActor 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class ThreatActorSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Threat Actor Data: Run: '#i'"() { 21 | when: "Generating Threat Actor Data" 22 | ThreatActor originalThreatActor = stixMockDataGenerator.mockThreatActor() 23 | // println "Original Object: ${originalThreatActor.toString()}" 24 | 25 | then: "Convert Threat Actor to Json" 26 | JsonNode originalJson = mapper.readTree(originalThreatActor.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Threat Actor Object" 31 | ThreatActor parsedThreatActor = (ThreatActor)StixParsers.parseObject(originalJsonString) 32 | ThreatActor parsedThreatActorGeneric = StixParsers.parse(originalJsonString, ThreatActor.class) 33 | // println "Parsed Object: ${parsedThreatActor}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Threat Actor back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedThreatActor.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/types/NtfsAlternateDataStreamObj.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.types; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.common.StixCustomProperties; 7 | import io.digitalstate.stix.validation.GenericValidation; 8 | import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; 9 | import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; 10 | import org.hibernate.validator.constraints.Length; 11 | import org.immutables.serial.Serial; 12 | import org.immutables.value.Value; 13 | 14 | import javax.validation.constraints.NotNull; 15 | import javax.validation.constraints.PositiveOrZero; 16 | import java.io.Serializable; 17 | import java.util.Map; 18 | import java.util.Optional; 19 | 20 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 21 | 22 | /** 23 | * The Alternate Data Stream type represents an NTFS alternate data stream. 24 | * 25 | */ 26 | @Value.Immutable @Serial.Version(1L) 27 | //@DefaultTypeValue(value = "alternate-data-stream-type", groups = {DefaultValuesProcessor.class}) 28 | @Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) 29 | @JsonSerialize(as = NtfsAlternateDataStream.class) @JsonDeserialize(builder = NtfsAlternateDataStream.Builder.class) 30 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 31 | @JsonPropertyOrder({ "name", "hashes", "size" }) 32 | //@JsonTypeName("alternate-data-stream-type") 33 | public interface NtfsAlternateDataStreamObj extends GenericValidation, StixCustomProperties, Serializable { 34 | 35 | @JsonProperty("name") 36 | @JsonPropertyDescription("Specifies the name of the alternate data stream.") 37 | @NotNull 38 | String getName(); 39 | 40 | @JsonProperty("hashes") 41 | @JsonPropertyDescription("Specifies a dictionary of hashes for the data contained in the alternate data stream.") 42 | Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); 43 | 44 | @JsonProperty("size") 45 | @JsonPropertyDescription("Specifies the size of the alternate data stream, in bytes, as a non-negative integer.") 46 | Optional<@PositiveOrZero Long> getSize(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/ObservedDataSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.ObservedData 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class ObservedDataSpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Observed-Data Data: Run: '#i'"() { 21 | when: "Generating Observed-Data Data" 22 | ObservedData originalObservedData = stixMockDataGenerator.mockObservedData() 23 | // println "Original Object: ${originalObservedData.toString()}" 24 | 25 | then: "Convert Observed-Data to Json" 26 | JsonNode originalJson = mapper.readTree(originalObservedData.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Observed-Data Object" 31 | ObservedData parsedObservedData = (ObservedData)StixParsers.parseObject(originalJsonString) 32 | ObservedData parsedObservedDataGeneric = StixParsers.parse(originalJsonString, ObservedData.class) 33 | // println "Parsed Object: ${parsedObservedData}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Observed-Data back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedObservedData.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..5000) // More tests are run because of the large variation of probabilities and number of combinations 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/extension/types/ArchiveFileExtensionExt.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.extension.types; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.extension.CyberObservableExtension; 7 | import io.digitalstate.stix.coo.objects.FileCoo; 8 | import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; 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 | import javax.validation.constraints.NotNull; 15 | import java.util.Optional; 16 | import java.util.Set; 17 | 18 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 19 | 20 | /** 21 | * The Archive File extension specifies a default extension for capturing 22 | * properties specific to archive files. 23 | * 24 | */ 25 | @Value.Immutable @Serial.Version(1L) 26 | @DefaultTypeValue(value = "archive-ext", groups = {DefaultValuesProcessor.class}) 27 | @Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) 28 | @JsonSerialize(as = ArchiveFileExtension.class) @JsonDeserialize(builder = ArchiveFileExtension.Builder.class) 29 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 30 | @JsonPropertyOrder({ "contains_refs", "version", "comment" }) 31 | @JsonTypeName("archive-ext") 32 | @AllowedParents({FileCoo.class}) 33 | public interface ArchiveFileExtensionExt extends CyberObservableExtension { 34 | 35 | @JsonProperty("contains_refs") 36 | @JsonPropertyDescription("Specifies the files contained in the archive, as a reference to one or more other File Objects. The objects referenced in this list MUST be of type file-object.") 37 | @NotNull 38 | Set getContainsRefs(); 39 | 40 | @JsonProperty("version") 41 | @JsonPropertyDescription("Specifies the version of the archive type used in the archive file.") 42 | Optional getVersion(); 43 | 44 | @JsonProperty("comment") 45 | @JsonPropertyDescription("Specifies a comment included as part of the archive file.") 46 | Optional getComment(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/groovy/stix/sdo/VulnerabilitySpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.sdo 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.json.StixParsers 6 | import io.digitalstate.stix.sdo.objects.Vulnerability 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class VulnerabilitySpec extends Specification { 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Vulnerability Data: Run: '#i'"() { 21 | when: "Generating Vulnerability Data" 22 | Vulnerability originalVulnerability = stixMockDataGenerator.mockVulnerability() 23 | // println "Original Object: ${originalVulnerability.toString()}" 24 | 25 | then: "Convert Vulnerability to Json" 26 | JsonNode originalJson = mapper.readTree(originalVulnerability.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Vulnerability Object" 31 | Vulnerability parsedVulnerability = (Vulnerability)StixParsers.parseObject(originalJsonString) 32 | Vulnerability parsedVulnerabilityGeneric = StixParsers.parse(originalJsonString, Vulnerability.class) 33 | // println "Parsed Object: ${parsedVulnerability}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalAttackPattern == parsedAttackPattern 38 | 39 | then: "Convert Parsed Vulnerability back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedVulnerability.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorString.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.vocab; 2 | 3 | import com.google.common.collect.Sets; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | public class StixVocabValidatorString implements ConstraintValidator { 13 | 14 | private Class vocabulary; 15 | 16 | @Override 17 | public void initialize(Vocab vocabConstraint) { 18 | vocabulary = vocabConstraint.value(); 19 | } 20 | 21 | @Override 22 | public boolean isValid(String vocab, ConstraintValidatorContext cxt) { 23 | if (vocab == null) { 24 | return true; 25 | 26 | } else { 27 | try { 28 | Set vocabTerms = vocabulary.newInstance().getAllTerms(); 29 | boolean evalContains = vocabTerms.contains(vocab); 30 | if (!evalContains) { 31 | cxt.disableDefaultConstraintViolation(); 32 | Sets.SetView difference = Sets.difference(new HashSet<>(Arrays.asList(vocab)), vocabTerms); 33 | 34 | String violationMessage = "Item: " + difference.toString() + " is not found in class " + vocabulary.getCanonicalName(); 35 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 36 | } 37 | 38 | return evalContains; 39 | 40 | } catch (InstantiationException e) { 41 | cxt.disableDefaultConstraintViolation(); 42 | String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); 43 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 44 | e.printStackTrace(); 45 | return false; 46 | 47 | } catch (IllegalAccessException e) { 48 | cxt.disableDefaultConstraintViolation(); 49 | String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); 50 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 51 | e.printStackTrace(); 52 | return false; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/objects/UrlCoo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.CyberObservableObject; 7 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 8 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 9 | import org.immutables.serial.Serial; 10 | import org.immutables.value.Value; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.Pattern; 14 | 15 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 16 | 17 | /** 18 | * url 19 | *

20 | * The URL Object represents the properties of a uniform resource locator (URL). 21 | * 22 | */ 23 | @Value.Immutable @Serial.Version(1L) 24 | @DefaultTypeValue(value = "url", groups = {DefaultValuesProcessor.class}) 25 | @Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 26 | @JsonTypeName("url") 27 | @JsonSerialize(as = Url.class) @JsonDeserialize(builder = Url.Builder.class) 28 | @JsonPropertyOrder({"type", "extensions", "value"}) 29 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 30 | public interface UrlCoo extends CyberObservableObject { 31 | 32 | /** 33 | * The value of this property MUST conform to [RFC3986], more specifically section 1.1.3 34 | * with reference to the definition for "Uniform Resource Locator" 35 | * (Required) 36 | * 37 | */ 38 | //@TODO can this be changed from @Pattern to @URL? 39 | @JsonProperty("value") 40 | @JsonPropertyDescription("Specifies the value of the URL.") 41 | @Pattern(regexp="^([a-zA-Z][a-zA-Z0-9+.-]*):(?:\\/\\/((?:(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:]|%[0-9a-fA-F]{2})*))(\\3)@)?(?=((?:\\[?(?:::[a-fA-F0-9]+(?::[a-fA-F0-9]+)?|(?:[a-fA-F0-9]+:)+(?::[a-fA-F0-9]+)+|(?:[a-fA-F0-9]+:)+(?::|(?:[a-fA-F0-9]+:?)*))\\]?)|(?:[a-zA-Z0-9-._~!$&'()*+,;=]|%[0-9a-fA-F]{2})*))\\5(?::(?=(\\d*))\\6)?)(\\/(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:@\\/]|%[0-9a-fA-F]{2})*))\\8)?|(\\/?(?!\\/)(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:@\\/]|%[0-9a-fA-F]{2})*))\\10)?)(?:\\?(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9a-fA-F]{2})*))\\11)?(?:#(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9a-fA-F]{2})*))\\12)?$") 42 | @NotNull 43 | String getValue(); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/groovy/stix/datamarkings/MarkingDefinitionSpec.groovy: -------------------------------------------------------------------------------- 1 | package stix.datamarkings 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import io.digitalstate.stix.datamarkings.MarkingDefinition 6 | import io.digitalstate.stix.json.StixParsers 7 | import org.skyscreamer.jsonassert.JSONAssert 8 | import org.skyscreamer.jsonassert.JSONCompareMode 9 | import spock.lang.Shared 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | import faker.StixMockDataGenerator 13 | 14 | class MarkingDefinitionSpec extends Specification{ 15 | 16 | @Shared ObjectMapper mapper = new ObjectMapper() 17 | @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() 18 | 19 | @Unroll 20 | def "Generate Marking Definition Data: Run: '#i'"() { 21 | when: "Generating Marking Definition Data" 22 | MarkingDefinition originalMarkingDefinition = stixMockDataGenerator.mockMarkingDefinition() 23 | // println "Original Object: ${originalMarkingDefinition.toString()}" 24 | 25 | then: "Convert Marking Definition to Json" 26 | JsonNode originalJson = mapper.readTree(originalMarkingDefinition.toJsonString()) 27 | String originalJsonString = mapper.writeValueAsString(originalJson) 28 | // println "Original Json: ${originalJsonString}" 29 | 30 | then: "Parse Json back into Marking Definition Object" 31 | MarkingDefinition parsedMarkingDefinition = (MarkingDefinition)StixParsers.parseObject(originalJsonString) 32 | MarkingDefinition parsedMarkingDefinitionGeneric = StixParsers.parse(originalJsonString, MarkingDefinition.class) 33 | // println "Parsed Object: ${parsedMarkingDefinition}" 34 | 35 | //@TODO needs to be setup to handle dehydrated object comparison 36 | // then: "Parsed object should match Original object" 37 | // assert originalMarkingDefinition == parsedMarkingDefinition 38 | 39 | then: "Convert Parsed Marking Definition Object back to into Json" 40 | JsonNode newJson = mapper.readTree(parsedMarkingDefinition.toJsonString()) 41 | String newJsonString = mapper.writeValueAsString(newJson) 42 | // println "New Json: ${newJsonString}" 43 | 44 | then: "New Json should match Original Json" 45 | JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) 46 | 47 | where: 48 | i << (1..100) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/coo/extension/types/NtfsFileExtenstionExt.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.coo.extension.types; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.coo.extension.CyberObservableExtension; 7 | import io.digitalstate.stix.coo.objects.FileCoo; 8 | import io.digitalstate.stix.coo.types.NtfsAlternateDataStreamObj; 9 | import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; 10 | import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; 11 | import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; 12 | import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; 13 | import org.immutables.serial.Serial; 14 | import org.immutables.value.Value; 15 | 16 | import java.util.Optional; 17 | import java.util.Set; 18 | 19 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 20 | 21 | /** 22 | * The NTFS file extension specifies a default extension for capturing properties specific to the storage of the file on the NTFS file system. 23 | * 24 | */ 25 | @Value.Immutable @Serial.Version(1L) 26 | @DefaultTypeValue(value = "ntfs-ext", groups = {DefaultValuesProcessor.class}) 27 | @Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) 28 | @JsonSerialize(as = NtfsFileExtenstion.class) @JsonDeserialize(builder = NtfsFileExtenstion.Builder.class) 29 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 30 | @JsonPropertyOrder({ "sid", "alternate_data_streams" }) 31 | @JsonTypeName("ntfs-ext") 32 | @AllowedParents({FileCoo.class}) 33 | @BusinessRule(ifExp = "true", thenExp = "getSid().isPresent() == true || getAlternateDataStreams().isEmpty() == false", errorMessage = "NTFS File Extension MUST contain at least one property from this extension") 34 | public interface NtfsFileExtenstionExt extends CyberObservableExtension { 35 | 36 | @JsonProperty("sid") 37 | @JsonPropertyDescription("Specifies the security ID (SID) value assigned to the file.") 38 | Optional getSid(); 39 | 40 | @JsonProperty("alternate_data_streams") 41 | @JsonPropertyDescription("Specifies a list of NTFS alternate data streams that exist for the file.") 42 | Set getAlternateDataStreams(); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorOptionalString.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.vocab; 2 | 3 | import com.google.common.collect.Sets; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.Optional; 11 | import java.util.Set; 12 | 13 | public class StixVocabValidatorOptionalString implements ConstraintValidator> { 14 | 15 | private Class vocabulary; 16 | 17 | @Override 18 | public void initialize(Vocab vocabConstraint) { 19 | vocabulary = vocabConstraint.value(); 20 | } 21 | 22 | @Override 23 | public boolean isValid(Optional vocab, ConstraintValidatorContext cxt) { 24 | if (vocab.isPresent()) { 25 | try { 26 | Set vocabTerms = vocabulary.newInstance().getAllTerms(); 27 | boolean evalContains = vocabTerms.contains(vocab.get()); 28 | if (!evalContains) { 29 | Sets.SetView difference = Sets.difference(new HashSet<>(Arrays.asList(vocab.get())), vocabTerms); 30 | cxt.disableDefaultConstraintViolation(); 31 | String violationMessage = "Item: " + difference.toString() + " is not found in class " + vocabulary.getCanonicalName(); 32 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 33 | } 34 | 35 | return evalContains; 36 | 37 | } catch (InstantiationException e) { 38 | cxt.disableDefaultConstraintViolation(); 39 | String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); 40 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 41 | e.printStackTrace(); 42 | return false; 43 | 44 | } catch (IllegalAccessException e) { 45 | cxt.disableDefaultConstraintViolation(); 46 | String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); 47 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 48 | e.printStackTrace(); 49 | return false; 50 | } 51 | } else { 52 | return true; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/sdo/types/KillChainPhaseType.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.sdo.types; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; 5 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 6 | import com.fasterxml.jackson.annotation.JsonValue; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import io.digitalstate.stix.common.StixCustomProperties; 10 | import io.digitalstate.stix.validation.GenericValidation; 11 | import org.immutables.serial.Serial; 12 | import org.immutables.value.Value; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | import java.io.Serializable; 16 | 17 | /** 18 | * kill-chain-phase 19 | *

20 | * The kill-chain-phase represents a phase in a kill chain. 21 | */ 22 | @Value.Immutable @Serial.Version(1L) 23 | @Value.Style(typeAbstract="*Type", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) 24 | @JsonSerialize(as = KillChainPhase.class) @JsonDeserialize(builder = KillChainPhase.Builder.class) 25 | @JsonPropertyOrder({ 26 | "kill_chain_name", 27 | "phase_name" 28 | }) 29 | public interface KillChainPhaseType extends GenericValidation, StixCustomProperties, Serializable { 30 | 31 | @NotBlank 32 | @JsonProperty("kill_chain_name") 33 | @JsonPropertyDescription("The name of the kill chain.") 34 | String killChainName(); 35 | 36 | @NotBlank 37 | @JsonProperty("phase_name") 38 | @JsonPropertyDescription("The name of the phase in the kill chain.") 39 | String phaseName(); 40 | 41 | 42 | /** 43 | * Create an Enumeration of the most common one: Lockheed-Martin 44 | */ 45 | //@TODO Convert to Vocab pattern 46 | public enum LockheedMartinKillChain { 47 | RECONNAISSANCE("reconnaissance"), 48 | WEAPONIZATION("weaponization"), 49 | DELIVERY("delivery"), 50 | EXPLOITATION("exploitation"), 51 | INSTALLATION("installation"), 52 | COMMAND_AND_CONTROL("command-and-control"), 53 | ACTIONS_ON_OBJECTIVE("actions-on-objective"); 54 | 55 | public static final String killChainName = "lockheed-martin-cyber-kill-chain"; 56 | 57 | String phase; 58 | 59 | LockheedMartinKillChain(String val) { 60 | this.phase = val; 61 | } 62 | 63 | @Override 64 | public String toString() {return phase;} 65 | 66 | @JsonValue 67 | public String getPhase() { return phase; } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/sdo/objects/AttackPatternSdo.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.sdo.objects; 2 | 3 | import com.fasterxml.jackson.annotation.*; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import io.digitalstate.stix.redaction.Redactable; 7 | import io.digitalstate.stix.sdo.DomainObject; 8 | import io.digitalstate.stix.sdo.types.KillChainPhaseType; 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 | import javax.validation.constraints.NotBlank; 15 | import java.util.Optional; 16 | import java.util.Set; 17 | 18 | import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; 19 | 20 | /** 21 | * attack-pattern 22 | *

23 | * Attack Patterns are a type of TTP that describe ways that adversaries attempt to compromise targets. 24 | * 25 | */ 26 | @Value.Immutable @Serial.Version(1L) 27 | @DefaultTypeValue(value = "attack-pattern", groups = {DefaultValuesProcessor.class}) 28 | @Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) 29 | @JsonTypeName("attack-pattern") 30 | @JsonSerialize(as = AttackPattern.class) @JsonDeserialize(builder = AttackPattern.Builder.class) 31 | @JsonPropertyOrder({"type", "id", "created_by_ref", "created", 32 | "modified", "revoked", "labels", "external_references", 33 | "object_marking_refs", "granular_markings", 34 | "name", "description", "kill_chain_phases"}) 35 | @Redactable 36 | public interface AttackPatternSdo extends DomainObject { 37 | 38 | @NotBlank 39 | @JsonProperty("name") 40 | @JsonPropertyDescription("The name used to identify the Attack Pattern.") 41 | @Redactable(useMask = true) 42 | String getName(); 43 | 44 | @JsonProperty("description") 45 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 46 | @JsonPropertyDescription("A description that provides more details and context about the Attack Pattern, potentially including its purpose and its key characteristics.") 47 | @Redactable 48 | Optional getDescription(); 49 | 50 | @JsonProperty("kill_chain_phases") 51 | @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) 52 | @JsonPropertyDescription("The list of kill chain phases for which this attack pattern is used.") 53 | @Redactable 54 | Set getKillChainPhases(); 55 | 56 | } -------------------------------------------------------------------------------- /src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/StixHashingVocabValidatorString.java: -------------------------------------------------------------------------------- 1 | package io.digitalstate.stix.validation.contraints.hashingvocab; 2 | 3 | import com.google.common.collect.Sets; 4 | import io.digitalstate.stix.vocabulary.StixVocabulary; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | public class StixHashingVocabValidatorString implements ConstraintValidator { 13 | 14 | private Class vocabulary; 15 | 16 | @Override 17 | public void initialize(HashingVocab hashingVocabConstraint) { 18 | vocabulary = hashingVocabConstraint.value(); 19 | } 20 | 21 | @Override 22 | public boolean isValid(String vocab, 23 | ConstraintValidatorContext cxt) { 24 | if (vocab.startsWith("x_")) { 25 | return true; 26 | } else { 27 | try { 28 | Set vocabTerms = vocabulary.newInstance().getAllTerms(); 29 | boolean evalContains = vocabTerms.contains(vocab); 30 | if (!evalContains) { 31 | Sets.SetView difference = Sets.difference(new HashSet<>(Arrays.asList(vocab)), vocabTerms); 32 | 33 | cxt.disableDefaultConstraintViolation(); 34 | String violationMessage = "Item: " + difference.toString() + " is not found in class " + vocabulary.getCanonicalName(); 35 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 36 | return false; 37 | } else { 38 | return true; 39 | } 40 | 41 | } catch (InstantiationException e) { 42 | cxt.disableDefaultConstraintViolation(); 43 | String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); 44 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 45 | e.printStackTrace(); 46 | return false; 47 | 48 | } catch (IllegalAccessException e) { 49 | cxt.disableDefaultConstraintViolation(); 50 | String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); 51 | cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); 52 | e.printStackTrace(); 53 | return false; 54 | } 55 | } 56 | } 57 | } 58 | --------------------------------------------------------------------------------