From 04c38eff439391d3f979ab2f34e08d8e3744084c Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Sun, 6 Oct 2019 20:12:55 -0400 Subject: [PATCH 01/21] Initial update for 2.1 with kotlin --- .mvn/wrapper/MavenWrapperDownloader.java | 110 - .mvn/wrapper/maven-wrapper.properties | 1 - .travis.yml | 12 - LICENSE | 21 - README.md | 683 ---- build.gradle | 44 + docs/BPMN/course_of_action_1.bpmn | 553 --- docs/BPMN/course_of_action_1.png | Bin 212698 -> 0 bytes docs/BPMN/cyber-observable-processing-1.bpmn | 134 - docs/BPMN/cyber-observable-processing-1.png | Bin 36813 -> 0 bytes docs/BPMN/report_review.bpmn | 392 --- docs/BPMN/report_review.png | Bin 159508 -> 0 bytes docs/BPMN/sample_processes_1.bpmn | 402 --- docs/BPMN/sample_processes_1.png | Bin 133467 -> 0 bytes docs/BPMN/stix_data_translation.bpmn | 383 --- docs/BPMN/stix_data_translation.png | Bin 119190 -> 0 bytes docs/BPMN/taxii_collection_propagation.bpmn | 397 --- docs/BPMN/taxii_collection_propagation.png | Bin 149518 -> 0 bytes docs/Common_Code_Snippets.md | 8 - docs/Diagrams/Generic-Data-Flow.png | Bin 116054 -> 0 bytes docs/images/stix-graph-example1.png | Bin 231007 -> 0 bytes docs/stix-java-notes.md | 35 - gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 + gradlew.bat | 84 + mvnw | 286 -- mvnw.cmd | 161 - pom.xml | 199 -- settings.gradle | 1 + .../stix/bundle/BundleObject.java | 97 - .../stix/bundle/BundleableObject.java | 27 - .../io/digitalstate/stix/common/Stix.java | 9 - .../digitalstate/stix/common/StixBoolean.java | 53 - .../stix/common/StixCommonProperties.java | 122 - .../stix/common/StixCustomObjectId.java | 28 - .../stix/common/StixCustomObjectType.java | 28 - .../stix/common/StixCustomProperties.java | 26 - .../digitalstate/stix/common/StixInstant.java | 105 - .../digitalstate/stix/common/StixLabels.java | 27 - .../stix/common/StixModified.java | 29 - .../digitalstate/stix/common/StixRevoked.java | 29 - .../stix/common/StixSpecVersion.java | 21 - .../stix/coo/CyberObservableObject.java | 15 - ...CyberObservableObjectCommonProperties.java | 55 - .../extension/CyberObservableExtension.java | 18 - ...erObservableExtensionCommonProperties.java | 23 - .../types/ArchiveFileExtensionExt.java | 48 - .../types/HttpRequestExtensionExt.java | 72 - .../coo/extension/types/IcmpExtensionExt.java | 48 - .../types/NetworkSocketExtensionExt.java | 80 - .../types/NtfsFileExtenstionExt.java | 44 - .../extension/types/PdfFileExtensionExt.java | 58 - .../types/RasterImageFileExtensionExt.java | 58 - .../coo/extension/types/TcpExtensionExt.java | 60 - .../types/UnixAccountExtensionExt.java | 54 - .../WindowsPeBinaryFileExtensionExt.java | 99 - .../types/WindowsProcessExtensionExt.java | 64 - .../types/WindowsServiceExtensionExt.java | 75 - ...ObservableExtensionsFieldDeserializer.java | 44 - ...erObservableExtensionsFieldSerializer.java | 38 - .../CyberObservableSetFieldDeserializer.java | 46 - .../CyberObservableSetFieldSerializer.java | 38 - .../stix/coo/objects/ArtifactCoo.java | 76 - .../stix/coo/objects/AutonomousSystemCoo.java | 43 - .../stix/coo/objects/DirectoryCoo.java | 64 - .../stix/coo/objects/DomainNameCoo.java | 40 - .../stix/coo/objects/EmailAddressCoo.java | 47 - .../stix/coo/objects/EmailMessageCoo.java | 98 - .../stix/coo/objects/FileCoo.java | 111 - .../stix/coo/objects/Ipv4AddressCoo.java | 60 - .../stix/coo/objects/Ipv6AddressCoo.java | 60 - .../stix/coo/objects/MacAddressCoo.java | 44 - .../stix/coo/objects/MutexCoo.java | 36 - .../stix/coo/objects/NetworkTrafficCoo.java | 140 - .../stix/coo/objects/ProcessCoo.java | 96 - .../stix/coo/objects/SoftwareCoo.java | 65 - .../digitalstate/stix/coo/objects/UrlCoo.java | 45 - .../stix/coo/objects/UserAccountCoo.java | 97 - .../coo/objects/WindowsRegistryKeyCoo.java | 60 - .../stix/coo/objects/X509CertificateCoo.java | 97 - .../stix/coo/types/MimePartTypeObj.java | 56 - .../coo/types/NtfsAlternateDataStreamObj.java | 48 - .../coo/types/WindowsPeOptionalHeaderObj.java | 170 - .../stix/coo/types/WindowsPeSectionObj.java | 55 - .../coo/types/WindowsRegistryValueObj.java | 45 - .../stix/coo/types/X509v3ExtensionsObj.java | 105 - .../stix/custom/StixCustomObject.java | 38 - .../custom/objects/GenericCustomObject.java | 25 - .../stix/datamarkings/GranularMarkingDm.java | 38 - .../datamarkings/MarkingDefinitionDm.java | 52 - .../stix/datamarkings/StixMarkingObject.java | 9 - .../objects/StatementMarkingObject.java | 27 - .../objects/TlpMarkingObject.java | 29 - .../stix/graph/GraphGenerator.java | 10 - .../stix/graph/StixGraphGenerator.java | 38 - .../bundle/BundleObjectGraphGenerator.java | 31 - .../BundleableObjectGraphGenerator.java | 48 - .../coo/CyberObservableGraphGenerator.java | 50 - .../MarkingDefinitionGraphGenerator.java | 44 - .../stix/graph/elements/Edge.java | 25 - .../stix/graph/elements/EdgeData.java | 96 - .../stix/graph/elements/GraphElement.java | 7 - .../stix/graph/elements/Node.java | 23 - .../stix/graph/elements/NodeData.java | 75 - .../graph/sdo/DomainObjectGraphGenerator.java | 46 - .../graph/sdo/ObservedDataGraphGenerator.java | 65 - .../sro/RelationshipSroGraphGenerator.java | 46 - .../graph/sro/SightingSroGraphGenerator.java | 85 - .../helpers/StixCustomPropertiesConfig.java | 41 - .../stix/helpers/StixDataFormats.java | 52 - .../stix/helpers/StixSpecVersion.java | 10 - .../stix/json/StixBooleanDeserializer.java | 25 - .../stix/json/StixBooleanSerializer.java | 30 - .../stix/json/StixInstantDeserializer.java | 25 - .../stix/json/StixInstantSerializer.java | 24 - .../json/StixParserValidationException.java | 27 - .../digitalstate/stix/json/StixParsers.java | 157 - .../dehydrated/BundleableObjectConverter.java | 42 - .../BundleableObjectSetConverter.java | 49 - .../dehydrated/DomainObjectConverter.java | 42 - .../DomainObjectOptionalConverter.java | 43 - .../MarkingDefinitionConverter.java | 42 - .../MarkingDefinitionSetConverter.java | 48 - .../stix/redaction/Redactable.java | 30 - .../BundleableObjectRedactionProcessor.java | 117 - .../digitalstate/stix/sdo/DomainObject.java | 30 - .../stix/sdo/objects/AttackPatternSdo.java | 56 - .../stix/sdo/objects/CampaignSdo.java | 76 - .../stix/sdo/objects/CourseOfActionSdo.java | 57 - .../stix/sdo/objects/IdentitySdo.java | 76 - .../stix/sdo/objects/IndicatorSdo.java | 86 - .../stix/sdo/objects/IntrusionSetSdo.java | 94 - .../stix/sdo/objects/MalwareSdo.java | 69 - .../stix/sdo/objects/ObservedDataSdo.java | 73 - .../stix/sdo/objects/ReportSdo.java | 81 - .../stix/sdo/objects/ThreatActorSdo.java | 109 - .../stix/sdo/objects/ToolSdo.java | 73 - .../stix/sdo/objects/VulnerabilitySdo.java | 46 - .../stix/sdo/types/ExternalReferenceType.java | 63 - .../stix/sdo/types/KillChainPhaseType.java | 70 - .../stix/sro/RelationshipObject.java | 14 - .../stix/sro/objects/RelationshipSro.java | 105 - .../stix/sro/objects/SightingSro.java | 95 - .../stix/validation/GenericValidation.java | 12 - .../stix/validation/SdoDefaultValidator.java | 33 - .../contraints/businessrule/BusinessRule.java | 84 - .../StixValidateBusinessRuleValidator.java | 65 - .../coo/allowedparents/AllowedParents.java | 21 - .../StixValidateParentCooValidator.java | 55 - .../allowedparents/ValidateExtensions.java | 25 - .../StixValidateParentCooValidator.java | 56 - .../ValidateReferences.java | 26 - .../defaulttypevalue/DefaultTypeValue.java | 33 - ...DefaultTypeValueBundleObjectValidator.java | 91 - ...ixDefaultTypeValueBundleableValidator.java | 94 - ...alueCyberObservableExtensionValidator.java | 59 - ...aultTypeValueCyberObservableValidator.java | 57 - .../contraints/hashingvocab/HashingVocab.java | 31 - .../StixHashingVocabValidatorString.java | 57 - .../MarkingDefinitionTypeLimit.java | 36 - ...ixMarkingDefinitionTypeLimitValidator.java | 78 - .../relationship/RelationshipLimit.java | 34 - .../relationship/RelationshipTypeLimit.java | 39 - .../StixRelationshipLimitValidator.java | 68 - .../StixRelationshipTypeLimitValidator.java | 47 - .../contraints/startswith/StartsWith.java | 31 - .../StixStartsWithValidatorString.java | 27 - .../vocab/StixVocabValidatorCollection.java | 51 - .../StixVocabValidatorOptionalString.java | 55 - .../vocab/StixVocabValidatorString.java | 56 - .../validation/contraints/vocab/Vocab.java | 36 - .../groups/DefaultValuesProcessor.java | 4 - .../stix/validation/groups/NoValidation.java | 4 - .../validation/groups/ValidateIdOnly.java | 8 - .../validation/sequences/SequenceDefault.java | 10 - .../sequences/SequenceValidationIdOnly.java | 10 - .../stix/vocabulary/StixVocabulary.java | 19 - .../vocabulary/vocabularies/AccountTypes.java | 31 - .../vocabularies/AttackMotivations.java | 33 - .../vocabularies/AttackResourceLevels.java | 33 - .../vocabularies/EncryptionAlgorithms.java | 35 - .../vocabularies/HashingAlgorithms.java | 33 - .../vocabularies/IdentityClasses.java | 33 - .../vocabularies/IndicatorLabels.java | 30 - .../vocabularies/IndustrySectors.java | 40 - .../vocabularies/MalwareLabels.java | 37 - .../NetworkSocketAddressFamilies.java | 31 - .../NetworkSocketProtocolFamilies.java | 36 - .../vocabularies/NetworkSocketTypes.java | 32 - .../vocabularies/RelationshipTypes.java | 38 - .../vocabulary/vocabularies/ReportLabels.java | 35 - .../vocabularies/ThreatActorLabels.java | 35 - .../vocabularies/ThreatActorRoles.java | 34 - .../ThreatActorSophistication.java | 34 - .../vocabulary/vocabularies/TlpLevels.java | 32 - .../vocabulary/vocabularies/ToolLabels.java | 34 - .../vocabularies/WindowsPeBinaryTypes.java | 30 - .../WindowsRegistryValueDataTypes.java | 34 - .../WindowsServiceStartTypes.java | 31 - .../vocabularies/WindowsServiceStatuses.java | 32 - .../vocabularies/WindowsServiceTypes.java | 31 - .../com/stephenott/stix/CommonProperties.kt | 48 + src/main/kotlin/com/stephenott/stix/Main.kt | 11 + .../kotlin/com/stephenott/stix/StixBundle.kt | 4 + .../kotlin/com/stephenott/stix/StixContent.kt | 5 + .../com/stephenott/stix/StixDataFormats.kt | 51 + .../kotlin/com/stephenott/stix/StixObject.kt | 8 + .../com/stephenott/stix/ValidatorManager.kt | 7 + .../kotlin/com/stephenott/stix/sdo/Sdo.kt | 28 + .../stephenott/stix/sdo/StixDomainObject.kt | 15 + .../stix/sdo/objects/AttackPattern.kt | 36 + .../stephenott/stix/sdo/objects/Campaign.kt | 40 + .../stix/sdo/objects/CourseOfAction.kt | 40 + .../stephenott/stix/sdo/objects/Grouping.kt | 38 + .../stephenott/stix/sdo/objects/Identity.kt | 42 + .../stephenott/stix/sdo/objects/Indicator.kt | 46 + .../stix/sdo/objects/Infrastructure.kt | 45 + .../stix/sdo/objects/IntrusionSet.kt | 48 + .../stephenott/stix/sdo/objects/Location.kt | 51 + .../stephenott/stix/sdo/objects/Malware.kt | 56 + .../stix/sdo/objects/MalwareAnalysis.kt | 58 + .../com/stephenott/stix/sdo/objects/Note.kt | 37 + .../stix/sdo/objects/ObservedData.kt | 37 + .../stephenott/stix/sdo/objects/Opinion.kt | 37 + .../com/stephenott/stix/sdo/objects/Report.kt | 41 + .../stix/sdo/objects/ThreatActor.kt | 55 + .../com/stephenott/stix/sdo/objects/Tool.kt | 42 + .../stix/sdo/objects/Vulnerability.kt | 33 + .../stix/serialization/JsonManager.kt | 13 + .../stix/type/ExternalReferences.kt | 16 + .../stephenott/stix/type/HashesDictionary.kt | 26 + .../com/stephenott/stix/type/LocationData.kt | 16 + .../stephenott/stix/type/OsExecutionEnvs.kt | 7 + .../com/stephenott/stix/type/Product.kt | 7 + .../com/stephenott/stix/type/StixBinary.kt | 3 + .../com/stephenott/stix/type/StixBoolean.kt | 29 + .../stephenott/stix/type/StixIdentifier.kt | 29 + .../com/stephenott/stix/type/StixInstant.kt | 54 + .../com/stephenott/stix/type/StixInteger.kt | 7 + .../com/stephenott/stix/type/StixLabels.kt | 9 + .../com/stephenott/stix/type/StixPattern.kt | 9 + .../stephenott/stix/type/StixSpecVersion.kt | 22 + .../stephenott/stix/type/StixStringList.kt | 7 + .../com/stephenott/stix/type/StixType.kt | 26 + .../com/stephenott/stix/type/StreetAddress.kt | 16 + .../stix/type/vocab/AdministrativeAreas.kt | 32 + .../stix/type/vocab/AttackMotivationOv.kt | 23 + .../stix/type/vocab/AttackResourceLevelOv.kt | 17 + .../com/stephenott/stix/type/vocab/Cities.kt | 32 + .../stephenott/stix/type/vocab/ClosedVocab.kt | 4 + .../stix/type/vocab/CourseOfActionTypeOv.kt | 17 + .../stix/type/vocab/GroupingContextOv.kt | 16 + .../stix/type/vocab/IdentityClassOv.kt | 21 + .../stix/type/vocab/IdentityRoles.kt | 32 + .../type/vocab/ImplementationLanguageOv.kt | 31 + .../stix/type/vocab/IndicatorTypeOv.kt | 27 + .../stix/type/vocab/IndustrySectorOv.kt | 30 + .../stix/type/vocab/InfrastructureTypeOv.kt | 28 + .../stix/type/vocab/KillChainPhases.kt | 21 + .../stix/type/vocab/MalwareAvResultOv.kt | 25 + .../stix/type/vocab/MalwareCapabilitiesOv.kt | 37 + .../stix/type/vocab/MalwareTypeOv.kt | 29 + .../stephenott/stix/type/vocab/OpenVocab.kt | 4 + .../stephenott/stix/type/vocab/OpinionEnum.kt | 19 + .../stix/type/vocab/PatternTypes.kt | 14 + .../type/vocab/ProcessorArchitectureOv.kt | 27 + .../stephenott/stix/type/vocab/RegionOv.kt | 24 + .../stix/type/vocab/ReportTypeOv.kt | 28 + .../stix/type/vocab/ThreatActorRoleOv.kt | 27 + .../type/vocab/ThreatActorSophisticationOv.kt | 23 + .../stix/type/vocab/ThreatActorTypeOv.kt | 28 + .../stephenott/stix/type/vocab/ToolTypeOv.kt | 24 + .../groovy/faker/StixMockDataGenerator.groovy | 2983 ----------------- .../ObservedDataGeneratorConfig.groovy | 87 - .../ObservedData_ArtifactCooConfig.groovy | 7 - ...ervedData_AutonomousSystemCooConfig.groovy | 7 - .../ObservedData_DirectoryCooConfig.groovy | 7 - .../ObservedData_DomainNameCooConfig.groovy | 7 - .../ObservedData_EmailAddressCooConfig.groovy | 7 - .../ObservedData_EmailMessageCooConfig.groovy | 7 - ...servedData_ExternalReferencesConfig.groovy | 7 - .../ObservedData_FileCooConfig.groovy | 7 - .../ObservedData_GranularMarkingConfig.groovy | 7 - .../ObservedData_Ipv4AddressCooConfig.groovy | 7 - .../ObservedData_Ipv6AddressCooConfig.groovy | 7 - .../ObservedData_MacAddressCooConfig.groovy | 7 - .../ObservedData_MutexCooConfig.groovy | 7 - ...bservedData_NetworkTrafficCooConfig.groovy | 7 - .../ObservedData_ObjectMarkingsConfig.groovy | 7 - .../ObservedData_ProcessCooConfig.groovy | 7 - .../ObservedData_SoftwareCooConfig.groovy | 7 - .../ObservedData_UrlCooConfig.groovy | 7 - .../ObservedData_UserAccountCooConfig.groovy | 7 - ...vedData_WindowsRegistryKeyCooConfig.groovy | 7 - ...servedData_X509CertificateCooConfig.groovy | 7 - .../groovy/stix/bundle/BundleGraphSpec.groovy | 69 - src/test/groovy/stix/bundle/BundleSpec.groovy | 125 - .../stix/custom/CustomObjectSpec.groovy | 30 - .../datamarkings/MarkingDefinitionSpec.groovy | 50 - .../groovy/stix/sdo/AttackPatternSpec.groovy | 50 - src/test/groovy/stix/sdo/CampaignSpec.groovy | 50 - .../groovy/stix/sdo/CourseOfActionSpec.groovy | 50 - src/test/groovy/stix/sdo/IdentitySpec.groovy | 50 - src/test/groovy/stix/sdo/IndicatorSpec.groovy | 50 - .../groovy/stix/sdo/IntrusionSetSpec.groovy | 50 - src/test/groovy/stix/sdo/MalwareSpec.groovy | 50 - .../groovy/stix/sdo/ObservedDataSpec.groovy | 50 - src/test/groovy/stix/sdo/ReportSpec.groovy | 50 - .../groovy/stix/sdo/ThreatActorSpec.groovy | 50 - src/test/groovy/stix/sdo/ToolSpec.groovy | 50 - .../groovy/stix/sdo/VulnerabilitySpec.groovy | 50 - .../groovy/stix/sro/RelationshipSpec.groovy | 50 - src/test/groovy/stix/sro/SightingSpec.groovy | 50 - .../stix/stixinstant/StixInstantSpec.groovy | 37 - .../AttackPatternValidationSpec.groovy | 95 - .../validation/BundleValidationSpec.groovy | 203 -- src/test/resources/stix/baseline/README.md | 7 - .../json/sdo/indicator/indicators.json | 20 - .../baseline/json/sdo/threatreport/apt1.json | 1101 ------ .../json/sdo/threatreport/poisonivy.json | 1810 ---------- .../stix/custom/custom_object_1.json | 16 - 321 files changed, 2202 insertions(+), 19853 deletions(-) delete mode 100755 .mvn/wrapper/MavenWrapperDownloader.java delete mode 100755 .mvn/wrapper/maven-wrapper.properties delete mode 100644 .travis.yml delete mode 100644 LICENSE delete mode 100644 README.md create mode 100644 build.gradle delete mode 100644 docs/BPMN/course_of_action_1.bpmn delete mode 100644 docs/BPMN/course_of_action_1.png delete mode 100644 docs/BPMN/cyber-observable-processing-1.bpmn delete mode 100644 docs/BPMN/cyber-observable-processing-1.png delete mode 100644 docs/BPMN/report_review.bpmn delete mode 100644 docs/BPMN/report_review.png delete mode 100644 docs/BPMN/sample_processes_1.bpmn delete mode 100644 docs/BPMN/sample_processes_1.png delete mode 100644 docs/BPMN/stix_data_translation.bpmn delete mode 100644 docs/BPMN/stix_data_translation.png delete mode 100644 docs/BPMN/taxii_collection_propagation.bpmn delete mode 100644 docs/BPMN/taxii_collection_propagation.png delete mode 100644 docs/Common_Code_Snippets.md delete mode 100644 docs/Diagrams/Generic-Data-Flow.png delete mode 100644 docs/images/stix-graph-example1.png delete mode 100644 docs/stix-java-notes.md create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat delete mode 100755 mvnw delete mode 100755 mvnw.cmd delete mode 100644 pom.xml create mode 100644 settings.gradle delete mode 100644 src/main/java/io/digitalstate/stix/bundle/BundleObject.java delete mode 100644 src/main/java/io/digitalstate/stix/bundle/BundleableObject.java delete mode 100644 src/main/java/io/digitalstate/stix/common/Stix.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixBoolean.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixCommonProperties.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixCustomObjectId.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixCustomObjectType.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixCustomProperties.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixInstant.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixLabels.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixModified.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixRevoked.java delete mode 100644 src/main/java/io/digitalstate/stix/common/StixSpecVersion.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/CyberObservableObject.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/CyberObservableObjectCommonProperties.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtension.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtensionCommonProperties.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/ArchiveFileExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/HttpRequestExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/IcmpExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/NetworkSocketExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/NtfsFileExtenstionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/PdfFileExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/RasterImageFileExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/TcpExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/UnixAccountExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/WindowsPeBinaryFileExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/WindowsProcessExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/extension/types/WindowsServiceExtensionExt.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldDeserializer.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldSerializer.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldDeserializer.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldSerializer.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/ArtifactCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/AutonomousSystemCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/DirectoryCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/DomainNameCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/EmailAddressCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/EmailMessageCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/FileCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/Ipv4AddressCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/Ipv6AddressCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/MacAddressCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/MutexCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/NetworkTrafficCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/ProcessCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/SoftwareCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/UrlCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/UserAccountCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/WindowsRegistryKeyCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/objects/X509CertificateCoo.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/types/MimePartTypeObj.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/types/NtfsAlternateDataStreamObj.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/types/WindowsPeOptionalHeaderObj.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/types/WindowsPeSectionObj.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/types/WindowsRegistryValueObj.java delete mode 100644 src/main/java/io/digitalstate/stix/coo/types/X509v3ExtensionsObj.java delete mode 100644 src/main/java/io/digitalstate/stix/custom/StixCustomObject.java delete mode 100644 src/main/java/io/digitalstate/stix/custom/objects/GenericCustomObject.java delete mode 100644 src/main/java/io/digitalstate/stix/datamarkings/GranularMarkingDm.java delete mode 100644 src/main/java/io/digitalstate/stix/datamarkings/MarkingDefinitionDm.java delete mode 100644 src/main/java/io/digitalstate/stix/datamarkings/StixMarkingObject.java delete mode 100644 src/main/java/io/digitalstate/stix/datamarkings/objects/StatementMarkingObject.java delete mode 100644 src/main/java/io/digitalstate/stix/datamarkings/objects/TlpMarkingObject.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/GraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/StixGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/bundle/BundleObjectGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/bundle/BundleableObjectGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/coo/CyberObservableGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/datamarkings/MarkingDefinitionGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/elements/Edge.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/elements/EdgeData.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/elements/GraphElement.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/elements/Node.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/elements/NodeData.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/sdo/DomainObjectGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/sdo/ObservedDataGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/sro/RelationshipSroGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/graph/sro/SightingSroGraphGenerator.java delete mode 100644 src/main/java/io/digitalstate/stix/helpers/StixCustomPropertiesConfig.java delete mode 100644 src/main/java/io/digitalstate/stix/helpers/StixDataFormats.java delete mode 100644 src/main/java/io/digitalstate/stix/helpers/StixSpecVersion.java delete mode 100644 src/main/java/io/digitalstate/stix/json/StixBooleanDeserializer.java delete mode 100644 src/main/java/io/digitalstate/stix/json/StixBooleanSerializer.java delete mode 100644 src/main/java/io/digitalstate/stix/json/StixInstantDeserializer.java delete mode 100644 src/main/java/io/digitalstate/stix/json/StixInstantSerializer.java delete mode 100644 src/main/java/io/digitalstate/stix/json/StixParserValidationException.java delete mode 100644 src/main/java/io/digitalstate/stix/json/StixParsers.java delete mode 100644 src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectConverter.java delete mode 100644 src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectSetConverter.java delete mode 100644 src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectConverter.java delete mode 100644 src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectOptionalConverter.java delete mode 100644 src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionConverter.java delete mode 100644 src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionSetConverter.java delete mode 100644 src/main/java/io/digitalstate/stix/redaction/Redactable.java delete mode 100644 src/main/java/io/digitalstate/stix/redaction/processors/BundleableObjectRedactionProcessor.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/DomainObject.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/AttackPatternSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/CampaignSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/CourseOfActionSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/IdentitySdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/IndicatorSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/IntrusionSetSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/MalwareSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/ObservedDataSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/ReportSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/ThreatActorSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/ToolSdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/objects/VulnerabilitySdo.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/types/ExternalReferenceType.java delete mode 100644 src/main/java/io/digitalstate/stix/sdo/types/KillChainPhaseType.java delete mode 100644 src/main/java/io/digitalstate/stix/sro/RelationshipObject.java delete mode 100644 src/main/java/io/digitalstate/stix/sro/objects/RelationshipSro.java delete mode 100644 src/main/java/io/digitalstate/stix/sro/objects/SightingSro.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/GenericValidation.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/SdoDefaultValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/businessrule/BusinessRule.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/businessrule/StixValidateBusinessRuleValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/AllowedParents.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/StixValidateParentCooValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/ValidateExtensions.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/StixValidateParentCooValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/ValidateReferences.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/DefaultTypeValue.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleObjectValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleableValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableExtensionValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/HashingVocab.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/StixHashingVocabValidatorString.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/MarkingDefinitionTypeLimit.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/StixMarkingDefinitionTypeLimitValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipLimit.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipTypeLimit.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipLimitValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipTypeLimitValidator.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/startswith/StartsWith.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/startswith/StixStartsWithValidatorString.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorCollection.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorOptionalString.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorString.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/contraints/vocab/Vocab.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/groups/DefaultValuesProcessor.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/groups/NoValidation.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/groups/ValidateIdOnly.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/sequences/SequenceDefault.java delete mode 100644 src/main/java/io/digitalstate/stix/validation/sequences/SequenceValidationIdOnly.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/StixVocabulary.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AccountTypes.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackMotivations.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackResourceLevels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/EncryptionAlgorithms.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/HashingAlgorithms.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IdentityClasses.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndicatorLabels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndustrySectors.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/MalwareLabels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketAddressFamilies.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketProtocolFamilies.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketTypes.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/RelationshipTypes.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ReportLabels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorLabels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorRoles.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorSophistication.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/TlpLevels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ToolLabels.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsPeBinaryTypes.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsRegistryValueDataTypes.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStartTypes.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStatuses.java delete mode 100644 src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceTypes.java create mode 100644 src/main/kotlin/com/stephenott/stix/CommonProperties.kt create mode 100644 src/main/kotlin/com/stephenott/stix/Main.kt create mode 100644 src/main/kotlin/com/stephenott/stix/StixBundle.kt create mode 100644 src/main/kotlin/com/stephenott/stix/StixContent.kt create mode 100644 src/main/kotlin/com/stephenott/stix/StixDataFormats.kt create mode 100644 src/main/kotlin/com/stephenott/stix/StixObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/ValidatorManager.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt create mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/ExternalReferences.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/LocationData.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/OsExecutionEnvs.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/Product.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixBinary.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixBoolean.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixInstant.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixInteger.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixLabels.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixPattern.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixStringList.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixType.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StreetAddress.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt delete mode 100644 src/test/groovy/faker/StixMockDataGenerator.groovy delete mode 100644 src/test/groovy/faker/configs/ObservedDataGeneratorConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ArtifactCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_AutonomousSystemCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DirectoryCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DomainNameCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailAddressCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailMessageCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ExternalReferencesConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_FileCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_GranularMarkingConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv4AddressCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv6AddressCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MacAddressCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MutexCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_NetworkTrafficCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ObjectMarkingsConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ProcessCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_SoftwareCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UrlCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UserAccountCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_WindowsRegistryKeyCooConfig.groovy delete mode 100644 src/test/groovy/faker/configs/sdo/observeddata/ObservedData_X509CertificateCooConfig.groovy delete mode 100644 src/test/groovy/stix/bundle/BundleGraphSpec.groovy delete mode 100644 src/test/groovy/stix/bundle/BundleSpec.groovy delete mode 100644 src/test/groovy/stix/custom/CustomObjectSpec.groovy delete mode 100644 src/test/groovy/stix/datamarkings/MarkingDefinitionSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/AttackPatternSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/CampaignSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/CourseOfActionSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/IdentitySpec.groovy delete mode 100644 src/test/groovy/stix/sdo/IndicatorSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/IntrusionSetSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/MalwareSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/ObservedDataSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/ReportSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/ThreatActorSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/ToolSpec.groovy delete mode 100644 src/test/groovy/stix/sdo/VulnerabilitySpec.groovy delete mode 100644 src/test/groovy/stix/sro/RelationshipSpec.groovy delete mode 100644 src/test/groovy/stix/sro/SightingSpec.groovy delete mode 100644 src/test/groovy/stix/stixinstant/StixInstantSpec.groovy delete mode 100644 src/test/groovy/stix/validation/AttackPatternValidationSpec.groovy delete mode 100644 src/test/groovy/stix/validation/BundleValidationSpec.groovy delete mode 100644 src/test/resources/stix/baseline/README.md delete mode 100644 src/test/resources/stix/baseline/json/sdo/indicator/indicators.json delete mode 100644 src/test/resources/stix/baseline/json/sdo/threatreport/apt1.json delete mode 100644 src/test/resources/stix/baseline/json/sdo/threatreport/poisonivy.json delete mode 100644 src/test/resources/stix/custom/custom_object_1.json diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100755 index fa4f7b4..0000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,110 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: : " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100755 index 00d32aa..0000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 68c7c6e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: java - -jdk: - - oraclejdk8 - - openjdk8 - -sudo: false -install: true - -cache: - directories: - - $HOME/.m2 \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 820eae4..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 https://github.com/StephenOTT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index eb83603..0000000 --- a/README.md +++ /dev/null @@ -1,683 +0,0 @@ -# STIX 2.x Java library - -[![Build Status](https://travis-ci.org/StephenOTT/STIX-Java.svg?branch=master)](https://travis-ci.org/StephenOTT/STIX-Java) - -The library aims to provide a flexible full implementation of [STIX 2.x](https://oasis-open.github.io/cti-documentation/resources#stix-20-specification). -This means that a default implementation is provided that meets the STIX JSON specification and the core objects -and properties are provided in such a way as you can easily override and extend any implementation detail to meet your -variation of the specification. - -De/Serilization is provided with Jackson. - -Current Spec Target: **2.0** - - -## Other Implementations - -### TAXII-springboot-bpmn - -Taxii server based on springboot and backed by bpmn automation. -This taxii server implements the this STIX-Java library. - -https://github.com/StephenOTT/TAXII-springboot-bpmn - -### stix2 (Gson based implementation) - -If you are looking for a gson based implementation, [CS-AWARE](https://cs-aware.eu) provides a [gson based implementation of the Stix2 library](https://github.com/cs-aware/stix2). - - -## Sub-Second Precision Support - -STIX-Java supports zero to 9 digit nanosecond precision with any date that is parsed by STIX-Java. -This means that while a date that was sourced/generated by the STIX-Java library will be at sub-second precision of 3 digits, -if you are parsing JSON with greater precision, or you supply a custom Instant with greater precision for a specific field, -STIX-Java will support this and store the extra precision. - -General rules to understand: - -1. By default, timestamps generated natively by the STIX-Java library will be be with 3 digits of sub-second precision (millisecond precision). -1. Sub-second precision from 0 to 9 digits (9 digits its nano second precision: `hh:mm:ss.999999999`) is supported. This means you can omit sub-seconds if you choose. -1. The StixInstant.class supports a `toString()` method that will generate a STIX Spec Date with the original precision preserved -1. Json parsing of JSON strings will support all of the above rules. - - ------ - -## Java Usage - -Example unit test showing usage. - -```groovy -import io.digitalstate.stix.bundle.Bundle -import io.digitalstate.stix.bundle.BundleObject -import io.digitalstate.stix.bundle.BundleableObject -import io.digitalstate.stix.common.StixParsers -import io.digitalstate.stix.sdo.objects.AttackPattern -import io.digitalstate.stix.sdo.objects.Malware -import io.digitalstate.stix.sro.objects.Relationship -import spock.lang.Specification - -import java.time.Instant - -class BundleSpec extends Specification { - - def "Basic 'uses' Relationship object and addition to bundle"(){ - when: "Create a Relationship with Attack Pattern and Malware" - - Relationship usesRel = Relationship.builder() - .relationshipType("uses") - .created(Instant.now()) - .sourceRef(AttackPattern.builder() - .name("Some Attack Pattern 1") - .build()) - .targetRef(Malware.builder() - .name("dog") - .addLabels("worm") - .build()) - .build() - - then: "print the JSON string version of the created relationship object" - println usesRel.toJsonString() - - then: "parse the string back into a relationship object" - BundleableObject parsedRelationship = StixParsers.parseObject(usesRel.toJsonString()) - assert parsedRelationship instanceof Relationship - - Relationship typedRelation = (Relationship)parsedRelationship - - and: "print the parsed relation" - println typedRelation - - then: "ensure the original JSON matches the new JSON" - assert usesRel.toJsonString() == typedRelation.toJsonString() - - then: "add the relationship into a bundle" - Bundle bundle = Bundle.builder() - .addObjects(usesRel) - .build() - - and: "print the bundle json" - println bundle.toJsonString() - - then: "parse json bundle back into object" - BundleObject parsedBundle = StixParsers.parseBundle(bundle.toJsonString()) - assert parsedBundle instanceof Bundle - Bundle typedBundle = (Bundle)parsedBundle - - then: "ensure original bundle and parsed bundles match in their json forms" - assert bundle.toJsonString() == typedBundle.toJsonString() - - } - - def "bundleable object parsing"(){ - when: "setup parser and object" - String attackPatternString = AttackPattern.builder() - .name("Some Attack Pattern 1") - .build().toJsonString() - - then: "can parse the json back into a attack Pattern" - BundleableObject parsedAttackPatternBo = StixParsers.parseObject(attackPatternString) - assert parsedAttackPatternBo instanceof AttackPattern - - AttackPattern parsedAttackPattern = (AttackPattern)StixParsers.parseObject(attackPatternString) - println parsedAttackPattern.toJsonString() - } -} -``` - - -## JSON - -1. All objects (Bundles, SDO, SRO, and Marking Definitions; anything that is a "bundleable object" + bundle) are able -to be individually converted into their json equivalent. - -1. All objects (Bundles, SDO, SRO, and Marking Definitions; anything that is a "bundleable object" + bundle) can be -individually parsed from Json into a object. - -1. Object references within out objects (for example a "object_markings" property), will create "dehydrated" objects -when parsing from json into objects. This means that the object will detect the "type" based on the Id value, -extract the type, and create a object of the specified type with the "hydrated" attribute marked as false. - - - -## STIX Marking Definitions and Granular Markings Data Redaction - -This library implements a redaction feature to support JSON redaction during serialization. -This feature allows the execution of Marking Definitions (`object_marking_refs`) and Granular Markings (`granular_marking_refs`). - -Current execution rules: - -1. Object Markings Refs are executed as a "Entire Object" rule. -1. Granular Markings are executed as "property value masks" and "property removal". -1. Properties that are "required" (Minimum properties needed to init the specific object) are Redacted using a mask. -1. Properties that are "optional" (Properties that are not required to init the specific object) are Redacted using property removal. -1. UNDER REVIEW: Nested objects cannot be redacted within the nested object. They must be redacted at the parent object level using the property in the parent that holds the child object. - -Marking Definitions and Granular Markings are enforced through a "Subject" security pattern: A Subject is defined at the -time of serialization of a Bundle or bundleable object. -A subject is characteristics about a subject that define the security rules. In STIX's most basic form a subject has a -list of "Object Markings" (TLP and Statements). This markings are used as the Subject's context to define what markings -the subject "has" at the time of serialization. - -When serialization occurs a the Subject's context -(a set of object markings) are passed into the serialization process. For each item in `object_marking_refs` and -`granular_marking_refs`, the Subject's context is used for evaluation. If the subject does not have all of the required -Object Markings, then redaction is actioned. - -`object_marking_refs` are executed first, and if the subject is denied, -then granular markings will be ignored, and the entire object will be redacted (removed and returned as `{}`). -In the case of a serialization of a Bundle, a redacted object would result in it be omitted from the -Bundle's `objects` array. - -If the subject passes all `object_marking_refs` validation, then `granular_marking_refs` are validated. -For each Granular Marking object, the Object Marking is validated, and if denied, the Granular Marking's Selectors -are actioned for redaction. Once all `granular_marking_refs` objects have been processed, then resulting object is returned. - - -Consider the following example: - -```groovy -Tlp tlp = Tlp.builder().tlp("red").build() -MarkingDefinition markingDefinition = MarkingDefinition.builder() - .definition(tlp) - .definitionType("tlp") - .build() - -GranularMarking granularMarking = GranularMarking.builder() - .markingRef(markingDefinition) - .addSelectors("granular_markings", "created_by_ref") - .addSelectors("created") - .build() - -AttackPattern attackPattern = AttackPattern.builder() - .name("some Attack Pattern") - .addGranularMarkings(granularMarking) - .createdByRef(Identity.builder() - .name("some Identity") - .identityClass("individual") - .build()) - .build() -``` - -The above serialized with a Subject context that only contains a TLP=White Object Marking would result in the following JSON: - -```json -{ - "type": "bundle", - "id": "bundle--3d6bdcdd-2137-4e97-a8a4-8020dd30bc8d", - "spec_version": "2.0", - "objects": [ - { - "type": "attack-pattern", - "id": "attack-pattern--0f4d3058-f4de-4743-ae4c-988645309d92", - "created_by_ref": "identity--__REDACTED__", - "created": "██REDACTED██", - "modified": "2018-12-19T20:49:06.403Z", - "revoked": false, - "name": "some Attack Pattern" - } - ] -} -``` - -The internal implementation is configured as follows: - -```java -class SomeClass { -//... - @JsonProperty("created_by_ref") @JsonInclude(value = NON_EMPTY, content = NON_EMPTY) - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = DomainObjectOptionalConverter.class) - @Redactable(useMask = true, redactionMask = "identity--__REDACTED__") - Optional getCreatedByRef(); - - @NotNull - @JsonProperty("created") - @Value.Default - @Redactable(useMask = true) - default Instant getCreated(){ - return Instant.now(); - } - - @NotNull - @JsonProperty("granular_markings") @JsonInclude(NON_EMPTY) - @Redactable - Set getGranularMarkings(); -//... -} -``` - -Notice the varying usage of the `@Redactable` annotation. - -If `object_marking_refs` were used and a bundle was generated with the Attack Pattern, the resulting bundle would contain: -```json -{ - "type": "bundle", - "id": "bundle--3d6bdcdd-2137-4e97-a8a4-8020dd30bc8d", - "spec_version": "2.0", - "objects": [] -} -``` - -`objects` is empty because the Attack Pattern would have been redacted at the object level. - - -Future Improvements: - -1. Customizable redactions passed in based on the toJsonString() method and the Subject's context -1. Config for Throwing errors when redactions cannot be processed -1. Config for Ignoring errors when redactions cannot be process - - -# STIX Graph Support / Network Graph - -This feature is currently a active work in progress, and is subject to change. - -This library provides the ability to generate cytoscape.js compliant json that can be imported into a cytoscape instance, and visualized. - -![stix-graph-example1](./docs/images/stix-graph-example1.png) - -The above image shows a web-app that consumes a STIX Bundle, passes it to the STIX-Java libary through a HTTP-request, -and the request returns the Cytoscape compliant json for Network graph rendering. - -## Example json output - -This library will consume a STIX Bundle, and convert it into Cytoscape.js nodes/edges json, such as: - -Output was generated from the [BundleGraphSpec](./src/test/groovy/stix/bundle/BundleGraphSpec.groovy) - -```json -[ - { - "data": { - "id": "ref-89093428-585b-4754-8a6d-8cb0e6ca0a60", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "7ac69a4d-a984-4c19-a54b-13e3dd1d80e8--60cddb5b-9ea2-4855-82b7-f54eca297214", - "label": "ipv4-addr", - "ref_type": "cyber_observable" - } - }, - { - "data": { - "id": "ref-8dca6278-645f-4737-9a56-f56c8cbb44a2", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "95a14320-0d63-4d97-9173-1994c2ed7b77--a3456ae7-cddb-45de-b366-d5b56b0c3ee0", - "label": "ipv4-addr", - "ref_type": "cyber_observable" - } - }, - { - "data": { - "id": "ref-a076819b-1e6e-4d3d-822c-96f215444f23", - "type": "ref", - "source": "sighting--16103a5c-22e9-46ec-91bc-0ae521c60206", - "target": "attack-pattern--4acfe66e-587f-42e4-8daa-4d9da233f7c9", - "label": "sighting-of", - "ref_type": "sighting_of_ref" - } - }, - { - "data": { - "id": "ref-2f53e5f1-9897-45cd-a1ca-12671419a021", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "1366d1a7-ffef-4155-af4e-18cef6517502--c427ea06-8615-49ed-9701-d9e7c04c7aea", - "label": "ipv4-addr", - "ref_type": "cyber_observable" - } - }, - { - "data": { - "id": "c0bedd31-6d9f-48b7-afe5-762c13da69a3--8bbe16e2-8128-4cde-973f-15ac7d32aaa2", - "type": "coo-domain-name", - "stix": { - "type": "domain-name", - "value": "http://google.com" - } - } - }, - { - "data": { - "id": "ref-3901a3de-a38a-4bea-b784-3a08caef4c5f", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "f8647fdf-9011-4f7b-a240-462a20c1a29d--5f13f5fd-19f8-4e04-a53a-fd03dd54a1a2", - "label": "ipv4-addr", - "ref_type": "cyber_observable" - } - }, - { - "data": { - "id": "attack-pattern--cd482a80-3e47-4eed-9542-bbcf69da9405", - "type": "attack-pattern", - "stix": { - "type": "attack-pattern", - "id": "attack-pattern--cd482a80-3e47-4eed-9542-bbcf69da9405", - "created": "2019-06-05T22:49:29.813Z", - "modified": "2019-06-05T22:49:29.813Z", - "name": "attk2" - } - } - }, - { - "data": { - "id": "sighting--16103a5c-22e9-46ec-91bc-0ae521c60206", - "type": "sighting", - "stix": { - "type": "sighting", - "id": "sighting--16103a5c-22e9-46ec-91bc-0ae521c60206", - "created": "2019-06-05T22:49:30.798Z", - "modified": "2019-06-05T22:49:30.798Z", - "first_seen": "2019-06-05T22:49:30.76Z", - "last_seen": "2019-06-05T22:49:30.788Z", - "count": 1, - "sighting_of_ref": "attack-pattern--4acfe66e-587f-42e4-8daa-4d9da233f7c9", - "observed_data_refs": [ - "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4" - ] - } - } - }, - { - "data": { - "id": "1366d1a7-ffef-4155-af4e-18cef6517502--c427ea06-8615-49ed-9701-d9e7c04c7aea", - "type": "coo-ipv4-addr", - "stix": { - "type": "ipv4-addr", - "value": "10.10.10.14" - } - } - }, - { - "data": { - "id": "ref-6b0818cf-9edf-4073-ade4-827df72de27a", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "c31c8cbf-7bd3-4865-8e89-7bbe1962d9af--34337862-99fd-46e0-a8c3-3fc7a774114e", - "label": "ipv4-addr", - "ref_type": "cyber_observable" - } - }, - { - "data": { - "id": "f8647fdf-9011-4f7b-a240-462a20c1a29d--5f13f5fd-19f8-4e04-a53a-fd03dd54a1a2", - "type": "coo-ipv4-addr", - "stix": { - "type": "ipv4-addr", - "value": "10.10.10.15" - } - } - }, - { - "data": { - "id": "attack-pattern--4acfe66e-587f-42e4-8daa-4d9da233f7c9", - "type": "attack-pattern", - "stix": { - "id": "attack-pattern--4acfe66e-587f-42e4-8daa-4d9da233f7c9", - "type": "attack-pattern", - "created": "2019-06-05T22:49:28.455Z", - "modified": "2019-06-05T22:49:28.455Z", - "name": "attk1" - } - } - }, - { - "data": { - "id": "relationship--70fca22d-4385-4194-a093-3a7cdb1a2a85", - "type": "relationship", - "source": "attack-pattern--4acfe66e-587f-42e4-8daa-4d9da233f7c9", - "target": "attack-pattern--cd482a80-3e47-4eed-9542-bbcf69da9405", - "stix": { - "type": "relationship", - "id": "relationship--70fca22d-4385-4194-a093-3a7cdb1a2a85", - "created": "2019-06-05T22:49:29.897Z", - "modified": "2019-06-05T22:49:29.897Z", - "relationship_type": "related-to", - "source_ref": "attack-pattern--4acfe66e-587f-42e4-8daa-4d9da233f7c9", - "target_ref": "attack-pattern--cd482a80-3e47-4eed-9542-bbcf69da9405" - }, - "label": "related-to", - "relationship_type": "related-to" - } - }, - { - "data": { - "id": "95a14320-0d63-4d97-9173-1994c2ed7b77--a3456ae7-cddb-45de-b366-d5b56b0c3ee0", - "type": "coo-ipv4-addr", - "stix": { - "type": "ipv4-addr", - "value": "10.10.10.11" - } - } - }, - { - "data": { - "id": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "type": "observed-data", - "stix": { - "id": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "type": "observed-data", - "created": "2019-06-05T22:49:30.479Z", - "modified": "2019-06-05T22:49:30.479Z", - "first_observed": "2019-06-05T22:49:30.47Z", - "last_observed": "2019-06-05T22:49:30.472Z", - "number_observed": 2, - "objects": { - "c0bedd31-6d9f-48b7-afe5-762c13da69a3": { - "type": "domain-name", - "value": "http://google.com" - }, - "7ac69a4d-a984-4c19-a54b-13e3dd1d80e8": { - "type": "ipv4-addr", - "value": "10.10.10.10" - }, - "95a14320-0d63-4d97-9173-1994c2ed7b77": { - "type": "ipv4-addr", - "value": "10.10.10.11" - }, - "c31c8cbf-7bd3-4865-8e89-7bbe1962d9af": { - "type": "ipv4-addr", - "value": "10.10.10.12" - }, - "59d133cc-d29c-4056-aeeb-6791842c0b75": { - "type": "ipv4-addr", - "value": "10.10.10.13" - }, - "1366d1a7-ffef-4155-af4e-18cef6517502": { - "type": "ipv4-addr", - "value": "10.10.10.14" - }, - "f8647fdf-9011-4f7b-a240-462a20c1a29d": { - "type": "ipv4-addr", - "value": "10.10.10.15" - } - } - } - } - }, - { - "data": { - "id": "59d133cc-d29c-4056-aeeb-6791842c0b75--c1efba8b-7ef4-4b12-a768-4b1d99d82eb7", - "type": "coo-ipv4-addr", - "stix": { - "type": "ipv4-addr", - "value": "10.10.10.13" - } - } - }, - { - "data": { - "id": "ref-029c8857-f9e0-49c6-bdaf-d3391acfbd61", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "59d133cc-d29c-4056-aeeb-6791842c0b75--c1efba8b-7ef4-4b12-a768-4b1d99d82eb7", - "label": "ipv4-addr", - "ref_type": "cyber_observable" - } - }, - { - "data": { - "id": "7ac69a4d-a984-4c19-a54b-13e3dd1d80e8--60cddb5b-9ea2-4855-82b7-f54eca297214", - "type": "coo-ipv4-addr", - "stix": { - "type": "ipv4-addr", - "value": "10.10.10.10" - } - } - }, - { - "data": { - "id": "c31c8cbf-7bd3-4865-8e89-7bbe1962d9af--34337862-99fd-46e0-a8c3-3fc7a774114e", - "type": "coo-ipv4-addr", - "stix": { - "type": "ipv4-addr", - "value": "10.10.10.12" - } - } - }, - { - "data": { - "id": "ref-a8cfe6f7-6d73-4e38-9199-0919fd6c3b74", - "type": "ref", - "source": "sighting--16103a5c-22e9-46ec-91bc-0ae521c60206", - "target": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "label": "observed-data", - "ref_type": "observed_data" - } - }, - { - "data": { - "id": "ref-0ef21e43-1451-4b0a-8f8e-866e01b91eb0", - "type": "ref", - "source": "observed-data--14ab5c4d-a696-483d-8792-f5686d61b2b4", - "target": "c0bedd31-6d9f-48b7-afe5-762c13da69a3--8bbe16e2-8128-4cde-973f-15ac7d32aaa2", - "label": "domain-name", - "ref_type": "cyber_observable" - } - } -] -``` - - - - -# Charon Data Flow - -![Charon data flow](./docs/Diagrams/Generic-Data-Flow.png) - -# Workflow / BPM / BPMN - -## Example Process Usage - -![course of action processing 1](./docs/BPMN/course_of_action_1.png) - -![taxii collection updates](./docs/BPMN/taxii_collection_propagation.png) - -![process example 1](./docs/BPMN/sample_processes_1.png) - -![Report Review Process 1](./docs/BPMN/report_review.png) - -![Translation Process](./docs/BPMN/stix_data_translation.png) - -![Cyber Observable Processing](./docs/BPMN/cyber-observable-processing-1.png) - ------ - -# Raw notes - -This project is a packaging of multiple components that will be split into individual projects at some point. - -1. A full [CTI STIX](https://oasis-open.github.io/cti-documentation/) (Structured Threat Information Expression Language) Java8 implementation. (`io.digitalstate.stix`). The goal is for flexibility and reuse by other Java projects. -1. Charon Application ("Charon Server"): - 1. A SpringBoot based instance of [Camunda BPM](https://docs.camunda.org/manual/7.9/) - 1. Possible: A [TAXII](https://oasis-open.github.io/cti-documentation/taxii/intro) server implemented in SpringBoot, tied to the Camunda BPM SpringBoot instance. - 1. A bridge of CTI STIX with Camunda BPM BPMN Java-Delegates and Scripting Usage. - 1. Possible: Mongo Spring Data Mapping from the STIX Java8 implementation to Mongo Documents. - 1. Possible: Additional REST APIs in addition to the TAXII API that provide missing functionality that is available from using Camunda. - 1. Monitoring of BPMN and STIX data usage with [Prometheus Camunda Plugin](https://github.com/StephenOTT/camunda-prometheus-process-engine-plugin) - - -**Overall Concept:** Enable organizations working with CTI and STIX to have the "application" -(Charon) easily adapt to the organizations internal or standardized security processes. -An organization should not have to change their security models for a "vendor". - -Further Notes: - -By Using STIX as a common language for CTI, automated and manual workflows can be created for multi-system use: -Where each CTI system can generate STIX data and the downstream workflows can process the CTI for whatever purpose -without the need to understand the upstream CTI system (whether it be new and shinny, old and legacy, or -some customized CTI DB that has little understanding outside of the specific domain-usage). - -Leverage CTI's Who, Why, Where, How, What data to process data into meaningful automation and iterate as needed with the Charon engine. -When you need manual intervention, with human eyes and hands, you can continue to leverage Charon to process those human tasks. - - - Process Information - - Detection - - Artifact storage and processing - - Analyze - - Respond - - - Share CTI across the CTI network with the common exchange format, and enrich the data with STIX Data Enrichment from downstream enrichment processes. - - "Automation Engine" with OSS. - - Initiate Translation Services through the automation: When translation of CTI is required, it can be packaged as a - STIX bundle and processed into a workflow for processing: automatically or with human intervention. - - -Share Workflows (Shareable Workflows) across organizations and within the organization. Allowing standardization regardless of the specific engine being used. - -Monitor and Track real-time events from your upstream incident response system, and process those events in STIX for -downstream CTI event processing by actors that may not have access or want access to the upstream incident response system. - -Integration ML and AI at any point within the automation. Do not tightly couple the ML and AI into the workflows: -This allows easy swapping of ML and AI technologies, products, and innovations as a underlying capability / -force multiplier without having to integration "a whole other system". - -Enable third-party organizations to deploy the same engine within your network, collect data, -and stream it upstream to central CTI processors. Enable the third-parties to implement their specific CTI processing -processes directly in the engine without the need to "conform to how the upstream system wants to do things". -Then when the upstream system receives the STIX CTI, it can be processed based on your knowledge of that third-party -systems processes: to which you can have transparency into with reviewing their "processes" defined in the engine. - -Implement organization HR conditions into the workflows: If someone does not response or review in X time, if someone is on holidays, etc - -Push out IOCs and Attack patterns to downstream consumers: Consumers can establish business rules that outline their -systems and networks, and as IOCs and APs are pushed, they can be evaluated automatically (and/or manually) -by the downstream consumer system. -Same can be applied to Abnormal Behaviour reporting or Pattern awareness. - -Data Interruption reporting: Reporting in standard formats for org, and having to consume into STIX. - -Use the same engine to automate CTI testing and python scripts. Engine can be wrapped up into a single JAR, including DB. -Can be used as a CLI tool as well. - -Measure the impacts of CTI and the various STIX data being received. - -Use the STIX stream as auditing trail capability: being able to collect STIX events and cross those -events based on aggregation, counts over period of time, etc. Then generate a Request for review based on the conditions. - -Very easy to tie in ML for Event data: Such as Kibana xPack reviewing the various types of -data and detecting new patterns such as counts and increases in periods or locations. - -Remove the problem of "Lack of Time to implement processes" (reported as 43% of the time). -Champions can create common processes to be easily and quickly adopted by downstream groups. - - - -Example Use Cases: -1. STIX Bureaucracy Management using Charon. -1. Security Approvals and Revocations for STIX data exposed through TAXII. -1. Managing TAXII "Hubs" / Central Databases and the review, adjustements, and approvals of submissions of data into the Charon / STIX DB. -1. Life-cycling STIX data through BPMN. -1. Managing TAXII / STIX data access requests, apply-and-removal of STIXX markings. -1. Multi-Department STIX Data Isolation with STIX data submission to UpStream STIX datasource. -1. Execute automated and manual workflows based on STIX data events (CRUD events in STIX data). -1. Concept of "Shareable Workflows": - 1. Ability to share common business and technical workflows across a single organization and across many organiztions. - 1. Ability to create Executable Workflows based on data events within STIX data. - \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..78d54d2 --- /dev/null +++ b/build.gradle @@ -0,0 +1,44 @@ +buildscript { + ext.kotlin_version = '1.3.50' + + repositories { + mavenCentral() + jcenter() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'java' +apply plugin: 'kotlin' +apply plugin: 'kotlin-kapt' + +apply plugin: 'application' + +sourceCompatibility = 1.8 + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} + +repositories { + mavenCentral() +} + + +test { + useJUnitPlatform() +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + implementation "org.jetbrains.kotlin:kotlin-reflect" + + testImplementation "org.jetbrains.kotlin:kotlin-test" + testImplementation "org.jetbrains.kotlin:kotlin-test-junit" +} \ No newline at end of file diff --git a/docs/BPMN/course_of_action_1.bpmn b/docs/BPMN/course_of_action_1.bpmn deleted file mode 100644 index be645d8..0000000 --- a/docs/BPMN/course_of_action_1.bpmn +++ /dev/null @@ -1,553 +0,0 @@ - - - - - SequenceFlow_0vgp55e - - - - - SequenceFlow_0vgp55e - SequenceFlow_0nbiopv - - - - - SequenceFlow_0nbiopv - SequenceFlow_0cphuti - - - DataStoreReference_1b7r9pa - Property_1xlm23y - - - DataStoreReference_1b7r9pa - - - - SequenceFlow_0cphuti - SequenceFlow_1nwrl1b - SequenceFlow_1kix2ig - - - - SequenceFlow_1nwrl1b - - - - SequenceFlow_1kix2ig - SequenceFlow_1x7glqv - - - SequenceFlow_0l4dcef - - - - SequenceFlow_02alnfr - - - - SequenceFlow_00rsegg - SequenceFlow_0tvr883 - - - SequenceFlow_0tvr883 - SequenceFlow_02alnfr - - - SequenceFlow_03vgqmo - SequenceFlow_00rsegg - SequenceFlow_1696x16 - SequenceFlow_1dorylw - SequenceFlow_1fg2lh4 - - - - - - SequenceFlow_1696x16 - SequenceFlow_0f2a997 - - - - SequenceFlow_1dorylw - SequenceFlow_1ghzfx7 - SequenceFlow_0ai75gz - - - SequenceFlow_0ai75gz - - - - SequenceFlow_0f2a997 - SequenceFlow_10ipd63 - SequenceFlow_1ghzfx7 - SequenceFlow_0jdo4py - - - - SequenceFlow_10ipd63 - - - - - SequenceFlow_0gmuasx - - - - SequenceFlow_1fg2lh4 - - - - - SequenceFlow_0l4dcef - SequenceFlow_03vgqmo - - - - SequenceFlow_0jdo4py - SequenceFlow_0gmuasx - - - Assigned based on Reviewer of CoA - - - - Custom business rules based on CoA requirements specific to cyber unit and network admins - - - - - - - SequenceFlow_1x7glqv - SequenceFlow_1hnq4c0 - - - - SequenceFlow_0tt02di - - - - - SequenceFlow_0tt02di - SequenceFlow_0r2zpdf - - - SequenceFlow_16jbxgr - SequenceFlow_16qnt0p - - - - - SequenceFlow_0r2zpdf - SequenceFlow_16jbxgr - - DataStoreReference_1qa6l9t - - - - - SequenceFlow_16qnt0p - - - - - SequenceFlow_1hnq4c0 - - - - - Notify all relevant parties that CoA was completed - - - - Relevant Parties must confirm review of CoA status Update. (Email link, login and review and press accept, etc) - - - - CoA log is updated with CoA Status Review Confirmations as they are received - - - - This will execute multiple times as the "confirmations" come in as parallel activity - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/BPMN/course_of_action_1.png b/docs/BPMN/course_of_action_1.png deleted file mode 100644 index 648bd78e4bf037296f6f4543b05bb6bed7f69e0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212698 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

    u-I!sAHX1v%Cp?!WW{> z%9?-Y*v;v@K{}GjmU$xuwGja(OVdVX=8Rn9+<3!6`fSoQp>@>ONN>zqL)*Fd@EBwVUCqc)a}BA4+pVzGE1g2sTuCu z4E!*Huq9~9rVi;|l(E)WDT$a_H+YaHYkc_A6oZQv=rr^2pn>x__zTfHfxG9E^Kjy- z7rom~4PXRf$CYH}xUVW#bMp`_h8%_S9^R;1;6vtWe9pX)pO!I|z+T7dJtNvL+r@vA zWFDDQswwPQp^sn9DN~Y7sa#rOu&2o`;l>rCchASL&;Crn|s(R>je9(R%B@X283I!!}-Q8sdXlWoj}UY9K9y<0XoNx7b5#2v-`9dJ?n`1$tB@Lpe3 z3=(PJ1S#EUg~?u%{x-FY97A8wU<=#iTLj1b-^UVFq(rQvlxdSWE=Cf8tc5c3ujP_q zCW*u!!o2H*1Axa^b%r@Y;-qOUnAKIglwPozVQ$9lqPPr0zN2g=t@c(8PH^Bd-lS2p zR7gUf)9kUeaNV0X^v5Hx(yk7qRHnfiq^B{;-%BG45V%A*=3UR&V{mc%Uapj?a?h?u z*?z;Ao$^s3SRg?^ZsItQCay*;N$b32l8e!sUnXEQMW`A2Ip#q#LEhz|ky%fQL9vqA z7$iQBZs1ftrN6m6p?6niWXFXgE5%zfn&bVlo93=Jb40%(OEB z@O(IVN~t9y{9}YpBVK^rW?p*Z>z<-j?R}ZI?dS+unc^4+*w6A`AaJFKwlh3WON!4| zZ*}oiNnH$HsbKYj3ihsX*+4s3UsX7Us<+G#caextU*?fF@1LzRh5^8P-4x}g=DQcv4ar+)91Hj(09(;yMic0dM0v~Zd~m*v{_fm%Pe|og7qvR8GTN5 z^F(^Yj1LhvF-3U1nTwk1T$`GV?mfGYm|yi1WaR1rMY+O zC+ZJU%}25Hl7;A)g!t8nrO!O?z*{c9t*F_jboXt`_(jc2rlBh>rm4ZFdM?3k%X)v+ zPYKo>1{*G%Fdso9|A&~60#wqNbtk#2*F7xqnamuB+VKH``HGGThZyc@j z;F~O`FGJV69UGtuS}Yk514eK0u_xXwNry$B3rp%du^m-Y;7vWiyQ;d>u23J`BdlB!u$H9lL$JTf7?1aF9QsA7-iXTFp&?S41eM zeZrdK>|v7ktPpb7Io3DsZ(>WD?{1JZFQ;VP*fn3AVzL0g&nSw%S>Ap22j<>std$eB)0!r8d|N3Jbn}OW*Oxhy=GmO7 z61DJopGToC#)H~)#MDaNsh&;-%+E{!^i1Y$Z`IXe1Y%m((!DSARK5kZf-${f{mdXs zjVsp$UQ;zcmS#4*O$xX!klJ(R`QYyYp7C?~b)WKzoV8MqA1hYI8ccTy#|bk2 z?PFH4LhI;yOn3005ia*&#OJ=~(8qrX%BP|^v#IF>b9>@p@x5oE|>ud&^_=Dn$>XR+S45E3D>%c^om3mAP3;mAzI!FoDS zpKJSVw|2NAJV2NAC3+i0!nA|*2=mw%@6H5!Me2K*84hgwRoi^y^^FyuceF(f_D}anEA7=WCo1TiKpQ*jftG zp?g#GR7%x`y0*?V1baNIJ_{5JBM1tHCUKt0e|a%~B@XHD>SUq-k1|olsN=wKwMQg3 z1Z^xoBFKH-YL-kOm5@=e!%dI{oLT%fbK(^`L+v?I_Y_RXc0SgbJu*D#hD7=29yq+R z`Odxlk3s}{)JHniJ!e$<=j?jS*PeagxqMp1yS-5n3BB6xUf`6phRh00L^t^JK<$sV z3sIb8HdzmP#+4ZCclzES&JcUTDPFYuNnhil9Fxr5kG&li7ev)1CH>3c|0%4#!uD?t zW%F2|FPf}>^XG&-q)TmjFMAPv<1@a>Vt$mtqKu;-mcPcsFTlWZ1&f2)&Q66=hbwT% z?*&@#`|ZI+Iq?IVxoK*HElUP*MJ+K`L)RUt#ENMS9zjm^FmM6Pr3zQHV~085x!yy; zV~n0}D|l0P(UquzG;Tk(JsA|=xMB@egov$praJ}rAKs6lPM+EGT3E%o$dmT-En8h6 z>>YsnubXmGe0kA0!BVHgOe$tdCaMl~y>bInxv^Kj2h>|HeVH_t?%jf@_PnIgZqL|4 zSWE8TN`Ke zR;=3Vkv7Smj9?V4>8*CwBSSYMJU?IR!DBH-3&&SoH6_7!qDlvSq&91Kpg9g3>Bity zaHe{|U^Ygt$sRenUlF7uj`uDR-ds0?&b1h`IrDI6Z0@t2hiB|Tg_}&yuIXBi22y4@ z4?bVEQ<*BTEGvG_W*0eExg+kT!JNtV6^VJ{CTHwjYVLb-x@W^Flj=pryLMuRql8h} zQkxN!86{dvm)E$Qc@wkwA!*|bqBLm%W#6?5$J`mHu}=eRZ= zk`gJ}9jvlX>#ykYJfD!ULOWq=&I9ZZ-$<8;lPcdsQh;-D`IV(xv4?Vm%MXAISm`gP zejJ7IxVA_-;mq+$w8Z>Zx0a-W8}-M-rDssQGAs$!oA* zt-oQ4dLtH2quJ^%_lno&EJ^-#fVkNBk3a>{=iQ|meJx2dh%(CeZ6G%d<&)_bvr#>J1?T)W~qu@sBlI|)+V#V`8;Sur5C>hiFQ}P zbm!yP-OnG+{57a@Y`^hZ0heNKh@A0cln}yuZ7n@mNO;w@i)CA5_rIGjPTSs_b~(QO6SvgQ7w<{ zgg?D`OxMe99^WVKke(Mf$LrZpO1XLG%}?E2Bk(IZSUaU?rh$RX_IcxkvzGQOwR`I! z${}ydy4eQ?x66paNs%9}6NHn6&+8^n&~PYt(;FE1r2JJcpF)s8KCFW803G{lV(THkJvQMPhZ3W*PilrqE;Eu$@Mt_@ z7O7Ewote!XxAT}nFw2thc|`?RF=m1lJXm29Nd|cOHv?^IjSv@N>hsWom%7-0oqT|l zbAT~^JWyKc=X$i{^c23UKajrFzNdejwrH33K1Y|LvB&@G)zCR{3JVDwuVFi%3q?3n z1j@G-?Ng7xY4h-g<}Ks6*xXx|Pf^S5U)7qtm7!qrSX+2{w|2tO8(y-P8oiGX3#ES9(c>LCi@9KFydjPXUNeme|gD6rE6?d-T(x(cf;R&#Xyy3C!MYU{y|z z+rg}Ox|Y&x)+dRs=Xc{qxd59~zC8+0+johBO2hGU;hsfTV}&InI{}-c;D`g?P8pZz z;vE;8Eqf|<)&Ej(o$8N{hkpFVA0%BgLMoqcdY^_$iZTWQ@moK_ z2{anATK9X4ZdEZppDR+Z{Za3{?;jyd1<$@=?IU(pi`C5lW7p>SCc>|hYbCOPYSu2I2RpaMGQXp9 z|9Q1N;tGRG^)d)5_fEkw^Q~n_W_-D5yt1CbBhhhTH&&{soPNbkpg$6ZC6g!Y+n45H+V>QyfPvHY zZ)P8le?uU`^Hm+zrI-^S?j4;FMV9coNvn))#$JU z1^KT^9l)~&9r(gzWLTY4z2H^JFSL_tX|pIS3jhxIg+DVeMN$`=m%JO#5&tn z14(#%Fqkhn6NieAB?{$Riqx>CbmK~mxZ$fch*v*42c7K|gSI<3gB^v;%X%>?@T$*zPO6d> z;KZan=0Zgj+!utY%2I z4in71AVz^VqX#Ue!7V(-Pc;1xHyfleJBH?qy523B9*#Ml*>?_U{vY<hq8L_m?0kQNM3kPxJ#Q949Q`oFeE zIGjiSzu)`i{q}x3=h5fP%)Rfu*Iw&d*L5xaS&{lfFaFm{DCiuN7W-|FY-U=96oaU! z1@9*G#l{@apThQ?Gmy!d2dbcJv8c@$=!7D${(^l^I}%Ewu3I$My~hhzPd>t0)Z8*5 zERIBdf0I%FT!$1Q>_t5@tV`MW%&P!91an(++rti7_bh35o>nqvZ9ny@p3OEQ#e^J; zJ%J;H$tnnH4$ceO0a)MaLpz{Nq0VjFMvYCA#2)gE@B2J!T)vtAC8g?oM!EdZTpbVI z_UG%k9Y4W)YgC0|b;HF#m_SJrP=rb8VPu9I?4na^lyuz*(=Q|`(vx*sh1OtgY73Fy^KVs|F=rP!?*t7N{gC8Y5TdqxH+ z_r(xo-*$1y3ds&@KYr>73KA(aaw%^0cwL|eLp*Ocne3Htustsjr(R`)Y ze!FIDa63q>C%y@1^cQ76^0;NJVp1j`lo?d0L1not8ea}#CMs}RT3DB>w>8xI^a;Am zWK8rcTCFJtUO~!kEE_GAOwIEVT0zRQ%?VLJDdA$j2FpDfrZ|3QX=keCep;*~lly=t z^efs1wL< z*h@0Y;XhE`;#8=vNRTpCRVviF++zyWuQqbN!u;%D$``m_dcE&v3V0G>ydjE?wqQUs zCI@aelMRjgVV)P^?8g(Av0&HKHMCvLX#65zc}T+ISlDl78p^v-C2ZE--)Hb2%MJ^ zK{FkFXSwc{j-`wa!VH`XH(+F?VQ<7f^Q)-7WJ~68UV~+OgP!qorl8_JVA~~O+4#Q) zHN3>GXYE~st?4h4)0Y})dB@W84QhA6>(T53p{V%*&_{Woc-J*~4*$a|ZRg+H5C=r} zD;_IaRsMI>f)jv@w4y}GwkCjENFOK3kWiLF_Rin6AuAZ)NyB=fqA&<=rW(naTU!g~%)7i(Ot) z!OyN6^}vutV$djsIpDTdDVPD9HvOKuq(GHPG3ZFoELt zny+y(`yF8Hv!lh(;g#XFhvH2iT(G*-uSCwvb>BR22|EEbez>9M*jpG6e|#I5&A4b) zdw^h9KM%4nG{fB)K6%D_n@GWgCRFMMk=@saFTH@yBa=;;V%R%x;NH!7%Q+T;scSP5 zA|QS{@Ti)f#zzj!6+m9)47YO=@QC}P5&O)h<{Y8>llSds+DYdDsCr%ilB~EV!Ut1+ z6b_}A+rW67EU3KaoZ20#WVC@FDz^w9d>)GJh+!y%WJR&|R?mOb&H-Y5*yHP+qrY03 zOKpBLA@;z3^o)ee`vNq>zdH(S!GMz*+i-;!m2#BO*mP@$Au@l)3H zzd{JS7~0BlO<(nXM;(eHg8`UE7%9_-={Y36j9EjV-x$lNNb;|iAox83>*_h;N!k;H zc<-gf{;gYkZ_&f;@oe)`a_IGNzt)qET2!f!A^FLitS^d!t)OZjk_4 zo3U)$of}P!eCu7p$ZMNV1_4JmjlCIlDMfV?WxN2fw}B3ITuFWjt)ORBlw3Pw1sL>= zeZO^m5#bjF;4&lzChtt#3F66S?$G|a3?O!RCAFmg^)@Ey?^oDEPrU-LuRIA5JlV5O zvej-ZYlwU08}3=%pkoC8k)hJ+_S_!u)@g0dzAzd!2AGE{Pa%S_fkEv7d)e#ZH4c}S zeO2~P`#4)!f~xn$Lu)uPU8{js!fHNUhsv;cm*1g-M})VW0PBInTA|&5UuD5ba=Bqb zn);m2e}&t{Z%sx!AfR96te-MssOruT8seVpbZko<@+H#&tz*)yMw7mI39$EUSgNMg z)p(>^&lU*j3(^<&f(f2nl)S6tF9@Q$zTH%d-B)p%RI(M`D~a)A;z%ZRUYinA3I|L# zhx@n!dI)dNvfi4^4wj|&JL`vu36o^L z$tBmuCZyY;QSZMn`KW{j41mXR@~ACpT@3A0JD{}f%ly0qXqoiMy+CO9b~hS*e<2mG zvcb2K8q3zlu#~#)O5o!X5B`e5mYqj#es6GGY{{}p#oJCz8nI?%KHZ$`tq~#lc}YLk z8j7WirylY^ft`Qvw%{U|V*eD+)oSsqat#)nFn8tMvu+?reD;CE9-y~?Jo5@+r8jN$ zk)%qXPmwrjZ&>=E8*Y$$4z7k8jz+K_t|2kzl!G88nnEI%g_^pARsmfs&w)aX&6wqM_m|N`kQ!V#>F309*na0_{2QqSKP2K);&o0Ly#I

    `#2fn{t4x`Ea|nDT%#2gN6%Iqjd3Fq^!(1S7JRnn7V@J|JA;p8*5>@F z3&3e)f09W{;2-hdk$Zgtijp6@m||>~oVbO(D9NJw+k{+!+Ejo`j_=PDsK8-60ePOV zu8vFV?}sIa`Z@Rlz0x2t)*ArXf>4g@KcmBZ7WG-zv+-`#u}Q_Z@*rrr*8a6Kpn~qH zc+AA%cF)?7{qznvGDfg3QQ?Z1%-fVeERVs)KxQdXEL*}h{7{_RsZ;`0075}t)1tP! zok)Q6>wO2k2d*GE`ei^apH&Vy#K`@yW1n_^Vt;S=gI z@pI>Ebtep2MM2ahilwsJLM1bnl1%f$vTEv;AN9@#BabBG+l5k&5$-asR|BbXD^n++ zW+z(mIBC+8{34v2-{)=h#rX$E6Z?2_1v-Knyl_%co%6G8C)jCi`669o)=mT1F*;sp z^gl`L49o!x^4>GGOoXQWBtntD7fh&*dqSH*D{ucLIseD6tCUIV`7xuGWFGx@VNvo~ zSWlovWdlWO&J#82P)G8DRB~oh=4JPEG1i__23m<1lM;1j465>T>KT1NRW(Hj8x{}?P! zyrUoGZ|K~#t9MRy`bIE&^`a@m8b94l7)CULArGz(FCjf_XNyDCWQ?m#`GOTe#^CgX ztPZ2MAL~iwDq&q*2~P5A?yiK3$yGC;$xZ?Wm;~m>#X!*e!rn=RmiudHr+u44e&typ zzOEfa+-lI(|J*!MIA>>ENznK-D77QSUI_wHgP|8E%THwI zF!~sGBvi#IyjnN&J)UE||$r>NzhJeK6^MJ@FBd`Z9>&E zP9FyhVxNfC1;}e(Vj`?9T>HP}g>o?56BfrVZC|byxiQqhd{K`-gPE`}v+pyY?1;oV z1M0a^NV!<;Ub(;ztupO>s(z(?celBh=qbHPJ@WE8MUelz-RG+Yt?MRyle3_;`9Y*; z{*ve?0b-1NY;&3QFd|`izi40;BJ$V+9ego1y*?bQ*1H$P1%sbmJPb|bFwi=bjQf>; z1Y`Mlzs!-G#XbfLnJr1d!vmGo;C8@th@bZ&^wvD`E@fIYjJ}>cKLmqmCdighe0y;? zWvX5Pr+W5@iIbC1wnwGdABpFa8^6;g73Ugm*nX&HR1qWNsPUAW!|?JhSL?vcHzcvv z$AZe%Hx@2yn&98+VYCi5MBh`=)#iToSbI9x<*~*)K0Peqt+k(vqujY#msvxX)^Bfn zk-`!z7DqJFds&5$3jb1NeAml1c5F>&OIm4-)@*!)^P?!0C_^W%>IKf$m;WfIRy-eYAbK*LmZ(jrd-&N(JpE`?VBgn-I@Nt3kl5y9U1hInQ6!Qd=hkc$k z&LcA*7XIj%DX3$5Qe);>s_O@VcwI0fk`a%Ne>9o%sksmI?bCyrV<$BZYdqZlmazaU zknl6n`oum>rPB*Z^46E%Z7HDnae%A8zZt zmL)C zgnm5}+BdE5!DTZ%m*pIuzf1vN&dyED;qrzSy6?pZ+dpIX7q7s^?YA@%^E^X;(J@piHTWg zv-U(^?Hjvc6xUZpsk$26T$8>5(>meyS(9q^!j%#^u=E(KDj?I<*im6*MCSgtZoY!= z0TY3;@EjA~<+~6c3DJ?zw}!&!qHuIg0o(d5icATqr0eHv%tDQBw6d(JmK`{VyKqC~ z3{IXFe#(T5WH8oC^7i5eB^hjjo4V6+=dvp4gvo5Z*u)^zk+g;cE}I`fXp91eaiY$9 zD2}`cMQef5%jPFXvt3OwPCKi;Vm*|KsayBxR}4!pH4yvV<#+CwM5^PQIQFGO`Alca+RlNzJ{-F@sj@)Lx?k zx=*Ix&2jFj*!A@g6ObkgC4Q9cWOR7++#{s~19?l~0)R4{BAS*=V#`;`GRlN2<3t(U zfe-_R<){K9--U?LYH!P3HQLW}Eu837^V3^6#v-P?`*hz;pw7LEH#5%XbASD)Htp?e zi$Fi~ZyMYw)XxGxfgm0=_eK3EYUNaQ&rjDzHELU#fF7*Sf>eKAXP>ThYGQ;tw6~sv zQx&KD%kL$O3kBG1p1|r8bNgW-+F||i2T))h>A4P0`fQLaliKb(S?g2*Sj1@{h^W5x zNQ#L3G7}TQP-#*hSVUU$ z!I%cnIq6itgu?Nx|MpAQ1a49W>nzph_N4FyyoGDD$lPM&epyNk^Pb@ z3SJ2JYj-KN%eiBX!X)v&qe5PC{Q0paB*{%ywR4|q6x+9TW?tMiD_gFSO|md>vybw( z*nDx_AFGS!*1lpwHBGx*b`WpU%BSKLw54XDSp1gP7&rawBDruQXO-ke5{Kt;}GN`(OlaCO{h7*6cEvlv6W(#*n$@;4{T0qNLJ+^xNOz$o)~ z|D)xE=XL-pl0~J#-c8F3&#gg%l2t0vB%aUnR9X2~HB1?l^fS7X4vhfpy43&B!k<#c zms3N~+nSb3Wp&OMKv4HFL*XF}so{e~n}t~^IrUltdbf>~Wo^tnilTAd+m#%oa=WV^H_-k8W;C=t${MZaip-)#eY1cB_xOa)p|!xy}#IaXiD% zPC##p*C_jL%yy6cwg|$Qj-|`sICqdVgoXDnzA7*O4gL)AT%D=aW=wgn` z^grL1$i0Ocvo*@7;Ycc_?7~9>ji3tZXOVjWX-#_i(c6ZHs4;4t?9yoJ|MA`9hD=Am zg-3KvuC-LMd+Z%f{I;&{P&9BYC4jSxMI%$zu4f6p#t{{K?kD7_Nx(nXzp2s6Vs@L| zS##`AcszHvYLBOH8b2*%G{EYoGDhEN`%6HXY+=Aqp|xYcT@cIkuy|#rEb^OB>)Kb?+)OSa3u4)=0v;%a6^druydM1KT7wLq zVMCSMGQ7XcyyJflh{5q6Lk`2g*M&HQ4zyqJcgIw$Xf%;agg=9m$$K!MD@L%Ae=nuv zjFoM&eSfCa7l&Ia8q$g8q)5#t7elsW8y$;U%US^p9*;aAD;+SC$L{Bv^;F8E?sJGA z!OeybO7YlqhSsEBxt!wlScT2X+s!vf^T?Ihb2tEhB7|X-_Bq7ca_JNY0^ATnSkH(} z_%3K^$eclaT$ceD97+~tx&)njkHhOQ;w&aj0GkYh#J9o?oPY}3aulM26;qCGaU23z zd`3LuT+VGU4r*HL^H0mqO)B?ILnSXKO!esJtFkq5F{T-tpGI}d z*gtPD_o0$JOV8xnZ5`G3iLPdRK;VKOeVr1)+uymrIi4F+*L6_tt?lz&UYy|l)ETiZ zX8NpT?_OuwiKb6KXa|TfRVNw+b&`joq*%!c>?9mpbzx0aa*1^F#GK8L2$5y{L7&4I zFgF^h)^0ZmF?4=t2rWQPwooweVlSLr@aTDH*$jz+TKUi=v)oE={ua38?@bvWhRxs%N&BN*`@ThD zUn2bH48{!$G2Ms}6(Ws1#oF})k{!Sz`||MYs48@Dn1AdUVjG=36Hk1HK+D4|7k$2D z{?6Q8YBz1eoy`RmJm42PnX$7f`Ef6skoauhV>K!o;hBxt_R;+Dz>g0Plp^2K&ScxHrgw;z z^W|K8ME(Kaz-b`MxrI;x5E0Z+I}7@|eZtw%@ne|>uUzjdpn3_NsuZ=(_f&wkbzj3jPrt1gN*^nTJ23U zr?EUWlY5l+f|JQ;42Ao(L~pn`>WEFkgt-elP6zMNsidLP+pWa0sm_hS!TOgQ2zj%7 z8mwws4iqG62E`jc1i1$S>a5n1^EU*y2hyb3*1xt_aOIxQqbRA~GS(Qry5rnsGNxJQ ztpuyiW8t>(T@58lDC@zAQ>YL#PN#~)yBF$n?uqQE6)=A)Q8>}(6nxS@DS1s`i zE^G#n`vO9gydW@eO4EnQ;{uFsH(Q>2It?v|cut1=7{7zhX<;&V0u**uP24TmO2aw~1)KqINAr~<(z)U6HUNQ{AcDWBM z-Bl48OQwZ`^-DT(**H;(MAjS-xriD`?Sff|>ZYHc{DcLO8n^FsqOMMZHBkQo`Wesn zG{So*b~GRsx#AP7jLf%1Kcl&{#xXoKgILLgIdbI~;F~j-w&W%&HVA}41<(XI^rhg8 zfWxGKWaFiXSwvkkuTQ6M9(~yz_yRRPnRXtP=yx*op+4j&RQB>VJ&pWwocpJk2Ce1|Z}%el!+_{n2CL_8YxJXW9~K4a>CeoD^;W;CFPo1oh;Ajx)9+vHog1uwoc* z#&z|9)41tT4yTi(_OOw|u^zIYycQl_S_CQ-#50w335-_?z5PKJVMu>ch)JV!P;w#< z^75JKc219>PC^LmzR$lBQT6X|#r(WW1KN@Oex`K-_;Thqg`C0K^z9+oHW(h0oL9*v zVqgWWgZ*Ad&oj_)=(IloJym9@jo0fDL(&J3I2hVm?f(3Z?%OjZeB|k9O9SOMm z6_s>?^5@p+NfNv#Uc6(?>(7Vyl1HXy8pFEKVl};Q#N`FGLLHO`{m;XBd`?`00@ke7 z(5)QO@Sg_vw9!2>ozJPCVuQ|?CTovFDefZfltE;4O=3^sE1A5X)%-5e>ght=al^U3 z0DD$5$k5&4A_$H;>h~EWA2lC-Hx(!QtAGrDUqd;!cjq_u@MyjYfapYMp-Ep<0Juin zll6|qg50<-0ty;m_Bc{50cJ)pc75vq^*uK=f6k(JzB9xYseqIYks4iS?24)Vh>lAO;3-w;i#PfLchVV1 ziALF44jlAXkX);4K>f>UP!EOEthVr#i9-=69!EsYP*HEu10#6Hfp1Y z^)NIday&MRS_lYMUU?&r!N0QUOU|BHN>}XlmwNt28rKv1o;f63bgw^c*#z`eYjFO4 z@21fg4Be0Lq4szW*}GmcZzo+L-Q}RF6EKG+v;=0X%l%f)Evcf0rx%OC?I{W9cgLoY zsLkaNMV}znW@^B^TxtK~B#*a;xjaMz%Ru{Oa4vv_UpbXq4$tF=%S(}G7|_GzJ$=PB zIyoP{Cs%{N<>{^kXhn_`9cKXyW6HF%>6FH_C(GHPrinJiaZ3CE>-m~=OjG`u z5+_04*fl}2-G5NTxr4SqB-?M!`EQ3aNR(u;mhp$vryY#~&H}zvG<08S*PUb;Z7*74 zE9YY=-g|tW2i*&#B5>W37<`eab%mM+8wT3^8U<=SI)Zg|qFS!r2Wzh>AY;32bI&~9 zu%8~>fwNGX%otc)p!5DYJ0ux5b*kJc=}?NvP07u^uS(RVhAoihboxB(gt;LgD`}J$ z+!>isE?2VbIQGzgT{z2C-<2}+pSQGz`W)J`QvxPyR`%>7CVihSF4?G+nSS%vB_ri` zk^Lf+ci{S|2DGFXJPKDsgNJlM1M(RRYR*=Kc9xqmzV>29%p zhdJDZSm_QSFPib7C(Pw%{-CDeqmc&e`t5%yK2KT8dQ1pGye2`p9)hiylYX=(f68@T z+j&XE9h&`x2g%o$P=oym2dve>B`+jR0gnXm+dr_BUGJ*X(Sg#IS0phE2KpLyU_v(a zk4TUk^vw>{kB>4L_u!{b1G`8@nPP{5oA$@_)hqG?XFHdoP(&(x*(YHzi)cOYYUbaBmHs~H0z0m)-)!YZ>vYgWB@%BhKuI<(Y6BnLL6xn(``8sb zf#aIJ|ICMcr6F~>Dixm7Lb zHv=<6mX`6Zr;8R|ZNC){Q>iD*roG25Fpcj8qg(0Q34vSw+}Ky}$G|L;xasUe>)%~Y z(({e{0mbC!5A)aMV-5fI0Isyr?+zTl0pmMn zgnR)s>-|T$U|xoy{aaQ<7EGEdgd7SH?doBA7ZO2%#g&IPCcjbaYlfj9{op}VYW z<8BG-roIA~y7RNoQD{0Qr(?GkFq2rvj__qu&DD)Z?m;L8Ef!cDNXx0vZ*oMfxA1#G z3ovwPoUok#IaUwN1%0p%L*{a)B4PgW@&!>Hiu(10f|qgV6P7$G#17DtoF-u zv9FPG+Zjj?Ux0dMh5!oGQ>~O9S7nqvCTB{f*=;SWdB~`8dFt146hLu;cnQwe{qUTg z7W1C1-o^VkC@BWb$GHG5IxiqjeKSCmdrOdrq#Ak*XL8cH0%kFUkjp?i zFo4Qd5y^QYX4lf^4I_bS>e1{v%_rB_gC!z}Q*FVsZc>CAN{}!$Ai6|){y4VO3UZaN z5jeyV8@RjIw1jpWw3wpiu5PnSeNib{4lQ&`bPdV?=eSKG8UiV&9dckx0tXKS=gIj` z!2r{^L$=Lzvh&{+@(|*^87{gslm|<(u~@YH(wv?>yk-1E0@e4k%P{7Ds)9|h*E;G~ zPVn_RuyxE1o_=eR@SfiS#?+Noig&M@8%RvCfuDZE<1@H7Gy9ib^SdID;~(G>95`~vfBOn`YcX;O>;4|YSxJ1&ai0|}3Z zsuh?f&um6>pujBb`vOqfLlN^b4IGNUz~=W{0$Hyv6yC!i_j-XakF%BG=a3>WF**0c zk_edCfN-bM(QxYpLa8BGZ6-y|SX7j1M};3=B_dtE3+TCnknf!x)ADLiHlq&Pk$b1s zU43*@ehNHjhM>(W<_AqM?|e1CS!hRharlOF2C%LZqsHti^83htrYO z9Vj(hHNZ;*t4SuBJ|_TNJMo08AXxFB=b61ATuS+Vq0IpXED{YG*&u4H)34X~E{} z#@vg4w(7%}q#J~%9?y`eOkS!`EgIl$GJe89&5qv~1I0=H$lae}+ zm^QI&(9L&EGraVE7bFq~rQoif_ky~rv}SXj)8JXq3Vu&hf+OPC0u-&WdnJedxOgEr zzyfprb%CM1xgU&4hrH1tK!P~--c!{VVE&5C&ki3kpz`Ixo{;vWB+~)q=_%4RazbIy zUPLJ-6MsUk&{xF#FF|D(*ol)*aFsIC28fb241n}>H37`>Y=9VBxZUSnvdJ47CTU^^ zwAuOT4$CU6&~cWR5+Xi-DjZwtdt-FVyPDez*87uX)GbSO?Nk#;YCv0XF>l~m;-%2! z!rAkAWAC=v0^YNCp%9pzMvV>i;L_>Q6~H&|V%qOx;UuJ-!BUc(H*y|PdBE9pcJsl= zyph~Jmd7wo>bf8_cM-1s#4!6DF}E?Chk`;vWM28r`h4G^A*&D5l>pMYe86wr$u)J7lMf5~-+zV9(We{X zKu?}1`s_vm#^HM*i}+E&^xlg1;Y@y*K-pOxg!+JI=V<*Ci!)t9p0+Y1_ zqcX%`^vE121BTx)YfPNsh5LUXVqKk++4qj(ID9c0>X# zJKMAA25jT%x|vP@U^9`sE~Ln|AVi${tYci0#_eHenoPT#Kdl_QbX{0=HVu)GWqD-N z9!gBUJO&ynTB&$bAM-$o-H+wzw)R5Q$Ui`8PO?q}bGM~DabKSF=?$~(dsNQyNPS7V zK0AA|jEN8i@M|MN%$IKVjV3j*2)zU~u?_T)F9W!CRu{5$B<$9AWS|G59T_~wuQ{=> z?+5ccynzncjLHu*G|!(3Fo$ykHs4>-Fg0S>4LZP-_)9~`OL0h4yo$gmUJw4DV9$HR zD1$0;3SNXeJ*c!!L6;=E37j*&r7#OMK5F{*XKU;VegL87ZQ%!2F0W%7bpbJA?*jwU z5e;_#BuE4~C5b0A{f;l3*k_!={y8o!p)rA*bXnVl{M|vk7Ze!7<%jv#6bEmiek-Ai z_#28?F91?HwN`#ka|3S=PNKK`E}HQcH}-yBHLgY5wyQ35iVx? z2z6AE1vThPYloo!+V)%pd~W)FqD%(y`|MtJ(lPB$n8QQ$QJ_+s^~KH_O+-XVjt@|O z!A`9-Z#+6^!%r7QXuD4Vy<%3C=5B5>U8_63s-cFwIl_NucN zs}8$h;JL)g0U%mX>v0m^dMGiV5Z3!@BBwHmF@Ty1 z>2Q!PJ+%YWf0-b^Z2{?3m2WZGwS1G6D!`kEoK6#asr^Ed0SX)?#(djgCdHQUm;m(d zB)EUk6+iQdercx4dp;aaoDjsY7pQp*&5vSDdbi4hQh!uvYp5S0>-_^gq<_Z`C;@NO z0^X2}=}*HL0zWTK|INB`p*M4ecZ}EZDPM;Ni{0Df9t2uLeYp4X_3;OZ-vK=Tf?9j1 znZp6&Mhbx$hk>eeoa{B!dtho@CtjTcnfkDUq-a34I@4Nblt0F-FbhK77hqSe$MN5c zg$9i-Wx4Z+`=Py0Z`sewl3`n%|Vpm{A0kx_GCr^z9Rk98g18p@h z6ta_{{6@I@z*A@7;DrWrFnmE{Wq{U@1s*A1U@X+ax5t5!AFjgu9yk!kM=|!!%gM>A z0)+G&Ahe)Nt#ZFr8CXyrdVnGYNWCpkI|;U&Xs-p91-Z+^{hIcXF33tYBU~PgIUK0z zV4OfIa0eU77{=$&$-jJ4X1b-e@MjkzQef2>vskWrH7noDZx8v=@s|4z#j6%>J_Qe@ z?hqWOEC6?#oI$m3_I8tS3E!*)2YUuoT)>NY1cXiJ%d?P_G^4k8=EMCl??fpA@*q?o zi8=8S{mJIHW$F>V5i**4kkozv{gcxiFg)`gld`}&rwJ*22F!3-P}aCwkya_1|GoW4 zF$htpE7{Jq{AXnfCU9R)!O}NEdJ|-?{ZKmV=4-*LG|%)hpCQ|yDM*YwJQlh#&X}d* zqe&pTYuT+H-gh6x07P(0g^yR|#nIGAZm%AA_I8T^Zju89sbBPCv zfne<==f_xAeC6*y>?Fd{pNRFY^pGw#_Y!U zSFx=8-6wbgfFp34FloMRnF|1isGt?>of;azK?3C#KP0yoiOQ1@?)jznHc()oLvHMA zDY(TSEK(j1!=3a3WO^QJI-uR2ag@{Tji~Ha!RmB69Rqtm7s2X55C{v?GESTZj89`2 zs-i4N>dP`5OCYhkB&#tSdCggbe@m{xu2m;udE4M6(RHmA<*)()vsQ2I=L^3`MpS-h zKqlrS`_KC%S6_)#hdIglC}S=Z$-`c|P%s%hoL(-{rfq0DSOrCP7?_-&Ehav>=4X2H7#f`FfG{*dHSOR>~6P`fODQx`2}*E zK8>P1FTX=xbsFlr>_%gaOV+AT4$N&td=BI$X_TRz7G^t$`DeDoBkLg;DgNE3Z?U0Q zCrTJY17%)sIak@~wq_P?=+AoltIffr;mOI=CcfC?c~o@Pii2TD)8G=a1%3+4P@(*1?y%#pR(P`E?o zSaw3UJUF^KR6ez%#j5eII|0=sq~*12<595R)fompP`VOJSg|VG;cm$ScdB19K;Tj$ z9BIx8TKqCdvN)0?PcIEwUYmrx>Bf&pj;C|&@q?jkBk&J$2894jbdJhI{(rE=rH`Hv z$E)!6IY5Db_alWxj7a3RiGBgs)~v*=)UZBWSjKFGs67j^o_q*Qk-ZIup{X#XZ1(bB zfJs=0dx`s`q_>f|2kgdzS`wKCD-{mGt?h_0i3|<|@9T`bVdh&F zj7DdoN>GuvDV78?PTQ}nSw!7Vh$9&^8^m8BgPdAjaO?eM+XEt3FJwlj8nKb3#N>-R zcnu&HpgwT1KpjygWt3_gfzrW+7cBCGPX3w=w0b}AOZ5v8Rsu8y$#aG(0{lTZF)tmY zJZyV{FQ-`5TBA-Nuq~jEl;Q&x~DD#>ekX#Sv<+~PB3ooE47oxdWcWrVMfp*?LWT9EOdjYt8 z(%_-Ot9v|p&0#~bA&_J)jnYP+5rgTAqvZ~%tg0pezF!tSKXa!MF~>ORiEF7_OR<=~ z%tkxfPA{IX%g3V9j+o~gVC>^^9nQs9aZl7)z%4xj#N!|*IS0$U-beytTsS?a^dwJ4YjpUA-4@3bH(r>9LE|gW?Z>l%uWx;zlEUKbmE<*=bhp1%hHOd%n`eSyT*qYAB$ z51p?=o^rcKeRuUK|EOI^sd_Tf+al()t9l_)hn`?c?dQs3LA0c{wTy5IMq={Cqf|a{ z=5-+#IUaN{~$3gHiW{34TI+*2~UZ z3y0UpY=>OnGC!-r(OX-;X_$|K^R5JhMVDrs&Z{u66g<;fFR7zRjA6MO6|#fv&B~ov$o5Y$Cpx-CaW``4E_aA!VOvTYo?&p$#Ws+f#sK#!ulJ%gneExmcAU zoSpU*mlSi3KX|vp(D|yAf4tq9Vu+5b4NEBWa)$`dY3hCvE9)({fpc>@O4dtN&iXVNS>lLl zaI9s)0SG@P$@=vyI9CjW2N87lE6ojx>#-43cJW)_@|;H7T7iw!TYfe6lFGDn><>K& z_T{T|8J>g=&?ktZ_cONix-T~^A>}UHPjY0>@P>(6iO^wzHv9)jQZ11`7$Q$R$mA`7 z4phI&mIOu;d5xY(XU$QrOi1m&S?^}q+XsVkMeT9HM?w#ty9=;lMKU-l2nVLYxt^PG zfttdTL+-TH6A#Y2P8{3UF6Z~Fij+U>Ti{1N$Lx=$B!PH$k9Z%&M_LgTyiza)co-1P z7ZBZmZ9s*rkWvY{_1s0@vAg&ci9nAHg8>RfYNekv8KDO{W)2=~-D2L0o^ne-sd~_^ zz-L>e8tNZwxNhxAI=`yKZBzwy!QmBk0a`K^YmzFz6>{)xd)fg>QYMsVB}Q)FShpQG zdlP2Pwlv*XgnTVT80O?1uy)$1T$jV$Hp-$&)Qtv53AW&);^2(w)$enlQv7urn94J; z^EUjY_9Bf#nR>KElE#wJv5Czg<0%RlVeSHB7{9B+= zM`;+l4z2@QN}13a{0NgSFTg$Xz2UPT9)?+QT+q^?@!KTtLgOS^&YcqPB&W~hTk(xziy>i{P*3IX+l~2Qy(a zFa;$vsNV~n&YLk;5Xp@`WxiYZxDZKuxZt1-gON91^_T$0Fc=Tcq3+34S}mQ9eS!1W(Vd!SFfg{vr7 ztojl?_#6H_+i5;$RB=SJabm6%I`ZUS_LJ@$Z$Kv6xM5`YInPbCm}Y+a7URVg%^ zi0@nd(f@pP5C)=Ay2+*-+EveeJRKj$o)IFfe-_f_C{e>|7+V$oRPz;{gJI$*fdswV zI&U3_c!?*U-gV)tj*A$VpZa@`Q$i7L)Wcl% zeO1)q@27sKR~>oCIv9Go2QA(8vTXXxIVZqL6r`Kllyh@XJ0ew~ettasZ)X58zX-&K zYf`#d-&Q`9WKyzE+RprEJmEiwLT&R+hk{iv&f+0Hjx7TUFa?;a5mErtBP6;|UmJC+ zL6>+IW>^GnW4oqa1!k4D6`}jYJf>{Gn5FaVfp~Qc+}RWt@iJdwtzI5xo6R(TE-0}D zs$!PuEf({uzVrS?lB$SR5s0##p_Xd|e%x7T<=IrK3J6i~cpLr>UY;x7gt7qr5`B=UB2sJ`AQa-pwJ`Tg z6QCr$!YQ?B^|xm@KMlWGVa)vd{zzRgRfZy7xz&8NzZYpcsY|~&tcDd(qG?*#txk!Aq?Qgl_Bef~`_ zz!6rFnkC~vcbf_4e>h~T?qC*_mdZacUk(1_KhW}9#ov@{VBW{{a39nEctiYp27UxG zC`EtRxB8cGpMh`FIubD$5b9qMy7qr;QhvQL7C!_q62FD6ewEPA!!`>%uAEe?P&ivb z^S8;d*X5xiKap@rb@hQ(27k)o`zLzccl7Z52loE=!R;zDuO6D{Bf3NS~S4#i? zUf=)S)7P<-<}9yb~vZs9NJ^`AJ@NUK}DVVlztE_)n~Yj zjRd{EMi>Q{1ZK?v3rvuVdT&^)uY#m06LkDdz*~JF+)b%9hr9Xv$$yism4A~!0}m@B z^hZ`v_(@}q3UY-{r}N%?+@(p&=t{Tx9{MNyCWV8Q9d~b~ zu*)hm5Ff}w{7i;6dvj%klvaMx%a`I|Fjg7Jx_n35yffNZipf?HTi&hAVOV)U`tX;q zaLk8eSEin#Z=rvI*E_|SppEN!|I<5Ur!H=bOY&d96JG^b9r61)bgP57#Z&xw^xpEh&;63jfI-9 zPXYwNO^FQK;?ZUG#B(aX2<(BTs;XRVI`WE~+UD?=`J&vKaCCO;s&ZO=7`Y9pfv=tc z!+o2fZ&T~!`NIcb#_nF1zG@z=UI)!tIHs{x0gwN(Uvy!d74F81ccH63uM9)|Otm`C zWmI)7_Aax4$zHQJ^eGnja@0FLec|}+zbtwN+@RNjADXPRFVMx>L5&t0U+l}k9Jogl z7Mr(J$l&Gom%VRFOFdpuT*tafWrArwp`q3b+S~tg`Tp}2Zyv$Q>4xolcjMPR{m*5+ zfsL4e2IzoIOuG5Vei4yM)P7-IJHeM}a%Gm7Nwa{|nb5u)3@wB@;4A|JgD+$H_tRjR z*GD{~Q(EN7!01ZqA!mqsKXTygsWm z%y>99a#Ej*|6~w{S||Cd{!Ub7W$RJS7{N~M85aLb9d*qaR;ET);pQrf7SeVhiXi5h zHm(_UU%+3ZtUgcs0T~!`yK~+_9j~UpvwA-t><0(C@V~sNH)OOQLU+Bp_Ip3<6U7O= zeA0U)kW=PS6?*+%@ogQ}L{?i|p93(u+IWnu`&#w%&>@&RaOOq20`4b+zg)E`mn$b`P?TKkx;n9Z)6s9&xq`{=-eiv#VeW&18Yl=30WL_*v`ylYg(6E= zcQ^7FE#)W&v9MaV5rkTVPF`i=;=D64+2 zU4~pK$MMcTcvB(~1Y66-K+vL4fZ~$nHh#8!H=(^3O>zM(`3nc#eGP|~s@}HT-)Hj2 z>fyOtVfj9(yi{>1#3B*O#`|@{SdDV;%T&jeR1d;>|TF9Z;SB zz!Rz=B1@~liUq`nSg%f>e!uNb*m^il%jD&KkYfM~X0W*C;#?{Hef|?AfE_;knZq^$ zTby&Fz%7Mt2ECdXCC~_70*m}OUh{RAHB!01o$Ff4xh8!wuqJAm3s7jlvic_b=UvgG zxxA@+9X9@c#cQ*kE0BO45s%-# zeS79q$OIUL0zBSS;>XCG5(=r@tk5R4Dt920FpC;yoA^&G8|0pgz$oCPAqI{%Sfw&;V-e8g4G3VH?*%X? zNk2R5=KYHrjLyJE_%-zL6@f7k$*XN!b3a4qFb6H*t!F01uuq4%mVWvpFvA%fuX|ti z?mFB;Jh+w-;_G1X)`kAhi4FUxXuNvCn#TsHl^@q@y_NaPcBAJufrOHN`RJ1N(r>@8 zhgSH$4p=Eb(_;OSzAdnav;iVd+58;{!ES3h5p@+(mO1h)-0!~}dOfD?kjxAbL^-jp zz(T*9@8tz4$Y6D}kIY210;l#*Al(cjdJCeDrkTC9ftQ4OOo*Lq13fb5*Xs}S(lN4_ zyzGw)(_F{q3Bq~=N*a3(w-w}xrk}sF1V$y_2VFo>LBoud>w5|DX!g+L3~*s=zLwoV z)R^+H!zl~E48JUz%XjMB=6zfN1U`zG9ux@`M zg`VsEOb|Xj@%eRkM@*Z%u}BCB?A>X1ZQ6F5m=5A0-M9xG*5!(DfpYSiEr+c5WVGBh z(IdFO-FR6IJA&9$8!5LvS-0BK?Z%LcY8Wd6*srwV0@WWd3+shO>~mL^CM|hXv5KA3 zMy}_MS5d0nCEt+6HA-tla(4t<5@<8OsF-J6jPZK>zCF;ZI(N9%O%SmA+?fjnulqqQI+f(g$}+I3;R|>NWvCiw ziT}F8TIY#kK1_w`WyiG}S^M!Q>|u&WDYIz~N%TFXYyi$vuW&i8j4zjZ)^<=FHcRNc zJMitIs1)iG<#~d{x?1kq40wj#8t!Q*Ls?t$+}}8A1N+il)uc+9k_EK%A!NiJ|N42OuplHGoAU$ZQ36{@EXwmLGo7A&vIwam7LDMQEi6(!yq+QMb0Pzz zPSYh}2ng|%3)XUGs`|H}R;tG1U>v#=isiS-^%`S+7c*EO)=b|o&7S>X!IND6*E-* zp^BYA)3b?Fq`jGt?hzA#nS~%*QQ8O6;Z7d~JgRT&NdR&79HQRUz3D#1o)Z>-OpCd@aJ3Ng@S8=8BEigKahMG$(B=+})dsC42%nno_}jPH{5W zAh(d{4L?lwHj|J6@b@ck9~s}YkwTIFQeW|@swz;Ps@KqK64o8GEjWHRxVF!5+NL3; zY~#t7FZ@O@=_f&?ms-Fs9YA;NfH`fbnoOU`Opn*x+R2P^M^>Yd89)l?2jioz|dn&wa7HRwL#t-pR7 zWP3ME!9KU~@3ZsP_iYE@3FFxvgvosFdT!K0+dD{wv zJ!JpTQ}pRYsVF;uL+G2e8?N2Xh+^V-SR=@OO1+nwM4!oyk$))HA6)M7(4}o$ZfVqZ zr-~#ou`A4JRx(Q*LkPoYsr(qzz4@hOW$r24zIcwvwKqsVlQ!S7{^$B?unPan1D$=* zQFQs}Hg!P|^9ri-cJrb~P-)sHRbF09IVvtC{|rQuP#Jdn%+~`@;uQw4xihEuR;x3T zMo`AHFL^brURH*gaIG!JjCE^MKwPWI=G|B% zuW}L*Z@!xZaCp9DXw$($uGO9lvpDLt>)RME49W&wkKBF?L$BrUVzCubgRQ#e6PdMB znmcoIeb}R+nPOM*;LvSF1 zK^vIqo`mYP;zZe5J#c&1h2L=uv|UnujP+P#=lGvGnS7-P(z3#<5p`cgbuNKZ-yYd> zq%-dcZCLed4s2`+(^#j)Bpke}0wr2oTNMi^OeMkBWAmAM);H+ag}NGi5S05@)!aTh zVM1%q5sihagQgun?-FzZxISXtnkc%sH-EvNYOsd!pdvXUOO-6QfCfP^0gWynMU+nV zaKUk@?ge8VJzt3(W;)K%=oO@{zwR%$p^MkgRm|r7*{u6)Uk63iG*c7Ax zxLu5vxTa_S8J;oKsLfhP84d=g1NN6RoE7u7J(?oCwk4*cML56G&9rerQIl-qT|vUS(wQ2|;%X;T!kdid_QgA#G%Gq96Vv8C&p4tn+-{VY zOvMpF^v^Nb#O>%l3{Qxnx!8E|`S6C5=4As=mVE7Na(3I&dnb&Uf#tshYmM?1GI*cw{=Om(Ky$%~t+<|%O96l`{uKYGyk6W;v&4EX*Z);RLeMz7M& z7M-FGr^Y&Dn8jQkY1|&mOtpVVxD-T^LP)1XcNfx>mfcK-$b>gM&$q^#R88p3?urjv zsQWcrQ%S>~0G+yuE~)mj3CW5`BeKGJ0*M@wL`Z;kq!I$UbsD?^$+;k8+HKgvwI}?& zsgg3p2%>DeFzf#Ece^2q`hwc=Ku-9;-W0fagYfbUCHd=3@mRZZqi5a3H{HRxAw5zx zIiH)cMRy8{6O2QMM()enPR}bFrnk2N_uDgIc~E)joKswbS%J;=O<<&kr|&cZ{ghCV zr#H^O*!J5|mn@Qd;yY3R!h0T+DbSSeBrVtF1rMx@HvpC7LqE74H!BT^S0=K?GQn-m zGViELZ}}8ICe7OZ{UUKmcnM8lnsbK0>G9;4-6oTddokxluqkWBhzl^mD(Q#3%@sRP zCS-7b65}~SS|$uE)piwSfn&##Bjc8?T~FsfGE>m@fM8Mgb(35GMftl3e4IzjJ{+1S zX_Zi{wGFHE&ep*sjHd{uq;v{W9?O822D1n^>(!$|bGblm|zfl|5DUfDU&$ zbvsp!tdX>Hfej{~=p_UU1c4UyJ%zn{lQPc_H@q5+!MidT#hiM2&Qb=vYGJQK(#k$9 z{aLUo)pEL#apua0h>xbm_0dO)%vfR{b+7mL^$0;6YN(V^K4!&Ra!LJwf5L^U`;IqN z0f5xD=FsQQrN}Cu6-@AHjZL^TY=4P%w*`l8>^ANvJoP_|%n8e%1@w#1F`W(GPOO-**w>xD#K@^Sqj+Z8}IepkZ&CwUiCg-V7h#GGr z=LVwuFO?%~uA0S7Y0o=rdx}2!2-T@!wL-d}!jmCwF|T`p{_ns3+P`3k=>h30k72W} z`(?1hETPBRVqJeKeqCTL=9uL@J4)gS-=OyL>O_AjnfCXEnzG3k!aXuLP*4&F*zVUk zr@50o6rF`rB#MW{J*~2pNm{8Q#THn6-s6$H5l!1K^^gJ!iT%J=^CE`^dmEjJqyKoGZPJe|IJd}LwtIs@yOq~j z)}}y{g{Mar#PjHkjHpF{&PNMH>*PQC)nA*|6Re}p-h=1jMg=P^djYDxTiLylv#>^_ zY4tHEsT`FWe8PNwjeTS*y>LmHDRkrgmjb^Wq*O~L0rJ|LP1W`|Ln%m`Ac4x4MMJhK z&vQ5i81o8V@(OJx(mfnP?E)fUkkIofvduzkdc(6Pu>9+sc(KVQ*}6E(w|2Xx3(se4 zLRjbe0Y^u@hJ?tcIezXFghc2`4AE-qi6h&tUR=#1A$z~HMt4U`%B)p$-*t4Bk%`k_ zfjM}G{(8C(Bm-7E@WpTEkF%s)M{FBK#ys1=O2>W&`sDTK{0lzZj8b*>Ku$&8@Pdcx zxg1_;FVI#;^kd&KV#kRZW&)`3NT58~G*vs9K4!z&b=?SX<_ zP5VR2T2_|6AQ8>)#o)s9q*w;v+Q;K+Q06>pt`F)uULBynmuarC_JY;vuH-js$8HLK zU{}^SK0Mg?$nK)vL8qL5K4MH<@~aE|4ypZESZ>ScDTw~eX~aR=E!Il}8kf)RTDCQ} zZZN>&=leyTOqLUwd#J89$rj5-R2QaT8N=pWEwaFYf;N4gS2n%^~&JdAD&#MIU~%IX%;r#g?F|>U%xCM|!*E zR#q9n1-8DX&PbetV%Q~uDA-!Ccu8E(dPH~Vj9z_wL;1ba^L#u>yeZJKYH06MaW+9s z(Wsqw3y*vn)-Y9cmwfJ^Fpu-E4yi-r`b& z!6&SIpP6Sd8}+9!yuWP%@yfNEIT`_Q1XCp$@MqvFf)@t47x%>s}hBwlcRs#>=9&`y{TkOrEmKaW0{Kh)7#)Wv>u|4QIQI{TOm*-B3tKxSFj6jrOHAsxb@ic@Sw8y~!N$5YWDZ{elh64u2cWO|Iby$l%*jP%lD;pi6$y5pL}g7BEj7IN zbgjtAr-GEPzxy3~$QjLL4GZ84>5}CXxoYSmkG>1_&m{MXxF6r9tCGSIF}x8l2C6tn z)A1yuf=QMKu~B!VV!KeEWNk^1=^nQMl-KWy9-DUoOY|V%BN3yi`s)J3Y^8&3^X)VlLo)LxW#!$jZ#hz~nc^An=@AsvM;hURxzt|aF6u`6G-M4^P zR})O1M2Nc+o$21g#Q%Z>18KoCXafFyifN-#JhZ7J@ZHH4Z}tMcPDMrjG|57Wv;<+p z2XOUf`($!LW~@24FM85J%wI2jDZw5$$LI3Rh^*}%wS@&~_E$@^j3kN=5bFdTTP%~g8ra*lPTHa? zC1+XMofQ>ZQT@$n8Rh+3D+rj=o~wUH>t-zp&VFkxSX>bf9_jUf{8NQ%R{c!T8(gGx zb8;^vDz=F3#cpyhtkOA_@ONpd#$wY3y`aCp$Jga;I&{Nu^?=Ua79&L7Oiom2*rmI} z9P+E!$+VYMkUJ3fK}!-=D+D#+wai5#zd5r#_l_xn%?haI84=Yve)YG)y?(Wg$)6DV znN@U$$E9``^!MYvHzS^&|9F+*eM~a8LiX8CkECtSJkSG*tv!}a*v<`5)@h)tHMDs1 zRM=O&iqXZ1U~|PZ{CA@5x%^GA`RS@Rk8`d)eyT|I(GX0016*rQrP%Y%rm6~Y_382c z$H?eNOT!z62(T+UW01~2q?cPhUZyq{&z<9)V^a$zMT$ub^wbU*g`PVo2j8fa=oL1< zT2$=vT)9!s*q11?+jXQWQWJI?8!}`P{%FUX#&ZoNmJd?M?3xv|8qv9Ad0w@TX-Vp9Yzd1}!gMX(U>-%De^M>?fZAAX=@Qv7W3s!S zJX3bI&K%}b%As32=v)u{^4o@DHpSRpL1MO;YC&tdD`Q#J$dASs_Z++WJ{jHMFSp0+ z2{EA+PMl*m%c5eLLWZ@K1w$c9_Vj$;?ZhuV43mQ_zxq`?DnducjhyU}@(Zk|%2NE2 zakuW7l19qNgX1%mKeZIzhIMfZY%DEQ6l}&bD)m5*j<@p107%naWAwRCWgy-+38V5! zD(BJg-aoR1)@F<2EJ(^PF*GA`N%j2;YJ!NlO|81c8{42)&|$Yz=|iKOy9i0hB7+b{Rx+Wko0mSDmg&EysUqs>6r=IDN_F)*8)Y;Qr{3~CF0Trbx zCZ99=JZ-m!2F@sk^-{B{NAoBSzpqrE7KHSazWW_@eo5c(P@G6vwha@ z#MK)F29{j9%^znZWcYfk>ufhhABWa zg*Y`3vj8HsDmyYhOie6mSbqmE{M+jk+BayY-^84xj@@TVL9x$?+8wW^{bZ}YJ!gI% z^N!iY7L@a@B#|z=jA`QR8=B&)bJugW{VVYfUUqh7fcfIvX-yijv!H6=IdEE3+q)3^ zy?)~fpgEPY2nlg(tFOq$kop@<$$?{QN%4_U8LGUM^%oF>kbf#YF)ttgB7i4RrSJKmKkc%~i$O>WUjf zkXS>jI`iI>s*=o&pgoP2Q`^eR|8nrCg)ghg$1mqy0gA( zQOjB(mC==ScLdcsnNlouf4RqUUh7WZDU0P#f+*>OJrh?u&cN;dZVai7Jvi=l5EKOJ zMyNuDVF zZJ@hJ*JY8;+6(2g-PH|t(08Wn7r1QQ`eNadiBdX?qsLxwzwvy_o0dTw%j z!t8gqHu34a(hl@;&$Q&rm*A5x{!9u_)gF~8$x!}9Tl?o{t+1B1bez)A(hogv87VTV z7h6=$Wb2INckI)&(g+p6w2~*uDPk6zGmVs;%5mK$r97}3;3lsD00diyBX+sgpBXZJ z)OUw41GiX6)zip7H`~K7##Kv#LT&%__3PI+N9-z<{41)R87!r!W&UzLXOgIr|FS#m3g6#6N0S14y|hWs*id=XlNCu|{TpyQQM8 z;lD_bvu9w%OuKxT;cjLYr*FgHxE8aQKA@(#C6x|?WWtwvy;uOJeF4k1>}D;){C1v=jNYDL%xMYq(@lv zEyn=;9vL3JrQuA^*>WkT!b4e55{^!`K#ZNW^k2Hfp|DaIwkoSlpBTGE%rLN-ZWFuZ zwEutm5Tm&il8H#xnEJh<3T=|u)v?<4m=s`UP}P-F)!zypZYGY*`OH5AZ`$78@iTv_ zxspiMy$}cqSEd>?YMasUWjyJQ$MFRRwbHUY=+9$cFRkV-gAKgn}r$|t?klX~UDrYY1c=PCye`Dv1 zo+`S64{G0YK8r_-%7(XAER=GvdL(h3&p>@>h7$ccaqlE_Rf3t5F@dU=ZqD0LBy5FL z{!2smLRW5@4U07**sMmK;!RGQIHW+d-yVP~cwd*N!{;zqIT20+q4H>fv}Ft>ZYL-> z)dVaj>Nw!HA^qQy*PPGS)k&@Kf4s5`K%T&GbGd@)$4AR`GfDe1+WhEhb#1E_let;M z)Rcto;35!DsBU|R^4!6M>r9k_pBSjn?beYcl-%vW*ZK}f=gEalx^An@a4{F2o!2;$#om*~SGDqm{1@}q}(iTQ4`jq=&v$f z4$XJwT#zHm3^2?c_?9@(>;5$2k?0f|gsi`BiE9Whb$8mnS3mx@*#l)#_m$owE&B3& zucUQJVoY_x>nk-+YcTNC<#;}{O%f}}9A9lSk%P&D^Ncz;US+wmx`>}?V#l-rOVtgV zfdsTtYzkUmqEJT@^okkN3{boCpagZa$64-KPE-$IY=^o^wX>5fS=sF24m&C{ z*HXzXqw0D5$##vQ94>&fSE2Mc?{>|@Xz8yuDf032oYkZkBs0x3=+UtGAx%bQb(qdT& z9VB(}Q%5TIV6@n5ZcUOa<5NvR3rg+Ij03dpOiE-z3rLoCK;iw!lxpAb&@XA36e-N4 z@uyCF3WNQgt60Z$VKW$)?oI-{^%l}N-CS*fz zzURsSXB)#VhDV*$AQxUxJYk~n@npM1n4V#g_9uCJdNX@cy66hu0ECFq>rFPARLC4U)RRCtIh~L~g z_v1Z391mE@qwrvpil|#nP@P^!+()9;>x61*aI&vs>$lN;myRi%WERbhxrOerupbk& zj_g)6ET3W;sB#{|ZDQ(CG~Uyz_$qwq5L9*M#fDt}Rv%e*sZ&Q1RifIQQ8`lCNqR4D zheZa*lt7|S;K%<G$ja9Ob!vt8hKdDamz{oLvCDZ`Ew=5e1E>%|3B zC>akmQ(lyaGPU81(qd5^w7{C6#Wagd-Tcm9tJv-ch%uVNdDO4t!2j;w2e-#ptOzj5 zdR`aN)!;?TK`=B(4hfgvpT6FeaWlos^QuFcMi3|a6)rR1IP&f%RUL;X#^5{A3Ifuz z4sf=l-GoC}eZ`UY%N@lV_B?94{CNq`n6MCW%BF+LGu((hnyDV6d@6{1&#PKb$^A$;%QY7`&-)B_1)!Y+M2Tq1VV=*t1$6nMp)SI#!a^?zdsf6CrHYb_PSj@2b^K!?LevC~~rbei@IM$7A68eZT~(oK~rQZow5 zXgsPZA))h3SO_GxpLU~$W1A_ib8h;SDM3?*%?(SRpIsZ;wvveBbw5bPfZfoSO}zNK z&f2YWHS^utq%#1de(Ls2PMQP(1(U>?8UpiXV0^vf3QE)Znd`A^_q(3m_VRLxG_07~ z=HIM@_MrtM!OuNaj&D)Kb%w0j)9d=DRKcF~+AGzO%igD4IcGkrkW9_Wl04py7VM>k)L}O{P+D6y;s8DpS!=52_oU zXm>webGU?Q(;mC>#ck=d;6q%uRPp7A7$~Y{N*QpH8H~AWNn<78Kq*xt~D>a2-Aw1X5C>hFB zv$jc}JrJQ8OaK*Ef$hI=6?f&5F4g3Z7b5%mMNwvf2t1}`-R0Y*J1x-ss5e$B$!9b_ zb6-}J2By~f^1se5JsH9E=tW{BQ^6)Zbn&+S<+bOI*{k zk4<9inDt{V3~`X2B5q+L^jH1xaDof(Kj&Hhe360p3Ei zY_Cw9d`O-J7Kjj4qYv2EiW3B=Zqv|~d>@{{zipC;_I@0MER2HIvWF~=jiu*iGBrtW<3T^%?Gv8XE7xk zo?pLA>IpE`HqEaII#1|U;6H7I9a}&J+BqSL+-*eGL5#f7mb8#Wjt-8n<+eqos4T=7!7fI%|4PFYxw?Do`_E`4SxNB&k{SnWsCsh3mT6+ zbb4VA!>~iE&m3(Sn2M^#*R~)ypa-YDH+m-$5FG+Jfe!|hw$Q|o$xUBAX%EXl=Ud(b zOVID_-ldgl1Q#c)xWHnHsy)Cn!tx~)GNN$k%G~&eWJp6&^r%k(KvMTMqvGB|LNe+~ zR?dI98Ms*{$@L%>!^Ga?)gRG9jFO0YXX$O{Ynzus8PQdUF4~XSSbBrqpXF6GlNi~M zwqIQ8U*MqsUT9c($tNWx6(vdX@hB365Fh&G4q<=@AzdJ|PUll~V0T|b8yb>H5+};Q zmTQh1fkdE>GQCsm7+?*ois^^M4VG)qHtlWU z%&_Osi&2ZMs4d1sDEOT?bEWl324g?OGY?Y6B4DzTOqVw|=c-1j5oXxq3NnF^nnY%C zyj0HYf7xi|$VU5zwx39#t3b4=iiu*93e#A0`_C9>JNzda;SFpk`=re~zV`!;6VUmC(B119}gs@WK(yI6m zm9l(_g@EnKGO6m6W&omA0?|o@%5MSCRV_JDnkKQS7ANX9HHpI(^;&N^nv%piT1N$V z$Czo%nW9Ple-=ExGTTg)R8JFAL^iQAi4=4Bw9~@&`8~|Jbo;YaZoMC0=7p)T0G9^c zbj3un@Gy%@LBenaF_bLEC+4(B+PrkdXv_H<2VCsJsl=c*l>|7vbVWW&jXS9a9awu+ ztChbq(0rpf3g#FbLuMbWzQUs>KgTyI7BI@lkquqdizoUwQ)x~}$SCY%?VaGA#%WO@ zHmrt_i<}fOeEv=a*hOqpoMzvDG3CcHT;za_>w;=vEIRupKAxeQRq}yD(B< z%GT_uA8$IHPhmvV=~1L@#xOQ{f zL1sY0?PMMGp=sBHK%dAtK}2b4)ld+)6_!XK1M;50Lw=k=-76Mm)|(rQ#;4A@2|4E* z6)C_}yH`x$TE!;pRheZym%?;T#!~Q&+5!E;<{+c9L+{L~WaLtLYSW7!&x67`Jk@TV zxud=16$Q33TmvGlsQps6sG2x8Ajoarjykgt08FbcXg#p}C%1*1hV=K?>~H-f;XGi8 zb#c|e>(GLyq(JZ{19xCJN`)#+ov&!7QX$Erg(j}Pv2NktZ|INuhQ65m!tzD4MZPu% ze8?{}4nH(VqWH(_%89gkS6j2+C();xhgWPo|K9v>PyHY`hfA}bkaA)ejq;)c-D)Z9 z=7qZy*#7z;E{r~UjugmfO$aP|5(2XNaK>Q4re=}Od_xkT8R{|44y7oOMQ9Q{m;!ppg zUt-6=zdY?`&1BKP%-QtzUw+`{g(1C}{5tRH??n&cko$<}6}Qsy!_VRk7mK)U|Jz?M s%R(ho9_TjnfBls|p78(co4z?PJWwiEHpm)&6Zp5(a@C@Ab658N0D45LJpcdz diff --git a/docs/BPMN/cyber-observable-processing-1.bpmn b/docs/BPMN/cyber-observable-processing-1.bpmn deleted file mode 100644 index 768ece3..0000000 --- a/docs/BPMN/cyber-observable-processing-1.bpmn +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - SequenceFlow_1ubr2qc - SequenceFlow_1wii5nr - SequenceFlow_0yi02pi - - - - SequenceFlow_1qlas1i - SequenceFlow_1ubr2qc - - - SequenceFlow_02uk7kg - - - - - SequenceFlow_0yi02pi - - - - SequenceFlow_1qlas1i - - - SequenceFlow_1wii5nr - SequenceFlow_02uk7kg - - - Cyber Observable is returned to parent process - - - - Cyber Observable is modified / cleaned based on human decisions - - - - Data cleanup processors - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/BPMN/cyber-observable-processing-1.png b/docs/BPMN/cyber-observable-processing-1.png deleted file mode 100644 index a7fd7101e8d4fcf96f5340d915e02d5a0fd3ea5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36813 zcmZ6zby$>L`vppjFo3|&(gO@34I-)J07EM!ARr+c0 zWKwV9;J|UzkP7oz-4as)y#yq1?~Q2LGOIZ#T-k{(F&gvgtn1T>9~&^waH{oab`CH~qJt z&j0qgJQ$R_I$s(M3xdJ$6y>2%TsAl)OrC{pM+m)eXYcbnUdaFFlm#>j;=uOrHUGO| znHi70-Dk~B%jkdi{(Br@A^isnY&=$%*dN`#G)Oi0k7UlwO!{35u zrb~k#dXb-eY5zOp0S<(2u}!9nV$TqKlK#Il-q?G#+37QQv1h_dPHFzT=LPnjZ4ToM zcI+8QNyC3<60d<)Jq=E_va|=S(IzeZW$<5LiNoQPuSm_4;%+L)BP2P?i}kQwi2Z8g z;2@NXVV#=FJ3L(Ug8rLg+3^4MniV1FwaGCd(Dd%sb#f!`R0M zjf_rpc^{Mxn*VQ1Vff&EB0+DJ+)08vP%a0*#%2o=WPg!KF5uKY?j(?~?xmi1ufuw@ z7|GgjYe2H#9(+J%`_4pbVBmxI+ONZK{T9RNW{M3ynjUSbF@zOEl-V-AuAWFXLJAd+cL+wp?HfpLtUqdRhCm^yJZmse2rwm~CRVl>58J zg%A?cBHgz$qeXgVHTGj2EM7wjgnz=$z6rH%N_%Z>2Ar(W9kt<;*D*Rb4ew^EhD?;2 z{XRYTy~g6Z&K84`yVxHgtJL;8`%~MWw%vL)_u73xY6~>n13b))^~Fx`*=E(?OjjJ^ z?A@c8C!TvpEPk7huUJGZ!zR`Dr_Zh$p8qZpI&E2smobf@71(^GM1EJydEzCP;J?FH z9|?wU>qP(7OUmGU)J`n@Ez^zfZ%KR~Hh2_$d{|rhu+AlqQ7)jVhXZ`_-P*MrzXox~ zsX7gz=20`xN$ag74MEoj=00!H-4l-j?{2#0p=Z^Sx$3J1q`zssk{o9CUIm(;us|cHtD6@ z3YFhItaWnquW_6nk3U$25*=P%O*^;Nb0pmPKI<`}wY8e~1&fcEITd_k%L?yU9-SLD35V=eyl19ntp&GjDbOsr)9m^)}ty4YW~M zhJ>TNFZqO$=<0V_tLaK_DzmsjkuGOA%l(P}LeqEbn4Ff6qi~f#)32Z0Js8!CgN#Ha z$$}r(s(g;j zqQ(C#syjT%LVPCg+gp*tpI>8U!C*V07r3;ts9`SoRgGH>A2~?Aww!G_NL!%2OQ0M2{3%( z0`c34ZNZGBAae}FcB=lz(kf7^YoOi6i(K@X#MpKhsKmLQZMU|nU7vgLS8F=uu!lQ0 z1Cek2Escx2?qK|(a-V%<~TdbzHsSWB4;f(u;oplSl82B|IZYb zm!#1v^BBG%T(9+^cJVc~P`Jaj@3HNngpKv?YgwPxli13cLYdepiDWkllVhtNfi+F5 z*5#Q$lf#wa-s7>`cWbbYO)27s7fK$RgfIdi9=dtygdCq8zaxS3N! zp$Ol_`AX}UC7qrmPA|pCK{VwMv5eq8lD)pkX~v=aQ^(-ky>D!fu#vaFB%HD2* z|IZRcTYL&WR|erD&_DO+79G*R87CYEjGGI%0&)@q7U$)u?DkWZXo%3GmYL_jMth12 z(Y3Y8F|=Dbep{}QEWRJk7DCBqupvI+>cUIaX};n~w+Uy>@6yJBy(8J3Hhjb8I5BI! z2c^}P4Aj~1!JUhvdFOi1n{PD*=fUk5bv|s|7pE;6jv!^Uu};%SBjrD3kDbt_#mWCh=lXp zGx{f%YMmLpFDk-mvv@@fmWt+Qkl-67E{ctngXIyFnmMMu_)5HNg|O+`Ip1$Db((g& z7_sqPL|2`H$PH2pg0NPGaJ%{q@U$7;^4Wm1KZZdeAynttZeJ74iW}}HW$?Py&bY<+ zU0ob~BA!X&)F`npRx9ka4;J&k9Sec|1%y#8H7sD{@G`OMDTK%$pFH}LXg3yeYytI&OtUYJrq^y&XM zMX2*ry?yI)O(GWL$*-@ye@4@a$Swrom)M)u4NAqM_Ul}i_)Rt^rCfg^YrS`WGH*Vc zZ)rA&=V<6Ng}1GIOCP)@VNV9e5fKGibzIuG<#wJog(!3ceRv2ntFqBjtv^E5fAQH_ zWAI#xli;$Qz0zKbqnohdwM_bU4lsk~pzK*mGj6uXlZeUmm#$Qq@(4e8`wV%xj0vTt z+F4IB_d_^Wn(oB#kHNd4@PWX~zqy4+W~0Y)=R2WT{IYrZi@SY>)u*awdQzXpk&;NT zSLRgqDHaW#<$kU=8Y?xME`-eBywk~bf+Q0VVH4BcqVMnl)57S_<^Ux)U7nwmf=|14 z!}tE`y?fC^iuxd7I93vwGaz+D`X6Qmz82+2hxx(8o+_dj3htQq9%eptk^-iMRh1-`X zic*k^RSb`QJV}U35E+Uafko}#GaL0OJ+A=~2R;d(*B6r;?h)j4WO zmpA7(@8K?2eC^X8bZYsu_B!9v1O#-aKHTSEWce=cwv6I))uE=bV{o1wkXmE%T6^u) zU{Yg0*{LMyCpoVZa#Q!qOw`^UZDruqWkg=90s(r0t*Stg=P0KG;k3AefKy46rK$jA zh()^AxXfD;$O-hOg8D`z4)N7m%ThXo{n~O906$aG3okBL`*~ym*6e;J+a$$&+dgGc zb;;c0PtAmhfi83hbb#keNR>GO8I!HU0>};J667;{yJoh5yO)K^Axp~IYu0!1!qFB8 z64MkO|LgS#-*3wCtTJ+j=Cj_2jQ4K&lwwW{004QJBW`@}yA~0HOJ~R4#MJ7tsOVS8 z`BHH|gM>gL^3Qbb^xB;`cag=AxEc1DHIO@>>%G&gh!O6ua_`F!p|v}lj%LOt*~wjy zOY2C4nueT&!HVqnm9xjz8;A(`LI*ZPf&=3O-&JQ*V0ApROv6hWz6M$UQzNT12V(MV zm`3SyOidjZrS-G1;#BnGLd!+O@H5QT<*R$pwg8k?r49=p8do; zF`(-NjP|CMnWP`QoP}t0D$da&yb1qjw`tVr>A}Pcm4$1q<&WO-L}$}^?Cun9^BNR~ zF%zn>?#a#F5q&2?_IE$w8>3b$BJqs&YE4%Ey*6_vwW+kny2W2Ok5X=v{~Z9&{#M=rYGjgL#tIFnxgsCydu-zX{Dg`8C6dd>M9 zE^95Pkl<26HhFDE|` zWWEio?z;7KK<;Dig)Bn)FwC&r z7t%`n;9#8ae6jW>0%T}cvaFo8;LsfTObmp|cqO%ep*@t88^c9s?4g+>`(s+O zqdzG2dS32d-^d9)5c1!16nBhNOweV?2#zkwt{Kov@Hp{{Z7z2$HHZAw;H;B!SzOCk ze^SK~$i8vnHooz16k`KX4D&p6ow+b7(zR^YqW^iU17c0lvyN6FN-Aw)5oR0M)+R*5 zVR>V!j*R^lyjV3X8CuY~dz!m@PD!-wH>iE7@{-ocv}LJ1cU?3Nr)?=g&Iy+g|9!ltd6!(L{4sMa_j^%bvSGi1;26~ggzx-3l>ngF67H>kH0*akeVB-m=cpz=i0=o z1LeCv?Xm=K{2L|2Si^y~INwa45|LvGg0>CaX;lhgYec$VkeXuZzGUNBQHtp#FclLW z{(h&C!t)sIF1)+n-q)>zSvs+cfpmvv4fGIUajfY~op9cRmnw;aBXV@8?J=|Uuh}RC zTN5_HUrpvDTDw$5WvJY$ym&d&z8L#wIibf9a~3b9b>>NM*%BL})M#nNA3iC6nQCXHky0}*t-8vzGprB*tbjq7Vz(1h7+5=~f zpyMzaZrW-6F+3YWTiD(56Ok-)A+Xv`95`k|9Xwx?D#}sk9yM#$IY@bD-6qN!13^gts>=m+PvKG7!me#%^J)5R`H!i5BvP^%C1g+Yi0Ot!?K zraW!*>YiatoF1}}BktR7BG0nr5A|hQ#qLW|sDJgLd|?GEefWEwh`z^E!)75gsD-+O zplb|<`DJ;MFo7en_VtGqq=5-@sS)p$y0fShhJ9vRX;;-?X9o*_@_j?c&vfhqo?gy(>7=2;i#3UMs= z+Fu`D^|GS*f$wRd#Cn`lL*qvSUKsMWosa+_zybVKGe95vxzMfHpj` zr{ksAzsgN*M}q)%go4|v%&gn62Y*o!j%^H8<47OGI^&a104Q_lJ$lNH6yWw zCnN%|AaR2naT>@Z(g)~5hed=vh`Azt+^_aO3HzPNwKesV|6xg~L*&^f6ov9t zArmpN#Lw6g1+b)D%zLY+-ziYb9l0J)M)Urer%F(Sn*fry%^^@%e6RPB7SxIg)ZvrM z0+U(#5X$-;?lIVNP_Ki!7mn9JYXf^h`uaX7fNv4E5)2~n8g`yi`o{@?Z@!1E;dpWq znK!W1&1{rIQyU!irwjkOO}af7W23#A&gff>bj_^13cxU86d)m}kjXb+Rb(o0k8cqy z!_TvH39zLc{Fp(y3Tj0{#8o6MQ&uF1G*`w}6JheA6i{}e!{R8{iOx>PWxd|M2|H7mwSq;)6kpX zmC(13_P+QITYR@hb7#0?zCO%TyvFzXVQYZ@?}S=w>J@K*@c3)_Cv;YKKuuztX}&v&}W@9YFk%I86eO@&p~*QndtSWfeC2z(;R<<4FBVkx%%TAiZ&-x2^K zc66cG{Qrp9Yk1+QiQL;l%VQg04c_uvQm}3*UD|N#0 z-$A)ga%EQAH{ZXqw93?Nr>=Taj%Tn|o?C0LFPJUqnor?5_xXNAIClpSMkY_<8eN%-P^9+{;^ zs@UrKB>67qPg4N)CYt@cI#Z?4KjVJ6V=E0nyp+4kp6@8dAO>^pPP0$__+rG=*_3(9 z9eQ#8CX`G)vvt&U56h&pr2SHZBA9XW*~AKA_5c<`gZ1I}ZV(-&iyY>c!?2{YNnu)` zUtCfh6S>-3gzH=NRAQ9Nhgqg+P?2T8=0NUJ;`1_ZmdlP zQRf3p7h(3%@YBpSI#UY@-dz)c1^={WEq@1OW<#!#%s97Rd>&ad7n5EZNW~3jhf|hZV~60y;xLGr zpX7bj1FVrev=9#q>j2r%p!_V?XO(_>lc3#UwxI?+`_Q?0tdN8|_eJG~B_SD8ol+q! zW%L{^vv8p&C{wopTPERVQfgYyoZlQksX3auZyQkgo5C2grA%jL#qbEjSD1}3EvTKGe0PgzzR(U6x`*zdc zcnbG2pCy&kI}Wt+ER4LAuet{&M*j;X)G8s-bCpuDS{4}kb4O4f$IEeQq{R{=^E&|W zZoG*8tYA1y;@3X{p~~d6|HgWCw0J7eH$ciL{$sTPOXWoBonR@6IXfq0c15t%ad+Il zPxaqX*G#OgbgRxmpY0wGp6%eV5(24(6`vB-A?#?2>NX6?YJc`qlR3DU=pFfnXzJuV z1uZBd1aNWe>l>zQiR=*X1C^;an&}SO!~3J3K#$@o!YPTk7+%gzDST3S4czl=H)XSr1cHdfkVrTrDZ=kzo6yTF$`uEGyr+0Z z%8AXq|I9B4Rq)mMnp|31l7yT630AfXI5w{OPh^Bc*x_vt>%dAi4mbpLbc6rV>;t0} zT#(9r7Ob>Hj-hw{_TAbl{#fd^)D@>k&3?R<6b^c9`37F%qr=CrYpcE`X{_L-YmvFP zzXfr)OxeA!zp3}G4lC&F{+m?)RY_~GEu(#OAPC7<#9^N5W)0j#q2K+35MX#kszFiP zGxhGXfDz3-Z5IP$1tTIWd}x&dJ|a^-C=#8lE!`tvS~rv`Xf~z!quMT6=J3apSs+{y z%YF-UZ1z2H1044enNqF8!~jrz{GD?B52Jeuom)=2%wFt%-2%FYTjOS>hUVHY3${cO zXgN_SkQT*h?1J3e3wHYz5R$Sp9%t7LPyf^X=pIH;B1E2nwh^f`O?3|7B9_G6Wu zl8Mn=+60QA!Of=ttFvmtwE@OWGgT-37bP6H-F!4RP8dd2i9%%b-vXt6nIBMk1VW@; z7UaF!uhG?k_46(6hDgW7;k5Hs{aQ}dHYmduOIuFuAJjUT9d$7}*M*WvW5nh`An<5C z-;0r145OOiD{K1MnSOhVbK%X)tjdM^|xHcL0`z}(YA9KgmMO8 zx{w#E(*Jn@N^*5R>)<4LFQZi(vTi%jCf+ko1tj_rEwg#EZ%yO=ca2#8?ho4a)e{&WL2N#G8_-u&^5KzN%hT-rDSrahtUPT_Fc0k$`5Y>qO z(Rz9M?Y868D9HEb)zve|XvmqGF#5jei)V99lLlqxv-vGRg7bLN9e;ZRx6l(PiR0v1 zrtB4oY6D8{cO1s~V`Q<~W&bOwk>`J=Zu1jG%qYHK`|wH+K$$9vpFpEZe`rPjQ(NB$ zoAyR@b2`Em~n`u``Ou;MBAu)*6C4MlzAL!x^<0UyX>amTF*(1*;ZKY%1$04vTK zNu?!Zbwl?8jM0t92dMJeU9zM*0q5Q^&@)C+sQ4c<_x{_5BF+?vO686IXNP}OLRh^Q zaARf^=g?B+yOG;bQ|$gX?)p}*{p7oF5*Vs&LaDwZafZ# zk%kzpVFoaSC0rov*AV;@4LJXegF*K@fN|v)z&!MtdCfkj{evwH zKue)_@XWtFD3sVK?ac^IQnI@4OZsXM`bOhP6-C+S8a6>mzPu0Ab|@UDh7B$(vz=** z=O{GHi4s1Y2Nl2&1yRH+N&%HXjcQtUB4g#+n6TgglWNhI3pe;Hd2;(8V-5hxa!gH)Qi^=nU z^msM+5X%`WEM!T_m>uGY44SW$yOy;>fwu@y?sx+Xk6s?4R2H8VO)fYeYoz%j#@pe2fFj0NBx3t zpfH0a8u140R5h{>$yV5CY#fI(Y@xj*<7YBKNRqrxi{Yvo$x+vT zwvY#F$65p4^GYk%98<{qshgB4kx6|KzlSDzX3_x~`$UN_PPXt(A5B*>DBf2|jKQW5 zIkV1{wCX>Is2}9dr&ULlGlxJNY6PHoFzI3>UiQ?M7ri^froMC3aX=w;Df_gfoM!>o_%%L3WKT$#fJO&=rD7ZJ_9gM5QRUahNL@ zQ%7jxVeEkbsmo^{L8HT$5W;V)K2J!)fE^%D{1JPyD)aMEZFg2+`Jv$i%kpkX2rcKs zv7}I(FEJRIPoR3HwbPGb#Vx~QEZT;rHVwlYG+c?)Zzx&h2AtYOsvLoQUQ5^p!tXSK z7-kby`M@iIusE8;haaut77 zE_u`%AkNs^wnslK6eV`%H9yc;R?=AGAr9 ztt3U@9dh@x<1~{)3%0rmf1?Sc=iFyuDN+1iZcuI-xP&&j1=KptYLYFZm+n!%_*L}2 z0jwWmIkB#`^c2*g`1kuiZ-i{3Ev#UdzdUvajh_Bf;a9Q(Xs~(oc)WiB%%57y%BIo- z^(Ijwr_=Q?wzl8npKkdtVX5w11=_j|y^yr)ghEX_A63MO6_&~| z846HaWYv&PF=qr$rqou$Z$Ur*1akucSqQwHS=yt#|0&Wfy0n5cXGx`7?9?h!L=+LuSM&m~$w0IaopQDAZAc5LrSpoTeF13_#cW<%oChK7KQpH>58fQ<{PdVMHRS~d zos4=z{v(8AibuzWF)Hs8HTZ!+85_T0^tj@F;tu0|x+KNdD-G3)u?!JSm+qB$BnRXF7admMGvBab#}s$;M)1+*A$ z=GO7-`Cflr-Wta&Gp_sg`NsFJXz57PB>i+fk+}6)UP`9BhMAH{y++kTHMv9&#oobC zGpTH8YI+n zf3S6EH3ux-1N|r1R>-a)Q7jQRBuZZLb@}|l(R|=Mz$Ja+-$@z7F!^ozvIV+nCLSQC zsz&NHBDH z%zL}oTH$3h^lX|l-Sp>ir;Swn+Kb}en=AVJ19U|3EFV3XH@Mqu5q3a~rlHVI#~0J? zrI2svim)N(x_PG5yEu+RfkjW>Q8IwvECt%Ragh-N`Y|!_n}fH$OmynKoDTqP@yWb- zYMCM>t{F>FE56LRE>L0e3^cks;z4l&8E0FLY|?yaluPmOgR2e2H^6}41hg>EKkwY$ zf{+CPgPAFRcd$D)%>#WqAAoPe8~Cq-ob zf&i&4&JZJ5#HHkUa8ds-suYXi$9K z0XWC<5AAB)udUpF;=S|;nfO|$&heHZOKp$fqjG|L^<`9xDlOcr^rxwF^CaTk^%8t^A5E9B+36q&VIzf`h4Psvc1pWuDMw6N(pb8g&)lfvp>eyE18AcWLa%>Sqg@TazwhEpK4P zk887FP&_gq+I)*RE5#nWrnTS~;w8C*#v1 zekL$w1gN}+k%Jcq&R93yl|J9;oCOhS(SD-Tk+7){%VqM&1y8`DCQpy z=~N#X=6~BoqN!@`uaN|!fDe)=Vt;3O-9Ve&52wu9n{wqQwdL=N@?*3s6Ec(AH2*7- z(vGRy;fBXs`%ESILO%B&k&MQdm{>VVwl?+-w0~E`6xL#EP-;49z5yyeb;R*ir!p*> zUwW=t%$P6NkB?$*NdGCMv~HgFIEBA(OsA3pDn=B60b#8~K0Ayy`u%{>@0UsuQqI80 z8NnLnGgIe!zqLzEeEnoD7xkdrFz6f|5QqrHiGfBQF(Qt!i5OJYwNX0&m{w5{VT)5N zUtEHZ0V(bE<{w=_i9&L8ek&xcu8h)@y?prsmwc;pjx|JmMyI(=m&bfR0U5ITRz8_P zGW~(U;h;b{B7@soEO&;V?C$=(WQ8y40ZOx-uR z$FQ=yx?X5}G$|(au=7i- zJX}p!j)k{p=*N$)tKlTvEW#>jidNzUA344T8N!tyKDt^Vf=VEp^c0xu4n4mT&AfbOI|^8a>jcpj`jf9*;D_qhl3h?y5af9 z-@^r}xI|k3{WPx*-c}3aR@*6afx3tmRLxZRXfUdWp_LI1f6aUY@)$T-DWKFuDqaxW zRU5QA-lo(PM+Y*Ktz-C|Zx3(}_es4E@t~MID(?3Aod5Ut$om8t;F(Oe3;2HaGa_t1 zlJriKx#rQi0nG1K!z>>D4?jP1Wt4ELCKZ`E9)j4gcQ6PfJsXk& zuGGy`ehF^!T-!mN{W{Rd``K1R^sk09%|@DN=Bnw+?5!2gM6!YoF8OQ}hRkA>c!vkxNAo4ps1oo`Wxl z`f#&nA#6XD!nz#*m02YN9?a~clcM!ucqz4`$ds0ZFl+09oVe+gbu3L%^`MZ}1RjmqWORUL_u^v~lhV`V$ zBReEsi78e_*%}ta_fX>n1IN&NLs4-=)-`rR;bymZU;8+k`r~+=Ya8s8O_pDYIq(24 zc*_mP*#BoV_YBy_;JBAX!J%UGh48xwP@}Nr+4RF8Y_SdYUUWBuG*tSyGvf1QyyQ=) z7uc9FP3Ddfr1Oeje9jIz06du&U_@_)Xk9r@Raam}0EP5RTlU222%x7%Kru3e&7 z?^Bx3&SB|e;LNUGe=gZr9{uGCaj^|{lgKQ^-XhKq*!5WqeEPP7HF_$AWQ|Tas98$( zWI6c|XJy4{7E7@Tq~FxzV!w`j0qRD-vle7D{>?XcFv z(2nH2(yBCf{o`i=qr)2ta2Vpp*>ED^kB??KIU85b)F=XHtNXW%U?fwfM?@ zOd(z%-%)F5@w)Wd4Fs2CcNeIXSW!V?}r05_5~< z57243#?*>-RCP)FItKDN+vZ09;jGeX)HrJBkdmfPYOgXxo@nkVJ^6G2=xB=jloSIU z*HVIdFfu&CE4SxLyG*Qa2QU87hnC6tGhDmX@zz$ugXy&pgi;xNX~uiQfWpiuJU<*D zx(77fsVRO-7ObBrs6!RYg!l6$68DjBb{{YESR+X4v%R)r8Lz*8*z#^>D-C}7}mSIt+2fw%smoK-y3klZEWIQ*G zYze_i#?YdImsc*Y-)`f`Lh7M!!X}l!FE}DXOyTMB+?t4t+vR|s&#Df&lkrNmIzH|; z?CD-~z`@vqcV?=3QRg9pXstJd5zvCWWJf+9;&L1je-o_eD2c@5Pl?=Km`|OQfEZ@{ z*}6E_cEat0|1PD)=?N&s`HR10Ut@5CinV}*^ZJ7Ill1Sg7#vkz14g1-6pNal-Yv&6 z_Gj(CzhOP9IrpIXtVqJ|ExXo$^gW>-W%v@}Mu@`gYqvbq%zEGc#_KB;|I!#EtTgPySu*KbySFlk#Jwtd{Aknh84~;^+Vz`7G(iCP+Owz5sVVOZ^>)UDHCqOdybw= z%{X2kF15ZwMZ+x0SXn&1pX>taYD%?)ZJb5ga}NGS>izD*SJ!9jv2OwV4KXZ_kF(K= z_~km1X?K%aK4f*Q9EGk%@Q6GDJl3vG(cC(;d^7QZ0b+rZR1(Qu+iCpB&s+8FZQU z9&sm4X&@!%QqlE)^No}95R%D92HOF@*xsGlua3v7#K+2asg*x~b3mm{b-)Uu-@UNv zFe~gtKL(F`tpDu}sAFW#;8QhyH6aImH5`*;bsaQX3QkJ2RCD@>DP$OxvT*a7Y84mo zew*!-hds}1DvS7){QQyDw(qd*Fz_@`GF3CWG<cm$MdaD{lOkpK`0eQ1nJrxr<_|?3b?omJ&Q(zW z$_iE%4g1m>`=lB5JLHDkc+VX@2U%B2&6Jo2gFPi4Y6{e+`x1pRIfjz-4sAj!D3!op z9M7R&eEn&Nv|kI2x^KZjqc%Cm29_Nd2Vc+qX3Up9e<&ahA@(yYxaKkuy+)y6r77s?F@CTYV%TZP3h)Y zYmaihW~C3d9Y-{{>d0v8V0)vN&EwJ7FEVyKdhYg|M7duRvR!#Qk+*}<}nc~we;gtDf%zx|UIBUgz=AUkr z#@dz)?q^TKmpd>f!h_<3b3!m&fXC7m!xm|@A7k&NV8mC! zuKZOhigd*(`k7zcPxfU-o9@7OCSsSE6vs>kiK&{+$#hNjK9gfmvyniB!_r@U1SN$K zhP7`yrgCO3)S6#18V4+57wjVDRW@-s=hOh6=GbHlns1o+aLTw;u=}stk{-}{9!7h= z4=6}C@?%Am^=bKyEI_?_Upzy!g1W@01_+(CU=QhkL2GIWw~I_$i)nnm&v{V zHKv16KW1tBo+Hn&Id5RN{hel?Ve-!x03&i;EEuV;`d+=yp9LNZhuq}WjQM_2i5ZKq ztfoq2O6CM`v{;+zE`%;c<=;9Qs&=f9O+hYQnM!kkKFM_#jS?nD>W}X+6m~tlMQxsU z18-y4_B{Tx(=5XXehSp(u=Km_c%hfvflX%5t0UetbhCp%F8?41XT4lX3{4Y(nQk~` zh4pGyx~i!~_1sTpuu>BZ$mQ=b&bITzi3{Bxh+Oh>E zjskwWXP4RFH$0O1qwUDMH~{%_(CSbDi3s4!qK}SRmCUa}(PKpsuhq<_3`Gdui-UEn9 zS8Nsf2CsnrOtA@^B8_10)y%B#?@tl(wPU}Oyb`~CN!+FmbU#qz79&4DPY9PjwpD%azPH-r4!lyjv?_a9ADesh#jy zx&yX97*ZslM83)re=~u_$?4WIxRtGWM+rMi0v{+I?lS(nEVj4JN{r5MfOwv;>IsKN z-eKt=c3^L-uN`3T$ADZh|5QqQZO$zi~j5T=DEc}dhcwu~nE9^yj8eKu49bv=CtnFBsn-;9P61yRgp zXKHtqdyLB1lHt!s!B$9{BpfJDy`Ej71%~CH?Y0f@Vg_~1yNc1d1z_KoaM_z?TgJj` zlpS0W=E`Sk&S1+~#X7gC`P|Uoj>+pAk*E>)>a&y($lo2*C`|N=QU|aq{D~v*#O~od zlyEj?cWjC2Vx6q~9Afpf8ktc`%%ZlK7e|_hF(-Do^8+6E_&UQLeW}^yJaKLe4xycL zVK=Rgvm?L4=b#XC_DF$%?rN7%fnH^HhyI~@J>)aDV{Z*}Bc}yrSgr&orS&bJ%^rQQO$fs1&B=GzFRAqXQWtozeYxF*&wrO#aQ~qX}K5_>x(qIJ-+Z>zDm}St%wWJC*^R1 ziZ8$aDlX=YLD-L%*nO^0*d117twGgsS3zri^LJGb%NqLn-te%;dNO{GAy51T8+)2; zCFZ!Z(EgdsSm#wtNko61ITda^!Pl8PTq#j*aw`fM4$8cYHV3~pzMBu3X)Lucf$Us#2JPn9`;u+Ctu>c#~a(+ z7`gHst72f+M2Z{Wa&;jI7P;Ge8?){@a6J+rr3Pj;5(*VsG#TmrbV0M3P;%Mzv*a5f-En4WL+fFfb@b^g}3!n?Z@b|`*#azgXPf{?|$j* z;;A(Q!a^?uN6PkYUW;yRA;eOdWrW0Grr_)8J<20_p8!kgsB)-Fj!5^i%<_vMTDLqPdNN9 zQJfUCxZ1g_Rd$6#&nSKjT%^FMkhjq)G%_J-0Ys1s^vI0R6Ai2DulXRl(SS`Wr=zf$ zkogX^A_nlUp8+ZgBIt(8QQY8$!Rll($e5rdlK<*tM==BGv6nkkXay zLnX6r&Ne~iyQyE9Pa5r?N zyEox8;HTvb1FGi$SuJjrF`@MTV{4NX!jyw@mf{m)*f`mL|WO zzH?S$`K=ALT=;4SXd*~u1mvup$X}o{VJcY@{ybn+pnZ z(4_+qNNP$w|-kD&0n*?2IQLk|cjh7?l06pHmo6HYq?WXu~)KzD~;EypGo{b#l2}bm2KMwTw1Y^VUZ!T zWr#xNvCP9#L^P<(Q)I|IWXL?vWEM%NkSX&RiX^2HAwz~F3MF&jajNHe-|hYL{rR@- zyMNrQAi=aal;(OiXid4BTd6 zNq!%_ohZPq|2BR*naS||Blx*;o-R!j)Q2BxZgPL`w z!Snml*LM$+54&CKzq5#huD>CfGPspy=n{ztiC{z0vh~UxoDlrr|4~DNOiKhnS+WL^#bKz#JF_Wy9jJizQzG3uUW? zJfIH9GYy5>Sq;D?u7fBS-ZcwAu6wi3Z;u|nN7BI5_k`fiy!g<2BY=Yz?OTv(19AXB^o`$B(I*-ZMFW9{ zsp~nrNX-AC#^LJ1cR<;l5nmGknzF{JsN-bM{y+g^1WgA+Smgz*UMFg>*{lN3`eX(uH6>~IOW*;$0iELn1vd@iB>IyF*}xv>}lg2$7Da`tC1sUvJswdn>;(X z!88K-dJHguj*rM}fFZ&36_wQQQ3K_Q{weS(@BmoX4OFCVNAGxY|*FRU;DdiYut;5>2fl z51EQ5S9L-*BF`cME;j|WA$AcoyNfsS>k*-0;p3A>YMzm@m%vWt+ZJ*}0j9(lBu_Q^ z=Qn^#>!Dg6aatwlNDj;wgupGkafsul7F0yO~()%i3Ne{Xs1Q%O|DePj79Y{s@*2q0@V+XgWV6;lWuNhAf3KL zo)ny7il+m1sq-~|G6u!mkzyeg(xP;Rs;vPe-AtNm3yz7#<+hX?9V;`vg34Nl8G49t zW|BYEEDl(10;AT2gw-uUt&a1(Jta4pFBpVm8RmPh&en*;7duHaMi5P%DT~Kq3 zk^5HO5wi|7;1|uniQ|<~{C)*|b+`gYRW}Y?4tvRQYoZZJKk%K@s`%nBp`5R?`@N|R z@*j5dKZOiu!6C+HwNHv%@|luqq(L8u1>EkddPlxwbLr+Rbo-1nXarIcs>)W*;c z&5e~A?Hc9IW}RI5z56<8^P1yr{c1vQ2TQB-lL>gW{@0UH)-O=H2;jJN)}YN@{(+P8 z`_7qlpP$!o9(!*j zY0enmcd{-HIJDDengaK_Zn4@ejsr0y9d5Z?G}(@w5{SRC5d;xanuYPpjh>rreJLLv z0%7MO2Kun%4p4>9kf&voFn@N+a=|k>S)& zCXjN&aJ*X-aoCvy>okS4uD|Y7HaOheyQG$&x$x%TxJ$1fub@(J=|5`c3QzubNb1?X zt7-tf=dcp=BVeXt;}u7_4FbplE%CrgyQm^bA<0I=WBr+HfDUm^@ga_^^)&AphC4N# zk0g$-i)jDOz_-t<8-7NK!3jtV=Aw%vbkIf60Q6YmmK6hjZye;*-B?oftcaMWQc6zkM?b9Qa z-9?(ir^u^p3|RkyRvbAJN()_zH-{DzhaRaMN3|2G}S-W3LQYMHg-3l&L!|8J3OJ* zZ5IuG3;E}4uYi7cbIs?n73Uzf{l;a6v>aG#W~qj6N3v3cMY3FYGFW=` zS{HM<+F$V%KP1{>p-s=gA&}z(uA7gP_hU5X>3GC0-i~9cSG^Yc?De1*sM~*l*r2)w zmS|q1ynzd_LXWL4e;#xHU^K_8a|hYn*<(Dq6F4tA#GJNN&E%(zE+LY^_<3Qr;E6ba zc&4J_HP`R2?|bn*3DX~+*ky4aAS(MS89oeYS%$LlXPCkX3s}n#Ofr74Ao(mAGpYKs z*Pw$N8NK-MJU>czxb7twN>(hh;!L|2k){Y_inB-?vnBOrMArRl9b`67-P(W;8@>^5 z^8=QX>)ebLb2^XM)45ZinkDWi7@+|P01;`O*(fjuVon9lD z{`S*i54tkCwVXd*;vJ2PHCZ?-rh!P%1zq{cFnmBYOWWOb_9Z`|SAD#jugNvP;=*%S z6*(|^q454aOt{dBQeiowK%VvoE{8TE8exgQZ*lZ&w#_YYLQ8o#`R?)Fo5u-5PaC9j z6ig97nuSmBx<3}@lkq;{X!xQKpdkDm99KE>iGc?&V^2r0v4z%~pd=*aSs z!Eulqtrr*JH&yRlE3d-Q@(|zEjjBf;E^LJf2B^uSl^2#bMKYv=@cQ`@RNOV8ii;~-=h;g*|Kbc@6^Y^0G0U|tfHIIM<0-R#~w$^ zW(2a9V+$aZde zZPc}&hV&s%;LSV8itb{Wj^=o=-y@Q3j~M7SCBW^}p||H@AGD+k#803qr0?$-F9uqW3+VdG58tK|e-L~O!)(LI7_f*-o~+MwhGFZNQvXdrFi-d)+M za~O)J_s_n}NVYyB{`Uz>!Gn0E-Fp2Z+^MAu5&Ny}3gB#A4wOvi`+?rs2Ss->XThrU1(!g>Eu*vbM9>^?U76M0SCD(>V$Gpt&rczp?jB3OE(T*U^H$yIz~$rWt&Z z6`n2!CYw-B9|%|N9o&W-DuE9&2h2CI-l|6XVbNNbnbVKj3h5PyU`0%W0wh~*IzeR= zc`1JV@c28El;gM4DSWtFMY8&P6UrRsp#Guq1_qcOt5`*UL>l=G>bnML^IZFscd|Us zvL~qw=va;b3gSDwCvIq;!&-8|hzx&`7#4uxdWRJN!!~dk^FDZC=&xcToYf=!5z84`)l&^icazO@0Ve-NdWOOWZ)yY1bwB1(GrJi8g;K9eH% z;U*BS!%vyxj8>ZV3Vi`};;Ef^F`?70^w3(f@KD0n|h!Y6OKS&PjWc>=~pfI zHEB%eAaNd|ZqBk2XO_cwx1+bZt|bxS4cq~3K*=!#QsWrZn)4vL`wrqRhMHP%B&N7XaULO*ceWO#Vl)X4R|{kf!7Z>_N}Rgv?pmtL=oE=p~UVmJb>US(qUM7Kr8MA zG65s-MblbEb#?fn&vbX<5@-%Mp=jcJbpHQH`l}Q~ zeV5?A15jqA`@CBK?wdwsBGay{g5nAk{xydV-i_@uB(TGv$&pO*WVJ)B<{@1IEw?3@ zAv!Oqa~%G1=SLFOJ85;WI5RP2t-^9lOrK~lF{n#xGDK7mB8>zItlzbOb7UPqF4@=7JR z+yXA$dKarR!v}F?S8JEf?zBQZUxSOYdEA=VXSy_6fAPEpP|xVwQ{p5~OC7W`ziiV75qcK6*#h^A|2Kp<%gIBQm2k-V2zV8RZeDN&?4oUF!Q_wV zVUDWAj}bU7ts*irN(Us7LDr)WLat0P?T0g5b05y_QAU)l>m&$?x0NzDi@5J9WqvPQBU&mlu8SUg60K>$&0Y^4X)9mBWFA0y`)I z9KjBzMkyNm0~QEc9H-=aRAZL`AXNrsq8x7KWT<2rav3_9brwN1p)iO=)Q0ZJIAoR@ ztI;sTr}1h60EADd$)}e`M?d2S^<3$YdL#|cXb34S5`~Te2Q9N+(`vaJ5V&tAGlVcM zhv7NVUN?Ik*WE$jfjJY5N2@<$J4`sMl zgJ@;=*Dh44?4tqu55%mmd^cbk$a{NMt`FAxeoJMjZyDB`bl`$e#j6RRu6q5sIKcdS4$%djm*xpBXSH z4D-8yAy9Wf?Q0b>1_lR9sK5t@t);$8+%QYsh!0gY0a5A__|g)-Te_HvCr-SSTSkiu zaKa?&RYaLv?*Vu>Zsa|4yXV_QKhnSygdDp>NT4Jm1Mzxki#6Evce_gz6695*c9ZC- zy$@uXOjCSf7f?Sox#JBmnwETfuvwk@9|HHDLI-{?cqb_buB;QT@{*R*{cY0?t{LiZ z)_~#@Z%p#r?k>Jop^`9XvBRXbNXP#Syc^pRL8h^q$|DwGbDr~u0xa53m|$XdYhn-@ zesP0KY!@|OJn(I*{+ty?4Q`s-97WuP>xmjd1LqF}z_Z<#&9V1x%EL93A(^{2EX~Nc)nUA4EdLS6AX=Jb z0kD0HkUw+DHFzyulaK0nAk2I9TU33r@KHq@B;Wqs%@tn_XS;Wwa)7H^EY%NXj%06TwV)L4V8 z&h5YfJFwLEm2`qveqgqaiTLIbfMopQV-aZ{7Rtwou99N6p-~Krs9=d75I#HrNNpb0 zI=>AlplIVghviMKkGt-pf2gsymAJ1G;Yb&p2CzN&YSCb%v`J{@8iC0g!5rwN$?%)! zT-1NK0ErV|L-3SvEQCgH9Uc_(yiuiP_bXr+Z+>pdajaq3b9)PbCpUKg z^}C4|qOgiga0!lymk7(Oo}-g@{MIfX*KY*V>fXTTXBsv8uPh zy>p&NphAv~9m;?P!W5ed$fpRF@5YY-!zEEC;>67>*y((=XqMa?;5J_URQ0^h$z|0` zWri}fz+6Uez9Z`C z%~!objW3Z!W|>kjv3kM+!11Q^NyJz%5WH9ycIYz*-h_kvfq2De8|GW(y#(S6e(AJs zG1omWe$d4;54QL$)jubcGf>38PlV?==)AJyC0=*3(<@forXp4C>|qr|#{Ecl$q*;0 zb~XR=7hleV|5XoSxzSpHZ%;6gj4L&1{e3PkmFMD~ZznJUcpb*5{%r4PC_e~uly#rPfYCg|nj_6arH>$DE za4=PN-9`c@WDra^w0XMnyeav>fuGNJ)iDhK;j1?!T6o143o+^D_ryfLBN`??XNhl@ z8g&XDehc_Tmdf%}m-bXZVMfIps_?3Ky*CMqAn{a-IT?*PYBe9W7(#NGTN32hG16E& z;v=yi3`lx}5?L0(Sbklqhx6d+i&tuF99}ys=kdV`6s=6yGh)1r<&MUPc~`0_O21i9 z=H+Cf*1hgD( z1+}qG;9p)9>-veeQFqIWF935-DK5nw5(0MxIyOr$YUQYo29Dzd8}j#`jh)&LrV^i^ zjU|yTkTO0~BqT$-`aXR)64nxrP3?=2h3R5owwboFJY^0Nw@sifAWbkXAcggN&mx6& zN|UomWH?|Ax3L?fujZT|zSRkn~!f{5&t)0*=gg43+O$KmD)D!W9! z<5$LYMPDAk`TxAaTR{D3=x!4B)18C?9c{o?AFv7ZQgroU#SK#ch#w4PU5RY!kw5Gk{nEAw-XPs1b+EHc^OQV zYnAUUQ_@_a79=)#F!nM6(U8|ELUK++7Lcu$@P;XQW4}zuEeoTDoMX4R!@Q(vSWbsQ z6W9T$VpgIOmy6|(oDyy@1+5S_D*L7>h5>I;9H6G2cPYg2GJU!4c!sIY*)65)g`0DF zDqT@$)g5p0gdTd3ri~GZ8(&8%<*%3b-pNkyeTvVwHWz>iSZ|T8?h+wRKXw6jm_llrb-v zq;OaAiojs~{TJ*N9}`QThjX>FZCF{bF!xuB2wiPH-YABkx_@2kg2NyeC`0&1M zrc8%zCuM!KJDr%?$CkPXsUWvBZLysetoeFzij|ts3m!3q{#%K-XuaP~>~&*mn$tA; z4;-dCBU3l+!Or?(6H!tVFfHpKH5q{f|J4e4vS<1W7ZwuRS;i>NY{&uPw#&{t-E%>0 zW8`~dFB7HdJ_zw0HLRKMjIAAhf=qP;zB@rBRKoF0W-i-eCXN52(x=Z(Yfde>7=6Cr zdfbshH_=cAC=HUu6?jJ2HN^zZ$bKEJDk`OldJvLia)ZqG6P!Kh$O8lQROw6B)`wu; z#wa}wgsDZ~A?o2ZLXBkb*%B-!nybw@fBx{DyeZ}jGu=IBx}+YylmO>=$go~KY*Vu4 z&oP83_UK1g3DJNuhDw=C1ljzQp5F6~pBAKhS_YWuJ`*Tc$!NcY}u0nW(I!#rOio+iE!zuBQ#R zxLWVdh8Oa1#S2pzWY`eww|TNINXpO}zfM;JFvsw)@}@D=_$8_w!v)dB0xl#EOGD|? zCd4dhsGoxc&Jog3Me_qvxA1i-k4rC>W+ocs*F$9}6{P8H6|n>{S|Im3z~^I^ujR~a zt}WV~Q+RAbIGA;Z<25S2nZ0;+p`qOXf%omPcen?i%FTjt6LL#+9r+Ea2_3_$&^~EE z*mulGa^EbLXw%j(3;ZyHdIHMUe73IVz?q0_tj(DAF71c z2=&O+u#AUSO;ZV7eRiV;i*pFd1rl{`1#ef1M7EB5&#`hn8f)~dRoTX55CnN5!Ks<; z*0wW}O2Domv3f7M*#LC27bTxQZ2U_K-Gj>sI{ z`nW2=!X&VOBf*~Ln{CjWkxI>;@z*6u$Aeu$C3LoTNNmqcBy$$^I(PS6O+7 zqFILNyPNnk(j7m`N*5#9*%FaTWRdvoSB?N6k9pJ>J*5Wd2fHS#9%4H=giMOm!DROn z?1r!ev`As^{UIsE#|DVRJCSSDyn_ten9xjgqKu+{m=H+jDLzM&D0KBCyrGFWR64;i>n5i?EASOx%{<%IwmUI1-B95cd zo^R9`hBv^^(gi>ODdpsnRERdcErwsiDQvqr;B~nqEEpk-yS#f=%|Z+RdbiK1z{SF$ z@cPU1FID_z5xBoGbEEGfLggRuB86+3nPya#Q*7)FU@eLFJ#KXvuI!R*4V8=MJ25T( z>6lw~KX1L$5mx*#?R92Jm7!##S?Tw?-C3cmGpZ_5Mb1o#rue5ly!WY1*wU4cKeaN~ zH%|HSMVRxyyM84=#}Rm7r7IenJGisCS@yQ7(xe!%+lTNwyMqxGsP`dfEvo|4SYS`5 zdp1cy^m9B=6%F>|3bEusReVM)T`9lLL;W+H@(!UIl15p$3#H!)-%wve2*U4Fkf7Dk z9(MlVn%8lh#ni^PK8kiv0Z|)F?5-+pc z{TxC_AFEgnf^O-79!4hs7m)FrgyvxW)7=tWPdV9u)=!4{P%sNXCRtTIS1VEDs2E2* zAI0>rnuYIH!NFNP31<|!8HJ$Dx#>t8CC4UtMd~)EF~;4P7vV76n@p%Xl)?$*Gf|t* zUk!F6$ZJw^LYnqRVphUn*%B#qF1{JzMzj*`s|+T=e;sLLh7p2y7mc}HU^jdFO87XF zTWf-uc&m7R*Ln7WbGlv8E6ToaD2p0a44o2_tm?xcHkWyyUxgIB4YCokLFEKQwz~{) zYCx2g4F$@e?(IFp3dH5(w1Z)T<`WZwUNzu;$Wkq4;!QVH22mS>rbL1R&L4+IL-PCu zI)4cq7@1QbRGvi*NwMQ@|Mvuh@<9aU$V34YQ_gby7N(aSkqD8S8ysy9PAoH(xwwl> zRj`bOFaEPcn}*`YsNJPx8z*q9n+d-7uOZY8M#%6u4G_sMK>{!y*m~~&%Jf5p}p>5=#R6N)z znt@)77If9$k1MV+5Xr5<@#8nRb$~`|uKnr9l)liT?`U~XQt}mZ10iMix!9SC-6Ktcs($K z%qBC5OyBx!Xn<|X`h}^8{?;t~PgC0Q_=BaGvo_@>$^6W1D8XK%e$)r&4#z|V6$v4IjRxSBm`ih zaJrZ+d-$%PjwzJV%THNObeCSPDxC4g!tWClk5lVnYM=5qYLF80xc7&APvBG;07!c3 z23$FuL|kFNn$v1-g+vL z+_y=A7Gab)m{1Xdo&J7qhdv+lZoEoLnD|4Quyw|>3cMNAFJS+kt(qRrW|vdjc>90O zM&RcU@;k&0W)7Uf(71R9JE##t|8qE6INaJsqYr8Ip+MM%st}az1-m<28e}0Vdv|{& zD*#{jqOktsbesJd3^iT%BlXI`tSAiqU#$?Y&0XSrriHf zLHOl<#~d-wA~cXMc=E79X(=Bx6^SU+0xos~#vYdrHRYIY1#`*XSiQRgCtHg#QiA6~ zhH(nS!6s@{aVJgxoq!`P06#PbAKF|Y!kX!GX0CaSrNa*cA7c4}9zC7wgYj#|88mr0 ze+80HbEwrxbXsoRYcPG>JvhR+_LJJn2SN^61de0(kBC_t5Id}j+$=(eNae`GI!0P} zB-7YX5J|TcC{7S(5x-%&wj{&@57Ay|zuJ%4q*Zyi23L1**5ui41MS7zn(J-Okz1pHtX zF;`^*Q*L~n)!DJw(=ZoonBfaGlV1e1RVM^*c%K<=ybM0@!yj&%jnH!Z`R&=dm2;cp zBU`E1-j{E}mkSllr40CC0^ILxGTwW7+^@ah;$l6m)eS9(GO&X)r6M*(X@94Ny&C+a z6gz%@AsI$pzQ3#Z-#g);sfZg*70-FZ55t-@#W)tS_i)hchKC)2S%E+MasT^rpALL^ z``S0kZ3f9t0A$w3=b-tm?&@5l45~ z&Zk3=S=ZhaApZ9{+hE|R>e4(Gqxl#~o}gRM?<{)kw`eyHVzHPyq2`|h;=b!Yl8Am@ zYTdlB3C+;Vmfc^EYv+yG{C>W18_+y@A0VgBCLnH}fdpOcBe-=>wc0&dnsv-KIEi)~ zc?=ENIj6Kw`hd`=rzl+ZZga%j661X=i4<~ARNy#kFTp|E#FbN-Dv*w`(8L@u7!cl^mkDdze)w;75UDr$3eggPln~OJYWdRkq?!4xs zh3iwljsMOxHW-sxnKPaP>X@j=iK1M8S!DSNdH^taWZ(S}_8x%mVK>lL7K!zr6w3h= zbsk{0@gG}YCKGNk3XFR3Yp=4afx=PwBNH0WO01NH8yjJm-Tv?8A?oCOxMt}G1eTiA+)(Yy|ryqx=R zh#pHJvI9;Y1(1goK%FL*1GU$rIN0g&bLt&|EJ^tzaM}m-bxz%e?aDpeq@7F)F2pd3BWn8Ud5 z&`4q0QvO+m?5%U4H*rpw4P(z6*g zC~Qwsy)C(1H5J@G4iml7Xyzds#f+xsiGmYP;yB`x61on8MRB2=3%G{2R-(rEi( zD+Fc5$AIZNMZ{NP#5=yyN%fry&Gj$ z4_dfC{k%@%nqtz>tLT{IcEEf7_@%HgQ%JCE?Vp;yF?*s~sI}ukmyvZsm@`&>zVEF_tbFTlm7sZB~!l2w(All@w}{zFk#^{!VPRZRx=nM6N3^glmF zTqwh;^HH&#lt%uZ&R1nl9Ix3){v5Lx`Cb#y>hE^tS%Vt(Fm3yEp2ydB{9{1Rolnl) zo(?l`!aIv-nze;CL3j0ObLSgw4Z6hWb(Qisb_Mw*m5{0RDNM3h@2$^;E9R_0VAQx4 zSsh^ho~kn2bZKYFv$YY&peH~*QNRvVmePp^>Brc4D&L6%iF)n5?%RHS5+78B9O{=R z#FNh)Vh9lulBVM*4hr1bNsRa9#wgh0rQ3a_`8v%YrC1ra5yH^Q*~w75|=NtD#kw zv>eDz!g_e2`st*fw;%iRbmAS6(|N8xOu=w2BYUH=qc?s-`!n9a*%9{@HnKcZV&Zrb zYddu`7T>G$>=!TZsL=o?F$L4%dCqUKY0G5JZG!zSK^x^t_62bXCt{tRIm{lEXPL8~ zp2e^`cjBC>Ha-+6>*6Vkm;uxJ8yw-D4iWMW5#3i6KFf8R;T4cwD8&3AvXD(?G*^c) zo&pla)J$y&HXc4voB_U8YdVxCf?qI5_Z7HX zF3-A7@HCEorEXI6d@1t0bEn|WMEcw%pYBrHn0?{4HN11}x#m0zFT4&lsqVcmnb`qn zos!whM;>Lj6=qrqbDc;69~?>GE_obWb$8a_r%lZ@>GeZ2=;=?hRE8HiCuZIJxaie+ zJ6Gp~YL>uxl<0`#SbdX633FZRk(1{+-v~ zU|#q6KV5okPBarSVV*o79Nc8UGWzmJsr0@ZDCa}zK;A5TOG4utQLK6%6He>cK9eo> zn_uoGZchC78}g8AEUX&FTt7}ztd6_UZ`Q#PNCpx7c)-m4Ql&g&RbiQm>Do<~ zOLiwcm-P0&Q6m5uVd~7{bqYn(Gf}1wrF8YvS*Aw6V$bX6RS@Tl7xOCZjBb}X-`BNX z)q5;;>j$1?X6(z-AOzE-qspb`FUrq-u1)$ivY8&eSoBVQL^f=6qh_$-$!M}G%Ajl{ zAZA~bbN=0^Npw{Y^Hx3mKtKQ_jGOq~6v z5*4E*zZBzpzRVPSk~5-QL9Sii^$WGX+`HCPGU}nAy}_sUVQ_&y>+#`nLAv0Dzd5@* zjMMw&)g@kT6z?u-KxE75Eq?3+sydC@YDis+Omh=d#9jetcEsqr}39B&x$=1#Mjf;UGFvT+ec-osj75d?AqBBFW6VeJpU?N z34fw`q~vwZx9#y)IujMVBz^6#Ir6^wo7`$P=FiUxT?fbX_Nb&?a&jXWP<=cQb(OoT1sfX&X zF+s#kZq{2LT6gKRMQ!4gWHG;m*P4m1TlW7uZ7lq%%v+s{^BP`iLMughuc>l{i&jXJ(l zw9Bg?#Jb4ZKP&J0>FC~Sa}|m$uU;|FF4?2Hr!k*x@F#oXyFv>jP6XkcBlEf_3Vnvb zFk2;Elp)_P{?r=wc~VV_benW*3=~g17(R26GjeUq_ZPnd{*iw~V{H`@l;a`1PY+8a z8%{y2RZSCTd8NEMTcolLvoxYUQlvJ7*$3Q@J-4%k!1KS9+*yY~QdJsX_iht=(>(v+ zDr`lO1jecCs~k(X_PXAe!K;0nS(%SVciN7Sq*D=`c@(1-D;c&uF?X z`+CyxF{lr{U^4^30c}Fj1XIJnh2Gj%><%5tXdMV-A&N3(Cn252@$m$KH^~ntw~Yo? zToY{8;T0Ggc)%M-0qTvPM{_?1;W5zqVlru7ed0SfD4oT13Bz?(Y%*)>hUymxt-lrK zLJfo@Z?s@rAiAq0oCTHFJ#Qy#V0m;`gNrkD)o^zosn8R7^X#8Nu%P*OG-q^-fjzkw zDbh^>4!BvGd>tp2*i z`!{iiYoB-~bDV2T8FM<)G+Vnx{pbv*NxZ~B@vky)7A8uQYM&WWhE=Dadj_i25l1o zi12K`kPuiC_-vYOr4Wo zk4>RzmyY(GF+Pn!6~F^f6LYdS2lSMx`A!j%o~=6_S1}Wsf6RaK>&+wR0FT_ z*`+_z-|-Kfb2lF5jt;@}n#FKs`uzGzXR`IZVWEtfm5HbZtUH$T$6ymSd@X}ngjh_~ z3**zJI(Nj((-;;A{T%)D%RMd8krUT{cMwA+qo)ee*T)95&yQSVxL93Q=)+DY*?o`q z1fZBEaSWg7A%vb_o(gcKoHo1EQCq)z!bu9npv+uI5jXmWST7Rt43E|s$YLH)V&A=7 zy#L}rK22ie4<3hoX%fs>iDSx#PX~6>bxw08UyG6J&8yrWvx^?CWLuIaKY ziSnsiCxHx+_#vj)nxh6+RgbS3+C=;FnKuUZW;ES9!7s4`s|I3#h3*$#k0ciJI{QM8f|zHx|VKYcF$ObF>UWR z;_PsQ)tm=%50exH)r4mnZQf?xUV13{E_3{>(HNBX9x56&r|ApWPFM|oaY-}j!e@n7 z+E*K*ZP<@ze6&O84@AiefRkN{eNCDftH_Vj8XJLbBNwpw6)0F`#ZguMCiWZD-4VIx z3KyVsju*1r9)pc^Vt-8d4%|tTtpOSQWkU@MlG9$x-=Tz7-!VCzVf6>L<_5D5wQF_o z)=RO1L(M$YJ!hbpjk{6{JV9Hxha=Wm4g-)z(J#L#58A9XRPmc~`Ft;%(FlmHi`6z9 zum}4M)djO`yX!27dRlSg`u;!DM-?I)nqp8odqw@4p-qDlsi^GK8^z#CyH3JYtFOdw zhk`esxsCgvqKeP;RB5V4983M1?T8;G8wc6hh8Vut+-d?6W6-MPh}gXU-&AT55~Yzl z55y#;1I_Vh_usR?!lX#_8ZrwHrB>TUSb{BZGdZV0QIoO;8;;rdi$GaQom|hg;1o7= zuk^$RXKlylb=!qB(hq?ic1=7lucrezcGp94tdGU( z6m^lGO{6-#cp%@=7KBHf@q?`iLsZVJ9x_+MyxnmC72cb?;M8u&|0$Kmp}YgaT2UP$ z;YO@ee~U1`Lig6}J5bOm&l;*UPJ^nUlV#fn0_3DP*C*WaOC2eh&LxB>G`P*vY!&en zD{lh7Kg!3{*xr~?_8-V~AE7z&2+c{y^4{K`7?C%f3H<|ghDsUNS#HbmFiF zRq>9;gS_>ZMHq~hoC2!Ox{mkt`N^>=qvwo?e8B7Usi2kR6lJ)Wr?AG7d&C4O zEw8=JqwTf>R3Wc>9ZtkxUwveKYgT5^Xg$CNO1iI3=c}Ayf7eip)68=&QyukP4)=eF zaa|=(j2h|+e8u`Q-bwu@e%h5pOBDCAHvj><)rW}l{lKmulImavR()ION;RMCq{uo_ z2X=U0W6ic~7`iR?9V`~cUHVIz|3VQjP+I!i)#K}int;4>J@DvzL4?&N zC`zD&`}OLl!o7rh*6RUzEj>y4;{|7bXoN1ifpj2^N0b~QzNy|ZYR4wGwXPHXA}mG& z6of!U%(b{<^Tb`MKM-8BSFHl+SXH(Y;@#67RPPoXN#P2*oc8ko=2+0}cUYIijbu^_ zc}x=!m>&QlRY5G%6R{LZ&Opqbo-h?yH^)|&cswS8Hz(+P1i*kt^<*P5>blu^OD&+r zpw!qv!08{PaOY~~^8B-RGa1QaDOAN-C}HLZl;i|#h&JZzfEMQTh48lJmz^ARG)_jE zD&0Eg_)=-J|G2iSv`&UGD;KQL41wC?2p=vzVnGWtC)?gN!07=doL*+}FQ^Z>!~@0S z1%Zo0Aw7Fk;M6?>U+g%z;mN@+0v;WSdqsUG{+SMJw-%Ay-s{lxMcs+4_A`2qGe(--5bFSL`NdVN0q*% zMBLlHTG8H&eW$!?{hRua$B4tFl}u$3hwGpEtS0^bR;bzVi2J&{{0r z`#VIFccc)Ja#%vl-ciAS>|}=~{=+n`s{vAeyzk|jl9 z(71B%RQ&5Rynt4kP<-;GC)DtFa@LxwdyTKaROmH14&U?q+=Z5I$^Rdk6?{Qgzm$(X z+#82#01KRY*_xS8s4L>WRsBPIZ2Vz_XB8|w4LITB(6%PSza#%T0LF^K%O=e5D)elG zuH6PjOPXuOhkw)LfBIQfzhLlccaJaz{u}K<_~8@YZ`=QwYVZqp4H!pH4Rgi+^|vSR zM9U=1=KeK;&~c09;945wX!bf<|9UCK5^!0iq;jo)kG{8Zjp5c6f*&cH>3 Ns-0CWQ#K9ye*o(#Yoh=F diff --git a/docs/BPMN/report_review.bpmn b/docs/BPMN/report_review.bpmn deleted file mode 100644 index 72d4be7..0000000 --- a/docs/BPMN/report_review.bpmn +++ /dev/null @@ -1,392 +0,0 @@ - - - - - - - - SequenceFlow_1cusqkf - SequenceFlow_1qkxa3p - - - SequenceFlow_1qkxa3p - SequenceFlow_0mfl44j - SequenceFlow_0ndbj3u - - - SequenceFlow_09yygo3 - - - SequenceFlow_1cusqkf - - - - SequenceFlow_0ndbj3u - SequenceFlow_09yygo3 - - - SequenceFlow_10i4zjb - - - - - - SequenceFlow_1f2lams - - - - SequenceFlow_1req4v4 - SequenceFlow_1f2lams - - - SequenceFlow_10i4zjb - SequenceFlow_0kbpron - SequenceFlow_1req4v4 - - - SequenceFlow_0kbpron - SequenceFlow_1r209mg - - - SequenceFlow_1r209mg - SequenceFlow_0u56fa7 - SequenceFlow_0ogug1j - - - - - SequenceFlow_0u56fa7 - SequenceFlow_082xzw0 - - - SequenceFlow_082xzw0 - - - - - SequenceFlow_0ogug1j - SequenceFlow_1u4s8kp - - DataStoreReference_13v4nfr - - - - - SequenceFlow_1u4s8kp - - - - Notifications are conditionally sent based on internal rules - - - - Assigned to Group.  Anyone From Group can review the information, but only 1 user can perform completion of task - - - - BPMN is determined dynamically based on the "Next Action" supplied by the person who completes the "Review Report" task.  Each "Group" can manage their own in-house processes or use enterprise processes or any combination of each. - - - - - - - - - - - SequenceFlow_0kiqpxs - - - SequenceFlow_0mfl44j - SequenceFlow_0kiqpxs - - DataStoreReference_13f61u4 - - - - - Mapping is managed in Dynamic mapping allowing real time changes through web UI - - - Mapping of Points of Interest with Groups that are interested in those POIs - - - POIs can by any indicator of interest that can be logically determined: From a Identity, with keywords, RegEx, Date Range, GeoLocation, Threat Actor, Cyber Observable Event Types, Data Marking Classification / Security Classifcations, etc - - - - - - - Receives the STIX JSON or STIX Java Object, and processes into Java Object for System Enrichment of Data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/BPMN/report_review.png b/docs/BPMN/report_review.png deleted file mode 100644 index 2386e5776cb23f5c6876e2271af390c76ce8a3a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159508 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ⅈwhfue9mtv|%98b>CH8w`2Js9%#*@PKe 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 - - - - - - - - SequenceFlow_09mh75s - - - - SequenceFlow_09mh75s - SequenceFlow_18ii8oi - - - SequenceFlow_18ii8oi - SequenceFlow_1fqe9af - SequenceFlow_076tslj - - - SequenceFlow_1fqe9af - SequenceFlow_04sm98b - - - SequenceFlow_1hmhlwg - - - SequenceFlow_0zvlit2 - SequenceFlow_076tslj - SequenceFlow_1hmhlwg - - DataStoreReference_0c3yt00 - - - - SequenceFlow_04sm98b - SequenceFlow_0zvlit2 - - - - - - - - - - - - Eval markings and various business rules - - - Based on content of Bundle, eval business rules to determine which parts of organization need to review - - - Each group determined by business rule will review the bundle contents - - - Each group can have their own managed process for STIX review (automated and manual) - - - - - - - - - SequenceFlow_0uuopg6 - SequenceFlow_0n98l6r - - - DataStoreReference_0g41h67 - Property_1sk77we - - - - SequenceFlow_0uuopg6 - - - - SequenceFlow_0n98l6r - SequenceFlow_0enf3pf - - - SequenceFlow_0krxv0m - - - - SequenceFlow_00gksn5 - SequenceFlow_1b382fh - SequenceFlow_0jscl1q - - - - - - SequenceFlow_0jscl1q - SequenceFlow_1spcf8e - - DataStoreReference_0nf65c4 - - - - SequenceFlow_1b382fh - SequenceFlow_1ob26vg - - DataStoreReference_0nf65c4 - - - - SequenceFlow_1ob26vg - - - - SequenceFlow_1spcf8e - - - - - SequenceFlow_0krxv0m - SequenceFlow_00gksn5 - - - - - - SequenceFlow_0enf3pf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/BPMN/sample_processes_1.png b/docs/BPMN/sample_processes_1.png deleted file mode 100644 index e3a3d8781b0f670236a97fc35c92a42f89835cfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133467 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 r8C!e}Y|Jjn`)zjD)oW`io=BAo;J@{&^;hMrv - - - - - - - SequenceFlow_0unhs0w - SequenceFlow_1f3ny23 - - - SequenceFlow_0unhs0w - - - - SequenceFlow_1k6bv0d - - - SequenceFlow_0gsryrb - SequenceFlow_1k6bv0d - - DataStoreReference_005s97h - - - - - SequenceFlow_1f3ny23 - SequenceFlow_0gsryrb - - SequenceFlow_0sqxg8t - - - - SequenceFlow_0sqxg8t - SequenceFlow_0kzpq0e - - - SequenceFlow_0kzpq0e - SequenceFlow_12kjyte - SequenceFlow_18yxhaf - - - - SequenceFlow_12kjyte - - - - - - - SequenceFlow_1mx5bdz - SequenceFlow_0ywnm5v - - - SequenceFlow_0ywnm5v - - - - SequenceFlow_0x0ey5x - SequenceFlow_0yf96lt - - - SequenceFlow_0m2l4gs - SequenceFlow_1mx5bdz - - - SequenceFlow_1dx7vq5 - - - - - SequenceFlow_064m5el - - - - SequenceFlow_1dx7vq5 - SequenceFlow_064m5el - - - - SequenceFlow_18yxhaf - SequenceFlow_0x0ey5x - SequenceFlow_1sedtuc - - - - SequenceFlow_1sedtuc - SequenceFlow_02kn65m - - - - - SequenceFlow_0yf96lt - SequenceFlow_02kn65m - SequenceFlow_0m2l4gs - - - Translator or Translator group is determined based on Business rule execution in "Determine Translator" task - - - - Task Priority determined by "Determine Priority" Tasks - - - - Dynamic rules based on Rules business configs - - - - Dynamic rules based on Rules business configs - - - - Notify's by Email or configured channle - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/BPMN/stix_data_translation.png b/docs/BPMN/stix_data_translation.png deleted file mode 100644 index c024180393b938102c7296d66ffbf8a5429c9b5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119190 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 diff --git a/docs/BPMN/taxii_collection_propagation.bpmn b/docs/BPMN/taxii_collection_propagation.bpmn deleted file mode 100644 index 2e90051..0000000 --- a/docs/BPMN/taxii_collection_propagation.bpmn +++ /dev/null @@ -1,397 +0,0 @@ - - - - - SequenceFlow_1gyqzll - - - - SequenceFlow_1muo31v - - - - SequenceFlow_1muo31v - SequenceFlow_1gyqzll - SequenceFlow_14gbzsl - - SequenceFlow_1c11d9n - - - - - - SequenceFlow_1c11d9n - SequenceFlow_12gdemf - - - SequenceFlow_12gdemf - SequenceFlow_1ujirty - - - SequenceFlow_1h37jpi - SequenceFlow_1ilhrdk - SequenceFlow_07ye9pf - - DataStoreReference_0ejwxjg - - - - SequenceFlow_07ye9pf - - - - - - SequenceFlow_1ujirty - SequenceFlow_1h37jpi - SequenceFlow_1dq8flp - - - - SequenceFlow_1fy9sih - SequenceFlow_1tmolyu - - - - SequenceFlow_1dq8flp - SequenceFlow_1fy9sih - - - SequenceFlow_1tmolyu - SequenceFlow_1ilhrdk - SequenceFlow_0bh4w4z - - - - - SequenceFlow_0z223gd - - - - - SequenceFlow_0bh4w4z - SequenceFlow_0z223gd - - DataStoreReference_00pr4pe - - - - - SequenceFlow_1fqthuu - - - - - SequenceFlow_1fqthuu - - - - Assigned to group/person specific to the special collection in question - - - - - Reminder is configured based on the Special Collection Reviewer's Group configuration and the sensitivity of Special Collection and Object - - - - - - - SequenceFlow_1iaiz0e - SequenceFlow_11u1xxz - - - - SequenceFlow_14gbzsl - SequenceFlow_1iaiz0e - SequenceFlow_0weeqkh - - - - - SequenceFlow_0weeqkh - SequenceFlow_11u1xxz - - - - - For each defined notification hook, they will be notified when processing of the object or bundle has been completed.  Can be used by System to System, and Human notification hooks. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/BPMN/taxii_collection_propagation.png b/docs/BPMN/taxii_collection_propagation.png deleted file mode 100644 index 3bbda07bfd9add4919135419e7da6041997d8731..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149518 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

    ea}ksN*ES2zTJdb(j%0 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

    Y2I9&-?r{O?A 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%0D4je!VY`l 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 RWEcETQ%x85M8zufe*sJAZqNV# diff --git a/docs/Common_Code_Snippets.md b/docs/Common_Code_Snippets.md deleted file mode 100644 index 421d6a0..0000000 --- a/docs/Common_Code_Snippets.md +++ /dev/null @@ -1,8 +0,0 @@ - -#Hashes - -```java - @JsonProperty("hashes") @JsonInclude(NON_EMPTY) - @Size(min = 1, message = "Must have at least 1 hash value") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); -``` \ No newline at end of file diff --git a/docs/Diagrams/Generic-Data-Flow.png b/docs/Diagrams/Generic-Data-Flow.png deleted file mode 100644 index dde44037405518df91a5dca66d9416e9bcab2ea9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116054 zcmeEubySq?_O>Dhwjdx%iGz2^)BU`qR?J-x^ zcFF_T}mKzQZH^un)oSmI{ zodtMpv1WXyMMXvV_)&Z)6b~H1WA9?)aP1b4js1zA7x{Ia3nup0vF0}&%x!I$k?USF zvUPNjWMM&W^w{!RyT(|_H`#(r~Ius}ZKFMOwY`T733Hk>Mfd@H7EVsC5Zh+JO7 z#@s;)CGq3nzkmI&yZt=tysh<3tckrnoF*l-dCKqK{rz|qthouS54nxh>CNMR|L*U{ zU%6rHU<-SVHNP%z<6we?n{A%`>j(blBQ~!k!G~sXI-p{_=r^k=5y$8iTQpNxKpmx-lHRJ~Xg#H%~}+Tq~Vv!N_{ z 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

    9W8aHR!Jr~o4N_f)%|g%x&O#f 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

    =}oPnq+Jj8+_UXOhvx~ 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

    8x41eF5aC*u9Z zb`QX~WfsuP7f12V22sGxaX9Xnbvtbh#u6$FJ5{z`gpo=60tEshHZyEiR@U8GAo3aD zZDRmD%s0FSe@`ML)(i+7Ap+aTj0;?y|AsgK>(j<(PG5{=7|;?K>v~j z=)u>3_oL_ccOfN+Wn^oTYBDU(*B9^1q*Nj5%FLWQ3f$5Ha zbK3po)=(ZFbbasR-T9_RJ5E0JVZOJ~=At;Y*qbQq9#)tH^W_JLK7j(Pt!kuZ)yP5> zMw&Mt<_H^pueC_8;;(~$$#Al$2+B0kbZsl5e_B(W#URE#N*%b@j01y=!Ig5@OjaHF z$6R?QK)|@qYmeQW-GQJz*s5F040oIF?NZOMgNHQN(ciEBBe#3|mW2k6!)DX_`f|%B z!_8v7`0(Hk8kOo9@htUPEOMxTI-3p zUgMnZq3MH|^*jF?1Swe#aQUe;{GIzUT4Ibx%1Tuqo zrseQewu1Y+o|?zIb8YcmR`%JPh(?r~SADD$+<`UFGvb9uYehbuik$R}zJf z;o^puZHKX=%f$(Mh!{vGX@CIum)DBMjauhj7N-!HZKj}Qj7Sy9Sq3dm%nSP|=U3uZQ^5FnpXBaCivy@DK{{$h#!CB~6T4k>DpGo_~k<`EI zxfmDNw3Y7PNnZZD7jACT%^a_08yeAADWE~NYUb;X-EX3q>JzAOlX97TkCD_r2r>m@9ty4ULR{Y zKYfvM`ymKSJb&kfGmOlDxy1URb=&WazYh!g6%yMq9$JU>c=5*G(4_rVjH^5geWVbSkjdWt04RjD?QL|+P3MIgW3>(+Z!2B#y~#~(m9kVO<4G45i8NqOkWE(qE(oQ~ zdu%glQf7PyAXs&@YH=0Y!-)`Z5{qdC;PrqmPy=j)b_eL=v&g#N8bx56+5lKZi#p}s z^=fH8vt&&f1sYc=4`{6TqKr%AX0h9Y1ChIvC1#5s$xu36Pj$)@3cL;D3Ls#RrU~MJ zU~NQY{iP!&3U26~mnS|RDa8w_19urc2;sWGm?JcD!uG-bCbd+c4^N}VmA;ew&U5REztEc~FVVVxq)RtR1*6>YSViOlD-Q!xm}?NJAsTW_fM2A{l-h%Yxb zv2=5NZV-n(=TM&RhQ-Sk+2L?nrul_n2&|Ufu_qWK$|#TMn38P19x(18D^dW6 zsT+<|7x7J_(G+f8V$nyh536LY^1LCe8@2kM93(*U8;Z_s;Be0ast1^3d?4> z@%vQuvFO8mn9_MDg#iJtTLzl(aKgl8NH5EfZ(4WGCP!e*u2$|82jI#_5F%e%Eb$I8HWSdwf5-5$@62ngqn^CMP$37h2hNZlKrMl<3mdW7T;41 zr4Gl-G4-ZA+fTejHxvh=w=#qu{;eke$jLBC1KiJlR=zkMY|Iw3xLRIct0`955c#3y zNbT%ylChGCIBLYONYAvNu}6}WK{7#HuT`+KX2ccry#;RMWAU?Dc}PK|8k`BFcz=BD zZbnWMB2{_uFg~kQifR*3;A*C~nli*-n-+Q(%H)5yRJ-;#$Z+pgQVPfk9 zr9S>LXjwh!qbdrKqDUg)Ej}n3W(pAMT2W)DY_Ttno`_;Zv}tYm&tn=E;YX>SC~+pc zURMbCcLl!Pkfewot2R(tms>kd%F-N9YC!jl@HE6?P$`RZUDv3FXcWqY?>Y&aVD*Yx z;bLd-K|mw^mYj{k<1zq>mc8Ys&t?Y!>)F096G(rqzCJ_;0p*5Isf70?{_Ye|%KWfy zpQOaK#+@tI{DRgz5KS<56KsxT2$UQee`Eyf0q~Ddz^184FM<4Taj*cZrE^dAFzVRJ zg|_?MS?BXUmV*ybJA$2HMFn~c2Qe%;-tmXj4qDz~T?yB3toqR>jgU%C3hb2qMBIAl zqS9)Cnst+gJ!0<-t=qDeJ^6oB?xo@&(cQ;rE1+8X>iWN+HxtE2!S!T8jm)G)I|{M<6vw6xao#LReUQ3-^{`V&Qii_;{PI>Wk|x1G=hgO zzFi-;zf}b42;kr8Jw$8T@hra29?uF$s~|rID0J1ohLD_Bj{FNx6y{%mNtx@4&fKQf zEA6&fDPoe756sU~j?QWuh%4ItFiSDEwP;$`bxaL(zREj(EGpQZF~&;6UKi7#kELPy zc+~4FiySG9w3se*GK`Ko^jqSpBc#=TSB)HuznFWnMt!fcWQdv+_m4GO6Y7oTHAg=t!&6+W4V*c5SeZFeVJeP5EMN@dfM7W1cFbicP9y zPyXIe)$?lz zOAG*#0~i$fLAfkzAD<8s1M{3M~! zdgmIREYzI$xNbKlr~MaqHrPZUYIT!HiNaSsZ|CGKi)iL`z>WGLJ5aFNfX03R@sSd4 zIwYH~zO@JDgZ6JU*HTrnezX!BZQt$(r-xb=;YpmgeHsdud$MIZS9haq1RRm$KlTiN zcT2)LVt&soWrj)KT_7a8?!BBPQxYU?ruT%jz-E7BEYNmMFa8l;k_k3;;y3wFy-|k1 z?s9|)P#FtUx@R4`-_-zgFbx)&r{3Zu|FE}(`woC%1=v&GSFBD$7XFHAQoYfZ=cO&nYdm+8i_>ql@CKr$>Tqfc(av^;(5L@l?M|jhoq0q;1|DxmXVk?FQ zTk6+WlDRDS{x1gc2D&7{#stx5)KWCNo>sKYXj0=y^eW&r8T7%wv6f3gXe7HdBQmtR zM|?xVBvqC4292(#_(=HNAy5dP%zmPS3d;vl$6BbqR73y}ky!zAH5%%TNCxesslXZ- z>_=L}GDWJ{0H|M~ii_6}X>C=094#nWXD{eVIZQNH z7S|gho4bjNhq!caA0ApxndAIa!jrkE%TcVK@K1jms{GI2HVp?*pz%+(#ebLmf3LV3 z@P@RR!PE+eqRzK_bAf|{8ypwa2q-H~*3%@P~}7+HEYnT9hFmM3o1~>&)SS&K=tt|9jAyC)_XRo|Ny#~CgtC@66Ig)t>bEycAC zI9UY(^qI{04L-}nKLEDRtl#IL1|6-OTYU^_PRY9Aln7ynIyTL&F&aYEJOC;QC;UQF zUY*N9ge5!ix4Yt=9Wf7EJ(cm=V!r zFx}(8Uf7#z4gv}LsZBod0}0{^t#0~`;%<@gaJJ54)*S_iEL$UzHAPX3a6IX)P# z7&SW%x2vc@WN^<7zzER6$ON*=U?c6HC>4d%^BA%!0iow4(*Ld@03kkd%-OR;lV)WU zcjNPEZY*v583aWdA>}qOSdanJiF7Zy#eeT1BHtq8{*fXHLUtby#o-dayJwWIHDVgD zor3hwC;DxBcPJr6Q4Hz78}ja0DUN$B7M@;CPEO{Xc9R3tThox$_FmBpcz@Gqj6C8( zKw9@ise^U^=5t)M1AO|LBY+Ok%zz0{?Q1}p#eC~{eQ6;R0BEUUKw)j(9lf;;xCuMC zJLw4vo&mPM%|}2XyibxC1p0V;x{7RbOSa$&$fkZ{p{fKSel{8XeVgh6fUV8Awzjs% z%*D#ZifJH9t;Q-cyagB3AE54eB-B)H0MvqcoEIEdgfh?$}Z*hW~Y3vS_S^t6eL0A>(ixNOwrDb88n0^Cpz&r06*vi zW(~{>u6w_UYMSYG84?pP^<43B{_mm-qiYn$8 zM6=7iOFR(cm~lDGWLIGqWmm?IpONfEr|1Acd()RIW`;mJ-6>~*q?MB+0q1jJ6EzNjzXj2-S8|;Kr{Up{&0L26cluSrN>dev&pj*$vMCtAqv=#+#X7N?kY0+R93UptyZ~Qzk8c**n4VAB;``a zbI!O(>rUW33e_?lnt5I}tnfj62sY-3q)Qw%280Xu{q^Nh4$U0oDc=4o^|RQ(GZ1Hz zW<_XUYriQf10PF4tKD2n1`;o+X?<(a9HN4Z$1*tT7UPVd8`P9i?*Rg3*PG%47sOSZ z3%VD+NQNH)1~z*S;>2;wAj%YABqw8Zk(Lii^q2lpBu^wm#Y8`ZhRUbB5l2X*(CySZ zUwBagko-Ug2nF*kb?LUTgMhoHE(mW+{HAe~l| zRpxgMeOdM~;l3ndQ4t7ilu_^TeLSQUa;R4QpUy0J5raWi$B5ME+akgpZbF68mpC)) z)%G^B2={2?3!#ma1DT|OdIovYk@mAihQOG(U2jQC9Q$keabaNJxDLMb-!#NxN4~du z26%rS5oMtQBemm)8OQaA%8ow@J6&Nhi0^bXTivR4tTr|xjMs2?bKx;ScV3ep!3N|6 zCqJyUB>Orxy7gru_UA@bHx4M?*=%hfW6U*v=(40!{LM=Ls)K3XxYi3r+JD*OMsk)p zc)mr9IkMJ`0q5L4<<1O#X+PTZTjT=pV_5kN{`z9K9(HW#9bwpn$Zgu9M)rDRPZV(K zP8Y$*Gh&FlTHrJ}Kr&*;Q5G-UaqVa|{mbRDrvs}uz*+obohXbE))RGt$cL@J4Z_WL_Jg%7<)S z$VT*nVGC+xHI+&kt6@K6LQPU^Ql~_G_#Xd~>+7~lDgEdLR{^ygMjN!;@xIJ?Ny>3kdSPg1#Sqa3}1#Bl+Vw#v(_OoIn)`|y7%*bMXAf&`GkcId+H5gtD(2lw3Y}|(i+1s+p z1=X}325^N>mv~I99x;az-d)sIfFytxUgt_ks*q|naFJ?X@(4^(0zl#H z7BNP<*+n5xTBUVv<$iS4u|pK^j>TpE6v)1Me?l57_W5(fCin-8fx9TOi9uJVJkiK2 zmYFAjK-#t&;cETM$z51NyBQCOG0$V70H?)mdkD`m4K3n2`T z)9E*e38Pay!eKphocgir>&KIK#p*u`UzYkf1P45|&I~;lY}&bseRgq4{Kt8#wKH^| zxP%;ECy@8|yV=eQS{cCkpD8QwHDJYQ$Bl%NKJ*JJ zTY4XQ_eysD>eT0nc?~&e8IC0^iYIH?6^#7mF<15weo%>lE5K&CtBC}x`rP5o`Yx1& zun-P)K-56-6ZjL#fF5qSX7c=V?<6T(n=wa{d!*y0S-#XsUF`UUS%N<9ecmde!OPty zGSF*@KUb{;lSrmINkPU>g#|gM=>q*ApeVOrodl;K8*!tUcf@dpqe^i!qHD{hco`$01abBn8amM`t(}5{sOxww z1Dzz9MBKyNgDA0CA%~Ftd89Zl<=^q#`hTnthSZ_sc)wxCnTsnk@*L)&#!1@b=@c5` zJyqcrZt~2^O@{1?Z^UpL;LHZl5x*q_PW#l{fE7{>@8bn!ze2f{XmP@^S%$N1tRBG?j=ak;2oIK-MD z{Ea8>@k05bQOlS+1ao7ko^;5ufR7NbufF%K}izN81}KSto;X5>BL^wuj9jhcq%)1Ov*84PJ7 z^#+`N|1Zk6{(dtOcYKuhRhLs z?fX>ZQYAgG?5nyy|HtejjWVLm(0Lvf;P3V-(^dJ|WOMF*r0O(+PYeE&K)dvQ9HcjM zK()2JnA1`sSmq&ykip6@3HVNq@vUualdROokp!ooTwt1FxtCn&3hQ+qcF~WHpDL{- zB+%%EMn!~k*gna-Wch$&f+Ovb;b-soZ8X~qIE#0_sZuruv$p7&Lae5I1&1oda z)bVKSkjpnXW{cn;b*XuK zhqtiuBK)S~YQMGO62m)bczfiC1Ef2C>HV|aIcB^MOrHqlEVG_u} z`=0T<=h^Q$^={3)TY8KEY$ymlo)hR+KM5np(=nlpfZmrvrh@bX_Asvm|LX-1{_I{A zAb~-qkdm%k46^b}!6-LWjG773@>Rug zSV;M}=}6{bG=qy~p~Xh#lLBjaji3I0?M@o0IxMlr4li}hSTI)B^Gho1 zhR5d$trdJ84_L1zEol(d$@T}mGAuEhRcU#qey`DSS(~Zl`X;=Zjf7A;vXi@o;jQn| zTQ!^s&#Bzok3+X<63%4rQad*zPg(fFo7#qs6PKWGiDaW;I2(QKaAG$dxRRH6kumMK zOaQY8j%xe+6xbawFx4@p?}OgqBVErCmG+n?z8PfkVau}|^fso_2{kpzJr^t|uF`%E zO9lhZ28wYbUB~H_BT6?-h!MZ?CE^vk9oXr}Z&k?3n<9S(q#TZHQO;#PQW*BC*{LD* zjK62?>sfD<))MXin$5D@fo1ALSH*X$@3KtL=%e+eoj!x$7OevL$FK2s?b*_@j(<{d zcWUhZxa$S=R1?ev+nXw;pT<5dubHAGr@|-#s3K{%YO$h7LiETGPIBzj-6=b51>GYS z+e}o^Y6Im#0tihSwlDFksoifP+DyIgw}PeXs7#{IDx)Wy5Ci3d5t6C!6hGPm&~WC~ zN1I#JYAg{Q4CJuZXVRcEZn+{5YTs+t!XI<*@hKo>UQel2EmI%M zJNFMVHb?lR-tTqa>UFct>C1Z(mY=6gV{*X7V{=}IY}UP+)tymjw6Pz_!K{gQV&m$% zV<9w1`?)%xO&7KziI>xa4k@8Y_~wWCbH-8^{di5OF8*@;g2`p@N$^2>z+cu_@mY6H z!DM?FrM z)4V2)t-{yT@Ylk-iTHS&wCQ<81ln7Z1-@r@i@{``3%WzHIQIHyAj1PIv1)&6wQZ-7 z&>&L_Z(X?)KUU6g5@zVS32OQ5Ws%NIxrFejcP|-9&i2nh;Y6w3ARhtmG6SLt{Gi%i zXP*%oTunH4Bg&c9~rR8`vwDz`2JI7G?PW>?ruN(-knk|>E%0i%zrSflx=flhGb zl}4=nHX$~%HVfi$c!4j=?_7HFr?Z*qpMyhrqEIkfKfW@^*xOU_`>w!}n;&r%u)90t z2sle~-qFENW|I#U3`hk_O?}cUSPeIR$U&< zG?lB~by8~@TyLb)LMHpBMmYe&r~br&geEAB&$!Tctq&do$J-zG$d{ZAN9=-|G}6h= zgfAUspL&naV4|hVQ8p>7iwoADg4fV@6*a`|qNAf@K>s=zROB`0>C^7uX*dw%<3awT z?>15Ufq5Z1v8piY5s!;Q`yRW2-sK_-4)8%ZBjQrcs)@hcHau}`t^WLe#CW}aES~}y z+(FZrDe;l@7AmS|-;SfvM!J#7|JQTal7k0%gOvOD+%Ns^`OAK9X_p$^2hNeX?KbI8 zBfHW&7yYsopKlo**)Km!xMXB|*=XprXYnFCI>$`iR~|V}7_kojq$YE=A}<*Q9ZF~- zBw*gMe40sweY#Rxepq!P)~Efgzu}6hsQIwdvA=|z$Clk+yyoWCY$}(F4UPnl%37nt zcZ9cuy8L+)|1S5kNc-;Dh;Y$)49`5}MT*Ni^?sQu7{nFdi^HemZ-bXu*8 zb_f>XQ8}vT>fw-`^-IO*MPj#!3ybRuv=m;h**54Fzh~m-*Xg84^YtO^fY(b7zG@KC+EM1aeG0E}kbDki@ks_=J z0=Au%H)clKl-NLTyIJ!CHLiGl*OG~lobpAD=oS%^VD`5lsIPXS+Xpswb@>ykkofE0 zfrK-0jY(hDIl9aBo==23OVC_er`caDLxY?AdlQ3{x5@M-$-Ro7Kp2a7^d@Sj3Jov@Rj#Y!kz0;7EK z)B3%|TD76KK)+z-Gl4;#MxdMGNWbPM!4yO5dC+9SWKrJl;N^aRaz*h`z3fUMBY@uq zMUN>YQBS(LDpFGQsQUjAc8<}J2W^|5*mfop+sVYXZQI7gw%M_5Yhv5Boe3s3wz z_t|fI>YV=6=X9U0>iXYx*LD4_f|xM^@*<$3gh<8MW+rir5^-h_mS#vpbJPSHP%b0E zyfzfo?TFjZ9-nN7Cq^!Rc+0dl@WN)hu2MAc>69TOz@*6H{d|pXlsqPPCEhu9R$mYY zqfMU9K|s!&zG#ves{=`2D+3JVBv(mCHs#-T#%bNoDzS0$AEP2y=Hvlb=@5`Rr|l-g zSku#lnHxn#5URa7x_3{fIc(t|kt=(=avTh$IeE%FI=!TW@3Xjja-)e{jF_4+XN;N4 z2|L1qGDLkta;?(-Ee8)j|T3>No3!_}?UZi38Z`#~puRBxQIMac9g?9CqYNKjQ+(LFZV z0DBw3&JD^4Gn2Gb8B?6lNw$kS-RK{s`1^82Qj1-VrAUkgwm!47r2Akhb+Oj5u_FBq z%6<6FTr35ul5TshloKpu#A>pPn=609xd|=RIc}hTkv0AfR>OjuC{@yoso==pN8o$} zJHBd<+`t93JHvW@Rx$N{HgvH`??Ggv8{Tb9F{K(ZDeZ|V26PWEs2MZW|N6%F@rs=B z%QkWptjBO`X!6F>*P6lieGMzjjsiyJ90`x~4;6w3%B4+-PFCUO=e?dSV~m+dl;_;r zdR|H^IJv^v5;a~}HY5ADfgZcCu!&>Cs|1Dn7ds43chLd+1*7*vnr4I%W>rR`Lj{)W zQKXu;&g{=tQJ1)l-`{exXhReMp}kQA0f*Sq7$0~uGFP-hiW}pH_@~6RhM9k@Y&f44 znxqM8_bvFY(SDWf=HQ`0cXfePs@sv+oaQKb-TD5e5>tF*xTN&~bHFjTT`}6cs4usT zSLx)B)eY|_DJc}Tgv-*p#z9>A5fBCFGKWL2hcVjFr*2KrFmS~NZ zTjW$0Az3;2{fOzjMlzu0ZmSt3>c}va+|I~8uXqwLx;|&%(bk87u>c#!f~ym%PsA-K z;}@)}RSi4XPk)>DQ24>8YU2Jujk$r{+MSy%e1d4Zv%pVntpkA(-nzd`-*~Atg)yAb zauGVR5_8T1GQ8Sv@P}1(6NDD66mSY>?gdzLsh@jY8VS{94~qC>=jN|t#{JV6&&~{Q zG>}pNm1|$D9CYuDHPQM~a?Q)rgA}Z10U9nx(lk!M-A#!)AMalg0xUyNKzZoWONF7r zcm$K0v4n0{#G7n!u$PX1VXUDC^5w-)TMw?e@8z%}TnuH2=AVqz2Vn{rKK!70HW5`} zqoPX109QhePj;ii|3ygiX7atV3f)wc60M;M)>KJ3r)6TKs^MTLngiaR8N@1sd?Uz& zj0&yDNr&9q9gw~0d*prO+&yi&9|I%DhSm$KOrDWuV8om}vz9}FHrmNlK4xn8Kxi*x zHx5n#sB8e7&yZSr7UP%pO*i3r+ht(pHX1Xl(SOgxj~~iYh`6&0njC`ih>F6qq(g}~ zSrQo@D6k`gVRcbRVvAGUX-vkUY0ERmIIUYw1d5wI$RNb$@AeQRSrY0Q&Ji2z0c_s) zlmaTQLm3gzcS-!|bakQfL2isqU*A+ny|OEw2{*|B;P^4RC=Q-=1i_e~>@hPRHrWz;IcXiU^#xToJ+@7k2 zFo>tE7UQ8+$bHmXzQ@EA&B|Vv`^TEHa&(-ds!eAH!Wl!#N7 z;2XH&2dn#a46cKJ_oHOd4b5~n_dwd>l^?}Owv1}j<>Eogd(C|tDvLa z6E~6iDx%4v}Q$WbQl4s^^K@a}PIb$hYX8W~hxTkwQsh=(zCo*1I2W*-J5(xa@d8IxjCX!YmYK(Nrg z!$ZaD4Q8PPB}kOeQ7diPE54bTyYWM-cNS?PP#2?1roius^o(BVsV*-^bpCmg04CFDZLc=AYKy}sG!AgMJfoYc zkf0j0=6K$SScUp6tdJm95t-~da5Ui7Sly7Fji9qtY)QKpN8EsrtNRU09t(w(jhsbx zc0w5GpfBAGg&@b3n$nbk^QutLvY?2Qll#>?tCk&dX`H3k9L(78X9MX&0msGF=g+SG zhEUytlH$Yr*5Iz++y%JJvg96gyFr559eIY9XEXd^k=@>y&K(58ZwDlb8zJ*0b9K|~ z$N8cHlz5%65zdRJ{x0m91`8dHb)2JUKJ52mjZ#Xu6@wO8*&V1mRY)Nfvg)*%e?tzG zYYNAcJSmOP{25CBFj|Jf#n$>pkAI90B+QrAKz;478_b`qJd-L@C?8F$lu7uu1Dt(& zX!tS}5bV^5iA1*A!`puN1@Y^yEZRrdciV>dE0NENiti3~Ca+eVg+u5ghvFTP{b6pK zWKM{q48JWT%ma>VcH&-e3>w2iV_yFSZ*vzvEhCcqgveA-zl?M zr<8>CXm|%=qkJcdS7`!licnqjkKe?Iz3vEFDP1YQBT-9=!>*VsLi)(PeT9BzADEq-6AgskZ*FCTX_3fdS0@H_SpXM+(ftBY|G({QVM1aS$)*2epU zyssbL2+Dl8w6})@EuwE84U)m;BfglVRbGGP>l>7r^B3ND#N2YjtbZaATG>A(xi3jh zaaYzCUE{4EAP+z3hblY!+?0KSj}Ei3w8K7|4X89=jL$W-HkdFD|K*P{^(7MdgK8*U zF-s77Dd1iGiLF14fa0a00NmEcG)>XOFnp!r>yWtRhnMt{MM#am?ywgu@pBfwgqh?0 zBV+i5h7J$iOX2PYs%Ab;laHhK5WEv>HS)dR;))K0y5{KD*c)~Gth(j|b$*HeJ{(iB zljR$^g8a?&*;NjG`3RBFtF%sp#v`fff#CbQU{_ahzD94|1^N#ufww}KgG7mZL%+3I z7wNnGq@o3_)ox`qnnG-OBuYI;n*pgA-V4l|CmSJm4{jO_7MAkCI`~hDl;9Ba5cI&c z`DXm~--%YMoqwU`1I8FOQ1L%LXj$&4p~+wQDNvX1yZz>Ek@$YJ!#1W8^ldZnjnqJ@ zGwpSyL*w!ch+s$#n()gfRESTQc+1{Ocv*bnlS1TTlG-H5Iq>UwqUOF7ub@22$P;gq9WowhEcwv>t#w2`yZNM8@sZj+lX%u`{(G1V3?{;x zPhK5gn~;Wk)Ji^Ej?k@~=$-JiX>$j$N*ou_^jtky!9ZJ-j6b`9o<6$q5{0KwHP{Lr z##HWZ=fQ}%`on6ktz*+S=fT-a8@lxlR;pG{j;${zKG+k<0-PE6j9${{ufICu&S=7e zS#O&+pl9@tnL+_f z@MUb7ETK-Opqop4q5tgQ*tc(*60;s|IM1se(g3Ji($mG1l7a<2LpE?RPo>u#3>2`b zOvZvVdMGV7W=>JfuaY$58VHHK8F8NP# zk6#C}6DBGfM5}EO{byUMjIX3q-L23-{{c8Sc%IkORX==~rTfwIPJn->0h0V~lMt@bBDo{@2v!Ac*2FDejyQW6-()nMQ27xn zgU_Mp=f4RO!Q16lR0P?wdUqc>zY-iK2<=)nsWOqK2OoLU!m$nK`C5aYc@a_KS10oK z(a7Tg&)iOwG%BwSq~llVVanRm#Pxb`I@&DwZ>(h3A4w^Jy-Zr#jsj?|^W9M~gZ=NcAw04xretNAd+kv|eb2=wi(oJLazisv%~NrA$>K(#3nc zjcg2?u|X8gpx}YFf!11kn23}a;y$cS6UA`M08-YcDOr7E4U?apJBL4#?ydspGNHm< z3z4=->l9sZbAvr&i|fI5GU(Dkj$@h0@HJygdZc!@U1 zkzJtRI{f(jWzRydMya;?&F6wuhm?DotfB6}=0p?YuY`_E$`IO@Es$`=#xPJ`siR)wHKzyT(6 zetSNLqvsNx$Qk+8>awV%N=3tpl2hb7W(p};mE5gBT=jm{s8D29FSE?!1;6Cpjpj>g z;qi4fQU|<@@S#0Ql)3b$h>20ME$+G1(@i4yz*!kw^9RIjZ_pcQ(Z>b+{d7C!!2Hgq z0sEz2YAE9Zw!EYtPA3wtrF`mD&Itos^)ctbbs!mCpFMWm!gd8 zBDOL_@Ky|3UPxe|S8bZdgp2-%B!-WCn=8`S#4KQI{cWJ$vk9tmJD=a4qKISd%WC#x z;t$Kx87Es(Hre=y%|Lk7;-oL8O~TbmY~{3lOIn=q#&(+OW~=93Ue z8X7`ZQX`?36C|ufHYlc%S3Qo2P|}O0;o;#yAewO1NayJS%5rhP02K0(?j__flgWSx zZ9SWFps-P>wDc=T4I-ajC6kZ%kD#rsZ=~ipKclr4_E--8#VNm};VV0iGnk~j{DI-M zuHjF>QX4RTZoJMvvCXOI=^##n4Wj>Hmn+lR=G7nTYtNT5lZ+8d?gogN$AGRw{b$6a zAbb8fsDUM(CoMfVk54V@s!x_~E`L~^GKme&{aX;8j`9qrO})$*c;gqsWIPPLK#(r3 zR?`^)JsQca77L?YoLy3=mJz9uuM^fs*9y( zf|rf-HA;D#FUaI13Rh~3QEvN^RIHhOc$KZgU}K#@|GZSapgQ@73(W8GR47bFCRZKx2* za7W!8DS*D3J6Rd2(aN7`x#%BVKV^CN+E8g0+HmN*>cm-d7ds|xgSNg(c8)Y`_bqD z7Sn-}!d}r&=cDeHBIW%m#t};DmtnG$(}ao9FwF9jo(On6esHxE4DV4W))OSP3QSiH zqVm&d9HT7VJ2a6QfZBl>`WuC``!Pp1XKV6v^`vo#K%Zb#pdqFDGDPqvS07)`uP0o$ zs#s}pyY71MSXbJ~Q}1y9R+RR8Yt%5Z zs)m9awH1CI6L=j`0RWq^8(q3lL=0WFI! zS)x~Z`{1l zRj)r<=(v7C&oG$*ZKBHkggL{x>f3Qh?`n5dfj?_l{QeU)%5FdaP`(4>jJNk_Pqccb zOR6<*{!y9XBN%LhzQ)>Po{&4b%B#TJ7NPI=p-NlwM17uL^e`jjA2HQ2^0?A!Ip*D6KWi^x~yT7Qu*uij&I*_hF=sA8^xm9RD-nxjFD3ExjMVOl?UuV%s1tNc9KUo>2M?!dDT|&rCP_pZ z*!{jh1)?cPfwzWkTDsGLM{tD^b}w9Y8T~i3 zNq9^M!qDDFJA*hk8ZJE8wEu;}5v&=?JCez^vb4+#c&+Rz50~s>0XG1XA>~Jb?D9kq zY-$9yC>JKH=FVg}Gu<%c@V;3}i{LFqwya@ki7Aaoo83hkr~XXCustMs`wcXcI<4*( zrqybq2XVVRb;DQX(uj~aIUMYb$&vaDMCsyibHCTK>x0+qFmFG}R|m)Eh?dsk1v>C*!lk})1+HZlPH#K6CR`w!XLP@H$yVg&a*2!7l{{G_IY8?dXXWFd zhk3}^am4&Lh2_gjObU|apU6;t3p4yih>KSkz8r4X?a#vNR|AmGW?vTYIU5kRkV^wl zXxe*?x%`7kxljcXbBmBxl1N30J3jsuvg4iTzn0A42P&j<^DrcJn&*f(+OD8e?q5Mj zCO$(6J$K15C`Cx%#_XeX4vR`|TJkf`VZAEYeQ(h$F)p^@d^#J>&a||uDj#LwWws%g zGJ;^h+~8a&GpVp$vM_saRnhF*aFCtea1m`DW<<-Cf+`Agh_2P3Z4Hy;2y1e?;wC_t zDAROQT}+0Zv`~&~$9W4o!9azOQIP|@lAfAro^FyZ`d`^{!Wqnd424V>&T4TNh-`5^ z-*3XhS7&-mGbp7_Dl6Cg@1FG!P#YSjyW=Sdm9<2vW-8RG&07(e%zVfq@5mN9X*1*s zq5#YhljfX#bZ|W^(9SYwv<6`QD4MGV9A=TAj?xsaC1x}g3{7fSgbbUt!8DU%hs_!@ z9@$0`R|CLYu?UQIYa7iB+I_Z9$Mble>EVFGDh2_i!8D(09m>H23T5V?K1*%4+hH%z zau$9hA|k>_u@30E*aJfI10Ce#`y1L*v4{^WH~u}qzbi|u!YfeY*J6WQ(NwC}4*Pt4 z6IoB`6+Tfj!Ft30Hu0OrCVI<~@xh)~*~> z?$J2TkiL{@`x;qwnNCrUiXVDg{*ZOt?gCNYR|4bxuM(K%e20*+jEca5@|bQXJa&f%vEvj>N@qtH`|y zgYmb#P1$jC9+F^2H-teiIG7CLFJP9C$262KOimA62GLHr9fE6w!f>VNM(F6AwL2){ zj}mM$h8}$lhN8N)zwZZiv%q z;Pi9d98)r}gUJ$H&aR>$6i6HZ&hSQ(5u?-Kps~hy=lC>9WJ9UIarA72s#hqQxj$X_ z8oxfMsxEjeA>B#tGah+EwWU${v&~(YG*DV>z~0Fi5~TYtV>FOWBFeN91Gy8v7!Jov8xi5RM zJFKrKEeK7_Z z{-2iYqK~-Y7mST zn|5!oZN1U$7wXv`pfhT8`UnHtmdAp8#>)h5`2?flEj+&2DYV2vn>4|ShVSB=T9w8> z?+Oe?n+_lF4t))hHdB0;FMAu2KFl!iH<0;NrqjlJae1j9PI~<7-{%rT1F;(Lz_bnF ztm5yWW5K!<^vAh`Ou-~IZxnSXysC7<$^w)#PUmarLBTaG{M)<8yY z{{k6!n;y(Vk`h4k{64|9eOR8IHrtSxmU+E>D7DJ*#YO%|IB)HZb?DU%<*c@eT2!oI z6WKaLM2CPa3TOC$!;7T+x*T@&(e-?r%3TCm<*!yWPjj^izS;?AP3|2WCJa?j^MRH{ zTJVdX?j3Ee2y1+vqVl1GSK&HUl!Mcpr5kTG^$V^C-I@NDWMT&xR zGQy>@KGSBgU;(O3=4@1dAtQ`?&hZyx$zv5#R9%qD@p*xs@D3fxAJxf1m31$v5^z;eI^$)H!z=_0#e!tbeOJwU zw4g+}qKwHtwP;_W;xvrIGN|!CVrxNGejEjpUW!3^aQy!Cb?` z0Kz`|7~K69eKkf{!~Sl6wyA-4ES<@^+MO7l9PUB?z2jSV-`G8FS<)D>TFq}3@oY=j zWb$zLen@)6`krC-HD{n6 zt>b|}!Ywe~W%x#lw(?&0Us{EmG%3FJf}*B9lh?fTFV%T+3d9$dxt_V@qnj z@|$)RyO76s2(-bFo3aY$m6bm1)H?I}V;I~*`nSlL4Xwdpi4TS)BYHCC%-~uGfGG*H z;IHl!N~*7ZjP0kI!5tZbMIRfuH0xLeuO;e0LtoRqbTfM+Fb&=OwvaeZX~wi6%$GY% zI_zqyxftpgb_T(@<+V)w`<=sixXu&z1 zj6Rwx@YHxp(WX~{w!#CB0&!cgsgT>sr3{gvp~LXrk8rW9O;TO=fj8PNA(v|sjrQVL zTu~MJ?N$83l2=-$Blk|%1OA6B(!EU9n7_n2@(fzA&gQNXBnebV?@l8wm)jkxAuMYp z2*nOqQA2@p#_`Uq+c#JO9x;-=d77binIF-b3gF^mm;Ep4M+z}&U)uW)sF{d-;y~!L zVpli<{$KYLww}YEK{kJTFP1jl|J2(y^JjRg;SI>eiLGH!AYUkAT(*1l2|oDSWUUs2 z7b4)5E`)2p3~>1EAlf&llyuYyssDJqbs3YjOs@OS(V!L;L~RU6;|cma)!t9nHT7ie z{8X<~JBTbrR5d_z)L1S5!E6#PgxEEnSyhxw0~L9b?U&(QO0Yw4Frz;?v$197j_>OdG0z-lw}qLoc(-#X2wFUogFk&oyF zw?A=m*9Iu_r5#PxGko$)P;l~6gyqeZ@2m3PlBC#d1N6#csUiJaP|pHj=d6FE(Vc)fz#@V}^f_ zUeJxtF^d~D99}s8e#!#sxX7X*whKZM$)9S-je040YbV@n!Q*(LZkF)MdGPEvtEwnjB76dnyh&W8iRDOaMCN8oBCm$mIGa4`M$O%b?G!$r7-z< z!TwCA4`mjzic!6So9nYFw^(Yj@$x*Z^oK;cTbH_~QON6=JvYBO?3W+hB5njs%&^h+ z-Xh<;p$5&Sek+{3%y@iK$-Y2~Pt>GX%k!1jX-{sb=*XinrtJmKIs3q2A8*6ZhSY&j zzVIv~1v&g^)@Uk!Twmhaj=@t7T^bdO8|Jm$4M;cP0-nw4M{$I{o2R9G}D1bo~Tj_Y~TsfM{Kw z8+N=MPf5hY^Dg1cmNd?4df0_UcV#TcN7Cc_?F_Sedc9Vwz9OO3#Z$QV8;*w=zev9> zdqoEtoHDK<`HAj@>q?>bMAc|z4*$HzksDVuN*B6_D zGG{IVW#RfQ^iCOH%^w~IC0|;}$*{4UV4o zNW?=pGoa|RPkoGUn{!5r70AeX&)3AzquYXObh!#O8_hhj3KPBUkU9J=5i3+FlQSuM zO<$)=`Y4h}t(rWiSWbrj93}jEg=KNO0yT|Tm2w06&WM~Pu z@Jo{XZoWKk+*0xvo(C*`dNGM5$!ALzBRz_C>~%0Y%xV217cGq4K6`ilm4`!k{9_G2 zCYjVcJ&cLqPW9PpOa&~RFC=+5XSvMd2Hj;ZY}R)+F0J0KOVemwXMF7>o0rWAHznA< zCQ|+(7NsTL5}Bvbi=;n3CvMbY#0JOAnRT))S10b`O4#+1>g`v<2+K+FH1=&sPBHmd zr=tjM>ef^TVd&%;sC=#v2Qc(^*|#2mE+$F7$$R=@RM-vNR^lprVLHtPIWt|lG5r%yI@DQ?d(=#fkugv56 z`o<>{Blbfo?6siolP_&mivNMTQM-SkRx^EcVFCkKyfQ}CGkqHQL+2FG^wYA|MXf^^=(d%$_6?yc2g4#S91C7h!OkBHWd!I$2 zbRuLD4DDkRLO2Fk1mQ}CJ-q|zCFSyIdGOCiWSea=Zy}>Wf2btM~)HHBlkjir00B*p5x)LnD7QU!bEZd;&JYOh5-IAVLk?z?jgt=5)3fAe zF7`^B?ILTN3e{Z>eFb0hRCo@Wae+k?))Bi>mw4mVOy3=7GEnqv4jR6J0a}~DecGtFA_;*b!~3& zQD+;X;9Syg9MqKBMt*xvB16u1;0#Uc1GTgNh9(gq!>KW$GdKzh@5$d^BEm$&>u3?A zaIMZKaw(#Q+-WjAH@_V8yhGGm)w2S7Cy09w{o?R>nQaJs?olRfHxWNRJ_MyL03XZ8 zZ9q-=X|7%KJhk_24w)zWT%M0CtCqTQp*1gpSkqFTv=7{>>G0VtvEDL$UEWX!stZd@ zj10RN+NqKCO4!?3*BSK>{&^TWhXZ=(NAE`5DIjYVomH5KM1X1IT7 zx{6{PfXPyreBW-Ft)O|U#cJ90HD%s;y~ur@y4NJs|CwojXI;MWy*%89Zj>^e68b#~ zSpmKNt01#ntcDb{4Xyp1H4>vme5@cA z+OKAqA?zO7!%7d=dUm1rI%r8@Xj3qmI=PR{zypN71?>FvH0^{ORgZmJ%J+*FT!Sj- zI~p?_CN0oxPq#^R50bOcAMY1c_#_jMwr~x*6Y0tF(4pVs>nF{WB=eK_x`DbTnu?n5 zOAFjWOv2*SS%aga(7%iGj(mRcua123*9vHf%0*9b?*yN;rBN%eANJpI3oLFEZ;AQ2 zn$f19*KloVtQcL)d3f$BZ+vBGL>4B^wWUeqAaE5nCO|{*7NH}UcNb(G zMV4{rzJ6G1tt!L_u?%@mQPIV2%LO8Y4Xlz zn~ibC$;?S;Nzo*Y2Bek)jG3c-z&QB9z9d zSBt{-X4J9c{W*Lh) zZVkp79;^6l22%AiHBd@hM4X3QU#>wZ{-6K(zgLgIZ+^kIP)>;<2=Mm-Tu z;^cd4_%4{^c-^esI!Iqry=$_pZ(P&`hGP3LY7 zlvb@gD%d0}nk2o_T6F*vN%+9wRf{7-g(2Eq7j=5uJ+zEtY!EWWdRNT={sCanX4Wmh26O;h!LVPeuYT6qK^TpMta!|gd1VUQ|h!b;<8u}CaoUN z_jo=~r<3J9g~%FOSYG!pmaEzzY2uF7N$|1Nf}|QbN+5(Rkc}iPH014x;3G*gpU4L! zCPGQ0vO*x+pTSl>ceMI_@r@_#1)vZ8$58d3-ta8=XjL=B{5h9(o)hXYOUPy@drT!B z&Href|GlHr61uJ-Nwr=K!LMJ+zR#Ultu`!PNsXZ#MsAa}w=^Dht8#t!1P~D}t&*P@ z2Ky4OuQI)eP&ZMlHC13&rA?N6d=FO>7jwZ`4Dolf>mMi^c2{s^Gd~}=q8duPJ?7}dw;$$cHXYtjFZ$7^i_m-S^Wk0@UZ`ryT%0Q z@SXySEQ;e8a_>HYDLefpKt<_v{l`480cCi9I(L14KS|%(>U%VfAsEPRx2cikykW6g zM+78*QC`FzQzm=~5Dp&!^Oi~XTS&R9o6m9_iyI4Kl>~1b)dzS~;^Ioew!ey@zvD4#6a z;r`o6@~Y&TYw-$0@e*7;{YuoE@L?15V*KQw}pg?O z7{g$*;Q(ZQJe_6(X^~Ay+~uNnf3&BQJ}8azPp^Z$pj`z`-BHT@j~3~l9tk*v{Pp_Y!`RIItT1N( zy9=U&JH>tTqihU}rBu1;lQvp~uYrz8HEXIL$~ot%;x8;_Z-mvY)ZC*iH#Ft0lXj}QQDtmZlW z)8Lmt?NP6CRJ#8w5aPfD%$kA6=fRfg=kvUW2BInlwC%gdFawQ3Zh=$p2m~TUB)=N10<*;IoW7a1Ja$3NJ`gR@WF~vV4l&F_Me&=iB`yL+!qgJyYL+jU$eR_BIQ~aBRt@wsuGh*Koz)p;Z{u4N|2zxDmD1 z-pm8(>c|&kp*5BKh%(a=!C|UMpqqXkne9MG>PEm#oi{kZ_<^`nAPBr!s#=~G?VQNr z^&+2F_5FVrlf`fZC=eF(e}}*}aql3uJcH_axB=NbR35|1uNqG#1fJ0%fpj5E?1vhJ zlpoRpJ_HD^I}(Kbqv3X}RUt8}XrDy})TJn{Ml<}2M&8cW7u_`>7zt9akZK3>>E4 z3<%8w^7{FKsKXE-CNO-+`TgSqg6e6EA{V+Ld`z9(A^n%%?I8@8+f_8M8&v>2T-G%= zV?+uJy8~wobP8A<3C1hS^djQ2NE|jg9Z3Nr^9Y1&DFM{AxSpCsHWkEXPKoSmGn;K^ ztei3Tj?8x_4l>(gmZM&4J1jrChYdAds3a)Qi3@D1)La~82#r)^eFS67nNl2FXNic1 zBhft~!Xp;)9QcT}CAZDZZN=Sh`?*_eh*Y!1!x zd37>kbHL)ZX%x8_qqdQ7J{3|h+@E#+{z8N|eZS6yVu3gX8V(MPoX4af=82)>hBqaG zQ;z1NEsHPAFmiUn**uPgJd*_xE)!(G^;c?Kl?r|O3kME<_}B%ux*kAll9>~DY{X0x$3ki z*b}gbUh>dXtdyoAhB5GruKGg@&?**|IPX=J|HA_CA<33jk@n@uE8u_t`R{-M2?h_nM@aMINRXg}o7_(Ir??mFI!cT! zi38@g*5kt=eUDFL-pW|?l}r#BwA#C%rR)C}9J5%Zsf_&H2Jn-Eg9D6cLPQgI`T^G* z1&?ftDV=8C>t8uECZ9LyxpzEdJbOBeAnE39ARqurF+Kej7;l;W%Eoaba<4IbFy%Vy zuKnZT13C66BULJ<(e9qCGY;?Ez?#e%togZv#&S*=UD6pggy0l$;(m-h^km< zr5_JamO?BN4^mY$D8(>`GbyA6`*aCAYH?}7q^-y=NyxP$TLSp;giewB^^VBZIes4U zhd(dTnlbAdRM6I>&dyXMiken)7fgtw?o3O`U2+AhQ4R?K`d_9a%KPc@$AVeFECoYg zxCRiw3|d|H1D0G#b8hM*XbSHBVh!yd{*Tb^#kV0aYl9H29C-XjUAXXQ_c1WYV}$Du zBG`m#tG(%k;&*d57UklePJP3aA27dz{&E?aVOGm(c!7FZ8#1Z7ScR8^r&~O=8To*4 zN44z@E@|nvnOghCn@}YX_M2tZiPPl~;)$$$X`)=hOFF}`@8J8G8d5ODQ^Ls&bwl%{ zxMg-Eq|EBaLN2SNbamE$>#{>SP*{}A-_O+L|EoG z)gfQgmlNM^>^XB;FzzO|2T}}C_tK!P)*6aAj)#`h4h~F&rCRjpE7~> zmmBKHN_YVgTxw95)GE+GW|Mzd7!;B5`SzAG3e&;NlAR2hf?GwcS5m2JiY##Vu8TQE*b>+4#<#t<(+6r^a+-X|IN6&R1h zkk{jhITE*~;>1fmvHGfTGRcSdQ?x9wv>5t^G}FH8ytL(2ClmQI3ObVzTV)0JrAl?x zJKaoa)+pg+6wwAmY2wpESh~fKNCtI~G+x*F52XzWo0zo-Uju`7TN&Sf zk_mG9mEgFG(%Lrco?h~trNtuzH`~+ycOx<|HwTJr^5Q8*vHYM6Q$lVzMye7^sruKE zmA+}^0W+6yKyRBIKGRN;+cY1>tmM`5n3!(c@z63m9W_4@)w{ht66k0QG?&~?rhm}t zck2M{oEs@JTpSVAz-M589L5k{SgFQD>_Nkph8p5>g{m}iL)9N^AZRXacuJ}lg+$Dt z*B>O%V81CX&nSeh131Gl7&w4|5-H$)nDfZm%UDsGG zon>Ia>@w{W$K&1r7xM(A{3i})ABWB!GY_fkExD6HEH+&J8$7?=)DnRdQSCIc-H?nl zg_Drqt7;AJw0j@~Ia)I_;(FngK<2HG;e=1j!ZC-9w8vtZV*er3TccJ=ts`gaPFq{s z*pk%UWc#~43P zW&p;}KR)(P_kkI^D~9D)1~jOUY+Hy1|F3*l;8-rJBiRFwZtr2G zrtXY8U1407)v@{^^0*%GfI#>Jo%Pdh+J}@Z6j;9X(un$B#?tFGfX`HSId+T+yn3dp z_|~!j@rosKk%BMrqSp?L(3#OPW+q2z8V6^?z+g$Cp>)+%zXevcb0DjS6q%b`!s#5F zJ9b2*2roCz6f|rB5FxGHvv~~IxGpiVQX9##Q(Jk_ay5MBT2W-i#BlhH5b( zVPl(1WXuzdH>%2y^NJ?@5~V!8B9>sJZ?2*qG%#5VvT}ogQD^LBkL_B*^*?jqPX6qoX4-3Ca9-86Z=KBvoGI zJ@w-qNWi2Lp*8Ne@>B!AbM$n(SY>*@ozN0lRUh0E>N+ct-f`!t>!r45>oPQ-y3~$L z7bt;U6^cOUiZvQrrt#X)!X2CZ#^5RL3ZPQrku>In-w0Y#boW9HZ9EIoU@jdvaJG5m z34o-jqRNRJwtm)df_&z=5e;ZP$7b?Qs}s-h7F_#?fzIl2g<7Icdi^dPpV16-SMjy8 zmsqwNuB`C(IwLwAM88(1Z5cvdc{8I~XQYlPs@QIl@OYF65y{8toTL`QPl9`PnzMKk zSOosqX_{b>AKqVF`CuZd!FrUP;L&+KeRtH4kPf1BN`)Ojn}Fa zt{*1I7Z$Z#7JO}LB2lM^Q$b!^+VlaAf7ZQHhOv!jmfbZk5M zpMGZM_s)EHzn*p0+I!coT~+sWUx2h82^sJaX&I`Ro+5D~TGi&JJM<;9@N78Nn*I(+ zfA0U=c6fEmdCyRtJ5yV#z&eN5OefD(vfVyLcIR>HqqmKQSlodQ{Ia-erbI?5b&Lq> z)-puZVCW$MDvFeA75&suzlFE-M1n-I%`R;x7qKgN1J`GykBb<}AFMpF+V9chF))Rh zO7;&D=AV$QB}a~dJo;UboR`Tf@7rQKxh@%O$@mWMf+6c3y}ILR6YwqAfNneN#ztx~1iH>d9uaF*Cgkt%kF>=$r0au~7BYjb)S0h8XPysyUiRBRpiLxRT z3H~PpJIfCX%SSG#0@NOfcjZCVXvOK47grPVJ3gf&Kul9lfbA&+P=F@&e}(=wVc?ev zIec>yOD}CGJQOe_BdwSFlqoa*zGc|j88PH*1!}S4uc>c45caOG^c>k=CnFrY;Q9uZ z>zHH)?ow-h89Y5XanK39ICNHbIwl~}&ksI`qTVwC(v#6@qn$Z5#ie8l5!2#{{1c&- zJS-KP>#NiOA^ioFJHiHy5~`*0k(h_|!aJbOX3x=Ff&)2w5?EBQWrR<$?rpBf9%y#X zy;&oTxif99fQT?xz;_9$W3AbK9qImhbANEK3Khfjp}z8J&+PgEZ#- zL(mE)AXuMT94xQ=GkO|V= z4rDwK`K|8ud+d2a`*^k}>`iq*7FYO(-EMA6>>17JC_(*Ms-4WGVFInt)ig{(?b&j3 zrLj6cryQVT|4dvz`)oAZA^hAv)_TA3SaUiz2S1}P@5a^LeJn1q%b))%_%}t`1mo7^ zEHN~Aj3b#<1Lqw=LcE|pnlpw+L_KA1cxiE~E9VI<6u2ZcO%;z92VSlR+TxsCkf?l( z5Glf#U^TOAZkGI6=N`DeT>|_8*&ZqEw~fvLz0gpWUczz6C&2n5DC4i+!Ej+E*V`G1 z_E(fLv3S|s%Un1@$wAYv%P{x2?K{gemQMy_;F-^7T+$W$!B1;6Gj9Ffap^7tFtxm{ z=ssU7dpEm?ms@MumgQj*(!A%~_r}Ln#T{<=QFF}jIGnK(%y4@wcyPG(D;d&uk=$T% z5He#jm`~6z_kv!Mg%vM0!cn>Ci((I?x19;DD@IGz?_um(rS`S!Ktnz-LscwaONgT; zlgKIF4~j3vu(~wd&D3nv@dQF@42~nI*sp$BeX~*qrPiT*$S}ur@Gf$nrqo$iiM-@R zRWtJweXZA}%yA|3_Hfx5td@G(ZLw8I;Bd|2OK!%Ev7RT`_2izOenK)uxEK*x6{XdS zz<|&bGEIPe4?aT|-i*a9LR0$pyx*P_^~F51n8}CuSZ+n0s#8Oz~|w~TRV+*jV@0f^MQ-Fv#cul7Aze3cUEwP=A!zD zc+(|(6fgsc0Lr5AM9DgmOw%+f=$<&J(t7uUgIrNjnF~`4jaAGd5v2vY(QdB&T=0Vc zhF>lQzYd>XtdIcDL&C<|iU)fX{Dp*~UQ$dTq0;+y6H=z=>)O>T3x{sgGG;%~Ywd35 zza=#s%(5Q3?5AM+|J<`|qpfJWb2!8Ffl*41xAqX0m*9BGBXob%3f!}dA<9!l zwR$&XW2|zQ%2h|fMz>6x~3^;B3xWMJvZjmw0af;2;TmKILTx7nxg2_(oh_pUhcCG@bo zOua($NHXzM4z(2pFa|ABJTc``xVxu)_9V=C4}@Rm_{4+^t0U`Kr%_YI@wJC*=$LN* zxj_vD09bQ8D9$$5sbubd{;QcCTDgP>&%@l>WXuVala{jDu>M#|Ize%;qkj3cgM@X_ z8>Y|N?4MJj5%IV>;(?|)%G3$n(!TqA02IHwqX0u_Qpgea!AA$gVnJ*E55oeYSyZ4_ zY(Igg$m!t?{y-s-LG?MYn^S<1#IHjf4`hVZk^T=FIb2m>r;HC9q?v21nJh;v1VH77 z=G|ObKCb<+C%wh!wY88N3cG?-6zk0f$L^3e0hs=I>>EqUzT6O{@e-J~auY|&=}urN z%ndD~t|7H*{PAlZ8`v1Y`jn?MrctZTG-u!U_wTI`&2_!rZIg^&I}CvIvlDN9e?+eP zihnbLdTk_)cA25A!VmrSTae!eSBkuSmuJaUBJaRdd_N7anI2vmaLbN$S;DEN?QQAg# zy&I~6HERSvUGze|om}*Rh9*A^#V>QDek*$CtcudUO1+8tDVHy)V4CUe`>RK$8xknk z{_{#|UFoVP60fC%`E=u{S9e6+-n>v;^X3Y46qlDTvh^wKy|J?ilZ%ej+(#BHdP&+B zPVj8_;RI>r9#LWa!`REK(GnO<+!}qpAf_rg*|8tWGia}<%onCXEXB8X>oUn(Q=efvVk zU67_FVZcr(Q$B&CQ+6&POn%I+a5$j*#h~$16$-hIwYBY*=-ZtMBQa2An$M9$7)hoy zPL%j5{ioL?C}2hthqP2p0YJyW5!2$Te}(`$T8XGJB|5K>zJa=gR|WKNBCg!lO)hMG z-OXb)r>CE@y_q4*EQvZTP7kQ1i4rZkXZNCkTNj#LnhzCQe!4a^YhFw1lbD~-=LOT|1Q@ijikvixkiA1_w;sy5EsV!v zhCI`Ap_Z_vw(y8vXeqgQl$4ZTD*IP5LcOK-A~U_F%l5Y}>zDEr zLDSGZ?f4yF1vA4l;o?PN%M#6`mP$%76iOsgmJvolyevK*HNktR* zSQCGpVkd^mYp=5G{rE+D#^r;2J2l~Z8cYn;K^PfI=`MzZofif-6qMi+J3R}b_0>C^ z8!0_{(A%X1VZ&77 z1vhT{YT#pRAt`P1nQXlR<>LgE?A?X`m4d$UD4h^-NpCP`ynC3CO>7&A3b`uutjlcx z6z7ySmD>_^Dks8U${TT>kR6I}ktRd>&BETGFC^77@auQ@DYNA+Qk2K+VZS8U+Uo?owf{-olu0Z@SW*fa81pRE;=3@%crwClBv z%T(L$G}9l3kI&g|x>$5AE^Y->78NA5wnGlCO5`N$I6o%qvvM9bp*utibD5}%b$k1A z9(Rv6T-B581x`N#X1W!yYheYHRl1=q&DHs5Xe{GsyUzOW4t=etR((ZQt36RZ9C z$h$6Cs3K1X&v;eUXJ)g#lU@|R3g(%!Zdcpvl9xG8kNeqcY2==VcfX&f_YhX0#ko3z z#4OO!6fr}TSD2lvym39y%!179O}nkGAq2oQ-s~MDfR`%TZGGfCJ5z<-uvi8hjFDHu zFbR1!aEj{@CT@;pbR34YF__J$3#^`YGh!F5!phm{$P(-!j4`JMK@v%`fJaZ+8ucO> z#rmq|ph4S*9zfFmSj2u6%mF$xVezYU`o&hIK#7Bsf}((g_1^OTrMUq^I)smsDkuqE z(BfD(vX2o>J`}Y5xpT|c*I;26bbg-Bi4n>lx;3h83IVPPq3o`TLPMdD8zst=!-<5< zuQBtQJy2noGQJI|G%9U(IS-eb@%M`k7Vq4d#P?$;Dw64vAU1l`*473914)QNtE$4@ zlOzE0gGl=Hx(E&!SZHvsA)Bo##b65zE<-K`tO5dvA7f-sz1mKMHa1m3A$A7I^yBW` zQ1VbDofn&5#(g?N2Z&u1IuIb8CHoe5x*O-aklSZIbhb47rLEpp;3mv#6w17LufB9+ zbznI9@PsciY1iGa>0_lS_gBKh&ZxhZIOZ-fN9v%2cXraL)Mfhzs;FELd*oli_i;Bsn)cn(P=3RzBFKics2|wc-eq@d%j?=dr zry{d`=z>4e8QmMfZCJw60EkzOs?W}_GgP;1oR4mbc>O?=)MQ)~XL;4jhn^68H0(E~ z4p#11G*Lnl+jo8MB2@C$32=69n=y9V;TJ$WjqVeEiSD^tMu^N!p)1Sc%Y#Hsub=#u zC7->g9(Up?n`3L2`4Z~+4N1bNj^(=cj87}pO^En-dVl0X{o;dO)td=~#wjV+;9>u% zTH}{Oy_fnO4Fs!`my~5v@9%HY1}gEQb#3&{&tiKu6rVEtBr_Iy&LkrZyy%SOb6zI^@`YeG-1A0bgFT>eTo39DkS(S(R(|IloB1>HG z-9GoZX!;4`{G*oySs~g-EhfFLu$>*S#!>`!yL82;n@X%>IxiMPeLrKcfgzvW&j+~6 zGt{Br5hun0d(u(f1jbf45Cw1~pv#DhMU~Sb1aA`bHz_`yNtUyyO^N|mTo8xYDj|t+ z62wm=y>R6&?zz-)Mw=dBrExz>%?pMXw!e^4PwERSen)#nBKu9yw~$eH#lYr2pfzwu z`mI2*=-uq@!<)O=l6aTwTo?1AU%8b{Y|1qp&x;o)7g2vOyt8>g6czb3 z;=uxDn%$d<71dsQmL0j}V!iKt^b6jdZ2s*&xJKb*>UBW3PnFgJXfkWExI|>0Z*&~g z;~Lc)kCWtD=E;63Tz{OBTJEAdG^HYnd^l7Y##d)JY}@4S5$ath5;meDZ=V~aDq)KH zF!tn__G8Jn)vgR(jWQyfrpx(g_0LJLq5M1W2G{tjw;9uo&$><`6jGiA%Q>@GRMyG8~C|N{}U!)S7&sUhsqOIG-fTF zsli)lIollTE{%j+Rm5njmhotB6W`gdFjV;51y%%fLXwSQ!TO%te!IS4l+Y=UMI~DCv^r}$)dAbbeKQc?iQNrf z8eD^WB8q83#JARL#Mw{v2H0ww$ncx;2J%pLc7L$F%$1dte5^q2LB2yX~Nz46PU9rD-U9{-+ z)!>~H{K457i`&%>S%6&Xnk3p;1dpe4&+#_zI^rRzWYV zL>La~LpHlRQ!qlLuCZ?vRbxQx&|wD9S3taMln@y8@vsj{E>(lSMGKr9!M9NIn#(1n zk(br_F*S%WS$vRX1%_sBFFs?ZVOLt7L}77TYgprbu53l2f?v{6;@w{_gMA!!p|ZAT zJ!#&m+2A=%w8EaJB8fw1j+jPcSnG8S_ePLQyuln2r7vx)5dCWc<{HzxuaIn0kkLGq zUPX%GK)I&w;B#k0MGT%^z_^E;osV<_lWjmEAWKUZ_8L(n^>V$1m_RIWFK(Cjc{RR& z`hwFxzn^Nkkjd&+fsNcQN)caSg_FdFT=<)#q_}+Fd^1mX>elA=#obn^j^oWTtzQhQ z2!Lo8Xh&-)tJXLcnp74ATYf8u2GBVCO$uSvhEZzjgvdrRYWWU>devJLE5FWK2KQRe z-x&uQwm^e}ZAyVW*ONSao+(#3HW}Af-GPC%cdKo(!?g8c1xthmzhc|`i@u20#YF=jx1^1;T zT3u$!dvuH~kQcFLcm&WayIa8P+^TG+3|^SPq^KTppyjCIF#FoUrb|)Ov`ph{JJh%` zRGniIK=${r<|~{VO58hBm4>y^Y>kU%U!tEb3KKnReLY&q9aks!xyAH}rCO>&dH)2H zc|n-Jt&Lo&QNYRjazv8&txSO)jgivWDV!M8+}u1gIvPWm95uZ&N2k8FHdmiQ1p?XQ z)EO(WQFvj#80Lc0#`AGlIr zV@BssOa>E_0hq*5Qk@V$g=Hb2Q9SYp#a2`flaDW77=EfP)%&SEYyx93NW9BR)nv`G zP&04I^Kp`1M^&tf(dr{pfDwhR3i5R;Ch-y3N)jaI2t_run(!`*h;7a110tBURJ0ju zv6a3O%ApxjKk3j^ihj4w(&HO|{`bj4b1i^uC$Z%Ri6V{SWSK5GR#*=!{G_}=llcb$ zFD2?_*lF~)R6mk-6mgcy(8m#F7oWyA1N`C5PvYP0*G1-{F0s8b%)Y_x{5i^u0)@=c zA^`~2_u+=TWlA?U&f$x}#7x5y$RAhmF+oDs9_uDOa*8WHAIM7f$Hz~*w)QUy9#+e1 znt04zE@I$UvoJwJb&zafbtER=OERw_$s!`wU;I_3S#&ALj@v@)YA3`oG}lV@s->j` z_aEkJdtjSrf(R~9@~lO(t5H>RL^ZY8QZv}|i)bJ!+pS_0zos%4nVs`H$hb4)GE_&P zXQ)NI6!*-0_i_^Nwqs8;230D;#lcp`ODtU>+{1G*B*rX-8IbELA@co(0p}GudE%TK z0!zRy2~tg9w$LiaWovUuzMC1(r6O?JgeZRjo?Au-tiN0zJY&)rk;an%x)2%LaBH@;V)QLu(<|VZ} zeVC|zCa5(HN}NQK3B>xQfKlyiD?O{Nne@!uNO71Zf>nHpo`9H8iIXnaTn&|g`Y z=DfB&@X&P7*n(KGh%eHEeY}~B`B;M#E>bs|VU;QvnkZ8>kD0K>f}!S!?=LGl_gIq7 zFn)(F-jee(W8Nc?_uYyaC*CYUY1WBPQ?<(1T0&~l5AS4XP)DM|in_9mdg&1c|6n&I zm~?BNd=4dyo!gR=b?Y(^;?{kZx)?>gJu{|V0iVC}lv+`qwdm-+{ZEG@y*2f8nXbwD zJ$3-$5@|u5hRlRH?R5S}m_V-|c;0HI?k1?SB}tL$AQ-%lhl!xeZ^4PF0=Sa zFS~^JPNlpaV4+H`{7qj@7j}$64-e5h(4`SZ9^I>Fn_u2+`t z3?}M;T~;RJiDgLK?i8a{IMF&a&9Q+9N`B~a^tuZ;BO20_?mUXC=X>f^xXKE9|5!Ko zWCo8MkL_87#mn7AlU=S2?K%Falv@X+b;+mAHYy_hbJ*<$jv7Wzux$&1r}&sTs%NJa z>V4VQ--V~2+aZ)p%C1{7>afCYpW@V7^^8y7HYiqW;Wk;xxS39>m<}m*Ux(4!3R?EBt$u`3eHM4)8Rs|Nw69K$QrBR{awVgFXJFgL`R1Jv@k>={UI-p6rYgL zppJAkWVP0JyA#E>i8Q9@?VFqH);p%&3G88u=cq*Jpk@Gf(Poj7`0;&@zK}I4b2*BJBy%Xk-kS-vI8-9;lKVuuQ z0;L#>=?C00n?`Yd82??Chh44D&4&S5meYyu+i8xpp3ubh2lTUJ@^oXJGK5Fw#z|Cy z?yKFu2>)&VV=$|7$?@5^2ntaKuJehOvfcFI}(vN_eQAn)gcE>0Vr~un*1E{jikZ`arq{VFm@*B&=VAl-t8d^oCNvmkn^-Bdepi!X+b+5RDl3g9nIukvlsk7_A{ygxD;LDLp?2v15 z>nLbcUSCumVfNcO>da84;QV^B`Mhp+u|XT8F3;aDyV-59<*NsQB?wC?m$&Ah$tRlS zX1+Fe=&ZL^;-4`Z4N2WK9n)7zD0RVRqt;>kGeX;u?jAE0iIs7{g&`Kg4;Y+lIGN4@ z1zSM2JV;%HlaedmV-jno2^~g8y;En-L5W`YBmeO8N0|g=UZ)J7t4*&Wwv0gA?y*=i z%E8@02QVHIi$UL4n;;9qz-`+NDA89_jr|*ju2!QvIpggMEkhoW>SuH3iGxIYa z2&5*K*}sTvaWuBo6Ck2VG`17-p;nScsh7HRS#G`v;sbX#$#!In2TWW5@3^dvSr-k;l{)fOrg8%9iQu64qRMGG%beZO$?~q+_$lNrX(3bKq-X z*pX68KC2u6=DV%3i)XE89||6ngpZ=N1R`B`7CTP0rcL5V_TwG#(`F6%fo)F6YTEps zHuA+>+Z@lGV}w`ONF2HZg(8n*Pt_SGLC5GVdxx^~A8D(_@{|Uy&w2uy(Je(ACgeID zSpGqaAMMWFhrR}j4H_{=bd%4KbRlnve>YD&Kc*9WIq$emK>!i5D=`#9og+}3v~!M$ zg%v7(Q)v7u&GP=`fN=6oucZ`oMx=4X8v@O5eo$QsYrySYdF^~dXFM6Q_ILR3KHMytILQvKtFdFSVMbVTw@ zH=D&$1tFw{_}IzF*ma*|s9LAt9G?%WVYBZ%+Yt?=VIHdRvHAy~O^md*HJ_8Yp|n+qQ`in&4^T!+HVtDVGiuluRkq=kb^+{YmiRv#R$ISh*?6STGt zRJ-0LGS;h?GUu9t4uhPc_b4tMbffSf-nFR1meq{#MpebZ9Ypw1>e~8Anr zYgZs85Rb=QnVVFCxMx|c7#+z}1s3{1B~LMt6j6gITtz6EXjg$Su0g&p^dN+hKoM!w zvR0xK3f2S;!b}q5ff$%tj&7vEtL~c2qvmRYnaEAqnLZl-uin#m1i!#%GBu8*$l3VX zYyAO=pC`ahliKh$EOCPT-xZx)crP%HJ5c8f&4mL%zfJl<)$6=zDe#?Nz$+px^8WsQ zXgDe(*YMK#7KVX(?#r-A#=z)=C^IoFi;OGHZFD-icYBiCw>zUAPJq<{8Weq$zkQJ+ z0|__jr-lk>W`*)_sY1b^5Q4(AQfD1?Dq&_OpN}qOKm)Ag@M8-=CBC77DV^CIkn)oc zV(KQoM98-*%FW7Vtr`B>Sglu54IQ~wa!>Kj+>aUJ*0gn^t3 zw0XFW3~?E#_tyfSG1@Xt(2gEmR{tl<<_qh2_7l!z#7 z88C-l5BLY~*-qm34X_pw6$Oxzf+Hg%ue|hppBXJbmrSD01M;OQb=rUCwPam-ZTEuI zt#dL`rR9?+41qxC`>8kqjHwXn>?PdOP~8SMO&SN&LrDu2?|y{WX1%0(uAul+0%3&<<->_m9CZjq!Cikpr9{n z!L>o7!1CJee3gZf*Ujb@3|;m2O{;N!lAg&}BJy4WVR3EN=v?UvY{h1MW{FsC?w`K7 zEe&0&e_p6AJ~+%vi!w@G&6Ij9Z5-5}P+w9*$3W5S|FmBygng?ZU5|NCSvY7vO=p{c z0k($6-?hxS#|JIumY3BPM{G!OV~_xhX%1v8gb*D8$RbxP`IIE9L_*=r0K=7Fq9pDe z@#7H=nNd2wfVC!@jdpox^#N8U*~(pbc=(|(REojo9k&hE1T8DJeq#nyz3&p>*+7rd zu^a8(x^gAE+X1wVh>>Day&N@L?v7WqL~d7SmIN}Xzx`6b6v%(SuJO60`eGEn5>eo{ zEC-*}DGywzW1-MIz*7?q*8G4H0hk4C*#|Hywr%P0vkH}puVP{&n1ZVe#N);kG)^me z98^@$c;XQOo{Yp+=H^totgNhp8){wdP6}J5G8niB`H6{Wd4k5=*lPI4CaEF6=qbVl zfBSV|%^hm+4Y&hOS-!&C3xa>gwjwClDhhbc0&WzXf5-N2MG`H$TIPlt_?N%G{%2q@ zBfp>qo_)IiRZe03P{SvO(;yoOB&X5W`#H_7yZF0iNzX?|IVAwazLrbpWU(rvR&x5h zCsb}Yv&>q@bm#Q)ei0 zce*4eTx%NijmB-Izn8(@2S|CdO#zSQp8Mvv!#v)b1uEkDlZl9k_>ukuTkr7#4f!P;Rms?_ zmXQz;5Ja-k*sKD8Md!a;_J3ciyf$*Iu$BF}+ltM9jd3_FC|)l$004?fr`@~J;nFbV zCov90lfw6}?oXE>69VbAfT@=`(EBOGNz^KmuA>gMbPuijUyRq~f?>A){4t&hIb#~P zSOpsR!3aKxyxw)gJ&kcn{);K_ui50P03{B15Fw_&r1{Ux`io0%bCC3U7vOo)eESAh zi81-h-xoa@7Q5i>!oo$#gjy$}gPhR@&q?+j^1(3a45)yt2bVQ+NS3%C9Tm6x z7>>dX2y^_`@bVjw^j47n8OZJzRY|7!`&qGp-w9jEBIt48vw>6dxxvJ5U1_$_{`U<4 z>kJ?8RJ;9NE$Qxhc!7icm-hH&+xej(h=p<$RH;op)>*|RVCd|GH4Ed6&ppe8>YtVs z$Y$xZ??)RQY=(x0fr9_Ef(&(1Qk6~P|NMpgjwjwnRZ;N%oT?m2>2U4;gL1qfi$|_T z*$!Lvn;1bP3jNrX=fl)q?}G9uh%zXat&ux1^60$3mTx`6AS4ayyUlT0wtu2FL{}|+ z|A7eSR>788HCxk$y6eaHfG2wNOj)?fo^PMbo!h23aTA8sflOv zj0G!TjqO+L2vNtfas{2fO^ycCyC>_Si}M1W5Wv54akpio1PQgoSEft#$!=S|l|$o4 zH(q%u{1Mj+;GeVU>L=&?&-d$VA%+Eab_ChWAnm0vN)kJ(N6xoBFYLJahm)zvtB2e^ zGvksr%#^KhFG613W{fU8+WK|A5-^3$1_t+RP`EDFvi-mVah8SWXsacgMDu4a@-ZuvvQR@s7y@@P zcgKi=m%tw#409WDuIOp|_rY?Pv3&G>ms2mk(C2v&e1b9-$0{srGwu9dF}9k|o|Qgx z6pu&i&$H+}9?pKCLq+t51`-_>$3e56 zoUPtG4^P_2_fvhJ1tV_4NXO~}b>y38 z99XuCm8oTl^jjC>&(?SQ0~(9!|KFMmq}MnK`ATr827K2T8N{PE`c20upq4KTotIy! zQn5&b`E;y8Z0O`Q@~#3DQ(gb%7khen1v!DV%*k&|oM=3xn9#43t5o>kYHDgq3aUaD z{;NFr;)kZK-u5*mN-}SRVczEQ2aXK}JWhnLuDS5A4Wfr2K3X zoka=w?jT>!-rc3z)onMlUA~n)W{Tm_{NAp=WK>1&e3G3NFmo(JcIYH*s_+zd+Gm|N zZluUsC)Go@H<}E#_TtSo^R2V(;0H(N2rgCbtY!{V*&dGWWp z)WoX?7*3%Yqfzl;TM5j-pxgnGJ7vNrPJOCvPQDswydW2A3=9>De&R&y^k+i2u-AbY zam9?!SdXC|TQx$+=U2oSv$qE0P@?Bsrm1^~1ET;!GtW;)G_4;T zdR_!l&eAj7M0s^QZbw$&>6F48=Y)C#bpbtle>NS}>%BBzgc9$#N^H^pXfhf~9gUB9 z=$7=Fe!Ql-_2Y?_MVrkrM;)A+Kh2%auJ1n;%5r#V(C>*Vr0UX>*wjz`&+ZdW3JP>I z^UpN8r$Ke#AR|ArSgZ_#L8tDlmy%)9LhYPjg-v^nTj-+IY8D1&APDQzKfHFiTmMG(+2fFeGN5t7 z2`M6MK_WfD0{BCsB6p4*;;g!qg?o%78xC6#2J-5IP5^m5*>#C`07sx7(zs5}g1@*U z-VKZQ>0^2;<*EmP=r_3DyUCL2{oBPC|B0JpooZ3=kLNQsMU0GIb?fvZT^LT)X=4i& zXi;^*M!iJt zFusR!G8^2}SNvd47liR>h=h|h2-BtN{Jnp(v1r8*rBUo1hDuVsrU`N13K+Z6!1BxR z$PMPkB?PvA7cf3=5qV{2t}4e`!jBuj-KH-s>~VubXKXKGdVI2pBkiFYbusJ?s4O?# z=tYi+Kv=uhqy?vurtHw)bv!y$u z&>j9_)$Tkl_xBvILXsQK?W-Y$u)Cw8ghlmWxQ=Ff_hj{TmF6u_#?K?^OhTO;zKaz# zM&*(RZYjQ2TWcwgPd{4fi=+kvd>p8fA%~1I%XYC&dW|o-Md!tr`?e5qQkGmmAL1!t zTJGCO0+7ONLMblAW%dlm4$dpcL7UMRsMLF5qTVrym}kr(U;abM^TjW?a@J0lsSyE^g64hhm6Y*ClF*jmp@!HDeAnwyZ%1%|-_mY)&7=yl#O*FK&g zd)|e5fBeS_z?~x066fb>@C?aUQw4I})dzT1($(Eb=d!Dtu|m(H%P;1l_MW)4FsUDZ z4%5_UDso%)GE?T0aS!Q+k56aBN%F5Mpiy@g-O##EG^JwQJ_yEZ`$&-b41|9ucC2&$ z926r7fr)@7AW=F98u0R_U)O%-IlzzVn-;NToc{AowK~t_?&FR>wc7lW_saqu{xHW< zTPaul3*dR>O`+8FFb?AtnfU^ zcI3sECB;N}Bsp^wDM@_`&_^b{+xJZdTA1BtAe6mfOg z_2M61t00h%#H8feCz?2FE;&82KDX#v+DkYV%Wh!LnB42l^(>2w{7Qs`&D>qS<@8^EXw`5^dllnDIx2>OP@8p zl#|=wCe)qX0@JE=fVcv|&d!cT`uHIN9y<}YDbOMT1Oc2p;L_Mng5YnFh6Mts?)NW1 zlKO*(4Ndgt`*c7Hp#K>E$@)Eo{R~yK8RDSyMN}#hpAqH0QYOz8Eexri)XvH_#BNJw z*?yawAfL3H`BM0!CaY;MoXToLQV5+OP!Az{2=n|z2>CK%%cn=^kCm4);Qn-vXws`a zMQ)fXW`FtI1I(PP%ABQl?dV>VM*)&NbT);7D{qbnQkdXws6{V_JXsb5a;&JhUF-#W zPvN{>9Gb_!sN-=*H6PSO8^>^4D_~|_5)C3`QO~eVwG!uiKN=C!JlfP-ZSZyPbx0U* z(O!tH#ADfq9gpURT;DTv#FY##q&jC$6o^pdj#+OrCCV<+BzwAsX)QDR2NNwmMSi`| zBe|>V3T6r7+2A2p!Tkfg=jHqrd}`}8qvq5r!sX_xU+8iqi=tM$;c_Z@so>|53*I#D zy>|U$$bJQyh2_uFN4k^MBmwOlo2M?uxZ`lB^)`&4*XuhUQk5A}y00H4f^pKqR?EYP z3{Dw!wT?$K3m`55Wzo8?S2O5LJChcbp?yQ4BzA=F2D_rlj?h!=~a zig8SHbMOi|ClBK>OcgJ~$~qyBUf%>v3*bT`_e|2(Lql-DIQ5LthwC^pk_o+OFE#DY zgbIJgzbsEworxE{W?i&}lMWzY16BkAUahV;5lH zRK>b%4Jrjh=Q_IGKA4qk!<*>FjzLaGk&|s&h~_(G@3hx66ydtgXP_1fARtV6UllD;qPIiqmTF3exu|Oq=dU98Z`{R zuDi59!G9~|Gh~LcWkg)(pU5EPKZ+3MAw}B7rJgy+a-s#>vSns=;_10#;+9TkzU-*2 z968s@hwrxJFht*!Syd|RY{F;W_k?al$elM|07sf7ZLC!zMce~PiJ zw%eckv$r}QNMCEjCzeBdFCkxTaa5WgM)UF7f)%LC)dgy(v@x;L6A**amTk!l`oN0n zZ`zZ`QpJJZ^a!J-23-t+|2PVLYNX5XXQv)Ny?jr-ImPMPRsB=KrW$dOjN`)!=D2DJ z#0ntY3~oY`uF_gSkgbb*%XH-;=dT9S0in!mKvuJ&S9<>Mt7d=7IR`%mg~cKNs<~}R zNjdTp=J7$Vx>+t9M@`fyX}^qQ?@q{=^r+rHs|c+=zGRhT%=;EwGUu9mSvKieQv;jh zc68&>WvHA!+oT9w&hO$L?d0TE%P0hgI=_&@Eapmp{H#69-vSh-TAAse82A!;068w#5f2Pex=uPkvb7=Jp@4`k)H;ixWuV%i_C*?nSM^j_<2%J?v|(4eBfO_A&=*pci5$0gfHF=la zulfYS4O~RC9lZU7#!0 zfA*Ddcu*i~oZah%`6=NX%0aSIxl5ryRG-FQ&7Q+s_MDYlH*>x=_?&gUxv5Dc1Ofl* zXoe&&{fA<*H4GeT&h7QJUr!Go9v&XBeTi1piM6bXWk~cFBlBBFo`w)J^d5^0%>`HX z(|WbQVsS2YhW3JSe`7*MW!vZrALx4;@})Sw?-6-gjyn5lmoqxUC>(7eKD<&THDG#e z1?WZ-6j|{Ww#I^V-^taZzL@;P5z|h|OI95k>2vQ^XTm+V3VSH<72tzUg+Q$T%6_wX zYGyaAq}=^m4D4>^!cD??&7j~|x5wCAJ;{5o)-`mPgmv8#XP6Fp=r8GcsStr9UOo+$ zQNn%zixztPP}+LUXdd!ZqDgWQEG}ldQ0HRmFutFMAz(hwkNhq+@`9(_2B_E#py!*9 zug%Oz3G)fpgzOIa9nBU;{6gRssC`mtRDOF&sA~bK#khPVZL};Vp_^Y>ETCH^Ie>wOGdR?(8oSI(l_*JfPbBd9(=+jWTO4juUM?>#IU9w(;cvWkNd)4&-iR8AMUh zDY7JcU%}8T=xAv4s#foZuhM6h-yhuuH3G5G zp$?Knm(rEml8KKIe>m;qc#A%t`{{X7PpCIYNR%~;RUn|>bgE|Ig!}xCj|!6+?r!;c z5z=P;*~>hs95jW=(c)%}H9a+dAp?i&4%WvNy^U7ZOyT_+auvlw3=3WZ>k>2sy`&^^ za>8ld4m%FFNk1lmQYX^pV!ZKRcqruq6e$Ho1ld7eULMfPHCcY}2c+amy~$AhPlVCI z2e2U?bTtvH18-AY7%3^K*0#1VeVx9(zEDipL5pkfx;K+jdR1YqG8Hn$L6Nx_@~7f_8hId!28* zj^og}gjib?VbW2%9)K3`J(47g2pR_~##Npt5d^Cgl!~(J%5#J^$PizrXi!?3q$MRM zUk-6t5wtj@OP8MDLJbM8=b(eKbj`PGqH%g7fJfgn{5?u^s&>^TC+>p10eQHXEr7oM z3$md@onV){u-LNmYNYgDN`mmi8K+^HAmAjWTe?UwvFKp5|eiN!xB^jeQ399cgo_4H`LSU9kwK78S zioyZc%_d=?5fpdL9l^ex9QJg{2?L?LasKUEZG~<+W5^auKRIgL53a*PZMR^}!qiwl zi1QLp+8d>dzgUaw^M)359ydXE8}6vl-fx0^a0STS&B>}YQu7d|EN8uEdup(lS|Uv< zqALt)ULV7V`D=)$U+CO>2fq_*48sg(m#Rq81BgT|#*%E16+L1gn6} zdO4|4T4GdauHHAJufySLef`Z$@?#7HnrEv`SZpxCZhMV?3ky$Fy`4X!H37}arHCuj z%(vqsRO)cbPtWM75>ZQ@%ET-r2>f@c`eXH>z$N)9%^|caE$bYiy9zC z-fOQ;r5N~5l9wyE)I(R%1C+l=^}0)AQzxfqG#vR2{0!ME`5n7w8?aZ*J)vmR{m$>bm4#@`;ynnypYM(5m(p-|&8SJRIF?kO1o( zMDC$ASw~PP8o;b>MNI?y`StsbD{P#{JS!Gc%ak+pe3se3dZuj0ecq%!Nhv-`80!%%1cK5tDsL4`Zzb1e_LRZ;waR(n+IlGGld!N zHN4cHWsZcDUpQ_DzvOou{y9qafO|o<7pnLHW7rMpw9125pWS#gUqkYuSH)d9e_h7d zk@CZvyLrS=9ql_U-_5sC4_uMA*g&Y&uWUG4XcRXYoyu@(uJGFrgn8CgcaLS3?w{Sp z=DzG|jk-4eIW>M{QF8i4Zb|a*)kE$had$s44M@EK8v_GMrOzaVqZtHaVsN3##l=;s zF%DfYz5qEepHGZu)=#X`*7>mE(aV=SzaZ8AO9{v4j@R=MrOo*av{6$YU!mDlDYtl_ zRK)#PkA@Bbsignt_D2>NGd|1YN#3p>S5{>5f4DH@m9EY83V59$O`j zp}4hG`CH2X)?wUl5VFBx5E!3%G;Gcv&H>WKl;s-t(5Q*HKk})Wv{kuHwpY8x8TEU}=IZtr_LCLt_zsXt zCBy6*o5o!5!iLbqlh7%6)mxT2q8D6}&{ISOhfV7nq3a?)EajZhu$(ILzUwXNJbp(K zp|tL=eHmiw?aN%^mME{USZMn+xA%gBUCh54-stq*P`%n^8oT`KdBi3=vi_(#Ss%aR ziqNtTN}hWL_pNmKsobQ5%a4WfSH_+Q@G&~^Uw#8^hk3ZDv6+>G&x(;90<1J>aGtDF z_Btdps#+;{yd*;OwvoaKh45Dq8?3|V$8+#%-<>g$+k67e-z@Xm?LWx`)94Y{Lmm<$ zg4iJP#g5?{!5LKmdY252x2|Zr*__&w%@(88e5iT5;zP5NOsQ@e*3c5b5KIfFe>1~y2|qlk}o|*n z(9h5G?#+qa``Ra$TxI%0Y_rhNr0@{9Qu02uL&!|lSMRwSICHZ&7g9azNZye%WeIus z%xEFcEhfAbn}^>P2o-TkC3~;71D79bb>UOvQ!ZY8NOGx`3D(Wb+x#&<4p}Gj5HO4n zf~V>imVI&z5nw;XjjnJ75OV)PEA?NdQQhD1|6(NBUHV>KB)TVg7(UNw@p_oWWCX@$7QdtB&Dy5Qnm&|?GVjU$a;}tZuAz) zML)7{RyOJhs}HdL9H_rAk8r#_n4EUm1kXu$UA6lC5TIA4uPu)JEawH?VmHm_&79{^ zc;Prvmpzrv@TRfOxo+yN&UxsT-rj1tXS&fW5gE=U^3w$2pp-V@Kf4p)=%e`Mct67- z;;{jqe>kL=eBw1B$b`WW5%AX)m#*=>#yULUr*7`sy-*#+^f2^>!o^ZJsCx4!e`bC% z*=_MN(Vxtf24w2sLq?x{G8s?xQ>oDC=_iZPy>p^bajVj7lA&r=ADIYyF&T>fyu-WW z>-l$x0+9AIF6TWxI(6I}AM8pylULT4QkKzqS=v2yomv4hC4~sSs@=+uS#X1Ck~MNE zl@V1wub9>}X7x_V$~qw#(y-jGl{>fMgsv~^!s!hxojQ+n8u95-UYayr%b)fyGW*i% zh&j%^pm~*$4%5C~XgZ;ECJ)+K&ExLd5*BlBbJ*V^c-nr^2iQ+OI&3h?9bSxT{yUio zIkrdiXqdoI?&3;7Qqu(OitdhQyzwW{X3|*SO8s-kF<)LS8A^pC)-xbsNNh{R*;jXHE^>wF^JjYIo=KUAHQ!)S66STzK0 z%+KhJu*eL-JEu>KkY>7A4BA8cUnXX{is*jt*y@dS^3S=oeNKnW?jG#x43v*F`{)mX zpAN?GSthGotepMi}i`td2;~$1=Or-WgkqF}R z)cLwNB5Dkg1uENfc!o`?K;2J)#t8&wYV;mj&B8I2Zy{NxXwyZCF7*|eGm|8a5t6O- z0_*W9_H#GPGFi7nVUkyJD&{dsW1BN@b!O6%Qx`+ku@I^+8eg8#lNr^nVt6@!4gv`@#|q z?Qi#>n5KRKoG@WAl0V(V8&+EOxvjZ-6o$>8dMaSxn_Sx1ZLLnoC4hLh!T zQ77^PPmvTM@4vK)Y_xBT`{92}QU2a?e%*jfpV9m`*$edKa z0Z(}S5=J0;?h6Si>A({p8tI;$May8f#k@-cgj-0TzXeuTGd?~)|26vNu-}0^o-Yge z>+!vIvDFhmvh=&%?MALh>YPtRXWm9eN~#}dMeb&jIa8N?!60>{Hy@r z3pJ2xXo;X=b>nJ)LCg1bi<^7HVzH8La06f$QP9yLX*4=ZB*n$Wd#6&v*4Z~)g(6*4 zmag0NfB)$5hw8Q1y*-+>b9Tl|hB>cayPM47GI(dKvE+bp+8_P9v&+3ixrF~D{1eEA zui)3&k@m63qbT!JSb?ESgJ0a=-X%wTH!&I(l^o?9R<_wEB6M3iVF?;#Vj=F5&01sZ zi~7n1r)4ftq?DsoYtDRlUs<83^DtHcyUs~IAEq47X;3is7?|S%9%}YpOUp)pUD4#@ z+Dk&^@d*Sm)o6VqyO>{Q=c*_gJ(I-g(N3w_z>XXQ=@5W=~Jo z^;Xp4NZb{{TOc45apl;n!yfENkRux`+oO>Speyp#R~b4oGG~)5*-tHOd~Sb`o>CWE zTja5CXb;(VmeT;pa$+}aI6+zD;QxSBm%oVr1_I{+F1|~hUR-LZk3iDG6ZNwlOFif(VEomZoC#`FL{?=ob+cg(u$va=*#vQl;i}S{#I3?RmXl)lgc=C6m#O znU4nKPnNNgM`$VJhaz!EN9&uKuprNYKY~aPfV!H0R+|{Oxw-q_?$1_XC?XlOTgkXh zKHgt!FBKdqLLw{;$cqpbl3XrsIJ6qA(k?^Keiy>;4C~rn?F{r5=>g`!V$#wBHHJfW zw}0fTS%&}%I4E&THRj!MWK3`f2viWrAAmD=0DdWz&!@9FTX{<=vhFzfaB8W>5K3Tj zqaGh2@-R#am^^lmh5W;CWdfq41SM-0j3v-43lU_wB?WlyG0h%~L2pSn7m{H)t`9mibyumT^ALMLL>YOAmp^R%=zKHTwLv?Y*0TJ6s*|?v05s*_oYy zu?yg~xEOfeePA$wCH(Eu#y;%t;r&AQb{^DB{yhCn_4J1!$D#eZ(Ia~KeYyvR=W9{Q z&EDO}RKwGVkjvyy`@%$2=HP0=;>Un)+=F{QmaeEe#J^BCKq!guS67m+*<^!`9_SAN zaLb|Os;a7a&_)eJW#DPt`-TSx2ZC_J3s9%zV-mfNGM50t7~ddANKx{0Gww1Lyc=21OlK*bpM()-+3~0GN83erC)iaq8s{0 zg{|(S8&y}n-f_q95fn#aq1AAkxLVbOulDo^#Fh=k5DtkFdIeG|7KPRfs6dw^>9#pn zxKee0x^UTWg8{rHcl5zycp6C`gY(_%CcyYmLIG1>V`KtuHSQ&~28*PUt-mB@`H=a$ zyZAx;f7T?x3(f!4Q`Jf*i)Q3;ukgT2xI%zWU#fdi?djoLWKjx5TJF;@1LLpB1P#MI zFTOOuq^Fh`0Ets7hK#)ag-N)nvM}{CEoo@Lnw$Mp{li?>_S!7t8jJVdSq;1+1d9ZOLJ%cp*i3((3rZKc!D5t(=WE%Toe;=m&B?*X0 z=#uNLRqy~J6Lzrqa<}@CVVn6tAW%OqjLfc~Ct;&NtoVOX3)zU^K^3(Vs&vxY)27>) zPLh!VYi}^FQb>tKY?FY}d3lpncYjn0zCP3M z3x+GCVOTcVl)Tq9&KWByp%T!t|EChbsKdgm4=Fq>t{rSt+OF?1mx>cdD0PY^N=0J` zdI$MW%{Cvg(X`5IjWxmP4}oVhaJk(VR2%DMmiEs9_g}tUwI+UMWCRZ2VZmM&uUX6Y z=qjmMP{;OiqyQWOo)?RNAbN`L10UeGceWv7C{=2Y(Ki6;62_=Hg{E8P)#Gk|;FlO> zZ?*2-_#tjh=W6EFg#{HgJVws3WL#2_NEBl>>lGy}nit`2qRs#GY~3^=zG+E3r$^*} zOV3zTFr0E4QUV8Al1m8^qJSSA@#|mk$p7|YsF10bhWmt?utpRg2xY^8XlNQz8Y8!- zsCsxXDps}E@rr{>Q~$m(0>jX@HY7i^Z!Np7Y}O(|FziwK7<IoByx^+1n#jEvZWqnG9 zs{n56pAP$@CAwXmum#E`NwGouVf+W)ae=z$u{GKv`i~9F+Y?}|9BS*p%QMg#M$^Mc zX0b{arpykH%_{VZ@-Ns7U!N;I zH#lb*3%>hdtA+BkP!5I^J-&p>Vb#39*gy@?QDa#JC5MS?tN7k%wYXl9fP;hAj^lWj zZ13)p9$?To0JQ`?p$57J^05EE8})G5RI1$IgCxg0s$uPHYCbooAZ~G;byh&J@jtNl zUtcc~-;ssYCfk4jY$zBQU?@z!`o%7mUZo3YDwIoJ$c|Fzq^9nO9t95^`|`RB$h~c2|EeYZ1Bp>r=(T zwim#P37<-?*w?Q7>)CT_LZg)Xz?B==kW)~Y3;fyS(kcdcMC; z9!kU)j_baL+vmOAbzdnrEG!I`zu?Aaf;xv==PSyvRKJ9!fBp%h=Z z-|8wJuD52|Yin97n16f|b3lHFQNI5NY;DE|wq3ni**c(hz}3ml}@J5fOE2u2bD7O(Qr*U{|BHXP8q@kKbJ(lq8!cNH98 zac(&`?CQtuChaJzt`Tpp*%obw^N;{>AJ%vSrQzG6UdIDZYO3+Lc5MMJvccGHMNCL< zP+1CkaKOo;Vayap!OGIld`qF*GxmReoH*4-qOiioDB? z{X1g_lg*niz5m*}DbC;+B z(AOHR1MI!D?97(kwQ8pC+9`4bt)YULk&H7sLLSmpCtdA{&xN0D`w#e{iHSt~9F()B zE7$+IwNljNLt@$dG79XZrxdqBKF{`R?c8wds?2zbv!*hLP)jJ;e4swh(G)X~Q1`<4 zytP0P2)N|cHV$QUKpWbw0mB`OuTR#sL-Nl8$U@0{9`Z#aY8 zw6yRmfrKG{kD71mj90JRKaSCXs!l#n5K7-(u2Sn6S-P*?K@<2LxT?TTb8CG?Y@?0b zj?En%l3r5P{%NGT%gasS9XhZm6=!i4ilj`qQ?hCP`o{;i$>hVI_)Y>13^Tx|V`^W` zCQ2Y}Nj@O+iF%WW3-r@@_a*0NuHB?rTs-=Ah){M-u=?HV;1^R819MfJ;QxODc7k%q zZOnuEAUMN#fcXfVqKgI34}E6nS2D{nnJ(IX&XNlf%ez(yH6NebEi24#lwe&^8-Ch5 zZU5g}-?KBQFVjAFO|o-g^voCREYqR8>cCPjuzdT_e7e^bpvY-vhs3;J_%_7++FBy{ zSQA6JwjicTPKP8d=0zUcFCg%FJazOkKSQ#fbi_Q=a>>IE<+m3DS94x}e@31F7XUE9 z_1b#he#(h_6T&ra31Gher--&W^eYg)6gR7=(9d)_VbaOb+oRBs1E7VtA)}m;)plRcL5O@kZ_hW z#}HO&bIMd7yiqH`VCR}wL+xENrn9h#l}pGkO%G#o@yrYG9ikP2S|`ZU3rvS@**Vve zakaA_|AnmDkMkQsDX@GZk#AXKKD|iF`xJVY6-2zL{XKv|6pr;&`(oZdu!6?`+WS;l z(kS}wE&4jMuhy_fZF8B`5G>f^`;N`K1wE3MIKLH={CL3P1eyOdb`-_Lz?ur~ynX%Q z*GM(oaPpTaL&L7c*c|TQCd4%kNt8dHw2BFcb;ipogYK{(73ESGHU=DUkI_5;8)X=* z9C*5DBr>=_=P+(LK0->}EByeJGb>@sQnN9$XGJ>yiW6N8a^bKE57qHn)Y1GII|NWj-I6MQs- zot-Jp^ScH@^MFpVx|JM~zWl81B=*`lDFno3qNR4DRvlH?Tj>gt^V$P~VHmh0%6x z!r73H?|tqoA$Wi7oMcP0!I`5FfeBLOxuBy6Q-gnk4Hc7@3(prrrSMwk-_^pXT~Ko;=(@0Mzw^K+uu%N$!>|?;DcqR78A(Vf+lS2cQ@Q2KdoV?m}Q5& zZ_hsg@uT8ciXxtPRH{R~`WxQu$3UNEHTS({Bw|fdS?!OL-6x&5nOvn{kwlV+1Xwct zlW+{%2--E~(=;~huCRg2tuR|t2`iI1hBC`!$A!qvYIBc3l2xxUsNEy_4h&2RO9YwF z!kdv|_d<1h^ae>Cv^_Ko0I@%77m4NIl-|6W0=(l9NqnY%qiw4wk6`IK$z8D}C zwk)c{Od1auTy=zH`^EBFK-v(1#q*YJ;Di|_tEA3#;g~S2(KkAq`*n(PA-?LYQ!N(o z{#uqX+)fl%V{p+zDL8K{R-49jaw#BMlV8#-ztzgGW@kof@Sm+Z;$!tmB#Cd0KFJ|p zHZt4J^tAoz>cx#)@*T6Ba1Miz0tZydSG!iW`2?O4Kek;4HlYZ&$4uANfBIs$ZHg{T zOTGA7%@5~V-M+qgcK2K1JeB6>=A-#B2~NnRO4~N<6j`mL0uu+W*Y8mlMkbtdeJ*?M z(gcY(YkW)OHPYwha*wMG%`&^12}Xot?8;EJq0__x`yAW3irkP3Ve1=CT8$cv9Hh1I zW^>2OP}R@v(@Z!R;P@vL(1-MG-Ftd@IJm!I0^UQhz#Gvucz43J$7Br#OI&J52fX}P zto!8|vQ3pgpc1UBD^nwCK$MR&&n2GJ=#m2y1F%o+^xvAU*}n|Z$6-f$+VYg|6AV?; zYEk!RruyZLX`xJe1)?+@&8`-5Jn`AEJR_x+z%Dr;FN%7x@8A?wGlto2$dFs^>>e31 z%@w=;>K+z?znrjlS^A_bk}I?zu1}18t<6nHY z1pS*nA6e1$BWmAT@m()_VB|{`$ju@~BLBQLRf+e1i?2!7`nXu2+?)VppJm7u%TGG9 zxC=Gj>fc&%)jQl9+*1JlI&Tf%tcm1f-F-2AlNa`TIbqO@AbJP)u5h9gC-n0lDR>~h z)1*kteJd?4VY)7O-Q_0YvyW)80AI0)LaFl`#V#;nqvO0E|9r(4o=MX{xa&%AuQ(FPDN1?mjUaUS~U3@U2Yyp2yC=Z|_Z zUU!kAR+6{n=j|63iWCQR6wN)FY|xUoOa6Q=-?#R^`AJL!dUY8##^BHk;(Ne?r7PmU zfSlnQJvs1S=dyUJcqckVLR?gLCxk_7G(D4flO}CT zq1uFa6(C!Vj%@A=&knI@K*=_gCK0P}%9?LT`o}jHdhb-rC?{1-b~9mY@iLs0$39!l z>Sh^&8xL#DVC#JAzNmGJ`SW-avti#J`>^-n8$Y%tOI3QKQUB<=&Q}K+1Zlz9Hcbsy z1RCbkPBPfM$j4P%i^>di~A4^IL=bFSD)|C!Z%ASa+x%GT#m zr14pZZ6M?FS~s-q0}7!6GjuE>ZRy-L?Mm7{Zt3tmpMnWVbTdN5nqGgTHzAcBP*;b( z<~Zo0x{d2>)`>*QsXr>&3EkeKEwwmfnXrBK&C#)pG^4y-=k-7k)c$0!)oiP9bx!Qn z3vcMMHFj5gsmubyjwCaep>RO|Q5?3^)(-Y(#}e`@I4&4)-nL`}d<(`0BCzZqFZGRi zT+Y&IW5!7!VPJk541|wMvcY^MEW4joe^|Ua(stB*UoN;Pw!>Yy(c)HlK@?r7608!itCXlyi;XL zZ8r$5=f@bDsMy8L$a^-s^mwE~IYX zpwy3TqD+rU9)jx6{eiJ^7*Q%En8cQj(WD8Anj+NkkA-j-0WwJCm-5U;NGk*d=gC#h z0S$thrCeNaV=x4)?s~^q(t7kdyyOO>0&K7uH}5cy@1ZzhK|Cnt)WRigBtf7 zijcpq6~o@-MR&=Y74Dq669lpzm~MUwxU9J08&5YDL}gIz7s(!679-|Ov8ppoyz0_q ze7<*uq-3ia+z}G5OTB zuVdo6=A`f`Eqw#5|3Cr0aCWx-&5d>;F~}WI5HDD#-~CZ)f=R2&kE1FPRcyb3Yf5}i zAjj=wB@_+F6_l7~JKOBir8_IcKjt3?rez9nbc4hr;!gl?`h{oTKJ2p7>xEzP>$9s- zXMf;AQDV>Q(NtV3A%`9R04baWEu9HYD9lx%O`@#?o<@XD8~P6BMaGFgT)8XUR5O8z&5GuS+=uGM6aVZ zW-G)?pf4CvnNaVRT7=tc4k)mgMSUf2*gcu$*}uiGo5|(=2xq(gK*CcA3=bZE!lZU|ctR__C!pf(f06 zAG|ESz%F~>-Go&}<#}E(6O&{ixiI@^EFn$HaU>fS$Gn%)_9KXH zbFvRJOsVaMBD!uDh{ia9Nx^}0u!dl8k+h$r)(m3qwv+4!CCzxusnYUi4kzrxabvmi zP+=fcVAv_vhxXci8_8`($EDe?&LcF9YRgKL z5jNdhGbQpjQcI4(s1I@VW?W=$12j1oaiV+Vv>Ji%4Alf_S+{uAP z01q~2vq{E?1_I4-{>ayg_aWmKClMAFrmjuB2I8#;DCfQafq@Loz}f)4r~fM%o|r{^ zAQ4!Prx+2PRgCGo_52br6=nH&Y(FeYHE1_bXG zg2rg!cW3<*jmX0^LGi1N){4z>-6U}1>yzr<$pJ6*0T+(eFz`eElTumjuZake89!Q8+`T?xqlnb#&G#40Fk*rnWV6RR^o{KWw-K&$@f2RHwQj0#6})rm6e8*&(cv*trS>$0 zBdiaO!_7(W6w%~>ZyeC~ z;-`z8#6$0?wH(U>7ex2Ux5^XVVf#A|=(~(|wc%duFC5T~^6_H;XkUsJ<~iw|?S(PR z;!|Y!F~A$`4uVLI*mMlG8f14e=05IwuaV0WJm#&!DWNfD(v=axxgdS9lA*1YiR^a3 z-85`B-RcXIfZiEz-S#pVs6{i?siO}=J&AqexY^^p+vXhmu7#8G!vJ3$Nk}y*>^;R2 zwm&;A32LYBE@CmHFu`1%74NOzbRUMVqxif{JAM<>;b0r(WRzMTF|Jl=6BOWDmF{{s zrhEA-bM{xl53h4sb&@w(FeR#_`_BF>wJ>sm+r?#-MobApiKZ9KF7D*_?2M*@4zQ;q zMN@B#ni-q@<*T?A91^u^b0@5Jv}GuGQ5R{NDpPHzUxq!Q+97k$Di215rqJeT+7yG| zdsv#`2j7;3p1&OxTW#3gp!hVjB^%UicI3j(X6__9)otZJ-u5@pxmF;TuGLp>m&sPM zR^kX^epO)ilKo!DlgI+P^kBE`hHAQY6)WeHrN@a>?c#;d?7@B9+9g)E37y~bAuQW} zWU39%PR=j-4xdkF5GzL}vPSw3XJZ^XU2U0<(9E!y$A`7n4V6c3Aw2C+<>o_#vLk0b z5GZn`k@D@ z*GIhhA7k5K2lCst5r4Rg&In1m&7NQxdIKE#u8|DB%!5gO102j^DMGoz=+_wjB!i<7 znWkQIsP&A1&O{-2*92Kdsq_^n@5NJkp7?b-vG8c&b`QBPJz3fvX=9zCx(Bxf&2xH= zV@AbQbHy(A;?Jc8Ty5p8PX8&e`AO}tNV)~^U;@dEIrR4aNe^HLWs+FqR5Y&c{8Xc)YR8cfjo_5eZ)^K9(FuOq<r|99dZ2^>^B zSbNbVzFNZ9gLrM7kDDpJ@Ny!saRHtRNpU+yyWDXwzB>|nGNV$yF%gpZ?~pk&4oRu8 z97NFgj0xSwS_c!{;#LX-FcbKCj!lloQ-^a|-{EGN2nToXW@Y)ZBvh)@>nM`MC`Ly7 zynz_WYOj}jC6a(=yY9)JZ*a)Rz$RWw88bQRIt&ixnHI~Q%%q$YG)1N^#)0N@F!j96 z%4GT^HZ3Rf@%|=Ykh*9^NlO`qv4C+yv;f`mlX>yu8Xs(duy2UaryQ27lclQj`Im(y z*$|=Et-Mq=xBC2g4BvanhJB|;w4e2MA2MJ6XcEoUl3~ntMbkzvz=PBnjezbtV{=Vk z4hq0dR#J)F&RY(Z-V$;b$zn+JTYM&YLoevVTnr|>?cSd6%;tY3rT@e9jD0WcS@lFv zXC3Qn+yPu3${inMsGM#Co$(j6;@tOFr$3irYP^?2?EItOIK<@+8h)Ip%)8*cv4u%y zf85T>N&xmN8Na-f&H~?P#3j9b*yBo!GIqW08OCG0rUT*7rt^eggp-*G zcI$LaxeoRe0C+|@4cLSywtn$%uD&pIOT~g1>$`xKaf%z>dB7d(Ipj8NBj9#r?0kQ= zoS&-6p%9SP3L|{s+3A0=T16l;Q>TE|K)QJeZ>?c4c?D|4o&6MD6-e3_vx6z-+L!{H z%-o+=28OrAr-#c`waCU@qr6ER)dZ0jQGkDa32zRzob3YpNz$_%4Co+Ha-BM0li}57 zcMpM*7$@e9r%Un43ys3a5%9B8!cNFF{%Cu5>XKgU#ol_O15$HDKfr^yNDb2&scM*4pEk0vYE_rigQK_ zBz{|=c0uv9GnhiUTU(@+P$QjxT9Xc$DK*3$@!28dQrBs1rqmj99$-f^2G>}VV{9Hd z9gHI<+)2<~V{#qGD>)(zjX2RA#9~2{o9bW|VXI9Tug!+x!V-;u^7!XVIDTCr`s@DT z^fe)t0e=<&^?se+2?PB1Us6Wy{Rib$E%@mwjXM{HTz1rX45Q135McICjKDj+`Ee}- z_DD(iD9x)Qj0@sIN=GZ{`uyc^dVSq1XpwvYXbXT=&5w%^EZzh^*U@gJRFRTZjl57l3Xo17n+IdQXt$Ej`Ek&U_K~xEBYZ zcMRl}2N`d5ClVt0mXeAi90*7<4i@6`8giDf!$Cj~lBxqih;EMjlI3FO3P>Z+$szlD z|76(%#%Gxl?y>DenDrJ0-;#r{9r$HCr~>w2XJ_0azRf%*Utzm`Z_#ak2bnQjfUrhljU zU5BCW-VE2C#CE~PIvMD_`P?s?{*r+nW^O~ReS)k4P)q&!cQqd?zWQKeubQ{3ECW14 z&26rJmuxiJi41mCb2EWX4GLMYOW6!IL{iacrL!jEvE=Z;m%Bw>41uxxpF$hN-JS79 zcyLl6>w3@qHi`U%0>T#*0`b=O*PAKquV%3%`Wdw zh948}nQv$nE6X{t+F7&;=#2Q)gQYLkTHn^&O!>Y?y+h!xSs!h?2FAy17fR#zrpE0V;jVTOC(s z{L!oD0)=3CDE<$b-?#(brs=EqmP=28c*0y9nr&v}9wd=5BQGUi7)=+1Sb-wp!H`oq zvNufpA9821*0kVF%EWQ4vy2Ex*1BKNTY@ADAaJsigP{kGUm~PoVsfk zv^XO6&vb6mwPbG>0Xr}af?md6$Em@4P8Em8efLN7?VAmRI@r&&uV_LwcBh|CQBx58 z7TOa-a@gBdHmX`x^wZFG4! zkysy;ffZ;PG`4rMhNY;e)x3~7yMg*Oap5mxA91znl9TJ=ig#;4OLMpI9&}VTNe%vR z1Z^Bh0yPS$ERj>zRXpbd2ANbA3NpGP@WOd#8Ps2062Nw-g-U;F+v&~^Qp2H7K8U%i zMVrfxV$9u!jC}L}Tj}_rvtr59Plw#ok4Vw%+VnkvUpI4DU=#NSy6j<<;^N`*@Hy}0 zWGcUQRh%~UzPQ=bu1AtOfqZg7^I34cLp43L!{bCJMe9F1rZraBY49Of=sXL2@f{|+ z*h#&$Lr$0xgy;v3Zbu5nD8p~0ghTcC_*~FPS~y~_xINQ;-$8uQehX3v!B(Ul@|dEW zBHuu;TFDamegWDbWwLTHgSfSVNvSDPxKA3SOgWKmc!321-nedDgFo?wQX*ZkuS&7R zm3|@&&P-<+eF@`0>g3crB+B;dXW}DMO3l79#}TH{7hS_yj5XF9@j$4LHDq-9dac5K z*R_fS24WKNAf+B~i3>yzQv%roAqOgZcq7!jl-m;U>WyL9JRV>2lfHvSJVBC9sy&T@ zJ6V-*960=LR>&e5#7zAz{2}eazJYk+oS=N5;@c#nf10fQ&}p*CBEegRi9IFr6U&bV zT4DKxZEGcHO&<+djsfm$6i(97A$I@?gCI z7aM`T z_Qi1NRP}0~a<_(g55~&GfS5i9|CQ-kMfN3Ya225*x}Eri^CoJf>5!y9M{Vg_e0BR2 zGcy@_$2-o&%jO4L5kgw%w&8-|VCqOaIiD2wbn~Vq_U}=d#1-3b4>=cphKwnvRIVc1 zyALT_Ts;XrA z8+q$wq=WI`3}@+fjCudr?`|%4=pTgRM}7$<7w*dgNQ7kfCxbi8NCN$)*1O7C(9cb8 zftIgS$pM{~F+54&W`75~nuT@b*@(9J4@Bgo@X zMy}zBMufdH)5n?rI2C+g~n7s+>Qt1@<0)fCAKzgkrs%) zYE@@4p|A>>l9j-}!$Y5f1P9lHCd-|-qrkh$UU4q=&&=-(%p|f8GlAj#X|e#P#|yyu zG$!yfoWebgh~9_69Fw>*D4vYi9MOXY;sr=auCGJ`7%5b0KjY`ox# zY-PQ6a1i(O4=*GWQju(ZqLnW%8V~?k+0>?)SEA@8rej`fcx%X*@9!(zMF!evwEK9N z45vsZwZGNIUP$4aUbF8R>AG-AfN~mZVG`#m)Iv-~Z4MN8sl%s3Duu7d@LnBh&+bs2 zFZ{TSMFL=77)D`jTz8F-G7to%d=jSI*AIi}&N z2^UUBsi9|PWFD&(LO#JjuNLWMr$^8UGp(_HK@!%B&D@Gt)*xOtBWkfwvosb-zviqc z5=)%kq@n9)hOZCaQCI`OUUP6Z7T@z~k3kXb`l6rY>=j35ZlXI$->JQzX8n`C<5oP2 z{ldkZg+{NpA*=c3a<0|Dotf;o_9v>-csbcjSv*3hl~V6zH{M3WCE;>@ds4s`l83HL z{ZL9sT*tDm`ap|lZ4e#{I$s~5V_o!@6 z(#)^bY8^te|A4t5kf~EwmJ7^ z^`F$^+&{kDuY^g59ELYLU9XTqmW80yL|@Mqsy>UoD|Xf#Qa+uzaCLP2Y*VF zddr$a#<^jsylHZ_$ynRN3VJ(PuZ&|_o$_EuL*PubudB5YLC#Y7pXcYo0In7NJg=&| zjSvP+tXG9m;(ODHXtykBQltHwA;9tbh{1ixx}l}@_hAqR@)%BHb1hQxpFTAr-bY|w zTf`>0{CB|xP9F-U{6Y*c)8u1!+WoS8|ZS3~2fvwTLYw6Sl3M5Bb-}7Ne z0nG~m0SlS!nIgNI?$xU=TTIEeXhb0~3pMX3z`*7dVD<>@ z`xekkl0uOE)7B{lvgdaMb2B7Q$|rArt5(bc?7gKTB!8Q++6(1BXMFC^m&l7`+;@;9nmn z*&+n731v{kkYJHD@?thfd}L)M3C})5nvu%UtC>Q0noX7c1+hG77$uO;bzz6q(#$~g zOr()fox!$}WdsdI4ROGnn2N(&4wtzMZ!Ip--d#J>9wV$<45%zx$?O@akR@8u?>+yj zvZmBy9sZOov(jfYYbx7*l8Axmd<=yVvKG))m31NA=1b-kaB+|mRXiSXkiEV32wFL@ zU*aPs)fWAmvWR8}=UPtMd&Nsl8_qQR`+@t1Y!@bvmIX~*#q-&Z12FtNnq|YucjB(s zpJbB^`1%8SZ@TiXaJbPMifGg;%j7DhwI8+AqlEUyRd=8~`%R?!rj`=;yzzZ@aGYU~ z5fMk@J&gI$$|-aj;hS{KEl8Z5iEaG-DU~971Va|WRl=RQem31if-2v$&-J|;^f6?V zn-n)8V{*LBt*`pL!tg&%8iO(3$PC!WG5@XyaZWvSMB78DHObsz}s#$t}EZH;VtBJUpK?)Qzck00|}M98_L``_Rj?4x=Bp zTdJ<{b(eun8zFU&Fh&p&h58JBNb5}85-&cVx9h66PI)tU{+`m9W39A` z@yKJgXi{%AVYgGMQs2q9apR_m*xy( znC1)+&s65Dk>!_c5AE?w|{Z9WWD#4f)BFB zmQj9;SD33n7J8{%YD^a-;IK=?G=FMi2TklkIsY`()7+wx)6yshu4r6H=>cF@od%|M z#_$_w?3n@RL?S*JFSHK3&f!#Ef{=Q@X&=w4CE(t}n56N)=!)9X>I{lzNTvEH#*zI} zl5@n~`;$}TX;1B*BpIEVl!f_V%-_^=Q9UzA^Gv@VraS5#RO(VC}J6D?a6|xz(fdJlWa&Q?*{TSZl8(& zNd2x{{8i==$3#~-|5FNqPo^fsQ^Y94#T%f)L`9ab(y^kXM<5ceeh3_0-5_f-8QyRa zramm7r{RM4e!W#aGs(*P_K%)GnEavwWgusAv3*K{dg}kG<*e_@fL3?MOuIffQ@A0>DB_SlfQ9 za1x0oRC-!$7x{q|9w}1KD)r&!F@mh)PAtsa`e_b8iFcbik`;zNVXamW@YqwV<^YNE z&$*cVwteTQmoS>zPm0v9o%wO`*HF&~;`3x%zi^Zr~oHI5LSc>t27_(Ts`*iW=-O40EbsJ%}nGQyFjq z!`;Y88*mp0tHSk7U8Zdv0Y?pALijnjRk6px!xR++0x5Wv*3>lZ`Og4Aym5l4KiY7^ zwXt6}y=ui~wXeGziQ-fv3FC$#AB2wDZK0=T*#5~bFM?Q;fIxly?l|Jx^jRG9;+ zkXNShi4_u4DgXWjHqJ!#PM7MczkR|1Ac3#|^8^p(7P(Rz=BG8k27hq|jatwOOQ~L` z?@)!oXF*tYjqu&(E8cECEHEju#Kf>gb>_7yk^|peSa1qu=aYra_>=bcH_gW+W8HdX z&bQD@khuTD`R|b9WDi);J{@`m#ixTMZt`-A2hS|Of2E=y!0VB~uFHls_Dla+K(L68 zQL>KRc>*;}W52|j03y-YFjXW?lQql8l!Epxbwa!H)JltaCWqrWxl@XCRw`#AuF@(5 zSvBk696w1CW|T~cY{Q9iSc1ukeJ(#?2Zs-4ljCn;#8Sg*Pw|eH7M4N4yp6ue9H|(i z@2DA-w=o+`C0NnkcPyN$&RQ4NKf0=CXZ${hBX)omyWxVM{>cC7Lr-9CE3gtxUYYcluI+ zAyC|~R-o8Y^d|@X4j!e?Jr`_*g2NCF!(#lJI@Hifq6!9-TMCnJzsfid*sw+7O*z0A z-lf&tPaA0NS(93eyS9(ki=zMv`F2&+uBqoS^ampA0!tyx8kbN+Ku&Y0>CTAX?I+L@ zqFQiL->7KNhEc*EVY`mnlUFmtv=P(cQ}c}e^#L{u!*6$9S8S(|p88|v&J(@lbc|oY zP2;VB`ENS&@E;qu3oZKf#u>JEZ#qm?!=jeGg9o^TdLQyJt0Rc;+lbmoXHvVN1 zLK1JY0zUrR1A~%quS$K)v&2OI=|wI$oGr_CH1OcnL4JmIS0Qm>TkUb#azxt>1N+lj z;hnVR&A|nvZ)?q{j?f=L%hg|Cay@%sg&Mj4?u;vVFzYuobaX$Q^H6Cvo)@}tW~gwF zPvs3;>gZtePBib8az8)FNJ-*M0vBPlE{I#B$9x|0G2m%62Dwb{_4V}^Q|T&~1z9-; z19HiypR1uAa-B@>v%(#-W%E{KPV_gEu4@9I1}bhr%lYC(IMzokK>wDi}cbJnmqtcEf z^U8Qa!Cx_+?xf>T7_El!e!qkG{bmaphm9mz6pnjr0dxW+@~9Kymg!w30spEENo-IT zfceR>u~xy3@Uh`n%R_XYiv}yQLrklJ+}>~e28M>SNPsr?fIdR_jWm1!YzYEDcx(UpHQ%HX|Ery7uq#Nra#r6Rr0HJ1S%VW=#ddsIO-iT)BZZY_dN z?JeJhV?pe}+Gt6pca)@O>DW~}e*M-zPexmA##Tqy1md9HUs%3Cx^6U%=Vx2&`r2D} z{-*W3Je$f>L%l;tM=e=-@MOn;)okiLPQI{*P)_%Hrrt<#=G;gf?Vl)Bb|rYR^N+49 zH+!GszHui}pvA_!;)i~%f=V6l$@)6zZL@l;CF95dtV9PYSj~-WQQMA7y7s`CtJ65! zP)ke$?)1^0W%@^aK7~SZn8E<@;!^+Dqv&{gup`H!LUAR$hk#sgNd4x`u8~@t9L*6t z#x)0Q7JORBN6FG~74TQBnR5uPO$2_U!?h6jL9~9TbpCc4JBE4h)a6fS(k$3$LX}Cg zdmp}O9I*pSN-q397};sp*s$&CS&lj&3mL8quku~Lry)r$3DaFsbBHiUilcm5!lA)7 z0Yj!JhwB+4pF`$7-I6^wm(q5`58b%xjB*3FM)mQ!Z9m<8itNvCbplbO_Z@wkW-&Yz zQJ+JKFHyo8<+giGNw8H%*qacjTn z)K;B#vOQ`WqBK)kaAW;)=oa{8k-IjWtfS3Wbe!NPdoenKQp!sKRo9qfcb>#@aoI;= z_47-Eiwk^WzgJilDU9;WKc)p?Ex3i~;F9--4)WiWOEhSSiYtoM#mNm!5q@FuAw8A{ zz6rK#FJ9>8%luow zJwYObI$@*jm0foIZ;v3%MBagDXlbCIKm%#^Im?Mw_0z#g037GfMeELP%oT;Me*a4g^o#Dfp5tm+EV2bU zh*y9Anxa&DZETtK3D61|WNp-fCgh8DIwKdYQ82d9(qOq0U9dn&Mm=IeXrsY%2n9xr zSDn*0)d^7(T^F>En2rHbLo^X_hCyRXhl`=Ta)moKeE|(dCm>lR^lv8E_(w_KCG?rG z3HX!zb7Jlqn5Oz+u-J-Z>a6q!Mf;?qAy7I*H_!x^i4MBn2>$xEZ6#g-L0xq;d6L|SS1D&4@c)!F0CmrOY=@a@2vF2 z&<$JH%CLnhD^QGd&HDW^&5uLIxF_Ci{5q(0rxp#H-tb$O*0S;|x^v+f{-vX4p2p~8 zoa)d|SYZ;HJ$s6t7FA>98wccxMITU)`JH%8&Pauc*AN_F_56n@Ke-%=$h*KZH}8L9 z;9cDg>=LZj= zVI6Aq*j>-H5RR=k2|OOt)lpDJPWKtpPUv>`JTv{8y;ef3XH-*s4(kTk1YR$+1+}oEDm7gs&>T4^h ziZ&yJqyt}b?D^mUObkFb>E&dBPHppmA}&t}%^Uw9(wv!gnCMe2qrMy;(e7rQ^5ZPS zx+8eeV)A3KmUD#?&DiVj(jb=V*XE)Al23W$Ez^8v6hhCIbJC>|tlh5{;0Q!?m;NYs z^adg`cj#)j$7BepoahmTNOUCzpWe|bzI@deif?d0TXs6>f2rU!C$1{y09>#>FA^lr z9N~{8VPkUt>G|=+JI-LffE1{wltH$gvyk|ALDYt|D5c+jQ)C9KHTQ+u9g|k&^4ihj z2K2!F4n_f|!mStYU8=nQgqF>?r8#Qel|HS~qZYeKwH>B) z3*BFmHn!;RwQSE;8fV;}#L1&E!EXwhydOU&$G%S7hX9p2M&Nj>b?{vO#hGyPyU8yy zr7&kNrUZZE`0CN8tq|_#9;RJ_XRD0Z>54i(f{LF9Zz6J%^4KGZya!35T~|J--s~+m zEz|&NvEvycX8GX8pvHzQtGXxzkIgKeJmMNn@axq8#DJO6BQ^T5Anu!1k2u8b(TZO^ z^;l0lnAk;M<~Pt|Ly_Cd>P$eev(kJnyXHlK8f^a@4eava>q4C~H4+g&Sk%W4Vo)k) z0x@~%9%G|TFma2ztN5V&0>bNwl=NwG6K<1Dh+~2d_PbfQD zztAsVI*$Cp_a!!c^QWy)i6u72dO2UN3ugCVlkRakkLa#W(Iq!J!)TdRNF#Q!?3_O) zhpZo7XtTGbOP5vgj#6HZ!pXk<1H=p%Ln`H)v3W`C+cRmT{o9Rv0C)BMk&!`Mp5Ba5 z!w7aLV4#yhIH+v+RY&@z2kUOcNKnNK?PXaPiitDzKtG_lnQvq-txULlUSWL_v_k|w zI5_r6en-bkBUX}x9TH*KvMHKX)WKKgaho%Ep=`?}(@~D=YwRp6`aDIUV1tAsyOUII zBc5#n(#i-EU!ETpl2m|i^+@VjwAIwg;{ZpVW;>3RXb%TIUFfrIzuxz%`%!3-lZQ_k z7LaUWB#K%MO;k{%B;8SdU3uo2l@yifRRwSi878-*d{h`>W#%#u-DADQ((&YL98*y3 zN)!ghUl@Z>bjx_zK4@cH8d#$SWBFcf{bpvh`|KtH2*$UZPr$=)1rwmtzq zOc8c1@XvbdDJ=1q$bs(=uPj$!K_fde?QX~|M70Rp-M_cgrqGY{$C}`B=zZo2#h@7# z_9Nf#derRfzU;XnG1x?@H>y+`qVYIOp>m}*q@lf6(w;YjZm7UTEZ>S4HXbZ_K=ZaV zBjjM6_QgSj`T&mOvfYa%_PdBCoOa8aPMixln{XjqS{)Yl5G z$$9ku{Q5rf*4)ql-Hs9$-r-wro(G;cb-}0bP#Ii!qnj0^ZK&FahEY6RwuU z*dh!Ok-=$m=7`j~lFS>rgF#iaJM39R37FnDGU-_NN_F>8`poOANJOb4_=|~)3gJL|K`#1Ms_Xu zRFQJx$j4kV+(D|1bnMWantN3Pv124%ImT$;Sl_9hgo+BwI=~OrY)nhuaF3sjjY@m1 zMS2x}7B7%F11I!XMMT$$3hhW4zjMVT0V1R9&i%5T7VCVcm~zjPKrN2PG%Md$Hi?4Q z04(g8Mk$>6VCi@#T<_E-Eu1K~F6O7bpUGFR2+tBnKwvN&*KMHSB=8uuhxLi)nbwNU z@BJ_0TmiCWIgPapFx`S`miZdEGW^h(ZRh&P$~df?L?A6LvI*HYD5Pb2YXcqvU2|JZW+2mdl!@B#3uQvkz@}iS!QJ8Vs=ArN zR`#?0H#S-~h)XDZ5^E^lc}KB~KB6tYM0~48vJYL*HqPizTy&XF7YcTy*Y@bhb_{>{ zYu6g+#es9vS;H0&QRxVr-FK{4CpM~N;&v0=VjM4ym`<<6Re(lQ!+-n;zY9#E$+ua( z1to7pR3pj`Nq*maaCH}_BO2t6RPUxTT@DzMGadz%Io1NZszi6nf8`m zN;G?8&Hs#(@RY(~#od(jyK83}!H_yqqJ4wngir`fHxkc>5_@V6H6{Dbpchu34Sr78 zRh7BGfdjjQ*58b63yDCoTJ^6`>>BTuUMZ_}pYE!!D?G+|)Wb5eD$~q0RkJ?roWFE~ zCi_=oaI}a!ty#IZ%|#!&W~(VxHG+kzK0k)mVIhfvb=IkUtFdRT+|>I?avb3JAi1*u zN$or@T9}rMaGae{Ma?NU)*B&IBsUzsZh?DwbLk`>RyzI%%F~h{S#6Oi|2Wt6-^2bL z5=i**2f!};bgXj37OxEfZ+Y-JqR%~#7HpQhKzxL0nYpkx<5*ygs+IKTL^(sR@WA$Z zcr@b?+s^=0G&xA$-ZNmLWspMJrmy?TBl*Y}j`clR)hklj3o*_T_md%oGeD9&YDr{? zid@{p`@{l`fM)QI90>*)H7itic=~Oou_Y1%0fwa0IOFOf((bV@D*oCZMOzwZlWRD@ zyie;ii~9zBGTMZC3yB0IOZ5t@f~Deb7N2{PEPqtMms3)N^_C#U&G&Bm-fW*UhX)0v z!jEbFmd~E7uUCG$yPdi}kM?k}MjpKQ#Epn;N179|fFXTDDg{Z$KZW@Q%p}Q|huMr< z8_zNpW>a~6C?kWQf2n|iYfO|}--lqa9TU7ce?`?Hz5wy(%o)zapB7brTh-XwBr4>6 zWPf3QBW%H4LzJI&==@D=vORurgI*sfae&opE6oLQw{jpdOxOl!^CX7We%g<;LYjGeF)e}8dnL1 zErFH4AupGhtZ77apJS4k7ujdk(n6;Ar4v=PeyZ4TcX*pgC!avX{FjBP;hkoyZMC<} zhZ{jkd#r8Wfp70!Vi)3y4lV^)Ue5+XYe&40uR%_do%CUshc|vhrsqGXcE23UWc;Zo zkAA|Ot7>mFncx4^n*Z%9K*^x++frS^%?%#aqI$580m%zQ%OMH<$hu|Sv7mEYYYX$!E{gBS*>WlA3r!FYswUzPL;Si=$;FaZS77}H{9UJBrkg@tW! zoaq(jT}{M)c(vfdQD|cbrCv(2Evsj%>8Bc;uF#KZ$H-LDOA?PPP^qWrwSP{OsbIeX zM0y$Jpc4^~4!U~?D*r)WkopZHvHQCx<68RN&rj*vVfFng?{RSoESvt(%rb>)RoUGT zp2esG&|vM74{r7deJuhI$NAo2j&#NFc=zt;Vt9!vs=Iuif%2EQYglY|CGz)0?jp$R zVN-m+4HN1!JY`xeFK!>hRax{0To|5D-A`T-4K_8PqiP*`Loum0$k|?CXWfsE`qh>cI zA{YCM=L5~w!lrW81N@d_AvALDb)Rve)vPnhM=1Rce6b0^h0ixlk3xYNlIi_0fVVB- zOTHncVMyAUO@w$o29E?jkOW+7GB$9pAY|I6);s_Hr@pL+b$lge z+&Nx-%d;Y=z_#HZf9UbbaGoA3Zy(}KxRz(@o03YvgJJ{}l^55V=|6_`Ub z0$9^$=;cH2s(i@_YCIo)@eRPi(7%~OZOf3!&h*HAf|C*CYC^R%>P9q6mQOl?edI3I zj+~O^V7k8CcFuA%SLk3Eu+j?8ZTnZD8d!*ONq4Oc@<@O842Qq8fA@Dl9CG~G)$0MI zPzP7;Zr!t9h5j3ESMS>lBvIsH#6Im~-eBBIvC4o(I;xQ26G8cR2IOg*HAn(}&`e>v z+?%@|Je^-M@9oC-uR~rrPbaASLneMwL)U3LQnIfY1NmN-x5eb&!e->nKJQk|*B7_r zTQKtemfR>8-q5|xLmCJM^TG@HIW0eZl5ToGLMr6wMsCNRKl}Fz{;wg=CEYkXi{y1Y zBJt=1$B5DhGP~DX(CS6C6ogK-oY$r~i*AtOjv7*?vq0k{u9{ajj{K3M3EoGtnJZH; zQZ?m#P2>Y)a&8~=$k1Rk6DPHphtw^VUA7(&dP7^!(F3m4HZS6Gf{(8UiLKd=AJ7&jOt0^pDn1)(mLK}qlbKk}{_46KQI z<7@?LJCdBmobrq(7;)scxWmy5Y?J@!LpJxV&_VoPjDO_~G*hOD{fo~zFo~8ZkC`!cx6nfWx6Wv*aixLrF?J1hC$?8y zgz4vOAhz{jx?ubiIehM-T(rZyE}y<4HC408!ZQoC0Tbln?q2f7mRXqbFt-+praB*r zpOV~C6)yML-{wUhQi9R1z61&gf=+BrRN~ZZ!VH*q{nri;uni~!Ld=(Vw*P(>z+w@A z#SKDg6g)jWZ2>MT|L5J^Ezyl|OO$DFSZfEu4Y)X3V5}pTT-2zUX|yH z(!66?^Z$`q1pGgl#R@9Owo(@eobxAIK-Lln5WR%iWP-3SDZ!4_YMK49P9dJaGYOqU zfu_O_Cr7`3q|vruMT<|+sFAZlTqDL%l1NTsf(_T8FRC;mTw$GsH8cW5_^ANPU+%K|aG$ z8~PFPA#6gVhMqQ|;d#qA&VWX+{Wp@x)>FqYR>a+DMzp;9s_Hwn=EpO7!oMXI-@MNn zqe}nYb1QP76-(V>Al##;u2Gkw}oYmC#Tvf?D}D$)~tL;OBl`l$SU+ zSEwka*|oNNdhA3mxQZ+;F21o;XQ5uacXo#KbhxwU#0!XiFu~IZ00DsT=uZwivJ)IM zCITOu75&4coS1_xs1jK)Qlry0Eo~z-pEyG*Qgt5JzVAs!jfrc^lCn5461p4KZ2EiH z&B(?~%!#9T&Oo-E5_y>a4^@Szzk{W9dWO`vSMruI3~IpVrB!{oI`x+Dsy@EsB4m1}qN5!6$(fBG0c zXfUS9$jHb(09H44y)*FIoPNWUq2j8FycLfvX+}8UIY`n4XUlY!`#mHqL}c=q?4N0T zGGrj~Fv31g!os3}d~}|iXt$3^!N7nZo5VC)tp^BhQDw;dNN> z_4TIM&Uy@k^($L+2ad9YI~;R@f-*dvA5F?#%`+zwl18HlM~C% z&TixLHNfisNJ>glL%>qbr4K1FpDpfOUl)q1oi^YAZwwN)wPiWT{=o!dgMRWa1A=C$ z^x464Dr{)MYp1LM>!f*V8pCpvBRzll2^w()Opme;*VYPnkCjh5uRQ1A9gzXAD zd|W~aar7STJEwOhzpCbfn=kewA}jD2XpBMm!)Lxv^PYr%$TE9WVmqA2jp8a#v|8gL z6XH1AZGAXi)~#~bM-|7omHZWIb!j3G1!iM&7Tc)j;~hC1t0lwx5Y6LZo~&Fk=)%duL2{MBl(fy#_aOS! zU-CtUYgV9x(WbNCc=Nln|93I^G~4A0g|*(jkUgrG31z%Q%yi!H=1Mgq-Um$&N;4*A#)-za4G;=-Rm4jXld9Z= zg*?3av{$?!v+O*Z(@@DwFM4>)P~&xiN@oOXytv(>Ff+zEomZwmplp`h>pwX?`Xlyc z6FJs=blSc*MsLd!5b7dgEeI>e3OBjzue3fyUTDz6?t5qDPxwpjQxQZ`36H71Oc^u$ z3iHDDa&+(lNS)Gnl&Jk?iqW9#+_$9YYD&oQj*j)IdI0sF0g04r=)1mQ>?bu!lVu`; z>Xyyv9{4a;%MmY%W=PnGGJp|)3*&om7&y!7>356=x*r0N_3HU_Z|6;q%V?9#nC%NGy z+~KyHNv2;z#)7!5@_W;D`luv zzMOGTkEslgb}&gG(`N*D4dR^U?93JSZ^Mud?+y@a&yM&SEqB7jX{DcI?FfXt?G-qC z=%envi2|M+LGc4zn*pK;Y#%xrbj;FyWm}gztDCEA14RJsP^S#@eRwqrld;^6%cHj@v~nH(@5#A^FlUc zVGI4aGI_wO`l~FLDEv!#%LeAIMl-}Knhv=n`MoXq%<41*Vd$sAg6j2LPwH{-?G-Rf zELWdg+Em}as{SPtvy6IV-u0t8C9aZ!SPZ)~zNOee4GvaT#B281;Oc@dnBf!eERj}u zT1Ve(9LsrF-N$^JMm>k~HDdW0#u75Eh1So6?#N~p(rP*wjqQBUE~R}ll=!w9!D<|{ zF)nx|1#;ctc}~@hS_#!=Ynzk~UYXg|cmkwY#nc%`#?<<>J&d4Ph*%bR1|`BW@(Kbi zUmKG^y;)#Hp1^X$y)4dy%l1smH^i2=dzoaqg^B8g`k_{whmYPLVCiyK7ztjl{ngGA zyT6yobg?z$s8o1e=<*_kaT3W%^-Q5HP`q<;(`8QOzVF);ahu4*3xNN14b|Lhwp5=i zru7X%Rt%u&M+UVg(rZ2~{Q#{+|b1o-v80tNq9 zjsIv!fnK(m-1caO*5iKF+2wkaE1`~nQJ7h$1+>Nm>IRbhXUZ7`AN@Zt=(KQqy*--l z`o&^7)K=!4>jq92$kSUivzv}(8V$w*BR-nR7;%HVd}Q>L`Z|0dSWQN7v>I$h9`T6) z<>;E%vz6sit;}G-xpJ(#$JoP6028>VsAxQqmXn--OyJdGMf+Q5Bq1N2ZtqN7kHKbF zK$XcTo-(usgrvAQjp*$_3^@o6t}74`e3aC_5GhL0EnRs*6Z9BHshg< zw&yd55HqyOT8dm{20OjA@Or0Hjl6DPuE#{K47ygclbkv_;38Cssp$!9i#wG4ZMj%Y z=j~UT%YbIkl7H3{&lb#=fy%r$Xz^6UsNMA8Pgr}D;FB~XF7>185=W;Hx|gVIUz7w`o8yqd#o zcGTl$)SQ{#6AH;F#6T{y^e4~mvY)sg7+UjHVCC#o_xO)qa)%xK|F21*=fTj9oDb@@D4eKbtx8Sa(ZbnKUd_h+-a18p!r8X^6et$R`F4ZCeeC5 z6!ps0bBUn#ii`ZnAsLPTb-BLM;M&f=$tBYxE@m?j(}v0ZQZE;V5#gP~E1+Xv$>>F^d^wi8={ymv=q`TVTh z^)Aej%_>r&&K<)TAqsY{XuKwYWO}LfjmYz&^?Ju!t}a$OkW|bB2;QVWqtewPz8UDh z>V-yWYqT-FTXIE0VfgU`_{+h;cSfIYT}~9rU!LTR2L#J0YXc~HwRp!S<``R~*MGFR ze;@l)l8(`tbk95W=u8TpK@01`JK!Bo!xrmL7E%wq}Nx`*jD5Yg-rLwg)IdI1LG*2P8>U18YJlXL?fDDbO9uGVG>c;$b3dOP22>= z`Oq!G=eEUJjD%RS}2kV=2#SyJwHz{A%z;`G1zZO*apwZ}d-*cX>TGM*{DZCcC)a$` zv@h4`_aH8~tq#`B_b;_YpuKGQY)0c3-+B~PiPbJlzk?DK-R-R{iY&ESdY3S6Rvmpz zdhyMT4Lu;jUW2zm{(&4bNFu~_EHv*__KJ1GngxvN!WSe?pg|QHx0tJ3U* zKx*eyi@{9he}Q|6tnS){)c+Z8;^Epp7UXQZ`tjtI3(vyTIo_k^W58jV5aGpaJkkSN zR@@?HMavk#8uZH}w^`b}ojH?P`-q4G0PK?n=HU zeiiZfkd5AQAOmt~M_7cf15C_1#3aO`vqDJkBLqJt-LVXxvFk3p9@u33>(0)TGU9ek z)^~C4w~1z6%0ZF-Q^;<;N>4nFB5y5{g|Q#1XNv+M$C#>z(K7m4ciM8F%Owu?_V{%O zCmDWu6d#Dig4}a%ZT%fwq=t--oDjq`Wb3diw9<;9L? zsh}i*v;~8#va$)|QM|RQ>5E!mC`nH*kY71it;`?hCRv@McfbJyLm*&>^_BQpX^y$uE338?dI|rQEl64#>N1HvnZH3h6zWLYa|+~_ z$bKiE^EIQVp^-)__Rc-6GgvXuA_HNGPGH^I|GlJY=qTXFyLG0SY)v`x=<5CnQ@@6@ zy9cThOX*%c59qK(%Fg^M?H)fMe7k4Nw@D5Zlo)Isju>6=;1C(2M>=*>Xibvv$BgO1 z6xJmcus>al91e8p$wr}@c?4a1!}mD1wsp<3dv(XDq@C>*j_?&=&0Q^l0eDGL+VfaudXT#qtMv8i!t`|bFBS6X+d=szp-o0)or zlAP~je=~jOC5>oz{$6s)r`&U2%jk7itb$b!t_1b7>tMBt2rsEJiOBCoYSW>GVZ(|f zZ0Uh*jb)WTERK}=;QWq*8zafM{}<`Lo%J-QD=RW ze2fGG*$X`3bRLtWQi!E$A7H%eX~DI%D6F*vB46?6WY36{oQ#uo(c#fCd7R8xTiW7{ zzYlP%z`@3zt%fy`TosROe#!-dl9A>#8`}`~Z~3e=m$FtPzj*YJY2dnpN_LFyZ!hMa zETN{T*5HcsLrI2;;OQDPSWRJe@8Co0gdHgr_w7SKN&f&sn*Tdig8yzgQvaTpAK?*r z@~yU^%mxR~f|ED_v@^qlyybeGjvLgePK0(p(?`e4$+3LG%~ zR~k_uP?SSk2D}fWmKi!YhWf}uLRsLUdLE}Va0Lx^Ff$28W>pE82KVqqjJTZz^{^Om7y

    CE-eX2iK_qX@_a3 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 AqW}N^ diff --git a/docs/stix-java-notes.md b/docs/stix-java-notes.md deleted file mode 100644 index c2eaeb2..0000000 --- a/docs/stix-java-notes.md +++ /dev/null @@ -1,35 +0,0 @@ -# STIX Java library Notes - -# Relation.class - -The _Relation_ class is a wrapper class for _Stix Relationships Objects_ (SROs) (Relationship.class and Sighting.class). - -Relation.class allows the wrapping of a String Id or a Bundleable Object. This means SDOs, SROs, and Data Markings -(Marking Definitions). - -When a object is converted into a JSON string, any relationships that are within the Java object -(attributes that link to another object, whether a list of 1-1) are converted to a String ID. -When a JSON string is parsed back into a Java Object, there is no guarantee that the parsed object will contain -the other objects to complete the relationship chain. Therefore the Relation.class wrapper is used. - -When any Object is converted into JSON String, it will convert all attributes that are relations into the String ID -of the related object. - -When any object is parsed from JSON into a Java object, all relation fields will be parsed into a Relation -object that contains only the ID. -If you wish to "hydrate" the objects' relations with the pointers to the actual Objects, and thus make them navigable, -you can use the "hydrate" method helpers found in each object. - -# Object Hydration - -The process of providing a list of bundleable objects (``) to which the object to whihc you call -the hydration method on, will search through the list and create pointers to the real object and update the -Relation Wrapper from being String ID to using the actual Object. -... - -# Bundle Auto Population - -The process of a bundle performing a recursive search on all objects in the bundle, and finding any nested objects -that should be added into the bundle as "Bundle Objects". This allows someone to generate STIX objects in a fluent -and nested fashion, and not have to worry about remembering to add all objects into the bundle. -... \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7873e26 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Sep 28 14:45:46 EDT 2019 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..af6708f --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..0f8d593 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mvnw b/mvnw deleted file mode 100755 index 5551fde..0000000 --- a/mvnw +++ /dev/null @@ -1,286 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - wget "$jarUrl" -O "$wrapperJarPath" - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - curl -o "$wrapperJarPath" "$jarUrl" - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd deleted file mode 100755 index 48363fa..0000000 --- a/mvnw.cmd +++ /dev/null @@ -1,161 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% -) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% -) -@REM End of extension - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml deleted file mode 100644 index c284203..0000000 --- a/pom.xml +++ /dev/null @@ -1,199 +0,0 @@ - - - 4.0.0 - - io.digitalstate.stix - stix - v0.10.0 - jar - - STIX 2 - STIX Java Library - - - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.8 - 2.9.9 - - - - - jcenter - https://jcenter.bintray.com/ - - - - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.verion} - compile - - - com.fasterxml.jackson.module - jackson-module-parameter-names - ${jackson.verion} - compile - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - ${jackson.verion} - compile - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.verion} - compile - - - com.fasterxml.jackson.datatype - jackson-datatype-guava - ${jackson.verion} - compile - - - - com.jayway.jsonpath - json-path - 2.4.0 - compile - - - - org.hibernate.validator - hibernate-validator - 6.0.13.Final - compile - - - org.glassfish - javax.el - 3.0.1-b09 - compile - - - - org.apache.commons - commons-lang3 - 3.8.1 - compile - - - - org.springframework - spring-expression - 5.1.4.RELEASE - compile - - - - - org.immutables - value - 2.7.3 - provided - - - org.immutables - annotate - 2.7.3 - provided - - - org.immutables - serial - 2.7.3 - provided - - - - - - org.spockframework - spock-core - 1.2-groovy-2.4 - test - - - - org.codehaus.groovy - groovy-all - 2.4.15 - test - - - net.bytebuddy - byte-buddy - 1.8.21 - test - - - org.objenesis - objenesis - 2.6 - test - - - org.hamcrest - hamcrest-core - 1.3 - test - - - - - net.andreinc.mockneat - mockneat - 0.3.2 - test - - - - org.skyscreamer - jsonassert - 1.5.0 - test - - - - - - - - - org.codehaus.gmavenplus - gmavenplus-plugin - 1.6 - - - - - compileTests - - - - - - - - maven-surefire-plugin - 2.20.1 - - false - - **/*Test.java - **/*Spec.java - - - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..4fe0a93 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'STIX-Java-Kotlin' \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/bundle/BundleObject.java b/src/main/java/io/digitalstate/stix/bundle/BundleObject.java deleted file mode 100644 index 15229b8..0000000 --- a/src/main/java/io/digitalstate/stix/bundle/BundleObject.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.digitalstate.stix.bundle; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import io.digitalstate.stix.common.Stix; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.helpers.StixSpecVersion; -import io.digitalstate.stix.json.StixParsers; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import java.io.IOException; -import java.io.Serializable; -import java.util.Set; - -/** - * bundle - *

    - * A Bundle is a collection of arbitrary STIX Objects and Marking Definitions grouped together in a single container. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "bundle", groups = {DefaultValuesProcessor.class}) -@JsonTypeName("bundle") -@Value.Style(typeImmutable = "Bundle", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Bundle.class) @JsonDeserialize(builder = Bundle.Builder.class) -@JsonPropertyOrder({"type", "id", "spec_version", "objects"}) -public interface BundleObject extends GenericValidation, Serializable, StixCustomProperties, Stix { - - @NotBlank - @JsonProperty("type") - @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).") - @Pattern(regexp = "^\\-?[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\-?$") - @Size(min = 3, max = 250) - String getType(); - - @NotBlank - @JsonProperty("id") - @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.") - @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}$") - String getId(); - - @NotBlank - @JsonProperty("spec_version") - @JsonPropertyDescription("The version of the STIX specification used to represent the content in this bundle.") - @Value.Default - default String getSpecVersion() { - return StixSpecVersion.SPECVERSION; - } - - @Size(min = 1, message = "Must have at least 1 object in bundle") - @JsonProperty(value = "objects", access = JsonProperty.Access.WRITE_ONLY) - @JsonPropertyDescription("Specifies a set of one or more STIX Objects.") - Set getObjects(); - - @JsonIgnore - @Value.Lazy - default String toJsonString() { - JsonNode response = StixParsers.getJsonMapper().valueToTree(this); - ObjectNode responseNode = (ObjectNode) response; - //@TODO Refactor as this is causing custom properties to come before the Objects prop: - responseNode.putArray("objects"); - ArrayNode objects = (ArrayNode) response.get("objects"); - - getObjects().forEach(o -> { - String redactedJson = o.toJsonString(); - try { - //@TODO refactor so the double JSON parsing does not need to happen - JsonNode redactedJsonNode = StixParsers.getJsonMapper().readTree(redactedJson); - ObjectNode redactedObjectNode = (ObjectNode) redactedJsonNode; - if (!redactedObjectNode.isNull() && redactedObjectNode.size() > 0) { - objects.add(redactedObjectNode); - } - } catch (IOException e) { - throw new IllegalStateException("Failed to Parse Object within bundle JSON"); - } - }); - - try { - return StixParsers.getJsonMapper().writeValueAsString(response); - } catch (JsonProcessingException e) { - throw new IllegalStateException("Failed to Parse Bundle JSON"); - } - - } -} diff --git a/src/main/java/io/digitalstate/stix/bundle/BundleableObject.java b/src/main/java/io/digitalstate/stix/bundle/BundleableObject.java deleted file mode 100644 index 12ec141..0000000 --- a/src/main/java/io/digitalstate/stix/bundle/BundleableObject.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.digitalstate.stix.bundle; - -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import io.digitalstate.stix.common.Stix; -import io.digitalstate.stix.custom.objects.CustomObject; -import io.digitalstate.stix.datamarkings.GranularMarkingDm; -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; - -import java.io.Serializable; -import java.util.Set; - -/** - * This interface is typically inherited by other interfaces that are considered "objects" that are part of a Bundle. - * Thus the name "BundleableObject". A Bundleable Object by STIX standard is: SDO, SRO, and Marking Definition. - * The Type field is used to determine the sub-types as registered in the {@link io.digitalstate.stix.json.StixParsers} - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true, defaultImpl = CustomObject.class) -public interface BundleableObject extends Serializable, Stix { - - String getType(); - String getId(); - Set getObjectMarkingRefs(); - Set getGranularMarkings(); - boolean getHydrated(); - String toJsonString(); - -} diff --git a/src/main/java/io/digitalstate/stix/common/Stix.java b/src/main/java/io/digitalstate/stix/common/Stix.java deleted file mode 100644 index dfedfea..0000000 --- a/src/main/java/io/digitalstate/stix/common/Stix.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.digitalstate.stix.common; - -/** - * Base generic interface used for indentifying that a object is a Stix based object (a Bundle or Bundleable object) - * Used mainly for generics and parsing. - * May be used in the future as a entry point for STIX instance creation - */ -public interface Stix { -} diff --git a/src/main/java/io/digitalstate/stix/common/StixBoolean.java b/src/main/java/io/digitalstate/stix/common/StixBoolean.java deleted file mode 100644 index c873461..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixBoolean.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.digitalstate.stix.common; - -/** - * Wrapper for boolean that is used in Stix to track if the boolean value was User provided. - * Custom class is used instead of a Boolean in order to provide a more robust api - */ -public class StixBoolean { - - private boolean stixBooleanValue; - private boolean isDefinedValue; - - public StixBoolean(boolean stixBooleanValue) { - this.stixBooleanValue = stixBooleanValue; - this.isDefinedValue = true; - } - - public StixBoolean(boolean stixBooleanValue, boolean isDefinedValue) { - this.stixBooleanValue = stixBooleanValue; - this.isDefinedValue = isDefinedValue; - } - - /** - * Defaults to StixBoolean value to false. Sets isDefinedValue to false - */ - public StixBoolean() { - this.stixBooleanValue = false; - isDefinedValue = false; - } - - public StixBoolean(String booleanString){ - this.stixBooleanValue = Boolean.valueOf(booleanString); - this.isDefinedValue = true; - } - - public boolean getStixBooleanValue() { - return stixBooleanValue; - } - - /** - * Indicates that the boolean value was explicitly defined, even if the value was false, - * and the original object's property defaults to false if no value is provided. - * @return boolean indicating if the value was defined. - */ - public boolean isdefinedValue() { - return isDefinedValue; - } - - @Override - public String toString() { - return String.valueOf(getStixBooleanValue()); - } - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixCommonProperties.java b/src/main/java/io/digitalstate/stix/common/StixCommonProperties.java deleted file mode 100644 index 2d19593..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixCommonProperties.java +++ /dev/null @@ -1,122 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.datamarkings.GranularMarkingDm; -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; -import io.digitalstate.stix.json.StixParsers; -import io.digitalstate.stix.json.converters.dehydrated.DomainObjectOptionalConverter; -import io.digitalstate.stix.json.converters.dehydrated.MarkingDefinitionSetConverter; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.objects.IdentitySdo; -import io.digitalstate.stix.sdo.types.ExternalReferenceType; -import io.digitalstate.stix.validation.SdoDefaultValidator; -import io.digitalstate.stix.validation.groups.ValidateIdOnly; -import org.immutables.value.Value; - -import javax.validation.ConstraintViolationException; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import javax.validation.groups.Default; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * Base interface used by Immutable STIX Bundleable Objects - */ -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface StixCommonProperties extends StixSpecVersion, SdoDefaultValidator, BundleableObject { - - /** - * Dictates if the object is hydrated. - * Hydration is defined as if the Object has only a "ID" or has been properly - * hydrated with the expected required fields - * @return boolean - */ - @NotNull - @Value.Default - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) - default boolean getHydrated(){ - return true; - } - - @JsonProperty("type") - @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).") - @Pattern(regexp = "^\\-?[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\-?$") - @Size(min = 3, max = 250) - @NotBlank(groups = {Default.class, ValidateIdOnly.class}, message = "Type is required") - String getType(); - - @JsonProperty("id") - @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.") - @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}$") - @NotBlank(groups = {Default.class, ValidateIdOnly.class}, message = "Id is required") - String getId(); - - @JsonProperty("created_by_ref") @JsonInclude(value = NON_EMPTY, content = NON_EMPTY) - @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.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = DomainObjectOptionalConverter.class) - @Redactable(useMask = true, redactionMask = "identity--__REDACTED__") - Optional getCreatedByRef(); - - @NotNull - @JsonProperty("created") - @JsonPropertyDescription("The created property represents the time at which the first version of this object was created. The timstamp value MUST be precise to the nearest millisecond.") - @Value.Default - @Redactable(useMask = true) - default StixInstant getCreated(){ - return new StixInstant(); - } - - @NotNull - @JsonProperty("external_references") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("A list of external references which refers to non-STIX information.") - @Redactable - Set getExternalReferences(); - - @NotNull - @JsonProperty("object_marking_refs") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The list of marking-definition objects to be applied to this object.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = MarkingDefinitionSetConverter.class) - @Redactable - Set getObjectMarkingRefs(); - - @NotNull - @JsonProperty("granular_markings") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The set of granular markings that apply to this object.") - @Redactable - Set getGranularMarkings(); - - @JsonIgnore - @Value.Lazy - @Value.Auxiliary - default String toJsonString() { - try { - String jsonString = StixParsers.getJsonMapper().writeValueAsString(this); -// return BundleableObjectRedactionProcessor.processObject(this, jsonString, new HashSet<>(Arrays.asList())); - return jsonString; - } catch (JsonProcessingException e) { - throw new IllegalStateException("Cannot process JSON", e); - } - } - - @Value.Check - default void checkHydrationValidation() throws ConstraintViolationException { - if (getHydrated()){ - this.validate(); - } else { - this.validateOnlyId(); - } - } - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixCustomObjectId.java b/src/main/java/io/digitalstate/stix/common/StixCustomObjectId.java deleted file mode 100644 index 7a7d1ed..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixCustomObjectId.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.validation.contraints.startswith.StartsWith; -import io.digitalstate.stix.validation.groups.ValidateIdOnly; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.groups.Default; - -/** - * - */ -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface StixCustomObjectId { - - @JsonProperty("id") - @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.") - @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}$") - @NotBlank(groups = {Default.class, ValidateIdOnly.class}, message = "Id is required") - @StartsWith("x-") - String getId(); - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixCustomObjectType.java b/src/main/java/io/digitalstate/stix/common/StixCustomObjectType.java deleted file mode 100644 index 04c6ffb..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixCustomObjectType.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import io.digitalstate.stix.validation.contraints.startswith.StartsWith; -import io.digitalstate.stix.validation.groups.ValidateIdOnly; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import javax.validation.groups.Default; - -/** - * - */ -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface StixCustomObjectType { - - @JsonProperty("type") - @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).") - @Pattern(regexp = "^\\-?[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\-?$") - @Size(min = 3, max = 250) - @NotBlank(groups = {Default.class, ValidateIdOnly.class}, message = "Type is required1") - @StartsWith("x-") - String getType(); - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixCustomProperties.java b/src/main/java/io/digitalstate/stix/common/StixCustomProperties.java deleted file mode 100644 index c8481a2..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixCustomProperties.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import io.digitalstate.stix.validation.contraints.startswith.StartsWith; -import org.hibernate.validator.constraints.Length; - -import java.util.Map; - -/** - * Stix Custom Properties - */ -public interface StixCustomProperties { - - /** - * Custom Properties for STIX Objects. - * Any object that supports custom properties will have a validation of the custom property prefix (typically "x_"). - * If the additional property in the JSON does not meet the StartsWith condition, then the JSON will be rejected. - * @return Map of custom properties {@code Map} - */ - @JsonProperty(access = JsonProperty.Access.READ_ONLY) - @JsonUnwrapped @JsonAnyGetter - 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(); - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixInstant.java b/src/main/java/io/digitalstate/stix/common/StixInstant.java deleted file mode 100644 index d2f7bf1..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixInstant.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.digitalstate.stix.common; - -import io.digitalstate.stix.helpers.StixDataFormats; - -import java.time.Instant; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Stores a Java.time Instant and stores the original sub-second precision digital count. - * SubSecond precision count means that the number of digits (even if all zeros / trailing zeros) are remembered. - * Use toString() to get the Stix string format for the datetime. - */ -public class StixInstant { - - public static final Pattern REGEX_SUBSECOND = Pattern.compile("(?\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(.(?[0-9]+))?Z)"); - - private final Instant instant; - private final int originalSubSecondPrecisionDigitCount; - - /** - * Generates a Instant.now() - * Defaults to 3 digits of sub second precision (milliseconds) - */ - public StixInstant() { - Instant now = Instant.now(); - this.instant = now; - this.originalSubSecondPrecisionDigitCount = 3; - } - - public StixInstant(Instant instant, int subSecondPrecision) { - Objects.requireNonNull(instant); - if (subSecondPrecision < 0 || subSecondPrecision > 9){ - throw new IllegalStateException("Subsecond precision must be between 0 and 9"); - } - - this.instant = instant; - this.originalSubSecondPrecisionDigitCount = subSecondPrecision; - } - - /** - * Defaults with parsed Instant's Nano second precision digit count. - * Note that trailing zeros would have been stripped. - * If you need to keep exact precision, including the trailing zeros, use {@link StixInstant#StixInstant(Instant, int)} - * @param instant - */ - public StixInstant(Instant instant) { - this(instant, String.valueOf(instant.getNano()).length()); - } - - /** - * Get the underlying Instant value. If you need the Stix Date with the Stix Precision then use {@link StixInstant#toString()} - * getInstant() should only be used in special cases where you need access to perform temporal work. - * @return - */ - public Instant getInstant() { - return instant; - } - - public int getOriginalSubSecondPrecisionDigitCount() { - return originalSubSecondPrecisionDigitCount; - } - - /** - * Generates a STIX Spec String of DateTime. - * Uses {@link StixDataFormats#getWriterStixDateTimeFormatter(int)} - * @return - */ - @Override - public String toString() { - return StixDataFormats.getWriterStixDateTimeFormatter(originalSubSecondPrecisionDigitCount) - .format(this.instant); - } - - public String toString(int subSecondPrecision) { - return StixDataFormats.getWriterStixDateTimeFormatter(subSecondPrecision) - .format(this.instant); - } - - public static StixInstant parse(String dateString){ - Objects.requireNonNull(dateString); - - Instant instant = Instant.from(StixDataFormats.getReaderStixDateTimeFormatter().parse(dateString)); - int subSecondPrecision = getSubSecondDigitCount(dateString); - - return new StixInstant(instant, subSecondPrecision); - } - - private static int getSubSecondDigitCount(String dateString){ - Matcher matcher = REGEX_SUBSECOND.matcher(dateString); - if (matcher.find()){ - String subSeconds = matcher.group("subSecond"); - // If no sub seconds were provided then return 0 as the precision - if (subSeconds == null){ - return 0; - } else { - return subSeconds.length(); - } - - } else { - throw new IllegalStateException("Unable to parse date"); - } - } -} diff --git a/src/main/java/io/digitalstate/stix/common/StixLabels.java b/src/main/java/io/digitalstate/stix/common/StixLabels.java deleted file mode 100644 index 7f9dbc8..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixLabels.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import io.digitalstate.stix.redaction.Redactable; -import org.hibernate.validator.constraints.Length; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * - */ -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface StixLabels { - - @NotNull - @JsonProperty("labels") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The labels property specifies a set of classifications.") - @Redactable - Set<@Length(min = 1) String> getLabels(); - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixModified.java b/src/main/java/io/digitalstate/stix/common/StixModified.java deleted file mode 100644 index bfe1b09..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixModified.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.json.StixInstantDeserializer; -import io.digitalstate.stix.json.StixInstantSerializer; -import io.digitalstate.stix.redaction.Redactable; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.time.Instant; - -/** - * - */ -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface StixModified { - - @NotNull - @JsonProperty("modified") - @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.") - @Value.Default - @Redactable - default StixInstant getModified(){ - return new StixInstant(); - } -} diff --git a/src/main/java/io/digitalstate/stix/common/StixRevoked.java b/src/main/java/io/digitalstate/stix/common/StixRevoked.java deleted file mode 100644 index a8f8e9a..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixRevoked.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import io.digitalstate.stix.redaction.Redactable; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * - */ -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface StixRevoked { - - @NotNull - @JsonProperty("revoked") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The revoked property indicates whether the object has been revoked.") - @Value.Default - @Redactable - default StixBoolean getRevoked(){ - return new StixBoolean(); - } - -} diff --git a/src/main/java/io/digitalstate/stix/common/StixSpecVersion.java b/src/main/java/io/digitalstate/stix/common/StixSpecVersion.java deleted file mode 100644 index 8bd5fa1..0000000 --- a/src/main/java/io/digitalstate/stix/common/StixSpecVersion.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.digitalstate.stix.common; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; - -public interface StixSpecVersion { - - /** - * Helper attribute to track the STIX Spec Version that was used for this object. - * @return String of STIX Spec Version, example: "2.0" - */ - @NotBlank - @JsonIgnore - @Value.Lazy - default String getSpecVersion(){ - return io.digitalstate.stix.helpers.StixSpecVersion.SPECVERSION; - } - -} diff --git a/src/main/java/io/digitalstate/stix/coo/CyberObservableObject.java b/src/main/java/io/digitalstate/stix/coo/CyberObservableObject.java deleted file mode 100644 index 1e8304a..0000000 --- a/src/main/java/io/digitalstate/stix/coo/CyberObservableObject.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.digitalstate.stix.coo; - -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.ValidateExtensions; - -import java.io.Serializable; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) -@ValidateExtensions -public interface CyberObservableObject extends Serializable, - CyberObservableObjectCommonProperties, - StixCustomProperties { - -} diff --git a/src/main/java/io/digitalstate/stix/coo/CyberObservableObjectCommonProperties.java b/src/main/java/io/digitalstate/stix/coo/CyberObservableObjectCommonProperties.java deleted file mode 100644 index 8bce213..0000000 --- a/src/main/java/io/digitalstate/stix/coo/CyberObservableObjectCommonProperties.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.digitalstate.stix.coo; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.json.extension.CyberObservableExtensionsFieldDeserializer; -import io.digitalstate.stix.coo.json.extension.CyberObservableExtensionsFieldSerializer; -import io.digitalstate.stix.sdo.objects.ObservedDataSdo; -import io.digitalstate.stix.validation.GenericValidation; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; -import java.util.Set; -import java.util.UUID; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -public interface CyberObservableObjectCommonProperties extends GenericValidation { - - @NotBlank - @JsonProperty("type") - @JsonPropertyDescription("Indicates that this object is an Observable Object. The value of this property MUST be a valid Observable Object type name, but to allow for custom objects this has been removed from the schema.") - @Pattern(regexp = "^\\-?[a-z0-9]+(-[a-z0-9]+)*\\-?$") - @Size(min = 3, max = 250) - String getType(); - - /** - * Multiple extensions can be added, but only 1 instance of a specific extension can be added. - * @return - */ - // @TODO Add validation to ensure that only 1 instance of each extension is applied as per the spec - @JsonProperty("extensions") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Specifies any extensions of the object, as a dictionary.") - @JsonSerialize(using = CyberObservableExtensionsFieldSerializer.class) - @JsonDeserialize(using = CyberObservableExtensionsFieldDeserializer.class) - Set getExtensions(); - - /** - * Used for generation of Map Keys by {@link ObservedDataSdo#getObjects()} - * Manually set this value if you want to control key names. Otherwise UUIDs will be used. - * @return - */ - @JsonProperty(value = "observable_object_key", access = JsonProperty.Access.WRITE_ONLY) - @Value.Default - default String getObservableObjectKey(){ - return UUID.randomUUID().toString(); - } - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtension.java b/src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtension.java deleted file mode 100644 index c08c5c7..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtension.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.digitalstate.stix.coo.extension; - -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; - - -/** - * Interface to tag Cyber Observable Extension classes - * - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY) -public interface CyberObservableExtension extends - CyberObservableExtensionCommonProperties, - GenericValidation, - StixCustomProperties { - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtensionCommonProperties.java b/src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtensionCommonProperties.java deleted file mode 100644 index 65fa007..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/CyberObservableExtensionCommonProperties.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.digitalstate.stix.coo.extension; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; - -@Value.Style(validationMethod = Value.Style.ValidationMethod.NONE) -public interface CyberObservableExtensionCommonProperties { - - /** - * This property is used for generation of the dictionary during serialization, and used as the "Type" mapping value for polymorphic when deserializing. - * @return - */ - @NotBlank - @JsonIgnore - @JsonProperty("type") - @Size(min = 3, max = 250) - String getType(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/ArchiveFileExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/ArchiveFileExtensionExt.java deleted file mode 100644 index 3981d2c..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/ArchiveFileExtensionExt.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.FileCoo; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The Archive File extension specifies a default extension for capturing - * properties specific to archive files. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "archive-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = ArchiveFileExtension.class) @JsonDeserialize(builder = ArchiveFileExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "contains_refs", "version", "comment" }) -@JsonTypeName("archive-ext") -@AllowedParents({FileCoo.class}) -public interface ArchiveFileExtensionExt extends CyberObservableExtension { - - @JsonProperty("contains_refs") - @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.") - @NotNull - Set getContainsRefs(); - - @JsonProperty("version") - @JsonPropertyDescription("Specifies the version of the archive type used in the archive file.") - Optional getVersion(); - - @JsonProperty("comment") - @JsonPropertyDescription("Specifies a comment included as part of the archive file.") - Optional getComment(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/HttpRequestExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/HttpRequestExtensionExt.java deleted file mode 100644 index bd1ca8d..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/HttpRequestExtensionExt.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.NetworkTrafficCoo; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * http-request-ext - *

    - * The HTTP request extension specifies a default extension for capturing - * network traffic properties specific to HTTP requests. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "http-request-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = HttpRequestExtension.class) @JsonDeserialize(builder = HttpRequestExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "request_method", "request_value", "request_version", "request_header", "message_body_length", - "message_body_data_ref" }) -@JsonTypeName("http-request-ext") -@AllowedParents({NetworkTrafficCoo.class}) -public interface HttpRequestExtensionExt extends CyberObservableExtension { - - @JsonProperty("request_method") - @JsonPropertyDescription("Specifies the HTTP method portion of the HTTP request line, as a lowercase string.") - @NotNull - String getRequestMethod(); - - @JsonProperty("request_value") - @JsonPropertyDescription("Specifies the value (typically a resource path) portion of the HTTP request line.") - @NotNull - String getRequestValue(); - - @JsonProperty("request_version") - @JsonPropertyDescription("Specifies the HTTP version portion of the HTTP request line, as a lowercase string.") - Optional getRequestVersion(); - - /** - * Currently only supports non-duplicate keys: https://github.com/oasis-tcs/cti-stix2/issues/137 - * @return - */ - @JsonProperty("request_header") - @JsonPropertyDescription("Specifies all of the HTTP header fields that may be found in the HTTP client request, as a dictionary.") - Map getRequestHeader(); - - //@TODO Review if this should be a long - @JsonProperty("message_body_length") - @JsonPropertyDescription("Specifies the length of the HTTP message body, if included, in bytes.") - Optional getMessageBodyLength(); - - /* - * Must be of type artifact - */ - @JsonProperty("message_body_data_ref") - @JsonPropertyDescription("Specifies the data contained in the HTTP message body, if included.") - Optional getMessageBodyDataRef(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/IcmpExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/IcmpExtensionExt.java deleted file mode 100644 index 90fdae3..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/IcmpExtensionExt.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.NetworkTrafficCoo; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * icmp-ext - *

    - * The ICMP extension specifies a default extension for capturing network - * traffic properties specific to ICMP. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "icmp-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = IcmpExtension.class) @JsonDeserialize(builder = IcmpExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "icmp_type_hex", "icmp_code_hex" }) -@JsonTypeName("icmp-ext") -@AllowedParents({NetworkTrafficCoo.class}) -public interface IcmpExtensionExt extends CyberObservableExtension { - - @JsonProperty("icmp_type_hex") - @JsonPropertyDescription("Specifies the ICMP type byte.") - @Pattern(regexp = "^([a-fA-F0-9]{2})+$") - @NotNull - String getOcmpTypeHex(); - - @JsonProperty("icmp_code_hex") - @JsonPropertyDescription("Specifies the ICMP code byte.") - @Pattern(regexp = "^([a-fA-F0-9]{2})+$") - @NotNull - String getIcmpCodeHex(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/NetworkSocketExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/NetworkSocketExtensionExt.java deleted file mode 100644 index 7c90ada..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/NetworkSocketExtensionExt.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.NetworkTrafficCoo; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.NetworkSocketAddressFamilies; -import io.digitalstate.stix.vocabulary.vocabularies.NetworkSocketProtocolFamilies; -import io.digitalstate.stix.vocabulary.vocabularies.NetworkSocketTypes; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.PositiveOrZero; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * socket-ext - *

    - * The Network Socket extension specifies a default extension for capturing - * network traffic properties associated with network sockets. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "socket-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = NetworkSocketExtension.class) @JsonDeserialize(builder = NetworkSocketExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({"address_family", "is_blocking", "is_listening", "protocol_family", "options", "socket_type", - "socket_descriptor", "socket_handle" }) -@JsonTypeName("socket-ext") -@AllowedParents({NetworkTrafficCoo.class}) -public interface NetworkSocketExtensionExt extends CyberObservableExtension { - - @JsonProperty("address_family") - @JsonPropertyDescription("Specifies the address family (AF_*) that the socket is configured for.") - @NotNull - @Vocab(NetworkSocketAddressFamilies.class) - String getAddressFamily(); - - @JsonProperty("is_blocking") - @JsonPropertyDescription("Specifies whether the socket is in blocking mode.") - @NotNull - Optional getBlocking(); - - @JsonProperty("is_listening") - @JsonPropertyDescription("Specifies whether the socket is in listening mode.") - @NotNull - Optional getListening(); - - @JsonProperty("protocol_family") - @JsonPropertyDescription("Specifies the protocol family (PF_*) that the socket is configured for.") - Optional<@Vocab(NetworkSocketProtocolFamilies.class) String> getProtocolFamily(); - - //@TODO Should this enforce SO_* ? - @JsonProperty("options") - @JsonPropertyDescription("Specifies any options (SO_*) that may be used by the socket, as a dictionary.") - Map getOptions(); - - @JsonProperty("socket_type") - @JsonPropertyDescription("Specifies the type of the socket.") - Optional<@Vocab(NetworkSocketTypes.class) String> getSocketType(); - - @JsonProperty("socket_descriptor") - @JsonPropertyDescription("Specifies the socket file descriptor value associated with the socket, as a non-negative integer.") - Optional<@PositiveOrZero Long> getSocketDescriptor(); - - @JsonProperty("socket_handle") - @JsonPropertyDescription("Specifies the handle or inode value associated with the socket.") - Optional getSocketHandle(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/NtfsFileExtenstionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/NtfsFileExtenstionExt.java deleted file mode 100644 index 6a6cd95..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/NtfsFileExtenstionExt.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.FileCoo; -import io.digitalstate.stix.coo.types.NtfsAlternateDataStreamObj; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The NTFS file extension specifies a default extension for capturing properties specific to the storage of the file on the NTFS file system. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "ntfs-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = NtfsFileExtenstion.class) @JsonDeserialize(builder = NtfsFileExtenstion.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "sid", "alternate_data_streams" }) -@JsonTypeName("ntfs-ext") -@AllowedParents({FileCoo.class}) -@BusinessRule(ifExp = "true", thenExp = "getSid().isPresent() == true || getAlternateDataStreams().isEmpty() == false", errorMessage = "NTFS File Extension MUST contain at least one property from this extension") -public interface NtfsFileExtenstionExt extends CyberObservableExtension { - - @JsonProperty("sid") - @JsonPropertyDescription("Specifies the security ID (SID) value assigned to the file.") - Optional getSid(); - - @JsonProperty("alternate_data_streams") - @JsonPropertyDescription("Specifies a list of NTFS alternate data streams that exist for the file.") - Set getAlternateDataStreams(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/PdfFileExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/PdfFileExtensionExt.java deleted file mode 100644 index 7018b21..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/PdfFileExtensionExt.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.FileCoo; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; -/** - * The PDF file extension specifies a default extension for capturing properties - * specific to PDF files. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "pdf-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = PdfFileExtension.class) @JsonDeserialize(builder = PdfFileExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "version", "is_optimized", "document_info_dict", "pdfid0", "pdfid1" }) -@JsonTypeName("pdf-ext") -@AllowedParents({FileCoo.class}) -//@TODO review creating a simple method that will do a "at least 1 field is used" so it can be used within the annotation rather than managing duplicate of all field names: -@BusinessRule(ifExp = "true", thenExp = "getVersion().isPresent() == true || isOptimized().isPresent() == true || getDocumentInfoDict().isEmpty() == false || getPdfId0().isPresent() == true || getPdfId1().isPresent() == true", errorMessage = "At least 1 field must be used in PDF Extension.") -public interface PdfFileExtensionExt extends CyberObservableExtension { - - @JsonProperty("version") - @JsonPropertyDescription("Specifies the decimal version number of the Optional from the PDF header that specifies the version of the PDF specification to which the PDF file conforms. E.g., '1.4'.") - Optional getVersion(); - - @JsonProperty("is_optimized") - @JsonPropertyDescription("Specifies whether the PDF file has been optimized.") - @NotNull - Optional isOptimized(); - - @JsonProperty("document_info_dict") - @JsonPropertyDescription("Specifies details of the PDF document information dictionary (DID), which includes properties like the document creation data and producer, as a dictionary.") - Map getDocumentInfoDict(); - - @JsonProperty("pdfid0") - @JsonPropertyDescription("Specifies the first file identifier found for the PDF file.") - Optional getPdfId0(); - - @JsonProperty("pdfid1") - @JsonPropertyDescription("Specifies the second file identifier found for the PDF file.") - Optional getPdfId1(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/RasterImageFileExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/RasterImageFileExtensionExt.java deleted file mode 100644 index 7b465ae..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/RasterImageFileExtensionExt.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.FileCoo; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The Raster Image file extension specifies a default extension for capturing - * properties specific to image files. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "raster-image-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = RasterImageFileExtension.class) @JsonDeserialize(builder = RasterImageFileExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "image_height", "image_width", "bits_per_pixel", "image_compression_algorithm", "exif_tags" }) -@JsonTypeName("raster-image-ext") -@AllowedParents({FileCoo.class}) -@BusinessRule(ifExp = "true", thenExp = "getImageHeight().isPresent() == true || getImageWidth().isPresent() == true || getBitsPerPixel().isPresent() == false || getImageCompressionAlgorithm().isPresent() == true || getExifTags().isEmpty() == true", errorMessage = "At least 1 field must be used in Raster Image File Extension.") -public interface RasterImageFileExtensionExt extends CyberObservableExtension { - - //@TODO Spec is missing direction about limits: Value likely needs to be MUST be positive - @JsonProperty("image_height") - @JsonPropertyDescription("Specifies the height of the image in the image file, in pixels.") - Optional getImageHeight(); - - //@TODO Spec is missing direction about limits: Value likely needs to be MUST be positive - @JsonProperty("image_width") - @JsonPropertyDescription("Specifies the width of the image in the image file, in pixels.") - Optional getImageWidth(); - - @JsonProperty("bits_per_pixel") - @JsonPropertyDescription("Specifies the sum of bits used for each color channel in the image in the image file, and thus the total number of pixels used for expressing the color depth of the image.") - Optional getBitsPerPixel(); - - @JsonProperty("image_compression_algorithm") - @JsonPropertyDescription("Specifies the name of the compression algorithm used to compress the image in the image file, if applicable.") - Optional getImageCompressionAlgorithm(); - - @JsonProperty("exif_tags") - @JsonPropertyDescription("Specifies the set of EXIF tags found in the image file, as a dictionary. Each key/value pair in the dictionary represents the name/value of a single EXIF tag.") - Map getExifTags(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/TcpExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/TcpExtensionExt.java deleted file mode 100644 index f7073a0..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/TcpExtensionExt.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.NetworkTrafficCoo; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.Pattern; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * tcp-ext - *

    - * The TCP extension specifies a default extension for capturing network traffic - * properties specific to TCP. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "tcp-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = TcpExtension.class) @JsonDeserialize(builder = TcpExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "src_flags_hex", "dst_flags_hex" }) -@JsonTypeName("tcp-ext") -@AllowedParents({NetworkTrafficCoo.class}) -@BusinessRule(ifExp = "true", thenExp = "getSrcFlagsHex().isPresent() == true || getDstFlagsHex().isPresent() == true", errorMessage = "TCP Extension MUST contain at least one property from this extension") -public interface TcpExtensionExt extends CyberObservableExtension { - - /** - * Specifies the source TCP flags, as the union of all TCP flags observed - * between the start of the traffic (as defined by the start property) and - * the end of the traffic (as defined by the end property). - * - */ - @JsonProperty("src_flags_hex") - @JsonPropertyDescription("Specifies the source TCP flags, as the union of all TCP flags observed between the start of the traffic (as defined by the start property) and the end of the traffic (as defined by the end property). ") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") - String> getSrcFlagsHex(); - - /** - * Specifies the destination TCP flags, as the union of all TCP flags - * observed between the start of the traffic (as defined by the start - * property) and the end of the traffic (as defined by the end property). - * - */ - @JsonProperty("dst_flags_hex") - @JsonPropertyDescription("Specifies the destination TCP flags, as the union of all TCP flags observed between the start of the traffic (as defined by the start property) and the end of the traffic (as defined by the end property).") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") - String> getDstFlagsHex(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/UnixAccountExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/UnixAccountExtensionExt.java deleted file mode 100644 index d92fa01..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/UnixAccountExtensionExt.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.UserAccountCoo; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * unix-account-ext - *

    - * The UNIX account extension specifies a default extension for capturing the additional information - * for an account on a UNIX system. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "unix-account-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = UnixAccountExtension.class) @JsonDeserialize(builder = UnixAccountExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "gid", "groups", "home_dir", "shell" }) -@JsonTypeName("unix-account-ext") -@AllowedParents({UserAccountCoo.class}) -@BusinessRule(ifExp = "true", thenExp = "getGid().isPresent() == true || getGroups().isEmpty() == false || getHomeDir().isPresent() == true || getShell().isPresent() == true", errorMessage = "At least one field must be provided for Unix Account Extension") -public interface UnixAccountExtensionExt extends CyberObservableExtension { - - @JsonProperty("gid") - @JsonPropertyDescription("Specifies the primary group ID of the account.") - Optional getGid(); - - @JsonProperty("groups") - @JsonPropertyDescription("Specifies a list of names of groups that the account is a member of.") - Set getGroups(); - - @JsonProperty("home_dir") - @JsonPropertyDescription("Specifies the home directory of the account.") - Optional getHomeDir(); - - @JsonProperty("shell") - @JsonPropertyDescription("Specifies the account\u2019s command shell.") - Optional getShell(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsPeBinaryFileExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsPeBinaryFileExtensionExt.java deleted file mode 100644 index d7eb470..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsPeBinaryFileExtensionExt.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.FileCoo; -import io.digitalstate.stix.coo.types.WindowsPeOptionalHeaderObj; -import io.digitalstate.stix.coo.types.WindowsPeSectionObj; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import io.digitalstate.stix.vocabulary.vocabularies.WindowsPeBinaryTypes; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.PositiveOrZero; -import java.time.Instant; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The Windows PE Binary File extension specifies a default extension for - * capturing properties specific to Windows portable executable (PE) files. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "windows-pebinary-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonSerialize(as = WindowsPeBinaryFileExtension.class) @JsonDeserialize(builder = WindowsPeBinaryFileExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "pe_type", "imphash", "machine_hex", "number_of_sections", "time_date_stamp", - "pointer_to_symbol_table_hex", "number_of_symbols", "size_of_optional_header", "characteristics_hex", - "file_header_hashes", "optional_header", "sections", "required" }) -@JsonTypeName("windows-pebinary-ext") -@AllowedParents({FileCoo.class}) -public interface WindowsPeBinaryFileExtensionExt extends CyberObservableExtension { - - @JsonProperty("pe_type") - @JsonPropertyDescription("Specifies the type of the PE binary. Open Vocabulary - windows-pebinary-type-ov") - @NotNull - @Vocab(WindowsPeBinaryTypes.class) - String getPeType(); - - @JsonProperty("imphash") - @JsonPropertyDescription("Specifies the special import hash, or 'imphash', calculated for the PE Binary based on its imported libraries and functions.") - Optional getImphash(); - - @JsonProperty("machine_hex") - @JsonPropertyDescription("Specifies the type of target machine.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getMachineHex(); - - @JsonProperty("number_of_sections") - @JsonPropertyDescription("Specifies the number of sections in the PE binary, as a non-negative integer.") - Optional getNumberOfSections(); - - @JsonProperty("time_date_stamp") - @JsonPropertyDescription("Specifies the time when the PE binary was created. The timestamp value MUST BE precise to the second.") - Optional getTimeDateStamp(); - - @JsonProperty("pointer_to_symbol_table_hex") - @JsonPropertyDescription("Specifies the file offset of the COFF symbol table.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getPointerToSymbolTableHex(); - - @JsonProperty("number_of_symbols") - @JsonPropertyDescription("Specifies the number of entries in the symbol table of the PE binary, as a non-negative integer.") - Optional getNumberOfSymbols(); - - @JsonProperty("size_of_optional_header") - @JsonPropertyDescription("Specifies the size of the optional header of the PE binary.") - Optional<@PositiveOrZero Long> getSizeOfOptionalHeader(); - - @JsonProperty("characteristics_hex") - @JsonPropertyDescription("Specifies the flags that indicate the file\u2019s characteristics.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getCharacteristicsHex(); - - @JsonProperty("file_header_hashes") - @JsonPropertyDescription("Specifies any hashes that were computed for the file header.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getFileHeaderHashes(); - - @JsonProperty("optional_header") - @JsonPropertyDescription("Specifies the PE optional header of the PE binary.") - Optional getOptionalHeader(); - - @JsonProperty("sections") - @JsonPropertyDescription("Specifies metadata about the sections in the PE file.") - Set getSections(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsProcessExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsProcessExtensionExt.java deleted file mode 100644 index d88551e..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsProcessExtensionExt.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.ProcessCoo; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The Windows Process extension specifies a default extension for capturing properties specific to Windows processes. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "windows-process-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = WindowsProcessExtension.class) @JsonDeserialize(builder = WindowsProcessExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "aslr_enabled", "dep_enabled", "priority", "owner_sid", "window_title", "startup_info" }) -@JsonTypeName("windows-process-ext") -@AllowedParents({ProcessCoo.class}) -@BusinessRule(ifExp = "isAslrEnabled().orElse(false) == true || isDepEnabled().orElse(false) == true", thenExp = "isDepEnabled().orElse(false) == false || isAslrEnabled().orElse(false) == false", errorMessage = "Dep and ASLR cannot both be enabled") -public interface WindowsProcessExtensionExt extends CyberObservableExtension { - - //@TODO Add business rule for having at least 1 property - - @JsonProperty("aslr_enabled") - @JsonPropertyDescription("Specifies whether Address Space Layout Randomization (ASLR) is enabled for the process.") - @NotNull - Optional isAslrEnabled(); - - @JsonProperty("dep_enabled") - @JsonPropertyDescription("Specifies whether Data Execution Prevention (DEP) is enabled for the process.") - @NotNull - Optional isDepEnabled(); - - @JsonProperty("priority") - @JsonPropertyDescription("Specifies the current priority class of the process in Windows.") - Optional getPriority(); - - @JsonProperty("owner_sid") - @JsonPropertyDescription("Specifies the Security ID (SID) value of the owner of the process.") - Optional getOwnerSid(); - - @JsonProperty("window_title") - @JsonPropertyDescription("Specifies the title of the main window of the process.") - Optional getWindowTitle(); - - @JsonProperty("startup_info") - @JsonPropertyDescription("Specifies the STARTUP_INFO struct used by the process, as a dictionary.") - Map getStartupInfo(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsServiceExtensionExt.java b/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsServiceExtensionExt.java deleted file mode 100644 index ade77b8..0000000 --- a/src/main/java/io/digitalstate/stix/coo/extension/types/WindowsServiceExtensionExt.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.digitalstate.stix.coo.extension.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.coo.objects.ProcessCoo; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.WindowsServiceStartTypes; -import io.digitalstate.stix.vocabulary.vocabularies.WindowsServiceStatuses; -import io.digitalstate.stix.vocabulary.vocabularies.WindowsServiceTypes; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * windows-service-ext - *

    - * The Windows Service extension specifies a default extension for capturing - * properties specific to Windows services. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "windows-service-ext", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Ext", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, passAnnotations = {AllowedParents.class}, depluralize = true) -@JsonSerialize(as = WindowsServiceExtension.class) @JsonDeserialize(builder = WindowsServiceExtension.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "service_name", "descriptions", "display_name", "group_name", "start_type", "service_dll_refs", - "service_type", "service_status" }) -@JsonTypeName("windows-service-ext") -@AllowedParents({ProcessCoo.class}) -public interface WindowsServiceExtensionExt extends CyberObservableExtension { - - @JsonProperty("service_name") - @JsonPropertyDescription("Specifies the name of the service.") - @NotBlank - String getServiceName(); - - @JsonProperty("descriptions") - @JsonPropertyDescription("Specifies the descriptions defined for the service.") - Set getDescriptions(); - - @JsonProperty("display_name") - @JsonPropertyDescription("Specifies the displayed name of the service in Windows GUI controls.") - Optional getDisplayName(); - - @JsonProperty("group_name") - @JsonPropertyDescription("Specifies the name of the load ordering group of which the service is a member.") - Optional getGroupName(); - - @JsonProperty("start_type") - @JsonPropertyDescription("Specifies the start options defined for the service. windows-service-start-enum") - Optional<@Vocab(WindowsServiceStartTypes.class) String> getServiceStartType(); - - @JsonProperty("service_dll_refs") - @JsonPropertyDescription("Specifies the DLLs loaded by the service, as a reference to one or more File Objects.") - Set getServiceDllRefs(); - - @JsonProperty("service_type") - @JsonPropertyDescription("Specifies the type of the service. windows-service-enum") - Optional<@Vocab(WindowsServiceTypes.class) String> getServiceType(); - - @JsonProperty("service_status") - @JsonPropertyDescription("Specifies the current status of the service. windows-service-status-enum") - Optional<@Vocab(WindowsServiceStatuses.class) String> getServiceStatus(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldDeserializer.java b/src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldDeserializer.java deleted file mode 100644 index 9472d7a..0000000 --- a/src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldDeserializer.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.digitalstate.stix.coo.json.extension; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.TreeNode; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.node.ObjectNode; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class CyberObservableExtensionsFieldDeserializer extends StdDeserializer> { - - public CyberObservableExtensionsFieldDeserializer() { - this(null); - } - - protected CyberObservableExtensionsFieldDeserializer(Class vc) { - super(vc); - } - - @Override - public Set deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - Set extensions = new HashSet<>(); - - TreeNode tree = p.readValueAsTree(); - - tree.fieldNames().forEachRemaining(f -> { - ObjectNode node = (ObjectNode) tree.get(f); - node.put("type", f); - try { - CyberObservableExtension extension = node.traverse(p.getCodec()).readValueAs(CyberObservableExtension.class); - extensions.add(extension); - } catch (IOException e) { - throw new IllegalStateException("Cannot deserialize COO extension: ", e); - } - }); - - return extensions; - } -} diff --git a/src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldSerializer.java b/src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldSerializer.java deleted file mode 100644 index adad3c0..0000000 --- a/src/main/java/io/digitalstate/stix/coo/json/extension/CyberObservableExtensionsFieldSerializer.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.digitalstate.stix.coo.json.extension; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; - -import java.io.IOException; -import java.util.Set; - -public class CyberObservableExtensionsFieldSerializer extends StdSerializer> { - - public CyberObservableExtensionsFieldSerializer() { - this(null); - } - - protected CyberObservableExtensionsFieldSerializer(Class> t) { - super(t); - } - - @Override - public boolean isEmpty(SerializerProvider provider, Set value) { - return value.isEmpty(); - } - - @Override - public void serialize(Set value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); - value.forEach(ext -> { - try { - gen.writeObjectField(ext.getType(), ext); - } catch (IOException e) { - throw new IllegalStateException("Unable to serialize COO Extension:", e); - } - }); - gen.writeEndObject(); - } -} diff --git a/src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldDeserializer.java b/src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldDeserializer.java deleted file mode 100644 index 4f0f1b1..0000000 --- a/src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldDeserializer.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.digitalstate.stix.coo.json.observables; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.TreeNode; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.node.ObjectNode; -import io.digitalstate.stix.coo.CyberObservableObject; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class CyberObservableSetFieldDeserializer extends StdDeserializer> { - - public CyberObservableSetFieldDeserializer() { - this(null); - } - - protected CyberObservableSetFieldDeserializer(Class vc) { - super(vc); - } - - @Override - public Set deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - Set objects = new HashSet<>(); - TreeNode tree = p.readValueAsTree(); - - tree.fieldNames().forEachRemaining(f->{ - ObjectNode node = (ObjectNode)tree.get(f); - node.put("observable_object_key", f); -// System.out.println(Node.toString()); - try { - CyberObservableObject object = node.traverse(p.getCodec()) - .readValueAs(CyberObservableObject.class); - objects.add(object); - - } catch (IOException e) { - throw new IllegalStateException("Unable to deserialize cyber observable object", e); - } - }); - - return objects; - } -} diff --git a/src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldSerializer.java b/src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldSerializer.java deleted file mode 100644 index 3634d3f..0000000 --- a/src/main/java/io/digitalstate/stix/coo/json/observables/CyberObservableSetFieldSerializer.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.digitalstate.stix.coo.json.observables; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import io.digitalstate.stix.coo.CyberObservableObject; - -import java.io.IOException; -import java.util.Set; - -public class CyberObservableSetFieldSerializer extends StdSerializer> { - - public CyberObservableSetFieldSerializer() { - this(null); - } - - protected CyberObservableSetFieldSerializer(Class> t) { - super(t); - } - - @Override - public boolean isEmpty(SerializerProvider provider, Set value) { - return value.isEmpty(); - } - - @Override - public void serialize(Set values, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); - values.forEach(observableObject -> { - try { - gen.writeObjectField(observableObject.getObservableObjectKey(), observableObject); - } catch (IOException e) { - throw new IllegalStateException("Unable to serialize object", e); - } - }); - gen.writeEndObject(); - } -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/ArtifactCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/ArtifactCoo.java deleted file mode 100644 index 67faac9..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/ArtifactCoo.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.Pattern; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * artifact - *

    - * The Artifact Object permits capturing an array of bytes (8-bits), as a base64-encoded string string, or linking to a file-like payload. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "artifact", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonTypeName("artifact") -@JsonSerialize(as = Artifact.class) @JsonDeserialize(builder = Artifact.Builder.class) -@JsonPropertyOrder({"type", "extensions", "mime_type", "payload_bin", "url", "hashes"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@BusinessRule(ifExp = "getUrl().isPresent() == true", thenExp = "getHashes().isEmpty() == false && getPayloadBin().isPresent() == false", errorMessage = "When Url is used, Hashes have at least 1 value, and payload_bin cannot be used.") -@BusinessRule(ifExp = "getPayloadBin().isPresent() == true", thenExp = "getUrl().isPresent() == false && getHashes().isEmpty() == true", errorMessage = "When payload_bin is used, Url and hashes cannot be used.") -public interface ArtifactCoo extends CyberObservableObject { - - /** - * The value of this property MUST be a valid MIME type as specified in the IANA Media Types registry. - * - */ - @JsonProperty("mime_type") - @JsonPropertyDescription("The value of this property MUST be a valid MIME type as specified in the IANA Media Types registry.") - Optional<@Pattern(regexp = "^(application|audio|font|image|message|model|multipart|text|video)/[a-zA-Z0-9.+_-]+") - String> getMimeType(); - - @JsonProperty("payload_bin") - @JsonPropertyDescription("Specifies the binary data contained in the artifact as a base64-encoded string.") - //Removed the @pattern from within the optional until it is clear on the usage and expectation. - //@Pattern(regexp = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$") - Optional getPayloadBin(); - - /** - * url-regex - *

    - * Matches the elements of a URL using a regular expression. Uses Diego Perini's regex from https://gist.github.com/dperini/729294. - * - */ - @JsonProperty("url") - @JsonPropertyDescription("The value of this property MUST be a valid URL that resolves to the unencoded content.") - //@TODO review if the @Url constraint can be used instead. - Optional<@Pattern(regexp = "^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\\.(?:[a-z\u00a1-\uffff]{2,}))\\.?)(?::\\d{2,5})?(?:[/?#]\\S*)?$") - String> getUrl(); - - //@TODO review logic requirements for Redactable on Hash values - /** - * hashes - *

    - * This MUST be provided when the url property is present. Optional if payload_bin is present. - */ - @JsonProperty("hashes") - @JsonPropertyDescription("Specifies a dictionary of hashes for the contents of the url or the payload_bin.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/AutonomousSystemCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/AutonomousSystemCoo.java deleted file mode 100644 index e337d43..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/AutonomousSystemCoo.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * autonomous-system - *

    - * The AS object represents the properties of an Autonomous Systems (AS). - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "autonomous-system", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("autonomous-system") -@JsonSerialize(as = AutonomousSystem.class) @JsonDeserialize(builder = AutonomousSystem.Builder.class) -@JsonPropertyOrder({"type", "extensions", "number", "name", "rir"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface AutonomousSystemCoo extends CyberObservableObject { - - @JsonProperty("number") - @JsonPropertyDescription("Specifies the number assigned to the AS. Such assignments are typically performed by a Regional Internet Registries (RIR)") - Long getNumber(); - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the AS.") - Optional getName(); - - @JsonProperty("rir") - @JsonPropertyDescription("Specifies the name of the Regional Internet Registry (RIR) that assigned the number to the AS.") - Optional getRir(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/DirectoryCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/DirectoryCoo.java deleted file mode 100644 index 4eaccee..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/DirectoryCoo.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.Pattern; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * directory - *

    - * The Directory Object represents the properties common to a file system directory. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "directory", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("directory") -@JsonSerialize(as = Directory.class) @JsonDeserialize(builder = Directory.Builder.class) -@JsonPropertyOrder({"type", "extensions", "path", "path_enc", "created", "modified", "accessed", "contains_refs"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface DirectoryCoo extends CyberObservableObject { - - @JsonProperty("path") - @JsonPropertyDescription("Specifies the path, as originally observed, to the directory on the file system.") - String getPath(); - - /** - * This value MUST be specified using the corresponding name from the 2013-12-20 revision of the IANA character set registry. - */ - @JsonProperty("path_enc") - @JsonPropertyDescription("Specifies the observed encoding for the path.") - Optional<@Pattern(regexp = "^[a-zA-Z0-9/\\.+_:-]{2,250}$") - String> getPathEnc(); - - @JsonProperty("created") - @JsonPropertyDescription("Specifies the date/time the directory was created.") - Optional getCreated(); - - @JsonProperty("modified") - @JsonPropertyDescription("Specifies the date/time the directory was last written to/modified.") - Optional getModified(); - - @JsonProperty("accessed") - @JsonPropertyDescription("Specifies the date/time the directory was last accessed.") - Optional getAccessed(); - - //@TODO add proper support for contains refs. Must be Set of File or Directory types - @JsonProperty("contains_refs") - @JsonPropertyDescription("Specifies a list of references to other File and/or Directory Objects contained within the directory.") - Set getContainsRefs(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/DomainNameCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/DomainNameCoo.java deleted file mode 100644 index 151881f..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/DomainNameCoo.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * domain-name - *

    - * The Domain Name represents the properties of a network domain name. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "domain-name", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("domain-name") -@JsonSerialize(as = DomainName.class) @JsonDeserialize(builder = DomainName.Builder.class) -@JsonPropertyOrder({"type", "extensions", "value", "resolves_to_refs"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface DomainNameCoo extends CyberObservableObject { - - @JsonProperty("value") - @JsonPropertyDescription("Specifies the value of the domain name.") - @NotNull - String getValue(); - - @JsonProperty("resolves_to_refs") - @JsonPropertyDescription("Specifies a list of references to one or more IP addresses or domain names that the domain name resolves to.") - Set getResolvesToRefs(); -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/EmailAddressCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/EmailAddressCoo.java deleted file mode 100644 index 91e9a85..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/EmailAddressCoo.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * email-addr - *

    - * The Email Address Object represents a single email address. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "email-addr", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("email-addr") -@JsonSerialize(as = EmailAddress.class) @JsonDeserialize(builder = EmailAddress.Builder.class) -@JsonPropertyOrder({"type", "extensions", "value", "display_name", "belongs_to_ref"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface EmailAddressCoo extends CyberObservableObject { - - @JsonProperty("value") - @JsonPropertyDescription("Specifies a single email address. This MUST not include the display name.") - @Pattern(regexp="(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$)") - @NotNull - String getValue(); - - @JsonProperty("display_name") - @JsonPropertyDescription("Specifies a single email display name, i.e., the name that is displayed to the human user of a mail application.") - Optional getDisplayName(); - - @JsonProperty("belongs_to_ref") - @JsonPropertyDescription("Specifies the user account that the email address belongs to, as a reference to a User Account Object.") - Optional getBelongsToRef(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/EmailMessageCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/EmailMessageCoo.java deleted file mode 100644 index e6c7a30..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/EmailMessageCoo.java +++ /dev/null @@ -1,98 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.coo.types.MimePartTypeObj; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The Email Message Object represents an instance of an email message. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "email-message", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("email-message") -@JsonSerialize(as = EmailMessage.class) @JsonDeserialize(builder = EmailMessage.Builder.class) -@JsonPropertyOrder({ "type", "extensions", "is_multipart", "date", "content_type", "from_ref", "sender_ref", "to_refs", "cc_refs", "bcc_refs", "subject", - "received_lines", "additional_header_fields", "body", "body_multipart", "raw_email_ref" }) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@BusinessRule(ifExp = "isMultipart() == true", thenExp = "getBody().isPresent() == false", errorMessage = "Body cannot be used if isMultipart equals true") -@BusinessRule(ifExp = "isMultipart() == false", thenExp = "getBodyMultipart().isEmpty() == true", errorMessage = "Body_Multipart cannot be used if isMultipart equals false") -public interface EmailMessageCoo extends CyberObservableObject { - - @JsonProperty("is_multipart") - @JsonPropertyDescription("Indicates whether the email body contains multiple MIME parts.") - @NotNull - Boolean isMultipart(); - - @JsonProperty("date") - @JsonPropertyDescription("Specifies the date/time that the email message was sent.") - Optional getDate(); - - @JsonProperty("content_type") - @JsonPropertyDescription("Specifies the value of the 'Content-Type' header of the email message.") - Optional getContentType(); - - @JsonProperty("from_ref") - @JsonPropertyDescription("Specifies the value of the 'From:' header of the email message.") - Optional getFromRef(); - - @JsonProperty("sender_ref") - @JsonPropertyDescription("Specifies the value of the 'From' field of the email message") - Optional getSenderRef(); - - @JsonProperty("to_refs") - @JsonPropertyDescription("Specifies the mailboxes that are 'To:' recipients of the email message") - Set getToRefs(); - - @JsonProperty("cc_refs") - @JsonPropertyDescription("Specifies the mailboxes that are 'CC:' recipients of the email message") - Set getCcRefs(); - - @JsonProperty("bcc_refs") - @JsonPropertyDescription("Specifies the mailboxes that are 'BCC:' recipients of the email message.") - Set getBccRefs(); - - @JsonProperty("subject") - @JsonPropertyDescription("Specifies the subject of the email message.") - Optional getSubject(); - - @JsonProperty("received_lines") - @JsonPropertyDescription("Specifies one or more Received header fields that may be included in the email headers.") - Set getReceivedLines(); - - //@TODO Should become a Multi-Map in the future https://github.com/oasis-tcs/cti-stix2/issues/138 - @JsonProperty("additional_header_fields") - @JsonPropertyDescription("Specifies any other header fields (except for date, received_lines, content_type, from_ref, sender_ref, to_refs, cc_refs, bcc_refs, and subject) found in the email message, as a dictionary.") - Map getAdditionalHeaderFields(); - - @JsonProperty("body") - @JsonPropertyDescription("Specifies a string containing the email body.") - Optional getBody(); - - @JsonProperty("body_multipart") - @JsonPropertyDescription("Specifies a list of the MIME parts that make up the email body.") - Set getBodyMultipart(); - - @JsonProperty("raw_email_ref") - @JsonPropertyDescription("Specifies the raw binary contents of the email message, including both the headers and body, as a reference to an Artifact Object.") - Optional getRawEmailRef(); - - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/FileCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/FileCoo.java deleted file mode 100644 index defe256..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/FileCoo.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.EncryptionAlgorithms; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Positive; -import java.time.Instant; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * file - *

    - * The File Object represents the properties of a file. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "file", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonTypeName("file") -@JsonSerialize(as = File.class) @JsonDeserialize(builder = File.Builder.class) -@JsonPropertyOrder({ "type", "extensions", "hashes", "size", "name", "name_enc", "magic_number_hex", "mime_type", "created", "modified", - "accessed", "parent_directory_ref", "is_encrypted", "encryption_algorithm", "decryption_key" , "contains_refs", "content_ref", }) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@BusinessRule(ifExp = "isEncrypted().orElse(false) == false", thenExp = "getEncryptionAlgorithm().isPresent() == false && getDecryptionKey().isPresent() == false", errorMessage = "Encryption Algorithm and Description Key cannot be used if Encrypted equals false.") -public interface FileCoo extends CyberObservableObject { - - @JsonProperty("hashes") - @JsonPropertyDescription("Specifies a dictionary of hashes for the contents of the file.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - - @JsonProperty("size") - @JsonPropertyDescription("Specifies the size of the file, in bytes, as a non-negative integer.") - Optional<@Positive Long> getSize(); - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the file.") - Optional getName(); - - @JsonProperty("name_enc") - @JsonPropertyDescription("Specifies the observed encoding for the name of the file.") - Optional<@Pattern(regexp = "^[a-zA-Z0-9/\\.+_:-]{2,250}$") - String> getNameEnc(); - - @JsonProperty("magic_number_hex") - @JsonPropertyDescription("Specifies the hexadecimal constant ('magic number') associated with a specific file format that corresponds to the file, if applicable.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") - String> getMagicNumberHex(); - - //@TODO Convert this to a Vocab Validation - @JsonProperty("mime_type") - @JsonPropertyDescription("Specifies the MIME type name specified for the file, e.g., 'application/msword'.") - Optional<@Pattern(regexp = "^(application|audio|font|image|message|model|multipart|text|video)/[a-zA-Z0-9.+_-]+") - String> getMimeType(); - - @JsonProperty("created") - @JsonPropertyDescription("Specifies the date/time the file was created.") - Optional getCreated(); - - @JsonProperty("modified") - @JsonPropertyDescription("Specifies the date/time the file was last written to/modified.") - Optional getModified(); - - @JsonProperty("accessed") - @JsonPropertyDescription("Specifies the date/time the file was last accessed.") - Optional getAccessed(); - - @JsonProperty("parent_directory_ref") - @JsonPropertyDescription("Specifies the parent directory of the file, as a reference to a Directory Object.") - Optional getParentDirectoryRef(); - - @JsonProperty("is_encrypted") - @JsonPropertyDescription("Specifies whether the file is encrypted.") - @NotNull - Optional isEncrypted(); - - @JsonProperty("encryption_algorithm") - @JsonPropertyDescription("Specifies the name of the encryption algorithm used to encrypt the file. Open Vocabulary - encryption-algorithm-ov") - Optional<@Vocab(EncryptionAlgorithms.class) String> getEncryptionAlgorithm(); - - @JsonProperty("decryption_key") - @JsonPropertyDescription("Specifies the decryption key used to decrypt the archive file.") - Optional getDecryptionKey(); - - @JsonProperty("contains_refs") - @JsonPropertyDescription("Specifies a list of references to other Observable Objects contained within the file.") - Set getContainsRefs(); - - @JsonProperty("content_ref") - @JsonPropertyDescription("Specifies the content of the file, represented as an Artifact Object.") - Optional getContentRef(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/Ipv4AddressCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/Ipv4AddressCoo.java deleted file mode 100644 index 45d8a52..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/Ipv4AddressCoo.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * ipv4-addr - *

    - * The IPv4 Address Object represents one or more IPv4 addresses expressed using CIDR notation. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "ipv4-addr", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonTypeName("ipv4-addr") -@JsonSerialize(as = Ipv4Address.class) @JsonDeserialize(builder = Ipv4Address.Builder.class) -@JsonPropertyOrder({"type", "extensions", "value", "resolves_to_refs", "belongs_to_refs"}) -public interface Ipv4AddressCoo extends CyberObservableObject { - // TODO Consider using regexp to validate: - // http://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ - - /** - * If a given IPv4 Address Object represents a single IPv4 address, the CIDR /32 suffix MAY be omitted. - * (Required) - * - */ - @JsonProperty("value") - @JsonPropertyDescription("Specifies one or more IPv4 addresses expressed using CIDR notation.") - @NotNull - String getValue(); - - /** - * The objects referenced in this list MUST be of type mac-addr. - * - */ - @JsonProperty("resolves_to_refs") - @JsonPropertyDescription("Specifies a list of references to one or more Layer 2 Media Access Control (MAC) addresses that the IPv4 address resolves to.") - Set getResolvesToRefs(); - - /** - * The objects referenced in this list MUST be of type autonomous-system. - * - */ - @JsonProperty("belongs_to_refs") - @JsonPropertyDescription("Specifies a reference to one or more autonomous systems (AS) that the IPv4 address belongs to.") - Set getBelongsToRefs(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/Ipv6AddressCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/Ipv6AddressCoo.java deleted file mode 100644 index 9b5022d..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/Ipv6AddressCoo.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * ipv6-addr - *

    - * The IPv6 Address Object represents one or more IPv6 addresses expressed using CIDR notation. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "ipv6-addr", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonTypeName("ipv6-addr") -@JsonSerialize(as = Ipv6Address.class) @JsonDeserialize(builder = Ipv6Address.Builder.class) -@JsonPropertyOrder({"type", "extensions", "value", "resolves_to_refs", "belongs_to_refs"}) -public interface Ipv6AddressCoo extends CyberObservableObject { - // TODO Consider using regexp to validate: - // http://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ - - /** - * If a given IPv6 Address Object represents a single IPv6 address, the CIDR /128 suffix MAY be omitted. - * (Required) - * - */ - @JsonProperty("value") - @JsonPropertyDescription("Specifies one or more IPv6 addresses expressed using CIDR notation.") - @NotNull - String getValue(); - - /** - * The objects referenced in this list MUST be of type mac-addr. - * - */ - @JsonProperty("resolves_to_refs") - @JsonPropertyDescription("Specifies a list of references to one or more Layer 2 Media Access Control (MAC) addresses that the IPv4 address resolves to.") - Set getResolvesToRefs(); - - /** - * The objects referenced in this list MUST be of type autonomous-system. - * - */ - @JsonProperty("belongs_to_refs") - @JsonPropertyDescription("Specifies a reference to one or more autonomous systems (AS) that the IPv4 address belongs to.") - Set getBelongsToRefs(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/MacAddressCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/MacAddressCoo.java deleted file mode 100644 index 50816f5..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/MacAddressCoo.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * mac-addr - *

    - * The MAC Address Object represents a single Media Access Control (MAC) address. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "mac-addr", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("mac-addr") -@JsonSerialize(as = MacAddress.class) @JsonDeserialize(builder = MacAddress.Builder.class) -@JsonPropertyOrder({"type", "extensions", "value"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface MacAddressCoo extends CyberObservableObject { - - /** - * The MAC address value MUST be represented as a single colon-delimited, lowercase MAC-48 address, - * which MUST include leading zeros for each octet. - * (Required) - * - */ - @JsonProperty("value") - @JsonPropertyDescription("Specifies one or more mac addresses expressed using CIDR notation.") - @Pattern(regexp="^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$") - @NotNull - String getValue(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/MutexCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/MutexCoo.java deleted file mode 100644 index 10f0df0..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/MutexCoo.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * mutex - *

    - * The Mutex Object represents the properties of a mutual exclusion (mutex) object. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "mutex", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("mutex") -@JsonSerialize(as = Mutex.class) @JsonDeserialize(builder = Mutex.Builder.class) -@JsonPropertyOrder({"type", "extensions", "name"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface MutexCoo extends CyberObservableObject { - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the mutex object.") - @NotNull - String getName(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/NetworkTrafficCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/NetworkTrafficCoo.java deleted file mode 100644 index d283a0d..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/NetworkTrafficCoo.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.hibernate.validator.constraints.Range; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - - -/** - * network-traffic - *

    - * The Network Traffic Object represents arbitrary network traffic that - * originates from a source and is addressed to a destination. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "network-traffic", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = NetworkTraffic.class) @JsonDeserialize(builder = NetworkTraffic.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonTypeName("network-traffic") -@JsonPropertyOrder({ "type", "extensions", "start", "end", "src_ref", "dst_ref", "src_port", "dst_port", "protocols", "src_byte_count", - "dst_byte_count", "src_packets", "dst_packets", "ipfix", "src_payload_ref", "dst_payload_ref", - "encapsulates_refs", "encapsulated_by_ref" }) -public interface NetworkTrafficCoo extends CyberObservableObject { - - @JsonProperty("start") - @JsonPropertyDescription("Specifies the date/time the network traffic was initiated, if known.") - Optional getStart(); - - @JsonProperty("end") - @JsonPropertyDescription("Specifies the date/time the network traffic ended, if known.") - Optional getEnd(); - - @JsonProperty("is_active") - @JsonPropertyDescription("Indicates whether the network traffic is still ongoing.") - @NotNull - Optional isActive(); - - /* - * Must be of type ipv4-addr, ipv6-addr, mac-addr, or domain-name - */ - @JsonProperty("src_ref") - @JsonPropertyDescription("Specifies the source of the network traffic, as a reference to one or more Observable Objects.") - Optional getSrcRef(); - - /* - * Must be of type ipv4-addr, ipv6-addr, mac-addr, or domain-name - */ - @JsonProperty("dst_ref") - @JsonPropertyDescription("Specifies the destination of the network traffic, as a reference to one or more Observable Objects.") - Optional getDstRef(); - - @JsonProperty("src_port") - @JsonPropertyDescription("Specifies the source port used in the network traffic, as an integer. The port value MUST be in the range of 0 - 65535.") - Optional<@Range(min = 0, max = 65535 ) Integer> getSrcPort(); - - @JsonProperty("dst_port") - @JsonPropertyDescription("Specifies the destination port used in the network traffic, as an integer. The port value MUST be in the range of 0 - 65535.") - Optional<@Range(min = 0, max = 65535) Integer> getDstPort(); - - /* - * Specifies the protocols observed in the network traffic, along with their - * corresponding state. Protocols MUST be listed in low to high order, from outer to inner in terms of packet encapsulation. - * That is, the protocols in the outer level of the packet, such as IP, MUST be listed first. - * The protocol names SHOULD come from the service names defined in the Service Name column of th - * IANA Service Name and Port Number Registry - * - */ - @JsonProperty("protocols") - @JsonPropertyDescription("Specifies the protocols observed in the network traffic, along with their corresponding state.") - @NotNull - Set getProtocols(); - - @JsonProperty("src_byte_count") - @JsonPropertyDescription("Specifies the number of bytes sent from the source to the destination.") - Optional getSrcByteCount(); - - @JsonProperty("dst_byte_count") - @JsonPropertyDescription("Specifies the number of bytes sent from the destination to the source.") - Optional getDstByteCount(); - - @JsonProperty("src_packets") - @JsonPropertyDescription("Specifies the number of packets sent from the source to the destination.") - Optional getSrcPackets(); - - @JsonProperty("dst_packets") - @JsonPropertyDescription("Specifies the number of packets sent destination to the source.") - Optional getDstPackets(); - - /* - * Objects much be Integers or Strings - */ - @JsonProperty("ipfix") - @JsonPropertyDescription("Specifies any IP Flow Information Export [IPFIX] data for the traffic, as a dictionary.") - Map getIpFix(); - - /* - * Must be of type artifact - */ - @JsonProperty("src_payload_ref") - @JsonPropertyDescription("Specifies the bytes sent from the source to the destination.") - Optional getSrcPayloadRef(); - - /* - * Must be of type artifact - */ - @JsonProperty("dst_payload_ref") - @JsonPropertyDescription("Specifies the bytes sent from the source to the destination.") - Optional getDstPayloadRef(); - - /* - * Must be of type network-traffic - */ - @JsonProperty("encapsulates_refs") - @JsonPropertyDescription("Links to other network-traffic objects encapsulated by a network-traffic.") - Set getEncapsulatesRefs(); - - /* - * Must be of type network-traffic - */ - @JsonProperty("encapsulated_by_ref") - @JsonPropertyDescription("Links to another network-traffic object which encapsulates this object.") - Optional getEncapsulatedByRef(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/ProcessCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/ProcessCoo.java deleted file mode 100644 index ec81bcd..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/ProcessCoo.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * process - *

    - * The Process Object represents common properties of an instance of a computer - * program as executed on an operating system. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "process", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Process.class) @JsonDeserialize(builder = Process.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonTypeName("process") -@JsonPropertyOrder({ "type", "extensions", "is_hidden", "pid", "name", "created", "cwd", "arguments", "command_line", - "environment_variables", "opened_connection_refs", "creator_user_ref", "binary_ref", "parent_ref", - "child_refs" }) -@BusinessRule(ifExp = "true", thenExp = "getExtensions().isEmpty() == false || isHidden().isPresent() == true || getPid().isPresent() == true || getName().isPresent() == true || getCreated().isPresent() == true || getCwd().isPresent() == true || getArguments().isEmpty() == false || getCommandLine().isPresent() == true || getEnvironmentVariables().isEmpty() == false || getOpenedConnectionRefs().isEmpty() == false || getCreatorUserRef().isPresent() == true || getBinaryRef().isPresent() == true || getParentRef().isPresent() == true || getChildRefs().isEmpty() == false", errorMessage = "A Process Object MUST contain at least one property (other than type) from this object (or one of its extensions).") -public interface ProcessCoo extends CyberObservableObject { - - @JsonProperty("is_hidden") - @JsonPropertyDescription("Specifies whether the process is hidden.") - @NotNull - Optional isHidden(); - - @JsonProperty("pid") - @JsonPropertyDescription("Specifies the Process ID, or PID, of the process.") - Optional getPid(); - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the process.") - Optional getName(); - - @JsonProperty("created") - @JsonPropertyDescription("Specifies the date/time at which the process was created.") - Optional getCreated(); - - @JsonProperty("cwd") - @JsonPropertyDescription("Specifies the current working directory of the process.") - Optional getCwd(); - - //@TODO need better clarification in the STIX SPEC about if this should be a SET or LIST. Are duplicate arguments allowed? - @JsonProperty("arguments") - @JsonPropertyDescription("Specifies the list of arguments used in executing the process.") - List getArguments(); - - @JsonProperty("command_line") - @JsonPropertyDescription("Specifies the full command line used in executing the process, including the process name (depending on the operating system).") - Optional getCommandLine(); - - @JsonProperty("environment_variables") - @JsonPropertyDescription("Specifies the list of environment variables associated with the process as a dictionary.") - Map getEnvironmentVariables(); - - @JsonProperty("opened_connection_refs") - @JsonPropertyDescription("Specifies the list of network connections opened by the process, as a reference to one or more Network Traffic Objects.") - Set getOpenedConnectionRefs(); - - @JsonProperty("creator_user_ref") - @JsonPropertyDescription("Specifies the user that created the process, as a reference to a User Account Object.") - Optional getCreatorUserRef(); - - @JsonProperty("binary_ref") - @JsonPropertyDescription("Specifies the executable binary that was executed as the process, as a reference to a File Object.") - Optional getBinaryRef(); - - @JsonProperty("parent_ref") - @JsonPropertyDescription("Specifies the other process that spawned (i.e. is the parent of) this one, as represented by a Process Object.") - Optional getParentRef(); - - @JsonProperty("child_refs") - @JsonPropertyDescription("Specifies the other processes that were spawned by (i.e. children of) this process, as a reference to one or more other Process Objects.") - Set getChildRefs(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/SoftwareCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/SoftwareCoo.java deleted file mode 100644 index 6936b75..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/SoftwareCoo.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * software - *

    - * The Software Object represents high-level properties associated with software, including software products. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "software", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Software.class) @JsonDeserialize(builder = Software.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonTypeName("software") -@JsonPropertyOrder({ - "type", "extensions", "cpe", "languages", "vendor", "version" }) -public interface SoftwareCoo extends CyberObservableObject { - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the software.") - @NotNull - String getName(); - - /** - * TODO The value for this property MUST be a CPE v2.3 entry from the official NVD CPE Dictionary. - * regex pattern = "^cpe:2\\.3:[aho](?::(?:[a-zA-Z0-9!\"#$%&'()*+,\\\\-_.\/();<=>?@\\[\\]^`{|}~]|\\:)+){10}$" - * Is not valid for the @Pattern annotation (invalid escape chars) - * Remove @Pattern(regexp="^cpe:2\\.3:[aho]") until working solution is provided - */ - @JsonProperty("cpe") - @JsonPropertyDescription("Specifies the Common Platform Enumeration (CPE) entry for the software, if available.") - Optional getCpe(); - - /** - * TODO The value of each list member MUST be an ISO 639-2 language code. - */ - @JsonProperty("languages") - @JsonPropertyDescription("Specifies the languages supported by the software.") - Set<@Pattern(regexp="^[a-z]{3}$") String> getLanguages(); - - @JsonProperty("vendor") - @JsonPropertyDescription("Specifies the name of the vendor of the software.") - Optional getVendor(); - - @JsonProperty("version") - @JsonPropertyDescription("Specifies the version of the software.") - Optional getVersion(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/UrlCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/UrlCoo.java deleted file mode 100644 index 3be6fdc..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/UrlCoo.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * url - *

    - * The URL Object represents the properties of a uniform resource locator (URL). - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "url", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("url") -@JsonSerialize(as = Url.class) @JsonDeserialize(builder = Url.Builder.class) -@JsonPropertyOrder({"type", "extensions", "value"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -public interface UrlCoo extends CyberObservableObject { - - /** - * The value of this property MUST conform to [RFC3986], more specifically section 1.1.3 - * with reference to the definition for "Uniform Resource Locator" - * (Required) - * - */ - //@TODO can this be changed from @Pattern to @URL? - @JsonProperty("value") - @JsonPropertyDescription("Specifies the value of the URL.") - @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)?$") - @NotNull - String getValue(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/UserAccountCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/UserAccountCoo.java deleted file mode 100644 index 5586cde..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/UserAccountCoo.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.AccountTypes; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * user-account - *

    - * The User Account Object represents an instance of any type of user account, - * including but not limited to operating system, device, messaging service, and - * social media platform accounts. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "user-account", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = UserAccount.class) @JsonDeserialize(builder = UserAccount.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonTypeName("user-account") -@JsonPropertyOrder({ "type", "extensions","user_id", "account_login", "account_type", "display_name", - "is_service_account", "is_privileged", "can_escalate_privs", "is_disabled", "account_created", - "account_expires", "password_last_changed", "account_first_login", "account_last_login" }) -public interface UserAccountCoo extends CyberObservableObject { - - @JsonProperty("user_id") - @JsonPropertyDescription("Specifies the identifier of the account.") - @NotNull - String getUserId(); - - @JsonProperty("account_login") - @JsonPropertyDescription("Specifies the account login string, used in cases where the user_id property specifies something other than what a user would type when they login.") - Optional getAccountLogin(); - - @JsonProperty("account_type") - @JsonPropertyDescription("Specifies the type of the account. This is an open vocabulary and values SHOULD come from the account-type-ov vocabulary.") - Optional<@Vocab(AccountTypes.class) String> getAccountType(); - - @JsonProperty("display_name") - @JsonPropertyDescription("Specifies the display name of the account, to be shown in user interfaces, if applicable.") - Optional getDisplayName(); - - @JsonProperty("is_service_account") - @JsonPropertyDescription("Indicates that the account is associated with a network service or system process (daemon), not a specific individual.") - @NotNull - Optional isServiceAccount(); - - @JsonProperty("is_privileged") - @JsonPropertyDescription("Specifies that the account has elevated privileges (i.e., in the case of root on Unix or the Windows Administrator account).") - @NotNull - Optional isPrivileged(); - - @JsonProperty("can_escalate_privs") - @JsonPropertyDescription("Specifies that the account has the ability to escalate privileges (i.e., in the case of sudo on Unix or a Windows Domain Admin account).") - @NotNull - Optional isCanEscalatePrivs(); - - @JsonProperty("is_disabled") - @JsonPropertyDescription("Specifies if the account is disabled.") - @NotNull - Optional isDisabled(); - - @JsonProperty("account_created") - @JsonPropertyDescription("Specifies when the account was created.") - Optional getAccountCreated(); - - @JsonProperty("account_expires") - @JsonPropertyDescription("Specifies the expiration date of the account.") - Optional getAccountExpires(); - - @JsonProperty("password_last_changed") - @JsonPropertyDescription("Specifies when the account password was last changed.") - Optional getPasswordLastChanged(); - - @JsonProperty("account_first_login") - @JsonPropertyDescription("Specifies when the account was first accessed.") - Optional getAccountFirstLogin(); - - @JsonProperty("account_last_login") - @JsonPropertyDescription("Specifies when the account was last accessed.") - Optional getAccountLastLogin(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/WindowsRegistryKeyCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/WindowsRegistryKeyCoo.java deleted file mode 100644 index 49017a9..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/WindowsRegistryKeyCoo.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.coo.types.WindowsRegistryValueObj; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * windows-registry-key - *

    - * The Registry Key Object represents the properties of a Windows registry key. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "windows-registry-key", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = WindowsRegistryKey.class) @JsonDeserialize(builder = WindowsRegistryKey.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "type", "key", "values", "modified", "creator_user_ref", "number_of_subkeys", "extensions" }) -@JsonTypeName("windows-registry-key") -public interface WindowsRegistryKeyCoo extends CyberObservableObject { - - @JsonProperty("key") - @JsonPropertyDescription("Specifies the full registry key including the hive.") - @Pattern(regexp = "^HKEY_LOCAL_MACHINE|hkey_local_machine|HKEY_CURRENT_USER|hkey_current_user|HKEY_CLASSES_ROOT|hkey_classes_root|HKEY_CURRENT_CONFIG|hkey_current_config|HKEY_PERFORMANCE_DATA|hkey_performance_data|HKEY_USERS|hkey_users|HKEY_DYN_DATA") - @NotNull - String getKey(); - - @JsonProperty("values") - @JsonPropertyDescription("Specifies the values found under the registry key.") - Set getValues(); - - @JsonProperty("modified") - @JsonPropertyDescription("Specifies the last date/time that the registry key was modified.") - Optional getModified(); - - //@TODO Must be of type user-account - @JsonProperty("creator_user_ref") - @JsonPropertyDescription("Specifies a reference to a user account, represented as a User Account Object, that created the registry key.") - Optional getCreatorUserRef(); - - @JsonProperty("number_of_subkeys") - @JsonPropertyDescription("Specifies the number of subkeys contained under the registry key.") - Optional getNumberOfSubkeys(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/objects/X509CertificateCoo.java b/src/main/java/io/digitalstate/stix/coo/objects/X509CertificateCoo.java deleted file mode 100644 index ed9672c..0000000 --- a/src/main/java/io/digitalstate/stix/coo/objects/X509CertificateCoo.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.digitalstate.stix.coo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.coo.types.X509v3ExtensionsObj; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.time.Instant; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * x509-certificate - *

    - * The X509 Certificate Object represents the properties of an X.509 certificate. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "x509-certificate", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Coo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonTypeName("x509-certificate") -@JsonSerialize(as = X509Certificate.class) @JsonDeserialize(builder = X509Certificate.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "type", "extensions", "is_self_signed", "hashes", "version", "serial_number", - "signature_algorithm", "issuer", "validity_not_before", "validity_not_after", - "subject", "subject_public_key_algorithm", "subject_public_key_modulus", - "subject_public_key_exponent", "x509_v3_extensions" -}) -//@TODO refactor BusinessRule annotation with custom emthod that looks up every field and does a check if there is a value -@BusinessRule(ifExp = "true", thenExp = "isSelfSigned().isPresent() == true || getHashes().isEmpty() == false || getVersion().isPresent() == true || getSerialNumber().isPresent() == true || getSignatureAlgorithm().isPresent() == true || getIssuer().isPresent() == true || getValidityNotBefore().isPresent() == true || getValidityNotAfter().isPresent() == true || getSubject().isPresent() == true || getSubjectPublicKeyAlgorithm().isPresent() == true || getSubjectPublicKeyModulus().isPresent() == true || getSubjectPublicKeyExponent().isPresent() == true || getX509V3Extensions().isPresent() == true", errorMessage = "At least 1 property must be provided") -public interface X509CertificateCoo extends CyberObservableObject { - - @JsonProperty("is_self_signed") - @JsonPropertyDescription("Specifies whether the certificate is self-signed, i.e., whether it is signed by the same entity whose identity it certifies.") - Optional isSelfSigned(); - - @JsonProperty("hashes") - @JsonPropertyDescription("Specifies any hashes that were calculated for the entire contents of the certificate.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - - @JsonProperty("version") - @JsonPropertyDescription("Specifies the version of the encoded certificate.") - Optional getVersion(); - - @JsonProperty("serial_number") - @JsonPropertyDescription("Specifies the unique identifier for the certificate, as issued by a specific Certificate Authority.") - Optional getSerialNumber(); - - @JsonProperty("signature_algorithm") - @JsonPropertyDescription("Specifies the name of the algorithm used to sign the certificate.") - Optional getSignatureAlgorithm(); - - @JsonProperty("issuer") - @JsonPropertyDescription("Specifies the name of the Certificate Authority that issued the certificate.") - Optional getIssuer(); - - @JsonProperty("validity_not_before") - @JsonPropertyDescription("Specifies the date on which the certificate validity period begins.") - Optional getValidityNotBefore(); - - @JsonProperty("validity_not_after") - @JsonPropertyDescription("Specifies the date on which the certificate validity period ends.") - Optional getValidityNotAfter(); - - @JsonProperty("subject") - @JsonPropertyDescription("Specifies the name of the entity associated with the public key stored in the subject public key field of the certificate.") - Optional getSubject(); - - @JsonProperty("subject_public_key_algorithm") - @JsonPropertyDescription("Specifies the name of the algorithm with which to encrypt data being sent to the subject.") - Optional getSubjectPublicKeyAlgorithm(); - - @JsonProperty("subject_public_key_modulus") - @JsonPropertyDescription("Specifies the modulus portion of the subject\u2019s public RSA key.") - Optional getSubjectPublicKeyModulus(); - - @JsonProperty("subject_public_key_exponent") - @JsonPropertyDescription("Specifies the exponent portion of the subject\u2019s public RSA key, as an integer.") - Optional getSubjectPublicKeyExponent(); - - @JsonProperty("x509_v3_extensions") - @JsonPropertyDescription("Specifies any standard X.509 v3 extensions that may be used in the certificate.") - Optional getX509V3Extensions(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/types/MimePartTypeObj.java b/src/main/java/io/digitalstate/stix/coo/types/MimePartTypeObj.java deleted file mode 100644 index 085f7ca..0000000 --- a/src/main/java/io/digitalstate/stix/coo/types/MimePartTypeObj.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.digitalstate.stix.coo.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.io.Serializable; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * Specifies a component of a multi-part email body. - * - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = MimePartType.class) @JsonDeserialize(builder = MimePartType.Builder.class) -@JsonPropertyOrder({"body", "body_raw_ref", "content_type", "content_disposition"}) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@BusinessRule(ifExp = "true", thenExp = "getBody().isPresent() == true || getBodyRawRef().isPresent() == true", errorMessage = "One of body OR body_raw_ref MUST be included.") -public interface MimePartTypeObj extends GenericValidation, StixCustomProperties, Serializable { - - /** - * Contents of body MUST be decoded to Unicode. - */ - @JsonProperty("body") - @JsonPropertyDescription("Specifies the contents of the MIME part if the content_type is not provided OR starts with text/") - Optional getBody(); - - /** - * The object referenced in this property MUST be of type artifact or file. - * For use cases where conveying the actual data contained in the MIME part is of primary importance, artifact SHOULD be used. - * Otherwise, for use cases where conveying metadata about the file-like properties of the MIME part is of primary importance, file SHOULD be used. - */ - @JsonProperty("body_raw_ref") - @JsonPropertyDescription("Specifies the contents of non-textual MIME parts, that is those whose content_type does not start with text/") - Optional getBodyRawRef(); - - /** - * Any additional “Content-Type” header field parameters such as charset SHOULD be included in this property. - */ - @JsonProperty("content_type") - @JsonPropertyDescription("Specifies the value of the 'Content-Type' header field of the MIME part.") - Optional getContentType(); - - @JsonProperty("content_disposition") - @JsonPropertyDescription("Specifies the value of the 'Content-Disposition' header field of the MIME part.") - Optional getContentDisposition(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/types/NtfsAlternateDataStreamObj.java b/src/main/java/io/digitalstate/stix/coo/types/NtfsAlternateDataStreamObj.java deleted file mode 100644 index fbb3894..0000000 --- a/src/main/java/io/digitalstate/stix/coo/types/NtfsAlternateDataStreamObj.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.digitalstate.stix.coo.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.PositiveOrZero; -import java.io.Serializable; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The Alternate Data Stream type represents an NTFS alternate data stream. - * - */ -@Value.Immutable @Serial.Version(1L) -//@DefaultTypeValue(value = "alternate-data-stream-type", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonSerialize(as = NtfsAlternateDataStream.class) @JsonDeserialize(builder = NtfsAlternateDataStream.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "name", "hashes", "size" }) -//@JsonTypeName("alternate-data-stream-type") -public interface NtfsAlternateDataStreamObj extends GenericValidation, StixCustomProperties, Serializable { - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the alternate data stream.") - @NotNull - String getName(); - - @JsonProperty("hashes") - @JsonPropertyDescription("Specifies a dictionary of hashes for the data contained in the alternate data stream.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - - @JsonProperty("size") - @JsonPropertyDescription("Specifies the size of the alternate data stream, in bytes, as a non-negative integer.") - Optional<@PositiveOrZero Long> getSize(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/types/WindowsPeOptionalHeaderObj.java b/src/main/java/io/digitalstate/stix/coo/types/WindowsPeOptionalHeaderObj.java deleted file mode 100644 index 3ecc6f7..0000000 --- a/src/main/java/io/digitalstate/stix/coo/types/WindowsPeOptionalHeaderObj.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.digitalstate.stix.coo.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.Pattern; -import javax.validation.constraints.PositiveOrZero; -import java.io.Serializable; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - - -/** - * The Windows PE Optional Header type represents the properties of the PE - * optional header. - * - */ -@Value.Immutable @Serial.Version(1L) -//@DefaultTypeValue(value = "windows-pe-optional-header-type", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonSerialize(as = WindowsPeOptionalHeader.class) @JsonDeserialize(builder = WindowsPeOptionalHeader.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "magic_hex", "major_linker_version", "minor_linker_version", "size_of_code", - "size_of_initialized_data", "size_of_uninitialized_data", "address_of_entry_point", "base_of_code", - "base_of_data", "image_base", "section_alignment", "file_alignment", "major_os_version", "minor_os_version", - "major_image_version", "minor_image_version", "major_subsystem_version", "minor_subsystem_version", - "win32_version_value_hex", "size_of_image", "size_of_headers", "checksum_hex", "subsystem_hex", - "dll_characteristics_hex", "size_of_stack_reserve", "size_of_stack_commit", "size_of_heap_reserve", - "size_of_heap_commit", "loader_flags_hex", "number_of_rva_and_sizes", "hashes" }) -//@JsonTypeName("windows-pe-optional-header-type") -@BusinessRule(ifExp = "true", thenExp = "getMagicHex().isPresent() == true || getMajorLinkerVersion().isPresent() == true || getMinorLinkerVersion().isPresent() == true || getSizeOfCode().isPresent() == true || getSizeOfInitializedData().isPresent() == true || getSizeOfUninitializedData().isPresent() == true || getAddressOfEntryPoint().isPresent() == true || getBaseOfCode().isPresent() == true || getBaseOfData().isPresent() == true || getImageBase().isPresent() == true || getSectionAlignment().isPresent() == true || getFileAlignment().isPresent() == true || getMajorOsVersion().isPresent() == true || getMinorOsVersion().isPresent() == true || getMajorImageVersion().isPresent() == true || getMinorImageVersion().isPresent() == true || getMajorSubsystemVersion().isPresent() == true || getMinorSubsystemVersion().isPresent() == true || getWin32VersionValueHex().isPresent() == true || getSizeOfImage().isPresent() == true || getSizeOfHeaders().isPresent() == true || getChecksumHex().isPresent() == true || getSubsystemHex().isPresent() == true || getDllCharacteristicsHex().isPresent() == true || getSizeOfStackReserve().isPresent() == true || getSizeOfStackCommit().isPresent() == true || getSizeOfHeapReserve().isPresent() == true || getSizeOfHeapCommit().isPresent() == true || getLoaderFlagsHex().isPresent() == true || getNumberOfRvaAndSizes().isPresent() == true || getHashes().isEmpty() == true", errorMessage = "At least 1 field must be used in Windows Pe Extension Optional Header Object.") -public interface WindowsPeOptionalHeaderObj extends GenericValidation, StixCustomProperties, Serializable { -//@TODO Add GITHUB issue that says the requirement for atleast 1 field to be present - - @JsonProperty("magic_hex") - @JsonPropertyDescription("Specifies the unsigned Optional that indicates the type of the PE binary.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getMagicHex(); - - @JsonProperty("major_linker_version") - @JsonPropertyDescription("Specifies the linker major version number.") - Optional getMajorLinkerVersion(); - - @JsonProperty("minor_linker_version") - @JsonPropertyDescription("Specifies the linker minor version number.") - Optional getMinorLinkerVersion(); - - @JsonProperty("size_of_code") - @JsonPropertyDescription("Specifies the size of the code (text) section. If there are multiple such sections, this refers to the sum of the sizes of each section.") - Optional<@PositiveOrZero Long> getSizeOfCode(); - - @JsonProperty("size_of_initialized_data") - @JsonPropertyDescription("Specifies the size of the initialized data section. If there are multiple such sections, this refers to the sum of the sizes of each section.") - Optional<@PositiveOrZero Long> getSizeOfInitializedData(); - - @JsonProperty("size_of_uninitialized_data") - @JsonPropertyDescription("Specifies the size of the uninitialized data section. If there are multiple such sections, this refers to the sum of the sizes of each section.") - Optional<@PositiveOrZero Long> getSizeOfUninitializedData(); - - @JsonProperty("address_of_entry_point") - @JsonPropertyDescription("Specifies the address of the entry point relative to the image base when the executable is loaded into memory.") - Optional getAddressOfEntryPoint(); - - @JsonProperty("base_of_code") - @JsonPropertyDescription("Specifies the address that is relative to the image base of the beginning-of-code section when it is loaded into memory.") - Optional getBaseOfCode(); - - @JsonProperty("base_of_data") - @JsonPropertyDescription("Specifies the address that is relative to the image base of the beginning-of-data section when it is loaded into memory.") - Optional getBaseOfData(); - - @JsonProperty("image_base") - @JsonPropertyDescription("Specifies the preferred address of the first byte of the image when loaded into memory.") - Optional getImageBase(); - - @JsonProperty("section_alignment") - @JsonPropertyDescription("Specifies the alignment (in bytes) of PE sections when they are loaded into memory.") - Optional getSectionAlignment(); - - @JsonProperty("file_alignment") - @JsonPropertyDescription("Specifies the factor (in bytes) that is used to align the raw data of sections in the image file.") - Optional getFileAlignment(); - - @JsonProperty("major_os_version") - @JsonPropertyDescription("Specifies the major version number of the required operating system.") - Optional getMajorOsVersion(); - - @JsonProperty("minor_os_version") - @JsonPropertyDescription("Specifies the minor version number of the required operating system.") - Optional getMinorOsVersion(); - - @JsonProperty("major_image_version") - @JsonPropertyDescription("Specifies the major version number of the image.") - Optional getMajorImageVersion(); - - @JsonProperty("minor_image_version") - @JsonPropertyDescription("Specifies the minor version number of the image.") - Optional getMinorImageVersion(); - - @JsonProperty("major_subsystem_version") - @JsonPropertyDescription("Specifies the major version number of the subsystem.") - Optional getMajorSubsystemVersion(); - - @JsonProperty("minor_subsystem_version") - @JsonPropertyDescription("Specifies the minor version number of the subsystem.") - Optional getMinorSubsystemVersion(); - - @JsonProperty("win32_version_value_hex") - @JsonPropertyDescription("Specifies the reserved win32 version value.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getWin32VersionValueHex(); - - @JsonProperty("size_of_image") - @JsonPropertyDescription("Specifies the size, in bytes, of the image, including all headers, as the image is loaded in memory.") - Optional<@PositiveOrZero Long> getSizeOfImage(); - - @JsonProperty("size_of_headers") - @JsonPropertyDescription("Specifies the combined size of the MS-DOS, PE header, and section headers, rounded up a multiple of the value specified in the file_alignment header.") - Optional<@PositiveOrZero Long> getSizeOfHeaders(); - - @JsonProperty("checksum_hex") - @JsonPropertyDescription("Specifies the checksum of the PE binary.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getChecksumHex(); - - @JsonProperty("subsystem_hex") - @JsonPropertyDescription("Specifies the subsystem (e.g., GUI, device driver, etc.) that is required to run this image.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getSubsystemHex(); - - @JsonProperty("dll_characteristics_hex") - @JsonPropertyDescription("Specifies the flags that characterize the PE binary.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getDllCharacteristicsHex(); - - @JsonProperty("size_of_stack_reserve") - @JsonPropertyDescription("Specifies the size of the stack to reserve") - Optional<@PositiveOrZero Long> getSizeOfStackReserve(); - - @JsonProperty("size_of_stack_commit") - @JsonPropertyDescription("Specifies the size of the stack to commit.") - Optional<@PositiveOrZero Long> getSizeOfStackCommit(); - - @JsonProperty("size_of_heap_reserve") - @JsonPropertyDescription("Specifies the size of the local heap space to reserve.") - Optional<@PositiveOrZero Long> getSizeOfHeapReserve(); - - @JsonProperty("size_of_heap_commit") - @JsonPropertyDescription("Specifies the size of the local heap space to commit.") - Optional<@PositiveOrZero Long> getSizeOfHeapCommit(); - - @JsonProperty("loader_flags_hex") - @JsonPropertyDescription("Specifies the reserved loader flags.") - Optional<@Pattern(regexp = "^([a-fA-F0-9]{2})+$") String> getLoaderFlagsHex(); - - @JsonProperty("number_of_rva_and_sizes") - @JsonPropertyDescription("Specifies the number of data-directory entries in the remainder of the optional header.") - Optional getNumberOfRvaAndSizes(); - - @JsonProperty("hashes") - @JsonPropertyDescription("Specifies any hashes that were computed for the optional header.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/coo/types/WindowsPeSectionObj.java b/src/main/java/io/digitalstate/stix/coo/types/WindowsPeSectionObj.java deleted file mode 100644 index 74ebe74..0000000 --- a/src/main/java/io/digitalstate/stix/coo/types/WindowsPeSectionObj.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.digitalstate.stix.coo.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.PositiveOrZero; -import java.io.Serializable; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * The PE Section type specifies metadata about a PE file section. - * - */ -@Value.Immutable @Serial.Version(1L) -//@DefaultTypeValue(value = "windows-pe-section-type", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonSerialize(as = WindowsPeSection.class) @JsonDeserialize(builder = WindowsPeSection.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "name", "size", "entropy", "hashes" }) -//@JsonTypeName("windows-pe-section-type") -public interface WindowsPeSectionObj extends GenericValidation, StixCustomProperties, Serializable { - - //@TODO Check and then add issue to GITHUB about missing spec docs about min required fields - //@TODO Add business rule with check for at least 1 required field. - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the section.") - @NotNull - Optional getName(); - - @JsonProperty("size") - @JsonPropertyDescription("Specifies the size of the section, in bytes.") - Optional<@PositiveOrZero Long> getSize(); - - @JsonProperty("entropy") - @JsonPropertyDescription("Specifies the calculated entropy for the section, as calculated using the Shannon algorithm.") - Optional getEntropy(); - - @JsonProperty("hashes") - @JsonPropertyDescription("Specifies any hashes computed over the section.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/types/WindowsRegistryValueObj.java b/src/main/java/io/digitalstate/stix/coo/types/WindowsRegistryValueObj.java deleted file mode 100644 index 326057a..0000000 --- a/src/main/java/io/digitalstate/stix/coo/types/WindowsRegistryValueObj.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.digitalstate.stix.coo.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.vocabulary.vocabularies.WindowsRegistryValueDataTypes; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.io.Serializable; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - - -/** - * The Windows Registry Value type captures the properties of a Windows Registry Key Value. - */ -@Value.Immutable @Serial.Version(1L) -//@DefaultTypeValue(value = "windows-registry-value-type", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = WindowsRegistryValue.class) @JsonDeserialize(builder = WindowsRegistryValue.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "name", "data", "data_type" }) -//@JsonTypeName("windows-registry-value-type") -public interface WindowsRegistryValueObj extends GenericValidation, StixCustomProperties, Serializable { - - @JsonProperty("name") - @JsonPropertyDescription("Specifies the name of the registry value. For specifying the default value in a registry key, an empty string MUST be used.") - @NotNull - String getName(); - - @JsonProperty("data") - @JsonPropertyDescription("Specifies the data contained in the registry value.") - Optional getData(); - - @JsonProperty("data_type") - @JsonPropertyDescription("Specifies the registry (REG_*) data type used in the registry value.") - Optional<@Vocab(WindowsRegistryValueDataTypes.class) String> getDataType(); - -} diff --git a/src/main/java/io/digitalstate/stix/coo/types/X509v3ExtensionsObj.java b/src/main/java/io/digitalstate/stix/coo/types/X509v3ExtensionsObj.java deleted file mode 100644 index 0a4d382..0000000 --- a/src/main/java/io/digitalstate/stix/coo/types/X509v3ExtensionsObj.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.digitalstate.stix.coo.types; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.businessrule.BusinessRule; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import java.io.Serializable; -import java.time.Instant; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * x509-certificate - *

    - * The X509 Certificate Object represents the properties of an X.509 certificate. - * Note that the X.509 v3 Extensions type is not a STIX Cyber Observables extension, - * it is a type that describes X.509 extensions. - * - */ -@Value.Immutable @Serial.Version(1L) -//@DefaultTypeValue(value = "x509-v3-extensions-type", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Obj", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = X509v3Extensions.class) @JsonDeserialize(builder = X509v3Extensions.Builder.class) -@JsonInclude(value = NON_EMPTY, content= NON_EMPTY) -@JsonPropertyOrder({ "basic_constraints", "name_constraints", "policy_constraints", "key_usage", "extended_key_usage", - "subject_key_identifier", "authority_key_identifier", "subject_alternative_name", "issuer_alternative_name", - "subject_directory_attributes", "crl_distribution_points", "inhibit_any_policy", - "private_key_usage_period_not_before", "private_key_usage_period_not_after", "certificate_policies", - "policy_mappings" }) -//@JsonTypeName("x509-v3-extensions-type") -@BusinessRule(ifExp = "true", thenExp = "getBasicConstraints().isPresent() == true || getNameConstraints().isPresent() == true || getPolicyConstraints().isPresent() == true || getKeyUsage().isPresent() == true || getExtendedKeyUsage().isPresent() == true || getSubjectKeyIdentifier().isPresent() == true || getAuthorityKeyIdentifier().isPresent() == true || getSubjectAlternativeName().isPresent() == true || getIssuerAlternativeName().isPresent() == true || getSubjectDirectoryAttributes().isPresent() == true || getCrlDistributionPoints().isPresent() == true || getInhibitAnyPolicy().isPresent() == true || getPrivateKeyUsagePeriodNotBefore().isPresent() == true || getPrivateKeyUsagePeriodNotAfter().isPresent() == true || getCertificatePolicies().isPresent() == true || getPolicyMappings().isPresent() == true", errorMessage = "At least 1 property must be provided") -public interface X509v3ExtensionsObj extends GenericValidation, StixCustomProperties, Serializable { - - @JsonProperty("basic_constraints") - @JsonPropertyDescription("Specifies a multi-valued extension which indicates whether a certificate is a CA certificate.") - Optional getBasicConstraints(); - - @JsonProperty("name_constraints") - @JsonPropertyDescription("Specifies a namespace within which all subject names in subsequent certificates in a certification path MUST be located.") - Optional getNameConstraints(); - - @JsonProperty("policy_constraints") - @JsonPropertyDescription("Specifies any constraints on path validation for certificates issued to CAs.") - Optional getPolicyConstraints(); - - @JsonProperty("key_usage") - @JsonPropertyDescription("Specifies a multi-valued extension consisting of a list of names of the permitted key usages.") - Optional getKeyUsage(); - - @JsonProperty("extended_key_usage") - @JsonPropertyDescription("Specifies a list of usages indicating purposes for which the certificate public key can be used for.") - Optional getExtendedKeyUsage(); - - @JsonProperty("subject_key_identifier") - @JsonPropertyDescription("Specifies the identifier that provides a means of identifying certificates that contain a particular public key.") - Optional getSubjectKeyIdentifier(); - - @JsonProperty("authority_key_identifier") - @JsonPropertyDescription("Specifies the identifier that provides a means of identifying the public key corresponding to the key used to sign a certificate.") - Optional getAuthorityKeyIdentifier(); - - @JsonProperty("subject_alternative_name") - @JsonPropertyDescription("Specifies the additional identities to be bound to the subject of the certificate.") - Optional getSubjectAlternativeName(); - - @JsonProperty("issuer_alternative_name") - @JsonPropertyDescription("Specifies the additional identities to be bound to the issuer of the certificate.") - Optional getIssuerAlternativeName(); - - @JsonProperty("subject_directory_attributes") - @JsonPropertyDescription("Specifies the identification attributes (e.g., nationality) of the subject.") - Optional getSubjectDirectoryAttributes(); - - @JsonProperty("crl_distribution_points") - @JsonPropertyDescription("Specifies how CRL information is obtained.") - Optional getCrlDistributionPoints(); - - @JsonProperty("inhibit_any_policy") - @JsonPropertyDescription("Specifies the number of additional certificates that may appear in the path before anyPolicy is no longer permitted.") - Optional getInhibitAnyPolicy(); - - @JsonProperty("private_key_usage_period_not_before") - @JsonPropertyDescription("Specifies the date on which the validity period begins for the key, if it is different from the validity period of the certificate.") - Optional getPrivateKeyUsagePeriodNotBefore(); - - @JsonProperty("private_key_usage_period_not_after") - @JsonPropertyDescription("Specifies the date on which the validity period ends for the key, if it is different from the validity period of the certificate.") - Optional getPrivateKeyUsagePeriodNotAfter(); - - @JsonProperty("certificate_policies") - @JsonPropertyDescription("Specifies a sequence of one or more policy information terms, each of which consists of an object identifier (OID) and optional qualifiers.") - Optional getCertificatePolicies(); - - @JsonProperty("policy_mappings") - @JsonPropertyDescription("Specifies one or more pairs of OIDs(); each pair includes an issuerDomainPolicy and a subjectDomainPolicy") - Optional getPolicyMappings(); - -} diff --git a/src/main/java/io/digitalstate/stix/custom/StixCustomObject.java b/src/main/java/io/digitalstate/stix/custom/StixCustomObject.java deleted file mode 100644 index 9d477d2..0000000 --- a/src/main/java/io/digitalstate/stix/custom/StixCustomObject.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.digitalstate.stix.custom; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import io.digitalstate.stix.common.*; -import io.digitalstate.stix.validation.contraints.startswith.StartsWith; -import org.hibernate.validator.constraints.Length; - -import java.util.Map; - -/** - * Provides a Generic STIX Custom Object to use for Bundleable objects when the object is not included in the mappings. - * Note: Custom properties (x_) are included in the CustomObjectProperties - */ -public interface StixCustomObject extends - StixCommonProperties, - StixLabels, - StixModified, - StixRevoked{ - - @Override - @StartsWith("x-") - String getType(); - - @Override - @StartsWith("x-") - String getId(); - - //@TODO Future enhancement to create a custom deserializer that will support the difference between x_ props and the CustomObjectProperties() - @JsonProperty(access = JsonProperty.Access.READ_ONLY) - @JsonUnwrapped @JsonAnyGetter - Map<@Length(min = 3, - max = 250, - message = "STIX Custom Properties must have a min key length of 3 and max of 250") - String, Object> getCustomObjectProperties(); - -} diff --git a/src/main/java/io/digitalstate/stix/custom/objects/GenericCustomObject.java b/src/main/java/io/digitalstate/stix/custom/objects/GenericCustomObject.java deleted file mode 100644 index ff35c98..0000000 --- a/src/main/java/io/digitalstate/stix/custom/objects/GenericCustomObject.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.digitalstate.stix.custom.objects; - -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.custom.StixCustomObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - - -@Value.Immutable @Serial.Version(1L) -//@DefaultTypeValue(value = "", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="Generic*", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = CustomObject.class) @JsonDeserialize(builder = CustomObject.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings"}) -public interface GenericCustomObject extends StixCustomObject { - - -} diff --git a/src/main/java/io/digitalstate/stix/datamarkings/GranularMarkingDm.java b/src/main/java/io/digitalstate/stix/datamarkings/GranularMarkingDm.java deleted file mode 100644 index 7fdf907..0000000 --- a/src/main/java/io/digitalstate/stix/datamarkings/GranularMarkingDm.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.digitalstate.stix.datamarkings; - -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.JsonIdentityReference; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.json.converters.dehydrated.MarkingDefinitionConverter; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.validation.SdoDefaultValidator; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.io.Serializable; -import java.util.Set; - -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Dm", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) -@JsonSerialize(as = GranularMarking.class) @JsonDeserialize(builder = GranularMarking.Builder.class) -@Redactable -public interface GranularMarkingDm extends StixCustomProperties, SdoDefaultValidator, Serializable { - - @NotNull - @JsonProperty("marking_ref") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = MarkingDefinitionConverter.class) - MarkingDefinitionDm getMarkingRef(); - - @Size(min = 1, message = "Must have as least 1 selector") - @JsonProperty("selectors") - Set getSelectors(); - -} diff --git a/src/main/java/io/digitalstate/stix/datamarkings/MarkingDefinitionDm.java b/src/main/java/io/digitalstate/stix/datamarkings/MarkingDefinitionDm.java deleted file mode 100644 index 23a8adb..0000000 --- a/src/main/java/io/digitalstate/stix/datamarkings/MarkingDefinitionDm.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.digitalstate.stix.datamarkings; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCommonProperties; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.datamarkings.objects.StatementMarkingObject; -import io.digitalstate.stix.datamarkings.objects.TlpMarkingObject; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.markingdefinitiontype.MarkingDefinitionTypeLimit; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - -/** - *

    Builder Required Fields:

    - *
      - *
    1. {@link MarkingDefinition#getDefinitionType()} - (A helper is in-place for this field that will pre-populate the value based on the specific Marking Object, which makes this field essentially optional).
    2. - *
    3. {@link MarkingDefinition#getDefinition()} - the Marking Object. Two objects are currently supported: {@link Tlp} and {@link Statement}.
    4. - *
    - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("marking-definition") -@DefaultTypeValue(value = "marking-definition", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Dm", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = MarkingDefinition.class) @JsonDeserialize(builder = MarkingDefinition.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "external_references", "object_marking_refs", "granular_markings", "definition_type", - "definition"}) -@MarkingDefinitionTypeLimit(markingObject = TlpMarkingObject.class, markingDefinitionType = "tlp", groups = {DefaultValuesProcessor.class}) -@MarkingDefinitionTypeLimit(markingObject = StatementMarkingObject.class, markingDefinitionType = "statement", groups = {DefaultValuesProcessor.class}) -@Redactable -public interface MarkingDefinitionDm extends StixCommonProperties, StixCustomProperties { - - @NotBlank - @JsonProperty("definition_type") - String getDefinitionType(); - - @NotNull - @JsonProperty("definition") - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "definition_type", include = JsonTypeInfo.As.EXTERNAL_PROPERTY) - StixMarkingObject getDefinition(); - -} diff --git a/src/main/java/io/digitalstate/stix/datamarkings/StixMarkingObject.java b/src/main/java/io/digitalstate/stix/datamarkings/StixMarkingObject.java deleted file mode 100644 index e2fe31c..0000000 --- a/src/main/java/io/digitalstate/stix/datamarkings/StixMarkingObject.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.digitalstate.stix.datamarkings; - -import io.digitalstate.stix.common.StixCustomProperties; - -import java.io.Serializable; - -public interface StixMarkingObject extends StixCustomProperties, Serializable { - -} diff --git a/src/main/java/io/digitalstate/stix/datamarkings/objects/StatementMarkingObject.java b/src/main/java/io/digitalstate/stix/datamarkings/objects/StatementMarkingObject.java deleted file mode 100644 index d76d2f6..0000000 --- a/src/main/java/io/digitalstate/stix/datamarkings/objects/StatementMarkingObject.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.digitalstate.stix.datamarkings.objects; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.datamarkings.StixMarkingObject; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.validation.GenericValidation; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; - -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeImmutable = "Statement", additionalJsonAnnotations = {JsonTypeName.class}, validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) -@JsonSerialize(as = Statement.class) @JsonDeserialize(builder = Statement.Builder.class) -@Redactable -@JsonTypeName("statement") -public interface StatementMarkingObject extends GenericValidation, StixMarkingObject { - - @NotBlank - @JsonProperty("statement") - @Length(min = 1) String getStatement(); - -} diff --git a/src/main/java/io/digitalstate/stix/datamarkings/objects/TlpMarkingObject.java b/src/main/java/io/digitalstate/stix/datamarkings/objects/TlpMarkingObject.java deleted file mode 100644 index 993a099..0000000 --- a/src/main/java/io/digitalstate/stix/datamarkings/objects/TlpMarkingObject.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.digitalstate.stix.datamarkings.objects; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.datamarkings.StixMarkingObject; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.vocabulary.vocabularies.TlpLevels; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; - -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeImmutable = "Tlp", additionalJsonAnnotations = {JsonTypeName.class}, validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) -@JsonSerialize(as = Tlp.class) @JsonDeserialize(builder = Tlp.Builder.class) -@Redactable -@JsonTypeName("tlp") -public interface TlpMarkingObject extends GenericValidation, StixMarkingObject { - - @NotNull - @JsonProperty("tlp") - @Vocab(TlpLevels.class) - String getTlp(); - -} diff --git a/src/main/java/io/digitalstate/stix/graph/GraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/GraphGenerator.java deleted file mode 100644 index 08e11d7..0000000 --- a/src/main/java/io/digitalstate/stix/graph/GraphGenerator.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.digitalstate.stix.graph; - -import io.digitalstate.stix.graph.elements.GraphElement; - -import java.util.Set; - -public interface GraphGenerator { - - Set process(); -} diff --git a/src/main/java/io/digitalstate/stix/graph/StixGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/StixGraphGenerator.java deleted file mode 100644 index 686ba50..0000000 --- a/src/main/java/io/digitalstate/stix/graph/StixGraphGenerator.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.digitalstate.stix.graph; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.digitalstate.stix.bundle.BundleObject; -import io.digitalstate.stix.graph.bundle.BundleObjectGraphGenerator; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.json.StixParsers; - -import java.util.HashSet; -import java.util.Set; - -public class StixGraphGenerator implements GraphGenerator { - - private BundleObject bundle; - private ObjectMapper jsonMapper = StixParsers.getJsonMapper(); - - public StixGraphGenerator(BundleObject bundle) { - this.bundle = bundle; - } - - @Override - public Set process(){ - Set elements = new HashSet<>(); - - elements.addAll(new BundleObjectGraphGenerator(bundle).process()); - - return elements; - } - - public String toJson(){ - try { - return jsonMapper.writeValueAsString(process()); - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); - } - } -} diff --git a/src/main/java/io/digitalstate/stix/graph/bundle/BundleObjectGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/bundle/BundleObjectGraphGenerator.java deleted file mode 100644 index dcdb77a..0000000 --- a/src/main/java/io/digitalstate/stix/graph/bundle/BundleObjectGraphGenerator.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.graph.bundle; - -import io.digitalstate.stix.bundle.BundleObject; -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.elements.GraphElement; - -import java.util.HashSet; -import java.util.Set; - -public class BundleObjectGraphGenerator implements GraphGenerator { - - private BundleObject object; - - public BundleObjectGraphGenerator(BundleObject object) { - this.object = object; - } - - public BundleObject getObject() { - return object; - } - - public Set process(){ - Set items = new HashSet<>(); - - object.getObjects().forEach(o->{ - items.addAll(new BundleableObjectGraphGenerator(o).process()); - }); - return items; - } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/bundle/BundleableObjectGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/bundle/BundleableObjectGraphGenerator.java deleted file mode 100644 index 5ef5133..0000000 --- a/src/main/java/io/digitalstate/stix/graph/bundle/BundleableObjectGraphGenerator.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.digitalstate.stix.graph.bundle; - -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.graph.sdo.DomainObjectGraphGenerator; -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.sro.RelationshipSroGraphGenerator; -import io.digitalstate.stix.graph.sro.SightingSroGraphGenerator; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sro.objects.RelationshipSro; -import io.digitalstate.stix.sro.objects.SightingSro; - -import java.util.HashSet; -import java.util.Set; - -public class BundleableObjectGraphGenerator implements GraphGenerator { - - private BundleableObject object; - - public BundleableObjectGraphGenerator(BundleableObject object) { - this.object = object; - } - - public BundleableObject getObject() { - return object; - } - - public Set process(){ - Set items = new HashSet<>(); - - Class objectClass = object.getClass(); - - if (DomainObject.class.isAssignableFrom(objectClass)){ - items.addAll(new DomainObjectGraphGenerator((DomainObject)object).process()); - - } else if (RelationshipSro.class.isAssignableFrom(objectClass)){ - items.addAll(new RelationshipSroGraphGenerator((RelationshipSro)object).process()); - - } else if (SightingSro.class.isAssignableFrom(objectClass)){ - items.addAll( - new SightingSroGraphGenerator((SightingSro) object).process() - ); - } - - return items; - } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/coo/CyberObservableGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/coo/CyberObservableGraphGenerator.java deleted file mode 100644 index e07cb0e..0000000 --- a/src/main/java/io/digitalstate/stix/graph/coo/CyberObservableGraphGenerator.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.digitalstate.stix.graph.coo; - -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.graph.elements.Node; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -/** - * Generally used by the Observed Data SDO Graph Generator - */ -public class CyberObservableGraphGenerator implements GraphGenerator { - - private CyberObservableObject object; - private String observedDataObjectId; - - public CyberObservableGraphGenerator(String observedDataObjectId, CyberObservableObject object) { - this.object = object; - this.observedDataObjectId = observedDataObjectId; - } - - public CyberObservableObject getObject() { - return object; - } - - public String getObservedDataObjectId() { - return observedDataObjectId; - } - - public Set process(){ - Set elements = new HashSet<>(); - elements.add(generateNode()); - - return elements; - } - - // Is public to support custom usage by Observed Data Graph Generator - public Node generateNode(){ - String uuid = object.getObservableObjectKey() + "--" + UUID.randomUUID().toString(); - String type = "coo-" + object.getType(); - - return new Node(uuid, type, null, object); - //@TODO Refactor to support the parent node prob for sub graph node support -// return new Node(uuid, type, observedDataObjectId, object); - } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/datamarkings/MarkingDefinitionGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/datamarkings/MarkingDefinitionGraphGenerator.java deleted file mode 100644 index 57a9a1f..0000000 --- a/src/main/java/io/digitalstate/stix/graph/datamarkings/MarkingDefinitionGraphGenerator.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.digitalstate.stix.graph.datamarkings; - -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.graph.elements.Node; - -import java.util.HashSet; -import java.util.Set; - -public class MarkingDefinitionGraphGenerator implements GraphGenerator{ - - private MarkingDefinitionDm object; - - public MarkingDefinitionGraphGenerator(MarkingDefinitionDm object) { - this.object = object; - } - - public MarkingDefinitionDm getObject() { - return object; - } - - public Set process(){ - Set elements = new HashSet<>(); - - elements.add(generateNode()); -// elements.addAll(generateEdges()); - - return elements; - } - - private Node generateNode(){ - return new Node(object.getId(), object.getType(), null, object); - } - -// private Set generateEdges() { -// Set edges = new HashSet<>(); -// -// edges.addAll(generateObjectMarkingRefEdges(object.getObjectMarkingRefs())); -// -// return edges; -// } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/elements/Edge.java b/src/main/java/io/digitalstate/stix/graph/elements/Edge.java deleted file mode 100644 index c9bddc6..0000000 --- a/src/main/java/io/digitalstate/stix/graph/elements/Edge.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.digitalstate.stix.graph.elements; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.validation.constraints.NotNull; - -public class Edge implements GraphElement { - - private EdgeData data; - - public Edge(@NotNull String id, String type, String source, String target, Object jsonData) { - this.data = new EdgeData(id, type, source, target, jsonData); - } - - @JsonProperty("data") - public EdgeData getData() { - return data; - } - - public void setData(EdgeData data) { - this.data = data; - } -} - - diff --git a/src/main/java/io/digitalstate/stix/graph/elements/EdgeData.java b/src/main/java/io/digitalstate/stix/graph/elements/EdgeData.java deleted file mode 100644 index df06cb5..0000000 --- a/src/main/java/io/digitalstate/stix/graph/elements/EdgeData.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.digitalstate.stix.graph.elements; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -import java.util.HashMap; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -public class EdgeData { - - private String id; - private String type; - private String source; - private String target; - private Object jsonData; - private String edgeLabel; - private HashMap additionalProperties = new HashMap<>(); - - public EdgeData(String id, String type, String source, String target, Object jsonData) { - this.id = id; - this.type = type; - this.source = source; - this.target = target; - this.jsonData = jsonData; - } - - @JsonProperty("id") - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @JsonProperty("type") - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - @JsonProperty("source") - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - @JsonProperty("target") - public String getTarget() { - return target; - } - - public void setTarget(String target) { - this.target = target; - } - - @JsonProperty("stix") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - public Object getJsonData() { - return jsonData; - } - - public void setJsonData(Object jsonData) { - this.jsonData = jsonData; - } - - @JsonProperty("label") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - public String getEdgeLabel() { - return edgeLabel; - } - - public void setEdgeLabel(String edgeLabel) { - this.edgeLabel = edgeLabel; - } - - @JsonUnwrapped - @JsonAnyGetter - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - public HashMap getAdditionalProperties() { - return additionalProperties; - } - - public void setAdditionalProperties(HashMap additionalProperties) { - this.additionalProperties = additionalProperties; - } -} diff --git a/src/main/java/io/digitalstate/stix/graph/elements/GraphElement.java b/src/main/java/io/digitalstate/stix/graph/elements/GraphElement.java deleted file mode 100644 index 0e391a8..0000000 --- a/src/main/java/io/digitalstate/stix/graph/elements/GraphElement.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.digitalstate.stix.graph.elements; - -import java.util.Set; - -public interface GraphElement { - -} diff --git a/src/main/java/io/digitalstate/stix/graph/elements/Node.java b/src/main/java/io/digitalstate/stix/graph/elements/Node.java deleted file mode 100644 index fac83fc..0000000 --- a/src/main/java/io/digitalstate/stix/graph/elements/Node.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.digitalstate.stix.graph.elements; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.validation.constraints.NotNull; - -public class Node implements GraphElement { - - private NodeData data; - - public Node(@NotNull String id, String type, String parent, Object jsonData) { - this.data = new NodeData(id, type, parent, jsonData); - } - - @JsonProperty("data") - public NodeData getData() { - return data; - } - - public void setData(NodeData data) { - this.data = data; - } -} diff --git a/src/main/java/io/digitalstate/stix/graph/elements/NodeData.java b/src/main/java/io/digitalstate/stix/graph/elements/NodeData.java deleted file mode 100644 index 5fcdc4e..0000000 --- a/src/main/java/io/digitalstate/stix/graph/elements/NodeData.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.digitalstate.stix.graph.elements; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -import java.util.HashMap; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -public class NodeData { - - private String id; - private String type; - private String parent; - private Object jsonData; - private HashMap additionalProperties = new HashMap<>(); - - public NodeData(String id, String type, String parent, Object jsonData) { - this.id = id; - this.type = type; - this.parent = parent; - this.jsonData = jsonData; - } - - @JsonProperty("id") - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @JsonProperty("type") - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - @JsonProperty("parent") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - public String getParent() { - return parent; - } - - public void setParent(String parent) { - this.parent = parent; - } - - @JsonProperty("stix") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - public Object getJsonData() { - return jsonData; - } - - public void setJsonData(Object jsonData) { - this.jsonData = jsonData; - } - - @JsonUnwrapped - @JsonAnyGetter - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - public HashMap getAdditionalProperties() { - return additionalProperties; - } - - public void setAdditionalProperties(HashMap additionalProperties) { - this.additionalProperties = additionalProperties; - } -} diff --git a/src/main/java/io/digitalstate/stix/graph/sdo/DomainObjectGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/sdo/DomainObjectGraphGenerator.java deleted file mode 100644 index 61e7185..0000000 --- a/src/main/java/io/digitalstate/stix/graph/sdo/DomainObjectGraphGenerator.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.digitalstate.stix.graph.sdo; - -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.graph.elements.Node; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.objects.ObservedDataSdo; - -import java.util.HashSet; -import java.util.Set; - -public class DomainObjectGraphGenerator implements GraphGenerator { - - private DomainObject object; - - public DomainObjectGraphGenerator(DomainObject object) { - this.object = object; - } - - @Override - public Set process() { - Set elements = new HashSet<>(); - - if (ObservedDataSdo.class.isAssignableFrom(object.getClass())){ - elements.addAll(generateObservedDataGraphElements((ObservedDataSdo)object)); - } else { - //@TODO Add various support for the other SDOs - elements.add(generateNode()); - } - - return elements; - } - - public DomainObject getObject() { - return object; - } - - private Node generateNode() { - return new Node(object.getId(), object.getType(), null, object); - } - - private Set generateObservedDataGraphElements(ObservedDataSdo observedDataSdo){ - return new ObservedDataGraphGenerator(observedDataSdo).process(); - } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/sdo/ObservedDataGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/sdo/ObservedDataGraphGenerator.java deleted file mode 100644 index 8639e91..0000000 --- a/src/main/java/io/digitalstate/stix/graph/sdo/ObservedDataGraphGenerator.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.digitalstate.stix.graph.sdo; - -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.coo.CyberObservableGraphGenerator; -import io.digitalstate.stix.graph.elements.Edge; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.graph.elements.Node; -import io.digitalstate.stix.sdo.objects.ObservedDataSdo; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -public class ObservedDataGraphGenerator implements GraphGenerator { - - private ObservedDataSdo object; - - public ObservedDataGraphGenerator(ObservedDataSdo object) { - this.object = object; - } - - @Override - public Set process() { - Set elements = new HashSet<>(); - - elements.add(generateNode()); - elements.addAll(generateCooElements()); - - return elements; - } - - private Node generateNode() { - return new Node(object.getId(), object.getType(), null, object); - } - - public ObservedDataSdo getObject() { - return object; - } - - private Set generateCooElements(){ - Set elements = new HashSet<>(); - - String observedDataId = object.getId(); - - object.getObjects().forEach(coo -> { - - // Generate the Cyber Observable node - Node cooGraphNode = new CyberObservableGraphGenerator(observedDataId, coo).generateNode(); - elements.add(cooGraphNode); - - String uuidPrefix = "ref"; - String uuid = uuidPrefix + "-" + UUID.randomUUID().toString(); - // Use the Cyber Observable node's ID as the target: - Edge edge = new Edge(uuid, uuidPrefix, observedDataId, cooGraphNode.getData().getId(), null); - - edge.getData().setEdgeLabel(coo.getType()); - edge.getData().getAdditionalProperties().put("ref_type", "cyber_observable"); - - elements.add(edge); - }); - - return elements; - } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/sro/RelationshipSroGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/sro/RelationshipSroGraphGenerator.java deleted file mode 100644 index 1ae0f3d..0000000 --- a/src/main/java/io/digitalstate/stix/graph/sro/RelationshipSroGraphGenerator.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.digitalstate.stix.graph.sro; - -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.elements.Edge; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.sro.objects.RelationshipSro; - -import java.util.HashSet; -import java.util.Set; - -public class RelationshipSroGraphGenerator implements GraphGenerator { - - private RelationshipSro object; - - public RelationshipSroGraphGenerator(RelationshipSro object) { - this.object = object; - } - - public RelationshipSro getObject() { - return object; - } - - @Override - public Set process() { - Set elements = new HashSet<>(); - elements.add(generateEdge()); - return elements; - } - - private Edge generateEdge() { - Edge edge = new Edge(object.getId(), - object.getType(), - object.getSourceRef().getId(), - object.getTargetRef().getId(), - object); - - edge.getData().setEdgeLabel(object.getRelationshipType()); - - edge.getData() - .getAdditionalProperties() - .put("relationship_type", object.getRelationshipType()); - - return edge; - } - -} diff --git a/src/main/java/io/digitalstate/stix/graph/sro/SightingSroGraphGenerator.java b/src/main/java/io/digitalstate/stix/graph/sro/SightingSroGraphGenerator.java deleted file mode 100644 index 4cb20d2..0000000 --- a/src/main/java/io/digitalstate/stix/graph/sro/SightingSroGraphGenerator.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.digitalstate.stix.graph.sro; - -import io.digitalstate.stix.graph.GraphGenerator; -import io.digitalstate.stix.graph.elements.Edge; -import io.digitalstate.stix.graph.elements.GraphElement; -import io.digitalstate.stix.graph.elements.Node; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.objects.ObservedDataSdo; -import io.digitalstate.stix.sro.objects.SightingSro; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -public class SightingSroGraphGenerator implements GraphGenerator { - - private SightingSro object; - - public SightingSroGraphGenerator(SightingSro object) { - this.object = object; - } - - public SightingSro getObject() { - return object; - } - - public Set process(){ - Set elements = new HashSet<>(); - - elements.add(generateNode()); - elements.addAll(generateEdges()); - - return elements; - } - - private Node generateNode(){ - return new Node(object.getId(), object.getType(), null, object); - } - - private Set generateEdges() { - Set edges = new HashSet<>(); - - edges.add(generateSightingOfRefEdge(object.getSightingOfRef())); - - edges.addAll(generateObservedDataEdges(object.getId(), object.getObservedDataRefs())); - - return edges; - } - - private Edge generateSightingOfRefEdge(DomainObject sightingOfRefDomainObject){ - String uuidPrefix = "ref"; - - DomainObject sor = object.getSightingOfRef(); - - String sorUuid = uuidPrefix + "-" + UUID.randomUUID().toString(); - - Edge edge = new Edge(sorUuid, uuidPrefix, object.getId(), sor.getId(), null); - - edge.getData().setEdgeLabel("sighting-of"); - - edge.getData().getAdditionalProperties().put("ref_type", "sighting_of_ref"); - - return edge; - } - - private Set generateObservedDataEdges(String sourceId, Set observedDataSdoSet){ - Set edges = new HashSet<>(); - - observedDataSdoSet.forEach(od -> { - String uuidPrefix = "ref"; - String odUuid = uuidPrefix + "-" + UUID.randomUUID().toString(); - - Edge edge = new Edge(odUuid, uuidPrefix, sourceId, od.getId(), null); - - edge.getData().setEdgeLabel("observed-data"); - - edge.getData().getAdditionalProperties().put("ref_type", "observed_data"); - - edges.add(edge); - }); - - return edges; - } - -} diff --git a/src/main/java/io/digitalstate/stix/helpers/StixCustomPropertiesConfig.java b/src/main/java/io/digitalstate/stix/helpers/StixCustomPropertiesConfig.java deleted file mode 100644 index 961a67a..0000000 --- a/src/main/java/io/digitalstate/stix/helpers/StixCustomPropertiesConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.digitalstate.stix.helpers; - -import java.util.HashSet; -import java.util.Set; - -/** - * STIX Custom properties configuration - */ -public class StixCustomPropertiesConfig { - - /** - * Default Custom Property Prefix: {@code x_ }. - */ - public static final String DEFAULT_CUSTOM_PROPERTY_PREFIX = "x_"; - - private static Set additionalPropertyPrefixes = new HashSet<>(); - - /** - * Get Additional STIX Custom Property prefixes. - */ - public static Set getAdditionalPropertyPrefixes() { - return additionalPropertyPrefixes; - } - - /** - * Set Additional STIX Custom Property prefixes. - */ - public static void setAdditionalPropertyPrefixes(Set additionalPropertyPrefixes) { - StixCustomPropertiesConfig.additionalPropertyPrefixes = additionalPropertyPrefixes; - } - - /** - * Returns a aggregate of The Default Custom Property Prefix and any defined additional customer property prefixes. - * @return Set of allowed custom property prefixes - */ - public static Set getAllCustomPropertyPrefixes(){ - Set allPrefixes = new HashSet<>(getAdditionalPropertyPrefixes()); - allPrefixes.add(DEFAULT_CUSTOM_PROPERTY_PREFIX); - return allPrefixes; - } -} diff --git a/src/main/java/io/digitalstate/stix/helpers/StixDataFormats.java b/src/main/java/io/digitalstate/stix/helpers/StixDataFormats.java deleted file mode 100644 index 4a72cd7..0000000 --- a/src/main/java/io/digitalstate/stix/helpers/StixDataFormats.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.digitalstate.stix.helpers; - -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.temporal.ChronoField; - -public class StixDataFormats { - - /** - * Default pattern for deserialization of date/times into a STIX compliant timestamp. - */ - public static final String TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]X"; - - /** - * Default Timezone used for Serialization and Deserialization. - */ - public static final String TIMEZONE = "UTC"; - - /** - * Supports 0-9 digits of Sub-Second precision storage. (Nano Second Support) - * @return - */ - public static DateTimeFormatter getReaderStixDateTimeFormatter() { - DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder() - .appendPattern("yyyy-MM-dd'T'HH:mm:ss") - .optionalStart() - .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) - .optionalEnd() - .appendPattern("X"); - - return formatterBuilder.toFormatter().withZone(ZoneId.of("UTC")); - } - - public static DateTimeFormatter getWriterStixDateTimeFormatter(int subSecondPrecision) { - if (subSecondPrecision > 9){ - throw new IllegalArgumentException("Sub-Second Precision can only be from 0 to 9 digits"); - } - DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder(); - - formatterBuilder.appendPattern("yyyy-MM-dd'T'HH:mm:ss"); - - if (subSecondPrecision > 0){ - formatterBuilder.appendFraction(ChronoField.NANO_OF_SECOND,subSecondPrecision, subSecondPrecision, true); - } - - formatterBuilder.appendPattern("X"); - - return formatterBuilder.toFormatter().withZone(ZoneId.of("UTC")); - } - -} diff --git a/src/main/java/io/digitalstate/stix/helpers/StixSpecVersion.java b/src/main/java/io/digitalstate/stix/helpers/StixSpecVersion.java deleted file mode 100644 index 93b5030..0000000 --- a/src/main/java/io/digitalstate/stix/helpers/StixSpecVersion.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.digitalstate.stix.helpers; - - -public class StixSpecVersion { - - /** - * STIX spec version number - */ - public static final String SPECVERSION = "2.0"; -} diff --git a/src/main/java/io/digitalstate/stix/json/StixBooleanDeserializer.java b/src/main/java/io/digitalstate/stix/json/StixBooleanDeserializer.java deleted file mode 100644 index f29157c..0000000 --- a/src/main/java/io/digitalstate/stix/json/StixBooleanDeserializer.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.digitalstate.stix.json; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import io.digitalstate.stix.common.StixBoolean; - -import java.io.IOException; - -public class StixBooleanDeserializer extends StdDeserializer { - - public StixBooleanDeserializer(){ - this(null); - } - - public StixBooleanDeserializer(Class vc) { - super(vc); - } - - @Override - public StixBoolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - return new StixBoolean(p.getBooleanValue()); - } -} diff --git a/src/main/java/io/digitalstate/stix/json/StixBooleanSerializer.java b/src/main/java/io/digitalstate/stix/json/StixBooleanSerializer.java deleted file mode 100644 index f8e7940..0000000 --- a/src/main/java/io/digitalstate/stix/json/StixBooleanSerializer.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.digitalstate.stix.json; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import io.digitalstate.stix.common.StixBoolean; - -import java.io.IOException; - -public class StixBooleanSerializer extends StdSerializer { - - public StixBooleanSerializer() { - this(null); - } - - public StixBooleanSerializer(Class t) { - super(t); - } - - @Override - public boolean isEmpty(SerializerProvider provider, StixBoolean value) { - return !value.isdefinedValue(); - //@TODO Future enhancement: Make this a configuration so the impl can decide if non defined values should still be serialized as their default values - } - - @Override - public void serialize(final StixBoolean value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeBoolean(value.getStixBooleanValue()); - } -} diff --git a/src/main/java/io/digitalstate/stix/json/StixInstantDeserializer.java b/src/main/java/io/digitalstate/stix/json/StixInstantDeserializer.java deleted file mode 100644 index 75c7f5f..0000000 --- a/src/main/java/io/digitalstate/stix/json/StixInstantDeserializer.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.digitalstate.stix.json; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import io.digitalstate.stix.common.StixInstant; - -import java.io.IOException; - -public class StixInstantDeserializer extends StdDeserializer { - - public StixInstantDeserializer(){ - this(null); - } - - public StixInstantDeserializer(Class vc) { - super(vc); - } - - @Override - public StixInstant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - return StixInstant.parse(p.getText()); - } -} diff --git a/src/main/java/io/digitalstate/stix/json/StixInstantSerializer.java b/src/main/java/io/digitalstate/stix/json/StixInstantSerializer.java deleted file mode 100644 index 4751998..0000000 --- a/src/main/java/io/digitalstate/stix/json/StixInstantSerializer.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.digitalstate.stix.json; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import io.digitalstate.stix.common.StixInstant; - -import java.io.IOException; - -public class StixInstantSerializer extends StdSerializer { - - public StixInstantSerializer() { - this(null); - } - - public StixInstantSerializer(Class t) { - super(t); - } - - @Override - public void serialize(final StixInstant value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeString(value.toString()); - } -} diff --git a/src/main/java/io/digitalstate/stix/json/StixParserValidationException.java b/src/main/java/io/digitalstate/stix/json/StixParserValidationException.java deleted file mode 100644 index c46ff17..0000000 --- a/src/main/java/io/digitalstate/stix/json/StixParserValidationException.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.digitalstate.stix.json; - -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.ValidationException; -import java.util.Collections; -import java.util.Set; - -public class StixParserValidationException extends RuntimeException { - - public StixParserValidationException(ValidationException exceptionCause) { - super(exceptionCause); - } - - /** - * If Cause is ${@link ConstraintViolationException}, - * then returns the set of constraint validations, or else returns null. - * @return Set of Constraint Violations - */ - public Set> getConstraintValidations() { - if (getCause().getClass().equals(ConstraintViolationException.class)) { - return ((ConstraintViolationException) getCause()).getConstraintViolations(); - } else { - return Collections.emptySet(); - } - } - } diff --git a/src/main/java/io/digitalstate/stix/json/StixParsers.java b/src/main/java/io/digitalstate/stix/json/StixParsers.java deleted file mode 100644 index a1a2f5e..0000000 --- a/src/main/java/io/digitalstate/stix/json/StixParsers.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.digitalstate.stix.json; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.guava.GuavaModule; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; -import io.digitalstate.stix.bundle.Bundle; -import io.digitalstate.stix.bundle.BundleObject; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.common.Stix; -import io.digitalstate.stix.common.StixBoolean; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.extension.types.*; -import io.digitalstate.stix.coo.objects.*; -import io.digitalstate.stix.coo.objects.Process; -import io.digitalstate.stix.datamarkings.MarkingDefinition; -import io.digitalstate.stix.datamarkings.objects.Statement; -import io.digitalstate.stix.datamarkings.objects.Tlp; -import io.digitalstate.stix.sdo.objects.*; -import io.digitalstate.stix.sro.objects.Relationship; -import io.digitalstate.stix.sro.objects.Sighting; - -import javax.validation.ValidationException; -import java.io.IOException; - -/** - * Default JSON Mapper is configured with JsonMapperBase configs + StixSubTypesModule + StixInstantModule - */ -public class StixParsers { - - private static ObjectMapper jsonMapper = new ObjectMapper() - .registerModule(new ParameterNamesModule()) - .registerModule(new Jdk8Module()) - .registerModule(new JavaTimeModule()) - .registerModule(new GuavaModule()) - .registerModule(generateStixSubTypesModule()) - .registerModule(generateStixInstantModule()) - .registerModule(generateStixBooleanModule()); - - /** - * Generates a Base Object Mapper with some generic modules. - * - * @return - */ - public static ObjectMapper generateJsonMapperBase() { - return new ObjectMapper() - .registerModule(new ParameterNamesModule()) - .registerModule(new Jdk8Module()) - .registerModule(new JavaTimeModule()) - .registerModule(new GuavaModule()); - } - - public static ObjectMapper getJsonMapper() { - return jsonMapper; - } - - /** - * Override for setting a custom configured ObjectMapper - * - * @param objectMapper - */ - public static void setJsonMapper(ObjectMapper objectMapper) { - jsonMapper = objectMapper; - } - - /** - * Generate a Jackson module for all STIX objects (SDOs, SROs, Markings, bundle, observables, and observable extensions) - * - * @return - */ - public static SimpleModule generateStixSubTypesModule() { - SimpleModule module = new SimpleModule(); - - Class[] sdoClasses = {AttackPattern.class, Campaign.class, CourseOfAction.class, - Identity.class, Indicator.class, IntrusionSet.class, Malware.class, ObservedData.class, - Report.class, ThreatActor.class, Tool.class, Vulnerability.class}; - - Class[] sroClasses = {Relationship.class, Sighting.class}; - - Class[] dataMarkingClasses = {MarkingDefinition.class, Statement.class, Tlp.class}; - - Class[] bundleClasses = {Bundle.class}; - - Class[] cyberObservableClasses = {Artifact.class, AutonomousSystem.class, Directory.class, - DomainName.class, EmailAddress.class, EmailMessage.class, File.class, Ipv4Address.class, Ipv6Address.class, - MacAddress.class, Mutex.class, NetworkTraffic.class, Process.class, Software.class, Url.class, - UserAccount.class, WindowsRegistryKey.class, X509Certificate.class}; - - Class[] cyberObservableExtensionClasses = {ArchiveFileExtension.class, HttpRequestExtension.class, IcmpExtension.class, - NetworkSocketExtension.class, NtfsFileExtenstion.class, PdfFileExtension.class, RasterImageFileExtension.class, - TcpExtension.class, UnixAccountExtension.class, WindowsPeBinaryFileExtension.class, WindowsProcessExtension.class, - WindowsServiceExtension.class}; - - module.registerSubtypes(sdoClasses); - module.registerSubtypes(sroClasses); - module.registerSubtypes(dataMarkingClasses); - module.registerSubtypes(bundleClasses); - module.registerSubtypes(cyberObservableClasses); - module.registerSubtypes(cyberObservableExtensionClasses); - - return module; - } - - public static SimpleModule generateStixInstantModule() { - SimpleModule module = new SimpleModule(); - module.addSerializer(StixInstant.class, new StixInstantSerializer()); - module.addDeserializer(StixInstant.class, new StixInstantDeserializer()); - return module; - } - - public static SimpleModule generateStixBooleanModule() { - SimpleModule module = new SimpleModule(); - module.addSerializer(StixBoolean.class, new StixBooleanSerializer()); - module.addDeserializer(StixBoolean.class, new StixBooleanDeserializer()); - return module; - } - - public static BundleObject parseBundle(String bundleJsonString) throws IOException, StixParserValidationException { - try { - return getJsonMapper().readValue(bundleJsonString, BundleObject.class); - } catch (IOException ex) { - if (ValidationException.class.isAssignableFrom(ex.getCause().getClass())) { - throw new StixParserValidationException((ValidationException) ex.getCause()); - } else { - throw ex; - } - } - } - - public static BundleableObject parseObject(String objectJsonString) throws IOException, StixParserValidationException { - try { - return getJsonMapper().readValue(objectJsonString, BundleableObject.class); - } catch (IOException ex) { - if (ValidationException.class.isAssignableFrom(ex.getCause().getClass())) { - throw new StixParserValidationException((ValidationException) ex.getCause()); - } else { - throw ex; - } - } - } - - public static T parse(String bundleJsonString, Class stixClass) throws IOException, StixParserValidationException { - try { - return getJsonMapper().readValue(bundleJsonString, stixClass); - } catch (IOException ex) { - if (ValidationException.class.isAssignableFrom(ex.getCause().getClass())) { - throw new StixParserValidationException((ValidationException) ex.getCause()); - } else { - throw ex; - } - } - } -} diff --git a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectConverter.java b/src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectConverter.java deleted file mode 100644 index d320502..0000000 --- a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.digitalstate.stix.json.converters.dehydrated; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.util.StdConverter; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.json.StixParsers; - -/** - * Generates a Dehydrated Bundleable Object based on a ID. - */ -public class BundleableObjectConverter extends StdConverter { - - @Override - public BundleableObject convert(String value) { - String[] parsedValue = value.split("--"); - - if (parsedValue.length == 2){ - ObjectMapper mapper = StixParsers.getJsonMapper(); - ObjectNode node = mapper.createObjectNode(); - - node.put("type", parsedValue[0]); - node.put("id", value); - node.put("hydrated", false); - - - try { - BundleableObject bundleableObject = mapper.treeToValue(node, BundleableObject.class); - //@TODO add more logic - return bundleableObject; - - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); - } - - } else { - throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); - } - } -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectSetConverter.java b/src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectSetConverter.java deleted file mode 100644 index 7b650c9..0000000 --- a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/BundleableObjectSetConverter.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.digitalstate.stix.json.converters.dehydrated; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.util.StdConverter; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.json.StixParsers; - -import java.util.HashSet; -import java.util.Set; - -/** - * Generates a Dehydrated Bundleable Object based on a ID from a Set of BundleableObjects. - */ -public class BundleableObjectSetConverter extends StdConverter, Set> { - - @Override - public Set convert(Set values) { - Set bundleableObjectSet = new HashSet<>(); - values.forEach(v -> { - String[] parsedValue = v.split("--"); - - if (parsedValue.length == 2) { - ObjectMapper mapper = StixParsers.getJsonMapper(); - ObjectNode node = mapper.createObjectNode(); - - node.put("type", parsedValue[0]); - node.put("id", v); - node.put("hydrated", false); - - - try { - BundleableObject bundleableObject = mapper.treeToValue(node, BundleableObject.class); - //@TODO add more logic - bundleableObjectSet.add(bundleableObject); - - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); - } - - } else { - throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + v); - } - }); - return bundleableObjectSet; - } -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectConverter.java b/src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectConverter.java deleted file mode 100644 index 453f13b..0000000 --- a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.digitalstate.stix.json.converters.dehydrated; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.util.StdConverter; -import io.digitalstate.stix.json.StixParsers; -import io.digitalstate.stix.sdo.DomainObject; - -/** - * Generates a Dehydrated Domain Object based on a ID. - */ -public class DomainObjectConverter extends StdConverter { - - @Override - public DomainObject convert(String value) { - String[] parsedValue = value.split("--"); - - if (parsedValue.length == 2){ - ObjectMapper mapper = StixParsers.getJsonMapper(); - ObjectNode node = mapper.createObjectNode(); - - node.put("type", parsedValue[0]); - node.put("id", value); - node.put("hydrated", false); - - - try { - DomainObject domainObject = mapper.treeToValue(node, DomainObject.class); - //@TODO add more logic - return domainObject; - - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); - } - - } else { - throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); - } - } -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectOptionalConverter.java b/src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectOptionalConverter.java deleted file mode 100644 index 6840819..0000000 --- a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/DomainObjectOptionalConverter.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.digitalstate.stix.json.converters.dehydrated; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.util.StdConverter; -import io.digitalstate.stix.json.StixParsers; -import io.digitalstate.stix.sdo.DomainObject; - -import java.util.Optional; - -/** - * Generates a Dehydrated Domain Object based on a ID. - */ -public class DomainObjectOptionalConverter extends StdConverter> { - - @Override - public Optional convert(String value) { - String[] parsedValue = value.split("--"); - - if (parsedValue.length == 2){ - ObjectMapper mapper = StixParsers.getJsonMapper(); - ObjectNode node = mapper.createObjectNode(); - - node.put("type", parsedValue[0]); - node.put("id", value); - node.put("hydrated", false); - - - try { - return Optional.ofNullable(mapper.treeToValue(node, DomainObject.class)); - //@TODO add more logic - - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); - } - - } else { - throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); - } - } -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionConverter.java b/src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionConverter.java deleted file mode 100644 index 702907d..0000000 --- a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.digitalstate.stix.json.converters.dehydrated; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.util.StdConverter; -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; -import io.digitalstate.stix.json.StixParsers; - -/** - * Generates a Dehydrated Domain Object based on a ID. - */ -public class MarkingDefinitionConverter extends StdConverter { - - @Override - public MarkingDefinitionDm convert(String value) { - String[] parsedValue = value.split("--"); - - if (parsedValue.length == 2){ - ObjectMapper mapper = StixParsers.getJsonMapper(); - ObjectNode node = mapper.createObjectNode(); - - node.put("type", parsedValue[0]); - node.put("id", value); - node.put("hydrated", false); - - - try { - MarkingDefinitionDm domainObject = mapper.treeToValue(node, MarkingDefinitionDm.class); - //@TODO add more logic - return domainObject; - - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); - } - - } else { - throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + value); - } - } -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionSetConverter.java b/src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionSetConverter.java deleted file mode 100644 index ba7b66e..0000000 --- a/src/main/java/io/digitalstate/stix/json/converters/dehydrated/MarkingDefinitionSetConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.digitalstate.stix.json.converters.dehydrated; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.util.StdConverter; -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; -import io.digitalstate.stix.json.StixParsers; - -import java.util.HashSet; -import java.util.Set; - -/** - * Generates a Dehydrated Marking Definition Set used for Deserialization - */ -public class MarkingDefinitionSetConverter extends StdConverter, Set> { - - @Override - public Set convert(Set values) { - Set markDefSet = new HashSet<>(); - values.forEach(v -> { - String[] parsedValue = v.split("--"); - - if (parsedValue.length == 2) { - ObjectMapper mapper = StixParsers.getJsonMapper(); - ObjectNode node = mapper.createObjectNode(); - - node.put("type", parsedValue[0]); - node.put("id", v); - node.put("hydrated", false); - - try { - MarkingDefinitionDm markingDef = mapper.treeToValue(node, MarkingDefinitionDm.class); - //@TODO add more logic - markDefSet.add(markingDef); - - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Cannot Parse Json: " + e.getMessage()); - } - - } else { - throw new IllegalArgumentException("Id is not valid format, Cannot Parse Value: " + v); - } - }); - return markDefSet; - } -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/redaction/Redactable.java b/src/main/java/io/digitalstate/stix/redaction/Redactable.java deleted file mode 100644 index dd47a50..0000000 --- a/src/main/java/io/digitalstate/stix/redaction/Redactable.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.digitalstate.stix.redaction; - -import org.immutables.annotate.InjectAnnotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.*; - -/** - * Marker to indicate which classes and methods are Redactable. - * Redaction is the modification of JSON properties or removal of properties and - * entire objects during serialization based on STIX Marking Definitions and Granular Markings. - */ -@Documented -@Target( { ANNOTATION_TYPE, TYPE, METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@InjectAnnotation(target = {InjectAnnotation.Where.ACCESSOR, InjectAnnotation.Where.IMMUTABLE_TYPE}, code = "([[*]])", type = Redactable.class) -public @interface Redactable { - - boolean useMask() default false; - String redactionMask() default ""+ ((char)0x2588) + ((char)0x2588) + "REDACTED" + ((char)0x2588) + ((char)0x2588); - - //@TODO: - // Add Masking config - // Add Conditional - // Add object Refs -} diff --git a/src/main/java/io/digitalstate/stix/redaction/processors/BundleableObjectRedactionProcessor.java b/src/main/java/io/digitalstate/stix/redaction/processors/BundleableObjectRedactionProcessor.java deleted file mode 100644 index aa2894d..0000000 --- a/src/main/java/io/digitalstate/stix/redaction/processors/BundleableObjectRedactionProcessor.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.digitalstate.stix.redaction.processors; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.jayway.jsonpath.Configuration; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import com.jayway.jsonpath.Option; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.datamarkings.GranularMarkingDm; -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; -import io.digitalstate.stix.datamarkings.StixMarkingObject; -import io.digitalstate.stix.redaction.Redactable; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class BundleableObjectRedactionProcessor { - - public static String processObject(BundleableObject bundleableObject, String jsonString, Set subjectsMarkingObjects) { - - String block = Character.toString((char) 0x2588); - String redactedMaskValue = block + block + "REDACTED" + block + block; - - Set markingDefinitionsSet = bundleableObject.getObjectMarkingRefs(); - - if (!markingDefinitionsSet.isEmpty()) { - // @TODO look into how to allow injection of annotaiton in multiple locations -// Redactable classRedactable = Optional.ofNullable(bundleableObject.getClass().getDeclaredAnnotation(Redactable.class)) -// .orElseThrow(IllegalStateException::new); - - Set objectMarkings = markingDefinitionsSet.stream() - .filter(md -> md.getDefinitionType().equals("tlp")) - .map(MarkingDefinitionDm::getDefinition) - .collect(Collectors.toSet()); - - if (!subjectsMarkingObjects.containsAll(objectMarkings)) { - return "{}"; - } - } - - - Set granularMarkingDms = bundleableObject.getGranularMarkings(); - - if (!granularMarkingDms.isEmpty()) { - Configuration conf = Configuration.builder() - .options(Option.AS_PATH_LIST).build(); - DocumentContext doc = JsonPath.using(conf).parse(jsonString); - - Set getMethods = Arrays.stream(bundleableObject.getClass().getDeclaredMethods()) - .filter(m -> m.getAnnotation(JsonProperty.class) != null) - .filter(m -> m.getName().startsWith("get")) - .collect(Collectors.toSet()); - System.out.println("getMethods: " + getMethods.toString()); - - granularMarkingDms.forEach(gm -> { - MarkingDefinitionDm markingDefinition = gm.getMarkingRef(); - StixMarkingObject markingObject = gm.getMarkingRef().getDefinition(); - Set selectors = gm.getSelectors(); - - if (markingDefinition.getDefinitionType().equals("tlp") && !subjectsMarkingObjects.contains(markingObject)) { - selectors.forEach(s -> { - - // Parse the selectors into JsonPath - List pathLists = doc.read("$." + s); - System.out.println("Parsed Selector Matches: " + pathLists.toString()); - - - final String regex = "\\$\\['(.*?)'\\]"; - final Pattern pattern = Pattern.compile(regex); - - pathLists.forEach(path -> { - // @TODO refactor to support multiple levels of inner object redaction - - Matcher jsonPathPropertyNameMatcher = pattern.matcher(path); - boolean matcherResult = jsonPathPropertyNameMatcher.find(); - if (!matcherResult) { - throw new IllegalStateException("Cannot make a match / cannot parse the json path pattern"); - } else if (jsonPathPropertyNameMatcher.groupCount() > 1) { - throw new IllegalStateException("Cannot make a match / Multiple Matches found"); - } - String jsonPathPropertyName = jsonPathPropertyNameMatcher.group(1); - - - Method methodForPath = getMethods.stream() - .filter(m -> m.getDeclaredAnnotation(JsonProperty.class).value().equals(jsonPathPropertyName)) - .findFirst().orElseThrow(IllegalStateException::new); - String jacksonPropertyName = methodForPath.getAnnotation(JsonProperty.class).value(); - - System.out.println("jsonPathName: " + jsonPathPropertyName); - System.out.println("JacksonPathname: " + jacksonPropertyName); - - Redactable redactableAnn = Optional - .ofNullable(methodForPath.getDeclaredAnnotation(Redactable.class)) - .orElseThrow(IllegalStateException::new); - - // Redaction Logic: - if (redactableAnn.useMask()) { - doc.set(path, redactableAnn.redactionMask()); - } else { - doc.delete(path); - } - - }); - }); - } - }); - return doc.jsonString(); - } - return jsonString; - } -} diff --git a/src/main/java/io/digitalstate/stix/sdo/DomainObject.java b/src/main/java/io/digitalstate/stix/sdo/DomainObject.java deleted file mode 100644 index 32ab918..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/DomainObject.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.digitalstate.stix.sdo; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.digitalstate.stix.common.*; -import io.digitalstate.stix.sro.objects.RelationshipSro; - -import javax.validation.constraints.NotNull; -import java.io.Serializable; -import java.util.Set; - -/** - * Base interface used by Immutable STIX Domain Objects - */ -public interface DomainObject extends Serializable, - StixCommonProperties, - StixCustomProperties, - StixLabels, - StixModified, - StixRevoked{ - - /** - * 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. - * Otherwise you would typically add these Relationship SROs that are specific to SDOs, can be grabbed during bundle creation. - * @return Set of Relationship SROs - */ - @NotNull - @JsonIgnore - Set getRelationships(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/AttackPatternSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/AttackPatternSdo.java deleted file mode 100644 index 40d7fe9..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/AttackPatternSdo.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.types.KillChainPhaseType; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * attack-pattern - *

    - * Attack Patterns are a type of TTP that describe ways that adversaries attempt to compromise targets. - * - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "attack-pattern", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("attack-pattern") -@JsonSerialize(as = AttackPattern.class) @JsonDeserialize(builder = AttackPattern.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", - "name", "description", "kill_chain_phases"}) -@Redactable -public interface AttackPatternSdo extends DomainObject { - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Attack Pattern.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about the Attack Pattern, potentially including its purpose and its key characteristics.") - @Redactable - Optional getDescription(); - - @JsonProperty("kill_chain_phases") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The list of kill chain phases for which this attack pattern is used.") - @Redactable - Set getKillChainPhases(); - -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/CampaignSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/CampaignSdo.java deleted file mode 100644 index 5a63ee1..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/CampaignSdo.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * campaign - *

    - * A Campaign is a grouping of adversary behavior that describes a set of malicious activities or attacks that occur over a period of time against a specific set of targets. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("campaign") -@DefaultTypeValue(value = "campaign", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Campaign.class) @JsonDeserialize(builder = Campaign.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", "description", - "aliases", "first_seen", "last_seen", "objective"}) -@Redactable -public interface CampaignSdo extends DomainObject { - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Campaign.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about the Campaign, potentially including its purpose and its key characteristics.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("aliases") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Alternative names used to identify this campaign.") - @Redactable - Set getAliases(); - - @JsonProperty("first_seen") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The time that this Campaign was first seen.") - @Redactable - Optional getFirstSeen(); - - //@TODO add support to ensure that Last Seen is AFTER the First Seen value - @JsonProperty("last_seen") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The time that this Campaign was last seen.") - @Redactable - Optional getLastSeen(); - - @JsonProperty("objective") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("This field defines the Campaign’s primary goal, objective, desired outcome, or intended effect.") - @Redactable - Optional getObjective(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/CourseOfActionSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/CourseOfActionSdo.java deleted file mode 100644 index 0d4d67e..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/CourseOfActionSdo.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * course-of-action - *

    - * A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("course-of-action") -@DefaultTypeValue(value = "course-of-action", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = CourseOfAction.class) @JsonDeserialize(builder = CourseOfAction.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", - "description", "action"}) -@Redactable -public interface CourseOfActionSdo extends DomainObject { - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Course of Action.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about the Course of Action, potentially including its purpose and its key characteristics.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("action") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("RESERVED – To capture structured/automated courses of action.") - @Redactable(useMask = true) - Set getAction(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/IdentitySdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/IdentitySdo.java deleted file mode 100644 index 33396f9..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/IdentitySdo.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.IdentityClasses; -import io.digitalstate.stix.vocabulary.vocabularies.IndustrySectors; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * identity - *

    - * Identities can represent actual individuals, organizations, or groups (e.g., ACME, Inc.) as well as classes of individuals, organizations, or groups. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("identity") -@DefaultTypeValue(value = "identity", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Identity.class) @JsonDeserialize(builder = Identity.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", "description", - "identity_class", "sectors", "contact_information"}) -@Redactable -public interface IdentitySdo extends DomainObject { - - // Note for the labels attribute: - // The list of roles that this Identity performs (e.g., CEO, Domain Administrators, Doctors, Hospital, or Retailer). No open vocabulary is yet defined for this property. - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name of this Identity.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about the Identity.") - @Redactable - Optional getDescription(); - - @NotBlank - @Vocab(IdentityClasses.class) - @JsonProperty("identity_class") - @JsonPropertyDescription("The type of entity that this Identity describes, e.g., an individual or organization. Open Vocab - identity-class-ov") - @Redactable(useMask = true) - String getIdentityClass(); - - @NotNull - @Vocab(IndustrySectors.class) - @JsonProperty("sectors") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The list of sectors that this Identity belongs to. Open Vocab - industry-sector-ov") - @Redactable - Set getSectors(); - - @JsonProperty("contact_information") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The contact information (e-mail, phone number, etc.) for this Identity.") - @Redactable - Optional getContactInformation(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/IndicatorSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/IndicatorSdo.java deleted file mode 100644 index 5493156..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/IndicatorSdo.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.types.KillChainPhaseType; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.IndicatorLabels; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * indicator - *

    - * Indicators contain a pattern that can be used to detect suspicious or malicious cyber activity. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("indicator") -@DefaultTypeValue(value = "indicator", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Indicator.class) @JsonDeserialize(builder = Indicator.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", - "description", "pattern", "valid_from", "valid_until", - "kill_chain_phases"}) -@Redactable -public interface IndicatorSdo extends DomainObject { - - @Override - @NotNull @Size(min = 1) - @Vocab(IndicatorLabels.class) - @JsonPropertyDescription("This field is an Open Vocabulary that specifies the type of indicator. Open vocab - indicator-label-ov") - @Redactable(useMask = true) - Set<@Length(min = 1) String> getLabels(); - - @JsonProperty("name") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The name used to identify the Indicator.") - @Redactable - Optional getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about this Indicator, potentially including its purpose and its key characteristics.") - @Redactable - Optional getDescription(); - - @NotBlank - @JsonProperty("pattern") - @JsonPropertyDescription("The detection pattern for this indicator. The default language is STIX Patterning.") - @Redactable(useMask = true) - String getPattern(); - - @NotNull - @JsonProperty("valid_from") - @JsonPropertyDescription("The time from which this indicator should be considered valuable intelligence.") - @Redactable(useMask = true) - StixInstant getValidFrom(); - - @JsonProperty("valid_until") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The time at which this indicator should no longer be considered valuable intelligence.") - @Redactable - Optional getValidUntil(); - - @NotNull - @JsonProperty("kill_chain_phases") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The list of kill chain phases for which this attack pattern is used.") - @Redactable - Set getKillChainPhases(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/IntrusionSetSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/IntrusionSetSdo.java deleted file mode 100644 index 41ac603..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/IntrusionSetSdo.java +++ /dev/null @@ -1,94 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.AttackMotivations; -import io.digitalstate.stix.vocabulary.vocabularies.AttackResourceLevels; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * intrusion-set - *

    - * An Intrusion Set is a grouped set of adversary behavior and resources with common properties that is believed to be orchestrated by a single organization. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("intrusion-set") -@DefaultTypeValue(value = "intrusion-set", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = IntrusionSet.class) @JsonDeserialize(builder = IntrusionSet.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", "description", "aliases", - "first_seen", "last_seen", "goals", "resource_level", - "primary_motivation", "secondary_motivation"}) -@Redactable -public interface IntrusionSetSdo extends DomainObject { - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Intrusion Set.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Provides more context and details about the Intrusion Set object.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("aliases") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("Alternative names used to identify this Intrusion Set.") - @Redactable - Set getAliases(); - - @JsonProperty("first_seen") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The time that this Intrusion Set was first seen.") - @Redactable - Optional getFirstSeen(); - - @JsonProperty("last_seen") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The time that this Intrusion Set was last seen.") - @Redactable - Optional getLastSeen(); - - @NotNull - @JsonProperty("goals") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The high level goals of this Intrusion Set, namely, what are they trying to do.") - @Redactable - Set getGoals(); - - @JsonProperty("resource_level") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("This defines the organizational level at which this Intrusion Set typically works. Open Vocab - attack-resource-level-ov") - @Redactable - Optional<@Vocab(AttackResourceLevels.class) String> getResourceLevel(); - - @JsonProperty("primary_motivation") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The primary reason, motivation, or purpose behind this Intrusion Set. Open Vocab - attack-motivation-ov") - @Redactable - Optional<@Vocab(AttackMotivations.class) String> getPrimaryMotivation(); - - @NotNull - @Vocab(AttackMotivations.class) - @JsonProperty("secondary_motivations") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The secondary reasons, motivations, or purposes behind this Intrusion Set. Open Vocab - attack-motivation-ov") - @Redactable - Set getSecondaryMotivations(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/MalwareSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/MalwareSdo.java deleted file mode 100644 index c3672d8..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/MalwareSdo.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.types.KillChainPhaseType; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.MalwareLabels; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - - -/** - * malware - *

    - * Malware is a type of TTP that is also known as malicious code and malicious software, refers to a program that is inserted into a system, - * usually covertly, with the intent of compromising the confidentiality, integrity, or availability of the victim's data, applications, - * or operating system (OS) or of otherwise annoying or disrupting the victim. - */ -@Value.Immutable @Serial.Version(1L) -@DefaultTypeValue(value = "malware", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("malware") -@JsonSerialize(as = Malware.class) @JsonDeserialize(builder = Malware.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", "description", - "kill_chain_phases"}) -@Redactable -public interface MalwareSdo extends DomainObject { - - @Override - @Vocab(MalwareLabels.class) - @JsonPropertyDescription("The type of malware being described. Open Vocab - malware-label-ov") - @NotNull @Size(min = 1, message = "At least one label from malware-label-ov must be used") - @Redactable(useMask = true) - Set<@Length(min = 1) String> getLabels(); - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Malware.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Provides more context and details about the Malware object.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("kill_chain_phases") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The list of kill chain phases for which this Malware instance can be used.") - @Redactable - Set getKillChainPhases(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/ObservedDataSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/ObservedDataSdo.java deleted file mode 100644 index 1a3169d..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/ObservedDataSdo.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.coo.json.observables.CyberObservableSetFieldDeserializer; -import io.digitalstate.stix.coo.json.observables.CyberObservableSetFieldSerializer; -import io.digitalstate.stix.json.StixInstantDeserializer; -import io.digitalstate.stix.json.StixInstantSerializer; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.hibernate.validator.constraints.Range; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.Size; -import java.time.Instant; -import java.util.Set; -/** - * observed-data - *

    - * Observed data conveys information that was observed on systems and networks, such as log data or network traffic, using the Cyber Observable specification. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("observed-data") -@DefaultTypeValue(value = "observed-data", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = ObservedData.class) @JsonDeserialize(builder = ObservedData.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "first_observed", "last_observed", - "number_observed", "objects"}) -@Redactable -public interface ObservedDataSdo extends DomainObject { - - @NotNull - @JsonProperty("first_observed") - @JsonPropertyDescription("The beginning of the time window that the data was observed during.") - @Redactable(useMask = true) - StixInstant getFirstObserved(); - - @NotNull - @JsonProperty("last_observed") - @JsonPropertyDescription("The end of the time window that the data was observed during.") - @Redactable(useMask = true) - StixInstant getLastObserved(); - - @NotNull @Positive - @JsonProperty("number_observed") - @JsonPropertyDescription("The number of times the data represented in the objects property was observed. This MUST be an integer between 1 and 999,999,999 inclusive.") - @Redactable(useMask = true) - @Range(min = 1, max = 999999999) - Integer getNumberObserved(); - - @NotNull @Size(min = 1, message = "At least one Cyber Observable Reference must be provided") - @JsonProperty("objects") - @JsonPropertyDescription("A dictionary of Cyber Observable Objects that describes the single 'fact' that was observed.") - @Redactable(useMask = true) - @JsonSerialize(using = CyberObservableSetFieldSerializer.class) - @JsonDeserialize(using = CyberObservableSetFieldDeserializer.class) - Set getObjects(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/ReportSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/ReportSdo.java deleted file mode 100644 index 4edf9a5..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/ReportSdo.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.bundle.BundleableObject; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.json.StixInstantDeserializer; -import io.digitalstate.stix.json.StixInstantSerializer; -import io.digitalstate.stix.json.converters.dehydrated.BundleableObjectSetConverter; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.ReportLabels; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * report - *

    - * Reports are collections of threat intelligence focused on one or more topics, such as a - * description of a threat actor, malware, or attack technique, including context and related details. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("report") -@DefaultTypeValue(value = "report", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Report.class) @JsonDeserialize(builder = Report.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", "description", - "published", "object_refs"}) -@Redactable -public interface ReportSdo extends DomainObject { - - @Override - @NotNull - @JsonPropertyDescription("This field is an Open Vocabulary that specifies the primary subject of this report. The suggested values for this field are in report-label-ov.") - @Redactable(useMask = true) - @Size(min = 1) - Set<@Vocab(ReportLabels.class) String> getLabels(); - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("A description that provides more details and context about Report.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about Report.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("published") - @JsonPropertyDescription("The date that this report object was officially published by the creator of this report.") - @Redactable(useMask = true) - StixInstant getPublished(); - - @NotNull @Size(min = 1, message = "Must have at least one Report object reference") - @JsonProperty("object_refs") - @JsonPropertyDescription("Specifies the STIX Objects that are referred to by this Report.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize( converter = BundleableObjectSetConverter.class) - @Redactable(useMask = true) - Set getObjectRefs(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/ThreatActorSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/ThreatActorSdo.java deleted file mode 100644 index 8109b0e..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/ThreatActorSdo.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.*; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * threat-actor - *

    - * Threat Actors are actual individuals, groups, or organizations believed to be operating with malicious intent. - * - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true, depluralizeDictionary = {"alias:aliases"}) -@JsonTypeName("threat-actor") -@DefaultTypeValue(value = "threat-actor", groups = {DefaultValuesProcessor.class}) -@JsonSerialize(as = ThreatActor.class) @JsonDeserialize(builder = ThreatActor.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "uses", "name", - "description", "aliases", "roles", "goals", "sophistication", - "resource_level", "primary_motivation", "secondary_motivation", "personal_motivations"}) -@Redactable -public interface ThreatActorSdo extends DomainObject { - - @Override - @NotNull @Size(min = 1, message = "Must have at least one value from threat-actor-label-ov") - @Vocab(ThreatActorLabels.class) - @JsonPropertyDescription("This field specifies the type of threat actor. Open Vocab - threat-actor-label-ov") - @Redactable(useMask = true) - Set<@Size(min = 1) String> getLabels(); - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("A name used to identify this Threat Actor or Threat Actor group.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that provides more details and context about the Threat Actor.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("aliases") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("A list of other names that this Threat Actor is believed to use.") - @Redactable - Set getAliases(); - - @NotNull - @Vocab(ThreatActorRoles.class) - @JsonProperty("roles") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("This is a list of roles the Threat Actor plays. Open Vocab - threat-actor-role-ov") - @Redactable - Set getRoles(); - - @NotNull - @JsonProperty("goals") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The high level goals of this Threat Actor, namely, what are they trying to do.") - @Redactable - Set<@Size(min = 1) String> getGoals(); - - @NotNull - @JsonProperty("sophistication") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The skill, specific knowledge, special training, or expertise a Threat Actor must have to perform the attack. Open Vocab - threat-actor-sophistication-ov") - @Redactable - Optional<@Vocab(ThreatActorSophistication.class) String> getSophistication(); - - @JsonProperty("resource_level") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("This defines the organizational level at which this Threat Actor typically works. Open Vocab - attack-resource-level-ov") - @Redactable - Optional<@Vocab(AttackResourceLevels.class) String> getResourceLevel(); - - @JsonProperty("primary_motivation") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The primary reason, motivation, or purpose behind this Threat Actor. Open Vocab - attack-motivation-ov") - @Redactable - Optional<@Vocab(AttackMotivations.class) String> getPrimaryMotivation(); - - @NotNull - @Vocab(AttackMotivations.class) - @JsonProperty("secondary_motivations") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The secondary reasons, motivations, or purposes behind this Threat Actor. Open Vocab - attack-motivation-ov") - @Redactable - Set getSecondaryMotivations(); - - @NotNull - @Vocab(AttackMotivations.class) - @JsonProperty("personal_motivations") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The personal reasons, motivations, or purposes of the Threat Actor regardless of organizational goals. Open Vocab - attack-motivation-ov") - @Redactable - Set getPersonalMotivations(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/ToolSdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/ToolSdo.java deleted file mode 100644 index 4866d36..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/ToolSdo.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.types.KillChainPhaseType; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.ToolLabels; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * tool - *

    - * Tools are legitimate software that can be used by threat actors to perform attacks. - * This SDO MUST NOT be used to characterize malware. - * Further, Tool MUST NOT be used to characterise tools used as part of a course of action in response to an attack. - * - */ -@Value.Immutable @Serial.Version(1L) -@JsonTypeName("tool") -@DefaultTypeValue(value = "tool", groups = {DefaultValuesProcessor.class}) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonSerialize(as = Tool.class) @JsonDeserialize(builder = Tool.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", - "description", "kill_chain_phases", "tool_version"}) -@Redactable -public interface ToolSdo extends DomainObject { - - @Override - @NotNull - @Vocab(ToolLabels.class) - @JsonPropertyDescription("The kind(s) of tool(s) being described. Open Vocab - tool-label-ov") - @Redactable(useMask = true) - Set<@Length(min = 1) String> getLabels(); - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Tool.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Provides more context and details about the Tool object.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("kill_chain_phases") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The list of kill chain phases for which this Tool instance can be used.") - @Redactable - Set getKillChainPhases(); - - @JsonProperty("tool_version") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The version identifier associated with the tool.") - @Redactable - Optional getToolVersion(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/objects/VulnerabilitySdo.java b/src/main/java/io/digitalstate/stix/sdo/objects/VulnerabilitySdo.java deleted file mode 100644 index 7e4b507..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/objects/VulnerabilitySdo.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.digitalstate.stix.sdo.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * vulnerability - *

    - * A Vulnerability is a mistake in software that can be directly used by a hacker to gain access to a system or network. - * - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Sdo", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@JsonTypeName("vulnerability") -@DefaultTypeValue(value = "vulnerability", groups = {DefaultValuesProcessor.class}) -@JsonSerialize(as = Vulnerability.class) @JsonDeserialize(builder = Vulnerability.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "name", "description"}) -@Redactable -public interface VulnerabilitySdo extends DomainObject { - - @NotBlank - @JsonProperty("name") - @JsonPropertyDescription("The name used to identify the Vulnerability.") - @Redactable(useMask = true) - String getName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Provides more context and details about the Vulnerability.") - @Redactable - Optional getDescription(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/types/ExternalReferenceType.java b/src/main/java/io/digitalstate/stix/sdo/types/ExternalReferenceType.java deleted file mode 100644 index c505f2e..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/types/ExternalReferenceType.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.digitalstate.stix.sdo.types; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import io.digitalstate.stix.validation.contraints.hashingvocab.HashingVocab; -import io.digitalstate.stix.vocabulary.vocabularies.HashingAlgorithms; -import org.hibernate.validator.constraints.Length; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import java.io.Serializable; -import java.util.Map; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * external-reference - *

    - * External references are used to describe pointers to information represented outside of STIX. - * - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Type", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true, depluralizeDictionary = {"hash:hashes"}) -@JsonSerialize(as = ExternalReference.class) @JsonDeserialize(builder = ExternalReference.Builder.class) -@JsonPropertyOrder({ - "source_name", - "description", - "url", - "hashes", - "external_id" -}) -public interface ExternalReferenceType extends GenericValidation, StixCustomProperties, Serializable { - - @NotBlank - @JsonProperty("source_name") - @JsonPropertyDescription("The source within which the external-reference is defined (system, registry, organization, etc.)") - String getSourceName(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A human readable description") - Optional getDescription(); - - @JsonProperty("url") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("Matches the elements of a URL using a regular expression. Uses Diego Perini's regex from https://gist.github.com/dperini/729294.") - Optional getUrl(); - - @JsonProperty("hashes") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("Specifies a dictionary of hashes for the file.") - Map<@Length(min = 3, max = 256) @HashingVocab(HashingAlgorithms.class) String, String> getHashes(); - - @JsonProperty("external_id") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("An identifier for the external reference content") - Optional getExternalId(); - -} diff --git a/src/main/java/io/digitalstate/stix/sdo/types/KillChainPhaseType.java b/src/main/java/io/digitalstate/stix/sdo/types/KillChainPhaseType.java deleted file mode 100644 index 9e56221..0000000 --- a/src/main/java/io/digitalstate/stix/sdo/types/KillChainPhaseType.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.digitalstate.stix.sdo.types; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonValue; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixCustomProperties; -import io.digitalstate.stix.validation.GenericValidation; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import java.io.Serializable; - -/** - * kill-chain-phase - *

    - * The kill-chain-phase represents a phase in a kill chain. - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Type", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, depluralize = true) -@JsonSerialize(as = KillChainPhase.class) @JsonDeserialize(builder = KillChainPhase.Builder.class) -@JsonPropertyOrder({ - "kill_chain_name", - "phase_name" -}) -public interface KillChainPhaseType extends GenericValidation, StixCustomProperties, Serializable { - - @NotBlank - @JsonProperty("kill_chain_name") - @JsonPropertyDescription("The name of the kill chain.") - String killChainName(); - - @NotBlank - @JsonProperty("phase_name") - @JsonPropertyDescription("The name of the phase in the kill chain.") - String phaseName(); - - - /** - * Create an Enumeration of the most common one: Lockheed-Martin - */ - //@TODO Convert to Vocab pattern - public enum LockheedMartinKillChain { - RECONNAISSANCE("reconnaissance"), - WEAPONIZATION("weaponization"), - DELIVERY("delivery"), - EXPLOITATION("exploitation"), - INSTALLATION("installation"), - COMMAND_AND_CONTROL("command-and-control"), - ACTIONS_ON_OBJECTIVE("actions-on-objective"); - - public static final String killChainName = "lockheed-martin-cyber-kill-chain"; - - String phase; - - LockheedMartinKillChain(String val) { - this.phase = val; - } - - @Override - public String toString() {return phase;} - - @JsonValue - public String getPhase() { return phase; } - } - -} diff --git a/src/main/java/io/digitalstate/stix/sro/RelationshipObject.java b/src/main/java/io/digitalstate/stix/sro/RelationshipObject.java deleted file mode 100644 index aad7aa4..0000000 --- a/src/main/java/io/digitalstate/stix/sro/RelationshipObject.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.digitalstate.stix.sro; - -import io.digitalstate.stix.common.*; - -import java.io.Serializable; - -public interface RelationshipObject extends Serializable, - StixCommonProperties, - StixCustomProperties, - StixLabels, - StixModified, - StixRevoked { - -} diff --git a/src/main/java/io/digitalstate/stix/sro/objects/RelationshipSro.java b/src/main/java/io/digitalstate/stix/sro/objects/RelationshipSro.java deleted file mode 100644 index 1280359..0000000 --- a/src/main/java/io/digitalstate/stix/sro/objects/RelationshipSro.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.digitalstate.stix.sro.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.json.converters.dehydrated.DomainObjectConverter; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.objects.*; -import io.digitalstate.stix.sro.RelationshipObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.contraints.relationship.RelationshipLimit; -import io.digitalstate.stix.validation.contraints.relationship.RelationshipTypeLimit; -import io.digitalstate.stix.validation.contraints.vocab.Vocab; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.vocabulary.vocabularies.RelationshipTypes; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.util.Optional; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * relationship - *

    - * The Relationship object is used to link together two SDOs in order to describe how they are related to each other. - * - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Sro", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@DefaultTypeValue(value = "relationship", groups = {DefaultValuesProcessor.class}) -@JsonTypeName("relationship") -@JsonSerialize(as = Relationship.class) @JsonDeserialize(builder = Relationship.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "relationship_type", "description", - "source_ref", "target_ref"}) -@Redactable -//@TODO Refactor RelationshipTypeLimit and RelationshipLimit as they are partially redundant -@RelationshipTypeLimit(source = AttackPatternSdo.class, relationshipTypes = {"targets", "uses"}) -@RelationshipTypeLimit(source = CampaignSdo.class, relationshipTypes = {"attributed-to", "targets", "uses"}) -@RelationshipTypeLimit(source = CourseOfActionSdo.class, relationshipTypes = {"mitigates"}) -@RelationshipTypeLimit(source = IdentitySdo.class, relationshipTypes = {"targets", "attributed-to", "impersonates"}) -@RelationshipTypeLimit(source = IndicatorSdo.class, relationshipTypes = {"indicates"}) -@RelationshipTypeLimit(source = IntrusionSetSdo.class, relationshipTypes = {"attributed-to", "targets", "uses"}) -@RelationshipTypeLimit(source = MalwareSdo.class, relationshipTypes = {"targets", "uses", "variant-of"}) -@RelationshipTypeLimit(source = ThreatActorSdo.class, relationshipTypes = {"attributed-to", "impersonates", "targets", "uses"}) -@RelationshipTypeLimit(source = ToolSdo.class, relationshipTypes = {"targets"}) -@RelationshipLimit(source = DomainObject.class, relationshipType = "derived-from", target = {DomainObject.class}, classEquality = true) -@RelationshipLimit(source = DomainObject.class, relationshipType = "duplicate-of", target = {DomainObject.class}, classEquality = true) -@RelationshipLimit(source = DomainObject.class, relationshipType = "related-to", target = {DomainObject.class}) -@RelationshipLimit(source = AttackPatternSdo.class, relationshipType = "targets", target = {IdentitySdo.class, VulnerabilitySdo.class}) -@RelationshipLimit(source = AttackPatternSdo.class, relationshipType = "uses", target = {MalwareSdo.class, ToolSdo.class}) -@RelationshipLimit(source = CampaignSdo.class, relationshipType = "attributed-to", target = {IntrusionSetSdo.class, ThreatActorSdo.class}) -@RelationshipLimit(source = CampaignSdo.class, relationshipType = "targets", target = {IdentitySdo.class, VulnerabilitySdo.class}) -@RelationshipLimit(source = CampaignSdo.class, relationshipType = "uses", target = {AttackPatternSdo.class, MalwareSdo.class, ToolSdo.class}) -@RelationshipLimit(source = CourseOfActionSdo.class, relationshipType = "mitigates", target = {AttackPatternSdo.class, MalwareSdo.class, ToolSdo.class, VulnerabilitySdo.class}) -@RelationshipLimit(source = IndicatorSdo.class, relationshipType = "indicates", target = {AttackPatternSdo.class, CampaignSdo.class, IntrusionSetSdo.class, MalwareSdo.class, ThreatActorSdo.class, ToolSdo.class}) -@RelationshipLimit(source = IntrusionSetSdo.class, relationshipType = "attributed-to", target = {ThreatActorSdo.class}) -@RelationshipLimit(source = IntrusionSetSdo.class, relationshipType = "targets", target = {IdentitySdo.class, VulnerabilitySdo.class}) -@RelationshipLimit(source = IntrusionSetSdo.class, relationshipType = "uses", target = {AttackPatternSdo.class, MalwareSdo.class, ToolSdo.class}) -@RelationshipLimit(source = MalwareSdo.class, relationshipType = "targets", target = {IdentitySdo.class, VulnerabilitySdo.class}) -@RelationshipLimit(source = MalwareSdo.class, relationshipType = "uses", target = {ToolSdo.class}) -@RelationshipLimit(source = MalwareSdo.class, relationshipType = "variant-of", target = {MalwareSdo.class}) -@RelationshipLimit(source = ThreatActorSdo.class, relationshipType = "attributed-to", target = {IdentitySdo.class}) -@RelationshipLimit(source = ThreatActorSdo.class, relationshipType = "impersonates", target = {IdentitySdo.class}) -@RelationshipLimit(source = ThreatActorSdo.class, relationshipType = "targets", target = {IdentitySdo.class, VulnerabilitySdo.class}) -@RelationshipLimit(source = ThreatActorSdo.class, relationshipType = "uses", target = {AttackPatternSdo.class, MalwareSdo.class, ToolSdo.class}) -@RelationshipLimit(source = ToolSdo.class, relationshipType = "targets", target = {IdentitySdo.class, VulnerabilitySdo.class}) -public interface RelationshipSro extends RelationshipObject { - - @NotBlank - @Vocab(RelationshipTypes.class) - @JsonProperty("relationship_type") - @JsonPropertyDescription("The name used to identify the type of relationship.") - @Redactable(useMask = true) - String getRelationshipType(); - - @JsonProperty("description") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("A description that helps provide context about the relationship.") - @Redactable - Optional getDescription(); - - @NotNull - @JsonProperty("source_ref") - @JsonPropertyDescription("The ID of the source (from) object.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = DomainObjectConverter.class) - @Redactable(useMask = true) - DomainObject getSourceRef(); - - @NotNull - @JsonProperty("target_ref") - @JsonPropertyDescription("The ID of the target (to) object.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = DomainObjectConverter.class) - @Redactable(useMask = true) - DomainObject getTargetRef(); - -} diff --git a/src/main/java/io/digitalstate/stix/sro/objects/SightingSro.java b/src/main/java/io/digitalstate/stix/sro/objects/SightingSro.java deleted file mode 100644 index 20aac6a..0000000 --- a/src/main/java/io/digitalstate/stix/sro/objects/SightingSro.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.digitalstate.stix.sro.objects; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.digitalstate.stix.common.StixBoolean; -import io.digitalstate.stix.common.StixInstant; -import io.digitalstate.stix.json.converters.dehydrated.DomainObjectConverter; -import io.digitalstate.stix.redaction.Redactable; -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sdo.objects.IdentitySdo; -import io.digitalstate.stix.sdo.objects.ObservedDataSdo; -import io.digitalstate.stix.sro.RelationshipObject; -import io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue; -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import org.hibernate.validator.constraints.Range; -import org.immutables.serial.Serial; -import org.immutables.value.Value; - -import javax.validation.constraints.NotNull; -import java.time.Instant; -import java.util.Optional; -import java.util.Set; - -import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; - -/** - * sighting - *

    - * A Sighting denotes the belief that something in CTI (e.g., an indicator, malware, tool, threat actor, etc.) was seen. - * - */ -@Value.Immutable @Serial.Version(1L) -@Value.Style(typeAbstract="*Sro", typeImmutable="*", validationMethod = Value.Style.ValidationMethod.NONE, additionalJsonAnnotations = {JsonTypeName.class}, depluralize = true) -@DefaultTypeValue(value = "sighting", groups = {DefaultValuesProcessor.class}) -@JsonTypeName("sighting") -@JsonSerialize(as = Sighting.class) @JsonDeserialize(builder = Sighting.Builder.class) -@JsonPropertyOrder({"type", "id", "created_by_ref", "created", - "modified", "revoked", "labels", "external_references", - "object_marking_refs", "granular_markings", "first_seen", "last_seen", - "count", "sighting_of_ref", "observed_data_refs", "where_sighted_refs", - "summary"}) -@Redactable -public interface SightingSro extends RelationshipObject { - - @JsonProperty("first_seen") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The beginning of the time window during which the SDO referenced by the sighting_of_ref property was sighted.") - @Redactable - Optional getFirstSeen(); - - @JsonProperty("last_seen") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The end of the time window during which the SDO referenced by the sighting_of_ref property was sighted.") - @Redactable - Optional getLastSeen(); - - @JsonProperty("count") @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("This is an integer between 0 and 999,999,999 inclusive and represents the number of times the object was sighted.") - @Redactable - Optional<@Range(min = 0, max = 999999999) Integer> getCount(); - - @JsonProperty("sighting_of_ref") - @JsonPropertyDescription("An ID reference to the object that has been sighted.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(converter = DomainObjectConverter.class) - @Redactable(useMask = true) - DomainObject getSightingOfRef(); - - @JsonProperty("observed_data_refs") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("A list of ID references to the Observed Data objects that contain the raw cyber data for this Sighting.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(contentConverter = DomainObjectConverter.class) - @Redactable - Set getObservedDataRefs(); - - @JsonProperty("where_sighted_refs") @JsonInclude(NON_EMPTY) - @JsonPropertyDescription("The ID of the Victim Target objects of the entities that saw the sighting.") - @JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id") - @JsonIdentityReference(alwaysAsId=true) - @JsonDeserialize(contentConverter = DomainObjectConverter.class) - @Redactable - Set getWhereSightedRefs(); - - @NotNull - @JsonProperty("summary") - @JsonInclude(value = NON_EMPTY, content= NON_EMPTY) - @JsonPropertyDescription("The summary property indicates whether the Sighting should be considered summary data.") - @Redactable - @Value.Default - default StixBoolean isSummary(){ - return new StixBoolean(); - } - -} diff --git a/src/main/java/io/digitalstate/stix/validation/GenericValidation.java b/src/main/java/io/digitalstate/stix/validation/GenericValidation.java deleted file mode 100644 index daff30b..0000000 --- a/src/main/java/io/digitalstate/stix/validation/GenericValidation.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.digitalstate.stix.validation; - -import org.immutables.value.Value; - -public interface GenericValidation extends SdoDefaultValidator { - - @Value.Check - default public void validateEntity(){ - this.validate(); - } - -} diff --git a/src/main/java/io/digitalstate/stix/validation/SdoDefaultValidator.java b/src/main/java/io/digitalstate/stix/validation/SdoDefaultValidator.java deleted file mode 100644 index c067fc0..0000000 --- a/src/main/java/io/digitalstate/stix/validation/SdoDefaultValidator.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.digitalstate.stix.validation; - -import io.digitalstate.stix.validation.sequences.SequenceDefault; -import io.digitalstate.stix.validation.sequences.SequenceValidationIdOnly; - -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.Set; - -public interface SdoDefaultValidator { - - Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); - - default void validate() throws ConstraintViolationException{ - Set> violations = VALIDATOR.validate(this, SequenceDefault.class); - if (!violations.isEmpty()) { - throw new ConstraintViolationException(violations); - } - } - - default void validateOnlyId() throws ConstraintViolationException{ - Set> violations = VALIDATOR.validate(this, SequenceValidationIdOnly.class); - if (!violations.isEmpty()) { - throw new ConstraintViolationException(violations); - } - } -} - - - - diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/businessrule/BusinessRule.java b/src/main/java/io/digitalstate/stix/validation/contraints/businessrule/BusinessRule.java deleted file mode 100644 index 1271f6c..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/businessrule/BusinessRule.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.digitalstate.stix.validation.contraints.businessrule; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -/** - * The interface Business rule. - */ -@Documented -@Constraint(validatedBy = {StixValidateBusinessRuleValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(BusinessRule.List.class) -public @interface BusinessRule { - - /** - * Message string. Not Used. The Error Message is used instead. - * - * @return the string - */ - String message() default "An business rule failed to validate"; - - /** - * Groups class [ ]. - * - * @return the class [ ] - */ - Class[] groups() default {}; - - /** - * Payload class [ ]. - * - * @return the class [ ] - */ - Class[] payload() default {}; - - /** - * If exp string. - * - * @return the string - */ - String ifExp(); - - /** - * Then exp string. - * - * @return the string - */ -//@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 - String thenExp(); - - /** - * Error message string. - * - * @return the string - */ - String errorMessage(); - - /** - * Expected result boolean. - * - * @return the boolean - */ - boolean expectedResult() default true; - - /** - * The interface List. - */ - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target( { ANNOTATION_TYPE, TYPE }) - @interface List { - /** - * Value business rule [ ]. - * - * @return the business rule [ ] - */ - BusinessRule[] value(); - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/businessrule/StixValidateBusinessRuleValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/businessrule/StixValidateBusinessRuleValidator.java deleted file mode 100644 index 9f36204..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/businessrule/StixValidateBusinessRuleValidator.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.digitalstate.stix.validation.contraints.businessrule; - -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.SpelCompilerMode; -import org.springframework.expression.spel.SpelParserConfiguration; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Optional; - - -public class StixValidateBusinessRuleValidator implements ConstraintValidator { - - private String ifExp; - private String thenExp; - private String errorMessage; - private boolean expectedResult; - - //@TODO review compilation settings and how to optimize this - private SpelParserConfiguration spelConfig = new SpelParserConfiguration(SpelCompilerMode.MIXED, Thread.currentThread().getContextClassLoader()); - private ExpressionParser parser = new SpelExpressionParser(spelConfig); - - @Override - public void initialize(BusinessRule constraintAnnotation) { - ifExp = constraintAnnotation.ifExp(); - thenExp = constraintAnnotation.thenExp(); - errorMessage = constraintAnnotation.errorMessage(); - expectedResult = constraintAnnotation.expectedResult(); - } - - @Override - public boolean isValid(Object value, ConstraintValidatorContext cxt) { - - StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); - evaluationContext.setRootObject(value); - - Expression evalIf = parser.parseExpression(ifExp); - boolean evalIfResult = Optional.ofNullable(evalIf.getValue(evaluationContext, Boolean.class)) - .orElseThrow(() -> new IllegalArgumentException("Unable to parse business rule's ifExp")); - - if (!evalIfResult) { - // If the if statement is false then no further eval is required as the rule does not apply - return true; - - } else { - // If the business rule applies then: - Expression evalThen = parser.parseExpression(thenExp); - boolean evalThenResult = Optional.ofNullable(evalThen.getValue(evaluationContext, Boolean.class)) - .orElseThrow(() -> new IllegalArgumentException("Unable to parse business rule's thenExp")); - - if (evalThenResult == expectedResult) { - return true; - - } else { - String violationMessage = errorMessage; - cxt.disableDefaultConstraintViolation(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/AllowedParents.java b/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/AllowedParents.java deleted file mode 100644 index 966ad14..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/AllowedParents.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.digitalstate.stix.validation.contraints.coo.allowedparents; - -import io.digitalstate.stix.coo.CyberObservableObject; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -/** - * Values must be Array of Cyber Observable Object Interfaces!. - */ -@Documented -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface AllowedParents { - Class[] value(); -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/StixValidateParentCooValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/StixValidateParentCooValidator.java deleted file mode 100644 index 539ea27..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/StixValidateParentCooValidator.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.digitalstate.stix.validation.contraints.coo.allowedparents; - -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.Optional; - -public class StixValidateParentCooValidator implements ConstraintValidator { - - //@TODO ADD LOGGING!!! - @Override - public boolean isValid(CyberObservableObject cyberObservableObject, ConstraintValidatorContext cxt) { - if (cyberObservableObject.getExtensions().size() >= 1) { - for (CyberObservableExtension ext : cyberObservableObject.getExtensions()) { - Optional annotation = Optional.ofNullable(ext.getClass().getDeclaredAnnotation(AllowedParents.class)); - - if (annotation.isPresent()) { -// System.out.println("FOUND"); - Class[] values = annotation.get().value(); - - if (values.length >= 1) { - boolean hasAllowedParent = Arrays.stream(values).anyMatch(i-> - i.isAssignableFrom(cyberObservableObject.getClass())); - - if (hasAllowedParent){ -// System.out.println("Class is assignable from Allowed-Parents interface list"); - return true; - } else { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Class attempting to use extension is not part of allowedParents interface list found on the Extension. Calling Cyber Observable Class: " + - ext.getClass().getCanonicalName() + " and Extension only supports interfaces: " + Arrays.toString(values); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - - } else { -// System.out.println("No classes defined"); - return true; - } - } else { -// System.out.println("NOT FOUND"); - return true; - } - } - } else { -// System.out.println("No Extensions"); - return true; - } -// System.out.println("nothing to do"); - return true; - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/ValidateExtensions.java b/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/ValidateExtensions.java deleted file mode 100644 index 04598c5..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/coo/allowedparents/ValidateExtensions.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.digitalstate.stix.validation.contraints.coo.allowedparents; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -/** - * Used to Validation Cyber Observable Extensions - * Should only be placed on Cyber Observable Object classes. - */ -@Documented -@Constraint(validatedBy = {StixValidateParentCooValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ValidateExtensions { - String message() default "{io.digitalstate.stix.validation.contraints.coo.allowedparents.ValidateReferences}"; - Class[] groups() default {}; - Class[] payload() default {}; -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/StixValidateParentCooValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/StixValidateParentCooValidator.java deleted file mode 100644 index 3be91c3..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/StixValidateParentCooValidator.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.digitalstate.stix.validation.contraints.coo.validateextensions; - -import io.digitalstate.stix.coo.CyberObservableObject; -import io.digitalstate.stix.coo.extension.CyberObservableExtension; -import io.digitalstate.stix.validation.contraints.coo.allowedparents.AllowedParents; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.Optional; - -public class StixValidateParentCooValidator implements ConstraintValidator { - - //@TODO ADD LOGGING!!! - @Override - public boolean isValid(CyberObservableObject cyberObservableObject, ConstraintValidatorContext cxt) { - if (cyberObservableObject.getExtensions().size() >= 1) { - for (CyberObservableExtension ext : cyberObservableObject.getExtensions()) { - Optional annotation = Optional.ofNullable(ext.getClass().getDeclaredAnnotation(AllowedParents.class)); - - if (annotation.isPresent()) { -// System.out.println("FOUND"); - Class[] values = annotation.get().value(); - - if (values.length >= 1) { - boolean hasAllowedParent = Arrays.stream(values).anyMatch(i-> - i.isAssignableFrom(cyberObservableObject.getClass())); - - if (hasAllowedParent){ -// System.out.println("Class is assignable from Allowed-Parents interface list"); - return true; - } else { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Class attempting to use extension is not part of allowedParents interface list found on the Extension. Calling Cyber Observable Class: " + - ext.getClass().getCanonicalName() + " and Extension only supports interfaces: " + Arrays.toString(values); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - - } else { -// System.out.println("No classes defined"); - return true; - } - } else { -// System.out.println("NOT FOUND"); - return true; - } - } - } else { -// System.out.println("No Extensions"); - return true; - } -// System.out.println("nothing to do"); - return true; - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/ValidateReferences.java b/src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/ValidateReferences.java deleted file mode 100644 index 5aa610b..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/coo/validateextensions/ValidateReferences.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.digitalstate.stix.validation.contraints.coo.validateextensions; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -/** - * Used to Validation Cyber Observable Extensions - * Should only be placed on Cyber Observable Object classes. - * 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. - */ -@Documented -@Constraint(validatedBy = {StixValidateParentCooValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ValidateReferences { - String message() default "{io.digitalstate.stix.validation.contraints.coo.allowedparents.ValidateReferences}"; - Class[] groups() default {}; - Class[] payload() default {}; -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/DefaultTypeValue.java b/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/DefaultTypeValue.java deleted file mode 100644 index d66daff..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/DefaultTypeValue.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.digitalstate.stix.validation.contraints.defaulttypevalue; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - - -/** - *

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

    - *
    - *

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

    - */ -@Documented -@Constraint(validatedBy = {StixDefaultTypeValueBundleableValidator.class, StixDefaultTypeValueBundleObjectValidator.class, StixDefaultTypeValueCyberObservableValidator.class, StixDefaultTypeValueCyberObservableExtensionValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface DefaultTypeValue { - String message() default "{io.digitalstate.stix.validation.contraints.defaulttypevalue.DefaultTypeValue}"; - Class[] groups() default {}; - Class[] payload() default {}; - - String value(); - -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleObjectValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleObjectValidator.java deleted file mode 100644 index 13e667d..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleObjectValidator.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.digitalstate.stix.validation.contraints.defaulttypevalue; - -import io.digitalstate.stix.bundle.BundleObject; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.lang.reflect.Field; -import java.util.UUID; - -/** - * This is used solely for a STIX Bundle. - */ -public class StixDefaultTypeValueBundleObjectValidator implements ConstraintValidator { - - private String defaultTypeValue; - - @Override - public void initialize(DefaultTypeValue relationshipTypeLimitConstraint) { - defaultTypeValue = relationshipTypeLimitConstraint.value(); - } - - @Override - public boolean isValid(BundleObject bundleObject, - ConstraintValidatorContext cxt) { - - String type = bundleObject.getType(); - if (type == null || type.isEmpty()){ - try { - Field typeField = bundleObject.getClass().getDeclaredField("type"); - typeField.setAccessible(true); - typeField.set(bundleObject, defaultTypeValue); - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'type' for: " + bundleObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'type' for: " + bundleObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - if (bundleObject.getType().equals(defaultTypeValue)){ - return true; - } else{ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Field 'type' must have value of " + defaultTypeValue + "for class " + bundleObject.getClass().getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - - String id = bundleObject.getId(); - if (id == null || id.isEmpty()){ - try { - Field idField = bundleObject.getClass().getDeclaredField("id"); - idField.setAccessible(true); - String idValue = String.join("--", defaultTypeValue, UUID.randomUUID().toString()); - idField.set(bundleObject, idValue); - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'id' for: " + bundleObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'id' for: " + bundleObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - boolean idStartsWithType = id.startsWith(defaultTypeValue + "--"); - if (!idStartsWithType){ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Id does not start with the proper type value(Looking for '" + defaultTypeValue + "--' : provided 'id' value was: " + id; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - return true; - } -} - diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleableValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleableValidator.java deleted file mode 100644 index a984f41..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueBundleableValidator.java +++ /dev/null @@ -1,94 +0,0 @@ -package io.digitalstate.stix.validation.contraints.defaulttypevalue; - -import io.digitalstate.stix.bundle.BundleableObject; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.lang.reflect.Field; -import java.util.UUID; - -/** - * This is used on any class that implements BundleableObject. - */ -public class StixDefaultTypeValueBundleableValidator implements ConstraintValidator { - - private String defaultTypeValue; - - @Override - public void initialize(DefaultTypeValue relationshipTypeLimitConstraint) { - defaultTypeValue = relationshipTypeLimitConstraint.value(); - } - - @Override - public boolean isValid(BundleableObject bundleableObject, - ConstraintValidatorContext cxt) { - - String type = bundleableObject.getType(); - if (type == null || type.isEmpty()){ - try { - Field typeField = bundleableObject.getClass().getDeclaredField("type"); - typeField.setAccessible(true); - typeField.set(bundleableObject, defaultTypeValue); - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'type' for: " + bundleableObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'type' for: " + bundleableObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - if (bundleableObject.getType().equals(defaultTypeValue)){ - return true; - } else{ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Field 'type' must have value of " + defaultTypeValue + "for class " + bundleableObject.getClass().getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - - // Validate the ID attribute - String id = bundleableObject.getId(); - if (id == null || id.isEmpty()){ - try { - Field idField = bundleableObject.getClass().getDeclaredField("id"); - idField.setAccessible(true); - String idValue = String.join("--", defaultTypeValue, UUID.randomUUID().toString()); - idField.set(bundleableObject, idValue); - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'id' for: " + bundleableObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'id' for: " + bundleableObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - boolean idStartsWithType = id.startsWith(defaultTypeValue + "--"); - if (!idStartsWithType){ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Id does not start with the proper type value(Looking for '" + defaultTypeValue + "--' : provided 'id' value was: " + id; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - //@TODO add optional logic to enforce a style of UUID - } - - return true; - } -} - diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableExtensionValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableExtensionValidator.java deleted file mode 100644 index 2e5eff7..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableExtensionValidator.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.digitalstate.stix.validation.contraints.defaulttypevalue; - -import io.digitalstate.stix.coo.extension.CyberObservableExtension; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.lang.reflect.Field; - -/** - * This is used on any class that implements CyberObservableObject. - */ -public class StixDefaultTypeValueCyberObservableExtensionValidator implements ConstraintValidator { - - private String defaultTypeValue; - - @Override - public void initialize(DefaultTypeValue relationshipTypeLimitConstraint) { - defaultTypeValue = relationshipTypeLimitConstraint.value(); - } - - @Override - public boolean isValid(CyberObservableExtension cyberObservableExtension, - ConstraintValidatorContext cxt) { - - String type = cyberObservableExtension.getType(); - if (type == null || type.isEmpty()){ - try { - Field typeField = cyberObservableExtension.getClass().getDeclaredField("type"); - typeField.setAccessible(true); - typeField.set(cyberObservableExtension, defaultTypeValue); - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'type' for: " + cyberObservableExtension.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'type' for: " + cyberObservableExtension.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - if (cyberObservableExtension.getType().equals(defaultTypeValue)){ - return true; - } else{ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Field 'type' must have value of " + defaultTypeValue + "for class " + cyberObservableExtension.getClass().getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - - return true; - } -} - diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableValidator.java deleted file mode 100644 index 4e47d90..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/defaulttypevalue/StixDefaultTypeValueCyberObservableValidator.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.digitalstate.stix.validation.contraints.defaulttypevalue; - -import io.digitalstate.stix.coo.CyberObservableObject; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.lang.reflect.Field; - -/** - * This is used on any class that implements CyberObservableObject. - */ -public class StixDefaultTypeValueCyberObservableValidator implements ConstraintValidator { - - private String defaultTypeValue; - - @Override - public void initialize(DefaultTypeValue relationshipTypeLimitConstraint) { - defaultTypeValue = relationshipTypeLimitConstraint.value(); - } - - @Override - public boolean isValid(CyberObservableObject cyberObservableObject, - ConstraintValidatorContext cxt) { - String type = cyberObservableObject.getType(); - if (type == null || type.isEmpty()){ - try { - Field typeField = cyberObservableObject.getClass().getDeclaredField("type"); - typeField.setAccessible(true); - typeField.set(cyberObservableObject, defaultTypeValue); - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'type' for: " + cyberObservableObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'type' for: " + cyberObservableObject.getClass(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - if (cyberObservableObject.getType().equals(defaultTypeValue)){ - return true; - } else{ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Field 'type' must have value of " + defaultTypeValue + "for class " + cyberObservableObject.getClass().getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - return true; - } -} - diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/HashingVocab.java b/src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/HashingVocab.java deleted file mode 100644 index ca1b0af..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/HashingVocab.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.validation.contraints.hashingvocab; - -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE_USE; - -/** - *

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

    - */ -@Documented -@Constraint(validatedBy = {StixHashingVocabValidatorString.class}) -@Target( { ANNOTATION_TYPE, TYPE_USE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface HashingVocab { - String message() default "{io.digitalstate.stix.validation.contraints.hashingvocab}"; - Class[] groups() default {}; - Class[] payload() default {}; - - Class value(); - -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/StixHashingVocabValidatorString.java b/src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/StixHashingVocabValidatorString.java deleted file mode 100644 index 9eb227e..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/hashingvocab/StixHashingVocabValidatorString.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.digitalstate.stix.validation.contraints.hashingvocab; - -import com.google.common.collect.Sets; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public class StixHashingVocabValidatorString implements ConstraintValidator { - - private Class vocabulary; - - @Override - public void initialize(HashingVocab hashingVocabConstraint) { - vocabulary = hashingVocabConstraint.value(); - } - - @Override - public boolean isValid(String vocab, - ConstraintValidatorContext cxt) { - if (vocab.startsWith("x_")) { - return true; - } else { - try { - Set vocabTerms = vocabulary.newInstance().getAllTerms(); - boolean evalContains = vocabTerms.contains(vocab); - if (!evalContains) { - Sets.SetView difference = Sets.difference(new HashSet<>(Arrays.asList(vocab)), vocabTerms); - - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Item: " + difference.toString() + " is not found in class " + vocabulary.getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } else { - return true; - } - - } catch (InstantiationException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/MarkingDefinitionTypeLimit.java b/src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/MarkingDefinitionTypeLimit.java deleted file mode 100644 index 7aa3ee4..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/MarkingDefinitionTypeLimit.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.digitalstate.stix.validation.contraints.markingdefinitiontype; - -import io.digitalstate.stix.datamarkings.StixMarkingObject; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -/** - * To only be used on STIX MarkingDefinition class. - * The annotation provides a Javax Validation that enforces Marking Definition Types based on the actual definition being used. - * This annotation enforces the STIX Relationship Type restrictions for each SDO. - */ -@Documented -@Constraint(validatedBy = {StixMarkingDefinitionTypeLimitValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(MarkingDefinitionTypeLimit.List.class) -public @interface MarkingDefinitionTypeLimit { - String message() default "{io.digitalstate.stix.validation.contraints.markingdefinitiontype.MarkingDefinitionTypeLimit}"; - Class[] groups() default {}; - Class[] payload() default {}; - - Class markingObject(); - String markingDefinitionType(); - - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target( { ANNOTATION_TYPE, TYPE }) - @interface List { - MarkingDefinitionTypeLimit[] value(); - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/StixMarkingDefinitionTypeLimitValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/StixMarkingDefinitionTypeLimitValidator.java deleted file mode 100644 index 4fc8084..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/markingdefinitiontype/StixMarkingDefinitionTypeLimitValidator.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.digitalstate.stix.validation.contraints.markingdefinitiontype; - -import io.digitalstate.stix.datamarkings.MarkingDefinitionDm; -import io.digitalstate.stix.datamarkings.StixMarkingObject; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.lang.reflect.Field; - -public class StixMarkingDefinitionTypeLimitValidator implements ConstraintValidator { - - private Class markingObject; - private String markingDefinitionType; - - @Override - public void initialize(MarkingDefinitionTypeLimit markingDefinitionTypeLimitConstraint) { - markingObject = markingDefinitionTypeLimitConstraint.markingObject(); - markingDefinitionType = markingDefinitionTypeLimitConstraint.markingDefinitionType(); - } - - @Override - public boolean isValid(MarkingDefinitionDm markingDefinitionDm, - ConstraintValidatorContext cxt) { - - if (markingDefinitionDm.getHydrated()) { - // Throws error is the definition property is null. - // This is semi duplication of the notNull annotation on the attribute - if (markingDefinitionDm.getDefinition() == null) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Definition attribute of a Marking Definition cannot be null"; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - - // Default Value helper to populate the definition_type attribute based on the definition attribute's class - } else if (markingDefinitionDm.getDefinitionType() == null) { - if (markingObject.isAssignableFrom(markingDefinitionDm.getDefinition().getClass())) { - try { - Field typeField = markingDefinitionDm.getClass().getDeclaredField("definitionType"); - typeField.setAccessible(true); - typeField.set(markingDefinitionDm, markingDefinitionType); - - } catch (NoSuchFieldException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Cant find Field: 'definitionType' for: " + markingObject; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Illegal Access Exception for: 'definitionType' for: " + markingObject; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } - } - - // Checks if the definition and the definition type match as per the spec and class requirements - // Requirements are stated in the class level(type) of the Marking Definition. - if (markingObject.isAssignableFrom(markingDefinitionDm.getDefinition().getClass())) { - if (markingDefinitionDm.getDefinitionType().equals(markingDefinitionType)) { - return true; - } else { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Marking Definition Type is incorrect for Marking Object"; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } else { - return true; - } - } else { - // If the object is not hydrated, then this logic does not matter - return true; - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipLimit.java b/src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipLimit.java deleted file mode 100644 index 9d58746..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipLimit.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.digitalstate.stix.validation.contraints.relationship; - -import io.digitalstate.stix.sdo.DomainObject; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -@Documented -@Constraint(validatedBy = {StixRelationshipLimitValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(RelationshipLimit.List.class) -public @interface RelationshipLimit { - String message() default "{io.digitalstate.stix.validation.contraints.relationship.RelationshipLimit}"; - Class[] groups() default {}; - Class[] payload() default {}; - - Class source(); - String relationshipType(); - Class[] target(); - boolean classEquality() default false; - - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target( { ANNOTATION_TYPE, TYPE }) - @interface List { - RelationshipLimit[] value(); - } - -} \ No newline at end of file diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipTypeLimit.java b/src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipTypeLimit.java deleted file mode 100644 index 4788142..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/RelationshipTypeLimit.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.digitalstate.stix.validation.contraints.relationship; - -import io.digitalstate.stix.sdo.DomainObject; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.*; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; - -/** - * To only be used on STIX Relationship class. - * The annotation provides a Javax Validation that looks at the RelationshipType being used. - * This annotation enforces the STIX Relationship Type restrictions for each SDO. - */ -@Documented -@Constraint(validatedBy = {StixRelationshipTypeLimitValidator.class}) -@Target( { ANNOTATION_TYPE, TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(RelationshipTypeLimit.List.class) -public @interface RelationshipTypeLimit { - String message() default "{io.digitalstate.stix.validation.contraints.relationship.DefaultTypeValue}"; - Class[] groups() default {}; - Class[] payload() default {}; - - boolean enforceCommonRelationshipTypes() default true; - // @TODO look to use reflection to populate the Common Relationship Types default values. - String[] commonRelationshipTypes() default {"duplicate-of", "derived-from", "related-to"}; - Class source(); - String[] relationshipTypes(); - - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target( { ANNOTATION_TYPE, TYPE }) - @interface List { - RelationshipTypeLimit[] value(); - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipLimitValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipLimitValidator.java deleted file mode 100644 index e23212f..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipLimitValidator.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.digitalstate.stix.validation.contraints.relationship; - -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sro.objects.RelationshipSro; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.stream.Collectors; - -public class StixRelationshipLimitValidator implements ConstraintValidator { - - private Class source; - private String relationshipType; - private Class[] target; - private boolean classEquality; - - @Override - public void initialize(RelationshipLimit relationshipLimit) { - source = relationshipLimit.source(); - target = relationshipLimit.target(); - relationshipType = relationshipLimit.relationshipType(); - classEquality = relationshipLimit.classEquality(); - } - - @Override - public boolean isValid(RelationshipSro object, - ConstraintValidatorContext cxt) { - - if (object.getRelationshipType().equals(relationshipType)){ - if (classEquality){ - if (!object.getSourceRef().getClass().equals(object.getTargetRef().getClass())){ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Class Equality mismatch: Source: " + - source.getCanonicalName() + " does not match Target: " + object.getTargetRef().getClass().getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } - //@TODO write detail docs about the logic and how to eval this for debugging - // The use of isAssignableFrom is typically confusing from basic logic between the interfaces and the Immutable class - if (source.isAssignableFrom(object.getSourceRef().getClass())){ - boolean hasInstance = Arrays.stream(target).anyMatch(t-> t.isAssignableFrom(object.getTargetRef().getClass())); - if (classEquality && object.getTargetRef().getClass().equals(object.getSourceRef().getClass())){ - return true; - - } else if (hasInstance){ - return true; - - }else { - cxt.disableDefaultConstraintViolation(); - String targetClasses = Arrays.stream(target).map(Class::getCanonicalName).collect(Collectors.toList()).toString(); - String violationMessage = "Source/Target/RelationshipType Mismatch: Source: " + - source.getCanonicalName() + " with Relationship-Type '" + relationshipType + - "' is only supported for Target(s): " + targetClasses; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - - } else { - return true; - } - - } else { - return true; - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipTypeLimitValidator.java b/src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipTypeLimitValidator.java deleted file mode 100644 index 92e7894..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/relationship/StixRelationshipTypeLimitValidator.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.digitalstate.stix.validation.contraints.relationship; - -import io.digitalstate.stix.sdo.DomainObject; -import io.digitalstate.stix.sro.objects.RelationshipSro; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.List; - -public class StixRelationshipTypeLimitValidator implements ConstraintValidator { - - private Class source; - private String[] relationshipTypes; - private String[] commonRelationshipsTypes; - private boolean enforceCommonRelationshipTypes; - - @Override - public void initialize(RelationshipTypeLimit relationshipTypeLimitConstraint) { - source = relationshipTypeLimitConstraint.source(); - relationshipTypes = relationshipTypeLimitConstraint.relationshipTypes(); - commonRelationshipsTypes = relationshipTypeLimitConstraint.commonRelationshipTypes(); - enforceCommonRelationshipTypes = relationshipTypeLimitConstraint.enforceCommonRelationshipTypes(); - } - - @Override - public boolean isValid(RelationshipSro relationshipSro, - ConstraintValidatorContext cxt) { - if (source.isAssignableFrom(relationshipSro.getSourceRef().getClass())){ - List typesList = Arrays.asList(relationshipTypes); - List commonTypesList = Arrays.asList(commonRelationshipsTypes); - if (typesList.contains(relationshipSro.getRelationshipType())){ - return true; - } else if (enforceCommonRelationshipTypes && commonTypesList.contains(relationshipSro.getRelationshipType())){ - return true; - }else { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Relationship Type: '" + relationshipSro.getRelationshipType() + - "' is not supported with class " + relationshipSro.getClass().getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } else { - return true; - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/startswith/StartsWith.java b/src/main/java/io/digitalstate/stix/validation/contraints/startswith/StartsWith.java deleted file mode 100644 index 96f4c52..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/startswith/StartsWith.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.validation.contraints.startswith; - -import io.digitalstate.stix.helpers.StixCustomPropertiesConfig; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE_USE; - -/** - *

    Provides a Starts With validator of String values.

    - *
    - *

    Defaults to {@link StixCustomPropertiesConfig#DEFAULT_CUSTOM_PROPERTY_PREFIX}

    - */ -@Documented -@Constraint(validatedBy = {StixStartsWithValidatorString.class}) -@Target( { ANNOTATION_TYPE, TYPE_USE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface StartsWith { - String message() default "{io.digitalstate.stix.validation.contraints.startswith.StartsWith}"; - Class[] groups() default {}; - Class[] payload() default {}; - - String value() default StixCustomPropertiesConfig.DEFAULT_CUSTOM_PROPERTY_PREFIX; - -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/startswith/StixStartsWithValidatorString.java b/src/main/java/io/digitalstate/stix/validation/contraints/startswith/StixStartsWithValidatorString.java deleted file mode 100644 index f022454..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/startswith/StixStartsWithValidatorString.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.digitalstate.stix.validation.contraints.startswith; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -public class StixStartsWithValidatorString implements ConstraintValidator { - - private String prefix; - - @Override - public void initialize(StartsWith startsWithConstraint) { - prefix = startsWithConstraint.value(); - } - - @Override - public boolean isValid(String value, ConstraintValidatorContext cxt) { - if (value.startsWith(prefix)){ - return true; - } else{ - cxt.disableDefaultConstraintViolation(); - String violationMessage = "StartsWith violation: string must start with value: " - + prefix + ", but provided value: " + value; - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - return false; - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorCollection.java b/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorCollection.java deleted file mode 100644 index 05c433b..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorCollection.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.digitalstate.stix.validation.contraints.vocab; - -import com.google.common.collect.Sets; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.HashSet; -import java.util.Set; - -public class StixVocabValidatorCollection implements ConstraintValidator> { - - private Class vocabulary; - - @Override - public void initialize(Vocab vocabConstraint) { - vocabulary = vocabConstraint.value(); - } - - @Override - public boolean isValid(Set vocab, - ConstraintValidatorContext cxt) { - try { - Set vocabTerms = vocabulary.newInstance().getAllTerms(); - boolean evalContains = vocabTerms.containsAll(vocab); - if (!evalContains){ - cxt.disableDefaultConstraintViolation(); - Sets.SetView difference = Sets.difference(new HashSet<>(vocab), vocabTerms); - - String violationMessage = "Items: " + difference.toString() + " are not found in class " + vocabulary.getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - } - - return evalContains; - - } catch (InstantiationException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorOptionalString.java b/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorOptionalString.java deleted file mode 100644 index 28f37d1..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorOptionalString.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.digitalstate.stix.validation.contraints.vocab; - -import com.google.common.collect.Sets; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -public class StixVocabValidatorOptionalString implements ConstraintValidator> { - - private Class vocabulary; - - @Override - public void initialize(Vocab vocabConstraint) { - vocabulary = vocabConstraint.value(); - } - - @Override - public boolean isValid(Optional vocab, ConstraintValidatorContext cxt) { - if (vocab.isPresent()) { - try { - Set vocabTerms = vocabulary.newInstance().getAllTerms(); - boolean evalContains = vocabTerms.contains(vocab.get()); - if (!evalContains) { - Sets.SetView difference = Sets.difference(new HashSet<>(Arrays.asList(vocab.get())), vocabTerms); - cxt.disableDefaultConstraintViolation(); - String violationMessage = "Item: " + difference.toString() + " is not found in class " + vocabulary.getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - } - - return evalContains; - - } catch (InstantiationException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } else { - return true; - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorString.java b/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorString.java deleted file mode 100644 index f62f776..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/StixVocabValidatorString.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.digitalstate.stix.validation.contraints.vocab; - -import com.google.common.collect.Sets; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public class StixVocabValidatorString implements ConstraintValidator { - - private Class vocabulary; - - @Override - public void initialize(Vocab vocabConstraint) { - vocabulary = vocabConstraint.value(); - } - - @Override - public boolean isValid(String vocab, ConstraintValidatorContext cxt) { - if (vocab == null) { - return true; - - } else { - try { - Set vocabTerms = vocabulary.newInstance().getAllTerms(); - boolean evalContains = vocabTerms.contains(vocab); - if (!evalContains) { - cxt.disableDefaultConstraintViolation(); - Sets.SetView difference = Sets.difference(new HashSet<>(Arrays.asList(vocab)), vocabTerms); - - String violationMessage = "Item: " + difference.toString() + " is not found in class " + vocabulary.getCanonicalName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - } - - return evalContains; - - } catch (InstantiationException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "InstantiationException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - - } catch (IllegalAccessException e) { - cxt.disableDefaultConstraintViolation(); - String violationMessage = "IllegalAccessException from " + vocabulary.getSimpleName(); - cxt.buildConstraintViolationWithTemplate(violationMessage).addConstraintViolation(); - e.printStackTrace(); - return false; - } - } - } -} diff --git a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/Vocab.java b/src/main/java/io/digitalstate/stix/validation/contraints/vocab/Vocab.java deleted file mode 100644 index 68bb70c..0000000 --- a/src/main/java/io/digitalstate/stix/validation/contraints/vocab/Vocab.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.digitalstate.stix.validation.contraints.vocab; - -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.*; - -/** - *

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

    - *
    - *

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

    - *
    - *

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

    - */ -@Documented -@Constraint(validatedBy = { - StixVocabValidatorString.class, - StixVocabValidatorCollection.class -}) -@Target( { METHOD, FIELD, TYPE_USE, ANNOTATION_TYPE, PARAMETER }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Vocab { - String message() default "{io.digitalstate.stix.validation.contraints.VocabContains}"; - Class[] groups() default {}; - Class[] payload() default {}; - - Class value(); - -} diff --git a/src/main/java/io/digitalstate/stix/validation/groups/DefaultValuesProcessor.java b/src/main/java/io/digitalstate/stix/validation/groups/DefaultValuesProcessor.java deleted file mode 100644 index 92c91cb..0000000 --- a/src/main/java/io/digitalstate/stix/validation/groups/DefaultValuesProcessor.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.digitalstate.stix.validation.groups; - -public interface DefaultValuesProcessor { -} diff --git a/src/main/java/io/digitalstate/stix/validation/groups/NoValidation.java b/src/main/java/io/digitalstate/stix/validation/groups/NoValidation.java deleted file mode 100644 index 3849ad6..0000000 --- a/src/main/java/io/digitalstate/stix/validation/groups/NoValidation.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.digitalstate.stix.validation.groups; - -public interface NoValidation { -} diff --git a/src/main/java/io/digitalstate/stix/validation/groups/ValidateIdOnly.java b/src/main/java/io/digitalstate/stix/validation/groups/ValidateIdOnly.java deleted file mode 100644 index ccec271..0000000 --- a/src/main/java/io/digitalstate/stix/validation/groups/ValidateIdOnly.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.digitalstate.stix.validation.groups; - -/** - * Validate ID Only Group used by javax.validation - */ - -public interface ValidateIdOnly { -} diff --git a/src/main/java/io/digitalstate/stix/validation/sequences/SequenceDefault.java b/src/main/java/io/digitalstate/stix/validation/sequences/SequenceDefault.java deleted file mode 100644 index cc899b9..0000000 --- a/src/main/java/io/digitalstate/stix/validation/sequences/SequenceDefault.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.digitalstate.stix.validation.sequences; - -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; - -import javax.validation.GroupSequence; -import javax.validation.groups.Default; - -@GroupSequence({DefaultValuesProcessor.class, Default.class}) -public interface SequenceDefault { -} diff --git a/src/main/java/io/digitalstate/stix/validation/sequences/SequenceValidationIdOnly.java b/src/main/java/io/digitalstate/stix/validation/sequences/SequenceValidationIdOnly.java deleted file mode 100644 index 7920858..0000000 --- a/src/main/java/io/digitalstate/stix/validation/sequences/SequenceValidationIdOnly.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.digitalstate.stix.validation.sequences; - -import io.digitalstate.stix.validation.groups.DefaultValuesProcessor; -import io.digitalstate.stix.validation.groups.ValidateIdOnly; - -import javax.validation.GroupSequence; - -@GroupSequence({DefaultValuesProcessor.class, ValidateIdOnly.class}) -public interface SequenceValidationIdOnly { -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/StixVocabulary.java b/src/main/java/io/digitalstate/stix/vocabulary/StixVocabulary.java deleted file mode 100644 index c6c412e..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/StixVocabulary.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.digitalstate.stix.vocabulary; - -import java.util.Set; - -public interface StixVocabulary { - - /** - * Get all default terms - * @return - */ - Set getAllTerms(); - - /** - * Get all default terms and append some additional terms - * @param terms - * @return - */ - Set getAllTermsWithAdditional(String[] terms); -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AccountTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AccountTypes.java deleted file mode 100644 index 0d65fa5..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AccountTypes.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class AccountTypes implements StixVocabulary { - @JsonProperty("account-type-ov") - private Set terms = new HashSet<>(Arrays.asList( - "unix", "windows local", "windows domain", "ldap", "tacacs", "radius", - "nis", "openid", "facebook", "skype", "twitter", "kavi" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackMotivations.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackMotivations.java deleted file mode 100644 index b6c155e..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackMotivations.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class AttackMotivations implements StixVocabulary { - - @JsonProperty("attack_motivations_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "accidental", "coercion", "dominance", - "ideology", "notoriety", "organizational-gain", - "personal-gain", "personal-satisfaction", "revenge", - "unpredictable")); - - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackResourceLevels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackResourceLevels.java deleted file mode 100644 index ec2a6ff..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/AttackResourceLevels.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class AttackResourceLevels implements StixVocabulary { - - @JsonProperty("attack_resource_levels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "individual", "club", "content", - "team", "organization", "government")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/EncryptionAlgorithms.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/EncryptionAlgorithms.java deleted file mode 100644 index 6cb0f49..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/EncryptionAlgorithms.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class EncryptionAlgorithms implements StixVocabulary { - - @JsonProperty("encryption-algo-ov") - private Set terms = new HashSet<>(Arrays.asList( - "AES128-ECB", "AES128-CBC", "AES128-CFB", "AES128-COFB", - "AES128-CTR", "AES128-XTS", "AES128-GCM", - "Salsa20", "Salsa8B", "ChaCha20-Poly1305", "ChaCha20", - "DES-CBC", "3DES-CBC", "DES-EBC", "3DES-EBC", - "CAST128-CBC", "CAST256-CBC", "RSA", "DSA" - )); - - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/HashingAlgorithms.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/HashingAlgorithms.java deleted file mode 100644 index 5e8a1c2..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/HashingAlgorithms.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class HashingAlgorithms implements StixVocabulary { - - @JsonProperty("hashing_algorithms_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "MD5", "MD6", "RIPEMD-160", "SHA-1", - "SHA-224", "SHA-256", "SHA-384", "SHA-512", - "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", - "SSDEEP", "WHIRLPOOL")); - - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IdentityClasses.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IdentityClasses.java deleted file mode 100644 index 143731f..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IdentityClasses.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class IdentityClasses implements StixVocabulary { - - @JsonProperty("identity_classes_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "individual", "group", "organization", - "class", "unknown")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndicatorLabels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndicatorLabels.java deleted file mode 100644 index 20539e9..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndicatorLabels.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class IndicatorLabels implements StixVocabulary { - - @JsonProperty("indicator_labels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "anomalous-activity", "anonymization", "benign", - "compromised", "malicious-activity", "attribution")); - - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndustrySectors.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndustrySectors.java deleted file mode 100644 index 59f4c5b..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/IndustrySectors.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class IndustrySectors implements StixVocabulary { - - @JsonProperty("industry_sectors_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "agriculture", "aerospace", "automotive", - "communications", "construction", "defence", - "education", "energy", "entertainment", - "financial-services", "government-national", "government-regional", - "government-local", "government-public-services", "healthcare", - "hospitality-leisure", "infrastructure", "insurance", - "manufacturing", "mining", "non-profit", - "pharmaceuticals", "retail", "technology", - "telecommunications", "transportation", "utilities")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/MalwareLabels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/MalwareLabels.java deleted file mode 100644 index 300f43c..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/MalwareLabels.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class MalwareLabels implements StixVocabulary { - - @JsonProperty("malware_labels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "adware", "backdoor", "bot", - "ddos", "dropper", "exploit-kit", - "keylogger", "ransomware", "remote-access-trojan", - "resource-exploitation", "rogue-security-software", "rootkit", - "screen-capture", "spyware", "trojan", - "virus", "worm")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketAddressFamilies.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketAddressFamilies.java deleted file mode 100644 index 9e50430..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketAddressFamilies.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class NetworkSocketAddressFamilies implements StixVocabulary { - - @JsonProperty("network-socket-address-family-enum") - private Set terms = new HashSet<>(Arrays.asList( - "AF_UNSPEC", "AF_INET", "AF_IPX", "AF_APPLETALK", - "AF_NETBIOS", "AF_INET_6", "AF_IRDA", "AF_BTH" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketProtocolFamilies.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketProtocolFamilies.java deleted file mode 100644 index a5d800c..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketProtocolFamilies.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class NetworkSocketProtocolFamilies implements StixVocabulary { - - @JsonProperty("network-socket-protocol-family-enum") - private Set terms = new HashSet<>(Arrays.asList( - "PF_INET", "PF_AX25", "PF_IPX", "PF_INET_6", - "PF_APPLETALK", "PF_NETROM", "PF_BRIDGE", "PF_ATMPVC", - "PF_X25", "PF_ROSE", "PF_DECNET", "PF_NETBEUI", - "PF_SECURITY", "PF_KEY", "PF_NETLINK", "PF_ROUTE", - "PF_PACKET", "PF_ASH", "PF_ECONET", "PF_ATMSVC", - "PF_SNA", "PF_IRDA", "PF_PPPOX", "PF_WANPIPE", - "PF_BLUETOOTH" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketTypes.java deleted file mode 100644 index 9b08a5e..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/NetworkSocketTypes.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class NetworkSocketTypes implements StixVocabulary { - - @JsonProperty("network-socket-type-enum") - private Set terms = new HashSet<>(Arrays.asList( - "SOCK_STREAM", "SOCK_DGRAM", - "SOCK_RAW", "SOCK_RDM", - "SOCK_SEQPACKET" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/RelationshipTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/RelationshipTypes.java deleted file mode 100644 index ea21fe4..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/RelationshipTypes.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class RelationshipTypes implements StixVocabulary { - - private static Set terms = new HashSet<>(Arrays.asList( - "targets", - "uses", - "attributed-to", - "mitigates", - "indicates", - "variant-of", - "impersonates")); - - private static Set commonTerms = new HashSet<>(Arrays.asList( - "duplicate-of", - "derived-from", - "related-to")); - - @Override - public Set getAllTerms() { - return Stream.concat(terms.stream(), commonTerms.stream()) - .collect(Collectors.toCollection(HashSet::new)); - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ReportLabels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ReportLabels.java deleted file mode 100644 index 4364e99..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ReportLabels.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ReportLabels implements StixVocabulary { - - @JsonProperty("report_labels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "threat-report", "attack-pattern", "campaign", - "identity", "indicator", "malware", - "observed-data", "threat-actor", "tool", - "vulnerability")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorLabels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorLabels.java deleted file mode 100644 index 1164f63..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorLabels.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ThreatActorLabels implements StixVocabulary { - - @JsonProperty("threat_actor_labels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "activist", "competitor", "crime-syndicate", - "criminal", "hacker", "insider-accidental", - "insider-disgruntled", "nation-state", "sensationalist", - "spy", "terrorist")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorRoles.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorRoles.java deleted file mode 100644 index 872cdf0..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorRoles.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ThreatActorRoles implements StixVocabulary { - - @JsonProperty("threat_actor_roles_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "agent", "director", "independent", - "infrastructure-architect", "infrastructure-operator", "malware-author", - "sponsor")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorSophistication.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorSophistication.java deleted file mode 100644 index fc5f979..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ThreatActorSophistication.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ThreatActorSophistication implements StixVocabulary { - - @JsonProperty("threat_actor_sophistication_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "none", "minimal", "intermediate", - "advanced", "expert", "innovator", - "strategic")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/TlpLevels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/TlpLevels.java deleted file mode 100644 index 8c68577..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/TlpLevels.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class TlpLevels implements StixVocabulary { - - @JsonProperty("tlp_levels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "white", "green", "amber", "red")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ToolLabels.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ToolLabels.java deleted file mode 100644 index 657e574..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/ToolLabels.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ToolLabels implements StixVocabulary { - - @JsonProperty("tool_labels_vocabulary") - private Set terms = new HashSet<>(Arrays.asList( - "denial-of-service", "exploitation", "information-gathering", - "network-capture", "credential-exploitation", "remote-access", - "vulnerability-scanning")); - - // - // Getters and Setters - // - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsPeBinaryTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsPeBinaryTypes.java deleted file mode 100644 index ee404b2..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsPeBinaryTypes.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class WindowsPeBinaryTypes implements StixVocabulary { - - @JsonProperty("windows-pebinary-type-ov") - private Set terms = new HashSet<>(Arrays.asList( - "exe", "dll", "sys" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsRegistryValueDataTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsRegistryValueDataTypes.java deleted file mode 100644 index eae3783..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsRegistryValueDataTypes.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class WindowsRegistryValueDataTypes implements StixVocabulary { - - @JsonProperty("data-types") - private Set terms = new HashSet<>(Arrays.asList( - "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", - "REG_BINARY", "REG_DWORD", "REG_DWORD_BIG_ENDIAN", - "REG_LINK", "REG_MULTI_SZ", "REG_RESOURCE_LIST", - "REG_FULL_RESOURCE_DESCRIPTION", "REG_RESOURCE_REQUIREMENTS_LIST", "REG_QWORD", - "REG_INVALID_TYPE" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStartTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStartTypes.java deleted file mode 100644 index c5584e6..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStartTypes.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class WindowsServiceStartTypes implements StixVocabulary { - - @JsonProperty("windows-service-start-type-enum") - private Set terms = new HashSet<>(Arrays.asList( - "SERVICE_AUTO_START", "SERVICE_BOOT_START", "SERVICE_DEMAND_START", - "SERVICE_DISABLED", "SERVICE_SYSTEM_ALERT" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStatuses.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStatuses.java deleted file mode 100644 index ed2ede1..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceStatuses.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class WindowsServiceStatuses implements StixVocabulary { - - @JsonProperty("windows-service-status-enum") - private Set terms = new HashSet<>(Arrays.asList( - "SERVICE_CONTINUE_PENDING", "SERVICE_PAUSE_PENDING", "SERVICE_PAUSED", - "SERVICE_RUNNING", "SERVICE_START_PENDING", "SERVICE_STOP_PENDING", - "SERVICE_STOPPED" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceTypes.java b/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceTypes.java deleted file mode 100644 index 6be021a..0000000 --- a/src/main/java/io/digitalstate/stix/vocabulary/vocabularies/WindowsServiceTypes.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.digitalstate.stix.vocabulary.vocabularies; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.digitalstate.stix.vocabulary.StixVocabulary; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class WindowsServiceTypes implements StixVocabulary { - - @JsonProperty("windows-service-type-enum") - private Set terms = new HashSet<>(Arrays.asList( - "SERVICE_KERNEL_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER", - "SERVICE_WIN32_OWN_PROCESS", "SERVICE_WIN32_SHARE_PROCESS" - )); - - @Override - public Set getAllTerms() { - return terms; - } - - @Override - public Set getAllTermsWithAdditional(String[] terms) { - return Stream.concat(getAllTerms().stream(), Arrays.stream(terms)) - .collect(Collectors.toCollection(HashSet::new)); - } - -} diff --git a/src/main/kotlin/com/stephenott/stix/CommonProperties.kt b/src/main/kotlin/com/stephenott/stix/CommonProperties.kt new file mode 100644 index 0000000..8837424 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/CommonProperties.kt @@ -0,0 +1,48 @@ +package com.stephenott.stix + +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixLabels + +interface StixTypeProp{ + val type: StixType +} + +interface StixIdentifierProp{ + val id: StixIdentifier +} + +interface StixCreatedByRef{ + val createdByRef: String? +} + +interface StixCreatedProp{ + val created: StixInstant +} + +interface StixExternalReferencesProp{ + val externalReferences: ExternalReferences? +} + +interface StixObjectMarkingsRefsProp{ + val objectMarkingsRefs: String? +} + +interface StixGranularMarkingsProp{ + val granularMarkings: String? +} + +interface StixSpecVersionProp{ + val specVersion: StixSpecVersion +} + +interface StixLabels { + val labels: StixLabels? +} + +interface StixModified { + val modified: StixInstant +} + +interface StixRevoked { + val revoked: StixBoolean +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/Main.kt b/src/main/kotlin/com/stephenott/stix/Main.kt new file mode 100644 index 0000000..4ac6618 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/Main.kt @@ -0,0 +1,11 @@ +package com.stephenott.stix + +import com.stephenott.stix.sdo.Sdo +import com.stephenott.stix.sdo.objects.AttackPattern + +class Main { + + fun main(){ + var dog = AttackPattern("name") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixBundle.kt b/src/main/kotlin/com/stephenott/stix/StixBundle.kt new file mode 100644 index 0000000..ce895d8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/StixBundle.kt @@ -0,0 +1,4 @@ +package com.stephenott.stix + +interface StixBundle: StixContent{ +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixContent.kt b/src/main/kotlin/com/stephenott/stix/StixContent.kt new file mode 100644 index 0000000..7f56cf4 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/StixContent.kt @@ -0,0 +1,5 @@ +package com.stephenott.stix + +interface StixContent : + StixTypeProp, + StixIdentifierProp {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixDataFormats.kt b/src/main/kotlin/com/stephenott/stix/StixDataFormats.kt new file mode 100644 index 0000000..96be67f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/StixDataFormats.kt @@ -0,0 +1,51 @@ +package com.stephenott.stix + +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder +import java.time.temporal.ChronoField + +object StixDataFormats { + + /** + * Default pattern for deserialization of date/times into a STIX compliant timestamp. + */ + val TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]X" + + /** + * Default Timezone used for Serialization and Deserialization. + */ + val TIMEZONE = "UTC" + + /** + * Supports 0-9 digits of Sub-Second precision storage. (Nano Second Support) + * @return + */ + val readerStixDateTimeFormatter: DateTimeFormatter + get() { + val formatterBuilder = DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd'T'HH:mm:ss") + .optionalStart() + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) + .optionalEnd() + .appendPattern("X") + + return formatterBuilder.toFormatter().withZone(ZoneId.of("UTC")) + } + + fun getWriterStixDateTimeFormatter(subSecondPrecision: Int): DateTimeFormatter { + require(subSecondPrecision <= 9) { "Sub-Second Precision can only be from 0 to 9 digits" } + val formatterBuilder = DateTimeFormatterBuilder() + + formatterBuilder.appendPattern("yyyy-MM-dd'T'HH:mm:ss") + + if (subSecondPrecision > 0) { + formatterBuilder.appendFraction(ChronoField.NANO_OF_SECOND, subSecondPrecision, subSecondPrecision, true) + } + + formatterBuilder.appendPattern("X") + + return formatterBuilder.toFormatter().withZone(ZoneId.of("UTC")) + } + +} diff --git a/src/main/kotlin/com/stephenott/stix/StixObject.kt b/src/main/kotlin/com/stephenott/stix/StixObject.kt new file mode 100644 index 0000000..27539fe --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/StixObject.kt @@ -0,0 +1,8 @@ +package com.stephenott.stix + +/** + * Parent object for all STIX objects: SDO, SCO, SRO, Metadata + */ +interface StixObject : StixContent{ + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/ValidatorManager.kt b/src/main/kotlin/com/stephenott/stix/ValidatorManager.kt new file mode 100644 index 0000000..f505047 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/ValidatorManager.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix + +interface ValidatorManager { + var isValid: Boolean + + fun validate(data: I ): O +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt b/src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt new file mode 100644 index 0000000..894f9fd --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt @@ -0,0 +1,28 @@ +package com.stephenott.stix.sdo + +import com.stephenott.stix.serialization.JsonReader +import com.stephenott.stix.serialization.JsonWriter +import com.stephenott.stix.ValidatorManager + +class Sdo + (data: T, validateOnCreation: Boolean = true): + JsonReader, + JsonWriter, + ValidatorManager> { + override fun readJson(jsonString: String): T { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun toJson(): String { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override var isValid: Boolean + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + set(value) {} + + override fun validate(data: T): Sdo { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt b/src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt new file mode 100644 index 0000000..182184a --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt @@ -0,0 +1,15 @@ +package com.stephenott.stix.sdo + +import com.stephenott.stix.* + +interface StixDomainObject : + StixObject, + StixCreatedByRef, + StixCreatedProp, + StixExternalReferencesProp, + StixObjectMarkingsRefsProp, + StixGranularMarkingsProp, + StixSpecVersionProp, + StixModified, + StixLabels, + StixRevoked {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt new file mode 100644 index 0000000..2044939 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt @@ -0,0 +1,36 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.KillChainPhases + +interface AttackPatternSdo : StixDomainObject { + val name: String + val description: String? + val killChainPhases: KillChainPhases? //@TODO +} + +data class AttackPattern + ( + override val name: String, + override val description: String? = null, + override val killChainPhases: KillChainPhases? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + AttackPatternSdo { + + companion object{ + val stixType = StixType("attack-pattern") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt new file mode 100644 index 0000000..b7b6897 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt @@ -0,0 +1,40 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* + +interface CampaignSdo : StixDomainObject { + val name: String + val description: String? + val aliases: String? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val objective: String? +} + +data class Campaign + ( + override val name: String, + override val description: String? = null, + override val aliases: String? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val objective: String? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : CampaignSdo { + + companion object{ + val stixType = StixType("campaign") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt new file mode 100644 index 0000000..c4954c4 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt @@ -0,0 +1,40 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.CourseOfActionTypeOv + +interface CourseOfActionSdo : StixDomainObject { + val name: String + val description: String? + val actionType: CourseOfActionTypeOv? + val osExecutionEnvs: OsExecutionEnvs? + val actionBin: StixBinary? + val actionReference: ExternalReference? +} + +data class CourseOfAction + ( + override val name: String, + override val description: String? = null, + override val actionType: CourseOfActionTypeOv? = null, + override val osExecutionEnvs: OsExecutionEnvs? = null, + override val actionBin: StixBinary? = null, + override val actionReference: ExternalReference? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : CourseOfActionSdo { + + companion object { + val stixType = StixType("course-of-action") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt new file mode 100644 index 0000000..2083ff9 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt @@ -0,0 +1,38 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.GroupingContextOv + +interface GroupingSdo : StixDomainObject { + val name: String? + val description: String? + val context: GroupingContextOv + val objectRefs: StixIdentifiers +} + +data class Grouping + ( + override val name: String? = null, + override val description: String? = null, + override val context: GroupingContextOv, + override val objectRefs: StixIdentifiers, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + GroupingSdo { + + companion object{ + val stixType = StixType("grouping") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt new file mode 100644 index 0000000..85ca234 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt @@ -0,0 +1,42 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.IdentityClass +import com.stephenott.stix.type.vocab.IdentityRoles +import com.stephenott.stix.type.vocab.IndustrySectors + +interface IdentitySdo : StixDomainObject { + val name: String + val description: String? + val roles: IdentityRoles? + val identityClass: IdentityClass? + val sectors: IndustrySectors? + val contactInformation: String? +} + +data class Identity( + override val name: String, + override val description: String? = null, + override val roles: IdentityRoles? = null, + override val identityClass: IdentityClass? = null, + override val sectors: IndustrySectors? = null, + override val contactInformation: String? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() + ) : IdentitySdo { + + companion object { + val stixType = StixType("identity") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt new file mode 100644 index 0000000..e78c458 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt @@ -0,0 +1,46 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.IndicatorTypes +import com.stephenott.stix.type.vocab.PatternType + +interface IndicatorSdo : StixDomainObject { + val name: String? + val description: String? + val indicatorTypes: IndicatorTypes + val pattern: StixPattern + val patternType: PatternType + val patternVersion: String? + +} + +data class Indicator( + override val name: String? = null, + override val description: String? = null, + override val indicatorTypes: IndicatorTypes, + override val pattern: StixPattern, + override val patternType: PatternType, + override val patternVersion: String? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : IndicatorSdo { + + companion object { + val stixType = StixType("indicator") + } + + init { + require(indicatorTypes.size >= 1) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt new file mode 100644 index 0000000..5a1bebb --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt @@ -0,0 +1,45 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.InfrastructureTypes +import com.stephenott.stix.type.vocab.KillChainPhases + +interface InfrastructureSdo : StixDomainObject { + val name: String + val description: String? + val infrastructureTypes: InfrastructureTypes + val aliases: StixStringList? + val killChainPhases: KillChainPhases? + val firstSeen: StixInstant? + val lastSeen: StixInstant? +} + +data class Infrastructure + ( + override val name: String, + override val description: String? = null, + override val infrastructureTypes: InfrastructureTypes, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + InfrastructureSdo { + + companion object{ + val stixType = StixType("infrastructure") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt new file mode 100644 index 0000000..25a0f1d --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt @@ -0,0 +1,48 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.* + +interface IntrusionSetSdo : StixDomainObject { + val name: String + val description: String? + val aliases: StixStringList? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val goals: StixStringList? + val resourceLevel: AttackResourceLevelOv? + val primaryMotivation: AttackMotivationOv? + val secondaryMotivations: AttackMotivations? +} + +data class IntrusionSet + ( + override val name: String, + override val description: String? = null, + override val aliases: StixStringList? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val goals: StixStringList? = null, + override val resourceLevel: AttackResourceLevelOv? = null, + override val primaryMotivation: AttackMotivationOv? = null, + override val secondaryMotivations: AttackMotivations? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + IntrusionSetSdo { + + companion object{ + val stixType = StixType("intrusion-set") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt new file mode 100644 index 0000000..5bd3050 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt @@ -0,0 +1,51 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.AdministrativeArea +import com.stephenott.stix.type.vocab.City +import com.stephenott.stix.type.vocab.RegionOv + +interface LocationSdo : StixDomainObject { + val name: String + val description: String? + val latitude: Latitude? + val longitude: Longitude? + val precision: LatLongPrecision? + val region: RegionOv? + val administrativeArea: AdministrativeArea? + val city: City? + val streetAddress: StreetAddress? + val postalCode: PostalCode? +} + +data class Location( + override val name: String, + override val description: String? = null, + override val latitude: Latitude? = null, + override val longitude: Longitude? = null, + override val precision: LatLongPrecision? = null, + override val region: RegionOv? = null, + override val administrativeArea: AdministrativeArea?, + override val city: City? = null, + override val streetAddress: StreetAddress? = null, + override val postalCode: PostalCode? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + LocationSdo { + + companion object { + val stixType = StixType("location") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt new file mode 100644 index 0000000..4af02b1 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt @@ -0,0 +1,56 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.* + +interface MalwareSdo : StixDomainObject { + val name: String? + val description: String? + val malwareTypes: MalwareTypes + val isFamily: StixBoolean + val aliases: StixStringList? + val killChainPhases: KillChainPhases? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val osExecutionEnvs: OsExecutionEnvs? + val architectureExecutionEnvs: ProcessorArchitectures? + val implementationLanguage: ImplementationLanguages? + val capabilities: MalwareCapabilities? + val sampleRefs: StixIdentifiers? +} + +data class Malware + ( + override val name: String, + override val description: String? = null, + override val malwareTypes: MalwareTypes, + override val isFamily: StixBoolean, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val osExecutionEnvs: OsExecutionEnvs? = null, + override val architectureExecutionEnvs: ProcessorArchitectures? = null, + override val implementationLanguage: ImplementationLanguages? = null, + override val capabilities: MalwareCapabilities? = null, + override val sampleRefs: StixIdentifiers? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + MalwareSdo { + + companion object{ + val stixType = StixType("malware") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt new file mode 100644 index 0000000..70ae103 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt @@ -0,0 +1,58 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.MalwareAvResult + +interface MalwareAnalysisSdo : StixDomainObject { + val product: Product + val version: String? + val hostVmRef: StixIdentifier? + val operatingSystemRef: StixIdentifier? + val installedSystemRefs: StixIdentifiers? + val configurationVersion: String? + val modules: StixStringList? + val analysisEngineVersion: String? + val analysisDefinitionVersion: String? + val submitted: StixInstant? + val analysisStarted: StixInstant? + val analysisEnded: StixInstant? + val avResult: MalwareAvResult? + val analysisScoRefs: StixIdentifiers? +} + +data class MalwareAnalysis + ( + override val product: Product, + override val version: String? = null, + override val hostVmRef: StixIdentifier? = null, + override val operatingSystemRef: StixIdentifier? = null, + override val installedSystemRefs: StixIdentifiers? = null, + override val configurationVersion: String? = null, + override val modules: StixStringList? = null, + override val analysisEngineVersion: String? = null, + override val analysisDefinitionVersion: String? = null, + override val submitted: StixInstant? = null, + override val analysisStarted: StixInstant? = null, + override val analysisEnded: StixInstant? = null, + override val avResult: MalwareAvResult? = null, + override val analysisScoRefs: StixIdentifiers? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + MalwareAnalysisSdo { + + companion object{ + val stixType = StixType("malware-analysis") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt new file mode 100644 index 0000000..77d6532 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt @@ -0,0 +1,37 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.KillChainPhases + +interface NoteSdo : StixDomainObject { + val abstract: String? + val content: String + val authors: StixStringList? + val objectRefs: StixIdentifiers +} + +data class Note( + override val abstract: String? = null, + override val content: String, + override val authors: StixStringList? = null, + override val objectRefs: StixIdentifiers, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + NoteSdo { + + companion object { + val stixType = StixType("note") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt new file mode 100644 index 0000000..b711182 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt @@ -0,0 +1,37 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* + +interface ObservedDataSdo : StixDomainObject { + val firstObserved: StixInstant + val lastObserved: StixInstant + val numberObserved: StixInteger + val objectRefs: StixIdentifiers +} + +data class ObservedData + ( + override val firstObserved: StixInstant, + override val lastObserved: StixInstant, + override val numberObserved: StixInteger, + override val objectRefs: StixIdentifiers, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + ObservedDataSdo { + + companion object{ + val stixType = StixType("observed-data") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt new file mode 100644 index 0000000..83f6f5b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt @@ -0,0 +1,37 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.OpinionEnum + +interface OpinionSdo : StixDomainObject { + val explanation: String? + val authors: StixStringList? + val opinion: OpinionEnum + val objectRefs: StixIdentifiers +} + +data class Opinion( + override val explanation: String? = null, + override val authors: StixStringList? = null, + override val opinion: OpinionEnum, + override val objectRefs: StixIdentifiers, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + OpinionSdo { + + companion object { + val stixType = StixType("opinion") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt new file mode 100644 index 0000000..651ee34 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt @@ -0,0 +1,41 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.vocab.ReportTypes + +interface ReportSdo : StixDomainObject { + val name: String + val description: String? + val reportTypes: ReportTypes + val published: StixInstant + val objectRefs: StixIdentifiers +} + +data class Report + ( + override val name: String, + override val description: String? = null, + override val reportTypes: ReportTypes, + override val published: StixInstant, + override val objectRefs: StixIdentifiers, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + ReportSdo { + + companion object{ + val stixType = StixType("report") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt new file mode 100644 index 0000000..fc40556 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt @@ -0,0 +1,55 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.* + +interface ThreatActorSdo : StixDomainObject { + val name: String + val description: String? + val threatActorTypes: ThreatActorTypes + val aliases: StixStringList? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val roles: ThreatActorRoles? + val goals: StixStringList? + val sophistication: ThreatActorSophisticationOv? + val resourceLevel: AttackResourceLevelOv? + val primaryMotivation: AttackMotivationOv? + val secondaryMotivation: AttackMotivationOv? + val personalMotivations: AttackMotivations? +} + +data class ThreatActor( + override val name: String, + override val description: String? = null, + override val threatActorTypes: ThreatActorTypes, + override val aliases: StixStringList? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val roles: ThreatActorRoles? = null, + override val goals: StixStringList? = null, + override val sophistication: ThreatActorSophisticationOv? = null, + override val resourceLevel: AttackResourceLevelOv? = null, + override val primaryMotivation: AttackMotivationOv? = null, + override val secondaryMotivation: AttackMotivationOv? = null, + override val personalMotivations: AttackMotivations? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + ThreatActorSdo { + + companion object{ + val stixType = StixType("threat-actor") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt new file mode 100644 index 0000000..ca20c57 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt @@ -0,0 +1,42 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.vocab.ToolTypes + +interface ToolSdo : StixDomainObject { + val name: String + val description: String? + val toolTypes: ToolTypes + val aliases: StixStringList? + val killChainPhases: KillChainPhases? + val toolVersion: String? +} + +data class Tool( + override val name: String, + override val description: String? = null, + override val toolTypes: ToolTypes, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val toolVersion: String? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + ToolSdo { + + companion object{ + val stixType = StixType("tool") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt new file mode 100644 index 0000000..ffdfd95 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt @@ -0,0 +1,33 @@ +package com.stephenott.stix.sdo.objects + +import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.type.* + +interface VulnerabilitySdo : StixDomainObject { + val name: String + val description: String? +} + +data class Vulnerability + ( + override val name: String, + override val description: String? = null, + override val type: StixType = stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + VulnerabilitySdo { + + companion object{ + val stixType = StixType("vulnerability") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt b/src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt new file mode 100644 index 0000000..330ebe4 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt @@ -0,0 +1,13 @@ +package com.stephenott.stix.serialization + +interface JsonReader { + + fun readJson(jsonString: String): T + +} + +interface JsonWriter { + + fun toJson(): String + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/ExternalReferences.kt b/src/main/kotlin/com/stephenott/stix/type/ExternalReferences.kt new file mode 100644 index 0000000..42ea023 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/ExternalReferences.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type + +class ExternalReferences(private val references: LinkedHashSet): + Set by references + +data class ExternalReference( + val sourceName: String, + val description: String?, + val url: String?, + val hashes: HashesDictionary?, + val externalId: String? +) { + init { + //@TODO add biz logic for what fields can be populated + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt new file mode 100644 index 0000000..f9abc1d --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt @@ -0,0 +1,26 @@ +package com.stephenott.stix.type + +import com.stephenott.stix.type.vocab.OpenVocab + +class HashesDictionary(private val immutableDictionary: HashMap = hashMapOf()): + Map by immutableDictionary { +} + +class HashingAlgorithm(private val algorithm: String): OpenVocab, CharSequence by algorithm{ + + companion object{ + var vocab: LinkedHashSet = linkedSetOf( + "MD5", "MD6", "RIPEMD-160", "SHA-1", + "SHA-224", "SHA-256", "SHA-384", "SHA-512", + "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", + "SSDEEP", "WHIRLPOOL") + set(value) { + require(value.all { it.length in 3..256 }) + field = value + } + } + + init { + require(this.algorithm in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/LocationData.kt b/src/main/kotlin/com/stephenott/stix/type/LocationData.kt new file mode 100644 index 0000000..263c526 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/LocationData.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type + +class Latitude(val value: Float){ + init { + require(value in -90.0..90.0) + } +} + +class Longitude(val value: Float){ + init { + require(value in -180.0..180.0) + } +} + +class LatLongPrecision(val value: Float){ +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/OsExecutionEnvs.kt b/src/main/kotlin/com/stephenott/stix/type/OsExecutionEnvs.kt new file mode 100644 index 0000000..8c5abd9 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/OsExecutionEnvs.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.type + +class OsExecutionEnvs(private val envs: LinkedHashSet): + Set by envs{ +} + +data class OsExecutionEnv(val env: String){} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/Product.kt b/src/main/kotlin/com/stephenott/stix/type/Product.kt new file mode 100644 index 0000000..c513078 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/Product.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.type + +class Product(private val product: String): CharSequence by product{ + init { + require(product.none { it.isWhitespace() && it.isUpperCase() }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt b/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt new file mode 100644 index 0000000..3ecd0f3 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt @@ -0,0 +1,3 @@ +package com.stephenott.stix.type + +data class StixBinary(val binary: String){} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixBoolean.kt b/src/main/kotlin/com/stephenott/stix/type/StixBoolean.kt new file mode 100644 index 0000000..40e792f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixBoolean.kt @@ -0,0 +1,29 @@ +package com.stephenott.stix.type + +data class StixBoolean( + val value: Boolean = false, + val isDefinedValue: Boolean = false +) { + + /** + * Defaults to StixBoolean value to false. Sets isDefinedValue to false. + * Essentially if the no arg constructor was used, we assume that its a default boolean value rather than explicitly being provided. + * This mainly has implications for JSON processing. + */ + constructor() : this(false, false) + + constructor(value: Boolean) : this(value, true) + + constructor(stixBoolean: StixBoolean) : this(stixBoolean.value, stixBoolean.isDefinedValue) + + companion object { + fun parse(booleanString: String): StixBoolean { + return StixBoolean(booleanString.toBoolean(), true) + } + } + + override fun toString(): String { + return this.value.toString() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt b/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt new file mode 100644 index 0000000..ef06ba5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt @@ -0,0 +1,29 @@ +package com.stephenott.stix.type + +import java.util.* +import kotlin.collections.LinkedHashSet + +class StixIdentifiers(private val identifiers: LinkedHashSet) : + Set by identifiers { + + //@TODO add limits on which Identifiers can be added for specific implementations +} + +data class StixIdentifier( + val type: StixType, + val uuid: String = generateUUIDv4() +) { + + constructor(type: String) : this(StixType(type)) + + //@TODO Add deterministic ID generation + + companion object { + fun generateUUIDv4(): String = UUID.randomUUID().toString() + const val typeUUIDSpacer = "--" + } + + fun getIdentifier(): String { + return type.toString() + typeUUIDSpacer + uuid + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt b/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt new file mode 100644 index 0000000..dfabf22 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt @@ -0,0 +1,54 @@ +package com.stephenott.stix.type + +import com.stephenott.stix.StixDataFormats +import java.time.Instant +import java.util.regex.Pattern + +data class StixInstant( + val instant: Instant, + val subSecondPrecision: Int) { + + init { + //@TODO how to detect what parameters were used + require(!(subSecondPrecision < 0 || subSecondPrecision > 9)) { "Sub-second precision must be between 0 and 9" } + } + + companion object { + var defaultSubSecondPrecision = 3 + var REGEX_SUBSECOND: Pattern = + Pattern.compile("(?\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(.(?[0-9]+))?Z)") + + fun parse(dateString: String): StixInstant { + val instant = Instant.from(StixDataFormats.readerStixDateTimeFormatter.parse(dateString)) + val subSecondPrecision = getSubSecondDigitCount(dateString) + + return StixInstant(instant, subSecondPrecision) + } + + private fun getSubSecondDigitCount(dateString: String): Int { + val matcher = REGEX_SUBSECOND.matcher(dateString) + if (matcher.find()) { + val subSeconds: String? = matcher.group("subSecond") + // If no sub seconds were provided then return 0 as the precision + return subSeconds?.length ?: 0 + + } else { + throw IllegalStateException("Unable to parse date") + } + } + } + + constructor(stixInstant: StixInstant) : this(stixInstant.instant, stixInstant.subSecondPrecision) + + constructor() : this(Instant.now(), defaultSubSecondPrecision){} + + constructor(instant: Instant) : this(instant, instant.nano.toString().length) + + override fun toString(): String { + return StixDataFormats.getWriterStixDateTimeFormatter(subSecondPrecision).format(this.instant) + } + + fun toString(subSecondPrecision: Int): String { + return StixDataFormats.getWriterStixDateTimeFormatter(subSecondPrecision).format(this.instant) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt b/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt new file mode 100644 index 0000000..136619e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.type + +class StixInteger (val value: Int){ + init { + require(value in 1..999999999) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt b/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt new file mode 100644 index 0000000..db15040 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt @@ -0,0 +1,9 @@ +package com.stephenott.stix.type + +class StixLabels(private val labels: LinkedHashSet): + Set by labels { + + init { + labels.all { it.isNotEmpty() } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixPattern.kt b/src/main/kotlin/com/stephenott/stix/type/StixPattern.kt new file mode 100644 index 0000000..75dfea7 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixPattern.kt @@ -0,0 +1,9 @@ +package com.stephenott.stix.type + +class StixPattern(private val pattern: String): + CharSequence by pattern { + + init { + pattern.isNotEmpty() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt b/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt new file mode 100644 index 0000000..acebdbb --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt @@ -0,0 +1,22 @@ +package com.stephenott.stix.type + +data class StixSpecVersion(private val versionEnum: StixVersions, val isDefinedValue: Boolean) { + + companion object{ + enum class StixVersions(val version: String) { + TWO_DOT_ZERO("2.0"), + TWO_DOT_ONE("2.1") + } + + fun parse(versionString: String): StixSpecVersion{ + return StixSpecVersion(StixVersions.values().first { it.version == versionString }, true) + } + } + + //@TODO Review if this is the correct default + constructor() : this(StixVersions.TWO_DOT_ZERO, false) + + override fun toString(): String { + return versionEnum.version + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt b/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt new file mode 100644 index 0000000..4857978 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.type + +class StixStringList(private val list: LinkedHashSet): Set by list{ + init { + list.all { it.isNotEmpty()} + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixType.kt b/src/main/kotlin/com/stephenott/stix/type/StixType.kt new file mode 100644 index 0000000..afe6c81 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixType.kt @@ -0,0 +1,26 @@ +package com.stephenott.stix.type + +data class StixType( + val type: String, + val isCustomType: Boolean = false) +{ + + init { + require(type.length in IntRange(3, 250)) + require(Regex("^\\-?[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\-?$").matches(type)) + } + + companion object{ + var customObjectPrefix: String = "x-" //@TODO refactor to lazy init and a outside factory + } + + constructor(stixType: StixType): this(stixType.type, stixType.isCustomType) + + override fun toString(): String { + return if (isCustomType){ + customObjectPrefix + type + } else { + type + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StreetAddress.kt b/src/main/kotlin/com/stephenott/stix/type/StreetAddress.kt new file mode 100644 index 0000000..6427673 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StreetAddress.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type + +data class StreetAddress(val address: String){ + + + override fun toString(): String { + return super.toString() + } +} + +data class PostalCode(val postalCode: String){ + + override fun toString(): String { + return super.toString() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt new file mode 100644 index 0000000..dc7670e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt @@ -0,0 +1,32 @@ +package com.stephenott.stix.type.vocab + +class AdministrativeAreas(private val areas: LinkedHashSet = linkedSetOf(), enforceVocab: Boolean = false) : + Set by areas { + + init { + if (enforceVocab) { + areas.all { AdministrativeArea.vocab.contains(it.toString()) } + } + } +} + +class AdministrativeArea(private val area: String, enforceVocab: Boolean = false) : OpenVocab, CharSequence by area { + + init { + if (enforceVocab) { + require(vocab.contains(area)) + } + } + + companion object { + + val vocabName = "administrative-areas" + + var vocab: LinkedHashSet = linkedSetOf( + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt new file mode 100644 index 0000000..1776566 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt @@ -0,0 +1,23 @@ +package com.stephenott.stix.type.vocab + +class AttackMotivations(private val motivations: LinkedHashSet = linkedSetOf()) : + Set by motivations { +} + +class AttackMotivationOv(private val motivation: String) : OpenVocab, CharSequence by motivation { + + companion object { + val vocabName = "attack-motivation-ov" + + val vocab: LinkedHashSet = linkedSetOf( + "accidental", "coercion", "dominance", + "ideology", "notoriety", "organizational-gain", + "personal-gain", "personal-satisfaction", + "revenge", "unpredictable" + ) + } + + init { + require(this.motivation in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt new file mode 100644 index 0000000..2f8afe8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt @@ -0,0 +1,17 @@ +package com.stephenott.stix.type.vocab + +class AttackResourceLevelOv(private val level: String) : OpenVocab, CharSequence by level { + + companion object { + val vocabName = "attack-resource-level-ov" + + val vocab: LinkedHashSet = linkedSetOf( + "individual", "club", "contest", + "team", "organization", "government" + ) + } + + init { + require(this.level in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt new file mode 100644 index 0000000..7a0431c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt @@ -0,0 +1,32 @@ +package com.stephenott.stix.type.vocab + +class Cities(private val cities: LinkedHashSet = linkedSetOf(), enforceVocab: Boolean = false) : + Set by cities { + + init { + if (enforceVocab) { + cities.all { City.vocab.contains(it.toString()) } + } + } +} + +class City(private val city: String, enforceVocab: Boolean = false) : OpenVocab, CharSequence by city { + + init { + if (enforceVocab) { + require(vocab.contains(city)) + } + } + + companion object { + + val vocabName = "cities" + + var vocab: LinkedHashSet = linkedSetOf( + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt new file mode 100644 index 0000000..eb9721f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt @@ -0,0 +1,4 @@ +package com.stephenott.stix.type.vocab + +interface ClosedVocab { +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt new file mode 100644 index 0000000..23a3cb0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt @@ -0,0 +1,17 @@ +package com.stephenott.stix.type.vocab + +class CourseOfActionTypeOv(private val type: String) : ClosedVocab, CharSequence by type { + + companion object { + val vocabName = "course-of-action-type-ov" + + val vocab: LinkedHashSet = linkedSetOf( + "textual:text/plain", "textual:text/html", "textual:text/md", + "textual:pdf" + ) + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt new file mode 100644 index 0000000..a39c801 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type.vocab + +class GroupingContextOv(private val groupingContext: String) : OpenVocab, CharSequence by groupingContext { + + companion object { + val vocabName = "grouping-context-ov" + + val vocab: LinkedHashSet = linkedSetOf( + "suspicious-activity", "malware-analysis", "unspecified" + ) + } + + init { + require(this.groupingContext in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt new file mode 100644 index 0000000..2a79c2a --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt @@ -0,0 +1,21 @@ +package com.stephenott.stix.type.vocab + +class IdentityClass(private val identityClass: String): OpenVocab, CharSequence by identityClass{ + + companion object{ + + val vocabName = "identity-class-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "individual", "group", "system", + "organization", "class", "unspecified") + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.identityClass in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt new file mode 100644 index 0000000..3739819 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt @@ -0,0 +1,32 @@ +package com.stephenott.stix.type.vocab + +class IdentityRoles(private val roles: LinkedHashSet = linkedSetOf(), enforceVocab: Boolean = false) : + Set by roles { + + init { + if (enforceVocab) { + roles.all { IdentityRole.vocab.contains(it.toString()) } + } + } +} + +class IdentityRole(private val role: String, enforceVocab: Boolean = false) : OpenVocab, CharSequence by role { + + init { + if (enforceVocab) { + require(vocab.contains(role)) + } + } + + companion object { + + val vocabName = "identity-roles" + + var vocab: LinkedHashSet = linkedSetOf( + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt new file mode 100644 index 0000000..9493c9b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt @@ -0,0 +1,31 @@ +package com.stephenott.stix.type.vocab + +class ImplementationLanguages(private val languages: LinkedHashSet = linkedSetOf()) : + Set by languages { +} + +class ImplementationLanguage(private val language: String) : OpenVocab, CharSequence by language { + + companion object { + + val vocabName = "implementation-language-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "applescript", "bash", "c", + "c++", "c#,", "go", + "java", "javascript", "lua", + "objective-c", "perl", "php", + "powershell", "python", "ruby", + "scala", "swift", "typescript", + "visual-basic", "x86-32", "x86-64" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.language in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt new file mode 100644 index 0000000..7a2de26 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt @@ -0,0 +1,27 @@ +package com.stephenott.stix.type.vocab + +class IndicatorTypes(private val types: LinkedHashSet = linkedSetOf()) : + Set by types { +} + +class IndicatorType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + + val vocabName = "indicator-type-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "anomalous-activity", "anonymization", "benign", + "compromised", "malicious-activity", "attribution", + "unknown" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt new file mode 100644 index 0000000..2778ba8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt @@ -0,0 +1,30 @@ +package com.stephenott.stix.type.vocab + +class IndustrySectors(private val sectors: LinkedHashSet = linkedSetOf()) : + Set by sectors { +} + +class IndustrySector(private val sector: String) : OpenVocab, CharSequence by sector { + + companion object { + var vocab: LinkedHashSet = linkedSetOf( + "agriculture", "aerospace", "automotive", + "communications", "construction", "defence", + "education", "energy", "entertainment", + "financial-services", "government-national", "government-regional", + "government-local", "government-public-services", "healthcare", + "hospitality-leisure", "infrastructure", "insurance", + "manufacturing", "mining", "non-profit", + "pharmaceuticals", "retail", "technology", + "telecommunications", "transportation", "utilities" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.sector in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt new file mode 100644 index 0000000..a099938 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt @@ -0,0 +1,28 @@ +package com.stephenott.stix.type.vocab + +class InfrastructureTypes(private val types: LinkedHashSet = linkedSetOf()) : + Set by types { +} + +class InfrastructureType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + + val vocabName = "infrastructure-type-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "amplification", "anonymization", "botnet", + "command-and-control", "exfiltration", "hosting-malware", + "hosting-target-lists", "phishing", "reconnaissance", + "staging", "undefined" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt new file mode 100644 index 0000000..a1061d1 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt @@ -0,0 +1,21 @@ +package com.stephenott.stix.type.vocab + +class KillChainPhases(private val phases: LinkedHashSet) : + Set by phases {} + +data class KillChainPhase(val killChainName: String, val phaseName: String) {} + +object CommonKillChainPhases { + + val lockheedMartinKillChainName = "lockheed-martin-cyber-kill-chain" + enum class LockheedMartinKillChainPhases(val phaseName: String) { + RECONNAISSANCE("reconnaissance"), + WEAPONIZATION("weaponization"), + DELIVERY("delivery"), + EXPLOITATION("exploitation"), + INSTALLATION("installation"), + COMMAND_AND_CONTROL("command-and-control"), + ACTIONS_ON_OBJECTIVE("actions-on-objective"); + } +} + diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt new file mode 100644 index 0000000..a694763 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt @@ -0,0 +1,25 @@ +package com.stephenott.stix.type.vocab + +class MalwareAvResults(private val results: LinkedHashSet = linkedSetOf()) : + Set by results { +} + +class MalwareAvResult(private val result: String) : OpenVocab, CharSequence by result { + + companion object { + val vocabName = "malware-av-result-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "malicious", "suspicious", "benign", + "unknown" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.result in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt new file mode 100644 index 0000000..22ff8b4 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt @@ -0,0 +1,37 @@ +package com.stephenott.stix.type.vocab + +class MalwareCapabilities(private val capabilities: LinkedHashSet = linkedSetOf()) : + Set by capabilities { +} + +class MalwareCapability(private val capability: String) : OpenVocab, CharSequence by capability { + + companion object { + + val vocabName = "malware-capabilities-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "accesses-remote-machines", "anti-debugging", "anti-disassembly", + "anti-emulation", "anti-memory-forensics", "anti-sandbox", + "anti-vm", "captures-input-peripherals", "captures-output-peripherals", + "captures-system-state-data", "cleans-traces-of-infection", "commits-fraud", + "communicates-with-c2", "compromises-data-availability", "compromises-data-integrity", + "compromises-system-availability", "controls-local-machine", "degrades-security-software", + "degrades-system-updates", "determines-c2-server", "emails-spam", + "escalates-privileges", "evades-av", "exfiltrates-data", + "fingerprints-host", "hides-artifacts", "hides-executing-code", + "infects-files", "infects-remote-machines", "installs-other-components", + "persists-after-system-reboot", "prevents-artifact-access", "prevents-artifact-deletion", + "probes-network-environment", "self-modifies", "steals-authentication-credentials", + "violates-system-operational-integrity" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.capability in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt new file mode 100644 index 0000000..31c925c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt @@ -0,0 +1,29 @@ +package com.stephenott.stix.type.vocab + +class MalwareTypes(private val types: LinkedHashSet = linkedSetOf()) : + Set by types { +} + +class MalwareType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + var vocab: LinkedHashSet = linkedSetOf( + "adware", "backdoor", "bot", + "bootkit", "ddos", "downloader", + "dropper", "exploit-kit", "keylogger", + "ransomware", "remote-access-trojan", "resource-exploitation", + "rogue-security-software", "rootkit", "screen-capture", + "spyware", "trojan", "unknown", + "virus", "webshell", "wiper", + "worm" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt new file mode 100644 index 0000000..b9275d3 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt @@ -0,0 +1,4 @@ +package com.stephenott.stix.type.vocab + +interface OpenVocab { +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt new file mode 100644 index 0000000..1f0ce84 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt @@ -0,0 +1,19 @@ +package com.stephenott.stix.type.vocab + +class OpinionEnum(private val opinion: String) : ClosedVocab, CharSequence by opinion { + + companion object { + var vocab: LinkedHashSet = linkedSetOf( + "strongly-disagree", "disagree", + "neutral", "agree", "strongly-agree" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.opinion in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt new file mode 100644 index 0000000..79a79af --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt @@ -0,0 +1,14 @@ +package com.stephenott.stix.type.vocab + +class PatternType(private val type: String) : ClosedVocab, CharSequence by type { + + companion object { + val vocab: LinkedHashSet = linkedSetOf( + "stix", "snort", "yara" + ) + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt new file mode 100644 index 0000000..f2801fa --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt @@ -0,0 +1,27 @@ +package com.stephenott.stix.type.vocab + +class ProcessorArchitectures(private val architectures: LinkedHashSet = linkedSetOf()) : + Set by architectures { +} + +class ProcessorArchitecture(private val architecture: String) : OpenVocab, CharSequence by architecture { + + companion object { + + val vocabName = "processor-architecture-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "alpha", "arm", "ia-64", + "mips", "powerpc", "sparc", + "x86", "x86-64" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.architecture in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt new file mode 100644 index 0000000..0bffae0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt @@ -0,0 +1,24 @@ +package com.stephenott.stix.type.vocab + +class RegionOv(private val region: String) : ClosedVocab, CharSequence by region { + + companion object { + val vocabName = "region-ov" + + val vocab: LinkedHashSet = linkedSetOf( + "africa", "eastern-africa", "middle-africa", + "northern-africa", "southern-africa", "western-africa", + "americas", "latin-america-caribbean", "south-america", + "caribbean", "central-america", "northern-america", + "asia", "central-asia", "eastern-asia", + "southern-asia", "western-asia", "europe", + "eastern-europe", "northern-europe", "southern-europe", + "western-europe", "oceania", "australia-new-zealand", + "melanesia", "micronesia", "polynesia", "antarctica" + ) + } + + init { + require(this.region in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt new file mode 100644 index 0000000..6338fe5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt @@ -0,0 +1,28 @@ +package com.stephenott.stix.type.vocab + +class ReportTypes(private val types: LinkedHashSet = linkedSetOf()) : + Set by types { +} + +class ReportType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + + val vocabName = "report-type-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "attack-pattern", "campaign", "identity", + "indicator", "intrusion-set", "malware", + "observed-data", "threat-actor", "threat-report", + "tool", "vulnerability" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt new file mode 100644 index 0000000..3cc3ef0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt @@ -0,0 +1,27 @@ +package com.stephenott.stix.type.vocab + +class ThreatActorRoles(private val roles: LinkedHashSet = linkedSetOf()) : + Set by roles { +} + +class ThreatActorRole(private val role: String) : OpenVocab, CharSequence by role { + + companion object { + + val vocabName = "threat-actor-role-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "agent", "director", "independent", + "infrastructure-architect", "infrastructure-operator", "malware-author", + "sponsor" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.role in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt new file mode 100644 index 0000000..72f35d8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt @@ -0,0 +1,23 @@ +package com.stephenott.stix.type.vocab + +class ThreatActorSophisticationOv(private val sophistication: String) : OpenVocab, CharSequence by sophistication { + + companion object { + + val vocabName = "threat-actor-sophistication" + + var vocab: LinkedHashSet = linkedSetOf( + "none", "minimal", "intermediate", + "advanced", "expert", "innovator", + "strategic" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.sophistication in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt new file mode 100644 index 0000000..1c95294 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt @@ -0,0 +1,28 @@ +package com.stephenott.stix.type.vocab + +class ThreatActorTypes(private val types: LinkedHashSet = linkedSetOf()) : + Set by types { +} + +class ThreatActorType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + + val vocabName = "threat-actor-type-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "activist", "competitor", "crime-syndicate", + "criminal", "hacker", "insider-accidental", + "insider-disgruntled", "nation-state", "sensationalist", + "spy", "terrorist", "unknown" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt new file mode 100644 index 0000000..ad04aa8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt @@ -0,0 +1,24 @@ +package com.stephenott.stix.type.vocab + +class ToolTypes(private val types: LinkedHashSet = linkedSetOf()) : + Set by types { +} + +class ToolType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + var vocab: LinkedHashSet = linkedSetOf( + "denial-of-service", "exploitation", "information-gathering", + "network-capture", "credential-exploitation", "remote-access", + "vulnerability-scanning", "unknown" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/test/groovy/faker/StixMockDataGenerator.groovy b/src/test/groovy/faker/StixMockDataGenerator.groovy deleted file mode 100644 index d4a90d4..0000000 --- a/src/test/groovy/faker/StixMockDataGenerator.groovy +++ /dev/null @@ -1,2983 +0,0 @@ -package faker - -import faker.configs.ObservedDataGeneratorConfig -import io.digitalstate.stix.bundle.Bundle -import io.digitalstate.stix.bundle.BundleableObject -import io.digitalstate.stix.common.StixBoolean -import io.digitalstate.stix.common.StixInstant -import io.digitalstate.stix.coo.extension.types.* -import io.digitalstate.stix.coo.objects.* -import io.digitalstate.stix.coo.types.* -import io.digitalstate.stix.datamarkings.GranularMarking -import io.digitalstate.stix.datamarkings.MarkingDefinition -import io.digitalstate.stix.datamarkings.objects.Statement -import io.digitalstate.stix.datamarkings.objects.Tlp -import io.digitalstate.stix.sdo.DomainObject -import io.digitalstate.stix.sdo.objects.* -import io.digitalstate.stix.sdo.types.* -import io.digitalstate.stix.sro.objects.* -import io.digitalstate.stix.vocabulary.vocabularies.* -import net.andreinc.mockneat.MockNeat - -import java.time.Instant -import java.time.LocalDate -import java.util.concurrent.ThreadLocalRandom - -public class StixMockDataGenerator { - - // MockNeat object - MockNeat mock = MockNeat.threadLocal() - - - Instant commonLowerDate = Instant.ofEpochMilli(LocalDate.of(2000, 1, 1).toEpochDay()) - - Instant generateRandomDate(Instant lower, Instant upper){ - return Instant.ofEpochMilli(ThreadLocalRandom.current() - .longs(lower.toEpochMilli(), upper.toEpochMilli()) - .findAny() - .getAsLong()) - } - - // - // Mocks and Generators: - // - - /** - * Generates a random set of single word labels - * @return - */ - Set generateRandomLabels() { - Set labels = new HashSet<>() - - mock.ints().range(1, 20).get().times { - labels.add(mock.words().get()) - } - return labels - } - - /** - * Generate a random Map of Custom Properties - * @return - */ - Map generateCustomProperties() { - - Map customProperties = new HashMap<>() - - mock.ints().range(0, 20).get().times { - String key = mock.words().prepend("x_").get() - switch (mock.ints().range(0, 5).get()) { - case 0: - customProperties.put(key, mock.words().get()) - break - case 1: - customProperties.put(key, mock.words().accumulate(mock.ints().range(1, 100).get(), " ").get()) - break - case 2: - customProperties.put(key, mock.ints().range(0, 999999).get()) - break - case 3: - customProperties.put(key, mock.doubles().range(0.0, 999999.0).get()) - break - case 4: - customProperties.put(key, mock.bools().probability(50).get()) - break - case 5: - HashMap map1 = new HashMap<>() - mock.ints().range(1, 30).get().times { - map1.put(mock.words().get(), mock.words().accumulate(mock.ints().range(1, 10).get(), " ").get()) - } - customProperties.put(key, map1) - break - } - } - return customProperties - } - - - /** - * Generate a random KillChain Phase - * @return - */ - KillChainPhase mockKillChainPhase() { - return KillChainPhase.builder() - .killChainName(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - .phaseName(mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get()) - .build() - } - - /** - * Generate a random External Reference - * @return - */ - ExternalReference mockExternalReference() { - ExternalReference.Builder builder = ExternalReference.builder() - .sourceName(mock.words().get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 100).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - builder.url(mock.urls().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - - if (mock.bools().probability(50).get()) { - builder.externalId(mock.uuids().get()) - } - - return builder.build() - } - - /** - * Generate a random Attack pattern - * @return - */ - AttackPattern mockAttackPattern() { - AttackPattern.Builder builder = AttackPattern.builder() - .name(mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get()) - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - builder.created(new StixInstant(objectCreated)) - - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(commonLowerDate, objectCreated))) - } - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 15).get().times { - builder.addKillChainPhase(mockKillChainPhase()) - } - } - - if (mock.bools().probability(50).get()) { - builder.addAllLabels(generateRandomLabels()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - /** - * Generate a random Campaign - * @return - */ - Campaign mockCampaign() { - Campaign.Builder builder = Campaign.builder() - .name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 5).get().times { - builder.addAliase(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - } - - Instant firstSeen = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.firstSeen(new StixInstant(firstSeen)) - } - - if (mock.bools().probability(50).get()) { - builder.lastSeen(new StixInstant(generateRandomDate(firstSeen, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.objective(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - builder.addAllLabels(generateRandomLabels()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - /** - * Generate a random Course of Action - * @return - */ - CourseOfAction mockCourseOfAction() { - CourseOfAction.Builder builder = CourseOfAction.builder() - .name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addAction(mock.words().accumulate(mock.ints().range(1, 30).get(), " ").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.addAllLabels(generateRandomLabels()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - /** - * Generate a random Identity - * @return - */ - Identity mockIdentity() { - Identity.Builder builder = Identity.builder() - .name(mock.names().full(33.33).get()) - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - builder.identityClass(mock.fromStrings(new IdentityClasses().getAllTerms().toList()).get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addSector(mock.fromStrings(new IndustrySectors().getAllTerms().toList()).get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.contactInformation("${mock.emails().get()} ${mock.departments().get()}") - } - - if (mock.bools().probability(50).get()) { - builder.addAllLabels(generateRandomLabels()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - //Note does not test against .createdByRef() to prevent possible recursion - - return builder.build() - } - - Indicator mockIndicator() { - Indicator.Builder builder = Indicator.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - builder.addLabel(mock.fromStrings(new IndicatorLabels().getAllTerms().toList()).get()) - - if (mock.bools().probability(50).get()) { - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - builder.pattern("SOME PATTERN GOES HERE") - - Instant validFrom = generateRandomDate(commonLowerDate, Instant.now()) - builder.validFrom(new StixInstant(validFrom)) - - if (mock.bools().probability(50).get()) { - builder.validUntil(new StixInstant(generateRandomDate(validFrom, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 15).get().times { - builder.addKillChainPhase(mockKillChainPhase()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - IntrusionSet mockIntrusionSet() { - IntrusionSet.Builder builder = IntrusionSet.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 5).get().times { - builder.addAliase(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - } - - Instant firstSeen = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.firstSeen(new StixInstant(firstSeen)) - } - - if (mock.bools().probability(50).get()) { - builder.lastSeen(new StixInstant(generateRandomDate(firstSeen, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 15).get().times { - builder.addGoal(mock.words().accumulate(mock.ints().range(1, 10).get(), " ").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.resourceLevel(mock.fromStrings(new AttackResourceLevels().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.primaryMotivation(mock.fromStrings(new AttackMotivations().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addSecondaryMotivation(mock.fromStrings(new AttackMotivations().getAllTerms().toList()).get()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - Malware mockMalware() { - Malware.Builder builder = Malware.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - builder.addLabel(mock.fromStrings(new MalwareLabels().getAllTerms().toList()).get()) - - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 15).get().times { - builder.addKillChainPhase(mockKillChainPhase()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - ObservedData mockObservedData(ObservedDataGeneratorConfig config = new ObservedDataGeneratorConfig()) { - ObservedData.Builder builder = ObservedData.builder() - - Instant objectCreated = generateRandomDate(config.propCreatedLowerDate, config.propCreatedUpperDate) - if (mock.bools().probability(config.propCreatedProbability).get()) { - builder.created(new StixInstant(objectCreated, config.propCreatedDateSubsecondPrecision)) - if (mock.bools().probability(config.propModifiedProbability).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, config.propModifiedUpperDate), config.propModifiedSubsecondPrecision)) - } - } - - Instant firstObserved = generateRandomDate(config.propFirstObservedLowerDate, config.propFirstObservedUpperDate) - builder.firstObserved(new StixInstant(firstObserved, config.propFirstObservedSubsecondPrecision)) - - builder.lastObserved(new StixInstant(generateRandomDate(firstObserved, config.propLastObservedUpperDate), config.propLastObservedSubsecondPrecision)) - - builder.numberObserved(mock.ints().range(config.propNumberObservedLowerCount, config.propNumberObservedUpperCount).get()) - - - if (mock.bools().probability(config.artifactCoo.occurrence_probability).get()) { - mock.ints().range(config.artifactCoo.occurs_count_lower, config.artifactCoo.occurs_count_upper).get().times { - builder.addObject(mockArtifactCoo()) - } - } - - if (mock.bools().probability(config.autonomousSystemCoo.occurrence_probability).get()) { - mock.ints().range(config.autonomousSystemCoo.occurs_count_lower, config.autonomousSystemCoo.occurs_count_upper).get().times { - builder.addObject(mockAutonomousSystemCoo()) - } - } - - if (mock.bools().probability(config.directoryCoo.occurrence_probability).get()) { - mock.ints().range(config.directoryCoo.occurs_count_lower, config.directoryCoo.occurs_count_upper).get().times { - builder.addObject(mockDirectoryCoo()) - } - } - - if (mock.bools().probability(config.domainNameCoo.occurrence_probability).get()) { - mock.ints().range(config.domainNameCoo.occurs_count_lower, config.domainNameCoo.occurs_count_upper).get().times { - builder.addObject(mockDomainNameCoo()) - } - } - - if (mock.bools().probability(config.emailAddressCoo.occurrence_probability).get()) { - mock.ints().range(config.emailAddressCoo.occurs_count_lower, config.emailAddressCoo.occurs_count_upper).get().times { - builder.addObject(mockEmailAddressCoo()) - } - } - - //@TODO Refactor to pass in Email address objects and artifacts - if (mock.bools().probability(config.emailMessageCoo.occurrence_probability).get()) { - mock.ints().range(config.emailMessageCoo.occurs_count_lower, config.emailMessageCoo.occurs_count_upper).get().times { - builder.addObject(mockEmailMessageCoo()) - } - } - - if (mock.bools().probability(config.fileCoo.occurrence_probability).get()) { - mock.ints().range(config.fileCoo.occurs_count_lower, config.fileCoo.occurs_count_upper).get().times { - builder.addObject(mockFileCoo()) - } - } - - if (mock.bools().probability(config.ipv4AddressCoo.occurrence_probability).get()) { - mock.ints().range(config.ipv4AddressCoo.occurs_count_lower, config.ipv4AddressCoo.occurs_count_upper).get().times { - builder.addObject(mockIpv4AddressCoo()) - } - } - - if (mock.bools().probability(config.ipv6AddressCoo.occurrence_probability).get()) { - mock.ints().range(config.ipv6AddressCoo.occurs_count_lower, config.ipv6AddressCoo.occurs_count_upper).get().times { - builder.addObject(mockIpv6AddressCoo()) - } - } - - if (mock.bools().probability(config.macAddressCoo.occurrence_probability).get()) { - mock.ints().range(config.macAddressCoo.occurs_count_lower, config.macAddressCoo.occurs_count_upper).get().times { - builder.addObject(mockMacAddress()) - } - } - - if (mock.bools().probability(config.mutexCoo.occurrence_probability).get()) { - mock.ints().range(config.mutexCoo.occurs_count_lower, config.mutexCoo.occurs_count_upper).get().times { - builder.addObject(mockMutexCoo()) - } - } - - if (mock.bools().probability(config.networkTrafficCoo.occurrence_probability).get()) { - mock.ints().range(config.networkTrafficCoo.occurs_count_lower, config.networkTrafficCoo.occurs_count_upper).get().times { - builder.addObject(mockNetworkTrafficCoo()) - } - } - - if (mock.bools().probability(config.processCoo.occurrence_probability).get()) { - mock.ints().range(config.processCoo.occurs_count_lower, config.processCoo.occurs_count_upper).get().times { - builder.addObject(mockProcessCoo()) - } - } - - if (mock.bools().probability(config.softwareCoo.occurrence_probability).get()) { - mock.ints().range(config.softwareCoo.occurs_count_lower, config.softwareCoo.occurs_count_upper).get().times { - builder.addObject(mockSoftwareCoo()) - } - } - - if (mock.bools().probability(config.urlCoo.occurrence_probability).get()) { - mock.ints().range(config.urlCoo.occurs_count_lower, config.urlCoo.occurs_count_upper).get().times { - builder.addObject(mockUrlCoo()) - } - } - - if (mock.bools().probability(config.userAccountCoo.occurrence_probability).get()) { - mock.ints().range(config.userAccountCoo.occurs_count_lower, config.userAccountCoo.occurs_count_upper).get().times { - builder.addObject(mockUserAccountCoo()) - } - } - - if (mock.bools().probability(config.windowsRegisteryKeyCoo.occurrence_probability).get()) { - mock.ints().range(config.windowsRegisteryKeyCoo.occurs_count_lower, config.windowsRegisteryKeyCoo.occurs_count_upper).get().times { - builder.addObject(mockWindowsRegistryKeyCoo()) - } - } - - if (mock.bools().probability(config.x509CertificateCoo.occurrence_probability).get()) { - mock.ints().range(config.x509CertificateCoo.occurs_count_lower, config.x509CertificateCoo.occurs_count_upper).get().times { - builder.addObject(mockX509CertificateCoo()) - } - } - - - if (mock.bools().probability(config.externalReferences.occurrence_probability).get()) { - mock.ints().range(config.externalReferences.occurs_count_lower, config.externalReferences.occurs_count_upper).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(config.propRevokedProbability).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(config.propCustomPropsProbability).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(config.propCreatedByRefProbability).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(config.objectMarkings.occurrence_probability).get()) { - mock.ints().range(config.objectMarkings.occurs_count_lower, config.objectMarkings.occurs_count_upper).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(config.granularMarkings.occurrence_probability).get()) { - mock.ints().range(config.granularMarkings.occurs_count_lower, config.granularMarkings.occurs_count_upper).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - if (mock.bools().probability(config.labelsOccurrenceProbability).get()) { - builder.labels(generateRandomLabels()) - } - - return builder.build() - } - - // Cyber Observables - - Artifact mockArtifactCoo() { - Artifact.Builder builder = Artifact.builder() - - if (mock.bools().probability(50).get()) { - builder.mimeType(mock.mimes().get()) - } - - if (mock.bools().probability(50).get()) { - builder.payloadBin(mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get()) - } else { - builder.url(mock.urls().get()) - builder.putHash("SHA-256", mock.hashes().sha256().get()) - - if (mock.bools().probability(33.33).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - - if (mock.bools().probability(33.33).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - } - - return builder.build() - } - - AutonomousSystem mockAutonomousSystemCoo() { - AutonomousSystem.Builder builder = AutonomousSystem.builder() - - builder.number(mock.longs().get()) - - if (mock.bools().probability(50).get()) { - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.rir(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - - return builder.build() - } - - Directory mockDirectoryCoo() { - Directory.Builder builder = Directory.builder() - - builder.path(mock.words().accumulate(mock.ints().range(1, 5).get(), "/").get()) - - if (mock.bools().probability(50).get()) { - builder.pathEnc("csASCII") - } - - Instant created = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(created)) - } - - Instant modified = generateRandomDate(created, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(modified)) - } - - if (mock.bools().probability(50).get()) { - builder.accessed(new StixInstant(generateRandomDate(created, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - if (mock.bools().probability(50).get()) { - builder.addContainsRef(mockFileCoo().getObservableObjectKey()) - } else { - //@TODO add support for contains_refs with directories - // This will require that Creating a Directory COO has a param to - // set where other Contains refs are generated so a endless cannot happen. - } - } - } - - return builder.build() - } - - DomainName mockDomainNameCoo() { - DomainName.Builder builder = DomainName.builder() - - builder.value(mock.domains().get()) - - //@TODO Add resolves_to_refs mocking - - return builder.build() - } - - EmailAddress mockEmailAddressCoo() { - EmailAddress.Builder builder = EmailAddress.builder() - - builder.value(mock.emails().get()) - - if (mock.bools().probability(50).get()) { - builder.displayName(mock.names().full(33.33).get()) - } - - //@TODO Add belongs_to_ref mocking - - return builder.build() - } - - EmailMessage mockEmailMessageCoo() { - EmailMessage.Builder builder = EmailMessage.builder() - - if (mock.bools().probability(50).get()) { - builder.isMultipart(true) - mock.ints().range(1, 5).get().times { - builder.addBodyMultipart(mockMimePartTypeCooType()) - } - } else { - builder.isMultipart(false) - builder.body(mock.words().accumulate(mock.ints().range(1, 100).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - builder.date(new StixInstant(generateRandomDate(commonLowerDate, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.contentType(mock.mimes().get()) - } - - if (mock.bools().probability(50).get()) { - builder.fromRef(mockEmailAddressCoo().getObservableObjectKey()) - } - - if (mock.bools().probability(50).get()) { - builder.senderRef(mockEmailAddressCoo().getObservableObjectKey()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addToRef(mockEmailAddressCoo().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addCcRef(mockEmailAddressCoo().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addBccRef(mockEmailAddressCoo().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - builder.subject(mock.words().accumulate(mock.ints().range(1, 10).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addReceivedLine(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.rawEmailRef(mockArtifactCoo().getObservableObjectKey()) - } - - return builder.build() - } - - MimePartType mockMimePartTypeCooType() { - MimePartType.Builder builder = MimePartType.builder() - - if (mock.bools().probability(50).get()) { - builder.body(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } else { - if (mock.bools().probability(50).get()) { - builder.bodyRawRef(mockArtifactCoo().getObservableObjectKey()) - } else { - builder.bodyRawRef(mockFileCoo().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - builder.contentType(mock.mimes().get()) - } - - if (mock.bools().probability(50).get()) { - builder.contentDisposition(mock.words().get()) - } - - return builder.build() - } - - File mockFileCoo() { - File.Builder builder = File.builder() - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockNtfsFileExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockRasterImageFileExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockPdfFileExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockArchiveFileExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockWindowsPeBinaryFileExtensionCooExt()) - } - } - } - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(20).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.size(mock.longs().range(0, 999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.nameEnc(mock.words().get()) - } - - //@TODO hardcoded the hex value until a proper generator can be built. - if (mock.bools().probability(50).get()) { - builder.magicNumberHex("F9") - } - - if (mock.bools().probability(50).get()) { - builder.mimeType(mock.mimes().get()) - } - - Instant created = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(created)) - } - - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(created, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.accessed(new StixInstant(generateRandomDate(created, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.parentDirectoryRef(mockDirectoryCoo().getObservableObjectKey()) - } - - if (mock.bools().probability(50).get()) { - builder.isEncrypted(true) - - if (mock.bools().probability(50).get()) { - builder.encryptionAlgorithm(mock.fromStrings(new EncryptionAlgorithms().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.decryptionKey(mock.words().accumulate(mock.ints().range(1, 20).get(), "").get()) - } - } - - //@TODO come up with a simple use case that does not cause recursion for the "contains_refs" field - - return builder.build() - } - - Ipv4Address mockIpv4AddressCoo() { - Ipv4Address.Builder builder = Ipv4Address.builder() - - builder.value(mock.ipv4s().get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addResolvesToRef(mockMacAddress().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addBelongsToRef(mockAutonomousSystemCoo().getObservableObjectKey()) - } - } - - return builder.build() - } - - Ipv6Address mockIpv6AddressCoo() { - Ipv6Address.Builder builder = Ipv6Address.builder() - - builder.value(mock.iPv6s().get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addResolvesToRef(mockMacAddress().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addBelongsToRef(mockAutonomousSystemCoo().getObservableObjectKey()) - } - } - - return builder.build() - } - - MacAddress mockMacAddress() { - MacAddress.Builder builder = MacAddress.builder() - - builder.value(mock.macs().get()) - - return builder.build() - } - - Mutex mockMutexCoo() { - Mutex.Builder builder = Mutex.builder() - - builder.name(mock.words().get()) - - return builder.build() - } - - NetworkTraffic mockNetworkTrafficCoo() { - NetworkTraffic.Builder builder = NetworkTraffic.builder() - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockHttpRequestExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockTcpExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockIcmpExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockNetworkSocketExtensionCooExt()) - } - } - } - - Instant start = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.start(new StixInstant(start)) - } - - if (mock.bools().probability(50).get()) { - builder.end(new StixInstant(generateRandomDate(start, Instant.now()))) - } - - // 33% true, 33% false, 33% never set / null: - if (mock.bools().probability(33).get()) { - builder.isActive(true) - } - - if (mock.bools().probability(33).get()) { - builder.isActive(false) - } - - if (mock.bools().probability(50).get()) { - switch (mock.ints().range(0, 3).get()) { - case 0: - builder.srcRef(mockIpv4AddressCoo().getObservableObjectKey()) - break - case 1: - builder.srcRef(mockIpv6AddressCoo().getObservableObjectKey()) - break - case 2: - builder.srcRef(mockMacAddress().getObservableObjectKey()) - break - case 3: - builder.srcRef(mockDomainNameCoo().getObservableObjectKey()) - break - } - } - - if (mock.bools().probability(50).get()) { - switch (mock.ints().range(0, 3).get()) { - case 0: - builder.dstRef(mockIpv4AddressCoo().getObservableObjectKey()) - break - case 1: - builder.dstRef(mockIpv6AddressCoo().getObservableObjectKey()) - break - case 2: - builder.dstRef(mockMacAddress().getObservableObjectKey()) - break - case 3: - builder.dstRef(mockDomainNameCoo().getObservableObjectKey()) - break - } - } - - if (mock.bools().probability(50).get()) { - builder.srcPort(mock.ints().range(0, 65535).get()) - } - - if (mock.bools().probability(50).get()) { - builder.dstPort(mock.ints().range(0, 65535).get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addProtocol(mock.fromStrings("ipv4", "tcp", "http", "udp", "ipv6", "ssl", "https").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.srcByteCount(mock.longs().range(0, 999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.dstByteCount(mock.longs().range(0, 999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.srcPackets(mock.longs().range(0, 999999999).get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - String key = mock.uuids().get() - if (mock.bools().probability(50).get()) { - builder.putIpFix(key, mock.words().get()) - } else { - builder.putIpFix(key, mock.ints().range(0, 999999999).get()) - } - } - } - - if (mock.bools().probability(50).get()) { - builder.srcPayloadRef(mockArtifactCoo().getObservableObjectKey()) - } - - //@TODO encapsulates_refs (List) - - //@TODO encapsulated_by_ref - - return builder.build() - } - - Process mockProcessCoo() { - Process.Builder builder = Process.builder() - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockWindowsProcessExtensionCooExt()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockWindowsServiceExtensionCooExt()) - } - } - } - - if (mock.bools().probability(33).get()) { - builder.isHidden(true) - } else { - builder.isHidden(false) - } - - if (mock.bools().probability(50).get()) { - builder.pid(mock.longs().range(0, 999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(generateRandomDate(commonLowerDate, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.cwd(mock.words().accumulate(mock.ints().range(1, 5).get(), "/").prepend("/").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addArgument(mock.uuids().get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.commandLine("${mock.words().get()} ${mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get()}") - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 20).get().times { - builder.putEnvironmentVariable(mock.uuids().get(), mock.words().get()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addOpenedConnectionRef(mockNetworkTrafficCoo().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - builder.creatorUserRef(mockUserAccountCoo().getObservableObjectKey()) - } - - if (mock.bools().probability(50).get()) { - builder.binaryRef(mockFileCoo().getObservableObjectKey()) - } - - - //@TODO parent_ref - - //@TODO child_refs - - - return builder.build() - } - - Software mockSoftwareCoo() { - Software.Builder builder = Software.builder() - - builder.name(mock.words().get()) - - if (mock.bools().probability(50).get()) { - builder.cpe("cpe:2.3:${mock.words().get()}") - } - - //@TODO add better language dictionary support - if (mock.bools().probability(50).get()) { - builder.addLanguage("eng") - if (mock.bools().probability(50).get()) { - builder.addLanguage("fre") - } - } - - if (mock.bools().probability(50).get()) { - builder.vendor(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.version("${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}") - } - - return builder.build() - } - - Url mockUrlCoo() { - Url.Builder builder = Url.builder() - - builder.value(mock.urls().get()) - - return builder.build() - } - - UserAccount mockUserAccountCoo() { - UserAccount.Builder builder = UserAccount.builder() - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addExtension(mockUnixAccountExtensionCooExt()) - } - } - } - - builder.userId(mock.uuids().get()) - - if (mock.bools().probability(50).get()) { - builder.accountLogin(mock.names().last().get()) - } - - if (mock.bools().probability(50).get()) { - builder.accountType(mock.fromStrings(new AccountTypes().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.displayName(mock.words().get()) - } - - if (mock.bools().probability(33).get()) { - builder.isServiceAccount(true) - } - - if (mock.bools().probability(33).get()) { - builder.isServiceAccount(false) - } - - if (mock.bools().probability(33).get()) { - builder.isPrivileged(true) - } - - if (mock.bools().probability(33).get()) { - builder.isPrivileged(false) - } - - if (mock.bools().probability(33).get()) { - builder.isCanEscalatePrivs(true) - } - - if (mock.bools().probability(33).get()) { - builder.isCanEscalatePrivs(false) - } - - if (mock.bools().probability(33).get()) { - builder.isDisabled(true) - } - - if (mock.bools().probability(33).get()) { - builder.isDisabled(false) - } - - Instant accountCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.accountCreated(new StixInstant(accountCreated)) - } - - if (mock.bools().probability(50).get()) { - builder.accountExpires(new StixInstant(generateRandomDate(accountCreated, Instant.now().plusMillis(1000000)))) - } - - if (mock.bools().probability(50).get()) { - builder.passwordLastChanged(new StixInstant(generateRandomDate(accountCreated, Instant.now()))) - } - - Instant firstLogin = generateRandomDate(accountCreated, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.accountFirstLogin(new StixInstant(firstLogin)) - } - - if (mock.bools().probability(50).get()) { - builder.accountLastLogin(new StixInstant(generateRandomDate(firstLogin, Instant.now()))) - } - - return builder.build() - } - - WindowsRegistryKey mockWindowsRegistryKeyCoo() { - WindowsRegistryKey.Builder builder = WindowsRegistryKey.builder() - - builder.key(mock.fromStrings("HKEY_LOCAL_MACHINE", "hkey_local_machine", "HKEY_CURRENT_USER", "hkey_current_user", "HKEY_CLASSES_ROOT", "hkey_classes_root", "HKEY_CURRENT_CONFIG", "hkey_current_config", "HKEY_PERFORMANCE_DATA", "hkey_performance_data", "HKEY_USERS", "hkey_users", "HKEY_DYN_DATA").get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addValue(mockWindowsRegistryValue()) - } - } - - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(commonLowerDate, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.creatorUserRef(mockUserAccountCoo().getObservableObjectKey()) - } - - if (mock.bools().probability(50).get()) { - builder.numberOfSubkeys(mock.longs().range(0, 999999999).get()) - } - - return builder.build() - } - - WindowsRegistryValue mockWindowsRegistryValue() { - WindowsRegistryValue.Builder builder = WindowsRegistryValue.builder() - - builder.name(mock.words().get()) - - if (mock.bools().probability(50).get()) { - builder.data(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.dataType(mock.fromStrings(new WindowsRegistryValueDataTypes().getAllTerms().toList()).get()) - } - - return builder.build() - } - - X509Certificate mockX509CertificateCoo() { - X509Certificate.Builder builder = X509Certificate.builder() - - if (mock.bools().probability(33).get()) { - builder.isSelfSigned(true) - } else { - builder.isSelfSigned(false) - } - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(20).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.version("${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}") - } - - if (mock.bools().probability(50).get()) { - builder.serialNumber(mock.ints().range(1, 9999999).get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.signatureAlgorithm(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.issuer(mock.words().get()) - } - - Instant validityNotBefore = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.validityNotBefore(new StixInstant(validityNotBefore)) - } - - if (mock.bools().probability(50).get()) { - builder.validityNotAfter(new StixInstant(generateRandomDate(validityNotBefore, Instant.now().plusMillis(1000000)))) - } - - if (mock.bools().probability(50).get()) { - builder.subject(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.subjectPublicKeyAlgorithm(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.subjectPublicKeyModulus(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.subjectPublicKeyExponent(mock.longs().get()) - } - - //@TODO x509_v3_extensions property - - return builder.build() - } - - Report mockReport() { - Report.Builder builder = Report.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - mock.ints().range(1, 5).get().times { - builder.addLabel(mock.fromStrings(new ReportLabels().getAllTerms().toList()).get()) - } - - builder.name(mock.words().accumulate(mock.ints().range(1, 8).get(), " ").get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - builder.published(new StixInstant(generateRandomDate(commonLowerDate, Instant.now()))) - - mock.ints().range(1, 50).get().times { - switch (mock.ints().range(1, 14).get()) { - case 1: - builder.addObjectRef(mockAttackPattern()) - break - case 2: - builder.addObjectRef(mockCampaign()) - break - case 3: - builder.addObjectRef(mockCourseOfAction()) - break - case 4: - builder.addObjectRef(mockIdentity()) - break - case 5: - builder.addObjectRef(mockIndicator()) - break - case 6: - builder.addObjectRef(mockIntrusionSet()) - break - case 7: - builder.addObjectRef(mockMalware()) - break - case 8: - builder.addObjectRef(mockObservedData()) - break - case 9: - builder.addObjectRef(mockThreatActor()) - break - case 10: - builder.addObjectRef(mockTool()) - break - case 11: - builder.addObjectRef(mockVulnerability()) - break - case 12: - builder.addObjectRef(mockMarkingDefinition()) - break - case 13: - builder.addObjectRef(mockRelationship()) - break - case 14: - builder.addObjectRef(mockSighting()) - break - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - ThreatActor mockThreatActor() { - ThreatActor.Builder builder = ThreatActor.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - mock.ints().range(1, 5).get().times { - builder.addLabel(mock.fromStrings(new ThreatActorLabels().getAllTerms().toList()).get()) - } - - builder.name(mock.names().get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addAlias(mock.names().get()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addRole(mock.fromStrings(new ThreatActorRoles().getAllTerms().toList()).get()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGoal(mock.words().accumulate(mock.ints().range(1, 10).get(), " ").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.sophistication(mock.fromStrings(new ThreatActorSophistication().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.resourceLevel(mock.fromStrings(new AttackResourceLevels().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.primaryMotivation(mock.fromStrings(new AttackMotivations().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addSecondaryMotivation(mock.fromStrings(new AttackMotivations().getAllTerms().toList()).get()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addPersonalMotivation(mock.fromStrings(new AttackMotivations().getAllTerms().toList()).get()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - Tool mockTool() { - Tool.Builder builder = Tool.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addLabel(mock.fromStrings(new ToolLabels().getAllTerms().toList()).get()) - } - } - - builder.name(mock.words().get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 15).get().times { - builder.addKillChainPhase(mockKillChainPhase()) - } - } - - if (mock.bools().probability(50).get()) { - builder.toolVersion("${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}") - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - Vulnerability mockVulnerability() { - Vulnerability.Builder builder = Vulnerability.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - builder.name(mock.words().get()) - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - builder.labels(generateRandomLabels()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - BundleableObject generateRandomBundleableObject(){ - switch (mock.ints().range(1, 14).get()) { - case 1: - return mockAttackPattern() - break - case 2: - return mockCampaign() - break - case 3: - return mockCourseOfAction() - break - case 4: - return mockIdentity() - break - case 5: - return mockIndicator() - break - case 6: - return mockIntrusionSet() - break - case 7: - return mockMalware() - break - case 8: - return mockObservedData() - break - case 9: - return mockThreatActor() - break - case 10: - return mockTool() - break - case 11: - return mockVulnerability() - break - case 12: - return mockMarkingDefinition() - break - case 13: - return mockRelationship() - break - case 14: - return mockSighting() - break - default: - throw new IllegalArgumentException("Unknown Bundleable Object was attempted to be generated") - break - } - } - - Bundle mockBundle(int bundleCountLowerValue = 1, int bundleCountUpperValue = 100) { - Bundle.Builder builder = Bundle.builder() - - mock.ints().range(bundleCountLowerValue, bundleCountUpperValue).get().times { - builder.addObject(generateRandomBundleableObject()) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - return builder.build() - } - - MarkingDefinition mockMarkingDefinition() { - MarkingDefinition.Builder builder = MarkingDefinition.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - } - - String type = mock.fromStrings("tlp", "statement").get() - - builder.definitionType(type) - - switch (type) { - case "tlp": - builder.definition(mockTlpMakingObject()) - break - case "statement": - builder.definition(mockStatementMarkingObject()) - break - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - builder.build() - } - - Statement mockStatementMarkingObject() { - Statement.Builder builder = Statement.builder() - - builder.statement(mock.words().accumulate(mock.ints().range(1, 30).get(), " ").get()) - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - return builder.build() - } - - Tlp mockTlpMakingObject() { - Tlp.Builder builder = Tlp.builder() - - builder.tlp(mock.fromStrings(new TlpLevels().getAllTerms().toList()).get()) - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - return builder.build() - } - - GranularMarking mockGranularMarking() { - GranularMarking.Builder builder = GranularMarking.builder() - - builder.markingRef(mockMarkingDefinition()) - - mock.ints().range(1, 10).get().times { - builder.addSelector(mock.words().get()) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - return builder.build() - } - - Relationship mockRelationship() { - Relationship.Builder builder = Relationship.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - switch (mock.ints().range(1, 11).get()) { - case 1: - builder.sourceRef(mockAttackPattern()) - - switch (mock.ints().range(1, 2).get()) { - case 1: - builder.relationshipType("targets") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockIdentity()) - } else { - builder.targetRef(mockVulnerability()) - } - break - case 2: - builder.relationshipType("uses") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockMalware()) - } else { - builder.targetRef(mockTool()) - } - break - } - - break - - case 2: - builder.sourceRef(mockCampaign()) - - switch (mock.ints().range(1, 3).get()) { - case 1: - builder.relationshipType("attributed-to") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockIntrusionSet()) - } else { - builder.targetRef(mockThreatActor()) - } - break - case 2: - builder.relationshipType("targets") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockIdentity()) - } else { - builder.targetRef(mockVulnerability()) - } - break - case 3: - builder.relationshipType("uses") - switch (mock.ints().range(1, 3).get()) { - case 1: - builder.targetRef(mockAttackPattern()) - break - case 2: - builder.targetRef(mockMalware()) - break - case 3: - builder.targetRef(mockTool()) - break - } - break - } - break - - case 3: - builder.sourceRef(mockCourseOfAction()) - builder.relationshipType("mitigates") - - switch (mock.ints().range(1, 4).get()) { - case 1: - builder.targetRef(mockAttackPattern()) - break - case 2: - builder.targetRef(mockMalware()) - break - case 3: - builder.targetRef(mockTool()) - break - case 4: - builder.targetRef(mockVulnerability()) - break - } - break - - case 4: - builder.sourceRef(mockIndicator()) - builder.relationshipType("indicates") - - switch (mock.ints().range(1, 6).get()) { - case 1: - builder.targetRef(mockAttackPattern()) - break - case 2: - builder.targetRef(mockCampaign()) - break - case 3: - builder.targetRef(mockIntrusionSet()) - break - case 4: - builder.targetRef(mockMalware()) - break - case 5: - builder.targetRef(mockThreatActor()) - break - case 6: - builder.targetRef(mockTool()) - break - } - break - - case 5: - builder.sourceRef(mockIntrusionSet()) - - switch (mock.ints().range(1, 3).get()) { - case 1: - builder.relationshipType("attributed-to") - builder.targetRef(mockThreatActor()) - break - case 2: - builder.relationshipType("targets") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockIdentity()) - } else { - builder.targetRef(mockVulnerability()) - } - break - case 3: - builder.relationshipType("uses") - switch (mock.ints().range(1, 3).get()) { - case 1: - builder.targetRef(mockAttackPattern()) - break - case 2: - builder.targetRef(mockMalware()) - break - case 3: - builder.targetRef(mockTool()) - break - } - break - } - break - - case 6: - builder.sourceRef(mockMalware()) - - switch (mock.ints().range(1, 3).get()) { - case 1: - builder.relationshipType("targets") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockIdentity()) - } else { - builder.targetRef(mockVulnerability()) - } - break - case 2: - builder.relationshipType("uses") - builder.targetRef(mockTool()) - break - case 3: - builder.relationshipType("variant-of") - builder.targetRef(mockMalware()) - break - } - break - - case 7: - builder.sourceRef(mockThreatActor()) - - switch (mock.ints().range(1, 4).get()) { - case 1: - builder.relationshipType("attributed-to") - builder.targetRef(mockIdentity()) - break - case 2: - builder.relationshipType("impersonates") - builder.targetRef(mockIdentity()) - break - case 3: - builder.relationshipType("targets") - if (mock.bools().probability(50).get()) { - builder.targetRef(mockIdentity()) - } else { - builder.targetRef(mockVulnerability()) - } - break - case 4: - builder.relationshipType("uses") - switch (mock.ints().range(1, 3).get()) { - case 1: - builder.targetRef(mockAttackPattern()) - break - case 2: - builder.targetRef(mockMalware()) - break - case 3: - builder.targetRef(mockTool()) - break - } - break - } - break - case 8: - builder.sourceRef(mockTool()) - - builder.relationshipType("targets") - switch (mock.ints().range(1, 2).get()) { - case 1: - builder.targetRef(mockIdentity()) - break - case 2: - builder.targetRef(mockVulnerability()) - break - } - break - - case 9: - DomainObject object = mockRandomDomainObject() - - builder.sourceRef(object) - - builder.relationshipType("derived-from") - - builder.targetRef(mockRandomDomainObject(object.getType())) - - break - - case 10: - DomainObject object = mockRandomDomainObject() - - builder.sourceRef(object) - - builder.relationshipType("duplicate-of") - - builder.targetRef(mockRandomDomainObject(object.getType())) - - break - - case 11: - DomainObject object = mockRandomDomainObject() - - builder.sourceRef(object) - - builder.relationshipType("related-to") - - builder.targetRef(mockRandomDomainObject()) - - break - } - - // Extra details - - if (mock.bools().probability(50).get()) { - builder.description(mock.words().accumulate(mock.ints().range(1, 50).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - builder.labels(generateRandomLabels()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(0, 10).get().times { - builder.addExternalReferences(mockExternalReference()) - } - } - - if (mock.bools().probability(50).get()) { - builder.revoked(new StixBoolean(true)) - } - - if (mock.bools().probability(50).get()) { - builder.customProperties(generateCustomProperties()) - } - - if (mock.bools().probability(50).get()) { - builder.createdByRef(mockIdentity()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addObjectMarkingRef(mockMarkingDefinition()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGranularMarking(mockGranularMarking()) - } - } - - return builder.build() - } - - Sighting mockSighting() { - Sighting.Builder builder = Sighting.builder() - - Instant objectCreated = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.created(new StixInstant(objectCreated)) - if (mock.bools().probability(50).get()) { - builder.modified(new StixInstant(generateRandomDate(objectCreated, Instant.now()))) - } - } - - Instant firstSeen = generateRandomDate(commonLowerDate, Instant.now()) - if (mock.bools().probability(50).get()) { - builder.firstSeen(new StixInstant(firstSeen)) - } - - //@TODO This data will fail tests in the future as it create dates that are BEFORE the firstSeen. Not currently enforced - if (mock.bools().probability(50).get()) { - builder.lastSeen(new StixInstant(generateRandomDate(firstSeen, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.count(mock.ints().range(0, 999999999).get()) - } - - builder.sightingOfRef(mockRandomDomainObject()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 30).get().times { - builder.addObservedDataRef(mockObservedData()) - } - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 30).get().times { - builder.addWhereSightedRef(mockIdentity()) - } - } - - if (mock.bools().probability(20).get()) { - builder.isSummary(new StixBoolean(true)) - } - - return builder.build() - } - - DomainObject mockRandomDomainObject(String manualType) { - - String[] types = ["attack-pattern", "campaign", - "course-of-action", "identity", - "indicator", "intrusion-set", - "malware", "observed-data", - "threat-actor", "tool", - "vulnerability"] - - if (manualType == null) { - manualType = mock.fromStrings(types).get() - } else { - if (!types.contains(manualType)) { - throw new IllegalArgumentException("invalid manualType") - } - } - - switch (manualType) { - case "attack-pattern": - return mockAttackPattern() - case "campaign": - return mockCampaign() - case "course-of-action": - return mockCourseOfAction() - case "identity": - return mockIdentity() - case "indicator": - return mockIndicator() - case "intrusion-set": - return mockIntrusionSet() - case "malware": - return mockMalware() - case "observed-data": - return mockObservedData() - case "threat-actor": - return mockThreatActor() - case "tool": - return mockTool() - case "vulnerability": - return mockVulnerability() - } - } - - ArchiveFileExtension mockArchiveFileExtensionCooExt() { - ArchiveFileExtension.Builder builder = ArchiveFileExtension.builder() - - if (mock.bools().probability(33).get()) { - mock.ints().range(1, 2).get().times { - builder.addContainsRef(mockFileCoo().getObservableObjectKey()) - } - } - - if (mock.bools().probability(50).get()) { - builder.version("${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}") - } - - if (mock.bools().probability(50).get()) { - builder.comment(mock.words().accumulate(mock.ints().range(1, 20).get(), " ").get()) - } - - return builder.build() - } - - HttpRequestExtension mockHttpRequestExtensionCooExt() { - HttpRequestExtension.Builder builder = HttpRequestExtension.builder() - - builder.requestMethod(mock.fromStrings("GET", "PUT", "POST", "PATCH", "DELETE").get()) - - builder.requestValue(mock.words().accumulate(mock.ints().range(1, 30).get(), " ").get().toString()) - - builder.requestVersion(mock.fromStrings("1.0", "1.1", "2.0").get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - String key = mock.words().accumulate(mock.ints().range(2, 3).get(), "").get() - String value = mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get() - builder.putRequestHeader(key, value) - } - } - - if (mock.bools().probability(50).get()) { - builder.messageBodyLength(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.messageBodyDataRef(mockArtifactCoo().getObservableObjectKey()) - } - - return builder.build() - } - - IcmpExtension mockIcmpExtensionCooExt() { - IcmpExtension.Builder builder = IcmpExtension.builder() - - builder.icmpCodeHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - - builder.ocmpTypeHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - - return builder.build() - } - - - NetworkSocketExtension mockNetworkSocketExtensionCooExt() { - NetworkSocketExtension.Builder builder = NetworkSocketExtension.builder() - - builder.addressFamily(mock.fromStrings(new NetworkSocketAddressFamilies().getAllTerms().toList()).get()) - - if (mock.bools().probability(50).get()) { - builder.blocking(true) - } - - if (mock.bools().probability(50).get()) { - builder.listening(true) - } - - if (mock.bools().probability(50).get()) { - builder.protocolFamily(mock.fromStrings(new NetworkSocketProtocolFamilies().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - String key = mock.words().accumulate(mock.ints().range(2, 3).get(), "").get() - String value = mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get() - builder.putOption("SO_${key}", value) - } - } - - if (mock.bools().probability(50).get()) { - builder.socketType(mock.fromStrings(new NetworkSocketTypes().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.socketDescriptor(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.socketHandle(mock.longs().range(0, 999999999999).get()) - } - - return builder.build() - } - - NtfsFileExtenstion mockNtfsFileExtensionCooExt() { - NtfsFileExtenstion.Builder builder = NtfsFileExtenstion.builder() - - if (mock.bools().probability(50).get()) { - builder.sid(mock.words().get()) - } else { - if (mock.bools().probability(50).get()) { - builder.sid(mock.words().get()) - } - mock.ints().range(1, 5).get().times { - builder.addAlternateDataStream(mockNtfsAlternateDataStreamCooExtObj()) - } - } - return builder.build() - } - - NtfsAlternateDataStream mockNtfsAlternateDataStreamCooExtObj() { - NtfsAlternateDataStream.Builder builder = NtfsAlternateDataStream.builder() - - builder.name(mock.words().get()) - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(20).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.size(mock.longs().range(0, 999999999999).get()) - } - - return builder.build() - } - - PdfFileExtension mockPdfFileExtensionCooExt() { - PdfFileExtension.Builder builder = PdfFileExtension.builder() - - if (mock.bools().probability(50).get()) { - builder.version("${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}.${mock.ints().range(0, 5).get()}") - } - - builder.isOptimized(mock.bools().probability(50).get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - String key = mock.words().accumulate(mock.ints().range(2, 3).get(), "").get() - String value = mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get() - builder.putDocumentInfoDict(key, value) - } - } - - if (mock.bools().probability(50).get()) { - builder.pdfId0(mock.words().accumulate(mock.ints().range(1, 2).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.pdfId1(mock.words().accumulate(mock.ints().range(1, 2).get(), "-").get()) - } - - return builder.build() - } - - RasterImageFileExtension mockRasterImageFileExtensionCooExt() { - RasterImageFileExtension.Builder builder = RasterImageFileExtension.builder() - - builder.imageHeight(mock.longs().range(0, 999999999999).get()) - - if (mock.bools().probability(50).get()) { - builder.imageWidth(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.bitsPerPixel(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.imageCompressionAlgorithm(mock.words().accumulate(mock.ints().range(1, 2).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - String key = mock.words().accumulate(mock.ints().range(2, 3).get(), "-").get() - String value = mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get() - builder.putExifTag(key, value) - } - } - - return builder.build() - } - - TcpExtension mockTcpExtensionCooExt() { - TcpExtension.Builder builder = TcpExtension.builder() - - builder.srcFlagsHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - - builder.dstFlagsHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - - return builder.build() - } - - UnixAccountExtension mockUnixAccountExtensionCooExt() { - UnixAccountExtension.Builder builder = UnixAccountExtension.builder() - - builder.gid(mock.longs().range(0, 999999999999).get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addGroup(mock.words().get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.homeDir("./${mock.words().accumulate(mock.ints().range(1, 5).get(), "/").get()}") - } - - if (mock.bools().probability(50).get()) { - builder.shell("${mock.words().get()} ${mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get()}") - } - - return builder.build() - } - - WindowsPeBinaryFileExtension mockWindowsPeBinaryFileExtensionCooExt() { - WindowsPeBinaryFileExtension.Builder builder = WindowsPeBinaryFileExtension.builder() - - builder.peType(mock.fromStrings(new WindowsPeBinaryTypes().getAllTerms().toList()).get()) - - if (mock.bools().probability(50).get()) { - builder.imphash(mock.hashes().md5().get()) - } - - if (mock.bools().probability(50).get()) { - builder.machineHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.numberOfSections(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.timeDateStamp(new StixInstant(generateRandomDate(commonLowerDate, Instant.now()))) - } - - if (mock.bools().probability(50).get()) { - builder.pointerToSymbolTableHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.numberOfSymbols(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfOptionalHeader(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.characteristicsHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(20).get()) { - builder.putFileHeaderHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putFileHeaderHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putFileHeaderHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putFileHeaderHash("SHA-1", mock.hashes().sha1().get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.optionalHeader(mockWindowsPeOptionalHeaderCooExtObj()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addSection(mockWindowsPeSectionCooExtObj()) - } - } - - return builder.build() - } - - WindowsPeOptionalHeader mockWindowsPeOptionalHeaderCooExtObj() { - WindowsPeOptionalHeader.Builder builder = WindowsPeOptionalHeader.builder() - - builder.magicHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - - if (mock.bools().probability(50).get()) { - builder.majorLinkerVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.minorLinkerVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfCode(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfInitializedData(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.addressOfEntryPoint(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.baseOfCode(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.baseOfData(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.imageBase(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sectionAlignment(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.fileAlignment(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.majorOsVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.minorOsVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.majorImageVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.minorImageVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.majorSubsystemVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.minorSubsystemVersion(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.win32VersionValueHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfImage(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfHeaders(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.checksumHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.subsystemHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.dllCharacteristicsHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfStackReserve(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfStackCommit(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfHeapReserve(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.sizeOfHeapCommit(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.loaderFlagsHex(mock.chars().hex().get().toString() + mock.chars().hex().get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.numberOfRvaAndSizes(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(20).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - } - - return builder.build() - } - - WindowsPeSection mockWindowsPeSectionCooExtObj() { - WindowsPeSection.Builder builder = WindowsPeSection.builder() - - if (mock.bools().probability(50).get()) { - builder.name(mock.words().accumulate(mock.ints().range(1, 5).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.size(mock.longs().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - builder.entropy(mock.floats().range(0, 999999999999).get()) - } - - if (mock.bools().probability(50).get()) { - if (mock.bools().probability(20).get()) { - builder.putHash("MD5", mock.hashes().md5().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-256", mock.hashes().sha256().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-512", mock.hashes().sha512().get()) - } - - if (mock.bools().probability(20).get()) { - builder.putHash("SHA-1", mock.hashes().sha1().get()) - } - } - - return builder.build() - } - - WindowsProcessExtension mockWindowsProcessExtensionCooExt() { - WindowsProcessExtension.Builder builder = WindowsProcessExtension.builder() - - if (mock.bools().probability(50).get()) { - builder.isAslrEnabled(true) - } else { - builder.isAslrEnabled(false) - builder.isDepEnabled(mock.bools().probability(50).get()) - } - - if (mock.bools().probability(50).get()) { - builder.priority(mock.words().accumulate(mock.ints().range(1, 3).get(), "_").get()) - } - - if (mock.bools().probability(50).get()) { - builder.ownerSid(mock.ints().range(1, 999999).get().toString()) - } - - if (mock.bools().probability(50).get()) { - builder.windowTitle(mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - String key = mock.words().accumulate(mock.ints().range(2, 3).get(), "-").get() - String value = mock.words().accumulate(mock.ints().range(1, 5).get(), " ").get() - builder.putStartupInfo(key, value) - } - } - - return builder.build() - } - - WindowsServiceExtension mockWindowsServiceExtensionCooExt() { - WindowsServiceExtension.Builder builder = WindowsServiceExtension.builder() - - builder.serviceName(mock.words().accumulate(mock.ints().range(1, 3).get(), "-").get()) - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 5).get().times { - builder.addDescription(mock.words().accumulate(mock.ints().range(1, 10).get(), " ").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.displayName(mock.words().accumulate(mock.ints().range(1, 2).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.groupName(mock.words().accumulate(mock.ints().range(1, 2).get(), "-").get()) - } - - if (mock.bools().probability(50).get()) { - builder.serviceStartType(mock.fromStrings(new WindowsServiceStartTypes().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - mock.ints().range(1, 10).get().times { - builder.addServiceDllRef(mock.words().accumulate(mock.ints().range(1, 3).get(), "-").get()) - } - } - - if (mock.bools().probability(50).get()) { - builder.serviceType(mock.fromStrings(new WindowsServiceTypes().getAllTerms().toList()).get()) - } - - if (mock.bools().probability(50).get()) { - builder.serviceStatus(mock.fromStrings(new WindowsServiceStatuses().getAllTerms().toList()).get()) - } - - return builder.build() - } -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/ObservedDataGeneratorConfig.groovy b/src/test/groovy/faker/configs/ObservedDataGeneratorConfig.groovy deleted file mode 100644 index c144e15..0000000 --- a/src/test/groovy/faker/configs/ObservedDataGeneratorConfig.groovy +++ /dev/null @@ -1,87 +0,0 @@ -package faker.configs - -import faker.configs.sdo.observeddata.ObservedData_ArtifactCooConfig -import faker.configs.sdo.observeddata.ObservedData_AutonomousSystemCooConfig -import faker.configs.sdo.observeddata.ObservedData_DirectoryCooConfig -import faker.configs.sdo.observeddata.ObservedData_DomainNameCooConfig -import faker.configs.sdo.observeddata.ObservedData_EmailAddressCooConfig -import faker.configs.sdo.observeddata.ObservedData_EmailMessageCooConfig -import faker.configs.sdo.observeddata.ObservedData_ExternalReferencesConfig -import faker.configs.sdo.observeddata.ObservedData_FileCooConfig -import faker.configs.sdo.observeddata.ObservedData_GranularMarkingConfig -import faker.configs.sdo.observeddata.ObservedData_Ipv4AddressCooConfig -import faker.configs.sdo.observeddata.ObservedData_Ipv6AddressCooConfig -import faker.configs.sdo.observeddata.ObservedData_MacAddressCooConfig -import faker.configs.sdo.observeddata.ObservedData_MutexCooConfig -import faker.configs.sdo.observeddata.ObservedData_NetworkTrafficCooConfig -import faker.configs.sdo.observeddata.ObservedData_ObjectMarkingsConfig -import faker.configs.sdo.observeddata.ObservedData_ProcessCooConfig -import faker.configs.sdo.observeddata.ObservedData_SoftwareCooConfig -import faker.configs.sdo.observeddata.ObservedData_UrlCooConfig -import faker.configs.sdo.observeddata.ObservedData_UserAccountCooConfig -import faker.configs.sdo.observeddata.ObservedData_WindowsRegistryKeyCooConfig -import faker.configs.sdo.observeddata.ObservedData_X509CertificateCooConfig - -import java.time.Instant -import java.time.LocalDate - -class ObservedDataGeneratorConfig { - - private Instant DEFAULT_LOWER_DATE = Instant.ofEpochMilli(LocalDate.of(2000, 1, 1).toEpochDay()) - private Instant DEFAULT_UPPER_DATE = Instant.now() - private int DEFAULT_DATE_SUBSECOND_PRECISION = 3 - - int propCreatedProbability = 50 - Instant propCreatedLowerDate = DEFAULT_LOWER_DATE - Instant propCreatedUpperDate = DEFAULT_UPPER_DATE - int propCreatedDateSubsecondPrecision = DEFAULT_DATE_SUBSECOND_PRECISION - - int propModifiedProbability = 50 - Instant propModifiedUpperDate = DEFAULT_UPPER_DATE - int propModifiedSubsecondPrecision = DEFAULT_DATE_SUBSECOND_PRECISION - - int propRevokedProbability = 50 - - int propCreatedByRefProbability = 50 - - int propCustomPropsProbability = 50 - - Instant propFirstObservedLowerDate = DEFAULT_LOWER_DATE - Instant propFirstObservedUpperDate = DEFAULT_UPPER_DATE - int propFirstObservedSubsecondPrecision = DEFAULT_DATE_SUBSECOND_PRECISION - - Instant propLastObservedUpperDate = DEFAULT_UPPER_DATE - int propLastObservedSubsecondPrecision = DEFAULT_DATE_SUBSECOND_PRECISION - - int propNumberObservedUpperCount = 999999999 - int propNumberObservedLowerCount = 1 - - ObservedData_ArtifactCooConfig artifactCoo = new ObservedData_ArtifactCooConfig() - ObservedData_AutonomousSystemCooConfig autonomousSystemCoo = new ObservedData_AutonomousSystemCooConfig() - ObservedData_DirectoryCooConfig directoryCoo = new ObservedData_DirectoryCooConfig() - ObservedData_DomainNameCooConfig domainNameCoo = new ObservedData_DomainNameCooConfig() - ObservedData_EmailAddressCooConfig emailAddressCoo = new ObservedData_EmailAddressCooConfig() - ObservedData_EmailMessageCooConfig emailMessageCoo = new ObservedData_EmailMessageCooConfig() - ObservedData_FileCooConfig fileCoo = new ObservedData_FileCooConfig() - ObservedData_Ipv4AddressCooConfig ipv4AddressCoo = new ObservedData_Ipv4AddressCooConfig() - ObservedData_Ipv6AddressCooConfig ipv6AddressCoo = new ObservedData_Ipv6AddressCooConfig() - ObservedData_MacAddressCooConfig macAddressCoo = new ObservedData_MacAddressCooConfig() - ObservedData_MutexCooConfig mutexCoo = new ObservedData_MutexCooConfig() - ObservedData_NetworkTrafficCooConfig networkTrafficCoo = new ObservedData_NetworkTrafficCooConfig() - ObservedData_ProcessCooConfig processCoo = new ObservedData_ProcessCooConfig() - ObservedData_SoftwareCooConfig softwareCoo = new ObservedData_SoftwareCooConfig() - ObservedData_UrlCooConfig urlCoo = new ObservedData_UrlCooConfig() - ObservedData_UserAccountCooConfig userAccountCoo = new ObservedData_UserAccountCooConfig() - ObservedData_WindowsRegistryKeyCooConfig windowsRegisteryKeyCoo = new ObservedData_WindowsRegistryKeyCooConfig() - ObservedData_X509CertificateCooConfig x509CertificateCoo = new ObservedData_X509CertificateCooConfig() - - - ObservedData_ExternalReferencesConfig externalReferences = new ObservedData_ExternalReferencesConfig() - - ObservedData_GranularMarkingConfig granularMarkings = new ObservedData_GranularMarkingConfig() - - ObservedData_ObjectMarkingsConfig objectMarkings = new ObservedData_ObjectMarkingsConfig() - - int labelsOccurrenceProbability = 30 - -} diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ArtifactCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ArtifactCooConfig.groovy deleted file mode 100644 index 802790d..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ArtifactCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_ArtifactCooConfig{ - int occurrence_probability = 100 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_AutonomousSystemCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_AutonomousSystemCooConfig.groovy deleted file mode 100644 index 1403b1a..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_AutonomousSystemCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_AutonomousSystemCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DirectoryCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DirectoryCooConfig.groovy deleted file mode 100644 index 1a964c3..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DirectoryCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_DirectoryCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DomainNameCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DomainNameCooConfig.groovy deleted file mode 100644 index 30c2ea1..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_DomainNameCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_DomainNameCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailAddressCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailAddressCooConfig.groovy deleted file mode 100644 index f073e1f..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailAddressCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_EmailAddressCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailMessageCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailMessageCooConfig.groovy deleted file mode 100644 index 41cae8d..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_EmailMessageCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_EmailMessageCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ExternalReferencesConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ExternalReferencesConfig.groovy deleted file mode 100644 index f65c832..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ExternalReferencesConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_ExternalReferencesConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_FileCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_FileCooConfig.groovy deleted file mode 100644 index 466a7cd..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_FileCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_FileCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_GranularMarkingConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_GranularMarkingConfig.groovy deleted file mode 100644 index ca50fc4..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_GranularMarkingConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_GranularMarkingConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv4AddressCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv4AddressCooConfig.groovy deleted file mode 100644 index fe5bbd3..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv4AddressCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_Ipv4AddressCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv6AddressCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv6AddressCooConfig.groovy deleted file mode 100644 index 6c9a2d1..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_Ipv6AddressCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_Ipv6AddressCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MacAddressCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MacAddressCooConfig.groovy deleted file mode 100644 index ce49157..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MacAddressCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_MacAddressCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MutexCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MutexCooConfig.groovy deleted file mode 100644 index 75751f5..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_MutexCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_MutexCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_NetworkTrafficCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_NetworkTrafficCooConfig.groovy deleted file mode 100644 index 4f1dfe9..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_NetworkTrafficCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_NetworkTrafficCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ObjectMarkingsConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ObjectMarkingsConfig.groovy deleted file mode 100644 index 258959d..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ObjectMarkingsConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_ObjectMarkingsConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ProcessCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ProcessCooConfig.groovy deleted file mode 100644 index 26a6f3b..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_ProcessCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_ProcessCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_SoftwareCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_SoftwareCooConfig.groovy deleted file mode 100644 index 70a6030..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_SoftwareCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_SoftwareCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UrlCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UrlCooConfig.groovy deleted file mode 100644 index de26ceb..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UrlCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_UrlCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UserAccountCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UserAccountCooConfig.groovy deleted file mode 100644 index 653876f..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_UserAccountCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_UserAccountCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_WindowsRegistryKeyCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_WindowsRegistryKeyCooConfig.groovy deleted file mode 100644 index b2b183c..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_WindowsRegistryKeyCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_WindowsRegistryKeyCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_X509CertificateCooConfig.groovy b/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_X509CertificateCooConfig.groovy deleted file mode 100644 index ca82ab9..0000000 --- a/src/test/groovy/faker/configs/sdo/observeddata/ObservedData_X509CertificateCooConfig.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package faker.configs.sdo.observeddata - -class ObservedData_X509CertificateCooConfig { - int occurrence_probability = 10 - int occurs_count_lower = 1 - int occurs_count_upper = 5 -} \ No newline at end of file diff --git a/src/test/groovy/stix/bundle/BundleGraphSpec.groovy b/src/test/groovy/stix/bundle/BundleGraphSpec.groovy deleted file mode 100644 index abccfb3..0000000 --- a/src/test/groovy/stix/bundle/BundleGraphSpec.groovy +++ /dev/null @@ -1,69 +0,0 @@ -package stix.bundle - -import io.digitalstate.stix.bundle.Bundle -import io.digitalstate.stix.common.StixInstant -import io.digitalstate.stix.coo.objects.DomainName -import io.digitalstate.stix.coo.objects.Ipv4Address -import io.digitalstate.stix.graph.StixGraphGenerator -import io.digitalstate.stix.sdo.objects.AttackPattern -import io.digitalstate.stix.sdo.objects.ObservedData -import io.digitalstate.stix.sro.objects.Relationship -import io.digitalstate.stix.sro.objects.Sighting -import spock.lang.Specification - -class BundleGraphSpec extends Specification { - - def "Generate a Simple Graph of STIX data"() { - when: "Create Objects" - AttackPattern attackPattern1 = AttackPattern.builder().name("attk1").build() - AttackPattern attackPattern2 = AttackPattern.builder().name("attk2").build() - - Relationship relationship1 = Relationship.builder() - .sourceRef(attackPattern1) - .targetRef(attackPattern2) - .relationshipType("related-to") - .build() - - DomainName domainName1 = DomainName.builder() - .value("http://google.com") - .build() - Ipv4Address ip1 = Ipv4Address.builder().value("10.10.10.10").build() - Ipv4Address ip2 = Ipv4Address.builder().value("10.10.10.11").build() - Ipv4Address ip3 = Ipv4Address.builder().value("10.10.10.12").build() - Ipv4Address ip4 = Ipv4Address.builder().value("10.10.10.13").build() - Ipv4Address ip5 = Ipv4Address.builder().value("10.10.10.14").build() - Ipv4Address ip6 = Ipv4Address.builder().value("10.10.10.15").build() - - ObservedData observedData1 = ObservedData.builder() - .addObjects(domainName1, ip1, ip2, ip3, ip4, ip5, ip6) - .firstObserved(new StixInstant()) - .lastObserved(new StixInstant()) - .numberObserved(2) - .build() - - Sighting sighting1 = Sighting.builder() - .firstSeen(new StixInstant()) - .lastSeen(new StixInstant()) - .count(1) - .sightingOfRef(attackPattern1) - .addObservedDataRef(observedData1) - .build() - - then: "build bundle" - - Bundle bundle = Bundle.builder() - .addObjects(attackPattern1, attackPattern2, relationship1, observedData1, sighting1) - .build() - - println bundle.toString() - - then: "generate graph json" - - StixGraphGenerator graph = new StixGraphGenerator(bundle) - - println graph.process().toString() - - println graph.toJson() - - } -} diff --git a/src/test/groovy/stix/bundle/BundleSpec.groovy b/src/test/groovy/stix/bundle/BundleSpec.groovy deleted file mode 100644 index 0562338..0000000 --- a/src/test/groovy/stix/bundle/BundleSpec.groovy +++ /dev/null @@ -1,125 +0,0 @@ -package stix.bundle - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.TreeNode -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.node.ArrayNode -import io.digitalstate.stix.bundle.Bundle -import io.digitalstate.stix.bundle.BundleableObject -import io.digitalstate.stix.json.StixParsers -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Ignore -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class BundleSpec extends Specification { - - @Shared - ObjectMapper mapper = new ObjectMapper() - @Shared - StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Bundle Data: Run: '#i'"() { - when: "Generating Bundle Data" - Bundle originalBundle = stixMockDataGenerator.mockBundle(1, 2) - - JsonNode originalJson = mapper.readTree(originalBundle.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Bundle Object" - Bundle parsedBundle = (Bundle) StixParsers.parseBundle(originalJsonString) - Bundle parsedBundleGeneric = StixParsers.parse(originalJsonString, Bundle.class) -// println "Parsed Object: ${parsedBundle}" - - //@TODO needs to be setup to handle dehydrated object comparison - then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Bundle back to into Json" - JsonNode newJson = mapper.readTree(parsedBundle.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations - } - - def "Parse Indicator Bundle"() { - - when: "setup file access to bundle" - - String bundleJson = getClass() - .getResource("/stix/baseline/json/sdo/indicator/indicators.json").getText("UTF-8") - - then: "Parse json into bundle" - Bundle bundle = (Bundle) StixParsers.parseBundle(bundleJson) -// println bundle.toJsonString() - - and: "the original bundle json matches the parsed object that was converted back to json" - assert mapper.readTree(bundle.toJsonString()) == mapper.readTree(bundleJson) - } - - @Ignore - def "Generate File of a large Bundle"() { - when: "Generating Bundle Data" - Bundle originalBundle = stixMockDataGenerator.mockBundle(1, 2) - - then: "Convert Bundle to Json" - FileWriter fileWriter = new FileWriter(new File("./bundle-large.json")) - - JsonGenerator jg = StixParsers.getJsonMapper() - .getFactory() - .createGenerator(fileWriter) - - // Change this value to generate different sizes - int objectCount = 50000 - - jg.writeStartObject() - - jg.writeStringField("id", originalBundle.getId()) - jg.writeStringField("type", originalBundle.getType()) - jg.writeStringField("spec_version", originalBundle.getSpecVersion()) - - jg.writeArrayFieldStart("objects") - objectCount.times({ o -> - jg.writeObject(stixMockDataGenerator.generateRandomBundleableObject()) - }) - jg.writeEndArray() - - jg.writeEndObject() - - jg.close() - fileWriter.close() - } - - @Ignore - def "Parse Large Bundle"() { - when: - File file = new File("./bundle-large.json") - - then: - JsonParser parser = StixParsers.getJsonMapper().getFactory().createParser(file) - - then: - TreeNode tree = parser.readValueAsTree() - TreeNode objectsArray = tree.get("objects") - if (objectsArray.isArray()){ - ((ArrayNode)objectsArray).each {o -> - BundleableObject bundleableObject = o.traverse(parser.getCodec()) - .readValueAs(BundleableObject.class) - - println bundleableObject - } - } - } -} diff --git a/src/test/groovy/stix/custom/CustomObjectSpec.groovy b/src/test/groovy/stix/custom/CustomObjectSpec.groovy deleted file mode 100644 index 49d4927..0000000 --- a/src/test/groovy/stix/custom/CustomObjectSpec.groovy +++ /dev/null @@ -1,30 +0,0 @@ -package stix.custom - -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.bundle.BundleableObject -import io.digitalstate.stix.custom.StixCustomObject -import io.digitalstate.stix.custom.objects.CustomObject -import io.digitalstate.stix.json.StixParsers -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll - -class CustomObjectSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - - @Unroll - def "Generic Object Test 1"() { - when: "Attempt to Parse a custom object" - String jsonString = getClass().getResource("/stix/custom/custom_object_1.json").getText("UTF-8") - - then: - StixCustomObject originalObject = (StixCustomObject)StixParsers.parseObject(jsonString) - StixCustomObject originalObjectGeneric = StixParsers.parse(jsonString, CustomObject.class) - BundleableObject bundleableObject = StixParsers.parse(jsonString, BundleableObject.class) -// println originalObject -// println originalObjectGeneric -// println bundleableObject -// println "********" - } -} diff --git a/src/test/groovy/stix/datamarkings/MarkingDefinitionSpec.groovy b/src/test/groovy/stix/datamarkings/MarkingDefinitionSpec.groovy deleted file mode 100644 index 4603a67..0000000 --- a/src/test/groovy/stix/datamarkings/MarkingDefinitionSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.datamarkings - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.datamarkings.MarkingDefinition -import io.digitalstate.stix.json.StixParsers -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class MarkingDefinitionSpec extends Specification{ - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Marking Definition Data: Run: '#i'"() { - when: "Generating Marking Definition Data" - MarkingDefinition originalMarkingDefinition = stixMockDataGenerator.mockMarkingDefinition() -// println "Original Object: ${originalMarkingDefinition.toString()}" - - then: "Convert Marking Definition to Json" - JsonNode originalJson = mapper.readTree(originalMarkingDefinition.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Marking Definition Object" - MarkingDefinition parsedMarkingDefinition = (MarkingDefinition)StixParsers.parseObject(originalJsonString) - MarkingDefinition parsedMarkingDefinitionGeneric = StixParsers.parse(originalJsonString, MarkingDefinition.class) -// println "Parsed Object: ${parsedMarkingDefinition}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalMarkingDefinition == parsedMarkingDefinition - - then: "Convert Parsed Marking Definition Object back to into Json" - JsonNode newJson = mapper.readTree(parsedMarkingDefinition.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/AttackPatternSpec.groovy b/src/test/groovy/stix/sdo/AttackPatternSpec.groovy deleted file mode 100644 index bec51f0..0000000 --- a/src/test/groovy/stix/sdo/AttackPatternSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.AttackPattern -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class AttackPatternSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Attack Pattern Data: Run: '#i'"() { - when: "Generating Attack Pattern Data" - AttackPattern originalAttackPattern = stixMockDataGenerator.mockAttackPattern() -// println "Original Object: ${originalAttackPattern.toString()}" - - then: "Convert Attack Pattern to Json" - JsonNode originalJson = mapper.readTree(originalAttackPattern.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Attack Pattern Object" - AttackPattern parsedAttackPattern = (AttackPattern)StixParsers.parseObject(originalJsonString) - AttackPattern parsedAttackPatternGeneric = StixParsers.parse(originalJsonString, AttackPattern.class) -// println "Parsed Object: ${parsedAttackPattern}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Attack Pattern Object back to into Json" - JsonNode newJson = mapper.readTree(parsedAttackPattern.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/CampaignSpec.groovy b/src/test/groovy/stix/sdo/CampaignSpec.groovy deleted file mode 100644 index e84bb4b..0000000 --- a/src/test/groovy/stix/sdo/CampaignSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Campaign -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class CampaignSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Campaign Data: Run: '#i'"() { - when: "Generating Campaign Data" - Campaign originalCampaign = stixMockDataGenerator.mockCampaign() -// println "Original Object: ${originalCampaign.toString()}" - - then: "Convert Campaign to Json" - JsonNode originalJson = mapper.readTree(originalCampaign.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Campaign Object" - Campaign parsedCampaign = (Campaign)StixParsers.parseObject(originalJsonString) - Campaign parsedCampaignGeneric = StixParsers.parse(originalJsonString, Campaign.class) -// println "Parsed Object: ${parsedCampaign}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Identity Object back to into Json" - JsonNode newJson = mapper.readTree(parsedCampaign.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/CourseOfActionSpec.groovy b/src/test/groovy/stix/sdo/CourseOfActionSpec.groovy deleted file mode 100644 index 6d11b4a..0000000 --- a/src/test/groovy/stix/sdo/CourseOfActionSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.CourseOfAction -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class CourseOfActionSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Course of Action Data: Run: '#i'"() { - when: "Generating Identity Data" - CourseOfAction originalCourseOfAction = stixMockDataGenerator.mockCourseOfAction() -// println "Original Object: ${originalCourseOfAction.toString()}" - - then: "Convert Course of Action to Json" - JsonNode originalJson = mapper.readTree(originalCourseOfAction.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Course of Action Object" - CourseOfAction parsedCourseOfAction = (CourseOfAction)StixParsers.parseObject(originalJsonString) - CourseOfAction parsedCourseOfActionGeneric = StixParsers.parse(originalJsonString, CourseOfAction.class) -// println "Parsed Object: ${parsedCourseOfAction}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Identity Object back to into Json" - JsonNode newJson = mapper.readTree(parsedCourseOfAction.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/IdentitySpec.groovy b/src/test/groovy/stix/sdo/IdentitySpec.groovy deleted file mode 100644 index a95359e..0000000 --- a/src/test/groovy/stix/sdo/IdentitySpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Identity -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class IdentitySpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Identity Data: Run: '#i'"() { - when: "Generating Identity Data" - Identity originalIdentity = stixMockDataGenerator.mockIdentity() -// println "Original Object: ${originalIdentity.toString()}" - - then: "Convert Identity to Json" - JsonNode originalJson = mapper.readTree(originalIdentity.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Attack Pattern Object" - Identity parsedIdentity = (Identity)StixParsers.parseObject(originalJsonString) - Identity parsedIdentityGeneric = StixParsers.parse(originalJsonString, Identity.class) -// println "Parsed Object: ${parsedIdentity}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Identity Object back to into Json" - JsonNode newJson = mapper.readTree(parsedIdentity.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/IndicatorSpec.groovy b/src/test/groovy/stix/sdo/IndicatorSpec.groovy deleted file mode 100644 index 8580dbc..0000000 --- a/src/test/groovy/stix/sdo/IndicatorSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Indicator -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class IndicatorSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Indicator Data: Run: '#i'"() { - when: "Generating Indicator Data" - Indicator originalIndicator = stixMockDataGenerator.mockIndicator() -// println "Original Object: ${originalIndicator.toString()}" - - then: "Convert Indicator to Json" - JsonNode originalJson = mapper.readTree(originalIndicator.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Indicator Object" - Indicator parsedIndicator = (Indicator)StixParsers.parseObject(originalJsonString) - Indicator parsedIndicatorGeneric = StixParsers.parse(originalJsonString, Indicator.class) -// println "Parsed Object: ${parsedIndicator}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Indicator back to into Json" - JsonNode newJson = mapper.readTree(parsedIndicator.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/IntrusionSetSpec.groovy b/src/test/groovy/stix/sdo/IntrusionSetSpec.groovy deleted file mode 100644 index 4c18fe1..0000000 --- a/src/test/groovy/stix/sdo/IntrusionSetSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.IntrusionSet -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class IntrusionSetSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Intrusion Set Data: Run: '#i'"() { - when: "Generating Intrusion Set Data" - IntrusionSet originalIntrusionSet = stixMockDataGenerator.mockIntrusionSet() -// println "Original Object: ${originalIntrusionSet.toString()}" - - then: "Convert Intrusion Set to Json" - JsonNode originalJson = mapper.readTree(originalIntrusionSet.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Intrusion Set Object" - IntrusionSet parsedIntrusionSet = (IntrusionSet)StixParsers.parseObject(originalJsonString) - IntrusionSet parsedIntrusionSetGeneric = StixParsers.parse(originalJsonString, IntrusionSet.class) -// println "Parsed Object: ${parsedIntrusionSet}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Intrusion Set back to into Json" - JsonNode newJson = mapper.readTree(parsedIntrusionSet.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/MalwareSpec.groovy b/src/test/groovy/stix/sdo/MalwareSpec.groovy deleted file mode 100644 index b047e9b..0000000 --- a/src/test/groovy/stix/sdo/MalwareSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Malware -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class MalwareSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Malware Data: Run: '#i'"() { - when: "Generating Malware Data" - Malware originalMalware = stixMockDataGenerator.mockMalware() -// println "Original Object: ${originalMalware.toString()}" - - then: "Convert Malware to Json" - JsonNode originalJson = mapper.readTree(originalMalware.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Malware Object" - Malware parsedMalware = (Malware)StixParsers.parseObject(originalJsonString) - Malware parsedMalwareGeneric = StixParsers.parse(originalJsonString, Malware.class) -// println "Parsed Object: ${parsedMalware}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Malware back to into Json" - JsonNode newJson = mapper.readTree(parsedMalware.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sdo/ObservedDataSpec.groovy b/src/test/groovy/stix/sdo/ObservedDataSpec.groovy deleted file mode 100644 index f83d202..0000000 --- a/src/test/groovy/stix/sdo/ObservedDataSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.ObservedData -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class ObservedDataSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Observed-Data Data: Run: '#i'"() { - when: "Generating Observed-Data Data" - ObservedData originalObservedData = stixMockDataGenerator.mockObservedData() -// println "Original Object: ${originalObservedData.toString()}" - - then: "Convert Observed-Data to Json" - JsonNode originalJson = mapper.readTree(originalObservedData.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Observed-Data Object" - ObservedData parsedObservedData = (ObservedData)StixParsers.parseObject(originalJsonString) - ObservedData parsedObservedDataGeneric = StixParsers.parse(originalJsonString, ObservedData.class) -// println "Parsed Object: ${parsedObservedData}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Observed-Data back to into Json" - JsonNode newJson = mapper.readTree(parsedObservedData.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..5000) // More tests are run because of the large variation of probabilities and number of combinations - } -} diff --git a/src/test/groovy/stix/sdo/ReportSpec.groovy b/src/test/groovy/stix/sdo/ReportSpec.groovy deleted file mode 100644 index f14288e..0000000 --- a/src/test/groovy/stix/sdo/ReportSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Report -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class ReportSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Report Data: Run: '#i'"() { - when: "Generating Report Data" - Report originalReport = stixMockDataGenerator.mockReport() -// println "Original Object: ${originalReport.toString()}" - - then: "Convert Report to Json" - JsonNode originalJson = mapper.readTree(originalReport.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Report Object" - Report parsedReport = (Report)StixParsers.parseObject(originalJsonString) - Report parsedReportGeneric = StixParsers.parse(originalJsonString, Report.class) -// println "Parsed Object: ${parsedReport}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Report back to into Json" - JsonNode newJson = mapper.readTree(parsedReport.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..500) // More tests are run because of the large variation of probabilities and number of combinations - } -} diff --git a/src/test/groovy/stix/sdo/ThreatActorSpec.groovy b/src/test/groovy/stix/sdo/ThreatActorSpec.groovy deleted file mode 100644 index 6bf5023..0000000 --- a/src/test/groovy/stix/sdo/ThreatActorSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.ThreatActor -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class ThreatActorSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Threat Actor Data: Run: '#i'"() { - when: "Generating Threat Actor Data" - ThreatActor originalThreatActor = stixMockDataGenerator.mockThreatActor() -// println "Original Object: ${originalThreatActor.toString()}" - - then: "Convert Threat Actor to Json" - JsonNode originalJson = mapper.readTree(originalThreatActor.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Threat Actor Object" - ThreatActor parsedThreatActor = (ThreatActor)StixParsers.parseObject(originalJsonString) - ThreatActor parsedThreatActorGeneric = StixParsers.parse(originalJsonString, ThreatActor.class) -// println "Parsed Object: ${parsedThreatActor}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Threat Actor back to into Json" - JsonNode newJson = mapper.readTree(parsedThreatActor.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations - } -} diff --git a/src/test/groovy/stix/sdo/ToolSpec.groovy b/src/test/groovy/stix/sdo/ToolSpec.groovy deleted file mode 100644 index 13e1b00..0000000 --- a/src/test/groovy/stix/sdo/ToolSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Tool -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class ToolSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Tool Data: Run: '#i'"() { - when: "Generating Tool Data" - Tool originalTool = stixMockDataGenerator.mockTool() -// println "Original Object: ${originalTool.toString()}" - - then: "Convert Tool to Json" - JsonNode originalJson = mapper.readTree(originalTool.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Tool Object" - Tool parsedTool = (Tool)StixParsers.parseObject(originalJsonString) - Tool parsedToolGeneric = StixParsers.parse(originalJsonString, Tool.class) -// println "Parsed Object: ${parsedTool}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Tool back to into Json" - JsonNode newJson = mapper.readTree(parsedTool.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations - } -} diff --git a/src/test/groovy/stix/sdo/VulnerabilitySpec.groovy b/src/test/groovy/stix/sdo/VulnerabilitySpec.groovy deleted file mode 100644 index b09d2ad..0000000 --- a/src/test/groovy/stix/sdo/VulnerabilitySpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sdo - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Vulnerability -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class VulnerabilitySpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Vulnerability Data: Run: '#i'"() { - when: "Generating Vulnerability Data" - Vulnerability originalVulnerability = stixMockDataGenerator.mockVulnerability() -// println "Original Object: ${originalVulnerability.toString()}" - - then: "Convert Vulnerability to Json" - JsonNode originalJson = mapper.readTree(originalVulnerability.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Vulnerability Object" - Vulnerability parsedVulnerability = (Vulnerability)StixParsers.parseObject(originalJsonString) - Vulnerability parsedVulnerabilityGeneric = StixParsers.parse(originalJsonString, Vulnerability.class) -// println "Parsed Object: ${parsedVulnerability}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalAttackPattern == parsedAttackPattern - - then: "Convert Parsed Vulnerability back to into Json" - JsonNode newJson = mapper.readTree(parsedVulnerability.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) // More tests are run because of the large variation of probabilities and number of combinations - } -} diff --git a/src/test/groovy/stix/sro/RelationshipSpec.groovy b/src/test/groovy/stix/sro/RelationshipSpec.groovy deleted file mode 100644 index fa6b38d..0000000 --- a/src/test/groovy/stix/sro/RelationshipSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sro - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sro.objects.Relationship -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class RelationshipSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Relationship SRO Data: Run: '#i'"() { - when: "Generating Relationship SRO Data" - Relationship originalRelationship = stixMockDataGenerator.mockRelationship() -// println "Original Object: ${originalRelationship.toString()}" - - then: "Convert Relationship to Json" - JsonNode originalJson = mapper.readTree(originalRelationship.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Relationship Object" - Relationship parsedRelationship = (Relationship)StixParsers.parseObject(originalJsonString) - Relationship parsedRelationshipGeneric = StixParsers.parse(originalJsonString, Relationship.class) -// println "Parsed Object: ${parsedRelationship}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalRelationship == parsedMarkingDefinition - - then: "Convert Parsed Relationship Object back to into Json" - JsonNode newJson = mapper.readTree(parsedRelationship.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/sro/SightingSpec.groovy b/src/test/groovy/stix/sro/SightingSpec.groovy deleted file mode 100644 index 9edca11..0000000 --- a/src/test/groovy/stix/sro/SightingSpec.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package stix.sro - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sro.objects.Sighting -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Unroll -import faker.StixMockDataGenerator - -class SightingSpec extends Specification { - - @Shared ObjectMapper mapper = new ObjectMapper() - @Shared StixMockDataGenerator stixMockDataGenerator = new StixMockDataGenerator() - - @Unroll - def "Generate Sighting SRO Data: Run: '#i'"() { - when: "Generating Sighting SRO Data" - Sighting originalSighting = stixMockDataGenerator.mockSighting() -// println "Original Object: ${originalSighting.toString()}" - - then: "Convert Sighting to Json" - JsonNode originalJson = mapper.readTree(originalSighting.toJsonString()) - String originalJsonString = mapper.writeValueAsString(originalJson) -// println "Original Json: ${originalJsonString}" - - then: "Parse Json back into Sighting Object" - Sighting parsedSighting = (Sighting)StixParsers.parseObject(originalJsonString) - Sighting parsedSightingGeneric = StixParsers.parse(originalJsonString, Sighting.class) -// println "Parsed Object: ${parsedSighting}" - - //@TODO needs to be setup to handle dehydrated object comparison -// then: "Parsed object should match Original object" -// assert originalSighting == parsedMarkingDefinition - - then: "Convert Parsed Sighting Object back to into Json" - JsonNode newJson = mapper.readTree(parsedSighting.toJsonString()) - String newJsonString = mapper.writeValueAsString(newJson) -// println "New Json: ${newJsonString}" - - then: "New Json should match Original Json" - JSONAssert.assertEquals(originalJsonString, newJsonString, JSONCompareMode.NON_EXTENSIBLE) - - where: - i << (1..100) - } -} diff --git a/src/test/groovy/stix/stixinstant/StixInstantSpec.groovy b/src/test/groovy/stix/stixinstant/StixInstantSpec.groovy deleted file mode 100644 index b08b810..0000000 --- a/src/test/groovy/stix/stixinstant/StixInstantSpec.groovy +++ /dev/null @@ -1,37 +0,0 @@ -package stix.stixinstant - -import io.digitalstate.stix.json.StixParsers -import io.digitalstate.stix.sdo.objects.Campaign -import spock.lang.Specification - -class StixInstantSpec extends Specification { - - def "time test to ensure Instant Precisions are maintained"(){ - when: - Campaign campaignCreatedNoSubSec = StixParsers.parseObject("{\"type\":\"campaign\",\"id\":\"campaign--854ae5bf-e07b-4853-95f8-0f85027af1ea\",\"created_by_ref\":\"identity--5eb4619f-b1b6-4b7b-9504-bd3b4d05ce8a\",\"created\":\"2019-04-26T21:14:11Z\",\"modified\":\"2019-04-26T21:14:11.851Z\",\"revoked\":false,\"labels\":[\"interspersed\",\"yep\",\"contemners\",\"spurts\",\"aft\",\"aldose\"],\"granular_markings\":[{\"marking_ref\":\"marking-definition--980dffe6-2006-45bc-86c7-9885c986fdaa\",\"selectors\":[\"broch\",\"lipped\"],\"x_delimitates\":\"thick\",\"x_chocolaty\":380412,\"x_disarmingly\":\"cataplexy eyeleted poss belike told reformism neglect grillage purer justiciars boosts about haste compulsive over reinvolve blackly obliterate gamy here suffused triaxial tuatara circlers fianchetto aforementioned viverrine drowsily ties screeds fun larvicidal wuthering condyloma epistolize intwines loins ditto litho bluish straightaway restaged\",\"x_dryer\":327849,\"x_unblushingly\":false},{\"marking_ref\":\"marking-definition--f82c9f27-edb4-496b-9eca-58080db5d16d\",\"selectors\":[\"bootleg\",\"muclucs\",\"eild\",\"vandalises\",\"sardiuses\",\"astray\",\"exhaustively\",\"pyrogallic\"]}],\"name\":\"disastrously\"}") - Campaign campaignCreated9SubSec = StixParsers.parseObject("{\"type\":\"campaign\",\"id\":\"campaign--854ae5bf-e07b-4853-95f8-0f85027af1ea\",\"created_by_ref\":\"identity--5eb4619f-b1b6-4b7b-9504-bd3b4d05ce8a\",\"created\":\"2019-04-26T21:14:11.999999999Z\",\"modified\":\"2019-04-26T21:14:11.851Z\",\"revoked\":false,\"labels\":[\"interspersed\",\"yep\",\"contemners\",\"spurts\",\"aft\",\"aldose\"],\"granular_markings\":[{\"marking_ref\":\"marking-definition--980dffe6-2006-45bc-86c7-9885c986fdaa\",\"selectors\":[\"broch\",\"lipped\"],\"x_delimitates\":\"thick\",\"x_chocolaty\":380412,\"x_disarmingly\":\"cataplexy eyeleted poss belike told reformism neglect grillage purer justiciars boosts about haste compulsive over reinvolve blackly obliterate gamy here suffused triaxial tuatara circlers fianchetto aforementioned viverrine drowsily ties screeds fun larvicidal wuthering condyloma epistolize intwines loins ditto litho bluish straightaway restaged\",\"x_dryer\":327849,\"x_unblushingly\":false},{\"marking_ref\":\"marking-definition--f82c9f27-edb4-496b-9eca-58080db5d16d\",\"selectors\":[\"bootleg\",\"muclucs\",\"eild\",\"vandalises\",\"sardiuses\",\"astray\",\"exhaustively\",\"pyrogallic\"]}],\"name\":\"disastrously\"}") - Campaign campaignCreatedZerosSubSec = StixParsers.parseObject("{\"type\":\"campaign\",\"id\":\"campaign--854ae5bf-e07b-4853-95f8-0f85027af1ea\",\"created_by_ref\":\"identity--5eb4619f-b1b6-4b7b-9504-bd3b4d05ce8a\",\"created\":\"2019-04-26T21:14:11.000000000Z\",\"modified\":\"2019-04-26T21:14:11.851Z\",\"revoked\":false,\"labels\":[\"interspersed\",\"yep\",\"contemners\",\"spurts\",\"aft\",\"aldose\"],\"granular_markings\":[{\"marking_ref\":\"marking-definition--980dffe6-2006-45bc-86c7-9885c986fdaa\",\"selectors\":[\"broch\",\"lipped\"],\"x_delimitates\":\"thick\",\"x_chocolaty\":380412,\"x_disarmingly\":\"cataplexy eyeleted poss belike told reformism neglect grillage purer justiciars boosts about haste compulsive over reinvolve blackly obliterate gamy here suffused triaxial tuatara circlers fianchetto aforementioned viverrine drowsily ties screeds fun larvicidal wuthering condyloma epistolize intwines loins ditto litho bluish straightaway restaged\",\"x_dryer\":327849,\"x_unblushingly\":false},{\"marking_ref\":\"marking-definition--f82c9f27-edb4-496b-9eca-58080db5d16d\",\"selectors\":[\"bootleg\",\"muclucs\",\"eild\",\"vandalises\",\"sardiuses\",\"astray\",\"exhaustively\",\"pyrogallic\"]}],\"name\":\"disastrously\"}") - Campaign campaignCreated3ZerosSubSec = StixParsers.parseObject("{\"type\":\"campaign\",\"id\":\"campaign--854ae5bf-e07b-4853-95f8-0f85027af1ea\",\"created_by_ref\":\"identity--5eb4619f-b1b6-4b7b-9504-bd3b4d05ce8a\",\"created\":\"2019-04-26T21:14:11.000Z\",\"modified\":\"2019-04-26T21:14:11.851Z\",\"revoked\":false,\"labels\":[\"interspersed\",\"yep\",\"contemners\",\"spurts\",\"aft\",\"aldose\"],\"granular_markings\":[{\"marking_ref\":\"marking-definition--980dffe6-2006-45bc-86c7-9885c986fdaa\",\"selectors\":[\"broch\",\"lipped\"],\"x_delimitates\":\"thick\",\"x_chocolaty\":380412,\"x_disarmingly\":\"cataplexy eyeleted poss belike told reformism neglect grillage purer justiciars boosts about haste compulsive over reinvolve blackly obliterate gamy here suffused triaxial tuatara circlers fianchetto aforementioned viverrine drowsily ties screeds fun larvicidal wuthering condyloma epistolize intwines loins ditto litho bluish straightaway restaged\",\"x_dryer\":327849,\"x_unblushingly\":false},{\"marking_ref\":\"marking-definition--f82c9f27-edb4-496b-9eca-58080db5d16d\",\"selectors\":[\"bootleg\",\"muclucs\",\"eild\",\"vandalises\",\"sardiuses\",\"astray\",\"exhaustively\",\"pyrogallic\"]}],\"name\":\"disastrously\"}") - Campaign campaignCreated3DigitsWithTrailingZerosSubSec = StixParsers.parseObject("{\"type\":\"campaign\",\"id\":\"campaign--854ae5bf-e07b-4853-95f8-0f85027af1ea\",\"created_by_ref\":\"identity--5eb4619f-b1b6-4b7b-9504-bd3b4d05ce8a\",\"created\":\"2019-04-26T21:14:11.111000000Z\",\"modified\":\"2019-04-26T21:14:11.851Z\",\"revoked\":false,\"labels\":[\"interspersed\",\"yep\",\"contemners\",\"spurts\",\"aft\",\"aldose\"],\"granular_markings\":[{\"marking_ref\":\"marking-definition--980dffe6-2006-45bc-86c7-9885c986fdaa\",\"selectors\":[\"broch\",\"lipped\"],\"x_delimitates\":\"thick\",\"x_chocolaty\":380412,\"x_disarmingly\":\"cataplexy eyeleted poss belike told reformism neglect grillage purer justiciars boosts about haste compulsive over reinvolve blackly obliterate gamy here suffused triaxial tuatara circlers fianchetto aforementioned viverrine drowsily ties screeds fun larvicidal wuthering condyloma epistolize intwines loins ditto litho bluish straightaway restaged\",\"x_dryer\":327849,\"x_unblushingly\":false},{\"marking_ref\":\"marking-definition--f82c9f27-edb4-496b-9eca-58080db5d16d\",\"selectors\":[\"bootleg\",\"muclucs\",\"eild\",\"vandalises\",\"sardiuses\",\"astray\",\"exhaustively\",\"pyrogallic\"]}],\"name\":\"disastrously\"}") - - then: - assert campaignCreatedNoSubSec.getCreated().toString() == "2019-04-26T21:14:11Z" - assert campaignCreatedNoSubSec.getCreated().getInstant().toString() == "2019-04-26T21:14:11Z" - - then: - assert campaignCreated9SubSec.getCreated().toString() == "2019-04-26T21:14:11.999999999Z" - assert campaignCreated9SubSec.getCreated().getInstant().toString() == "2019-04-26T21:14:11.999999999Z" - - then: - assert campaignCreatedZerosSubSec.getCreated().toString() == "2019-04-26T21:14:11.000000000Z" - assert campaignCreatedZerosSubSec.getCreated().getInstant().toString() == "2019-04-26T21:14:11Z" - - then: - assert campaignCreated3ZerosSubSec.getCreated().toString() == "2019-04-26T21:14:11.000Z" - assert campaignCreated3ZerosSubSec.getCreated().getInstant().toString() == "2019-04-26T21:14:11Z" - - then: - assert campaignCreated3DigitsWithTrailingZerosSubSec.getCreated().toString() == "2019-04-26T21:14:11.111000000Z" - assert campaignCreated3DigitsWithTrailingZerosSubSec.getCreated().getInstant().toString() == "2019-04-26T21:14:11.111Z" - } -} diff --git a/src/test/groovy/stix/validation/AttackPatternValidationSpec.groovy b/src/test/groovy/stix/validation/AttackPatternValidationSpec.groovy deleted file mode 100644 index 0da30c6..0000000 --- a/src/test/groovy/stix/validation/AttackPatternValidationSpec.groovy +++ /dev/null @@ -1,95 +0,0 @@ -package stix.validation - -import io.digitalstate.stix.json.StixParserValidationException -import io.digitalstate.stix.json.StixParsers -import spock.lang.Specification - -class AttackPatternValidationSpec extends Specification { - - def "Validate invalid Attack-Pattern JSON"() { - when: "Parsing invalid Attack-Pattern JSON " - String json = ''' - { - "type": "attack-pattern", - "id": "attack-pattern--17e6110c-0f51-4d91-8c1c-417d3f886bda", - "created_by_ref": "identity--826cf0f0-2105-4cf3-a56a-06998d17b1ec", - "created": "2019-03-13T21:41:01.373Z", - "modified": "2019-03-13T21:41:01.374Z", - "revoked": true, - "labels": [ - "111", - "crabbing", - "metricized", - "potentates", - "cresylic", - "ultrasonic" - ], - "object_marking_refs": [ - "marking-definition--4805e1e6-f9c8-476c-bae3-1fa8b3a89197", - "marking-definition--3966fea2-f4a3-4eac-8cb8-c37be13e7fe5", - "marking-definition--2301e050-08c1-4d68-abe1-372a9c9bf0af" - ], - "granular_markings": [ - { - "marking_ref": "marking-definition--a4cb9815-d20e-41d7-a729-e2f3d432144f", - "selectors": [ - "tithe" - ] - }, - { - "marking_ref": "marking-definition--d15d48de-abc7-4c3e-a3ba-bad2198478c4", - "selectors": [ - "cozily", - "atremble", - "twaddle", - "moreish", - "cruciferous", - "recommence", - "bluntly", - "scudded", - "quiescent" - ] - }, - { - "marking_ref": "marking-definition--1a6676db-d3f9-403f-8975-3ba1f8f983a6", - "selectors": [ - "adagio", - "teff", - "eburnation", - "sousaphones", - "whisks" - ] - }, - { - "marking_ref": "marking-definition--ad10add9-9008-45c7-b40b-0ccb27bb9c27", - "selectors": [ - "neuk", - "reuses", - "squat" - ] - } - ], - "name": "dog", - "description": "quicksilver nobbut lame subahs heathfowl slightly bountifulness vitellines creepies custom both boldly darkly unwooded", - "x_breakfasts": 848823, - "xx_gats": "respondent persistently trephining anodizes washiest untimely jibe" - } - ''' - - then: "Should have 1 error" - try { - StixParsers.parseObject(json) - } catch (StixParserValidationException ex) { - assert ex.getConstraintValidations().size() == 1 -// ex.getConstraintValidations().each { x -> -// println "------" -// println "Type: ${x.getRootBean().getClass().getSimpleName()}" -// println "Message: ${x.getMessage()}" -// println "path: ${x.getPropertyPath()}" -// println "invalid_value: ${x.getInvalidValue().toString()}" -// println "------" -// } - } - } - -} diff --git a/src/test/groovy/stix/validation/BundleValidationSpec.groovy b/src/test/groovy/stix/validation/BundleValidationSpec.groovy deleted file mode 100644 index a00617c..0000000 --- a/src/test/groovy/stix/validation/BundleValidationSpec.groovy +++ /dev/null @@ -1,203 +0,0 @@ -package stix.validation - -import io.digitalstate.stix.json.StixParserValidationException -import io.digitalstate.stix.json.StixParsers -import spock.lang.Specification - -class BundleValidationSpec extends Specification { - - def "Validate invalid Bundle JSON"() { - when: "Parsing invalid Bundle JSON " - String json = ''' - { - "type": "bundle", - "id": "bundle--830d3023-6c2d-4d91-b1dc-b594959b8d48", - "spec_version": "2.0", - "x_indolently": 232355.30549115842, - "x_signora": "girts", - "x_cementitious": "teetotally expansionism impudently loams solonchak hooligans awry yale snappingly blamed refutably natation schoolgirlish reshapes bought jots protectively waur whimsically pailfuls reddle deprivation quite bituminised thin falls mailman amie hugely crousely summarises saintliest faithfully gap flam eighth perdie choicely phut pears bifilar impetrative specialises yeah vainly religieuse pistole flown flail unplaced spicate repossesses trindles fidge cattishly limbed sural frits generousness hoppings palpitating stultifies topes intercity mat now mews slay bistouries instigating secretively rebuttable legitimate unslung compatibly rayless summarily reassure fresh canny shiftily inarm vernalizing crimp repopulates landscaping fumier abnegations hippophiles chastely bight unbrotherly coldly archaizes", - "x_simular": 940778.1190796582, - "x_orchestrates": 800790, - "x_else": true, - "x_scrumptiously": 189963.7126364522, - "x_entraps": true, - "x_pentathlons": "illustrious coequally forte seditiously topee geotropic undress bumpily dinars admeasure sham pruriently antiquations grate unsayable playable fey courteously lineally highlands stains barbarous euphonized intonate quickly insulators adumbrated wield telly girt acromion remorselessly discrete perigee truculently ducks subzero umpire sapindaceous lieve treats germane snecked clot uncordial lint mow gaiety perplexity starchily polyploidy stopless ablins flongs tongued weans chotts", - "x_ghastfully": 947223, - "x_repurifies": 348826, - "x_lengthways": "precess", - "x_altruistic": "scabious dissipating transmutably malefactors scatophagy ban disimpassioned ought negotiates deign vividly puddocks upstair equalising lampoonist monogenic acrogens nightmarishly dominances fishily lubricous pronely unknightly sass sport frogged clause backwater glumes suffumigates scag nomadic plaguy casket proud connectedly umbonate forbidder derestrict yare outtelling yodelling logicises poulterers breakaways prod fricassees divinizing days devitalize burglarises chomps lalangs entwining medalling interspacing xerography lochia outdoors sighs obnoxious moochers deceptively leadless revocably salving larch hairiest stomatitis outdares indiscreet tenth doomwatchers decretive socialistic manifold counterpoises cornier flaunts ninth confessedly recountal fruitions savorous toothed gorgonise keyless commensurate loads instructor", - "x_tairas": "icier", - "x_mumms": 410175, - "x_marigold": 471065.5268547859, - "x_annex": "overlive publicize sepulcher days kerchiefed objectivist quire whisk posh unsurfaced brooms chalk thrice lysing trichologist mair interplants transmigrated catacumbal when multipurpose bloated days clues strung reinstated bucktooth soppiest reinterring rimming picked charged strop licht dupes why pedicellate detribalized molto constructs obumbrates eradicate ruinable retail vivo throngs myall unnoticed concussive ever sugarless bleeds diffusedly hithermost cleverish crummier whiles fun monadelphous embowelling buoyancy linoleum alway arranges fasciate unbalances malingerers feal deputation federally lamellibranch reconquer beefeaters shill swith expressively mickles rhodic disport cauterized adhibit coves pencils dern likely inflaming neatly", - "objects": [ - { - "id": "vulnerability--f5c62801-f9b0-42a7-983e-78ed03560b2b", - "type": "vulnerability", - "created_by_ref": "identity--44fda38a-2010-4510-bd69-3e176224abe3", - "created": "2019-03-21T20:51:39.872Z", - "modified": "2019-03-21T20:51:39.872Z", - "revoked": false, - "labels": [ - "amphibrachic", - "longly", - "lode", - "forehanded", - "daglock", - "yes", - "unimportuned", - "garotting", - "loaf", - "ferroconcrete", - "downstairs" - ], - "granular_markings": [ - { - "marking_ref": "marking-definition--2ad5b13d-c9c7-4bea-9dfa-5b1e5a2194b7", - "selectors": [ - "perisarc", - "landslide", - "thorps", - "sonnets", - "dryer", - "skimmings" - ], - "x_wifeless": "befitting prescriptivism slums lucubrated paynims mattresses thwacks welts quattrocento videodisk fraudful runed chugs moodier splodges psts swerve slink abloom incages filthily there overrates buss unaccounted promotive sisterliness alleviates wit bloodily smiles influent smut farawayness interbreedings massacres what geophagous scrimps sized inlets pocks melilots verifiers chirper docs inveigh forsooth quadrifid hybridizes doctoral aught whiles stoop around tidily worst choppy stridulous smash heap unsustaining untouchable doggone phut mischarging spasms attributing gratifiers interchanges militating invincibly dytiscid overselling prompt windmills slanderously counsellable" - }, - { - "marking_ref": "marking-definition--893f95c0-ea68-4c58-ba83-84c99e6a6eb3", - "selectors": [ - "trailingly", - "redesigns", - "amounts", - "hipped", - "metaphrase", - "meanwhile", - "deeply" - ], - "x_squirrelly": true, - "x_nominated": true, - "x_toothed": "loads goads quells wipes vagarious lampads sometimes proems flatter distastefully dotard monochromes brief luculent blithely basidial wastes", - "x_cloying": 325664, - "x_loath": "eructations", - "x_lambkin": false, - "x_pauperized": "sordid", - "x_romantics": 831690, - "x_conciliar": "yesterevening brought atomises", - "x_doabs": "consistently", - "x_tierced": false - }, - { - "marking_ref": "marking-definition--3697dbf5-c4d0-4c99-ba83-797dcb183387", - "selectors": [ - "vivacities" - ], - "x_dully": 509046, - "x_fends": "byzants retting tight slap thig golfs robustly setts dentexes grout snide tight epos ways unhorsing ayahs microtones miters explorations chalkpits cuttles tight unchastely systematized surely restructure vixen uptearing clearly overplied floodlighted riverlike worse skirts unconstant unbespoken dent respirable", - "x_bilk": "jargonises", - "x_spang": 533218.6108306068, - "x_smoulders": 193127.38563754444, - "x_otherwhere": 500157, - "x_compactedly": true, - "x_reactively": "distinguish hues flops here maims undiminished condemnable casually engrossments unhusk loads copulating heliograph citruses succulently clam communizing cheesing weighs candelabrum cauld embedments", - "x_explicitly": "swearings maliciously insolubly roaringly laggingly blamed griping undeserving aecidium sorobans transnational assai produces depresses tenderized fumed dutiable dismally carburizing course lest aestivate sump killikinick quintan clomb tinkling thin redips caracol tracklessness worst pitifully demagnetize yet crack unreproved pegh draftiness crawfishes romaines overcloud desiccate caustically pluckily thick rehearsed dyeable regulate entresol flawier wittingly imperium mistiest stannaries lucubrate ditto scorched patting incurs inward cholent stingingly expects screams mercilessly havocs ruffians bead pitched promises gurge hafts glumaceous chartists tressed since seriatim halest gainless imprisons between cognitions", - "x_secretly": true, - "x_banderilla": "pepino", - "x_rewash": 315436.90735046, - "x_convulsively": false, - "x_daftly": "preannounce", - "x_far": 444089.75319646124, - "x_trichinized": true, - "x_counteract": false, - "x_mighty": 404750, - "x_mauve": 123931 - } - ], - "namee": "megaliths", - "description": "towered left away symphysial cronk artier pocks unobtained tsarist could endarch revengingly nocuously could disquietly" - }, - { - "id": "vulnerability--f36853bb-7745-403f-af15-b3c324c057c7", - "type": "vulnerability", - "created_by_ref": "identity--c39f93f0-072f-4c97-a2e4-ec9358ecb093", - "created": "2019-03-21T20:51:39.878Z", - "modified": "2019-03-21T20:51:39.878Z", - "revoked": true, - "external_references": [ - { - "source_name": "cognizes", - "description": "smash barbeques shoehorn spang jade", - "url": "http://www.chayscaprice.org", - "hashes": { - "SHA-512": "db39af5838979105222d43fbbd08f7ae8ee064e103c9a9523d8819f395bb2cdd56ecb0af54b4db3569497868556f8642277129280faf031e2dba369bbd5d950c" - }, - "external_id": "1c5c3cb8-62b5-4a9b-a920-e3405235cb9a" - }, - { - "source_name": "vocative", - "hashes": { - "SHA-256": "e0ccc55e8669f4c6470de2f36553d377096136cebb0752566a4e644c1b79d3ad" - } - } - ], - "object_marking_refs": [ - "marking-definition--c631a64f-df15-4be2-ad76-4350a4adb4e7" - ], - "granular_markings": [ - { - "marking_ref": "marking-definition--fe9ff6b4-8548-41d4-b6c5-9c304658581c", - "selectors": [ - "discourages", - "quaveringly", - "goad", - "evaded", - "scores", - "wrest", - "lest", - "historicism" - ] - }, - { - "marking_ref": "marking-definition--60900ddb-a0e2-4508-8d3d-f1122f644856", - "selectors": [ - "boot", - "newsier" - ] - }, - { - "marking_ref": "marking-definition--f941ff40-a304-4f8b-93c0-7cc7bf708540", - "selectors": [ - "unovercome", - "pewter", - "cheesing", - "cruelly" - ], - "x_obtrusively": false, - "x_motorises": true - } - ], - "namee": "beefcakes", - "description": "sober fossilize hypostyle proprietors miscellanists deviceful keloidal fowls mesenteric scotomatous suburbanize nights undiscording inverter pinfolds dang redeems avail underdrawing reallotted segments arytenoid evaded lowering resiliently hereto excessively theologised demonstrative quads seaplanes lushy quirt equipoised typing reground dryer shoreward" - } - ] - } - ''' - - then: "Should have 1 error" - try { - StixParsers.parseBundle(json) - } catch (StixParserValidationException ex) { - assert ex.getConstraintValidations().size() == 2 -// ex.getConstraintValidations().each { x -> -// println "------" -// println "Type: ${x.getRootBean().getClass().getSimpleName()}" -// println "Object Id: ${x.getRootBean().asType(BundleableObject).getId()}" -// println "Message: ${x.getMessage()}" -// println "path: ${x.getPropertyPath()}" -// println "invalid_value: ${x.getInvalidValue().toString()}" -// println "------" -// } - } - } - -} diff --git a/src/test/resources/stix/baseline/README.md b/src/test/resources/stix/baseline/README.md deleted file mode 100644 index bc2769e..0000000 --- a/src/test/resources/stix/baseline/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Baseline JSON file - -This package contains a set of baseline JSON files that contain "happy state"/valid STIX data. - -The goal is to have a consistent set of files that represent basic valid json for each of the STIX related objects. - -This JSON files are used in tests to validate serialization capabilities. \ No newline at end of file diff --git a/src/test/resources/stix/baseline/json/sdo/indicator/indicators.json b/src/test/resources/stix/baseline/json/sdo/indicator/indicators.json deleted file mode 100644 index bd2ab32..0000000 --- a/src/test/resources/stix/baseline/json/sdo/indicator/indicators.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "bundle", - "id": "bundle--6fab341c-36c5-4a7f-be29-0a6e3b85e7b0", - "spec_version": "2.0", - "objects": [ - { - "type": "indicator", - "id": "indicator--59ccb738-921a-4941-8ab2-33da522bd4e1", - "created": "2019-04-08T22:59:50.0Z", - "modified": "2019-04-08T22:59:50.00Z", - "revoked": false, - "labels": [ - "malicious-activity" - ], - "name": "128.0.0.1", - "pattern": "[ipv4-addr:value = '128.0.0.1']", - "valid_from": "2019-04-08T22:59:50.000Z" - } - ] -} \ No newline at end of file diff --git a/src/test/resources/stix/baseline/json/sdo/threatreport/apt1.json b/src/test/resources/stix/baseline/json/sdo/threatreport/apt1.json deleted file mode 100644 index b0cde43..0000000 --- a/src/test/resources/stix/baseline/json/sdo/threatreport/apt1.json +++ /dev/null @@ -1,1101 +0,0 @@ -{ - "type": "bundle", - "id": "bundle--cf20f99b-3ed2-4a9f-b4f1-d660a7fc8241", - "spec_version": "2.0", - "objects": [ - { - "type": "intrusion-set", - "id": "intrusion-set--da1065ce-972c-4605-8755-9cd1074e3b5a", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "APT1", - "description": "APT1 is a single organization of operators that has conducted a cyber espionage campaign against a broad range of victims since at least 2006.", - "first_seen": "2006-06-01T18:13:15.684Z", - "resource_level": "government", - "primary_motivation": "organizational-gain", - "aliases": [ - "Comment Crew", - "Comment Group", - "Shady Rat" - ] - }, - { - "type": "threat-actor", - "id": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Ugly Gorilla", - "labels": [ - "nation-state", - "spy" - ], - "roles": [ - "malware-author", - "agent", - "infrastructure-operator" - ], - "resource_level": "government", - "aliases": [ - "Greenfield", - "JackWang", - "Wang Dong" - ], - "primary_motivation": "organizational-gain" - }, - { - "type": "threat-actor", - "id": "threat-actor--d84cf283-93be-4ca7-890d-76c63eff3636", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "DOTA", - "labels": [ - "nation-state", - "spy" - ], - "aliases": [ - "dota", - "Rodney", - "Raith" - ], - "resource_level": "government", - "roles": [ - "agent", - "infrastructure-operator" - ], - "primary_motivation": "organizational-gain" - }, - { - "type": "threat-actor", - "id": "threat-actor--02e7c48f-0301-4c23-b3e4-02e5a0114c21", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "SuperHard", - "labels": [ - "nation-state" - ], - "sophistication": "expert", - "aliases": [ - "dota", - "Rodney", - "Raith" - ], - "resource_level": "government", - "roles": [ - "malware-author" - ], - "primary_motivation": "organizational-gain" - }, - { - "type": "threat-actor", - "id": "threat-actor--d5b62b58-df7c-46b1-a435-4d01945fe21d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Communist Party of China", - "description": " The CPC is the ultimate authority in Mainland China and tasks the PLA to commit cyber espionage and data theft against organizations around the world.", - "labels": [ - "nation-state" - ], - "resource_level": "government", - "roles": [ - "sponsor", - "director" - ], - "aliases": [ - "CPC" - ], - "primary_motivation": "organizational-gain" - }, - { - "type": "threat-actor", - "id": "threat-actor--94624865-2709-443f-9b4c-2891985fd69b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Unit 61398", - "description": "Unit 61398 functions as the Third Department's premier entity targeting the United States and Canada, most likely focusing on political, economic, and military-related intelligence.", - "labels": [ - "nation-state" - ], - "resource_level": "government", - "roles": [ - "agent" - ], - "aliases": [ - "PLA GSD's 3rd Department, 2nd Bureau", - "Military Unit Cover Designator (MUCD) 61398" - ], - "primary_motivation": "organizational-gain" - }, - { - "type": "identity", - "id": "identity--a9119a87-6576-46af-bfd7-4fbe55926671", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "JackWang", - "identity_class": "individual", - "sectors": [ - "government-national" - ], - "contact_information": "uglygorilla@163.com" - }, - { - "type": "identity", - "id": "identity--e88ab115-7768-4630-baa3-3d49a7d946ea", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Wang Dong", - "identity_class": "individual", - "sectors": [ - "government-national" - ], - "contact_information": "uglygorilla@163.com" - }, - { - "type": "identity", - "id": "identity--0e9d20d9-fb11-42e3-94bc-b89fb5b007ca", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "dota", - "identity_class": "individual", - "sectors": [ - "government-national" - ], - "contact_information": "dota.d013@gmail.com" - }, - { - "type": "identity", - "id": "identity--ecf1c7de-d96c-41c6-a510-b9c65cdc9e3b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Mei Qiang", - "identity_class": "individual", - "sectors": [ - "government-national" - ], - "contact_information": "mei_qiang_82@sohu.com" - }, - { - "type": "indicator", - "id": "indicator--031778a4-057f-48e6-9db9-c8d72b81ccd5", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "HTRAN Hop Point Accessor", - "pattern": "[ipv4-addr:value = '223.166.0.0/15']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "indicator", - "id": "indicator--da1d061b-2bc9-467a-b16f-8d14f468e1f0", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "HTRAN Hop Point Accessor", - "pattern": "[ipv4-addr:value = '58.246.0.0/15']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "indicator", - "id": "indicator--2173d108-5714-42fd-8213-4f3790259fda", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "HTRAN Hop Point Accessor", - "pattern": "[ipv4-addr:value = '112.64.0.0/15']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "indicator", - "id": "indicator--8ce03314-dfea-4498-ac9b-136e41ab00e4", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "HTRAN Hop Point Accessor", - "pattern": "[ipv4-addr:value = '139.226.0.0/15']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "indicator", - "id": "indicator--3f3ff9f1-bb4e-4392-89e5-1991179042ba", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "FQDN hugesoft.org", - "pattern": "[domain-name:value = 'hugesoft.org']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--8390fd29-24ed-45d4-84d7-c5e5feaf195d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "FQDN arrowservice.net", - "pattern": "[domain-name:value = 'arrowservice.net']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--1002c58e-cbde-4930-b5ee-490037fd4f7e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "FQDN msnhome.org", - "pattern": "[domain-name:value = 'msnhome.org']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--8d12f44f-8ac0-4b12-8b4a-3699ca8c9691", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Appendix E MD5 hash '001dd76872d80801692ff942308c64e6'", - "pattern": "[file:hashes.md5 = '001dd76872d80801692ff942308c64e6']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--745e1537-b4f3-49da-9f64-df6b1b5df190", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Appendix E MD5 hash '002325a0a67fded0381b5648d7fe9b8e'", - "pattern": "[file:hashes.md5 = '002325a0a67fded0381b5648d7fe9b8e']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--1dbe6ed0-c305-458f-9cce-f83c678f5afd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Appendix E MD5 hash '00dbb9e1c09dbdafb360f3163ba5a3de'", - "pattern": "[file:hashes.md5 = '00dbb9e1c09dbdafb360f3163ba5a3de']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b3b6b540-d838-11e2-853b-005056c00008", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Appendix F SSL Certificate for serial number '(Negative)4c:0b:1d:19:74:86:a7:66:b4:1a:bf:40:27:21:76:28'", - "pattern": "[x509-certificate:issuer = 'CN=WEBMAIL' AND x509-certificate:serial_number = '4c:0b:1d:19:74:86:a7:66:b4:1a:bf:40:27:21:76:28']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b3b7035e-d838-11e2-8d38-005056c00008", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Appendix F SSL Certificate for serial number '0e:97:88:1c:6c:a1:37:96:42:03:bc:45:42:24:75:6c'", - "pattern": "[x509-certificate:issuer = 'CN=LM-68AB71FBD8F5' AND x509-certificate:serial_number = '0e:97:88:1c:6c:a1:37:96:42:03:bc:45:42:24:75:6c']", - "labels": [ - "malicious-activity" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "malware", - "id": "malware--2485b844-4efe-4343-84c8-eb33312dd56f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "MANITSME", - "labels": [ - "backdoor", - "dropper", - "remote-access-trojan" - ], - "description": "This malware will beacon out at random intervals to the remote attacker. The attacker can run programs, execute arbitrary commands, and easily upload and download files." - }, - { - "type": "malware", - "id": "malware--c0217091-9d3d-42a1-8952-ccc12d4ad8d0", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "WEBC2-UGX", - "labels": [ - "backdoor", - "remote-access-trojan" - ], - "description": "A WEBC2 backdoor is designed to retrieve a Web page from a C2 server. It expects the page to contain special HTML tags; the backdoor will attempt to interpret the data between the tags as commands." - }, - { - "type": "malware", - "id": "malware--0f01c5a3-f516-4450-9381-4dd9f2279411", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "WEBC2 Backdoor", - "labels": [ - "backdoor", - "remote-access-trojan" - ], - "description": "A WEBC2 backdoor is designed to retrieve a Web page from a C2 server. It expects the page to contain special HTML tags; the backdoor will attempt to interpret the data between the tags as commands.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "malware", - "id": "malware--33159b98-3264-4e10-a968-d67975b6272f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "HUC Packet Transmit Tool (HTRAN)", - "labels": [ - "backdoor", - "remote-access-trojan" - ], - "description": "When APT1 attackers are not using WEBC2, they require a “command and control” (C2) user interface so they can issue commands to the backdoor. This interface sometimes runs on their personal attack system, which is typically in Shanghai. In these instances, when a victim backdoor makes contact with a hop, the communications need to be forwarded from the hop to the intruder’s Shanghai system so the backdoor can talk to the C2 server software. We have observed 767 separate instances in which APT1 intruders used the publicly available “HUC Packet Transmit Tool” or HTRAN on a hopThe HTRAN utility is merely a middle-man, facilitating connections between the victim and the attacker who is using the hop point.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "malware", - "id": "malware--fb490cdb-6760-41eb-a79b-0b930a50c017", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "AURIGA", - "labels": [ - "backdoor", - "keylogger" - ], - "description": "Malware family that contains functionality for keystroke logging, creating and killing processes, performing file system and registry modifications, etc." - }, - { - "type": "malware", - "id": "malware--ea50ecb7-2cd4-4895-bd08-31cd591ed0ca", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "BANGAT", - "labels": [ - "backdoor", - "keylogger" - ], - "description": "Malware family that contains functionality for keylogging, creating and killing processes, performing filesystem and registry modifications, etc." - }, - { - "type": "tool", - "id": "tool--ce45f721-af14-4fc0-938c-000c16186418", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "cachedump", - "labels": [ - "credential-exploitation" - ], - "description": "This program extracts cached password hashes from a system’s registry.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ] - }, - { - "type": "tool", - "id": "tool--e9778c42-bc2f-4eda-9fb4-6a931834f68c", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "fgdump", - "labels": [ - "credential-exploitation" - ], - "description": "Windows password hash dumper", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ], - "external_references": [ - { - "source_name": "fgdump", - "url": "http://www.foofus.net/fizzgig/fgdump/" - } - ] - }, - { - "type": "tool", - "id": "tool--1cf6a3b8-be43-4c1a-b042-546a890c31b2", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "gsecdump", - "labels": [ - "credential-exploitation" - ], - "description": "Obtains password hashes from the Windows registry, including the SAM file, cached domain credentials, and LSA secrets", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ], - "external_references": [ - { - "source_name": "gsecdump", - "url": "http://www.truesec.se" - } - ] - }, - { - "type": "tool", - "id": "tool--4d82bd3e-24a3-4f9d-b8f3-b57267fe06a9", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "lslsass", - "labels": [ - "credential-exploitation" - ], - "description": "Dump active logon session password hashes from the lsass process", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ], - "external_references": [ - { - "source_name": "lslsass", - "url": "http://www.truesec.se" - } - ] - }, - { - "type": "tool", - "id": "tool--7de5dfcc-6809-4772-9f11-cf26c2be53aa", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "mimikatz", - "labels": [ - "credential-exploitation" - ], - "description": "A utility primarily used for dumping password hashes", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ], - "external_references": [ - { - "source_name": "mimikatz", - "url": "http://blog.gentilkiwi.com/mimikatz" - } - ] - }, - { - "type": "tool", - "id": "tool--266b12f2-aa16-4607-809e-f2d33eebb52e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "pass-the-hash toolkit", - "labels": [ - "credential-exploitation" - ], - "description": "Allows an intruder to “pass” a password hash (without knowing the original password) to log in to systems", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ], - "external_references": [ - { - "source_name": "pass-the-hash toolkit", - "url": "http://oss.coresecurity.com/projects/pshtoolkit.htm" - } - ] - }, - { - "type": "tool", - "id": "tool--98fd8dc1-6cc7-4908-899f-07473f55149a", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "pwdump7", - "labels": [ - "credential-exploitation" - ], - "description": "Dumps password hashes from the Windows registry", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ], - "external_references": [ - { - "source_name": "pwdump7", - "url": "http://www.tarasco.org/security/pwdump_7/" - } - ] - }, - { - "type": "tool", - "id": "tool--4215b0e5-928e-4b2a-9b5f-64819f287f48", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "pwdumpX", - "labels": [ - "credential-exploitation" - ], - "description": "Dumps password hashes from the Windows registry", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ] - }, - { - "type": "tool", - "id": "tool--a6dd62d0-9683-48bf-a9cd-61e7eceae57e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "GETMAIL", - "labels": [ - "information-gathering" - ], - "description": "GETMAIL was designed specifically to extract email messages, attachments, and folders from within Microsoft Outlook archive (“PST”) files.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "complete-mission" - } - ] - }, - { - "type": "tool", - "id": "tool--806a8f83-4913-4216-bb19-02b48ae25da5", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "MAPIGET", - "labels": [ - "information-gathering" - ], - "description": "MAPIGET was designed specifically to steal email that has not yet been archived and still resides on a Microsoft Exchange Server.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "complete-mission" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--3098c57b-d623-4c11-92f4-5905da66658b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Initial Compromise", - "description": "As with most other APT groups, spear phishing is APT1’s most commonly used technique. The spear phishing emails contain either a malicious attachment or a hyperlink to a malicious file. The subject line and the text in the email body are usually relevant to the recipient. APT1 also creates webmail accounts using real peoples’ names — names that are familiar to the recipient, such as a colleague, a company executive, an IT department employee, or company counsel. The files they use contain malicious executables that install a custom APT1 backdoor that we call WEBC2-TABLE.", - "external_references": [ - { - "source_name": "capec", - "description": "spear phishing", - "external_id": "CAPEC-163" - } - ], - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "initial-compromise" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--1e2c4237-d469-4144-9c0b-9e5c0c513c49", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Establishing a Foothold", - "description": "APT1 establishes a foothold once email recipients open a malicious file and a backdoor is subsequently installed. In almost every case, APT backdoors initiate outbound connections to the intruder’s 'command and control' (C2) server. While APT1 intruders occasionally use publicly available backdoors such as Poison Ivy and Gh0st RAT, the vast majority of the time they use what appear to be their own custom backdoors. APT1’s backdoors are in two categories: 'Beachhead Backdoors' and 'Standard Backdoors.' Beachhead Backdoors offer the attacker a toe-hold to perform simple tasks like retrieve files, gather basic system information and trigger the execution of other more significant capabilities such as a standard backdoor. APT1’s beachhead backdoors are usually what we call WEBC2 backdoors. WEBC2 backdoors are probably the most well-known kind of APT1 backdoor, and are the reason why some security companies refer to APT1 as the Comment Crew. A WEBC2 backdoor is designed to retrieve a webpage from a C2 server. It expects the webpage to contain special HTML tags; the backdoor will attempt to interpret the data between the tags as commands. WEBC2 backdoors are often packaged with spear phishing emails. Once installed, APT1 intruders have the option to tell victim systems to download and execute additional malicious software of their choice. The standard, non-WEBC2 APT1 backdoor typically communicates using the HTTP protocol (to blend in with legitimate web traffic) or a custom protocol that the malware authors designed themselves. The BISCUIT backdoor (so named for the command “bdkzt”) is an illustrative example of the range of commands that APT1 has built into its “standard” backdoors. APT1 has used and steadily modified BISCUIT since as early as 2007 and continues to use it presently. Some APT backdoors attempt to mimic legitimate Internet traffic other than the HTTP protocol. When network defenders see the communications between these backdoors and their C2 servers, they might easily dismiss them as legitimate network traffic. Additionally, many of APT1’s backdoors use SSL encryption so that communications are hidden in an encrypted SSL tunnel.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "establish-foothold" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Privilege Escalation", - "description": "Escalating privileges involves acquiring items (most often usernames and passwords) that will allow access to more resources within the network. APT1 predominantly uses publicly available tools to dump password hashes from victim systems in order to obtain legitimate user credentials.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "escalate-privileges" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--5728f45b-2eca-4942-a7f6-bc4267c1ab8d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Internal Reconnaisance", - "description": "In the Internal Reconnaissance stage, the intruder collects information about the victim environment. Like most APT (and non-APT) intruders, APT1 primarily uses built-in operating system commands to explore a compromised system and its networked environment. Although they usually simply type these commands into a command shell, sometimes intruders may use batch scripts to speed up the process.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "internal-recon" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--0bea2358-c244-4905-a664-a5cdce7bb767", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Lateral Movement", - "description": "Once an APT intruder has a foothold inside the network and a set of legitimate credentials, it is simple for the intruder to move around the network undetected. They can connect to shared resources on other systems. They can execute commands on other systems using the publicly available 'psexec' tool from Microsoft Sysinternals or the built-in Windows Task Scheduler ('at.exe').", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "move-laterally" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--7151c6d0-7e97-47ce-9290-087315ea3db7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Maintain Presence", - "description": "In this stage, the intruder takes actions to ensure continued, long-term control over key systems in the network environment from outside of the network. APT1 does this in three ways: Install new backdoors on multiple systems, use legitimate VPN credentials, and log in to web portals.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "maintain-presence" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--0781fe70-4c94-4300-8865-4b08b98611b4", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Completing the Mission", - "description": "Similar to other APT groups we track, once APT1 finds files of interest they pack them into archive files before stealing them. APT intruders most commonly use the RAR archiving utility for this task and ensure that the archives are password protected. Sometimes APT1 intruders use batch scripts to assist them in the process. After creating files compressed via RAR, the APT1 attackers will transfer files out of the network in ways that are consistent with other APT groups, including using the File Transfer Protocol (FTP) or their existing backdoors. Many times their RAR files are so large that the attacker splits them into chunks before transferring them. Unlike most other APT groups we track, APT1 uses two email-stealing utilities that we believe are unique to APT1. The first, GETMAIL, was designed specifically to extract email messages, attachments, and folders from within Microsoft Outlook archive ('PST') files. The GETMAIL utility allows APT1 intruders the flexibility to take only the emails between dates of their choice. In one case, we observed an APT1 intruder return to a compromised system once a week for four weeks in a row to steal only the past week’s emails. Whereas GETMAIL steals email in Outlook archive files, the second utility, MAPIGET, was designed specifically to steal email that has not yet been archived and still resides on a Microsoft Exchange Server. In order to operate successfully, MAPIGET requires username/password combinations that the Exchange server will accept. MAPIGET extracts email from specified accounts into text files (for the email body) and separate attachments, if there are any.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "complete-mission" - } - ] - }, - { - "type": "report", - "id": "report--e33ffe07-2f4c-48d8-b0af-ee2619d765cf", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "APT1: Exposing One of China's Cyber Espionage Units", - "labels": [ - "threat-report", - "threat-actor" - ], - "published": "2013-02-19T00:00:00.000000Z", - "description": "Since 2004, Mandiant has investigated computer security breaches at hundreds of organizations around the world. The majority of these security breaches are attributed to advanced threat actors referred to as the 'Advanced Persistent Threat' (APT). We first published details about the APT in our January 2010 M-Trends report. As we stated in the report, our position was that 'The Chinese government may authorize this activity, but theres no way to determine the\textent of its involvement.' Now, three years later, we have the evidence required to change our assessment. The details\twe have analyzed during hundreds of investigations convince us that the groups conducting these activities are based primarily in China and that the Chinese Government is aware of them. Mandiant continues to track dozens of APT groups around the world; however, this report is focused on the most prolific of these groups. We refer to this group as 'APT1' and it is one of more than 20 APT groups with origins in China. APT1 is a single organization of operators that has conducted a cyber espionage campaign against a broad range of victims since at least 2006. From our observations, it is one of the most prolific cyber espionage groups in terms of the sheer quantity of information stolen. The scale and impact of APT1's operations compelled us to write this report. The activity we have directly observed likely represents only a small fraction of the cyber espionage that APT1 has conducted. Though our visibility of APT1's activities is incomplete, we have analyzed the group's intrusions against nearly 150 victims over seven years. From our unique vantage point responding to victims, we tracked APT1 back to four large networks in Shanghai, two of which are allocated directly to the Pudong New Area. We uncovered a substantial amount of APT1's attack infrastructure, command and control, and modus operandi (tools, tactics, and procedures). In an effort to underscore there are actual individuals behind the keyboard, Mandiant is revealing three personas we have attributed to APT1. These operators, like soldiers, may merely be following orders given to them by others. Our analysis has led us to conclude that APT1 is likely government-sponsored and one of the most persistent of China's cyber threat actors. We believe that APT1 is able to wage such a long-running and extensive cyber espionage campaign in large part because it receives direct government support. In seeking to identify the organization behind this activity, our research found that People's Liberation Army (PLA's) Unit 61398 is similar to APT1 in its mission, capabilities, and resources. PLA Unit 61398 is also located in precisely the same area from which APT1 activity appears to originate.", - "object_refs": [ - "attack-pattern--3098c57b-d623-4c11-92f4-5905da66658b", - "attack-pattern--1e2c4237-d469-4144-9c0b-9e5c0c513c49", - "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "attack-pattern--5728f45b-2eca-4942-a7f6-bc4267c1ab8d", - "attack-pattern--0bea2358-c244-4905-a664-a5cdce7bb767", - "attack-pattern--7151c6d0-7e97-47ce-9290-087315ea3db7", - "attack-pattern--0781fe70-4c94-4300-8865-4b08b98611b4", - "identity--a9119a87-6576-46af-bfd7-4fbe55926671", - "identity--e88ab115-7768-4630-baa3-3d49a7d946ea", - "identity--0e9d20d9-fb11-42e3-94bc-b89fb5b007ca", - "identity--ecf1c7de-d96c-41c6-a510-b9c65cdc9e3b", - "indicator--031778a4-057f-48e6-9db9-c8d72b81ccd5", - "indicator--da1d061b-2bc9-467a-b16f-8d14f468e1f0", - "indicator--2173d108-5714-42fd-8213-4f3790259fda", - "indicator--8ce03314-dfea-4498-ac9b-136e41ab00e4", - "indicator--3f3ff9f1-bb4e-4392-89e5-1991179042ba", - "indicator--8390fd29-24ed-45d4-84d7-c5e5feaf195d", - "indicator--1002c58e-cbde-4930-b5ee-490037fd4f7e", - "indicator--8d12f44f-8ac0-4b12-8b4a-3699ca8c9691", - "indicator--745e1537-b4f3-49da-9f64-df6b1b5df190", - "indicator--1dbe6ed0-c305-458f-9cce-f83c678f5afd", - "indicator--b3b6b540-d838-11e2-853b-005056c00008", - "indicator--b3b7035e-d838-11e2-8d38-005056c00008", - "intrusion-set--da1065ce-972c-4605-8755-9cd1074e3b5a", - "malware--2485b844-4efe-4343-84c8-eb33312dd56f", - "malware--c0217091-9d3d-42a1-8952-ccc12d4ad8d0", - "malware--0f01c5a3-f516-4450-9381-4dd9f2279411", - "malware--33159b98-3264-4e10-a968-d67975b6272f", - "malware--fb490cdb-6760-41eb-a79b-0b930a50c017", - "malware--ea50ecb7-2cd4-4895-bd08-31cd591ed0ca", - "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65", - "threat-actor--d84cf283-93be-4ca7-890d-76c63eff3636", - "threat-actor--02e7c48f-0301-4c23-b3e4-02e5a0114c21", - "threat-actor--d5b62b58-df7c-46b1-a435-4d01945fe21d", - "threat-actor--94624865-2709-443f-9b4c-2891985fd69b", - "tool--ce45f721-af14-4fc0-938c-000c16186418", - "tool--e9778c42-bc2f-4eda-9fb4-6a931834f68c", - "tool--1cf6a3b8-be43-4c1a-b042-546a890c31b2", - "tool--4d82bd3e-24a3-4f9d-b8f3-b57267fe06a9", - "tool--7de5dfcc-6809-4772-9f11-cf26c2be53aa", - "tool--266b12f2-aa16-4607-809e-f2d33eebb52e", - "tool--4215b0e5-928e-4b2a-9b5f-64819f287f48", - "tool--a6dd62d0-9683-48bf-a9cd-61e7eceae57e", - "tool--806a8f83-4913-4216-bb19-02b48ae25da5", - "tool--98fd8dc1-6cc7-4908-899f-07473f55149a", - "relationship--6598bf44-1c10-4218-af9f-75b5b71c23a7", - "relationship--35f7a2bb-e4e2-4e56-8693-665bbb64162c", - "relationship--fd5cda8b-f45f-43bd-a9da-e521ddd7126e", - "relationship--a20b8626-a15e-41f0-bcb1-c05321e126f0", - "relationship--d84cf283-93be-4ca7-890d-76c63eff3636", - "relationship--71e6832f-17ee-42fd-938d-c7f881be2028", - "relationship--9dd881a7-6e9b-4c35-bef5-7a777bca65d3", - "relationship--306ce398-f708-47f9-88a1-38aa5b9985fc", - "relationship--8668d82a-1c97-4bea-a367-e391b025e00e", - "relationship--e0ca2caa-7fa0-4f36-ad19-96f107eb6023", - "relationship--765815fb-d993-4a1d-959f-7f7bcc4a5eb3", - "relationship--85b2a834-e4b5-4299-9a6b-bf2ac26dde7b", - "relationship--61f4fd3b-f581-4497-9149-e624c317287b", - "relationship--7cede760-b866-490e-ad5b-1df34bc14f8d", - "relationship--b2806dec-6f20-4a0d-ae9a-d4b1f7be71e3", - "relationship--3921b161-5872-4c21-8ab0-b5b84233f3dc", - "relationship--81827b05-8c20-4247-b5d8-674295a1c611", - "relationship--066593e1-49a4-4a3d-a5bb-2e0b4ce1a63c", - "relationship--b385d984-ba8a-4180-8e0e-af7b9987bcb8", - "relationship--6ffbec81-fa01-4b98-8726-c9d9fb2ef6b6", - "relationship--25586f60-bc27-47d6-9a8e-d1c6456c2f28", - "relationship--d080c1ea-1dd7-4da9-b64b-e68bb1c5887e", - "relationship--c9c66478-c9cf-49cd-bca2-66ce34a9c56d", - "relationship--44686fda-311c-4cdb-abef-80e922e7a3fb", - "relationship--340cb676-79ff-49e9-b6ba-cd27e06772c4", - "relationship--9908520f-b25d-44a8-900b-d4e0825dcd0d", - "relationship--1fbd9a8d-4c14-431c-9520-3ccc50b748c1", - "relationship--389a8dcd-8663-4f18-8584-d69a77bd71aa", - "relationship--b345f1d0-09c5-4a71-bfc6-a52bd5923a01", - "relationship--912b31d0-09c5-4a71-bfc6-a52bd5989a1b" - ] - }, - { - "type": "relationship", - "id": "relationship--6598bf44-1c10-4218-af9f-75b5b71c23a7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65", - "target_ref": "malware--2485b844-4efe-4343-84c8-eb33312dd56f" - }, - { - "type": "relationship", - "id": "relationship--35f7a2bb-e4e2-4e56-8693-665bbb64162c", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65", - "target_ref": "malware--c0217091-9d3d-42a1-8952-ccc12d4ad8d0" - }, - { - "type": "relationship", - "id": "relationship--fd5cda8b-f45f-43bd-a9da-e521ddd7126e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65", - "target_ref": "identity--a9119a87-6576-46af-bfd7-4fbe55926671" - }, - { - "type": "relationship", - "id": "relationship--a20b8626-a15e-41f0-bcb1-c05321e126f0", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65", - "target_ref": "identity--e88ab115-7768-4630-baa3-3d49a7d946ea" - }, - { - "type": "relationship", - "id": "relationship--d84cf283-93be-4ca7-890d-76c63eff3636", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "threat-actor--d84cf283-93be-4ca7-890d-76c63eff3636", - "target_ref": "identity--0e9d20d9-fb11-42e3-94bc-b89fb5b007ca" - }, - { - "type": "relationship", - "id": "relationship--71e6832f-17ee-42fd-938d-c7f881be2028", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "threat-actor--02e7c48f-0301-4c23-b3e4-02e5a0114c21", - "target_ref": "identity--ecf1c7de-d96c-41c6-a510-b9c65cdc9e3b" - }, - { - "type": "relationship", - "id": "relationship--9dd881a7-6e9b-4c35-bef5-7a777bca65d3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "threat-actor--02e7c48f-0301-4c23-b3e4-02e5a0114c21", - "target_ref": "malware--fb490cdb-6760-41eb-a79b-0b930a50c017" - }, - { - "type": "relationship", - "id": "relationship--306ce398-f708-47f9-88a1-38aa5b9985fc", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "threat-actor--02e7c48f-0301-4c23-b3e4-02e5a0114c21", - "target_ref": "malware--ea50ecb7-2cd4-4895-bd08-31cd591ed0ca" - }, - { - "type": "relationship", - "id": "relationship--8668d82a-1c97-4bea-a367-e391b025e00e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "intrusion-set--da1065ce-972c-4605-8755-9cd1074e3b5a", - "target_ref": "threat-actor--94624865-2709-443f-9b4c-2891985fd69b" - }, - { - "type": "relationship", - "id": "relationship--e0ca2caa-7fa0-4f36-ad19-96f107eb6023", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "intrusion-set--da1065ce-972c-4605-8755-9cd1074e3b5a", - "target_ref": "threat-actor--d5b62b58-df7c-46b1-a435-4d01945fe21d" - }, - { - "type": "relationship", - "id": "relationship--765815fb-d993-4a1d-959f-7f7bcc4a5eb3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "attributed-to", - "source_ref": "intrusion-set--da1065ce-972c-4605-8755-9cd1074e3b5a", - "target_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65" - }, - { - "type": "relationship", - "id": "relationship--85b2a834-e4b5-4299-9a6b-bf2ac26dde7b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--1e2c4237-d469-4144-9c0b-9e5c0c513c49", - "target_ref": "malware--0f01c5a3-f516-4450-9381-4dd9f2279411" - }, - { - "type": "relationship", - "id": "relationship--61f4fd3b-f581-4497-9149-e624c317287b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--1e2c4237-d469-4144-9c0b-9e5c0c513c49", - "target_ref": "malware--33159b98-3264-4e10-a968-d67975b6272f" - }, - { - "type": "relationship", - "id": "relationship--7cede760-b866-490e-ad5b-1df34bc14f8d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--031778a4-057f-48e6-9db9-c8d72b81ccd5", - "target_ref": "malware--33159b98-3264-4e10-a968-d67975b6272f" - }, - { - "type": "relationship", - "id": "relationship--b2806dec-6f20-4a0d-ae9a-d4b1f7be71e3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--da1d061b-2bc9-467a-b16f-8d14f468e1f0", - "target_ref": "malware--33159b98-3264-4e10-a968-d67975b6272f" - }, - { - "type": "relationship", - "id": "relationship--3921b161-5872-4c21-8ab0-b5b84233f3dc", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--2173d108-5714-42fd-8213-4f3790259fda", - "target_ref": "malware--33159b98-3264-4e10-a968-d67975b6272f" - }, - { - "type": "relationship", - "id": "relationship--81827b05-8c20-4247-b5d8-674295a1c611", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--8ce03314-dfea-4498-ac9b-136e41ab00e4", - "target_ref": "malware--33159b98-3264-4e10-a968-d67975b6272f" - }, - { - "type": "relationship", - "id": "relationship--066593e1-49a4-4a3d-a5bb-2e0b4ce1a63c", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--ce45f721-af14-4fc0-938c-000c16186418" - }, - { - "type": "relationship", - "id": "relationship--b385d984-ba8a-4180-8e0e-af7b9987bcb8", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--e9778c42-bc2f-4eda-9fb4-6a931834f68c" - }, - { - "type": "relationship", - "id": "relationship--6ffbec81-fa01-4b98-8726-c9d9fb2ef6b6", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--1cf6a3b8-be43-4c1a-b042-546a890c31b2" - }, - { - "type": "relationship", - "id": "relationship--25586f60-bc27-47d6-9a8e-d1c6456c2f28", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--4d82bd3e-24a3-4f9d-b8f3-b57267fe06a9" - }, - { - "type": "relationship", - "id": "relationship--d080c1ea-1dd7-4da9-b64b-e68bb1c5887e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--7de5dfcc-6809-4772-9f11-cf26c2be53aa" - }, - { - "type": "relationship", - "id": "relationship--c9c66478-c9cf-49cd-bca2-66ce34a9c56d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--266b12f2-aa16-4607-809e-f2d33eebb52e" - }, - { - "type": "relationship", - "id": "relationship--44686fda-311c-4cdb-abef-80e922e7a3fb", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--98fd8dc1-6cc7-4908-899f-07473f55149a" - }, - { - "type": "relationship", - "id": "relationship--340cb676-79ff-49e9-b6ba-cd27e06772c4", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--e13f3e6d-4f9c-4265-b1cf-f997a1bf7827", - "target_ref": "tool--4215b0e5-928e-4b2a-9b5f-64819f287f48" - }, - { - "type": "relationship", - "id": "relationship--9908520f-b25d-44a8-900b-d4e0825dcd0d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--0781fe70-4c94-4300-8865-4b08b98611b4", - "target_ref": "tool--a6dd62d0-9683-48bf-a9cd-61e7eceae57e" - }, - { - "type": "relationship", - "id": "relationship--1fbd9a8d-4c14-431c-9520-3ccc50b748c1", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "attack-pattern--0781fe70-4c94-4300-8865-4b08b98611b4", - "target_ref": "tool--806a8f83-4913-4216-bb19-02b48ae25da5" - }, - { - "type": "relationship", - "id": "relationship--389a8dcd-8663-4f18-8584-d69a77bd71aa", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--3f3ff9f1-bb4e-4392-89e5-1991179042ba", - "target_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65" - }, - { - "type": "relationship", - "id": "relationship--b345f1d0-09c5-4a71-bfc6-a52bd5923a01", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--8390fd29-24ed-45d4-84d7-c5e5feaf195d", - "target_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65" - }, - { - "type": "relationship", - "id": "relationship--912b31d0-09c5-4a71-bfc6-a52bd5989a1b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--1002c58e-cbde-4930-b5ee-490037fd4f7e", - "target_ref": "threat-actor--6d179234-61fc-40c4-ae86-3d53308d8e65" - } - ] -} diff --git a/src/test/resources/stix/baseline/json/sdo/threatreport/poisonivy.json b/src/test/resources/stix/baseline/json/sdo/threatreport/poisonivy.json deleted file mode 100644 index 5ca55b2..0000000 --- a/src/test/resources/stix/baseline/json/sdo/threatreport/poisonivy.json +++ /dev/null @@ -1,1810 +0,0 @@ -{ - "type": "bundle", - "id": "bundle--ac946f1d-6a0e-4a9d-bc83-3f1f3bfda6ba", - "spec_version": "2.0", - "objects": [ - { - "type": "malware", - "id": "malware--591f0cb7-d66f-4e14-a8e6-5927b597f920", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Poison Ivy", - "description": "Poison Ivy is a remote access tool, first released in 2005 but unchanged since 2008. It includes features common to most Windows-based RATs, including key logging, screen capturing, video capturing, file transfers, system administration, password theft, and traffic relaying.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "identity", - "id": "identity--81cade27-7df8-4730-836b-62d880e6d9d3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "FireEye, Inc.", - "identity_class": "organization", - "sectors": [ - "technology" - ] - }, - { - "type": "campaign", - "id": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "admin@338", - "description": "Active since 2008, this campaign mostly targets the financial services industry, though we have also seen activity in the telecom, government, and defense sectors.", - "first_seen": "2008-01-07T00:00:00.000000Z" - }, - { - "type": "campaign", - "id": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "th3bug", - "description": "This ongoing campaign targets a number of industries but appears to prefer targets in higher education and the healthcare sectors.", - "first_seen": "2009-10-26T00:00:00.000000Z" - }, - { - "type": "campaign", - "id": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "menuPass", - "description": "The threat actor behind menuPass prefers to target U.S. and foreign defense contractors.", - "first_seen": "2009-12-14T00:00:00.000000Z" - }, - { - "type": "attack-pattern", - "id": "attack-pattern--19da6e1c-69a8-4c2f-886d-d620d09d3b5a", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "external_references": [ - { - "source_name": "capec", - "description": "spear phishing", - "external_id": "CAPEC-163" - } - ], - "name": "Spear Phishing Attack Pattern used by admin@338", - "description": "The preferred attack vector used by admin@338 is spear-phishing emails. Using content that is relevant to the target, these emails are designed to entice the target to open an attachment that contains the malicious PIVY server code.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "initial-compromise" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--ea2c747d-4aa3-4573-8853-37b7159bc180", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Strategic Web Compromise Attack Pattern used by th3bug", - "description": "Attacks attributed to th3bug use a strategic Web compromise to infect targets. This approach is more indiscriminate, which probably accounts for the more disparate range of targets.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "initial-compromise" - } - ] - }, - { - "type": "attack-pattern", - "id": "attack-pattern--fb6aa549-c94a-4e45-b4fd-7e32602dad85", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "external_references": [ - { - "source_name": "capec", - "description": "spear phishing", - "external_id": "CAPEC-163" - } - ], - "name": "Spear Phishing Attack Pattern used by menuPass", - "description": "menuPass appears to favor spear phishing to deliver payloads to the intended targets. While the attackers behind menuPass have used other RATs in their campaign, it appears that they use PIVY as their primary persistence mechanism.", - "kill_chain_phases": [ - { - "kill_chain_name": "mandiant-attack-lifecycle-model", - "phase_name": "initial-compromise" - } - ] - }, - { - "type": "course-of-action", - "id": "course-of-action--70b3d5f6-374b-4488-8688-729b6eedac5b", - "created_by_ref": "identity--81cade27-7df8-4730-836b-62d880e6d9d3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Analyze with FireEye Calamine Toolset", - "description": "Calamine is a set of free tools to help organizations detect and examine Poison Ivy infections on their systems. The package includes these components: PIVY callback-decoding tool (ChopShop Module) and PIVY memory-decoding tool (PIVY PyCommand Script).", - "external_references": [ - { - "source_name": "Calamine ChopShop Module", - "description": "The FireEye Poison Ivy decoder checks the beginning of each TCP session for possible PIVY challengeresponse sequences. If found, the module will try to validate the response using one or more passwords supplied as arguments.", - "url": "https://github.com/fireeye/chopshop" - }, - { - "source_name": "Calamine PyCommand Script", - "description": "Helps locate the PIVY password.", - "url": "https://github.com/fireeye/pycommands" - } - ] - }, - { - "type": "malware", - "id": "malware--61a62a6a-9a18-4758-8e52-622431c4b8ae", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (808e21d6efa2884811fbd0adf67fda78)", - "description": "The key@123 sample (password for admin@338), 808e21d6efa2884811fbd0adf67fda78, connects directly to 219.76.208.163", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--30ea087f-7d2b-496b-9ed1-5f000c8b7695", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (8010cae3e8431bb11ed6dc9acabb93b7,)", - "description": "Two CnC domain names from the admin@338 sample 8010cae3e8431bb11ed6dc9acabb93b7, connect to www.webserver.dynssl.com and www.webserver.freetcp.com and resolve to 219.76.208.163. It also connects to the CnC domain www.webserver.fartit.com.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--4de25c38-5826-4ee7-b84d-878064de87ad", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (0323de551aa10ca6221368c4a73732e6,)", - "description": "The gwx@123 sample 0323de551aa10ca6221368c4a73732e6 connects to the CnC domain names microsofta.byinter.net, microsoftb.byinter.net, microsoftc.byinter. net, and microsofte.byinter.net.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--dc669921-4a1a-470d-bfae-694e740ce181", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (8002debc47e04d534b45f7bb7dfcab4d)", - "description": "The sample 8002debc47e04d534b45f7bb7dfcab4d connected to kr.iphone.qpoe.com with the PIVY password admin.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--f86febd3-609b-4d2e-9fec-aa805cb498bf", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (55a3b2656ceac2ba6257b6e39f4a5b5a)", - "description": "The sample 55a3b2656ceac2ba6257b6e39f4a5b5a connected to ct.toh.info domain with the PIVY password th3bug.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--80c260d9-a075-4148-9301-ebe4af27f449", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (b08694e14a9b966d8033b42b58ab727d)", - "description": "This sample (b08694e14a9b966d8033b42b58ab727d) for menuPass connected to a control server at js001.3322.org with a password xiaoxiaohuli (Chinese translation: 'little little fox')", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--3ed0364f-62c8-4ebc-b136-deaf6966880b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (d8c00fed6625e5f8d0b8188a5caac115)", - "description": "The sample d8c00fed6625e5f8d0b8188a5caac115 connected to apple.cmdnetview.com with the password XGstone.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--17099f03-5ec8-456d-a2de-968aebaafc78", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (b1deff736b6d12b8d98b485e20d318ea)", - "description": "The sample b1deff736b6d12b8d98b485e20d318ea connected to autuo.xicp.net with the password keaidestone.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--e14b6476-40b5-4b0b-bde7-0e856ab00b6c", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (08709f35581e0958d1ca4e50b7d86dba)", - "description": "The sample 08709f35581e0958d1ca4e50b7d86dba has a compile time of July 20. 2012 and connected to tw.2012yearleft.com with the password keaidestone. This sample also used the CBricksDoc launcher.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--feaf146d-ea67-4eb1-946a-6f352ff79a81", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (e84853c0484b02b7518dd683787d04f)", - "description": "The sample e84853c0484b02b7518dd6837 87d04fc connected to dedydns.ns01.us with the password smallfish and used the CBricksDoc launcher.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--13791e02-6621-45fb-8c10-f6b72e1bf553", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (cf8094c07c15aa394dddd4eca4aa8c8b)", - "description": "The sample cf8094c07c15aa394dddd4eca4aa8c8b connected to maofajapa.3322.org with the password happyyongzi.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--703a15a7-eb85-475d-a27a-77d8fcf8f7b9", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (410eeaa18dbec01a27c5b41753b3c7ed )", - "description": "The sample 410eeaa18dbec01a27c5b41753b3c7ed connected to send.have8000.com with the password of suzuki.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--fade08cb-fa57-485e-97f8-fab5a1bd4460", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (b2dc98caa647e64a2a8105c298218462)", - "description": "The sample b2dc98caa647e64a2a8105c298218462 connected to apple.cmdnetview.com with the password XGstone.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--3050937d-6330-44c7-83ba-8821e1f7e7bd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (68fec995a13762184a2616bda86757f8)", - "description": "68fec995a13762184a2616bda86757f8 had a compile time of March 25, 2012 and connected to fbi.zyns.com with the password menuPass. This sample also used the CBricksDoc launcher.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--9d995717-edc3-4bd8-8554-aecf773bdecc", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (39a59411e7b12236c0b4351168fb47ce)", - "description": "The sample 39a59411e7b12236c0b4351168fb47ce had a compile time of April 2, 2010 and connected to weile3322b.3322.org with the password keaidestone. This sample used a launcher of CPiShellPutDoc.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--40e15fa5-df8d-4771-a682-21dab0a024fd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (f5315fb4a654087d30c69c768d80f826)", - "description": "The sample f5315fb4a654087d30c69c768d80f826 had a compile time of May 21, 2010 and connected to ngcc.8800.org with the password menuPass. This sample also used a launcher of CPiShellPutDoc.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--69101c2f-da92-47af-b402-7c60a39a982f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (e6ca06e9b000933567a8604300094a85)", - "description": "The sample e6ca06e9b000933567a8604300094a85 connected to the domain sh.chromeenter.com with the password happyyongzi.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--1601b8c2-5e6f-4a18-a413-10527e5d90b7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (56cff0d0e0ce486aa0b9e4bc0bf2a141)", - "description": "The sample 56cff0d0e0ce486aa0b9e4bc0bf2a141 was compiled on 2011-08-31 and connected to mf.ddns.info with the password menuPass.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--626badcc-4257-4222-946c-6d6e889836ea", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (60963553335fa5877bd5f9be9d8b23a6 )", - "description": "The sample 60963553335fa5877bd5f9be9d8b23a6 was compiled on June 9, 2012 and connected to av.ddns.us with the password of admin", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--3b275ed1-9c2e-4443-b1dd-5cfb51eaef2e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (6d989302166ba1709d66f90066c2fd59)", - "description": "A number of menuPass and admin samples also shared the same CBricksDoc launcher including but not limited to 6d989302166ba1709d66f90066c2fd59.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--f138b6e0-9a7d-4cd9-a904-08a7df2eabb1", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (4bc6cab128f623f34bb97194da21d7b6)", - "description": "A number of menuPass and admin samples also shared the same CBricksDoc launcher including but not limited to 4bc6cab128f623f34bb97194da21d7b6.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--302ac5b5-486c-4c99-8cad-4426aeaf47b6", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (4e84b1448cf96fabe88c623b222057c4)", - "description": "The sample 4e84b1448cf96fabe88c623b222057c4 connected to jj.mysecondarydns.com with the password menuPass", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--e1c02dca-d3fe-48f1-bb4b-3cacd2bc3619", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (494e65cf21ad559fccf3dacdd69acc94)", - "description": "The sample 494e65cf21ad559fccf3dacdd69acc94 connected to mongoles.3322.org with the password fishplay. It also connects to CBricksDoc.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "malware", - "id": "malware--a4f315bd-e159-4bfb-8439-0d5a8330fc70", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "PIVY Variant (a5965b750997dbecec61358d41ac93c7)", - "description": "The sample a5965b750997dbecec61358d41ac93c7 connected to 3q.wubangtu.info with the password menuPass. It also connects to CBricksDoc.", - "labels": [ - "remote-access-trojan" - ] - }, - { - "type": "indicator", - "id": "indicator--e8094b09-7df4-4b13-b207-1e27af3c4bde", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "IP address: 219.76.208.163", - "description": "IP address for key@123 sample 808e21d6efa2884811fbd0adf67fda78", - "pattern": "[ipv4-addr:value = '219.76.208.163']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--329ae6e9-25bd-49e8-89d1-aae4ca52e4a7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "www.webserver.dynssl.com", - "description": "www.webserver.dynssl.com resolved to 113.10.246.30, 219.90.112.203, 219.90.112.203, 75.126.95.138, 219.90.112.197, and 202.65.222.45, which overlap with the gwx@123 IP addresses.", - "pattern": "[domain-name:value = 'www.webserver.dynssl.com' OR ipv4-addr:value = '113.10.246.30' OR ipv4-addr:value = '219.90.112.203' OR ipv4-addr:value = '75.126.95.138' OR ipv4-addr:value = '219.90.112.197' OR ipv4-addr:value = '202.65.222.45']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--54e1e351-fec0-41a4-b62c-d7f86101e241", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "www.webserver.freetcp.com", - "description": "www.webserver.freetcp.com resolved to 113.10.246.30, 219.90.112.203, 202.65.220.64, 75.126.95.138, 219.90.112.197, and 202.65.222.45, which overlap with the gwx@123 IP addresses.", - "pattern": "[domain-name:value = 'www.webserver.freetcp.com' OR ipv4-addr:value = '113.10.246.30' OR ipv4-addr:value = '219.90.112.203' OR ipv4-addr:value = '202.65.220.64' OR ipv4-addr:value = '75.126.95.138' OR ipv4-addr:value = '219.90.112.197' OR ipv4-addr:value = '202.65.222.45']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--2e59f00b-0986-437e-9ebd-e0d61900d688", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "www.webserver.fartit.com", - "description": "www.webserver.fartit.com resolved to 113.10.246.30, 219.90.112.203, 202.65.220.64, and 75.126.95.138, which overlap with the gwx@123 IP addresses.", - "pattern": "[domain-name:value = 'www.webserver.fartit.com' OR ipv4-addr:value = '113.10.246.30' OR ipv4-addr:value = '219.90.112.203' OR ipv4-addr:value = '202.65.220.64' OR ipv4-addr:value = '75.126.95.138']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--8da68996-f175-4ae0-bd74-aad4913873b8", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "microsoft.byinter.net", - "description": "The gwx@123 sample 0323de551aa10ca6221368c4a73732e6 connects to the CnC domain names microsofta.byinter.net, microsoftb.byinter.net, microsoftc.byinter.net, and microsofte.byinter.net. These domain names resolved to 113.10.246.30, 219.90.112.203, 202.65.220.64, 75.126.95.138, 219.90.112.197, 202.65.222.45, and 98.126.148.114.", - "pattern": "[domain-name:value = 'microsofta.byinter.net' OR domain-name:value = 'microsoftb.byinter.net' OR domain-name:value = 'microsoftc.byinter.net' OR domain-name:value = 'microsofte.byinter.net' OR ipv4-addr:value = '113.10.246.30' OR ipv4-addr:value = '219.90.112.203' OR ipv4-addr:value = '202.65.220.64' OR ipv4-addr:value = '75.126.95.138' OR ipv4-addr:value = '219.90.112.197' OR ipv4-addr:value = '202.65.222.45' OR ipv4-addr:value = '98.126.148.114']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--4e11b23f-732b-418e-b786-4dbf65459d50", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "nkr.iphone.qpoe.com", - "description": "th3bug used domain name: nkr.iphone.qpoe.com. The domain nkr.iphone.qpoe.com resolved to 180.210.206.96 on January 12, 2012 and also 101.78.151.179 on December 23, 2011.", - "pattern": "[domain-name:value = 'nkr.iphone.qpoe.com' OR ipv4-addr:value = '180.210.206.96' OR ipv4-addr:value = '101.78.151.179']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b7fa7e73-e645-4813-9723-161bbd8dda62", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "e.ct.toh.info", - "description": "th3bug used domain name: e.ct.toh.info. The domain e.ct.toh.info resolved to 101.78.151.179 on June 12, 2012. The sample 55a3b2656ceac2ba6257b6e39f4a5b5a connected to ct.toh.info domain with the PIVY password th3bug.", - "pattern": "[domain-name:value = 'e.ct.toh.info' OR ipv4-addr:value = '101.78.151.179']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b2f09ce0-2db4-480f-bd2f-073ddb3a0c87", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "js001.3322.org", - "description": "menuPass used control server: js001.3322.org. The sample (b08694e14a9b966d8033b42b58ab727d) connected to a control server at js001.3322.org with a password xiaoxiaohuli (Chinese translation: 'little little fox')", - "pattern": "[domain-name:value = 'js001.3322.org' OR ipv4-addr:value = '101.78.151.179']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--9842a3b9-fc5b-44c4-bb48-578cf6f728d9", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "apple.cmdnetview.com", - "description": "menuPass used domain: apple.cmdnetview.com. The sample d8c00fed6625e5f8d0b8188a5caac115 connected to apple.cmdnetview.com with the password XGstone. IP 60.10.1.120 hosted this domain.", - "pattern": "[domain-name:value = 'apple.cmdnetview.com' OR ipv4-addr:value = '60.10.1.120']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--4e4c4ad7-4909-456a-b6fa-e24a6f682a40", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "autuo.xicp.net", - "description": "menuPass used domain: autuo.xicp.net. The sample b1deff736b6d12b8d98b485e20d318ea connected to autuo.xicp.net with the password keaidestone. IP 60.10.1.115 hosted this domain.", - "pattern": "[domain-name:value = 'domain autuo.xicp.net' OR ipv4-addr:value = '60.10.1.115']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CBricksDoc", - "description": "menuPass uses Microsoft Foundation Class Library class name CBricksDoc as a launcher for PIVY.", - "pattern": "[file:name = 'CBricksDoc']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--9695dc2f-d92a-4f2b-8b16-b0e21d7c631d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "tw.2012yearleft.com", - "description": "08709f35581e0958d1ca4e50b7d86dba has a compile time of July 20. 2012 and connected to tw.2012yearleft.com with the password keaidestone. 2012yearleft.com was registered on February 13, 2012 by zhengyanbin8@gmail.com.", - "pattern": "[domain-name:value = 'tw.2012yearleft.com' OR ipv4-addr:value = '60.10.1.114' OR ipv4-addr:value = '60.1.1.114']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--7fd865ed-93e9-481f-953b-82ab386190ae", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "dedydns.ns01.us", - "description": "The domain dedydns.ns01.us resolved to 60.10.1.121. The sample e84853c0484b02b7518dd6837 87d04fc connected to dedydns.ns01.us with the password smallfish and used the CBricksDoc launcher.", - "pattern": "[domain-name:value = 'dedydns.ns01.us' OR ipv4-addr:value = '60.10.1.121']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--e5bc6507-d052-447f-93c7-db7ef32211da", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "maofajapa.3322.org", - "description": "The domain maofajapa.3322.org resolved to 60.10.1.121. The sample cf8094c07c15aa394dddd4eca4aa8c8b connected to maofajapa.3322.org with the password happyyongzi.", - "pattern": "[domain-name:value = 'maofajapa.3322.org' OR ipv4-addr:value = '60.10.1.121']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--fead5c52-9533-405c-b822-a034092a1ba8", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "send.have8000.com", - "description": "The sample 410eeaa18dbec01a27c5b41753b3c7ed connected to send.have8000.com with the password of suzuki. The domain have8000.com was registered on 2012-02-13 via the email zhengyanbin8@ gmail.com.", - "pattern": "[domain-name:value = 'send.have8000.com']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--405ff732-2c35-4f46-9f78-2a632ce36e03", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "fbi.zyns.com", - "description": "The domain fbi.zyns.com resolved to 60.10.1.118 on August 21, 2012. 68fec995a13762184a2616bda86757f8 had a compile time of March 25, 2012 and connected to fbi.zyns.com with the password menuPass.", - "pattern": "[domain-name:value = 'fbi.zyns.com' OR ipv4-addr:value = '60.10.1.118']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--4d58096e-b5c9-47d8-af9a-1af5f4762d6b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "weile3322b.3322.org", - "description": "The sample 39a59411e7b12236c0b4351168fb47ce had a compile time of April 2, 2010 and connected to weile3322b.3322.org with the password keaidestone.", - "pattern": "[domain-name:value = 'weile3322b.3322.org']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--9c725598-a160-4e91-8b93-ed0956709892", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "ngcc.8800.org", - "description": "The sample f5315fb4a654087d30c69c768d80f826 had a compile time of May 21, 2010 and connected to ngcc.8800.org with the password menuPass", - "pattern": "[domain-name:value = 'ngcc.8800.org']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--2efe7c62-1b96-4568-81ee-c85b840bde39", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "sh.chromeenter.com", - "description": "The sample e6ca06e9b000933567a8604300094a85 connected to the domain sh.chromeenter.com with the password happyyongzi. The domain sh.chromeenter.com previously resolved to the IP 60.2.148.167.", - "pattern": "[domain-name:value = 'sh.chromeenter.com' OR ipv4-addr:value = '60.2.148.167']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b8322c9b-8031-4fb3-9cbc-8a1ea0fe3cfa", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "mf.ddns.info", - "description": "The sample 56cff0d0e0ce486aa0b9e4bc0bf2a141 was compiled on 2011-08-31 and connected to mf.ddns.info with the password menuPass. The domain mf.ddns.info resolved to 54.241.8.84 on November 22, 2012. This same IP also hosted the domain av.ddns.us on the same date.", - "pattern": "[domain-name:value = 'mf.ddns.info' OR ipv4-addr:value = '60.2.148.167']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b08f9631-dd94-4d99-a96c-32b42af2ea81", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "av.ddns.us", - "description": "The sample 60963553335fa5877bd5f9be9d8b23a6 was compiled on June 9, 2012 and connected to av.ddns.us with the password of admin.", - "pattern": "[domain-name:value = 'av.ddns.us' OR ipv4-addr:value = '60.2.148.167']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--950c01b8-c647-4cc8-b0c1-3612fa780108", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "jj.mysecondarydns.com", - "description": "The sample 4e84b1448cf96fabe88c623b222057c4 connected to jj.mysecondarydns.com with the password menuPass. The domain jj.mysecondarydns.com also resolved to 60.2.148.167.", - "pattern": "[domain-name:value = 'jj.mysecondarydns.com' OR ipv4-addr:value = '60.2.148.167']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--ae29faa6-5f70-4eb8-981b-30818433a52e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "mongoles.3322.org", - "description": "The sample 494e65cf21ad559fccf3dacdd69acc94 connected to mongoles.3322.org with the password fishplay.", - "pattern": "[domain-name:value = 'mongoles.3322.org' OR ipv4-addr:value = '123.183.210.28']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--b6cc482d-89db-4e6b-a592-723070f6d22d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "3q.wubangtu.info", - "description": "The sample a5965b750997dbecec61358d41ac93c7 connected to 3q.wubangtu.info with the password menuPass. The domain wubangtu.info also resolved to 123.183.210.28.", - "pattern": "[domain-name:value = '3q.wubangtu.info' OR ipv4-addr:value = '123.183.210.28']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "indicator", - "id": "indicator--0b71628d-31dd-4eb8-baee-39f19c0a14b0", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CPiShellPutDoc", - "description": "menuPass uses CPiShellPutDoc as a launcher for PIVY.", - "pattern": "[file:name = 'CPiShellPutDoc']", - "labels": [ - "malicious-activity", - "attribution" - ], - "valid_from": "2015-05-15T09:12:16.432678Z" - }, - { - "type": "vulnerability", - "id": "vulnerability--c7cab3fb-0822-43a5-b1ba-c9bab34361a2", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CVE-2012-0158", - "description": "Weaponized Microsoft Word document used by admin@338", - "external_references": [ - { - "source_name": "cve", - "external_id": "CVE-2012-0158" - } - ] - }, - { - "type": "vulnerability", - "id": "vulnerability--6a2eab9c-9789-4437-812b-d74323fa3bca", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CVE-2009-4324", - "description": "Adobe acrobat PDF's used by admin@338", - "external_references": [ - { - "source_name": "cve", - "external_id": "CVE-2009-4324" - } - ] - }, - { - "type": "vulnerability", - "id": "vulnerability--2b7f00d8-b133-4a92-9118-46ce5f8b2531", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CVE-2013-0422", - "description": "Java 7 vulnerability exploited by th3bug", - "external_references": [ - { - "source_name": "cve", - "external_id": "CVE-2013-0422" - } - ] - }, - { - "type": "vulnerability", - "id": "vulnerability--4d7dc9cb-983f-40b4-b597-d7a38b2d9a4b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CVE-2013-1347", - "description": "Microsoft Internet Explorer 8 vulnerability exploited by th3bug", - "external_references": [ - { - "source_name": "cve", - "external_id": "CVE-2013-1347" - } - ] - }, - { - "type": "vulnerability", - "id": "vulnerability--8323404c-1fdd-4272-822b-829f85556c53", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CVE-2011-3544", - "description": "JRE vulnerability exploited by th3bug", - "external_references": [ - { - "source_name": "cve", - "external_id": "CVE-2011-3544" - } - ] - }, - { - "type": "vulnerability", - "id": "vulnerability--717cb1c9-eab3-4330-8340-e4858055aa80", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "CVE-2010-3333", - "description": "menuPass campaign using weaponized Microsoft Word documents, exploiting this vulnerability", - "external_references": [ - { - "source_name": "cve", - "external_id": "CVE-2010-3333" - } - ] - }, - { - "type": "report", - "id": "report--f2b63e80-b523-4747-a069-35c002c690db", - "created_by_ref": "identity--81cade27-7df8-4730-836b-62d880e6d9d3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "name": "Poison Ivy: Assessing Damage and Extracting Intelligence", - "labels": [ - "threat-report", - "malware" - ], - "published": "2013-08-21T00:00:00.000000Z", - "description": "This report spotlights Poison Ivy (PIVY), a RAT that remains popular and effective a full eight years after its release, despite its age and familiarity in IT security circles. Poison Ivy is a remote access tool that is freely available for download from its official web site at www.poisonivy-rat.com. First released in 2005, the tool has gone unchanged since 2008 with version 2.3.2. Poison Ivy includes features common to most Windows-based RATs, including key logging, screen capturing, video capturing, file transfers, system administration, password theft, and traffic relaying. Poison Ivy's wide availability and easy-to-use features make it a popular choice for all kinds of criminals. But it is probably most notable for its role in many high profile, targeted APT attacks. These APTs pursue specific targets, using RATs to maintain a persistent presence within the target's network. They move laterally and escalate system privileges to extract sensitive information-whenever the attacker wants to do so. Because some RATs used in targeted attacks are widely available, determining whether an attack is part of a broader APT campaign can be difficult. Equally challenging is identifying malicious traffic to determine the attacker's post-compromise activities and assess overall damage - these RATs often encrypt their network communications after the initial exploit. In 2011, three years after the most recent release of PIVY, attackers used the RAT to compromise security firm RSA and steal data about its SecureID authentication system. That data was subsequently used in other attacks. The RSA attack was linked to Chinese threat actors and described at the time as extremely sophisticated. Exploiting a zero-day vulnerability, the attack delivered PIVY as the payload. It was not an isolated incident. The campaign appears to have started in 2010, with many other companies compromised. PIVY also played a key role in the 2011 campaign known as Nitro that targeted chemical makers, government agencies, defense contractors, and human rights groups. Still active a year later, the Nitro attackers used a zero-day vulnerability in Java to deploy PIVY in 2012. Just recently, PIVY was the payload of a zero-day exploit in Internet Explorer used in what is known as a 'strategic web compromise' attack against visitors to a U.S. government website and a variety of others. RATs require live, direct, real-time human interaction by the APT attacker. This characteristic is distinctly different from crimeware (malware focused on cybercrime), where the criminal can issue commands to their botnet of compromised endpoints whenever they please and set them to work on a common goal such as a spam relay. In contrast, RATs are much more personal and may indicate that you are dealing with a dedicated threat actor that is interested in your organization specifically.", - "object_refs": [ - "malware--591f0cb7-d66f-4e14-a8e6-5927b597f920", - "malware--61a62a6a-9a18-4758-8e52-622431c4b8ae", - "malware--30ea087f-7d2b-496b-9ed1-5f000c8b7695", - "malware--4de25c38-5826-4ee7-b84d-878064de87ad", - "malware--dc669921-4a1a-470d-bfae-694e740ce181", - "malware--f86febd3-609b-4d2e-9fec-aa805cb498bf", - "malware--80c260d9-a075-4148-9301-ebe4af27f449", - "malware--3ed0364f-62c8-4ebc-b136-deaf6966880b", - "malware--17099f03-5ec8-456d-a2de-968aebaafc78", - "malware--feaf146d-ea67-4eb1-946a-6f352ff79a81", - "malware--13791e02-6621-45fb-8c10-f6b72e1bf553", - "malware--703a15a7-eb85-475d-a27a-77d8fcf8f7b9", - "malware--fade08cb-fa57-485e-97f8-fab5a1bd4460", - "malware--3050937d-6330-44c7-83ba-8821e1f7e7bd", - "malware--9d995717-edc3-4bd8-8554-aecf773bdecc", - "malware--40e15fa5-df8d-4771-a682-21dab0a024fd", - "malware--69101c2f-da92-47af-b402-7c60a39a982f", - "malware--1601b8c2-5e6f-4a18-a413-10527e5d90b7", - "malware--626badcc-4257-4222-946c-6d6e889836ea", - "malware--3b275ed1-9c2e-4443-b1dd-5cfb51eaef2e", - "malware--f138b6e0-9a7d-4cd9-a904-08a7df2eabb1", - "malware--302ac5b5-486c-4c99-8cad-4426aeaf47b6", - "malware--e1c02dca-d3fe-48f1-bb4b-3cacd2bc3619", - "malware--a4f315bd-e159-4bfb-8439-0d5a8330fc70", - "identity--81cade27-7df8-4730-836b-62d880e6d9d3", - "campaign--752c225d-d6f6-4456-9130-d9580fd4007b", - "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "attack-pattern--19da6e1c-69a8-4c2f-886d-d620d09d3b5a", - "attack-pattern--ea2c747d-4aa3-4573-8853-37b7159bc180", - "attack-pattern--fb6aa549-c94a-4e45-b4fd-7e32602dad85", - "course-of-action--70b3d5f6-374b-4488-8688-729b6eedac5b", - "indicator--e8094b09-7df4-4b13-b207-1e27af3c4bde", - "indicator--329ae6e9-25bd-49e8-89d1-aae4ca52e4a7", - "indicator--54e1e351-fec0-41a4-b62c-d7f86101e241", - "indicator--2e59f00b-0986-437e-9ebd-e0d61900d688", - "indicator--8da68996-f175-4ae0-bd74-aad4913873b8", - "indicator--4e11b23f-732b-418e-b786-4dbf65459d50", - "indicator--b7fa7e73-e645-4813-9723-161bbd8dda62", - "indicator--b2f09ce0-2db4-480f-bd2f-073ddb3a0c87", - "indicator--9842a3b9-fc5b-44c4-bb48-578cf6f728d9", - "indicator--4e4c4ad7-4909-456a-b6fa-e24a6f682a40", - "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "indicator--9695dc2f-d92a-4f2b-8b16-b0e21d7c631d", - "indicator--7fd865ed-93e9-481f-953b-82ab386190ae", - "indicator--e5bc6507-d052-447f-93c7-db7ef32211da", - "indicator--fead5c52-9533-405c-b822-a034092a1ba8", - "indicator--405ff732-2c35-4f46-9f78-2a632ce36e03", - "indicator--4d58096e-b5c9-47d8-af9a-1af5f4762d6b", - "indicator--9c725598-a160-4e91-8b93-ed0956709892", - "indicator--2efe7c62-1b96-4568-81ee-c85b840bde39", - "indicator--b8322c9b-8031-4fb3-9cbc-8a1ea0fe3cfa", - "indicator--b08f9631-dd94-4d99-a96c-32b42af2ea81", - "indicator--950c01b8-c647-4cc8-b0c1-3612fa780108", - "indicator--ae29faa6-5f70-4eb8-981b-30818433a52e", - "indicator--b6cc482d-89db-4e6b-a592-723070f6d22d", - "indicator--0b71628d-31dd-4eb8-baee-39f19c0a14b0", - "vulnerability--c7cab3fb-0822-43a5-b1ba-c9bab34361a2", - "vulnerability--6a2eab9c-9789-4437-812b-d74323fa3bca", - "vulnerability--2b7f00d8-b133-4a92-9118-46ce5f8b2531", - "vulnerability--4d7dc9cb-983f-40b4-b597-d7a38b2d9a4b", - "vulnerability--8323404c-1fdd-4272-822b-829f85556c53", - "vulnerability--717cb1c9-eab3-4330-8340-e4858055aa80", - "relationship--26c5311c-9d9b-4b9b-b3b5-bac10e16a7a3", - "relationship--e794befc-3270-4050-b560-b6b080ab0418", - "relationship--77a4c40e-3c33-43dc-8c78-04992ebcabf2", - "relationship--a91f3d5c-ceac-44cf-b92b-efb819241606", - "relationship--134c393e-cbe0-433c-9a7a-95263ed8578f", - "relationship--900b11dc-bfa7-4dea-adb6-0e8d726b4ded", - "relationship--8076ec7c-f6f6-4dca-a239-8bb6b5ad0c10", - "relationship--0dd66a71-c45b-4786-bd7b-92cf952afdc1", - "relationship--dc37f2bb-1a45-48b1-864e-c34dcde75d1d", - "relationship--670ae011-1649-44e2-a63e-ead0b4a4cffd", - "relationship--1a2a3630-5764-4d6e-a3c3-cb4ca27ff5f5", - "relationship--b5046891-d2c0-4497-a167-594f778517f8", - "relationship--253dbb93-c6f9-4839-8ce9-026c7b0a81e1", - "relationship--d70ebcc3-5640-423d-b9b0-7158c532c040", - "relationship--3bb540a4-c3be-478e-85e2-2a6c294c3dbd", - "relationship--4e726ced-0207-4196-8a14-4400c09b039e", - "relationship--b9736cd3-9482-4094-9178-1cde2b273aff", - "relationship--70205e3e-195d-4bd5-a208-ada6cdf143e3", - "relationship--6bb5a995-b874-4e17-88eb-38e00c8e5740", - "relationship--d4247377-5302-4ede-a0f2-579f7db67bb6", - "relationship--b8617e55-00c0-4066-8222-927846edcafe", - "relationship--f34d9e2e-715f-4baf-8226-40abfcb91012", - "relationship--937f310a-396a-403f-bb6f-400ad8920018", - "relationship--14a06709-3c0b-4e72-ad49-dd0f6d775e65", - "relationship--5f6c6509-ca0c-43db-8c0e-8e138f6d913c", - "relationship--ca99fa83-0d1b-4ddd-88c8-0dee38856a88", - "relationship--38a52125-130f-4ce7-9b38-f234553ba83d", - "relationship--e13b17d0-1fef-4f98-a4a8-895c3e4cf1e2", - "relationship--262a8234-d7e2-477a-baeb-ed65b639e33a", - "relationship--f4ceabc6-9302-4dc5-9cc1-4d40ef43503c", - "relationship--56b1023c-9e28-4449-8b4f-bc2adde45e1a", - "relationship--8997440e-00f5-48e1-8b56-69d3b6f9f1fd", - "relationship--80ac0601-0660-4057-b3b0-dca0fe35a6b4", - "relationship--2583921a-2f02-42c5-bd25-0f37eb2e6ef9", - "relationship--7231e729-42e3-4f29-ae6f-6d80192c4bd1", - "relationship--201ee2d5-74f4-4beb-b13a-34d948854655", - "relationship--afee4dc4-7d0e-450d-9164-4429649ab386", - "relationship--ed403d0d-b55d-4e78-94d3-4e035a045c39", - "relationship--4303ebf2-9590-4ec0-a702-e7bfff64bc5f", - "relationship--54f845bb-0967-4c0f-ac8a-8ad4785cbbe6", - "relationship--0bd19ca0-2bbb-4df0-92ec-59a4e9169c64", - "relationship--89ddeb74-ea26-44f9-bb6d-3f17c9d4efaa", - "relationship--eb400750-c866-47c3-89a2-fa6d1a90e9e7", - "relationship--7450856e-051f-4d49-953c-ad24f170af0e", - "relationship--1d6b0425-603d-4217-948a-fabb2a398450", - "relationship--1895dd86-dc46-4505-ba62-5724a1df2362", - "relationship--a4e0751d-8d59-4447-96ea-3799fecf66d7", - "relationship--258796f6-e46a-421a-b3f5-7db6114fb2bc", - "relationship--9431d9f9-6d8b-4373-b42c-172a663391b3", - "relationship--07d2f213-1794-483e-b95b-03761826c052", - "relationship--aa430e5b-0519-4e94-bc2c-8836d196acd7", - "relationship--c0786bd4-9c15-48ee-a19e-a9d6aba25d67", - "relationship--498b9f3b-488b-40d5-aaaf-e67b93c1d92b", - "relationship--d875538e-cc47-4353-a572-2dae27ef0a44", - "relationship--313d56c4-eef9-417e-952d-073690c20ee4", - "relationship--6b091c0f-a700-4f3e-9d98-0b8abf9a306b", - "relationship--5aff864a-1789-4df2-87fa-03ec43cf4fdd", - "relationship--325ebcb6-723c-4f50-8a32-aca18809e6eb", - "relationship--0cb9c725-3d55-4165-b2a9-9414d7933987", - "relationship--640a0454-57eb-408f-aa13-b5732b4d0b6f", - "relationship--41550302-6e95-4cf6-8d7c-d417a99d98dc", - "relationship--911dcbb0-96f4-4995-9961-5ea4b2fa7ce2", - "relationship--7b6ba584-fa87-4a6f-8c21-8123fa88db74", - "relationship--69101c2f-da92-47af-b402-7c60a39a982f", - "relationship--25055108-a2ae-4855-bd5f-6ab396aacbc5", - "relationship--44c80cab-73ce-4b17-a4cb-9a36e2585403", - "relationship--32655cb3-7455-4761-b1f2-0b82153a0540", - "relationship--fe963c8c-65a4-49ea-910b-e1cf3c80f1b4", - "relationship--4b0abf75-6f05-4bd5-8ac5-19778b245274", - "relationship--154049a5-731d-4e50-af13-f0f2c9b71f91", - "relationship--db55db06-499d-4867-9ab9-3ed4331eedb2", - "relationship--cc802697-7677-4bd7-a8b9-e728788ac783", - "relationship--a371be18-8ca5-4453-80f5-ae52d982c21b", - "relationship--9a3bd620-01b5-4764-beb0-f085417ed8f3", - "relationship--48906405-9980-4583-8559-2085c111bf89", - "relationship--13222c71-d8fa-4688-adae-c3f8ca43a41b", - "relationship--73c4529e-560e-4831-8497-a0db72f7dfd8", - "relationship--8d3e1ed6-7d9c-4aa5-b121-f4eb193312cf", - "relationship--2c11dcc0-7968-4c07-bdde-791a8f5e2e37", - "relationship--fd97d0ef-370e-4b6f-b2d3-8fb881aadc3f", - "relationship--c05d2410-848c-47e5-a94f-c64510e2b08d", - "relationship--92a21b52-2961-42aa-8b01-54ea294d9d73", - "relationship--76a9283a-b844-47a5-a5d0-b31859115f88", - "relationship--bbd3ba5c-2a75-4902-bd42-1215a2bc320e", - "relationship--4f784f2f-7d8e-4f12-9ddd-b685055f8076", - "relationship--263e38f4-8ecb-414f-b3c4-0f045d1be5ed", - "relationship--a56e8582-fc6e-4be8-bf35-7e939269d65e", - "relationship--d7d9952c-4443-4711-a48c-7009a0f0f8ea", - "relationship--78f110e6-2cd6-442e-971f-a2ff40c3b843", - "relationship--b2fb88f2-5ad7-4c07-b4b2-61986decb477" - ] - }, - { - "type": "relationship", - "id": "relationship--26c5311c-9d9b-4b9b-b3b5-bac10e16a7a3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b", - "target_ref": "attack-pattern--19da6e1c-69a8-4c2f-886d-d620d09d3b5a" - }, - { - "type": "relationship", - "id": "relationship--e794befc-3270-4050-b560-b6b080ab0418", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "target_ref": "attack-pattern--ea2c747d-4aa3-4573-8853-37b7159bc180" - }, - { - "type": "relationship", - "id": "relationship--77a4c40e-3c33-43dc-8c78-04992ebcabf2", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "attack-pattern--fb6aa549-c94a-4e45-b4fd-7e32602dad85" - }, - { - "type": "relationship", - "id": "relationship--134c393e-cbe0-433c-9a7a-95263ed8578f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "mitigates", - "source_ref": "course-of-action--70b3d5f6-374b-4488-8688-729b6eedac5b", - "target_ref": "malware--591f0cb7-d66f-4e14-a8e6-5927b597f920" - }, - { - "type": "relationship", - "id": "relationship--a91f3d5c-ceac-44cf-b92b-efb819241606", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b", - "target_ref": "malware--61a62a6a-9a18-4758-8e52-622431c4b8ae" - }, - { - "type": "relationship", - "id": "relationship--900b11dc-bfa7-4dea-adb6-0e8d726b4ded", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--e8094b09-7df4-4b13-b207-1e27af3c4bde", - "target_ref": "malware--61a62a6a-9a18-4758-8e52-622431c4b8ae" - }, - { - "type": "relationship", - "id": "relationship--8076ec7c-f6f6-4dca-a239-8bb6b5ad0c10", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--e8094b09-7df4-4b13-b207-1e27af3c4bde", - "target_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b" - }, - { - "type": "relationship", - "id": "relationship--0dd66a71-c45b-4786-bd7b-92cf952afdc1", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--329ae6e9-25bd-49e8-89d1-aae4ca52e4a7", - "target_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b" - }, - { - "type": "relationship", - "id": "relationship--dc37f2bb-1a45-48b1-864e-c34dcde75d1d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--329ae6e9-25bd-49e8-89d1-aae4ca52e4a7", - "target_ref": "malware--30ea087f-7d2b-496b-9ed1-5f000c8b7695" - }, - { - "type": "relationship", - "id": "relationship--670ae011-1649-44e2-a63e-ead0b4a4cffd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--8da68996-f175-4ae0-bd74-aad4913873b8", - "target_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b" - }, - { - "type": "relationship", - "id": "relationship--1a2a3630-5764-4d6e-a3c3-cb4ca27ff5f5", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--8da68996-f175-4ae0-bd74-aad4913873b8", - "target_ref": "malware--4de25c38-5826-4ee7-b84d-878064de87ad" - }, - { - "type": "relationship", - "id": "relationship--b5046891-d2c0-4497-a167-594f778517f8", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--2e59f00b-0986-437e-9ebd-e0d61900d688", - "target_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b" - }, - { - "type": "relationship", - "id": "relationship--253dbb93-c6f9-4839-8ce9-026c7b0a81e1", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--2e59f00b-0986-437e-9ebd-e0d61900d688", - "target_ref": "malware--30ea087f-7d2b-496b-9ed1-5f000c8b7695" - }, - { - "type": "relationship", - "id": "relationship--d70ebcc3-5640-423d-b9b0-7158c532c040", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b", - "target_ref": "vulnerability--c7cab3fb-0822-43a5-b1ba-c9bab34361a2" - }, - { - "type": "relationship", - "id": "relationship--3bb540a4-c3be-478e-85e2-2a6c294c3dbd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "campaign--752c225d-d6f6-4456-9130-d9580fd4007b", - "target_ref": "vulnerability--6a2eab9c-9789-4437-812b-d74323fa3bca" - }, - { - "type": "relationship", - "id": "relationship--4e726ced-0207-4196-8a14-4400c09b039e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "attack-pattern--19da6e1c-69a8-4c2f-886d-d620d09d3b5a", - "target_ref": "vulnerability--c7cab3fb-0822-43a5-b1ba-c9bab34361a2" - }, - { - "type": "relationship", - "id": "relationship--b9736cd3-9482-4094-9178-1cde2b273aff", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "attack-pattern--19da6e1c-69a8-4c2f-886d-d620d09d3b5a", - "target_ref": "vulnerability--6a2eab9c-9789-4437-812b-d74323fa3bca" - }, - { - "type": "relationship", - "id": "relationship--70205e3e-195d-4bd5-a208-ada6cdf143e3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "target_ref": "vulnerability--2b7f00d8-b133-4a92-9118-46ce5f8b2531" - }, - { - "type": "relationship", - "id": "relationship--6bb5a995-b874-4e17-88eb-38e00c8e5740", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "target_ref": "vulnerability--4d7dc9cb-983f-40b4-b597-d7a38b2d9a4b" - }, - { - "type": "relationship", - "id": "relationship--d4247377-5302-4ede-a0f2-579f7db67bb6", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "target_ref": "vulnerability--8323404c-1fdd-4272-822b-829f85556c53" - }, - { - "type": "relationship", - "id": "relationship--b8617e55-00c0-4066-8222-927846edcafe", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "target_ref": "malware--dc669921-4a1a-470d-bfae-694e740ce181" - }, - { - "type": "relationship", - "id": "relationship--f34d9e2e-715f-4baf-8226-40abfcb91012", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e", - "target_ref": "malware--f86febd3-609b-4d2e-9fec-aa805cb498bf" - }, - { - "type": "relationship", - "id": "relationship--937f310a-396a-403f-bb6f-400ad8920018", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--4e11b23f-732b-418e-b786-4dbf65459d50", - "target_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e" - }, - { - "type": "relationship", - "id": "relationship--14a06709-3c0b-4e72-ad49-dd0f6d775e65", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--4e11b23f-732b-418e-b786-4dbf65459d50", - "target_ref": "malware--dc669921-4a1a-470d-bfae-694e740ce181" - }, - { - "type": "relationship", - "id": "relationship--5f6c6509-ca0c-43db-8c0e-8e138f6d913c", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b7fa7e73-e645-4813-9723-161bbd8dda62", - "target_ref": "campaign--d02a1560-ff69-49f4-ac34-919b8aa4b91e" - }, - { - "type": "relationship", - "id": "relationship--ca99fa83-0d1b-4ddd-88c8-0dee38856a88", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b7fa7e73-e645-4813-9723-161bbd8dda62", - "target_ref": "malware--f86febd3-609b-4d2e-9fec-aa805cb498bf" - }, - { - "type": "relationship", - "id": "relationship--38a52125-130f-4ce7-9b38-f234553ba83d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--80c260d9-a075-4148-9301-ebe4af27f449" - }, - { - "type": "relationship", - "id": "relationship--e13b17d0-1fef-4f98-a4a8-895c3e4cf1e2", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b2f09ce0-2db4-480f-bd2f-073ddb3a0c87", - "target_ref": "malware--80c260d9-a075-4148-9301-ebe4af27f449" - }, - { - "type": "relationship", - "id": "relationship--262a8234-d7e2-477a-baeb-ed65b639e33a", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b2f09ce0-2db4-480f-bd2f-073ddb3a0c87", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--f4ceabc6-9302-4dc5-9cc1-4d40ef43503c", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "vulnerability--717cb1c9-eab3-4330-8340-e4858055aa80" - }, - { - "type": "relationship", - "id": "relationship--56b1023c-9e28-4449-8b4f-bc2adde45e1a", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "targets", - "source_ref": "attack-pattern--fb6aa549-c94a-4e45-b4fd-7e32602dad85", - "target_ref": "vulnerability--717cb1c9-eab3-4330-8340-e4858055aa80" - }, - { - "type": "relationship", - "id": "relationship--8997440e-00f5-48e1-8b56-69d3b6f9f1fd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9842a3b9-fc5b-44c4-bb48-578cf6f728d9", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--80ac0601-0660-4057-b3b0-dca0fe35a6b4", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9842a3b9-fc5b-44c4-bb48-578cf6f728d9", - "target_ref": "malware--3ed0364f-62c8-4ebc-b136-deaf6966880b" - }, - { - "type": "relationship", - "id": "relationship--2583921a-2f02-42c5-bd25-0f37eb2e6ef9", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--3ed0364f-62c8-4ebc-b136-deaf6966880b" - }, - { - "type": "relationship", - "id": "relationship--7231e729-42e3-4f29-ae6f-6d80192c4bd1", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--4e4c4ad7-4909-456a-b6fa-e24a6f682a40", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--201ee2d5-74f4-4beb-b13a-34d948854655", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9842a3b9-fc5b-44c4-bb48-578cf6f728d9", - "target_ref": "malware--17099f03-5ec8-456d-a2de-968aebaafc78" - }, - { - "type": "relationship", - "id": "relationship--afee4dc4-7d0e-450d-9164-4429649ab386", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--17099f03-5ec8-456d-a2de-968aebaafc78" - }, - { - "type": "relationship", - "id": "relationship--ed403d0d-b55d-4e78-94d3-4e035a045c39", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--17099f03-5ec8-456d-a2de-968aebaafc78" - }, - { - "type": "relationship", - "id": "relationship--4303ebf2-9590-4ec0-a702-e7bfff64bc5f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--3ed0364f-62c8-4ebc-b136-deaf6966880b" - }, - { - "type": "relationship", - "id": "relationship--54f845bb-0967-4c0f-ac8a-8ad4785cbbe6", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--0bd19ca0-2bbb-4df0-92ec-59a4e9169c64", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--e14b6476-40b5-4b0b-bde7-0e856ab00b6c" - }, - { - "type": "relationship", - "id": "relationship--89ddeb74-ea26-44f9-bb6d-3f17c9d4efaa", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9695dc2f-d92a-4f2b-8b16-b0e21d7c631d", - "target_ref": "malware--e14b6476-40b5-4b0b-bde7-0e856ab00b6c" - }, - { - "type": "relationship", - "id": "relationship--eb400750-c866-47c3-89a2-fa6d1a90e9e7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9695dc2f-d92a-4f2b-8b16-b0e21d7c631d", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--7450856e-051f-4d49-953c-ad24f170af0e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--e14b6476-40b5-4b0b-bde7-0e856ab00b6c" - }, - { - "type": "relationship", - "id": "relationship--1d6b0425-603d-4217-948a-fabb2a398450", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--7fd865ed-93e9-481f-953b-82ab386190ae", - "target_ref": "malware--feaf146d-ea67-4eb1-946a-6f352ff79a81" - }, - { - "type": "relationship", - "id": "relationship--1895dd86-dc46-4505-ba62-5724a1df2362", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--7fd865ed-93e9-481f-953b-82ab386190ae", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--a4e0751d-8d59-4447-96ea-3799fecf66d7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--feaf146d-ea67-4eb1-946a-6f352ff79a81" - }, - { - "type": "relationship", - "id": "relationship--258796f6-e46a-421a-b3f5-7db6114fb2bc", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--e5bc6507-d052-447f-93c7-db7ef32211da", - "target_ref": "malware--13791e02-6621-45fb-8c10-f6b72e1bf553" - }, - { - "type": "relationship", - "id": "relationship--9431d9f9-6d8b-4373-b42c-172a663391b3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--e5bc6507-d052-447f-93c7-db7ef32211da", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--07d2f213-1794-483e-b95b-03761826c052", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--13791e02-6621-45fb-8c10-f6b72e1bf553" - }, - { - "type": "relationship", - "id": "relationship--aa430e5b-0519-4e94-bc2c-8836d196acd7", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--fead5c52-9533-405c-b822-a034092a1ba8", - "target_ref": "malware--703a15a7-eb85-475d-a27a-77d8fcf8f7b9" - }, - { - "type": "relationship", - "id": "relationship--c0786bd4-9c15-48ee-a19e-a9d6aba25d67", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--fead5c52-9533-405c-b822-a034092a1ba8", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--498b9f3b-488b-40d5-aaaf-e67b93c1d92b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--703a15a7-eb85-475d-a27a-77d8fcf8f7b9" - }, - { - "type": "relationship", - "id": "relationship--d875538e-cc47-4353-a572-2dae27ef0a44", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9842a3b9-fc5b-44c4-bb48-578cf6f728d9", - "target_ref": "malware--fade08cb-fa57-485e-97f8-fab5a1bd4460" - }, - { - "type": "relationship", - "id": "relationship--313d56c4-eef9-417e-952d-073690c20ee4", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--fade08cb-fa57-485e-97f8-fab5a1bd4460" - }, - { - "type": "relationship", - "id": "relationship--6b091c0f-a700-4f3e-9d98-0b8abf9a306b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--405ff732-2c35-4f46-9f78-2a632ce36e03", - "target_ref": "malware--3050937d-6330-44c7-83ba-8821e1f7e7bd" - }, - { - "type": "relationship", - "id": "relationship--5aff864a-1789-4df2-87fa-03ec43cf4fdd", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--405ff732-2c35-4f46-9f78-2a632ce36e03", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--325ebcb6-723c-4f50-8a32-aca18809e6eb", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--3050937d-6330-44c7-83ba-8821e1f7e7bd" - }, - { - "type": "relationship", - "id": "relationship--0cb9c725-3d55-4165-b2a9-9414d7933987", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--4d58096e-b5c9-47d8-af9a-1af5f4762d6b", - "target_ref": "malware--9d995717-edc3-4bd8-8554-aecf773bdecc" - }, - { - "type": "relationship", - "id": "relationship--640a0454-57eb-408f-aa13-b5732b4d0b6f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--4d58096e-b5c9-47d8-af9a-1af5f4762d6b", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--41550302-6e95-4cf6-8d7c-d417a99d98dc", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--9d995717-edc3-4bd8-8554-aecf773bdecc" - }, - { - "type": "relationship", - "id": "relationship--911dcbb0-96f4-4995-9961-5ea4b2fa7ce2", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9c725598-a160-4e91-8b93-ed0956709892", - "target_ref": "malware--40e15fa5-df8d-4771-a682-21dab0a024fd" - }, - { - "type": "relationship", - "id": "relationship--7b6ba584-fa87-4a6f-8c21-8123fa88db74", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--9c725598-a160-4e91-8b93-ed0956709892", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--69101c2f-da92-47af-b402-7c60a39a982f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--40e15fa5-df8d-4771-a682-21dab0a024fd" - }, - { - "type": "relationship", - "id": "relationship--25055108-a2ae-4855-bd5f-6ab396aacbc5", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--2efe7c62-1b96-4568-81ee-c85b840bde39", - "target_ref": "malware--69101c2f-da92-47af-b402-7c60a39a982f" - }, - { - "type": "relationship", - "id": "relationship--44c80cab-73ce-4b17-a4cb-9a36e2585403", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--2efe7c62-1b96-4568-81ee-c85b840bde39", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--32655cb3-7455-4761-b1f2-0b82153a0540", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--69101c2f-da92-47af-b402-7c60a39a982f" - }, - { - "type": "relationship", - "id": "relationship--fe963c8c-65a4-49ea-910b-e1cf3c80f1b4", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b8322c9b-8031-4fb3-9cbc-8a1ea0fe3cfa", - "target_ref": "malware--1601b8c2-5e6f-4a18-a413-10527e5d90b7" - }, - { - "type": "relationship", - "id": "relationship--4b0abf75-6f05-4bd5-8ac5-19778b245274", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b8322c9b-8031-4fb3-9cbc-8a1ea0fe3cfa", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--154049a5-731d-4e50-af13-f0f2c9b71f91", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--1601b8c2-5e6f-4a18-a413-10527e5d90b7" - }, - { - "type": "relationship", - "id": "relationship--db55db06-499d-4867-9ab9-3ed4331eedb2", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b08f9631-dd94-4d99-a96c-32b42af2ea81", - "target_ref": "malware--626badcc-4257-4222-946c-6d6e889836ea" - }, - { - "type": "relationship", - "id": "relationship--cc802697-7677-4bd7-a8b9-e728788ac783", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b08f9631-dd94-4d99-a96c-32b42af2ea81", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--a371be18-8ca5-4453-80f5-ae52d982c21b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--626badcc-4257-4222-946c-6d6e889836ea" - }, - { - "type": "relationship", - "id": "relationship--9a3bd620-01b5-4764-beb0-f085417ed8f3", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--3b275ed1-9c2e-4443-b1dd-5cfb51eaef2e" - }, - { - "type": "relationship", - "id": "relationship--48906405-9980-4583-8559-2085c111bf89", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--3b275ed1-9c2e-4443-b1dd-5cfb51eaef2e" - }, - { - "type": "relationship", - "id": "relationship--13222c71-d8fa-4688-adae-c3f8ca43a41b", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--f138b6e0-9a7d-4cd9-a904-08a7df2eabb1" - }, - { - "type": "relationship", - "id": "relationship--73c4529e-560e-4831-8497-a0db72f7dfd8", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--f138b6e0-9a7d-4cd9-a904-08a7df2eabb1" - }, - { - "type": "relationship", - "id": "relationship--8d3e1ed6-7d9c-4aa5-b121-f4eb193312cf", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--950c01b8-c647-4cc8-b0c1-3612fa780108", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--2c11dcc0-7968-4c07-bdde-791a8f5e2e37", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--302ac5b5-486c-4c99-8cad-4426aeaf47b6" - }, - { - "type": "relationship", - "id": "relationship--fd97d0ef-370e-4b6f-b2d3-8fb881aadc3f", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--950c01b8-c647-4cc8-b0c1-3612fa780108", - "target_ref": "malware--302ac5b5-486c-4c99-8cad-4426aeaf47b6" - }, - { - "type": "relationship", - "id": "relationship--c05d2410-848c-47e5-a94f-c64510e2b08d", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--ae29faa6-5f70-4eb8-981b-30818433a52e", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--92a21b52-2961-42aa-8b01-54ea294d9d73", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--e1c02dca-d3fe-48f1-bb4b-3cacd2bc3619" - }, - { - "type": "relationship", - "id": "relationship--76a9283a-b844-47a5-a5d0-b31859115f88", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--ae29faa6-5f70-4eb8-981b-30818433a52e", - "target_ref": "malware--e1c02dca-d3fe-48f1-bb4b-3cacd2bc3619" - }, - { - "type": "relationship", - "id": "relationship--bbd3ba5c-2a75-4902-bd42-1215a2bc320e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--e1c02dca-d3fe-48f1-bb4b-3cacd2bc3619" - }, - { - "type": "relationship", - "id": "relationship--4f784f2f-7d8e-4f12-9ddd-b685055f8076", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b6cc482d-89db-4e6b-a592-723070f6d22d", - "target_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05" - }, - { - "type": "relationship", - "id": "relationship--263e38f4-8ecb-414f-b3c4-0f045d1be5ed", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "uses", - "source_ref": "campaign--721976f9-56d7-4749-8c69-b3ac7c315f05", - "target_ref": "malware--a4f315bd-e159-4bfb-8439-0d5a8330fc70" - }, - { - "type": "relationship", - "id": "relationship--a56e8582-fc6e-4be8-bf35-7e939269d65e", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--b6cc482d-89db-4e6b-a592-723070f6d22d", - "target_ref": "malware--a4f315bd-e159-4bfb-8439-0d5a8330fc70" - }, - { - "type": "relationship", - "id": "relationship--d7d9952c-4443-4711-a48c-7009a0f0f8ea", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--137acf67-cedc-4a07-8719-72759174de3a", - "target_ref": "malware--a4f315bd-e159-4bfb-8439-0d5a8330fc70" - }, - { - "type": "relationship", - "id": "relationship--78f110e6-2cd6-442e-971f-a2ff40c3b843", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--0b71628d-31dd-4eb8-baee-39f19c0a14b0", - "target_ref": "malware--40e15fa5-df8d-4771-a682-21dab0a024fd" - }, - { - "type": "relationship", - "id": "relationship--b2fb88f2-5ad7-4c07-b4b2-61986decb477", - "created": "2015-05-15T09:12:16.432Z", - "modified": "2015-05-15T09:12:16.432Z", - "relationship_type": "indicates", - "source_ref": "indicator--0b71628d-31dd-4eb8-baee-39f19c0a14b0", - "target_ref": "malware--69101c2f-da92-47af-b402-7c60a39a982f" - } - ] -} diff --git a/src/test/resources/stix/custom/custom_object_1.json b/src/test/resources/stix/custom/custom_object_1.json deleted file mode 100644 index 946db44..0000000 --- a/src/test/resources/stix/custom/custom_object_1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "external_references": [ - { - "external_id": "TA0006", - "source_name": "mitre-attack", - "url": "https://attack.mitre.org/tactics/TA0006" - } - ], - "id": "x-mitre-tactic--2558fd61-8c75-4730-94c4-11926db2a263", - "name": "Credential Access", - "created": "2018-10-17T00:14:20.652Z", - "modified": "2018-10-17T00:14:20.652Z", - "type": "x-mitre-tactic", - "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.", - "x_mitre_shortname": "credential-access" -} \ No newline at end of file From 9ecabe2d5f136c4d4f57b96dec3539f05220448f Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Thu, 10 Oct 2019 19:38:37 -0400 Subject: [PATCH 02/21] refactor with additional SDOs and Types --- README.md | 4 + src/main/kotlin/com/stephenott/stix/Main.kt | 11 - .../kotlin/com/stephenott/stix/MainRunner.kt | 21 ++ .../kotlin/com/stephenott/stix/StixContent.kt | 3 + .../kotlin/com/stephenott/stix/StixObject.kt | 8 - .../stix/{ => common}/CommonProperties.kt | 12 +- .../stix/{ => common}/StixDataFormats.kt | 2 +- .../stix/common/StixObjectRegistry.kt | 29 +++ .../common/StixObjectRelationshipRegistry.kt | 30 +++ .../stix/{ => common}/ValidatorManager.kt | 2 +- .../com/stephenott/stix/object/StixObject.kt | 14 ++ .../object/sco/StixCyberObservableObject.kt | 15 ++ .../stix/{ => object}/sdo/StixDomainObject.kt | 5 +- .../stix/object/sdo/object/AttackPattern.kt | 77 +++++++ .../stix/object/sdo/object/Campaign.kt | 109 +++++++++ .../stix/object/sdo/object/CourseOfAction.kt | 93 ++++++++ .../objects => object/sdo/object}/Grouping.kt | 21 +- .../objects => object/sdo/object}/Identity.kt | 28 ++- .../stix/object/sdo/object/Indicator.kt | 95 ++++++++ .../stix/object/sdo/object/Infrastructure.kt | 141 ++++++++++++ .../stix/object/sdo/object/IntrusionSet.kt | 122 +++++++++++ .../objects => object/sdo/object}/Location.kt | 18 +- .../stix/object/sdo/object/Malware.kt | 207 ++++++++++++++++++ .../sdo/object}/MalwareAnalysis.kt | 41 +++- .../objects => object/sdo/object}/Note.kt | 21 +- .../sdo/object}/ObservedData.kt | 22 +- .../objects => object/sdo/object}/Opinion.kt | 19 +- .../objects => object/sdo/object}/Report.kt | 22 +- .../stix/object/sdo/object/ThreatActor.kt | 136 ++++++++++++ .../stephenott/stix/object/sdo/object/Tool.kt | 94 ++++++++ .../sdo/object}/Vulnerability.kt | 21 +- .../stix/object/sro/StixRelationshipObject.kt | 18 ++ .../stix/object/sro/object/Relationship.kt | 128 +++++++++++ .../stix/object/sro/object/Sighting.kt | 5 + .../kotlin/com/stephenott/stix/sdo/Sdo.kt | 28 --- .../stix/sdo/objects/AttackPattern.kt | 36 --- .../stephenott/stix/sdo/objects/Campaign.kt | 40 ---- .../stix/sdo/objects/CourseOfAction.kt | 40 ---- .../stephenott/stix/sdo/objects/Indicator.kt | 46 ---- .../stix/sdo/objects/Infrastructure.kt | 45 ---- .../stix/sdo/objects/IntrusionSet.kt | 48 ---- .../stephenott/stix/sdo/objects/Malware.kt | 56 ----- .../stix/sdo/objects/ThreatActor.kt | 55 ----- .../com/stephenott/stix/sdo/objects/Tool.kt | 42 ---- .../stix/serialization/JsonManager.kt | 13 -- .../stix/serialization/json/JsonExtensions.kt | 30 +++ .../stephenott/stix/type/RelationshipType.kt | 8 + .../stephenott/stix/type/StixConfidence.kt | 107 +++++++++ .../stephenott/stix/type/StixIdentifier.kt | 4 + .../com/stephenott/stix/type/StixInstant.kt | 2 +- .../com/stephenott/stix/type/StixInteger.kt | 2 +- .../com/stephenott/stix/type/StixLang.kt | 15 ++ 52 files changed, 1674 insertions(+), 537 deletions(-) create mode 100644 README.md delete mode 100644 src/main/kotlin/com/stephenott/stix/Main.kt create mode 100644 src/main/kotlin/com/stephenott/stix/MainRunner.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/StixObject.kt rename src/main/kotlin/com/stephenott/stix/{ => common}/CommonProperties.kt (76%) rename src/main/kotlin/com/stephenott/stix/{ => common}/StixDataFormats.kt (97%) create mode 100644 src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt create mode 100644 src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt rename src/main/kotlin/com/stephenott/stix/{ => common}/ValidatorManager.kt (74%) create mode 100644 src/main/kotlin/com/stephenott/stix/object/StixObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt rename src/main/kotlin/com/stephenott/stix/{ => object}/sdo/StixDomainObject.kt (67%) create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Grouping.kt (66%) rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Identity.kt (64%) create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Location.kt (74%) create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/MalwareAnalysis.kt (62%) rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Note.kt (64%) rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/ObservedData.kt (65%) rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Opinion.kt (66%) rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Report.kt (67%) create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt rename src/main/kotlin/com/stephenott/stix/{sdo/objects => object/sdo/object}/Vulnerability.kt (60%) create mode 100644 src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt create mode 100644 src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixLang.kt diff --git a/README.md b/README.md new file mode 100644 index 0000000..e023f36 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# STIX-JAVA-KOTLIN + +A STIX 2.1 library built in Kotlin with targets of Kotlin, Java, and Kotlin-Native + diff --git a/src/main/kotlin/com/stephenott/stix/Main.kt b/src/main/kotlin/com/stephenott/stix/Main.kt deleted file mode 100644 index 4ac6618..0000000 --- a/src/main/kotlin/com/stephenott/stix/Main.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.stephenott.stix - -import com.stephenott.stix.sdo.Sdo -import com.stephenott.stix.sdo.objects.AttackPattern - -class Main { - - fun main(){ - var dog = AttackPattern("name") - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt new file mode 100644 index 0000000..810b04a --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -0,0 +1,21 @@ +package com.stephenott.stix + +import com.stephenott.stix.`object`.sdo.`object`.AttackPattern +import com.stephenott.stix.`object`.sro.`object`.Relationship +import com.stephenott.stix.type.RelationshipType + +object MainRunner { + + @JvmStatic + fun main(args: Array){ + + val ap1 = AttackPattern("124") + val ap2 = AttackPattern("124") + val rel = Relationship( + relationshipType = RelationshipType("related-to"), + sourceRef = ap1, + targetRef = ap2 + ) + println(rel) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixContent.kt b/src/main/kotlin/com/stephenott/stix/StixContent.kt index 7f56cf4..f78a929 100644 --- a/src/main/kotlin/com/stephenott/stix/StixContent.kt +++ b/src/main/kotlin/com/stephenott/stix/StixContent.kt @@ -1,5 +1,8 @@ package com.stephenott.stix +import com.stephenott.stix.common.StixIdentifierProp +import com.stephenott.stix.common.StixTypeProp + interface StixContent : StixTypeProp, StixIdentifierProp {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixObject.kt b/src/main/kotlin/com/stephenott/stix/StixObject.kt deleted file mode 100644 index 27539fe..0000000 --- a/src/main/kotlin/com/stephenott/stix/StixObject.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.stephenott.stix - -/** - * Parent object for all STIX objects: SDO, SCO, SRO, Metadata - */ -interface StixObject : StixContent{ - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/CommonProperties.kt b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt similarity index 76% rename from src/main/kotlin/com/stephenott/stix/CommonProperties.kt rename to src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt index 8837424..0552125 100644 --- a/src/main/kotlin/com/stephenott/stix/CommonProperties.kt +++ b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt @@ -1,7 +1,9 @@ -package com.stephenott.stix +package com.stephenott.stix.common import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixConfidence import com.stephenott.stix.type.StixLabels +import com.stephenott.stix.type.StixLang interface StixTypeProp{ val type: StixType @@ -45,4 +47,12 @@ interface StixModified { interface StixRevoked { val revoked: StixBoolean +} + +interface StixConfidence { + val confidence: StixConfidence +} + +interface StixLang { + val lang: StixLang } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixDataFormats.kt b/src/main/kotlin/com/stephenott/stix/common/StixDataFormats.kt similarity index 97% rename from src/main/kotlin/com/stephenott/stix/StixDataFormats.kt rename to src/main/kotlin/com/stephenott/stix/common/StixDataFormats.kt index 96be67f..43d7c37 100644 --- a/src/main/kotlin/com/stephenott/stix/StixDataFormats.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixDataFormats.kt @@ -1,4 +1,4 @@ -package com.stephenott.stix +package com.stephenott.stix.common import java.time.ZoneId import java.time.format.DateTimeFormatter diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt new file mode 100644 index 0000000..6f2703f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -0,0 +1,29 @@ +package com.stephenott.stix.common + +import com.stephenott.stix.`object`.StixObject +import com.stephenott.stix.`object`.sdo.`object`.* +import com.stephenott.stix.type.StixType +import kotlin.reflect.KClass + +object StixObjectRegistry { + + var registry: Map> = mutableMapOf( + Pair(AttackPatternSdo.stixType, AttackPattern::class), + Pair(CampaignSdo.stixType, Campaign::class), + Pair(CourseOfActionSdo.stixType, CourseOfAction::class), + Pair(GroupingSdo.stixType, Grouping::class), + Pair(IdentitySdo.stixType, Identity::class), + Pair(IndicatorSdo.stixType, Indicator::class), + Pair(InfrastructureSdo.stixType, Infrastructure::class), + Pair(IntrusionSetSdo.stixType, IntrusionSet::class), + Pair(LocationSdo.stixType, Location::class), + Pair(MalwareSdo.stixType, Malware::class), + Pair(MalwareAnalysisSdo.stixType, MalwareAnalysis::class), + Pair(NoteSdo.stixType, Note::class), + Pair(ObservedDataSdo.stixType, ObservedData::class), + Pair(OpinionSdo.stixType, Opinion::class), + Pair(ReportSdo.stixType, Report::class), + Pair(ThreatActorSdo.stixType, ThreatActor::class), + Pair(VulnerabilitySdo.stixType, Vulnerability::class) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt new file mode 100644 index 0000000..e920c06 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt @@ -0,0 +1,30 @@ +package com.stephenott.stix.common + +import com.stephenott.stix.`object`.sdo.`object`.* +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro + +object StixObjectRelationshipRegistry { + + var registry: List = + RelationshipSro.allowedCommonRelationships + + AttackPatternSdo.allowedRelationships + + CampaignSdo.allowedRelationships + + CourseOfActionSdo.allowedRelationships + + GroupingSdo.allowedRelationships + + IdentitySdo.allowedRelationships + + IndicatorSdo.allowedRelationships + + InfrastructureSdo.allowedRelationships + + IntrusionSetSdo.allowedRelationships + + LocationSdo.allowedRelationships + + MalwareSdo.allowedRelationships + + MalwareAnalysisSdo.allowedRelationships + + NoteSdo.allowedRelationships + + ObservedDataSdo.allowedRelationships + + OpinionSdo.allowedRelationships + + ReportSdo.allowedRelationships + + ThreatActorSdo.allowedRelationships + + ToolSdo.allowedRelationships + + VulnerabilitySdo.allowedRelationships + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/ValidatorManager.kt b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt similarity index 74% rename from src/main/kotlin/com/stephenott/stix/ValidatorManager.kt rename to src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt index f505047..0ae6a50 100644 --- a/src/main/kotlin/com/stephenott/stix/ValidatorManager.kt +++ b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt @@ -1,4 +1,4 @@ -package com.stephenott.stix +package com.stephenott.stix.common interface ValidatorManager { var isValid: Boolean diff --git a/src/main/kotlin/com/stephenott/stix/object/StixObject.kt b/src/main/kotlin/com/stephenott/stix/object/StixObject.kt new file mode 100644 index 0000000..0b390c3 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/StixObject.kt @@ -0,0 +1,14 @@ +package com.stephenott.stix.`object` + +import com.stephenott.stix.StixContent +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship + +/** + * Parent object for all STIX objects: SDO, SCO, SRO, Metadata + */ +interface StixObject : + StixContent { + + fun allowedRelationships(): List + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt b/src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt new file mode 100644 index 0000000..3a11d71 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt @@ -0,0 +1,15 @@ +package com.stephenott.stix.object.sco + +import com.stephenott.stix.common.* +import com.stephenott.stix.`object`.StixObject + +interface StixCyberObservableObject: + StixObject, + StixCreatedProp, + StixObjectMarkingsRefsProp, + StixGranularMarkingsProp, + StixSpecVersionProp, + StixModified + StixRevoked { + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/StixDomainObject.kt similarity index 67% rename from src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/StixDomainObject.kt index 182184a..031aab9 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/StixDomainObject.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/StixDomainObject.kt @@ -1,6 +1,7 @@ -package com.stephenott.stix.sdo +package com.stephenott.stix.`object`.sdo -import com.stephenott.stix.* +import com.stephenott.stix.common.* +import com.stephenott.stix.`object`.StixObject interface StixDomainObject : StixObject, diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt new file mode 100644 index 0000000..2e104b6 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt @@ -0,0 +1,77 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.KillChainPhases + +interface AttackPatternSdo : StixDomainObject { + val name: String + val description: String? + val killChainPhases: KillChainPhases? //@TODO + + companion object { + val stixType = StixType("attack-pattern") + + val allowedRelationships: List = listOf( + AllowedRelationship( + AttackPatternSdo::class, + RelationshipType("delivers"), + MalwareSdo::class + ), + + AllowedRelationship( + AttackPatternSdo::class, + RelationshipType("targets"), + IdentitySdo::class + ), + AllowedRelationship( + AttackPatternSdo::class, + RelationshipType("targets"), + LocationSdo::class + ), + AllowedRelationship( + AttackPatternSdo::class, + RelationshipType("targets"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + AttackPatternSdo::class, + RelationshipType("uses"), + MalwareSdo::class + ), + AllowedRelationship( + AttackPatternSdo::class, + RelationshipType("uses"), + ToolSdo::class + ) + ) + } + +} + +data class AttackPattern( + override val name: String, + override val description: String? = null, + override val killChainPhases: KillChainPhases? = null, + override val type: StixType = AttackPatternSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + AttackPatternSdo { + + override fun allowedRelationships(): List { + return AttackPatternSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt new file mode 100644 index 0000000..a247e9b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt @@ -0,0 +1,109 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* + +interface CampaignSdo : StixDomainObject { + val name: String + val description: String? + val aliases: String? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val objective: String? + + companion object{ + val stixType = StixType("campaign") + + val allowedRelationships: List = listOf( + AllowedRelationship( + CampaignSdo::class, + RelationshipType("attributed-to"), + IntrusionSetSdo::class + ), + AllowedRelationship( + CampaignSdo::class, + RelationshipType("attributed-to"), + ThreatActorSdo::class + ), + + AllowedRelationship( + CampaignSdo::class, + RelationshipType("compromises"), + InfrastructureSdo::class + ), + + AllowedRelationship( + CampaignSdo::class, + RelationshipType("originates-from"), + LocationSdo::class + ), + + AllowedRelationship( + CampaignSdo::class, + RelationshipType("targets"), + IdentitySdo::class + ), + AllowedRelationship( + CampaignSdo::class, + RelationshipType("targets"), + LocationSdo::class + ), + AllowedRelationship( + CampaignSdo::class, + RelationshipType("targets"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + CampaignSdo::class, + RelationshipType("uses"), + AttackPatternSdo::class + ), + AllowedRelationship( + CampaignSdo::class, + RelationshipType("uses"), + InfrastructureSdo::class + ), + AllowedRelationship( + CampaignSdo::class, + RelationshipType("uses"), + MalwareSdo::class + ), + AllowedRelationship( + CampaignSdo::class, + RelationshipType("uses"), + ToolSdo::class + ) + ) + } + +} + +data class Campaign + ( + override val name: String, + override val description: String? = null, + override val aliases: String? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val objective: String? = null, + override val type: StixType = CampaignSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : CampaignSdo { + + override fun allowedRelationships(): List { + return CampaignSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt new file mode 100644 index 0000000..8c69322 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt @@ -0,0 +1,93 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.CourseOfActionTypeOv + +interface CourseOfActionSdo : StixDomainObject { + val name: String + val description: String? + val actionType: CourseOfActionTypeOv? + val osExecutionEnvs: OsExecutionEnvs? + val actionBin: StixBinary? + val actionReference: ExternalReference? + + companion object { + val stixType = StixType("course-of-action") + + val allowedRelationships: List = listOf( + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("investigates"), + IndicatorSdo::class + ), + + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("mitigates"), + AttackPatternSdo::class + ), + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("mitigates"), + IndicatorSdo::class + ), + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("mitigates"), + MalwareSdo::class + ), + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("mitigates"), + ToolSdo::class + ), + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("mitigates"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("remediates"), + MalwareSdo::class + ), + AllowedRelationship( + CourseOfActionSdo::class, + RelationshipType("remediates"), + VulnerabilitySdo::class + ) + ) + } + + +} + +data class CourseOfAction( + override val name: String, + override val description: String? = null, + override val actionType: CourseOfActionTypeOv? = null, + override val osExecutionEnvs: OsExecutionEnvs? = null, + override val actionBin: StixBinary? = null, + override val actionReference: ExternalReference? = null, + override val type: StixType = CourseOfActionSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : CourseOfActionSdo { + + override fun allowedRelationships(): List { + return CourseOfActionSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Grouping.kt similarity index 66% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Grouping.kt index 2083ff9..8dda406 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Grouping.kt @@ -1,6 +1,8 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.GroupingContextOv @@ -9,15 +11,20 @@ interface GroupingSdo : StixDomainObject { val description: String? val context: GroupingContextOv val objectRefs: StixIdentifiers + + companion object{ + val stixType = StixType("grouping") + + val allowedRelationships: List = listOf() + } } -data class Grouping - ( +data class Grouping( override val name: String? = null, override val description: String? = null, override val context: GroupingContextOv, override val objectRefs: StixIdentifiers, - override val type: StixType = stixType, + override val type: StixType = GroupingSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -31,8 +38,8 @@ data class Grouping ) : GroupingSdo { - companion object{ - val stixType = StixType("grouping") + override fun allowedRelationships(): List { + return GroupingSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Identity.kt similarity index 64% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Identity.kt index 85ca234..fa98738 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Identity.kt @@ -1,6 +1,8 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.IdentityClass import com.stephenott.stix.type.vocab.IdentityRoles @@ -13,6 +15,18 @@ interface IdentitySdo : StixDomainObject { val identityClass: IdentityClass? val sectors: IndustrySectors? val contactInformation: String? + + companion object{ + val stixType = StixType("identity") + + val allowedRelationships: List = listOf( + AllowedRelationship( + IdentitySdo::class, + RelationshipType("located-at"), + LocationSdo::class + ) + ) + } } data class Identity( @@ -22,7 +36,7 @@ data class Identity( override val identityClass: IdentityClass? = null, override val sectors: IndustrySectors? = null, override val contactInformation: String? = null, - override val type: StixType = stixType, + override val type: StixType = IdentitySdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -33,10 +47,10 @@ data class Identity( override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean() - ) : IdentitySdo { +) : IdentitySdo { - companion object { - val stixType = StixType("identity") + override fun allowedRelationships(): List { + return IdentitySdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt new file mode 100644 index 0000000..82e4ae6 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt @@ -0,0 +1,95 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.IndicatorTypes +import com.stephenott.stix.type.vocab.PatternType + +interface IndicatorSdo : StixDomainObject { + val name: String? + val description: String? + val indicatorTypes: IndicatorTypes + val pattern: StixPattern + val patternType: PatternType + val patternVersion: String? + + companion object{ + val stixType = StixType("indicator") + + val allowedRelationships: List = listOf( + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + AttackPatternSdo::class + ), + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + CampaignSdo::class + ), + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + InfrastructureSdo::class + ), + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + IntrusionSetSdo::class + ), + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + MalwareSdo::class + ), + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + ThreatActorSdo::class + ), + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("indicates"), + ToolSdo::class + ), + + AllowedRelationship( + IndicatorSdo::class, + RelationshipType("based-on"), + ObservedDataSdo::class + ) + ) + } +} + +data class Indicator( + override val name: String? = null, + override val description: String? = null, + override val indicatorTypes: IndicatorTypes, + override val pattern: StixPattern, + override val patternType: PatternType, + override val patternVersion: String? = null, + override val type: StixType = IndicatorSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : IndicatorSdo { + + override fun allowedRelationships(): List { + return IndicatorSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + + init { + require(indicatorTypes.size >= 1) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt new file mode 100644 index 0000000..36b016f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt @@ -0,0 +1,141 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.InfrastructureTypes +import com.stephenott.stix.type.vocab.KillChainPhases + +interface InfrastructureSdo : StixDomainObject { + val name: String + val description: String? + val infrastructureTypes: InfrastructureTypes + val aliases: StixStringList? + val killChainPhases: KillChainPhases? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + + companion object{ + val stixType = StixType("infrastructure") + + val allowedRelationships: List = listOf( + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("communicates-with"), + InfrastructureSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("communicates-with"), + CampaignSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("communicates-with"), + InfrastructureSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("communicates-with"), + IntrusionSetSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("communicates-with"), + MalwareSdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("consists-of"), + InfrastructureSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("consists-of"), + ObservedDataSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("consists-of"), + ObservedDataSdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("controls"), + InfrastructureSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("controls"), + MalwareSdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("delivers"), + MalwareSdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("has"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("hosts"), + ToolSdo::class + ), + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("hosts"), + MalwareSdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("located-at"), + LocationSdo::class + ), + + AllowedRelationship( + InfrastructureSdo::class, + RelationshipType("uses"), + InfrastructureSdo::class + ) + ) + } + +} + +data class Infrastructure( + override val name: String, + override val description: String? = null, + override val infrastructureTypes: InfrastructureTypes, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val type: StixType = InfrastructureSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + InfrastructureSdo { + + override fun allowedRelationships(): List { + return InfrastructureSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt new file mode 100644 index 0000000..29be9a2 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt @@ -0,0 +1,122 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.* + +interface IntrusionSetSdo : StixDomainObject { + val name: String + val description: String? + val aliases: StixStringList? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val goals: StixStringList? + val resourceLevel: AttackResourceLevelOv? + val primaryMotivation: AttackMotivationOv? + val secondaryMotivations: AttackMotivations? + + companion object{ + val stixType = StixType("intrusion-set") + + val allowedRelationships: List = listOf( + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("attributed-to"), + ThreatActorSdo::class + ), + + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("compromises"), + InfrastructureSdo::class + ), + + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("hosts"), + InfrastructureSdo::class + ), + + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("owns"), + InfrastructureSdo::class + ), + + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("originates-from"), + LocationSdo::class + ), + + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("targets"), + IdentitySdo::class + ), + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("targets"), + LocationSdo::class + ), + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("targets"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("uses"), + AttackPatternSdo::class + ), + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("uses"), + InfrastructureSdo::class + ), + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("uses"), + MalwareSdo::class + ), + AllowedRelationship( + IntrusionSetSdo::class, + RelationshipType("uses"), + ToolSdo::class + ) + ) + } +} + +data class IntrusionSet( + override val name: String, + override val description: String? = null, + override val aliases: StixStringList? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val goals: StixStringList? = null, + override val resourceLevel: AttackResourceLevelOv? = null, + override val primaryMotivation: AttackMotivationOv? = null, + override val secondaryMotivations: AttackMotivations? = null, + override val type: StixType = IntrusionSetSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + IntrusionSetSdo { + + override fun allowedRelationships(): List { + return IntrusionSetSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Location.kt similarity index 74% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Location.kt index 5bd3050..5ba454d 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Location.kt @@ -1,6 +1,8 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.AdministrativeArea import com.stephenott.stix.type.vocab.City @@ -17,6 +19,12 @@ interface LocationSdo : StixDomainObject { val city: City? val streetAddress: StreetAddress? val postalCode: PostalCode? + + companion object{ + val stixType = StixType("location") + + val allowedRelationships:List = listOf() + } } data class Location( @@ -30,7 +38,7 @@ data class Location( override val city: City? = null, override val streetAddress: StreetAddress? = null, override val postalCode: PostalCode? = null, - override val type: StixType = stixType, + override val type: StixType = LocationSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -44,8 +52,8 @@ data class Location( ) : LocationSdo { - companion object { - val stixType = StixType("location") + override fun allowedRelationships(): List { + return LocationSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt new file mode 100644 index 0000000..c22fc14 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt @@ -0,0 +1,207 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.* + +interface MalwareSdo : StixDomainObject { + val name: String? + val description: String? + val malwareTypes: MalwareTypes + val isFamily: StixBoolean + val aliases: StixStringList? + val killChainPhases: KillChainPhases? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val osExecutionEnvs: OsExecutionEnvs? + val architectureExecutionEnvs: ProcessorArchitectures? + val implementationLanguage: ImplementationLanguages? + val capabilities: MalwareCapabilities? + val sampleRefs: StixIdentifiers? + + companion object{ + val stixType = StixType("malware") + + val allowedRelationships: List = listOf( + //@TODO convert allowedrelationship to a Kotlin DSL + AllowedRelationship( + MalwareSdo::class, + RelationshipType("authored-by"), + ThreatActorSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("authored-by"), + IntrusionSetSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("beacons-to"), + InfrastructureSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("exfiltrates-to"), + InfrastructureSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("communicates-with"), + ThreatActorSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("communicates-with"), + ThreatActorSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("communicates-with"), + ThreatActorSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("communicates-with"), + ThreatActorSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("controls"), + MalwareSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("downloads"), + MalwareSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("downloads"), + ToolSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("downloads"), + ThreatActorSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("drops"), + MalwareSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("drops"), + ToolSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("drops"), + ThreatActorSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("exploits"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("originates-from"), + LocationSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("targets"), + IdentitySdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("targets"), + InfrastructureSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("targets"), + LocationSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("targets"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("uses"), + AttackPatternSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("uses"), + InfrastructureSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("uses"), + MalwareSdo::class + ), + AllowedRelationship( + MalwareSdo::class, + RelationshipType("uses"), + ToolSdo::class + ), + + AllowedRelationship( + MalwareSdo::class, + RelationshipType("variant-of"), + MalwareSdo::class + ) + ) + } + +} + +data class Malware + ( + override val name: String, + override val description: String? = null, + override val malwareTypes: MalwareTypes, + override val isFamily: StixBoolean, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val osExecutionEnvs: OsExecutionEnvs? = null, + override val architectureExecutionEnvs: ProcessorArchitectures? = null, + override val implementationLanguage: ImplementationLanguages? = null, + override val capabilities: MalwareCapabilities? = null, + override val sampleRefs: StixIdentifiers? = null, + override val type: StixType = MalwareSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + MalwareSdo { + override fun allowedRelationships(): List { + return MalwareSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/MalwareAnalysis.kt similarity index 62% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/MalwareAnalysis.kt index 70ae103..2b3ceee 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/MalwareAnalysis.kt @@ -1,6 +1,8 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.MalwareAvResult @@ -19,6 +21,33 @@ interface MalwareAnalysisSdo : StixDomainObject { val analysisEnded: StixInstant? val avResult: MalwareAvResult? val analysisScoRefs: StixIdentifiers? + + companion object{ + val stixType = StixType("malware-analysis") + + val allowedRelationships: List = listOf( + AllowedRelationship( + MalwareAnalysisSdo::class, + RelationshipType("characterizes"), + MalwareSdo::class + ), + AllowedRelationship( + MalwareAnalysisSdo::class, + RelationshipType("av-analysis-of"), + MalwareSdo::class + ), + AllowedRelationship( + MalwareAnalysisSdo::class, + RelationshipType("static-analysis-of"), + MalwareSdo::class + ), + AllowedRelationship( + MalwareAnalysisSdo::class, + RelationshipType("dynamic-analysis-of"), + MalwareSdo::class + ) + ) + } } data class MalwareAnalysis @@ -37,7 +66,7 @@ data class MalwareAnalysis override val analysisEnded: StixInstant? = null, override val avResult: MalwareAvResult? = null, override val analysisScoRefs: StixIdentifiers? = null, - override val type: StixType = stixType, + override val type: StixType = MalwareAnalysisSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -50,9 +79,7 @@ data class MalwareAnalysis override val revoked: StixBoolean = StixBoolean() ) : MalwareAnalysisSdo { - - companion object{ - val stixType = StixType("malware-analysis") + override fun allowedRelationships(): List { + return MalwareAnalysisSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } - } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Note.kt similarity index 64% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Note.kt index 77d6532..4e8cf2e 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Note.kt @@ -1,14 +1,22 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.KillChainPhases interface NoteSdo : StixDomainObject { val abstract: String? val content: String val authors: StixStringList? val objectRefs: StixIdentifiers + + companion object{ + val stixType = StixType("note") + + val allowedRelationships: List = listOf() + } + } data class Note( @@ -16,7 +24,7 @@ data class Note( override val content: String, override val authors: StixStringList? = null, override val objectRefs: StixIdentifiers, - override val type: StixType = stixType, + override val type: StixType = NoteSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -29,9 +37,8 @@ data class Note( override val revoked: StixBoolean = StixBoolean() ) : NoteSdo { - - companion object { - val stixType = StixType("note") + override fun allowedRelationships(): List { + return NoteSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/ObservedData.kt similarity index 65% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/ObservedData.kt index b711182..d3a611f 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/ObservedData.kt @@ -1,6 +1,8 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* interface ObservedDataSdo : StixDomainObject { @@ -8,15 +10,20 @@ interface ObservedDataSdo : StixDomainObject { val lastObserved: StixInstant val numberObserved: StixInteger val objectRefs: StixIdentifiers + + companion object{ + val stixType = StixType("observed-data") + + val allowedRelationships: List = listOf() + } } -data class ObservedData - ( +data class ObservedData( override val firstObserved: StixInstant, override val lastObserved: StixInstant, override val numberObserved: StixInteger, override val objectRefs: StixIdentifiers, - override val type: StixType = stixType, + override val type: StixType = ObservedDataSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -29,9 +36,8 @@ data class ObservedData override val revoked: StixBoolean = StixBoolean() ) : ObservedDataSdo { - - companion object{ - val stixType = StixType("observed-data") + override fun allowedRelationships(): List { + return ObservedDataSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Opinion.kt similarity index 66% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Opinion.kt index 83f6f5b..453a4ce 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Opinion.kt @@ -1,6 +1,8 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.OpinionEnum @@ -9,6 +11,12 @@ interface OpinionSdo : StixDomainObject { val authors: StixStringList? val opinion: OpinionEnum val objectRefs: StixIdentifiers + + companion object{ + val stixType = StixType("opinion") + + val allowedRelationships: List = listOf() + } } data class Opinion( @@ -16,7 +24,7 @@ data class Opinion( override val authors: StixStringList? = null, override val opinion: OpinionEnum, override val objectRefs: StixIdentifiers, - override val type: StixType = stixType, + override val type: StixType = OpinionSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -29,9 +37,8 @@ data class Opinion( override val revoked: StixBoolean = StixBoolean() ) : OpinionSdo { - - companion object { - val stixType = StixType("opinion") + override fun allowedRelationships(): List { + return OpinionSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Report.kt similarity index 67% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Report.kt index 651ee34..c278980 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Report.kt @@ -1,8 +1,9 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.KillChainPhases import com.stephenott.stix.type.vocab.ReportTypes interface ReportSdo : StixDomainObject { @@ -11,16 +12,21 @@ interface ReportSdo : StixDomainObject { val reportTypes: ReportTypes val published: StixInstant val objectRefs: StixIdentifiers + + companion object{ + val stixType = StixType("report") + + val allowedRelationships: List = listOf() + } } -data class Report - ( +data class Report( override val name: String, override val description: String? = null, override val reportTypes: ReportTypes, override val published: StixInstant, override val objectRefs: StixIdentifiers, - override val type: StixType = stixType, + override val type: StixType = ReportSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -34,8 +40,8 @@ data class Report ) : ReportSdo { - companion object{ - val stixType = StixType("report") + override fun allowedRelationships(): List { + return ReportSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt new file mode 100644 index 0000000..2b32e7e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt @@ -0,0 +1,136 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.* + +interface ThreatActorSdo : StixDomainObject { + val name: String + val description: String? + val threatActorTypes: ThreatActorTypes + val aliases: StixStringList? + val firstSeen: StixInstant? + val lastSeen: StixInstant? + val roles: ThreatActorRoles? + val goals: StixStringList? + val sophistication: ThreatActorSophisticationOv? + val resourceLevel: AttackResourceLevelOv? + val primaryMotivation: AttackMotivationOv? + val secondaryMotivation: AttackMotivationOv? + val personalMotivations: AttackMotivations? + + companion object{ + val stixType = StixType("threat-actor") + + val allowedRelationships: List = listOf( + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("attributed-to"), + IdentitySdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("compromises"), + InfrastructureSdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("hosts"), + InfrastructureSdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("owns"), + InfrastructureSdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("impersonates"), + IdentitySdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("located-at"), + LocationSdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("targets"), + IdentitySdo::class + ), + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("targets"), + LocationSdo::class + ), + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("targets"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("uses"), + AttackPatternSdo::class + ), + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("uses"), + InfrastructureSdo::class + ), + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("uses"), + MalwareSdo::class + ), + AllowedRelationship( + ThreatActorSdo::class, + RelationshipType("uses"), + ToolSdo::class + ) + ) + } +} + +data class ThreatActor( + override val name: String, + override val description: String? = null, + override val threatActorTypes: ThreatActorTypes, + override val aliases: StixStringList? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val roles: ThreatActorRoles? = null, + override val goals: StixStringList? = null, + override val sophistication: ThreatActorSophisticationOv? = null, + override val resourceLevel: AttackResourceLevelOv? = null, + override val primaryMotivation: AttackMotivationOv? = null, + override val secondaryMotivation: AttackMotivationOv? = null, + override val personalMotivations: AttackMotivations? = null, + override val type: StixType = ThreatActorSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + ThreatActorSdo { + + override fun allowedRelationships(): List { + return ThreatActorSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt new file mode 100644 index 0000000..c6f6523 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt @@ -0,0 +1,94 @@ +package com.stephenott.stix.`object`.sdo.`object` + +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.vocab.ToolTypes + +interface ToolSdo : StixDomainObject { + val name: String + val description: String? + val toolTypes: ToolTypes + val aliases: StixStringList? + val killChainPhases: KillChainPhases? + val toolVersion: String? + + companion object{ + val stixType = StixType("tool") + + val allowedRelationships: List = listOf( + AllowedRelationship( + ToolSdo::class, + RelationshipType("delivers"), + MalwareSdo::class + ), + + AllowedRelationship( + ToolSdo::class, + RelationshipType("drops"), + MalwareSdo::class + ), + + AllowedRelationship( + ToolSdo::class, + RelationshipType("has"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + ToolSdo::class, + RelationshipType("targets"), + IdentitySdo::class + ), + AllowedRelationship( + ToolSdo::class, + RelationshipType("targets"), + InfrastructureSdo::class + ), + AllowedRelationship( + ToolSdo::class, + RelationshipType("targets"), + LocationSdo::class + ), + AllowedRelationship( + ToolSdo::class, + RelationshipType("targets"), + VulnerabilitySdo::class + ), + + AllowedRelationship( + ToolSdo::class, + RelationshipType("uses"), + InfrastructureSdo::class + ) + ) + } +} + +data class Tool( + override val name: String, + override val description: String? = null, + override val toolTypes: ToolTypes, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val toolVersion: String? = null, + override val type: StixType = ToolSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : + ToolSdo { + override fun allowedRelationships(): List { + return ToolSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Vulnerability.kt similarity index 60% rename from src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt rename to src/main/kotlin/com/stephenott/stix/object/sdo/object/Vulnerability.kt index ffdfd95..a706adb 100644 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/object/sdo/object/Vulnerability.kt @@ -1,18 +1,25 @@ -package com.stephenott.stix.sdo.objects +package com.stephenott.stix.`object`.sdo.`object` -import com.stephenott.stix.sdo.StixDomainObject +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.`object`.sro.`object`.RelationshipSro import com.stephenott.stix.type.* interface VulnerabilitySdo : StixDomainObject { val name: String val description: String? + + companion object{ + val stixType = StixType("vulnerability") + + val allowedRelationships: List = listOf() + } } -data class Vulnerability - ( +data class Vulnerability( override val name: String, override val description: String? = null, - override val type: StixType = stixType, + override val type: StixType = VulnerabilitySdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, override val created: StixInstant = StixInstant(), @@ -26,8 +33,8 @@ data class Vulnerability ) : VulnerabilitySdo { - companion object{ - val stixType = StixType("vulnerability") + override fun allowedRelationships(): List { + return VulnerabilitySdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt b/src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt new file mode 100644 index 0000000..79f6c46 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt @@ -0,0 +1,18 @@ +package com.stephenott.stix.`object`.sro + +import com.stephenott.stix.common.* +import com.stephenott.stix.`object`.StixObject + +interface StixRelationshipObject: + StixObject, + StixCreatedByRef, + StixCreatedProp, + StixExternalReferencesProp, + StixObjectMarkingsRefsProp, + StixGranularMarkingsProp, + StixSpecVersionProp, + StixModified, + StixLabels, + StixRevoked { + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt b/src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt new file mode 100644 index 0000000..877cb58 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt @@ -0,0 +1,128 @@ +package com.stephenott.stix.`object`.sro.`object` + +import com.stephenott.stix.`object`.StixObject +import com.stephenott.stix.`object`.sro.StixRelationshipObject +import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.common.StixObjectRelationshipRegistry +import com.stephenott.stix.type.* +import java.lang.IllegalStateException +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf + +interface RelationshipSro : StixRelationshipObject { + val relationshipType: RelationshipType + val description: String? + val sourceRef: StixIdentifier + val targetRef: StixIdentifier + val startTime: StixInstant? + val stopTime: StixInstant? + + companion object { + val stixType = StixType("relationship") + + val allowedCommonRelationships: List = listOf( + AllowedRelationship( + StixObject::class, + RelationshipType("duplicate-of"), + StixObject::class + ), + AllowedRelationship( + StixObject::class, + RelationshipType("derived-from"), + StixObject::class + ), + AllowedRelationship( + StixObject::class, + RelationshipType("related-to"), + StixObject::class + ) + ) + } +} + +data class AllowedRelationship( + val from: KClass, + val type: RelationshipType, + val to: KClass +) {} + +data class Relationship( + override val relationshipType: RelationshipType, + override val description: String? = null, + override val sourceRef: StixIdentifier, + override val targetRef: StixIdentifier, + override val startTime: StixInstant? = null, + override val stopTime: StixInstant? = null, + override val type: StixType = RelationshipSro.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +) : RelationshipSro { + + /** + * No Relationships are supported for RelationshipSro + */ + override fun allowedRelationships(): List { + return listOf() + } + + constructor( + relationshipType: RelationshipType, + description: String? = null, + sourceRef: StixObject, + targetRef: StixObject, + startTime: StixInstant? = null, + stopTime: StixInstant? = null, + type: StixType = RelationshipSro.stixType, + id: StixIdentifier = StixIdentifier(type), + createdByRef: String? = null, + created: StixInstant = StixInstant(), + externalReferences: ExternalReferences? = null, + objectMarkingsRefs: String? = null, + granularMarkings: String? = null, + specVersion: StixSpecVersion = StixSpecVersion(), + labels: StixLabels? = null, + modified: StixInstant = StixInstant(created), + revoked: StixBoolean = StixBoolean() + ) : this( + relationshipType, description, sourceRef.id, + targetRef.id, startTime, stopTime, + type, id, createdByRef, + created, externalReferences, objectMarkingsRefs, + granularMarkings, specVersion, labels, + modified, revoked + ) + + + init { + val sourceClass: KClass = StixObjectRegistry.registry[sourceRef.type] + ?: throw IllegalStateException("Unable to find sourceRef in Object Registry") + + val targetClass: KClass = StixObjectRegistry.registry[targetRef.type] + ?: throw IllegalStateException("Unable to find targetRef in Object Registry") + + //@TODO add support for x- custom objects + val allowedRelationships: List = StixObjectRelationshipRegistry + .registry.filter { sourceClass.isSubclassOf(it.from) && + it.type == this.relationshipType && + targetClass.isSubclassOf(it.to) + } + + if (allowedRelationships.size > 1){ + println("Duplicate relationships found: $allowedRelationships") + //@TODO add some logging for a warning to indicate multiple duplication objects are registered + } + + require(allowedRelationships.isNotEmpty(), lazyMessage = { + "${this.id} is not a valid relationship for a ${this.sourceRef.type}" + }) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt b/src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt new file mode 100644 index 0000000..eed0754 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt @@ -0,0 +1,5 @@ +package com.stephenott.stix.`object`.sro.`object` + +class Sighting{ + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt b/src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt deleted file mode 100644 index 894f9fd..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/Sdo.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.stephenott.stix.sdo - -import com.stephenott.stix.serialization.JsonReader -import com.stephenott.stix.serialization.JsonWriter -import com.stephenott.stix.ValidatorManager - -class Sdo - (data: T, validateOnCreation: Boolean = true): - JsonReader, - JsonWriter, - ValidatorManager> { - override fun readJson(jsonString: String): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun toJson(): String { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override var isValid: Boolean - get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. - set(value) {} - - override fun validate(data: T): Sdo { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt deleted file mode 100644 index 2044939..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/AttackPattern.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.KillChainPhases - -interface AttackPatternSdo : StixDomainObject { - val name: String - val description: String? - val killChainPhases: KillChainPhases? //@TODO -} - -data class AttackPattern - ( - override val name: String, - override val description: String? = null, - override val killChainPhases: KillChainPhases? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : - AttackPatternSdo { - - companion object{ - val stixType = StixType("attack-pattern") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt deleted file mode 100644 index b7b6897..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Campaign.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* - -interface CampaignSdo : StixDomainObject { - val name: String - val description: String? - val aliases: String? - val firstSeen: StixInstant? - val lastSeen: StixInstant? - val objective: String? -} - -data class Campaign - ( - override val name: String, - override val description: String? = null, - override val aliases: String? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val objective: String? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : CampaignSdo { - - companion object{ - val stixType = StixType("campaign") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt deleted file mode 100644 index c4954c4..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/CourseOfAction.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.CourseOfActionTypeOv - -interface CourseOfActionSdo : StixDomainObject { - val name: String - val description: String? - val actionType: CourseOfActionTypeOv? - val osExecutionEnvs: OsExecutionEnvs? - val actionBin: StixBinary? - val actionReference: ExternalReference? -} - -data class CourseOfAction - ( - override val name: String, - override val description: String? = null, - override val actionType: CourseOfActionTypeOv? = null, - override val osExecutionEnvs: OsExecutionEnvs? = null, - override val actionBin: StixBinary? = null, - override val actionReference: ExternalReference? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : CourseOfActionSdo { - - companion object { - val stixType = StixType("course-of-action") - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt deleted file mode 100644 index e78c458..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Indicator.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.IndicatorTypes -import com.stephenott.stix.type.vocab.PatternType - -interface IndicatorSdo : StixDomainObject { - val name: String? - val description: String? - val indicatorTypes: IndicatorTypes - val pattern: StixPattern - val patternType: PatternType - val patternVersion: String? - -} - -data class Indicator( - override val name: String? = null, - override val description: String? = null, - override val indicatorTypes: IndicatorTypes, - override val pattern: StixPattern, - override val patternType: PatternType, - override val patternVersion: String? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : IndicatorSdo { - - companion object { - val stixType = StixType("indicator") - } - - init { - require(indicatorTypes.size >= 1) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt deleted file mode 100644 index 5a1bebb..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Infrastructure.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.InfrastructureTypes -import com.stephenott.stix.type.vocab.KillChainPhases - -interface InfrastructureSdo : StixDomainObject { - val name: String - val description: String? - val infrastructureTypes: InfrastructureTypes - val aliases: StixStringList? - val killChainPhases: KillChainPhases? - val firstSeen: StixInstant? - val lastSeen: StixInstant? -} - -data class Infrastructure - ( - override val name: String, - override val description: String? = null, - override val infrastructureTypes: InfrastructureTypes, - override val aliases: StixStringList? = null, - override val killChainPhases: KillChainPhases? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : - InfrastructureSdo { - - companion object{ - val stixType = StixType("infrastructure") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt deleted file mode 100644 index 25a0f1d..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/IntrusionSet.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.* - -interface IntrusionSetSdo : StixDomainObject { - val name: String - val description: String? - val aliases: StixStringList? - val firstSeen: StixInstant? - val lastSeen: StixInstant? - val goals: StixStringList? - val resourceLevel: AttackResourceLevelOv? - val primaryMotivation: AttackMotivationOv? - val secondaryMotivations: AttackMotivations? -} - -data class IntrusionSet - ( - override val name: String, - override val description: String? = null, - override val aliases: StixStringList? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val goals: StixStringList? = null, - override val resourceLevel: AttackResourceLevelOv? = null, - override val primaryMotivation: AttackMotivationOv? = null, - override val secondaryMotivations: AttackMotivations? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : - IntrusionSetSdo { - - companion object{ - val stixType = StixType("intrusion-set") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt deleted file mode 100644 index 4af02b1..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Malware.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.* - -interface MalwareSdo : StixDomainObject { - val name: String? - val description: String? - val malwareTypes: MalwareTypes - val isFamily: StixBoolean - val aliases: StixStringList? - val killChainPhases: KillChainPhases? - val firstSeen: StixInstant? - val lastSeen: StixInstant? - val osExecutionEnvs: OsExecutionEnvs? - val architectureExecutionEnvs: ProcessorArchitectures? - val implementationLanguage: ImplementationLanguages? - val capabilities: MalwareCapabilities? - val sampleRefs: StixIdentifiers? -} - -data class Malware - ( - override val name: String, - override val description: String? = null, - override val malwareTypes: MalwareTypes, - override val isFamily: StixBoolean, - override val aliases: StixStringList? = null, - override val killChainPhases: KillChainPhases? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val osExecutionEnvs: OsExecutionEnvs? = null, - override val architectureExecutionEnvs: ProcessorArchitectures? = null, - override val implementationLanguage: ImplementationLanguages? = null, - override val capabilities: MalwareCapabilities? = null, - override val sampleRefs: StixIdentifiers? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : - MalwareSdo { - - companion object{ - val stixType = StixType("malware") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt deleted file mode 100644 index fc40556..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/ThreatActor.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.* - -interface ThreatActorSdo : StixDomainObject { - val name: String - val description: String? - val threatActorTypes: ThreatActorTypes - val aliases: StixStringList? - val firstSeen: StixInstant? - val lastSeen: StixInstant? - val roles: ThreatActorRoles? - val goals: StixStringList? - val sophistication: ThreatActorSophisticationOv? - val resourceLevel: AttackResourceLevelOv? - val primaryMotivation: AttackMotivationOv? - val secondaryMotivation: AttackMotivationOv? - val personalMotivations: AttackMotivations? -} - -data class ThreatActor( - override val name: String, - override val description: String? = null, - override val threatActorTypes: ThreatActorTypes, - override val aliases: StixStringList? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val roles: ThreatActorRoles? = null, - override val goals: StixStringList? = null, - override val sophistication: ThreatActorSophisticationOv? = null, - override val resourceLevel: AttackResourceLevelOv? = null, - override val primaryMotivation: AttackMotivationOv? = null, - override val secondaryMotivation: AttackMotivationOv? = null, - override val personalMotivations: AttackMotivations? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : - ThreatActorSdo { - - companion object{ - val stixType = StixType("threat-actor") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt deleted file mode 100644 index ca20c57..0000000 --- a/src/main/kotlin/com/stephenott/stix/sdo/objects/Tool.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.stephenott.stix.sdo.objects - -import com.stephenott.stix.sdo.StixDomainObject -import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.KillChainPhases -import com.stephenott.stix.type.vocab.ToolTypes - -interface ToolSdo : StixDomainObject { - val name: String - val description: String? - val toolTypes: ToolTypes - val aliases: StixStringList? - val killChainPhases: KillChainPhases? - val toolVersion: String? -} - -data class Tool( - override val name: String, - override val description: String? = null, - override val toolTypes: ToolTypes, - override val aliases: StixStringList? = null, - override val killChainPhases: KillChainPhases? = null, - override val toolVersion: String? = null, - override val type: StixType = stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() -) : - ToolSdo { - - companion object{ - val stixType = StixType("tool") - } - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt b/src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt deleted file mode 100644 index 330ebe4..0000000 --- a/src/main/kotlin/com/stephenott/stix/serialization/JsonManager.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.stephenott.stix.serialization - -interface JsonReader { - - fun readJson(jsonString: String): T - -} - -interface JsonWriter { - - fun toJson(): String - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt new file mode 100644 index 0000000..d01a0b5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -0,0 +1,30 @@ +package com.stephenott.stix.serialization.json + +import com.stephenott.stix.StixBundle +import com.stephenott.stix.StixContent +import com.stephenott.stix.`object`.sdo.StixDomainObject +import com.stephenott.stix.`object`.sro.StixRelationshipObject + +fun StixContent.toJson(){ + TODO("To be implemented") +} + +fun StixBundle.toJson(){ + TODO("To be implemented") +} + +fun StixContent.parse(json: String){ + TODO("To be implemented") +} + +fun StixBundle.parse(json: String){ + TODO("To be implemented") +} + +fun StixDomainObject.parse(json: String){ + TODO("To be implemented") +} + +fun StixRelationshipObject.parse(json: String){ + TODO("To be implemented") +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt b/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt new file mode 100644 index 0000000..ac953db --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt @@ -0,0 +1,8 @@ +package com.stephenott.stix.type + +data class RelationshipType(val type: String){ + init { + //@TODO + //The value of this property MUST be in ASCII and is limited to characters a–z (lowercase ASCII), 0–9, and hyphen (-). + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt b/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt new file mode 100644 index 0000000..bcfc1a2 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt @@ -0,0 +1,107 @@ +package com.stephenott.stix.type + +import java.lang.IllegalArgumentException + +data class StixConfidence(val realValue: Int?, val stixValue: Int?, val scaleValue: String) { + init { + if (realValue != null) { + require(stixValue != null) + require(realValue in 0..100) + + } + if (stixValue != null) { + require(realValue != null) + require(stixValue in 0..100) + } + require(scaleValue.isNotEmpty()) + } + + companion object { + //@TODO Interop Comment for Github: Review the use of Null for the confidence values, as null is usually only used for "not provided". Confirm on GH issues. + fun noneLowMedHighScale(realValue: Int?): StixConfidence { + return when (realValue) { + null -> StixConfidence(null, null, "Not Specified") + 0 -> StixConfidence(realValue, 0, "None") + in 1..29 -> StixConfidence(realValue, 15, "Low") + in 30..69 -> StixConfidence(realValue, 50, "Med") + in 70..100 -> StixConfidence(realValue, 85, "High") + else -> { + throw IllegalArgumentException("Invalid value.") + } + } + } + + fun zeroToTenScale(realValue: Int?): StixConfidence { + return when (realValue) { + null -> StixConfidence(null, null, "Not Specified") + in 0..4 -> StixConfidence(realValue, 0, "0") + in 5..14 -> StixConfidence(realValue, 10, "1") + in 15..24 -> StixConfidence(realValue, 20, "2") + in 25..34 -> StixConfidence(realValue, 30, "3") + in 35..44 -> StixConfidence(realValue, 40, "4") + in 45..54 -> StixConfidence(realValue, 50, "5") + in 55..64 -> StixConfidence(realValue, 60, "6") + in 65..74 -> StixConfidence(realValue, 70, "7") + in 75..84 -> StixConfidence(realValue, 80, "8") + in 85..94 -> StixConfidence(realValue, 90, "9") + in 95..100 -> StixConfidence(realValue, 100, "10") + else -> { + throw IllegalArgumentException("Invalid value.") + } + } + } + + fun admiraltyCredibility(realValue: Int?): StixConfidence { + return when (realValue) { + null -> StixConfidence(null, null, "6 - Truth cannot be judged") + in 0..19 -> StixConfidence(realValue, 10, "5 - Improbable") + in 20..39 -> StixConfidence(realValue, 30, "4 - Doubtful") + in 40..59 -> StixConfidence(realValue, 50, "3 - Possibly True") + in 60..79 -> StixConfidence(realValue, 70, "2 - Probably True") + in 80..100 -> StixConfidence(realValue, 90, "1 - Confirmed by other sources") + else -> { + throw IllegalArgumentException("Invalid value.") + } + } + } + + /** + * Words of Estimative Probability (WEP) + */ + fun wepScale(realValue: Int?): StixConfidence { + //@TODO Review the scaleValues in the 2.1 Docs for typos: Inconsistencies in the capitalization + return when (realValue) { + null -> StixConfidence(null, null, "Not Specified") + 0 -> StixConfidence(realValue, 0, "Impossible") + in 1..19 -> StixConfidence(realValue, 10, "Highly Unlikely/Almost Certainly Not") + in 20..39 -> StixConfidence(realValue, 30, "Unlikely/Probably Not") + in 40..59 -> StixConfidence(realValue, 50, "Even Chance") + in 60..79 -> StixConfidence(realValue, 70, "Likely/Probable") + in 80..99 -> StixConfidence(realValue, 90, "Highly likely/Almost Certain") + 100 -> StixConfidence(realValue, 100, "Certain") + else -> { + throw IllegalArgumentException("Invalid value.") + } + } + } + + /** + * DNI Scale: ICD 203 + */ + fun dniScale(realValue: Int?): StixConfidence { + return when (realValue) { + null -> StixConfidence(null, null, "Not Specified") + in 0..9 -> StixConfidence(realValue, 5, "Almost No Chance / Remote") + in 10..19 -> StixConfidence(realValue, 15, "Very Unlikely / Highly Improbable") + in 20..39 -> StixConfidence(realValue, 30, "Unlikely/Improbable") + in 40..59 -> StixConfidence(realValue, 50, "Rough Even Chance / Roughly Even Odds") + in 60..79 -> StixConfidence(realValue, 70, "Likely / Probable") + in 80..89 -> StixConfidence(realValue, 85, "Very Likely / Highly Probable") + in 90..100 -> StixConfidence(realValue, 95, "Almost Certain / Nearly Certain") + else -> { + throw IllegalArgumentException("Invalid value.") + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt b/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt index ef06ba5..402727c 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt @@ -26,4 +26,8 @@ data class StixIdentifier( fun getIdentifier(): String { return type.toString() + typeUUIDSpacer + uuid } + + override fun toString(): String { + return getIdentifier() + } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt b/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt index dfabf22..cfb0b1c 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.type -import com.stephenott.stix.StixDataFormats +import com.stephenott.stix.common.StixDataFormats import java.time.Instant import java.util.regex.Pattern diff --git a/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt b/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt index 136619e..57f3ab3 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.type -class StixInteger (val value: Int){ +class StixInteger (val value: Int) { init { require(value in 1..999999999) } diff --git a/src/main/kotlin/com/stephenott/stix/type/StixLang.kt b/src/main/kotlin/com/stephenott/stix/type/StixLang.kt new file mode 100644 index 0000000..80aff22 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixLang.kt @@ -0,0 +1,15 @@ +package com.stephenott.stix.type + +data class StixLang (val lang: String, val isDefinedValue: Boolean){ + init { + //@TODO add RFC5646 language code validation + } + + constructor(): this(DEFAULT_LANG, false) + + constructor(lang: String): this(lang, true) + + companion object{ + const val DEFAULT_LANG = "en" + } +} \ No newline at end of file From a6ab9e4579e8286be993bec0a263faf85a498cb1 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 14 Oct 2019 21:53:20 -0400 Subject: [PATCH 03/21] refactor for data markings --- src/main/kotlin/com/stephenott/stix/Bundle.kt | 2 + .../kotlin/com/stephenott/stix/MainRunner.kt | 4 +- .../kotlin/com/stephenott/stix/StixBundle.kt | 18 ++++++++ .../stix/common/CommonProperties.kt | 18 +++++++- .../stix/common/StixMarkingObjectRegistry.kt | 15 +++++++ .../stix/common/StixObjectRegistry.kt | 4 +- .../common/StixObjectRelationshipRegistry.kt | 6 +-- .../stix/object/sro/object/Sighting.kt | 5 --- .../stix/{object => objects}/StixObject.kt | 4 +- .../stix/objects/core/StixCoreObject.kt | 6 +++ .../core}/sco/StixCyberObservableObject.kt | 2 +- .../core/sco/extension/ScoExtension.java | 4 ++ .../core}/sdo/StixDomainObject.kt | 8 ++-- .../core/sdo/objects}/AttackPattern.kt | 12 ++--- .../core/sdo/objects}/Campaign.kt | 12 ++--- .../core/sdo/objects}/CourseOfAction.kt | 12 ++--- .../core/sdo/objects}/Grouping.kt | 12 ++--- .../core/sdo/objects}/Identity.kt | 12 ++--- .../core/sdo/objects}/Indicator.kt | 12 ++--- .../core/sdo/objects}/Infrastructure.kt | 12 ++--- .../core/sdo/objects}/IntrusionSet.kt | 12 ++--- .../core/sdo/objects}/Location.kt | 12 ++--- .../core/sdo/objects}/Malware.kt | 12 ++--- .../core/sdo/objects}/MalwareAnalysis.kt | 12 ++--- .../core/sdo/objects}/Note.kt | 12 ++--- .../core/sdo/objects}/ObservedData.kt | 12 ++--- .../core/sdo/objects}/Opinion.kt | 12 ++--- .../core/sdo/objects}/Report.kt | 12 ++--- .../core/sdo/objects}/ThreatActor.kt | 12 ++--- .../core/sdo/objects}/Tool.kt | 12 ++--- .../core/sdo/objects}/Vulnerability.kt | 12 ++--- .../core}/sro/StixRelationshipObject.kt | 4 +- .../core/sro/objects}/Relationship.kt | 6 +-- .../stix/objects/core/sro/objects/Sighting.kt | 5 +++ .../stix/objects/meta/StixMetaObject.kt | 7 +++ .../objects/meta/datamarking/DataMarking.kt | 13 ++++++ .../meta/datamarking/GranularMarking.kt | 32 +++++++++++++ .../meta/datamarking/MarkingDefinition.kt | 36 +++++++++++++++ .../objects/meta/datamarking/MarkingObject.kt | 5 +++ .../meta/datamarking/objects/Statement.kt | 13 ++++++ .../objects/meta/datamarking/objects/Tlp.kt | 13 ++++++ .../objects/meta/lco/LanguageContentObject.kt | 20 +++++++++ .../meta/lco/objects/LanguageContent.kt | 45 +++++++++++++++++++ .../stix/serialization/json/JsonExtensions.kt | 4 +- .../com/stephenott/stix/type/Extensions.kt | 7 +++ .../stix/type/LanguageContentDictionary.kt | 21 +++++++++ .../type/vocab/MarkingDefinitionTypeOv.kt | 20 +++++++++ 47 files changed, 446 insertions(+), 117 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/Bundle.kt create mode 100644 src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt rename src/main/kotlin/com/stephenott/stix/{object => objects}/StixObject.kt (66%) create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/StixCoreObject.kt rename src/main/kotlin/com/stephenott/stix/{object => objects/core}/sco/StixCyberObservableObject.kt (85%) create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java rename src/main/kotlin/com/stephenott/stix/{object => objects/core}/sdo/StixDomainObject.kt (66%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/AttackPattern.kt (85%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Campaign.kt (89%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/CourseOfAction.kt (88%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Grouping.kt (77%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Identity.kt (81%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Indicator.kt (88%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Infrastructure.kt (91%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/IntrusionSet.kt (90%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Location.kt (82%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Malware.kt (94%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/MalwareAnalysis.kt (88%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Note.kt (75%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/ObservedData.kt (77%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Opinion.kt (77%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Report.kt (77%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/ThreatActor.kt (91%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Tool.kt (87%) rename src/main/kotlin/com/stephenott/stix/{object/sdo/object => objects/core/sdo/objects}/Vulnerability.kt (74%) rename src/main/kotlin/com/stephenott/stix/{object => objects/core}/sro/StixRelationshipObject.kt (76%) rename src/main/kotlin/com/stephenott/stix/{object/sro/object => objects/core/sro/objects}/Relationship.kt (96%) create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/StixMetaObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/DataMarking.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/GranularMarking.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/Extensions.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/LanguageContentDictionary.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt diff --git a/src/main/kotlin/com/stephenott/stix/Bundle.kt b/src/main/kotlin/com/stephenott/stix/Bundle.kt new file mode 100644 index 0000000..0f7e90e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/Bundle.kt @@ -0,0 +1,2 @@ +package com.stephenott.stix + diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index 810b04a..d879e7b 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,7 +1,7 @@ package com.stephenott.stix -import com.stephenott.stix.`object`.sdo.`object`.AttackPattern -import com.stephenott.stix.`object`.sro.`object`.Relationship +import com.stephenott.stix.objects.core.sdo.objects.AttackPattern +import com.stephenott.stix.objects.core.sro.objects.Relationship import com.stephenott.stix.type.RelationshipType object MainRunner { diff --git a/src/main/kotlin/com/stephenott/stix/StixBundle.kt b/src/main/kotlin/com/stephenott/stix/StixBundle.kt index ce895d8..0e81b5d 100644 --- a/src/main/kotlin/com/stephenott/stix/StixBundle.kt +++ b/src/main/kotlin/com/stephenott/stix/StixBundle.kt @@ -1,4 +1,22 @@ package com.stephenott.stix +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.type.StixIdentifier +import com.stephenott.stix.type.StixType + interface StixBundle: StixContent{ + val objects: LinkedHashSet + + companion object { + val stixType = StixType("bundle") + } +} + + + +data class Bundle(override val type: StixType = StixBundle.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val objects: LinkedHashSet +): StixBundle{ + } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt index 0552125..6e7244b 100644 --- a/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt +++ b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt @@ -50,9 +50,23 @@ interface StixRevoked { } interface StixConfidence { - val confidence: StixConfidence + val confidence: StixConfidence? } interface StixLang { - val lang: StixLang + val lang: StixLang? +} + +/** + * Only used on SCO + */ +interface StixExtensions { + val extensions: Extensions +} + +/** + * Only used on SCO + */ +interface defanged { + val defanged: StixBoolean } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt new file mode 100644 index 0000000..a083a34 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt @@ -0,0 +1,15 @@ +package com.stephenott.stix.common + +import com.stephenott.stix.objects.meta.datamarking.MarkingObject +import com.stephenott.stix.objects.meta.datamarking.objects.Statement +import com.stephenott.stix.objects.meta.datamarking.objects.Tlp +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv +import kotlin.reflect.KClass + +object StixMarkingObjectRegistry { + + val registry: MutableMap> = mutableMapOf( + Pair(MarkingDefinitionTypeOv("statement"), Statement::class), + Pair(MarkingDefinitionTypeOv("tlp"), Tlp::class) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt index 6f2703f..a0f27ee 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -1,7 +1,7 @@ package com.stephenott.stix.common -import com.stephenott.stix.`object`.StixObject -import com.stephenott.stix.`object`.sdo.`object`.* +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sdo.objects.* import com.stephenott.stix.type.StixType import kotlin.reflect.KClass diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt index e920c06..710b455 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt @@ -1,8 +1,8 @@ package com.stephenott.stix.common -import com.stephenott.stix.`object`.sdo.`object`.* -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.objects.* +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro object StixObjectRelationshipRegistry { diff --git a/src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt b/src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt deleted file mode 100644 index eed0754..0000000 --- a/src/main/kotlin/com/stephenott/stix/object/sro/object/Sighting.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.stephenott.stix.`object`.sro.`object` - -class Sighting{ - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/StixObject.kt b/src/main/kotlin/com/stephenott/stix/objects/StixObject.kt similarity index 66% rename from src/main/kotlin/com/stephenott/stix/object/StixObject.kt rename to src/main/kotlin/com/stephenott/stix/objects/StixObject.kt index 0b390c3..01eb74b 100644 --- a/src/main/kotlin/com/stephenott/stix/object/StixObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/StixObject.kt @@ -1,7 +1,7 @@ -package com.stephenott.stix.`object` +package com.stephenott.stix.objects import com.stephenott.stix.StixContent -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship /** * Parent object for all STIX objects: SDO, SCO, SRO, Metadata diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/StixCoreObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/StixCoreObject.kt new file mode 100644 index 0000000..96af30e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/StixCoreObject.kt @@ -0,0 +1,6 @@ +package com.stephenott.stix.objects.core + +import com.stephenott.stix.objects.StixObject + +interface StixCoreObject: StixObject { +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt similarity index 85% rename from src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt index 3a11d71..942013f 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sco/StixCyberObservableObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt @@ -1,7 +1,7 @@ package com.stephenott.stix.object.sco import com.stephenott.stix.common.* -import com.stephenott.stix.`object`.StixObject +import com.stephenott.stix.objects.StixObject interface StixCyberObservableObject: StixObject, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java new file mode 100644 index 0000000..6851f71 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java @@ -0,0 +1,4 @@ +package com.stephenott.stix.objects.core.sco.extension; + +public interface ScoExtension { +} diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/StixDomainObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt similarity index 66% rename from src/main/kotlin/com/stephenott/stix/object/sdo/StixDomainObject.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt index 031aab9..33cebe5 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/StixDomainObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt @@ -1,7 +1,7 @@ -package com.stephenott.stix.`object`.sdo +package com.stephenott.stix.objects.core.sdo import com.stephenott.stix.common.* -import com.stephenott.stix.`object`.StixObject +import com.stephenott.stix.objects.StixObject interface StixDomainObject : StixObject, @@ -13,4 +13,6 @@ interface StixDomainObject : StixSpecVersionProp, StixModified, StixLabels, - StixRevoked {} \ No newline at end of file + StixRevoked, + StixLang, + StixConfidence {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt similarity index 85% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt index 2e104b6..a66e9b7 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/AttackPattern.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.KillChainPhases @@ -66,7 +66,9 @@ data class AttackPattern( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : AttackPatternSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt similarity index 89% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt index a247e9b..01da79d 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Campaign.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* interface CampaignSdo : StixDomainObject { @@ -99,7 +99,9 @@ data class Campaign override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : CampaignSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt similarity index 88% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt index 8c69322..0702062 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/CourseOfAction.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.CourseOfActionTypeOv @@ -83,7 +83,9 @@ data class CourseOfAction( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : CourseOfActionSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Grouping.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt similarity index 77% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Grouping.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt index 8dda406..a853590 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.GroupingContextOv @@ -34,7 +34,9 @@ data class Grouping( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : GroupingSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Identity.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt similarity index 81% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Identity.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt index fa98738..596a188 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.IdentityClass import com.stephenott.stix.type.vocab.IdentityRoles @@ -46,7 +46,9 @@ data class Identity( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : IdentitySdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt similarity index 88% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index 82e4ae6..fcc42fb 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.IndicatorTypes import com.stephenott.stix.type.vocab.PatternType @@ -81,7 +81,9 @@ data class Indicator( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : IndicatorSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt similarity index 91% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index 36b016f..13b784b 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.InfrastructureTypes import com.stephenott.stix.type.vocab.KillChainPhases @@ -130,7 +130,9 @@ data class Infrastructure( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : InfrastructureSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt similarity index 90% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt index 29be9a2..61910c9 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/IntrusionSet.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.* @@ -111,7 +111,9 @@ data class IntrusionSet( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : IntrusionSetSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Location.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt similarity index 82% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Location.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt index 5ba454d..4adc6ec 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.AdministrativeArea import com.stephenott.stix.type.vocab.City @@ -48,7 +48,9 @@ data class Location( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : LocationSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt similarity index 94% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt index c22fc14..99bee1d 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Malware.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.* @@ -197,7 +197,9 @@ data class Malware override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : MalwareSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt similarity index 88% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/MalwareAnalysis.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt index 2b3ceee..0062cb6 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.MalwareAvResult @@ -76,7 +76,9 @@ data class MalwareAnalysis override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : MalwareAnalysisSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Note.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt similarity index 75% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Note.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt index 4e8cf2e..c06e1db 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* interface NoteSdo : StixDomainObject { @@ -34,7 +34,9 @@ data class Note( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : NoteSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt similarity index 77% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/ObservedData.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt index d3a611f..de14653 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* interface ObservedDataSdo : StixDomainObject { @@ -33,7 +33,9 @@ data class ObservedData( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : ObservedDataSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Opinion.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt similarity index 77% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Opinion.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt index 453a4ce..3c29a82 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.OpinionEnum @@ -34,7 +34,9 @@ data class Opinion( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : OpinionSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Report.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt similarity index 77% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Report.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt index c278980..bddfb29 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.ReportTypes @@ -36,7 +36,9 @@ data class Report( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : ReportSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt similarity index 91% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt index 2b32e7e..ec8062f 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/ThreatActor.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.* @@ -125,7 +125,9 @@ data class ThreatActor( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : ThreatActorSdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt similarity index 87% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index c6f6523..16c04aa 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.KillChainPhases import com.stephenott.stix.type.vocab.ToolTypes @@ -84,7 +84,9 @@ data class Tool( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : ToolSdo { override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt similarity index 74% rename from src/main/kotlin/com/stephenott/stix/object/sdo/object/Vulnerability.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt index a706adb..c5843bb 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sdo/object/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt @@ -1,8 +1,8 @@ -package com.stephenott.stix.`object`.sdo.`object` +package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.`object`.AllowedRelationship -import com.stephenott.stix.`object`.sro.`object`.RelationshipSro +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* interface VulnerabilitySdo : StixDomainObject { @@ -29,7 +29,9 @@ data class Vulnerability( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null ) : VulnerabilitySdo { diff --git a/src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt similarity index 76% rename from src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt index 79f6c46..b71d7eb 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sro/StixRelationshipObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt @@ -1,7 +1,7 @@ -package com.stephenott.stix.`object`.sro +package com.stephenott.stix.objects.core.sro import com.stephenott.stix.common.* -import com.stephenott.stix.`object`.StixObject +import com.stephenott.stix.objects.StixObject interface StixRelationshipObject: StixObject, diff --git a/src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt similarity index 96% rename from src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt rename to src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index 877cb58..6a4218f 100644 --- a/src/main/kotlin/com/stephenott/stix/object/sro/object/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -1,7 +1,7 @@ -package com.stephenott.stix.`object`.sro.`object` +package com.stephenott.stix.objects.core.sro.objects -import com.stephenott.stix.`object`.StixObject -import com.stephenott.stix.`object`.sro.StixRelationshipObject +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sro.StixRelationshipObject import com.stephenott.stix.common.StixObjectRegistry import com.stephenott.stix.common.StixObjectRelationshipRegistry import com.stephenott.stix.type.* diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt new file mode 100644 index 0000000..b31711c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -0,0 +1,5 @@ +package com.stephenott.stix.objects.core.sro.objects + +class Sighting{ + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/StixMetaObject.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/StixMetaObject.kt new file mode 100644 index 0000000..b9cb6c8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/StixMetaObject.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.objects.meta + +import com.stephenott.stix.objects.StixObject + + +interface StixMetaObject: + StixObject {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/DataMarking.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/DataMarking.kt new file mode 100644 index 0000000..4974be0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/DataMarking.kt @@ -0,0 +1,13 @@ +package com.stephenott.stix.objects.meta.datamarking + +import com.stephenott.stix.objects.meta.StixMetaObject +import com.stephenott.stix.common.* + +interface DataMarking: StixMetaObject, + StixCreatedByRef, + StixCreatedProp, + StixExternalReferencesProp, + StixObjectMarkingsRefsProp, + StixGranularMarkingsProp, + StixSpecVersionProp{ +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/GranularMarking.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/GranularMarking.kt new file mode 100644 index 0000000..1889ab2 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/GranularMarking.kt @@ -0,0 +1,32 @@ +package com.stephenott.stix.objects.meta.datamarking + +import com.stephenott.stix.type.StixIdentifier +import com.stephenott.stix.type.StixLang +import com.stephenott.stix.type.StixStringList + +interface GranularMarkingGm{ + val lang: StixLang? + val markingRef: StixIdentifier? + val selectors: StixStringList +} + +data class GranularMarking( + override val lang: StixLang?, + override val markingRef: StixIdentifier?, + override val selectors: StixStringList +): GranularMarkingGm { + + init { + if (lang == null){ + require(markingRef != null) + require(markingRef.type == MarkingDefinitionDm.stixType) + } else { + require(markingRef == null) + } + + + } + + constructor(lang: StixLang, selectors: StixStringList): this(lang, null, selectors) + constructor(markingRef: StixIdentifier, selectors: StixStringList): this(null, markingRef, selectors) +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt new file mode 100644 index 0000000..1a564f2 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt @@ -0,0 +1,36 @@ +package com.stephenott.stix.objects.meta.datamarking + +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv + +interface MarkingDefinitionDm: DataMarking{ + val name: String? + val definitionType: MarkingDefinitionTypeOv + val definition: MarkingObject + + companion object { + val stixType = StixType("marking-definition") + } +} + +data class MarkingDefinition( + override val name: String? = null, + override val definitionType: MarkingDefinitionTypeOv, + override val definition: MarkingObject, + override val type: StixType = MarkingDefinitionDm.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion() + +): MarkingDefinitionDm { + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt new file mode 100644 index 0000000..68efc15 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt @@ -0,0 +1,5 @@ +package com.stephenott.stix.objects.meta.datamarking + +interface MarkingObject { +} + diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt new file mode 100644 index 0000000..35a09cb --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt @@ -0,0 +1,13 @@ +package com.stephenott.stix.objects.meta.datamarking.objects + +import com.stephenott.stix.objects.meta.datamarking.MarkingObject + +interface StatementMo : MarkingObject { + val statement: String + +} + +data class Statement( + override val statement: String + +): StatementMo \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt new file mode 100644 index 0000000..dc65502 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt @@ -0,0 +1,13 @@ +package com.stephenott.stix.objects.meta.datamarking.objects + +import com.stephenott.stix.objects.meta.datamarking.MarkingObject + + +interface TlpMo : MarkingObject { + val tlp: String +} + +data class Tlp( + override val tlp: String + +): TlpMo \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt new file mode 100644 index 0000000..07c0e75 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt @@ -0,0 +1,20 @@ +package com.stephenott.stix.objects.meta.lco + +import com.stephenott.stix.objects.meta.StixMetaObject +import com.stephenott.stix.common.* + +/** + * Built to support future custom language handling classes + */ +interface LanguageContentObject: StixMetaObject, + StixCreatedByRef, + StixCreatedProp, + StixExternalReferencesProp, + StixObjectMarkingsRefsProp, + StixGranularMarkingsProp, + StixSpecVersionProp, + StixModified, + StixLabels, + StixRevoked, + StixConfidence { +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt new file mode 100644 index 0000000..021b0f3 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt @@ -0,0 +1,45 @@ +package com.stephenott.stix.objects.meta.lco.objects + +import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.meta.lco.LanguageContentObject +import com.stephenott.stix.type.* + +interface LanguageContentLco : LanguageContentObject { + val objectRef: StixIdentifier + val objectModified: StixInstant? + val contents: LanguageContentDictionary + + companion object { + val stixType = StixType("language-content") + + val allowedRelationships: List = listOf( + ) + + } +} + +data class LanguageContent( + override val objectRef: StixIdentifier, + override val objectModified: StixInstant? = null, + override val contents: LanguageContentDictionary, + override val type: StixType = AttackPatternSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null +) : + LanguageContentLco { + + override fun allowedRelationships(): List { + return LanguageContentLco.allowedRelationships + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index d01a0b5..ceda42e 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -2,8 +2,8 @@ package com.stephenott.stix.serialization.json import com.stephenott.stix.StixBundle import com.stephenott.stix.StixContent -import com.stephenott.stix.`object`.sdo.StixDomainObject -import com.stephenott.stix.`object`.sro.StixRelationshipObject +import com.stephenott.stix.objects.core.sdo.StixDomainObject +import com.stephenott.stix.objects.core.sro.StixRelationshipObject fun StixContent.toJson(){ TODO("To be implemented") diff --git a/src/main/kotlin/com/stephenott/stix/type/Extensions.kt b/src/main/kotlin/com/stephenott/stix/type/Extensions.kt new file mode 100644 index 0000000..76b58bf --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/Extensions.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.type + +import com.stephenott.stix.objects.core.sco.extension.ScoExtension + +class Extensions(private val extensions: Set = linkedSetOf()): + Set by extensions { +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/LanguageContentDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/LanguageContentDictionary.kt new file mode 100644 index 0000000..b6ace7b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/LanguageContentDictionary.kt @@ -0,0 +1,21 @@ +package com.stephenott.stix.type + +class LanguageContentDictionary(private val languages: Set = linkedSetOf()): + Map by languages.associateBy({it.languageCode}){ + + init { + //@TODO + + //@TODO Add a JsonCreator constructor for the Map + + } +} + +data class LanguageContent(val languageCode: String, val content: Map) { + + init { + //@TODO add language code validation + //@TODO add better type handler for the content Map + //@TODO add support for untranslated content + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt new file mode 100644 index 0000000..4027e2d --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt @@ -0,0 +1,20 @@ +package com.stephenott.stix.type.vocab + +data class MarkingDefinitionTypeOv(private val definitionType: String): OpenVocab, CharSequence by definitionType{ + + companion object{ + + val vocabName = "marking-definition-type-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "statement", "tlp") + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.definitionType in vocab) + } +} \ No newline at end of file From d403fe5b14daff4e8795c68840dbe637383b8333 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Wed, 16 Oct 2019 06:24:40 -0400 Subject: [PATCH 04/21] add more SCOs --- .../stix/common/CommonProperties.kt | 16 +-- .../stix/common/ValidatorManager.kt | 22 +++- .../core/sco/StixCyberObservableObject.kt | 13 +-- .../stix/objects/core/sco/objects/Artifact.kt | 84 ++++++++++++++ .../core/sco/objects/AutonomousSystem.kt | 63 +++++++++++ .../objects/core/sco/objects/Directory.kt | 71 ++++++++++++ .../objects/core/sco/objects/DomainName.kt | 73 ++++++++++++ .../objects/core/sco/objects/EmailAddress.kt | 63 +++++++++++ .../objects/core/sco/objects/EmailMessage.kt | 106 ++++++++++++++++++ .../stix/objects/core/sco/objects/File.kt | 58 ++++++++++ .../objects/core/sco/objects/UserAccount.kt | 56 +++++++++ .../stix/objects/core/sdo/StixDomainObject.kt | 10 +- .../core/sro/StixRelationshipObject.kt | 6 +- .../objects/meta/lco/LanguageContentObject.kt | 8 +- .../type/AdditionalHeaderFieldsDictionary.kt | 16 +++ .../stephenott/stix/type/HashesDictionary.kt | 2 +- .../com/stephenott/stix/type/MimePartType.kt | 31 +++++ .../com/stephenott/stix/type/StixBinary.kt | 6 +- .../stephenott/stix/type/StixStringList.kt | 2 +- .../type/vocab/EncryptionAlgorithmEnum.kt | 16 +++ 20 files changed, 688 insertions(+), 34 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/AdditionalHeaderFieldsDictionary.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/MimePartType.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt diff --git a/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt index 6e7244b..030faaa 100644 --- a/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt +++ b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt @@ -37,36 +37,36 @@ interface StixSpecVersionProp{ val specVersion: StixSpecVersion } -interface StixLabels { +interface StixLabelsProp { val labels: StixLabels? } -interface StixModified { +interface StixModifiedProp { val modified: StixInstant } -interface StixRevoked { +interface StixRevokedProp { val revoked: StixBoolean } -interface StixConfidence { +interface StixConfidenceProp { val confidence: StixConfidence? } -interface StixLang { +interface StixLangProp { val lang: StixLang? } /** * Only used on SCO */ -interface StixExtensions { - val extensions: Extensions +interface StixExtensionsProp { + val extensions: Extensions? } /** * Only used on SCO */ -interface defanged { +interface StixDefangedProp { val defanged: StixBoolean } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt index 0ae6a50..00bf55f 100644 --- a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt +++ b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt @@ -1,7 +1,23 @@ package com.stephenott.stix.common -interface ValidatorManager { - var isValid: Boolean +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sco.objects.AutonomousSystemSco +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.StixType +import kotlin.reflect.KProperty1 - fun validate(data: I ): O +interface BusinessRulesValidator{ + fun objectValidationRules(obj: T) +} + +interface CompanionStixType{ + val stixType: StixType +} + +interface CompanionIdContributingProperties{ + val idContributingProperties: List> +} + +interface CompanionAllowedRelationships{ + val allowedRelationships: List } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt index 942013f..62e0a66 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt @@ -1,15 +1,12 @@ -package com.stephenott.stix.object.sco +package com.stephenott.stix.objects.core.sco import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject -interface StixCyberObservableObject: +interface StixCyberObservableObject : StixObject, - StixCreatedProp, + StixSpecVersionProp, StixObjectMarkingsRefsProp, StixGranularMarkingsProp, - StixSpecVersionProp, - StixModified - StixRevoked { - -} \ No newline at end of file + StixDefangedProp, + StixExtensionsProp {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt new file mode 100644 index 0000000..39c4b54 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt @@ -0,0 +1,84 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import com.stephenott.stix.type.vocab.EncryptionAlgorithmEnum +import kotlin.reflect.KProperty1 + +interface ArtifactSco : StixCyberObservableObject { + + val mimeType: String? + val payloadBin: StixBinary? + val url: String? + val hashes: HashesDictionary? + val encryptionAlgorithm: EncryptionAlgorithmEnum? + val decryptionKey: String? + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships{ + + override val stixType = StixType("artifact") + + override val idContributingProperties: List> = listOf( + ArtifactSco::hashes, + ArtifactSco::payloadBin + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: ArtifactSco) { + if (obj.url != null) { + require(obj.payloadBin == null, lazyMessage = { "payload_bin must not be present if url is provided." }) + } + if (obj.payloadBin != null) { + require(obj.url == null, lazyMessage = { "url must not be present if payload_bin is provided." }) + } + if (obj.url != null) { + require(obj.hashes != null, lazyMessage = { "hashes must be present when url property is present" }) + } + if (obj.encryptionAlgorithm == null) { + require( + obj.decryptionKey == null, + lazyMessage = { "decryption_key must not be present when encryption_algorithm is absent." }) + } + } + + } +} + +data class Artifact( + override val mimeType: String? = null, + override val payloadBin: StixBinary? = null, + override val url: String? = null, + override val hashes: HashesDictionary? = null, + override val encryptionAlgorithm: EncryptionAlgorithmEnum? = null, + override val decryptionKey: String? = null, + override val type: StixType = StixType(ArtifactSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : ArtifactSco { + + init { + ArtifactSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt new file mode 100644 index 0000000..ff1b1f5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt @@ -0,0 +1,63 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface AutonomousSystemSco : StixCyberObservableObject { + + val number: StixInteger + val name: String? + val rir: String? + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships{ + + override val stixType = StixType("autonomous-system") + + override val idContributingProperties: List> = listOf( + AutonomousSystemSco::number + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: AutonomousSystemSco) { + + } + + } +} + +data class AutonomousSystem( + override val number: StixInteger, + override val name: String? = null, + override val rir: String? = null, + override val type: StixType = StixType(AutonomousSystemSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : AutonomousSystemSco { + + init { + AutonomousSystemSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt new file mode 100644 index 0000000..51931eb --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -0,0 +1,71 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface DirectorySco : StixCyberObservableObject { + + val path: String + val pathEnc: String? //@TODO add validation + val ctime: StixInstant? + val mtime: StixInstant? + val atime: StixInstant? + val containsRefs: StixIdentifiers? + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships { + + override val stixType = StixType("directory") + + override val idContributingProperties: List> = listOf( + DirectorySco::path + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: DirectorySco) { + require(obj.containsRefs?.all { + it.type == DirectorySco.stixType || it.type == FileSco.stixType } ?: true, + lazyMessage = {"contains_refs must only contain SCOs of type Directory and File."}) + } + + } +} + +data class Directory( + override val path: String, + override val pathEnc: String? = null, + override val ctime: StixInstant? = null, + override val mtime: StixInstant? = null, + override val atime: StixInstant? = null, + override val containsRefs: StixIdentifiers?, + override val type: StixType = StixType(DirectorySco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : DirectorySco { + + init { + DirectorySco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt new file mode 100644 index 0000000..3c348e6 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt @@ -0,0 +1,73 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface DomainNameSco : StixCyberObservableObject { + + val value: String //@TODO add validation + //@TODO deprecated resolves_to_refs, add elevator code to create relationships from the 2.1 objects + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships { + + override val stixType = StixType("domain-name") + + override val idContributingProperties: List> = listOf( + DomainNameSco::value + ) + + override val allowedRelationships: List = listOf( + AllowedRelationship( + DomainNameSco::class, + RelationshipType("resolves-to"), + DomainNameSco::class + ), + AllowedRelationship( + DomainNameSco::class, + RelationshipType("resolves-to"), + DomainNameSco::class // @TODO IPV4-addr + ), + AllowedRelationship( + DomainNameSco::class, + RelationshipType("resolves-to"), + DomainNameSco::class // @TODO IPV6-addr + ) + ) + + override fun objectValidationRules(obj: DomainNameSco) { + } + + } +} + +data class DomainName( + override val value: String, + override val type: StixType = StixType(DomainNameSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : DomainNameSco { + + init { + DomainNameSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt new file mode 100644 index 0000000..a0e0f37 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -0,0 +1,63 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface EmailAddressSco : StixCyberObservableObject { + + val value: String //@TODO add validation + val displayName: String? //@TODO add validation + val belongsToRef: StixIdentifier? + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships { + + override val stixType = StixType("email-addr") + + override val idContributingProperties: List> = listOf( + EmailAddressSco::value + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: EmailAddressSco) { + require(obj.belongsToRef?.type == UserAccountSco.stixType, lazyMessage = {"belongs_to_ref must reference a user-account SCO."}) + } + + } +} + +data class EmailAddress( + override val value: String, + override val displayName: String? = null, + override val belongsToRef: StixIdentifier? = null, + override val type: StixType = StixType(EmailAddressSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : EmailAddressSco { + + init { + EmailAddressSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt new file mode 100644 index 0000000..9429c75 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -0,0 +1,106 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface EmailMessageSco : StixCyberObservableObject { + + val isMultipart: StixBoolean + val date: StixInstant? + val contentType: String? + val fromRef: StixIdentifier? + val senderRef: StixIdentifier? + val toRefs: StixIdentifiers? + val ccRefs: StixIdentifiers? + val bccRefs: StixIdentifiers? + val messageId: String? + val subject: String? + val receivedLines: StixStringList? + val additionalHeaderFields: AdditionalHeaderFieldsDictionary? + val body: String? + val bodyMultipart: MimePartTypes? + val rawEmailRef: StixIdentifier? + + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships { + + override val stixType = StixType("email-message") + + override val idContributingProperties: List> = listOf( + EmailMessageSco::fromRef, + EmailMessageSco::subject, + EmailMessageSco::body + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: EmailMessageSco) { + require(obj.fromRef?.type == EmailAddressSco.stixType, + lazyMessage = {"from_ref must be references to email-address SCO"}) + require(obj.senderRef?.type == EmailAddressSco.stixType, + lazyMessage = {"sender_ref must be references to email-address SCO"}) + require(obj.toRefs?.all { it.type == EmailAddressSco.stixType } ?: true, + lazyMessage = {"to_refs must be references to email-address SCO"}) + require(obj.ccRefs?.all { it.type == EmailAddressSco.stixType } ?: true, + lazyMessage = {"cc_refs must be references to email-address SCO"}) + require(obj.bccRefs?.all { it.type == EmailAddressSco.stixType } ?: true, + lazyMessage = {"bcc_refs must be references to email-address SCO"}) + + if (obj.isMultipart.value){ + require(obj.body == null, + lazyMessage = {"body cannot be used when is_multipart is true"}) + } else { + require(obj.bodyMultipart == null, + lazyMessage = {"body_multipart cannot be used when is_multipart is false"}) + } + } + } +} + +data class EmailMessage( + override val isMultipart: StixBoolean, + override val date: StixInstant? = null, + override val contentType: String? = null, + override val fromRef: StixIdentifier? = null, + override val senderRef: StixIdentifier? = null, + override val toRefs: StixIdentifiers? = null, + override val ccRefs: StixIdentifiers? = null, + override val bccRefs: StixIdentifiers? = null, + override val messageId: String? = null, + override val subject: String? = null, + override val receivedLines: StixStringList? = null, + override val additionalHeaderFields: AdditionalHeaderFieldsDictionary? = null, + override val body: String? = null, + override val bodyMultipart: MimePartTypes? = null, + override val rawEmailRef: StixIdentifier? = null, + override val type: StixType = StixType(EmailMessageSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : EmailMessageSco { + + init { + EmailMessageSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt new file mode 100644 index 0000000..9f880c8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -0,0 +1,58 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface FileSco : StixCyberObservableObject { + + + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships{ + + override val stixType = StixType("file") + + override val idContributingProperties: List> = listOf( + + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: FileSco) { + + } + + } +} + +data class File( + override val type: StixType = StixType(FileSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : FileSco { + + init { + FileSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt new file mode 100644 index 0000000..a279e21 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt @@ -0,0 +1,56 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionIdContributingProperties +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KProperty1 + +interface UserAccountSco : StixCyberObservableObject { + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships{ + + override val stixType = StixType("user-account") + + override val idContributingProperties: List> = listOf( + + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: UserAccountSco) { + + } + + } +} + +data class UserAccount( + override val type: StixType = StixType(UserAccountSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : UserAccountSco { + + init { + UserAccountSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt index 33cebe5..2dc43b4 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt @@ -11,8 +11,8 @@ interface StixDomainObject : StixObjectMarkingsRefsProp, StixGranularMarkingsProp, StixSpecVersionProp, - StixModified, - StixLabels, - StixRevoked, - StixLang, - StixConfidence {} \ No newline at end of file + StixModifiedProp, + StixLabelsProp, + StixRevokedProp, + StixLangProp, + StixConfidenceProp {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt index b71d7eb..0020220 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt @@ -11,8 +11,8 @@ interface StixRelationshipObject: StixObjectMarkingsRefsProp, StixGranularMarkingsProp, StixSpecVersionProp, - StixModified, - StixLabels, - StixRevoked { + StixModifiedProp, + StixLabelsProp, + StixRevokedProp { } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt index 07c0e75..4e47f10 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/LanguageContentObject.kt @@ -13,8 +13,8 @@ interface LanguageContentObject: StixMetaObject, StixObjectMarkingsRefsProp, StixGranularMarkingsProp, StixSpecVersionProp, - StixModified, - StixLabels, - StixRevoked, - StixConfidence { + StixModifiedProp, + StixLabelsProp, + StixRevokedProp, + StixConfidenceProp { } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/AdditionalHeaderFieldsDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/AdditionalHeaderFieldsDictionary.kt new file mode 100644 index 0000000..475438f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/AdditionalHeaderFieldsDictionary.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type + +class AdditionalHeaderFieldsDictionary(private val fields: Map>): + Map> by fields { + + init { + require(unallowedFields.any { it in fields.keys }) + } + + companion object{ + val unallowedFields: List = listOf( + "date","received_lines", "content_type", + "from_ref", "sender_ref", "to_refs", + "cc_refs", "bcc_refs", "subject") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt index f9abc1d..a8796d6 100644 --- a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt +++ b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt @@ -2,7 +2,7 @@ package com.stephenott.stix.type import com.stephenott.stix.type.vocab.OpenVocab -class HashesDictionary(private val immutableDictionary: HashMap = hashMapOf()): +class HashesDictionary(private val immutableDictionary: LinkedHashMap = linkedMapOf()): Map by immutableDictionary { } diff --git a/src/main/kotlin/com/stephenott/stix/type/MimePartType.kt b/src/main/kotlin/com/stephenott/stix/type/MimePartType.kt new file mode 100644 index 0000000..062ca29 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/MimePartType.kt @@ -0,0 +1,31 @@ +package com.stephenott.stix.type + +import com.stephenott.stix.objects.core.sco.objects.ArtifactSco +import com.stephenott.stix.objects.core.sco.objects.FileSco + +class MimePartTypes(private val parts: List): List by parts{ + init { + //@TODO + } +} + +data class MimePartType( + val body: String?, + val bodyRawRef: StixIdentifier?, + val ContentType: String?, + val contentDisposition: String? +) { + + init { + if (body != null) { + require(bodyRawRef == null, lazyMessage = { "Only one of body or body_raw_ref must be included." }) + } + if (bodyRawRef != null) { + require(body == null, lazyMessage = { "Only one of body or body_raw_ref must be included." }) + require(bodyRawRef.type == ArtifactSco.stixType || + bodyRawRef.type == FileSco.stixType, + lazyMessage = { "body_raw_ref must be a SCO of type artifact or file." }) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt b/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt index 3ecd0f3..bc3d20e 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixBinary.kt @@ -1,3 +1,7 @@ package com.stephenott.stix.type -data class StixBinary(val binary: String){} \ No newline at end of file +data class StixBinary(val binary: String){ + init { + //@TODO base 64 + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt b/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt index 4857978..4c1fde6 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.type -class StixStringList(private val list: LinkedHashSet): Set by list{ +class StixStringList(private val list: List): List by list{ init { list.all { it.isNotEmpty()} } diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt new file mode 100644 index 0000000..1018daa --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type.vocab + +class EncryptionAlgorithmEnum(private val algorithm: String) : OpenVocab, CharSequence by algorithm { + + companion object { + val vocabName = "encryption-algorithm-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "AES-256-GCM​", "ChaCha20-Poly1305", "mime-type-indicated" + ) + } + + init { + require(this.algorithm in vocab) + } +} \ No newline at end of file From f76161d7918c046342c707f41000a1127a720ea8 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Fri, 18 Oct 2019 17:41:21 -0400 Subject: [PATCH 05/21] Add SCOs and refactor --- .../stix/common/ValidatorManager.kt | 10 ++ .../extension/objects/ArchiveFileExtension.kt | 35 ++++ .../extension/objects/HttpRequestExtension.kt | 43 +++++ .../sco/extension/objects/IcmpExtension.kt | 34 ++++ .../objects/NetworkSocketExtension.kt | 46 ++++++ .../extension/objects/NtfsFileExtension.kt | 33 ++++ .../sco/extension/objects/PdfFileExtension.kt | 45 ++++++ .../objects/RasterImageFileExtension.kt | 44 +++++ .../sco/extension/objects/TcpExtension.kt | 35 ++++ .../extension/objects/UnixAccountExtension.kt | 40 +++++ .../objects/WindowsPeBinaryFileExtension.kt | 65 ++++++++ .../objects/WindowsProcessExtension.kt | 65 ++++++++ .../objects/WindowsServiceExtension.kt | 56 +++++++ .../stix/objects/core/sco/objects/Artifact.kt | 12 +- .../core/sco/objects/AutonomousSystem.kt | 12 +- .../objects/core/sco/objects/Directory.kt | 12 +- .../objects/core/sco/objects/DomainName.kt | 12 +- .../objects/core/sco/objects/EmailAddress.kt | 12 +- .../objects/core/sco/objects/EmailMessage.kt | 13 +- .../stix/objects/core/sco/objects/File.kt | 63 ++++++-- .../objects/core/sco/objects/IPv4Address.kt | 71 ++++++++ .../objects/core/sco/objects/IPv6Address.kt | 71 ++++++++ .../objects/core/sco/objects/MacAddress.kt | 61 +++++++ .../stix/objects/core/sco/objects/Mutex.kt | 60 +++++++ .../core/sco/objects/NetworkTraffic.kt | 151 ++++++++++++++++++ .../stix/objects/core/sco/objects/Process.kt | 100 ++++++++++++ .../stix/objects/core/sco/objects/Software.kt | 73 +++++++++ .../stix/objects/core/sco/objects/Url.kt | 62 +++++++ .../objects/core/sco/objects/UserAccount.kt | 51 +++++- .../core/sco/objects/WindowsRegistryKey.kt | 82 ++++++++++ .../core/sco/objects/X509Certificate.kt | 101 ++++++++++++ .../stix/type/AlternateDataStreams.kt | 18 +++ .../stix/type/ExifTagsDictionary.kt | 20 +++ .../com/stephenott/stix/type/Extensions.kt | 4 +- .../stephenott/stix/type/HashesDictionary.kt | 2 + .../stix/type/HttpRequestHeaderDictionary.kt | 9 ++ .../stephenott/stix/type/IpfixDictionary.kt | 20 +++ .../{MimePartType.kt => MimePartTypes.kt} | 0 .../com/stephenott/stix/type/StixFloat.kt | 3 + .../com/stephenott/stix/type/StixHex.kt | 7 + .../com/stephenott/stix/type/StixInteger.kt | 6 +- .../stephenott/stix/type/StixStringList.kt | 4 +- .../stix/type/WindowsPeOptionalHeaderType.kt | 60 +++++++ .../stix/type/WindowsPeSectionType.kt | 18 +++ .../stix/type/WindowsRegistryValueType.kt | 21 +++ .../stix/type/X509v3ExtensionsTypes.kt | 40 +++++ .../stix/type/vocab/AccountTypeOv.kt | 24 +++ .../stix/type/vocab/LanguageCodes.kt | 24 +++ .../vocab/NetworkSocketAddressFamilyEnum.kt | 18 +++ .../stix/type/vocab/NetworkSocketTypeEnum.kt | 17 ++ .../type/vocab/WindowsIntegrityLevelEnum.kt | 17 ++ .../stix/type/vocab/WindowsPebinaryTypeOv.kt | 16 ++ .../type/vocab/WindowsRegistryDataTypeEnum.kt | 20 +++ .../type/vocab/WindowsServiceStartTypeEnum.kt | 17 ++ .../type/vocab/WindowsServiceStatusEnum.kt | 18 +++ .../stix/type/vocab/WindowsServiceTypeEnum.kt | 18 +++ 56 files changed, 1934 insertions(+), 57 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/AlternateDataStreams.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/ExifTagsDictionary.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/HttpRequestHeaderDictionary.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/IpfixDictionary.kt rename src/main/kotlin/com/stephenott/stix/type/{MimePartType.kt => MimePartTypes.kt} (100%) create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixFloat.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/StixHex.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/WindowsPeOptionalHeaderType.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/WindowsPeSectionType.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/WindowsRegistryValueType.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt create mode 100644 src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt diff --git a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt index 00bf55f..e5da1de 100644 --- a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt +++ b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt @@ -1,9 +1,11 @@ package com.stephenott.stix.common import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sco.objects.AutonomousSystemSco import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.StixType +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface BusinessRulesValidator{ @@ -14,10 +16,18 @@ interface CompanionStixType{ val stixType: StixType } +interface CompanionExtensionType{ + val extensionType: String +} + interface CompanionIdContributingProperties{ val idContributingProperties: List> } interface CompanionAllowedRelationships{ val allowedRelationships: List +} + +interface CompanionAllowedExtensions{ + val allowedExtensions: List> } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt new file mode 100644 index 0000000..d685cd5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt @@ -0,0 +1,35 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.objects.DirectorySco +import com.stephenott.stix.objects.core.sco.objects.FileSco +import com.stephenott.stix.type.StixIdentifiers + +interface ArchiveFileExtensionExt : ScoExtension { + val containsRefs: StixIdentifiers + val comment: String? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "archive-ext" + + override fun objectValidationRules(obj: ArchiveFileExtensionExt) { + require(obj.containsRefs.all { it.type in listOf(FileSco.stixType, DirectorySco.stixType) }, + lazyMessage = { "contains_refs can only contain references to File and Directory SCOs." }) + } + } +} + +data class ArchiveFileExtension( + override val containsRefs: StixIdentifiers, + override val comment: String? = null +) : ArchiveFileExtensionExt { + + init { + ArchiveFileExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt new file mode 100644 index 0000000..49984b0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt @@ -0,0 +1,43 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.objects.ArtifactSco +import com.stephenott.stix.type.HttpRequestHeaderDictionary +import com.stephenott.stix.type.StixIdentifier +import com.stephenott.stix.type.StixInteger + +interface HttpRequestExtensionExt : ScoExtension { + val requestMethod: String + val requestValue: String + val requestVersion: String? + val requestHeader: HttpRequestHeaderDictionary? + val messageBodyLength: StixInteger? + val messageBodyDataRef: StixIdentifier? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "http-request-ext" + + override fun objectValidationRules(obj: HttpRequestExtensionExt) { + require(obj.messageBodyDataRef?.type == ArtifactSco.stixType) + } + } +} + +data class HttpRequestExtension( + override val requestMethod: String, + override val requestValue: String, + override val requestVersion: String? = null, + override val requestHeader: HttpRequestHeaderDictionary? = null, + override val messageBodyLength: StixInteger? = null, + override val messageBodyDataRef: StixIdentifier? = null +) : HttpRequestExtensionExt { + + init { + HttpRequestExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt new file mode 100644 index 0000000..04018ad --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt @@ -0,0 +1,34 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.* + +interface IcmpExtensionExt: ScoExtension{ + + val icmpTypeHex: StixHex + val icmpCodeHex: StixHex + + companion object: + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "icmp-ext" + + override fun objectValidationRules(obj: IcmpExtensionExt) { + + } + } +} + +data class IcmpExtension( + override val icmpTypeHex: StixHex, + override val icmpCodeHex: StixHex + +): IcmpExtensionExt { + + init { + IcmpExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt new file mode 100644 index 0000000..288e4db --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt @@ -0,0 +1,46 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.NetworkSocketAddressFamilyEnum +import com.stephenott.stix.type.vocab.NetworkSocketTypeEnum + +interface NetworkSocketExtensionExt: ScoExtension{ + + val addressFamily: NetworkSocketAddressFamilyEnum + val isBlocking: StixBoolean? + val isListening: StixBoolean? + val options: LinkedHashMap? //@TODO *** Refactor: https://github.com/oasis-tcs/cti-stix2/issues/185 + val socketType: NetworkSocketTypeEnum? + val socketDescriptor: StixInteger? + val socketHandle: StixInteger? + + companion object: + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "socket-ext" + + override fun objectValidationRules(obj: NetworkSocketExtensionExt) { + + } + } +} + +data class NetworkSocketExtension( + override val addressFamily: NetworkSocketAddressFamilyEnum, + override val isBlocking: StixBoolean? = null, + override val isListening: StixBoolean? = null, + override val options: LinkedHashMap? = null, + override val socketType: NetworkSocketTypeEnum? = null, + override val socketDescriptor: StixInteger? = null, + override val socketHandle: StixInteger? = null + +): NetworkSocketExtensionExt { + + init { + NetworkSocketExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt new file mode 100644 index 0000000..bd9a8a0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt @@ -0,0 +1,33 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.AlternateDataStreams + +interface NtfsFileExtensionExt: ScoExtension{ + val sid: String? + val alternateDataStreams: AlternateDataStreams? + + companion object: + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "ntfs-ext" + + override fun objectValidationRules(obj: NtfsFileExtensionExt) { + require(obj.sid != null || obj.alternateDataStreams != null, + lazyMessage = {"At least 1 property must be provided in the ntfs-ext extension."}) + } + } +} + +data class NtfsFileExtension( + override val sid: String? = null, + override val alternateDataStreams: AlternateDataStreams? = null +): NtfsFileExtensionExt { + + init { + NtfsFileExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt new file mode 100644 index 0000000..ebb8365 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt @@ -0,0 +1,45 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.AlternateDataStreams +import com.stephenott.stix.type.StixBoolean + +interface PdfFileExtensionExt: ScoExtension{ + + val version: String? + val isOptimized: StixBoolean? + val documentInfoDict: LinkedHashMap? + val pdfid0: String? + val pdfid1: String? + + companion object: + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "pdf-ext" + + override fun objectValidationRules(obj: PdfFileExtensionExt) { + require(obj.version != null || + obj.isOptimized != null || + obj.documentInfoDict != null || + obj.pdfid0 != null || + obj.pdfid1 != null, + lazyMessage = {"PDF File Extension must have at least one property provided."}) + } + } +} + +data class PdfFileExtension( + override val version: String? = null, + override val isOptimized: StixBoolean? = null, + override val documentInfoDict: LinkedHashMap? = null, + override val pdfid0: String? = null, + override val pdfid1: String? = null +): PdfFileExtensionExt { + + init { + PdfFileExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt new file mode 100644 index 0000000..a4ca2eb --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt @@ -0,0 +1,44 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.AlternateDataStreams +import com.stephenott.stix.type.ExifTagsDictionary +import com.stephenott.stix.type.StixBoolean +import com.stephenott.stix.type.StixInteger + +interface RasterImageFileExtensionExt: ScoExtension{ + + val imageHeight: StixInteger? + val imageWidth: StixInteger? + val bitsPerPixel: StixInteger? + val exifTags: ExifTagsDictionary? + + companion object: + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "raster-image-ext" + + override fun objectValidationRules(obj: RasterImageFileExtensionExt) { + require(obj.imageHeight != null || + obj.imageWidth != null || + obj.bitsPerPixel != null || + obj.exifTags != null, + lazyMessage = {"At least 1 property must be provided for RasterImage File Extension"}) + } + } +} + +data class RasterImageFileExtension( + override val imageHeight: StixInteger? = null, + override val imageWidth: StixInteger? = null, + override val bitsPerPixel: StixInteger? = null, + override val exifTags: ExifTagsDictionary? = null +): RasterImageFileExtensionExt { + + init { + RasterImageFileExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt new file mode 100644 index 0000000..6d6e691 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt @@ -0,0 +1,35 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.StixHex + +interface TcpExtensionExt : ScoExtension { + + val srcFlagsHex: StixHex? + val dstFlagsHex: StixHex? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "tcp-ext" + + override fun objectValidationRules(obj: TcpExtensionExt) { + require(obj.srcFlagsHex != null || + obj.dstFlagsHex != null, + lazyMessage = { "At least one property must be provided for tcp-ext" }) + } + } +} + +data class TcpExtension( + override val srcFlagsHex: StixHex? = null, + override val dstFlagsHex: StixHex? = null +) : TcpExtensionExt { + + init { + TcpExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt new file mode 100644 index 0000000..619648c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt @@ -0,0 +1,40 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.StixHex +import com.stephenott.stix.type.StixInteger +import com.stephenott.stix.type.StixStringList + +interface UnixAccountExtensionExt : ScoExtension { + + val gid: StixInteger? + val groups: StixStringList? + val homeDir: String? + val shell: String? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "unix-account-ext" + + override fun objectValidationRules(obj: UnixAccountExtensionExt) { + require(listOf(obj.gid, obj.groups, obj.homeDir, obj.shell).any { it != null }, + lazyMessage = {"unix-account-ext must have at least one property provided/is not null."}) + } + } +} + +data class UnixAccountExtension( + override val gid: StixInteger? = null, + override val groups: StixStringList? = null, + override val homeDir: String? = null, + override val shell: String? = null +) : UnixAccountExtensionExt { + + init { + UnixAccountExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt new file mode 100644 index 0000000..cbed87c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt @@ -0,0 +1,65 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.WindowsPebinaryTypeOv +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +interface WindowsPeBinaryFileExtensionExt : ScoExtension { + + val peType: WindowsPebinaryTypeOv + val imphash: String? + val machineHex: StixHex? + val numberOfSections: StixInteger? + val timeDateStamp: StixInstant? + val pointerToSymbolTableHex: StixHex? + val numberOfSymbols: StixInteger? + val sizeOfOptionalHeader: StixInteger? + val characteristicsHex: StixHex? + val fileHeaderHashes: HashesDictionary? + val optionalHeader: WindowsPeOptionalHeaderType? + val sections: WindowsPeSectionTypes? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "windows-pebinary-ext" + + override fun objectValidationRules(obj: WindowsPeBinaryFileExtensionExt) { + require(obj.timeDateStamp?.subSecondPrecision == 0, + lazyMessage = {"time_date_stamp cannot have sub-second precision"}) + require(obj.sizeOfOptionalHeader?.value!! >= 0, + lazyMessage = {"size_of_optional_header must not be negative."}) + require(obj::class.memberProperties + .filter { + it.visibility == KVisibility.PUBLIC && it.name != obj::peType.name + }.any { it.getter.call() != null }, + lazyMessage = { "At least one property other than pe_type must be provided/be not null." }) + } + } +} + +data class WindowsPeBinaryFileExtension( + override val peType: WindowsPebinaryTypeOv, + override val imphash: String? = null, + override val machineHex: StixHex? = null, + override val numberOfSections: StixInteger? = null, + override val timeDateStamp: StixInstant? = null, + override val pointerToSymbolTableHex: StixHex? = null, + override val numberOfSymbols: StixInteger? = null, + override val sizeOfOptionalHeader: StixInteger? = null, + override val characteristicsHex: StixHex? = null, + override val fileHeaderHashes: HashesDictionary? = null, + override val optionalHeader: WindowsPeOptionalHeaderType? = null, + override val sections: WindowsPeSectionTypes? = null + +) : WindowsPeBinaryFileExtensionExt { + + init { + WindowsPeBinaryFileExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt new file mode 100644 index 0000000..6368d8f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt @@ -0,0 +1,65 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.StixBoolean +import com.stephenott.stix.type.vocab.WindowsIntegrityLevelEnum +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +interface WindowsProcessExtensionExt : ScoExtension { + + val aslrEnabled: StixBoolean? + val depEnabled: StixBoolean? + val priority: WindowsProcessExtensionPriority? + val ownerSid: String? + val windowTitle: String? + val startupInfo: LinkedHashMap? //@TODO refactor with https://github.com/oasis-tcs/cti-stix2/issues/185#issuecomment-543309219 + val integrityLevel: WindowsIntegrityLevelEnum? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "windows-process-ext" + + override fun objectValidationRules(obj: WindowsProcessExtensionExt) { + require(obj::class.memberProperties + .filter {it.visibility == KVisibility.PUBLIC} + .any { it.getter.call() != null }, + lazyMessage = {"At least one property must be provided/not null."}) + } + } +} + +data class WindowsProcessExtensionPriority( + val priority: String, + val enforceSuffixRule: Boolean = WindowsProcessExtensionPriority.enforceSuffixRuleDefault +): CharSequence by priority { + + companion object{ + var enforceSuffixRuleDefault: Boolean = true + } + + init { + if (enforceSuffixRule){ + require(priority.endsWith("_CLASS")) + } + } +} + +data class WindowsProcessExtension( + override val aslrEnabled: StixBoolean? = null, + override val depEnabled: StixBoolean? = null, + override val priority: WindowsProcessExtensionPriority? = null, + override val ownerSid: String? = null, + override val windowTitle: String? = null, + override val startupInfo: LinkedHashMap? = null, + override val integrityLevel: WindowsIntegrityLevelEnum? = null +) : WindowsProcessExtensionExt { + + init { + WindowsProcessExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt new file mode 100644 index 0000000..0325ba1 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt @@ -0,0 +1,56 @@ +package com.stephenott.stix.objects.core.sco.extension.objects + +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionExtensionType +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.type.StixHex +import com.stephenott.stix.type.StixIdentifiers +import com.stephenott.stix.type.StixStringList +import com.stephenott.stix.type.vocab.WindowsServiceStartTypeEnum +import com.stephenott.stix.type.vocab.WindowsServiceStatusEnum +import com.stephenott.stix.type.vocab.WindowsServiceTypeEnum +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +interface WindowsServiceExtensionExt : ScoExtension { + + val serviceName: String? + val descriptions: StixStringList? + val displayName: String? + val groupName: String? + val startType: WindowsServiceStartTypeEnum? + val serviceDllRefs: StixIdentifiers? + val serviceType: WindowsServiceTypeEnum? + val serviceStatus: WindowsServiceStatusEnum? + + companion object : + CompanionExtensionType, + BusinessRulesValidator { + + override val extensionType: String = "windows-service-ext" + + override fun objectValidationRules(obj: WindowsServiceExtensionExt) { + require(this::class.memberProperties + .filter {it.visibility == KVisibility.PUBLIC} + .any { it.getter.call() != null }, + lazyMessage = {"windows-service-extension requires at least one property must be provided/not null."}) + } + } +} + +data class WindowsServiceExtension( + override val serviceName: String? = null, + override val descriptions: StixStringList? = null, + override val displayName: String? = null, + override val groupName: String? = null, + override val startType: WindowsServiceStartTypeEnum? = null, + override val serviceDllRefs: StixIdentifiers? = null, + override val serviceType: WindowsServiceTypeEnum? = null, + override val serviceStatus: WindowsServiceStatusEnum? = null + +) : WindowsServiceExtensionExt { + + init { + WindowsServiceExtensionExt.objectValidationRules(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt index 39c4b54..781e693 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt @@ -1,14 +1,13 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions import com.stephenott.stix.type.vocab.EncryptionAlgorithmEnum +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface ArtifactSco : StixCyberObservableObject { @@ -24,7 +23,10 @@ interface ArtifactSco : StixCyberObservableObject { CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships{ + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() override val stixType = StixType("artifact") diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt index ff1b1f5..7a08145 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt @@ -1,13 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface AutonomousSystemSco : StixCyberObservableObject { @@ -20,7 +19,10 @@ interface AutonomousSystemSco : StixCyberObservableObject { CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships{ + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() override val stixType = StixType("autonomous-system") diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt index 51931eb..c85bfea 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -1,13 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface DirectorySco : StixCyberObservableObject { @@ -23,7 +22,10 @@ interface DirectorySco : StixCyberObservableObject { CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships { + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() override val stixType = StixType("directory") diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt index 3c348e6..f0af713 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt @@ -1,13 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface DomainNameSco : StixCyberObservableObject { @@ -19,7 +18,10 @@ interface DomainNameSco : StixCyberObservableObject { CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships { + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() override val stixType = StixType("domain-name") diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt index a0e0f37..71eac43 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -1,13 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface EmailAddressSco : StixCyberObservableObject { @@ -20,7 +19,10 @@ interface EmailAddressSco : StixCyberObservableObject { CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships { + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() override val stixType = StixType("email-addr") diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt index 9429c75..028766f 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -1,13 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface EmailMessageSco : StixCyberObservableObject { @@ -28,12 +27,14 @@ interface EmailMessageSco : StixCyberObservableObject { val bodyMultipart: MimePartTypes? val rawEmailRef: StixIdentifier? - companion object: CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships { + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() override val stixType = StixType("email-message") diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt index 9f880c8..6627c99 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -1,43 +1,86 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.extension.objects.* import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 +import kotlin.reflect.full.isSubclassOf interface FileSco : StixCyberObservableObject { - - - companion object: + val hashes: HashesDictionary? + val size: StixInteger? + val name: String? + val nameEnc: String? + val magicNumberHex: StixHex? + val mimeType: String? + val cTime: StixInstant? + val mTime: StixInstant? + val aTime: StixInstant? + val parentDirectoryRef: StixIdentifier? + val containsRefs: StixIdentifiers? + val contentRef: StixIdentifier? + + companion object : CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships{ + CompanionAllowedRelationships, + CompanionAllowedExtensions { override val stixType = StixType("file") override val idContributingProperties: List> = listOf( - + FileSco::hashes, + FileSco::name, + FileSco::extensions ) override val allowedRelationships: List = listOf( ) - override fun objectValidationRules(obj: FileSco) { + override val allowedExtensions: List> = listOf( + NtfsFileExtensionExt::class, + RasterImageFileExtensionExt::class, + PdfFileExtensionExt::class, + ArchiveFileExtensionExt::class, + WindowsPeBinaryFileExtensionExt::class + ) + override fun objectValidationRules(obj: FileSco) { + require(obj.size?.value!! >= 0, + lazyMessage = { "size must not be a negative number." }) + require(obj.parentDirectoryRef?.type == DirectorySco.stixType, + lazyMessage = { "parent_directory_ref must only contain a reference to a Directory object SCO." }) + require(obj.containsRefs?.all { + StixObjectRegistry.registry.getValue(it.type).isSubclassOf(StixCyberObservableObject::class) + }!!, lazyMessage = { "contains_refs can only contain references to SCOs." }) + require(obj.contentRef?.type == ArtifactSco.stixType, + lazyMessage = { "content_ref must only contain a reference to a Artifact SCO." }) } } } data class File( + override val hashes: HashesDictionary? = null, + override val size: StixInteger? = null, + override val name: String? = null, + override val nameEnc: String? = null, + override val magicNumberHex: StixHex? = null, + override val mimeType: String? = null, + override val cTime: StixInstant? = null, + override val mTime: StixInstant? = null, + override val aTime: StixInstant? = null, + override val parentDirectoryRef: StixIdentifier? = null, + override val containsRefs: StixIdentifiers? = null, + override val contentRef: StixIdentifier? = null, override val type: StixType = StixType(FileSco.stixType), override val id: StixIdentifier = StixIdentifier(type), override val objectMarkingsRefs: String? = null, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt new file mode 100644 index 0000000..0243985 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt @@ -0,0 +1,71 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface IPv4AddressSco : StixCyberObservableObject { + + val value: String +// val resolvesToRefs: StixIdentifiers //@TODO DEPRECATED add elevator +// val belongsToRefs: StixIdentifiers //@TODO DEPRECATED add elevator + + companion object: + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() + + override val stixType = StixType("ipv4-addr") + + override val idContributingProperties: List> = listOf( + IPv4AddressSco::value + ) + + override val allowedRelationships: List = listOf( + AllowedRelationship( + IPv4AddressSco::class, + RelationshipType("resolves-to"), + DomainNameSco::class //@TODO mac-addr + ), + AllowedRelationship( + IPv4AddressSco::class, + RelationshipType("belongs-to"), + AutonomousSystemSco::class + ) + ) + + override fun objectValidationRules(obj: IPv4AddressSco) { + } + + } +} + +data class IPv4Address( + override val value: String, + override val type: StixType = StixType(IPv4AddressSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : IPv4AddressSco { + + init { + IPv4AddressSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt new file mode 100644 index 0000000..6283ff5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt @@ -0,0 +1,71 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface IPv6AddressSco : StixCyberObservableObject { + + val value: String +// val resolvesToRefs: StixIdentifiers //@TODO DEPRECATED add elevator +// val belongsToRefs: StixIdentifiers //@TODO DEPRECATED add elevator + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() + + override val stixType = StixType("ipv6-addr") + + override val idContributingProperties: List> = listOf( + IPv6AddressSco::value + ) + + override val allowedRelationships: List = listOf( + AllowedRelationship( + IPv6AddressSco::class, + RelationshipType("resolves-to"), + DomainNameSco::class //@TODO mac-addr + ), + AllowedRelationship( + IPv6AddressSco::class, + RelationshipType("belongs-to"), + AutonomousSystemSco::class + ) + ) + + override fun objectValidationRules(obj: IPv6AddressSco) { + } + + } +} + +data class IPv6Address( + override val value: String, + override val type: StixType = StixType(IPv6AddressSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : IPv6AddressSco { + + init { + IPv6AddressSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt new file mode 100644 index 0000000..a4b2117 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt @@ -0,0 +1,61 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface MacAddressSco : StixCyberObservableObject { + + val value: String + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() + + override val stixType = StixType("mac-addr") + + override val idContributingProperties: List> = listOf( + MacAddressSco::value + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: MacAddressSco) { + //@TODO The MAC address value ​MUST​ be represented as a single colon-delimited, lowercase MAC-48 address, which ​MUST​ include leading zeros for each octet. + } + + } +} + +data class MacAddress( + override val value: String, + override val type: StixType = StixType(MacAddressSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : MacAddressSco { + + init { + MacAddressSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt new file mode 100644 index 0000000..8898d1a --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt @@ -0,0 +1,60 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface MutexSco : StixCyberObservableObject { + + val name: String + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() + + override val stixType = StixType("mutex") + + override val idContributingProperties: List> = listOf( + MutexSco::name + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: MutexSco) { + } + + } +} + +data class Mutex( + override val name: String, + override val type: StixType = StixType(MutexSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : MutexSco { + + init { + MutexSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt new file mode 100644 index 0000000..f1d8bd6 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt @@ -0,0 +1,151 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.extension.objects.* +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface NetworkTrafficSco : StixCyberObservableObject { + + val start: StixInstant? + val end: StixInstant? + val isActive: StixBoolean? + val srcRef: StixIdentifier? + val dstRef: StixIdentifier? + val srcPort: StixInteger? + val dstPort: StixInteger? + val protocols: StixStringList + val srcByteCount: StixInteger? + val dstByteCount: StixInteger? + val srcPackets: StixInteger? + val dstPackets: StixInteger? + val ipfix: IpfixDictionary? + val srcPayloadRef: StixIdentifier? + val dstPayloadRef: StixIdentifier? + val encapsulatesRefs: StixIdentifiers? + val encapsulatedByRef: StixIdentifier? + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val stixType = StixType("network-traffic") + + override val idContributingProperties: List> = listOf( + NetworkTrafficSco::start, + NetworkTrafficSco::srcRef, + NetworkTrafficSco::dstRef, + NetworkTrafficSco::srcPort, + NetworkTrafficSco::dstPort, + NetworkTrafficSco::protocols + ) + + override val allowedRelationships: List = listOf( + + ) + + override val allowedExtensions: List> = listOf( + HttpRequestExtensionExt::class, + TcpExtensionExt::class, + IcmpExtensionExt::class, + NetworkSocketExtensionExt::class + ) + + override fun objectValidationRules(obj: NetworkTrafficSco) { + if (obj.isActive?.value!!) { + require(obj.end == null, + lazyMessage = { "If is_active is true then end must not be included." }) + } + if (obj.start != null && obj.end != null) { + require(obj.end?.instant!!.isAfter(obj.start?.instant), + lazyMessage = { "if start and end are both defined then end must be later than start." }) + } + if (obj.end != null) { + require(obj.isActive != null && + obj.isActive?.value == false && + obj.isActive?.isDefinedValue == true, + lazyMessage = { "If end is provided then is_active must be provided and have a value of false." }) + } + + require(obj.srcRef?.type in listOf( + IPv4AddressSco.stixType, + IPv6AddressSco.stixType, + MacAddressSco.stixType, + DomainNameSco.stixType + ), + lazyMessage = { "src_ref must be a reference to one of: ipv4-addr, ipv6-addr, mac-addr, domain-name." }) + + require(obj.dstRef?.type in listOf( + IPv4AddressSco.stixType, + IPv6AddressSco.stixType, + MacAddressSco.stixType, + DomainNameSco.stixType + ), + lazyMessage = { "dst_ref must be a reference to one of: ipv4-addr, ipv6-addr, mac-addr, domain-name." }) + + require(obj.srcPort?.value!! in 0..65535, + lazyMessage = { "src_port must be in range 0 to 65535" }) + + require(obj.dstPort?.value!! in 0..65535, + lazyMessage = { "dst_port must be in range 0 to 65535" }) + + require(obj.srcPayloadRef?.type == ArtifactSco.stixType, + lazyMessage = { "src_payload_ref must only reference type artifact" }) + + require(obj.dstPayloadRef?.type == ArtifactSco.stixType, + lazyMessage = { "dst_payload_ref must only reference type artifact" }) + + require(obj.encapsulatesRefs?.all { it.type == NetworkTrafficSco.stixType }!!, + lazyMessage = { "encapsulates_refs values must only reference type network-traffic" }) + + require(obj.encapsulatedByRef?.type == NetworkTrafficSco.stixType, + lazyMessage = { "encapsulated_by_ref must only reference type network-traffic" }) + } + + } +} + +data class NetworkTraffic( + override val start: StixInstant? = null, + override val end: StixInstant? = null, + override val isActive: StixBoolean? = null, + override val srcRef: StixIdentifier? = null, + override val dstRef: StixIdentifier? = null, + override val srcPort: StixInteger? = null, + override val dstPort: StixInteger? = null, + override val protocols: StixStringList, + override val srcByteCount: StixInteger? = null, + override val dstByteCount: StixInteger? = null, + override val srcPackets: StixInteger? = null, + override val dstPackets: StixInteger? = null, + override val ipfix: IpfixDictionary? = null, + override val srcPayloadRef: StixIdentifier? = null, + override val dstPayloadRef: StixIdentifier? = null, + override val encapsulatesRefs: StixIdentifiers? = null, + override val encapsulatedByRef: StixIdentifier? = null, + override val type: StixType = StixType(NetworkTrafficSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : NetworkTrafficSco { + + init { + NetworkTrafficSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt new file mode 100644 index 0000000..7b24ec5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt @@ -0,0 +1,100 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.extension.objects.WindowsProcessExtensionExt +import com.stephenott.stix.objects.core.sco.extension.objects.WindowsServiceExtensionExt +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import com.stephenott.stix.type.vocab.EncryptionAlgorithmEnum +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface ProcessSco : StixCyberObservableObject { + + val isHidden: StixBoolean? + val pid: StixInteger? + val createdTime: StixInstant? + val cwd: String? + val commandLine: String? + val environmentVariables: LinkedHashMap? //@TODO Refactor: https://github.com/oasis-tcs/cti-stix2/issues/185#issuecomment-543299610 + val openedConnectionRef: StixIdentifier? + val creatorUserRef: StixIdentifier? + val imageRef: StixIdentifier? + val parentRef: StixIdentifier? + val childRefs: StixIdentifiers? + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val stixType = StixType("process") + + override val idContributingProperties: List> = listOf( + //@TODO review as there are no ID contributing props as per current spec + ) + + override val allowedRelationships: List = listOf( + + ) + + override val allowedExtensions: List> = listOf( + WindowsProcessExtensionExt::class, + WindowsServiceExtensionExt::class + ) + + override fun objectValidationRules(obj: ProcessSco) { + require(obj.openedConnectionRef?.type == NetworkTrafficSco.stixType, + lazyMessage = { "opened_connection_ref must only reference network-traffic SCO." }) + + require(obj.creatorUserRef?.type == UserAccountSco.stixType, + lazyMessage = { "creator_user_ref must only reference user-account SCO." }) + + require(obj.imageRef?.type == FileSco.stixType, + lazyMessage = { "image_ref must only reference file SCO." }) + + require(obj.parentRef?.type == ProcessSco.stixType, + lazyMessage = { "parent_ref must only reference process SCO." }) + + require(obj.childRefs?.all { it.type == ProcessSco.stixType }!!, + lazyMessage = { "child_refs must only have values that reference process SCO." }) + } + + } +} + +data class Process( + override val isHidden: StixBoolean? = null, + override val pid: StixInteger? = null, + override val createdTime: StixInstant? = null, + override val cwd: String? = null, + override val commandLine: String? = null, + override val environmentVariables: LinkedHashMap? = null, + override val openedConnectionRef: StixIdentifier? = null, + override val creatorUserRef: StixIdentifier? = null, + override val imageRef: StixIdentifier? = null, + override val parentRef: StixIdentifier? = null, + override val childRefs: StixIdentifiers? = null, + override val type: StixType = StixType(ProcessSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), //@TODO review as spec currently says that a Process SCO uses a UUID v4 + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : ProcessSco { + + init { + ProcessSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt new file mode 100644 index 0000000..1bf6eb2 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt @@ -0,0 +1,73 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import com.stephenott.stix.type.vocab.LanguageCodes +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface SoftwareSco : StixCyberObservableObject { + + val name: String + val cpe: String? + val languages: LanguageCodes? + val vendor: String? + val version: String? + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() + + override val stixType = StixType("software") + + override val idContributingProperties: List> = listOf( + SoftwareSco::name, + SoftwareSco::cpe, + SoftwareSco::vendor, + SoftwareSco::version + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: SoftwareSco) { + + } + + } +} + +data class Software( + override val name: String, + override val cpe: String? = null, + override val languages: LanguageCodes? = null, + override val vendor: String? = null, + override val version: String? = null, + override val type: StixType = StixType(SoftwareSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : SoftwareSco { + + init { + SoftwareSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt new file mode 100644 index 0000000..e59549c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt @@ -0,0 +1,62 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import com.stephenott.stix.type.vocab.LanguageCodes +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface UrlSco : StixCyberObservableObject { + + val value: String + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val allowedExtensions: List> = listOf() + + override val stixType = StixType("url") + + override val idContributingProperties: List> = listOf( + UrlSco::value + ) + + override val allowedRelationships: List = listOf( + + ) + + override fun objectValidationRules(obj: UrlSco) { + + } + + } +} + +data class Url( + override val value: String, + override val type: StixType = StixType(UrlSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : UrlSco { + + init { + UrlSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt index a279e21..cb05052 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt @@ -1,33 +1,56 @@ package com.stephenott.stix.objects.core.sco.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionIdContributingProperties -import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.extension.objects.UnixAccountExtensionExt import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import com.stephenott.stix.type.vocab.AccountType +import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface UserAccountSco : StixCyberObservableObject { - companion object: + val userId: String? + val credential: String? + val accountLogin: String? + val accountType: AccountType? + val displayName: String? + val isServiceAccount: StixBoolean? + val isPrivilege: StixBoolean? + val canEscalatePrivs: StixBoolean? + val isDisabled: StixBoolean? + val accountCreated: StixInstant? + val accountExpires: StixInstant? + val credentialLastChanged: StixInstant? + val accountFirstLogin: StixInstant? + val accountLastLogin: StixInstant? + + companion object : CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, - CompanionAllowedRelationships{ + CompanionAllowedRelationships, + CompanionAllowedExtensions { override val stixType = StixType("user-account") override val idContributingProperties: List> = listOf( - + UserAccountSco::accountType, + UserAccountSco::userId, + UserAccountSco::accountLogin ) override val allowedRelationships: List = listOf( ) + override val allowedExtensions: List> = listOf( + UnixAccountExtensionExt::class + ) + override fun objectValidationRules(obj: UserAccountSco) { } @@ -36,6 +59,20 @@ interface UserAccountSco : StixCyberObservableObject { } data class UserAccount( + override val userId: String? = null, + override val credential: String? = null, + override val accountLogin: String? = null, + override val accountType: AccountType? = null, + override val displayName: String? = null, + override val isServiceAccount: StixBoolean? = null, + override val isPrivilege: StixBoolean? = null, + override val canEscalatePrivs: StixBoolean? = null, + override val isDisabled: StixBoolean? = null, + override val accountCreated: StixInstant? = null, + override val accountExpires: StixInstant? = null, + override val credentialLastChanged: StixInstant? = null, + override val accountFirstLogin: StixInstant? = null, + override val accountLastLogin: StixInstant? = null, override val type: StixType = StixType(UserAccountSco.stixType), override val id: StixIdentifier = StixIdentifier(type), override val objectMarkingsRefs: String? = null, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt new file mode 100644 index 0000000..6437192 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt @@ -0,0 +1,82 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sco.extension.objects.UnixAccountExtensionExt +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import com.stephenott.stix.type.vocab.AccountType +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface WindowsRegistryKeySco : StixCyberObservableObject { + + val key: String? + val values: WindowsRegistryValueTypes? + val modifiedTimed: StixInstant? + val creatorUserRef: StixIdentifier? + val numberOfSubkeys: StixInteger? + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val stixType = StixType("windows-registry-key") + + override val idContributingProperties: List> = listOf( + WindowsRegistryKeySco::key, + WindowsRegistryKeySco::values // @TODO ** There is another pattern like this where only 1 value from this prop should be used in the ID. Should look to standardize on this to make the code simpler + ) + + override val allowedRelationships: List = listOf( + + ) + + override val allowedExtensions: List> = listOf( + + ) + + override fun objectValidationRules(obj: WindowsRegistryKeySco) { + require( + listOf(obj.key, obj.values, obj.modifiedTimed, obj.creatorUserRef, obj.numberOfSubkeys) + .any { it != null }, + lazyMessage = { "windows-registry-key requires at least one property must be included." } + ) + + require(obj.creatorUserRef?.type == UserAccountSco.stixType, + lazyMessage = { "creator_user_ref must only reference a user-account SCO" } + ) + } + + } +} + +data class WindowsRegistryKey( + override val key: String? = null, + override val values: WindowsRegistryValueTypes? = null, + override val modifiedTimed: StixInstant? = null, + override val creatorUserRef: StixIdentifier? = null, + override val numberOfSubkeys: StixInteger? = null, + override val type: StixType = StixType(WindowsRegistryKeySco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : WindowsRegistryKeySco { + + init { + WindowsRegistryKeySco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt new file mode 100644 index 0000000..d8d3127 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt @@ -0,0 +1,101 @@ +package com.stephenott.stix.objects.core.sco.objects + +import com.stephenott.stix.common.* +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.extension.ScoExtension +import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 + +interface X509CertificateSco : StixCyberObservableObject { + + val isSelfSigned: StixBoolean? + val hashes: HashesDictionary? + val version: String? + val serialNumber: String? + val signatureAlgorithm: String? + val issuer: String? + val validityNotBefore: StixInstant? + val validityNotAfter: StixInstant? + val subject: String? + val subjectPublicKeyAlgorithm: String? + val subjectPublicKeyModulus: String? + val subjectPublicKeyExponent: StixInteger? + val x509v3Extensions: X509v3ExtensionsTypes? + + companion object : + CompanionStixType, + BusinessRulesValidator, + CompanionIdContributingProperties, + CompanionAllowedRelationships, + CompanionAllowedExtensions { + + override val stixType = StixType("x509-certificate") + + override val idContributingProperties: List> = listOf( + X509CertificateSco::hashes, //@TODO If the ​hashes​ property is present, include only one hash. The selected hash ​SHOULD​ come from this ordered list (based on the following order of preference) [ MD5, SHA-1, SHA-256, SHA-512 ]. + X509CertificateSco::serialNumber + ) + + override val allowedRelationships: List = listOf( + + ) + + override val allowedExtensions: List> = listOf( + + ) + + override fun objectValidationRules(obj: X509CertificateSco) { + require(listOf( //@TODO review against Stix 2 Issues against 182 + obj.isSelfSigned, + obj.hashes, + obj.version, + obj.serialNumber, + obj.signatureAlgorithm, + obj.issuer, + obj.validityNotBefore, + obj.validityNotAfter, + obj.subject, + obj.subjectPublicKeyAlgorithm, + obj.subjectPublicKeyModulus, + obj.subjectPublicKeyExponent, + obj.x509v3Extensions + ).any { it != null }, + lazyMessage = { "X509-Certificate must contain at least one property (other than " }) + } + } +} + +data class X509Certificate( + override val isSelfSigned: StixBoolean? = null, + override val hashes: HashesDictionary? = null, + override val version: String? = null, + override val serialNumber: String? = null, + override val signatureAlgorithm: String? = null, + override val issuer: String? = null, + override val validityNotBefore: StixInstant? = null, + override val validityNotAfter: StixInstant? = null, + override val subject: String? = null, + override val subjectPublicKeyAlgorithm: String? = null, + override val subjectPublicKeyModulus: String? = null, + override val subjectPublicKeyExponent: StixInteger? = null, + override val x509v3Extensions: X509v3ExtensionsTypes? = null, + override val type: StixType = StixType(X509CertificateSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean() +) : X509CertificateSco { + + init { + X509CertificateSco.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/AlternateDataStreams.kt b/src/main/kotlin/com/stephenott/stix/type/AlternateDataStreams.kt new file mode 100644 index 0000000..3c2600a --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/AlternateDataStreams.kt @@ -0,0 +1,18 @@ +package com.stephenott.stix.type + +class AlternateDataStreams(private val streams: List): + List by streams{ + + init { + //@TODO + } +} + +data class AlternateDataStreamType( + val name: String, + val hashes: HashesDictionary, + val size: StixInteger){ + init { + require(size.value >= 0, lazyMessage = {"size must not be negative."}) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/ExifTagsDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/ExifTagsDictionary.kt new file mode 100644 index 0000000..b459112 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/ExifTagsDictionary.kt @@ -0,0 +1,20 @@ +package com.stephenott.stix.type + +class ExifTagsDictionary(private val dictionary: LinkedHashMap): + Map by dictionary { + + init { + //@TODO + + //@TODO Add a JsonCreator constructor for the Map + + } +} + +interface ExifTag{ + val value: Any +} + +data class ExifTagStringValue(override val value: String): ExifTag + +data class ExifTagIntegerValue(override val value: StixInteger): ExifTag \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/Extensions.kt b/src/main/kotlin/com/stephenott/stix/type/Extensions.kt index 76b58bf..6d717d9 100644 --- a/src/main/kotlin/com/stephenott/stix/type/Extensions.kt +++ b/src/main/kotlin/com/stephenott/stix/type/Extensions.kt @@ -2,6 +2,4 @@ package com.stephenott.stix.type import com.stephenott.stix.objects.core.sco.extension.ScoExtension -class Extensions(private val extensions: Set = linkedSetOf()): - Set by extensions { -} \ No newline at end of file +class Extensions(private val dictionary: Set): Set by dictionary {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt index a8796d6..687a3e6 100644 --- a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt +++ b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt @@ -9,6 +9,8 @@ class HashesDictionary(private val immutableDictionary: LinkedHashMap = linkedSetOf( "MD5", "MD6", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", diff --git a/src/main/kotlin/com/stephenott/stix/type/HttpRequestHeaderDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/HttpRequestHeaderDictionary.kt new file mode 100644 index 0000000..9c3959f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/HttpRequestHeaderDictionary.kt @@ -0,0 +1,9 @@ +package com.stephenott.stix.type + +class HttpRequestHeaderDictionary(private val headers: Map>): + Map> by headers { + + init { + //note that this dictionary allows for a value in the List to be a empty non-null string such as "". + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/IpfixDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/IpfixDictionary.kt new file mode 100644 index 0000000..cfc7c7b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/IpfixDictionary.kt @@ -0,0 +1,20 @@ +package com.stephenott.stix.type + +class IpfixDictionary(private val dictionary: LinkedHashMap): + Map by dictionary { + + init { + //@TODO + + //@TODO Add a JsonCreator constructor for the Map + + } +} + +interface IpFixElement{ + val value: Any +} + +data class IpfixElementStringValue(override val value: String): IpFixElement + +data class IpfixElementIntegerValue(override val value: StixInteger): IpFixElement \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/MimePartType.kt b/src/main/kotlin/com/stephenott/stix/type/MimePartTypes.kt similarity index 100% rename from src/main/kotlin/com/stephenott/stix/type/MimePartType.kt rename to src/main/kotlin/com/stephenott/stix/type/MimePartTypes.kt diff --git a/src/main/kotlin/com/stephenott/stix/type/StixFloat.kt b/src/main/kotlin/com/stephenott/stix/type/StixFloat.kt new file mode 100644 index 0000000..7d1376e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixFloat.kt @@ -0,0 +1,3 @@ +package com.stephenott.stix.type + +data class StixFloat (val value: Float) {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixHex.kt b/src/main/kotlin/com/stephenott/stix/type/StixHex.kt new file mode 100644 index 0000000..6343e4f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/StixHex.kt @@ -0,0 +1,7 @@ +package com.stephenott.stix.type + +data class StixHex (val value: String) { + init { + //@TODO add hex validation + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt b/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt index 57f3ab3..211a710 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixInteger.kt @@ -1,7 +1,3 @@ package com.stephenott.stix.type -class StixInteger (val value: Int) { - init { - require(value in 1..999999999) - } -} \ No newline at end of file +data class StixInteger (val value: Int) {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt b/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt index 4c1fde6..260adb9 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixStringList.kt @@ -1,7 +1,9 @@ package com.stephenott.stix.type + +//@TODO refactor to generic class StixStringList(private val list: List): List by list{ init { - list.all { it.isNotEmpty()} + require(list.isNotEmpty(), lazyMessage = {"STIX Spec does not allow empty lists."}) // https://github.com/oasis-tcs/cti-stix2/issues/144#issuecomment-481370524 } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/WindowsPeOptionalHeaderType.kt b/src/main/kotlin/com/stephenott/stix/type/WindowsPeOptionalHeaderType.kt new file mode 100644 index 0000000..bd8a950 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/WindowsPeOptionalHeaderType.kt @@ -0,0 +1,60 @@ +package com.stephenott.stix.type + +import kotlin.reflect.KVisibility +import kotlin.reflect.full.* + + +data class WindowsPeOptionalHeaderType( + val magicHex: StixHex? = null, + val majorLinkerVersion: StixInteger? = null, + val minorLinkerVersion: StixInteger? = null, + val sizeOfCode: StixInteger? = null, + val sizeOfInitializedData: StixInteger? = null, + val sizeOfUninitializedData: StixInteger? = null, + val addressOfEntryPoint: StixInteger? = null, + val baseOfCode: StixInteger? = null, + val baseOfData: StixInteger? = null, + val imageBase: StixInteger? = null, + val sectionAlignment: StixInteger? = null, + val fileAlignment: StixInteger? = null, + val majorOsVersion: StixInteger? = null, + val minorOsVersion: StixInteger? = null, + val majorImageVersion: StixInteger? = null, + val minorImageVersion: StixInteger? = null, + val majorSubsystemVersion: StixInteger? = null, + val minorSubsystemVersion: StixInteger? = null, + val win32VersionValueHex: StixHex? = null, + val sizeOfImage: StixInteger? = null, + val sizeOfHeaders: StixInteger? = null, + val checksumHex: StixHex? = null, + val subsystemHex: StixHex? = null, + val dllCharacteristics: StixHex? = null, + val sizeOfStackReserve: StixInteger? = null, + val sizeOfStackCommit: StixInteger? = null, + val sizeOfHeapReserve: StixInteger? = null, + val sizeOfHeapCommit: StixInteger? = null, + val loaderFlagsHex: StixHex? = null, + val numberOfRvaAndSizes: StixInteger? = null, + val hashes: HashesDictionary? = null + +) { + + init { + + require(this::class.memberProperties + .filter {it.visibility == KVisibility.PUBLIC} + .any { it.getter.call() != null }, + lazyMessage = {"At least one property must be provided/be not null."}) + + require(sizeOfCode?.value!! >= 0) + require(sizeOfInitializedData?.value!! >= 0) + require(sizeOfUninitializedData?.value!! >= 0) + require(sizeOfImage?.value!! >= 0) + require(sizeOfHeaders?.value!! >= 0) + require(sizeOfStackReserve?.value!! >= 0) + require(sizeOfStackCommit?.value!! >= 0) + require(sizeOfHeapReserve?.value!! >= 0) + require(sizeOfHeapCommit?.value!! >= 0) + } + + } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/WindowsPeSectionType.kt b/src/main/kotlin/com/stephenott/stix/type/WindowsPeSectionType.kt new file mode 100644 index 0000000..78ed133 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/WindowsPeSectionType.kt @@ -0,0 +1,18 @@ +package com.stephenott.stix.type + +class WindowsPeSectionTypes(private val types: List): List by types{ + +} + +data class WindowsPeSectionType( + val name: String, + val size: StixInteger?, + val entropy: StixFloat?, + val hashes: HashesDictionary? +) { + + init { + require(size?.value!! >= 0) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/WindowsRegistryValueType.kt b/src/main/kotlin/com/stephenott/stix/type/WindowsRegistryValueType.kt new file mode 100644 index 0000000..d7e3680 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/WindowsRegistryValueType.kt @@ -0,0 +1,21 @@ +package com.stephenott.stix.type + +import com.stephenott.stix.type.vocab.WindowsRegistryDataTypeEnum + +class WindowsRegistryValueTypes(private val types: List): List by types{ + + init { + //@TODO + } + +} + +data class WindowsRegistryValueType( + val name: String?, + val data: String?, + val dataType: WindowsRegistryDataTypeEnum? +) { + init { + require(listOf(name, data, dataType).any { it != null }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt b/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt new file mode 100644 index 0000000..c4e6215 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt @@ -0,0 +1,40 @@ +package com.stephenott.stix.type + +import com.stephenott.stix.objects.core.sco.objects.ArtifactSco +import com.stephenott.stix.objects.core.sco.objects.FileSco +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +class X509v3ExtensionsTypes(private val types: List): List by types{ + init { + require(types.isNotEmpty()) + } +} + +data class X509v3ExtensionsType( + val basicConstraints: String?, + val nameConstraints: String?, + val policyConstraints: String?, + val keyUsage: String?, + val extendedKeyUsage: String?, + val subjectKeyIdentifier: String?, + val authorityKeyIdentifier: String?, + val subjectAlternativeName: String?, + val issuerAlternativeName: String?, + val subjectDirectoryAttributes: String?, + val crlDistributionPoints: String?, + val inhibitAnyPolicy: String?, + val privateKeyUsagePeriodNotBefore: StixInstant?, + val privateKeyUsagePeriodNotAfter: StixInstant?, + val certificatePolicies: String?, + val policyMappings: String? +) { + + init { + require(this::class.memberProperties + .filter {it.visibility == KVisibility.PUBLIC} + .any { it.getter.call() != null }, + lazyMessage = {"At least one property must be provided/not null."}) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt new file mode 100644 index 0000000..e49cc4c --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt @@ -0,0 +1,24 @@ +package com.stephenott.stix.type.vocab + +class AccountType(private val type: String) : OpenVocab, CharSequence by type { + + companion object { + + val vocabName = "account-type-ov" + + var vocab: LinkedHashSet = linkedSetOf( + "facebook", "ldap", "nis", + "openid", "radius", "skype", + "tacacs", "twitter", "unix", + "windows-local", "windows-domain" + ) + set(value) { + require(value.isNotEmpty()) + field = value + } + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt new file mode 100644 index 0000000..708fa6b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt @@ -0,0 +1,24 @@ +package com.stephenott.stix.type.vocab + +class LanguageCodes(private val codes: LinkedHashSet = linkedSetOf()) : + Set by codes { + + init { + //@TODO + } +} + +data class LanguageCode(private val code: String) : ClosedVocab, CharSequence by code { + + init { + require(LanguageCode.vocab.contains(code)) + } + + companion object { + val vocabName = "iso-639-2-language-codes" + + val vocab: LinkedHashSet = linkedSetOf( + "en" //@TODO add rest of language codes + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt new file mode 100644 index 0000000..987ce4b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt @@ -0,0 +1,18 @@ +package com.stephenott.stix.type.vocab + +class NetworkSocketAddressFamilyEnum(private val addressFamily: String) : ClosedVocab, CharSequence by addressFamily { + + companion object { + val vocabName = "network-socket-address-family-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "AF_UNSPEC", "AF_INET", "AF_IPX", + "AF_APPLETALK", "AF_NETBIOS", "AF_INET6", + "AF_IRDA", "AF_BTH" + ) + } + + init { + require(this.addressFamily in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt new file mode 100644 index 0000000..ddde6f0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt @@ -0,0 +1,17 @@ +package com.stephenott.stix.type.vocab + +class NetworkSocketTypeEnum(private val type: String) : ClosedVocab, CharSequence by type { + + companion object { + val vocabName = "network-socket-type-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "SOCK_STREAM", "AF_ISOCK_DGRAMNET", "SOCK_RAW", + "SOCK_RDM", "SOCK_SEQPACKET" + ) + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt new file mode 100644 index 0000000..b596077 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt @@ -0,0 +1,17 @@ +package com.stephenott.stix.type.vocab + +class WindowsIntegrityLevelEnum(private val level: String) : ClosedVocab, CharSequence by level { + + companion object { + val vocabName = "windows-integrity-level-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "low", "medium", "high", + "system" + ) + } + + init { + require(this.level in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt new file mode 100644 index 0000000..6d474a3 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt @@ -0,0 +1,16 @@ +package com.stephenott.stix.type.vocab + +class WindowsPebinaryTypeOv(private val type: String) : ClosedVocab, CharSequence by type { + + companion object { + val vocabName = "windows-pebinary-type-ov" + + val vocab: LinkedHashSet = linkedSetOf( + "dll", "exe", "sys" + ) + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt new file mode 100644 index 0000000..7d44ef8 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt @@ -0,0 +1,20 @@ +package com.stephenott.stix.type.vocab + +class WindowsRegistryDataTypeEnum(private val datatype: String) : ClosedVocab, CharSequence by datatype { + + companion object { + val vocabName = "windows-registry-datatype-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", + "REG_BINARY", "REG_DWORD", "REG_DWORD_BIG_ENDIAN", + "REG_DWORD_LITTLE_ENDIAN", "REG_LINK", "REG_MULTI_SZ", + "REG_RESOURCE_LIST", "REG_FULL_RESOURCE_DESCRIPTION", "REG_RESOURCE_REQUIREMENTS_LIST", + "REG_QWORD", "REG_INVALID_TYPE" + ) + } + + init { + require(this.datatype in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt new file mode 100644 index 0000000..19f1c13 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt @@ -0,0 +1,17 @@ +package com.stephenott.stix.type.vocab + +class WindowsServiceStartTypeEnum(private val type: String) : ClosedVocab, CharSequence by type { + + companion object { + val vocabName = "windows-service-start-type-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "SERVICE_AUTO_START", "SERVICE_BOOT_START", "SERVICE_DEMAND_START", + "SERVICE_DISABLED", "SERVICE_SYSTEM_ALERT" + ) + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt new file mode 100644 index 0000000..e32981f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt @@ -0,0 +1,18 @@ +package com.stephenott.stix.type.vocab + +class WindowsServiceStatusEnum(private val status: String) : ClosedVocab, CharSequence by status { + + companion object { + val vocabName = "windows-service-status-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "SERVICE_CONTINUE_PENDING", "SERVICE_PAUSE_PENDING", "SERVICE_PAUSED", + "SERVICE_RUNNING", "SERVICE_START_PENDING", "SERVICE_STOP_PENDING", + "SERVICE_STOPPED" + ) + } + + init { + require(this.status in vocab) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt new file mode 100644 index 0000000..12f0e01 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt @@ -0,0 +1,18 @@ +package com.stephenott.stix.type.vocab + +class WindowsServiceTypeEnum(private val type: String) : ClosedVocab, CharSequence by type { + + companion object { + val vocabName = "windows-service-type-enum" + + val vocab: LinkedHashSet = linkedSetOf( + "SERVICE_KERNEL_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER", "SERVICE_WIN32_OWN_PROCESS", + "SERVICE_WIN32_SHARE_PROCESS" + + ) + } + + init { + require(this.type in vocab) + } +} \ No newline at end of file From 4377dda0ee10e4968cb2815b73bba3456632a5cc Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 21 Oct 2019 07:12:36 -0400 Subject: [PATCH 06/21] add validations for SDOs and add validation for rel rules --- .../stix/common/StixObjectRegistry.kt | 26 +++++++++- .../objects/core/sdo/objects/AttackPattern.kt | 22 +++++++-- .../stix/objects/core/sdo/objects/Campaign.kt | 22 +++++++-- .../core/sdo/objects/CourseOfAction.kt | 29 +++++++++-- .../stix/objects/core/sdo/objects/Grouping.kt | 20 ++++++-- .../stix/objects/core/sdo/objects/Identity.kt | 20 ++++++-- .../objects/core/sdo/objects/Indicator.kt | 33 ++++++++++--- .../core/sdo/objects/Infrastructure.kt | 44 +++++++++++------ .../objects/core/sdo/objects/IntrusionSet.kt | 29 +++++++---- .../stix/objects/core/sdo/objects/Location.kt | 27 ++++++++-- .../stix/objects/core/sdo/objects/Malware.kt | 49 +++++++++++-------- .../core/sdo/objects/MalwareAnalysis.kt | 31 ++++++++++-- .../stix/objects/core/sdo/objects/Note.kt | 23 +++++++-- .../objects/core/sdo/objects/ObservedData.kt | 30 ++++++++++-- .../stix/objects/core/sdo/objects/Opinion.kt | 23 +++++++-- .../stix/objects/core/sdo/objects/Report.kt | 22 +++++++-- .../objects/core/sdo/objects/ThreatActor.kt | 30 ++++++++---- .../stix/objects/core/sdo/objects/Tool.kt | 25 +++++++--- .../objects/core/sdo/objects/Vulnerability.kt | 20 ++++++-- .../objects/core/sro/objects/Relationship.kt | 33 ++++++++++--- .../stix/type/vocab/IndicatorTypeOv.kt | 6 ++- .../stix/type/vocab/KillChainPhases.kt | 8 ++- 22 files changed, 454 insertions(+), 118 deletions(-) diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt index a0f27ee..f9c8869 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -1,13 +1,14 @@ package com.stephenott.stix.common import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sco.objects.* import com.stephenott.stix.objects.core.sdo.objects.* import com.stephenott.stix.type.StixType import kotlin.reflect.KClass object StixObjectRegistry { - var registry: Map> = mutableMapOf( + var sdoRegistry: Map> = mutableMapOf( Pair(AttackPatternSdo.stixType, AttackPattern::class), Pair(CampaignSdo.stixType, Campaign::class), Pair(CourseOfActionSdo.stixType, CourseOfAction::class), @@ -26,4 +27,27 @@ object StixObjectRegistry { Pair(ThreatActorSdo.stixType, ThreatActor::class), Pair(VulnerabilitySdo.stixType, Vulnerability::class) ) + + var scoRegistry: Map> = mutableMapOf( + Pair(ArtifactSco.stixType, Artifact::class), + Pair(AutonomousSystemSco.stixType, AutonomousSystem::class), + Pair(DirectorySco.stixType, Directory::class), + Pair(DomainNameSco.stixType, DomainName::class), + Pair(EmailAddressSco.stixType, EmailAddress::class), + Pair(EmailMessageSco.stixType, EmailMessage::class), + Pair(FileSco.stixType, File::class), + Pair(IPv4AddressSco.stixType, IPv4Address::class), + Pair(IPv6AddressSco.stixType, IPv6Address::class), + Pair(MacAddressSco.stixType, MacAddress::class), + Pair(MutexSco.stixType, Mutex::class), + Pair(NetworkTrafficSco.stixType, NetworkTraffic::class), + Pair(ProcessSco.stixType, Process::class), + Pair(SoftwareSco.stixType, Software::class), + Pair(UrlSco.stixType, Url::class), + Pair(UserAccountSco.stixType, UserAccount::class), + Pair(WindowsRegistryKeySco.stixType, WindowsRegistryKey::class), + Pair(X509CertificateSco.stixType, X509Certificate::class) + ) + + var registry: Map> = sdoRegistry + scoRegistry } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt index a66e9b7..7453845 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -11,10 +14,17 @@ interface AttackPatternSdo : StixDomainObject { val description: String? val killChainPhases: KillChainPhases? //@TODO - companion object { - val stixType = StixType("attack-pattern") + companion object: CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("attack-pattern") + + override fun objectValidationRules(obj: AttackPatternSdo) { + + } + + override val allowedRelationships: List = listOf( AllowedRelationship( AttackPatternSdo::class, RelationshipType("delivers"), @@ -48,6 +58,7 @@ interface AttackPatternSdo : StixDomainObject { ToolSdo::class ) ) + } } @@ -72,8 +83,11 @@ data class AttackPattern( ) : AttackPatternSdo { + init { + AttackPatternSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return AttackPatternSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } - } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt index 01da79d..0ca3867 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -13,10 +16,19 @@ interface CampaignSdo : StixDomainObject { val lastSeen: StixInstant? val objective: String? - companion object{ - val stixType = StixType("campaign") + companion object: CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override fun objectValidationRules(obj: CampaignSdo) { + if (obj.firstSeen != null){ + require(obj.lastSeen?.instant!!.isAfter(obj.firstSeen!!.instant)) + } + } + + override val stixType = StixType("campaign") + + override val allowedRelationships: List = listOf( AllowedRelationship( CampaignSdo::class, RelationshipType("attributed-to"), @@ -104,6 +116,10 @@ data class Campaign override val lang: StixLang? = null ) : CampaignSdo { + init { + CampaignSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return CampaignSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt index 0702062..ff0d079 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -9,15 +12,29 @@ import com.stephenott.stix.type.vocab.CourseOfActionTypeOv interface CourseOfActionSdo : StixDomainObject { val name: String val description: String? - val actionType: CourseOfActionTypeOv? + val actionType: CourseOfActionTypeOv? //@TODO add option to override type value as the spec says it's a "should" val osExecutionEnvs: OsExecutionEnvs? val actionBin: StixBinary? val actionReference: ExternalReference? - companion object { - val stixType = StixType("course-of-action") + companion object: CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("course-of-action") + + override fun objectValidationRules(obj: CourseOfActionSdo) { + if (obj.actionReference != null){ + require(obj.actionBin == null, + lazyMessage = {"action_bin must not be present if action_reference is provided."}) + } + if (obj.actionBin != null){ + require(obj.actionReference == null, + lazyMessage = {"action_reference must not be present if action_bin is provided."}) + } + } + + override val allowedRelationships: List = listOf( AllowedRelationship( CourseOfActionSdo::class, RelationshipType("investigates"), @@ -88,6 +105,10 @@ data class CourseOfAction( override val lang: StixLang? = null ) : CourseOfActionSdo { + init { + CourseOfActionSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return CourseOfActionSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt index a853590..7ae060e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -12,10 +15,17 @@ interface GroupingSdo : StixDomainObject { val context: GroupingContextOv val objectRefs: StixIdentifiers - companion object{ - val stixType = StixType("grouping") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf() + override val stixType = StixType("grouping") + + override fun objectValidationRules(obj: GroupingSdo) { + + } + + override val allowedRelationships: List = listOf() } } @@ -40,6 +50,10 @@ data class Grouping( ) : GroupingSdo { + init { + GroupingSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return GroupingSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt index 596a188..47f60f9 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -16,10 +19,17 @@ interface IdentitySdo : StixDomainObject { val sectors: IndustrySectors? val contactInformation: String? - companion object{ - val stixType = StixType("identity") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("identity") + + override fun objectValidationRules(obj: IdentitySdo) { + + } + + override val allowedRelationships: List = listOf( AllowedRelationship( IdentitySdo::class, RelationshipType("located-at"), @@ -51,6 +61,10 @@ data class Identity( override val lang: StixLang? = null ) : IdentitySdo { + init { + IdentitySdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return IdentitySdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index fcc42fb..c410dba 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -1,10 +1,14 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.IndicatorTypes +import com.stephenott.stix.type.vocab.KillChainPhases import com.stephenott.stix.type.vocab.PatternType interface IndicatorSdo : StixDomainObject { @@ -14,11 +18,22 @@ interface IndicatorSdo : StixDomainObject { val pattern: StixPattern val patternType: PatternType val patternVersion: String? + val validFrom: StixInstant + val validUntil: StixInstant? + val killChainPhases: KillChainPhases? - companion object{ - val stixType = StixType("indicator") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("indicator") + + override fun objectValidationRules(obj: IndicatorSdo) { + require(obj.validUntil?.instant!!.isAfter(obj.validFrom.instant), + lazyMessage = {"valid_until must come after valid_from."}) + } + + override val allowedRelationships: List = listOf( AllowedRelationship( IndicatorSdo::class, RelationshipType("indicates"), @@ -71,6 +86,9 @@ data class Indicator( override val pattern: StixPattern, override val patternType: PatternType, override val patternVersion: String? = null, + override val validFrom: StixInstant, + override val validUntil: StixInstant?, + override val killChainPhases: KillChainPhases?, override val type: StixType = IndicatorSdo.stixType, override val id: StixIdentifier = StixIdentifier(type), override val createdByRef: String? = null, @@ -86,12 +104,11 @@ data class Indicator( override val lang: StixLang? = null ) : IndicatorSdo { - override fun allowedRelationships(): List { - return IndicatorSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships - } - init { - require(indicatorTypes.size >= 1) + IndicatorSdo.objectValidationRules(this) } + override fun allowedRelationships(): List { + return IndicatorSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships + } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index 13b784b..04ff675 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -1,5 +1,13 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject +import com.stephenott.stix.objects.core.sco.objects.DomainNameSco +import com.stephenott.stix.objects.core.sco.objects.IPv4AddressSco +import com.stephenott.stix.objects.core.sco.objects.IPv6AddressSco +import com.stephenott.stix.objects.core.sco.objects.UrlSco import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -16,10 +24,19 @@ interface InfrastructureSdo : StixDomainObject { val firstSeen: StixInstant? val lastSeen: StixInstant? - companion object{ - val stixType = StixType("infrastructure") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("infrastructure") + + override fun objectValidationRules(obj: InfrastructureSdo) { + if (obj.firstSeen != null && obj.lastSeen != null){ + require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant) + } + } + + override val allowedRelationships: List = listOf( AllowedRelationship( InfrastructureSdo::class, RelationshipType("communicates-with"), @@ -28,24 +45,23 @@ interface InfrastructureSdo : StixDomainObject { AllowedRelationship( InfrastructureSdo::class, RelationshipType("communicates-with"), - CampaignSdo::class + IPv4AddressSco::class ), AllowedRelationship( InfrastructureSdo::class, RelationshipType("communicates-with"), - InfrastructureSdo::class + IPv6AddressSco::class ), AllowedRelationship( InfrastructureSdo::class, RelationshipType("communicates-with"), - IntrusionSetSdo::class + DomainNameSco::class ), AllowedRelationship( InfrastructureSdo::class, RelationshipType("communicates-with"), - MalwareSdo::class + UrlSco::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("consists-of"), @@ -59,9 +75,8 @@ interface InfrastructureSdo : StixDomainObject { AllowedRelationship( InfrastructureSdo::class, RelationshipType("consists-of"), - ObservedDataSdo::class + StixCyberObservableObject::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("controls"), @@ -72,19 +87,16 @@ interface InfrastructureSdo : StixDomainObject { RelationshipType("controls"), MalwareSdo::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("delivers"), MalwareSdo::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("has"), VulnerabilitySdo::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("hosts"), @@ -95,13 +107,11 @@ interface InfrastructureSdo : StixDomainObject { RelationshipType("hosts"), MalwareSdo::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("located-at"), LocationSdo::class ), - AllowedRelationship( InfrastructureSdo::class, RelationshipType("uses"), @@ -136,6 +146,10 @@ data class Infrastructure( ) : InfrastructureSdo { + init { + InfrastructureSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return InfrastructureSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt index 61910c9..a7e8cc5 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -17,40 +20,45 @@ interface IntrusionSetSdo : StixDomainObject { val primaryMotivation: AttackMotivationOv? val secondaryMotivations: AttackMotivations? - companion object{ - val stixType = StixType("intrusion-set") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("intrusion-set") + + override fun objectValidationRules(obj: IntrusionSetSdo) { + if (obj.firstSeen != null && obj.lastSeen != null){ + require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant, + lazyMessage = {"last_seen must be equal or greater than first_seen."}) + } + } + + override val allowedRelationships: List = listOf( AllowedRelationship( IntrusionSetSdo::class, RelationshipType("attributed-to"), ThreatActorSdo::class ), - AllowedRelationship( IntrusionSetSdo::class, RelationshipType("compromises"), InfrastructureSdo::class ), - AllowedRelationship( IntrusionSetSdo::class, RelationshipType("hosts"), InfrastructureSdo::class ), - AllowedRelationship( IntrusionSetSdo::class, RelationshipType("owns"), InfrastructureSdo::class ), - AllowedRelationship( IntrusionSetSdo::class, RelationshipType("originates-from"), LocationSdo::class ), - AllowedRelationship( IntrusionSetSdo::class, RelationshipType("targets"), @@ -66,7 +74,6 @@ interface IntrusionSetSdo : StixDomainObject { RelationshipType("targets"), VulnerabilitySdo::class ), - AllowedRelationship( IntrusionSetSdo::class, RelationshipType("uses"), @@ -117,6 +124,10 @@ data class IntrusionSet( ) : IntrusionSetSdo { + init { + IntrusionSetSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return IntrusionSetSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt index 4adc6ec..35ba5c3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -20,10 +23,24 @@ interface LocationSdo : StixDomainObject { val streetAddress: StreetAddress? val postalCode: PostalCode? - companion object{ - val stixType = StixType("location") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships:List = listOf() + override val stixType = StixType("location") + + override fun objectValidationRules(obj: LocationSdo) { + if (obj.latitude != null) require(obj.longitude != null, + lazyMessage = { "longitude must be provided when latitude is used." }) + if (obj.longitude != null) require(obj.latitude != null, + lazyMessage = { "latitude must be provided when longitude is used." }) + if (obj.precision != null) require(obj.latitude != null && obj.longitude != null, + lazyMessage = { "latitude and longitude must be provided when precision is used." }) + } + + override val allowedRelationships: List = listOf( + + ) } } @@ -54,6 +71,10 @@ data class Location( ) : LocationSdo { + init { + LocationSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return LocationSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt index 99bee1d..4affbba 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt @@ -1,5 +1,9 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.objects.* import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -21,10 +25,20 @@ interface MalwareSdo : StixDomainObject { val capabilities: MalwareCapabilities? val sampleRefs: StixIdentifiers? - companion object{ - val stixType = StixType("malware") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("malware") + + override fun objectValidationRules(obj: MalwareSdo) { + if (obj.firstSeen != null && obj.lastSeen != null) { + require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant, + lazyMessage = { "last_seen must greater than or equal to first_seen." }) + } + } + + override val allowedRelationships: List = listOf( //@TODO convert allowedrelationship to a Kotlin DSL AllowedRelationship( MalwareSdo::class, @@ -36,46 +50,41 @@ interface MalwareSdo : StixDomainObject { RelationshipType("authored-by"), IntrusionSetSdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("beacons-to"), InfrastructureSdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("exfiltrates-to"), InfrastructureSdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("communicates-with"), - ThreatActorSdo::class + IPv4AddressSco::class ), AllowedRelationship( MalwareSdo::class, RelationshipType("communicates-with"), - ThreatActorSdo::class + IPv6AddressSco::class ), AllowedRelationship( MalwareSdo::class, RelationshipType("communicates-with"), - ThreatActorSdo::class + DomainNameSco::class ), AllowedRelationship( MalwareSdo::class, RelationshipType("communicates-with"), - ThreatActorSdo::class + UrlSco::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("controls"), MalwareSdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("downloads"), @@ -89,9 +98,8 @@ interface MalwareSdo : StixDomainObject { AllowedRelationship( MalwareSdo::class, RelationshipType("downloads"), - ThreatActorSdo::class + FileSco::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("drops"), @@ -105,21 +113,18 @@ interface MalwareSdo : StixDomainObject { AllowedRelationship( MalwareSdo::class, RelationshipType("drops"), - ThreatActorSdo::class + FileSco::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("exploits"), VulnerabilitySdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("originates-from"), LocationSdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("targets"), @@ -140,7 +145,6 @@ interface MalwareSdo : StixDomainObject { RelationshipType("targets"), VulnerabilitySdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("uses"), @@ -161,7 +165,6 @@ interface MalwareSdo : StixDomainObject { RelationshipType("uses"), ToolSdo::class ), - AllowedRelationship( MalwareSdo::class, RelationshipType("variant-of"), @@ -169,7 +172,6 @@ interface MalwareSdo : StixDomainObject { ) ) } - } data class Malware @@ -202,6 +204,11 @@ data class Malware override val lang: StixLang? = null ) : MalwareSdo { + + init { + MalwareSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return MalwareSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt index 0062cb6..02dcf55 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt @@ -1,5 +1,9 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.objects.core.sco.objects.SoftwareSco import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -22,10 +26,26 @@ interface MalwareAnalysisSdo : StixDomainObject { val avResult: MalwareAvResult? val analysisScoRefs: StixIdentifiers? - companion object{ - val stixType = StixType("malware-analysis") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("malware-analysis") + + override fun objectValidationRules(obj: MalwareAnalysisSdo) { + //@TODO Product Name Validation enhancement: The name of the analysis engine or product that was used. Product names ​SHOULD​ be all lowercase with words separated by a dash "-". For cases where the name of a product cannot be specified, a value of "anonymized" MUST ​be used. + + require(obj.hostVmRef?.type == SoftwareSco.stixType, + lazyMessage = {"host_vm_ref must only reference a software SCO."}) + + require(obj.operatingSystemRef?.type == SoftwareSco.stixType, + lazyMessage = {"operating_system_ref must only reference a software SCO."}) + + require(obj.installedSystemRefs?.all { it.type == SoftwareSco.stixType }!!, + lazyMessage = {"installed_system_refs must only have values that reference a software SCO."}) + } + + override val allowedRelationships: List = listOf( AllowedRelationship( MalwareAnalysisSdo::class, RelationshipType("characterizes"), @@ -81,6 +101,11 @@ data class MalwareAnalysis override val lang: StixLang? = null ) : MalwareAnalysisSdo { + + init { + MalwareAnalysisSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return MalwareAnalysisSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt index c06e1db..e06909c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -11,10 +14,19 @@ interface NoteSdo : StixDomainObject { val authors: StixStringList? val objectRefs: StixIdentifiers - companion object{ - val stixType = StixType("note") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf() + override val stixType = StixType("note") + + override fun objectValidationRules(obj: NoteSdo) { + + } + + override val allowedRelationships: List = listOf( + + ) } } @@ -39,6 +51,11 @@ data class Note( override val lang: StixLang? = null ) : NoteSdo { + + init { + NoteSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return NoteSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt index de14653..5ebed15 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt @@ -1,5 +1,10 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -11,10 +16,24 @@ interface ObservedDataSdo : StixDomainObject { val numberObserved: StixInteger val objectRefs: StixIdentifiers - companion object{ - val stixType = StixType("observed-data") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf() + override val stixType = StixType("observed-data") + + override fun objectValidationRules(obj: ObservedDataSdo) { + require(obj.lastObserved.instant >= obj.firstObserved.instant, + lazyMessage = { "last_observed must be greater than or equal to first_observed." }) + + require(obj.numberObserved.value in 1..999999999, + lazyMessage = { "number_observed must be between 1 and 999,999,999." }) + + require(obj.objectRefs.any { it.type in StixObjectRegistry.scoRegistry.keys }, + lazyMessage = { "object_refs must contain at least one SCO." }) + } + + override val allowedRelationships: List = listOf() } } @@ -38,6 +57,11 @@ data class ObservedData( override val lang: StixLang? = null ) : ObservedDataSdo { + + init { + ObservedDataSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return ObservedDataSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt index 3c29a82..8b56a69 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -12,10 +15,19 @@ interface OpinionSdo : StixDomainObject { val opinion: OpinionEnum val objectRefs: StixIdentifiers - companion object{ - val stixType = StixType("opinion") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf() + override val stixType = StixType("opinion") + + override fun objectValidationRules(obj: OpinionSdo) { + + } + + override val allowedRelationships: List = listOf( + + ) } } @@ -39,6 +51,11 @@ data class Opinion( override val lang: StixLang? = null ) : OpinionSdo { + + init { + OpinionSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return OpinionSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt index bddfb29..a40f979 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -13,10 +16,19 @@ interface ReportSdo : StixDomainObject { val published: StixInstant val objectRefs: StixIdentifiers - companion object{ - val stixType = StixType("report") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf() + override val stixType = StixType("report") + + override fun objectValidationRules(obj: ReportSdo) { + + } + + override val allowedRelationships: List = listOf( + + ) } } @@ -42,6 +54,10 @@ data class Report( ) : ReportSdo { + init { + ReportSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return ReportSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt index ec8062f..6cd55aa 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -21,46 +24,50 @@ interface ThreatActorSdo : StixDomainObject { val secondaryMotivation: AttackMotivationOv? val personalMotivations: AttackMotivations? - companion object{ - val stixType = StixType("threat-actor") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("threat-actor") + + override fun objectValidationRules(obj: ThreatActorSdo) { + if (obj.firstSeen != null && obj.lastSeen != null){ + require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant, + lazyMessage = {"last_seen must be greater than or equal to first_Seen."}) + } + } + + override val allowedRelationships: List = listOf( AllowedRelationship( ThreatActorSdo::class, RelationshipType("attributed-to"), IdentitySdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("compromises"), InfrastructureSdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("hosts"), InfrastructureSdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("owns"), InfrastructureSdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("impersonates"), IdentitySdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("located-at"), LocationSdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("targets"), @@ -76,7 +83,6 @@ interface ThreatActorSdo : StixDomainObject { RelationshipType("targets"), VulnerabilitySdo::class ), - AllowedRelationship( ThreatActorSdo::class, RelationshipType("uses"), @@ -131,6 +137,10 @@ data class ThreatActor( ) : ThreatActorSdo { + init { + ThreatActorSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return ThreatActorSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index 16c04aa..28e11ca 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -15,28 +18,32 @@ interface ToolSdo : StixDomainObject { val killChainPhases: KillChainPhases? val toolVersion: String? - companion object{ - val stixType = StixType("tool") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf( + override val stixType = StixType("tool") + + override fun objectValidationRules(obj: ToolSdo) { + + } + + override val allowedRelationships: List = listOf( AllowedRelationship( ToolSdo::class, RelationshipType("delivers"), MalwareSdo::class ), - AllowedRelationship( ToolSdo::class, RelationshipType("drops"), MalwareSdo::class ), - AllowedRelationship( ToolSdo::class, RelationshipType("has"), VulnerabilitySdo::class ), - AllowedRelationship( ToolSdo::class, RelationshipType("targets"), @@ -57,7 +64,6 @@ interface ToolSdo : StixDomainObject { RelationshipType("targets"), VulnerabilitySdo::class ), - AllowedRelationship( ToolSdo::class, RelationshipType("uses"), @@ -89,6 +95,11 @@ data class Tool( override val lang: StixLang? = null ) : ToolSdo { + + init { + ToolSdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return ToolSdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt index c5843bb..8212d52 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt @@ -1,5 +1,8 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -9,10 +12,17 @@ interface VulnerabilitySdo : StixDomainObject { val name: String val description: String? - companion object{ - val stixType = StixType("vulnerability") + companion object : CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { - val allowedRelationships: List = listOf() + override val stixType = StixType("vulnerability") + + override fun objectValidationRules(obj: VulnerabilitySdo) { + + } + + override val allowedRelationships: List = listOf() } } @@ -35,6 +45,10 @@ data class Vulnerability( ) : VulnerabilitySdo { + init { + VulnerabilitySdo.objectValidationRules(this) + } + override fun allowedRelationships(): List { return VulnerabilitySdo.allowedRelationships + RelationshipSro.allowedCommonRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index 6a4218f..dca89c6 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -24,12 +24,20 @@ interface RelationshipSro : StixRelationshipObject { AllowedRelationship( StixObject::class, RelationshipType("duplicate-of"), - StixObject::class + StixObject::class, + RelationshipRule { from, type, to -> { + require(from == to, lazyMessage = {"source and target must be same type"}) + } + } ), AllowedRelationship( StixObject::class, RelationshipType("derived-from"), - StixObject::class + StixObject::class, + RelationshipRule { from, type, to -> { + require(from == to, lazyMessage = {"source and target must be same type"}) + } + } ), AllowedRelationship( StixObject::class, @@ -40,10 +48,16 @@ interface RelationshipSro : StixRelationshipObject { } } +data class RelationshipRule( + val rule: (from: KClass, type: RelationshipType, to: KClass) -> Unit +) {} + + data class AllowedRelationship( val from: KClass, val type: RelationshipType, - val to: KClass + val to: KClass, + val rule: RelationshipRule? = null ) {} data class Relationship( @@ -108,14 +122,16 @@ data class Relationship( val targetClass: KClass = StixObjectRegistry.registry[targetRef.type] ?: throw IllegalStateException("Unable to find targetRef in Object Registry") + //@TODO add support for x- custom objects val allowedRelationships: List = StixObjectRelationshipRegistry - .registry.filter { sourceClass.isSubclassOf(it.from) && + .registry.filter { + sourceClass.isSubclassOf(it.from) && it.type == this.relationshipType && targetClass.isSubclassOf(it.to) - } + } - if (allowedRelationships.size > 1){ + if (allowedRelationships.size > 1) { println("Duplicate relationships found: $allowedRelationships") //@TODO add some logging for a warning to indicate multiple duplication objects are registered } @@ -123,6 +139,11 @@ data class Relationship( require(allowedRelationships.isNotEmpty(), lazyMessage = { "${this.id} is not a valid relationship for a ${this.sourceRef.type}" }) + + //@TODO To a deeper test of this functionality + if (allowedRelationships[0].rule != null) { + allowedRelationships[0].rule?.rule!!(sourceClass, relationshipType, targetClass) + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt index 7a2de26..bef0cdf 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt @@ -1,7 +1,11 @@ package com.stephenott.stix.type.vocab -class IndicatorTypes(private val types: LinkedHashSet = linkedSetOf()) : +class IndicatorTypes(private val types: LinkedHashSet) : Set by types { + + init { + require(types.size > 0) + } } class IndicatorType(private val type: String) : OpenVocab, CharSequence by type { diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt index a1061d1..80d41c5 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt @@ -1,7 +1,11 @@ package com.stephenott.stix.type.vocab -class KillChainPhases(private val phases: LinkedHashSet) : - Set by phases {} +class KillChainPhases(private val phases: LinkedHashSet) : Set by phases { + + init { + require(phases.size >0) + } +} data class KillChainPhase(val killChainName: String, val phaseName: String) {} From ad2a6c2d6f1498137d611bd4e886e19f9e03b2a2 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 21 Oct 2019 21:22:11 -0400 Subject: [PATCH 07/21] Add SightingSdo --- .../stix/objects/core/sro/objects/Sighting.kt | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt index b31711c..864da4c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -1,5 +1,78 @@ package com.stephenott.stix.objects.core.sro.objects -class Sighting{ +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.objects.core.sdo.objects.IdentitySdo +import com.stephenott.stix.objects.core.sdo.objects.LocationSdo +import com.stephenott.stix.objects.core.sdo.objects.ObservedDataSdo +import com.stephenott.stix.objects.core.sro.StixRelationshipObject +import com.stephenott.stix.type.* + +interface SightingSro: StixRelationshipObject{ + val description: String? + val firstSeen: StixInstant? + val lastSeen: StixInstant? //@TODO *** REVIEW SPEC: Says must be after first seen. But does not say "equals + val count: StixInteger? + val sightingOfRef: StixIdentifier + val observedDataRefs: StixIdentifiers? + val whereSightedRefs: StixIdentifiers? + val summary: StixBoolean + + companion object : CompanionStixType, + BusinessRulesValidator { + + override val stixType: StixType = StixType("sighting") + + override fun objectValidationRules(obj: SightingSro) { + if (obj.firstSeen != null && obj.lastSeen != null){ + require(obj.lastSeen!!.instant.isAfter(obj.firstSeen!!.instant), + lazyMessage = {"last_seen must be later than first_seen."}) + } + require(obj.count?.value in 0..999999999, + lazyMessage = {"count must be between 0 and 999,999,999."}) + + require(obj.sightingOfRef.type in StixObjectRegistry.sdoRegistry.keys, + lazyMessage = {"sighting_of_ref must reference only a SDO."}) // @TODO should also support custom objects + + require(obj.observedDataRefs?.all { it.type == ObservedDataSdo.stixType }!!, + lazyMessage = {"observed_data_refs must only have values that are references to Observed Data SDO."}) + + require(obj.whereSightedRefs?.all { it.type in listOf(IdentitySdo.stixType, LocationSdo.stixType) }!!, + lazyMessage = {"where_sighted_refs must only have values that reference Identity or Location SDO."}) + } + + } +} + +data class Sighting( + override val description: String? = null, + override val firstSeen: StixInstant? = null, + override val lastSeen: StixInstant? = null, + override val count: StixInteger? = null, + override val sightingOfRef: StixIdentifier, + override val observedDataRefs: StixIdentifiers? = null, + override val whereSightedRefs: StixIdentifiers? = null, + override val summary: StixBoolean = StixBoolean(), + override val type: StixType = SightingSro.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixInstant = StixInstant(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixInstant = StixInstant(created), + override val revoked: StixBoolean = StixBoolean() +): SightingSro{ + + init { + SightingSro.objectValidationRules(this) + } + + override fun allowedRelationships(): List { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } } \ No newline at end of file From 85d977cb5801c4db04b8a97502ee6e32a07bf496 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 21 Oct 2019 21:22:22 -0400 Subject: [PATCH 08/21] cleanup relationship --- .../objects/core/sro/objects/Relationship.kt | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index dca89c6..b03f122 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -1,9 +1,8 @@ package com.stephenott.stix.objects.core.sro.objects +import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject import com.stephenott.stix.objects.core.sro.StixRelationshipObject -import com.stephenott.stix.common.StixObjectRegistry -import com.stephenott.stix.common.StixObjectRelationshipRegistry import com.stephenott.stix.type.* import java.lang.IllegalStateException import kotlin.reflect.KClass @@ -17,26 +16,32 @@ interface RelationshipSro : StixRelationshipObject { val startTime: StixInstant? val stopTime: StixInstant? - companion object { - val stixType = StixType("relationship") + companion object: CompanionStixType, + BusinessRulesValidator { + + override val stixType = StixType("relationship") + + override fun objectValidationRules(obj: RelationshipSro) { + + } val allowedCommonRelationships: List = listOf( AllowedRelationship( StixObject::class, RelationshipType("duplicate-of"), StixObject::class, - RelationshipRule { from, type, to -> { - require(from == to, lazyMessage = {"source and target must be same type"}) - } + RelationshipRule { obj -> + require(obj.sourceRef.type == obj.targetRef.type, + lazyMessage = { "duplicate-of relationship (${obj.id}) requires source(${obj.sourceRef}) and target(${obj.targetRef}) must be same type" }) } ), AllowedRelationship( StixObject::class, RelationshipType("derived-from"), StixObject::class, - RelationshipRule { from, type, to -> { - require(from == to, lazyMessage = {"source and target must be same type"}) - } + RelationshipRule { obj -> + require(obj.sourceRef.type == obj.targetRef.type, + lazyMessage = { "derived-from relationship (${obj.id}) requires source(${obj.sourceRef}) and target(${obj.targetRef}) must be same type" }) } ), AllowedRelationship( @@ -48,10 +53,11 @@ interface RelationshipSro : StixRelationshipObject { } } -data class RelationshipRule( - val rule: (from: KClass, type: RelationshipType, to: KClass) -> Unit -) {} - +data class RelationshipRule(private val rule: (relObj: RelationshipSro) -> Unit) { + fun execute(relObj: RelationshipSro){ + rule(relObj) + } +} data class AllowedRelationship( val from: KClass, @@ -136,14 +142,13 @@ data class Relationship( //@TODO add some logging for a warning to indicate multiple duplication objects are registered } - require(allowedRelationships.isNotEmpty(), lazyMessage = { - "${this.id} is not a valid relationship for a ${this.sourceRef.type}" - }) + require(allowedRelationships.isNotEmpty(), + lazyMessage = { "${this.id} is not a valid relationship for a ${this.sourceRef.type}" } + ) //@TODO To a deeper test of this functionality - if (allowedRelationships[0].rule != null) { - allowedRelationships[0].rule?.rule!!(sourceClass, relationshipType, targetClass) - } + allowedRelationships[0].rule?.execute(this) + allowedRelationships[0].rule?.execute(this) } } \ No newline at end of file From 390519ad6852b6b73650ca6e47626992f5aaf9c8 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 21 Oct 2019 21:22:37 -0400 Subject: [PATCH 09/21] convert scoExtension to a proper KT --- .../stix/objects/core/sco/extension/ScoExtension.java | 4 ---- .../stix/objects/core/sco/extension/ScoExtension.kt | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java create mode 100644 src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.kt diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java deleted file mode 100644 index 6851f71..0000000 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.stephenott.stix.objects.core.sco.extension; - -public interface ScoExtension { -} diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.kt new file mode 100644 index 0000000..fbcebf5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/ScoExtension.kt @@ -0,0 +1,3 @@ +package com.stephenott.stix.objects.core.sco.extension + +interface ScoExtension From 087660145d31db154134420b8af40f36d4c9e03e Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 21 Oct 2019 21:22:56 -0400 Subject: [PATCH 10/21] Add rel example in runner --- src/main/kotlin/com/stephenott/stix/MainRunner.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index d879e7b..efdad5d 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,5 +1,6 @@ package com.stephenott.stix +import com.stephenott.stix.objects.core.sco.objects.IPv6Address import com.stephenott.stix.objects.core.sdo.objects.AttackPattern import com.stephenott.stix.objects.core.sro.objects.Relationship import com.stephenott.stix.type.RelationshipType @@ -11,11 +12,13 @@ object MainRunner { val ap1 = AttackPattern("124") val ap2 = AttackPattern("124") + val ip6 = IPv6Address("dog") + + println(ap1) val rel = Relationship( - relationshipType = RelationshipType("related-to"), + relationshipType = RelationshipType("duplicate-of"), sourceRef = ap1, - targetRef = ap2 + targetRef = ip6 ) - println(rel) } } \ No newline at end of file From 5c351ab3d19f56b5dd1f80bab6af1391a7ad2ddd Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Tue, 22 Oct 2019 21:26:11 -0400 Subject: [PATCH 11/21] Add Json serialization support with other small refactors --- .../kotlin/com/stephenott/stix/MainRunner.kt | 32 +++++- .../stix/common/StixObjectRegistry.kt | 43 +++++++- .../stix/serialization/json/JsonExtensions.kt | 99 ++++++++++++++++--- .../json/StixBooleanSerialization.kt | 39 ++++++++ .../json/StixContentSerialization.kt | 39 ++++++++ .../json/StixIdentifierSerialization.kt | 34 +++++++ .../json/StixInstantSerialization.kt | 34 +++++++ .../json/StixSpecVersionSerialization.kt | 37 +++++++ .../json/StixTypeSerialization.kt | 33 +++++++ .../stephenott/stix/type/StixIdentifier.kt | 7 ++ .../stephenott/stix/type/StixSpecVersion.kt | 13 ++- .../com/stephenott/stix/type/StixType.kt | 12 +++ 12 files changed, 399 insertions(+), 23 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixBooleanSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixIdentifierSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixSpecVersionSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index efdad5d..c64fb56 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,9 +1,20 @@ package com.stephenott.stix +import com.fasterxml.jackson.module.kotlin.readValue import com.stephenott.stix.objects.core.sco.objects.IPv6Address +import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sdo.objects.AttackPattern import com.stephenott.stix.objects.core.sro.objects.Relationship +import com.stephenott.stix.serialization.json.createStixMapper import com.stephenott.stix.type.RelationshipType +import com.stephenott.stix.type.StixBoolean +import com.stephenott.stix.objects.core.sdo.StixDomainObject.* +import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo +import com.stephenott.stix.objects.core.sro.StixRelationshipObject +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro +import com.stephenott.stix.objects.meta.StixMetaObject +import com.stephenott.stix.serialization.json.StixContentMapper +import com.stephenott.stix.serialization.json.toJson object MainRunner { @@ -11,14 +22,29 @@ object MainRunner { fun main(args: Array){ val ap1 = AttackPattern("124") - val ap2 = AttackPattern("124") + val ap2 = AttackPattern(name="1245") val ip6 = IPv6Address("dog") - println(ap1) + val mapper = StixContentMapper() + + val jsonString: String = ap1.toJson(mapper) + println(jsonString) + + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + + val rel = Relationship( relationshipType = RelationshipType("duplicate-of"), sourceRef = ap1, - targetRef = ip6 + targetRef = ap2 ) + println(rel.toJson(mapper)) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt index f9c8869..93becaa 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -1,14 +1,22 @@ package com.stephenott.stix.common +import com.stephenott.stix.StixContent import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.objects.* +import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sdo.objects.* +import com.stephenott.stix.objects.core.sro.StixRelationshipObject +import com.stephenott.stix.objects.core.sro.objects.Relationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro +import com.stephenott.stix.objects.core.sro.objects.Sighting +import com.stephenott.stix.objects.core.sro.objects.SightingSro import com.stephenott.stix.type.StixType import kotlin.reflect.KClass object StixObjectRegistry { - var sdoRegistry: Map> = mutableMapOf( + var sdoRegistry: Map> = mutableMapOf( Pair(AttackPatternSdo.stixType, AttackPattern::class), Pair(CampaignSdo.stixType, Campaign::class), Pair(CourseOfActionSdo.stixType, CourseOfAction::class), @@ -28,7 +36,7 @@ object StixObjectRegistry { Pair(VulnerabilitySdo.stixType, Vulnerability::class) ) - var scoRegistry: Map> = mutableMapOf( + var scoRegistry: Map> = mutableMapOf( Pair(ArtifactSco.stixType, Artifact::class), Pair(AutonomousSystemSco.stixType, AutonomousSystem::class), Pair(DirectorySco.stixType, Directory::class), @@ -49,5 +57,34 @@ object StixObjectRegistry { Pair(X509CertificateSco.stixType, X509Certificate::class) ) - var registry: Map> = sdoRegistry + scoRegistry + var sroRegistry: Map> = mutableMapOf( + Pair(RelationshipSro.stixType, Relationship::class), + Pair(SightingSro.stixType, Sighting::class) + ) + + var customSdoRegistry: Map> = mutableMapOf( + ) + var customScoRegistry: Map> = mutableMapOf( + ) + var customSroRegistry: Map> = mutableMapOf( + ) + + private fun aggregateObjects(): Map> { + val objects = mutableMapOf>() + objects.plusAssign(sdoRegistry) + objects.plusAssign(scoRegistry) + objects.plusAssign(sroRegistry) + objects.plusAssign(customSdoRegistry) + objects.plusAssign(customScoRegistry) + objects.plusAssign(customSroRegistry) + return objects + } + + private var registryAggregate: Map> = aggregateObjects() + + fun refreshRegistry(){ + registryAggregate = aggregateObjects() + } + + val registry: Map> = registryAggregate } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index ceda42e..daa59d0 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -1,30 +1,101 @@ package com.stephenott.stix.serialization.json +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.stephenott.stix.StixBundle import com.stephenott.stix.StixContent -import com.stephenott.stix.objects.core.sdo.StixDomainObject -import com.stephenott.stix.objects.core.sro.StixRelationshipObject +import kotlin.reflect.full.cast -fun StixContent.toJson(){ - TODO("To be implemented") +fun createStixMapper(): ObjectMapper { + return jacksonObjectMapper() + .registerModule(JavaTimeModule()) + .registerModule(createStixSdoModule()) + .registerModule(createStixSroModule()) + .registerModule(createStixScoModule()) + .registerModule(createStixMetaObjectModule()) + .registerModule(createStixBundleModule()) + .registerModule(createStixCustomObjectsModule()) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + .registerModule(createStixInstantSerializationModule()) + .registerModule(createStixIdentifierSerializationModule()) + .registerModule(createStixTypeSerializationModule()) + .registerModule(createStixSpecVersionSerializationModule()) + .registerModule(createStixBooleanSerializationModule()) + .registerModule(createStixContentSerializationModule()) } -fun StixBundle.toJson(){ - TODO("To be implemented") +class StixContentMapper(){ + /** + * Should generally not be needed. But provided just in case + */ + val jsonMapper: ObjectMapper = createStixMapper() + + /** + * Parse a json string into any kind of Stix Content (SDO, SCO, SRO, Relationships, etc) + */ + inline fun parseJson(json: String): T { + val content: StixContent + + try { + content = jsonMapper.readValue(json, StixContent::class.java) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse json.", e) + } + + try { + return T::class.cast(content) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse json.", e) + } + } + +} + +fun createStixSdoModule(): SimpleModule { + val module = SimpleModule() + + return module +} + +fun createStixScoModule(): SimpleModule { + val module = SimpleModule() + + return module } -fun StixContent.parse(json: String){ - TODO("To be implemented") +fun createStixSroModule(): SimpleModule { + val module = SimpleModule() + + return module +} + +fun createStixMetaObjectModule(): SimpleModule { + val module = SimpleModule() + + return module } -fun StixBundle.parse(json: String){ - TODO("To be implemented") +fun createStixBundleModule(): SimpleModule { + val module = SimpleModule() + + return module +} + +fun createStixCustomObjectsModule(): SimpleModule { + val module = SimpleModule() + + return module } -fun StixDomainObject.parse(json: String){ - TODO("To be implemented") +fun StixContent.toJson(mapper: StixContentMapper): String{ + return mapper.jsonMapper.writeValueAsString(this) } -fun StixRelationshipObject.parse(json: String){ - TODO("To be implemented") +fun StixBundle.toJson(mapper: StixContentMapper): String{ + return mapper.jsonMapper.writeValueAsString(this) } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixBooleanSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixBooleanSerialization.kt new file mode 100644 index 0000000..b8f484f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixBooleanSerialization.kt @@ -0,0 +1,39 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixBoolean +import kotlin.IllegalArgumentException + + +fun createStixBooleanSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(StixBoolean::class.java, StixBooleanSerializer()) + .addDeserializer(StixBoolean::class.java, StixBooleanDeserializer()) +} + +class StixBooleanSerializer() : StdSerializer(StixBoolean::class.java) { + + override fun isEmpty(provider: SerializerProvider?, value: StixBoolean?): Boolean { + return value == null || !value.isDefinedValue + } + + override fun serialize(value: StixBoolean?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeBoolean(value!!.value) + } +} + +class StixBooleanDeserializer() : StdDeserializer(StixBoolean::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixBoolean { + try { + return StixBoolean.parse(p!!.text) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse boolean.", e) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt new file mode 100644 index 0000000..89a1f2f --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt @@ -0,0 +1,39 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.stephenott.stix.StixContent +import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.type.StixType +import kotlin.reflect.KClass + + +fun createStixContentSerializationModule(): SimpleModule { + return SimpleModule() + .addDeserializer(StixContent::class.java, StixContentDeserializer()) +} + +class StixContentDeserializer() : StdDeserializer(StixContent::class.java) { + + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixContent { + + val node: JsonNode = p!!.codec.readTree(p) + val typeProp = node.get("type") + if (typeProp != null) { + val stixType = StixType.parse(typeProp.asText()) + + val objectClass: KClass = StixObjectRegistry.registry.getOrElse(stixType, defaultValue = { + throw IllegalArgumentException("Unable to parse the object. Ensure object type is supported.") + }) + + return p.codec.treeToValue(node, objectClass.java) + + } else { + throw IllegalArgumentException("type property was null or not provided.") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixIdentifierSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixIdentifierSerialization.kt new file mode 100644 index 0000000..63f2820 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixIdentifierSerialization.kt @@ -0,0 +1,34 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixIdentifier + + +fun createStixIdentifierSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(StixIdentifier::class.java, StixIdentifierSerializer()) + .addDeserializer(StixIdentifier::class.java, StixIdentifierDeserializer()) +} + +class StixIdentifierSerializer() : StdSerializer(StixIdentifier::class.java) { + override fun serialize(value: StixIdentifier?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeString(value!!.toString()) + } +} + +class StixIdentifierDeserializer() : StdDeserializer(StixIdentifier::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixIdentifier { + try { + return StixIdentifier.parse(p!!.text) + } catch (e: Exception) { + throw IllegalArgumentException("Unable to parse identifier", e) + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt new file mode 100644 index 0000000..fb30d75 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt @@ -0,0 +1,34 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixInstant + + +fun createStixInstantSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(StixInstant::class.java, StixInstantSerializer()) + .addDeserializer(StixInstant::class.java, StixInstantDeserializer()) +} + +class StixInstantSerializer() : StdSerializer(StixInstant::class.java) { + override fun serialize(value: StixInstant, gen: JsonGenerator, provider: SerializerProvider) { + gen.writeString(value.toString()) + } +} + +class StixInstantDeserializer() : StdDeserializer(StixInstant::class.java) { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): StixInstant { + try { + return StixInstant.parse(p.text) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse timestamp.", e) + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixSpecVersionSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixSpecVersionSerialization.kt new file mode 100644 index 0000000..8768414 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixSpecVersionSerialization.kt @@ -0,0 +1,37 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixSpecVersion + + +fun createStixSpecVersionSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(StixSpecVersion::class.java, StixSpecVersionSerializer()) + .addDeserializer(StixSpecVersion::class.java, StixSpecVersionDeserializer()) +} + +class StixSpecVersionSerializer() : StdSerializer(StixSpecVersion::class.java) { + override fun isEmpty(provider: SerializerProvider?, value: StixSpecVersion?): Boolean { + return value == null || !value.isDefinedValue + } + + override fun serialize(value: StixSpecVersion?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeString(value.toString()) + } +} + +class StixSpecVersionDeserializer() : StdDeserializer(StixSpecVersion::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixSpecVersion { + try { + return StixSpecVersion.parse(p!!.text) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse spec_version.", e) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt new file mode 100644 index 0000000..501b396 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt @@ -0,0 +1,33 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixType + + +fun createStixTypeSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(StixType::class.java, StixTypeSerializer()) + .addDeserializer(StixType::class.java, StixTypeDeserializer()) +} + +class StixTypeSerializer() : StdSerializer(StixType::class.java) { + override fun serialize(value: StixType?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeString(value!!.toString()) + } +} + +class StixTypeDeserializer() : StdDeserializer(StixType::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixType { + try { + return StixType.parse(p!!.text) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse type.", e) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt b/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt index 402727c..880c506 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixIdentifier.kt @@ -21,6 +21,13 @@ data class StixIdentifier( companion object { fun generateUUIDv4(): String = UUID.randomUUID().toString() const val typeUUIDSpacer = "--" + + fun parse(stringIdentifier: String): StixIdentifier{ + val type: String = stringIdentifier.substringBefore(typeUUIDSpacer) + val uuid: String = stringIdentifier.substringAfter(typeUUIDSpacer) + return StixIdentifier(StixType(type), uuid) + } + } fun getIdentifier(): String { diff --git a/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt b/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt index acebdbb..aea6553 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixSpecVersion.kt @@ -1,20 +1,27 @@ package com.stephenott.stix.type +import java.lang.IllegalArgumentException + data class StixSpecVersion(private val versionEnum: StixVersions, val isDefinedValue: Boolean) { companion object{ enum class StixVersions(val version: String) { - TWO_DOT_ZERO("2.0"), +// TWO_DOT_ZERO("2.0"), //@TODO review this TWO_DOT_ONE("2.1") } fun parse(versionString: String): StixSpecVersion{ - return StixSpecVersion(StixVersions.values().first { it.version == versionString }, true) + try { + return StixSpecVersion(StixVersions.values().first { it.version == versionString }, true) + } catch (e: Exception){ + throw IllegalArgumentException("Unable to parse spec version. Note that only spec version 2.1 is supported. Use an elevator to pre-convert 2.0 to 2.1", e) + } + } } //@TODO Review if this is the correct default - constructor() : this(StixVersions.TWO_DOT_ZERO, false) + constructor() : this(StixVersions.TWO_DOT_ONE, true) override fun toString(): String { return versionEnum.version diff --git a/src/main/kotlin/com/stephenott/stix/type/StixType.kt b/src/main/kotlin/com/stephenott/stix/type/StixType.kt index afe6c81..28586d9 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixType.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixType.kt @@ -12,6 +12,18 @@ data class StixType( companion object{ var customObjectPrefix: String = "x-" //@TODO refactor to lazy init and a outside factory + + fun parse(typeString: String): StixType{ + val isCustomObject: Boolean = typeString.startsWith(customObjectPrefix) + val type: String = if (isCustomObject){ + typeString.substringAfter(customObjectPrefix) + } else { + typeString + } + + return StixType(type, isCustomObject) + } + } constructor(stixType: StixType): this(stixType.type, stixType.isCustomType) From a0bee901c497cadfa342656c7cfc7ad7bc16a3c7 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Wed, 23 Oct 2019 07:24:40 -0400 Subject: [PATCH 12/21] add serialization deps --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index 78d54d2..7e804c6 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,11 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-reflect" + implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.0" + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.0" +// implementation "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.0" +// implementation "com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.0" + testImplementation "org.jetbrains.kotlin:kotlin-test" testImplementation "org.jetbrains.kotlin:kotlin-test-junit" } \ No newline at end of file From ecb99292f6240c731e7c6890abbf7d3789d0f091 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Wed, 23 Oct 2019 07:24:59 -0400 Subject: [PATCH 13/21] Add additional serialization capabilities for specialized types --- .../kotlin/com/stephenott/stix/MainRunner.kt | 32 +++++-- .../core/sco/StixCyberObservableObject.kt | 3 +- .../objects/core/sco/objects/Directory.kt | 11 +-- .../objects/core/sco/objects/EmailAddress.kt | 9 +- .../objects/core/sco/objects/EmailMessage.kt | 38 +++++--- .../stix/objects/core/sco/objects/File.kt | 26 +++--- .../core/sco/objects/NetworkTraffic.kt | 90 ++++++++++--------- .../stix/objects/core/sdo/StixDomainObject.kt | 3 +- .../core/sro/StixRelationshipObject.kt | 3 +- .../stix/objects/core/sro/objects/Sighting.kt | 30 ++++--- .../stix/serialization/json/JsonExtensions.kt | 2 + .../json/RelationshipTypeSerialization.kt | 29 ++++++ .../json/StixIntegerSerialization.kt | 33 +++++++ .../stephenott/stix/type/RelationshipType.kt | 6 +- .../com/stephenott/stix/type/StixLabels.kt | 4 +- 15 files changed, 220 insertions(+), 99 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/RelationshipTypeSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixIntegerSerialization.kt diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index c64fb56..840498d 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,20 +1,25 @@ package com.stephenott.stix import com.fasterxml.jackson.module.kotlin.readValue +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.StixCoreObject import com.stephenott.stix.objects.core.sco.objects.IPv6Address +import com.stephenott.stix.objects.core.sco.objects.NetworkTraffic import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sdo.objects.AttackPattern import com.stephenott.stix.objects.core.sro.objects.Relationship import com.stephenott.stix.serialization.json.createStixMapper -import com.stephenott.stix.type.RelationshipType -import com.stephenott.stix.type.StixBoolean import com.stephenott.stix.objects.core.sdo.StixDomainObject.* import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo +import com.stephenott.stix.objects.core.sdo.objects.ObservedData import com.stephenott.stix.objects.core.sro.StixRelationshipObject import com.stephenott.stix.objects.core.sro.objects.RelationshipSro +import com.stephenott.stix.objects.core.sro.objects.Sighting import com.stephenott.stix.objects.meta.StixMetaObject import com.stephenott.stix.serialization.json.StixContentMapper import com.stephenott.stix.serialization.json.toJson +import com.stephenott.stix.type.* +import java.time.Instant object MainRunner { @@ -22,15 +27,23 @@ object MainRunner { fun main(args: Array){ val ap1 = AttackPattern("124") - val ap2 = AttackPattern(name="1245") + val ap2 = AttackPattern("1245") val ip6 = IPv6Address("dog") + val sighting1 = Sighting(sightingOfRef = ap1.id) + val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) + val mapper = StixContentMapper() + println(net1.toJson(mapper)) + println(mapper.parseJson(net1.toJson(mapper))) + val jsonString: String = ap1.toJson(mapper) println(jsonString) println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) println(mapper.parseJson(jsonString)) println(mapper.parseJson(jsonString)) println(mapper.parseJson(jsonString)) @@ -41,10 +54,13 @@ object MainRunner { sourceRef = ap1, targetRef = ap2 ) - println(rel.toJson(mapper)) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + + println(mapper.parseJson(sighting1.toJson(mapper))) } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt index 62e0a66..c91ea8d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/StixCyberObservableObject.kt @@ -2,9 +2,10 @@ package com.stephenott.stix.objects.core.sco import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.StixCoreObject interface StixCyberObservableObject : - StixObject, + StixCoreObject, StixSpecVersionProp, StixObjectMarkingsRefsProp, StixGranularMarkingsProp, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt index c85bfea..ad250b1 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -18,7 +18,7 @@ interface DirectorySco : StixCyberObservableObject { val atime: StixInstant? val containsRefs: StixIdentifiers? - companion object: + companion object : CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, @@ -38,11 +38,12 @@ interface DirectorySco : StixCyberObservableObject { ) override fun objectValidationRules(obj: DirectorySco) { - require(obj.containsRefs?.all { - it.type == DirectorySco.stixType || it.type == FileSco.stixType } ?: true, - lazyMessage = {"contains_refs must only contain SCOs of type Directory and File."}) + obj.containsRefs?.let { + require(it.all { id -> id.type == stixType || id.type == FileSco.stixType }, + lazyMessage = { "contains_refs must only contain SCOs of type Directory and File." } + ) + } } - } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt index 71eac43..4608f7b 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -15,7 +15,7 @@ interface EmailAddressSco : StixCyberObservableObject { val displayName: String? //@TODO add validation val belongsToRef: StixIdentifier? - companion object: + companion object : CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, @@ -35,9 +35,12 @@ interface EmailAddressSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: EmailAddressSco) { - require(obj.belongsToRef?.type == UserAccountSco.stixType, lazyMessage = {"belongs_to_ref must reference a user-account SCO."}) + obj.belongsToRef?.let { + require(it.type == UserAccountSco.stixType, + lazyMessage = { "belongs_to_ref must reference a user-account SCO." } + ) + } } - } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt index 028766f..d78d270 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -27,7 +27,7 @@ interface EmailMessageSco : StixCyberObservableObject { val bodyMultipart: MimePartTypes? val rawEmailRef: StixIdentifier? - companion object: + companion object : CompanionStixType, BusinessRulesValidator, CompanionIdContributingProperties, @@ -49,23 +49,33 @@ interface EmailMessageSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: EmailMessageSco) { - require(obj.fromRef?.type == EmailAddressSco.stixType, - lazyMessage = {"from_ref must be references to email-address SCO"}) - require(obj.senderRef?.type == EmailAddressSco.stixType, - lazyMessage = {"sender_ref must be references to email-address SCO"}) - require(obj.toRefs?.all { it.type == EmailAddressSco.stixType } ?: true, - lazyMessage = {"to_refs must be references to email-address SCO"}) - require(obj.ccRefs?.all { it.type == EmailAddressSco.stixType } ?: true, - lazyMessage = {"cc_refs must be references to email-address SCO"}) - require(obj.bccRefs?.all { it.type == EmailAddressSco.stixType } ?: true, - lazyMessage = {"bcc_refs must be references to email-address SCO"}) + obj.fromRef?.let { + require(it.type == EmailAddressSco.stixType, + lazyMessage = { "from_ref must be references to email-address SCO" }) + } + obj.senderRef?.let { + require(it.type == EmailAddressSco.stixType, + lazyMessage = { "sender_ref must be references to email-address SCO" }) + } + obj.toRefs?.let { + require(it.all { id -> id.type == EmailAddressSco.stixType }, + lazyMessage = { "to_refs must be references to email-address SCO" }) + } + obj.ccRefs?.let { + require(it.all { id -> id.type == EmailAddressSco.stixType }, + lazyMessage = { "cc_refs must be references to email-address SCO" }) + } + obj.bccRefs?.let { + require(it.all { id -> id.type == EmailAddressSco.stixType }, + lazyMessage = { "bcc_refs must be references to email-address SCO" }) + } - if (obj.isMultipart.value){ + if (obj.isMultipart.value) { require(obj.body == null, - lazyMessage = {"body cannot be used when is_multipart is true"}) + lazyMessage = { "body cannot be used when is_multipart is true" }) } else { require(obj.bodyMultipart == null, - lazyMessage = {"body_multipart cannot be used when is_multipart is false"}) + lazyMessage = { "body_multipart cannot be used when is_multipart is false" }) } } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt index 6627c99..2fb474d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -54,17 +54,23 @@ interface FileSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: FileSco) { - require(obj.size?.value!! >= 0, - lazyMessage = { "size must not be a negative number." }) - require(obj.parentDirectoryRef?.type == DirectorySco.stixType, - lazyMessage = { "parent_directory_ref must only contain a reference to a Directory object SCO." }) - require(obj.containsRefs?.all { - StixObjectRegistry.registry.getValue(it.type).isSubclassOf(StixCyberObservableObject::class) - }!!, lazyMessage = { "contains_refs can only contain references to SCOs." }) - require(obj.contentRef?.type == ArtifactSco.stixType, - lazyMessage = { "content_ref must only contain a reference to a Artifact SCO." }) + obj.size?.let { + require(it.value >= 0, + lazyMessage = { "size must not be a negative number." }) + } + obj.parentDirectoryRef?.let { + require(it.type == DirectorySco.stixType, + lazyMessage = { "parent_directory_ref must only contain a reference to a Directory object SCO." }) + } + obj.containsRefs?.let { + require(it.all { id -> StixObjectRegistry.registry.getValue(id.type).isSubclassOf(StixCyberObservableObject::class) }, + lazyMessage = { "contains_refs can only contain references to SCOs." }) + } + obj.contentRef?.let { + require(it.type == ArtifactSco.stixType, + lazyMessage = { "content_ref must only contain a reference to a Artifact SCO." }) + } } - } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt index f1d8bd6..abcad55 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt @@ -60,54 +60,64 @@ interface NetworkTrafficSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: NetworkTrafficSco) { - if (obj.isActive?.value!!) { - require(obj.end == null, - lazyMessage = { "If is_active is true then end must not be included." }) + obj.isActive?.let { + if (it.value) { + require(obj.end == null, + lazyMessage = { "If is_active is true then end must not be included." }) + } } if (obj.start != null && obj.end != null) { require(obj.end?.instant!!.isAfter(obj.start?.instant), lazyMessage = { "if start and end are both defined then end must be later than start." }) } - if (obj.end != null) { + obj.end?.let { require(obj.isActive != null && - obj.isActive?.value == false && - obj.isActive?.isDefinedValue == true, + !obj.isActive!!.value && + obj.isActive!!.isDefinedValue, lazyMessage = { "If end is provided then is_active must be provided and have a value of false." }) } - - require(obj.srcRef?.type in listOf( - IPv4AddressSco.stixType, - IPv6AddressSco.stixType, - MacAddressSco.stixType, - DomainNameSco.stixType - ), - lazyMessage = { "src_ref must be a reference to one of: ipv4-addr, ipv6-addr, mac-addr, domain-name." }) - - require(obj.dstRef?.type in listOf( - IPv4AddressSco.stixType, - IPv6AddressSco.stixType, - MacAddressSco.stixType, - DomainNameSco.stixType - ), - lazyMessage = { "dst_ref must be a reference to one of: ipv4-addr, ipv6-addr, mac-addr, domain-name." }) - - require(obj.srcPort?.value!! in 0..65535, - lazyMessage = { "src_port must be in range 0 to 65535" }) - - require(obj.dstPort?.value!! in 0..65535, - lazyMessage = { "dst_port must be in range 0 to 65535" }) - - require(obj.srcPayloadRef?.type == ArtifactSco.stixType, - lazyMessage = { "src_payload_ref must only reference type artifact" }) - - require(obj.dstPayloadRef?.type == ArtifactSco.stixType, - lazyMessage = { "dst_payload_ref must only reference type artifact" }) - - require(obj.encapsulatesRefs?.all { it.type == NetworkTrafficSco.stixType }!!, - lazyMessage = { "encapsulates_refs values must only reference type network-traffic" }) - - require(obj.encapsulatedByRef?.type == NetworkTrafficSco.stixType, - lazyMessage = { "encapsulated_by_ref must only reference type network-traffic" }) + obj.srcRef?.let { + require(it.type in listOf( + IPv4AddressSco.stixType, + IPv6AddressSco.stixType, + MacAddressSco.stixType, + DomainNameSco.stixType + ), + lazyMessage = { "src_ref must be a reference to one of: ipv4-addr, ipv6-addr, mac-addr, domain-name." }) + } + obj.dstRef?.let { + require(it.type in listOf( + IPv4AddressSco.stixType, + IPv6AddressSco.stixType, + MacAddressSco.stixType, + DomainNameSco.stixType + ), + lazyMessage = { "dst_ref must be a reference to one of: ipv4-addr, ipv6-addr, mac-addr, domain-name." }) + } + obj.srcPort?.let { + require(it.value in 0..65535, + lazyMessage = { "src_port must be in range 0 to 65535" }) + } + obj.dstPort?.let { + require(it.value in 0..65535, + lazyMessage = { "dst_port must be in range 0 to 65535" }) + } + obj.srcPayloadRef?.let { + require(it.type == ArtifactSco.stixType, + lazyMessage = { "src_payload_ref must only reference type artifact" }) + } + obj.dstPayloadRef?.let { + require(it.type == ArtifactSco.stixType, + lazyMessage = { "dst_payload_ref must only reference type artifact" }) + } + obj.encapsulatesRefs?.let { + require(it.all { id -> id.type == NetworkTrafficSco.stixType }, + lazyMessage = { "encapsulates_refs values must only reference type network-traffic" }) + } + obj.encapsulatedByRef?.let { + require(it.type == NetworkTrafficSco.stixType, + lazyMessage = { "encapsulated_by_ref must only reference type network-traffic" }) + } } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt index 2dc43b4..942de27 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/StixDomainObject.kt @@ -2,9 +2,10 @@ package com.stephenott.stix.objects.core.sdo import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.StixCoreObject interface StixDomainObject : - StixObject, + StixCoreObject, StixCreatedByRef, StixCreatedProp, StixExternalReferencesProp, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt index 0020220..53ac3a4 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/StixRelationshipObject.kt @@ -2,9 +2,10 @@ package com.stephenott.stix.objects.core.sro import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.StixCoreObject interface StixRelationshipObject: - StixObject, + StixCoreObject, StixCreatedByRef, StixCreatedProp, StixExternalReferencesProp, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt index 864da4c..7d5a514 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -9,7 +9,7 @@ import com.stephenott.stix.objects.core.sdo.objects.ObservedDataSdo import com.stephenott.stix.objects.core.sro.StixRelationshipObject import com.stephenott.stix.type.* -interface SightingSro: StixRelationshipObject{ +interface SightingSro : StixRelationshipObject { val description: String? val firstSeen: StixInstant? val lastSeen: StixInstant? //@TODO *** REVIEW SPEC: Says must be after first seen. But does not say "equals @@ -25,23 +25,29 @@ interface SightingSro: StixRelationshipObject{ override val stixType: StixType = StixType("sighting") override fun objectValidationRules(obj: SightingSro) { - if (obj.firstSeen != null && obj.lastSeen != null){ + if (obj.firstSeen != null && obj.lastSeen != null) { require(obj.lastSeen!!.instant.isAfter(obj.firstSeen!!.instant), - lazyMessage = {"last_seen must be later than first_seen."}) + lazyMessage = { "last_seen must be later than first_seen." }) + } + + obj.count?.let { + require(it.value in 0..999999999, + lazyMessage = { "count must be between 0 and 999,999,999." }) } - require(obj.count?.value in 0..999999999, - lazyMessage = {"count must be between 0 and 999,999,999."}) require(obj.sightingOfRef.type in StixObjectRegistry.sdoRegistry.keys, - lazyMessage = {"sighting_of_ref must reference only a SDO."}) // @TODO should also support custom objects + lazyMessage = { "sighting_of_ref must reference only a SDO." }) // @TODO should also support custom objects - require(obj.observedDataRefs?.all { it.type == ObservedDataSdo.stixType }!!, - lazyMessage = {"observed_data_refs must only have values that are references to Observed Data SDO."}) + obj.observedDataRefs?.let { + require(it.all { id -> id.type == ObservedDataSdo.stixType }, + lazyMessage = { "observed_data_refs must only have values that are references to Observed Data SDO." }) + } - require(obj.whereSightedRefs?.all { it.type in listOf(IdentitySdo.stixType, LocationSdo.stixType) }!!, - lazyMessage = {"where_sighted_refs must only have values that reference Identity or Location SDO."}) + obj.whereSightedRefs?.let { + require(it.all { id -> id.type in listOf(IdentitySdo.stixType, LocationSdo.stixType) }, + lazyMessage = { "where_sighted_refs must only have values that reference Identity or Location SDO." }) + } } - } } @@ -65,7 +71,7 @@ data class Sighting( override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean() -): SightingSro{ +) : SightingSro { init { SightingSro.objectValidationRules(this) diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index daa59d0..75319de 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -27,6 +27,8 @@ fun createStixMapper(): ObjectMapper { .registerModule(createStixSpecVersionSerializationModule()) .registerModule(createStixBooleanSerializationModule()) .registerModule(createStixContentSerializationModule()) + .registerModule(createRelationshipTypeSerializationModule()) + .registerModule(createStixIntegerSerializationModule()) } class StixContentMapper(){ diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/RelationshipTypeSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/RelationshipTypeSerialization.kt new file mode 100644 index 0000000..72c8984 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/RelationshipTypeSerialization.kt @@ -0,0 +1,29 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.RelationshipType + + +fun createRelationshipTypeSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(RelationshipType::class.java, RelationshipTypeSerializer()) + .addDeserializer(RelationshipType::class.java, RelationshipTypeDeserializer()) +} + +class RelationshipTypeSerializer() : StdSerializer(RelationshipType::class.java) { + override fun serialize(value: RelationshipType?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeString(value.toString()) + } +} + +class RelationshipTypeDeserializer() : StdDeserializer(RelationshipType::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): RelationshipType { + return RelationshipType(p!!.text) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixIntegerSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixIntegerSerialization.kt new file mode 100644 index 0000000..3a52bbe --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixIntegerSerialization.kt @@ -0,0 +1,33 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixInteger + + +fun createStixIntegerSerializationModule(): SimpleModule { + return SimpleModule() + .addSerializer(StixInteger::class.java, StixIntegerSerializer()) + .addDeserializer(StixInteger::class.java, StixIntegerDeserializer()) +} + +class StixIntegerSerializer() : StdSerializer(StixInteger::class.java) { + override fun serialize(value: StixInteger?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeNumber(value!!.value) + } +} + +class StixIntegerDeserializer() : StdDeserializer(StixInteger::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixInteger { + if (p!!.numberType == JsonParser.NumberType.INT) { + return StixInteger(p.intValue) + } else { + throw IllegalArgumentException("value is not expected integer format.") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt b/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt index ac953db..99790c2 100644 --- a/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt +++ b/src/main/kotlin/com/stephenott/stix/type/RelationshipType.kt @@ -1,8 +1,12 @@ package com.stephenott.stix.type -data class RelationshipType(val type: String){ +data class RelationshipType(val type: String) { init { //@TODO //The value of this property MUST be in ASCII and is limited to characters a–z (lowercase ASCII), 0–9, and hyphen (-). } + + override fun toString(): String { + return type + } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt b/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt index db15040..192ac9e 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixLabels.kt @@ -1,8 +1,6 @@ package com.stephenott.stix.type -class StixLabels(private val labels: LinkedHashSet): - Set by labels { - +class StixLabels(private val labels: LinkedHashSet): Set by labels { init { labels.all { it.isNotEmpty() } } From b2ae2f180448aab926f237fd4fc538ec2ce278b9 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Wed, 30 Oct 2019 16:57:15 -0400 Subject: [PATCH 14/21] cleanup and add common validations --- .../kotlin/com/stephenott/stix/MainRunner.kt | 2 +- .../stix/common/CommonValidations.kt | 9 +++++ .../stix/common/StixObjectRegistry.kt | 1 + .../stix/objects/core/sco/objects/Artifact.kt | 2 + .../core/sco/objects/AutonomousSystem.kt | 2 +- .../objects/core/sco/objects/Directory.kt | 2 + .../objects/core/sco/objects/DomainName.kt | 2 + .../objects/core/sco/objects/EmailAddress.kt | 2 + .../objects/core/sco/objects/EmailMessage.kt | 2 + .../stix/objects/core/sco/objects/File.kt | 2 + .../objects/core/sco/objects/IPv4Address.kt | 2 + .../objects/core/sco/objects/IPv6Address.kt | 2 + .../objects/core/sco/objects/MacAddress.kt | 2 + .../stix/objects/core/sco/objects/Mutex.kt | 2 + .../core/sco/objects/NetworkTraffic.kt | 2 + .../stix/objects/core/sco/objects/Process.kt | 2 + .../stix/objects/core/sco/objects/Software.kt | 1 + .../stix/objects/core/sco/objects/Url.kt | 2 +- .../objects/core/sco/objects/UserAccount.kt | 2 +- .../core/sco/objects/WindowsRegistryKey.kt | 1 + .../core/sco/objects/X509Certificate.kt | 2 + .../objects/core/sdo/objects/AttackPattern.kt | 3 +- .../stix/objects/core/sdo/objects/Campaign.kt | 2 + .../core/sdo/objects/CourseOfAction.kt | 3 ++ .../stix/objects/core/sdo/objects/Grouping.kt | 3 +- .../stix/objects/core/sdo/objects/Identity.kt | 3 +- .../objects/core/sdo/objects/Indicator.kt | 3 ++ .../core/sdo/objects/Infrastructure.kt | 3 ++ .../objects/core/sdo/objects/IntrusionSet.kt | 3 ++ .../stix/objects/core/sdo/objects/Location.kt | 3 ++ .../stix/objects/core/sdo/objects/Malware.kt | 3 ++ .../core/sdo/objects/MalwareAnalysis.kt | 3 ++ .../stix/objects/core/sdo/objects/Note.kt | 2 + .../objects/core/sdo/objects/ObservedData.kt | 7 ++-- .../stix/objects/core/sdo/objects/Opinion.kt | 2 + .../stix/objects/core/sdo/objects/Report.kt | 2 + .../objects/core/sdo/objects/ThreatActor.kt | 3 ++ .../stix/objects/core/sdo/objects/Tool.kt | 2 + .../objects/core/sdo/objects/Vulnerability.kt | 3 +- .../objects/core/sro/objects/Relationship.kt | 1 + .../stix/objects/core/sro/objects/Sighting.kt | 3 ++ .../meta/datamarking/MarkingDefinition.kt | 21 +++++++++- .../meta/lco/objects/LanguageContent.kt | 17 +++++++-- .../stix/serialization/json/JsonExtensions.kt | 1 + .../json/StixConfidenceSerialization.kt | 38 +++++++++++++++++++ .../com/stephenott/stix/type/Product.kt | 6 ++- .../stephenott/stix/type/StixConfidence.kt | 31 +++++++++++---- 47 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/common/CommonValidations.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixConfidenceSerialization.kt diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index 840498d..eb9aece 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -26,7 +26,7 @@ object MainRunner { @JvmStatic fun main(args: Array){ - val ap1 = AttackPattern("124") + val ap1 = AttackPattern(name = "124", confidence = StixConfidence(33)) val ap2 = AttackPattern("1245") val ip6 = IPv6Address("dog") diff --git a/src/main/kotlin/com/stephenott/stix/common/CommonValidations.kt b/src/main/kotlin/com/stephenott/stix/common/CommonValidations.kt new file mode 100644 index 0000000..f940ae0 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/common/CommonValidations.kt @@ -0,0 +1,9 @@ +package com.stephenott.stix.common + +import com.stephenott.stix.StixContent +import com.stephenott.stix.type.StixType + +fun requireStixType(type: StixType, obj: StixContent){ + require(obj.type == type, + lazyMessage = {"Object has incorrect type value. Value was: ${obj.type}, but expected type value was $type"}) +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt index 93becaa..27f5559 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -14,6 +14,7 @@ import com.stephenott.stix.objects.core.sro.objects.SightingSro import com.stephenott.stix.type.StixType import kotlin.reflect.KClass +//@TODO move to a instance so it can be passed into content handlers (such as JSON content mapper) object StixObjectRegistry { var sdoRegistry: Map> = mutableMapOf( diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt index 781e693..ce8d832 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt @@ -40,6 +40,8 @@ interface ArtifactSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: ArtifactSco) { + requireStixType(this.stixType, obj) + if (obj.url != null) { require(obj.payloadBin == null, lazyMessage = { "payload_bin must not be present if url is provided." }) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt index 7a08145..4fc6706 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt @@ -35,7 +35,7 @@ interface AutonomousSystemSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: AutonomousSystemSco) { - + requireStixType(this.stixType, obj) } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt index ad250b1..712437d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -38,6 +38,8 @@ interface DirectorySco : StixCyberObservableObject { ) override fun objectValidationRules(obj: DirectorySco) { + requireStixType(this.stixType, obj) + obj.containsRefs?.let { require(it.all { id -> id.type == stixType || id.type == FileSco.stixType }, lazyMessage = { "contains_refs must only contain SCOs of type Directory and File." } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt index f0af713..b3f7e82 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt @@ -48,6 +48,8 @@ interface DomainNameSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: DomainNameSco) { + requireStixType(this.stixType, obj) + } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt index 4608f7b..1d8d153 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -35,6 +35,8 @@ interface EmailAddressSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: EmailAddressSco) { + requireStixType(this.stixType, obj) + obj.belongsToRef?.let { require(it.type == UserAccountSco.stixType, lazyMessage = { "belongs_to_ref must reference a user-account SCO." } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt index d78d270..c35876c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -49,6 +49,8 @@ interface EmailMessageSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: EmailMessageSco) { + requireStixType(this.stixType, obj) + obj.fromRef?.let { require(it.type == EmailAddressSco.stixType, lazyMessage = { "from_ref must be references to email-address SCO" }) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt index 2fb474d..7e28757 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -54,6 +54,8 @@ interface FileSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: FileSco) { + requireStixType(this.stixType, obj) + obj.size?.let { require(it.value >= 0, lazyMessage = { "size must not be a negative number." }) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt index 0243985..4c84fb0 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt @@ -44,6 +44,8 @@ interface IPv4AddressSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: IPv4AddressSco) { + requireStixType(this.stixType, obj) + } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt index 6283ff5..f3f6f4b 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt @@ -44,6 +44,8 @@ interface IPv6AddressSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: IPv6AddressSco) { + requireStixType(this.stixType, obj) + } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt index a4b2117..055e063 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt @@ -33,6 +33,8 @@ interface MacAddressSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: MacAddressSco) { + requireStixType(this.stixType, obj) + //@TODO The MAC address value ​MUST​ be represented as a single colon-delimited, lowercase MAC-48 address, which ​MUST​ include leading zeros for each octet. } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt index 8898d1a..6562d74 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt @@ -33,6 +33,8 @@ interface MutexSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: MutexSco) { + requireStixType(this.stixType, obj) + } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt index abcad55..31dbb74 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt @@ -60,6 +60,8 @@ interface NetworkTrafficSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: NetworkTrafficSco) { + requireStixType(this.stixType, obj) + obj.isActive?.let { if (it.value) { require(obj.end == null, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt index 7b24ec5..02a0ed5 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt @@ -49,6 +49,8 @@ interface ProcessSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: ProcessSco) { + requireStixType(this.stixType, obj) + require(obj.openedConnectionRef?.type == NetworkTrafficSco.stixType, lazyMessage = { "opened_connection_ref must only reference network-traffic SCO." }) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt index 1bf6eb2..fa663a8 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt @@ -41,6 +41,7 @@ interface SoftwareSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: SoftwareSco) { + requireStixType(this.stixType, obj) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt index e59549c..5f4c7b8 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt @@ -34,7 +34,7 @@ interface UrlSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: UrlSco) { - + requireStixType(this.stixType, obj) } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt index cb05052..319b257 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt @@ -52,7 +52,7 @@ interface UserAccountSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: UserAccountSco) { - + requireStixType(this.stixType, obj) } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt index 6437192..62c461a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt @@ -42,6 +42,7 @@ interface WindowsRegistryKeySco : StixCyberObservableObject { ) override fun objectValidationRules(obj: WindowsRegistryKeySco) { + requireStixType(this.stixType, obj) require( listOf(obj.key, obj.values, obj.modifiedTimed, obj.creatorUserRef, obj.numberOfSubkeys) .any { it != null }, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt index d8d3127..5b4ffae 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt @@ -48,6 +48,8 @@ interface X509CertificateSco : StixCyberObservableObject { ) override fun objectValidationRules(obj: X509CertificateSco) { + requireStixType(this.stixType, obj) + require(listOf( //@TODO review against Stix 2 Issues against 182 obj.isSelfSigned, obj.hashes, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt index 7453845..4fef6c3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -21,7 +22,7 @@ interface AttackPatternSdo : StixDomainObject { override val stixType = StixType("attack-pattern") override fun objectValidationRules(obj: AttackPatternSdo) { - + requireStixType(this.stixType, obj) } override val allowedRelationships: List = listOf( diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt index 0ca3867..1ce2b63 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -21,6 +22,7 @@ interface CampaignSdo : StixDomainObject { CompanionAllowedRelationships { override fun objectValidationRules(obj: CampaignSdo) { + requireStixType(this.stixType, obj) if (obj.firstSeen != null){ require(obj.lastSeen?.instant!!.isAfter(obj.firstSeen!!.instant)) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt index ff0d079..712be95 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -24,6 +25,8 @@ interface CourseOfActionSdo : StixDomainObject { override val stixType = StixType("course-of-action") override fun objectValidationRules(obj: CourseOfActionSdo) { + requireStixType(this.stixType, obj) + if (obj.actionReference != null){ require(obj.actionBin == null, lazyMessage = {"action_bin must not be present if action_reference is provided."}) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt index 7ae060e..b19bbcc 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -22,7 +23,7 @@ interface GroupingSdo : StixDomainObject { override val stixType = StixType("grouping") override fun objectValidationRules(obj: GroupingSdo) { - + requireStixType(this.stixType, obj) } override val allowedRelationships: List = listOf() diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt index 47f60f9..ce91d24 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -26,7 +27,7 @@ interface IdentitySdo : StixDomainObject { override val stixType = StixType("identity") override fun objectValidationRules(obj: IdentitySdo) { - + requireStixType(this.stixType, obj) } override val allowedRelationships: List = listOf( diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index c410dba..dd1a968 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -29,6 +30,8 @@ interface IndicatorSdo : StixDomainObject { override val stixType = StixType("indicator") override fun objectValidationRules(obj: IndicatorSdo) { + requireStixType(this.stixType, obj) + require(obj.validUntil?.instant!!.isAfter(obj.validFrom.instant), lazyMessage = {"valid_until must come after valid_from."}) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index 04ff675..dede683 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.objects.DomainNameSco import com.stephenott.stix.objects.core.sco.objects.IPv4AddressSco @@ -31,6 +32,8 @@ interface InfrastructureSdo : StixDomainObject { override val stixType = StixType("infrastructure") override fun objectValidationRules(obj: InfrastructureSdo) { + requireStixType(this.stixType, obj) + if (obj.firstSeen != null && obj.lastSeen != null){ require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt index a7e8cc5..9d36066 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -27,6 +28,8 @@ interface IntrusionSetSdo : StixDomainObject { override val stixType = StixType("intrusion-set") override fun objectValidationRules(obj: IntrusionSetSdo) { + requireStixType(this.stixType, obj) + if (obj.firstSeen != null && obj.lastSeen != null){ require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant, lazyMessage = {"last_seen must be equal or greater than first_seen."}) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt index 35ba5c3..5b8f651 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -30,6 +31,8 @@ interface LocationSdo : StixDomainObject { override val stixType = StixType("location") override fun objectValidationRules(obj: LocationSdo) { + requireStixType(this.stixType, obj) + if (obj.latitude != null) require(obj.longitude != null, lazyMessage = { "longitude must be provided when latitude is used." }) if (obj.longitude != null) require(obj.latitude != null, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt index 4affbba..39302d1 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sco.objects.* import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship @@ -32,6 +33,8 @@ interface MalwareSdo : StixDomainObject { override val stixType = StixType("malware") override fun objectValidationRules(obj: MalwareSdo) { + requireStixType(this.stixType, obj) + if (obj.firstSeen != null && obj.lastSeen != null) { require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant, lazyMessage = { "last_seen must greater than or equal to first_seen." }) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt index 02dcf55..eaa8636 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sco.objects.SoftwareSco import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship @@ -33,6 +34,8 @@ interface MalwareAnalysisSdo : StixDomainObject { override val stixType = StixType("malware-analysis") override fun objectValidationRules(obj: MalwareAnalysisSdo) { + requireStixType(this.stixType, obj) + //@TODO Product Name Validation enhancement: The name of the analysis engine or product that was used. Product names ​SHOULD​ be all lowercase with words separated by a dash "-". For cases where the name of a product cannot be specified, a value of "anonymized" MUST ​be used. require(obj.hostVmRef?.type == SoftwareSco.stixType, diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt index e06909c..4de187d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -21,6 +22,7 @@ interface NoteSdo : StixDomainObject { override val stixType = StixType("note") override fun objectValidationRules(obj: NoteSdo) { + requireStixType(this.stixType, obj) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt index 5ebed15..86b9224 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt @@ -1,9 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects -import com.stephenott.stix.common.BusinessRulesValidator -import com.stephenott.stix.common.CompanionAllowedRelationships -import com.stephenott.stix.common.CompanionStixType -import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship @@ -23,6 +20,8 @@ interface ObservedDataSdo : StixDomainObject { override val stixType = StixType("observed-data") override fun objectValidationRules(obj: ObservedDataSdo) { + requireStixType(this.stixType, obj) + require(obj.lastObserved.instant >= obj.firstObserved.instant, lazyMessage = { "last_observed must be greater than or equal to first_observed." }) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt index 8b56a69..0ba2131 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -22,6 +23,7 @@ interface OpinionSdo : StixDomainObject { override val stixType = StixType("opinion") override fun objectValidationRules(obj: OpinionSdo) { + requireStixType(this.stixType, obj) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt index a40f979..48f84c7 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -23,6 +24,7 @@ interface ReportSdo : StixDomainObject { override val stixType = StixType("report") override fun objectValidationRules(obj: ReportSdo) { + requireStixType(this.stixType, obj) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt index 6cd55aa..9192aca 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -31,6 +32,8 @@ interface ThreatActorSdo : StixDomainObject { override val stixType = StixType("threat-actor") override fun objectValidationRules(obj: ThreatActorSdo) { + requireStixType(this.stixType, obj) + if (obj.firstSeen != null && obj.lastSeen != null){ require(obj.lastSeen!!.instant >= obj.firstSeen!!.instant, lazyMessage = {"last_seen must be greater than or equal to first_Seen."}) diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index 28e11ca..2baff99 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -25,6 +26,7 @@ interface ToolSdo : StixDomainObject { override val stixType = StixType("tool") override fun objectValidationRules(obj: ToolSdo) { + requireStixType(this.stixType, obj) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt index 8212d52..4b0b843 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro @@ -19,7 +20,7 @@ interface VulnerabilitySdo : StixDomainObject { override val stixType = StixType("vulnerability") override fun objectValidationRules(obj: VulnerabilitySdo) { - + requireStixType(this.stixType, obj) } override val allowedRelationships: List = listOf() diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index b03f122..910cada 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -22,6 +22,7 @@ interface RelationshipSro : StixRelationshipObject { override val stixType = StixType("relationship") override fun objectValidationRules(obj: RelationshipSro) { + requireStixType(this.stixType, obj) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt index 7d5a514..b2531b9 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.objects.core.sro.objects import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.objects.IdentitySdo import com.stephenott.stix.objects.core.sdo.objects.LocationSdo import com.stephenott.stix.objects.core.sdo.objects.ObservedDataSdo @@ -25,6 +26,8 @@ interface SightingSro : StixRelationshipObject { override val stixType: StixType = StixType("sighting") override fun objectValidationRules(obj: SightingSro) { + requireStixType(this.stixType, obj) + if (obj.firstSeen != null && obj.lastSeen != null) { require(obj.lastSeen!!.instant.isAfter(obj.firstSeen!!.instant), lazyMessage = { "last_seen must be later than first_seen." }) diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt index 1a564f2..49e3f7c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt @@ -1,6 +1,11 @@ package com.stephenott.stix.objects.meta.datamarking +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.meta.lco.objects.LanguageContentLco import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv @@ -9,8 +14,20 @@ interface MarkingDefinitionDm: DataMarking{ val definitionType: MarkingDefinitionTypeOv val definition: MarkingObject - companion object { - val stixType = StixType("marking-definition") + companion object: CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { + + override val stixType = StixType("marking-definition") + + override val allowedRelationships: List = listOf( + //@TODO review if this is needed for this object + ) + + override fun objectValidationRules(obj: MarkingDefinitionDm) { + requireStixType(this.stixType, obj) + } + } } diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt index 021b0f3..e41752a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt @@ -1,7 +1,12 @@ package com.stephenott.stix.objects.meta.lco.objects +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionAllowedRelationships +import com.stephenott.stix.common.CompanionStixType +import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship +import com.stephenott.stix.objects.core.sro.objects.SightingSro import com.stephenott.stix.objects.meta.lco.LanguageContentObject import com.stephenott.stix.type.* @@ -10,10 +15,16 @@ interface LanguageContentLco : LanguageContentObject { val objectModified: StixInstant? val contents: LanguageContentDictionary - companion object { - val stixType = StixType("language-content") + companion object: CompanionStixType, + BusinessRulesValidator, + CompanionAllowedRelationships { + override fun objectValidationRules(obj: LanguageContentLco) { + requireStixType(this.stixType, obj) + } - val allowedRelationships: List = listOf( + override val stixType = StixType("language-content") + + override val allowedRelationships: List = listOf( ) } diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index 75319de..d869183 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -29,6 +29,7 @@ fun createStixMapper(): ObjectMapper { .registerModule(createStixContentSerializationModule()) .registerModule(createRelationshipTypeSerializationModule()) .registerModule(createStixIntegerSerializationModule()) + .registerModule(createStixConfidenceSerializationModule()) } class StixContentMapper(){ diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixConfidenceSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixConfidenceSerialization.kt new file mode 100644 index 0000000..cfb3b0e --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixConfidenceSerialization.kt @@ -0,0 +1,38 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.StixConfidence +import com.stephenott.stix.type.StixType + + +fun createStixConfidenceSerializationModule(): SimpleModule{ + return SimpleModule() + .addSerializer(StixConfidence::class.java, StixConfidenceSerializer()) + .addDeserializer(StixConfidence::class.java, StixConfidenceDeserializer()) +} + +class StixConfidenceSerializer() : StdSerializer(StixConfidence::class.java) { + override fun isEmpty(provider: SerializerProvider?, value: StixConfidence?): Boolean { + return value == null || value.stixValue == null + } + + override fun serialize(value: StixConfidence?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeNumber(value!!.getConfidence()) + } +} + +class StixConfidenceDeserializer() : StdDeserializer(StixConfidence::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixConfidence { + if (p!!.numberType == JsonParser.NumberType.INT){ + return StixConfidence(p.intValue) + } else { + throw IllegalArgumentException("Invalid confidence value.") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/Product.kt b/src/main/kotlin/com/stephenott/stix/type/Product.kt index c513078..67f857b 100644 --- a/src/main/kotlin/com/stephenott/stix/type/Product.kt +++ b/src/main/kotlin/com/stephenott/stix/type/Product.kt @@ -1,7 +1,11 @@ package com.stephenott.stix.type -class Product(private val product: String): CharSequence by product{ +data class Product(private val product: String){ init { require(product.none { it.isWhitespace() && it.isUpperCase() }) } + + override fun toString(): String { + return product + } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt b/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt index bcfc1a2..852246c 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixConfidence.kt @@ -1,19 +1,36 @@ package com.stephenott.stix.type import java.lang.IllegalArgumentException +import java.lang.IllegalStateException + +/** + * stixValue must be provided to be considered a valid StixConfidence. + * + */ +data class StixConfidence(val realValue: Int?, val stixValue: Int?, val scaleValue: String?) { + + constructor(confidence: Int): this(null, confidence, null) -data class StixConfidence(val realValue: Int?, val stixValue: Int?, val scaleValue: String) { init { - if (realValue != null) { + realValue?.let { require(stixValue != null) - require(realValue in 0..100) - } - if (stixValue != null) { - require(realValue != null) + stixValue?.let { require(stixValue in 0..100) } - require(scaleValue.isNotEmpty()) + scaleValue?.let { + require(it.isNotEmpty()) + } + + require(realValue != null || stixValue != null || scaleValue != null) + } + + fun getConfidence(): Int{ + if (stixValue != null){ + return stixValue + } else { + throw IllegalStateException("Unable to determine confidence value because it is null.") + } } companion object { From b0feee71b67184f1ea921793d60897a9df39d2b8 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Fri, 8 Nov 2019 20:00:35 -0500 Subject: [PATCH 15/21] refactor to support better content mapping with json --- .../kotlin/com/stephenott/stix/MainRunner.kt | 88 +++++++++++------ .../stix/common/StixContentMapper.kt | 9 ++ .../stix/common/StixMarkingObjectRegistry.kt | 2 +- .../stix/common/StixObjectRegistry.kt | 97 ++++++++++++++++++- .../objects/core/sdo/objects/AttackPattern.kt | 2 +- .../objects/core/sdo/objects/Indicator.kt | 2 +- .../core/sdo/objects/Infrastructure.kt | 2 +- .../stix/objects/core/sdo/objects/Tool.kt | 2 +- .../meta/datamarking/MarkingDefinition.kt | 8 +- .../objects/meta/datamarking/MarkingObject.kt | 7 ++ .../meta/datamarking/objects/Statement.kt | 7 +- .../objects/meta/datamarking/objects/Tlp.kt | 6 ++ .../meta/lco/objects/LanguageContent.kt | 4 + .../stix/serialization/json/JsonExtensions.kt | 64 +++--------- .../json/StixContentSerialization.kt | 80 ++++++++++----- .../json/StixMarkingObjectSerialization.kt | 36 +++++++ .../json/StixOpenVocabSerialization.kt | 32 ++++++ .../json/StixTypeSerialization.kt | 20 +++- .../stephenott/stix/type/HashesDictionary.kt | 7 +- .../stix/type/{vocab => }/KillChainPhases.kt | 2 +- .../stix/type/vocab/AccountTypeOv.kt | 5 +- .../stix/type/vocab/AdministrativeAreas.kt | 8 +- .../stix/type/vocab/AttackMotivationOv.kt | 7 +- .../stix/type/vocab/AttackResourceLevelOv.kt | 6 +- .../com/stephenott/stix/type/vocab/Cities.kt | 6 +- .../stephenott/stix/type/vocab/ClosedVocab.kt | 3 + .../stix/type/vocab/CourseOfActionTypeOv.kt | 8 +- .../type/vocab/EncryptionAlgorithmEnum.kt | 8 +- .../stix/type/vocab/GroupingContextOv.kt | 8 +- .../stix/type/vocab/IdentityClassOv.kt | 8 +- .../stix/type/vocab/IdentityRoles.kt | 6 +- .../type/vocab/ImplementationLanguageOv.kt | 8 +- .../stix/type/vocab/IndicatorTypeOv.kt | 7 +- .../stix/type/vocab/IndustrySectorOv.kt | 9 +- .../stix/type/vocab/InfrastructureTypeOv.kt | 8 +- .../stix/type/vocab/LanguageCodes.kt | 7 +- .../stix/type/vocab/MalwareAvResultOv.kt | 7 +- .../stix/type/vocab/MalwareCapabilitiesOv.kt | 8 +- .../stix/type/vocab/MalwareTypeOv.kt | 9 +- .../type/vocab/MarkingDefinitionTypeOv.kt | 9 +- .../vocab/NetworkSocketAddressFamilyEnum.kt | 8 +- .../stix/type/vocab/NetworkSocketTypeEnum.kt | 8 +- .../stephenott/stix/type/vocab/OpenVocab.kt | 2 + .../stephenott/stix/type/vocab/OpinionEnum.kt | 8 +- .../stix/type/vocab/PatternTypes.kt | 9 +- .../type/vocab/ProcessorArchitectureOv.kt | 8 +- .../stephenott/stix/type/vocab/RegionOv.kt | 8 +- .../stix/type/vocab/ReportTypeOv.kt | 7 +- .../stix/type/vocab/ThreatActorRoleOv.kt | 8 +- .../type/vocab/ThreatActorSophisticationOv.kt | 8 +- .../stix/type/vocab/ThreatActorTypeOv.kt | 8 +- .../stephenott/stix/type/vocab/ToolTypeOv.kt | 6 +- .../type/vocab/WindowsIntegrityLevelEnum.kt | 7 +- .../stix/type/vocab/WindowsPebinaryTypeOv.kt | 8 +- .../type/vocab/WindowsRegistryDataTypeEnum.kt | 8 +- .../type/vocab/WindowsServiceStartTypeEnum.kt | 8 +- .../type/vocab/WindowsServiceStatusEnum.kt | 8 +- .../stix/type/vocab/WindowsServiceTypeEnum.kt | 9 +- 58 files changed, 571 insertions(+), 187 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixOpenVocabSerialization.kt rename src/main/kotlin/com/stephenott/stix/type/{vocab => }/KillChainPhases.kt (94%) diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index eb9aece..313c717 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -11,14 +11,20 @@ import com.stephenott.stix.objects.core.sro.objects.Relationship import com.stephenott.stix.serialization.json.createStixMapper import com.stephenott.stix.objects.core.sdo.StixDomainObject.* import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo +import com.stephenott.stix.objects.core.sdo.objects.Campaign import com.stephenott.stix.objects.core.sdo.objects.ObservedData import com.stephenott.stix.objects.core.sro.StixRelationshipObject import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.objects.core.sro.objects.Sighting import com.stephenott.stix.objects.meta.StixMetaObject +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinitionDm +import com.stephenott.stix.objects.meta.datamarking.objects.Statement +import com.stephenott.stix.objects.meta.datamarking.objects.Tlp import com.stephenott.stix.serialization.json.StixContentMapper import com.stephenott.stix.serialization.json.toJson import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv import java.time.Instant object MainRunner { @@ -27,40 +33,58 @@ object MainRunner { fun main(args: Array){ val ap1 = AttackPattern(name = "124", confidence = StixConfidence(33)) - val ap2 = AttackPattern("1245") - val ip6 = IPv6Address("dog") - - val sighting1 = Sighting(sightingOfRef = ap1.id) - val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) - +// val ap2 = AttackPattern("1245") +// val ip6 = IPv6Address("dog") +// +// val sighting1 = Sighting(sightingOfRef = ap1.id) +// val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) +// val mapper = StixContentMapper() - println(net1.toJson(mapper)) - println(mapper.parseJson(net1.toJson(mapper))) - - val jsonString: String = ap1.toJson(mapper) - println(jsonString) - - println(mapper.parseJson(jsonString)) - println(mapper.parseJson(jsonString)) - println(mapper.parseJson(jsonString)) - println(mapper.parseJson(jsonString)) - println(mapper.parseJson(jsonString)) - println(mapper.parseJson(jsonString)) - - - val rel = Relationship( - relationshipType = RelationshipType("duplicate-of"), - sourceRef = ap1, - targetRef = ap2 - ) - println(mapper.parseJson(rel.toJson(mapper))) - println(mapper.parseJson(rel.toJson(mapper))) - println(mapper.parseJson(rel.toJson(mapper))) - println(mapper.parseJson(rel.toJson(mapper))) - println(mapper.parseJson(rel.toJson(mapper))) - println(mapper.parseJson(rel.toJson(mapper))) + println(ap1.toJson(mapper)) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) - println(mapper.parseJson(sighting1.toJson(mapper))) +// println(net1.toJson(mapper)) +// println(mapper.parseJson(net1.toJson(mapper))) +// +// val jsonString: String = ap1.toJson(mapper) +// println(jsonString) +// +// println(mapper.parseJson(jsonString)) +// println(mapper.parseJson(jsonString)) +// println(mapper.parseJson(jsonString)) +// println(mapper.parseJson(jsonString)) +// println(mapper.parseJson(jsonString)) +// println(mapper.parseJson(jsonString)) +// +// +// val rel = Relationship( +// relationshipType = RelationshipType("duplicate-of"), +// sourceRef = ap1, +// targetRef = ap2 +// ) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// println(mapper.parseJson(rel.toJson(mapper))) +// +// println(mapper.parseJson(sighting1.toJson(mapper))) +// +// val markDef1 = MarkingDefinition("test", MarkingDefinitionTypeOv("tlp"), +// Tlp("white")) +// println(mapper.parseJson(markDef1.toJson(mapper))) +// println(markDef1.toJson(mapper)) +// +// val markDef2 = MarkingDefinition("test2", MarkingDefinitionTypeOv("statement"), +// Statement("white-statement")) +// println(mapper.parseJson(markDef2.toJson(mapper))) +// println(markDef2.toJson(mapper)) } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt b/src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt new file mode 100644 index 0000000..9450cfc --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt @@ -0,0 +1,9 @@ +package com.stephenott.stix.common + +interface StixContentMapper { + + val markingObjectRegistry: StixMarkingObjectRegistry + val objectRegistry: StixObjectRegistry + val relationshipRegistry: StixObjectRelationshipRegistry + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt index a083a34..75cb916 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt @@ -6,7 +6,7 @@ import com.stephenott.stix.objects.meta.datamarking.objects.Tlp import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv import kotlin.reflect.KClass -object StixMarkingObjectRegistry { +class StixMarkingObjectRegistry { val registry: MutableMap> = mutableMapOf( Pair(MarkingDefinitionTypeOv("statement"), Statement::class), diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt index 27f5559..30e6ac9 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -1,6 +1,5 @@ package com.stephenott.stix.common -import com.stephenott.stix.StixContent import com.stephenott.stix.objects.StixObject import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.objects.* @@ -11,13 +10,27 @@ import com.stephenott.stix.objects.core.sro.objects.Relationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.objects.core.sro.objects.Sighting import com.stephenott.stix.objects.core.sro.objects.SightingSro +import com.stephenott.stix.objects.meta.StixMetaObject +import com.stephenott.stix.objects.meta.datamarking.GranularMarkingGm +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinitionDm +import com.stephenott.stix.objects.meta.datamarking.MarkingObject +import com.stephenott.stix.objects.meta.datamarking.objects.Statement +import com.stephenott.stix.objects.meta.datamarking.objects.StatementMo +import com.stephenott.stix.objects.meta.datamarking.objects.Tlp +import com.stephenott.stix.objects.meta.datamarking.objects.TlpMo +import com.stephenott.stix.objects.meta.lco.objects.LanguageContent +import com.stephenott.stix.objects.meta.lco.objects.LanguageContentLco import com.stephenott.stix.type.StixType +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv import kotlin.reflect.KClass //@TODO move to a instance so it can be passed into content handlers (such as JSON content mapper) object StixObjectRegistry { - var sdoRegistry: Map> = mutableMapOf( + // STIX OBJECT Registry: + + val sdoRegistry: Map> = mapOf( Pair(AttackPatternSdo.stixType, AttackPattern::class), Pair(CampaignSdo.stixType, Campaign::class), Pair(CourseOfActionSdo.stixType, CourseOfAction::class), @@ -37,7 +50,7 @@ object StixObjectRegistry { Pair(VulnerabilitySdo.stixType, Vulnerability::class) ) - var scoRegistry: Map> = mutableMapOf( + val scoRegistry: Map> = mapOf( Pair(ArtifactSco.stixType, Artifact::class), Pair(AutonomousSystemSco.stixType, AutonomousSystem::class), Pair(DirectorySco.stixType, Directory::class), @@ -58,34 +71,110 @@ object StixObjectRegistry { Pair(X509CertificateSco.stixType, X509Certificate::class) ) - var sroRegistry: Map> = mutableMapOf( + val sroRegistry: Map> = mapOf( Pair(RelationshipSro.stixType, Relationship::class), Pair(SightingSro.stixType, Sighting::class) ) + val metaObjectRegistry: Map> = mapOf( + Pair(MarkingDefinitionDm.stixType, MarkingDefinition::class), + Pair(LanguageContentLco.stixType, LanguageContent::class) + ) + + /** + * Custom SDO Objects + */ var customSdoRegistry: Map> = mutableMapOf( ) + + /** + * Custom SCO Objects + */ var customScoRegistry: Map> = mutableMapOf( ) + + /** + * Custom SRO Objects + */ var customSroRegistry: Map> = mutableMapOf( ) + /** + * Custom Meta Objects + */ + var customMetaObjectRegistry: Map> = mutableMapOf( + ) + + /** + * Aggregate of the Stix Objects (spec defined and custom) + */ private fun aggregateObjects(): Map> { val objects = mutableMapOf>() objects.plusAssign(sdoRegistry) objects.plusAssign(scoRegistry) objects.plusAssign(sroRegistry) + objects.plusAssign(metaObjectRegistry) objects.plusAssign(customSdoRegistry) objects.plusAssign(customScoRegistry) objects.plusAssign(customSroRegistry) + objects.plusAssign(customMetaObjectRegistry) return objects } private var registryAggregate: Map> = aggregateObjects() + /** + * Refreshes the Stix Object registry / regenerates the aggregated map of StixType and KClasses. + * When you add new objects to the specific registries (SDO, SRO, Custom, etc) + * you need to refresh for the values to become available. + * + * Typically used for polymorphic serialization/deserialization. + * + * The Refresh allows new objects to be added at runtime. + */ fun refreshRegistry(){ registryAggregate = aggregateObjects() } + /** + * Master repository of all objects that are considered Stix Objects (Bundleable Objects) / tier 1 objects in a bundle. + */ val registry: Map> = registryAggregate + + + + // MARKING OBJECT REGISTRY: + + val markingObjectRegistry: Map> = mapOf( + Pair(TlpMo.definitionType, Tlp::class), + Pair(StatementMo.definitionType, Statement::class) + ) + + /** + * Custom Data Marking Objects + */ + var customMarkingObjectRegistry: Map> = mutableMapOf( + ) + + private fun aggregateMarkingObjects(): Map> { + val objects = mutableMapOf>() + objects.plusAssign(markingObjectRegistry) + objects.plusAssign(customMarkingObjectRegistry) + + return objects + } + + private var markingObjectRegistryAggregate: Map> = aggregateMarkingObjects() + + /** + * Refreshes the Marking Object registry + * + * Typically used for polymorphic serialization/deserialization. + * + * The Refresh allows new objects to be added at runtime. + */ + fun refreshMarkingObjectRegistry(){ + markingObjectRegistryAggregate = aggregateMarkingObjects() + } + } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt index 4fef6c3..0d112d0 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt @@ -8,7 +8,7 @@ import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.KillChainPhases interface AttackPatternSdo : StixDomainObject { val name: String diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index dd1a968..a4ae595 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -9,7 +9,7 @@ import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.IndicatorTypes -import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.KillChainPhases import com.stephenott.stix.type.vocab.PatternType interface IndicatorSdo : StixDomainObject { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index dede683..57e0efa 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -14,7 +14,7 @@ import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.InfrastructureTypes -import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.KillChainPhases interface InfrastructureSdo : StixDomainObject { val name: String diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index 2baff99..724e80e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -8,7 +8,7 @@ import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.KillChainPhases +import com.stephenott.stix.type.KillChainPhases import com.stephenott.stix.type.vocab.ToolTypes interface ToolSdo : StixDomainObject { diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt index 49e3f7c..7828cd2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt @@ -5,7 +5,6 @@ import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship -import com.stephenott.stix.objects.meta.lco.objects.LanguageContentLco import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv @@ -18,6 +17,8 @@ interface MarkingDefinitionDm: DataMarking{ BusinessRulesValidator, CompanionAllowedRelationships { + const val definitionPolymorphicFieldName: String = "definition_type" + override val stixType = StixType("marking-definition") override val allowedRelationships: List = listOf( @@ -46,6 +47,11 @@ data class MarkingDefinition( ): MarkingDefinitionDm { + init { + MarkingDefinitionDm.objectValidationRules(this) + } + + override fun allowedRelationships(): List { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt index 68efc15..2de3953 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingObject.kt @@ -1,5 +1,12 @@ package com.stephenott.stix.objects.meta.datamarking +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv + interface MarkingObject { + +} + +interface MarkingObjectCompanionDefType{ + val definitionType: MarkingDefinitionTypeOv } diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt index 35a09cb..dd61866 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Statement.kt @@ -1,13 +1,18 @@ package com.stephenott.stix.objects.meta.datamarking.objects import com.stephenott.stix.objects.meta.datamarking.MarkingObject +import com.stephenott.stix.objects.meta.datamarking.MarkingObjectCompanionDefType +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv interface StatementMo : MarkingObject { + companion object: MarkingObjectCompanionDefType { + override val definitionType: MarkingDefinitionTypeOv = MarkingDefinitionTypeOv("statement") + } + val statement: String } data class Statement( override val statement: String - ): StatementMo \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt index dc65502..f7bc398 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/objects/Tlp.kt @@ -1,9 +1,15 @@ package com.stephenott.stix.objects.meta.datamarking.objects import com.stephenott.stix.objects.meta.datamarking.MarkingObject +import com.stephenott.stix.objects.meta.datamarking.MarkingObjectCompanionDefType +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv interface TlpMo : MarkingObject { + companion object: MarkingObjectCompanionDefType{ + override val definitionType: MarkingDefinitionTypeOv = MarkingDefinitionTypeOv("tlp") + } + val tlp: String } diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt index e41752a..d3f0a1d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt @@ -49,6 +49,10 @@ data class LanguageContent( ) : LanguageContentLco { + init { + LanguageContentLco.objectValidationRules(this) + } + override fun allowedRelationships(): List { return LanguageContentLco.allowedRelationships } diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index d869183..bb4f909 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -5,20 +5,17 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule import com.stephenott.stix.StixBundle import com.stephenott.stix.StixContent import kotlin.reflect.full.cast fun createStixMapper(): ObjectMapper { - return jacksonObjectMapper() + return ObjectMapper() + .registerModule(KotlinModule()) //@TODO see jackson kotlin module issue #87. Waiting for fix. Currently if a subtype does not exist then it fails to provide a meaningful error. .registerModule(JavaTimeModule()) - .registerModule(createStixSdoModule()) - .registerModule(createStixSroModule()) - .registerModule(createStixScoModule()) - .registerModule(createStixMetaObjectModule()) - .registerModule(createStixBundleModule()) - .registerModule(createStixCustomObjectsModule()) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .registerModule(createStixInstantSerializationModule()) @@ -30,6 +27,8 @@ fun createStixMapper(): ObjectMapper { .registerModule(createRelationshipTypeSerializationModule()) .registerModule(createStixIntegerSerializationModule()) .registerModule(createStixConfidenceSerializationModule()) + .registerModule(createStixOpenVocabSerializationModule()) + .registerModule(createStixMarkingObjectSerializationModule()) } class StixContentMapper(){ @@ -42,59 +41,24 @@ class StixContentMapper(){ * Parse a json string into any kind of Stix Content (SDO, SCO, SRO, Relationships, etc) */ inline fun parseJson(json: String): T { - val content: StixContent +// val content: T try { - content = jsonMapper.readValue(json, StixContent::class.java) + return jsonMapper.readValue(json, T::class.java) +// return content } catch (e: Exception){ throw IllegalArgumentException("Unable to parse json.", e) } - try { - return T::class.cast(content) - } catch (e: Exception){ - throw IllegalArgumentException("Unable to parse json.", e) - } +// try { +// return T::class.cast(content) +// } catch (e: Exception){ +// throw IllegalArgumentException("Unable to parse json.", e) +// } } } -fun createStixSdoModule(): SimpleModule { - val module = SimpleModule() - - return module -} - -fun createStixScoModule(): SimpleModule { - val module = SimpleModule() - - return module -} - -fun createStixSroModule(): SimpleModule { - val module = SimpleModule() - - return module -} - -fun createStixMetaObjectModule(): SimpleModule { - val module = SimpleModule() - - return module -} - -fun createStixBundleModule(): SimpleModule { - val module = SimpleModule() - - return module -} - -fun createStixCustomObjectsModule(): SimpleModule { - val module = SimpleModule() - - return module -} - fun StixContent.toJson(mapper: StixContentMapper): String{ return mapper.jsonMapper.writeValueAsString(this) } diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt index 89a1f2f..5f6d4e0 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt @@ -1,39 +1,69 @@ package com.stephenott.stix.serialization.json -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.databind.jsontype.NamedType import com.fasterxml.jackson.databind.module.SimpleModule import com.stephenott.stix.StixContent import com.stephenott.stix.common.StixObjectRegistry -import com.stephenott.stix.objects.StixObject -import com.stephenott.stix.type.StixType -import kotlin.reflect.KClass fun createStixContentSerializationModule(): SimpleModule { - return SimpleModule() - .addDeserializer(StixContent::class.java, StixContentDeserializer()) -} + val module: SimpleModule = SimpleModule() -class StixContentDeserializer() : StdDeserializer(StixContent::class.java) { + StixObjectRegistry.registry.forEach { (type, clazz) -> + module.registerSubtypes(NamedType(clazz.java, type.toString())) + } - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixContent { + module.setMixInAnnotation(StixContent::class.java, StixContentTypeMixin::class.java) - val node: JsonNode = p!!.codec.readTree(p) - val typeProp = node.get("type") - if (typeProp != null) { - val stixType = StixType.parse(typeProp.asText()) + return module +} - val objectClass: KClass = StixObjectRegistry.registry.getOrElse(stixType, defaultValue = { - throw IllegalArgumentException("Unable to parse the object. Ensure object type is supported.") - }) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + visible = true, + property = "type" +) +interface StixContentTypeMixin { +} - return p.codec.treeToValue(node, objectClass.java) - } else { - throw IllegalArgumentException("type property was null or not provided.") - } - } -} \ No newline at end of file +//class StixContentSerializer(): StdSerializer(StixContent::class.java) { +// override fun serializeWithType( +// value: StixContent?, +// gen: JsonGenerator?, +// serializers: SerializerProvider?, +// typeSer: TypeSerializer? +// ) { +// +// super.serializeWithType(value, gen, serializers, typeSer) +// } +// +// override fun serialize(value: StixContent?, gen: JsonGenerator?, provider: SerializerProvider?) { +// +// } +// +//} + + +//class StixContentDeserializer() : StdDeserializer(StixContent::class.java) { +// +// override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixContent { +// +// val node: JsonNode = p!!.codec.readTree(p) +// val typeProp = node.get("type") +// if (typeProp != null) { +// val stixType = StixType.parse(typeProp.asText()) +// +// val objectClass: KClass = StixObjectRegistry.registry.getOrElse(stixType, defaultValue = { +// throw IllegalArgumentException("Unable to parse the object. Ensure object type is supported.") +// }) +// +// return p.codec.treeToValue(node, objectClass.java) +// +// } else { +// throw IllegalArgumentException("type property was null or not provided.") +// } +// } +//} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt new file mode 100644 index 0000000..7c091fa --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt @@ -0,0 +1,36 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.databind.jsontype.NamedType +import com.fasterxml.jackson.databind.module.SimpleModule +import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinitionDm +import com.stephenott.stix.objects.meta.datamarking.MarkingObject +import com.stephenott.stix.objects.meta.datamarking.objects.Statement +import com.stephenott.stix.objects.meta.datamarking.objects.Tlp + +fun createStixMarkingObjectSerializationModule(): SimpleModule { + + val module = SimpleModule() + + // Registers each markingObject that is located in the MarkingObjectRegistry + //@TODO review implications for race event where these will not be loaded when create the StixMapper class. Likely will want to pass the data into the function. + StixObjectRegistry.markingObjectRegistry.forEach { (typeOv, clazz) -> + module.registerSubtypes(NamedType(clazz.java, typeOv.getValue())) + } + + module.setMixInAnnotation(MarkingDefinition::class.java, MarkingObjectDefinitionTypeMixin::class.java) + + return module +} + +abstract class MarkingObjectDefinitionTypeMixin { + + @get:JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = MarkingDefinitionDm.definitionPolymorphicFieldName + ) + abstract val definition: MarkingObject +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixOpenVocabSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixOpenVocabSerialization.kt new file mode 100644 index 0000000..90fe8be --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixOpenVocabSerialization.kt @@ -0,0 +1,32 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.* +import com.fasterxml.jackson.databind.deser.ContextualDeserializer +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import com.stephenott.stix.type.vocab.OpenVocab + + +fun createStixOpenVocabSerializationModule(): SimpleModule { + return SimpleModule() + .addSerializer(OpenVocab::class.java, StixOpenVocabSerializer()) + .addDeserializer(OpenVocab::class.java, StixOpenVocabDeserializer()) +} + +class StixOpenVocabSerializer() : StdSerializer(OpenVocab::class.java) { + override fun serialize(value: OpenVocab?, gen: JsonGenerator?, provider: SerializerProvider?) { + gen!!.writeString(value!!.getValue()) + } +} + +class StixOpenVocabDeserializer() : StdDeserializer(OpenVocab::class.java) { + + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): OpenVocab { + println(ctxt!!.contextualType) + + return p!!.readValueAs(ctxt.contextualType.rawClass) as OpenVocab + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt index 501b396..c748284 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt @@ -2,9 +2,13 @@ package com.stephenott.stix.serialization.json import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.JsonToken +import com.fasterxml.jackson.core.type.WritableTypeId import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer +import com.fasterxml.jackson.databind.jsontype.TypeSerializer import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.ser.std.StdSerializer import com.stephenott.stix.type.StixType @@ -17,17 +21,31 @@ fun createStixTypeSerializationModule(): SimpleModule{ } class StixTypeSerializer() : StdSerializer(StixType::class.java) { + +// override fun serializeWithType( +// value: StixType?, +// gen: JsonGenerator?, +// serializers: SerializerProvider?, +// typeSer: TypeSerializer? +// ) { +// val typeId: WritableTypeId = typeSer!!.typeId(value, JsonToken.VALUE_STRING) +// gen!!.writeString(value.toString()) +// typeId.wrapperWritten = !gen.canWriteTypeId() +// typeSer.writeTypeSuffix(gen, typeId) +// } + override fun serialize(value: StixType?, gen: JsonGenerator?, provider: SerializerProvider?) { gen!!.writeString(value!!.toString()) } } class StixTypeDeserializer() : StdDeserializer(StixType::class.java) { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixType { try { return StixType.parse(p!!.text) } catch (e: Exception){ - throw IllegalArgumentException("Unable to parse type.", e) + throw IllegalArgumentException("Unable to parse type property.", e) } } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt index 687a3e6..9742fb8 100644 --- a/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt +++ b/src/main/kotlin/com/stephenott/stix/type/HashesDictionary.kt @@ -6,10 +6,13 @@ class HashesDictionary(private val immutableDictionary: LinkedHashMap by immutableDictionary { } -class HashingAlgorithm(private val algorithm: String): OpenVocab, CharSequence by algorithm{ +class HashingAlgorithm(private val algorithm: String): OpenVocab { + override fun getValue(): String { + return algorithm + } companion object{ - const val VOCAB_NAME: String = "hash-algorithm-ov​" + const val vocabName: String = "hash-algorithm-ov​" var vocab: LinkedHashSet = linkedSetOf( "MD5", "MD6", "RIPEMD-160", "SHA-1", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt b/src/main/kotlin/com/stephenott/stix/type/KillChainPhases.kt similarity index 94% rename from src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt rename to src/main/kotlin/com/stephenott/stix/type/KillChainPhases.kt index 80d41c5..75f7722 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/KillChainPhases.kt +++ b/src/main/kotlin/com/stephenott/stix/type/KillChainPhases.kt @@ -1,4 +1,4 @@ -package com.stephenott.stix.type.vocab +package com.stephenott.stix.type class KillChainPhases(private val phases: LinkedHashSet) : Set by phases { diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt index e49cc4c..692f5e2 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AccountTypeOv.kt @@ -1,10 +1,13 @@ package com.stephenott.stix.type.vocab class AccountType(private val type: String) : OpenVocab, CharSequence by type { + override fun getValue(): String { + return type + } companion object { - val vocabName = "account-type-ov" + const val vocabName = "account-type-ov" var vocab: LinkedHashSet = linkedSetOf( "facebook", "ldap", "nis", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt index dc7670e..8a4d8c5 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AdministrativeAreas.kt @@ -10,7 +10,11 @@ class AdministrativeAreas(private val areas: LinkedHashSet = } } -class AdministrativeArea(private val area: String, enforceVocab: Boolean = false) : OpenVocab, CharSequence by area { +class AdministrativeArea(private val area: String, enforceVocab: Boolean = false) : OpenVocab { + + override fun getValue(): String { + return area + } init { if (enforceVocab) { @@ -20,7 +24,7 @@ class AdministrativeArea(private val area: String, enforceVocab: Boolean = false companion object { - val vocabName = "administrative-areas" + const val vocabName = "administrative-areas" var vocab: LinkedHashSet = linkedSetOf( ) diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt index 1776566..90f3c64 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackMotivationOv.kt @@ -4,10 +4,13 @@ class AttackMotivations(private val motivations: LinkedHashSet by motivations { } -class AttackMotivationOv(private val motivation: String) : OpenVocab, CharSequence by motivation { +class AttackMotivationOv(private val motivation: String) : OpenVocab { + override fun getValue(): String { + return motivation + } companion object { - val vocabName = "attack-motivation-ov" + const val vocabName = "attack-motivation-ov" val vocab: LinkedHashSet = linkedSetOf( "accidental", "coercion", "dominance", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt index 2f8afe8..b997d1b 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/AttackResourceLevelOv.kt @@ -2,8 +2,12 @@ package com.stephenott.stix.type.vocab class AttackResourceLevelOv(private val level: String) : OpenVocab, CharSequence by level { + override fun getValue(): String { + return level + } + companion object { - val vocabName = "attack-resource-level-ov" + const val vocabName = "attack-resource-level-ov" val vocab: LinkedHashSet = linkedSetOf( "individual", "club", "contest", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt index 7a0431c..2ef7e00 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/Cities.kt @@ -12,6 +12,10 @@ class Cities(private val cities: LinkedHashSet = linkedSetOf(), enforceVoc class City(private val city: String, enforceVocab: Boolean = false) : OpenVocab, CharSequence by city { + override fun getValue(): String { + return city + } + init { if (enforceVocab) { require(vocab.contains(city)) @@ -20,7 +24,7 @@ class City(private val city: String, enforceVocab: Boolean = false) : OpenVocab, companion object { - val vocabName = "cities" + const val vocabName = "cities" var vocab: LinkedHashSet = linkedSetOf( ) diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt index eb9721f..eaf831b 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ClosedVocab.kt @@ -1,4 +1,7 @@ package com.stephenott.stix.type.vocab interface ClosedVocab { + + fun getValue(): String + } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt index 23a3cb0..4c52740 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/CourseOfActionTypeOv.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class CourseOfActionTypeOv(private val type: String) : ClosedVocab, CharSequence by type { +class CourseOfActionTypeOv(private val type: String) : ClosedVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "course-of-action-type-ov" + const val vocabName = "course-of-action-type-ov" val vocab: LinkedHashSet = linkedSetOf( "textual:text/plain", "textual:text/html", "textual:text/md", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt index 1018daa..bc9c5a5 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/EncryptionAlgorithmEnum.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class EncryptionAlgorithmEnum(private val algorithm: String) : OpenVocab, CharSequence by algorithm { +class EncryptionAlgorithmEnum(private val algorithm: String) : OpenVocab { + + override fun getValue(): String { + return algorithm + } companion object { - val vocabName = "encryption-algorithm-enum" + const val vocabName = "encryption-algorithm-enum" val vocab: LinkedHashSet = linkedSetOf( "AES-256-GCM​", "ChaCha20-Poly1305", "mime-type-indicated" diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt index a39c801..912a9e9 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/GroupingContextOv.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class GroupingContextOv(private val groupingContext: String) : OpenVocab, CharSequence by groupingContext { +class GroupingContextOv(private val groupingContext: String) : OpenVocab { + + override fun getValue(): String { + return groupingContext + } companion object { - val vocabName = "grouping-context-ov" + const val vocabName = "grouping-context-ov" val vocab: LinkedHashSet = linkedSetOf( "suspicious-activity", "malware-analysis", "unspecified" diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt index 2a79c2a..39b7d7e 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityClassOv.kt @@ -1,10 +1,14 @@ package com.stephenott.stix.type.vocab -class IdentityClass(private val identityClass: String): OpenVocab, CharSequence by identityClass{ +class IdentityClass(private val identityClass: String): OpenVocab { + + override fun getValue(): String { + return identityClass + } companion object{ - val vocabName = "identity-class-ov" + const val vocabName = "identity-class-ov" var vocab: LinkedHashSet = linkedSetOf( "individual", "group", "system", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt index 3739819..28c79af 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IdentityRoles.kt @@ -10,7 +10,11 @@ class IdentityRoles(private val roles: LinkedHashSet = linkedSetOf } } -class IdentityRole(private val role: String, enforceVocab: Boolean = false) : OpenVocab, CharSequence by role { +class IdentityRole(private val role: String, enforceVocab: Boolean = false) : OpenVocab { + + override fun getValue(): String { + return role + } init { if (enforceVocab) { diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt index 9493c9b..3964314 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ImplementationLanguageOv.kt @@ -4,11 +4,15 @@ class ImplementationLanguages(private val languages: LinkedHashSet by languages { } -class ImplementationLanguage(private val language: String) : OpenVocab, CharSequence by language { +class ImplementationLanguage(private val language: String) : OpenVocab { + + override fun getValue(): String { + return language + } companion object { - val vocabName = "implementation-language-ov" + const val vocabName = "implementation-language-ov" var vocab: LinkedHashSet = linkedSetOf( "applescript", "bash", "c", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt index bef0cdf..8540f8a 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IndicatorTypeOv.kt @@ -8,11 +8,14 @@ class IndicatorTypes(private val types: LinkedHashSet) : } } -class IndicatorType(private val type: String) : OpenVocab, CharSequence by type { +class IndicatorType(private val type: String) : OpenVocab { + override fun getValue(): String { + return type + } companion object { - val vocabName = "indicator-type-ov" + const val vocabName = "indicator-type-ov" var vocab: LinkedHashSet = linkedSetOf( "anomalous-activity", "anonymization", "benign", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt index 2778ba8..068fbea 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/IndustrySectorOv.kt @@ -4,9 +4,16 @@ class IndustrySectors(private val sectors: LinkedHashSet = linke Set by sectors { } -class IndustrySector(private val sector: String) : OpenVocab, CharSequence by sector { +class IndustrySector(private val sector: String) : OpenVocab { + + override fun getValue(): String { + return sector + } companion object { + + const val vocabName = "industry-sector-ov" + var vocab: LinkedHashSet = linkedSetOf( "agriculture", "aerospace", "automotive", "communications", "construction", "defence", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt index a099938..80bf22b 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/InfrastructureTypeOv.kt @@ -4,11 +4,15 @@ class InfrastructureTypes(private val types: LinkedHashSet = Set by types { } -class InfrastructureType(private val type: String) : OpenVocab, CharSequence by type { +class InfrastructureType(private val type: String) : OpenVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "infrastructure-type-ov" + const val vocabName = "infrastructure-type-ov" var vocab: LinkedHashSet = linkedSetOf( "amplification", "anonymization", "botnet", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt index 708fa6b..20189d8 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/LanguageCodes.kt @@ -8,14 +8,17 @@ class LanguageCodes(private val codes: LinkedHashSet = linkedSetOf } } -data class LanguageCode(private val code: String) : ClosedVocab, CharSequence by code { +data class LanguageCode(private val code: String) : ClosedVocab { + override fun getValue(): String { + return code + } init { require(LanguageCode.vocab.contains(code)) } companion object { - val vocabName = "iso-639-2-language-codes" + const val vocabName = "iso-639-2-language-codes" val vocab: LinkedHashSet = linkedSetOf( "en" //@TODO add rest of language codes diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt index a694763..08e4821 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareAvResultOv.kt @@ -4,10 +4,13 @@ class MalwareAvResults(private val results: LinkedHashSet = lin Set by results { } -class MalwareAvResult(private val result: String) : OpenVocab, CharSequence by result { +class MalwareAvResult(private val result: String) : OpenVocab { + override fun getValue(): String { + return result + } companion object { - val vocabName = "malware-av-result-ov" + const val vocabName = "malware-av-result-ov" var vocab: LinkedHashSet = linkedSetOf( "malicious", "suspicious", "benign", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt index 22ff8b4..31d18a9 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareCapabilitiesOv.kt @@ -4,11 +4,15 @@ class MalwareCapabilities(private val capabilities: LinkedHashSet by capabilities { } -class MalwareCapability(private val capability: String) : OpenVocab, CharSequence by capability { +class MalwareCapability(private val capability: String) : OpenVocab { + + override fun getValue(): String { + return capability + } companion object { - val vocabName = "malware-capabilities-ov" + const val vocabName = "malware-capabilities-ov" var vocab: LinkedHashSet = linkedSetOf( "accesses-remote-machines", "anti-debugging", "anti-disassembly", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt index 31c925c..b4db5a1 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MalwareTypeOv.kt @@ -4,9 +4,16 @@ class MalwareTypes(private val types: LinkedHashSet = linkedSetOf() Set by types { } -class MalwareType(private val type: String) : OpenVocab, CharSequence by type { +class MalwareType(private val type: String) : OpenVocab { + + override fun getValue(): String { + return type + } companion object { + + const val vocabName = "malware-type-ov" + var vocab: LinkedHashSet = linkedSetOf( "adware", "backdoor", "bot", "bootkit", "ddos", "downloader", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt index 4027e2d..2ad1886 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/MarkingDefinitionTypeOv.kt @@ -1,10 +1,13 @@ package com.stephenott.stix.type.vocab -data class MarkingDefinitionTypeOv(private val definitionType: String): OpenVocab, CharSequence by definitionType{ +data class MarkingDefinitionTypeOv(private val definitionType: String): OpenVocab { + override fun getValue(): String { + return definitionType + } companion object{ - val vocabName = "marking-definition-type-ov" + const val vocabName = "marking-definition-type-ov" var vocab: LinkedHashSet = linkedSetOf( "statement", "tlp") @@ -15,6 +18,6 @@ data class MarkingDefinitionTypeOv(private val definitionType: String): OpenVoca } init { - require(this.definitionType in vocab) + require(this.definitionType in vocab, lazyMessage = {"Marking Definition is using a unsupported definition_type"}) } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt index 987ce4b..8cf6ce5 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketAddressFamilyEnum.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class NetworkSocketAddressFamilyEnum(private val addressFamily: String) : ClosedVocab, CharSequence by addressFamily { +class NetworkSocketAddressFamilyEnum(private val addressFamily: String) : ClosedVocab { + + override fun getValue(): String { + return addressFamily + } companion object { - val vocabName = "network-socket-address-family-enum" + const val vocabName = "network-socket-address-family-enum" val vocab: LinkedHashSet = linkedSetOf( "AF_UNSPEC", "AF_INET", "AF_IPX", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt index ddde6f0..b43b6af 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/NetworkSocketTypeEnum.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class NetworkSocketTypeEnum(private val type: String) : ClosedVocab, CharSequence by type { +class NetworkSocketTypeEnum(private val type: String) : ClosedVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "network-socket-type-enum" + const val vocabName = "network-socket-type-enum" val vocab: LinkedHashSet = linkedSetOf( "SOCK_STREAM", "AF_ISOCK_DGRAMNET", "SOCK_RAW", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt index b9275d3..94c1cad 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/OpenVocab.kt @@ -1,4 +1,6 @@ package com.stephenott.stix.type.vocab interface OpenVocab { + fun getValue(): String + } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt index 1f0ce84..73e9469 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/OpinionEnum.kt @@ -1,8 +1,14 @@ package com.stephenott.stix.type.vocab -class OpinionEnum(private val opinion: String) : ClosedVocab, CharSequence by opinion { +class OpinionEnum(private val opinion: String) : ClosedVocab { + override fun getValue(): String { + return opinion + } companion object { + + const val vocabName = "opinion-enum" + var vocab: LinkedHashSet = linkedSetOf( "strongly-disagree", "disagree", "neutral", "agree", "strongly-agree" diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt index 79a79af..03a18cc 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/PatternTypes.kt @@ -1,8 +1,15 @@ package com.stephenott.stix.type.vocab -class PatternType(private val type: String) : ClosedVocab, CharSequence by type { +class PatternType(private val type: String) : ClosedVocab { + + override fun getValue(): String { + return type + } companion object { + + const val vocabName = "pattern-type" //@TODO + val vocab: LinkedHashSet = linkedSetOf( "stix", "snort", "yara" ) diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt index f2801fa..001d701 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ProcessorArchitectureOv.kt @@ -4,11 +4,15 @@ class ProcessorArchitectures(private val architectures: LinkedHashSet by architectures { } -class ProcessorArchitecture(private val architecture: String) : OpenVocab, CharSequence by architecture { +class ProcessorArchitecture(private val architecture: String) : OpenVocab { + + override fun getValue(): String { + return architecture + } companion object { - val vocabName = "processor-architecture-ov" + const val vocabName = "processor-architecture-ov" var vocab: LinkedHashSet = linkedSetOf( "alpha", "arm", "ia-64", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt index 0bffae0..119aaed 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/RegionOv.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class RegionOv(private val region: String) : ClosedVocab, CharSequence by region { +class RegionOv(private val region: String) : ClosedVocab { + + override fun getValue(): String { + return region + } companion object { - val vocabName = "region-ov" + const val vocabName = "region-ov" val vocab: LinkedHashSet = linkedSetOf( "africa", "eastern-africa", "middle-africa", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt index 6338fe5..40a32b8 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ReportTypeOv.kt @@ -4,11 +4,14 @@ class ReportTypes(private val types: LinkedHashSet = linkedSetOf()) Set by types { } -class ReportType(private val type: String) : OpenVocab, CharSequence by type { +class ReportType(private val type: String) : OpenVocab { + override fun getValue(): String { + return type + } companion object { - val vocabName = "report-type-ov" + const val vocabName = "report-type-ov" var vocab: LinkedHashSet = linkedSetOf( "attack-pattern", "campaign", "identity", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt index 3cc3ef0..706a60b 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorRoleOv.kt @@ -4,11 +4,15 @@ class ThreatActorRoles(private val roles: LinkedHashSet = linke Set by roles { } -class ThreatActorRole(private val role: String) : OpenVocab, CharSequence by role { +class ThreatActorRole(private val role: String) : OpenVocab { + + override fun getValue(): String { + return role + } companion object { - val vocabName = "threat-actor-role-ov" + const val vocabName = "threat-actor-role-ov" var vocab: LinkedHashSet = linkedSetOf( "agent", "director", "independent", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt index 72f35d8..d4c74cd 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorSophisticationOv.kt @@ -1,10 +1,14 @@ package com.stephenott.stix.type.vocab -class ThreatActorSophisticationOv(private val sophistication: String) : OpenVocab, CharSequence by sophistication { +class ThreatActorSophisticationOv(private val sophistication: String) : OpenVocab { + + override fun getValue(): String { + return sophistication + } companion object { - val vocabName = "threat-actor-sophistication" + const val vocabName = "threat-actor-sophistication" var vocab: LinkedHashSet = linkedSetOf( "none", "minimal", "intermediate", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt index 1c95294..7ebb991 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ThreatActorTypeOv.kt @@ -4,11 +4,15 @@ class ThreatActorTypes(private val types: LinkedHashSet = linke Set by types { } -class ThreatActorType(private val type: String) : OpenVocab, CharSequence by type { +class ThreatActorType(private val type: String) : OpenVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "threat-actor-type-ov" + const val vocabName = "threat-actor-type-ov" var vocab: LinkedHashSet = linkedSetOf( "activist", "competitor", "crime-syndicate", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt index ad04aa8..3e343e5 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/ToolTypeOv.kt @@ -4,7 +4,11 @@ class ToolTypes(private val types: LinkedHashSet = linkedSetOf()) : Set by types { } -class ToolType(private val type: String) : OpenVocab, CharSequence by type { +class ToolType(private val type: String) : OpenVocab { + + override fun getValue(): String { + return type + } companion object { var vocab: LinkedHashSet = linkedSetOf( diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt index b596077..5a6afc0 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsIntegrityLevelEnum.kt @@ -1,9 +1,12 @@ package com.stephenott.stix.type.vocab -class WindowsIntegrityLevelEnum(private val level: String) : ClosedVocab, CharSequence by level { +class WindowsIntegrityLevelEnum(private val level: String) : ClosedVocab { + override fun getValue(): String { + return level + } companion object { - val vocabName = "windows-integrity-level-enum" + const val vocabName = "windows-integrity-level-enum" val vocab: LinkedHashSet = linkedSetOf( "low", "medium", "high", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt index 6d474a3..e539cc1 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsPebinaryTypeOv.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class WindowsPebinaryTypeOv(private val type: String) : ClosedVocab, CharSequence by type { +class WindowsPebinaryTypeOv(private val type: String) : ClosedVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "windows-pebinary-type-ov" + const val vocabName = "windows-pebinary-type-ov" val vocab: LinkedHashSet = linkedSetOf( "dll", "exe", "sys" diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt index 7d44ef8..300bb45 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsRegistryDataTypeEnum.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class WindowsRegistryDataTypeEnum(private val datatype: String) : ClosedVocab, CharSequence by datatype { +class WindowsRegistryDataTypeEnum(private val datatype: String) : ClosedVocab { + + override fun getValue(): String { + return datatype + } companion object { - val vocabName = "windows-registry-datatype-enum" + const val vocabName = "windows-registry-datatype-enum" val vocab: LinkedHashSet = linkedSetOf( "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt index 19f1c13..8ee9190 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStartTypeEnum.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class WindowsServiceStartTypeEnum(private val type: String) : ClosedVocab, CharSequence by type { +class WindowsServiceStartTypeEnum(private val type: String) : ClosedVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "windows-service-start-type-enum" + const val vocabName = "windows-service-start-type-enum" val vocab: LinkedHashSet = linkedSetOf( "SERVICE_AUTO_START", "SERVICE_BOOT_START", "SERVICE_DEMAND_START", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt index e32981f..a4b4138 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceStatusEnum.kt @@ -1,9 +1,13 @@ package com.stephenott.stix.type.vocab -class WindowsServiceStatusEnum(private val status: String) : ClosedVocab, CharSequence by status { +class WindowsServiceStatusEnum(private val status: String) : ClosedVocab { + + override fun getValue(): String { + return status + } companion object { - val vocabName = "windows-service-status-enum" + const val vocabName = "windows-service-status-enum" val vocab: LinkedHashSet = linkedSetOf( "SERVICE_CONTINUE_PENDING", "SERVICE_PAUSE_PENDING", "SERVICE_PAUSED", diff --git a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt index 12f0e01..06ee28b 100644 --- a/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt +++ b/src/main/kotlin/com/stephenott/stix/type/vocab/WindowsServiceTypeEnum.kt @@ -1,14 +1,17 @@ package com.stephenott.stix.type.vocab -class WindowsServiceTypeEnum(private val type: String) : ClosedVocab, CharSequence by type { +class WindowsServiceTypeEnum(private val type: String) : ClosedVocab { + + override fun getValue(): String { + return type + } companion object { - val vocabName = "windows-service-type-enum" + const val vocabName = "windows-service-type-enum" val vocab: LinkedHashSet = linkedSetOf( "SERVICE_KERNEL_DRIVER", "SERVICE_FILE_SYSTEM_DRIVER", "SERVICE_WIN32_OWN_PROCESS", "SERVICE_WIN32_SHARE_PROCESS" - ) } From 0cc4a4038e7f8420bee9514b719c7f46bc0f8dee Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Fri, 15 Nov 2019 20:50:22 -0500 Subject: [PATCH 16/21] refactor for instance based configs --- .../kotlin/com/stephenott/stix/MainRunner.kt | 33 +++---- src/main/kotlin/com/stephenott/stix/Stix.kt | 21 +++++ .../kotlin/com/stephenott/stix/StixBundle.kt | 3 +- .../kotlin/com/stephenott/stix/StixContent.kt | 7 +- .../stix/common/StixContentMapper.kt | 9 -- .../stix/common/StixMarkingObjectRegistry.kt | 2 +- .../stix/common/StixObjectRegistry.kt | 40 ++------- .../common/StixObjectRelationshipRegistry.kt | 2 +- .../stix/common/ValidatorManager.kt | 5 ++ .../extension/objects/ArchiveFileExtension.kt | 4 +- .../extension/objects/HttpRequestExtension.kt | 4 +- .../sco/extension/objects/IcmpExtension.kt | 6 +- .../objects/NetworkSocketExtension.kt | 7 +- .../extension/objects/NtfsFileExtension.kt | 4 +- .../sco/extension/objects/PdfFileExtension.kt | 5 +- .../objects/RasterImageFileExtension.kt | 6 +- .../sco/extension/objects/TcpExtension.kt | 4 +- .../extension/objects/UnixAccountExtension.kt | 5 +- .../objects/WindowsPeBinaryFileExtension.kt | 4 +- .../objects/WindowsProcessExtension.kt | 4 +- .../objects/WindowsServiceExtension.kt | 5 +- .../stix/objects/core/sco/objects/Artifact.kt | 9 +- .../core/sco/objects/AutonomousSystem.kt | 9 +- .../objects/core/sco/objects/Directory.kt | 9 +- .../objects/core/sco/objects/DomainName.kt | 9 +- .../objects/core/sco/objects/EmailAddress.kt | 9 +- .../objects/core/sco/objects/EmailMessage.kt | 9 +- .../stix/objects/core/sco/objects/File.kt | 11 ++- .../objects/core/sco/objects/IPv4Address.kt | 11 +-- .../objects/core/sco/objects/IPv6Address.kt | 10 ++- .../objects/core/sco/objects/MacAddress.kt | 9 +- .../stix/objects/core/sco/objects/Mutex.kt | 9 +- .../core/sco/objects/NetworkTraffic.kt | 9 +- .../stix/objects/core/sco/objects/Process.kt | 9 +- .../stix/objects/core/sco/objects/Software.kt | 9 +- .../stix/objects/core/sco/objects/Url.kt | 9 +- .../objects/core/sco/objects/UserAccount.kt | 9 +- .../core/sco/objects/WindowsRegistryKey.kt | 17 ++-- .../core/sco/objects/X509Certificate.kt | 17 ++-- .../objects/core/sdo/objects/AttackPattern.kt | 12 +-- .../stix/objects/core/sdo/objects/Campaign.kt | 12 +-- .../core/sdo/objects/CourseOfAction.kt | 9 +- .../stix/objects/core/sdo/objects/Grouping.kt | 12 +-- .../stix/objects/core/sdo/objects/Identity.kt | 9 +- .../objects/core/sdo/objects/Indicator.kt | 9 +- .../core/sdo/objects/Infrastructure.kt | 12 +-- .../objects/core/sdo/objects/IntrusionSet.kt | 12 +-- .../stix/objects/core/sdo/objects/Location.kt | 9 +- .../stix/objects/core/sdo/objects/Malware.kt | 12 +-- .../core/sdo/objects/MalwareAnalysis.kt | 9 +- .../stix/objects/core/sdo/objects/Note.kt | 17 ++-- .../objects/core/sdo/objects/ObservedData.kt | 14 +-- .../stix/objects/core/sdo/objects/Opinion.kt | 9 +- .../stix/objects/core/sdo/objects/Report.kt | 14 +-- .../objects/core/sdo/objects/ThreatActor.kt | 12 +-- .../stix/objects/core/sdo/objects/Tool.kt | 13 +-- .../objects/core/sdo/objects/Vulnerability.kt | 12 +-- .../objects/core/sro/objects/Relationship.kt | 13 +-- .../stix/objects/core/sro/objects/Sighting.kt | 11 ++- .../meta/datamarking/MarkingDefinition.kt | 10 ++- .../meta/lco/objects/LanguageContent.kt | 12 +-- .../stix/serialization/StixContentMapper.kt | 10 +++ .../stix/serialization/json/JsonExtensions.kt | 86 +++++++++---------- .../json/StixContentSerialization.kt | 52 ++--------- .../json/StixMarkingObjectSerialization.kt | 5 +- .../json/StixRegistriesSerialization.kt | 24 ++++++ .../json/StixTypeSerialization.kt | 30 +++---- 67 files changed, 445 insertions(+), 379 deletions(-) create mode 100644 src/main/kotlin/com/stephenott/stix/Stix.kt delete mode 100644 src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/StixContentMapper.kt create mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index 313c717..a10efe2 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,31 +1,23 @@ package com.stephenott.stix -import com.fasterxml.jackson.module.kotlin.readValue import com.stephenott.stix.objects.StixObject import com.stephenott.stix.objects.core.StixCoreObject import com.stephenott.stix.objects.core.sco.objects.IPv6Address import com.stephenott.stix.objects.core.sco.objects.NetworkTraffic import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sdo.objects.AttackPattern -import com.stephenott.stix.objects.core.sro.objects.Relationship -import com.stephenott.stix.serialization.json.createStixMapper -import com.stephenott.stix.objects.core.sdo.StixDomainObject.* import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo -import com.stephenott.stix.objects.core.sdo.objects.Campaign -import com.stephenott.stix.objects.core.sdo.objects.ObservedData import com.stephenott.stix.objects.core.sro.StixRelationshipObject +import com.stephenott.stix.objects.core.sro.objects.Relationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.objects.core.sro.objects.Sighting -import com.stephenott.stix.objects.meta.StixMetaObject import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition -import com.stephenott.stix.objects.meta.datamarking.MarkingDefinitionDm import com.stephenott.stix.objects.meta.datamarking.objects.Statement import com.stephenott.stix.objects.meta.datamarking.objects.Tlp -import com.stephenott.stix.serialization.json.StixContentMapper +import com.stephenott.stix.serialization.json.StixJsonContentMapper import com.stephenott.stix.serialization.json.toJson import com.stephenott.stix.type.* import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv -import java.time.Instant object MainRunner { @@ -33,13 +25,12 @@ object MainRunner { fun main(args: Array){ val ap1 = AttackPattern(name = "124", confidence = StixConfidence(33)) -// val ap2 = AttackPattern("1245") -// val ip6 = IPv6Address("dog") -// -// val sighting1 = Sighting(sightingOfRef = ap1.id) -// val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) -// - val mapper = StixContentMapper() + val ap2 = AttackPattern("1245") + + val sighting1 = Sighting(sightingOfRef = ap1.id) + val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) + + val mapper = StixJsonContentMapper(StixRegistries()) println(ap1.toJson(mapper)) println(mapper.parseJson(ap1.toJson(mapper))) @@ -48,7 +39,7 @@ object MainRunner { println(mapper.parseJson(ap1.toJson(mapper))) println(mapper.parseJson(ap1.toJson(mapper))) println(mapper.parseJson(ap1.toJson(mapper))) - +// // println(net1.toJson(mapper)) // println(mapper.parseJson(net1.toJson(mapper))) // @@ -78,12 +69,14 @@ object MainRunner { // println(mapper.parseJson(sighting1.toJson(mapper))) // // val markDef1 = MarkingDefinition("test", MarkingDefinitionTypeOv("tlp"), -// Tlp("white")) +// Tlp("white") +// ) // println(mapper.parseJson(markDef1.toJson(mapper))) // println(markDef1.toJson(mapper)) // // val markDef2 = MarkingDefinition("test2", MarkingDefinitionTypeOv("statement"), -// Statement("white-statement")) +// Statement("white-statement") +// ) // println(mapper.parseJson(markDef2.toJson(mapper))) // println(markDef2.toJson(mapper)) } diff --git a/src/main/kotlin/com/stephenott/stix/Stix.kt b/src/main/kotlin/com/stephenott/stix/Stix.kt new file mode 100644 index 0000000..a69579b --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/Stix.kt @@ -0,0 +1,21 @@ +package com.stephenott.stix + +import com.stephenott.stix.common.StixMarkingObjectRegistry +import com.stephenott.stix.common.StixObjectRegistry +import com.stephenott.stix.common.StixObjectRelationshipRegistry + +class Stix() { + + //@TODO future place that common configurations will go. + + companion object { + val defaultRegistries: StixRegistries = StixRegistries() + } + +} + +data class StixRegistries( + val objectRegistry: StixObjectRegistry = StixObjectRegistry(), + val relationshipRegistry: StixObjectRelationshipRegistry = StixObjectRelationshipRegistry(), + val markingObjectRegistry: StixMarkingObjectRegistry = StixMarkingObjectRegistry() +) {} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixBundle.kt b/src/main/kotlin/com/stephenott/stix/StixBundle.kt index 0e81b5d..4e5d13d 100644 --- a/src/main/kotlin/com/stephenott/stix/StixBundle.kt +++ b/src/main/kotlin/com/stephenott/stix/StixBundle.kt @@ -16,7 +16,8 @@ interface StixBundle: StixContent{ data class Bundle(override val type: StixType = StixBundle.stixType, override val id: StixIdentifier = StixIdentifier(type), - override val objects: LinkedHashSet + override val objects: LinkedHashSet, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ): StixBundle{ } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixContent.kt b/src/main/kotlin/com/stephenott/stix/StixContent.kt index f78a929..b78e87d 100644 --- a/src/main/kotlin/com/stephenott/stix/StixContent.kt +++ b/src/main/kotlin/com/stephenott/stix/StixContent.kt @@ -1,8 +1,13 @@ package com.stephenott.stix +import com.fasterxml.jackson.annotation.JsonIgnore import com.stephenott.stix.common.StixIdentifierProp import com.stephenott.stix.common.StixTypeProp interface StixContent : StixTypeProp, - StixIdentifierProp {} \ No newline at end of file + StixIdentifierProp { + + val stixRegistries: StixRegistries + +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt b/src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt deleted file mode 100644 index 9450cfc..0000000 --- a/src/main/kotlin/com/stephenott/stix/common/StixContentMapper.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.stephenott.stix.common - -interface StixContentMapper { - - val markingObjectRegistry: StixMarkingObjectRegistry - val objectRegistry: StixObjectRegistry - val relationshipRegistry: StixObjectRelationshipRegistry - -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt index 75cb916..413bd88 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixMarkingObjectRegistry.kt @@ -6,7 +6,7 @@ import com.stephenott.stix.objects.meta.datamarking.objects.Tlp import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv import kotlin.reflect.KClass -class StixMarkingObjectRegistry { +class StixMarkingObjectRegistry() { val registry: MutableMap> = mutableMapOf( Pair(MarkingDefinitionTypeOv("statement"), Statement::class), diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt index 30e6ac9..7e295b8 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRegistry.kt @@ -11,7 +11,6 @@ import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.objects.core.sro.objects.Sighting import com.stephenott.stix.objects.core.sro.objects.SightingSro import com.stephenott.stix.objects.meta.StixMetaObject -import com.stephenott.stix.objects.meta.datamarking.GranularMarkingGm import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition import com.stephenott.stix.objects.meta.datamarking.MarkingDefinitionDm import com.stephenott.stix.objects.meta.datamarking.MarkingObject @@ -25,8 +24,13 @@ import com.stephenott.stix.type.StixType import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv import kotlin.reflect.KClass -//@TODO move to a instance so it can be passed into content handlers (such as JSON content mapper) -object StixObjectRegistry { +data class StixObjectRegistry( + val customSdoRegistry: Map> = mapOf(), + val customScoRegistry: Map> = mapOf(), + val customSroRegistry: Map> = mapOf(), + val customMetaObjectRegistry: Map> = mapOf(), + val customMarkingObjectRegistry: Map> = mapOf() +) { // STIX OBJECT Registry: @@ -81,30 +85,6 @@ object StixObjectRegistry { Pair(LanguageContentLco.stixType, LanguageContent::class) ) - /** - * Custom SDO Objects - */ - var customSdoRegistry: Map> = mutableMapOf( - ) - - /** - * Custom SCO Objects - */ - var customScoRegistry: Map> = mutableMapOf( - ) - - /** - * Custom SRO Objects - */ - var customSroRegistry: Map> = mutableMapOf( - ) - - /** - * Custom Meta Objects - */ - var customMetaObjectRegistry: Map> = mutableMapOf( - ) - /** * Aggregate of the Stix Objects (spec defined and custom) */ @@ -150,12 +130,6 @@ object StixObjectRegistry { Pair(StatementMo.definitionType, Statement::class) ) - /** - * Custom Data Marking Objects - */ - var customMarkingObjectRegistry: Map> = mutableMapOf( - ) - private fun aggregateMarkingObjects(): Map> { val objects = mutableMapOf>() objects.plusAssign(markingObjectRegistry) diff --git a/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt b/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt index 710b455..66af8e6 100644 --- a/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt +++ b/src/main/kotlin/com/stephenott/stix/common/StixObjectRelationshipRegistry.kt @@ -4,7 +4,7 @@ import com.stephenott.stix.objects.core.sdo.objects.* import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro -object StixObjectRelationshipRegistry { +class StixObjectRelationshipRegistry() { var registry: List = RelationshipSro.allowedCommonRelationships + diff --git a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt index e5da1de..d858b9e 100644 --- a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt +++ b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt @@ -1,5 +1,6 @@ package com.stephenott.stix.common +import com.stephenott.stix.StixRegistries import com.stephenott.stix.objects.StixObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sco.objects.AutonomousSystemSco @@ -9,6 +10,10 @@ import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface BusinessRulesValidator{ + fun objectValidationRules(obj: T, stixRegistries: StixRegistries) +} + +interface BusinessRulesExtensionValidator{ fun objectValidationRules(obj: T) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt index d685cd5..c5e97c2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/ArchiveFileExtension.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sco.objects.DirectorySco @@ -13,7 +13,7 @@ interface ArchiveFileExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "archive-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt index 49984b0..026fa4a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/HttpRequestExtension.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sco.objects.ArtifactSco @@ -18,7 +18,7 @@ interface HttpRequestExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "http-request-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt index 04018ad..022b9a0 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/IcmpExtension.kt @@ -1,9 +1,9 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixHex interface IcmpExtensionExt: ScoExtension{ @@ -12,7 +12,7 @@ interface IcmpExtensionExt: ScoExtension{ companion object: CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "icmp-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt index 288e4db..c87d589 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NetworkSocketExtension.kt @@ -1,9 +1,10 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.type.* +import com.stephenott.stix.type.StixBoolean +import com.stephenott.stix.type.StixInteger import com.stephenott.stix.type.vocab.NetworkSocketAddressFamilyEnum import com.stephenott.stix.type.vocab.NetworkSocketTypeEnum @@ -19,7 +20,7 @@ interface NetworkSocketExtensionExt: ScoExtension{ companion object: CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "socket-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt index bd9a8a0..9b9dbf5 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/NtfsFileExtension.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.type.AlternateDataStreams @@ -11,7 +11,7 @@ interface NtfsFileExtensionExt: ScoExtension{ companion object: CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "ntfs-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt index ebb8365..538ab81 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/PdfFileExtension.kt @@ -1,9 +1,8 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.type.AlternateDataStreams import com.stephenott.stix.type.StixBoolean interface PdfFileExtensionExt: ScoExtension{ @@ -16,7 +15,7 @@ interface PdfFileExtensionExt: ScoExtension{ companion object: CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "pdf-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt index a4ca2eb..0cd5de1 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/RasterImageFileExtension.kt @@ -1,11 +1,9 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.type.AlternateDataStreams import com.stephenott.stix.type.ExifTagsDictionary -import com.stephenott.stix.type.StixBoolean import com.stephenott.stix.type.StixInteger interface RasterImageFileExtensionExt: ScoExtension{ @@ -17,7 +15,7 @@ interface RasterImageFileExtensionExt: ScoExtension{ companion object: CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "raster-image-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt index 6d6e691..2b250e9 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/TcpExtension.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.type.StixHex @@ -12,7 +12,7 @@ interface TcpExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "tcp-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt index 619648c..ed1ee31 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/UnixAccountExtension.kt @@ -1,9 +1,8 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.type.StixHex import com.stephenott.stix.type.StixInteger import com.stephenott.stix.type.StixStringList @@ -16,7 +15,7 @@ interface UnixAccountExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "unix-account-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt index cbed87c..dc86271 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.type.* @@ -25,7 +25,7 @@ interface WindowsPeBinaryFileExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "windows-pebinary-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt index 6368d8f..232e8d2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsProcessExtension.kt @@ -1,6 +1,6 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.type.StixBoolean @@ -20,7 +20,7 @@ interface WindowsProcessExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "windows-process-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt index 0325ba1..53b5073 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsServiceExtension.kt @@ -1,9 +1,8 @@ package com.stephenott.stix.objects.core.sco.extension.objects -import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.BusinessRulesExtensionValidator import com.stephenott.stix.common.CompanionExtensionType import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.type.StixHex import com.stephenott.stix.type.StixIdentifiers import com.stephenott.stix.type.StixStringList import com.stephenott.stix.type.vocab.WindowsServiceStartTypeEnum @@ -25,7 +24,7 @@ interface WindowsServiceExtensionExt : ScoExtension { companion object : CompanionExtensionType, - BusinessRulesValidator { + BusinessRulesExtensionValidator { override val extensionType: String = "windows-service-ext" diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt index ce8d832..8c57516 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -39,7 +41,7 @@ interface ArtifactSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: ArtifactSco) { + override fun objectValidationRules(obj: ArtifactSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.url != null) { @@ -74,11 +76,12 @@ data class Artifact( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : ArtifactSco { init { - ArtifactSco.objectValidationRules(this) + ArtifactSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt index 4fc6706..9daaaab 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -34,7 +36,7 @@ interface AutonomousSystemSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: AutonomousSystemSco) { + override fun objectValidationRules(obj: AutonomousSystemSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -51,11 +53,12 @@ data class AutonomousSystem( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : AutonomousSystemSco { init { - AutonomousSystemSco.objectValidationRules(this) + AutonomousSystemSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt index 712437d..ac9a51a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -37,7 +39,7 @@ interface DirectorySco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: DirectorySco) { + override fun objectValidationRules(obj: DirectorySco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) obj.containsRefs?.let { @@ -62,11 +64,12 @@ data class Directory( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : DirectorySco { init { - DirectorySco.objectValidationRules(this) + DirectorySco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt index b3f7e82..5a5f83a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -47,7 +49,7 @@ interface DomainNameSco : StixCyberObservableObject { ) ) - override fun objectValidationRules(obj: DomainNameSco) { + override fun objectValidationRules(obj: DomainNameSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -63,11 +65,12 @@ data class DomainName( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : DomainNameSco { init { - DomainNameSco.objectValidationRules(this) + DomainNameSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt index 1d8d153..af98c93 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -34,7 +36,7 @@ interface EmailAddressSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: EmailAddressSco) { + override fun objectValidationRules(obj: EmailAddressSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) obj.belongsToRef?.let { @@ -56,11 +58,12 @@ data class EmailAddress( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : EmailAddressSco { init { - EmailAddressSco.objectValidationRules(this) + EmailAddressSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt index c35876c..2e11e43 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -48,7 +50,7 @@ interface EmailMessageSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: EmailMessageSco) { + override fun objectValidationRules(obj: EmailMessageSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) obj.fromRef?.let { @@ -105,11 +107,12 @@ data class EmailMessage( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : EmailMessageSco { init { - EmailMessageSco.objectValidationRules(this) + EmailMessageSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt index 7e28757..876c964 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -53,7 +55,7 @@ interface FileSco : StixCyberObservableObject { WindowsPeBinaryFileExtensionExt::class ) - override fun objectValidationRules(obj: FileSco) { + override fun objectValidationRules(obj: FileSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) obj.size?.let { @@ -65,7 +67,7 @@ interface FileSco : StixCyberObservableObject { lazyMessage = { "parent_directory_ref must only contain a reference to a Directory object SCO." }) } obj.containsRefs?.let { - require(it.all { id -> StixObjectRegistry.registry.getValue(id.type).isSubclassOf(StixCyberObservableObject::class) }, + require(it.all { id -> stixRegistries.objectRegistry.registry.getValue(id.type).isSubclassOf(StixCyberObservableObject::class) }, lazyMessage = { "contains_refs can only contain references to SCOs." }) } obj.contentRef?.let { @@ -95,11 +97,12 @@ data class File( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : FileSco { init { - FileSco.objectValidationRules(this) + FileSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt index 4c84fb0..6d74aff 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -43,11 +45,9 @@ interface IPv4AddressSco : StixCyberObservableObject { ) ) - override fun objectValidationRules(obj: IPv4AddressSco) { + override fun objectValidationRules(obj: IPv4AddressSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) - } - } } @@ -59,11 +59,12 @@ data class IPv4Address( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : IPv4AddressSco { init { - IPv4AddressSco.objectValidationRules(this) + IPv4AddressSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt index f3f6f4b..9411e58 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -43,9 +45,8 @@ interface IPv6AddressSco : StixCyberObservableObject { ) ) - override fun objectValidationRules(obj: IPv6AddressSco) { + override fun objectValidationRules(obj: IPv6AddressSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) - } } @@ -59,11 +60,12 @@ data class IPv6Address( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : IPv6AddressSco { init { - IPv6AddressSco.objectValidationRules(this) + IPv6AddressSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt index 055e063..7b7b090 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -32,7 +34,7 @@ interface MacAddressSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: MacAddressSco) { + override fun objectValidationRules(obj: MacAddressSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) //@TODO The MAC address value ​MUST​ be represented as a single colon-delimited, lowercase MAC-48 address, which ​MUST​ include leading zeros for each octet. @@ -49,11 +51,12 @@ data class MacAddress( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : MacAddressSco { init { - MacAddressSco.objectValidationRules(this) + MacAddressSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt index 6562d74..8df72a2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -32,7 +34,7 @@ interface MutexSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: MutexSco) { + override fun objectValidationRules(obj: MutexSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -48,11 +50,12 @@ data class Mutex( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : MutexSco { init { - MutexSco.objectValidationRules(this) + MutexSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt index 31dbb74..83b79e0 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -59,7 +61,7 @@ interface NetworkTrafficSco : StixCyberObservableObject { NetworkSocketExtensionExt::class ) - override fun objectValidationRules(obj: NetworkTrafficSco) { + override fun objectValidationRules(obj: NetworkTrafficSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) obj.isActive?.let { @@ -149,11 +151,12 @@ data class NetworkTraffic( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : NetworkTrafficSco { init { - NetworkTrafficSco.objectValidationRules(this) + NetworkTrafficSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt index 02a0ed5..ffe6164 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -48,7 +50,7 @@ interface ProcessSco : StixCyberObservableObject { WindowsServiceExtensionExt::class ) - override fun objectValidationRules(obj: ProcessSco) { + override fun objectValidationRules(obj: ProcessSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) require(obj.openedConnectionRef?.type == NetworkTrafficSco.stixType, @@ -88,11 +90,12 @@ data class Process( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : ProcessSco { init { - ProcessSco.objectValidationRules(this) + ProcessSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt index fa663a8..df3cbf0 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -40,7 +42,7 @@ interface SoftwareSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: SoftwareSco) { + override fun objectValidationRules(obj: SoftwareSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -60,11 +62,12 @@ data class Software( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : SoftwareSco { init { - SoftwareSco.objectValidationRules(this) + SoftwareSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt index 5f4c7b8..c6ed157 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -33,7 +35,7 @@ interface UrlSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: UrlSco) { + override fun objectValidationRules(obj: UrlSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -48,11 +50,12 @@ data class Url( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : UrlSco { init { - UrlSco.objectValidationRules(this) + UrlSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt index 319b257..c824ae0 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -51,7 +53,7 @@ interface UserAccountSco : StixCyberObservableObject { UnixAccountExtensionExt::class ) - override fun objectValidationRules(obj: UserAccountSco) { + override fun objectValidationRules(obj: UserAccountSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -79,11 +81,12 @@ data class UserAccount( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : UserAccountSco { init { - UserAccountSco.objectValidationRules(this) + UserAccountSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt index 62c461a..0627858 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -33,15 +35,11 @@ interface WindowsRegistryKeySco : StixCyberObservableObject { WindowsRegistryKeySco::values // @TODO ** There is another pattern like this where only 1 value from this prop should be used in the ID. Should look to standardize on this to make the code simpler ) - override val allowedRelationships: List = listOf( + override val allowedRelationships: List = listOf() - ) - - override val allowedExtensions: List> = listOf( - - ) + override val allowedExtensions: List> = listOf() - override fun objectValidationRules(obj: WindowsRegistryKeySco) { + override fun objectValidationRules(obj: WindowsRegistryKeySco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) require( listOf(obj.key, obj.values, obj.modifiedTimed, obj.creatorUserRef, obj.numberOfSubkeys) @@ -69,11 +67,12 @@ data class WindowsRegistryKey( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : WindowsRegistryKeySco { init { - WindowsRegistryKeySco.objectValidationRules(this) + WindowsRegistryKeySco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt index 5b4ffae..be89ca2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -39,15 +41,11 @@ interface X509CertificateSco : StixCyberObservableObject { X509CertificateSco::serialNumber ) - override val allowedRelationships: List = listOf( + override val allowedRelationships: List = listOf() - ) - - override val allowedExtensions: List> = listOf( - - ) + override val allowedExtensions: List> = listOf() - override fun objectValidationRules(obj: X509CertificateSco) { + override fun objectValidationRules(obj: X509CertificateSco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) require(listOf( //@TODO review against Stix 2 Issues against 182 @@ -90,11 +88,12 @@ data class X509Certificate( override val granularMarkings: String? = null, override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean() + override val defanged: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : X509CertificateSco { init { - X509CertificateSco.objectValidationRules(this) + X509CertificateSco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt index 0d112d0..4adadac 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -21,7 +23,7 @@ interface AttackPatternSdo : StixDomainObject { override val stixType = StixType("attack-pattern") - override fun objectValidationRules(obj: AttackPatternSdo) { + override fun objectValidationRules(obj: AttackPatternSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -80,12 +82,12 @@ data class AttackPattern( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - AttackPatternSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : AttackPatternSdo { init { - AttackPatternSdo.objectValidationRules(this) + AttackPatternSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt index 1ce2b63..15a8e51 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -21,7 +23,7 @@ interface CampaignSdo : StixDomainObject { BusinessRulesValidator, CompanionAllowedRelationships { - override fun objectValidationRules(obj: CampaignSdo) { + override fun objectValidationRules(obj: CampaignSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.firstSeen != null){ require(obj.lastSeen?.instant!!.isAfter(obj.firstSeen!!.instant)) @@ -95,8 +97,7 @@ interface CampaignSdo : StixDomainObject { } -data class Campaign - ( +data class Campaign ( override val name: String, override val description: String? = null, override val aliases: String? = null, @@ -115,11 +116,12 @@ data class Campaign override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : CampaignSdo { init { - CampaignSdo.objectValidationRules(this) + CampaignSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt index 712be95..14567a1 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -24,7 +26,7 @@ interface CourseOfActionSdo : StixDomainObject { override val stixType = StixType("course-of-action") - override fun objectValidationRules(obj: CourseOfActionSdo) { + override fun objectValidationRules(obj: CourseOfActionSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.actionReference != null){ @@ -105,11 +107,12 @@ data class CourseOfAction( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : CourseOfActionSdo { init { - CourseOfActionSdo.objectValidationRules(this) + CourseOfActionSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt index b19bbcc..3201dff 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -22,7 +24,7 @@ interface GroupingSdo : StixDomainObject { override val stixType = StixType("grouping") - override fun objectValidationRules(obj: GroupingSdo) { + override fun objectValidationRules(obj: GroupingSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -47,12 +49,12 @@ data class Grouping( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - GroupingSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : GroupingSdo { init { - GroupingSdo.objectValidationRules(this) + GroupingSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt index ce91d24..1db62d3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -26,7 +28,7 @@ interface IdentitySdo : StixDomainObject { override val stixType = StixType("identity") - override fun objectValidationRules(obj: IdentitySdo) { + override fun objectValidationRules(obj: IdentitySdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -59,11 +61,12 @@ data class Identity( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : IdentitySdo { init { - IdentitySdo.objectValidationRules(this) + IdentitySdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index a4ae595..005590c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -29,7 +31,7 @@ interface IndicatorSdo : StixDomainObject { override val stixType = StixType("indicator") - override fun objectValidationRules(obj: IndicatorSdo) { + override fun objectValidationRules(obj: IndicatorSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) require(obj.validUntil?.instant!!.isAfter(obj.validFrom.instant), @@ -104,11 +106,12 @@ data class Indicator( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : IndicatorSdo { init { - IndicatorSdo.objectValidationRules(this) + IndicatorSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index 57e0efa..dd44404 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -31,7 +33,7 @@ interface InfrastructureSdo : StixDomainObject { override val stixType = StixType("infrastructure") - override fun objectValidationRules(obj: InfrastructureSdo) { + override fun objectValidationRules(obj: InfrastructureSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null){ @@ -145,12 +147,12 @@ data class Infrastructure( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - InfrastructureSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : InfrastructureSdo { init { - InfrastructureSdo.objectValidationRules(this) + InfrastructureSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt index 9d36066..5c20c14 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -27,7 +29,7 @@ interface IntrusionSetSdo : StixDomainObject { override val stixType = StixType("intrusion-set") - override fun objectValidationRules(obj: IntrusionSetSdo) { + override fun objectValidationRules(obj: IntrusionSetSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null){ @@ -123,12 +125,12 @@ data class IntrusionSet( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - IntrusionSetSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : IntrusionSetSdo { init { - IntrusionSetSdo.objectValidationRules(this) + IntrusionSetSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt index 5b8f651..0badec3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -30,7 +32,7 @@ interface LocationSdo : StixDomainObject { override val stixType = StixType("location") - override fun objectValidationRules(obj: LocationSdo) { + override fun objectValidationRules(obj: LocationSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.latitude != null) require(obj.longitude != null, @@ -70,12 +72,13 @@ data class Location( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : LocationSdo { init { - LocationSdo.objectValidationRules(this) + LocationSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt index 39302d1..1f1134e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -32,7 +34,7 @@ interface MalwareSdo : StixDomainObject { override val stixType = StixType("malware") - override fun objectValidationRules(obj: MalwareSdo) { + override fun objectValidationRules(obj: MalwareSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null) { @@ -204,12 +206,12 @@ data class Malware override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - MalwareSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : MalwareSdo { init { - MalwareSdo.objectValidationRules(this) + MalwareSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt index eaa8636..6d9c05f 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -33,7 +35,7 @@ interface MalwareAnalysisSdo : StixDomainObject { override val stixType = StixType("malware-analysis") - override fun objectValidationRules(obj: MalwareAnalysisSdo) { + override fun objectValidationRules(obj: MalwareAnalysisSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) //@TODO Product Name Validation enhancement: The name of the analysis engine or product that was used. Product names ​SHOULD​ be all lowercase with words separated by a dash "-". For cases where the name of a product cannot be specified, a value of "anonymized" MUST ​be used. @@ -101,12 +103,13 @@ data class MalwareAnalysis override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : MalwareAnalysisSdo { init { - MalwareAnalysisSdo.objectValidationRules(this) + MalwareAnalysisSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt index 4de187d..2e02714 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -21,14 +23,11 @@ interface NoteSdo : StixDomainObject { override val stixType = StixType("note") - override fun objectValidationRules(obj: NoteSdo) { + override fun objectValidationRules(obj: NoteSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) - } - override val allowedRelationships: List = listOf( - - ) + override val allowedRelationships: List = listOf() } } @@ -50,12 +49,12 @@ data class Note( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - NoteSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : NoteSdo { init { - NoteSdo.objectValidationRules(this) + NoteSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt index 86b9224..6d5c553 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sdo.StixDomainObject @@ -19,7 +21,7 @@ interface ObservedDataSdo : StixDomainObject { override val stixType = StixType("observed-data") - override fun objectValidationRules(obj: ObservedDataSdo) { + override fun objectValidationRules(obj: ObservedDataSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) require(obj.lastObserved.instant >= obj.firstObserved.instant, @@ -28,7 +30,7 @@ interface ObservedDataSdo : StixDomainObject { require(obj.numberObserved.value in 1..999999999, lazyMessage = { "number_observed must be between 1 and 999,999,999." }) - require(obj.objectRefs.any { it.type in StixObjectRegistry.scoRegistry.keys }, + require(obj.objectRefs.any { it.type in stixRegistries.objectRegistry.scoRegistry.keys }, lazyMessage = { "object_refs must contain at least one SCO." }) } @@ -53,12 +55,12 @@ data class ObservedData( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - ObservedDataSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : ObservedDataSdo { init { - ObservedDataSdo.objectValidationRules(this) + ObservedDataSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt index 0ba2131..c6bd94c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -22,7 +24,7 @@ interface OpinionSdo : StixDomainObject { override val stixType = StixType("opinion") - override fun objectValidationRules(obj: OpinionSdo) { + override fun objectValidationRules(obj: OpinionSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -50,12 +52,13 @@ data class Opinion( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : OpinionSdo { init { - OpinionSdo.objectValidationRules(this) + OpinionSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt index 48f84c7..caef30a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -23,14 +25,11 @@ interface ReportSdo : StixDomainObject { override val stixType = StixType("report") - override fun objectValidationRules(obj: ReportSdo) { + override fun objectValidationRules(obj: ReportSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) - } - override val allowedRelationships: List = listOf( - - ) + override val allowedRelationships: List = listOf() } } @@ -52,12 +51,13 @@ data class Report( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : ReportSdo { init { - ReportSdo.objectValidationRules(this) + ReportSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt index 9192aca..b6ef98e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -31,7 +33,7 @@ interface ThreatActorSdo : StixDomainObject { override val stixType = StixType("threat-actor") - override fun objectValidationRules(obj: ThreatActorSdo) { + override fun objectValidationRules(obj: ThreatActorSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null){ @@ -136,12 +138,12 @@ data class ThreatActor( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - ThreatActorSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : ThreatActorSdo { init { - ThreatActorSdo.objectValidationRules(this) + ThreatActorSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index 724e80e..186489a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -25,9 +27,8 @@ interface ToolSdo : StixDomainObject { override val stixType = StixType("tool") - override fun objectValidationRules(obj: ToolSdo) { + override fun objectValidationRules(obj: ToolSdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) - } override val allowedRelationships: List = listOf( @@ -94,12 +95,12 @@ data class Tool( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - ToolSdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : ToolSdo { init { - ToolSdo.objectValidationRules(this) + ToolSdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt index 4b0b843..69a1551 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sdo.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -19,7 +21,7 @@ interface VulnerabilitySdo : StixDomainObject { override val stixType = StixType("vulnerability") - override fun objectValidationRules(obj: VulnerabilitySdo) { + override fun objectValidationRules(obj: VulnerabilitySdo, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -42,12 +44,12 @@ data class Vulnerability( override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), override val confidence: StixConfidence? = null, - override val lang: StixLang? = null -) : - VulnerabilitySdo { + override val lang: StixLang? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : VulnerabilitySdo { init { - VulnerabilitySdo.objectValidationRules(this) + VulnerabilitySdo.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index 910cada..7d36908 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sro.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject import com.stephenott.stix.objects.core.sro.StixRelationshipObject @@ -21,7 +23,7 @@ interface RelationshipSro : StixRelationshipObject { override val stixType = StixType("relationship") - override fun objectValidationRules(obj: RelationshipSro) { + override fun objectValidationRules(obj: RelationshipSro, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -84,7 +86,8 @@ data class Relationship( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : RelationshipSro { /** @@ -123,15 +126,15 @@ data class Relationship( init { - val sourceClass: KClass = StixObjectRegistry.registry[sourceRef.type] + val sourceClass: KClass = this.stixRegistries.objectRegistry.registry[sourceRef.type] ?: throw IllegalStateException("Unable to find sourceRef in Object Registry") - val targetClass: KClass = StixObjectRegistry.registry[targetRef.type] + val targetClass: KClass = this.stixRegistries.objectRegistry.registry[targetRef.type] ?: throw IllegalStateException("Unable to find targetRef in Object Registry") //@TODO add support for x- custom objects - val allowedRelationships: List = StixObjectRelationshipRegistry + val allowedRelationships: List = this.stixRegistries.relationshipRegistry .registry.filter { sourceClass.isSubclassOf(it.from) && it.type == this.relationshipType && diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt index b2531b9..764ced3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.core.sro.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.common.StixObjectRegistry @@ -25,7 +27,7 @@ interface SightingSro : StixRelationshipObject { override val stixType: StixType = StixType("sighting") - override fun objectValidationRules(obj: SightingSro) { + override fun objectValidationRules(obj: SightingSro, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null) { @@ -38,7 +40,7 @@ interface SightingSro : StixRelationshipObject { lazyMessage = { "count must be between 0 and 999,999,999." }) } - require(obj.sightingOfRef.type in StixObjectRegistry.sdoRegistry.keys, + require(obj.sightingOfRef.type in stixRegistries.objectRegistry.sdoRegistry.keys, lazyMessage = { "sighting_of_ref must reference only a SDO." }) // @TODO should also support custom objects obj.observedDataRefs?.let { @@ -73,11 +75,12 @@ data class Sighting( override val specVersion: StixSpecVersion = StixSpecVersion(), override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean() + override val revoked: StixBoolean = StixBoolean(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ) : SightingSro { init { - SightingSro.objectValidationRules(this) + SightingSro.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt index 7828cd2..41de47f 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.meta.datamarking +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -25,7 +27,7 @@ interface MarkingDefinitionDm: DataMarking{ //@TODO review if this is needed for this object ) - override fun objectValidationRules(obj: MarkingDefinitionDm) { + override fun objectValidationRules(obj: MarkingDefinitionDm, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -43,12 +45,12 @@ data class MarkingDefinition( override val externalReferences: ExternalReferences? = null, override val objectMarkingsRefs: String? = null, override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion() - + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val stixRegistries: StixRegistries = Stix.defaultRegistries ): MarkingDefinitionDm { init { - MarkingDefinitionDm.objectValidationRules(this) + MarkingDefinitionDm.objectValidationRules(this, stixRegistries) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt index d3f0a1d..e05d921 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt @@ -1,5 +1,7 @@ package com.stephenott.stix.objects.meta.lco.objects +import com.stephenott.stix.Stix +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -18,7 +20,7 @@ interface LanguageContentLco : LanguageContentObject { companion object: CompanionStixType, BusinessRulesValidator, CompanionAllowedRelationships { - override fun objectValidationRules(obj: LanguageContentLco) { + override fun objectValidationRules(obj: LanguageContentLco, stixRegistries: StixRegistries) { requireStixType(this.stixType, obj) } @@ -45,12 +47,12 @@ data class LanguageContent( override val labels: StixLabels? = null, override val modified: StixInstant = StixInstant(created), override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null -) : - LanguageContentLco { + override val confidence: StixConfidence? = null, + override val stixRegistries: StixRegistries = Stix.defaultRegistries +) : LanguageContentLco { init { - LanguageContentLco.objectValidationRules(this) + LanguageContentLco.objectValidationRules(this, stixRegistries) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/serialization/StixContentMapper.kt b/src/main/kotlin/com/stephenott/stix/serialization/StixContentMapper.kt new file mode 100644 index 0000000..35163bf --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/StixContentMapper.kt @@ -0,0 +1,10 @@ +package com.stephenott.stix.serialization + +import com.stephenott.stix.StixRegistries + +interface StixContentMapper { + val mapper: Any + + val stixRegistries: StixRegistries +} + diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index bb4f909..30a3b7d 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -3,66 +3,64 @@ package com.stephenott.stix.serialization.json import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategy -import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.KotlinModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.registerKotlinModule import com.stephenott.stix.StixBundle import com.stephenott.stix.StixContent -import kotlin.reflect.full.cast +import com.stephenott.stix.StixRegistries +import com.stephenott.stix.serialization.StixContentMapper +import com.fasterxml.jackson.module.kotlin.* -fun createStixMapper(): ObjectMapper { - return ObjectMapper() - .registerModule(KotlinModule()) //@TODO see jackson kotlin module issue #87. Waiting for fix. Currently if a subtype does not exist then it fails to provide a meaningful error. - .registerModule(JavaTimeModule()) - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .registerModule(createStixInstantSerializationModule()) - .registerModule(createStixIdentifierSerializationModule()) - .registerModule(createStixTypeSerializationModule()) - .registerModule(createStixSpecVersionSerializationModule()) - .registerModule(createStixBooleanSerializationModule()) - .registerModule(createStixContentSerializationModule()) - .registerModule(createRelationshipTypeSerializationModule()) - .registerModule(createStixIntegerSerializationModule()) - .registerModule(createStixConfidenceSerializationModule()) - .registerModule(createStixOpenVocabSerializationModule()) - .registerModule(createStixMarkingObjectSerializationModule()) -} - -class StixContentMapper(){ - /** - * Should generally not be needed. But provided just in case - */ - val jsonMapper: ObjectMapper = createStixMapper() +class StixJsonContentMapper( + override val stixRegistries: StixRegistries = StixRegistries(), + override val mapper: ObjectMapper = createStixJsonObjectMapper(stixRegistries) +) : StixContentMapper { /** * Parse a json string into any kind of Stix Content (SDO, SCO, SRO, Relationships, etc) */ - inline fun parseJson(json: String): T { -// val content: T - + inline fun parseJson(json: String): T { try { - return jsonMapper.readValue(json, T::class.java) -// return content - } catch (e: Exception){ + return mapper.readValue(json) + } catch (e: Exception) { throw IllegalArgumentException("Unable to parse json.", e) } + } -// try { -// return T::class.cast(content) -// } catch (e: Exception){ -// throw IllegalArgumentException("Unable to parse json.", e) -// } + fun toJson(value: StixContent): String { + return this.mapper.writeValueAsString(value) } + fun toJson(value: StixBundle): String { + return this.mapper.writeValueAsString(value) + } + + companion object { + fun createStixJsonObjectMapper(stixRegistries: StixRegistries): ObjectMapper { + return ObjectMapper() + .registerModule(KotlinModule()) //@TODO see jackson kotlin module issue #87. Waiting for fix. Currently if a subtype does not exist then it fails to provide a meaningful error. + .registerModule(JavaTimeModule()) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + .registerModule(createStixInstantSerializationModule()) + .registerModule(createStixIdentifierSerializationModule()) + .registerModule(createStixTypeSerializationModule()) + .registerModule(createStixSpecVersionSerializationModule()) + .registerModule(createStixBooleanSerializationModule()) + .registerModule(createStixContentSerializationModule(stixRegistries.objectRegistry)) + .registerModule(createRelationshipTypeSerializationModule()) + .registerModule(createStixIntegerSerializationModule()) + .registerModule(createStixConfidenceSerializationModule()) + .registerModule(createStixOpenVocabSerializationModule()) + .registerModule(createStixMarkingObjectSerializationModule(stixRegistries.markingObjectRegistry)) + } + } } -fun StixContent.toJson(mapper: StixContentMapper): String{ - return mapper.jsonMapper.writeValueAsString(this) + +fun StixContent.toJson(mapper: StixJsonContentMapper): String { + return mapper.mapper.writeValueAsString(this) } -fun StixBundle.toJson(mapper: StixContentMapper): String{ - return mapper.jsonMapper.writeValueAsString(this) +fun StixBundle.toJson(mapper: StixJsonContentMapper): String { + return mapper.mapper.writeValueAsString(this) } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt index 5f6d4e0..6d71d7a 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt @@ -1,16 +1,18 @@ package com.stephenott.stix.serialization.json +import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.databind.jsontype.NamedType import com.fasterxml.jackson.databind.module.SimpleModule import com.stephenott.stix.StixContent +import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.StixObjectRegistry -fun createStixContentSerializationModule(): SimpleModule { +fun createStixContentSerializationModule(objectRegistry: StixObjectRegistry): SimpleModule { val module: SimpleModule = SimpleModule() - StixObjectRegistry.registry.forEach { (type, clazz) -> + objectRegistry.registry.forEach { (type, clazz) -> module.registerSubtypes(NamedType(clazz.java, type.toString())) } @@ -25,45 +27,7 @@ fun createStixContentSerializationModule(): SimpleModule { visible = true, property = "type" ) -interface StixContentTypeMixin { -} - - -//class StixContentSerializer(): StdSerializer(StixContent::class.java) { -// override fun serializeWithType( -// value: StixContent?, -// gen: JsonGenerator?, -// serializers: SerializerProvider?, -// typeSer: TypeSerializer? -// ) { -// -// super.serializeWithType(value, gen, serializers, typeSer) -// } -// -// override fun serialize(value: StixContent?, gen: JsonGenerator?, provider: SerializerProvider?) { -// -// } -// -//} - - -//class StixContentDeserializer() : StdDeserializer(StixContent::class.java) { -// -// override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixContent { -// -// val node: JsonNode = p!!.codec.readTree(p) -// val typeProp = node.get("type") -// if (typeProp != null) { -// val stixType = StixType.parse(typeProp.asText()) -// -// val objectClass: KClass = StixObjectRegistry.registry.getOrElse(stixType, defaultValue = { -// throw IllegalArgumentException("Unable to parse the object. Ensure object type is supported.") -// }) -// -// return p.codec.treeToValue(node, objectClass.java) -// -// } else { -// throw IllegalArgumentException("type property was null or not provided.") -// } -// } -//} \ No newline at end of file +interface StixContentTypeMixin{ + @get:JsonIgnore //@TODO Review for need of a 'set:' + val stixRegistries: StixRegistries +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt index 7c091fa..7a2a5c2 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixMarkingObjectSerialization.kt @@ -3,6 +3,7 @@ package com.stephenott.stix.serialization.json import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.databind.jsontype.NamedType import com.fasterxml.jackson.databind.module.SimpleModule +import com.stephenott.stix.common.StixMarkingObjectRegistry import com.stephenott.stix.common.StixObjectRegistry import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition import com.stephenott.stix.objects.meta.datamarking.MarkingDefinitionDm @@ -10,13 +11,13 @@ import com.stephenott.stix.objects.meta.datamarking.MarkingObject import com.stephenott.stix.objects.meta.datamarking.objects.Statement import com.stephenott.stix.objects.meta.datamarking.objects.Tlp -fun createStixMarkingObjectSerializationModule(): SimpleModule { +fun createStixMarkingObjectSerializationModule(markingObjectRegistry: StixMarkingObjectRegistry): SimpleModule { val module = SimpleModule() // Registers each markingObject that is located in the MarkingObjectRegistry //@TODO review implications for race event where these will not be loaded when create the StixMapper class. Likely will want to pass the data into the function. - StixObjectRegistry.markingObjectRegistry.forEach { (typeOv, clazz) -> + markingObjectRegistry.registry.forEach { (typeOv, clazz) -> module.registerSubtypes(NamedType(clazz.java, typeOv.getValue())) } diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt new file mode 100644 index 0000000..550bfb5 --- /dev/null +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt @@ -0,0 +1,24 @@ +package com.stephenott.stix.serialization.json + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.databind.module.SimpleModule +import com.stephenott.stix.StixContent +import com.stephenott.stix.StixRegistries + +/** + * Provides JsonIgnore annotation on the stixRegistries object in StixContent + */ +fun createStixContentStixRegistriesSerializationModule(): SimpleModule { + val module: SimpleModule = SimpleModule() + + module.setMixInAnnotation(StixContent::class.java, StixContentStixRegistriesMixin::class.java) + + return module +} + + +interface StixContentStixRegistriesMixin { + + @get:JsonIgnore + val stixRegistries: StixRegistries +} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt index c748284..9798de1 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTypeSerialization.kt @@ -2,45 +2,39 @@ package com.stephenott.stix.serialization.json import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.JsonToken -import com.fasterxml.jackson.core.type.WritableTypeId import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.KeyDeserializer import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.deser.std.StdDeserializer -import com.fasterxml.jackson.databind.jsontype.TypeDeserializer -import com.fasterxml.jackson.databind.jsontype.TypeSerializer import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.ser.std.StdSerializer import com.stephenott.stix.type.StixType - fun createStixTypeSerializationModule(): SimpleModule{ return SimpleModule() .addSerializer(StixType::class.java, StixTypeSerializer()) .addDeserializer(StixType::class.java, StixTypeDeserializer()) + .addKeyDeserializer(StixType::class.java, StixTypeKeyDeserializer()) } class StixTypeSerializer() : StdSerializer(StixType::class.java) { -// override fun serializeWithType( -// value: StixType?, -// gen: JsonGenerator?, -// serializers: SerializerProvider?, -// typeSer: TypeSerializer? -// ) { -// val typeId: WritableTypeId = typeSer!!.typeId(value, JsonToken.VALUE_STRING) -// gen!!.writeString(value.toString()) -// typeId.wrapperWritten = !gen.canWriteTypeId() -// typeSer.writeTypeSuffix(gen, typeId) -// } - override fun serialize(value: StixType?, gen: JsonGenerator?, provider: SerializerProvider?) { gen!!.writeString(value!!.toString()) } } -class StixTypeDeserializer() : StdDeserializer(StixType::class.java) { +class StixTypeKeyDeserializer() : KeyDeserializer(){ + //@TODO *** why this was needed. Was getting a key map error from jackson when not inlcuded. + // It started to occur for a unknown reason + //This should not be required + override fun deserializeKey(key: String?, ctxt: DeserializationContext?): Any { + return key!! + } +} + +class StixTypeDeserializer() : StdDeserializer(StixType::class.java) { override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StixType { try { return StixType.parse(p!!.text) From c6b1c91c89ec74298b824b14323f211c91a45e61 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Fri, 15 Nov 2019 20:52:57 -0500 Subject: [PATCH 17/21] cleanup for gradle --- .gitignore | 129 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 3119c0e..6ef70a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,90 @@ # Created by .ignore support plugin (hsz.mobi) -### Java template +### Gradle template +.gradle/ +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/ + + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Kotlin template # Compiled class file +*.class # Log file *.log @@ -14,6 +98,7 @@ # Package Files # *.jar *.war +*.nar *.ear *.zip *.tar.gz @@ -22,33 +107,15 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties - -.settings -bin/ -build-reports/ - -# Eclipse -.classpath -.factorypath -.project -.settings/ - -# Intellij -.idea/ -*.iws -*.iml - -# Mac -.DS_Store - -# Maven -log/ \ No newline at end of file +### Java template +# Compiled class file + +# Log file + +# BlueJ files + +# Mobile Tools for Java (J2ME) + +# Package Files # + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml From 17b8d42b280ad5cff70f87bc7e792e1d435aaec9 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Fri, 15 Nov 2019 21:12:56 -0500 Subject: [PATCH 18/21] bundle cleanup --- .../kotlin/com/stephenott/stix/StixBundle.kt | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/stephenott/stix/StixBundle.kt b/src/main/kotlin/com/stephenott/stix/StixBundle.kt index 4e5d13d..e33033d 100644 --- a/src/main/kotlin/com/stephenott/stix/StixBundle.kt +++ b/src/main/kotlin/com/stephenott/stix/StixBundle.kt @@ -1,23 +1,36 @@ package com.stephenott.stix +import com.stephenott.stix.common.BusinessRulesValidator +import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.sco.objects.ArtifactSco import com.stephenott.stix.type.StixIdentifier import com.stephenott.stix.type.StixType -interface StixBundle: StixContent{ +interface StixBundle : StixContent { val objects: LinkedHashSet - companion object { - val stixType = StixType("bundle") + companion object : + CompanionStixType, + BusinessRulesValidator { + + override val stixType = StixType("bundle") + + override fun objectValidationRules(obj: StixBundle, stixRegistries: StixRegistries) { + //@TODO + } } } - data class Bundle(override val type: StixType = StixBundle.stixType, override val id: StixIdentifier = StixIdentifier(type), override val objects: LinkedHashSet, override val stixRegistries: StixRegistries = Stix.defaultRegistries -): StixBundle{ +) : StixBundle { + + init { + StixBundle.objectValidationRules(this, stixRegistries) + } } \ No newline at end of file From 1ea6bede0b460517cf9f862d1ca8622c7848f196 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Mon, 18 Nov 2019 21:36:19 -0500 Subject: [PATCH 19/21] Refactor to support Configurable Stix Instances allowing trust groups to confgure configurations --- .../kotlin/com/stephenott/stix/MainRunner.kt | 44 +++++----- src/main/kotlin/com/stephenott/stix/Stix.kt | 21 ++++- .../kotlin/com/stephenott/stix/StixBundle.kt | 8 +- .../kotlin/com/stephenott/stix/StixContent.kt | 3 +- .../stix/common/CommonProperties.kt | 4 +- .../stix/common/ValidatorManager.kt | 6 +- .../objects/WindowsPeBinaryFileExtension.kt | 26 +++--- .../stix/objects/core/sco/objects/Artifact.kt | 8 +- .../core/sco/objects/AutonomousSystem.kt | 8 +- .../objects/core/sco/objects/Directory.kt | 40 ++++----- .../objects/core/sco/objects/DomainName.kt | 8 +- .../objects/core/sco/objects/EmailAddress.kt | 8 +- .../objects/core/sco/objects/EmailMessage.kt | 54 ++++++------ .../stix/objects/core/sco/objects/File.kt | 54 ++++++------ .../objects/core/sco/objects/IPv4Address.kt | 8 +- .../objects/core/sco/objects/IPv6Address.kt | 8 +- .../objects/core/sco/objects/MacAddress.kt | 8 +- .../stix/objects/core/sco/objects/Mutex.kt | 8 +- .../core/sco/objects/NetworkTraffic.kt | 60 ++++++------- .../stix/objects/core/sco/objects/Process.kt | 47 +++++------ .../stix/objects/core/sco/objects/Software.kt | 8 +- .../stix/objects/core/sco/objects/Url.kt | 9 +- .../objects/core/sco/objects/UserAccount.kt | 60 ++++++------- .../core/sco/objects/WindowsRegistryKey.kt | 36 ++++---- .../core/sco/objects/X509Certificate.kt | 52 ++++++------ .../objects/core/sdo/objects/AttackPattern.kt | 42 +++++----- .../stix/objects/core/sdo/objects/Campaign.kt | 50 +++++------ .../core/sdo/objects/CourseOfAction.kt | 46 +++++----- .../stix/objects/core/sdo/objects/Grouping.kt | 42 +++++----- .../stix/objects/core/sdo/objects/Identity.kt | 46 +++++----- .../objects/core/sdo/objects/Indicator.kt | 56 ++++++------- .../core/sdo/objects/Infrastructure.kt | 52 ++++++------ .../objects/core/sdo/objects/IntrusionSet.kt | 56 ++++++------- .../stix/objects/core/sdo/objects/Location.kt | 57 +++++++------ .../stix/objects/core/sdo/objects/Malware.kt | 64 +++++++------- .../core/sdo/objects/MalwareAnalysis.kt | 71 ++++++++-------- .../stix/objects/core/sdo/objects/Note.kt | 42 +++++----- .../objects/core/sdo/objects/ObservedData.kt | 49 ++++++----- .../stix/objects/core/sdo/objects/Opinion.kt | 45 +++++----- .../stix/objects/core/sdo/objects/Report.kt | 49 ++++++----- .../objects/core/sdo/objects/ThreatActor.kt | 64 +++++++------- .../stix/objects/core/sdo/objects/Tool.kt | 46 +++++----- .../objects/core/sdo/objects/Vulnerability.kt | 38 ++++----- .../objects/core/sro/objects/Relationship.kt | 84 +++++++++---------- .../stix/objects/core/sro/objects/Sighting.kt | 53 ++++++------ .../meta/datamarking/MarkingDefinition.kt | 30 +++---- .../meta/lco/objects/LanguageContent.kt | 41 +++++---- .../stix/serialization/json/JsonExtensions.kt | 11 ++- .../json/StixContentSerialization.kt | 14 ++-- .../json/StixRegistriesSerialization.kt | 24 ------ ...ation.kt => StixTimestampSerialization.kt} | 18 ++-- .../type/{StixInstant.kt => StixTimestamp.kt} | 8 +- .../stix/type/X509v3ExtensionsTypes.kt | 34 ++++---- 53 files changed, 911 insertions(+), 917 deletions(-) delete mode 100644 src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt rename src/main/kotlin/com/stephenott/stix/serialization/json/{StixInstantSerialization.kt => StixTimestampSerialization.kt} (54%) rename src/main/kotlin/com/stephenott/stix/type/{StixInstant.kt => StixTimestamp.kt} (87%) diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index a10efe2..4836cd0 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,44 +1,42 @@ package com.stephenott.stix -import com.stephenott.stix.objects.StixObject -import com.stephenott.stix.objects.core.StixCoreObject -import com.stephenott.stix.objects.core.sco.objects.IPv6Address import com.stephenott.stix.objects.core.sco.objects.NetworkTraffic -import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sdo.objects.AttackPattern -import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo -import com.stephenott.stix.objects.core.sro.StixRelationshipObject -import com.stephenott.stix.objects.core.sro.objects.Relationship -import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.objects.core.sro.objects.Sighting -import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition -import com.stephenott.stix.objects.meta.datamarking.objects.Statement -import com.stephenott.stix.objects.meta.datamarking.objects.Tlp import com.stephenott.stix.serialization.json.StixJsonContentMapper import com.stephenott.stix.serialization.json.toJson import com.stephenott.stix.type.* -import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv object MainRunner { @JvmStatic - fun main(args: Array){ + fun main(args: Array) { - val ap1 = AttackPattern(name = "124", confidence = StixConfidence(33)) - val ap2 = AttackPattern("1245") + // Create a Stix instance (used to provide configurations of supported objects + val stix1 = Stix() + + // generation of a Attack Pattern using the default configs + val ap = AttackPattern(name = "124", + confidence = StixConfidence(33)) + + // Generation of a Attack Pattern using the stix1 instance ("trust group") + // This is used to force a specific stix instance to be used: this would be used to + // validate/transfer objects between trust groups, ensuring that content is valid + val ap1: AttackPattern = stix1.create(AttackPattern(name = "124", + confidence = StixConfidence(33))) val sighting1 = Sighting(sightingOfRef = ap1.id) val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) - val mapper = StixJsonContentMapper(StixRegistries()) - + val mapper = StixJsonContentMapper.fromStixInstance(stix1) + val dog = "{\"name\":\"124\",\"type\":\"attack-pattern\",\"id\":\"attack-pattern--771e3ada-0db7-42be-b389-0590181f64c4\",\"created\":\"2019-11-19T00:29:54.294Z\",\"spec_version\":\"2.1\",\"modified\":\"2019-11-19T00:29:54.294Z\",\"confidence\":33,\"stixValidateOnConstruction\": \"222\"}" println(ap1.toJson(mapper)) - println(mapper.parseJson(ap1.toJson(mapper))) - println(mapper.parseJson(ap1.toJson(mapper))) - println(mapper.parseJson(ap1.toJson(mapper))) - println(mapper.parseJson(ap1.toJson(mapper))) - println(mapper.parseJson(ap1.toJson(mapper))) - println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(dog)) +// println(mapper.parseJson(ap1.toJson(mapper))) +// println(mapper.parseJson(ap1.toJson(mapper))) +// println(mapper.parseJson(ap1.toJson(mapper))) +// println(mapper.parseJson(ap1.toJson(mapper))) +// println(mapper.parseJson(ap1.toJson(mapper))) // // println(net1.toJson(mapper)) // println(mapper.parseJson(net1.toJson(mapper))) diff --git a/src/main/kotlin/com/stephenott/stix/Stix.kt b/src/main/kotlin/com/stephenott/stix/Stix.kt index a69579b..9925858 100644 --- a/src/main/kotlin/com/stephenott/stix/Stix.kt +++ b/src/main/kotlin/com/stephenott/stix/Stix.kt @@ -3,13 +3,32 @@ package com.stephenott.stix import com.stephenott.stix.common.StixMarkingObjectRegistry import com.stephenott.stix.common.StixObjectRegistry import com.stephenott.stix.common.StixObjectRelationshipRegistry +import com.sun.org.apache.xpath.internal.operations.Bool +import kotlin.reflect.full.createInstance +import kotlin.reflect.full.instanceParameter +import kotlin.reflect.full.memberFunctions -class Stix() { +data class Stix( + val registries: StixRegistries = StixRegistries() +) { //@TODO future place that common configurations will go. companion object { val defaultRegistries: StixRegistries = StixRegistries() + + var defaultValidateOnConstruction: Boolean = true + + val defaultStixInstance: Stix = Stix(defaultRegistries) + } + + + inline fun create(stixContent: T): T{ + val copy = stixContent::class.memberFunctions.first { it.name == "copy" } + val instanceParam = copy.instanceParameter!! + val validateParam = copy.parameters.first { it.name == StixContent::stixValidateOnConstruction.name } + val stixInstanceParam = copy.parameters.first { it.name == StixContent::stixInstance.name } + return copy.callBy(mapOf(instanceParam to stixContent, validateParam to true, stixInstanceParam to this)) as T } } diff --git a/src/main/kotlin/com/stephenott/stix/StixBundle.kt b/src/main/kotlin/com/stephenott/stix/StixBundle.kt index e33033d..9809f0c 100644 --- a/src/main/kotlin/com/stephenott/stix/StixBundle.kt +++ b/src/main/kotlin/com/stephenott/stix/StixBundle.kt @@ -3,7 +3,6 @@ package com.stephenott.stix import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.objects.StixObject -import com.stephenott.stix.objects.core.sco.objects.ArtifactSco import com.stephenott.stix.type.StixIdentifier import com.stephenott.stix.type.StixType @@ -16,7 +15,7 @@ interface StixBundle : StixContent { override val stixType = StixType("bundle") - override fun objectValidationRules(obj: StixBundle, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: StixBundle, stixInstance: Stix) { //@TODO } } @@ -26,11 +25,12 @@ interface StixBundle : StixContent { data class Bundle(override val type: StixType = StixBundle.stixType, override val id: StixIdentifier = StixIdentifier(type), override val objects: LinkedHashSet, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : StixBundle { init { - StixBundle.objectValidationRules(this, stixRegistries) + StixBundle.objectValidationRules(this, stixInstance) } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixContent.kt b/src/main/kotlin/com/stephenott/stix/StixContent.kt index b78e87d..a8166fc 100644 --- a/src/main/kotlin/com/stephenott/stix/StixContent.kt +++ b/src/main/kotlin/com/stephenott/stix/StixContent.kt @@ -8,6 +8,7 @@ interface StixContent : StixTypeProp, StixIdentifierProp { - val stixRegistries: StixRegistries + val stixInstance: Stix + val stixValidateOnConstruction: Boolean } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt index 030faaa..b7d262f 100644 --- a/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt +++ b/src/main/kotlin/com/stephenott/stix/common/CommonProperties.kt @@ -18,7 +18,7 @@ interface StixCreatedByRef{ } interface StixCreatedProp{ - val created: StixInstant + val created: StixTimestamp } interface StixExternalReferencesProp{ @@ -42,7 +42,7 @@ interface StixLabelsProp { } interface StixModifiedProp { - val modified: StixInstant + val modified: StixTimestamp } interface StixRevokedProp { diff --git a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt index d858b9e..a3e9dcb 100644 --- a/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt +++ b/src/main/kotlin/com/stephenott/stix/common/ValidatorManager.kt @@ -1,16 +1,14 @@ package com.stephenott.stix.common -import com.stephenott.stix.StixRegistries -import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.Stix import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.objects.core.sco.objects.AutonomousSystemSco import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.StixType import kotlin.reflect.KClass import kotlin.reflect.KProperty1 interface BusinessRulesValidator{ - fun objectValidationRules(obj: T, stixRegistries: StixRegistries) + fun objectValidationRules(obj: T, stixInstance: Stix) } interface BusinessRulesExtensionValidator{ diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt index dc86271..279d938 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/extension/objects/WindowsPeBinaryFileExtension.kt @@ -14,7 +14,7 @@ interface WindowsPeBinaryFileExtensionExt : ScoExtension { val imphash: String? val machineHex: StixHex? val numberOfSections: StixInteger? - val timeDateStamp: StixInstant? + val timeDateStamp: StixTimestamp? val pointerToSymbolTableHex: StixHex? val numberOfSymbols: StixInteger? val sizeOfOptionalHeader: StixInteger? @@ -44,18 +44,18 @@ interface WindowsPeBinaryFileExtensionExt : ScoExtension { } data class WindowsPeBinaryFileExtension( - override val peType: WindowsPebinaryTypeOv, - override val imphash: String? = null, - override val machineHex: StixHex? = null, - override val numberOfSections: StixInteger? = null, - override val timeDateStamp: StixInstant? = null, - override val pointerToSymbolTableHex: StixHex? = null, - override val numberOfSymbols: StixInteger? = null, - override val sizeOfOptionalHeader: StixInteger? = null, - override val characteristicsHex: StixHex? = null, - override val fileHeaderHashes: HashesDictionary? = null, - override val optionalHeader: WindowsPeOptionalHeaderType? = null, - override val sections: WindowsPeSectionTypes? = null + override val peType: WindowsPebinaryTypeOv, + override val imphash: String? = null, + override val machineHex: StixHex? = null, + override val numberOfSections: StixInteger? = null, + override val timeDateStamp: StixTimestamp? = null, + override val pointerToSymbolTableHex: StixHex? = null, + override val numberOfSymbols: StixInteger? = null, + override val sizeOfOptionalHeader: StixInteger? = null, + override val characteristicsHex: StixHex? = null, + override val fileHeaderHashes: HashesDictionary? = null, + override val optionalHeader: WindowsPeOptionalHeaderType? = null, + override val sections: WindowsPeSectionTypes? = null ) : WindowsPeBinaryFileExtensionExt { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt index 8c57516..240168f 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -41,7 +40,7 @@ interface ArtifactSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: ArtifactSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: ArtifactSco, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.url != null) { @@ -77,11 +76,12 @@ data class Artifact( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : ArtifactSco { init { - ArtifactSco.objectValidationRules(this, stixRegistries) + ArtifactSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt index 9daaaab..9441eb4 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -36,7 +35,7 @@ interface AutonomousSystemSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: AutonomousSystemSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: AutonomousSystemSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -54,11 +53,12 @@ data class AutonomousSystem( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : AutonomousSystemSco { init { - AutonomousSystemSco.objectValidationRules(this, stixRegistries) + AutonomousSystemSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt index ac9a51a..456c040 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -15,9 +14,9 @@ interface DirectorySco : StixCyberObservableObject { val path: String val pathEnc: String? //@TODO add validation - val ctime: StixInstant? - val mtime: StixInstant? - val atime: StixInstant? + val ctime: StixTimestamp? + val mtime: StixTimestamp? + val atime: StixTimestamp? val containsRefs: StixIdentifiers? companion object : @@ -39,7 +38,7 @@ interface DirectorySco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: DirectorySco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: DirectorySco, stixInstance: Stix) { requireStixType(this.stixType, obj) obj.containsRefs?.let { @@ -52,24 +51,25 @@ interface DirectorySco : StixCyberObservableObject { } data class Directory( - override val path: String, - override val pathEnc: String? = null, - override val ctime: StixInstant? = null, - override val mtime: StixInstant? = null, - override val atime: StixInstant? = null, - override val containsRefs: StixIdentifiers?, - override val type: StixType = StixType(DirectorySco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val path: String, + override val pathEnc: String? = null, + override val ctime: StixTimestamp? = null, + override val mtime: StixTimestamp? = null, + override val atime: StixTimestamp? = null, + override val containsRefs: StixIdentifiers?, + override val type: StixType = StixType(DirectorySco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : DirectorySco { init { - DirectorySco.objectValidationRules(this, stixRegistries) + DirectorySco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt index 5a5f83a..52c7bce 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -49,7 +48,7 @@ interface DomainNameSco : StixCyberObservableObject { ) ) - override fun objectValidationRules(obj: DomainNameSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: DomainNameSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -66,11 +65,12 @@ data class DomainName( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : DomainNameSco { init { - DomainNameSco.objectValidationRules(this, stixRegistries) + DomainNameSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt index af98c93..de1f099 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -36,7 +35,7 @@ interface EmailAddressSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: EmailAddressSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: EmailAddressSco, stixInstance: Stix) { requireStixType(this.stixType, obj) obj.belongsToRef?.let { @@ -59,11 +58,12 @@ data class EmailAddress( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : EmailAddressSco { init { - EmailAddressSco.objectValidationRules(this, stixRegistries) + EmailAddressSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt index 2e11e43..e8bf0aa 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -14,7 +13,7 @@ import kotlin.reflect.KProperty1 interface EmailMessageSco : StixCyberObservableObject { val isMultipart: StixBoolean - val date: StixInstant? + val date: StixTimestamp? val contentType: String? val fromRef: StixIdentifier? val senderRef: StixIdentifier? @@ -50,7 +49,7 @@ interface EmailMessageSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: EmailMessageSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: EmailMessageSco, stixInstance: Stix) { requireStixType(this.stixType, obj) obj.fromRef?.let { @@ -86,33 +85,34 @@ interface EmailMessageSco : StixCyberObservableObject { } data class EmailMessage( - override val isMultipart: StixBoolean, - override val date: StixInstant? = null, - override val contentType: String? = null, - override val fromRef: StixIdentifier? = null, - override val senderRef: StixIdentifier? = null, - override val toRefs: StixIdentifiers? = null, - override val ccRefs: StixIdentifiers? = null, - override val bccRefs: StixIdentifiers? = null, - override val messageId: String? = null, - override val subject: String? = null, - override val receivedLines: StixStringList? = null, - override val additionalHeaderFields: AdditionalHeaderFieldsDictionary? = null, - override val body: String? = null, - override val bodyMultipart: MimePartTypes? = null, - override val rawEmailRef: StixIdentifier? = null, - override val type: StixType = StixType(EmailMessageSco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val isMultipart: StixBoolean, + override val date: StixTimestamp? = null, + override val contentType: String? = null, + override val fromRef: StixIdentifier? = null, + override val senderRef: StixIdentifier? = null, + override val toRefs: StixIdentifiers? = null, + override val ccRefs: StixIdentifiers? = null, + override val bccRefs: StixIdentifiers? = null, + override val messageId: String? = null, + override val subject: String? = null, + override val receivedLines: StixStringList? = null, + override val additionalHeaderFields: AdditionalHeaderFieldsDictionary? = null, + override val body: String? = null, + override val bodyMultipart: MimePartTypes? = null, + override val rawEmailRef: StixIdentifier? = null, + override val type: StixType = StixType(EmailMessageSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : EmailMessageSco { init { - EmailMessageSco.objectValidationRules(this, stixRegistries) + EmailMessageSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt index 876c964..721f01b 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -21,9 +20,9 @@ interface FileSco : StixCyberObservableObject { val nameEnc: String? val magicNumberHex: StixHex? val mimeType: String? - val cTime: StixInstant? - val mTime: StixInstant? - val aTime: StixInstant? + val cTime: StixTimestamp? + val mTime: StixTimestamp? + val aTime: StixTimestamp? val parentDirectoryRef: StixIdentifier? val containsRefs: StixIdentifiers? val contentRef: StixIdentifier? @@ -55,7 +54,7 @@ interface FileSco : StixCyberObservableObject { WindowsPeBinaryFileExtensionExt::class ) - override fun objectValidationRules(obj: FileSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: FileSco, stixInstance: Stix) { requireStixType(this.stixType, obj) obj.size?.let { @@ -67,7 +66,7 @@ interface FileSco : StixCyberObservableObject { lazyMessage = { "parent_directory_ref must only contain a reference to a Directory object SCO." }) } obj.containsRefs?.let { - require(it.all { id -> stixRegistries.objectRegistry.registry.getValue(id.type).isSubclassOf(StixCyberObservableObject::class) }, + require(it.all { id -> stixInstance.registries.objectRegistry.registry.getValue(id.type).isSubclassOf(StixCyberObservableObject::class) }, lazyMessage = { "contains_refs can only contain references to SCOs." }) } obj.contentRef?.let { @@ -79,30 +78,31 @@ interface FileSco : StixCyberObservableObject { } data class File( - override val hashes: HashesDictionary? = null, - override val size: StixInteger? = null, - override val name: String? = null, - override val nameEnc: String? = null, - override val magicNumberHex: StixHex? = null, - override val mimeType: String? = null, - override val cTime: StixInstant? = null, - override val mTime: StixInstant? = null, - override val aTime: StixInstant? = null, - override val parentDirectoryRef: StixIdentifier? = null, - override val containsRefs: StixIdentifiers? = null, - override val contentRef: StixIdentifier? = null, - override val type: StixType = StixType(FileSco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val hashes: HashesDictionary? = null, + override val size: StixInteger? = null, + override val name: String? = null, + override val nameEnc: String? = null, + override val magicNumberHex: StixHex? = null, + override val mimeType: String? = null, + override val cTime: StixTimestamp? = null, + override val mTime: StixTimestamp? = null, + override val aTime: StixTimestamp? = null, + override val parentDirectoryRef: StixIdentifier? = null, + override val containsRefs: StixIdentifiers? = null, + override val contentRef: StixIdentifier? = null, + override val type: StixType = StixType(FileSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : FileSco { init { - FileSco.objectValidationRules(this, stixRegistries) + FileSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt index 6d74aff..ee4d616 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -45,7 +44,7 @@ interface IPv4AddressSco : StixCyberObservableObject { ) ) - override fun objectValidationRules(obj: IPv4AddressSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: IPv4AddressSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } } @@ -60,11 +59,12 @@ data class IPv4Address( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : IPv4AddressSco { init { - IPv4AddressSco.objectValidationRules(this, stixRegistries) + IPv4AddressSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt index 9411e58..dbf2962 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -45,7 +44,7 @@ interface IPv6AddressSco : StixCyberObservableObject { ) ) - override fun objectValidationRules(obj: IPv6AddressSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: IPv6AddressSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -61,11 +60,12 @@ data class IPv6Address( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : IPv6AddressSco { init { - IPv6AddressSco.objectValidationRules(this, stixRegistries) + IPv6AddressSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt index 7b7b090..1aa9e02 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -34,7 +33,7 @@ interface MacAddressSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: MacAddressSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: MacAddressSco, stixInstance: Stix) { requireStixType(this.stixType, obj) //@TODO The MAC address value ​MUST​ be represented as a single colon-delimited, lowercase MAC-48 address, which ​MUST​ include leading zeros for each octet. @@ -52,11 +51,12 @@ data class MacAddress( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : MacAddressSco { init { - MacAddressSco.objectValidationRules(this, stixRegistries) + MacAddressSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt index 8df72a2..286bf01 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -34,7 +33,7 @@ interface MutexSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: MutexSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: MutexSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -51,11 +50,12 @@ data class Mutex( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : MutexSco { init { - MutexSco.objectValidationRules(this, stixRegistries) + MutexSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt index 83b79e0..8f3bdc3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -14,8 +13,8 @@ import kotlin.reflect.KProperty1 interface NetworkTrafficSco : StixCyberObservableObject { - val start: StixInstant? - val end: StixInstant? + val start: StixTimestamp? + val end: StixTimestamp? val isActive: StixBoolean? val srcRef: StixIdentifier? val dstRef: StixIdentifier? @@ -61,7 +60,7 @@ interface NetworkTrafficSco : StixCyberObservableObject { NetworkSocketExtensionExt::class ) - override fun objectValidationRules(obj: NetworkTrafficSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: NetworkTrafficSco, stixInstance: Stix) { requireStixType(this.stixType, obj) obj.isActive?.let { @@ -128,35 +127,36 @@ interface NetworkTrafficSco : StixCyberObservableObject { } data class NetworkTraffic( - override val start: StixInstant? = null, - override val end: StixInstant? = null, - override val isActive: StixBoolean? = null, - override val srcRef: StixIdentifier? = null, - override val dstRef: StixIdentifier? = null, - override val srcPort: StixInteger? = null, - override val dstPort: StixInteger? = null, - override val protocols: StixStringList, - override val srcByteCount: StixInteger? = null, - override val dstByteCount: StixInteger? = null, - override val srcPackets: StixInteger? = null, - override val dstPackets: StixInteger? = null, - override val ipfix: IpfixDictionary? = null, - override val srcPayloadRef: StixIdentifier? = null, - override val dstPayloadRef: StixIdentifier? = null, - override val encapsulatesRefs: StixIdentifiers? = null, - override val encapsulatedByRef: StixIdentifier? = null, - override val type: StixType = StixType(NetworkTrafficSco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val start: StixTimestamp? = null, + override val end: StixTimestamp? = null, + override val isActive: StixBoolean? = null, + override val srcRef: StixIdentifier? = null, + override val dstRef: StixIdentifier? = null, + override val srcPort: StixInteger? = null, + override val dstPort: StixInteger? = null, + override val protocols: StixStringList, + override val srcByteCount: StixInteger? = null, + override val dstByteCount: StixInteger? = null, + override val srcPackets: StixInteger? = null, + override val dstPackets: StixInteger? = null, + override val ipfix: IpfixDictionary? = null, + override val srcPayloadRef: StixIdentifier? = null, + override val dstPayloadRef: StixIdentifier? = null, + override val encapsulatesRefs: StixIdentifiers? = null, + override val encapsulatedByRef: StixIdentifier? = null, + override val type: StixType = StixType(NetworkTrafficSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : NetworkTrafficSco { init { - NetworkTrafficSco.objectValidationRules(this, stixRegistries) + NetworkTrafficSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt index ffe6164..4ae7925 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -10,7 +9,6 @@ import com.stephenott.stix.objects.core.sco.extension.objects.WindowsServiceExte import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions -import com.stephenott.stix.type.vocab.EncryptionAlgorithmEnum import kotlin.reflect.KClass import kotlin.reflect.KProperty1 @@ -18,7 +16,7 @@ interface ProcessSco : StixCyberObservableObject { val isHidden: StixBoolean? val pid: StixInteger? - val createdTime: StixInstant? + val createdTime: StixTimestamp? val cwd: String? val commandLine: String? val environmentVariables: LinkedHashMap? //@TODO Refactor: https://github.com/oasis-tcs/cti-stix2/issues/185#issuecomment-543299610 @@ -50,7 +48,7 @@ interface ProcessSco : StixCyberObservableObject { WindowsServiceExtensionExt::class ) - override fun objectValidationRules(obj: ProcessSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: ProcessSco, stixInstance: Stix) { requireStixType(this.stixType, obj) require(obj.openedConnectionRef?.type == NetworkTrafficSco.stixType, @@ -73,29 +71,30 @@ interface ProcessSco : StixCyberObservableObject { } data class Process( - override val isHidden: StixBoolean? = null, - override val pid: StixInteger? = null, - override val createdTime: StixInstant? = null, - override val cwd: String? = null, - override val commandLine: String? = null, - override val environmentVariables: LinkedHashMap? = null, - override val openedConnectionRef: StixIdentifier? = null, - override val creatorUserRef: StixIdentifier? = null, - override val imageRef: StixIdentifier? = null, - override val parentRef: StixIdentifier? = null, - override val childRefs: StixIdentifiers? = null, - override val type: StixType = StixType(ProcessSco.stixType), - override val id: StixIdentifier = StixIdentifier(type), //@TODO review as spec currently says that a Process SCO uses a UUID v4 - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val isHidden: StixBoolean? = null, + override val pid: StixInteger? = null, + override val createdTime: StixTimestamp? = null, + override val cwd: String? = null, + override val commandLine: String? = null, + override val environmentVariables: LinkedHashMap? = null, + override val openedConnectionRef: StixIdentifier? = null, + override val creatorUserRef: StixIdentifier? = null, + override val imageRef: StixIdentifier? = null, + override val parentRef: StixIdentifier? = null, + override val childRefs: StixIdentifiers? = null, + override val type: StixType = StixType(ProcessSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), //@TODO review as spec currently says that a Process SCO uses a UUID v4 + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : ProcessSco { init { - ProcessSco.objectValidationRules(this, stixRegistries) + ProcessSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt index df3cbf0..0e6f7b4 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -42,7 +41,7 @@ interface SoftwareSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: SoftwareSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: SoftwareSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -63,11 +62,12 @@ data class Software( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : SoftwareSco { init { - SoftwareSco.objectValidationRules(this, stixRegistries) + SoftwareSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt index c6ed157..a6fd8cb 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt @@ -1,14 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions -import com.stephenott.stix.type.vocab.LanguageCodes import kotlin.reflect.KClass import kotlin.reflect.KProperty1 @@ -35,7 +33,7 @@ interface UrlSco : StixCyberObservableObject { ) - override fun objectValidationRules(obj: UrlSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: UrlSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -51,11 +49,12 @@ data class Url( override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), override val extensions: Extensions? = null, override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : UrlSco { init { - UrlSco.objectValidationRules(this, stixRegistries) + UrlSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt index c824ae0..4307531 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -24,11 +23,11 @@ interface UserAccountSco : StixCyberObservableObject { val isPrivilege: StixBoolean? val canEscalatePrivs: StixBoolean? val isDisabled: StixBoolean? - val accountCreated: StixInstant? - val accountExpires: StixInstant? - val credentialLastChanged: StixInstant? - val accountFirstLogin: StixInstant? - val accountLastLogin: StixInstant? + val accountCreated: StixTimestamp? + val accountExpires: StixTimestamp? + val credentialLastChanged: StixTimestamp? + val accountFirstLogin: StixTimestamp? + val accountLastLogin: StixTimestamp? companion object : CompanionStixType, @@ -53,7 +52,7 @@ interface UserAccountSco : StixCyberObservableObject { UnixAccountExtensionExt::class ) - override fun objectValidationRules(obj: UserAccountSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: UserAccountSco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -61,32 +60,33 @@ interface UserAccountSco : StixCyberObservableObject { } data class UserAccount( - override val userId: String? = null, - override val credential: String? = null, - override val accountLogin: String? = null, - override val accountType: AccountType? = null, - override val displayName: String? = null, - override val isServiceAccount: StixBoolean? = null, - override val isPrivilege: StixBoolean? = null, - override val canEscalatePrivs: StixBoolean? = null, - override val isDisabled: StixBoolean? = null, - override val accountCreated: StixInstant? = null, - override val accountExpires: StixInstant? = null, - override val credentialLastChanged: StixInstant? = null, - override val accountFirstLogin: StixInstant? = null, - override val accountLastLogin: StixInstant? = null, - override val type: StixType = StixType(UserAccountSco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val userId: String? = null, + override val credential: String? = null, + override val accountLogin: String? = null, + override val accountType: AccountType? = null, + override val displayName: String? = null, + override val isServiceAccount: StixBoolean? = null, + override val isPrivilege: StixBoolean? = null, + override val canEscalatePrivs: StixBoolean? = null, + override val isDisabled: StixBoolean? = null, + override val accountCreated: StixTimestamp? = null, + override val accountExpires: StixTimestamp? = null, + override val credentialLastChanged: StixTimestamp? = null, + override val accountFirstLogin: StixTimestamp? = null, + override val accountLastLogin: StixTimestamp? = null, + override val type: StixType = StixType(UserAccountSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : UserAccountSco { init { - UserAccountSco.objectValidationRules(this, stixRegistries) + UserAccountSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt index 0627858..2f4ef3e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt @@ -1,15 +1,12 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension -import com.stephenott.stix.objects.core.sco.extension.objects.UnixAccountExtensionExt import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.type.* import com.stephenott.stix.type.StixSpecVersion.Companion.StixVersions -import com.stephenott.stix.type.vocab.AccountType import kotlin.reflect.KClass import kotlin.reflect.KProperty1 @@ -17,7 +14,7 @@ interface WindowsRegistryKeySco : StixCyberObservableObject { val key: String? val values: WindowsRegistryValueTypes? - val modifiedTimed: StixInstant? + val modifiedTimed: StixTimestamp? val creatorUserRef: StixIdentifier? val numberOfSubkeys: StixInteger? @@ -39,7 +36,7 @@ interface WindowsRegistryKeySco : StixCyberObservableObject { override val allowedExtensions: List> = listOf() - override fun objectValidationRules(obj: WindowsRegistryKeySco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: WindowsRegistryKeySco, stixInstance: Stix) { requireStixType(this.stixType, obj) require( listOf(obj.key, obj.values, obj.modifiedTimed, obj.creatorUserRef, obj.numberOfSubkeys) @@ -56,23 +53,24 @@ interface WindowsRegistryKeySco : StixCyberObservableObject { } data class WindowsRegistryKey( - override val key: String? = null, - override val values: WindowsRegistryValueTypes? = null, - override val modifiedTimed: StixInstant? = null, - override val creatorUserRef: StixIdentifier? = null, - override val numberOfSubkeys: StixInteger? = null, - override val type: StixType = StixType(WindowsRegistryKeySco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val key: String? = null, + override val values: WindowsRegistryValueTypes? = null, + override val modifiedTimed: StixTimestamp? = null, + override val creatorUserRef: StixIdentifier? = null, + override val numberOfSubkeys: StixInteger? = null, + override val type: StixType = StixType(WindowsRegistryKeySco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : WindowsRegistryKeySco { init { - WindowsRegistryKeySco.objectValidationRules(this, stixRegistries) + WindowsRegistryKeySco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt index be89ca2..8530ebf 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sco.extension.ScoExtension @@ -19,8 +18,8 @@ interface X509CertificateSco : StixCyberObservableObject { val serialNumber: String? val signatureAlgorithm: String? val issuer: String? - val validityNotBefore: StixInstant? - val validityNotAfter: StixInstant? + val validityNotBefore: StixTimestamp? + val validityNotAfter: StixTimestamp? val subject: String? val subjectPublicKeyAlgorithm: String? val subjectPublicKeyModulus: String? @@ -45,7 +44,7 @@ interface X509CertificateSco : StixCyberObservableObject { override val allowedExtensions: List> = listOf() - override fun objectValidationRules(obj: X509CertificateSco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: X509CertificateSco, stixInstance: Stix) { requireStixType(this.stixType, obj) require(listOf( //@TODO review against Stix 2 Issues against 182 @@ -69,31 +68,32 @@ interface X509CertificateSco : StixCyberObservableObject { } data class X509Certificate( - override val isSelfSigned: StixBoolean? = null, - override val hashes: HashesDictionary? = null, - override val version: String? = null, - override val serialNumber: String? = null, - override val signatureAlgorithm: String? = null, - override val issuer: String? = null, - override val validityNotBefore: StixInstant? = null, - override val validityNotAfter: StixInstant? = null, - override val subject: String? = null, - override val subjectPublicKeyAlgorithm: String? = null, - override val subjectPublicKeyModulus: String? = null, - override val subjectPublicKeyExponent: StixInteger? = null, - override val x509v3Extensions: X509v3ExtensionsTypes? = null, - override val type: StixType = StixType(X509CertificateSco.stixType), - override val id: StixIdentifier = StixIdentifier(type), - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), - override val extensions: Extensions? = null, - override val defanged: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val isSelfSigned: StixBoolean? = null, + override val hashes: HashesDictionary? = null, + override val version: String? = null, + override val serialNumber: String? = null, + override val signatureAlgorithm: String? = null, + override val issuer: String? = null, + override val validityNotBefore: StixTimestamp? = null, + override val validityNotAfter: StixTimestamp? = null, + override val subject: String? = null, + override val subjectPublicKeyAlgorithm: String? = null, + override val subjectPublicKeyModulus: String? = null, + override val subjectPublicKeyExponent: StixInteger? = null, + override val x509v3Extensions: X509v3ExtensionsTypes? = null, + override val type: StixType = StixType(X509CertificateSco.stixType), + override val id: StixIdentifier = StixIdentifier(type), + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(StixVersions.TWO_DOT_ONE, false), + override val extensions: Extensions? = null, + override val defanged: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : X509CertificateSco { init { - X509CertificateSco.objectValidationRules(this, stixRegistries) + X509CertificateSco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt index 4adadac..e04744e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/AttackPattern.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -23,7 +22,7 @@ interface AttackPatternSdo : StixDomainObject { override val stixType = StixType("attack-pattern") - override fun objectValidationRules(obj: AttackPatternSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: AttackPatternSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -67,27 +66,30 @@ interface AttackPatternSdo : StixDomainObject { } data class AttackPattern( - override val name: String, - override val description: String? = null, - override val killChainPhases: KillChainPhases? = null, - override val type: StixType = AttackPatternSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val killChainPhases: KillChainPhases? = null, + override val type: StixType = AttackPatternSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : AttackPatternSdo { init { - AttackPatternSdo.objectValidationRules(this, stixRegistries) + if (this.stixValidateOnConstruction) { + AttackPatternSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt index 15a8e51..7949910 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -15,15 +14,15 @@ interface CampaignSdo : StixDomainObject { val name: String val description: String? val aliases: String? - val firstSeen: StixInstant? - val lastSeen: StixInstant? + val firstSeen: StixTimestamp? + val lastSeen: StixTimestamp? val objective: String? companion object: CompanionStixType, BusinessRulesValidator, CompanionAllowedRelationships { - override fun objectValidationRules(obj: CampaignSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: CampaignSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.firstSeen != null){ require(obj.lastSeen?.instant!!.isAfter(obj.firstSeen!!.instant)) @@ -98,30 +97,31 @@ interface CampaignSdo : StixDomainObject { } data class Campaign ( - override val name: String, - override val description: String? = null, - override val aliases: String? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val objective: String? = null, - override val type: StixType = CampaignSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val aliases: String? = null, + override val firstSeen: StixTimestamp? = null, + override val lastSeen: StixTimestamp? = null, + override val objective: String? = null, + override val type: StixType = CampaignSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : CampaignSdo { init { - CampaignSdo.objectValidationRules(this, stixRegistries) + CampaignSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt index 14567a1..41f4fac 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -26,7 +25,7 @@ interface CourseOfActionSdo : StixDomainObject { override val stixType = StixType("course-of-action") - override fun objectValidationRules(obj: CourseOfActionSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: CourseOfActionSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.actionReference != null){ @@ -89,30 +88,31 @@ interface CourseOfActionSdo : StixDomainObject { } data class CourseOfAction( - override val name: String, - override val description: String? = null, - override val actionType: CourseOfActionTypeOv? = null, - override val osExecutionEnvs: OsExecutionEnvs? = null, - override val actionBin: StixBinary? = null, - override val actionReference: ExternalReference? = null, - override val type: StixType = CourseOfActionSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val actionType: CourseOfActionTypeOv? = null, + override val osExecutionEnvs: OsExecutionEnvs? = null, + override val actionBin: StixBinary? = null, + override val actionReference: ExternalReference? = null, + override val type: StixType = CourseOfActionSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : CourseOfActionSdo { init { - CourseOfActionSdo.objectValidationRules(this, stixRegistries) + CourseOfActionSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt index 3201dff..a2fd96c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -24,7 +23,7 @@ interface GroupingSdo : StixDomainObject { override val stixType = StixType("grouping") - override fun objectValidationRules(obj: GroupingSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: GroupingSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -33,28 +32,29 @@ interface GroupingSdo : StixDomainObject { } data class Grouping( - override val name: String? = null, - override val description: String? = null, - override val context: GroupingContextOv, - override val objectRefs: StixIdentifiers, - override val type: StixType = GroupingSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String? = null, + override val description: String? = null, + override val context: GroupingContextOv, + override val objectRefs: StixIdentifiers, + override val type: StixType = GroupingSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : GroupingSdo { init { - GroupingSdo.objectValidationRules(this, stixRegistries) + GroupingSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt index 1db62d3..af1fb11 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -28,7 +27,7 @@ interface IdentitySdo : StixDomainObject { override val stixType = StixType("identity") - override fun objectValidationRules(obj: IdentitySdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: IdentitySdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -43,30 +42,31 @@ interface IdentitySdo : StixDomainObject { } data class Identity( - override val name: String, - override val description: String? = null, - override val roles: IdentityRoles? = null, - override val identityClass: IdentityClass? = null, - override val sectors: IndustrySectors? = null, - override val contactInformation: String? = null, - override val type: StixType = IdentitySdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val roles: IdentityRoles? = null, + override val identityClass: IdentityClass? = null, + override val sectors: IndustrySectors? = null, + override val contactInformation: String? = null, + override val type: StixType = IdentitySdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : IdentitySdo { init { - IdentitySdo.objectValidationRules(this, stixRegistries) + IdentitySdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index 005590c..83b1364 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -21,8 +20,8 @@ interface IndicatorSdo : StixDomainObject { val pattern: StixPattern val patternType: PatternType val patternVersion: String? - val validFrom: StixInstant - val validUntil: StixInstant? + val validFrom: StixTimestamp + val validUntil: StixTimestamp? val killChainPhases: KillChainPhases? companion object : CompanionStixType, @@ -31,7 +30,7 @@ interface IndicatorSdo : StixDomainObject { override val stixType = StixType("indicator") - override fun objectValidationRules(obj: IndicatorSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: IndicatorSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) require(obj.validUntil?.instant!!.isAfter(obj.validFrom.instant), @@ -85,33 +84,34 @@ interface IndicatorSdo : StixDomainObject { } data class Indicator( - override val name: String? = null, - override val description: String? = null, - override val indicatorTypes: IndicatorTypes, - override val pattern: StixPattern, - override val patternType: PatternType, - override val patternVersion: String? = null, - override val validFrom: StixInstant, - override val validUntil: StixInstant?, - override val killChainPhases: KillChainPhases?, - override val type: StixType = IndicatorSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String? = null, + override val description: String? = null, + override val indicatorTypes: IndicatorTypes, + override val pattern: StixPattern, + override val patternType: PatternType, + override val patternVersion: String? = null, + override val validFrom: StixTimestamp, + override val validUntil: StixTimestamp?, + override val killChainPhases: KillChainPhases?, + override val type: StixType = IndicatorSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : IndicatorSdo { init { - IndicatorSdo.objectValidationRules(this, stixRegistries) + IndicatorSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index dd44404..cbee996 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -24,8 +23,8 @@ interface InfrastructureSdo : StixDomainObject { val infrastructureTypes: InfrastructureTypes val aliases: StixStringList? val killChainPhases: KillChainPhases? - val firstSeen: StixInstant? - val lastSeen: StixInstant? + val firstSeen: StixTimestamp? + val lastSeen: StixTimestamp? companion object : CompanionStixType, BusinessRulesValidator, @@ -33,7 +32,7 @@ interface InfrastructureSdo : StixDomainObject { override val stixType = StixType("infrastructure") - override fun objectValidationRules(obj: InfrastructureSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: InfrastructureSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null){ @@ -128,31 +127,32 @@ interface InfrastructureSdo : StixDomainObject { } data class Infrastructure( - override val name: String, - override val description: String? = null, - override val infrastructureTypes: InfrastructureTypes, - override val aliases: StixStringList? = null, - override val killChainPhases: KillChainPhases? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val type: StixType = InfrastructureSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val infrastructureTypes: InfrastructureTypes, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val firstSeen: StixTimestamp? = null, + override val lastSeen: StixTimestamp? = null, + override val type: StixType = InfrastructureSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : InfrastructureSdo { init { - InfrastructureSdo.objectValidationRules(this, stixRegistries) + InfrastructureSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt index 5c20c14..b7bd35e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -16,8 +15,8 @@ interface IntrusionSetSdo : StixDomainObject { val name: String val description: String? val aliases: StixStringList? - val firstSeen: StixInstant? - val lastSeen: StixInstant? + val firstSeen: StixTimestamp? + val lastSeen: StixTimestamp? val goals: StixStringList? val resourceLevel: AttackResourceLevelOv? val primaryMotivation: AttackMotivationOv? @@ -29,7 +28,7 @@ interface IntrusionSetSdo : StixDomainObject { override val stixType = StixType("intrusion-set") - override fun objectValidationRules(obj: IntrusionSetSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: IntrusionSetSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null){ @@ -104,33 +103,34 @@ interface IntrusionSetSdo : StixDomainObject { } data class IntrusionSet( - override val name: String, - override val description: String? = null, - override val aliases: StixStringList? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val goals: StixStringList? = null, - override val resourceLevel: AttackResourceLevelOv? = null, - override val primaryMotivation: AttackMotivationOv? = null, - override val secondaryMotivations: AttackMotivations? = null, - override val type: StixType = IntrusionSetSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val aliases: StixStringList? = null, + override val firstSeen: StixTimestamp? = null, + override val lastSeen: StixTimestamp? = null, + override val goals: StixStringList? = null, + override val resourceLevel: AttackResourceLevelOv? = null, + override val primaryMotivation: AttackMotivationOv? = null, + override val secondaryMotivations: AttackMotivations? = null, + override val type: StixType = IntrusionSetSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : IntrusionSetSdo { init { - IntrusionSetSdo.objectValidationRules(this, stixRegistries) + IntrusionSetSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt index 0badec3..e67ef57 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -32,7 +31,7 @@ interface LocationSdo : StixDomainObject { override val stixType = StixType("location") - override fun objectValidationRules(obj: LocationSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: LocationSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.latitude != null) require(obj.longitude != null, @@ -50,35 +49,35 @@ interface LocationSdo : StixDomainObject { } data class Location( - override val name: String, - override val description: String? = null, - override val latitude: Latitude? = null, - override val longitude: Longitude? = null, - override val precision: LatLongPrecision? = null, - override val region: RegionOv? = null, - override val administrativeArea: AdministrativeArea?, - override val city: City? = null, - override val streetAddress: StreetAddress? = null, - override val postalCode: PostalCode? = null, - override val type: StixType = LocationSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries -) : - LocationSdo { + override val name: String, + override val description: String? = null, + override val latitude: Latitude? = null, + override val longitude: Longitude? = null, + override val precision: LatLongPrecision? = null, + override val region: RegionOv? = null, + override val administrativeArea: AdministrativeArea?, + override val city: City? = null, + override val streetAddress: StreetAddress? = null, + override val postalCode: PostalCode? = null, + override val type: StixType = LocationSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction +) : LocationSdo { init { - LocationSdo.objectValidationRules(this, stixRegistries) + LocationSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt index 1f1134e..4f0ab5c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -20,8 +19,8 @@ interface MalwareSdo : StixDomainObject { val isFamily: StixBoolean val aliases: StixStringList? val killChainPhases: KillChainPhases? - val firstSeen: StixInstant? - val lastSeen: StixInstant? + val firstSeen: StixTimestamp? + val lastSeen: StixTimestamp? val osExecutionEnvs: OsExecutionEnvs? val architectureExecutionEnvs: ProcessorArchitectures? val implementationLanguage: ImplementationLanguages? @@ -34,7 +33,7 @@ interface MalwareSdo : StixDomainObject { override val stixType = StixType("malware") - override fun objectValidationRules(obj: MalwareSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: MalwareSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null) { @@ -181,37 +180,38 @@ interface MalwareSdo : StixDomainObject { data class Malware ( - override val name: String, - override val description: String? = null, - override val malwareTypes: MalwareTypes, - override val isFamily: StixBoolean, - override val aliases: StixStringList? = null, - override val killChainPhases: KillChainPhases? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val osExecutionEnvs: OsExecutionEnvs? = null, - override val architectureExecutionEnvs: ProcessorArchitectures? = null, - override val implementationLanguage: ImplementationLanguages? = null, - override val capabilities: MalwareCapabilities? = null, - override val sampleRefs: StixIdentifiers? = null, - override val type: StixType = MalwareSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val malwareTypes: MalwareTypes, + override val isFamily: StixBoolean, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val firstSeen: StixTimestamp? = null, + override val lastSeen: StixTimestamp? = null, + override val osExecutionEnvs: OsExecutionEnvs? = null, + override val architectureExecutionEnvs: ProcessorArchitectures? = null, + override val implementationLanguage: ImplementationLanguages? = null, + override val capabilities: MalwareCapabilities? = null, + override val sampleRefs: StixIdentifiers? = null, + override val type: StixType = MalwareSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : MalwareSdo { init { - MalwareSdo.objectValidationRules(this, stixRegistries) + MalwareSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt index 6d9c05f..725cf2b 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -23,9 +22,9 @@ interface MalwareAnalysisSdo : StixDomainObject { val modules: StixStringList? val analysisEngineVersion: String? val analysisDefinitionVersion: String? - val submitted: StixInstant? - val analysisStarted: StixInstant? - val analysisEnded: StixInstant? + val submitted: StixTimestamp? + val analysisStarted: StixTimestamp? + val analysisEnded: StixTimestamp? val avResult: MalwareAvResult? val analysisScoRefs: StixIdentifiers? @@ -35,7 +34,7 @@ interface MalwareAnalysisSdo : StixDomainObject { override val stixType = StixType("malware-analysis") - override fun objectValidationRules(obj: MalwareAnalysisSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: MalwareAnalysisSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) //@TODO Product Name Validation enhancement: The name of the analysis engine or product that was used. Product names ​SHOULD​ be all lowercase with words separated by a dash "-". For cases where the name of a product cannot be specified, a value of "anonymized" MUST ​be used. @@ -77,39 +76,39 @@ interface MalwareAnalysisSdo : StixDomainObject { data class MalwareAnalysis ( - override val product: Product, - override val version: String? = null, - override val hostVmRef: StixIdentifier? = null, - override val operatingSystemRef: StixIdentifier? = null, - override val installedSystemRefs: StixIdentifiers? = null, - override val configurationVersion: String? = null, - override val modules: StixStringList? = null, - override val analysisEngineVersion: String? = null, - override val analysisDefinitionVersion: String? = null, - override val submitted: StixInstant? = null, - override val analysisStarted: StixInstant? = null, - override val analysisEnded: StixInstant? = null, - override val avResult: MalwareAvResult? = null, - override val analysisScoRefs: StixIdentifiers? = null, - override val type: StixType = MalwareAnalysisSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries -) : - MalwareAnalysisSdo { + override val product: Product, + override val version: String? = null, + override val hostVmRef: StixIdentifier? = null, + override val operatingSystemRef: StixIdentifier? = null, + override val installedSystemRefs: StixIdentifiers? = null, + override val configurationVersion: String? = null, + override val modules: StixStringList? = null, + override val analysisEngineVersion: String? = null, + override val analysisDefinitionVersion: String? = null, + override val submitted: StixTimestamp? = null, + override val analysisStarted: StixTimestamp? = null, + override val analysisEnded: StixTimestamp? = null, + override val avResult: MalwareAvResult? = null, + override val analysisScoRefs: StixIdentifiers? = null, + override val type: StixType = MalwareAnalysisSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction +) : MalwareAnalysisSdo { init { - MalwareAnalysisSdo.objectValidationRules(this, stixRegistries) + MalwareAnalysisSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt index 2e02714..6da3e40 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -23,7 +22,7 @@ interface NoteSdo : StixDomainObject { override val stixType = StixType("note") - override fun objectValidationRules(obj: NoteSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: NoteSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -33,28 +32,29 @@ interface NoteSdo : StixDomainObject { } data class Note( - override val abstract: String? = null, - override val content: String, - override val authors: StixStringList? = null, - override val objectRefs: StixIdentifiers, - override val type: StixType = NoteSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val abstract: String? = null, + override val content: String, + override val authors: StixStringList? = null, + override val objectRefs: StixIdentifiers, + override val type: StixType = NoteSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : NoteSdo { init { - NoteSdo.objectValidationRules(this, stixRegistries) + NoteSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt index 6d5c553..4677049 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt @@ -1,17 +1,15 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* -import com.stephenott.stix.objects.core.sco.StixCyberObservableObject import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.type.* interface ObservedDataSdo : StixDomainObject { - val firstObserved: StixInstant - val lastObserved: StixInstant + val firstObserved: StixTimestamp + val lastObserved: StixTimestamp val numberObserved: StixInteger val objectRefs: StixIdentifiers @@ -21,7 +19,7 @@ interface ObservedDataSdo : StixDomainObject { override val stixType = StixType("observed-data") - override fun objectValidationRules(obj: ObservedDataSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: ObservedDataSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) require(obj.lastObserved.instant >= obj.firstObserved.instant, @@ -30,7 +28,7 @@ interface ObservedDataSdo : StixDomainObject { require(obj.numberObserved.value in 1..999999999, lazyMessage = { "number_observed must be between 1 and 999,999,999." }) - require(obj.objectRefs.any { it.type in stixRegistries.objectRegistry.scoRegistry.keys }, + require(obj.objectRefs.any { it.type in stixInstance.registries.objectRegistry.scoRegistry.keys }, lazyMessage = { "object_refs must contain at least one SCO." }) } @@ -39,28 +37,29 @@ interface ObservedDataSdo : StixDomainObject { } data class ObservedData( - override val firstObserved: StixInstant, - override val lastObserved: StixInstant, - override val numberObserved: StixInteger, - override val objectRefs: StixIdentifiers, - override val type: StixType = ObservedDataSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val firstObserved: StixTimestamp, + override val lastObserved: StixTimestamp, + override val numberObserved: StixInteger, + override val objectRefs: StixIdentifiers, + override val type: StixType = ObservedDataSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : ObservedDataSdo { init { - ObservedDataSdo.objectValidationRules(this, stixRegistries) + ObservedDataSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt index c6bd94c..009a565 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -24,7 +23,7 @@ interface OpinionSdo : StixDomainObject { override val stixType = StixType("opinion") - override fun objectValidationRules(obj: OpinionSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: OpinionSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -36,29 +35,29 @@ interface OpinionSdo : StixDomainObject { } data class Opinion( - override val explanation: String? = null, - override val authors: StixStringList? = null, - override val opinion: OpinionEnum, - override val objectRefs: StixIdentifiers, - override val type: StixType = OpinionSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries -) : - OpinionSdo { + override val explanation: String? = null, + override val authors: StixStringList? = null, + override val opinion: OpinionEnum, + override val objectRefs: StixIdentifiers, + override val type: StixType = OpinionSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction +) : OpinionSdo { init { - OpinionSdo.objectValidationRules(this, stixRegistries) + OpinionSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt index caef30a..efde316 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -16,7 +15,7 @@ interface ReportSdo : StixDomainObject { val name: String val description: String? val reportTypes: ReportTypes - val published: StixInstant + val published: StixTimestamp val objectRefs: StixIdentifiers companion object : CompanionStixType, @@ -25,7 +24,7 @@ interface ReportSdo : StixDomainObject { override val stixType = StixType("report") - override fun objectValidationRules(obj: ReportSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: ReportSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -34,30 +33,30 @@ interface ReportSdo : StixDomainObject { } data class Report( - override val name: String, - override val description: String? = null, - override val reportTypes: ReportTypes, - override val published: StixInstant, - override val objectRefs: StixIdentifiers, - override val type: StixType = ReportSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries -) : - ReportSdo { + override val name: String, + override val description: String? = null, + override val reportTypes: ReportTypes, + override val published: StixTimestamp, + override val objectRefs: StixIdentifiers, + override val type: StixType = ReportSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction +) : ReportSdo { init { - ReportSdo.objectValidationRules(this, stixRegistries) + ReportSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt index b6ef98e..49200d9 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -17,8 +16,8 @@ interface ThreatActorSdo : StixDomainObject { val description: String? val threatActorTypes: ThreatActorTypes val aliases: StixStringList? - val firstSeen: StixInstant? - val lastSeen: StixInstant? + val firstSeen: StixTimestamp? + val lastSeen: StixTimestamp? val roles: ThreatActorRoles? val goals: StixStringList? val sophistication: ThreatActorSophisticationOv? @@ -33,7 +32,7 @@ interface ThreatActorSdo : StixDomainObject { override val stixType = StixType("threat-actor") - override fun objectValidationRules(obj: ThreatActorSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: ThreatActorSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null){ @@ -113,37 +112,38 @@ interface ThreatActorSdo : StixDomainObject { } data class ThreatActor( - override val name: String, - override val description: String? = null, - override val threatActorTypes: ThreatActorTypes, - override val aliases: StixStringList? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val roles: ThreatActorRoles? = null, - override val goals: StixStringList? = null, - override val sophistication: ThreatActorSophisticationOv? = null, - override val resourceLevel: AttackResourceLevelOv? = null, - override val primaryMotivation: AttackMotivationOv? = null, - override val secondaryMotivation: AttackMotivationOv? = null, - override val personalMotivations: AttackMotivations? = null, - override val type: StixType = ThreatActorSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val threatActorTypes: ThreatActorTypes, + override val aliases: StixStringList? = null, + override val firstSeen: StixTimestamp? = null, + override val lastSeen: StixTimestamp? = null, + override val roles: ThreatActorRoles? = null, + override val goals: StixStringList? = null, + override val sophistication: ThreatActorSophisticationOv? = null, + override val resourceLevel: AttackResourceLevelOv? = null, + override val primaryMotivation: AttackMotivationOv? = null, + override val secondaryMotivation: AttackMotivationOv? = null, + override val personalMotivations: AttackMotivations? = null, + override val type: StixType = ThreatActorSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : ThreatActorSdo { init { - ThreatActorSdo.objectValidationRules(this, stixRegistries) + ThreatActorSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index 186489a..18f4e01 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -27,7 +26,7 @@ interface ToolSdo : StixDomainObject { override val stixType = StixType("tool") - override fun objectValidationRules(obj: ToolSdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: ToolSdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -77,30 +76,31 @@ interface ToolSdo : StixDomainObject { } data class Tool( - override val name: String, - override val description: String? = null, - override val toolTypes: ToolTypes, - override val aliases: StixStringList? = null, - override val killChainPhases: KillChainPhases? = null, - override val toolVersion: String? = null, - override val type: StixType = ToolSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val toolTypes: ToolTypes, + override val aliases: StixStringList? = null, + override val killChainPhases: KillChainPhases? = null, + override val toolVersion: String? = null, + override val type: StixType = ToolSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : ToolSdo { init { - ToolSdo.objectValidationRules(this, stixRegistries) + ToolSdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt index 69a1551..17200a2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sdo.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -21,7 +20,7 @@ interface VulnerabilitySdo : StixDomainObject { override val stixType = StixType("vulnerability") - override fun objectValidationRules(obj: VulnerabilitySdo, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: VulnerabilitySdo, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -30,26 +29,27 @@ interface VulnerabilitySdo : StixDomainObject { } data class Vulnerability( - override val name: String, - override val description: String? = null, - override val type: StixType = VulnerabilitySdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val lang: StixLang? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String, + override val description: String? = null, + override val type: StixType = VulnerabilitySdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val lang: StixLang? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : VulnerabilitySdo { init { - VulnerabilitySdo.objectValidationRules(this, stixRegistries) + VulnerabilitySdo.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index 7d36908..083fd56 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.core.sro.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.* import com.stephenott.stix.objects.StixObject import com.stephenott.stix.objects.core.sro.StixRelationshipObject @@ -15,15 +14,15 @@ interface RelationshipSro : StixRelationshipObject { val description: String? val sourceRef: StixIdentifier val targetRef: StixIdentifier - val startTime: StixInstant? - val stopTime: StixInstant? + val startTime: StixTimestamp? + val stopTime: StixTimestamp? companion object: CompanionStixType, BusinessRulesValidator { override val stixType = StixType("relationship") - override fun objectValidationRules(obj: RelationshipSro, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: RelationshipSro, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -70,24 +69,25 @@ data class AllowedRelationship( ) {} data class Relationship( - override val relationshipType: RelationshipType, - override val description: String? = null, - override val sourceRef: StixIdentifier, - override val targetRef: StixIdentifier, - override val startTime: StixInstant? = null, - override val stopTime: StixInstant? = null, - override val type: StixType = RelationshipSro.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val relationshipType: RelationshipType, + override val description: String? = null, + override val sourceRef: StixIdentifier, + override val targetRef: StixIdentifier, + override val startTime: StixTimestamp? = null, + override val stopTime: StixTimestamp? = null, + override val type: StixType = RelationshipSro.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : RelationshipSro { /** @@ -98,23 +98,23 @@ data class Relationship( } constructor( - relationshipType: RelationshipType, - description: String? = null, - sourceRef: StixObject, - targetRef: StixObject, - startTime: StixInstant? = null, - stopTime: StixInstant? = null, - type: StixType = RelationshipSro.stixType, - id: StixIdentifier = StixIdentifier(type), - createdByRef: String? = null, - created: StixInstant = StixInstant(), - externalReferences: ExternalReferences? = null, - objectMarkingsRefs: String? = null, - granularMarkings: String? = null, - specVersion: StixSpecVersion = StixSpecVersion(), - labels: StixLabels? = null, - modified: StixInstant = StixInstant(created), - revoked: StixBoolean = StixBoolean() + relationshipType: RelationshipType, + description: String? = null, + sourceRef: StixObject, + targetRef: StixObject, + startTime: StixTimestamp? = null, + stopTime: StixTimestamp? = null, + type: StixType = RelationshipSro.stixType, + id: StixIdentifier = StixIdentifier(type), + createdByRef: String? = null, + created: StixTimestamp = StixTimestamp(), + externalReferences: ExternalReferences? = null, + objectMarkingsRefs: String? = null, + granularMarkings: String? = null, + specVersion: StixSpecVersion = StixSpecVersion(), + labels: StixLabels? = null, + modified: StixTimestamp = StixTimestamp(created), + revoked: StixBoolean = StixBoolean() ) : this( relationshipType, description, sourceRef.id, targetRef.id, startTime, stopTime, @@ -126,15 +126,15 @@ data class Relationship( init { - val sourceClass: KClass = this.stixRegistries.objectRegistry.registry[sourceRef.type] + val sourceClass: KClass = this.stixInstance.registries.objectRegistry.registry[sourceRef.type] ?: throw IllegalStateException("Unable to find sourceRef in Object Registry") - val targetClass: KClass = this.stixRegistries.objectRegistry.registry[targetRef.type] + val targetClass: KClass = this.stixInstance.registries.objectRegistry.registry[targetRef.type] ?: throw IllegalStateException("Unable to find targetRef in Object Registry") //@TODO add support for x- custom objects - val allowedRelationships: List = this.stixRegistries.relationshipRegistry + val allowedRelationships: List = this.stixInstance.registries.relationshipRegistry .registry.filter { sourceClass.isSubclassOf(it.from) && it.type == this.relationshipType && diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt index 764ced3..8bdc7ef 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -1,10 +1,8 @@ package com.stephenott.stix.objects.core.sro.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionStixType -import com.stephenott.stix.common.StixObjectRegistry import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.objects.IdentitySdo import com.stephenott.stix.objects.core.sdo.objects.LocationSdo @@ -14,8 +12,8 @@ import com.stephenott.stix.type.* interface SightingSro : StixRelationshipObject { val description: String? - val firstSeen: StixInstant? - val lastSeen: StixInstant? //@TODO *** REVIEW SPEC: Says must be after first seen. But does not say "equals + val firstSeen: StixTimestamp? + val lastSeen: StixTimestamp? //@TODO *** REVIEW SPEC: Says must be after first seen. But does not say "equals val count: StixInteger? val sightingOfRef: StixIdentifier val observedDataRefs: StixIdentifiers? @@ -27,7 +25,7 @@ interface SightingSro : StixRelationshipObject { override val stixType: StixType = StixType("sighting") - override fun objectValidationRules(obj: SightingSro, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: SightingSro, stixInstance: Stix) { requireStixType(this.stixType, obj) if (obj.firstSeen != null && obj.lastSeen != null) { @@ -40,7 +38,7 @@ interface SightingSro : StixRelationshipObject { lazyMessage = { "count must be between 0 and 999,999,999." }) } - require(obj.sightingOfRef.type in stixRegistries.objectRegistry.sdoRegistry.keys, + require(obj.sightingOfRef.type in stixInstance.registries.objectRegistry.sdoRegistry.keys, lazyMessage = { "sighting_of_ref must reference only a SDO." }) // @TODO should also support custom objects obj.observedDataRefs?.let { @@ -57,30 +55,31 @@ interface SightingSro : StixRelationshipObject { } data class Sighting( - override val description: String? = null, - override val firstSeen: StixInstant? = null, - override val lastSeen: StixInstant? = null, - override val count: StixInteger? = null, - override val sightingOfRef: StixIdentifier, - override val observedDataRefs: StixIdentifiers? = null, - override val whereSightedRefs: StixIdentifiers? = null, - override val summary: StixBoolean = StixBoolean(), - override val type: StixType = SightingSro.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val description: String? = null, + override val firstSeen: StixTimestamp? = null, + override val lastSeen: StixTimestamp? = null, + override val count: StixInteger? = null, + override val sightingOfRef: StixIdentifier, + override val observedDataRefs: StixIdentifiers? = null, + override val whereSightedRefs: StixIdentifiers? = null, + override val summary: StixBoolean = StixBoolean(), + override val type: StixType = SightingSro.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : SightingSro { init { - SightingSro.objectValidationRules(this, stixRegistries) + SightingSro.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt index 41de47f..d85a1a6 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt @@ -1,7 +1,6 @@ package com.stephenott.stix.objects.meta.datamarking import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType @@ -27,7 +26,7 @@ interface MarkingDefinitionDm: DataMarking{ //@TODO review if this is needed for this object ) - override fun objectValidationRules(obj: MarkingDefinitionDm, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: MarkingDefinitionDm, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -35,22 +34,23 @@ interface MarkingDefinitionDm: DataMarking{ } data class MarkingDefinition( - override val name: String? = null, - override val definitionType: MarkingDefinitionTypeOv, - override val definition: MarkingObject, - override val type: StixType = MarkingDefinitionDm.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val name: String? = null, + override val definitionType: MarkingDefinitionTypeOv, + override val definition: MarkingObject, + override val type: StixType = MarkingDefinitionDm.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ): MarkingDefinitionDm { init { - MarkingDefinitionDm.objectValidationRules(this, stixRegistries) + MarkingDefinitionDm.objectValidationRules(this, stixInstance) } diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt index e05d921..81a7d99 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/lco/objects/LanguageContent.kt @@ -1,26 +1,24 @@ package com.stephenott.stix.objects.meta.lco.objects import com.stephenott.stix.Stix -import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.BusinessRulesValidator import com.stephenott.stix.common.CompanionAllowedRelationships import com.stephenott.stix.common.CompanionStixType import com.stephenott.stix.common.requireStixType import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo import com.stephenott.stix.objects.core.sro.objects.AllowedRelationship -import com.stephenott.stix.objects.core.sro.objects.SightingSro import com.stephenott.stix.objects.meta.lco.LanguageContentObject import com.stephenott.stix.type.* interface LanguageContentLco : LanguageContentObject { val objectRef: StixIdentifier - val objectModified: StixInstant? + val objectModified: StixTimestamp? val contents: LanguageContentDictionary companion object: CompanionStixType, BusinessRulesValidator, CompanionAllowedRelationships { - override fun objectValidationRules(obj: LanguageContentLco, stixRegistries: StixRegistries) { + override fun objectValidationRules(obj: LanguageContentLco, stixInstance: Stix) { requireStixType(this.stixType, obj) } @@ -33,26 +31,27 @@ interface LanguageContentLco : LanguageContentObject { } data class LanguageContent( - override val objectRef: StixIdentifier, - override val objectModified: StixInstant? = null, - override val contents: LanguageContentDictionary, - override val type: StixType = AttackPatternSdo.stixType, - override val id: StixIdentifier = StixIdentifier(type), - override val createdByRef: String? = null, - override val created: StixInstant = StixInstant(), - override val externalReferences: ExternalReferences? = null, - override val objectMarkingsRefs: String? = null, - override val granularMarkings: String? = null, - override val specVersion: StixSpecVersion = StixSpecVersion(), - override val labels: StixLabels? = null, - override val modified: StixInstant = StixInstant(created), - override val revoked: StixBoolean = StixBoolean(), - override val confidence: StixConfidence? = null, - override val stixRegistries: StixRegistries = Stix.defaultRegistries + override val objectRef: StixIdentifier, + override val objectModified: StixTimestamp? = null, + override val contents: LanguageContentDictionary, + override val type: StixType = AttackPatternSdo.stixType, + override val id: StixIdentifier = StixIdentifier(type), + override val createdByRef: String? = null, + override val created: StixTimestamp = StixTimestamp(), + override val externalReferences: ExternalReferences? = null, + override val objectMarkingsRefs: String? = null, + override val granularMarkings: String? = null, + override val specVersion: StixSpecVersion = StixSpecVersion(), + override val labels: StixLabels? = null, + override val modified: StixTimestamp = StixTimestamp(created), + override val revoked: StixBoolean = StixBoolean(), + override val confidence: StixConfidence? = null, + override val stixInstance: Stix = Stix.defaultStixInstance, + override val stixValidateOnConstruction: Boolean = Stix.defaultValidateOnConstruction ) : LanguageContentLco { init { - LanguageContentLco.objectValidationRules(this, stixRegistries) + LanguageContentLco.objectValidationRules(this, stixInstance) } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index 30a3b7d..c586bc0 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -1,6 +1,7 @@ package com.stephenott.stix.serialization.json import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule @@ -9,6 +10,7 @@ import com.stephenott.stix.StixContent import com.stephenott.stix.StixRegistries import com.stephenott.stix.serialization.StixContentMapper import com.fasterxml.jackson.module.kotlin.* +import com.stephenott.stix.Stix class StixJsonContentMapper( override val stixRegistries: StixRegistries = StixRegistries(), @@ -35,13 +37,20 @@ class StixJsonContentMapper( } companion object { + + fun fromStixInstance(instance: Stix): StixJsonContentMapper { + return StixJsonContentMapper(instance.registries) + } + fun createStixJsonObjectMapper(stixRegistries: StixRegistries): ObjectMapper { return ObjectMapper() .registerModule(KotlinModule()) //@TODO see jackson kotlin module issue #87. Waiting for fix. Currently if a subtype does not exist then it fails to provide a meaningful error. .registerModule(JavaTimeModule()) .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .registerModule(createStixInstantSerializationModule()) + .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true) + .registerModule(createStixTimestampSerializationModule()) .registerModule(createStixIdentifierSerializationModule()) .registerModule(createStixTypeSerializationModule()) .registerModule(createStixSpecVersionSerializationModule()) diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt index 6d71d7a..59a00b2 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixContentSerialization.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.databind.jsontype.NamedType import com.fasterxml.jackson.databind.module.SimpleModule +import com.stephenott.stix.Stix import com.stephenott.stix.StixContent import com.stephenott.stix.StixRegistries import com.stephenott.stix.common.StixObjectRegistry @@ -22,12 +23,15 @@ fun createStixContentSerializationModule(objectRegistry: StixObjectRegistry): Si } @JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - visible = true, - property = "type" + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + visible = true, + property = "type" ) interface StixContentTypeMixin{ @get:JsonIgnore //@TODO Review for need of a 'set:' - val stixRegistries: StixRegistries + val stixInstance: Stix + + @get:JsonIgnore + val stixValidateOnConstruction: Boolean } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt deleted file mode 100644 index 550bfb5..0000000 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixRegistriesSerialization.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.stephenott.stix.serialization.json - -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.databind.module.SimpleModule -import com.stephenott.stix.StixContent -import com.stephenott.stix.StixRegistries - -/** - * Provides JsonIgnore annotation on the stixRegistries object in StixContent - */ -fun createStixContentStixRegistriesSerializationModule(): SimpleModule { - val module: SimpleModule = SimpleModule() - - module.setMixInAnnotation(StixContent::class.java, StixContentStixRegistriesMixin::class.java) - - return module -} - - -interface StixContentStixRegistriesMixin { - - @get:JsonIgnore - val stixRegistries: StixRegistries -} \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTimestampSerialization.kt similarity index 54% rename from src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt rename to src/main/kotlin/com/stephenott/stix/serialization/json/StixTimestampSerialization.kt index fb30d75..b9f638a 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/StixInstantSerialization.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/StixTimestampSerialization.kt @@ -7,25 +7,25 @@ import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.ser.std.StdSerializer -import com.stephenott.stix.type.StixInstant +import com.stephenott.stix.type.StixTimestamp -fun createStixInstantSerializationModule(): SimpleModule{ +fun createStixTimestampSerializationModule(): SimpleModule{ return SimpleModule() - .addSerializer(StixInstant::class.java, StixInstantSerializer()) - .addDeserializer(StixInstant::class.java, StixInstantDeserializer()) + .addSerializer(StixTimestamp::class.java, StixTimestampSerializer()) + .addDeserializer(StixTimestamp::class.java, StixTimestampDeserializer()) } -class StixInstantSerializer() : StdSerializer(StixInstant::class.java) { - override fun serialize(value: StixInstant, gen: JsonGenerator, provider: SerializerProvider) { +class StixTimestampSerializer() : StdSerializer(StixTimestamp::class.java) { + override fun serialize(value: StixTimestamp, gen: JsonGenerator, provider: SerializerProvider) { gen.writeString(value.toString()) } } -class StixInstantDeserializer() : StdDeserializer(StixInstant::class.java) { - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): StixInstant { +class StixTimestampDeserializer() : StdDeserializer(StixTimestamp::class.java) { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): StixTimestamp { try { - return StixInstant.parse(p.text) + return StixTimestamp.parse(p.text) } catch (e: Exception){ throw IllegalArgumentException("Unable to parse timestamp.", e) } diff --git a/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt b/src/main/kotlin/com/stephenott/stix/type/StixTimestamp.kt similarity index 87% rename from src/main/kotlin/com/stephenott/stix/type/StixInstant.kt rename to src/main/kotlin/com/stephenott/stix/type/StixTimestamp.kt index cfb0b1c..ede4c37 100644 --- a/src/main/kotlin/com/stephenott/stix/type/StixInstant.kt +++ b/src/main/kotlin/com/stephenott/stix/type/StixTimestamp.kt @@ -4,7 +4,7 @@ import com.stephenott.stix.common.StixDataFormats import java.time.Instant import java.util.regex.Pattern -data class StixInstant( +data class StixTimestamp( val instant: Instant, val subSecondPrecision: Int) { @@ -18,11 +18,11 @@ data class StixInstant( var REGEX_SUBSECOND: Pattern = Pattern.compile("(?\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(.(?[0-9]+))?Z)") - fun parse(dateString: String): StixInstant { + fun parse(dateString: String): StixTimestamp { val instant = Instant.from(StixDataFormats.readerStixDateTimeFormatter.parse(dateString)) val subSecondPrecision = getSubSecondDigitCount(dateString) - return StixInstant(instant, subSecondPrecision) + return StixTimestamp(instant, subSecondPrecision) } private fun getSubSecondDigitCount(dateString: String): Int { @@ -38,7 +38,7 @@ data class StixInstant( } } - constructor(stixInstant: StixInstant) : this(stixInstant.instant, stixInstant.subSecondPrecision) + constructor(stixTimestamp: StixTimestamp) : this(stixTimestamp.instant, stixTimestamp.subSecondPrecision) constructor() : this(Instant.now(), defaultSubSecondPrecision){} diff --git a/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt b/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt index c4e6215..9d83ee0 100644 --- a/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt +++ b/src/main/kotlin/com/stephenott/stix/type/X509v3ExtensionsTypes.kt @@ -1,7 +1,5 @@ package com.stephenott.stix.type -import com.stephenott.stix.objects.core.sco.objects.ArtifactSco -import com.stephenott.stix.objects.core.sco.objects.FileSco import kotlin.reflect.KVisibility import kotlin.reflect.full.memberProperties @@ -12,22 +10,22 @@ class X509v3ExtensionsTypes(private val types: List): List } data class X509v3ExtensionsType( - val basicConstraints: String?, - val nameConstraints: String?, - val policyConstraints: String?, - val keyUsage: String?, - val extendedKeyUsage: String?, - val subjectKeyIdentifier: String?, - val authorityKeyIdentifier: String?, - val subjectAlternativeName: String?, - val issuerAlternativeName: String?, - val subjectDirectoryAttributes: String?, - val crlDistributionPoints: String?, - val inhibitAnyPolicy: String?, - val privateKeyUsagePeriodNotBefore: StixInstant?, - val privateKeyUsagePeriodNotAfter: StixInstant?, - val certificatePolicies: String?, - val policyMappings: String? + val basicConstraints: String?, + val nameConstraints: String?, + val policyConstraints: String?, + val keyUsage: String?, + val extendedKeyUsage: String?, + val subjectKeyIdentifier: String?, + val authorityKeyIdentifier: String?, + val subjectAlternativeName: String?, + val issuerAlternativeName: String?, + val subjectDirectoryAttributes: String?, + val crlDistributionPoints: String?, + val inhibitAnyPolicy: String?, + val privateKeyUsagePeriodNotBefore: StixTimestamp?, + val privateKeyUsagePeriodNotAfter: StixTimestamp?, + val certificatePolicies: String?, + val policyMappings: String? ) { init { From e97e74f98e5a55f363ba91712ff23e956c8c6d2c Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Thu, 21 Nov 2019 18:37:44 -0500 Subject: [PATCH 20/21] cleanup for optional object validation support related to Stix instance configuration --- README.md | 90 +++++++++++++- .../kotlin/com/stephenott/stix/MainRunner.kt | 111 +++++++++-------- .../kotlin/com/stephenott/stix/StixContent.kt | 2 + .../stix/objects/core/sco/objects/Artifact.kt | 4 +- .../core/sco/objects/AutonomousSystem.kt | 4 +- .../objects/core/sco/objects/Directory.kt | 4 +- .../objects/core/sco/objects/DomainName.kt | 4 +- .../objects/core/sco/objects/EmailAddress.kt | 4 +- .../objects/core/sco/objects/EmailMessage.kt | 4 +- .../stix/objects/core/sco/objects/File.kt | 4 +- .../objects/core/sco/objects/IPv4Address.kt | 4 +- .../objects/core/sco/objects/IPv6Address.kt | 4 +- .../objects/core/sco/objects/MacAddress.kt | 4 +- .../stix/objects/core/sco/objects/Mutex.kt | 4 +- .../core/sco/objects/NetworkTraffic.kt | 4 +- .../stix/objects/core/sco/objects/Process.kt | 4 +- .../stix/objects/core/sco/objects/Software.kt | 4 +- .../stix/objects/core/sco/objects/Url.kt | 4 +- .../objects/core/sco/objects/UserAccount.kt | 4 +- .../core/sco/objects/WindowsRegistryKey.kt | 4 +- .../core/sco/objects/X509Certificate.kt | 4 +- .../stix/objects/core/sdo/objects/Campaign.kt | 4 +- .../core/sdo/objects/CourseOfAction.kt | 4 +- .../stix/objects/core/sdo/objects/Grouping.kt | 4 +- .../stix/objects/core/sdo/objects/Identity.kt | 4 +- .../objects/core/sdo/objects/Indicator.kt | 4 +- .../core/sdo/objects/Infrastructure.kt | 4 +- .../objects/core/sdo/objects/IntrusionSet.kt | 4 +- .../stix/objects/core/sdo/objects/Location.kt | 4 +- .../stix/objects/core/sdo/objects/Malware.kt | 4 +- .../core/sdo/objects/MalwareAnalysis.kt | 4 +- .../stix/objects/core/sdo/objects/Note.kt | 4 +- .../objects/core/sdo/objects/ObservedData.kt | 4 +- .../stix/objects/core/sdo/objects/Opinion.kt | 4 +- .../stix/objects/core/sdo/objects/Report.kt | 4 +- .../objects/core/sdo/objects/ThreatActor.kt | 4 +- .../stix/objects/core/sdo/objects/Tool.kt | 4 +- .../objects/core/sdo/objects/Vulnerability.kt | 4 +- .../objects/core/sro/objects/Relationship.kt | 114 +++++++++--------- .../stix/objects/core/sro/objects/Sighting.kt | 4 +- .../meta/datamarking/MarkingDefinition.kt | 4 +- .../stix/serialization/json/JsonExtensions.kt | 46 ++++--- 42 files changed, 352 insertions(+), 159 deletions(-) diff --git a/README.md b/README.md index e023f36..25ccdc8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,92 @@ # STIX-JAVA-KOTLIN -A STIX 2.1 library built in Kotlin with targets of Kotlin, Java, and Kotlin-Native +A STIX 2.1 library built in Kotlin with targets of Kotlin and Java. +## Concepts + +The entire STIX 2.1 spec covered in interfaces and implementations. +This allows for anyone to build new implementations from the common interfaces that map out the entire STIX 2.1 spec. +All business rules can be found in the interfaces such as supported relationships, supported extensions, and field level controls. + +The interfaces maintain the hierarchy of the STIX spec: + +1. Stix Content + 1.1 Stix Object + 1.1.1 Stix Core Object + 1.1.1.1 Stix Domain Object (SDO) + 1.1.1.2 Stix Relationship Object (SRO) + 1.1.1.3 Stix Cyber Observable Object (SCO) + 1.1.2 Stix Meta Object + 1.1.2.1 Data Marking + 1.1.2.2 Language Content Object + 1.2. Stix Bundle + +### Technical design details + +1. All Stix Content is immutable. +1. Stix Content can be created using the default spec as its configuration, but using the `Stix` instance you can transform th object to using the `Stix` instance's configuration. + + +## `Sitx` instance + +A `Stix` class instance provides the ability to create instance of a Stix spec configuration. +A Stix spec configuration provides the ability to configure the rules and configurations for the specific trust group. + +Example: A system you are developing only accepts the Indicator SDO. When creating a `Stix` instance you can disable +all other objects except for the Indicator SDO. When you generate a JSON content mapper and attempt to pass another object +other than the Indicator SDO, the parser will throw an error. The default JSON content mapper that this library provides, +allows you to create a JSON content mapper from the `Stix` instance. + +`Stix` instances are applied to object creation. There is a default `Stix` instance available as a static object that represents a spec matching configuration. + +A `Stix` instance provides a `create(...)` method that takes a StixContent value. This value will be shallow copied and have the business rules validation run against the calling `Stix` instance. + +## JSON Serialization + +Example: + +```kotlin +val stix1 = Stix() + +val ap = AttackPattern(name = "124", confidence = StixConfidence(33)) + +val ap1: AttackPattern = stix1.create(AttackPattern(name = "124")) + +val ap2: AttackPattern = stix1.create(AttackPattern(name = "124")) + +val mapper = stix1.toJsonMapper() +// val mapper = StixJsonContentMapper.fromStixInstance(stix1) + +// Note that any of the object implementations or any of the interfaces in the hierarchy can be used. +// The parser will use the polymorphic support to detect what is the proper class to return. + +mapper.parseJson(mapper.toJson(ap1)) // You can use the mapper to convert a object to json +mapper.parseJson(mapper.toJson(ap1)) +mapper.parseJson(ap1.toJson(mapper)) // A extension provides StixContent and StixBundle will a toJson(mapper) method +mapper.parseJson(ap1.toJson(mapper)) +mapper.parseJson(ap1.toJson(mapper)) +mapper.parseJson(ap1.toJson(mapper)) +mapper.parseJson(ap1.toJson(mapper)) + + +val rel = Relationship( + relationshipType = RelationshipType("duplicate-of"), + sourceRef = ap1, + targetRef = ap2 +) +mapper.parseJson(rel.toJson(mapper)) +mapper.parseJson(rel.toJson(mapper)) +mapper.parseJson(rel.toJson(mapper)) +mapper.parseJson(rel.toJson(mapper)) +mapper.parseJson(rel.toJson(mapper)) +mapper.parseJson(rel.toJson(mapper)) + + +``` + + +## Object Validation + +All Stix Content has a "stixValidateOnConstruction" parameter used to indicate if business rule validation should be executed during object creation. + +When you use `stixInstance.create(...)` it will force business rule validation. \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/MainRunner.kt b/src/main/kotlin/com/stephenott/stix/MainRunner.kt index 4836cd0..71d5a6c 100644 --- a/src/main/kotlin/com/stephenott/stix/MainRunner.kt +++ b/src/main/kotlin/com/stephenott/stix/MainRunner.kt @@ -1,11 +1,24 @@ package com.stephenott.stix +import com.stephenott.stix.objects.StixObject +import com.stephenott.stix.objects.core.StixCoreObject import com.stephenott.stix.objects.core.sco.objects.NetworkTraffic +import com.stephenott.stix.objects.core.sdo.StixDomainObject import com.stephenott.stix.objects.core.sdo.objects.AttackPattern +import com.stephenott.stix.objects.core.sdo.objects.AttackPatternSdo +import com.stephenott.stix.objects.core.sro.StixRelationshipObject +import com.stephenott.stix.objects.core.sro.objects.Relationship +import com.stephenott.stix.objects.core.sro.objects.RelationshipSro import com.stephenott.stix.objects.core.sro.objects.Sighting +import com.stephenott.stix.objects.meta.datamarking.MarkingDefinition +import com.stephenott.stix.objects.meta.datamarking.objects.Statement +import com.stephenott.stix.objects.meta.datamarking.objects.Tlp import com.stephenott.stix.serialization.json.StixJsonContentMapper +import com.stephenott.stix.serialization.json.jsonMapper import com.stephenott.stix.serialization.json.toJson +import com.stephenott.stix.serialization.json.toJsonMapper import com.stephenott.stix.type.* +import com.stephenott.stix.type.vocab.MarkingDefinitionTypeOv object MainRunner { @@ -25,57 +38,59 @@ object MainRunner { val ap1: AttackPattern = stix1.create(AttackPattern(name = "124", confidence = StixConfidence(33))) + val ap2: AttackPattern = stix1.create(AttackPattern(name = "124", + confidence = StixConfidence(33))) + val sighting1 = Sighting(sightingOfRef = ap1.id) val net1 = NetworkTraffic(isActive = StixBoolean(true), protocols = StixStringList(listOf("http"))) - val mapper = StixJsonContentMapper.fromStixInstance(stix1) - val dog = "{\"name\":\"124\",\"type\":\"attack-pattern\",\"id\":\"attack-pattern--771e3ada-0db7-42be-b389-0590181f64c4\",\"created\":\"2019-11-19T00:29:54.294Z\",\"spec_version\":\"2.1\",\"modified\":\"2019-11-19T00:29:54.294Z\",\"confidence\":33,\"stixValidateOnConstruction\": \"222\"}" + val mapper = stix1.toJsonMapper() +// val mapper = StixJsonContentMapper.fromStixInstance(stix1) println(ap1.toJson(mapper)) - println(mapper.parseJson(dog)) -// println(mapper.parseJson(ap1.toJson(mapper))) -// println(mapper.parseJson(ap1.toJson(mapper))) -// println(mapper.parseJson(ap1.toJson(mapper))) -// println(mapper.parseJson(ap1.toJson(mapper))) -// println(mapper.parseJson(ap1.toJson(mapper))) -// -// println(net1.toJson(mapper)) -// println(mapper.parseJson(net1.toJson(mapper))) -// -// val jsonString: String = ap1.toJson(mapper) -// println(jsonString) -// -// println(mapper.parseJson(jsonString)) -// println(mapper.parseJson(jsonString)) -// println(mapper.parseJson(jsonString)) -// println(mapper.parseJson(jsonString)) -// println(mapper.parseJson(jsonString)) -// println(mapper.parseJson(jsonString)) -// -// -// val rel = Relationship( -// relationshipType = RelationshipType("duplicate-of"), -// sourceRef = ap1, -// targetRef = ap2 -// ) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// println(mapper.parseJson(rel.toJson(mapper))) -// -// println(mapper.parseJson(sighting1.toJson(mapper))) -// -// val markDef1 = MarkingDefinition("test", MarkingDefinitionTypeOv("tlp"), -// Tlp("white") -// ) -// println(mapper.parseJson(markDef1.toJson(mapper))) -// println(markDef1.toJson(mapper)) -// -// val markDef2 = MarkingDefinition("test2", MarkingDefinitionTypeOv("statement"), -// Statement("white-statement") -// ) -// println(mapper.parseJson(markDef2.toJson(mapper))) -// println(markDef2.toJson(mapper)) + println(mapper.parseJson(mapper.toJson(ap1))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + println(mapper.parseJson(ap1.toJson(mapper))) + + println(net1.toJson(mapper)) + println(mapper.parseJson(net1.toJson(mapper))) + + val jsonString: String = ap1.toJson(mapper) + println(jsonString) + + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + println(mapper.parseJson(jsonString)) + + + val rel = Relationship( + relationshipType = RelationshipType("duplicate-of"), + sourceRef = ap1, + targetRef = ap2 + ) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + println(mapper.parseJson(rel.toJson(mapper))) + + println(mapper.parseJson(sighting1.toJson(mapper))) + + val markDef1 = MarkingDefinition("test", MarkingDefinitionTypeOv("tlp"), + Tlp("white") + ) + println(mapper.parseJson(markDef1.toJson(mapper))) + println(markDef1.toJson(mapper)) + + val markDef2 = MarkingDefinition("test2", MarkingDefinitionTypeOv("statement"), + Statement("white-statement") + ) + println(mapper.parseJson(markDef2.toJson(mapper))) + println(markDef2.toJson(mapper)) } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/StixContent.kt b/src/main/kotlin/com/stephenott/stix/StixContent.kt index a8166fc..a20edd0 100644 --- a/src/main/kotlin/com/stephenott/stix/StixContent.kt +++ b/src/main/kotlin/com/stephenott/stix/StixContent.kt @@ -11,4 +11,6 @@ interface StixContent : val stixInstance: Stix val stixValidateOnConstruction: Boolean + companion object{} + } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt index 240168f..f93f3ff 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Artifact.kt @@ -81,7 +81,9 @@ data class Artifact( ) : ArtifactSco { init { - ArtifactSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + ArtifactSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt index 9441eb4..d21274c 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/AutonomousSystem.kt @@ -58,7 +58,9 @@ data class AutonomousSystem( ) : AutonomousSystemSco { init { - AutonomousSystemSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + AutonomousSystemSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt index 456c040..db2e860 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Directory.kt @@ -69,7 +69,9 @@ data class Directory( ) : DirectorySco { init { - DirectorySco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + DirectorySco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt index 52c7bce..2cd6f0a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/DomainName.kt @@ -70,7 +70,9 @@ data class DomainName( ) : DomainNameSco { init { - DomainNameSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + DomainNameSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt index de1f099..0f5f4e6 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailAddress.kt @@ -63,7 +63,9 @@ data class EmailAddress( ) : EmailAddressSco { init { - EmailAddressSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + EmailAddressSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt index e8bf0aa..31676ce 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/EmailMessage.kt @@ -112,7 +112,9 @@ data class EmailMessage( ) : EmailMessageSco { init { - EmailMessageSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + EmailMessageSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt index 721f01b..9327b72 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/File.kt @@ -102,7 +102,9 @@ data class File( ) : FileSco { init { - FileSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + FileSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt index ee4d616..ece9446 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv4Address.kt @@ -64,7 +64,9 @@ data class IPv4Address( ) : IPv4AddressSco { init { - IPv4AddressSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + IPv4AddressSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt index dbf2962..363ee78 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/IPv6Address.kt @@ -65,7 +65,9 @@ data class IPv6Address( ) : IPv6AddressSco { init { - IPv6AddressSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + IPv6AddressSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt index 1aa9e02..bedd38d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/MacAddress.kt @@ -56,7 +56,9 @@ data class MacAddress( ) : MacAddressSco { init { - MacAddressSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + MacAddressSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt index 286bf01..6ed3f16 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Mutex.kt @@ -55,7 +55,9 @@ data class Mutex( ) : MutexSco { init { - MutexSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + MutexSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt index 8f3bdc3..b2eec67 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/NetworkTraffic.kt @@ -156,7 +156,9 @@ data class NetworkTraffic( ) : NetworkTrafficSco { init { - NetworkTrafficSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + NetworkTrafficSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt index 4ae7925..fcec321 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Process.kt @@ -94,7 +94,9 @@ data class Process( ) : ProcessSco { init { - ProcessSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + ProcessSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt index 0e6f7b4..fccea54 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Software.kt @@ -67,7 +67,9 @@ data class Software( ) : SoftwareSco { init { - SoftwareSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + SoftwareSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt index a6fd8cb..4bb946e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/Url.kt @@ -54,7 +54,9 @@ data class Url( ) : UrlSco { init { - UrlSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + UrlSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt index 4307531..7cae06a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/UserAccount.kt @@ -86,7 +86,9 @@ data class UserAccount( ) : UserAccountSco { init { - UserAccountSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + UserAccountSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt index 2f4ef3e..ff3040f 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/WindowsRegistryKey.kt @@ -70,7 +70,9 @@ data class WindowsRegistryKey( ) : WindowsRegistryKeySco { init { - WindowsRegistryKeySco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + WindowsRegistryKeySco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt index 8530ebf..675f375 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sco/objects/X509Certificate.kt @@ -93,7 +93,9 @@ data class X509Certificate( ) : X509CertificateSco { init { - X509CertificateSco.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + X509CertificateSco.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt index 7949910..a588cb7 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Campaign.kt @@ -121,7 +121,9 @@ data class Campaign ( ) : CampaignSdo { init { - CampaignSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + CampaignSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt index 41f4fac..faa21e9 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/CourseOfAction.kt @@ -112,7 +112,9 @@ data class CourseOfAction( ) : CourseOfActionSdo { init { - CourseOfActionSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + CourseOfActionSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt index a2fd96c..0610d1f 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Grouping.kt @@ -54,7 +54,9 @@ data class Grouping( ) : GroupingSdo { init { - GroupingSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + GroupingSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt index af1fb11..2007684 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Identity.kt @@ -66,7 +66,9 @@ data class Identity( ) : IdentitySdo { init { - IdentitySdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + IdentitySdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt index 83b1364..85b573e 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Indicator.kt @@ -111,7 +111,9 @@ data class Indicator( ) : IndicatorSdo { init { - IndicatorSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + IndicatorSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt index cbee996..38695d2 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Infrastructure.kt @@ -152,7 +152,9 @@ data class Infrastructure( ) : InfrastructureSdo { init { - InfrastructureSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + InfrastructureSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt index b7bd35e..885e135 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/IntrusionSet.kt @@ -130,7 +130,9 @@ data class IntrusionSet( ) : IntrusionSetSdo { init { - IntrusionSetSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + IntrusionSetSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt index e67ef57..f5c2427 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Location.kt @@ -77,7 +77,9 @@ data class Location( ) : LocationSdo { init { - LocationSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + LocationSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt index 4f0ab5c..5700d73 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Malware.kt @@ -211,7 +211,9 @@ data class Malware ) : MalwareSdo { init { - MalwareSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + MalwareSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt index 725cf2b..6b14bb3 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/MalwareAnalysis.kt @@ -108,7 +108,9 @@ data class MalwareAnalysis ) : MalwareAnalysisSdo { init { - MalwareAnalysisSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + MalwareAnalysisSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt index 6da3e40..a38b35a 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Note.kt @@ -54,7 +54,9 @@ data class Note( ) : NoteSdo { init { - NoteSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + NoteSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt index 4677049..9568010 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ObservedData.kt @@ -59,7 +59,9 @@ data class ObservedData( ) : ObservedDataSdo { init { - ObservedDataSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + ObservedDataSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt index 009a565..b7e1440 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Opinion.kt @@ -57,7 +57,9 @@ data class Opinion( ) : OpinionSdo { init { - OpinionSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + OpinionSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt index efde316..92442de 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Report.kt @@ -56,7 +56,9 @@ data class Report( ) : ReportSdo { init { - ReportSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + ReportSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt index 49200d9..3288a30 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/ThreatActor.kt @@ -143,7 +143,9 @@ data class ThreatActor( ) : ThreatActorSdo { init { - ThreatActorSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + ThreatActorSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt index 18f4e01..435a07d 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Tool.kt @@ -100,7 +100,9 @@ data class Tool( ) : ToolSdo { init { - ToolSdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + ToolSdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt index 17200a2..5284ba5 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sdo/objects/Vulnerability.kt @@ -49,7 +49,9 @@ data class Vulnerability( ) : VulnerabilitySdo { init { - VulnerabilitySdo.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + VulnerabilitySdo.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt index 083fd56..dda5a80 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Relationship.kt @@ -17,8 +17,8 @@ interface RelationshipSro : StixRelationshipObject { val startTime: StixTimestamp? val stopTime: StixTimestamp? - companion object: CompanionStixType, - BusinessRulesValidator { + companion object : CompanionStixType, + BusinessRulesValidator { override val stixType = StixType("relationship") @@ -28,44 +28,44 @@ interface RelationshipSro : StixRelationshipObject { } val allowedCommonRelationships: List = listOf( - AllowedRelationship( - StixObject::class, - RelationshipType("duplicate-of"), - StixObject::class, - RelationshipRule { obj -> - require(obj.sourceRef.type == obj.targetRef.type, - lazyMessage = { "duplicate-of relationship (${obj.id}) requires source(${obj.sourceRef}) and target(${obj.targetRef}) must be same type" }) - } - ), - AllowedRelationship( - StixObject::class, - RelationshipType("derived-from"), - StixObject::class, - RelationshipRule { obj -> - require(obj.sourceRef.type == obj.targetRef.type, - lazyMessage = { "derived-from relationship (${obj.id}) requires source(${obj.sourceRef}) and target(${obj.targetRef}) must be same type" }) - } - ), - AllowedRelationship( - StixObject::class, - RelationshipType("related-to"), - StixObject::class - ) + AllowedRelationship( + StixObject::class, + RelationshipType("duplicate-of"), + StixObject::class, + RelationshipRule { obj -> + require(obj.sourceRef.type == obj.targetRef.type, + lazyMessage = { "duplicate-of relationship (${obj.id}) requires source(${obj.sourceRef}) and target(${obj.targetRef}) must be same type" }) + } + ), + AllowedRelationship( + StixObject::class, + RelationshipType("derived-from"), + StixObject::class, + RelationshipRule { obj -> + require(obj.sourceRef.type == obj.targetRef.type, + lazyMessage = { "derived-from relationship (${obj.id}) requires source(${obj.sourceRef}) and target(${obj.targetRef}) must be same type" }) + } + ), + AllowedRelationship( + StixObject::class, + RelationshipType("related-to"), + StixObject::class + ) ) } } data class RelationshipRule(private val rule: (relObj: RelationshipSro) -> Unit) { - fun execute(relObj: RelationshipSro){ + fun execute(relObj: RelationshipSro) { rule(relObj) } } data class AllowedRelationship( - val from: KClass, - val type: RelationshipType, - val to: KClass, - val rule: RelationshipRule? = null + val from: KClass, + val type: RelationshipType, + val to: KClass, + val rule: RelationshipRule? = null ) {} data class Relationship( @@ -116,43 +116,45 @@ data class Relationship( modified: StixTimestamp = StixTimestamp(created), revoked: StixBoolean = StixBoolean() ) : this( - relationshipType, description, sourceRef.id, - targetRef.id, startTime, stopTime, - type, id, createdByRef, - created, externalReferences, objectMarkingsRefs, - granularMarkings, specVersion, labels, - modified, revoked + relationshipType, description, sourceRef.id, + targetRef.id, startTime, stopTime, + type, id, createdByRef, + created, externalReferences, objectMarkingsRefs, + granularMarkings, specVersion, labels, + modified, revoked ) init { - val sourceClass: KClass = this.stixInstance.registries.objectRegistry.registry[sourceRef.type] - ?: throw IllegalStateException("Unable to find sourceRef in Object Registry") + if (this.stixValidateOnConstruction) { + val sourceClass: KClass = this.stixInstance.registries.objectRegistry.registry[sourceRef.type] + ?: throw IllegalStateException("Unable to find sourceRef in Object Registry") - val targetClass: KClass = this.stixInstance.registries.objectRegistry.registry[targetRef.type] - ?: throw IllegalStateException("Unable to find targetRef in Object Registry") + val targetClass: KClass = this.stixInstance.registries.objectRegistry.registry[targetRef.type] + ?: throw IllegalStateException("Unable to find targetRef in Object Registry") - //@TODO add support for x- custom objects - val allowedRelationships: List = this.stixInstance.registries.relationshipRegistry - .registry.filter { - sourceClass.isSubclassOf(it.from) && - it.type == this.relationshipType && - targetClass.isSubclassOf(it.to) - } + //@TODO add support for x- custom objects + val allowedRelationships: List = this.stixInstance.registries.relationshipRegistry + .registry.filter { + sourceClass.isSubclassOf(it.from) && + it.type == this.relationshipType && + targetClass.isSubclassOf(it.to) + } - if (allowedRelationships.size > 1) { - println("Duplicate relationships found: $allowedRelationships") - //@TODO add some logging for a warning to indicate multiple duplication objects are registered - } + if (allowedRelationships.size > 1) { + println("Duplicate relationships found: $allowedRelationships") + //@TODO add some logging for a warning to indicate multiple duplication objects are registered + } - require(allowedRelationships.isNotEmpty(), - lazyMessage = { "${this.id} is not a valid relationship for a ${this.sourceRef.type}" } - ) + require(allowedRelationships.isNotEmpty(), + lazyMessage = { "${this.id} is not a valid relationship for a ${this.sourceRef.type}" } + ) - //@TODO To a deeper test of this functionality - allowedRelationships[0].rule?.execute(this) + //@TODO To a deeper test of this functionality allowedRelationships[0].rule?.execute(this) +// allowedRelationships[0].rule?.execute(this) + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt index 8bdc7ef..6fbac84 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/core/sro/objects/Sighting.kt @@ -79,7 +79,9 @@ data class Sighting( ) : SightingSro { init { - SightingSro.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + SightingSro.objectValidationRules(this, stixInstance) + } } override fun allowedRelationships(): List { diff --git a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt index d85a1a6..1df4749 100644 --- a/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt +++ b/src/main/kotlin/com/stephenott/stix/objects/meta/datamarking/MarkingDefinition.kt @@ -50,7 +50,9 @@ data class MarkingDefinition( ): MarkingDefinitionDm { init { - MarkingDefinitionDm.objectValidationRules(this, stixInstance) + if (this.stixValidateOnConstruction) { + MarkingDefinitionDm.objectValidationRules(this, stixInstance) + } } diff --git a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt index c586bc0..c0f88df 100644 --- a/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt +++ b/src/main/kotlin/com/stephenott/stix/serialization/json/JsonExtensions.kt @@ -13,8 +13,8 @@ import com.fasterxml.jackson.module.kotlin.* import com.stephenott.stix.Stix class StixJsonContentMapper( - override val stixRegistries: StixRegistries = StixRegistries(), - override val mapper: ObjectMapper = createStixJsonObjectMapper(stixRegistries) + override val stixRegistries: StixRegistries = StixRegistries(), + override val mapper: ObjectMapper = createStixJsonObjectMapper(stixRegistries) ) : StixContentMapper { /** @@ -44,32 +44,44 @@ class StixJsonContentMapper( fun createStixJsonObjectMapper(stixRegistries: StixRegistries): ObjectMapper { return ObjectMapper() - .registerModule(KotlinModule()) //@TODO see jackson kotlin module issue #87. Waiting for fix. Currently if a subtype does not exist then it fails to provide a meaningful error. - .registerModule(JavaTimeModule()) - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + .registerModule(KotlinModule()) //@TODO see jackson kotlin module issue #87. Waiting for fix. Currently if a subtype does not exist then it fails to provide a meaningful error. + .registerModule(JavaTimeModule()) + .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true) .configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true) - .registerModule(createStixTimestampSerializationModule()) - .registerModule(createStixIdentifierSerializationModule()) - .registerModule(createStixTypeSerializationModule()) - .registerModule(createStixSpecVersionSerializationModule()) - .registerModule(createStixBooleanSerializationModule()) - .registerModule(createStixContentSerializationModule(stixRegistries.objectRegistry)) - .registerModule(createRelationshipTypeSerializationModule()) - .registerModule(createStixIntegerSerializationModule()) - .registerModule(createStixConfidenceSerializationModule()) - .registerModule(createStixOpenVocabSerializationModule()) - .registerModule(createStixMarkingObjectSerializationModule(stixRegistries.markingObjectRegistry)) + .registerModule(createStixTimestampSerializationModule()) + .registerModule(createStixIdentifierSerializationModule()) + .registerModule(createStixTypeSerializationModule()) + .registerModule(createStixSpecVersionSerializationModule()) + .registerModule(createStixBooleanSerializationModule()) + .registerModule(createStixContentSerializationModule(stixRegistries.objectRegistry)) + .registerModule(createRelationshipTypeSerializationModule()) + .registerModule(createStixIntegerSerializationModule()) + .registerModule(createStixConfidenceSerializationModule()) + .registerModule(createStixOpenVocabSerializationModule()) + .registerModule(createStixMarkingObjectSerializationModule(stixRegistries.markingObjectRegistry)) } } } +/** + * Creates a StixJsonContentMapper based on the Stix instance + */ +fun Stix.toJsonMapper(): StixJsonContentMapper { + return StixJsonContentMapper(this.registries) +} +/** + * Convert StixContent to a json string. + */ fun StixContent.toJson(mapper: StixJsonContentMapper): String { return mapper.mapper.writeValueAsString(this) } +/** + * Convert a StixBundle to a json string. + */ fun StixBundle.toJson(mapper: StixJsonContentMapper): String { return mapper.mapper.writeValueAsString(this) } \ No newline at end of file From bd494ed00992b777ba20fff684ba7544dc662da5 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Tue, 11 Feb 2020 18:54:37 -0500 Subject: [PATCH 21/21] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..820eae4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 https://github.com/StephenOTT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.