pycti 6.0.9__py3-none-any.whl → 6.1.0__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 pycti might be problematic. Click here for more details.

@@ -286,9 +286,31 @@ class IntrusionSet:
286
286
  "orderMode": order_mode,
287
287
  },
288
288
  )
289
- return self.opencti.process_multiple(
290
- result["data"]["intrusionSets"], with_pagination
291
- )
289
+ if get_all:
290
+ final_data = []
291
+ data = self.opencti.process_multiple(result["data"]["intrusionSets"])
292
+ final_data = final_data + data
293
+ while result["data"]["intrusionSets"]["pageInfo"]["hasNextPage"]:
294
+ after = result["data"]["intrusionSets"]["pageInfo"]["endCursor"]
295
+ self.opencti.app_logger.info("Listing Intrusion-Sets", {"after": after})
296
+ result = self.opencti.query(
297
+ query,
298
+ {
299
+ "filters": filters,
300
+ "search": search,
301
+ "first": first,
302
+ "after": after,
303
+ "orderBy": order_by,
304
+ "orderMode": order_mode,
305
+ },
306
+ )
307
+ data = self.opencti.process_multiple(result["data"]["intrusionSets"])
308
+ final_data = final_data + data
309
+ return final_data
310
+ else:
311
+ return self.opencti.process_multiple(
312
+ result["data"]["intrusionSets"], with_pagination
313
+ )
292
314
 
293
315
  """
294
316
  Read a Intrusion-Set object
@@ -363,6 +385,7 @@ class IntrusionSet:
363
385
  secondary_motivations = kwargs.get("secondary_motivations", None)
364
386
  x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None)
365
387
  granted_refs = kwargs.get("objectOrganization", None)
388
+ x_opencti_workflow_id = kwargs.get("x_opencti_workflow_id", None)
366
389
  update = kwargs.get("update", False)
367
390
 
368
391
  if name is not None:
@@ -402,6 +425,7 @@ class IntrusionSet:
402
425
  "primary_motivation": primary_motivation,
403
426
  "secondary_motivations": secondary_motivations,
404
427
  "x_opencti_stix_ids": x_opencti_stix_ids,
428
+ "x_opencti_workflow_id": x_opencti_workflow_id,
405
429
  "update": update,
406
430
  }
407
431
  },
@@ -435,7 +459,11 @@ class IntrusionSet:
435
459
  stix_object["x_opencti_granted_refs"] = (
436
460
  self.opencti.get_attribute_in_extension("granted_refs", stix_object)
437
461
  )
438
-
462
+ if "x_opencti_workflow_id" not in stix_object:
463
+ stix_object["x_opencti_workflow_id"] = (
464
+ self.opencti.get_attribute_in_extension("workflow_id", stix_object)
465
+ )
466
+ 7
439
467
  return self.create(
440
468
  stix_id=stix_object["id"],
441
469
  createdBy=(
@@ -500,6 +528,11 @@ class IntrusionSet:
500
528
  if "x_opencti_granted_refs" in stix_object
501
529
  else None
502
530
  ),
531
+ x_opencti_workflow_id=(
532
+ stix_object["x_opencti_workflow_id"]
533
+ if "x_opencti_workflow_id" in stix_object
534
+ else None
535
+ ),
503
536
  update=update,
504
537
  )
505
538
  else:
@@ -401,6 +401,7 @@ class MalwareAnalysis:
401
401
  analysisSco = kwargs.get("analysisSco", None)
402
402
  x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None)
403
403
  granted_refs = kwargs.get("objectOrganization", None)
404
+ x_opencti_workflow_id = kwargs.get("x_opencti_workflow_id", None)
404
405
  update = kwargs.get("update", False)
405
406
 
406
407
  if product is not None and result_name is not None:
@@ -449,6 +450,7 @@ class MalwareAnalysis:
449
450
  "analysisSample": sample,
450
451
  "analysisSco": analysisSco,
451
452
  "x_opencti_stix_ids": x_opencti_stix_ids,
453
+ "x_opencti_workflow_id": x_opencti_workflow_id,
452
454
  "update": update,
453
455
  }
454
456
  },
@@ -482,6 +484,10 @@ class MalwareAnalysis:
482
484
  stix_object["x_opencti_granted_refs"] = (
483
485
  self.opencti.get_attribute_in_extension("granted_refs", stix_object)
484
486
  )
487
+ if "x_opencti_workflow_id" not in stix_object:
488
+ stix_object["x_opencti_workflow_id"] = (
489
+ self.opencti.get_attribute_in_extension("workflow_id", stix_object)
490
+ )
485
491
 
486
492
  return self.create(
487
493
  stix_id=stix_object["id"],
@@ -574,6 +580,11 @@ class MalwareAnalysis:
574
580
  if "x_opencti_granted_refs" in stix_object
575
581
  else None
576
582
  ),
583
+ x_opencti_workflow_id=(
584
+ stix_object["x_opencti_workflow_id"]
585
+ if "x_opencti_workflow_id" in stix_object
586
+ else None
587
+ ),
577
588
  update=update,
578
589
  )
579
590
  else:
@@ -466,6 +466,19 @@ class Report:
466
466
  id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
467
467
  return "report--" + id
468
468
 
469
+ @staticmethod
470
+ def generate_fixed_fake_id(name, published=None):
471
+ name = name.lower().strip()
472
+ if isinstance(published, datetime.datetime):
473
+ published = published.isoformat()
474
+ if published is not None:
475
+ data = {"name": name, "published": published, "fake": "fake"}
476
+ else:
477
+ data = {"name": name, "fake": "fake"}
478
+ data = canonicalize(data, utf8=False)
479
+ id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
480
+ return "report--" + id
481
+
469
482
  """
