evorthon-data 1.0.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.
Files changed (68) hide show
  1. evorthon_data/__init__.py +5 -0
  2. evorthon_data/boundary_patterns.py +353 -0
  3. evorthon_data/cli.py +551 -0
  4. evorthon_data/composition.py +3103 -0
  5. evorthon_data/delivery/__init__.py +100 -0
  6. evorthon_data/delivery/autobuild.py +365 -0
  7. evorthon_data/delivery/declaration_draft.py +933 -0
  8. evorthon_data/delivery/ergasterion.py +886 -0
  9. evorthon_data/delivery/pinax.py +1168 -0
  10. evorthon_data/dependencies.py +16 -0
  11. evorthon_data/engagement/__init__.py +128 -0
  12. evorthon_data/engagement/aggregate.py +882 -0
  13. evorthon_data/engagement/stages.py +11 -0
  14. evorthon_data/engagement/use_case.py +1696 -0
  15. evorthon_data/engagement/versions.py +313 -0
  16. evorthon_data/koine_projection.py +183 -0
  17. evorthon_data/koine_session.py +2688 -0
  18. evorthon_data/lifecycle.py +749 -0
  19. evorthon_data/presentation/__init__.py +34 -0
  20. evorthon_data/presentation/use_case.py +138 -0
  21. evorthon_data/public_boundary.py +681 -0
  22. evorthon_data/pytest_reporter.py +68 -0
  23. evorthon_data/readiness/__init__.py +58 -0
  24. evorthon_data/readiness/projection.py +793 -0
  25. evorthon_data/runtime_adapters.py +40 -0
  26. evorthon_data/security/__init__.py +31 -0
  27. evorthon_data/security/model_egress.py +328 -0
  28. evorthon_data/stages.py +11 -0
  29. evorthon_data/synthetic/__init__.py +58 -0
  30. evorthon_data/synthetic/generator.py +924 -0
  31. evorthon_data/synthetic/request.py +179 -0
  32. evorthon_data/verification/__init__.py +8 -0
  33. evorthon_data/verification/adapters/__init__.py +87 -0
  34. evorthon_data/verification/adapters/conformance.py +784 -0
  35. evorthon_data/verification/adapters/declaration.py +126 -0
  36. evorthon_data/verification/adapters/fixtures.py +374 -0
  37. evorthon_data/verification/adapters/material.py +95 -0
  38. evorthon_data/verification/adapters/memory.py +123 -0
  39. evorthon_data/verification/core/__init__.py +56 -0
  40. evorthon_data/verification/core/canonical.py +716 -0
  41. evorthon_data/verification/core/fault.py +726 -0
  42. evorthon_data/verification/core/localisation.py +605 -0
  43. evorthon_data/verification/core/reconciliation.py +1617 -0
  44. evorthon_data/verification/domain/__init__.py +150 -0
  45. evorthon_data/verification/domain/contracts.py +919 -0
  46. evorthon_data/verification/enforcement/__init__.py +85 -0
  47. evorthon_data/verification/enforcement/privacy.py +448 -0
  48. evorthon_data/verification/enforcement/schema.py +188 -0
  49. evorthon_data/verification/enforcement/serialization.py +206 -0
  50. evorthon_data/verification/enforcement/validation.py +1566 -0
  51. evorthon_data/verification/ports/__init__.py +38 -0
  52. evorthon_data/verification/ports/contracts.py +187 -0
  53. evorthon_data/verification/presentation/__init__.py +144 -0
  54. evorthon_data/verification/presentation/remediation.py +354 -0
  55. evorthon_data/verification/presentation/report.py +492 -0
  56. evorthon_data/verification/workflows/__init__.py +214 -0
  57. evorthon_data/verification/workflows/adviser.py +560 -0
  58. evorthon_data/verification/workflows/delivery_contract.py +1005 -0
  59. evorthon_data/verification/workflows/diagnosis.py +134 -0
  60. evorthon_data/verification/workflows/intake.py +567 -0
  61. evorthon_data/verification/workflows/reconciliation.py +221 -0
  62. evorthon_data/verification/workflows/remediation.py +717 -0
  63. evorthon_data/verification/workflows/replay.py +321 -0
  64. evorthon_data-1.0.0.dist-info/METADATA +186 -0
  65. evorthon_data-1.0.0.dist-info/RECORD +68 -0
  66. evorthon_data-1.0.0.dist-info/WHEEL +4 -0
  67. evorthon_data-1.0.0.dist-info/entry_points.txt +2 -0
  68. evorthon_data-1.0.0.dist-info/licenses/LICENSE +15 -0
