fluidattacks_core_contextualizes 2.0.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.
- fluidattacks_core_contextualizes-2.0.0/.envrc +1 -0
- fluidattacks_core_contextualizes-2.0.0/.gitignore +9 -0
- fluidattacks_core_contextualizes-2.0.0/PKG-INFO +15 -0
- fluidattacks_core_contextualizes-2.0.0/build/default.nix +19 -0
- fluidattacks_core_contextualizes-2.0.0/build/filter.nix +13 -0
- fluidattacks_core_contextualizes-2.0.0/flake.lock +303 -0
- fluidattacks_core_contextualizes-2.0.0/flake.nix +29 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/__init__.py +48 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/components.py +111 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/envelope.py +57 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/observations.py +28 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/paths.py +53 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/py.typed +0 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/schema.py +35 -0
- fluidattacks_core_contextualizes-2.0.0/fluidattacks_core/contextualizes/schemas/contextualizer_output.schema.json +220 -0
- fluidattacks_core_contextualizes-2.0.0/import-linter.cfg +16 -0
- fluidattacks_core_contextualizes-2.0.0/mypy.ini +32 -0
- fluidattacks_core_contextualizes-2.0.0/pyproject.toml +42 -0
- fluidattacks_core_contextualizes-2.0.0/ruff.toml +45 -0
- fluidattacks_core_contextualizes-2.0.0/tests/__init__.py +0 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/discovers_output.json +41 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/explores_output.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_absolute_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_attributed_observation.json +55 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_backslash_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_coerced_schema_version.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_control_character_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_dot_segment_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_duplicate_component.json +67 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_duplicate_normalization.json +67 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_empty_segment_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_missing_enrichment.json +53 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_missing_status.json +53 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_trailing_separator_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/data/invalid_traversal_path.json +54 -0
- fluidattacks_core_contextualizes-2.0.0/tests/test_components.py +67 -0
- fluidattacks_core_contextualizes-2.0.0/tests/test_envelope.py +107 -0
- fluidattacks_core_contextualizes-2.0.0/tests/test_paths.py +50 -0
- fluidattacks_core_contextualizes-2.0.0/tests/test_schema.py +58 -0
- fluidattacks_core_contextualizes-2.0.0/uv.lock +1027 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use_flake ".#python311.devShell"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fluidattacks_core_contextualizes
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Fluid Attacks Core Contextualizes Library
|
|
5
|
+
Author-email: Development <development@fluidattacks.com>
|
|
6
|
+
License: MPL-2.0
|
|
7
|
+
Classifier: Development Status :: 1 - Planning
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: fluidattacks-core-filesystem<15,>=14.0.0
|
|
15
|
+
Requires-Dist: pydantic<3,>=2.12.3
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{ src, workspace_root }:
|
|
2
|
+
{
|
|
3
|
+
inherit src workspace_root;
|
|
4
|
+
root_path = "develops/fluidattacks-core/python/contextualizes";
|
|
5
|
+
module_name = "fluidattacks_core";
|
|
6
|
+
pypi_token = {
|
|
7
|
+
method = "oidc";
|
|
8
|
+
};
|
|
9
|
+
override = {
|
|
10
|
+
checks = {
|
|
11
|
+
basedpyright = false;
|
|
12
|
+
};
|
|
13
|
+
configs = {
|
|
14
|
+
import_linter = ../import-linter.cfg;
|
|
15
|
+
mypy = ../mypy.ini;
|
|
16
|
+
ruff = ../ruff.toml;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"flake-parts": {
|
|
4
|
+
"inputs": {
|
|
5
|
+
"nixpkgs-lib": "nixpkgs-lib"
|
|
6
|
+
},
|
|
7
|
+
"locked": {
|
|
8
|
+
"lastModified": 1762810396,
|
|
9
|
+
"narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=",
|
|
10
|
+
"owner": "hercules-ci",
|
|
11
|
+
"repo": "flake-parts",
|
|
12
|
+
"rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923",
|
|
13
|
+
"type": "github"
|
|
14
|
+
},
|
|
15
|
+
"original": {
|
|
16
|
+
"owner": "hercules-ci",
|
|
17
|
+
"repo": "flake-parts",
|
|
18
|
+
"type": "github"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"nix_filter": {
|
|
22
|
+
"locked": {
|
|
23
|
+
"lastModified": 1757882181,
|
|
24
|
+
"narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=",
|
|
25
|
+
"owner": "numtide",
|
|
26
|
+
"repo": "nix-filter",
|
|
27
|
+
"rev": "59c44d1909c72441144b93cf0f054be7fe764de5",
|
|
28
|
+
"type": "github"
|
|
29
|
+
},
|
|
30
|
+
"original": {
|
|
31
|
+
"owner": "numtide",
|
|
32
|
+
"repo": "nix-filter",
|
|
33
|
+
"type": "github"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"nixpkgs": {
|
|
37
|
+
"locked": {
|
|
38
|
+
"lastModified": 1758690382,
|
|
39
|
+
"narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
|
|
40
|
+
"owner": "nixos",
|
|
41
|
+
"repo": "nixpkgs",
|
|
42
|
+
"rev": "e643668fd71b949c53f8626614b21ff71a07379d",
|
|
43
|
+
"type": "github"
|
|
44
|
+
},
|
|
45
|
+
"original": {
|
|
46
|
+
"owner": "nixos",
|
|
47
|
+
"ref": "nixos-unstable",
|
|
48
|
+
"repo": "nixpkgs",
|
|
49
|
+
"type": "github"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"nixpkgs-lib": {
|
|
53
|
+
"locked": {
|
|
54
|
+
"lastModified": 1761765539,
|
|
55
|
+
"narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
|
|
56
|
+
"owner": "nix-community",
|
|
57
|
+
"repo": "nixpkgs.lib",
|
|
58
|
+
"rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
|
|
59
|
+
"type": "github"
|
|
60
|
+
},
|
|
61
|
+
"original": {
|
|
62
|
+
"owner": "nix-community",
|
|
63
|
+
"repo": "nixpkgs.lib",
|
|
64
|
+
"type": "github"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"nixpkgs_2": {
|
|
68
|
+
"locked": {
|
|
69
|
+
"lastModified": 1758690382,
|
|
70
|
+
"narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
|
|
71
|
+
"owner": "nixos",
|
|
72
|
+
"repo": "nixpkgs",
|
|
73
|
+
"rev": "e643668fd71b949c53f8626614b21ff71a07379d",
|
|
74
|
+
"type": "github"
|
|
75
|
+
},
|
|
76
|
+
"original": {
|
|
77
|
+
"owner": "nixos",
|
|
78
|
+
"ref": "nixos-unstable",
|
|
79
|
+
"repo": "nixpkgs",
|
|
80
|
+
"type": "github"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"nixpkgs_3": {
|
|
84
|
+
"locked": {
|
|
85
|
+
"lastModified": 1761373498,
|
|
86
|
+
"narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
|
|
87
|
+
"owner": "nixos",
|
|
88
|
+
"repo": "nixpkgs",
|
|
89
|
+
"rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
|
|
90
|
+
"type": "github"
|
|
91
|
+
},
|
|
92
|
+
"original": {
|
|
93
|
+
"owner": "nixos",
|
|
94
|
+
"ref": "nixos-unstable",
|
|
95
|
+
"repo": "nixpkgs",
|
|
96
|
+
"type": "github"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"nixpkgs_flake": {
|
|
100
|
+
"locked": {
|
|
101
|
+
"lastModified": 1778253243,
|
|
102
|
+
"narHash": "sha256-KkH2QvU6iRLDTGGEwH5jnMqHWj4zMTuHxi1S1zdr01w=",
|
|
103
|
+
"owner": "nixos",
|
|
104
|
+
"repo": "nixpkgs",
|
|
105
|
+
"rev": "4fcda7103530b23c5ca44286344912082aad4220",
|
|
106
|
+
"type": "github"
|
|
107
|
+
},
|
|
108
|
+
"original": {
|
|
109
|
+
"owner": "nixos",
|
|
110
|
+
"repo": "nixpkgs",
|
|
111
|
+
"type": "github"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"pyproject-build-systems": {
|
|
115
|
+
"inputs": {
|
|
116
|
+
"nixpkgs": "nixpkgs",
|
|
117
|
+
"pyproject-nix": "pyproject-nix",
|
|
118
|
+
"uv2nix": "uv2nix"
|
|
119
|
+
},
|
|
120
|
+
"locked": {
|
|
121
|
+
"lastModified": 1761781027,
|
|
122
|
+
"narHash": "sha256-YDvxPAm2WnxrznRqWwHLjryBGG5Ey1ATEJXrON+TWt8=",
|
|
123
|
+
"owner": "pyproject-nix",
|
|
124
|
+
"repo": "build-system-pkgs",
|
|
125
|
+
"rev": "795a980d25301e5133eca37adae37283ec3c8e66",
|
|
126
|
+
"type": "github"
|
|
127
|
+
},
|
|
128
|
+
"original": {
|
|
129
|
+
"owner": "pyproject-nix",
|
|
130
|
+
"repo": "build-system-pkgs",
|
|
131
|
+
"type": "github"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"pyproject-nix": {
|
|
135
|
+
"inputs": {
|
|
136
|
+
"nixpkgs": [
|
|
137
|
+
"python_builder",
|
|
138
|
+
"pyproject-build-systems",
|
|
139
|
+
"nixpkgs"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"locked": {
|
|
143
|
+
"lastModified": 1758265079,
|
|
144
|
+
"narHash": "sha256-amLaLNwKSZPShQHzfgmc/9o76dU8xzN0743dWgvYlr8=",
|
|
145
|
+
"owner": "nix-community",
|
|
146
|
+
"repo": "pyproject.nix",
|
|
147
|
+
"rev": "02e9418fd4af638447dca4b17b1280da95527fc9",
|
|
148
|
+
"type": "github"
|
|
149
|
+
},
|
|
150
|
+
"original": {
|
|
151
|
+
"owner": "nix-community",
|
|
152
|
+
"repo": "pyproject.nix",
|
|
153
|
+
"type": "github"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"pyproject-nix_2": {
|
|
157
|
+
"inputs": {
|
|
158
|
+
"nixpkgs": "nixpkgs_2"
|
|
159
|
+
},
|
|
160
|
+
"locked": {
|
|
161
|
+
"lastModified": 1762427963,
|
|
162
|
+
"narHash": "sha256-CkPlAbIQ87wmjy5qHibfzk4DmMGBNqFer+lLfXjpP5M=",
|
|
163
|
+
"owner": "pyproject-nix",
|
|
164
|
+
"repo": "pyproject.nix",
|
|
165
|
+
"rev": "4540ea004e04fcd12dd2738d51383d10f956f7b9",
|
|
166
|
+
"type": "github"
|
|
167
|
+
},
|
|
168
|
+
"original": {
|
|
169
|
+
"owner": "pyproject-nix",
|
|
170
|
+
"repo": "pyproject.nix",
|
|
171
|
+
"type": "github"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"pyproject-nix_3": {
|
|
175
|
+
"inputs": {
|
|
176
|
+
"nixpkgs": [
|
|
177
|
+
"python_builder",
|
|
178
|
+
"uv2nix",
|
|
179
|
+
"nixpkgs"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
"locked": {
|
|
183
|
+
"lastModified": 1760402624,
|
|
184
|
+
"narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=",
|
|
185
|
+
"owner": "pyproject-nix",
|
|
186
|
+
"repo": "pyproject.nix",
|
|
187
|
+
"rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2",
|
|
188
|
+
"type": "github"
|
|
189
|
+
},
|
|
190
|
+
"original": {
|
|
191
|
+
"owner": "pyproject-nix",
|
|
192
|
+
"repo": "pyproject.nix",
|
|
193
|
+
"type": "github"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"python_builder": {
|
|
197
|
+
"inputs": {
|
|
198
|
+
"nix_filter": "nix_filter",
|
|
199
|
+
"nixpkgs_flake": "nixpkgs_flake",
|
|
200
|
+
"pyproject-build-systems": "pyproject-build-systems",
|
|
201
|
+
"pyproject-nix": "pyproject-nix_2",
|
|
202
|
+
"shell-helpers": "shell-helpers",
|
|
203
|
+
"uv2nix": "uv2nix_2"
|
|
204
|
+
},
|
|
205
|
+
"locked": {
|
|
206
|
+
"dir": "common/utils/python-builder",
|
|
207
|
+
"lastModified": 1787078362,
|
|
208
|
+
"narHash": "sha256-HYL7Rn6SuNjYxkt/PwFLksZjsauCiY+ENbOWryLsIlw=",
|
|
209
|
+
"ref": "refs/heads/trunk",
|
|
210
|
+
"rev": "09fd25a13309eb5e3b5b2a061bb340df3ae5d281",
|
|
211
|
+
"shallow": true,
|
|
212
|
+
"type": "git",
|
|
213
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
214
|
+
},
|
|
215
|
+
"original": {
|
|
216
|
+
"dir": "common/utils/python-builder",
|
|
217
|
+
"rev": "09fd25a13309eb5e3b5b2a061bb340df3ae5d281",
|
|
218
|
+
"shallow": true,
|
|
219
|
+
"type": "git",
|
|
220
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"root": {
|
|
224
|
+
"inputs": {
|
|
225
|
+
"python_builder": "python_builder"
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"shell-helpers": {
|
|
229
|
+
"inputs": {
|
|
230
|
+
"flake-parts": "flake-parts",
|
|
231
|
+
"nixpkgs": [
|
|
232
|
+
"python_builder",
|
|
233
|
+
"nixpkgs_flake"
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
"locked": {
|
|
237
|
+
"dir": "common/utils/shell-helpers",
|
|
238
|
+
"lastModified": 1762806066,
|
|
239
|
+
"narHash": "sha256-Wz99Fl7SpPU1NkTHJ36XOo6DjOEAMXCw3ThRABOS2Hs=",
|
|
240
|
+
"ref": "refs/heads/trunk",
|
|
241
|
+
"rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
|
|
242
|
+
"shallow": true,
|
|
243
|
+
"type": "git",
|
|
244
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
245
|
+
},
|
|
246
|
+
"original": {
|
|
247
|
+
"dir": "common/utils/shell-helpers",
|
|
248
|
+
"rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
|
|
249
|
+
"shallow": true,
|
|
250
|
+
"type": "git",
|
|
251
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"uv2nix": {
|
|
255
|
+
"inputs": {
|
|
256
|
+
"nixpkgs": [
|
|
257
|
+
"python_builder",
|
|
258
|
+
"pyproject-build-systems",
|
|
259
|
+
"nixpkgs"
|
|
260
|
+
],
|
|
261
|
+
"pyproject-nix": [
|
|
262
|
+
"python_builder",
|
|
263
|
+
"pyproject-build-systems",
|
|
264
|
+
"pyproject-nix"
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
"locked": {
|
|
268
|
+
"lastModified": 1758933732,
|
|
269
|
+
"narHash": "sha256-HAmm1GBS1myZCFuog0DC2ZLaynvZtiUI2Crmo+cdQI0=",
|
|
270
|
+
"owner": "pyproject-nix",
|
|
271
|
+
"repo": "uv2nix",
|
|
272
|
+
"rev": "273ce18f913d8559e0d04f820d724308966d7c4d",
|
|
273
|
+
"type": "github"
|
|
274
|
+
},
|
|
275
|
+
"original": {
|
|
276
|
+
"owner": "pyproject-nix",
|
|
277
|
+
"repo": "uv2nix",
|
|
278
|
+
"type": "github"
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"uv2nix_2": {
|
|
282
|
+
"inputs": {
|
|
283
|
+
"nixpkgs": "nixpkgs_3",
|
|
284
|
+
"pyproject-nix": "pyproject-nix_3"
|
|
285
|
+
},
|
|
286
|
+
"locked": {
|
|
287
|
+
"lastModified": 1761872265,
|
|
288
|
+
"narHash": "sha256-i25GRgp2vUOebY70L3NTAgkd+Pr1hnn5xM3qHxH0ONU=",
|
|
289
|
+
"owner": "pyproject-nix",
|
|
290
|
+
"repo": "uv2nix",
|
|
291
|
+
"rev": "74dfb62871be152ad3673b143b0cc56105a4f3c5",
|
|
292
|
+
"type": "github"
|
|
293
|
+
},
|
|
294
|
+
"original": {
|
|
295
|
+
"owner": "pyproject-nix",
|
|
296
|
+
"repo": "uv2nix",
|
|
297
|
+
"type": "github"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
"root": "root",
|
|
302
|
+
"version": 7
|
|
303
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
description = "Fluid Attacks Core contextualizes package.";
|
|
3
|
+
|
|
4
|
+
inputs = {
|
|
5
|
+
python_builder = {
|
|
6
|
+
url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=09fd25a13309eb5e3b5b2a061bb340df3ae5d281&dir=common/utils/python-builder";
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
outputs =
|
|
11
|
+
{ self, ... }@inputs:
|
|
12
|
+
let
|
|
13
|
+
build_args =
|
|
14
|
+
{
|
|
15
|
+
system,
|
|
16
|
+
python_version,
|
|
17
|
+
nixpkgs,
|
|
18
|
+
builders,
|
|
19
|
+
scripts,
|
|
20
|
+
}:
|
|
21
|
+
import ./build {
|
|
22
|
+
src = import ./build/filter.nix nixpkgs.nix-filter self;
|
|
23
|
+
workspace_root = ./.;
|
|
24
|
+
};
|
|
25
|
+
in
|
|
26
|
+
{
|
|
27
|
+
packages = inputs.python_builder.outputs.build build_args;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from fluidattacks_core.contextualizes.components import (
|
|
2
|
+
MANIFEST_KINDS,
|
|
3
|
+
Component,
|
|
4
|
+
ComponentKind,
|
|
5
|
+
Enrichment,
|
|
6
|
+
kind_for_manifests,
|
|
7
|
+
)
|
|
8
|
+
from fluidattacks_core.contextualizes.envelope import (
|
|
9
|
+
SCHEMA_VERSION,
|
|
10
|
+
ContextualizerOutput,
|
|
11
|
+
Repository,
|
|
12
|
+
RunStatus,
|
|
13
|
+
)
|
|
14
|
+
from fluidattacks_core.contextualizes.observations import Observation, ObservationKind
|
|
15
|
+
from fluidattacks_core.contextualizes.paths import (
|
|
16
|
+
MAX_PATH_LENGTH,
|
|
17
|
+
REPOSITORY_ROOT,
|
|
18
|
+
RelativePath,
|
|
19
|
+
ensure_relative_path,
|
|
20
|
+
)
|
|
21
|
+
from fluidattacks_core.contextualizes.schema import (
|
|
22
|
+
SCHEMA_PATH,
|
|
23
|
+
build_schema,
|
|
24
|
+
load_schema,
|
|
25
|
+
render_schema,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"MANIFEST_KINDS",
|
|
30
|
+
"MAX_PATH_LENGTH",
|
|
31
|
+
"REPOSITORY_ROOT",
|
|
32
|
+
"SCHEMA_PATH",
|
|
33
|
+
"SCHEMA_VERSION",
|
|
34
|
+
"Component",
|
|
35
|
+
"ComponentKind",
|
|
36
|
+
"ContextualizerOutput",
|
|
37
|
+
"Enrichment",
|
|
38
|
+
"Observation",
|
|
39
|
+
"ObservationKind",
|
|
40
|
+
"RelativePath",
|
|
41
|
+
"Repository",
|
|
42
|
+
"RunStatus",
|
|
43
|
+
"build_schema",
|
|
44
|
+
"ensure_relative_path",
|
|
45
|
+
"kind_for_manifests",
|
|
46
|
+
"load_schema",
|
|
47
|
+
"render_schema",
|
|
48
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from pathlib import PurePosixPath
|
|
4
|
+
from typing import Annotated, ClassVar
|
|
5
|
+
|
|
6
|
+
from fluidattacks_core.filesystem.defaults import MANIFEST_MARKERS, WORKSPACE_MARKERS
|
|
7
|
+
from fluidattacks_core.filesystem.language import Language
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, StringConstraints
|
|
9
|
+
|
|
10
|
+
from fluidattacks_core.contextualizes.paths import RelativePath
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ComponentKind(StrEnum):
|
|
14
|
+
"""Ecosystem of a component, decided by a filename, never by a model.
|
|
15
|
+
|
|
16
|
+
Spelled as the platform's package ecosystem ids so a component joins the
|
|
17
|
+
package inventory without a translation table. `gradle` and `sbt` have no
|
|
18
|
+
id of their own there; both resolve from the maven registry.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
CARGO = "cargo"
|
|
22
|
+
GO = "go"
|
|
23
|
+
GRADLE = "gradle"
|
|
24
|
+
MAVEN = "maven"
|
|
25
|
+
NPM = "npm"
|
|
26
|
+
NUGET = "nuget"
|
|
27
|
+
PACKAGIST = "packagist"
|
|
28
|
+
PUB = "pub"
|
|
29
|
+
PYPI = "pypi"
|
|
30
|
+
RUBYGEMS = "rubygems"
|
|
31
|
+
SBT = "sbt"
|
|
32
|
+
SWIFTURL = "swifturl"
|
|
33
|
+
UNKNOWN = "unknown"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_GRADLE_FILES = WORKSPACE_MARKERS["gradle"]["declares"] | WORKSPACE_MARKERS["gradle"]["claims"]
|
|
37
|
+
|
|
38
|
+
# Derived, not copied, so an upstream manifest cannot resolve to UNKNOWN here.
|
|
39
|
+
MANIFEST_KINDS: dict[ComponentKind, frozenset[str]] = {
|
|
40
|
+
ComponentKind.CARGO: MANIFEST_MARKERS[Language.Rust],
|
|
41
|
+
ComponentKind.GO: MANIFEST_MARKERS[Language.Go],
|
|
42
|
+
ComponentKind.GRADLE: (
|
|
43
|
+
MANIFEST_MARKERS[Language.Kotlin] | (MANIFEST_MARKERS[Language.Java] & _GRADLE_FILES)
|
|
44
|
+
),
|
|
45
|
+
ComponentKind.MAVEN: MANIFEST_MARKERS[Language.Java] - _GRADLE_FILES,
|
|
46
|
+
ComponentKind.NPM: (
|
|
47
|
+
MANIFEST_MARKERS[Language.JavaScript] | MANIFEST_MARKERS[Language.TypeScript]
|
|
48
|
+
),
|
|
49
|
+
ComponentKind.NUGET: MANIFEST_MARKERS[Language.CSharp],
|
|
50
|
+
ComponentKind.PACKAGIST: MANIFEST_MARKERS[Language.PHP],
|
|
51
|
+
ComponentKind.PUB: MANIFEST_MARKERS[Language.Dart],
|
|
52
|
+
ComponentKind.PYPI: MANIFEST_MARKERS[Language.Python],
|
|
53
|
+
ComponentKind.RUBYGEMS: MANIFEST_MARKERS[Language.Ruby],
|
|
54
|
+
ComponentKind.SBT: MANIFEST_MARKERS[Language.Scala],
|
|
55
|
+
ComponentKind.SWIFTURL: MANIFEST_MARKERS[Language.Swift],
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# A case-insensitive checkout returns whatever spelling it stored.
|
|
59
|
+
_KIND_BY_MANIFEST: dict[str, ComponentKind] = {
|
|
60
|
+
manifest.casefold(): kind
|
|
61
|
+
for kind, manifests in MANIFEST_KINDS.items()
|
|
62
|
+
for manifest in manifests
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# A polyglot component belongs to the ecosystem that ships it, not the one
|
|
66
|
+
# that tools it: maturin is pypi, gradle wrapping npm is gradle.
|
|
67
|
+
_KIND_PRIORITY: dict[ComponentKind, int] = {
|
|
68
|
+
ComponentKind.GRADLE: 90,
|
|
69
|
+
ComponentKind.MAVEN: 85,
|
|
70
|
+
ComponentKind.SBT: 80,
|
|
71
|
+
ComponentKind.SWIFTURL: 75,
|
|
72
|
+
ComponentKind.PUB: 70,
|
|
73
|
+
ComponentKind.NUGET: 65,
|
|
74
|
+
ComponentKind.GO: 60,
|
|
75
|
+
ComponentKind.PYPI: 55,
|
|
76
|
+
ComponentKind.CARGO: 50,
|
|
77
|
+
ComponentKind.RUBYGEMS: 45,
|
|
78
|
+
ComponentKind.PACKAGIST: 40,
|
|
79
|
+
ComponentKind.NPM: 35,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def kind_for_manifests(manifests: Iterable[str]) -> ComponentKind:
|
|
84
|
+
"""Pick a component's kind from the manifest file names found in it."""
|
|
85
|
+
matched = {
|
|
86
|
+
_KIND_BY_MANIFEST[key]
|
|
87
|
+
for name in manifests
|
|
88
|
+
if (key := PurePosixPath(name.replace("\\", "/")).name.casefold()) in _KIND_BY_MANIFEST
|
|
89
|
+
}
|
|
90
|
+
if not matched:
|
|
91
|
+
return ComponentKind.UNKNOWN
|
|
92
|
+
|
|
93
|
+
return max(matched, key=lambda kind: (_KIND_PRIORITY[kind], kind.value))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Enrichment(StrEnum):
|
|
97
|
+
"""Deep-pass outcome; the only way to tell a missing field from a gap."""
|
|
98
|
+
|
|
99
|
+
ABSENT = "absent"
|
|
100
|
+
FAILED = "failed"
|
|
101
|
+
PRESENT = "present"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Component(BaseModel):
|
|
105
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid", strict=True)
|
|
106
|
+
|
|
107
|
+
path: RelativePath
|
|
108
|
+
name: Annotated[str, StringConstraints(min_length=1, max_length=512)]
|
|
109
|
+
kind: ComponentKind
|
|
110
|
+
enrichment: Enrichment
|
|
111
|
+
properties: dict[str, object] = Field(default_factory=dict)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import unicodedata
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Annotated, ClassVar
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, StringConstraints, model_validator
|
|
6
|
+
|
|
7
|
+
from fluidattacks_core.contextualizes.components import Component
|
|
8
|
+
from fluidattacks_core.contextualizes.observations import Observation
|
|
9
|
+
|
|
10
|
+
SCHEMA_VERSION = 1
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RunStatus(StrEnum):
|
|
14
|
+
"""How much of the repository the run covered.
|
|
15
|
+
|
|
16
|
+
A consumer reconciles by deleting what the output omits, so it must be
|
|
17
|
+
able to refuse anything but `complete`.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
COMPLETE = "complete"
|
|
21
|
+
FAILED = "failed"
|
|
22
|
+
PARTIAL = "partial"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Repository(BaseModel):
|
|
26
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid", strict=True)
|
|
27
|
+
|
|
28
|
+
url: Annotated[str, StringConstraints(min_length=1, max_length=2048)]
|
|
29
|
+
branch: Annotated[str, StringConstraints(min_length=1, max_length=512)] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ContextualizerOutput(BaseModel):
|
|
33
|
+
"""One contextualizer's report over one repository.
|
|
34
|
+
|
|
35
|
+
Contextualizer vocabulary only: nothing about scope, groups or asset
|
|
36
|
+
types, none of which is observable from a bare checkout.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid", strict=True)
|
|
40
|
+
|
|
41
|
+
schema_version: int = Field(ge=1, le=SCHEMA_VERSION)
|
|
42
|
+
producer: Annotated[str, StringConstraints(min_length=1, max_length=64)]
|
|
43
|
+
repository: Repository
|
|
44
|
+
status: RunStatus
|
|
45
|
+
components: tuple[Component, ...] = ()
|
|
46
|
+
observations: tuple[Observation, ...] = ()
|
|
47
|
+
|
|
48
|
+
@model_validator(mode="after")
|
|
49
|
+
def _reject_duplicate_component_paths(self) -> "ContextualizerOutput":
|
|
50
|
+
# Folded: two normalization forms are two files here, but a consumer
|
|
51
|
+
# keying on the normalized path sees one, and loses an entry.
|
|
52
|
+
paths = [unicodedata.normalize("NFC", component.path) for component in self.components]
|
|
53
|
+
if len(set(paths)) != len(paths):
|
|
54
|
+
msg = "components must not repeat a path"
|
|
55
|
+
raise ValueError(msg)
|
|
56
|
+
|
|
57
|
+
return self
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Annotated, ClassVar
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, StringConstraints
|
|
5
|
+
|
|
6
|
+
from fluidattacks_core.contextualizes.paths import RelativePath
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ObservationKind(StrEnum):
|
|
10
|
+
AI_MODEL = "ai_model"
|
|
11
|
+
ENDPOINT = "endpoint"
|
|
12
|
+
PACKAGE = "package"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Observation(BaseModel):
|
|
16
|
+
"""Something found at a path, with no statement of which component owns it.
|
|
17
|
+
|
|
18
|
+
Unattributed so a producer need not run the partitioner, and a
|
|
19
|
+
repartition re-attributes everything with no rewrite here.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid", strict=True)
|
|
23
|
+
|
|
24
|
+
path: RelativePath
|
|
25
|
+
kind: ObservationKind
|
|
26
|
+
line: Annotated[int, Field(ge=1)] | None = None
|
|
27
|
+
properties: dict[str, object] = Field(default_factory=dict)
|
|
28
|
+
seen_by: Annotated[str, StringConstraints(min_length=1, max_length=128)] | None = None
|