Skip to content

Commit cb209fa

Browse files
authored
Merge pull request #87 from casework/bump_case_to_1_4_0
Update CASE validation to 1.4.0 and add necessary API and example adjustments
2 parents 66ece2a + e9f07f0 commit cb209fa

File tree

7 files changed

+338
-328
lines changed

7 files changed

+338
-328
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ jobs:
4141

4242
# Ensure that the example output is a valid CASE JSON-LD graph
4343
- name: CASE Export Validation
44-
uses: kchason/case-validation-action@v2.9.0
44+
uses: kchason/case-validation-action@v2.10.0
4545
with:
4646
case-path: ./
47-
case-version: "case-1.3.0"
47+
case-version: "case-1.4.0"
4848
extension-filter: "jsonld"
4949

5050
- name: Convert example

case.jsonld

Lines changed: 275 additions & 285 deletions
Large diffs are not rendered by default.

case_mapping/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,9 @@ def __init__(
246246
@unpack_args_array
247247
def append_facets(self, *args):
248248
"""
249-
:param args: A single/tuple of ObservableObjects
249+
:param args: A single/tuple of Facets
250250
"""
251-
self._append_observable_objects("uco-core:hasFacet", *args)
251+
self._append_stuff("uco-core:hasFacet", *args, objects=True)
252252

253253
@unpack_args_array
254254
def append_core_objects(self, *args):

case_mapping/uco/action.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,7 @@ def __init__(
4848
}
4949
)
5050
if action_status:
51-
self["uco-action:actionStatus"] = {
52-
"@type": "uco-vocabulary:ActionStatusTypeVocab",
53-
"@value": action_status,
54-
}
51+
self["uco-action:actionStatus"] = action_status
5552
self._datetime_vars(
5653
**{"uco-action:startTime": start_time, "uco-action:endTime": end_time}
5754
)

case_mapping/uco/core.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,13 @@ class Compilation(UcoObject):
1010
def __init__(
1111
self,
1212
*args: Any,
13-
core_objects: Optional[Sequence[UcoObject]] = None,
1413
**kwargs: Any,
1514
) -> None:
1615
"""
1716
A compilation is a grouping of things.
1817
"""
1918
super().__init__(*args, **kwargs)
2019
self["@type"] = "uco-core:Compilation"
21-
if core_objects is not None and len(core_objects) > 0:
22-
self.append_core_objects(core_objects)
2320

2421
@unpack_args_array
2522
def append_to_uco_object(self, *args) -> None:
@@ -34,21 +31,16 @@ class ContextualCompilation(Compilation):
3431
def __init__(
3532
self,
3633
*args: Any,
37-
core_objects: Sequence[UcoObject],
34+
core_objects: Optional[Sequence[UcoObject]] = None,
3835
**kwargs: Any,
3936
) -> None:
4037
"""
4138
A contextual compilation is a grouping of things sharing some context (e.g., a set of network connections observed on a given day, all accounts associated with a given person).
42-
43-
Future implementation note: At and before CASE 1.3.0, at least one core:object must be supplied at instantiation time of a contextual compilation. At and after CASE 1.4.0, these objects will be optional.
4439
"""
45-
if len(core_objects) == 0:
46-
raise ValueError(
47-
"A ContextualCompilation is required to have at least one UcoObject to link at initiation time. This will become optional in CASE 1.4.0."
48-
)
4940
super().__init__(*args, **kwargs)
5041
self["@type"] = "uco-core:ContextualCompilation"
51-
self.append_core_objects(core_objects)
42+
if core_objects is not None and len(core_objects) > 0:
43+
self.append_core_objects(core_objects)
5244

5345

5446
class EnclosingCompilation(Compilation):

case_mapping/uco/observable.py

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from datetime import datetime
22
from typing import Any, Dict, List, Optional, Union
3+
from warnings import warn
34

45
from cdo_local_uuid import local_uuid
56

