evadex 2.2.0__tar.gz → 3.0.2__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.
- evadex-3.0.2/PKG-INFO +1452 -0
- evadex-3.0.2/README.md +1412 -0
- {evadex-2.2.0 → evadex-3.0.2}/pyproject.toml +12 -11
- evadex-3.0.2/src/evadex/__main__.py +4 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/dlpscan/adapter.py +1 -1
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/dlpscan_cli/adapter.py +14 -2
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/presidio/adapter.py +1 -1
- evadex-3.0.2/src/evadex/audit.py +82 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/cli/app.py +6 -3
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/cli/commands/compare.py +40 -6
- evadex-3.0.2/src/evadex/cli/commands/falsepos.py +252 -0
- evadex-3.0.2/src/evadex/cli/commands/generate.py +182 -0
- evadex-3.0.2/src/evadex/cli/commands/init.py +22 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/cli/commands/list_techniques.py +4 -2
- evadex-3.0.2/src/evadex/cli/commands/scan.py +608 -0
- evadex-3.0.2/src/evadex/config.py +256 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/core/engine.py +7 -1
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/core/registry.py +2 -1
- evadex-3.0.2/src/evadex/core/result.py +1219 -0
- evadex-3.0.2/src/evadex/falsepos/__init__.py +1 -0
- evadex-3.0.2/src/evadex/falsepos/generators.py +262 -0
- evadex-3.0.2/src/evadex/feedback/regression_writer.py +105 -0
- evadex-3.0.2/src/evadex/feedback/report.py +79 -0
- evadex-3.0.2/src/evadex/feedback/suggestions.py +592 -0
- evadex-3.0.2/src/evadex/generate/filler.py +391 -0
- evadex-3.0.2/src/evadex/generate/generator.py +219 -0
- evadex-3.0.2/src/evadex/generate/writers/__init__.py +25 -0
- evadex-3.0.2/src/evadex/generate/writers/csv_writer.py +34 -0
- evadex-3.0.2/src/evadex/generate/writers/docx_writer.py +121 -0
- evadex-3.0.2/src/evadex/generate/writers/pdf_writer.py +138 -0
- evadex-3.0.2/src/evadex/generate/writers/txt_writer.py +65 -0
- evadex-3.0.2/src/evadex/generate/writers/xlsx_writer.py +168 -0
- evadex-3.0.2/src/evadex/payloads/builtins.py +933 -0
- evadex-3.0.2/src/evadex/reporters/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/reporters/base.py +0 -4
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/reporters/compare_html_reporter.py +8 -10
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/reporters/compare_reporter.py +0 -1
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/reporters/html_reporter.py +1 -2
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/reporters/json_reporter.py +12 -10
- evadex-3.0.2/src/evadex/synthetic/__init__.py +6 -0
- evadex-3.0.2/src/evadex/synthetic/base.py +30 -0
- evadex-3.0.2/src/evadex/synthetic/ca_corporate.py +69 -0
- evadex-3.0.2/src/evadex/synthetic/ca_drivers_licences.py +88 -0
- evadex-3.0.2/src/evadex/synthetic/ca_health_cards.py +80 -0
- evadex-3.0.2/src/evadex/synthetic/credit_card.py +36 -0
- evadex-3.0.2/src/evadex/synthetic/email.py +62 -0
- evadex-3.0.2/src/evadex/synthetic/iban.py +64 -0
- evadex-3.0.2/src/evadex/synthetic/phone.py +56 -0
- evadex-3.0.2/src/evadex/synthetic/ramq.py +63 -0
- evadex-3.0.2/src/evadex/synthetic/registry.py +46 -0
- evadex-3.0.2/src/evadex/synthetic/sin.py +41 -0
- evadex-3.0.2/src/evadex/synthetic/validators.py +83 -0
- evadex-3.0.2/src/evadex/variants/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/context_injection.py +51 -0
- evadex-3.0.2/src/evadex/variants/delimiter.py +214 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/encoding.py +1 -1
- evadex-3.0.2/src/evadex/variants/encoding_chains.py +98 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/morse_code.py +6 -0
- evadex-3.0.2/src/evadex/variants/soft_hyphen.py +249 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/splitting.py +11 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/structural.py +7 -3
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/unicode_encoding.py +6 -2
- evadex-3.0.2/src/evadex/variants/unicode_whitespace.py +203 -0
- evadex-3.0.2/src/evadex.egg-info/PKG-INFO +1452 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex.egg-info/SOURCES.txt +34 -0
- evadex-3.0.2/src/evadex.egg-info/requires.txt +13 -0
- evadex-2.2.0/PKG-INFO +0 -472
- evadex-2.2.0/README.md +0 -433
- evadex-2.2.0/src/evadex/__main__.py +0 -13
- evadex-2.2.0/src/evadex/cli/commands/scan.py +0 -239
- evadex-2.2.0/src/evadex/core/result.py +0 -124
- evadex-2.2.0/src/evadex/payloads/builtins.py +0 -131
- evadex-2.2.0/src/evadex/variants/delimiter.py +0 -55
- evadex-2.2.0/src/evadex/variants/soft_hyphen.py +0 -109
- evadex-2.2.0/src/evadex/variants/unicode_whitespace.py +0 -63
- evadex-2.2.0/src/evadex.egg-info/PKG-INFO +0 -472
- evadex-2.2.0/src/evadex.egg-info/requires.txt +0 -12
- {evadex-2.2.0 → evadex-3.0.2}/LICENSE +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/setup.cfg +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/base.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/dlpscan/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/dlpscan/client.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/dlpscan/file_builder.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/dlpscan_cli/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/presidio/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/adapters/presidio/client.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/cli/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/cli/commands/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/cli/commands/list_payloads.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/core/__init__.py +0 -0
- {evadex-2.2.0/src/evadex/payloads → evadex-3.0.2/src/evadex/feedback}/__init__.py +0 -0
- {evadex-2.2.0/src/evadex/reporters → evadex-3.0.2/src/evadex/generate}/__init__.py +0 -0
- {evadex-2.2.0/src/evadex/variants → evadex-3.0.2/src/evadex/payloads}/__init__.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/base.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/bidirectional.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/leetspeak.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex/variants/regional_digits.py +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex.egg-info/dependency_links.txt +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex.egg-info/entry_points.txt +0 -0
- {evadex-2.2.0 → evadex-3.0.2}/src/evadex.egg-info/top_level.txt +0 -0
evadex-3.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,1452 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evadex
|
|
3
|
+
Version: 3.0.2
|
|
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<9,>=8.1
|
|
28
|
+
Requires-Dist: httpx<1,>=0.27
|
|
29
|
+
Requires-Dist: python-docx<2,>=1.1
|
|
30
|
+
Requires-Dist: fpdf2<3,>=2.7.9
|
|
31
|
+
Requires-Dist: openpyxl<4,>=3.1
|
|
32
|
+
Requires-Dist: jinja2<4,>=3.1
|
|
33
|
+
Requires-Dist: rich<14,>=13.0
|
|
34
|
+
Requires-Dist: pyyaml<7,>=6.0
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest<10,>=8.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-asyncio<2,>=0.23; extra == "dev"
|
|
38
|
+
Requires-Dist: respx<1,>=0.21; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# evadex
|
|
42
|
+
|
|
43
|
+
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.
|
|
44
|
+
|
|
45
|
+
Built and tested with [dlpscan](https://github.com/oxide11/dlpscan); works with any scanner via its adapter interface. Detection rates vary by scanner, configuration, and ruleset — run evadex against your own deployment to see your results.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## What it does
|
|
50
|
+
|
|
51
|
+
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.
|
|
52
|
+
|
|
53
|
+
**Evasion categories:**
|
|
54
|
+
|
|
55
|
+
| Generator | Techniques |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `unicode_encoding` | Zero-width chars, fullwidth digits, homoglyphs, NFD/NFC/NFKC/NFKD normalization, HTML entities (decimal + hex), URL encoding (full, digits-only, mixed) |
|
|
58
|
+
| `delimiter` | Space, hyphen, dot, slash, tab, newline, mixed, doubled, none |
|
|
59
|
+
| `splitting` | Mid-value line break, HTML/CSS comment injection, prefix/suffix noise, JSON field split, whitespace padding, XML wrapping |
|
|
60
|
+
| `leetspeak` | Minimal, moderate, and aggressive substitution tiers |
|
|
61
|
+
| `regional_digits` | Arabic-Indic, Extended Arabic-Indic, Devanagari, Bengali, Thai, Myanmar, Khmer, Mongolian, NKo, Tibetan — plus mixed-script variants |
|
|
62
|
+
| `structural` | Left/right padding (spaces + zeros), noise embedding, partial values, case variation, repeated value |
|
|
63
|
+
| `encoding` | Base64 (standard, URL-safe, no-padding, MIME line-breaks, partial, double), ROT13, full/group reversal, double URL encoding, mixed NFD/NFC/NFKD normalization |
|
|
64
|
+
| `context_injection` | Value wrapped in email body, JSON record, XML element, CSV row, SQL snippet, and more |
|
|
65
|
+
| `unicode_whitespace` | Spaces replaced with NBSP, en-space, em-space, or a mixed pattern |
|
|
66
|
+
| `bidirectional` | Unicode bidirectional control characters (RLO, LRO, RLE, RLI, ALM) injected around or within the value |
|
|
67
|
+
| `soft_hyphen` | Soft hyphen (U+00AD) and word joiner (U+2060) inserted at group boundaries or between every character |
|
|
68
|
+
| `morse_code` | Digits encoded as International Morse Code — space-separated, slash-separated, concatenated, or newline-separated; applies to `credit_card`, `ssn`, `sin`, `iban`, `phone`, and related numeric categories |
|
|
69
|
+
| `encoding_chains` | Chained multi-step encodings: `base64(rot13)`, `base64(hex)`, `hex(base64)`, `rot13(base64)`, `url(base64)`, `base64(base64)`, and the triple chain `base64(rot13(hex))` — defeats scanners that only decode one layer |
|
|
70
|
+
|
|
71
|
+
**Submission strategies** (for dlpscan-cli adapter):
|
|
72
|
+
|
|
73
|
+
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.
|
|
74
|
+
|
|
75
|
+
**Built-in test payloads:**
|
|
76
|
+
|
|
77
|
+
Payloads are classified as **structured** or **heuristic** — see [Structured vs heuristic categories](#structured-vs-heuristic-categories) below.
|
|
78
|
+
|
|
79
|
+
554 payloads across 489 categories covering **489/557 sub-patterns** (88%) of the dlpscan-rs pattern library, with 421 structured categories confirmed detected by seed scan. See [Coverage](#coverage) for a breakdown by sub-pattern.
|
|
80
|
+
|
|
81
|
+
#### North America
|
|
82
|
+
|
|
83
|
+
| Label | Value | Category | Type |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| Visa 16-digit | `4532015112830366` | `credit_card` | structured |
|
|
86
|
+
| Amex 15-digit | `378282246310005` | `credit_card` | structured |
|
|
87
|
+
| Mastercard 16-digit | `5105105105105100` | `credit_card` | structured |
|
|
88
|
+
| Discover 16-digit | `6011111111111117` | `credit_card` | structured |
|
|
89
|
+
| JCB 16-digit | `3530111333300000` | `credit_card` | structured |
|
|
90
|
+
| UnionPay 16-digit | `6250941006528599` | `credit_card` | structured |
|
|
91
|
+
| Diners Club 14-digit | `30569309025904` | `credit_card` | structured |
|
|
92
|
+
| US SSN | `123-45-6789` | `ssn` | structured |
|
|
93
|
+
| US ITIN | `912-34-5678` | `us_itin` | structured |
|
|
94
|
+
| US EIN | `12-3456789` | `us_ein` | structured |
|
|
95
|
+
| US Medicare Beneficiary ID | `1EG4-TE5-MK72` | `us_mbi` | structured |
|
|
96
|
+
| US Passport | `340000136` | `us_passport` | structured |
|
|
97
|
+
| US state driver's licences (51) | one per state + DC | `us_dl` | structured |
|
|
98
|
+
| Canada SIN | `046 454 286` | `sin` | structured |
|
|
99
|
+
| Canadian passport | `AB123456` | `ca_passport` | structured |
|
|
100
|
+
| Quebec RAMQ health card | `BOUD 1234 5678` | `ca_ramq` | structured |
|
|
101
|
+
| Ontario health card | `1234-567-890-AB` | `ca_ontario_health` | structured |
|
|
102
|
+
| BC CareCard | `9123456789` | `ca_bc_carecard` | structured |
|
|
103
|
+
| Alberta health card | `123456789` | `ca_ab_health` | structured |
|
|
104
|
+
| Manitoba health card | `987654321` | `ca_mb_health` | structured |
|
|
105
|
+
| Saskatchewan health card | `234567890` | `ca_sk_health` | structured |
|
|
106
|
+
| Nova Scotia health card | `1234 567 890` | `ca_ns_health` | structured |
|
|
107
|
+
| New Brunswick health card | `1234567890` | `ca_nb_health` | structured |
|
|
108
|
+
| PEI health card | `123456789012` | `ca_pei_health` | structured |
|
|
109
|
+
| Newfoundland health card | `9876543210` | `ca_nl_health` | structured |
|
|
110
|
+
| Quebec driver's licence | `B123456789012` | `ca_qc_drivers` | structured |
|
|
111
|
+
| Ontario driver's licence | `A1234-56789-01234` | `ca_on_drivers` | structured |
|
|
112
|
+
| BC driver's licence | `1234567` | `ca_bc_drivers` | structured |
|
|
113
|
+
| Manitoba driver's licence | `AB-123-456-789` | `ca_mb_drivers` | structured |
|
|
114
|
+
| Saskatchewan driver's licence | `12345678` | `ca_sk_drivers` | structured |
|
|
115
|
+
| Nova Scotia driver's licence | `AB1234567` | `ca_ns_drivers` | structured |
|
|
116
|
+
| New Brunswick driver's licence | `1234567` | `ca_nb_drivers` | structured |
|
|
117
|
+
| PEI driver's licence | `123456` | `ca_pei_drivers` | structured |
|
|
118
|
+
| Newfoundland driver's licence | `A123456789` | `ca_nl_drivers` | structured |
|
|
119
|
+
| Canadian Business Number | `111222333` | `ca_business_number` | structured |
|
|
120
|
+
| Canadian GST/HST registration | `111222333RT0001` | `ca_gst_hst` | structured |
|
|
121
|
+
| Canadian transit/routing number | `12345-678` | `ca_transit_number` | structured |
|
|
122
|
+
| Canadian bank account | `12345678` | `ca_bank_account` | structured |
|
|
123
|
+
| Mexico CURP | `BADD110313HCMLNS09` | `mx_curp` | structured |
|
|
124
|
+
|
|
125
|
+
#### Europe
|
|
126
|
+
|
|
127
|
+
| Label | Value | Category | Type |
|
|
128
|
+
|---|---|---|---|
|
|
129
|
+
| UK IBAN | `GB82WEST12345698765432` | `iban` | structured |
|
|
130
|
+
| Germany IBAN | `DE89370400440532013000` | `iban` | structured |
|
|
131
|
+
| France IBAN | `FR7630006000011234567890189` | `iban` | structured |
|
|
132
|
+
| Spain IBAN | `ES9121000418450200051332` | `iban` | structured |
|
|
133
|
+
| SWIFT/BIC code | `DEUTDEDB` | `swift_bic` | structured |
|
|
134
|
+
| ABA routing number | `021000021` | `aba_routing` | structured |
|
|
135
|
+
| UK National Insurance Number | `AB123456C` | `uk_nin` | structured |
|
|
136
|
+
| UK driving licence | `MORGA753116SM9IJ` | `uk_dl` | structured |
|
|
137
|
+
| German Personalausweis | `L01X00T47` | `de_id` | structured |
|
|
138
|
+
| Germany Steuer-IdNr | `86095742719` | `de_tax_id` | structured |
|
|
139
|
+
| French CNI | `880692310285` | `fr_cni` | structured |
|
|
140
|
+
| France INSEE (NIR) | `282097505604213` | `fr_insee` | structured |
|
|
141
|
+
| Spanish DNI | `12345678Z` | `es_dni` | structured |
|
|
142
|
+
| Italian Codice Fiscale | `RSSMRA85T10A562S` | `it_cf` | structured |
|
|
143
|
+
| Dutch BSN | `111222333` | `nl_bsn` | structured |
|
|
144
|
+
| Swedish Personnummer | `811228-9874` | `se_pin` | structured |
|
|
145
|
+
| Norwegian Fødselsnummer | `01010112345` | `no_fnr` | structured |
|
|
146
|
+
| Finnish Henkilötunnus | `131052-308T` | `fi_hetu` | structured |
|
|
147
|
+
| Polish PESEL | `44051401458` | `pl_pesel` | structured |
|
|
148
|
+
| Swiss AHV | `756.1234.5678.97` | `ch_ahv` | structured |
|
|
149
|
+
| Austria social insurance | `1234-010150` | `at_svn` | structured |
|
|
150
|
+
| Belgium National Register Number | `85.01.01-234.56` | `be_nrn` | structured |
|
|
151
|
+
| Bulgaria EGN | `8501010001` | `bg_egn` | structured |
|
|
152
|
+
| Croatia OIB | `12345678901` | `hr_oib` | structured |
|
|
153
|
+
| Cyprus tax ID | `12345678A` | `cy_tin` | structured |
|
|
154
|
+
| Czech birth number | `850101/1234` | `cz_rc` | structured |
|
|
155
|
+
| Denmark CPR | `010185-1234` | `dk_cpr` | structured |
|
|
156
|
+
| Estonia personal code | `38501010002` | `ee_ik` | structured |
|
|
157
|
+
| EU VAT number | `DE123456789` | `eu_vat` | structured |
|
|
158
|
+
| Greece AMKA | `01018512345` | `gr_amka` | structured |
|
|
159
|
+
| Hungary TAJ | `123 456 789` | `hu_taj` | structured |
|
|
160
|
+
| Iceland kennitala | `010185-1234` | `is_kt` | structured |
|
|
161
|
+
| Ireland PPS number | `1234567A` | `ie_pps` | structured |
|
|
162
|
+
| Latvia personal code | `010185-12345` | `lv_pk` | structured |
|
|
163
|
+
| Liechtenstein passport | `A12345` | `li_pp` | structured |
|
|
164
|
+
| Lithuania personal code | `38501010002` | `lt_ak` | structured |
|
|
165
|
+
| Luxembourg national ID | `1985012312345` | `lu_nin` | structured |
|
|
166
|
+
| Malta identity card | `12345A` | `mt_id` | structured |
|
|
167
|
+
| Portugal NIF | `123456789` | `pt_nif` | structured |
|
|
168
|
+
| Romania CNP | `1850101123456` | `ro_cnp` | structured |
|
|
169
|
+
| Slovakia birth number | `850101/1234` | `sk_bn` | structured |
|
|
170
|
+
| Slovenia EMSO | `0101850500003` | `si_emso` | structured |
|
|
171
|
+
| Turkey TC identity | `12345678901` | `tr_tc` | structured |
|
|
172
|
+
|
|
173
|
+
#### Asia-Pacific
|
|
174
|
+
|
|
175
|
+
| Label | Value | Category | Type |
|
|
176
|
+
|---|---|---|---|
|
|
177
|
+
| Australia TFN | `123 456 78` | `au_tfn` | structured |
|
|
178
|
+
| Australian Medicare card | `2123456701` | `au_medicare` | structured |
|
|
179
|
+
| Australian passport | `PA1234567` | `au_passport` | structured |
|
|
180
|
+
| New Zealand IRD | `123456789` | `nz_ird` | structured |
|
|
181
|
+
| Singapore NRIC | `S1234567D` | `sg_nric` | structured |
|
|
182
|
+
| Hong Kong HKID | `A123456(3)` | `hk_hkid` | structured |
|
|
183
|
+
| Japanese My Number | `123456789012` | `jp_my_number` | structured |
|
|
184
|
+
| Indian Aadhaar | `2345 6789 0123` | `in_aadhaar` | structured |
|
|
185
|
+
| Indian PAN | `ABCDE1234F` | `in_pan` | structured |
|
|
186
|
+
| Bangladesh National ID | `1234567890` | `bd_nid` | structured |
|
|
187
|
+
| Indonesia NIK | `3201234567890001` | `id_nik` | structured |
|
|
188
|
+
| Malaysia MyKad | `850101-01-1234` | `my_mykad` | structured |
|
|
189
|
+
| Pakistan CNIC | `12345-1234567-1` | `pk_cnic` | structured |
|
|
190
|
+
| Philippines PhilSys | `1234-5678-9012` | `ph_philsys` | structured |
|
|
191
|
+
| South Korea RRN | `880101-1234567` | `kr_rrn` | structured |
|
|
192
|
+
| Sri Lanka NIC | `123456789V` | `lk_nic` | structured |
|
|
193
|
+
| Thailand national ID | `1-1001-00001-85-1` | `th_nid` | structured |
|
|
194
|
+
| Vietnam CCCD | `001012345678` | `vn_cccd` | structured |
|
|
195
|
+
|
|
196
|
+
#### Latin America
|
|
197
|
+
|
|
198
|
+
| Label | Value | Category | Type |
|
|
199
|
+
|---|---|---|---|
|
|
200
|
+
| Brazilian CPF | `123.456.789-09` | `br_cpf` | structured |
|
|
201
|
+
| Brazilian CNPJ | `11.222.333/0001-81` | `br_cnpj` | structured |
|
|
202
|
+
| Argentine DNI | `12345678` | `ar_dni` | structured |
|
|
203
|
+
| Chilean RUT | `12.345.678-9` | `cl_rut` | structured |
|
|
204
|
+
| Colombia cédula | `123.456.789-0` | `co_cedula` | structured |
|
|
205
|
+
| Costa Rica cédula | `1-0123-0456` | `cr_cedula` | structured |
|
|
206
|
+
| Ecuador cédula | `1234567890` | `ec_cedula` | structured |
|
|
207
|
+
| Paraguay RUC | `12345678-9` | `py_ruc` | structured |
|
|
208
|
+
| Peru DNI | `12345678` | `pe_dni` | structured |
|
|
209
|
+
| Uruguay cédula | `1.234.567-8` | `uy_ci` | structured |
|
|
210
|
+
| Venezuela cédula | `V-12345678` | `ve_cedula` | structured |
|
|
211
|
+
|
|
212
|
+
#### Middle East & Africa
|
|
213
|
+
|
|
214
|
+
| Label | Value | Category | Type |
|
|
215
|
+
|---|---|---|---|
|
|
216
|
+
| UAE Emirates ID | `784-1234-1234567-1` | `uae_eid` | structured |
|
|
217
|
+
| Saudi National ID | `1234567890` | `sa_nid` | structured |
|
|
218
|
+
| South African ID | `9202204720082` | `za_id` | structured |
|
|
219
|
+
| Israeli Teudat Zehut | `123456782` | `il_id` | structured |
|
|
220
|
+
| Bahrain CPR | `850101234` | `bh_cpr` | structured |
|
|
221
|
+
| Iran Melli code | `1234567890` | `ir_melli` | structured |
|
|
222
|
+
| Iraq national ID | `123456789012` | `iq_nid` | structured |
|
|
223
|
+
| Jordan national ID | `9001012345` | `jo_nid` | structured |
|
|
224
|
+
| Kuwait civil ID | `285010112345` | `kw_civil` | structured |
|
|
225
|
+
| Lebanon passport | `RL123456` | `lb_pp` | structured |
|
|
226
|
+
| Qatar QID | `28501011234` | `qa_qid` | structured |
|
|
227
|
+
|
|
228
|
+
#### Africa
|
|
229
|
+
|
|
230
|
+
| Label | Value | Category | Type |
|
|
231
|
+
|---|---|---|---|
|
|
232
|
+
| Egypt National ID | `28503251234567` | `eg_nid` | structured |
|
|
233
|
+
| Ethiopia passport | `EP1234567` | `et_passport` | structured |
|
|
234
|
+
| Ghana card | `GHA-123456789-1` | `gh_card` | structured |
|
|
235
|
+
| Kenya KRA PIN | `A123456789B` | `ke_kra` | structured |
|
|
236
|
+
| Morocco CIN | `AB12345` | `ma_cin` | structured |
|
|
237
|
+
| Nigeria BVN | `12345678901` | `ng_bvn` | structured |
|
|
238
|
+
| Tanzania NIDA | `12345678901234567890` | `tz_nida` | structured |
|
|
239
|
+
| Tunisia CIN | `12345678` | `tn_cin` | structured |
|
|
240
|
+
| Uganda NIN | `CM12345678ABCD` | `ug_nin` | structured |
|
|
241
|
+
|
|
242
|
+
#### Functional
|
|
243
|
+
|
|
244
|
+
| Label | Value | Category | Type |
|
|
245
|
+
|---|---|---|---|
|
|
246
|
+
| Session token (32-char hex) | `abc123def456abc123def456abc123de` | `session_id` | structured |
|
|
247
|
+
| PIN block (ISO format 0) | `0123456789ABCDEF` | `pin_block` | structured |
|
|
248
|
+
| Biometric ID (UUID-style) | `12345678-ABCD-1234-EFGH-123456789ABC` | `biometric_id` | structured |
|
|
249
|
+
| Card expiry | `12/26` | `card_expiry` | structured |
|
|
250
|
+
| Card track 1 | `%B4532015112830366^SMITH/JOHN^2512101000000000?` | `card_track` | structured |
|
|
251
|
+
| MICR check line | `⑈021000021⑈ 123456789012 1234` | `micr` | structured |
|
|
252
|
+
| Financial amount | `USD 12,345.67` | `financial_amount` | structured |
|
|
253
|
+
| ISO 8601 date | `2024-01-15` | `date_iso` | structured |
|
|
254
|
+
| SIM ICCID | `89014103211118510720` | `iccid` | structured |
|
|
255
|
+
| Educational email | `john.smith@mit.edu` | `edu_email` | structured |
|
|
256
|
+
| Employee ID | `EMP1234567` | `employee_id` | structured |
|
|
257
|
+
| GPS coordinates | `40.7128,-74.0060` | `gps_coords` | structured |
|
|
258
|
+
| Insurance policy number | `POL123456789` | `insurance_policy` | structured |
|
|
259
|
+
| Bank reference | `ACCT12345678` | `bank_ref` | structured |
|
|
260
|
+
| Legal case number | `1:24-cv-12345` | `legal_case` | structured |
|
|
261
|
+
| Loan/mortgage number | `ABCD00123456789012345678` | `loan_number` | structured |
|
|
262
|
+
| National Drug Code | `0069-3190-03` | `ndc_code` | structured |
|
|
263
|
+
| Date of birth | `01/15/1985` | `dob` | structured |
|
|
264
|
+
| Postal code | `SW1A 1AA` | `postal_code` | structured |
|
|
265
|
+
| Masked PAN | `4532 XXXX XXXX 0366` | `masked_pan` | structured |
|
|
266
|
+
| Property parcel number | `123-456-789` | `parcel_number` | structured |
|
|
267
|
+
| AML case ID | `AML-123456789` | `aml_case_id` | structured |
|
|
268
|
+
| ISIN | `US0378331005` | `isin` | structured |
|
|
269
|
+
| Twitter/X handle | `@johnsmith` | `twitter_handle` | structured |
|
|
270
|
+
| URL with embedded credentials | `https://admin:password123@example.com/api` | `url_with_creds` | structured |
|
|
271
|
+
| Vehicle Identification Number | `1HGBH41JXMN109186` | `vin` | structured |
|
|
272
|
+
| Fedwire IMAD | `20240101AAAA12345678001234` | `fedwire_imad` | structured |
|
|
273
|
+
|
|
274
|
+
#### Global
|
|
275
|
+
|
|
276
|
+
| Label | Value | Category | Type |
|
|
277
|
+
|---|---|---|---|
|
|
278
|
+
| Bitcoin legacy address | `1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2` | `bitcoin` | structured |
|
|
279
|
+
| Ethereum address | `0x742d35Cc6634C0532925a3b844Bc454e4438f44e` | `ethereum` | structured |
|
|
280
|
+
| Email address | `test.user@example.com` | `email` | structured |
|
|
281
|
+
| US phone number | `+1-555-867-5309` | `phone` | structured |
|
|
282
|
+
| AWS Access Key ID | `AKIAIOSFODNN7EXAMPLE` | `aws_key` | heuristic |
|
|
283
|
+
| GitHub classic token | `ghp_16C7e42F292c6912E7710c838347Ae178B4a` | `github_token` | heuristic |
|
|
284
|
+
| Stripe test secret key | `sk_test_4eC39HqLyjWDarjtT7en6bh8Xy9mPqZ` | `stripe_key` | heuristic |
|
|
285
|
+
| Slack bot token | `xoxb-EXAMPLE-BOTTOKEN-abc123def` | `slack_token` | heuristic |
|
|
286
|
+
| Sample JWT | *(compact JWT string)* | `jwt` | heuristic |
|
|
287
|
+
| Top Secret classification label | `TOP SECRET` | `classification` | heuristic |
|
|
288
|
+
| HIPAA privacy label | `HIPAA` | `classification` | heuristic |
|
|
289
|
+
| Corporate confidential label | `Company Confidential` | `corp_classification` | heuristic |
|
|
290
|
+
| MNPI label | `MNPI` | `mnpi` | heuristic |
|
|
291
|
+
| Cardholder name (PCI) | `John Smith` | `cardholder_name` | heuristic |
|
|
292
|
+
| Privacy/compliance label | `PCI-DSS` | `privacy_label` | heuristic |
|
|
293
|
+
| Attorney-client privilege marker | `Attorney-Client Privileged` | `attorney_client` | heuristic |
|
|
294
|
+
| Confidential supervisory info | `Confidential Supervisory Information` | `supervisory_info` | heuristic |
|
|
295
|
+
|
|
296
|
+
Heuristic payloads are excluded from the default scan. Use `--include-heuristic` to include them.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Canadian French support
|
|
301
|
+
|
|
302
|
+
evadex generates test content in Canadian French (`fr-CA`) so you can verify that your DLP scanner catches sensitive data when surrounded by French-language business text — a common real-world condition in Canadian financial institutions.
|
|
303
|
+
|
|
304
|
+
### French keyword context
|
|
305
|
+
|
|
306
|
+
The following French Canadian keywords are used as surrounding context in generated documents and evasion variants:
|
|
307
|
+
|
|
308
|
+
| Category | Keywords |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `credit_card` | *carte de crédit*, *numéro de carte*, *mon numéro de carte est*, *carte bancaire*, *numéro de carte bancaire*, *paiement par carte* |
|
|
311
|
+
| `sin` | *numéro d'assurance sociale*, *NAS*, *mon NAS est*, *assurance sociale* |
|
|
312
|
+
| `iban` | *numéro de compte*, *virement bancaire*, *coordonnées bancaires*, *relevé bancaire* |
|
|
313
|
+
| `email` | *courriel*, *adresse courriel*, *mon courriel est* |
|
|
314
|
+
| `phone` | *numéro de téléphone*, *composez le*, *téléphone*, *cellulaire* |
|
|
315
|
+
| all categories | *renseignements personnels*, *données confidentielles*, *informations personnelles*, *vie privée* |
|
|
316
|
+
|
|
317
|
+
French keywords are active in two places:
|
|
318
|
+
1. **`context_injection` variants** — 10 additional French CA sentence templates are generated alongside the standard English ones during `evadex scan`.
|
|
319
|
+
2. **`splitting` variants** — French noise text is prepended/appended in `fr_ca_prefix_noise` and `fr_ca_suffix_noise` variants.
|
|
320
|
+
|
|
321
|
+
### `--language fr-CA`
|
|
322
|
+
|
|
323
|
+
Pass `--language fr-CA` to the `generate` command to produce test documents with French keyword context sentences:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
evadex generate --format docx --category credit_card --category sin \
|
|
327
|
+
--count 200 --language fr-CA --output test_fr_ca.docx
|
|
328
|
+
|
|
329
|
+
evadex generate --format csv --category ca_ramq --count 500 \
|
|
330
|
+
--language fr-CA --output ramq_fr.csv
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Without `--language`, the default is English (`en`).
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## False positive rate and the `--require-context` tradeoff
|
|
338
|
+
|
|
339
|
+
The `evadex falsepos` command generates structurally-plausible but provably-invalid values (Luhn-failing credit card numbers, SSNs with reserved area codes, IBANs with wrong check digits, etc.) and submits them to your scanner. Any match is a false positive.
|
|
340
|
+
|
|
341
|
+
### What we measured
|
|
342
|
+
|
|
343
|
+
Three conditions were tested against dlpscan-rs with 100 values per category (7 categories, 700 total):
|
|
344
|
+
|
|
345
|
+
| Condition | What the scanner receives | What the scanner does |
|
|
346
|
+
|---|---|---|
|
|
347
|
+
| **Baseline** | Bare invalid value — `4123456789012341` | Matches on structure alone |
|
|
348
|
+
| **+`--require-context`** | Bare invalid value — `4123456789012341` | Requires surrounding keywords |
|
|
349
|
+
| **+`--wrap-context` + `--require-context`** | Invalid value inside a keyword sentence — `"Please charge my credit card number 4123456789012341 for..."` | Has both pattern match and keyword context |
|
|
350
|
+
|
|
351
|
+
### Results — false positive rates
|
|
352
|
+
|
|
353
|
+
| Category | Baseline | `--require-context` | `--wrap-context` + `--require-context` |
|
|
354
|
+
|---|---|---|---|
|
|
355
|
+
| `credit_card` | 100.0% | 100.0% | 100.0% |
|
|
356
|
+
| `ssn` | 100.0% | 100.0% | 100.0% |
|
|
357
|
+
| `sin` | 100.0% | 100.0% | 100.0% |
|
|
358
|
+
| `iban` | 100.0% | 100.0% | 100.0% |
|
|
359
|
+
| `phone` | 100.0% | 100.0% | 100.0% |
|
|
360
|
+
| `email` | 95.0% | 98.0% | 100.0% |
|
|
361
|
+
| `ca_ramq` | 99.0% | 99.0% | 100.0% |
|
|
362
|
+
| **Overall** | **99.1%** | **99.6%** | **100.0%** |
|
|
363
|
+
|
|
364
|
+
*100 values per category, seed=default, dlpscan-rs rust adapter, text strategy.*
|
|
365
|
+
|
|
366
|
+
### Key findings
|
|
367
|
+
|
|
368
|
+
**`--require-context` does not reduce false positives for structurally-similar invalid values.**
|
|
369
|
+
The FP rate is statistically unchanged between the baseline (99.1%) and require-context (99.6%) runs — the difference is within normal statistical noise. dlpscan-rs is matching on value *structure* (digit count, prefix, format), not on semantic validity (Luhn check, reserved area codes, mod-97 checksum). The context requirement does not gate out pattern-matched values when the pattern match itself is very confident.
|
|
370
|
+
|
|
371
|
+
**Adding keyword context makes it worse, not better.**
|
|
372
|
+
When invalid values are embedded in realistic keyword sentences (`--wrap-context`), the FP rate rises to 100.0%. This is the most realistic production scenario — real documents that contain a string resembling a credit card number will almost always have surrounding financial language — and it confirms the scanner flags all structurally-plausible values regardless of validity.
|
|
373
|
+
|
|
374
|
+
**The FP problem is in the pattern layer, not the context layer.**
|
|
375
|
+
Reducing false positives against dlpscan-rs requires the scanner to perform checksum validation (Luhn for credit cards and SINs, mod-97 for IBANs, reserved-code filtering for SSNs), not keyword-context gating. `--require-context` is an effective tool for reducing noisy matches in free-form text, but it cannot help when the pattern match itself is the source of the false positive.
|
|
376
|
+
|
|
377
|
+
### Detection rate tradeoff
|
|
378
|
+
|
|
379
|
+
To quantify the cost of enabling `--require-context` on real evasion testing, we ran the evadex evasion suite (credit card, SSN, SIN, IBAN — text strategy) under both conditions:
|
|
380
|
+
|
|
381
|
+
| | Baseline | `--require-context` | Delta |
|
|
382
|
+
|---|---|---|---|
|
|
383
|
+
| **Overall detection rate** | **94.1%** | **94.0%** | **−0.1 pp** |
|
|
384
|
+
|
|
385
|
+
Per-technique breakdown:
|
|
386
|
+
|
|
387
|
+
| Technique | Baseline DR | `--require-context` DR | Delta |
|
|
388
|
+
|---|---|---|---|
|
|
389
|
+
| `bidirectional` | 100.0% | 100.0% | 0.0 pp |
|
|
390
|
+
| `context_injection` | 100.0% | 100.0% | 0.0 pp |
|
|
391
|
+
| `delimiter` | 100.0% | 99.1% | −0.9 pp |
|
|
392
|
+
| `encoding` | 85.0% | 90.3% | **+5.3 pp** |
|
|
393
|
+
| `encoding_chains` | 72.5% | 65.9% | **−6.6 pp** |
|
|
394
|
+
| `morse_code` | 65.4% | 55.8% | **−9.6 pp** |
|
|
395
|
+
| `regional_digits` | 100.0% | 100.0% | 0.0 pp |
|
|
396
|
+
| `soft_hyphen` | 100.0% | 100.0% | 0.0 pp |
|
|
397
|
+
| `splitting` | 100.0% | 100.0% | 0.0 pp |
|
|
398
|
+
| `structural` | 94.2% | 92.8% | −1.4 pp |
|
|
399
|
+
| `unicode_encoding` | 94.6% | 95.4% | +0.8 pp |
|
|
400
|
+
| `unicode_whitespace` | 100.0% | 100.0% | 0.0 pp |
|
|
401
|
+
|
|
402
|
+
**`--require-context` reduces detection of obfuscated forms the most.** Morse code (−9.6 pp) and encoding chains (−6.6 pp) suffer the largest drops — these techniques produce output that contains no recognizable keyword context, so the scanner's context requirement causes it to skip matches it would otherwise make. Conversely, single-layer encoding improves slightly (+5.3 pp) because the decoded context may now satisfy the keyword requirement.
|
|
403
|
+
|
|
404
|
+
### Recommendation for compliance teams
|
|
405
|
+
|
|
406
|
+
> dlpscan-rs's ~99% false positive rate on structurally-plausible invalid values is a fundamental property of its **pattern-first** detection model. It is intentional: the scanner is tuned for high recall (catch everything) rather than high precision (avoid flagging invalid data).
|
|
407
|
+
|
|
408
|
+
For production deployments:
|
|
409
|
+
|
|
410
|
+
- **Do not rely on `--require-context` to reduce false positives on free-form document content.** It has negligible effect on FP rates when the values are structurally valid-looking, and it costs real detection rate on obfuscated variants (especially morse code and multi-layer encoding).
|
|
411
|
+
- **If false positive rate is a concern**, the appropriate mitigation is downstream triage (review queue, confidence thresholding) rather than scanner-level context gating.
|
|
412
|
+
- **For evasion testing specifically**, run `evadex scan` without `--require-context`. The baseline detection rate (94.1% on these categories) represents the scanner's real-world behavior for the majority of documents.
|
|
413
|
+
- **`--require-context` is most useful** when scanning large repositories of generic text where you want to reduce noise from coincidental pattern matches — not when testing against structured financial data.
|
|
414
|
+
|
|
415
|
+
### Reproducing the results
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
# Baseline FP test
|
|
419
|
+
evadex falsepos --tool dlpscan-cli \
|
|
420
|
+
--exe /path/to/dlpscan --cmd-style rust \
|
|
421
|
+
--count 100 --format json -o falsepos_baseline.json
|
|
422
|
+
|
|
423
|
+
# With require-context (scanner-side flag)
|
|
424
|
+
evadex falsepos --tool dlpscan-cli \
|
|
425
|
+
--exe /path/to/dlpscan --cmd-style rust \
|
|
426
|
+
--count 100 --require-context --format json -o falsepos_require_context.json
|
|
427
|
+
|
|
428
|
+
# Most realistic: invalid values embedded in keyword context, with require-context
|
|
429
|
+
evadex falsepos --tool dlpscan-cli \
|
|
430
|
+
--exe /path/to/dlpscan --cmd-style rust \
|
|
431
|
+
--count 100 --wrap-context --require-context --format json -o falsepos_full_context.json
|
|
432
|
+
|
|
433
|
+
# Evasion scan detection rate without require-context
|
|
434
|
+
evadex scan --tool dlpscan-cli \
|
|
435
|
+
--exe /path/to/dlpscan --cmd-style rust \
|
|
436
|
+
--strategy text --category credit_card --category ssn --format json -o evasion_baseline.json
|
|
437
|
+
|
|
438
|
+
# Evasion scan with require-context (detection rate tradeoff)
|
|
439
|
+
evadex scan --tool dlpscan-cli \
|
|
440
|
+
--exe /path/to/dlpscan --cmd-style rust \
|
|
441
|
+
--strategy text --category credit_card --category ssn \
|
|
442
|
+
--require-context --format json -o evasion_require_context.json
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Structured vs heuristic categories
|
|
448
|
+
|
|
449
|
+
evadex classifies its built-in payload categories into two groups:
|
|
450
|
+
|
|
451
|
+
**Structured** — formats with well-defined, mathematically or syntactically validatable patterns. DLP scanners typically enforce these patterns precisely (e.g., Luhn check on credit cards, fixed-length digit groups for SSN/SIN, checksum-verified IBAN). Evasion results in this group reflect meaningful signal: a variant that evades detection is a real gap in coverage.
|
|
452
|
+
|
|
453
|
+
Categories: `credit_card`, `ssn`, `sin`, `us_itin`, `us_ein`, `us_mbi`, `us_dl`, `us_passport`, `iban`, `swift_bic`, `aba_routing`, `bitcoin`, `ethereum`, `au_tfn`, `au_medicare`, `au_passport`, `de_tax_id`, `de_id`, `fr_insee`, `fr_cni`, `uk_nin`, `uk_dl`, `es_dni`, `it_cf`, `nl_bsn`, `se_pin`, `no_fnr`, `fi_hetu`, `pl_pesel`, `ch_ahv`, `at_svn`, `be_nrn`, `bg_egn`, `hr_oib`, `cy_tin`, `cz_rc`, `dk_cpr`, `ee_ik`, `eu_vat`, `gr_amka`, `hu_taj`, `is_kt`, `ie_pps`, `lv_pk`, `li_pp`, `lt_ak`, `lu_nin`, `mt_id`, `pt_nif`, `ro_cnp`, `sk_bn`, `si_emso`, `tr_tc`, `nz_ird`, `sg_nric`, `hk_hkid`, `jp_my_number`, `in_aadhaar`, `in_pan`, `bd_nid`, `id_nik`, `my_mykad`, `pk_cnic`, `ph_philsys`, `kr_rrn`, `lk_nic`, `th_nid`, `vn_cccd`, `br_cpf`, `br_cnpj`, `mx_curp`, `ar_dni`, `cl_rut`, `co_cedula`, `cr_cedula`, `ec_cedula`, `py_ruc`, `pe_dni`, `uy_ci`, `ve_cedula`, `uae_eid`, `sa_nid`, `za_id`, `il_id`, `bh_cpr`, `ir_melli`, `iq_nid`, `jo_nid`, `kw_civil`, `lb_pp`, `qa_qid`, `eg_nid`, `et_passport`, `gh_card`, `ke_kra`, `ma_cin`, `ng_bvn`, `tz_nida`, `tn_cin`, `ug_nin`, `email`, `phone`, `ca_ramq`, `ca_ontario_health`, `ca_bc_carecard`, `ca_ab_health`, `ca_qc_drivers`, `ca_on_drivers`, `ca_bc_drivers`, `ca_passport`, `ca_mb_health`, `ca_sk_health`, `ca_ns_health`, `ca_nb_health`, `ca_pei_health`, `ca_nl_health`, `ca_mb_drivers`, `ca_sk_drivers`, `ca_ns_drivers`, `ca_nb_drivers`, `ca_pei_drivers`, `ca_nl_drivers`, `ca_business_number`, `ca_gst_hst`, `ca_transit_number`, `ca_bank_account`, `session_id`, `pin_block`, `biometric_id`, `card_expiry`, `card_track`, `micr`, `financial_amount`, `date_iso`, `iccid`, `edu_email`, `employee_id`, `gps_coords`, `insurance_policy`, `bank_ref`, `legal_case`, `loan_number`, `ndc_code`, `dob`, `postal_code`, `masked_pan`, `parcel_number`, `aml_case_id`, `isin`, `twitter_handle`, `url_with_creds`, `vin`, `fedwire_imad`
|
|
454
|
+
|
|
455
|
+
**Heuristic** — formats where detection relies on fixed prefixes, high-entropy pattern matching, or loosely defined structure. DLP rules for these categories vary widely between scanners and configurations, and a "fail" result may simply reflect that the scanner never had a strong rule for that specific format variant — not that a real exfiltration path was found.
|
|
456
|
+
|
|
457
|
+
Categories: `aws_key`, `jwt`, `github_token`, `stripe_key`, `slack_token`, `classification`, `corp_classification`, `mnpi`, `cardholder_name`, `privacy_label`, `attorney_client`, `supervisory_info`
|
|
458
|
+
|
|
459
|
+
Heuristic categories are excluded from the default scan to avoid misleading results. Include them with:
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
evadex scan --tool dlpscan-cli --include-heuristic
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
A warning is printed to stderr whenever `--include-heuristic` is active reminding you to interpret those results with caution.
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Installation
|
|
470
|
+
|
|
471
|
+
Requires Python 3.10+.
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
pip install evadex
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Or install from source:
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
git clone https://github.com/tbustenk/evadex
|
|
481
|
+
cd evadex
|
|
482
|
+
pip install -e ".[dev]"
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
For reproducible installs with pinned, hash-verified dependencies (recommended for regulated environments):
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
pip install -r requirements.txt # runtime only
|
|
489
|
+
pip install -r requirements-dev.txt # runtime + test dependencies
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
These lockfiles are generated with `pip-compile --generate-hashes` and updated with each release.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## Quick start
|
|
497
|
+
|
|
498
|
+
Run the full built-in suite against dlpscan (text strategy):
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
evadex scan --tool dlpscan-cli --strategy text
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Test a single value:
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
evadex scan --tool dlpscan-cli --input "4532015112830366" --strategy text
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Test with all file strategies (slower — exercises DOCX/PDF/XLSX extraction):
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
evadex scan --tool dlpscan-cli --input "4532015112830366"
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Generate an HTML report:
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
evadex scan --tool dlpscan-cli --strategy text --format html -o report.html
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Configuration
|
|
525
|
+
|
|
526
|
+
evadex supports an optional `evadex.yaml` config file. Config file values are defaults — any CLI flag you pass overrides the corresponding config value.
|
|
527
|
+
|
|
528
|
+
### Generating a starter config
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
evadex init
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Creates `evadex.yaml` in the current directory:
|
|
535
|
+
|
|
536
|
+
```yaml
|
|
537
|
+
# evadex configuration file
|
|
538
|
+
# Run 'evadex scan --config evadex.yaml' to use this file.
|
|
539
|
+
# CLI flags take precedence over values in this file.
|
|
540
|
+
|
|
541
|
+
tool: dlpscan-cli
|
|
542
|
+
strategy: text
|
|
543
|
+
min_detection_rate: 85
|
|
544
|
+
scanner_label: production
|
|
545
|
+
exe: null
|
|
546
|
+
cmd_style: python
|
|
547
|
+
categories:
|
|
548
|
+
- credit_card
|
|
549
|
+
- ssn
|
|
550
|
+
- iban
|
|
551
|
+
include_heuristic: false
|
|
552
|
+
concurrency: 5
|
|
553
|
+
timeout: 30.0
|
|
554
|
+
output: results.json
|
|
555
|
+
format: json
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Using a config file
|
|
559
|
+
|
|
560
|
+
Pass it explicitly:
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
evadex scan --config evadex.yaml
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Or drop `evadex.yaml` in the current directory and evadex will pick it up automatically — no flag needed.
|
|
567
|
+
|
|
568
|
+
CLI flags always win. To override a config value for one run:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
# Config says scanner_label: production — this run uses "staging" instead
|
|
572
|
+
evadex scan --config evadex.yaml --scanner-label staging
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Config keys
|
|
576
|
+
|
|
577
|
+
| Key | Type | CLI equivalent | Description |
|
|
578
|
+
|---|---|---|---|
|
|
579
|
+
| `tool` | string | `--tool` | Adapter name (`dlpscan-cli`, `dlpscan`, `presidio`) |
|
|
580
|
+
| `strategy` | string or list | `--strategy` | Submission strategy: `text`, `docx`, `pdf`, `xlsx`. Use a list for multiple. |
|
|
581
|
+
| `min_detection_rate` | number | `--min-detection-rate` | CI/CD gate threshold (0–100) |
|
|
582
|
+
| `scanner_label` | string | `--scanner-label` | Label recorded in JSON `meta.scanner` |
|
|
583
|
+
| `exe` | string or null | `--exe` | Path to scanner executable |
|
|
584
|
+
| `cmd_style` | `python` or `rust` | `--cmd-style` | Command format for dlpscan-cli |
|
|
585
|
+
| `categories` | list of strings | `--category` | Payload categories to test |
|
|
586
|
+
| `include_heuristic` | boolean | `--include-heuristic` | Include heuristic categories |
|
|
587
|
+
| `concurrency` | integer | `--concurrency` | Max concurrent requests |
|
|
588
|
+
| `timeout` | number | `--timeout` | Request timeout in seconds |
|
|
589
|
+
| `output` | string or null | `--output` | Output file path (null = stdout) |
|
|
590
|
+
| `format` | `json` or `html` | `--format` | Output format |
|
|
591
|
+
| `audit_log` | string or null | `--audit-log` | Append-only audit log file (see [Audit log](#audit-log)) |
|
|
592
|
+
|
|
593
|
+
### Validation
|
|
594
|
+
|
|
595
|
+
evadex validates the config file on load and exits with a clear error for invalid values:
|
|
596
|
+
|
|
597
|
+
```
|
|
598
|
+
Error: Config 'min_detection_rate' must be between 0 and 100, got: 150.0
|
|
599
|
+
Error: Invalid strategy value(s): foobar. Valid: docx, pdf, text, xlsx
|
|
600
|
+
Error: Unknown config key(s): bad_key. Valid keys: categories, cmd_style, ...
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
## Example output
|
|
606
|
+
|
|
607
|
+
### Terminal summary
|
|
608
|
+
|
|
609
|
+
```
|
|
610
|
+
Running evadex scan against dlpscan-cli at http://localhost:8080...
|
|
611
|
+
Done. 590 tests — N detected, N evaded
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Detection rates depend on your scanner, its version, and how it's configured.
|
|
615
|
+
|
|
616
|
+
### JSON output (`--format json`, default)
|
|
617
|
+
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"meta": {
|
|
621
|
+
"timestamp": "2026-04-01T22:01:36.172424+00:00",
|
|
622
|
+
"scanner": "rust-2.0.0",
|
|
623
|
+
"total": 590,
|
|
624
|
+
"pass": 514,
|
|
625
|
+
"fail": 76,
|
|
626
|
+
"error": 0,
|
|
627
|
+
"pass_rate": 87.1,
|
|
628
|
+
"summary_by_category": {
|
|
629
|
+
"credit_card": { "pass": 109, "fail": 15, "error": 0 },
|
|
630
|
+
"ssn": { "pass": 43, "fail": 10, "error": 0 },
|
|
631
|
+
"iban": { "pass": 36, "fail": 8, "error": 0 }
|
|
632
|
+
},
|
|
633
|
+
"summary_by_generator": {
|
|
634
|
+
"delimiter": { "pass": 72, "fail": 10, "error": 0 },
|
|
635
|
+
"unicode_encoding": { "pass": 54, "fail": 13, "error": 0 }
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
"results": [
|
|
639
|
+
{
|
|
640
|
+
"payload": {
|
|
641
|
+
"value": "5105105105105100",
|
|
642
|
+
"category": "credit_card",
|
|
643
|
+
"category_type": "structured",
|
|
644
|
+
"label": "Mastercard 16-digit"
|
|
645
|
+
},
|
|
646
|
+
"variant": {
|
|
647
|
+
"value": "5105105105105100",
|
|
648
|
+
"generator": "delimiter",
|
|
649
|
+
"technique": "no_delimiter",
|
|
650
|
+
"transform_name": "All delimiters removed",
|
|
651
|
+
"strategy": "text"
|
|
652
|
+
},
|
|
653
|
+
"detected": true,
|
|
654
|
+
"severity": "pass",
|
|
655
|
+
"duration_ms": 371.01,
|
|
656
|
+
"error": null,
|
|
657
|
+
"raw_response": { "matches": [{ "type": "credit_card", "value": "5105105105105100" }] }
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
"payload": {
|
|
661
|
+
"value": "046 454 286",
|
|
662
|
+
"category": "sin",
|
|
663
|
+
"category_type": "structured",
|
|
664
|
+
"label": "Canada SIN"
|
|
665
|
+
},
|
|
666
|
+
"variant": {
|
|
667
|
+
"value": "Ο4б 4Ƽ4 ΚȢб",
|
|
668
|
+
"generator": "unicode_encoding",
|
|
669
|
+
"technique": "homoglyph_substitution",
|
|
670
|
+
"transform_name": "Visually similar Cyrillic/Greek characters substituted",
|
|
671
|
+
"strategy": "text"
|
|
672
|
+
},
|
|
673
|
+
"detected": false,
|
|
674
|
+
"severity": "fail",
|
|
675
|
+
"duration_ms": 378.57,
|
|
676
|
+
"error": null,
|
|
677
|
+
"raw_response": { "matches": [] }
|
|
678
|
+
}
|
|
679
|
+
]
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**Severity values:**
|
|
684
|
+
|
|
685
|
+
| Value | Meaning |
|
|
686
|
+
|---|---|
|
|
687
|
+
| `pass` | Scanner detected the variant (good) |
|
|
688
|
+
| `fail` | Scanner missed the variant — evasion succeeded |
|
|
689
|
+
| `error` | Adapter error (network, timeout, malformed scanner response, etc.) |
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
## CLI reference
|
|
694
|
+
|
|
695
|
+
### `evadex scan`
|
|
696
|
+
|
|
697
|
+
Run DLP evasion tests against a scanner.
|
|
698
|
+
|
|
699
|
+
```
|
|
700
|
+
evadex scan [OPTIONS]
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
| Flag | Default | Description |
|
|
704
|
+
|---|---|---|
|
|
705
|
+
| `--config` | *(auto-discovered)* | Path to `evadex.yaml` config file. Auto-discovered from current directory if present. CLI flags always override config values. |
|
|
706
|
+
| `--tool`, `-t` | `dlpscan-cli` | Adapter to use. Built-in adapters: `dlpscan-cli`, `dlpscan`, `presidio`. |
|
|
707
|
+
| `--input`, `-i` | *(all built-ins)* | Single value to test. If omitted, runs all 211 structured built-in payloads (add `--include-heuristic` for all 225). Category is auto-detected (Luhn check, regex patterns for SSN/IBAN/AWS/JWT/email/phone). |
|
|
708
|
+
| `--format`, `-f` | `json` | Output format: `json` or `html` |
|
|
709
|
+
| `--output`, `-o` | stdout | Write report to file instead of stdout |
|
|
710
|
+
| `--strategy` | all four | Submission strategy: `text`, `docx`, `pdf`, `xlsx`. Repeat the flag for multiple. Omit to run all four. |
|
|
711
|
+
| `--concurrency` | `5` | Max concurrent requests |
|
|
712
|
+
| `--timeout` | `30.0` | Request timeout in seconds |
|
|
713
|
+
| `--url` | `http://localhost:8080` | Base URL (for HTTP-based adapters: `dlpscan`, `presidio`) |
|
|
714
|
+
| `--api-key` | *(env: `EVADEX_API_KEY`)* | API key passed as `Authorization: Bearer`. Use the environment variable in preference to the CLI flag to avoid exposure in shell history and process listings. |
|
|
715
|
+
| `--category` | *(all structured)* | Filter built-in payloads by category. Repeat for multiple. Values: `credit_card`, `ssn`, `sin`, `iban`, `swift_bic`, `aba_routing`, `bitcoin`, `ethereum`, `us_passport`, `au_tfn`, `de_tax_id`, `fr_insee`, `email`, `phone`, `aws_key`, `jwt`, `github_token`, `stripe_key`, `slack_token`, `classification` |
|
|
716
|
+
| `--variant-group` | *(all)* | Limit to specific generator(s). Repeat for multiple. Values: `unicode_encoding`, `delimiter`, `splitting`, `leetspeak`, `regional_digits`, `structural`, `encoding`, `context_injection`, `unicode_whitespace`, `bidirectional`, `soft_hyphen`, `morse_code` |
|
|
717
|
+
| `--include-heuristic` | off | Also run heuristic categories (`aws_key`, `jwt`, `github_token`, `stripe_key`, `slack_token`, `classification`). A warning is printed when enabled — see [Structured vs heuristic categories](#structured-vs-heuristic-categories). |
|
|
718
|
+
| `--scanner-label` | *(empty)* | Label recorded in the JSON `meta.scanner` field. Use to tag a specific scanner version, e.g. `python-1.3.0` or `rust-2.0.0`. Useful when comparing results across scanner builds. |
|
|
719
|
+
| `--exe` | `dlpscan` | Path to the scanner executable (dlpscan-cli adapter only). Use when `dlpscan` is not on `PATH` or you need to target a specific build. |
|
|
720
|
+
| `--cmd-style` | `python` | Command format for dlpscan-cli: `python` (invokes `dlpscan -f json <file>`) or `rust` (invokes `dlpscan --format json scan <file>`). |
|
|
721
|
+
| `--min-detection-rate` | *(off)* | Exit with code 1 if the detection rate falls below this threshold (0–100). Intended for CI/CD pipeline gating. Report is always written before the exit. |
|
|
722
|
+
| `--baseline` | *(off)* | Save this run's JSON results to a file for future comparison. |
|
|
723
|
+
| `--compare-baseline` | *(off)* | Compare this run against a previously saved baseline and print a regression summary to stderr. |
|
|
724
|
+
| `--audit-log` | *(off)* | Append a one-line JSON audit record for this run to a file. Parent directories are created if they do not exist. Can also be set via `audit_log` in `evadex.yaml`. |
|
|
725
|
+
| `--feedback-report` | *(off)* | Save a structured JSON feedback report to PATH. Contains per-technique evasion counts with example variant values, actionable fix suggestions, and the generated regression test code as a string field. Always written when specified, even if there are no evasions. |
|
|
726
|
+
| `--require-context` | off | Pass `--require-context` to dlpscan-rs: only flag matches when surrounding keywords are present. Reduces false positives but may reduce detection rate for obfuscated variants lacking keyword context. Requires `--cmd-style rust`. Can also be set via `require_context` in `evadex.yaml`. |
|
|
727
|
+
|
|
728
|
+
### `evadex generate`
|
|
729
|
+
|
|
730
|
+
Generate test documents filled with synthetic sensitive data for DLP scanner testing. Values are embedded in realistic business sentences, tables, and paragraphs. Evasion variants use the same obfuscation techniques as `evadex scan`.
|
|
731
|
+
|
|
732
|
+
```
|
|
733
|
+
evadex generate --format FORMAT --output PATH [OPTIONS]
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
| Flag | Default | Description |
|
|
737
|
+
|---|---|---|
|
|
738
|
+
| `--format` | *(required)* | Output file format: `xlsx`, `docx`, `pdf`, `csv`, `txt` |
|
|
739
|
+
| `--output` | *(required)* | Output file path |
|
|
740
|
+
| `--category` | *(all structured)* | Payload category to include. Repeat for multiple. Omit for all structured categories. |
|
|
741
|
+
| `--count` | `100` | Number of test values to generate **per category** |
|
|
742
|
+
| `--evasion-rate` | `0.5` | Fraction of values that are evasion variants (0.0–1.0) |
|
|
743
|
+
| `--keyword-rate` | `0.5` | Fraction of values wrapped in keyword context sentences (0.0–1.0) |
|
|
744
|
+
| `--technique` | *(all)* | Limit evasion variants to specific technique names. Repeat for multiple. |
|
|
745
|
+
| `--random` | off | Randomise categories, evasion rate, and keyword rate |
|
|
746
|
+
| `--seed` | *(none)* | Integer seed for reproducible output |
|
|
747
|
+
| `--include-heuristic` | off | Also include heuristic categories (AWS keys, tokens, JWT, etc.) |
|
|
748
|
+
| `--language` | `en` | Language for keyword context sentences: `en` (English) or `fr-CA` (Canadian French) |
|
|
749
|
+
|
|
750
|
+
**Format details:**
|
|
751
|
+
|
|
752
|
+
- **`xlsx`** — Multiple sheets: one `Summary` sheet plus one sheet per category. Columns include embedded text, plain value, variant value, technique, and generator. Evasion rows are highlighted yellow.
|
|
753
|
+
- **`docx`** — Title page with disclaimer; one heading per category; two-thirds prose paragraphs, one-third tabular layout.
|
|
754
|
+
- **`pdf`** — Sections per category with header/footer; evasion rows highlighted.
|
|
755
|
+
- **`csv`** — Flat CSV with columns: `category`, `plain_value`, `variant_value`, `technique`, `generator`, `transform_name`, `has_keywords`, `embedded_text`.
|
|
756
|
+
- **`txt`** — Plain-text document with section headings and numbered entry list.
|
|
757
|
+
|
|
758
|
+
**Examples:**
|
|
759
|
+
|
|
760
|
+
```bash
|
|
761
|
+
# 100 credit cards, 40% evasion variants → XLSX
|
|
762
|
+
evadex generate --format xlsx --category credit_card --count 100 \
|
|
763
|
+
--evasion-rate 0.4 --output test_cards.xlsx
|
|
764
|
+
|
|
765
|
+
# Mixed categories → DOCX
|
|
766
|
+
evadex generate --format docx \
|
|
767
|
+
--category credit_card --category ssn --category iban \
|
|
768
|
+
--count 50 --evasion-rate 0.5 --output test_mixed.docx
|
|
769
|
+
|
|
770
|
+
# Specific evasion techniques only → PDF
|
|
771
|
+
evadex generate --format pdf --count 200 --evasion-rate 0.6 \
|
|
772
|
+
--technique homoglyph_substitution --technique zero_width_zwsp \
|
|
773
|
+
--output test_homoglyph.pdf
|
|
774
|
+
|
|
775
|
+
# Reproducible random document
|
|
776
|
+
evadex generate --format xlsx --random --count 500 --seed 42 --output random.xlsx
|
|
777
|
+
|
|
778
|
+
# CSV for programmatic inspection
|
|
779
|
+
evadex generate --format csv --category ssn --count 1000 \
|
|
780
|
+
--evasion-rate 0.3 --output ssn_variants.csv
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**Value generation:**
|
|
784
|
+
|
|
785
|
+
evadex generates values two ways:
|
|
786
|
+
|
|
787
|
+
- **Synthetic generators** (preferred, unlimited) — Produce structurally valid values algorithmically, so `--count 1000` always returns 1000 distinct values. Registered for:
|
|
788
|
+
- `credit_card` — Luhn-valid numbers for Visa, Mastercard, Amex, Discover
|
|
789
|
+
- `sin` — Valid Canadian SINs (Luhn checksum, NNN NNN NNN format)
|
|
790
|
+
- `iban` — Valid IBANs for GB, DE, and FR (ISO 13616 mod-97 checksum)
|
|
791
|
+
- `phone` — Canadian E.164 numbers (`+1-NPA-NXX-XXXX`) from real area codes
|
|
792
|
+
- `email` — Realistic addresses with common Canadian and international domains
|
|
793
|
+
- `ca_ramq` — Quebec RAMQ health card numbers (XXXX YYMM DDSS format)
|
|
794
|
+
- `ca_mb_health`, `ca_sk_health` — 9-digit Manitoba/Saskatchewan health cards
|
|
795
|
+
- `ca_ns_health` — Nova Scotia 10-digit health card (NNNN NNN NNN format)
|
|
796
|
+
- `ca_nb_health`, `ca_nl_health` — 10-digit NB/NL health cards
|
|
797
|
+
- `ca_pei_health` — 12-digit PEI health card
|
|
798
|
+
- `ca_mb_drivers` — Manitoba licence (LL-NNN-NNN-NNN format)
|
|
799
|
+
- `ca_sk_drivers` — Saskatchewan 8-digit licence
|
|
800
|
+
- `ca_ns_drivers` — Nova Scotia licence (2 letters + 7 digits)
|
|
801
|
+
- `ca_nb_drivers` — New Brunswick 7-digit licence
|
|
802
|
+
- `ca_pei_drivers` — PEI 6-digit licence
|
|
803
|
+
- `ca_nl_drivers` — Newfoundland licence (1 letter + 9 digits)
|
|
804
|
+
- `ca_business_number` — Canadian Business Number (9 digits, CRA)
|
|
805
|
+
- `ca_gst_hst` — GST/HST registration (9-digit BN + RT + 4 digits)
|
|
806
|
+
- `ca_transit_number` — Transit/routing number (NNNNN-NNN format)
|
|
807
|
+
- `ca_bank_account` — Bank account (7–12 random digits)
|
|
808
|
+
- **Seed rotation fallback** — Categories without a synthetic generator rotate through the built-in seed values.
|
|
809
|
+
- **Evasion variants** — Drawn from all 12 evadex generators (same techniques as `evadex scan`). Use `--technique` to restrict to specific techniques.
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
### `evadex compare`
|
|
814
|
+
|
|
815
|
+
Diff two evadex scan result JSON files and report what changed between them.
|
|
816
|
+
|
|
817
|
+
```
|
|
818
|
+
evadex compare [OPTIONS] FILE_A FILE_B
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
| Flag | Default | Description |
|
|
822
|
+
|---|---|---|
|
|
823
|
+
| `--format`, `-f` | `json` | Output format: `json` or `html` |
|
|
824
|
+
| `--output`, `-o` | stdout | Write report to file instead of stdout |
|
|
825
|
+
| `--label-a` | *(from JSON meta.scanner)* | Override the label for the first file |
|
|
826
|
+
| `--label-b` | *(from JSON meta.scanner)* | Override the label for the second file |
|
|
827
|
+
|
|
828
|
+
The compare report includes:
|
|
829
|
+
- Overall delta in detection rate (percentage points)
|
|
830
|
+
- Per-category detection rate changes
|
|
831
|
+
- Per-technique detection rate changes (only techniques where the rate changed)
|
|
832
|
+
- Per-variant diff list (variants where severity changed between the two runs)
|
|
833
|
+
|
|
834
|
+
### `evadex init`
|
|
835
|
+
|
|
836
|
+
Generate a default `evadex.yaml` config file in the current directory.
|
|
837
|
+
|
|
838
|
+
```
|
|
839
|
+
evadex init
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
Creates `evadex.yaml` with sensible defaults. Edit the file and run `evadex scan --config evadex.yaml`, or drop it in the working directory for auto-discovery.
|
|
843
|
+
|
|
844
|
+
### `evadex falsepos`
|
|
845
|
+
|
|
846
|
+
Measure scanner false positive rate — values that look like sensitive data but are provably invalid.
|
|
847
|
+
|
|
848
|
+
Generates structurally plausible but mathematically invalid values (Luhn-failing credit card numbers, SSNs with reserved area codes, SINs with wrong checksums, IBAN-shaped strings with invalid mod-97 checks, etc.) and submits them to the scanner. Any value the scanner flags is a false positive.
|
|
849
|
+
|
|
850
|
+
```
|
|
851
|
+
evadex falsepos [OPTIONS]
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
| Flag | Default | Description |
|
|
855
|
+
|---|---|---|
|
|
856
|
+
| `--tool`, `-t` | `dlpscan-cli` | Adapter to use |
|
|
857
|
+
| `--category` | *(all)* | Category to test. Repeat for multiple. Supported: `credit_card`, `ssn`, `sin`, `iban`, `email`, `phone`, `ca_ramq` |
|
|
858
|
+
| `--count` | `100` | Number of false positive values per category |
|
|
859
|
+
| `--format`, `-f` | `table` | Output format: `table` (summary to stderr) or `json` (full report) |
|
|
860
|
+
| `--output`, `-o` | stdout | Write JSON report to file |
|
|
861
|
+
| `--exe` | `dlpscan` | Path to scanner executable (dlpscan-cli only) |
|
|
862
|
+
| `--cmd-style` | `python` | Command format for dlpscan-cli: `python` or `rust` |
|
|
863
|
+
| `--timeout` | `30.0` | Request timeout in seconds |
|
|
864
|
+
| `--concurrency` | `5` | Max concurrent scanner requests |
|
|
865
|
+
| `--seed` | *(random)* | Integer seed for reproducible false positive values |
|
|
866
|
+
| `--require-context` | off | Pass `--require-context` to dlpscan-rs: only flag matches when surrounding keywords are present. Requires `--cmd-style rust`. See [False positive rate and the `--require-context` tradeoff](#false-positive-rate-and-the---require-context-tradeoff) for measured impact. |
|
|
867
|
+
| `--wrap-context` | off | Embed each invalid value in a realistic category-specific sentence before submitting. Simulates how sensitive data appears in real documents. Use with `--require-context` for the most realistic false positive measurement. |
|
|
868
|
+
|
|
869
|
+
**Examples:**
|
|
870
|
+
|
|
871
|
+
```bash
|
|
872
|
+
# Test false positive rate for credit cards
|
|
873
|
+
evadex falsepos --tool dlpscan-cli --category credit_card --count 100
|
|
874
|
+
|
|
875
|
+
# All categories
|
|
876
|
+
evadex falsepos --tool dlpscan-cli --count 100
|
|
877
|
+
|
|
878
|
+
# Save JSON report
|
|
879
|
+
evadex falsepos --tool dlpscan-cli --count 100 --format json -o falsepos_report.json
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
**Output:**
|
|
883
|
+
|
|
884
|
+
```
|
|
885
|
+
credit_card 0/100 flagged (0.0%)
|
|
886
|
+
ssn 2/100 flagged (2.0%)
|
|
887
|
+
sin 0/100 flagged (0.0%)
|
|
888
|
+
...
|
|
889
|
+
|
|
890
|
+
Overall false positive rate: 0.3% (2/700)
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
The JSON report includes per-category rates, overall rate, and the list of specific values that were incorrectly flagged:
|
|
894
|
+
|
|
895
|
+
```json
|
|
896
|
+
{
|
|
897
|
+
"tool": "dlpscan-cli",
|
|
898
|
+
"count_per_category": 100,
|
|
899
|
+
"total_tested": 700,
|
|
900
|
+
"total_flagged": 2,
|
|
901
|
+
"overall_false_positive_rate": 0.3,
|
|
902
|
+
"by_category": {
|
|
903
|
+
"credit_card": {
|
|
904
|
+
"total": 100,
|
|
905
|
+
"flagged": 0,
|
|
906
|
+
"false_positive_rate": 0.0,
|
|
907
|
+
"flagged_values": []
|
|
908
|
+
},
|
|
909
|
+
"ssn": {
|
|
910
|
+
"total": 100,
|
|
911
|
+
"flagged": 2,
|
|
912
|
+
"false_positive_rate": 2.0,
|
|
913
|
+
"flagged_values": ["000-12-3456", "666-99-0001"]
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
**False positive generators by category:**
|
|
920
|
+
|
|
921
|
+
| Category | Generation strategy |
|
|
922
|
+
|---|---|
|
|
923
|
+
| `credit_card` | 16-digit numbers with card-like prefixes (4, 51, 37, 6011) that fail the Luhn check |
|
|
924
|
+
| `ssn` | `NNN-NN-NNNN` with reserved area codes: 000, 666, 900–999 |
|
|
925
|
+
| `sin` | `NNN NNN NNN` with valid first digit (1–7) but wrong Luhn check digit |
|
|
926
|
+
| `iban` | IBAN-shaped strings (GB/DE/FR) with a deliberately wrong mod-97 check digit |
|
|
927
|
+
| `email` | `user@domain.invalid` — uses IANA-reserved TLDs (`.invalid`, `.test`, `.example`, `.localhost`) |
|
|
928
|
+
| `phone` | `+1-NPA-NXX-XXXX` with invalid NANP area codes (000, 555, 911, etc.) |
|
|
929
|
+
| `ca_ramq` | RAMQ-shaped `XXXX YYMM DDSS` with invalid birth month codes (00, 13–50, 63–99) |
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
### `evadex list-payloads`
|
|
934
|
+
|
|
935
|
+
List all built-in test payloads with their categories and types.
|
|
936
|
+
|
|
937
|
+
```
|
|
938
|
+
evadex list-payloads [--type structured|heuristic]
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
| Flag | Default | Description |
|
|
942
|
+
|---|---|---|
|
|
943
|
+
| `--type` | *(all)* | Filter to `structured` or `heuristic` payloads only |
|
|
944
|
+
|
|
945
|
+
### `evadex list-techniques`
|
|
946
|
+
|
|
947
|
+
List all registered evasion generators and the techniques each one applies.
|
|
948
|
+
|
|
949
|
+
```
|
|
950
|
+
evadex list-techniques [--generator NAME]
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
| Flag | Default | Description |
|
|
954
|
+
|---|---|---|
|
|
955
|
+
| `--generator`, `-g` | *(all)* | Show techniques for a specific generator only |
|
|
956
|
+
|
|
957
|
+
### Examples
|
|
958
|
+
|
|
959
|
+
```bash
|
|
960
|
+
# Only test credit card payloads
|
|
961
|
+
evadex scan --tool dlpscan-cli --strategy text --category credit_card
|
|
962
|
+
|
|
963
|
+
# Only run unicode evasion techniques
|
|
964
|
+
evadex scan --tool dlpscan-cli --strategy text --variant-group unicode_encoding
|
|
965
|
+
|
|
966
|
+
# Only run unicode + delimiter techniques on SSN and IBAN
|
|
967
|
+
evadex scan --tool dlpscan-cli --strategy text \
|
|
968
|
+
--category ssn --category iban \
|
|
969
|
+
--variant-group unicode_encoding --variant-group delimiter
|
|
970
|
+
|
|
971
|
+
# Test a custom value (category auto-detected)
|
|
972
|
+
evadex scan --tool dlpscan-cli --input "AKIAIOSFODNN7EXAMPLE" --strategy text
|
|
973
|
+
|
|
974
|
+
# File strategy only — test DOCX extraction pipeline
|
|
975
|
+
evadex scan --tool dlpscan-cli --input "4532015112830366" --strategy docx
|
|
976
|
+
|
|
977
|
+
# Save HTML report
|
|
978
|
+
evadex scan --tool dlpscan-cli --strategy text --format html -o report.html
|
|
979
|
+
|
|
980
|
+
# Target a specific scanner binary, tag the output
|
|
981
|
+
evadex scan --tool dlpscan-cli --exe /opt/dlpscan/dlpscan --cmd-style rust \
|
|
982
|
+
--scanner-label "rust-2.0.0" --format json -o rust_results.json
|
|
983
|
+
|
|
984
|
+
# Compare two scanner builds
|
|
985
|
+
evadex scan --tool dlpscan-cli --scanner-label "python-1.3.0" -o python.json
|
|
986
|
+
evadex scan --tool dlpscan-cli --exe /opt/rust-dlpscan --cmd-style rust \
|
|
987
|
+
--scanner-label "rust-2.0.0" -o rust.json
|
|
988
|
+
evadex compare python.json rust.json --format html -o comparison.html
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
---
|
|
992
|
+
|
|
993
|
+
## CI/CD integration
|
|
994
|
+
|
|
995
|
+
evadex supports a `--min-detection-rate` flag that exits with code 1 if the scanner's detection rate falls below a threshold. Use it as a pipeline gate to prevent deploying a scanner configuration that regresses detection coverage.
|
|
996
|
+
|
|
997
|
+
```bash
|
|
998
|
+
evadex scan --tool dlpscan-cli \
|
|
999
|
+
--strategy text \
|
|
1000
|
+
--scanner-label "$(dlpscan --version)" \
|
|
1001
|
+
--format json -o results.json \
|
|
1002
|
+
--min-detection-rate 90
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
Exit code 0 means the threshold was met; exit code 1 means it was not. The report is always written before the exit check.
|
|
1006
|
+
|
|
1007
|
+
To track regressions against a known-good baseline:
|
|
1008
|
+
|
|
1009
|
+
```bash
|
|
1010
|
+
# Save a baseline from the current production scanner
|
|
1011
|
+
evadex scan --tool dlpscan-cli --scanner-label "prod-baseline" \
|
|
1012
|
+
--baseline baseline.json
|
|
1013
|
+
|
|
1014
|
+
# In CI: compare the candidate scanner against the baseline
|
|
1015
|
+
evadex scan --tool dlpscan-cli --scanner-label "candidate" \
|
|
1016
|
+
--compare-baseline baseline.json \
|
|
1017
|
+
--min-detection-rate 90
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
The `--compare-baseline` flag prints a regression summary to stderr listing any variants that were previously detected and are now missed, and any improvements.
|
|
1021
|
+
|
|
1022
|
+
---
|
|
1023
|
+
|
|
1024
|
+
## Audit log
|
|
1025
|
+
|
|
1026
|
+
evadex can append a one-line JSON record to a log file after every scan. This gives you a durable, append-only history of what was tested, when, and what the result was — useful for compliance reviews, trend tracking, and demonstrating that regular scans are being performed.
|
|
1027
|
+
|
|
1028
|
+
```bash
|
|
1029
|
+
evadex scan --tool dlpscan-cli \
|
|
1030
|
+
--scanner-label "rust-2.0.0" \
|
|
1031
|
+
--strategy text \
|
|
1032
|
+
--audit-log /var/log/evadex/audit.jsonl
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
Or set it in `evadex.yaml` so it fires automatically on every run:
|
|
1036
|
+
|
|
1037
|
+
```yaml
|
|
1038
|
+
audit_log: /var/log/evadex/audit.jsonl
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
### Audit record format
|
|
1042
|
+
|
|
1043
|
+
Each run appends exactly one line. Fields:
|
|
1044
|
+
|
|
1045
|
+
| Field | Type | Description |
|
|
1046
|
+
|---|---|---|
|
|
1047
|
+
| `timestamp` | ISO 8601 string | When the scan ran (UTC) |
|
|
1048
|
+
| `evadex_version` | string | Installed evadex version |
|
|
1049
|
+
| `operator` | string | OS username of the person who ran the scan |
|
|
1050
|
+
| `scanner_label` | string | Value of `--scanner-label` (empty if not set) |
|
|
1051
|
+
| `tool` | string | Adapter used |
|
|
1052
|
+
| `strategies` | array | Submission strategies used |
|
|
1053
|
+
| `categories` | array | Categories filtered to (empty = all structured) |
|
|
1054
|
+
| `include_heuristic` | bool | Whether heuristic categories were included |
|
|
1055
|
+
| `total` | int | Total test cases run |
|
|
1056
|
+
| `pass` | int | Variants detected |
|
|
1057
|
+
| `fail` | int | Variants that evaded scanner |
|
|
1058
|
+
| `error` | int | Adapter errors |
|
|
1059
|
+
| `pass_rate` | float | Detection rate percentage |
|
|
1060
|
+
| `output_file` | string \| null | Path of the report file written, or null |
|
|
1061
|
+
| `baseline_saved` | string \| null | Path of baseline saved, or null |
|
|
1062
|
+
| `compare_baseline` | string \| null | Path of baseline compared against, or null |
|
|
1063
|
+
| `min_detection_rate` | float \| null | Gate threshold used, or null |
|
|
1064
|
+
| `exit_code` | int | `0` if scan succeeded, `1` if detection-rate gate failed |
|
|
1065
|
+
|
|
1066
|
+
### Notes
|
|
1067
|
+
|
|
1068
|
+
- The log file is opened in append mode — existing entries are never modified or deleted.
|
|
1069
|
+
- Parent directories are created automatically if they do not exist.
|
|
1070
|
+
- A write failure (permissions, disk full, bad path) is silently ignored. The scan result and exit code are never affected by audit log errors.
|
|
1071
|
+
- The log contains detection rates and category breakdowns but **not** variant values. It is safe to store in shared log aggregation systems.
|
|
1072
|
+
|
|
1073
|
+
---
|
|
1074
|
+
|
|
1075
|
+
## Feedback loop
|
|
1076
|
+
|
|
1077
|
+
evadex Phase 2 implements a GAN-inspired feedback cycle: evadex is the **adversarial fuzzer** and your DLP scanner is the **discriminator**. When the fuzzer finds an evasion that works, the system automatically surfaces what failed and how to close the gap — without requiring manual triage.
|
|
1078
|
+
|
|
1079
|
+
After any scan that produces evasions, evadex does three things automatically:
|
|
1080
|
+
|
|
1081
|
+
1. **Prints fix suggestions to stderr** — one concrete, actionable normalisation step per unique bypass technique.
|
|
1082
|
+
2. **Writes `evadex_regressions.py`** to the current directory — a pytest file with one test function per evasion, using dlpscan's `InputGuard` API. These tests fail until the scanner is fixed.
|
|
1083
|
+
3. **Optionally writes a structured JSON feedback report** via `--feedback-report PATH`.
|
|
1084
|
+
|
|
1085
|
+
### Fix suggestions
|
|
1086
|
+
|
|
1087
|
+
Suggestions are printed to stderr after the scan summary whenever evasions are found:
|
|
1088
|
+
|
|
1089
|
+
```
|
|
1090
|
+
=== Fix Suggestions ===
|
|
1091
|
+
• homoglyph_substitution (unicode_encoding)
|
|
1092
|
+
Add Cyrillic/Greek lookalikes to homoglyph normalisation map: О→0, З→3, ο→0, Α→A, Ζ→Z.
|
|
1093
|
+
Apply NFKC normalisation then a homoglyph table lookup before scanning
|
|
1094
|
+
• zero_width_zwsp (unicode_encoding)
|
|
1095
|
+
Strip U+200B (Zero Width Space) from input in the normalisation pipeline before pattern matching
|
|
1096
|
+
• base64_standard (encoding)
|
|
1097
|
+
Add a base64 decode pass to the normalisation pipeline; scan the decoded content
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
Each suggestion names the technique, the generator group it belongs to, and a specific normalisation step to add to the scanner's input pipeline.
|
|
1101
|
+
|
|
1102
|
+
### Regression test file
|
|
1103
|
+
|
|
1104
|
+
`evadex_regressions.py` is written to the current directory whenever there are evasions. Each test function:
|
|
1105
|
+
|
|
1106
|
+
- Is named after the payload label and evasion technique (`test_visa_16_digit_homoglyph_substitution`)
|
|
1107
|
+
- Imports and invokes dlpscan's `InputGuard` with the appropriate preset (`PCI_DSS`, `PII`, or `CREDENTIALS`)
|
|
1108
|
+
- Scans the exact obfuscated variant value that evaded detection
|
|
1109
|
+
- Asserts `not result.is_clean` — the test passes once the scanner is fixed
|
|
1110
|
+
|
|
1111
|
+
```python
|
|
1112
|
+
def test_visa_16_digit_homoglyph_substitution():
|
|
1113
|
+
"""Visa 16-digit evaded via homoglyph_substitution — should be detected"""
|
|
1114
|
+
from dlpscan import InputGuard, Preset
|
|
1115
|
+
guard = InputGuard(presets=[Preset.PCI_DSS])
|
|
1116
|
+
result = guard.scan('4532\u041e15112830366') # Visually similar Cyrillic/Greek characters substituted
|
|
1117
|
+
assert not result.is_clean
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def test_canada_sin_zero_width_zwsp():
|
|
1121
|
+
"""Canada SIN evaded via zero_width_zwsp — should be detected"""
|
|
1122
|
+
from dlpscan import InputGuard, Preset
|
|
1123
|
+
guard = InputGuard(presets=[Preset.PII])
|
|
1124
|
+
result = guard.scan('0\u200b4\u200b6\u200b \u200b4\u200b5\u200b4\u200b \u200b2\u200b8\u200b6') # Zero-width ZWSP between every character
|
|
1125
|
+
assert not result.is_clean
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
Run the generated file with:
|
|
1129
|
+
|
|
1130
|
+
```bash
|
|
1131
|
+
pytest evadex_regressions.py
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
Tests fail until the scanner is patched. Each time you fix a technique and re-run evadex, failing tests disappear and the regression file is regenerated to reflect the remaining gaps.
|
|
1135
|
+
|
|
1136
|
+
### `--feedback-report PATH`
|
|
1137
|
+
|
|
1138
|
+
Saves a structured JSON report containing everything in one file:
|
|
1139
|
+
|
|
1140
|
+
```bash
|
|
1141
|
+
evadex scan --feedback-report feedback.json
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
**Report structure:**
|
|
1145
|
+
|
|
1146
|
+
```json
|
|
1147
|
+
{
|
|
1148
|
+
"meta": {
|
|
1149
|
+
"timestamp": "2026-04-07T14:22:01.123456+00:00",
|
|
1150
|
+
"scanner": "python-1.6.0",
|
|
1151
|
+
"total_tests": 590,
|
|
1152
|
+
"total_evasions": 76
|
|
1153
|
+
},
|
|
1154
|
+
"techniques": [
|
|
1155
|
+
{
|
|
1156
|
+
"technique": "homoglyph_substitution",
|
|
1157
|
+
"generator": "unicode_encoding",
|
|
1158
|
+
"count": 23,
|
|
1159
|
+
"example_variants": ["4532\u041e15112830366", "4\u03bf32015112830366"]
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
"technique": "zero_width_zwsp",
|
|
1163
|
+
"generator": "unicode_encoding",
|
|
1164
|
+
"count": 18,
|
|
1165
|
+
"example_variants": ["0\u200b4\u200b6 4\u200b5\u200b4 2\u200b8\u200b6"]
|
|
1166
|
+
}
|
|
1167
|
+
],
|
|
1168
|
+
"fix_suggestions": [
|
|
1169
|
+
{
|
|
1170
|
+
"technique": "homoglyph_substitution",
|
|
1171
|
+
"generator": "unicode_encoding",
|
|
1172
|
+
"description": "Sensitive values bypassed detection by substituting ASCII digits/letters with visually identical Unicode characters from Cyrillic, Greek, or other scripts",
|
|
1173
|
+
"suggested_fix": "Add Cyrillic/Greek lookalikes to homoglyph normalisation map: О→0, З→3, ο→0, Α→A, Ζ→Z. Apply NFKC normalisation then a homoglyph table lookup before scanning"
|
|
1174
|
+
}
|
|
1175
|
+
],
|
|
1176
|
+
"regression_test_code": "\"\"\"Regression tests generated by evadex.\n...\"\"\"\nimport pytest\n\n\ndef test_visa_16_digit_homoglyph_substitution():\n ..."
|
|
1177
|
+
}
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
The report is always written, even when there are no evasions (techniques and fix_suggestions will be empty arrays, regression_test_code will be an empty string).
|
|
1181
|
+
|
|
1182
|
+
### Three-phase design
|
|
1183
|
+
|
|
1184
|
+
| Phase | Role | Status |
|
|
1185
|
+
|---|---|---|
|
|
1186
|
+
| Phase 1 | Adversarial fuzzer — evasion generators test known-sensitive values against the scanner | ✅ Done |
|
|
1187
|
+
| Phase 2 | Feedback generator — surfaces fix suggestions, regression tests, and structured reports when evasions succeed | ✅ Done |
|
|
1188
|
+
| Phase 3 | False-positive adversary — generates values that *look* sensitive but aren't, to measure scanner precision | ✅ Done (`evadex falsepos`) |
|
|
1189
|
+
|
|
1190
|
+
Together, Phase 1 measures **false negatives** (sensitive values the scanner misses) and Phase 3 measures **false positives** (non-sensitive values the scanner incorrectly flags). Both are needed for a complete picture of scanner accuracy.
|
|
1191
|
+
|
|
1192
|
+
---
|
|
1193
|
+
|
|
1194
|
+
## Adapters
|
|
1195
|
+
|
|
1196
|
+
### Built-in: `dlpscan-cli`
|
|
1197
|
+
|
|
1198
|
+
Invokes the [dlpscan](https://github.com/oxide11/dlpscan) CLI directly as a subprocess. evadex was built and tested with dlpscan as the reference scanner. Requires `dlpscan` to be installed and on `PATH` (or provide `--exe`).
|
|
1199
|
+
|
|
1200
|
+
```bash
|
|
1201
|
+
evadex scan --tool dlpscan-cli
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
For file strategies, evadex builds the document in memory and writes it to a temp file, runs the scanner against it, then immediately deletes the temp file. No persistent disk footprint from test data. File extraction support in dlpscan requires `pip install dlpscan[office]`.
|
|
1205
|
+
|
|
1206
|
+
### Built-in: `dlpscan`
|
|
1207
|
+
|
|
1208
|
+
Generic HTTP adapter for any DLP tool that exposes a REST API. Sends plain text to `POST /scan` with a `{"content": "..."}` body, and file uploads to `POST /scan/file` as multipart form data. Expects a JSON response with a `detected` boolean (configurable via the `response_detected_key` extra config option).
|
|
1209
|
+
|
|
1210
|
+
```bash
|
|
1211
|
+
evadex scan --tool dlpscan --url http://my-dlpscan-server:8080 --api-key my-key
|
|
1212
|
+
```
|
|
1213
|
+
|
|
1214
|
+
### Adding a custom adapter
|
|
1215
|
+
|
|
1216
|
+
1. Create a file anywhere in your project, e.g. `my_adapter.py`.
|
|
1217
|
+
|
|
1218
|
+
2. Subclass `BaseAdapter` and implement `submit()`:
|
|
1219
|
+
|
|
1220
|
+
```python
|
|
1221
|
+
from evadex.adapters.base import BaseAdapter
|
|
1222
|
+
from evadex.core.registry import register_adapter
|
|
1223
|
+
from evadex.core.result import Payload, Variant, ScanResult
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
@register_adapter("my-tool")
|
|
1227
|
+
class MyToolAdapter(BaseAdapter):
|
|
1228
|
+
name = "my-tool"
|
|
1229
|
+
|
|
1230
|
+
async def submit(self, payload: Payload, variant: Variant) -> ScanResult:
|
|
1231
|
+
# Send variant.value to your scanner however it expects it.
|
|
1232
|
+
# variant.strategy is "text", "docx", "pdf", or "xlsx".
|
|
1233
|
+
# Return a ScanResult with detected=True/False.
|
|
1234
|
+
response = await call_my_scanner(variant.value)
|
|
1235
|
+
detected = response.get("found", False)
|
|
1236
|
+
return ScanResult(
|
|
1237
|
+
payload=payload,
|
|
1238
|
+
variant=variant,
|
|
1239
|
+
detected=detected,
|
|
1240
|
+
raw_response=response,
|
|
1241
|
+
)
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
3. Import your adapter before invoking evadex (so the `@register_adapter` decorator fires), then use it:
|
|
1245
|
+
|
|
1246
|
+
```bash
|
|
1247
|
+
python -c "import my_adapter" && evadex scan --tool my-tool
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
Or wire it up properly as a package with an entry point in `pyproject.toml`:
|
|
1251
|
+
|
|
1252
|
+
```toml
|
|
1253
|
+
[project.entry-points."evadex.adapters"]
|
|
1254
|
+
my-tool = "my_package.my_adapter"
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
**Optional hooks:**
|
|
1258
|
+
|
|
1259
|
+
```python
|
|
1260
|
+
async def setup(self):
|
|
1261
|
+
# Called once before the batch — open connections, authenticate, etc.
|
|
1262
|
+
self._session = await open_session()
|
|
1263
|
+
|
|
1264
|
+
async def teardown(self):
|
|
1265
|
+
# Called once after the batch — clean up connections.
|
|
1266
|
+
await self._session.close()
|
|
1267
|
+
|
|
1268
|
+
async def health_check(self) -> bool:
|
|
1269
|
+
# Optional — verify the scanner is reachable.
|
|
1270
|
+
return await ping_scanner()
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
**File strategies:** `variant.strategy` tells you which format evadex wants to use. If your scanner only supports one method, handle what you need:
|
|
1274
|
+
|
|
1275
|
+
```python
|
|
1276
|
+
from evadex.adapters.dlpscan.file_builder import FileBuilder
|
|
1277
|
+
|
|
1278
|
+
async def submit(self, payload, variant):
|
|
1279
|
+
if variant.strategy == "text":
|
|
1280
|
+
raw = await self._scan_text(variant.value)
|
|
1281
|
+
else:
|
|
1282
|
+
data, mime = FileBuilder.build(variant.value, variant.strategy)
|
|
1283
|
+
raw = await self._scan_file(data, mime)
|
|
1284
|
+
...
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
`FileBuilder.build(text, fmt)` returns `(bytes, mime_type)` entirely in memory — no disk writes.
|
|
1288
|
+
|
|
1289
|
+
---
|
|
1290
|
+
|
|
1291
|
+
## Output schema
|
|
1292
|
+
|
|
1293
|
+
### Top-level
|
|
1294
|
+
|
|
1295
|
+
```json
|
|
1296
|
+
{
|
|
1297
|
+
"meta": { ... },
|
|
1298
|
+
"results": [ ... ]
|
|
1299
|
+
}
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
### `meta`
|
|
1303
|
+
|
|
1304
|
+
| Field | Type | Description |
|
|
1305
|
+
|---|---|---|
|
|
1306
|
+
| `timestamp` | ISO 8601 string | When the scan ran (UTC) |
|
|
1307
|
+
| `scanner` | string | Scanner label from `--scanner-label` (empty string if not set) |
|
|
1308
|
+
| `total` | int | Total test cases run |
|
|
1309
|
+
| `pass` | int | Variants detected by scanner |
|
|
1310
|
+
| `fail` | int | Variants that evaded scanner |
|
|
1311
|
+
| `error` | int | Adapter errors |
|
|
1312
|
+
| `pass_rate` | float | `pass / total * 100`, rounded to one decimal |
|
|
1313
|
+
| `summary_by_category` | object | Per-category pass/fail/error counts, sorted alphabetically by category name |
|
|
1314
|
+
| `summary_by_generator` | object | Per-generator pass/fail/error counts, sorted alphabetically by generator name |
|
|
1315
|
+
|
|
1316
|
+
### `results[]`
|
|
1317
|
+
|
|
1318
|
+
| Field | Type | Description |
|
|
1319
|
+
|---|---|---|
|
|
1320
|
+
| `payload.value` | string | Original sensitive value |
|
|
1321
|
+
| `payload.category` | string | Detected category enum value |
|
|
1322
|
+
| `payload.category_type` | string | `structured` or `heuristic` — see [Structured vs heuristic categories](#structured-vs-heuristic-categories) |
|
|
1323
|
+
| `payload.label` | string | Human-readable label |
|
|
1324
|
+
| `variant.value` | string | Transformed/obfuscated value submitted to scanner |
|
|
1325
|
+
| `variant.generator` | string | Which generator produced this variant |
|
|
1326
|
+
| `variant.technique` | string | Machine-readable technique name |
|
|
1327
|
+
| `variant.transform_name` | string | Human-readable description of the transform |
|
|
1328
|
+
| `variant.strategy` | string | Submission strategy: `text`, `docx`, `pdf`, `xlsx` |
|
|
1329
|
+
| `detected` | bool | Whether the scanner flagged this variant. `false` for error results — check `severity` to distinguish |
|
|
1330
|
+
| `severity` | string | `pass` (detected), `fail` (not detected), or `error` (adapter error) |
|
|
1331
|
+
| `duration_ms` | float | Time for this test case in milliseconds |
|
|
1332
|
+
| `error` | string \| null | Error message if adapter threw; `null` otherwise |
|
|
1333
|
+
| `raw_response` | object | Raw parsed response from the adapter. For `dlpscan-cli` this is `{"matches": [...]}`. May contain match objects that include the variant value — treat the output file accordingly. |
|
|
1334
|
+
|
|
1335
|
+
---
|
|
1336
|
+
|
|
1337
|
+
## Coverage
|
|
1338
|
+
|
|
1339
|
+
evadex payload coverage relative to the dlpscan-rs pattern library (**557 individual sub-patterns** across 126 categories).
|
|
1340
|
+
|
|
1341
|
+
Each row shows coverage at the **sub-pattern level** — e.g. "Credit Card Numbers — 7/7" means all seven card-network variants (Visa, Amex, Mastercard, Discover, JCB, UnionPay, Diners) have a dedicated seed payload.
|
|
1342
|
+
|
|
1343
|
+
### Identity documents
|
|
1344
|
+
|
|
1345
|
+
| Region / Category | dlpscan-rs sub-patterns | evadex coverage | Notes |
|
|
1346
|
+
|---|---:|---:|---|
|
|
1347
|
+
| Credit Card Numbers | 7 | **7/7** ✓ | Visa, Amex, Mastercard, Discover, JCB, UnionPay, Diners |
|
|
1348
|
+
| US Driver's Licences | 51 + 1 generic | **52/52** ✓ | All 50 states + DC + generic |
|
|
1349
|
+
| US — other identifiers | 12 | **12/12** ✓ | SSN, ITIN, EIN, MBI, Passport, Passport Card, NPI, DoD ID, KTN, DEA, USA Routing Number, US Phone Number — completed this release |
|
|
1350
|
+
| North America — Canada | 29 | **29/29** ✓ | All provincial health/DL/corporate/BN/SIN; 3 DL payloads corrected this release |
|
|
1351
|
+
| North America — Mexico | 7 | **7/7** ✓ | CURP, RFC, Clave Elector, INE CIC, INE OCR, NSS, Passport — all added this release |
|
|
1352
|
+
| Europe — United Kingdom | 7 | **7/7** ✓ | NIN, DL, NHS, Passport, Phone, Sort Code, UTR — completed this release |
|
|
1353
|
+
| Europe — Germany | 6 | **6/6** ✓ | Tax ID, ID, IBAN, Social Insurance, DL, Passport — completed this release |
|
|
1354
|
+
| Europe — France | 5 | **5/5** ✓ | NIR, CNI, IBAN, DL, Passport — completed this release |
|
|
1355
|
+
| Europe — Spain | 5 | **5/5** ✓ | DNI, IBAN, NIE, NSS, Passport — completed this release |
|
|
1356
|
+
| Europe — Italy | 5 | **5/5** ✓ | Codice Fiscale/SSN, DL, Partita IVA, Passport — completed this release |
|
|
1357
|
+
| Europe — Netherlands | 4 | **4/4** ✓ | BSN, DL, IBAN, Passport — completed this release |
|
|
1358
|
+
| Europe — Poland | 6 | **6/6** ✓ | PESEL, NIP, REGON, DL, ID Card, Passport — completed this release |
|
|
1359
|
+
| Europe — Sweden | 4 | **4/4** ✓ | PIN, Org Number, DL, Passport — completed this release |
|
|
1360
|
+
| Europe — Norway | 4 | **4/4** ✓ | FNR, D-Number, DL, Passport — completed this release |
|
|
1361
|
+
| Europe — Switzerland | 4 | **4/4** ✓ | AHV, UID, DL, Passport — completed this release |
|
|
1362
|
+
| Europe — Finland | 3 | **3/3** ✓ | HETU, DL, Passport — completed this release |
|
|
1363
|
+
| Europe — Austria | 5 | **5/5** ✓ | SVN, Tax, DL, ID Card, Passport — completed this release |
|
|
1364
|
+
| Europe — Belgium | 4 | **4/4** ✓ | NRN, VAT, DL, Passport — completed this release |
|
|
1365
|
+
| Europe — (19 other EU/EEA countries) | ~75 | **~75/75** ✓ | Bulgaria, Croatia, Cyprus, Czech, Denmark, EU-ETD, Estonia, Greece, Hungary, Iceland, Ireland, Latvia, Liechtenstein, Lithuania, Luxembourg, Malta, Portugal, Romania, Slovakia, Slovenia, Turkey — all sub-patterns added this release |
|
|
1366
|
+
| Asia-Pacific — Australia | 11 | **11/11** ✓ | TFN, Medicare, Passport, 8 state DL variants — completed this release |
|
|
1367
|
+
| Asia-Pacific — China / HK / Macau / TW | 5 | **5/5** ✓ | Resident ID, Passport, HK ID, Macau ID, TW NID — completed this release |
|
|
1368
|
+
| Asia-Pacific — India | 6 | **6/6** ✓ | Aadhaar, PAN, DL, Passport, Ration Card, Voter ID — completed this release |
|
|
1369
|
+
| Asia-Pacific — Japan | 6 | **6/6** ✓ | My Number, DL, Health Ins, Juminhyo, Passport, Residence Card — completed this release |
|
|
1370
|
+
| Asia-Pacific — Singapore | 4 | **4/4** ✓ | NRIC, FIN, DL, Passport — completed this release |
|
|
1371
|
+
| Asia-Pacific — South Korea | 3 | **3/3** ✓ | RRN, DL, Passport — completed this release |
|
|
1372
|
+
| Asia-Pacific — New Zealand | 4 | **4/4** ✓ | IRD, NHI, DL, Passport — completed this release |
|
|
1373
|
+
| Asia-Pacific — Philippines | 6 | **6/6** ✓ | PhilSys, PhilHealth, SSS, TIN, UMID, Passport — completed this release |
|
|
1374
|
+
| Asia-Pacific — (7 other AP countries) | ~24 | **~24/24** ✓ | Bangladesh, Indonesia, Malaysia, Pakistan, Sri Lanka, Thailand, Vietnam — all sub-patterns added this release |
|
|
1375
|
+
| Latin America — Brazil | 6 | **6/6** ✓ | CPF, CNPJ, CNH, RG, SUS, Passport — completed this release |
|
|
1376
|
+
| Latin America — Argentina | 3 | **3/3** ✓ | DNI, CUIL/CUIT, Passport — completed this release |
|
|
1377
|
+
| Latin America — Chile | 2 | **2/2** ✓ | RUT, Passport — completed this release |
|
|
1378
|
+
| Latin America — Colombia | 4 | **4/4** ✓ | Cedula, NIT, NUIP, Passport — completed this release |
|
|
1379
|
+
| Latin America — (8 other LatAm countries) | ~27 | **~27/27** ✓ | Costa Rica, Ecuador, Paraguay, Peru, Uruguay, Venezuela — all sub-patterns added this release |
|
|
1380
|
+
| Middle East — UAE | 3 | **3/3** ✓ | Emirates ID, Passport, Visa — completed this release |
|
|
1381
|
+
| Middle East — (10 other ME countries) | ~21 | **~21/21** ✓ | Bahrain, Iran, Iraq, Israel, Jordan, Kuwait, Lebanon, Qatar, Saudi Arabia — all sub-patterns added this release |
|
|
1382
|
+
| Africa — South Africa | 3 | **3/3** ✓ | ID, DL, Passport — completed this release |
|
|
1383
|
+
| Africa — (9 other African countries) | ~27 | **~27/27** ✓ | Egypt, Ethiopia, Ghana, Kenya, Morocco, Nigeria, Tanzania, Tunisia, Uganda — all sub-patterns added this release |
|
|
1384
|
+
|
|
1385
|
+
### Financial, secrets, and functional
|
|
1386
|
+
|
|
1387
|
+
| Category | dlpscan-rs sub-patterns | evadex coverage | Notes |
|
|
1388
|
+
|---|---:|---:|---|
|
|
1389
|
+
| Banking & Financial | 5 | **5/5** ✓ | IBAN, SWIFT, ABA, Canada Transit, US Bank Account |
|
|
1390
|
+
| IBAN (country-specific) | 4 named | **4/4** ✓ | UK, DE, FR, ES, NL IBANs all represented |
|
|
1391
|
+
| Banking Authentication | 3 | **3/3** ✓ | PIN Block, Encryption Key, HSM Key — completed this release |
|
|
1392
|
+
| Cryptocurrency | 7 | **7/7** ✓ | Bitcoin (legacy + Bech32), Ethereum, Bitcoin Cash, Litecoin, Monero, Ripple — completed this release |
|
|
1393
|
+
| Card Track Data | 2 | **2/2** ✓ | Track 1, Track 2 — completed this release |
|
|
1394
|
+
| Check & MICR | 3 | **3/3** ✓ | MICR, Cashier Check, Check Number — completed this release |
|
|
1395
|
+
| Cloud Secrets | 3 | **3/3** ✓ | AWS Access Key, AWS Secret Key, Google API Key — completed this release |
|
|
1396
|
+
| Code Platform Secrets | 5 | **5/5** ✓ | GitHub Classic, OAuth, Fine-Grained PAT, NPM Token, PyPI Token — completed this release |
|
|
1397
|
+
| Messaging Secrets | 6 | **6/6** ✓ | Slack Bot, Slack User, Slack Webhook, Mailgun, SendGrid, Twilio — completed this release |
|
|
1398
|
+
| Generic Secrets | 4 | **4/4** ✓ | JWT, Bearer Token, DB Connection String, Private Key — completed this release |
|
|
1399
|
+
| Payment Secrets | 2 | **2/2** ✓ | Stripe Secret Key, Stripe Publishable Key — completed this release |
|
|
1400
|
+
| Contact Information | 5 | **5/5** ✓ | Email, Phone (E.164), IPv4, IPv6, MAC Address — completed this release |
|
|
1401
|
+
| Device Identifiers | 5 | **5/5** ✓ | ICCID, IDFA/IDFV, IMEI, IMEISV, MEID — completed this release |
|
|
1402
|
+
| Geolocation | 2 | **2/2** ✓ | GPS Coordinates, Geohash — completed this release |
|
|
1403
|
+
| Securities Identifiers | 6 | **6/6** ✓ | ISIN, CUSIP, FIGI, LEI, SEDOL, Ticker Symbol — completed this release |
|
|
1404
|
+
| Medical Identifiers | 4 | **4/4** ✓ | NDC Code, DEA Number, Health Plan ID, ICD-10 Code — completed this release |
|
|
1405
|
+
| Loan & Mortgage | 4 | **4/4** ✓ | Loan Number, ULI, LTV Ratio, MERS MIN — completed this release |
|
|
1406
|
+
| Legal Identifiers | 2 | **2/2** ✓ | US Federal Case Number, Court Docket Number — completed this release |
|
|
1407
|
+
| Regulatory Identifiers | 6 | **6/6** ✓ | AML Case ID, CTR, Compliance Case, FinCEN, OFAC SDN, SAR — completed this release |
|
|
1408
|
+
| Insurance Identifiers | 2 | **2/2** ✓ | Policy Number, Claim Number — completed this release |
|
|
1409
|
+
| Internal Banking Refs | 2 | **2/2** ✓ | Internal Account Ref, Teller ID — completed this release |
|
|
1410
|
+
| Property Identifiers | 2 | **2/2** ✓ | Parcel Number, Title Deed — completed this release |
|
|
1411
|
+
| Social Media | 2 | **2/2** ✓ | Twitter Handle, Hashtag — completed this release |
|
|
1412
|
+
| Employment | 2 | **2/2** ✓ | Employee ID, Work Permit — completed this release |
|
|
1413
|
+
| Education | 1 | **1/1** ✓ | EDU Email |
|
|
1414
|
+
| Dates | 3 | **3/3** ✓ | ISO, US, EU date formats |
|
|
1415
|
+
| Postal Codes | 5 | **5/5** ✓ | UK, US ZIP+4, Canada, Brazil CEP, Japan — completed this release |
|
|
1416
|
+
| Personal Identifiers | 2 | **2/2** ✓ | Date of Birth, Gender Marker — completed this release |
|
|
1417
|
+
| Primary Account Numbers | 2 | **2/2** ✓ | PAN (via credit cards), Masked PAN |
|
|
1418
|
+
| Customer Financial Data | 4 | **4/4** ✓ | Balance with Currency, Account Balance, DTI Ratio, Income Amount — completed this release |
|
|
1419
|
+
| Authentication Tokens | 1 | **1/1** ✓ | Session ID |
|
|
1420
|
+
| Biometric Identifiers | 2 | **2/2** ✓ | Template ID, Biometric Hash (via IDFA payload) |
|
|
1421
|
+
| VIN | 1 | **1/1** ✓ | Vehicle Identification Number |
|
|
1422
|
+
| Wire Transfer | 6 | **6/6** ✓ | Fedwire IMAD, CHIPS UID, Wire Reference Number, ACH Trace Number, ACH Batch Number, SEPA Reference — completed this release |
|
|
1423
|
+
|
|
1424
|
+
### Classification & governance labels
|
|
1425
|
+
|
|
1426
|
+
| Category | dlpscan-rs sub-patterns | evadex coverage | Notes |
|
|
1427
|
+
|---|---:|---:|---|
|
|
1428
|
+
| Corporate Classification | 9 | **9/9** ✓ | Confidential, DND, Embargoed, Eyes Only, Highly Conf, Internal Only, NTK, Proprietary, Restricted — completed this release |
|
|
1429
|
+
| Data Classification Labels | 8 | **8/8** ✓ | Top Secret, CUI, Classified Conf, FOUO, LES, NOFORN, SBU, Secret — completed this release |
|
|
1430
|
+
| Privacy Classification | 10 | **10/10** ✓ | HIPAA, PCI-DSS, CCPA, FERPA, GDPR, GLBA, NPI, PHI, PII, SOX — completed this release |
|
|
1431
|
+
| Financial Regulatory Labels | 7 | **7/7** ✓ | MNPI, Draft-Not-for-Circ, Info Barrier, Inside Info, Invest Restricted, Market Sensitive, Pre-Decisional — completed this release |
|
|
1432
|
+
| Privileged Information | 7 | **7/7** ✓ | Attorney-Client, Legal Privilege, Litigation Hold, Privileged Info, P&C, Protected by Priv, Work Product — completed this release |
|
|
1433
|
+
| Supervisory Information | 6 | **6/6** ✓ | CSI, Exam Findings, Non-Public, Restricted, Supervisory Conf, Supervisory Ctrl — completed this release |
|
|
1434
|
+
| URLs with Credentials | 2 | **2/2** ✓ | URL with Password, URL with Token — completed this release |
|
|
1435
|
+
| PCI Sensitive Data | 1 | **1/1** ✓ | Cardholder Name |
|
|
1436
|
+
|
|
1437
|
+
**Summary:** evadex covers **489/557 sub-patterns** (88%) across all 126 dlpscan-rs categories with **554 seed payloads**. Of those 489: 421 structured categories confirmed detected by direct dlpscan-rs seed scan; 68 heuristic categories excluded from scanner verification per design (JWT, API keys, labels). The remaining 68 unrepresented sub-patterns are low-specificity numeric patterns (e.g. 6–9 digit sequences) where the same dlpscan regex fires on dozens of existing payloads — no distinct seed value is feasible without a context keyword. Seed-scan verified against dlpscan-rs — see `new_cat_verification.json` for per-category results.
|
|
1438
|
+
|
|
1439
|
+
---
|
|
1440
|
+
|
|
1441
|
+
## Security notes
|
|
1442
|
+
|
|
1443
|
+
- **API keys:** Prefer the `EVADEX_API_KEY` environment variable over the `--api-key` CLI flag. Command-line arguments are visible in process listings (`ps aux`) and may be saved in shell history.
|
|
1444
|
+
- **Output files:** The JSON report's `raw_response` fields may contain scanner match objects that echo variant values (transformed versions of sensitive test data). Apply appropriate access controls to report files.
|
|
1445
|
+
- **Temp files:** The `dlpscan-cli` adapter writes each test variant to a temp file for subprocess invocation and deletes it immediately after the scan. No persistent disk footprint from test data.
|
|
1446
|
+
- **Network isolation:** Run evadex and the scanner on an isolated test network. Test variant values are obfuscated but structurally derived from real sensitive patterns.
|
|
1447
|
+
|
|
1448
|
+
---
|
|
1449
|
+
|
|
1450
|
+
## License
|
|
1451
|
+
|
|
1452
|
+
MIT — see [LICENSE](LICENSE).
|