470
483
  List Report objects
471
484
 
@@ -435,9 +435,18 @@ class StixCoreObject:
435
435
  }
436
436
  }
437
437
  }
438
- }
438
+ }
439
439
  ... on StixCyberObservable {
440
440
  observable_value
441
+ indicators {
442
+ edges {
443
+ node {
444
+ id
445
+ pattern
446
+ pattern_type
447
+ }
448
+ }
449
+ }
441
450
  }
442
451
  ... on AutonomousSystem {
443
452
  number
@@ -615,6 +624,12 @@ class StixCoreObject:
615
624
  ... on PhoneNumber {
616
625
  value
617
626
  }
627
+ ... on TrackingNumber {
628
+ value
629
+ }
630
+ ... on Credential {
631
+ value
632
+ }
618
633
  ... on PaymentCard {
619
634
  card_number
620
635
  expiration_date
@@ -1084,9 +1099,18 @@ class StixCoreObject:
1084
1099
  }
1085
1100
  }
1086
1101
  }
1087
- }
1102
+ }
1088
1103
  ... on StixCyberObservable {
1089
1104
  observable_value
1105
+ indicators {
1106
+ edges {
1107
+ node {
1108
+ id
1109
+ pattern
1110
+ pattern_type
1111
+ }
1112
+ }
1113
+ }
1090
1114
  }
1091
1115
  ... on AutonomousSystem {
1092
1116
  number
@@ -1453,13 +1477,21 @@ class StixCoreObject:
1453
1477
  return entity["importFiles"]
1454
1478
 
1455
1479
  def push_list_export(
1456
- self, entity_id, entity_type, file_name, data, list_filters="", mime_type=None
1480
+ self,
1481
+ entity_id,
1482
+ entity_type,
1483
+ file_name,
1484
+ file_markings,
1485
+ data,
1486
+ list_filters="",
1487
+ mime_type=None,
1457
1488
  ):
1458
1489
  query = """
1459
- mutation StixCoreObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $listFilters: String) {
1460
- stixCoreObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, listFilters: $listFilters)
1490
+ mutation StixCoreObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $file_markings: [String]!, $listFilters: String) {
1491
+ stixCoreObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, file_markings: $file_markings, listFilters: $listFilters)
1461
1492
  }
1462
1493
  """
1494
+
1463
1495
  if mime_type is None:
1464
1496
  file = self.file(file_name, data)
1465
1497
  else:
@@ -1470,6 +1502,7 @@ class StixCoreObject:
1470
1502
  "entity_id": entity_id,
1471
1503
  "entity_type": entity_type,
1472
1504
  "file": file,
1505
+ "file_markings": file_markings,
1473
1506
  "listFilters": list_filters,
1474
1507
  },
1475
1508
  )
@@ -282,6 +282,12 @@ class StixCyberObservable:
282
282
  ... on PhoneNumber {
283
283
  value
284
284
  }
