mmisp-lib 0.1.11__tar.gz → 0.2.0__tar.gz

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.
Files changed (91) hide show
  1. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/PKG-INFO +3 -1
  2. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/pyproject.toml +10 -1
  3. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/attributes.py +1 -1
  4. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/events.py +8 -7
  5. mmisp_lib-0.2.0/src/mmisp/api_schemas/logs.py +9 -0
  6. mmisp_lib-0.2.0/src/mmisp/api_schemas/responses/check_graph_response.py +172 -0
  7. {mmisp_lib-0.1.11/src/mmisp/api_schemas → mmisp_lib-0.2.0/src/mmisp/api_schemas/responses}/standard_status_response.py +6 -3
  8. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/sharing_groups.py +1 -1
  9. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/warninglists.py +1 -1
  10. mmisp_lib-0.2.0/src/mmisp/api_schemas/workflows.py +30 -0
  11. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/config.py +3 -1
  12. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/database.py +9 -6
  13. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/mixins.py +2 -2
  14. mmisp_lib-0.2.0/src/mmisp/db/models/admin_setting.py +17 -0
  15. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/attribute.py +4 -4
  16. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/auth_key.py +1 -1
  17. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/event.py +2 -2
  18. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/galaxy.py +1 -1
  19. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/galaxy_cluster.py +1 -1
  20. mmisp_lib-0.2.0/src/mmisp/db/models/log.py +33 -0
  21. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/object.py +1 -1
  22. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/organisation.py +1 -1
  23. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/role.py +1 -1
  24. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/sharing_group.py +1 -1
  25. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/sighting.py +1 -1
  26. mmisp_lib-0.2.0/src/mmisp/db/models/workflow.py +38 -0
  27. mmisp_lib-0.2.0/src/mmisp/db/models/workflow_blueprint.py +28 -0
  28. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/mypy.py +1 -1
  29. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/print_changes.py +1 -2
  30. mmisp_lib-0.2.0/src/mmisp/lib/actions.py +11 -0
  31. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/lib/attributes.py +1 -1
  32. mmisp_lib-0.2.0/src/mmisp/lib/logging.py +58 -0
  33. mmisp_lib-0.2.0/src/mmisp/lib/py.typed +0 -0
  34. mmisp_lib-0.2.0/src/mmisp/lib/uuid.py +10 -0
  35. mmisp_lib-0.2.0/src/mmisp/plugins/py.typed +0 -0
  36. mmisp_lib-0.2.0/src/mmisp/util/__init__.py +0 -0
  37. mmisp_lib-0.2.0/src/mmisp/util/crypto.py +11 -0
  38. mmisp_lib-0.2.0/src/mmisp/util/models.py +14 -0
  39. mmisp_lib-0.2.0/src/mmisp/util/partial.py +76 -0
  40. mmisp_lib-0.2.0/src/mmisp/util/py.typed +0 -0
  41. mmisp_lib-0.2.0/src/mmisp/util/uuid.py +10 -0
  42. mmisp_lib-0.2.0/src/mmisp/workflows/__init__.py +0 -0
  43. mmisp_lib-0.2.0/src/mmisp/workflows/execution.py +238 -0
  44. mmisp_lib-0.2.0/src/mmisp/workflows/fastapi.py +162 -0
  45. mmisp_lib-0.2.0/src/mmisp/workflows/graph.py +803 -0
  46. mmisp_lib-0.2.0/src/mmisp/workflows/input.py +463 -0
  47. mmisp_lib-0.2.0/src/mmisp/workflows/legacy.py +596 -0
  48. mmisp_lib-0.2.0/src/mmisp/workflows/misp_core_format.py +163 -0
  49. mmisp_lib-0.2.0/src/mmisp/workflows/modules.py +1359 -0
  50. mmisp_lib-0.2.0/src/mmisp/workflows/py.typed +0 -0
  51. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp_lib.egg-info/PKG-INFO +3 -1
  52. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp_lib.egg-info/SOURCES.txt +29 -3
  53. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp_lib.egg-info/requires.txt +2 -0
  54. mmisp_lib-0.1.11/tests/test_dummy.py +0 -2
  55. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/LICENSE +0 -0
  56. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/README.md +0 -0
  57. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/setup.cfg +0 -0
  58. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/__init__.py +0 -0
  59. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/auth_keys.py +0 -0
  60. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/authentication.py +0 -0
  61. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/common.py +0 -0
  62. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/feeds.py +0 -0
  63. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/galaxies.py +0 -0
  64. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/noticelists.py +0 -0
  65. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/objects.py +0 -0
  66. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/organisations.py +0 -0
  67. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/py.typed +0 -0
  68. {mmisp_lib-0.1.11/src/mmisp/db → mmisp_lib-0.2.0/src/mmisp/api_schemas/responses}/__init__.py +0 -0
  69. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/roles.py +0 -0
  70. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/sightings.py +0 -0
  71. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/tags.py +0 -0
  72. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/taxonomies.py +0 -0
  73. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/user_settings.py +0 -0
  74. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/api_schemas/users.py +0 -0
  75. {mmisp_lib-0.1.11/src/mmisp/db/models → mmisp_lib-0.2.0/src/mmisp/db}/__init__.py +0 -0
  76. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/all_models.py +0 -0
  77. {mmisp_lib-0.1.11/src/mmisp/lib → mmisp_lib-0.2.0/src/mmisp/db/models}/__init__.py +0 -0
  78. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/feed.py +0 -0
  79. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/identity_provider.py +0 -0
  80. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/noticelist.py +0 -0
  81. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/server.py +0 -0
  82. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/tag.py +0 -0
  83. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/taxonomy.py +0 -0
  84. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/user.py +0 -0
  85. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/user_setting.py +0 -0
  86. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/models/warninglist.py +0 -0
  87. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/db/py.typed +0 -0
  88. /mmisp_lib-0.1.11/src/mmisp/lib/py.typed → /mmisp_lib-0.2.0/src/mmisp/lib/__init__.py +0 -0
  89. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp/lib/permissions.py +0 -0
  90. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp_lib.egg-info/dependency_links.txt +0 -0
  91. {mmisp_lib-0.1.11 → mmisp_lib-0.2.0}/src/mmisp_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mmisp-lib
