Skip to content

Update CASE validation to 1.4.0 and add necessary API and example adjustments #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ jobs:

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

- name: Convert example
Expand Down
560 changes: 275 additions & 285 deletions case.jsonld

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions case_mapping/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ def __init__(
@unpack_args_array
def append_facets(self, *args):
"""
:param args: A single/tuple of ObservableObjects
:param args: A single/tuple of Facets
"""
self._append_observable_objects("uco-core:hasFacet", *args)
self._append_stuff("uco-core:hasFacet", *args, objects=True)

@unpack_args_array
def append_core_objects(self, *args):
Expand Down
5 changes: 1 addition & 4 deletions case_mapping/uco/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ def __init__(
}
)
if action_status:
self["uco-action:actionStatus"] = {
"@type": "uco-vocabulary:ActionStatusTypeVocab",
"@value": action_status,
}
self["uco-action:actionStatus"] = action_status
self._datetime_vars(
**{"uco-action:startTime": start_time, "uco-action:endTime": end_time}
)
Expand Down
14 changes: 3 additions & 11 deletions case_mapping/uco/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@ class Compilation(UcoObject):
def __init__(
self,
*args: Any,
core_objects: Optional[Sequence[UcoObject]] = None,
**kwargs: Any,
) -> None:
"""
A compilation is a grouping of things.
"""
super().__init__(*args, **kwargs)
self["@type"] = "uco-core:Compilation"
if core_objects is not None and len(core_objects) > 0:
self.append_core_objects(core_objects)

@unpack_args_array
def append_to_uco_object(self, *args) -> None:
Expand All @@ -34,21 +31,16 @@ class ContextualCompilation(Compilation):
def __init__(
self,
*args: Any,
core_objects: Sequence[UcoObject],
core_objects: Optional[Sequence[UcoObject]] = None,
**kwargs: Any,
) -> None:
"""
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).

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.
"""
if len(core_objects) == 0:
raise ValueError(
"A ContextualCompilation is required to have at least one UcoObject to link at initiation time. This will become optional in CASE 1.4.0."
)
super().__init__(*args, **kwargs)
self["@type"] = "uco-core:ContextualCompilation"
self.append_core_objects(core_objects)
if core_objects is not None and len(core_objects) > 0:
self.append_core_objects(core_objects)


class EnclosingCompilation(Compilation):
Expand Down
56 changes: 39 additions & 17 deletions case_mapping/uco/observable.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime
from typing import Any, Dict, List, Optional, Union
from warnings import warn

from cdo_local_uuid import local_uuid

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

if byte_order:
self["uco-observable:byteOrder"] = {
"@type": "uco-vocabulary:EndiannessTypeVocab",
"@value": byte_order,
}
self["uco-observable:byteOrder"] = byte_order

if hash_method is not None or hash_value is not None or hash_value != "-":
data: dict[str, Any] = {
"@id": self.prefix_label + ":" + str(local_uuid()),
"@type": "uco-types:Hash",
}
if hash_method is not None:
data["uco-types:hashMethod"] = {
"@type": "uco-vocabulary:HashNameVocab",
"@value": hash_method,
}
data["uco-types:hashMethod"] = hash_method
if hash_value is not None:
data["uco-types:hashValue"] = {
"@type": "xsd:hexBinary",
Expand Down Expand Up @@ -1300,19 +1295,52 @@ def __init__(
)


class SoftwareFacet(Facet):
def __init__(
self,
*args: Any,
manufacturer: Optional[Identity] = None,
version: Optional[str] = None,
**kwargs: Any,
) -> None:
super().__init__()

self["@type"] = "uco-observable:SoftwareFacet"

self._str_vars(
**{
"uco-observable:version": version,
}
)
self._node_reference_vars(
**{
"uco-observable:manufacturer": manufacturer,
}
)


class OperatingSystemFacet(Facet):
def __init__(
self,
*args: Any,
os_advertisingID: Optional[str] = None,
os_bitness: Optional[str] = None,
os_environment_variables: Union[None, Dict, Dictionary] = None,
os_install_date: Optional[datetime] = None,
os_isLimitAdTrackingEnabled: Optional[bool] = None,
os_manufacturer: Union[None, Identity] = None,
os_version: Optional[str] = None,
os_environment_variables: Union[None, Dict, Dictionary] = None,
**kwargs: Any,
):
if "os_manufacturer" in kwargs:
warn(
"'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.",
DeprecationWarning,
)
if "os_version" in kwargs:
warn(
"'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.",
DeprecationWarning,
)

super().__init__()

self["@type"] = "uco-observable:OperatingSystemFacet"
Expand All @@ -1334,19 +1362,13 @@ def __init__(
**{
"uco-observable:advertisingID": os_advertisingID,
"uco-observable:bitness": os_bitness,
"uco-observable:version": os_version,
}
)
self._datetime_vars(**{"uco-observable:installDate": os_install_date})

self._bool_vars(
**{"uco-observable:isLimitAdTrackingEnabled": os_isLimitAdTrackingEnabled}
)
self._node_reference_vars(
**{
"uco-observable:manufacturer": os_manufacturer,
}
)


class PathRelationFacet(Facet):
Expand Down
23 changes: 16 additions & 7 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _next_timestamp() -> datetime:
modified_time=bundle_modified_time,
name="json ld file",
object_created_time=bundle_created_time,
spec_version="UCO/CASE 1.3",
spec_version="UCO/CASE 1.4",
tag="Artifacts extracted from a mobile phone",
)

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

# TODO AJN: Modeling suggestion - The SoftwareFacet and
# OperatingSystemFacet pertain to an OperatingSystem object, not the
# Device object. There needs to be a model of the time-bounded
# relationship between device and OS, whether a Relationship or a
# time-bounding on the OperatingSystem object.
software_facet = uco.observable.SoftwareFacet(
manufacturer=manufacturer_apple,
version="17.4.1",
)
os_facet = uco.observable.OperatingSystemFacet(
os_manufacturer=manufacturer_apple,
os_advertisingID="DX4CDXKN",
os_bitness="64-bit",
os_install_date=os_date,
os_isLimitAdTrackingEnabled=True,
os_version="17.4.1",
os_environment_variables=os_env_vars,
)

device_camera.append_facets(device1, os_facet)
device_camera.append_facets(device1, software_facet, os_facet)
bundle.append_to_uco_object(device_camera)

##################################
Expand Down Expand Up @@ -863,16 +870,18 @@ def _next_timestamp() -> datetime:
)

os_object = uco.observable.ObservableObject()
software_facet = uco.observable.SoftwareFacet(
manufacturer=manufacturer_apple,
version="17.4.1",
)
os_facet = uco.observable.OperatingSystemFacet(
os_manufacturer=manufacturer_apple,
os_advertisingID="XX908WN",
os_bitness="64-bit",
os_install_date=os_date,
os_isLimitAdTrackingEnabled=True,
os_version="17.4.1",
os_environment_variables=os_env_vars,
)
os_object.append_facets(os_facet)
os_object.append_facets(software_facet, os_facet)
bundle.append_to_uco_object(os_object)

app_telegram_facet = uco.observable.ApplicationFacet(
Expand Down