evadex 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.
Files changed (44) hide show
  1. evadex-0.1.0/LICENSE +21 -0
  2. evadex-0.1.0/PKG-INFO +412 -0
  3. evadex-0.1.0/README.md +373 -0
  4. evadex-0.1.0/pyproject.toml +60 -0
  5. evadex-0.1.0/setup.cfg +4 -0
  6. evadex-0.1.0/src/evadex/__init__.py +5 -0
  7. evadex-0.1.0/src/evadex/__main__.py +3 -0
  8. evadex-0.1.0/src/evadex/adapters/__init__.py +0 -0
  9. evadex-0.1.0/src/evadex/adapters/base.py +49 -0
  10. evadex-0.1.0/src/evadex/adapters/dlpscan/__init__.py +0 -0
  11. evadex-0.1.0/src/evadex/adapters/dlpscan/adapter.py +69 -0
  12. evadex-0.1.0/src/evadex/adapters/dlpscan/client.py +74 -0
  13. evadex-0.1.0/src/evadex/adapters/dlpscan/file_builder.py +112 -0
  14. evadex-0.1.0/src/evadex/adapters/dlpscan_cli/__init__.py +0 -0
  15. evadex-0.1.0/src/evadex/adapters/dlpscan_cli/adapter.py +56 -0
  16. evadex-0.1.0/src/evadex/cli/__init__.py +0 -0
  17. evadex-0.1.0/src/evadex/cli/app.py +15 -0
  18. evadex-0.1.0/src/evadex/cli/commands/__init__.py +0 -0
  19. evadex-0.1.0/src/evadex/cli/commands/scan.py +114 -0
  20. evadex-0.1.0/src/evadex/core/__init__.py +0 -0
  21. evadex-0.1.0/src/evadex/core/engine.py +81 -0
  22. evadex-0.1.0/src/evadex/core/registry.py +46 -0
  23. evadex-0.1.0/src/evadex/core/result.py +76 -0
  24. evadex-0.1.0/src/evadex/payloads/__init__.py +0 -0
  25. evadex-0.1.0/src/evadex/payloads/builtins.py +85 -0
  26. evadex-0.1.0/src/evadex/reporters/__init__.py +0 -0
  27. evadex-0.1.0/src/evadex/reporters/base.py +12 -0
  28. evadex-0.1.0/src/evadex/reporters/html_reporter.py +135 -0
  29. evadex-0.1.0/src/evadex/reporters/json_reporter.py +32 -0
  30. evadex-0.1.0/src/evadex/variants/__init__.py +0 -0
  31. evadex-0.1.0/src/evadex/variants/base.py +20 -0
  32. evadex-0.1.0/src/evadex/variants/delimiter.py +55 -0
  33. evadex-0.1.0/src/evadex/variants/encoding.py +150 -0
  34. evadex-0.1.0/src/evadex/variants/leetspeak.py +36 -0
  35. evadex-0.1.0/src/evadex/variants/regional_digits.py +70 -0
  36. evadex-0.1.0/src/evadex/variants/splitting.py +54 -0
  37. evadex-0.1.0/src/evadex/variants/structural.py +34 -0
  38. evadex-0.1.0/src/evadex/variants/unicode_encoding.py +100 -0
  39. evadex-0.1.0/src/evadex.egg-info/PKG-INFO +412 -0
  40. evadex-0.1.0/src/evadex.egg-info/SOURCES.txt +42 -0
  41. evadex-0.1.0/src/evadex.egg-info/dependency_links.txt +1 -0
  42. evadex-0.1.0/src/evadex.egg-info/entry_points.txt +2 -0
  43. evadex-0.1.0/src/evadex.egg-info/requires.txt +12 -0
  44. evadex-0.1.0/src/evadex.egg-info/top_level.txt +1 -0