3
- Version: 0.1.11
3
+ Version: 0.2.0
4
4
  Requires-Python: >=3.11.0
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -19,11 +19,13 @@ Requires-Dist: argon2-cffi==23.1.0
19
19
  Requires-Dist: bcrypt==4.1.2
20
20
  Requires-Dist: nanoid==2.0.0
21
21
  Requires-Dist: cryptography==42.0.5
22
+ Requires-Dist: jinja2~=3.1.4
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: ruff>=0.3.7; extra == "dev"
24
25
  Requires-Dist: mypy==1.8.0; extra == "dev"
25
26
  Requires-Dist: pre-commit==3.6.0; extra == "dev"
26
27
  Requires-Dist: pytest==8.0.0; extra == "dev"
28
+ Requires-Dist: pytest-test-groups; extra == "dev"
27
29
  Requires-Dist: pytest-asyncio==0.23.5.post1; extra == "dev"
28
30
  Requires-Dist: pytest-cov==4.1.0; extra == "dev"
29
31
  Requires-Dist: respx==0.20.2; extra == "dev"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mmisp-lib"
3
- version = "0.1.11"
3
+ version = "0.2.0"
4
4
  description = ""
5
5
  authors = []
6
6
  readme = "README.md"
@@ -23,6 +23,7 @@ dependencies = [
23
23
  "bcrypt==4.1.2",
24
24
  "nanoid==2.0.0",
25
25
  "cryptography==42.0.5",
26
+ "jinja2~=3.1.4",
26
27
  ]
27
28
 
28
29
 