285
+ ... on TrackingNumber {
286
+ value
287
+ }
288
+ ... on Credential {
289
+ value
290
+ }
285
291
  ... on PaymentCard {
286
292
  card_number
287
293
  expiration_date
@@ -576,6 +582,12 @@ class StixCyberObservable:
576
582
  ... on PhoneNumber {
577
583
  value
578
584
  }
585
+ ... on TrackingNumber {
586
+ value
587
+ }
588
+ ... on Credential {
589
+ value
590
+ }
579
591
  ... on PaymentCard {
580
592
  card_number
581
593
  expiration_date
@@ -857,6 +869,15 @@ class StixCyberObservable:
857
869
  type = "IPv6-Addr"
858
870
  elif type.lower() == "hostname" or type.lower() == "x-opencti-hostname":
859
871
  type = "Hostname"
872
+ elif type.lower() == "payment-card" or type.lower() == "x-opencti-payment-card":
873
+ type = "Payment-Card"
874
+ elif type.lower() == "credential" or type.lower() == "x-opencti-credential":
875
+ type = "Credential"
876
+ elif (
877
+ type.lower() == "tracking-number"
878
+ or type.lower() == "x-opencti-tracking-number"
879
+ ):
880
+ type = "Tracking-Number"
860
881
  elif (
861
882
  type.lower() == "cryptocurrency-wallet"
862
883
  or type.lower() == "x-opencti-cryptocurrency-wallet"
@@ -974,6 +995,8 @@ class StixCyberObservable:
974
995
  $UserAgent: UserAgentAddInput
975
996
  $BankAccount: BankAccountAddInput
976
997
  $PhoneNumber: PhoneNumberAddInput
998
+ $Credential: CredentialAddInput
999
+ $TrackingNumber: TrackingNumberAddInput
977
1000
  $PaymentCard: PaymentCardAddInput
978
1001
  $MediaContent: MediaContentAddInput
979
1002
  ) {
@@ -1016,6 +1039,8 @@ class StixCyberObservable:
1016
1039
  UserAgent: $UserAgent
1017
1040
  BankAccount: $BankAccount
1018
1041
  PhoneNumber: $PhoneNumber
1042
+ Credential: $Credential
1043
+ TrackingNumber: $TrackingNumber
1019
1044
  PaymentCard: $PaymentCard
1020
1045
  MediaContent: $MediaContent
1021
1046
  ) {
@@ -1508,15 +1533,6 @@ class StixCyberObservable:
1508
1533
  observable_data["value"] if "value" in observable_data else None
1509
1534
  ),
1510
1535
  }
1511
- elif (
1512
- type == "Cryptocurrency-Wallet"
1513
- or type == "X-OpenCTI-Cryptocurrency-Wallet"
1514
- ):
1515
- input_variables["CryptocurrencyWallet"] = {
1516
- "value": (
1517
- observable_data["value"] if "value" in observable_data else None
1518
- ),
1519
- }
1520
1536
  elif type == "Hostname":
1521
1537
  input_variables["Hostname"] = {
1522
1538
  "value": (
@@ -1588,8 +1604,50 @@ class StixCyberObservable:
1588
1604
  else None
1589
1605
  ),
1590
1606
  }
1607
+ elif type == "Payment-Card" or type.lower() == "x-opencti-payment-card":
1608
+ input_variables["PaymentCard"] = {
1609
+ "card_number": (
1610
+ observable_data["card_number"]
1611
+ if "card_number" in observable_data
1612
+ else None
1613
+ ),
1614
+ "expiration_date": (
1615
+ observable_data["expiration_date"]
1616
+ if "expiration_date" in observable_data
1617
+ else None
1618
+ ),
1619
+ "cvv": observable_data["cvv"] if "cvv" in observable_data else None,
1620
+ "holder_name": (
1621
+ observable_data["holder_name"]
1622
+ if "holder_name" in observable_data
1623
+ else None
1624
+ ),
1625
+ }
1626
+ elif (
1627
+ type == "Cryptocurrency-Wallet"
1628
+ or type.lower() == "x-opencti-cryptocurrency-wallet"
1629
+ ):
1630
+ input_variables["CryptocurrencyWallet"] = {
1631
+ "value": (
1632
+ observable_data["value"] if "value" in observable_data else None
1633
+ ),
1634
+ }
1635
+ elif type == "Credential" or type.lower() == "x-opencti-credential":
1636
+ input_variables["Credential"] = {
1637
+ "value": (
1638
+ observable_data["value"] if "value" in observable_data else None
1639
+ ),
1640
+ }
1641
+ elif (
1642
+ type == "Tracking-Number" or type.lower() == "x-opencti-tracking-number"
1643
+ ):
1644
+ input_variables["TrackingNumber"] = {
1645
+ "value": (
1646
+ observable_data["value"] if "value" in observable_data else None
1647
+ ),
1648
+ }
1591
1649
  result = self.opencti.query(query, input_variables)
1592
- if "payload_bin" in observable_data and "mime/type" in observable_data:
1650
+ if "payload_bin" in observable_data and "mime_type" in observable_data:
1593
1651
  self.add_file(
1594
1652
  id=result["data"]["stixCyberObservableAdd"]["id"],
1595
1653
  file_name=(
@@ -2258,11 +2316,30 @@ class StixCyberObservable:
2258
2316
  return False
2259
2317
 
2260
2318
  def push_list_export(
2261
- self, entity_id, entity_type, file_name, data, list_filters="", mime_type=None
2319
+ self,
2320
+ entity_id,
2321
+ entity_type,
2322
+ file_name,
2323
+ file_markings,
2324
+ data,
2325
+ list_filters="",
2326
+ mime_type=None,
2262
2327
  ):
2263
2328
  query = """
2264
- mutation StixCyberObservablesExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $listFilters: String) {
2265
- stixCyberObservablesExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, listFilters: $listFilters)
2329
+ mutation StixCyberObservablesExportPush(
2330
+ $entity_id: String,
2331
+ $entity_type: String!,
2332
+ $file: Upload!,
2333
+ $file_markings: [String]!,
2334
+ $listFilters: String
2335
+ ) {
2336
+ stixCyberObservablesExportPush(
2337
+ entity_id: $entity_id,
2338
+ entity_type: $entity_type,
2339
+ file: $file,
2340
+ file_markings: $file_markings,
2341
+ listFilters: $listFilters
2342
+ )
2266
2343
  }
2267
2344
  """
2268
2345
  if mime_type is None:
@@ -2275,6 +2352,7 @@ class StixCyberObservable:
2275
2352
  "entity_id": entity_id,
2276
2353
  "entity_type": entity_type,
2277
2354
  "file": file,
2355
+ "file_markings": file_markings,
2278
2356
  "listFilters": list_filters,
2279
2357
  },
2280
2358
  )
@@ -1325,11 +1325,18 @@ class StixDomainObject:
1325
1325
  return None
1326
1326
 
1327
1327
  def push_list_export(
1328
- self, entity_id, entity_type, file_name, data, list_filters="", mime_type=None
1328
+ self,
1329
+ entity_id,
1330
+ entity_type,
1331
+ file_name,
1332
+ file_markings,
1333
+ data,
1334
+ list_filters="",
1335
+ mime_type=None,
1329
1336
  ):
1330
1337
  query = """
1331
- mutation StixDomainObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $listFilters: String) {
1332
- stixDomainObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, listFilters: $listFilters)
1338
+ mutation StixDomainObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $file_markings: [String]!, $listFilters: String) {
1339
+ stixDomainObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, file_markings: $file_markings, listFilters: $listFilters)
1333
1340
  }
1334
1341
  """
1335
1342
  if mime_type is None:
@@ -1342,15 +1349,24 @@ class StixDomainObject:
1342
1349
  "entity_id": entity_id,
1343
1350
  "entity_type": entity_type,
1344
1351
  "file": file,
1352
+ "file_markings": file_markings,
1345
1353
  "listFilters": list_filters,
1346
1354
  },