@@ -0,0 +1,5 @@
1
+ """Evorthon Data product boundary."""
2
+
3
+ # evorthon-component: composition
4
+
5
+ __version__ = "1.0.0"
@@ -0,0 +1,353 @@
1
+ """The closed shapes every boundary of this product reads the same way.
2
+
3
+ Four places in this product decide whether a text carries a route to somebody's
4
+ machine, a credential, or a row of written data: the use-case aggregate reading
5
+ a fact locator, the privacy gate reading a fault packet, the public-candidate
6
+ scan reading a shipped file, and the tracker adapter reading a note. A fifth,
7
+ the intake credential scan, reads an artefact. When each of them wrote its own
8
+ rule they disagreed, and the weakest of them decided what a record kept and
9
+ what crossed to a model.
10
+
11
+ This module is the one owner of those shapes. It declares them as named
12
+ compiled patterns and it reads them. It refuses nothing and it decides nothing:
13
+ each boundary keeps its own refusal, its own reason and its own message,
14
+ because what a refusal means is the boundary's to say.
15
+
16
+ A boundary names the shapes it reads. It reads every shape it can, and where it
17
+ cannot read one it says so in its own docstring with the reason. The subject
18
+ differs: three of the boundaries read one declared value that this product
19
+ wrote, while the public-candidate scan reads whole files of source, fixtures
20
+ and documentation, where a backslash writes a character escape and a slash
21
+ after a space is ordinary prose. A shape that is unambiguous in a declared
22
+ value is not always unambiguous in a page of shipped text, and pretending
23
+ otherwise would either blind a boundary or flood it.
24
+
25
+ The shapes are assembled from character codes, so this file carries no drive
26
+ letter, no address scheme and no machine path of its own, and the scan that
27
+ reads it finds nothing to report.
28
+
29
+ Some shapes therefore come in two readings, a prose one and a declared-value
30
+ one, and both are declared here. The prose reading of a traversal wants the
31
+ value or the line to begin with it, and the prose reading of an absolute path
32
+ wants a first segment that continues into another one or ends in a suffix,
33
+ because shipped documentation links with a traversal segment inside a longer
34
+ relative link and writes a bare slash between two names for a rate or a pair of
35
+ sides. The declared-value reading of each wants neither: a locator this product
36
+ wrote is one value, so a traversal anywhere in it, a single-segment path from
37
+ the root, a lettered volume with no separator after it and a share host with
38
+ nothing after it are all routes. A bare word and a bare whole number cannot be
39
+ told apart from a name and a count by reading them, so nothing here claims to.
40
+ A credential shape names a written secret, not a high-entropy token with no name
41
+ beside it.
42
+
43
+ Two forms these shapes do not read at all, at any boundary, so that what is
44
+ missed is missed in one place and can be closed in one place.
45
+
46
+ * A home path written with the character that stands for one. The shapes read
47
+ the written words for a home directory, not that character.
48
+ * A traversal or a separator written in percent-encoded form. Nothing here
49
+ decodes an encoding before reading, so an encoded route reads as text.
50
+
51
+ A fourth family is advisory. It is read for a warning and never for a refusal,
52
+ and no boundary keeps a refusal reason for it. It holds two forms. A bare host
53
+ is a dotted name with a path or a port after it and no scheme in front of it;
54
+ the dotted name alone is not read, because it cannot be told from an ordinary
55
+ written name at all, and refusing it would refuse names a team has every reason
56
+ to write. A connection string is a connection key written against a value. A
57
+ boundary that reads this family labels what it found and admits it, and says in
58
+ its own docstring what it does with the label.
59
+
60
+ Two machine-route shapes read less than their names suggest, and each stops
61
+ short for a stated reason. A lettered volume with no separator after it does
62
+ not read a single letter or digit after the mark, because a spreadsheet column
63
+ range and a short written value are both written that way and name ordinary
64
+ positions inside an artefact; a volume followed by one character and nothing
65
+ else is admitted with them. The declared reading of a path from the root does
66
+ not read a separator that follows a space and a number, because that is how a
67
+ rate is written in a sentence; a path from the root written straight after a
68
+ number is admitted with it. Each narrowing leaves the route it was written for
69
+ still read, and what each gives up is named here and nowhere else.
70
+
71
+ Six further forms are read at the three boundaries that read one declared value
72
+ and stay unread at the one boundary that reads whole shipped files, because in
73
+ a page of prose they would report the product's own documentation rather than a
74
+ leak. They are the prose and the declared-value readings of an absolute path,
75
+ the declared-value reading of a traversal, a lettered volume with no separator
76
+ after it, a share host with nothing after it, and, in program source and the
77
+ JSON family only, a relative path written with a machine path's separator,
78
+ where those bytes write a character escape instead. The scan states each
79
+ allowance with its reason in its own module.
80
+
81
+ Three address forms are allowed at that same scan and at no other boundary,
82
+ because no declared value and no tracker note has any reason to carry an
83
+ address at all. They are declared below: the secure web scheme, which is how
84
+ this product's documentation links to published material; the insecure web
85
+ scheme where the drawing namespace follows it, which every generated diagram
86
+ declares; and this product's own logical reference scheme, which names a record
87
+ inside this product and no host. The file scheme and the insecure web scheme
88
+ are named below too, because the scan reports each of them under a finding of
89
+ its own rather than as a generic address.
90
+ """
91
+ from __future__ import annotations
92
+
93
+ # evorthon-component: boundary_patterns
94
+
95
+ import re
96
+ from collections.abc import Mapping, Sequence
97
+ from types import MappingProxyType
98
+
99
+ # The characters a machine route is written from, by code point, so no route
100
+ # is spelled out in this file.
101
+ _ESCAPE = chr(92)
102
+ _MARK = chr(58)
103
+ _STOP = chr(46)
104
+ # One separator of a machine path, and one segment of a relative one.
105
+ _SEPARATOR = "[" + _ESCAPE * 2 + "/]"
106
+ _SEGMENT = "[A-Za-z0-9_" + _STOP + "-]"
107
+ # The two steps upward a traversal is written with, escaped for a pattern.
108
+ _UPWARD = _ESCAPE + _STOP + _ESCAPE + _STOP
109
+ # One part of a written address, up to the next mark, separator or host sign.
110
+ _ADDRESS_PART = r"[^/\s" + _MARK + "@]+"
111
+
112
+ DRIVE_PATH = "drive path"
113
+ DRIVE_RELATIVE_PATH = "drive-relative path"
114
+ NETWORK_SHARE_PATH = "network share path"
115
+ NETWORK_SHARE_HOST = "network share host"
116
+ USER_HOME_PATH = "user home path"
117
+ ABSOLUTE_PATH = "absolute path"
118
+ DECLARED_ABSOLUTE_PATH = "declared absolute path"
119
+ LOCAL_FILE_ADDRESS = "local file address"
120
+ WEB_ADDRESS = "web address"
121
+ SCHEME_ADDRESS = "scheme address"
122
+ TRAVERSAL = "traversal"
123
+ DECLARED_TRAVERSAL = "declared traversal"
124
+ BACKSLASH_RELATIVE_PATH = "backslash-relative path"
125
+
126
+ MACHINE_ROUTE_PATTERNS: Mapping[str, re.Pattern[str]] = MappingProxyType(
127
+ {
128
+ # A lettered volume on a machine.
129
+ DRIVE_PATH: re.compile(r"\b[a-z]" + _MARK + _SEPARATOR, re.I),
130
+ # The same volume with no separator after it, which names wherever the
131
+ # machine stands on that volume. A single letter or digit that ends
132
+ # there is not read: that is a column range or a short written value,
133
+ # not a volume.
134
+ DRIVE_RELATIVE_PATH: re.compile(
135
+ r"\b[a-z]" + _MARK + r"(?![a-z0-9]\b)[^" + _ESCAPE * 2 + r"/\s]", re.I
136
+ ),
137
+ # A share on another machine, named by two separators and a host.
138
+ NETWORK_SHARE_PATH: re.compile(
139
+ _ESCAPE * 4 + "[^" + _ESCAPE * 2 + "/]+" + _SEPARATOR
140
+ ),
141
+ # The host half of that share, with nothing after it. It names the
142
+ # machine even when no share on it is named yet.
143
+ NETWORK_SHARE_HOST: re.compile(
144
+ _ESCAPE * 4 + "[^" + _ESCAPE * 2 + r"/\s]"
145
+ ),
146
+ # One person's directory on a machine.
147
+ USER_HOME_PATH: re.compile(r"/(?:home|users)/[^/\s]+", re.I),
148
+ # A path from the root of a machine, read in prose. The first segment
149
+ # must continue into another one or end in a suffix, because a bare
150
+ # slash between two names is how a rate or a pair of sides is written
151
+ # in a sentence.
152
+ ABSOLUTE_PATH: re.compile(
153
+ r"(?m)(?:^|\s)/" + _SEGMENT + "+(?:" + _SEPARATOR + "|" + _ESCAPE + _STOP + "[A-Za-z0-9]{1,8}" + r"\b)"
154
+ ),
155
+ # The same path read in a declared value, where one segment is enough
156
+ # because the value is not a sentence. A separator that follows a space
157
+ # and a number is not read: that is a rate, not a path.
158
+ DECLARED_ABSOLUTE_PATH: re.compile(r"(?m)(?:^|(?<![0-9])\s)/" + _SEGMENT),
159
+ # A file on a machine, written as an address.
160
+ LOCAL_FILE_ADDRESS: re.compile(r"\bfile" + _MARK + r"\s*//", re.I),
161
+ # A host reached over the network, written as an address.
162
+ WEB_ADDRESS: re.compile(r"\bhttps?" + _MARK + "//", re.I),
163
+ # An address under any scheme at all, which is how a cloud store, a
164
+ # transfer, a share, a database and a lake are all written. The forms
165
+ # one boundary allows are declared below and nowhere else.
166
+ SCHEME_ADDRESS: re.compile(r"\b[a-z][a-z0-9]*" + _MARK + "//", re.I),
167
+ # A route that climbs out of where it starts, read in prose at the
168
+ # start of a value or a line.
169
+ TRAVERSAL: re.compile(r"(?m)(?:^|\s)" + _UPWARD + _SEPARATOR),
170
+ # The same climb read in a declared value, wherever it sits in it.
171
+ DECLARED_TRAVERSAL: re.compile(_UPWARD + _SEPARATOR),
172
+ # A relative path written with the separator a machine path uses.
173
+ BACKSLASH_RELATIVE_PATH: re.compile(_SEGMENT + _ESCAPE * 2 + _SEGMENT),
174
+ }
175
+ )
176
+
177
+ # The address forms one boundary allows, declared once here so the scan that
178
+ # allows them and the boundaries that refuse them read the same text. A
179
+ # declared value and a tracker note carry no address at all, so every boundary
180
+ # but the shipped-file scan refuses each of these too.
181
+ SECURE_WEB_SCHEME = "https" + _MARK + "//"
182
+ INSECURE_WEB_SCHEME = "http" + _MARK + "//"
183
+ LOCAL_FILE_SCHEME = "file" + _MARK + "//"
184
+ LOGICAL_REFERENCE_SCHEME = "koine" + _MARK + "//"
185
+ # The one root this product writes after that scheme. The scheme alone names no
186
+ # record, so the allowance below is the composed form and not the prefix.
187
+ LOGICAL_REFERENCE_ROOT = "use-case/"
188
+ LOGICAL_REFERENCE_FORM = LOGICAL_REFERENCE_SCHEME + LOGICAL_REFERENCE_ROOT
189
+ DRAWING_NAMESPACE = "www" + _STOP + "w3" + _STOP + "org/2000/svg"
190
+ # The three schemes a boundary may name in a finding of its own rather than as
191
+ # a generic address, and the one form that is not a route to any machine.
192
+ NAMED_SCHEME_FORMS: tuple[str, ...] = (
193
+ SECURE_WEB_SCHEME,
194
+ INSECURE_WEB_SCHEME,
195
+ LOCAL_FILE_SCHEME,
196
+ )
197
+ ALLOWED_SCHEME_FORMS: tuple[str, ...] = (SECURE_WEB_SCHEME, LOGICAL_REFERENCE_FORM)
198
+
199
+ BARE_HOST = "bare host"
200
+ CONNECTION_STRING = "connection string"
201
+
202
+ # The advisory family. Nothing refuses on these; a boundary that reads them
203
+ # labels what it found. Both forms are written by people who mean no harm as
204
+ # often as by a leak, which is why the reading stops at a label.
205
+ ADVISORY_PATTERNS: Mapping[str, re.Pattern[str]] = MappingProxyType(
206
+ {
207
+ # A dotted name that reads like a server. Three or more labels in front
208
+ # of a separator, because two labels in front of one is how an ordinary
209
+ # file inside a directory is written; or a dotted name in front of a
210
+ # port, where the port is the part that makes it an address.
211
+ BARE_HOST: re.compile(
212
+ r"\b(?:[a-z0-9-]+" + _ESCAPE + _STOP + r"){2,}[a-z0-9-]+(?=" + _SEPARATOR + ")"
213
+ r"|\b(?:[a-z0-9-]+" + _ESCAPE + _STOP + r")+[a-z0-9-]+" + _MARK + r"[0-9]{1,5}\b",
214
+ re.I,
215
+ ),
216
+ # A connection key written against a value, which is how a driver is
217
+ # told where to connect.
218
+ CONNECTION_STRING: re.compile(
219
+ r"\b(?:server|host|hostname|data[ _-]?source|initial[ _-]?catalog|endpoint|dsn)"
220
+ r"[ \t]*=[ \t]*[\"']?\S",
221
+ re.I,
222
+ ),
223
+ }
224
+ )
225
+
226
+ NAMED_SECRET = "named secret"
227
+ NAMED_KEY = "named key"
228
+ PRESENTED_TOKEN = "presented token"
229
+ KEY_BLOCK = "key block"
230
+ ASSIGNED_SECRET = "assigned secret"
231
+ CONNECTION_CREDENTIAL = "connection credential"
232
+ BEARER_AUTHORIZATION = "bearer authorization"
233
+
234
+ CREDENTIAL_PATTERNS: Mapping[str, re.Pattern[str]] = MappingProxyType(
235
+ {
236
+ # The word for a secret, written in a sentence.
237
+ NAMED_SECRET: re.compile(r"\b(?:password|passphrase|secret|credential)s?\b", re.I),
238
+ # The word for a key, written in a sentence.
239
+ NAMED_KEY: re.compile(r"\b(?:api|access|private|signing)[_ -]?key\b", re.I),
240
+ # A token presented for authorization.
241
+ PRESENTED_TOKEN: re.compile(r"\b(?:bearer|basic)\s+\S{8,}", re.I),
242
+ # The opening line of a stored key of any kind. The shape reads what it
243
+ # catches: a private key most of the time, a public one sometimes, and
244
+ # the name says so rather than claiming the narrower thing.
245
+ KEY_BLOCK: re.compile("-{5}begin[a-z ]*key-{5}", re.I),
246
+ # A named secret with a value written against it.
247
+ ASSIGNED_SECRET: re.compile(
248
+ r"\b(?:password|passwd|secret|api[_-]?key|access[_-]?key|"
249
+ r"client[_-]?secret|auth[_-]?token|credential)\b[ \t]*[" + _MARK + r"=][ \t]*"
250
+ r"[\"']?[^\s\"']{8,}",
251
+ re.I,
252
+ ),
253
+ # A user and a secret written inside an address.
254
+ CONNECTION_CREDENTIAL: re.compile(
255
+ _MARK + "//" + _ADDRESS_PART + _MARK + _ADDRESS_PART + "@"
256
+ ),
257
+ # A presented token written against the header that carries it.
258
+ BEARER_AUTHORIZATION: re.compile(
259
+ r"\bauthorization\b[ \t]*[" + _MARK + r"=][ \t]*[\"']?bearer[ \t]+\S+", re.I
260
+ ),
261
+ }
262
+ )
263
+
264
+ CONTROL_CHARACTER = "control character"
265
+ WRITTEN_OBJECT = "written object"
266
+ DELIMITED_FIELDS = "delimited fields"
267
+
268
+ RAW_ROW_PATTERNS: Mapping[str, re.Pattern[str]] = MappingProxyType(
269
+ {
270
+ # A record separator, or anything else below the printable range.
271
+ CONTROL_CHARACTER: re.compile(r"[\x00-\x1f]"),
272
+ # A written object with quoted field names.
273
+ WRITTEN_OBJECT: re.compile(r"\{\s*[\"']"),
274
+ # A run of fields written between delimiters.
275
+ DELIMITED_FIELDS: re.compile(r"\|[^|]*\|"),
276
+ }
277
+ )
278
+
279
+ MACHINE_ROUTE_SHAPES: tuple[str, ...] = tuple(MACHINE_ROUTE_PATTERNS)
280
+ CREDENTIAL_SHAPES: tuple[str, ...] = tuple(CREDENTIAL_PATTERNS)
281
+ RAW_ROW_SHAPES: tuple[str, ...] = tuple(RAW_ROW_PATTERNS)
282
+ ADVISORY_SHAPES: tuple[str, ...] = tuple(ADVISORY_PATTERNS)
283
+
284
+
285
+ def shapes(
286
+ patterns: Mapping[str, re.Pattern[str]], names: Sequence[str]
287
+ ) -> tuple[tuple[str, re.Pattern[str]], ...]:
288
+ """Return the named shapes of one family, in the order a boundary named them.
289
+
290
+ A name this module does not declare raises, so a boundary cannot read a
291
+ shape that has no owner and cannot quietly lose one to a rename.
292
+ """
293
+ return tuple((name, patterns[name]) for name in names)
294
+
295
+
296
+ def shape_carried(read: Sequence[tuple[str, re.Pattern[str]]], value: str) -> str | None:
297
+ """Name the first of the given shapes a text carries, or nothing.
298
+
299
+ This reads. What a carried shape means, and what to do about it, belongs to
300
+ the boundary that asked.
301
+ """
302
+ for name, pattern in read:
303
+ if pattern.search(value):
304
+ return name
305
+ return None
306
+
307
+
308
+ __all__ = [
309
+ "ABSOLUTE_PATH",
310
+ "ADVISORY_PATTERNS",
311
+ "ADVISORY_SHAPES",
312
+ "ALLOWED_SCHEME_FORMS",
313
+ "ASSIGNED_SECRET",
314
+ "BACKSLASH_RELATIVE_PATH",
315
+ "BARE_HOST",
316
+ "BEARER_AUTHORIZATION",
317
+ "CONNECTION_CREDENTIAL",
318
+ "CONNECTION_STRING",
319
+ "CONTROL_CHARACTER",
320
+ "CREDENTIAL_PATTERNS",
321
+ "CREDENTIAL_SHAPES",
322
+ "DECLARED_ABSOLUTE_PATH",
323
+ "DECLARED_TRAVERSAL",
324
+ "DELIMITED_FIELDS",
325
+ "DRAWING_NAMESPACE",
326
+ "DRIVE_PATH",
327
+ "DRIVE_RELATIVE_PATH",
328
+ "INSECURE_WEB_SCHEME",
329
+ "LOCAL_FILE_ADDRESS",
330
+ "LOCAL_FILE_SCHEME",
331
+ "LOGICAL_REFERENCE_FORM",
332
+ "LOGICAL_REFERENCE_ROOT",
333
+ "LOGICAL_REFERENCE_SCHEME",
334
+ "MACHINE_ROUTE_PATTERNS",
335
+ "MACHINE_ROUTE_SHAPES",
336
+ "NAMED_KEY",
337
+ "NAMED_SCHEME_FORMS",
338
+ "NAMED_SECRET",
339
+ "NETWORK_SHARE_HOST",
340
+ "NETWORK_SHARE_PATH",
341
+ "PRESENTED_TOKEN",
342
+ "KEY_BLOCK",
343
+ "RAW_ROW_PATTERNS",
344
+ "RAW_ROW_SHAPES",
345
+ "SCHEME_ADDRESS",
346
+ "SECURE_WEB_SCHEME",
347
+ "TRAVERSAL",
348
+ "USER_HOME_PATH",
349
+ "WEB_ADDRESS",
350
+ "WRITTEN_OBJECT",
351
+ "shape_carried",
352
+ "shapes",
353
+ ]