Skip to content
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

Load schemas dynamically #1135

Merged
merged 39 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f6df03c
Load observables dynamically
udgover Sep 14, 2024
76e4a38
Ruff format
udgover Sep 14, 2024
12a0b97
Remove end of file observables imports
udgover Sep 16, 2024
9ed714b
Add dependencies to poetry / pyproject
udgover Sep 16, 2024
b15222e
Add load_observables function and handle guess type
udgover Sep 16, 2024
888ede4
Rename GenericObservable with Generic in tests
udgover Sep 16, 2024
2eb59c4
Remove validators and replace with static method is_valid
udgover Sep 16, 2024
1c5e3a9
Implement validators for previously handled observables
udgover Sep 16, 2024
df542ff
Ruff format
udgover Sep 16, 2024
bb0faeb
Add entity loader
udgover Sep 17, 2024
b03b7a2
Move entities under entities folder
udgover Sep 17, 2024
16e6d0d
Use enum.Enum for ObservableType definition
udgover Sep 17, 2024
ce32276
Add guess ObservableType if not already part of enum
udgover Sep 17, 2024
c6f3f8d
Remove unused declarations
udgover Sep 17, 2024
2a9fc2b
Remove Field(discriminator=type) which is only relevant when subclass…
udgover Sep 17, 2024
71ecd7e
Update tests to reflect entities changes
udgover Sep 17, 2024
9a09504
Ruff format
udgover Sep 17, 2024
93a501d
Add __init__.py in entities/observables private folder
udgover Sep 17, 2024
002c3b1
Add indicators and update type literal for all objects
udgover Sep 17, 2024
a2ea66c
Ruff format
udgover Sep 17, 2024
e209466
Update analytics to import relevant entities
udgover Sep 17, 2024
bf83486
Update analytics to correctly import schemas
udgover Sep 17, 2024
120d299
Update feeds to correctly import schemas
udgover Sep 17, 2024
59260a4
global naming change
tomchop Sep 18, 2024
d99a42e
Formatting
tomchop Sep 18, 2024
bc8c401
Ruff formatting
tomchop Sep 18, 2024
580acfd
Register entities, observables and indicators object accordingly to s…
udgover Sep 18, 2024
8afe499
Revert tests to main version
udgover Sep 18, 2024
0a6c3b1
Revert analytics to main version
udgover Sep 18, 2024
0c074dc
Replace ssdeep.SsdeepHash with ssdeep.Ssdeep
udgover Sep 18, 2024
913f8e3
Revert feeds to main version
udgover Sep 18, 2024
fff01fd
Replace ssdeep.SsdeepHash with ssdeep.Ssdeep
udgover Sep 18, 2024
1c221ba
Restore apiv2 to main version
udgover Sep 18, 2024
1169561
Revert dfiq to main version
udgover Sep 18, 2024
f4ccb2c
create objects from indicator. Replace indicator variable with regex
udgover Sep 18, 2024
decbf6f
Ruff format
udgover Sep 18, 2024
0dcd89b
Remove path_validator function and return regex matches in is_valid
udgover Sep 18, 2024
d920b00
Update comments to detail where types are populated
udgover Sep 19, 2024
d809ec2
Give more details about conversion from filename to entity name
udgover Sep 19, 2024
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
25 changes: 25 additions & 0 deletions core/schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import importlib
import inspect
from pathlib import Path

import aenum
udgover marked this conversation as resolved.
Show resolved Hide resolved

from core.schemas import observable

print("Registering observable types")
udgover marked this conversation as resolved.
Show resolved Hide resolved

for observable_file in Path(__file__).parent.glob("observables/**/*.py"):
udgover marked this conversation as resolved.
Show resolved Hide resolved
if observable_file.stem == "__init__":
continue
print(f"Registering observable type {observable_file.stem}")
if observable_file.parent.stem == "observables":
module_name = f"core.schemas.observables.{observable_file.stem}"
elif observable_file.parent.stem == "private":
module_name = f"core.schemas.observables.private.{observable_file.stem}"
aenum.extend_enum(
observable.ObservableType, observable_file.stem, observable_file.stem
)
module = importlib.import_module(module_name)
for _, obj in inspect.getmembers(module, inspect.isclass):
if issubclass(obj, observable.Observable):
observable.TYPE_MAPPING[observable_file.stem] = obj
114 changes: 48 additions & 66 deletions core/schemas/observable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import datetime
import re
from enum import Enum

# from enum import Enum, EnumMeta
from typing import ClassVar, Literal