1347
1355
  )
1348
1356
 
1349
- def push_entity_export(self, entity_id, file_name, data, mime_type=None):
1357
+ def push_entity_export(
1358
+ self, entity_id, file_name, data, file_markings, mime_type=None
1359
+ ):
1350
1360
  query = """
1351
- mutation StixDomainObjectEdit($id: ID!, $file: Upload!) {
1361
+ mutation StixDomainObjectEdit(
1362
+ $id: ID!, $file: Upload!,
1363
+ $file_markings: [String]!
1364
+ ) {
1352
1365
  stixDomainObjectEdit(id: $id) {
1353
- exportPush(file: $file)
1366
+ exportPush(
1367
+ file: $file,
1368
+ file_markings: $file_markings
1369
+ )
1354
1370
  }
1355
1371
  }
1356
1372
  """
@@ -1358,7 +1374,9 @@ class StixDomainObject:
1358
1374
  file = self.file(file_name, data)
1359
1375
  else:
1360
1376
  file = self.file(file_name, data, mime_type)
1361
- self.opencti.query(query, {"id": entity_id, "file": file})
1377
+ self.opencti.query(
1378
+ query, {"id": entity_id, "file": file, "file_markings": file_markings}
1379
+ )
1362
1380
 
1363
1381
  """
1364
1382
  Update the Identity author of a Stix-Domain-Object object (created_by)
@@ -1,3 +1,6 @@
1
+ import json
2
+
3
+
1
4
  class StixObjectOrStixRelationship:
2
5
  def __init__(self, opencti):
3
6
  self.opencti = opencti
@@ -329,11 +332,15 @@ class StixObjectOrStixRelationship:
329
332
  }
330
333
  ... on Case {
331
334
  name
332
- }
335
+ }
333
336
  ... on StixCyberObservable {
334
337
  observable_value
335
338
  }
336
339
  ... on StixCoreRelationship {
340
+ id
341
+ standard_id
342
+ entity_type
343
+ parent_types
337
344
  createdBy {
338
345
  ... on Identity {
339
346
  id
@@ -396,6 +403,10 @@ class StixObjectOrStixRelationship:
396
403
  stop_time
397
404
  }
398
405
  ... on StixSightingRelationship {
406
+ id
407
+ standard_id
408
+ entity_type
409
+ parent_types
399
410
  createdBy {
400
411
  ... on Identity {
401
412
  id
@@ -476,9 +487,9 @@ class StixObjectOrStixRelationship:
476
487
  )
477
488
  query = (
478
489
  """