@@ -332,21 +333,15 @@ def __init__(
332333
self._bool_vars(**{"uco-observable:isEncrypted": is_encrypted})
333334

334335
if byte_order:
335-
self["uco-observable:byteOrder"] = {
336-
"@type": "uco-vocabulary:EndiannessTypeVocab",
337-
"@value": byte_order,
338-
}
336+
self["uco-observable:byteOrder"] = byte_order
339337

340338
if hash_method is not None or hash_value is not None or hash_value != "-":
341339
data: dict[str, Any] = {
342340
"@id": self.prefix_label + ":" + str(local_uuid()),
343341
"@type": "uco-types:Hash",
344342
}
345343
if hash_method is not None:
346-
data["uco-types:hashMethod"] = {
347-
"@type": "uco-vocabulary:HashNameVocab",
348-
"@value": hash_method,
349-
}
344+
data["uco-types:hashMethod"] = hash_method
350345
if hash_value is not None:
351346
data["uco-types:hashValue"] = {
352347
"@type": "xsd:hexBinary",
@@ -1300,19 +1295,52 @@ def __init__(
13001295
)
13011296

13021297

1298+
class SoftwareFacet(Facet):
1299+
def __init__(
1300+
self,
1301+
*args: Any,
1302+
manufacturer: Optional[Identity] = None,
1303+
version: Optional[str] = None,
1304+
**kwargs: Any,
1305+
) -> None:
1306+
super().__init__()
1307+
1308+
self["@type"] = "uco-observable:SoftwareFacet"
1309+
1310+
self._str_vars(
1311+
**{
1312+
"uco-observable:version": version,
1313+
}
1314+
)
1315+
self._node_reference_vars(
1316+
**{
1317+
"uco-observable:manufacturer": manufacturer,
1318+
}
1319+
)
1320+
1321+
13031322
class OperatingSystemFacet(Facet):
13041323
def __init__(
13051324
self,
13061325
*args: Any,
13071326
os_advertisingID: Optional[str] = None,
13081327
os_bitness: Optional[str] = None,
1328+
os_environment_variables: Union[None, Dict, Dictionary] = None,
13091329
os_install_date: Optional[datetime] = None,
13101330
os_isLimitAdTrackingEnabled: Optional[bool] = None,
1311-
os_manufacturer: Union[None, Identity] = None,
1312-
os_version: Optional[str] = None,
1313-
os_environment_variables: Union[None, Dict, Dictionary] = None,
13141331
**kwargs: Any,
13151332
):
1333+
if "os_manufacturer" in kwargs:
1334+
warn(
1335+
"'os_manufacturer' should not be used on an OperatingSystemFacet as of UCO 1.4.0. Instead, use 'manufacturer' on a SoftwareFacet attached to the same OperatingSystem object.",
1336+
DeprecationWarning,
1337+
)
1338+
if "os_version" in kwargs:
1339+
warn(
1340+
"'os_version' should not be used on an OperatingSystemFacet as of UCO 1.4.0. Instead, use 'version' on a SoftwareFacet attached to the same OperatingSystem object.",
1341+
DeprecationWarning,
1342+
)
1343+
13161344
super().__init__()
13171345

13181346
self["@type"] = "uco-observable:OperatingSystemFacet"
@@ -1334,19 +1362,13 @@ def __init__(
13341362
**{
13351363
"uco-observable:advertisingID": os_advertisingID,
13361364
"uco-observable:bitness": os_bitness,
1337-
"uco-observable:version": os_version,
13381365
}
13391366
)
13401367
self._datetime_vars(**{"uco-observable:installDate": os_install_date})
13411368

13421369
self._bool_vars(
13431370
**{"uco-observable:isLimitAdTrackingEnabled": os_isLimitAdTrackingEnabled}
13441371
)
1345-
self._node_reference_vars(
1346-
**{
1347-
"uco-observable:manufacturer": os_manufacturer,
1348-
}
1349-
)
13501372

13511373

13521374
class PathRelationFacet(Facet):

example.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def _next_timestamp() -> datetime:
4545
modified_time=bundle_modified_time,
4646
name="json ld file",
4747
object_created_time=bundle_created_time,
48-
spec_version="UCO/CASE 1.3",
48+
spec_version="UCO/CASE 1.4",
4949
tag="Artifacts extracted from a mobile phone",
5050
)
5151

@@ -67,17 +67,24 @@ def _next_timestamp() -> datetime:
6767
}
6868
manufacturer_apple = uco.identity.Organization(name="Apple")
6969

70+
# TODO AJN: Modeling suggestion - The SoftwareFacet and
71+
# OperatingSystemFacet pertain to an OperatingSystem object, not the
72+
# Device object. There needs to be a model of the time-bounded
73+
# relationship between device and OS, whether a Relationship or a
74+
# time-bounding on the OperatingSystem object.
75+
software_facet = uco.observable.SoftwareFacet(
76+
manufacturer=manufacturer_apple,
77+
version="17.4.1",
78+
)
7079
os_facet = uco.observable.OperatingSystemFacet(
71-
os_manufacturer=manufacturer_apple,
7280
os_advertisingID="DX4CDXKN",
7381
os_bitness="64-bit",
7482
os_install_date=os_date,
7583
os_isLimitAdTrackingEnabled=True,
76-
os_version="17.4.1",
7784
os_environment_variables=os_env_vars,
7885
)
7986

80-
device_camera.append_facets(device1, os_facet)
87+
device_camera.append_facets(device1, software_facet, os_facet)
8188
bundle.append_to_uco_object(device_camera)
8289

8390
##################################
@@ -863,16 +870,18 @@ def _next_timestamp() -> datetime:
863870
)
864871

865872
os_object = uco.observable.ObservableObject()
873+
software_facet = uco.observable.SoftwareFacet(
874+
manufacturer=manufacturer_apple,
875+
version="17.4.1",
876+
)
866877
os_facet = uco.observable.OperatingSystemFacet(
867-
os_manufacturer=manufacturer_apple,
868878
os_advertisingID="XX908WN",
869879
os_bitness="64-bit",
870880
os_install_date=os_date,
871881
os_isLimitAdTrackingEnabled=True,
872-
os_version="17.4.1",
873882
os_environment_variables=os_env_vars,
874883
)
875-
os_object.append_facets(os_facet)
884+
os_object.append_facets(software_facet, os_facet)
876885
bundle.append_to_uco_object(os_object)
877886

878887
app_telegram_facet = uco.observable.ApplicationFacet(

0 commit comments

Comments
 (0)