# Data Schema
# Dynamically register all observable types
import aenum
import validators
from pydantic import Field, computed_field

Expand All @@ -13,39 +17,11 @@
from core.schemas.model import YetiTagModel


# Data Schema
class ObservableType(str, Enum):
asn = "asn"
bic = "bic"
certificate = "certificate"
cidr = "cidr"
command_line = "command_line"
docker_image = "docker_image"
email = "email"
file = "file"
guess = "guess"
hostname = "hostname"
iban = "iban"
imphash = "imphash"
ipv4 = "ipv4"
ipv6 = "ipv6"
ja3 = "ja3"
jarm = "jarm"
mac_address = "mac_address"
md5 = "md5"
generic = "generic"
path = "path"
registry_key = "registry_key"
sha1 = "sha1"
sha256 = "sha256"
ssdeep = "ssdeep"
tlsh = "tlsh"
url = "url"
user_agent = "user_agent"
user_account = "user_account"
wallet = "wallet"
mutex = "mutex"
named_pipe = "named_pipe"
class ObservableType(str, aenum.Enum):
pass


TYPE_MAPPING = {}


class Observable(YetiTagModel, database_arango.ArangoYetiConnector):
Expand Down Expand Up @@ -151,6 +127,12 @@ def delete_context(
return self.save()


TYPE_MAPPING.update({"observable": Observable, "observables": Observable})


TYPE_VALIDATOR_MAP = {}


TYPE_VALIDATOR_MAP = {
ObservableType.ipv4: validators.ipv4,
ObservableType.ipv6: validators.ipv6,
Expand Down Expand Up @@ -202,35 +184,35 @@ def find_type(value: str) -> ObservableType | None:
# Import all observable types, as these register themselves in the TYPE_MAPPING
# disable: pylint=wrong-import-position
# noqa: F401, E402
from core.schemas.observables import (
asn,
bic,
certificate,
cidr,
command_line,
docker_image,
email,
file,
generic_observable,
hostname,
iban,
imphash,
ipv4,
ipv6,
ja3,
jarm,
mac_address,
md5,
mutex,
named_pipe,
path,
registry_key,
sha1,
sha256,
ssdeep,
tlsh,
url,
user_account,
user_agent,
wallet,
)
# from core.schemas.observables import (
# asn,
# bic,
# certificate,
# cidr,
# command_line,
# docker_image,
# email,
# file,
# generic_observable,
# hostname,
# iban,
# imphash,
# ipv4,
# ipv6,
# ja3,
# jarm,
# mac_address,
# md5,
# mutex,
# named_pipe,
# path,
# registry_key,
# sha1,
# sha256,
# ssdeep,
# tlsh,
# url,
# user_account,
# user_agent,
# wallet,
# )
1 change: 1 addition & 0 deletions core/schemas/observables/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

7 changes: 1 addition & 6 deletions core/schemas/observables/asn.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
from typing import Literal

from core.schemas import observable


class ASN(observable.Observable):
type: Literal[observable.ObservableType.asn] = observable.ObservableType.asn
type: observable.ObservableType = observable.ObservableType.asn
country: str | None = None
description: str | None = None


observable.TYPE_MAPPING[observable.ObservableType.asn] = ASN
7 changes: 1 addition & 6 deletions core/schemas/observables/bic.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class BIC(observable.Observable):
type: Literal[observable.ObservableType.bic] = observable.ObservableType.bic


observable.TYPE_MAPPING[observable.ObservableType.bic] = BIC
type: observable.ObservableType = observable.ObservableType.bic
8 changes: 1 addition & 7 deletions core/schemas/observables/certificate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import datetime
import hashlib
from typing import Literal

from pydantic import Field

Expand All @@ -22,9 +21,7 @@ class Certificate(observable.Observable):
fingerprint: the certificate fingerprint.
"""

type: Literal[observable.ObservableType.certificate] = (
observable.ObservableType.certificate
)
type: observable.ObservableType = observable.ObservableType.certificate
last_seen: datetime.datetime = Field(default_factory=now)
first_seen: datetime.datetime = Field(default_factory=now)
issuer: str | None = None
Expand All @@ -38,6 +35,3 @@ class Certificate(observable.Observable):
def from_data(cls, data: bytes):
hash_256 = hashlib.sha256(data).hexdigest()
return cls(value=f"CERT:{hash_256}")


observable.TYPE_MAPPING[observable.ObservableType.certificate] = Certificate
7 changes: 1 addition & 6 deletions core/schemas/observables/cidr.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class CIDR(observable.Observable):
type: Literal[observable.ObservableType.cidr] = observable.ObservableType.cidr


observable.TYPE_MAPPING[observable.ObservableType.cidr] = CIDR
type: observable.ObservableType = observable.ObservableType.cidr
9 changes: 1 addition & 8 deletions core/schemas/observables/command_line.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
from typing import Literal

from core.schemas import observable


class CommandLine(observable.Observable):
type: Literal[observable.ObservableType.command_line] = (
observable.ObservableType.command_line
)


observable.TYPE_MAPPING[observable.ObservableType.command_line] = CommandLine
type: observable.ObservableType = observable.ObservableType.command_line
9 changes: 1 addition & 8 deletions core/schemas/observables/docker_image.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
from typing import Literal

from core.schemas import observable


class DockerImage(observable.Observable):
type: Literal[observable.ObservableType.docker_image] = (
observable.ObservableType.docker_image
)


observable.TYPE_MAPPING[observable.ObservableType.docker_image] = DockerImage
type: observable.ObservableType = observable.ObservableType.docker_image
7 changes: 1 addition & 6 deletions core/schemas/observables/email.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class Email(observable.Observable):
type: Literal[observable.ObservableType.email] = observable.ObservableType.email


observable.TYPE_MAPPING[observable.ObservableType.email] = Email
type: observable.ObservableType = observable.ObservableType.email
7 changes: 1 addition & 6 deletions core/schemas/observables/file.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Literal

from core.schemas import observable


Expand All @@ -10,13 +8,10 @@ class File(observable.Observable):
Value should to be in the form FILE:<HASH>.
"""

type: Literal[observable.ObservableType.file] = observable.ObservableType.file
type: observable.ObservableType = observable.ObservableType.file
name: str | None = None
size: int | None = None
sha256: str | None = None
md5: str | None = None
sha1: str | None = None
mime_type: str | None = None


observable.TYPE_MAPPING[observable.ObservableType.file] = File
9 changes: 9 additions & 0 deletions core/schemas/observables/generic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import Literal

from core.schemas import observable


class Generic(observable.Observable):
"""Use this type of Observable for any type of observable that doesn't fit into any other category."""

type: observable.ObservableType = observable.ObservableType.generic
12 changes: 0 additions & 12 deletions core/schemas/observables/generic_observable.py

This file was deleted.

9 changes: 1 addition & 8 deletions core/schemas/observables/hostname.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
from typing import Literal

from core.schemas import observable


class Hostname(observable.Observable):
type: Literal[observable.ObservableType.hostname] = (
observable.ObservableType.hostname
)


observable.TYPE_MAPPING[observable.ObservableType.hostname] = Hostname
type: observable.ObservableType = observable.ObservableType.hostname
7 changes: 1 addition & 6 deletions core/schemas/observables/iban.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class IBAN(observable.Observable):
type: Literal[observable.ObservableType.iban] = observable.ObservableType.iban


observable.TYPE_MAPPING[observable.ObservableType.iban] = IBAN
type: observable.ObservableType = observable.ObservableType.iban
7 changes: 1 addition & 6 deletions core/schemas/observables/imphash.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class Imphash(observable.Observable):
type: Literal[observable.ObservableType.imphash] = observable.ObservableType.imphash


observable.TYPE_MAPPING[observable.ObservableType.imphash] = Imphash
type: observable.ObservableType = observable.ObservableType.imphash
7 changes: 1 addition & 6 deletions core/schemas/observables/ipv4.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class IPv4(observable.Observable):
type: Literal[observable.ObservableType.ipv4] = observable.ObservableType.ipv4


observable.TYPE_MAPPING[observable.ObservableType.ipv4] = IPv4
type: observable.ObservableType = observable.ObservableType.ipv4
7 changes: 1 addition & 6 deletions core/schemas/observables/ipv6.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class IPv6(observable.Observable):
type: Literal[observable.ObservableType.ipv6] = observable.ObservableType.ipv6


observable.TYPE_MAPPING[observable.ObservableType.ipv6] = IPv6
type: observable.ObservableType = observable.ObservableType.ipv6
7 changes: 1 addition & 6 deletions core/schemas/observables/ja3.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from typing import Literal

from core.schemas import observable


class JA3(observable.Observable):
type: Literal[observable.ObservableType.ja3] = observable.ObservableType.ja3


observable.TYPE_MAPPING[observable.ObservableType.ja3] = JA3
type: observable.ObservableType = observable.ObservableType.ja3
Loading
Loading