stidantic 0.1.3__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of stidantic might be problematic. Click here for more details.
- stidantic/__init__.py +0 -0
- stidantic/bundle.py +8 -13
- stidantic/extension.py +5 -4
- stidantic/extensions/__init__.py +0 -0
- stidantic/extensions/pap.py +8 -9
- stidantic/language.py +6 -4
- stidantic/marking.py +5 -4
- stidantic/sco.py +147 -192
- stidantic/sdo.py +52 -57
- stidantic/serializers.py +16 -0
- stidantic/sro.py +16 -13
- stidantic/types.py +98 -53
- stidantic/utils.py +15 -0
- stidantic/validators.py +17 -4
- stidantic/vocab.py +1 -3
- {stidantic-0.1.3.dist-info → stidantic-0.2.1.dist-info}/METADATA +13 -10
- stidantic-0.2.1.dist-info/RECORD +19 -0
- stidantic-0.1.3.dist-info/RECORD +0 -15
- {stidantic-0.1.3.dist-info → stidantic-0.2.1.dist-info}/WHEEL +0 -0
- {stidantic-0.1.3.dist-info → stidantic-0.2.1.dist-info}/licenses/LICENSE +0 -0
stidantic/sdo.py
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import Any, Literal, Annotated, Self
|
|
3
|
-
from typing_extensions import deprecated
|
|
4
|
-
from annotated_types import Ge, Gt, Le
|
|
1
|
+
from typing import Annotated, Any, Literal, Self
|
|
5
2
|
|
|
6
|
-
from
|
|
3
|
+
from annotated_types import Ge, Le
|
|
4
|
+
from pydantic import AfterValidator, Field
|
|
7
5
|
from pydantic.functional_validators import model_validator
|
|
6
|
+
from typing_extensions import deprecated
|
|
8
7
|
|
|
9
|
-
from stidantic.types import
|
|
8
|
+
from stidantic.types import (
|
|
9
|
+
ExternalReference,
|
|
10
|
+
Identifier,
|
|
11
|
+
KillChainPhase,
|
|
12
|
+
StixDomain,
|
|
13
|
+
StixTimestamp,
|
|
14
|
+
)
|
|
15
|
+
from stidantic.validators import identifier_of_type
|
|
10
16
|
from stidantic.vocab import OpinionEnum
|
|
11
17
|
|
|
12
18
|
|
|
@@ -66,12 +72,12 @@ class Campaign(StixDomain):
|
|
|
66
72
|
# A summary property of data from sightings and other data that may or may not be available in STIX.
|
|
67
73
|
# If new sightings are received that are earlier than the first seen timestamp,
|
|
68
74
|
# the object may be updated to account for the new data.
|
|
69
|
-
first_seen:
|
|
75
|
+
first_seen: StixTimestamp | None = None
|
|
70
76
|
# The time that this Campaign was last seen.
|
|
71
77
|
# A summary property of data from sightings and other data that may or may not be available in STIX.
|
|
72
78
|
# If new sightings are received that are later than the last seen timestamp,
|
|
73
79
|
# the object may be updated to account for the new data.
|
|
74
|
-
last_seen:
|
|
80
|
+
last_seen: StixTimestamp | None = None
|
|
75
81
|
# The Campaign’s primary goal, objective, desired outcome, or intended effect
|
|
76
82
|
# — what the Threat Actor or Intrusion Set hopes to accomplish with this Campaign.
|
|
77
83
|
objective: str | None = None
|
|
@@ -113,7 +119,7 @@ class CourseOfAction(StixDomain):
|
|
|
113
119
|
# potentially including its purpose and its key characteristics.
|
|
114
120
|
description: str | None = None
|
|
115
121
|
# A reserved property that serves as a placeholder for future inclusion of machine automatable courses of action.
|
|
116
|
-
# action: str | None = None
|
|
122
|
+
# action: str | None = None # noqa: ERA001
|
|
117
123
|
|
|
118
124
|
|
|
119
125
|
# 4.4 Grouping
|
|
@@ -236,12 +242,12 @@ class Indicator(StixDomain):
|
|
|
236
242
|
# this object’s creation.
|
|
237
243
|
pattern_version: str | None = None
|
|
238
244
|
# The time from which this Indicator is considered a valid indicator of the behaviors it is related or represents.
|
|
239
|
-
valid_from:
|
|
245
|
+
valid_from: StixTimestamp
|
|
240
246
|
# The time at which this Indicator should no longer be considered a valid indicator of the behaviors it is
|
|
241
247
|
# related to or represents.
|
|
242
248
|
# If the valid_until property is omitted, then there is no constraint on the latest time for which the
|
|
243
249
|
# Indicator is valid.
|
|
244
|
-
valid_until:
|
|
250
|
+
valid_until: StixTimestamp | None = None
|
|
245
251
|
# The kill chain phase(s) to which this Indicator corresponds.
|
|
246
252
|
kill_chain_phases: list[KillChainPhase] | None = None
|
|
247
253
|
|
|
@@ -251,9 +257,7 @@ class Indicator(StixDomain):
|
|
|
251
257
|
The valid_until property MUST be greater than the timestamp in the valid_from property.
|
|
252
258
|
"""
|
|
253
259
|
if self.valid_from and self.valid_until and self.valid_from > self.valid_until:
|
|
254
|
-
raise ValueError(
|
|
255
|
-
"The valid_until property MUST be greater than the timestamp in the valid_from property"
|
|
256
|
-
)
|
|
260
|
+
raise ValueError("The valid_until property MUST be greater than the timestamp in the valid_from property")
|
|
257
261
|
return self
|
|
258
262
|
|
|
259
263
|
|
|
@@ -282,9 +286,9 @@ class Infrastructure(StixDomain):
|
|
|
282
286
|
# The list of Kill Chain Phases for which this Infrastructure is used.
|
|
283
287
|
kill_chain_phases: list[KillChainPhase] | None = None
|
|
284
288
|
# The time that this Infrastructure was first seen performing malicious activities.
|
|
285
|
-
first_seen:
|
|
289
|
+
first_seen: StixTimestamp | None = None
|
|
286
290
|
# The time that this Infrastructure was last seen performing malicious activities.
|
|
287
|
-
last_seen:
|
|
291
|
+
last_seen: StixTimestamp | None = None
|
|
288
292
|
|
|
289
293
|
@model_validator(mode="after")
|
|
290
294
|
def validate_last_seen_after_first_seen(self) -> Self:
|
|
@@ -329,12 +333,12 @@ class IntrusionSet(StixDomain):
|
|
|
329
333
|
# A summary property of data from sightings and other data that may or may not be available in STIX.
|
|
330
334
|
# If new sightings are received that are earlier than the first seen timestamp, the object may be updated to
|
|
331
335
|
# account for the new data.
|
|
332
|
-
first_seen:
|
|
336
|
+
first_seen: StixTimestamp | None = None
|
|
333
337
|
# The time that this Intrusion Set was last seen.
|
|
334
338
|
# This property is a summary property of data from sightings and other data that may or may not be available.
|
|
335
339
|
# If new sightings are received that are later than the last seen timestamp, the object may be updated to
|
|
336
340
|
# account for the new data.
|
|
337
|
-
last_seen:
|
|
341
|
+
last_seen: StixTimestamp | None = None
|
|
338
342
|
# The high-level goals of this Intrusion Set, namely, what are they trying to do.
|
|
339
343
|
# For example, they may be motivated by personal gain, but their goal is to steal credit card numbers.
|
|
340
344
|
# To do this, they may execute specific Campaigns that have detailed objectives like compromising point of sale
|
|
@@ -423,14 +427,8 @@ class Location(StixDomain):
|
|
|
423
427
|
- country
|
|
424
428
|
- latitude and longitude
|
|
425
429
|
"""
|
|
426
|
-
if (
|
|
427
|
-
|
|
428
|
-
and not self.country
|
|
429
|
-
and not (self.latitude and self.longitude)
|
|
430
|
-
):
|
|
431
|
-
raise ValueError(
|
|
432
|
-
"At least one of region, country, or both latitude and longitude MUST be provided"
|
|
433
|
-
)
|
|
430
|
+
if not self.region and not self.country and not (self.latitude and self.longitude):
|
|
431
|
+
raise ValueError("At least one of region, country, or both latitude and longitude MUST be provided")
|
|
434
432
|
|
|
435
433
|
if self.latitude and not self.longitude:
|
|
436
434
|
raise ValueError("If latitude is present, longitude MUST be present")
|
|
@@ -439,9 +437,7 @@ class Location(StixDomain):
|
|
|
439
437
|
raise ValueError("If longitude is present, latitude MUST be present")
|
|
440
438
|
|
|
441
439
|
if self.precision and (not self.latitude or not self.longitude):
|
|
442
|
-
raise ValueError(
|
|
443
|
-
"If precision is present, latitude and longitude MUST be present"
|
|
444
|
-
)
|
|
440
|
+
raise ValueError("If precision is present, latitude and longitude MUST be present")
|
|
445
441
|
|
|
446
442
|
return self
|
|
447
443
|
|
|
@@ -479,15 +475,15 @@ class Malware(StixDomain):
|
|
|
479
475
|
# This property is a summary property of data from sightings and other data that may or may not be available in
|
|
480
476
|
# STIX. If new sightings are received that are earlier than the first seen timestamp,
|
|
481
477
|
# the object may be updated to account for the new data.
|
|
482
|
-
first_seen:
|
|
478
|
+
first_seen: StixTimestamp | None = None
|
|
483
479
|
# The time that the malware family or malware instance was last seen.
|
|
484
480
|
# This property is a summary property of data from sightings and other data that may or may not be available in
|
|
485
481
|
# STIX. If new sightings are received that are later than the last_seen timestamp,
|
|
486
482
|
# the object may be updated to account for the new data.
|
|
487
|
-
last_seen:
|
|
483
|
+
last_seen: StixTimestamp | None = None
|
|
488
484
|
# The operating systems that the malware family or malware instance is executable on.
|
|
489
485
|
# This applies to virtualized operating systems as well as those running on bare metal.
|
|
490
|
-
operating_system_refs: list[Identifier] | None = None
|
|
486
|
+
operating_system_refs: list[Annotated[Identifier, AfterValidator(identifier_of_type("software"))]] | None = None
|
|
491
487
|
# The processor architectures (e.g., x86, ARM, etc.) that the malware instance or family is executable on.
|
|
492
488
|
# The values for this property SHOULD come from the processor-architecture-ov open vocabulary.
|
|
493
489
|
architecture_execution_envs: list[str] | None = None
|
|
@@ -499,7 +495,7 @@ class Malware(StixDomain):
|
|
|
499
495
|
capabilities: list[str] | None = None
|
|
500
496
|
# The sample_refs property specifies a list of identifiers of the SCO file or artifact objects associated with
|
|
501
497
|
# this malware instance(s) or family.
|
|
502
|
-
sample_refs: list[Identifier] | None = None
|
|
498
|
+
sample_refs: list[Annotated[Identifier, AfterValidator(identifier_of_type("file", "artifact"))]] | None = None
|
|
503
499
|
|
|
504
500
|
@model_validator(mode="after")
|
|
505
501
|
def validate_last_seen_after_first_seen(self) -> Self:
|
|
@@ -541,16 +537,13 @@ class MalwareAnalysis(StixDomain):
|
|
|
541
537
|
# used for the dynamic analysis of the malware instance or family. If this value is not included in conjunction
|
|
542
538
|
# with the operating_system_ref property, this means that the dynamic analysis may have been performed on bare
|
|
543
539
|
# metal (i.e. without virtualization) or the information was redacted.
|
|
544
|
-
|
|
545
|
-
host_vm_ref: Identifier | None = None
|
|
540
|
+
host_vm_ref: Annotated[Identifier, AfterValidator(identifier_of_type("software"))] | None = None
|
|
546
541
|
# The operating system used for the dynamic analysis of the malware instance or family. This applies to
|
|
547
542
|
# virtualized operating systems as well as those running on bare metal.
|
|
548
|
-
|
|
549
|
-
operating_system_ref: Identifier | None = None
|
|
543
|
+
operating_system_ref: Annotated[Identifier, AfterValidator(identifier_of_type("software"))] | None = None
|
|
550
544
|
# Any non-standard software installed on the operating system (specified through the operating-system value)
|
|
551
545
|
# used for the dynamic analysis of the malware instance or family.
|
|
552
|
-
|
|
553
|
-
installed_software_refs: list[Identifier] | None = None
|
|
546
|
+
installed_software_refs: list[Annotated[Identifier, AfterValidator(identifier_of_type("software"))]] | None = None
|
|
554
547
|
# The named configuration of additional product configuration parameters for this analysis run. For example, when
|
|
555
548
|
# a product is configured to do full depth analysis of Window™ PE files. This configuration may have a named
|
|
556
549
|
# version and that named version can be captured in this property. This will ensure additional runs can be
|
|
@@ -565,11 +558,11 @@ class MalwareAnalysis(StixDomain):
|
|
|
565
558
|
analysis_definition_version: str | None = None
|
|
566
559
|
# The date and time that the malware was first submitted for scanning or analysis. This value will stay constant
|
|
567
560
|
# while the scanned date can change. For example, when Malware was submitted to a virus analysis tool.
|
|
568
|
-
submitted:
|
|
561
|
+
submitted: StixTimestamp | None = None
|
|
569
562
|
# The date and time that the malware analysis was initiated.
|
|
570
|
-
analysis_started:
|
|
563
|
+
analysis_started: StixTimestamp | None = None
|
|
571
564
|
# The date and time that the malware analysis ended.
|
|
572
|
-
analysis_ended:
|
|
565
|
+
analysis_ended: StixTimestamp | None = None
|
|
573
566
|
# The classification result or name assigned to the malware instance by the scanner tool.
|
|
574
567
|
result_name: str | None = None
|
|
575
568
|
# The classification result as determined by the scanner or tool analysis process.
|
|
@@ -583,7 +576,15 @@ class MalwareAnalysis(StixDomain):
|
|
|
583
576
|
# Analysis objects when the Malware sample_refs property does not contain the SCO that is included in the
|
|
584
577
|
# Malware Analysis sample_ref property. Note, this property can also contain a reference to an SCO which is not
|
|
585
578
|
# associated with Malware (i.e., some SCO which was scanned and found to be benign.)
|
|
586
|
-
sample_ref:
|
|
579
|
+
sample_ref: (
|
|
580
|
+
list[
|
|
581
|
+
Annotated[
|
|
582
|
+
Identifier,
|
|
583
|
+
AfterValidator(identifier_of_type("file", "artifact", "network-traffic")),
|
|
584
|
+
]
|
|
585
|
+
]
|
|
586
|
+
| None
|
|
587
|
+
) = None
|
|
587
588
|
|
|
588
589
|
@model_validator(mode="after")
|
|
589
590
|
def at_least_one_of(self) -> Self:
|
|
@@ -657,15 +658,15 @@ class ObservedData(StixDomain):
|
|
|
657
658
|
|
|
658
659
|
type: Literal["observed-data"] = "observed-data" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
659
660
|
# The beginning of the time window during which the data was seen.
|
|
660
|
-
first_observed:
|
|
661
|
+
first_observed: StixTimestamp
|
|
661
662
|
# The end of the time window during which the data was seen.
|
|
662
|
-
last_observed:
|
|
663
|
+
last_observed: StixTimestamp
|
|
663
664
|
# The number of times that each Cyber-observable object represented in the objects or object_refs property was
|
|
664
665
|
# seen. If present, this MUST be an integer between 1 and 999,999,999 inclusive.
|
|
665
666
|
# If the number_observed property is greater than 1, the data contained in the objects or object_refs property was
|
|
666
667
|
# seen multiple times. In these cases, object creators MAY omit properties of the SCO (such as timestamps) that are
|
|
667
668
|
# specific to a single instance of that observed data.
|
|
668
|
-
number_observed: Annotated[int,
|
|
669
|
+
number_observed: Annotated[int, Ge(1), Le(999999999)]
|
|
669
670
|
# A dictionary of SCO representing the observation. The dictionary MUST contain at least one object.
|
|
670
671
|
# The cyber observable content MAY include multiple objects if those objects are related as part of a single
|
|
671
672
|
# observation. Multiple objects not related to each other via cyber observable Relationships MUST NOT be contained
|
|
@@ -683,11 +684,7 @@ class ObservedData(StixDomain):
|
|
|
683
684
|
"""
|
|
684
685
|
The last_observed property MUST be greater than or equal to the timestamp in the first_observed property.
|
|
685
686
|
"""
|
|
686
|
-
if
|
|
687
|
-
self.first_observed
|
|
688
|
-
and self.last_observed
|
|
689
|
-
and self.first_observed > self.last_observed
|
|
690
|
-
):
|
|
687
|
+
if self.first_observed and self.last_observed and self.first_observed > self.last_observed:
|
|
691
688
|
raise ValueError(
|
|
692
689
|
"The last_observed property MUST be greater than or equal to the the first_observed property"
|
|
693
690
|
)
|
|
@@ -700,9 +697,7 @@ class ObservedData(StixDomain):
|
|
|
700
697
|
but both MUST NOT be present at the same time.
|
|
701
698
|
"""
|
|
702
699
|
if self.objects is not None and self.object_refs is not None:
|
|
703
|
-
raise ValueError(
|
|
704
|
-
"The objects and object_refs properties MUST NOT be present at the same time"
|
|
705
|
-
)
|
|
700
|
+
raise ValueError("The objects and object_refs properties MUST NOT be present at the same time")
|
|
706
701
|
if self.objects is None and self.object_refs is None:
|
|
707
702
|
raise ValueError("Either objects or object_refs MUST be provided")
|
|
708
703
|
return self
|
|
@@ -770,7 +765,7 @@ class Report(StixDomain):
|
|
|
770
765
|
# The date that this Report object was officially published by the creator of this report.
|
|
771
766
|
# The publication date (public release, legal release, etc.) may be different than the date the report was
|
|
772
767
|
# created or shared internally (the date in the created property).
|
|
773
|
-
published:
|
|
768
|
+
published: StixTimestamp
|
|
774
769
|
# Specifies the STIX Objects that are referred to by this Report.
|
|
775
770
|
object_refs: list[Identifier]
|
|
776
771
|
|
|
@@ -804,12 +799,12 @@ class ThreatActor(StixDomain):
|
|
|
804
799
|
# This property is a summary property of data from sightings and other data that may or may not be available in
|
|
805
800
|
# STIX. If new sightings are received that are earlier than the first seen timestamp, the object may be updated
|
|
806
801
|
# to account for the new data.
|
|
807
|
-
first_seen:
|
|
802
|
+
first_seen: StixTimestamp | None = None
|
|
808
803
|
# The time that this Threat Actor was last seen.
|
|
809
804
|
# This property is a summary property of data from sightings and other data that may or may not be available in
|
|
810
805
|
# STIX. If new sightings are received that are later than the last seen timestamp, the object may be updated to
|
|
811
806
|
# account for the new data
|
|
812
|
-
last_seen:
|
|
807
|
+
last_seen: StixTimestamp | None = None
|
|
813
808
|
# A list of roles the Threat Actor plays.
|
|
814
809
|
# The values for this property SHOULD come from the threat-actor-role-ov open vocabulary.
|
|
815
810
|
roles: list[str] | None = None
|
stidantic/serializers.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def ser_datetime(value: datetime) -> str:
|
|
5
|
+
"""
|
|
6
|
+
The timestamp property MUST be a valid RFC 3339-formatted timestamp [RFC3339] using the format
|
|
7
|
+
YYYY-MM-DDTHH:mm:ss[.s+]Z where the "s+" represents 1 or more sub-second values.
|
|
8
|
+
The brackets denote that sub-second precision is optional, and that if no digits are provided,
|
|
9
|
+
the decimal place MUST NOT be present.
|
|
10
|
+
The timestamp MUST be represented in the UTC timezone and MUST use the "Z" designation to indicate this.
|
|
11
|
+
NOTE: when using precisions greater than nanoseconds there may be implications for interoperability as they may be
|
|
12
|
+
truncated when stored as a UNIX timestamp or floating point number due to the precision of those formats.
|
|
13
|
+
"""
|
|
14
|
+
if value.microsecond == 0:
|
|
15
|
+
return value.astimezone(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
16
|
+
return value.astimezone(UTC).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
stidantic/sro.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
from typing import Literal, Self
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
1
|
+
from typing import Annotated, Literal, Self
|
|
2
|
+
|
|
3
|
+
from pydantic import AfterValidator, Field
|
|
4
4
|
from pydantic.functional_validators import model_validator
|
|
5
5
|
from pydantic.types import PositiveInt
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
from stidantic.types import Identifier, StixRelationship, StixTimestamp, StixType
|
|
8
|
+
from stidantic.validators import identifier_of_type
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
# 5.1 Relationship
|
|
@@ -51,13 +53,13 @@ class Relationship(StixRelationship):
|
|
|
51
53
|
# time at which relationship will be asserted to be true.
|
|
52
54
|
# If it is not specified, then the earliest time at which the relationship between the objects exists
|
|
53
55
|
# is not defined.
|
|
54
|
-
start_time:
|
|
56
|
+
start_time: StixTimestamp | None = None
|
|
55
57
|
# The latest time at which the Relationship between the objects exists. If this property is a future timestamp,
|
|
56
58
|
# at the time the stop_time property is defined, then this represents an estimate by the producer of the
|
|
57
59
|
# intelligence of the latest time at which relationship will be asserted to be true.
|
|
58
60
|
# If stop_time is not specified, then the latest time at which the relationship between
|
|
59
61
|
# the objects exists is either not known, not disclosed, or has no defined stop time.
|
|
60
|
-
stop_time:
|
|
62
|
+
stop_time: StixTimestamp | None = None
|
|
61
63
|
|
|
62
64
|
@model_validator(mode="after")
|
|
63
65
|
def validate_start_stop_interval(self) -> Self:
|
|
@@ -110,9 +112,9 @@ class Sighting(StixRelationship):
|
|
|
110
112
|
# A description that provides more details and context about the Sighting.
|
|
111
113
|
description: str | None = None
|
|
112
114
|
# The beginning of the time window during which the SDO referenced by the sighting_of_ref property was sighted.
|
|
113
|
-
first_seen:
|
|
115
|
+
first_seen: StixTimestamp | None = None
|
|
114
116
|
# The end of the time window during which the SDO referenced by the sighting_of_ref property was sighted.
|
|
115
|
-
last_seen:
|
|
117
|
+
last_seen: StixTimestamp | None = None
|
|
116
118
|
# If present, this MUST be an integer between 0 and 999,999,999 inclusive and represents the number of times the
|
|
117
119
|
# SDO referenced by the sighting_of_ref property was sighted.
|
|
118
120
|
# Observed Data has a similar property called number_observed, which refers to the number of times the data was
|
|
@@ -129,19 +131,20 @@ class Sighting(StixRelationship):
|
|
|
129
131
|
# A list of ID references to the Observed Data objects that contain the raw cyber data for this Sighting.
|
|
130
132
|
# For example, a Sighting of an Indicator with an IP address could include the Observed Data for the
|
|
131
133
|
# network connection that the Indicator was used to detect.
|
|
132
|
-
|
|
133
|
-
observed_data_refs: list[Identifier] | None = None
|
|
134
|
+
observed_data_refs: list[Annotated[Identifier, AfterValidator(identifier_of_type("observed-data"))]] | None = None
|
|
134
135
|
# A list of ID references to the Identity or Location objects describing the entities or types of entities
|
|
135
136
|
# that saw the sighting.
|
|
136
137
|
# Omitting the where_sighted_refs property does not imply that the sighting was seen by the object creator.
|
|
137
138
|
# To indicate that the sighting was seen by the object creator, an Identity representing the object creator
|
|
138
139
|
# should be listed in where_sighted_refs.
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
where_sighted_refs: (
|
|
141
|
+
list[Annotated[Identifier, AfterValidator(identifier_of_type("identity", "location"))]] | None
|
|
142
|
+
) = None
|
|
141
143
|
# The summary property indicates whether the Sighting should be considered summary data.
|
|
142
144
|
# Summary data is an aggregation of previous Sightings reports and should not be considered primary source data.
|
|
143
145
|
# Default value is false.
|
|
144
|
-
|
|
146
|
+
# WARN: Spec says it's a string, but description describes a boolean which defaults to false...
|
|
147
|
+
summary: bool | str | None = None
|
|
145
148
|
|
|
146
149
|
@model_validator(mode="after")
|
|
147
150
|
def validate_first_last_interval(self) -> Self:
|