lupaxa-data-poison 0.1.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.
- lupaxa_data_poison-0.1.0/.gitignore +21 -0
- lupaxa_data_poison-0.1.0/LICENCE +21 -0
- lupaxa_data_poison-0.1.0/PKG-INFO +613 -0
- lupaxa_data_poison-0.1.0/README.md +554 -0
- lupaxa_data_poison-0.1.0/docs/README.md +23 -0
- lupaxa_data_poison-0.1.0/pyproject.toml +100 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/__init__.py +16 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/__main__.py +10 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/catalogue.py +196 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/__init__.py +23 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/csv_doc.py +167 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/dates.py +274 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/emails.py +186 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/filenames.py +236 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/json_doc.py +237 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/numbers.py +347 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/unicode_text.py +364 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/urls.py +276 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/categories/xml_doc.py +159 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/cli.py +518 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/config.py +96 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/engine.py +142 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/errors.py +39 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/limits.py +29 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/manifest.py +130 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/models.py +81 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/mutate.py +253 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/output.py +112 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/profiles.py +77 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/py.typed +0 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/recipe.py +168 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/report.py +94 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/runner.py +250 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/schema_gen.py +304 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/shrink.py +159 -0
- lupaxa_data_poison-0.1.0/src/lupaxa/data_poison/version.py +12 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) The Lupaxa Project
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lupaxa-data-poison
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate, mutate, explain, test and minimise difficult or malformed data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/lupaxa-developers-toolbox/data-poison
|
|
6
|
+
Project-URL: Documentation, https://github.com/lupaxa-developers-toolbox/data-poison
|
|
7
|
+
Project-URL: Repository, https://github.com/lupaxa-developers-toolbox/data-poison
|
|
8
|
+
Project-URL: Issues, https://github.com/lupaxa-developers-toolbox/data-poison/issues
|
|
9
|
+
Author: The Lupaxa Project
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) The Lupaxa Project
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENCE
|
|
32
|
+
Keywords: cli,fuzzing,json,testing,unicode
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Environment :: Console
|
|
35
|
+
Classifier: Intended Audience :: Developers
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
43
|
+
Classifier: Topic :: Software Development :: Testing
|
|
44
|
+
Classifier: Topic :: Utilities
|
|
45
|
+
Requires-Python: >=3.11
|
|
46
|
+
Requires-Dist: defusedxml>=0.7
|
|
47
|
+
Requires-Dist: jsonschema>=4.18
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: bump-my-version>=1.2.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: hatch>=1.12.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: mypy>=1.12.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
53
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
54
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
55
|
+
Provides-Extra: test
|
|
56
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'test'
|
|
57
|
+
Requires-Dist: pytest>=8.0; extra == 'test'
|
|
58
|
+
Description-Content-Type: text/markdown
|
|
59
|
+
|
|
60
|
+
<p align="center">
|
|
61
|
+
<a href="https://github.com/lupaxa-developers-toolbox">
|
|
62
|
+
<img src="https://raw.githubusercontent.com/the-lupaxa-project/brand-assets/master/logos/organisations/developers-toolbox/readme-logo.png" alt="Developers Toolbox" />
|
|
63
|
+
</a>
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
<h1 align="center">Data Poison</h1>
|
|
67
|
+
|
|
68
|
+
Generate, mutate, explain, test, and minimise difficult or malformed data.
|
|
69
|
+
Every case says what it is, why it is unusual, and which behaviour it is
|
|
70
|
+
meant to expose. The same seed produces the same bytes.
|
|
71
|
+
|
|
72
|
+
The PyPI name is `lupaxa-data-poison`. The import path is
|
|
73
|
+
`lupaxa.data_poison`. The console script is `data-poison`. `lupaxa` is a
|
|
74
|
+
namespace package — there is no `lupaxa/__init__.py`.
|
|
75
|
+
|
|
76
|
+
Public names: `generate`, `mutate_payload`, `Case`, `__version__`,
|
|
77
|
+
`get_version()`.
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install lupaxa-data-poison
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Requires Python 3.11 or newer. Runtime dependencies are `jsonschema` and
|
|
86
|
+
`defusedxml`. The tool does not use the network.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
python -m lupaxa.data_poison --version
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
data-poison unicode --count 5 --seed 12345
|
|
96
|
+
data-poison explain unicode.zero-width
|
|
97
|
+
data-poison catalogue --category json --validity malformed
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## What it Does
|
|
101
|
+
|
|
102
|
+
A case is a payload plus metadata: identifier, category, generator,
|
|
103
|
+
encoding, validity, contract, explanation, intended behaviour, seed, and
|
|
104
|
+
the generator and catalogue versions. Payloads may be text or raw bytes.
|
|
105
|
+
|
|
106
|
+
Generation reads a built-in catalogue. Profiles choose which risks and
|
|
107
|
+
validity classes are in play. `--case` asks for one named case. Mutation
|
|
108
|
+
changes a copy of a file. A recipe repeats a job from TOML. `test` runs a
|
|
109
|
+
program you name. `shrink` shortens a payload that still fails the same
|
|
110
|
+
way. `replay` returns saved bytes and checks their SHA-256.
|
|
111
|
+
|
|
112
|
+
> **Note:** Shrinking is a heuristic. A smaller payload is not a proof
|
|
113
|
+
> that no smaller one exists. URL and email checks are syntax policies.
|
|
114
|
+
> They do not mean a host or a mailbox exists.
|
|
115
|
+
|
|
116
|
+
## Validity
|
|
117
|
+
|
|
118
|
+
| Class | Meaning |
|
|
119
|
+
| ----------- | -------------------------------------------------------------------- |
|
|
120
|
+
| `valid` | Allowed by the named contract, and still a likely surprise. |
|
|
121
|
+
| `invalid` | Representable, but it breaks a declared rule in that contract. |
|
|
122
|
+
| `malformed` | It breaks the syntax or encoding of that contract. |
|
|
123
|
+
| `unknown` | The tool will not guess. This is a qualification, not a fourth mode. |
|
|
124
|
+
|
|
125
|
+
The contract is part of the case. A newline can be valid text and invalid
|
|
126
|
+
for one field. JSON `NaN` is malformed JSON and a normal IEEE-754 value
|
|
127
|
+
when the contract is a Python float.
|
|
128
|
+
|
|
129
|
+
## Command Line
|
|
130
|
+
|
|
131
|
+
| Command | Purpose |
|
|
132
|
+
| ----------- | -------------------------------------------- |
|
|
133
|
+
| `generate` | Cases from the whole catalogue |
|
|
134
|
+
| `unicode` | Text and encoding edges |
|
|
135
|
+
| `numbers` | Integers, floats, decimals, and JSON numbers |
|
|
136
|
+
| `dates` | Calendar and timezone edges |
|
|
137
|
+
| `urls` | URL syntax, with no DNS |
|
|
138
|
+
| `emails` | Address syntax, with no SMTP |
|
|
139
|
+
| `filenames` | Name strings, never created on disk |
|
|
140
|
+
| `json` | Valid and malformed JSON documents |
|
|
141
|
+
| `csv` | One strict comma dialect |
|
|
142
|
+
| `xml` | XML with entity expansion disabled |
|
|
143
|
+
| `schema` | A documented JSON Schema subset |
|
|
144
|
+
| `mutate` | A copy of an existing file |
|
|
145
|
+
| `recipe` | A TOML job |
|
|
146
|
+
| `test` | A local program, not a sandbox |
|
|
147
|
+
| `shrink` | A smaller payload with the same failure |
|
|
148
|
+
| `replay` | Saved bytes and a hash check |
|
|
149
|
+
| `explain` | One catalogue entry |
|
|
150
|
+
| `catalogue` | Filter the catalogue |
|
|
151
|
+
| `profiles` | The five profiles |
|
|
152
|
+
| `inspect` | A manifest, without dumping payloads |
|
|
153
|
+
| `report` | A saved test report |
|
|
154
|
+
|
|
155
|
+
`--help` works on the root command and on every subcommand. `--version`
|
|
156
|
+
prints `data-poison x.y.z`.
|
|
157
|
+
|
|
158
|
+
| Flag | Default | Meaning |
|
|
159
|
+
| ----------------------- | ---------------------------------------- | ------------------------------------------------------------------------- |
|
|
160
|
+
| `--count` | `1` | How many cases. |
|
|
161
|
+
| `--seed` | chosen and stored | Repeatable selection. |
|
|
162
|
+
| `--profile` | `awkward` | `mild`, `awkward`, `hostile`, `nightmare`, or `apocalypse`. |
|
|
163
|
+
| `--case` | | One catalogue identifier or a unique suffix. |
|
|
164
|
+
| `--category` | the command name | Comma-separated categories for `generate` and `test`. |
|
|
165
|
+
| `--validity` | profile | `valid`, `invalid`, `malformed`, or `all`. |
|
|
166
|
+
| `--format` | `text`, or `jsonl` when count is above 1 | `text`, `json`, `jsonl`, `raw`, `hex`, `base64`. |
|
|
167
|
+
| `--output` | stdout | File or directory. |
|
|
168
|
+
| `--manifest` | | JSON manifest path. |
|
|
169
|
+
| `--max-bytes` | 1 MiB | Per-case ceiling, hard stop 32 MiB. |
|
|
170
|
+
| `--max-depth` | 32 | Nesting ceiling, hard stop 128. |
|
|
171
|
+
| `--max-total-bytes` | 100 MiB | Run ceiling, hard stop 512 MiB. |
|
|
172
|
+
| `--quiet` / `--verbose` | quiet | Errors still go to stderr. |
|
|
173
|
+
| `--force` | off | Overwrite an existing output file. |
|
|
174
|
+
| `--frame length-prefix` | | Several raw payloads on one stream: 4-byte big-endian length, then bytes. |
|
|
175
|
+
|
|
176
|
+
A single `--format raw` write is the payload bytes only. No newline and no
|
|
177
|
+
label. Several raw cases need a directory or `--frame length-prefix`.
|
|
178
|
+
|
|
179
|
+
| Exit | Meaning |
|
|
180
|
+
| ----- | ---------------------------------------------------------- |
|
|
181
|
+
| `0` | Finished. |
|
|
182
|
+
| `1` | Operational error, or shrink could not keep the failure. |
|
|
183
|
+
| `2` | Bad arguments or configuration. |
|
|
184
|
+
| `3` | Input, parse, or validation error. |
|
|
185
|
+
| `4` | A safety ceiling. |
|
|
186
|
+
| `5` | The test run found a crash, timeout, or unexpected result. |
|
|
187
|
+
| `6` | Replay hash or integrity failure. |
|
|
188
|
+
| `130` | Interrupted. |
|
|
189
|
+
|
|
190
|
+
Expected invalid data does not make generation fail. A non-zero program
|
|
191
|
+
exit is a rejection, not a crash. A crash is a signal.
|
|
192
|
+
|
|
193
|
+
## Profiles
|
|
194
|
+
|
|
195
|
+
`mild` is empty values, whitespace, and small boundaries. `awkward` is
|
|
196
|
+
valid input that still surprises. `hostile` is invalid and malformed.
|
|
197
|
+
`nightmare` mixes those and stacks mutations in the profile description.
|
|
198
|
+
`apocalypse` pushes size and depth up to the ceilings you set, never past
|
|
199
|
+
the hard caps.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
data-poison profiles
|
|
203
|
+
data-poison profiles show nightmare
|
|
204
|
+
data-poison generate --profile apocalypse --count 10 --seed 1
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Catalogue
|
|
208
|
+
|
|
209
|
+
Identifiers stay put until the meaning changes. `unicode.zero-width` is
|
|
210
|
+
the stable name. Fixed cases return the same bytes every time.
|
|
211
|
+
Parameterised cases may use the seed and the limits.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
data-poison catalogue
|
|
215
|
+
data-poison catalogue --category unicode
|
|
216
|
+
data-poison catalogue --validity malformed
|
|
217
|
+
data-poison catalogue --tag boundary
|
|
218
|
+
data-poison explain unicode.normalisation.nfc-nfd
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Text and JSON catalogue output are both supported.
|
|
222
|
+
|
|
223
|
+
## Unicode
|
|
224
|
+
|
|
225
|
+
The catalogue includes empty strings, whitespace, newlines, non-breaking
|
|
226
|
+
and zero-width characters, joiners, combining marks, NFC/NFD and
|
|
227
|
+
NFKC/NFKD samples, bidi controls, right-to-left text, confusables, emoji
|
|
228
|
+
sequences, skin tones, regional indicators, variation selectors,
|
|
229
|
+
supplementary-plane characters, a noncharacter, long strings, NUL and
|
|
230
|
+
ASCII controls, line and paragraph separators, Turkish I, sharp S, mixed
|
|
231
|
+
scripts, and invalid, truncated, and surrogate UTF-8.
|
|
232
|
+
|
|
233
|
+
Confusable characters are not treated as the same character. Normalisation
|
|
234
|
+
form names are NFC, NFD, NFKC, and NFKD. Explanations name code points
|
|
235
|
+
when Python knows the name.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
data-poison unicode --count 20 --seed 1
|
|
239
|
+
data-poison unicode --case zero-width
|
|
240
|
+
data-poison unicode --profile nightmare --count 5
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Numbers
|
|
244
|
+
|
|
245
|
+
Modes are integer, float, decimal, text, and JSON. `--bits` and
|
|
246
|
+
`--signed` / `--no-signed` apply to integer bounds. `--minimum` and
|
|
247
|
+
`--maximum` apply to the outside-range case. `--precision` and `--scale`
|
|
248
|
+
are accepted for decimal work and ignored by cases that do not use them.
|
|
249
|
+
`--bits` with `--numeric-mode float` is an error.
|
|
250
|
+
|
|
251
|
+
JSON number cases never emit `NaN` or `Infinity`. Those tokens exist as
|
|
252
|
+
malformed JSON and, separately, as IEEE-754 text.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
data-poison numbers --case float-precision
|
|
256
|
+
data-poison numbers --case integer-bounds --bits 32 --signed
|
|
257
|
+
data-poison numbers --case outside-range --minimum 0 --maximum 100
|
|
258
|
+
data-poison numbers --case json-overflow
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Dates
|
|
262
|
+
|
|
263
|
+
Leap day, non-leap 29 February, month and year boundaries, midnight, hour
|
|
264
|
+
24, offsets, fractional seconds, ISO and RFC 3339 samples, epoch values,
|
|
265
|
+
date-only and time-only values, and naive versus aware timestamps are in
|
|
266
|
+
the catalogue.
|
|
267
|
+
|
|
268
|
+
`--timezone` selects an IANA zone for `dates.dst-gap` and
|
|
269
|
+
`dates.dst-overlap`. The gap and overlap come from that zone's 2024
|
|
270
|
+
transitions. The defaults are `Europe/London` and `America/New_York`. A
|
|
271
|
+
zone with no such transition, or a machine without timezone data, is an
|
|
272
|
+
error rather than a made-up clock change.
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
data-poison dates --case leap-year
|
|
276
|
+
data-poison dates --case dst-gap --timezone Europe/London
|
|
277
|
+
data-poison dates --case dst-overlap --timezone America/New_York
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## URLs
|
|
281
|
+
|
|
282
|
+
No DNS and no HTTP. The contracts are `absolute-url`,
|
|
283
|
+
`relative-reference`, and `url-syntax`. A syntactically absolute URL can
|
|
284
|
+
still be one an application should reject, such as `javascript:`.
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
data-poison urls --count 10 --seed 1
|
|
288
|
+
data-poison urls --case percent-encoding
|
|
289
|
+
data-poison urls --case ipv6
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Emails
|
|
293
|
+
|
|
294
|
+
Policies are `conservative` (everyday ASCII, plus-tags allowed),
|
|
295
|
+
`standards` (a documented subset that allows a quoted local part and
|
|
296
|
+
Unicode), and `ascii` (non-ASCII is invalid). None of them send mail.
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
data-poison emails --profile hostile --count 5 --seed 1
|
|
300
|
+
data-poison emails --case unicode-local --policy ascii
|
|
301
|
+
data-poison emails --case quoted-local --policy standards
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Filenames
|
|
305
|
+
|
|
306
|
+
Cases are strings. They are not created, and they are not used as the
|
|
307
|
+
`--output` path. Contracts cover POSIX names, POSIX paths, Windows names,
|
|
308
|
+
Windows paths, and macOS normalisation. `--platform` is `posix`,
|
|
309
|
+
`windows`, or `macos`.
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
data-poison filenames --platform windows --case reserved-name
|
|
313
|
+
data-poison filenames --case unicode-collision
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## JSON, CSV, and XML
|
|
317
|
+
|
|
318
|
+
Valid JSON is parsed by the standard library. Malformed JSON is emitted
|
|
319
|
+
as raw text and is not repaired. Duplicate keys are valid syntax and an
|
|
320
|
+
interoperability hazard, not a syntax error.
|
|
321
|
+
|
|
322
|
+
CSV uses comma, double quote, and LF. A header is part of that contract.
|
|
323
|
+
Values that start with `=`, `+`, `-`, or `@` are flagged because a
|
|
324
|
+
spreadsheet may treat them as formulas. This tool does not open them.
|
|
325
|
+
|
|
326
|
+
XML parsing uses `defusedxml`. External entities and expansion are not
|
|
327
|
+
resolved. The entity-expansion fixture is a few characters, not a large
|
|
328
|
+
bomb. Deep nesting stops at `--max-depth`.
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
data-poison json --validity valid --count 10 --seed 1
|
|
332
|
+
data-poison json --validity malformed --count 5 --seed 1
|
|
333
|
+
data-poison json --case duplicate-keys
|
|
334
|
+
data-poison csv --case embedded-newline
|
|
335
|
+
data-poison xml --case mismatched-tags
|
|
336
|
+
data-poison xml --case entity-expansion
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Schema
|
|
340
|
+
|
|
341
|
+
`schema` reads a local JSON Schema document. The supported Draft 2020-12
|
|
342
|
+
subset is `type`, `properties`, `required`, boolean `additionalProperties`,
|
|
343
|
+
`items`, size and length bounds, `minimum` and `maximum` including the
|
|
344
|
+
exclusive forms, `multipleOf`, `enum`, `const`, a short `pattern`,
|
|
345
|
+
`format` of `email`, `date-time`, `uri`, `uuid`, `ipv4`, and `ipv6`,
|
|
346
|
+
property counts, `uniqueItems`, and local `$defs` / `$ref`.
|
|
347
|
+
|
|
348
|
+
Anything else that would change validation is an error in the default
|
|
349
|
+
strict mode. `--permissive` still generates and marks validity `unknown`.
|
|
350
|
+
Remote `$ref` is never fetched.
|
|
351
|
+
|
|
352
|
+
Modes are `valid`, `invalid`, `boundary`, and `mixed`. Invalid cases aim
|
|
353
|
+
at one constraint. Each case records the expected result, the validator
|
|
354
|
+
result, and the constraint name.
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
data-poison schema customer.schema.json --count 20 --seed 1
|
|
358
|
+
data-poison schema customer.schema.json --mode boundary
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Recipes
|
|
362
|
+
|
|
363
|
+
Paths in a recipe are relative to the recipe file. Unknown keys, bad
|
|
364
|
+
types, a minimum above a maximum, and a missing schema path fail
|
|
365
|
+
validation. `--count` and `--seed` on the command replace the recipe.
|
|
366
|
+
|
|
367
|
+
```toml
|
|
368
|
+
[recipe]
|
|
369
|
+
name = "Customer Registration"
|
|
370
|
+
description = "Difficult customer fields"
|
|
371
|
+
count = 20
|
|
372
|
+
seed = 12345
|
|
373
|
+
profile = "nightmare"
|
|
374
|
+
|
|
375
|
+
[fields.username]
|
|
376
|
+
type = "string"
|
|
377
|
+
poison = ["unicode", "whitespace", "length"]
|
|
378
|
+
|
|
379
|
+
[fields.age]
|
|
380
|
+
type = "integer"
|
|
381
|
+
minimum = 0
|
|
382
|
+
maximum = 120
|
|
383
|
+
poison = ["boundaries", "wrong-type", "null"]
|
|
384
|
+
|
|
385
|
+
[fields.email]
|
|
386
|
+
type = "email"
|
|
387
|
+
poison = ["format"]
|
|
388
|
+
|
|
389
|
+
[output]
|
|
390
|
+
format = "jsonl"
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
data-poison recipe customer.toml --validate
|
|
395
|
+
data-poison recipe customer.toml --seed 999
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Field poison values are `unicode`, `whitespace`, `length`, `boundaries`,
|
|
399
|
+
`wrong-type`, `null`, and `format`.
|
|
400
|
+
|
|
401
|
+
## Mutation
|
|
402
|
+
|
|
403
|
+
`mutate` reads a file and writes new payloads. It does not open the
|
|
404
|
+
source for writing. Modes are `semantic` (JSON shape kept, then
|
|
405
|
+
re-serialised), `syntax` (deliberately broken), and `bytes`. Strategies
|
|
406
|
+
are `single`, `combined`, `systematic`, and `random`.
|
|
407
|
+
|
|
408
|
+
The strategy names include character and byte edits, whitespace and
|
|
409
|
+
controls, Unicode substitution, truncation, field removal and
|
|
410
|
+
duplication, type and null swaps, numeric and length changes, array
|
|
411
|
+
edits, nesting, and delimiter, quote, escape, and encoding corruption.
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
data-poison mutate customer.json --strategy single --count 10 --seed 1
|
|
415
|
+
data-poison mutate customer.json --mode syntax --output mutations/
|
|
416
|
+
data-poison mutate input.bin --mode bytes --count 5 --seed 1
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## Test Runner
|
|
420
|
+
|
|
421
|
+
You name the command. The default delivery is stdin. `--input-mode file`
|
|
422
|
+
writes a temporary file and deletes it afterwards. `{input}` is replaced
|
|
423
|
+
as one argument. The command is split with `shlex` and run with
|
|
424
|
+
`shell` left off, so the payload is not a shell script.
|
|
425
|
+
|
|
426
|
+
A timeout, a captured-output cap, and the case limit apply. A non-zero
|
|
427
|
+
exit is `rejected_expected` when the case is invalid or malformed, and
|
|
428
|
+
`unexpected_rejection` when the case is valid. Exit 0 on invalid input is
|
|
429
|
+
`unexpected_acceptance`. A signal is `crash`. Expiry is `timeout`. A
|
|
430
|
+
missing program is `execution_error`. Anything the oracle cannot judge is
|
|
431
|
+
`inconclusive`.
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
data-poison test --command "./validate-customer" --category json --count 20 --seed 1
|
|
435
|
+
data-poison test --command "./parser {input}" --input-mode file --count 5 --seed 1
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
> **Note:** The runner is not a security sandbox. It runs the command you
|
|
439
|
+
> typed, with your user privileges.
|
|
440
|
+
|
|
441
|
+
## Shrinking
|
|
442
|
+
|
|
443
|
+
`shrink` keeps a predicate: `same-exit`, `same-signal`, `timeout`, or
|
|
444
|
+
`nonzero`. It deletes byte ranges, caches candidates by SHA-256, and
|
|
445
|
+
stops at `--attempts`. The original file is only read. The report gives
|
|
446
|
+
the old size, the new size, the reduction, and the attempt count, and it
|
|
447
|
+
says the result is not mathematically minimal.
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
data-poison shrink failing.bin --command "./validate-json" --predicate same-exit --output minimal.bin
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Replay and Manifests
|
|
454
|
+
|
|
455
|
+
A manifest records version, case id, seed, category, generator, validity,
|
|
456
|
+
contract, mutations, encoding, length, and SHA-256. Payloads are inline
|
|
457
|
+
base64 or a sibling `.bin` whose path cannot leave the manifest
|
|
458
|
+
directory. `replay --verify` checks the hash. Saved bytes stay replayable
|
|
459
|
+
even if a later generator changes. A hash mismatch exits `6`.
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
data-poison replay cases/manifest.json --verify --format raw --output reproduced.bin
|
|
463
|
+
data-poison inspect cases/manifest.json
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Reports
|
|
467
|
+
|
|
468
|
+
`test` can write `--report results.json`. `report` prints text, JSON, or
|
|
469
|
+
Markdown: counts by validity and by result class, durations, and total
|
|
470
|
+
bytes. There is no single quality score.
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
data-poison report results.json --format markdown
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Output Formats
|
|
477
|
+
|
|
478
|
+
`text` escapes control characters. `json` and `jsonl` carry base64 plus
|
|
479
|
+
hex so binary payloads are not replaced with U+FFFD. `hex` and `base64`
|
|
480
|
+
are one payload per line. `raw` is the exact bytes.
|
|
481
|
+
|
|
482
|
+
## Safety Limits
|
|
483
|
+
|
|
484
|
+
Defaults are one case, profile `awkward`, 1 MiB per payload, depth 32,
|
|
485
|
+
and 100 MiB in total. You can raise them. The hard ceilings are 100,000
|
|
486
|
+
cases, 32 MiB per payload, depth 128, and 512 MiB in total. Existing
|
|
487
|
+
files are not overwritten without `--force`. Mutation inputs are not
|
|
488
|
+
modified. Manifests and reports are written by replacing a temporary
|
|
489
|
+
file. Temporary command inputs are removed unless
|
|
490
|
+
`keep_temporary` is set in the config.
|
|
491
|
+
|
|
492
|
+
## Configuration
|
|
493
|
+
|
|
494
|
+
TOML. Precedence is the command line, then the recipe, then `--config`,
|
|
495
|
+
then `~/.config/data-poison/config.toml`, then built-in defaults. Unknown
|
|
496
|
+
keys are an error.
|
|
497
|
+
|
|
498
|
+
```toml
|
|
499
|
+
profile = "awkward"
|
|
500
|
+
count = 1
|
|
501
|
+
format = "text"
|
|
502
|
+
max_bytes = 1048576
|
|
503
|
+
timeout_seconds = 5
|
|
504
|
+
expect = "by-validity"
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
`expect` may be `by-validity`, `accept`, or `reject`.
|
|
508
|
+
|
|
509
|
+
## Platform Differences
|
|
510
|
+
|
|
511
|
+
Filename contracts differ for Windows, POSIX, and macOS. They describe
|
|
512
|
+
operating-system name rules, not one vendor's filesystem. Timezone cases
|
|
513
|
+
need IANA data; if it is missing, the command says so. Process signals
|
|
514
|
+
and process-tree kill behaviour follow the host. The tool does not claim
|
|
515
|
+
identical signal numbers on Windows.
|
|
516
|
+
|
|
517
|
+
## Python API
|
|
518
|
+
|
|
519
|
+
```python
|
|
520
|
+
from lupaxa.data_poison import generate
|
|
521
|
+
|
|
522
|
+
cases = generate(count=3, seed=12345, categories=["unicode"], profile="awkward")
|
|
523
|
+
print(cases[0].explanation)
|
|
524
|
+
print(cases[0].payload)
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Generators return `Case` values. They do not print. `mutate_payload`
|
|
528
|
+
works on bytes the same way.
|
|
529
|
+
|
|
530
|
+
## Troubleshooting
|
|
531
|
+
|
|
532
|
+
`unknown case` means the identifier or suffix matched nothing, or matched
|
|
533
|
+
more than one entry. Use `catalogue` and the full identifier. `unsupported
|
|
534
|
+
schema keyword` means strict mode refused a feature outside the subset.
|
|
535
|
+
`timezone ... is not available` means the IANA database has no such zone.
|
|
536
|
+
`already exists` means pass `--force`. A hash error on replay means the
|
|
537
|
+
payload bytes and the manifest disagree; the bytes were not rewritten to
|
|
538
|
+
match.
|
|
539
|
+
|
|
540
|
+
## Examples
|
|
541
|
+
|
|
542
|
+
Difficult usernames:
|
|
543
|
+
|
|
544
|
+
```bash
|
|
545
|
+
data-poison unicode --profile nightmare --count 20 --seed 12345 --format jsonl --output usernames.jsonl
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Numeric boundaries:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
data-poison numbers --case outside-range --minimum 0 --maximum 100 --seed 1
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Malformed JSON, one file per case:
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
data-poison json --validity malformed --count 10 --seed 1 --format raw --output malformed-cases/
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Schema boundaries:
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
data-poison schema customer.schema.json --mode boundary --count 20 --seed 1 --manifest cases.manifest.json
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Mutate a JSON file:
|
|
567
|
+
|
|
568
|
+
```bash
|
|
569
|
+
data-poison mutate customer.json --strategy single --count 20 --seed 1 --output mutations/
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
Run a local checker:
|
|
573
|
+
|
|
574
|
+
```bash
|
|
575
|
+
data-poison test --command "./validate-json" --category json --profile hostile --count 50 --seed 12345 --report results.json
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
Replay:
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
data-poison replay cases/manifest.json --verify --format raw --output reproduced.bin
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Shrink:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
data-poison shrink failing.bin --command "./validate-json" --predicate nonzero --output minimal.bin
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
A recipe:
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
data-poison recipe customer.toml --validate
|
|
594
|
+
data-poison recipe customer.toml --seed 12345
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
A Markdown report:
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
data-poison report results.json --format markdown
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
## Development
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
make init
|
|
607
|
+
make python-install-dev
|
|
608
|
+
make python-check
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
<a href="https://github.com/the-lupaxa-project">
|
|
612
|
+
<img src="https://raw.githubusercontent.com/the-lupaxa-project/brand-assets/master/logos/components/footer-for-child-orgs.svg" alt="The Lupaxa Project Footer" width="100%" />
|
|
613
|
+
</a>
|