479
- query StixObjectOrStixRelationship($id: String!) {
480
- stixObjectOrStixRelationship(id: $id) {
481
- """
490
+ query StixObjectOrStixRelationship($id: String!) {
491
+ stixObjectOrStixRelationship(id: $id) {
492
+ """
482
493
  + (
483
494
  custom_attributes
484
495
  if custom_attributes is not None
@@ -496,3 +507,51 @@ class StixObjectOrStixRelationship:
496
507
  else:
497
508
  self.opencti.app_logger.error("Missing parameters: id")
498
509
  return None
510
+
511
+ def list(self, **kwargs):
512
+ filters = kwargs.get("filters", None)
513
+ search = kwargs.get("search", None)
514
+ first = kwargs.get("first", 100)
515
+ after = kwargs.get("after", None)
516
+ with_pagination = kwargs.get("with_pagination", False)
517
+ custom_attributes = kwargs.get("customAttributes", None)
518
+
519
+ self.opencti.app_logger.info(
520
+ "Listing StixObjectOrStixRelationships with filters",
521
+ {"filters": json.dumps(filters)},
522
+ )
523
+ query = (
524
+ """
525
+ query StixObjectOrStixRelationships($filters: FilterGroup, $search: String, $first: Int, $after: ID) {
526
+ stixObjectOrStixRelationships(filters: $filters, search: $search, first: $first, after: $after) {
527
+ edges {
528
+ node {
529
+ """
530
+ + (custom_attributes if custom_attributes is not None else self.properties)
531
+ + """
532
+ }
533
+ }
534
+ pageInfo {
535
+ startCursor
536
+ endCursor
537
+ hasNextPage
538
+ hasPreviousPage
539
+ globalCount
540
+ }
541
+ }
542
+ }
543
+ """
544
+ )
545
+ result = self.opencti.query(
546
+ query,
547
+ {
548
+ "filters": filters,
549
+ "search": search,
550
+ "first": first,
551
+ "after": after,
552
+ },
553
+ )
554
+
555
+ return self.opencti.process_multiple(
556
+ result["data"]["stixObjectOrStixRelationships"], with_pagination
557
+ )
pycti/utils/constants.py CHANGED
@@ -42,6 +42,8 @@ class StixCyberObservableTypes(Enum):
42
42
  USER_AGENT = "User-Agent"
43
43
  BANK_ACCOUNT = "Bank-Account"
44
44
  PHONE_NUMBER = "Phone-Number"
45
+ CREDENTIAL = "Credential"
46
+ TRACKING_NUMBER = "Tracking-Number"
45
47
  PAYMENT_CARD = "Payment-Card"
46
48
  MEDIA_CONTENT = "Media-Content"
47
49
  SIMPLE_OBSERVABLE = "Simple-Observable"
@@ -66,6 +68,7 @@ class IdentityTypes(Enum):
66
68
 
67
69
  class ThreatActorTypes(Enum):
68
70
  THREAT_ACTOR_GROUP = "Threat-Actor-Group"
71
+ THREAT_ACTOR_INDIVIDUAL = "Threat-Actor-Individual"
69
72
 
70
73
  @classmethod
71
74
  def has_value(cls, value):
@@ -126,7 +129,6 @@ class MultipleRefRelationship(Enum):
126
129
  CHILD = "child"
127
130
  BODY_MULTIPART = "body-multipart"
128
131
  VALUES = "values"
129
- LINKED = "x_opencti_linked-to"
130
132
  SERVICE_DDL = "service-dll"
131
133
  INSTALLED_SOFTWARE = "installed-software"
132
134
  RELATION_ANALYSIS_SCO = "analysis-sco"
@@ -263,6 +265,73 @@ class CustomObservableText:
263
265
  pass
264
266
 
265
267
 
268
+ @CustomObservable(
269
+ "payment-card",
270
+ [
271
+ ("value", StringProperty(required=True)),
272
+ ("card_number", StringProperty(required=True)),
273
+ ("expiration_date", StringProperty(required=False)),
274
+ ("cvv", StringProperty(required=False)),
275
+ ("holder_name", StringProperty(required=False)),
276
+ ("spec_version", StringProperty(fixed="2.1")),
277
+ (
278
+ "object_marking_refs",
279
+ ListProperty(
280
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
281
+ ),
282
+ ),
283
+ ],
284
+ ["card_number"],
285
+ )
286
+ class CustomObservablePaymentCard:
287
+ """Payment card observable."""
288
+
289
+ pass
290
+
291
+
292
+ @CustomObservable(
293
+ "bank-account",
294
+ [
295
+ ("value", StringProperty(required=True)),
296
+ ("iban", StringProperty(required=True)),
297
+ ("bic", StringProperty(required=False)),
298
+ ("account_number", StringProperty(required=False)),
299
+ ("spec_version", StringProperty(fixed="2.1")),
300
+ (
301
+ "object_marking_refs",
302
+ ListProperty(
303
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
304
+ ),
305
+ ),
306
+ ],
307
+ ["iban"],
308
+ )
309
+ class CustomObservableBankAccount:
310
+ """Bank Account observable."""
311
+
312
+ pass
313
+
314
+
315
+ @CustomObservable(
316
+ "credential",
317
+ [
318
+ ("value", StringProperty(required=True)),
319
+ ("spec_version", StringProperty(fixed="2.1")),
320
+ (
321
+ "object_marking_refs",
322
+ ListProperty(
323
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
324
+ ),
325
+ ),
326
+ ],
327
+ ["value"],
328
+ )
329
+ class CustomObservableCredential:
330
+ """Credential observable."""
331
+
332
+ pass
333
+
334
+
266
335
  @CustomObservable(
267
336
  "cryptocurrency-wallet",
268
337
  [
@@ -283,6 +352,46 @@ class CustomObservableCryptocurrencyWallet:
283
352
  pass
284
353
 
285
354
 
355
+ @CustomObservable(
356
+ "phone-number",
357
+ [
358
+ ("value", StringProperty(required=True)),
359
+ ("spec_version", StringProperty(fixed="2.1")),
360
+ (
361
+ "object_marking_refs",
362
+ ListProperty(
363
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
364
+ ),
365
+ ),
366
+ ],
367
+ ["value"],
368
+ )
369
+ class CustomObservablePhoneNumber:
370
+ """Phone number observable."""
371
+
372
+ pass
373
+
374
+
375
+ @CustomObservable(
376
+ "tracking-number",
377
+ [
378
+ ("value", StringProperty(required=True)),
379
+ ("spec_version", StringProperty(fixed="2.1")),
380
+ (
381
+ "object_marking_refs",
382
+ ListProperty(
383
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
384
+ ),
385
+ ),
386
+ ],
387
+ ["value"],
388
+ )
389
+ class CustomObservableTrackingNumber:
390
+ """Tracking number observable."""
391
+
392
+ pass
393
+
394
+
286
395
  @CustomObservable(
287
396
  "user-agent",
288
397
  [