diff --git a/ontology/uco/core/core.ttl b/ontology/uco/core/core.ttl index 6293d3a7..ca3e3601 100644 --- a/ontology/uco/core/core.ttl +++ b/ontology/uco/core/core.ttl @@ -284,6 +284,26 @@ core:IdentityAbstraction sh:targetClass core:IdentityAbstraction ; . +core:InformationResource + a + owl:Class , + sh:NodeShape + ; + rdfs:subClassOf core:UcoThing ; + owl:disjointWith core:NeverInformationResource ; + sh:targetClass core:InformationResource ; + . + +core:InformationResource-disjointWith-NeverInformationResource-shape + a sh:NodeShape ; + sh:message "core:InformationResource and core:NeverInformationResource are disjoint classes."@en ; + sh:not [ + a sh:NodeShape ; + sh:class core:NeverInformationResource ; + ] ; + sh:targetClass core:InformationResource ; + . + core:Item a owl:Class , @@ -317,6 +337,24 @@ core:ModusOperandi sh:targetClass core:ModusOperandi ; . +core:NeverInformationResource + a + owl:Class , + sh:NodeShape + ; + rdfs:subClassOf core:NonInformationResource ; + sh:targetClass core:NeverInformationResource ; + . + +core:NonInformationResource + a + owl:Class , + sh:NodeShape + ; + rdfs:subClassOf core:UcoThing ; + sh:targetClass core:NonInformationResource ; + . + core:Relationship a owl:Class , @@ -484,6 +522,7 @@ core:UcoThing-identifier-regex-shape a sh:NodeShape ; rdfs:comment "This shape is given an independent IRI for applications that have sufficient controls in place to deactivate this advisory of node identification practice."@en ; rdfs:seeAlso sh:deactivated ; + sh:description "This shape advises that nodes identify themselves with IRIs that are sufficiently unique that collisions between independent node assigners are unlikely. This shape excepts nodes that are explicitly typed as Information Resources."@en ; sh:severity sh:Info ; sh:sparql [ a sh:SPARQLConstraint ; @@ -495,6 +534,9 @@ core:UcoThing-identifier-regex-shape SELECT $this WHERE { $this a/rdfs:subClassOf* core:UcoThing . + FILTER NOT EXISTS { + $this a/rdfs:subClassOf* core:InformationResource . + } FILTER ( ! REGEX ( STR($this), diff --git a/ontology/uco/identity/identity.ttl b/ontology/uco/identity/identity.ttl index 93c20b6f..06df8e18 100644 --- a/ontology/uco/identity/identity.ttl +++ b/ontology/uco/identity/identity.ttl @@ -160,7 +160,10 @@ identity:Organization owl:Class , sh:NodeShape ; - rdfs:subClassOf identity:Identity ; + rdfs:subClassOf + core:NeverInformationResource , + identity:Identity + ; rdfs:label "Organization"@en ; rdfs:comment "An organization is a grouping of identifying characteristics unique to a group of people who work together in an organized way for a shared purpose. [based on https://dictionary.cambridge.org/us/dictionary/english/organization]"@en ; sh:targetClass identity:Organization ; @@ -182,7 +185,10 @@ identity:Person owl:Class , sh:NodeShape ; - rdfs:subClassOf identity:Identity ; + rdfs:subClassOf + core:NeverInformationResource , + identity:Identity + ; rdfs:label "Person"@en ; rdfs:comment "A person is a grouping of identifying characteristics unique to a human being regarded as an individual. [based on https://www.lexico.com/en/definition/person]"@en ; sh:targetClass identity:Person ; diff --git a/ontology/uco/observable/observable.ttl b/ontology/uco/observable/observable.ttl index e29bc164..33c8b0d4 100644 --- a/ontology/uco/observable/observable.ttl +++ b/ontology/uco/observable/observable.ttl @@ -2214,7 +2214,10 @@ observable:Device owl:Class , sh:NodeShape ; - rdfs:subClassOf observable:ObservableObject ; + rdfs:subClassOf + core:NeverInformationResource , + observable:ObservableObject + ; rdfs:label "Device"@en ; rdfs:comment "A device is a piece of equipment or a mechanism designed to serve a special purpose or perform a special function. [based on https://www.merriam-webster.com/dictionary/device]"@en ; sh:targetClass observable:Device ; @@ -7350,12 +7353,26 @@ observable:WebPage owl:Class , sh:NodeShape ; - rdfs:subClassOf observable:ObservableObject ; + rdfs:subClassOf + core:InformationResource , + observable:WebResource + ; rdfs:label "WebPage"@en ; rdfs:comment "A web page is a specific collection of information provided by a website and displayed to a user in a web browser. A website typically consists of many web pages linked together in a coherent fashion. [based on https://en.wikipedia.org/wiki/Web_page]"@en ; sh:targetClass observable:WebPage ; . +observable:WebResource + a + owl:Class , + sh:NodeShape + ; + rdfs:subClassOf observable:ObservableObject ; + rdfs:label "WebResource"@en ; + rdfs:seeAlso ; + sh:targetClass observable:WebResource ; + . + observable:WhoIs a owl:Class , diff --git a/ontology/uco/types/types.ttl b/ontology/uco/types/types.ttl index 9153e742..4a8323aa 100644 --- a/ontology/uco/types/types.ttl +++ b/ontology/uco/types/types.ttl @@ -57,7 +57,10 @@ types:Dictionary owl:Class , sh:NodeShape ; - rdfs:subClassOf core:UcoInherentCharacterizationThing ; + rdfs:subClassOf + core:NeverInformationResource , + core:UcoInherentCharacterizationThing + ; rdfs:label "Dictionary"@en ; rdfs:comment "A dictionary is list of (term/key, value) pairs with each term/key existing no more than once."@en ; sh:property [ @@ -101,7 +104,10 @@ types:Hash owl:Class , sh:NodeShape ; - rdfs:subClassOf core:UcoInherentCharacterizationThing ; + rdfs:subClassOf + core:NeverInformationResource , + core:UcoInherentCharacterizationThing + ; rdfs:label "Hash"@en ; rdfs:comment "A hash is a grouping of characteristics unique to the result of applying a mathematical algorithm that maps data of arbitrary size to a bit string (the 'hash') and is a one-way function, that is, a function which is practically infeasible to invert. This is commonly used for integrity checking of data. [based on https://en.wikipedia.org/wiki/Cryptographic_hash_function]"@en ; sh:property @@ -184,6 +190,7 @@ types:Thread ; rdfs:subClassOf co:Bag , + core:NeverInformationResource , core:UcoInherentCharacterizationThing ; rdfs:label "Thread"@en ; diff --git a/tests/examples/Makefile b/tests/examples/Makefile index 55e5c298..1e27456d 100644 --- a/tests/examples/Makefile +++ b/tests/examples/Makefile @@ -35,6 +35,8 @@ all: \ has_facet_inverse_functional_XFAIL_validation.ttl \ hash_PASS_validation.ttl \ hash_XFAIL_validation.ttl \ + information_resource_PASS_validation.ttl \ + information_resource_XFAIL_validation.ttl \ location_PASS_validation.ttl \ location_XFAIL_validation.ttl \ message_thread_PASS_validation.ttl \ @@ -108,6 +110,8 @@ check: \ has_facet_inverse_functional_XFAIL_validation.ttl \ hash_PASS_validation.ttl \ hash_XFAIL_validation.ttl \ + information_resource_PASS_validation.ttl \ + information_resource_XFAIL_validation.ttl \ location_PASS_validation.ttl \ location_XFAIL_validation.ttl \ message_thread_PASS_validation.ttl \ diff --git a/tests/examples/information_resource_PASS.json b/tests/examples/information_resource_PASS.json new file mode 100644 index 00000000..ecdd397e --- /dev/null +++ b/tests/examples/information_resource_PASS.json @@ -0,0 +1,71 @@ +{ + "@context": { + "core": "https://ontology.unifiedcyberontology.org/uco/core/", + "kb": "http://example.org/kb/", + "identity": "https://ontology.unifiedcyberontology.org/uco/identity/", + "observable": "https://ontology.unifiedcyberontology.org/uco/observable/", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#" + }, + "@graph": [ + { + "@id": "http://example.org/~bob", + "@type": "observable:WebResource", + "core:name": "Bob", + "core:description": "Bob's company home page.", + "rdfs:seeAlso": [ + { + "@id": "kb:Person-a3d3af3d-ea1d-47f6-bc02-ac334ded6549" + }, + { + "@id": "kb:WebPage-1c05c378-124e-4d3c-898a-fb5a8d178cf8" + } + ] + }, + + { + "@id": "kb:Person-a3d3af3d-ea1d-47f6-bc02-ac334ded6549", + "@type": "identity:Person", + "core:name": "Bob", + "rdfs:seeAlso": { + "@id": "http://example.org/~bob" + } + }, + { + "@id": "kb:WebPage-1c05c378-124e-4d3c-898a-fb5a8d178cf8", + "@type": "observable:WebPage", + "core:description": "Bob's company home page.", + "rdfs:seeAlso": { + "@id": "http://example.org/~bob" + } + }, + { + "@id": "http://example.org/~chris", + "@type": "observable:WebResource", + "rdfs:comment": "This node will trigger an info-level result from not designating itself a InformationResource, and not ending with a UUID." + }, + { + "@id": "https://mc.example.co.jp/", + "@type": "observable:WebPage", + "rdfs:comment": [ + "This node should trigger no errors, even when incorporating the annotations on the same identifier from another JSON dict.", + "This JSON dict was provided by a market analyst in Japan." + ] + }, + { + "@id": "https://mc.example.co.jp/", + "@type": ["core:NonInformationResource", "observable:WebResource"], + "rdfs:comment": [ + "This node should trigger no errors, even when incorporating the annotations on the same identifier from another JSON dict.", + "This JSON dict was provided by a market analyst in France." + ] + }, + { + "@id": "https://mc.example.co.jp/lang-fr/", + "@type": "observable:WebPage", + "rdfs:comment": [ + "This node should trigger no errors.", + "This JSON dict was provided by a market analyst in France." + ] + } + ] +} diff --git a/tests/examples/information_resource_PASS_validation.ttl b/tests/examples/information_resource_PASS_validation.ttl new file mode 100644 index 00000000..3c878b4c --- /dev/null +++ b/tests/examples/information_resource_PASS_validation.ttl @@ -0,0 +1,78 @@ +@prefix core: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix xsd: . + +[] + a sh:ValidationReport ; + sh:conforms "true"^^xsd:boolean ; + sh:result + [ + a sh:ValidationResult ; + sh:focusNode ; + sh:resultMessage "UcoThings are suggested to end with a UUID." ; + sh:resultSeverity sh:Info ; + sh:sourceConstraint [ + a sh:SPARQLConstraint ; + rdfs:seeAlso ; + sh:message "UcoThings are suggested to end with a UUID."@en ; + sh:select ''' + PREFIX rdfs: + PREFIX core: + SELECT $this + WHERE { + $this a/rdfs:subClassOf* core:UcoThing . + FILTER NOT EXISTS { + $this a/rdfs:subClassOf* core:InformationResource . + } + FILTER ( + ! REGEX ( + STR($this), + "[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$", + "i" + ) + ) + } + ''' ; + ] ; + sh:sourceConstraintComponent sh:SPARQLConstraintComponent ; + sh:sourceShape core:UcoThing-identifier-regex-shape ; + sh:value ; + ] , + [ + a sh:ValidationResult ; + sh:focusNode ; + sh:resultMessage "UcoThings are suggested to end with a UUID." ; + sh:resultSeverity sh:Info ; + sh:sourceConstraint [ + a sh:SPARQLConstraint ; + rdfs:seeAlso ; + sh:message "UcoThings are suggested to end with a UUID."@en ; + sh:select ''' + PREFIX rdfs: + PREFIX core: + SELECT $this + WHERE { + $this a/rdfs:subClassOf* core:UcoThing . + FILTER NOT EXISTS { + $this a/rdfs:subClassOf* core:InformationResource . + } + FILTER ( + ! REGEX ( + STR($this), + "[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$", + "i" + ) + ) + } + ''' ; + ] ; + sh:sourceConstraintComponent sh:SPARQLConstraintComponent ; + sh:sourceShape core:UcoThing-identifier-regex-shape ; + sh:value ; + ] + ; + . + diff --git a/tests/examples/information_resource_XFAIL.json b/tests/examples/information_resource_XFAIL.json new file mode 100644 index 00000000..b1f7f080 --- /dev/null +++ b/tests/examples/information_resource_XFAIL.json @@ -0,0 +1,29 @@ +{ + "@context": { + "core": "https://ontology.unifiedcyberontology.org/uco/core/", + "kb": "http://example.org/kb/", + "identity": "https://ontology.unifiedcyberontology.org/uco/identity/", + "observable": "https://ontology.unifiedcyberontology.org/uco/observable/", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#" + }, + "@graph": [ + { + "@id": "http://example.org/~bob", + "@type": [ + "identity:Person", + "observable:WebPage" + ], + "core:name": "Bob", + "core:description": "Bob's company home page.", + "rdfs:comment": "This node will trigger an error from conflating a node as both a person and the person's home page.", + "rdfs:seeAlso": [ + { + "@id": "kb:Person-a3d3af3d-ea1d-47f6-bc02-ac334ded6549" + }, + { + "@id": "kb:WebPage-1c05c378-124e-4d3c-898a-fb5a8d178cf8" + } + ] + } + ] +} diff --git a/tests/examples/information_resource_XFAIL_validation.ttl b/tests/examples/information_resource_XFAIL_validation.ttl new file mode 100644 index 00000000..3d7bc871 --- /dev/null +++ b/tests/examples/information_resource_XFAIL_validation.ttl @@ -0,0 +1,21 @@ +@prefix core: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix sh: . +@prefix xsd: . + +[] + a sh:ValidationReport ; + sh:conforms "false"^^xsd:boolean ; + sh:result [ + a sh:ValidationResult ; + sh:focusNode ; + sh:resultMessage "core:InformationResource and core:NeverInformationResource are disjoint classes."@en ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NotConstraintComponent ; + sh:sourceShape core:InformationResource-disjointWith-NeverInformationResource-shape ; + sh:value ; + ] ; + . + diff --git a/tests/examples/test_validation.py b/tests/examples/test_validation.py index 9fa6dae1..ff125931 100644 --- a/tests/examples/test_validation.py +++ b/tests/examples/test_validation.py @@ -269,6 +269,25 @@ def test_hash_XFAIL() -> None: } ) +def test_information_resource_PASS_validation() -> None: + confirm_validation_results( + "information_resource_PASS_validation.ttl", + True, + expected_focus_node_severities={ + ("http://example.org/~bob", str(NS_SH.Info)), + ("http://example.org/~chris", str(NS_SH.Info)), + } + ) + +def test_information_resource_XFAIL_validation() -> None: + confirm_validation_results( + "information_resource_XFAIL_validation.ttl", + False, + expected_focus_node_severities={ + ("http://example.org/~bob", str(NS_SH.Violation)), + } + ) + def test_co_PASS_validation() -> None: confirm_validation_results("co_PASS_validation.ttl", True) diff --git a/tests/examples/uco_thing_XFAIL_validation.ttl b/tests/examples/uco_thing_XFAIL_validation.ttl index ab5f6013..67e35a37 100644 --- a/tests/examples/uco_thing_XFAIL_validation.ttl +++ b/tests/examples/uco_thing_XFAIL_validation.ttl @@ -24,6 +24,9 @@ SELECT $this WHERE { $this a/rdfs:subClassOf* core:UcoThing . + FILTER NOT EXISTS { + $this a/rdfs:subClassOf* core:InformationResource . + } FILTER ( ! REGEX ( STR($this), @@ -53,6 +56,9 @@ SELECT $this WHERE { $this a/rdfs:subClassOf* core:UcoThing . + FILTER NOT EXISTS { + $this a/rdfs:subClassOf* core:InformationResource . + } FILTER ( ! REGEX ( STR($this), @@ -113,6 +119,9 @@ SELECT $this WHERE { $this a/rdfs:subClassOf* core:UcoThing . + FILTER NOT EXISTS { + $this a/rdfs:subClassOf* core:InformationResource . + } FILTER ( ! REGEX ( STR($this),