@@ -32,6 +33,7 @@ dev = [
32
33
  "mypy==1.8.0",
33
34
  "pre-commit==3.6.0",
34
35
  "pytest==8.0.0",
36
+ "pytest-test-groups",
35
37
  "pytest-asyncio==0.23.5.post1",
36
38
  "pytest-cov==4.1.0",
37
39
  "respx==0.20.2",
@@ -42,12 +44,15 @@ dev = [
42
44
  "mmisp.lib" = ["py.typed"]
43
45
  "mmisp.api_schemas" = ["py.typed"]
44
46
  "mmisp.db" = ["py.typed"]
47
+ "mmisp.plugins" = ["py.typed"]
48
+ "mmisp.util" = ["py.typed"]
45
49
 
46
50
  [tool.ruff]
47
51
  fix = true
48
52
  line-length = 120
49
53
  required-version = ">=0.3.7"
50
54
  src = ["src"]
55
+ unsafe-fixes = true
51
56
 
52
57
  [tool.ruff.lint]
53
58
  select = ["E", "F", "W", "I", "ICN", "ANN"]
@@ -55,3 +60,7 @@ ignore = ["ANN002", "ANN003", "ANN401"]
55
60
 
56
61
  [tool.coverage.run]
57
62
  concurrency = ["greenlet", "thread"]
63
+ include = [
64
+ "/usr/local/lib/python3.11/site-packages/mmisp/*",
65
+ "/home/mmisp/.local/lib/python3.11/site-packages/mmisp/*"
66
+ ]
@@ -384,7 +384,7 @@ class AddAttributeResponse(BaseModel):
384
384
 
385
385
 
386
386
  class AddAttributeBody(BaseModel):
387
- type: literal_valid_attribute_types
387
+ type: literal_valid_attribute_types # type:ignore[valid-type]
388
388
  value: str | None = None
389
389
  value1: str | None = None
390
390
  value2: str | None = None
@@ -330,11 +330,19 @@ class PublishEventResponse(BaseModel):
330
330
  orm_mode = True
331
331
 
332
332
 
333
+ class GetAllEventsEventTagTag(BaseModel):
334
+ id: str
335
+ name: str
336
+ colour: str
337
+ is_galaxy: bool
338
+
339
+
333
340
  class IndexEventsEventTag(BaseModel):
334
341
  id: str
335
342
  event_id: str
336
343
  tag_id: str
337
344
  local: bool
345
+ Tag: GetAllEventsEventTagTag
338
346
 
339
347
 
340
348
  class IndexEventsAttributes(BaseModel):
@@ -403,13 +411,6 @@ class ObjectEventResponse(BaseModel):
403
411
  orgc_id: str | None = None
404
412
 
405
413
 
406
- class GetAllEventsEventTagTag(BaseModel):
407
- id: str
408
- name: str
409
- colour: str
410
- is_galaxy: bool
411
-
412
-
413
414
  class GetAllEventsEventTag(BaseModel):
414
415
  id: str
415
416
  event_id: str
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class LogsRequest(BaseModel):
5
+ model: str | None = None
6
+ action: str | None = None
7
+ model_id: int | None = None
8
+ page: int = 1
9
+ limit: int = 50
@@ -0,0 +1,172 @@
1
+ from typing import Dict, List
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class IsAcyclicInfo(BaseModel):
7
+ nodeID1: int
8
+ nodeID2: int
9
+ cycle: str = "Cycle"
10
+
11
+
12
+ class IsAcyclic(BaseModel):
13
+ """
14
+ Represents the whether graph is acyclic and details of the first detected cycle.
15
+
16
+ - **is_acyclic**: False if the graph contains at least one cycle.
17
+ - **cycles**: A list of entries, each containing two node IDs and a "Cycle" string.
18
+ Conbined they result in the cycle.
19
+
20
+ Example:
21
+ ```json
22
+ "is_acyclic": {
23
+ "is_acyclic": false,
24
+ "cycles": [
25
+ [
26
+ 4,
27
+ 3,
28
+ "Cycle"
29
+ ],
30
+ [
31
+ 3,
32
+ 4,
33
+ "Cycle"
34
+ ]
35
+ ]
36
+ }
37
+ ```
38
+ """
39
+
40
+ is_acyclic: bool
41
+ cycles: List[IsAcyclicInfo]
42
+
43
+
44
+ class MultipleOutputConnection(BaseModel):
45
+ """
46
+ Represents the status and details of nodes with illegal multiple output connections in a graph.
47
+
48
+ - **has_multiple_output_connection**: True if at least one node has multiple output
49
+ connections that are not allowed.
50
+ For example, the 'Concurrent Task' node can have multiple output connections while the value here is `False`.
51
+ - **edges**: A dictionary where the key is the ID of a node with multiple illegal connections,
52
+ and the value is a list of node IDs to which these illegal connections are made.
53
+
54
+ Example:
55
+ ```json
56
+ "multiple_output_connection": {
57
+ "has_multiple_output_connection": true,
58
+ "edges": {
59
+ "1": [
60
+ 5,
61
+ 3
62
+ ]
63
+ }
64
+ }
65
+ ```
66
+ """
67
+
68
+ has_multiple_output_connection: bool
69
+ edges: Dict[int, List[int]]
70
+
71
+
72
+ class PathWarningsInfo(BaseModel):
73
+ source_id: int # is a string in legacy misp
74
+ next_node_id: int # is a string in legacy misp
75
+ warning: str
76
+ blocking: bool
77
+ module_name: str
78
+ module_id: int
79
+
80
+
81
+ class PathWarnings(BaseModel):
82
+ """
83
+ Represents warnings for paths in a graph.
84
+
85
+ - **has_path_warnings**: True if the graph contains at least one warning.
86
+ - **edges**: A list containing all connections which are flagged as warnings.
87
+
88
+ Example:
89
+ ```json
90
+ "path_warnings": {
91
+ "has_path_warnings": true,
92
+ "edges": [
93
+ [
94
+ 5,
95
+ 2,
96
+ "This path leads to a blocking node from a non-blocking context",
97
+ true,
98
+ "stop-execution",
99
+ 2
100
+ ]
101
+ ]
102
+ }
103
+ ```
104
+ """
105
+
106
+ has_path_warnings: bool
107
+ edges: List[PathWarningsInfo]
108
+
109
+
110
+ class MiscellaneousGraphValidationError(BaseModel):
111
+ """
112
+ Validation errors that do no fit in the legacy MISP json response format for Graph Validation will be returned as
113
+ errors in this format.
114
+ """
115
+
116
+ error_id: str
117
+ """
118
+ The type of error that this instance represents.
119
+ """
120
+
121
+ message: str
122
+ """
123
+ The error message of this instance.
124
+ """
125
+
126
+
127
+ class CheckGraphResponse(BaseModel):
128
+ """
129
+ Response schema from the API for checking a graph.
130
+
131
+ - **is_acyclic**: Indicates whether the graph is acyclic and provides information
132
+ about the first detected cycle, if any.
133
+ - **multiple_output_connection**: Indicates whether the graph has illegal multiple output connections,
134
+ detailing the nodes involved.
135
+ - **path_warnings**: Records warnings if a path leads to a blocking node from a
136
+ 'Concurrent Task' node, providing relevant details. Not used in Modern MISP, and will be returned empty.
137
+ - **unsupported_modules"" List of the modules (identified with their graph_id) that are currently unsupported in
138
+ Modern MISP (not yet implemented) causing the workflow to be invalid.
139
+ - **misc_errors** Other miscellaneous errors indicating that the workflow graph is broken or etc. (edges registered
140
+ at ports outside the valid range, inconsistencies between the incoming and outgoing adjacency lists etc.)
141
+
142
+ Example JSON structure:
143
+ ```json
144
+ {
145
+ "is_acyclic": {
146
+ "is_acyclic": false,
147
+ "cycles": [
148
+ [4, 3, "Cycle"],
149
+ [3, 4, "Cycle"]
150
+ ]
151
+ },
152
+ "multiple_output_connection": {
153
+ "has_multiple_output_connection": true,
154
+ "edges": {
155
+ "1": [5, 3]
156
+ }
157
+ },
158
+ "path_warnings": {
159
+ "has_path_warnings": true,
160
+ "edges": [
161
+ [5, 2, "This path leads to a blocking node from a non-blocking context", true, "stop-execution", 2]
162
+ ]
163
+ }
164
+ }
165
+ ```
166
+ """
167
+
168
+ is_acyclic: IsAcyclic
169
+ multiple_output_connection: MultipleOutputConnection
170
+ path_warnings: PathWarnings
171
+ unsupported_modules: List[int]
172
+ misc_errors: List[MiscellaneousGraphValidationError]
@@ -1,13 +1,16 @@
1
1
  from pydantic import BaseModel
2
2
 
3
3
 
4
- class StandardStatusResponse(BaseModel):
5
- saved: bool
6
- success: bool
4
+ class StandartResponse(BaseModel):
7
5
  name: str
8
6
  message: str
9
7
  url: str
10
8
 
11
9
 
10
+ class StandardStatusResponse(StandartResponse):
11
+ saved: bool
12
+ success: bool
13
+
14
+
12
15
  class StandardStatusIdentifiedResponse(StandardStatusResponse):
13
16
  id: str
@@ -3,7 +3,7 @@ from datetime import datetime
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
  from mmisp.api_schemas.organisations import Organisation
6
- from mmisp.api_schemas.standard_status_response import StandardStatusResponse
6
+ from mmisp.api_schemas.responses.standard_status_response import StandardStatusResponse
7
7
 
8
8
 
9
9
  class SharingGroup(BaseModel):
@@ -115,7 +115,7 @@ class CreateWarninglistBody(BaseModel):
115
115
  enabled: bool
116
116
  default: bool
117
117
  category: WarninglistCategory
118
- valid_attributes: list[literal_valid_attribute_types]
118
+ valid_attributes: list[literal_valid_attribute_types] # type:ignore[valid-type]
119
119
  values: str = Field(min_length=1, max_length=65535)
120
120
 
121
121
  class Config:
@@ -0,0 +1,30 @@
1
+ from typing import Any, Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class GraphRequest(BaseModel):
7
+ workflow_graph: dict
8
+
9
+
10
+ class WorkflowEditRequest(BaseModel):
11
+ # workflow_name: str
12
+ # workflow_description: str
13
+ # workflow_graph: dict
14
+ data: Any
15
+
16
+
17
+ class ModuleIndexRequest(BaseModel):
18
+ enabled: Optional[bool] = None
19
+ type: str = "action"
20
+ actiontype: str = "all"
21
+ limit: int = 50
22
+ page: int = 1
23
+
24
+
25
+ class TriggerRequest(BaseModel):
26
+ scope: Optional[str] = None
27
+ enabled: Optional[bool] = None
28
+ blocking: Optional[bool] = None
29
+ limit: int = 50
30
+ page: int = 1
@@ -13,4 +13,6 @@ class DatabaseConfig:
13
13
  load_dotenv(getenv("ENV_FILE", ".env"))
14
14
 
15
15
 
16
- config: DatabaseConfig = DatabaseConfig(DATABASE_URL=getenv("DATABASE_URL", ""), DEBUG=bool(getenv("DEBUG", False)))
16
+ config: DatabaseConfig = DatabaseConfig(
17
+ DATABASE_URL=getenv("DATABASE_URL", "sqlite:///dev/null"), DEBUG=bool(getenv("DEBUG", False))
18
+ )
@@ -16,18 +16,18 @@ Base = declarative_base()
16
16
 
17
17
 
18
18
  class DatabaseSessionManager:
19
- def __init__(self: Self) -> None:
19
+ def __init__(self: Self, db_url: str = config.DATABASE_URL) -> None:
20
20
  self._engine: AsyncEngine | None = None
21
21
  self._sessionmaker: sessionmaker | None = None
22
22
 
23
- self._url = make_url(config.DATABASE_URL)
23
+ self._url = make_url(db_url)
24
24
 
25
25
  def init(self: Self) -> None:
26
26
  if config.DEBUG:
27
27
  self._engine = create_async_engine(self._url, echo=True)
28
28
  else:
29
29
  self._engine = create_async_engine(self._url)
30
- self._sessionmaker = sessionmaker(
30
+ self._sessionmaker = sessionmaker( # type:ignore[call-overload]
31
31
  autocommit=False, expire_on_commit=False, bind=self._engine, class_=AsyncSession
32
32
  )
33
33
 
@@ -68,16 +68,19 @@ class DatabaseSessionManager:
68
68
  async def create_all(self: Self, engine: AsyncEngine | None = None) -> None:
69
69
  if engine is None:
70
70
  engine = self._engine
71
+ assert engine is not None
71
72
  async with engine.begin() as conn:
72
- await conn.run_sync(Base.metadata.create_all)
73
+ await conn.run_sync(Base.metadata.create_all) # type:ignore[attr-defined]
73
74
 
74
75
  async def drop_all(self: Self, engine: AsyncEngine | None = None) -> None:
75
76
  if engine is None:
76
77
  engine = self._engine
77
- await engine.run_sync(Base.metadata.drop_all)
78
+ assert engine is not None
79
+ async with engine.begin() as conn:
80
+ await conn.run_sync(Base.metadata.drop_all) # type:ignore[attr-defined]
78
81
 
79
82
 
80
- async def get_db() -> Session:
83
+ async def get_db() -> AsyncIterator[Session]:
81
84
  async with sessionmanager.session() as session:
82
85
  yield session
83
86
 
@@ -7,11 +7,11 @@ from sqlalchemy.ext.hybrid import hybrid_property
7
7
  class DictMixin:
8
8
  def asdict(self: Self) -> dict:
9
9
  d = {}
10
- for key in self.__mapper__.c.keys():
10
+ for key in self.__mapper__.c.keys(): # type:ignore[attr-defined]
11
11
  if not key.startswith("_"):
12
12
  d[key] = getattr(self, key)
13
13
 
14
- for key, prop in inspect(self.__class__).all_orm_descriptors.items():
14
+ for key, prop in inspect(self.__class__).all_orm_descriptors.items(): # type:ignore[union-attr]
15
15
  if isinstance(prop, hybrid_property):
16
16
  d[key] = getattr(self, key)
17
17
  return d
@@ -0,0 +1,17 @@
1
+ from sqlalchemy import Integer, String, Text
2
+
3
+ from mmisp.db.mypy import Mapped, mapped_column
4
+
5
+ from ..database import Base
6
+
7
+
8
+ class AdminSetting(Base):
9
+ """
10
+ A python class representation of the database model for admin settings in MISP
11
+ """
12
+
13
+ __tablename__ = "admin_settings"
14
+
15
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False, autoincrement=True)
16
+ setting: Mapped[str] = mapped_column(String(255), nullable=False)
17
+ value: Mapped[str] = mapped_column(Text, nullable=False)
@@ -8,7 +8,7 @@ from sqlalchemy.orm.decl_api import DeclarativeMeta
8
8
  from mmisp.db.mixins import DictMixin
9
9
  from mmisp.db.mypy import Mapped, mapped_column
10
10
  from mmisp.lib.attributes import categories, default_category, mapper_safe_clsname_val, to_ids
11
- from mmisp.util.uuid import uuid
11
+ from mmisp.lib.uuid import uuid
12
12
 
13
13
  from ..database import Base
14
14
  from .event import Event
@@ -39,7 +39,7 @@ class Attribute(Base, DictMixin):
39
39
  first_seen: Mapped[int] = mapped_column(BigInteger, index=True)
40
40
  last_seen: Mapped[int] = mapped_column(BigInteger, index=True)
41
41
 
42
- event = relationship("Event", back_populates="attributes", lazy="joined")
42
+ event = relationship("Event", back_populates="attributes", lazy="joined") # type:ignore[var-annotated]
43
43
 
44
44
  __mapper_args__ = {"polymorphic_on": "type"}
45
45
 
@@ -62,7 +62,7 @@ class Attribute(Base, DictMixin):
62
62
  return self.value1
63
63
  return f"{self.value1}|{self.value2}"
64
64
 
65
- @value.setter
65
+ @value.setter # type: ignore[no-redef]
66
66
  def value(self: Self, value: str) -> None:
67
67
  split = value.split("|", 1)
68
68
  self.value1 = split[0]
@@ -89,7 +89,7 @@ class AttributeMeta(DeclarativeMeta):
89
89
  dct["categories"] = categories[mapper_safe_clsname_val[key]]
90
90
  dct["default_to_ids"] = to_ids[mapper_safe_clsname_val[key]]
91
91
  dct["__mapper_args__"] = {"polymorphic_identity": mapper_safe_clsname_val[key]}
92
- return super().__new__(cls, clsname, bases, dct)
92
+ return super().__new__(cls, clsname, bases, dct) # type:ignore[misc]
93
93
 
94
94
 
95
95
  for k, _ in mapper_safe_clsname_val.items():
@@ -3,7 +3,7 @@ from time import time
3
3
  from sqlalchemy import Boolean, ForeignKey, Integer, String
4
4
 
5
5
  from mmisp.db.mypy import Mapped, mapped_column
6
- from mmisp.util.uuid import uuid
6
+ from mmisp.lib.uuid import uuid
7
7
 
8
8
  from ..database import Base
9
9
 
@@ -4,7 +4,7 @@ from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
4
4
  from sqlalchemy.orm import relationship
5
5
 
6
6
  from mmisp.db.mypy import Mapped, mapped_column
7
- from mmisp.util.uuid import uuid
7
+ from mmisp.lib.uuid import uuid
8
8
 
9
9
  from ..database import Base
10
10
  from .organisation import Organisation
@@ -37,7 +37,7 @@ class Event(Base):
37
37
  extends_uuid: Mapped[str] = mapped_column(String(40), default="", index=True)
38
38
  protected: Mapped[bool] = mapped_column(Boolean)
39
39
 
40
- attributes = relationship("Attribute", back_populates="event")
40
+ attributes = relationship("Attribute", back_populates="event") # type:ignore[assignment,var-annotated]
41
41
 
42
42
 
43
43
  class EventReport(Base):
@@ -1,7 +1,7 @@
1
1
  from sqlalchemy import Boolean, Integer, String, Text
2
2
 
3
3
  from mmisp.db.mypy import Mapped, mapped_column
4
- from mmisp.util.uuid import uuid
4
+ from mmisp.lib.uuid import uuid
5
5
 
6
6
  from ..database import Base
7
7
 
@@ -1,7 +1,7 @@
1
1
  from sqlalchemy import Boolean, ForeignKey, Integer, String, Text
2
2
 
3
3
  from mmisp.db.mypy import Mapped, mapped_column
4
- from mmisp.util.uuid import uuid
4
+ from mmisp.lib.uuid import uuid
5
5
 
6
6
  from ..database import Base
7
7
  from .galaxy import Galaxy
@@ -0,0 +1,33 @@
1
+ from datetime import datetime
2
+
3
+ from sqlalchemy import DateTime, Integer, String, Text
4
+
5
+ from mmisp.db.mypy import Mapped, mapped_column
6
+
7
+ from ..database import Base
8
+
9
+
10
+ class Log(Base):
11
+ """
12
+ A python class representation of the database model for logs in MISP.
13
+
14
+ Further explanation for some of the central attributes of the database model:
15
+ - Action: Describes the action that was logged, e.g. a login or workflow execution
16
+ - Change: A string-representation of the changes made to the logged object or of
17
+ central information about the logged object.
18
+ """
19
+
20
+ __tablename__ = "logs"
21
+
22
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False)
23
+ title: Mapped[str] = mapped_column(Text, nullable=True)
24
+ created: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
25
+ model: Mapped[str] = mapped_column(String(80), nullable=False)
26
+ model_id: Mapped[int] = mapped_column(Integer, nullable=False)
27
+ action: Mapped[str] = mapped_column(String(20), nullable=False)
28
+ user_id: Mapped[int] = mapped_column(Integer, nullable=False)
29
+ change: Mapped[str] = mapped_column(Text, nullable=True)
30
+ email: Mapped[str] = mapped_column(String(255), nullable=False)
31
+ org: Mapped[str] = mapped_column(String(255), nullable=False)
32
+ description: Mapped[str] = mapped_column(Text, nullable=True)
33
+ ip: Mapped[str] = mapped_column(String(45), nullable=True)
@@ -3,7 +3,7 @@ from sqlalchemy import Boolean, ForeignKey, Integer, String
3
3
  from mmisp.db.database import Base
4
4
  from mmisp.db.mixins import DictMixin
5
5
  from mmisp.db.mypy import Mapped, mapped_column
6
- from mmisp.util.uuid import uuid
6
+ from mmisp.lib.uuid import uuid
7
7
 
8
8
 
9
9
  class Object(Base, DictMixin):
@@ -4,7 +4,7 @@ from sqlalchemy import Boolean, DateTime, Integer, String, Text
4
4
 
5
5
  from mmisp.db.mixins import DictMixin
6
6
  from mmisp.db.mypy import Mapped, mapped_column
7
- from mmisp.util.uuid import uuid
7
+ from mmisp.lib.uuid import uuid
8
8
 
9
9
  from ..database import Base
10
10
 
@@ -24,7 +24,7 @@ RoleAttrs = {
24
24
  RoleModel = type("RoleModel", (Base,), RoleAttrs)
25
25
 
26
26
 
27
- class Role(RoleModel):
27
+ class Role(RoleModel): # type:ignore[misc,valid-type]
28
28
  def get_permissions(self: Self) -> set[Permission]:
29
29
  d: list[Permission] = []
30
30
 
@@ -4,7 +4,7 @@ from sqlalchemy import Boolean, DateTime, Integer, String, Text
4
4
 
5
5
  from mmisp.db.mixins import DictMixin
6
6
  from mmisp.db.mypy import Mapped, mapped_column
7
- from mmisp.util.uuid import uuid
7
+ from mmisp.lib.uuid import uuid
8
8
 
9
9
  from ..database import Base
10
10
 
@@ -2,7 +2,7 @@ from sqlalchemy import BigInteger, ForeignKey, Integer, String
2
2
 
3
3
  from mmisp.db.database import Base
4
4
  from mmisp.db.mypy import Mapped, mapped_column
5
- from mmisp.util.uuid import uuid
5
+ from mmisp.lib.uuid import uuid
6
6
 
7
7
  from .attribute import Attribute
8
8
  from .event import Event
@@ -0,0 +1,38 @@
1
+ from uuid import uuid4 as _uuid4
2
+
3
+ from sqlalchemy import Boolean, Integer, String
4
+
5
+ from mmisp.db.mypy import Mapped, mapped_column
6
+
7
+ from ...workflows.graph import WorkflowGraph
8
+ from ...workflows.legacy import JSONGraphType
9
+ from ..database import Base
10
+
11
+
12
+ def uuid() -> str:
13
+ return str(_uuid4())
14
+
15
+
16
+ class Workflow(Base):
17
+ """
18
+ A python class representation of the database model for workflows in MISP.
19
+
20
+ The most central of the attributes in this model is the data attribute,
21
+ containing the information about the workflow structure and the modules contained in the workflow,
22
+ represented/stored as a JSON-String.
23
+ (The other attributes are what their name sais, e.g. counter represents the numer
24
+ of times the workflow was executed.)
25
+ """
26
+
27
+ __tablename__ = "workflows"
28
+
29
+ id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False, autoincrement=True)
30
+ uuid: Mapped[str] = mapped_column(String(40), default=uuid, nullable=False, index=True)
31
+ name: Mapped[str] = mapped_column(String(191), nullable=False, index=True)
32
+ description: Mapped[str] = mapped_column(String(191), nullable=False)
33
+ timestamp: Mapped[int] = mapped_column(Integer, nullable=False, default=0, index=True)
34
+ enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
35
+ counter: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
36
+ trigger_id: Mapped[str] = mapped_column(String(191), nullable=False, index=True)
37
+ debug_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=0)
38
+ data: Mapped[WorkflowGraph] = mapped_column(JSONGraphType, nullable=False, default=0)