evadex-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 evadex contributors
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.
evadex-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,412 @@
1
+ Metadata-Version: 2.4
2
+ Name: evadex
3
+ Version: 0.1.0
4
+ Summary: Comprehensive DLP evasion test suite — scanner-agnostic, file-aware
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/tbustenk/evadex
7
+ Project-URL: Repository, https://github.com/tbustenk/evadex
8
+ Project-URL: Bug Tracker, https://github.com/tbustenk/evadex/issues
9
+ Project-URL: Changelog, https://github.com/tbustenk/evadex/blob/main/CHANGELOG.md
10
+ Keywords: dlp,security,evasion,testing,compliance,pci-dss,scanner
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: System :: Systems Administration
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: click>=8.1
28
+ Requires-Dist: httpx>=0.27
29
+ Requires-Dist: python-docx>=1.1
30
+ Requires-Dist: fpdf2>=2.7.9
31
+ Requires-Dist: openpyxl>=3.1
32
+ Requires-Dist: jinja2>=3.1
33
+ Requires-Dist: rich>=13.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=8.0; extra == "dev"
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
37
+ Requires-Dist: respx>=0.21; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # evadex
41
+
42
+ A scanner-agnostic DLP evasion test suite. evadex generates hundreds of obfuscated variants of known-sensitive values and submits them to your DLP scanner to find what slips through — including through file extraction pipelines (DOCX, PDF, XLSX), not just plain-text API calls.
43
+
44
+ Built with [dlpscan](https://github.com/oxide11/dlpscan) as the default target, with a clean adapter interface for testing any DLP tool.
45
+
46
+ ---
47
+
48
+ ## What it does
49
+
50
+ evadex takes a sensitive value (a credit card number, SSN, AWS key, etc.), runs it through every evasion technique it knows — unicode tricks, delimiter manipulation, encoding variants, regional digit scripts, homoglyphs, and more — and records which variants your scanner catches and which it misses.
51
+
52
+ **Evasion categories:**
53
+
54
+ | Generator | Techniques |
55
+ |---|---|
56
+ | `unicode_encoding` | Zero-width chars, fullwidth digits, homoglyphs, NFD/NFC/NFKC/NFKD normalization, HTML entities (decimal + hex), URL encoding (full, digits-only, mixed) |
57
+ | `delimiter` | Space, hyphen, dot, slash, tab, newline, mixed, doubled, none |
58
+ | `splitting` | Mid-value line break, HTML/CSS comment injection, prefix/suffix noise, JSON field split, whitespace padding, XML wrapping |
59
+ | `leetspeak` | Minimal, moderate, and aggressive substitution tiers |
60
+ | `regional_digits` | Arabic-Indic, Extended Arabic-Indic, Devanagari, Bengali, Thai, Myanmar, Khmer, Mongolian, NKo, Tibetan — plus mixed-script variants |
61
+ | `structural` | Left/right padding (spaces + zeros), noise embedding, partial values, case variation, repeated value |
62
+ | `encoding` | Base64 (standard, URL-safe, no-padding, MIME line-breaks, partial, double), ROT13, full/group reversal, double URL encoding, mixed NFD/NFC/NFKD normalization |
63
+
64
+ **Submission strategies** (for dlpscan-cli adapter):
65
+
66
+ Each variant is tested four ways by default: as plain text, embedded in a DOCX, embedded in a PDF, and embedded in an XLSX. This exercises your scanner's file extraction pipeline, not just its regex layer.
67
+
68
+ **Built-in test payloads:**
69
+
70
+ | Label | Value |
71
+ |---|---|
72
+ | Visa 16-digit | `4532015112830366` |
73
+ | Amex 15-digit | `378282246310005` |
74
+ | Mastercard 16-digit | `5105105105105100` |
75
+ | US SSN | `123-45-6789` |
76
+ | Canada SIN | `046 454 286` |
77
+ | UK IBAN | `GB82WEST12345698765432` |
78
+ | AWS Access Key ID | `AKIAIOSFODNN7EXAMPLE` |
79
+ | Sample JWT | *(compact JWT string)* |
80
+ | Email address | `test.user@example.com` |
81
+ | US phone number | `+1-555-867-5309` |
82
+
83
+ ---
84
+
85
+ ## Installation
86
+
87
+ Requires Python 3.10+.
88
+
89
+ ```bash
90
+ pip install evadex
91
+ ```
92
+
93
+ Or install from source:
94
+
95
+ ```bash
96
+ git clone https://github.com/your-org/evadex
97
+ cd evadex
98
+ pip install -e ".[dev]"
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Quick start
104
+
105
+ Run the full built-in suite against dlpscan (text strategy):
106
+
107
+ ```bash
108
+ evadex scan --tool dlpscan-cli --strategy text
109
+ ```
110
+
111
+ Test a single value:
112
+
113
+ ```bash
114
+ evadex scan --tool dlpscan-cli --input "4532015112830366" --strategy text
115
+ ```
116
+
117
+ Test with all file strategies (slower — exercises DOCX/PDF/XLSX extraction):
118
+
119
+ ```bash
120
+ evadex scan --tool dlpscan-cli --input "4532015112830366"
121
+ ```
122
+
123
+ Generate an HTML report:
124
+
125
+ ```bash
126
+ evadex scan --tool dlpscan-cli --strategy text --format html -o report.html
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Example output
132
+
133
+ ### Terminal summary
134
+
135
+ ```
136
+ Running evadex scan against dlpscan-cli at http://localhost:8080...
137
+ Done. 501 tests — 425 detected, 76 evaded
138
+ ```
139
+
140
+ ### JSON output (`--format json`, default)
141
+
142
+ ```json
143
+ {
144
+ "meta": {
145
+ "timestamp": "2026-04-01T22:01:36.172424+00:00",
146
+ "total": 501,
147
+ "pass": 425,
148
+ "fail": 76,
149
+ "error": 0,
150
+ "pass_rate": 84.8,
151
+ "summary_by_category": {
152
+ "credit_card": { "pass": 150, "fail": 9, "error": 0 },
153
+ "ssn": { "pass": 51, "fail": 2, "error": 0 },
154
+ "sin": { "pass": 51, "fail": 2, "error": 0 },
155
+ "iban": { "pass": 49, "fail": 6, "error": 0 },
156
+ "aws_key": { "pass": 27, "fail": 19, "error": 0 },
157
+ "jwt": { "pass": 25, "fail": 22, "error": 0 },
158
+ "email": { "pass": 21, "fail": 14, "error": 0 },
159
+ "phone": { "pass": 51, "fail": 2, "error": 0 }
160
+ }
161
+ },
162
+ "results": [
163
+ {
164
+ "payload": {
165
+ "value": "5105105105105100",
166
+ "category": "credit_card",
167
+ "label": "Mastercard 16-digit"
168
+ },
169
+ "variant": {
170
+ "value": "5105105105105100",
171
+ "generator": "delimiter",
172
+ "technique": "no_delimiter",
173
+ "transform_name": "All delimiters removed",
174
+ "strategy": "text"
175
+ },
176
+ "detected": true,
177
+ "severity": "pass",
178
+ "duration_ms": 371.01,
179
+ "error": null
180
+ },
181
+ {
182
+ "payload": {
183
+ "value": "046 454 286",
184
+ "category": "sin",
185
+ "label": "Canada SIN"
186
+ },
187
+ "variant": {
188
+ "value": "Ο4б 4Ƽ4 ΚȢб",
189
+ "generator": "unicode_encoding",
190
+ "technique": "homoglyph_substitution",
191
+ "transform_name": "Visually similar Cyrillic/Greek characters substituted",
192
+ "strategy": "text"
193
+ },
194
+ "detected": false,
195
+ "severity": "fail",
196
+ "duration_ms": 378.57,
197
+ "error": null
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ **Severity values:**
204
+
205
+ | Value | Meaning |
206
+ |---|---|
207
+ | `pass` | Scanner detected the variant (good) |
208
+ | `fail` | Scanner missed the variant — evasion succeeded |
209
+ | `error` | Adapter error (network, timeout, etc.) |
210
+
211
+ ---
212
+
213
+ ## CLI reference
214
+
215
+ ```
216
+ evadex scan [OPTIONS]
217
+ ```
218
+
219
+ | Flag | Default | Description |
220
+ |---|---|---|
221
+ | `--tool`, `-t` | `dlpscan-cli` | Adapter name to use |
222
+ | `--input`, `-i` | *(all built-ins)* | Single value to test. If omitted, runs all 10 built-in payloads. Category is auto-detected (Luhn check, regex patterns for SSN/IBAN/AWS/JWT/email/phone). |
223
+ | `--format`, `-f` | `json` | Output format: `json` or `html` |
224
+ | `--output`, `-o` | stdout | Write report to file instead of stdout |
225
+ | `--strategy` | all four | Submission strategy: `text`, `docx`, `pdf`, `xlsx`. Repeat the flag for multiple. Omit to run all four. |
226
+ | `--concurrency` | `5` | Max concurrent requests |
227
+ | `--timeout` | `30.0` | Request timeout in seconds |
228
+ | `--url` | `http://localhost:8080` | Base URL (for HTTP-based adapters) |
229
+ | `--api-key` | *(env: `EVADEX_API_KEY`)* | API key passed as `Authorization: Bearer` |
230
+ | `--category` | *(all)* | Filter built-in payloads by category. Repeat for multiple. Values: `credit_card`, `ssn`, `sin`, `iban`, `aws_key`, `jwt`, `email`, `phone` |
231
+ | `--variant-group` | *(all)* | Limit to specific generator(s). Repeat for multiple. Values: `unicode_encoding`, `delimiter`, `splitting`, `leetspeak`, `regional_digits`, `structural`, `encoding` |
232
+
233
+ ### Examples
234
+
235
+ ```bash
236
+ # Only test credit card payloads
237
+ evadex scan --tool dlpscan-cli --strategy text --category credit_card
238
+
239
+ # Only run unicode evasion techniques
240
+ evadex scan --tool dlpscan-cli --strategy text --variant-group unicode_encoding
241
+
242
+ # Only run unicode + delimiter techniques on SSN and IBAN
243
+ evadex scan --tool dlpscan-cli --strategy text \
244
+ --category ssn --category iban \
245
+ --variant-group unicode_encoding --variant-group delimiter
246
+
247
+ # Test a custom value (category auto-detected)
248
+ evadex scan --tool dlpscan-cli --input "AKIAIOSFODNN7EXAMPLE" --strategy text
249
+
250
+ # File strategy only — test DOCX extraction pipeline
251
+ evadex scan --tool dlpscan-cli --input "4532015112830366" --strategy docx
252
+
253
+ # Save HTML report
254
+ evadex scan --tool dlpscan-cli --strategy text --format html -o report.html
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Adapters
260
+
261
+ ### Built-in: `dlpscan-cli`
262
+
263
+ Invokes the [dlpscan](https://github.com/oxide11/dlpscan) CLI directly as a subprocess. Requires `dlpscan` to be installed and on `PATH`.
264
+
265
+ ```bash
266
+ evadex scan --tool dlpscan-cli
267
+ ```
268
+
269
+ For file strategies, evadex builds the document in memory and writes it to a temp file, runs `dlpscan <file> -f json`, then deletes the temp file.
270
+
271
+ ### Built-in: `dlpscan`
272
+
273
+ HTTP adapter for a dlpscan instance running as a REST API. Targets `POST /scan` (text) and `POST /scan/file` (multipart upload).
274
+
275
+ ```bash
276
+ evadex scan --tool dlpscan --url http://my-dlpscan-server:8080 --api-key my-key
277
+ ```
278
+
279
+ ### Adding a custom adapter
280
+
281
+ 1. Create a file anywhere in your project, e.g. `my_adapter.py`.
282
+
283
+ 2. Subclass `BaseAdapter` and implement `submit()`:
284
+
285
+ ```python
286
+ from evadex.adapters.base import BaseAdapter
287
+ from evadex.core.registry import register_adapter
288
+ from evadex.core.result import Payload, Variant, ScanResult
289
+
290
+
291
+ @register_adapter("my-tool")
292
+ class MyToolAdapter(BaseAdapter):
293
+ name = "my-tool"
294
+
295
+ async def submit(self, payload: Payload, variant: Variant) -> ScanResult:
296
+ # Send variant.value to your scanner however it expects it.
297
+ # variant.strategy is "text", "docx", "pdf", or "xlsx".
298
+ # Return a ScanResult with detected=True/False.
299
+ response = await call_my_scanner(variant.value)
300
+ detected = response.get("found", False)
301
+ return ScanResult(
302
+ payload=payload,
303
+ variant=variant,
304
+ detected=detected,
305
+ raw_response=response,
306
+ )
307
+ ```
308
+
309
+ 3. Import your adapter before invoking evadex (so the `@register_adapter` decorator fires), then use it:
310
+
311
+ ```bash
312
+ python -c "import my_adapter" && evadex scan --tool my-tool
313
+ ```
314
+
315
+ Or wire it up properly as a package with an entry point in `pyproject.toml`:
316
+
317
+ ```toml
318
+ [project.entry-points."evadex.adapters"]
319
+ my-tool = "my_package.my_adapter"
320
+ ```
321
+
322
+ **Optional hooks:**
323
+
324
+ ```python
325
+ async def setup(self):
326
+ # Called once before the batch — open connections, authenticate, etc.
327
+ self._session = await open_session()
328
+
329
+ async def teardown(self):
330
+ # Called once after the batch — clean up connections.
331
+ await self._session.close()
332
+
333
+ async def health_check(self) -> bool:
334
+ # Optional — verify the scanner is reachable.
335
+ return await ping_scanner()
336
+ ```
337
+
338
+ **File strategies:** `variant.strategy` tells you which format evadex wants to use. If your scanner only supports one method, ignore strategies you don't need and handle the rest:
339
+
340
+ ```python
341
+ from evadex.adapters.dlpscan.file_builder import FileBuilder
342
+
343
+ async def submit(self, payload, variant):
344
+ if variant.strategy == "text":
345
+ raw = await self._scan_text(variant.value)
346
+ else:
347
+ data, mime = FileBuilder.build(variant.value, variant.strategy)
348
+ raw = await self._scan_file(data, mime)
349
+ ...
350
+ ```
351
+
352
+ `FileBuilder.build(text, fmt)` returns `(bytes, mime_type)` entirely in memory — no disk writes.
353
+
354
+ ---
355
+
356
+ ## Output schema
357
+
358
+ ### Top-level
359
+
360
+ ```json
361
+ {
362
+ "meta": { ... },
363
+ "results": [ ... ]
364
+ }
365
+ ```
366
+
367
+ ### `meta`
368
+
369
+ | Field | Type | Description |
370
+ |---|---|---|
371
+ | `timestamp` | ISO 8601 string | When the scan ran |
372
+ | `total` | int | Total test cases run |
373
+ | `pass` | int | Variants detected by scanner |
374
+ | `fail` | int | Variants that evaded scanner |
375
+ | `error` | int | Adapter errors |
376
+ | `pass_rate` | float | `pass / total * 100` |
377
+ | `summary_by_category` | object | Per-category pass/fail/error counts |
378
+
379
+ ### `results[]`
380
+
381
+ | Field | Type | Description |
382
+ |---|---|---|
383
+ | `payload.value` | string | Original sensitive value |
384
+ | `payload.category` | string | Detected category enum value |
385
+ | `payload.label` | string | Human-readable label |
386
+ | `variant.value` | string | Transformed/obfuscated value submitted to scanner |
387
+ | `variant.generator` | string | Which generator produced this variant |
388
+ | `variant.technique` | string | Machine-readable technique name |
389
+ | `variant.transform_name` | string | Human-readable description of the transform |
390
+ | `variant.strategy` | string | Submission strategy: `text`, `docx`, `pdf`, `xlsx` |
391
+ | `detected` | bool | Whether the scanner flagged this variant |
392
+ | `severity` | string | `pass`, `fail`, or `error` |
393
+ | `duration_ms` | float | Time for this test case in milliseconds |
394
+ | `error` | string \| null | Error message if adapter threw |
395
+
396
+ ---
397
+
398
+ ## Publishing to PyPI
399
+
400
+ ```bash
401
+ pip install build twine
402
+ python -m build
403
+ twine upload dist/*
404
+ ```
405
+
406
+ Update the `[project.urls]` section in `pyproject.toml` with your real GitHub repository URL before publishing.
407
+
408
+ ---
409
+
410
+ ## License
411
+
412
+ MIT — see [LICENSE](LICENSE).