authztrace 0.3.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohamed Taha Slimani (@Asttr0)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,327 @@
1
+ Metadata-Version: 2.4
2
+ Name: authztrace
3
+ Version: 0.3.1
4
+ Summary: Authorization contract testing for IDOR/BOLA — prove user A can't touch user B's objects, in CI.
5
+ Author: Mohamed Taha Slimani (@Asttr0)
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Asttr0/authztrace
8
+ Project-URL: Issues, https://github.com/Asttr0/authztrace/issues
9
+ Keywords: security,idor,bola,api-security,authorization,owasp,appsec,ci,dast
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Security
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: requests>=2.28
18
+ Requires-Dist: PyYAML>=6.0
19
+ Provides-Extra: dev
20
+ Requires-Dist: build>=1.2; extra == "dev"
21
+ Requires-Dist: pytest>=8; extra == "dev"
22
+ Requires-Dist: ruff>=0.8; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # AuthzTrace
26
+
27
+ **Authorization tests for IDOR/BOLA bugs.**
28
+
29
+ AuthzTrace proves that one user cannot access another user's objects by replaying an authorization contract against your real API.
30
+
31
+ ![status](https://img.shields.io/badge/status-alpha-orange)
32
+ ![license](https://img.shields.io/badge/license-MIT-blue)
33
+ ![OWASP API](https://img.shields.io/badge/OWASP_API-%231_BOLA-red)
34
+ ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
35
+
36
+ Most scanners cannot reliably find IDOR/BOLA because they do not know ownership. They can see `/api/invoices/inv_123`, but they do not know that `inv_123` belongs to Alice and must not be readable by Bob.
37
+
38
+ AuthzTrace makes that missing context explicit:
39
+
40
+ ```text
41
+ Alice owns inv_alice_001
42
+ Bob owns inv_bob_002
43
+
44
+ AuthzTrace checks:
45
+ - Alice can read Alice's invoice
46
+ - Bob cannot read Alice's invoice
47
+ - Anonymous users cannot read either invoice
48
+ - The same policy holds for path IDs, query IDs, JSON body IDs, and more
49
+ ```
50
+
51
+ ```mermaid
52
+ flowchart LR
53
+ A[Ownership contract] --> B[Generated actor x object matrix]
54
+ B --> C[Preflight setup checks]
55
+ C --> D[Authorization attack rows]
56
+ D --> E[SARIF, JSON, JUnit, terminal]
57
+ E --> F[Fail the PR on real BOLA]
58
+ ```
59
+
60
+ ## Why It Exists
61
+
62
+ Broken Object Level Authorization is the classic IDOR problem: changing an object ID lets one user reach another user's data. It is still hard to catch automatically because authorization is business logic.
63
+
64
+ AuthzTrace is not a crawler. It is closer to a contract test:
65
+
66
+ | You declare | AuthzTrace proves |
67
+ |---|---|
68
+ | Actors and auth tokens | Each identity can authenticate |
69
+ | Object IDs and owners | Owners can access their own objects |
70
+ | API endpoints | Other users are denied |
71
+ | Response markers | Denied responses do not leak protected data |
72
+
73
+ If credentials or test fixtures are broken, AuthzTrace exits with a setup error instead of giving a false green result.
74
+
75
+ ## Quick Demo
76
+
77
+ Install AuthzTrace:
78
+
79
+ ```bash
80
+ pip install authztrace
81
+ ```
82
+
83
+ For local development from this repo:
84
+
85
+ ```bash
86
+ git clone https://github.com/Asttr0/AuthzTrace.git
87
+ cd AuthzTrace
88
+ pip install -e .
89
+ pip install -r examples/vulnerable-api/requirements.txt
90
+ ```
91
+
92
+ Start the deliberately vulnerable demo API:
93
+
94
+ ```bash
95
+ python examples/vulnerable-api/app.py
96
+ ```
97
+
98
+ In another terminal, run AuthzTrace:
99
+
100
+ ```bash
101
+ export ALICE_TOKEN=alice-token
102
+ export BOB_TOKEN=bob-token
103
+ authztrace run -c examples/authztrace.yaml
104
+ ```
105
+
106
+ You get a real authorization finding:
107
+
108
+ ```text
109
+ RESULT ACTOR TARGET EXPECT STATUS METHOD PATH
110
+ ------------------------------------------------------------------------------------------------
111
+ PASS alice alice allow 200 GET /api/invoices/inv_alice_001
112
+ FAIL bob alice deny 200 GET /api/invoices/inv_alice_001
113
+ -> BOLA: 'bob' accessed alice's invoice (inv_alice_001) - HTTP 200
114
+ PASS anon alice deny 401 GET /api/invoices/inv_alice_001
115
+ ...
116
+ SKIP alice alice allow - DELETE /api/invoices/inv_alice_001
117
+ -> unsafe DELETE skipped in read-only mode
118
+ ...
119
+ 12 passed, 6 failed, 0 warnings, 0 errors, 6 skipped, 24 checks
120
+ categories: bola=6, unsafe_skipped=6
121
+ ```
122
+
123
+ Stop the first server with `Ctrl+C`, then run the fixed API:
124
+
125
+ ```bash
126
+ SECURE=1 python examples/vulnerable-api/app.py
127
+ authztrace run -c examples/authztrace.yaml
128
+ ```
129
+
130
+ The same contract becomes green:
131
+
132
+ ```text
133
+ 18 passed, 0 failed, 0 warnings, 0 errors, 6 skipped, 24 checks
134
+ categories: unsafe_skipped=6
135
+ ```
136
+
137
+ ## Minimal Contract
138
+
139
+ ```yaml
140
+ base_url: http://localhost:3000
141
+
142
+ actors:
143
+ alice: { auth: { type: bearer, token: "${ALICE_TOKEN}" } }
144
+ bob: { auth: { type: bearer, token: "${BOB_TOKEN}" } }
145
+ anon: { auth: { type: none } }
146
+
147
+ resources:
148
+ invoice:
149
+ ids:
150
+ alice: inv_alice_001
151
+ bob: inv_bob_002
152
+ markers:
153
+ alice: "Alice private invoice"
154
+ bob: "Bob private invoice"
155
+ endpoints:
156
+ - name: read invoice
157
+ request: GET /api/invoices/{id}
158
+ assertions:
159
+ allow_contains: ["{marker}"]
160
+ deny_not_contains: ["{marker}"]
161
+
162
+ policy:
163
+ default: owner-only
164
+ deny_status: [401, 403, 404]
165
+ ```
166
+
167
+ From this, AuthzTrace generates the full matrix:
168
+
169
+ ```text
170
+ alice -> alice invoice should allow
171
+ bob -> alice invoice should deny
172
+ anon -> alice invoice should deny
173
+ alice -> bob invoice should deny
174
+ bob -> bob invoice should allow
175
+ anon -> bob invoice should deny
176
+ ```
177
+
178
+ ## Endpoint Shapes
179
+
180
+ Object IDs can live almost anywhere:
181
+
182
+ ```yaml
183
+ endpoints:
184
+ - request: GET /api/invoices/{id}
185
+
186
+ - method: GET
187
+ path: /api/invoices
188
+ query:
189
+ id: "{id}"
190
+
191
+ - method: POST
192
+ path: /api/invoices/lookup
193
+ safe: true
194
+ json:
195
+ invoice_id: "{id}"
196
+ ```
197
+
198
+ Exact placeholders preserve their type. If an ID is numeric, `invoice_id: "{id}"` sends a number, not a string.
199
+
200
+ For endpoints that are intentionally shared, override the default owner-only rule:
201
+
202
+ ```yaml
203
+ - request: GET /api/admin/invoices/{id}
204
+ allow: [owner, admin]
205
+
206
+ - request: GET /api/team/invoices/{id}
207
+ allow: [authenticated]
208
+ ```
209
+
210
+ ## Safe By Default
211
+
212
+ AuthzTrace is designed for CI, so it does not execute mutating endpoints by default.
213
+
214
+ | Method | Default |
215
+ |---|---|
216
+ | `GET`, `HEAD`, `OPTIONS` | executed |
217
+ | `POST`, `PUT`, `PATCH`, `DELETE` | skipped |
218
+
219
+ Mark read-like POST endpoints as safe:
220
+
221
+ ```yaml
222
+ - method: POST
223
+ path: /api/search
224
+ safe: true
225
+ ```
226
+
227
+ Run unsafe endpoints only when you have disposable test data:
228
+
229
+ ```bash
230
+ authztrace run -c authztrace.yaml --include-unsafe
231
+ ```
232
+
233
+ Skipped endpoints are reported as `SKIP`, not counted as passes.
234
+
235
+ ## CI Usage
236
+
237
+ AuthzTrace returns clear exit codes:
238
+
239
+ | Code | Meaning |
240
+ |---:|---|
241
+ | `0` | clean |
242
+ | `1` | real finding, or warning in `--strict` mode |
243
+ | `2` | setup or operational failure |
244
+
245
+ Generate SARIF for GitHub code scanning:
246
+
247
+ ```bash
248
+ authztrace run -c authztrace.yaml --sarif authztrace.sarif
249
+ ```
250
+
251
+ Or use the composite GitHub Action:
252
+
253
+ ```yaml
254
+ - uses: Asttr0/AuthzTrace@v0.3.1
255
+ with:
256
+ config: authztrace.yaml
257
+ sarif: authztrace.sarif
258
+ strict: "true"
259
+ include-unsafe: "false"
260
+ ```
261
+
262
+ JSON and JUnit are also available:
263
+
264
+ ```bash
265
+ authztrace run -c authztrace.yaml \
266
+ --json authztrace.json \
267
+ --junit authztrace.xml
268
+ ```
269
+
270
+ ## Generate A Starter Contract
271
+
272
+ You can scaffold a contract from OpenAPI:
273
+
274
+ ```bash
275
+ authztrace init --from openapi.yaml --output authztrace.yaml
276
+ ```
277
+
278
+ The generator is conservative. It detects simple single-object endpoints such as:
279
+
280
+ ```text
281
+ GET /api/invoices/{invoice_id}
282
+ GET /api/invoices?id=...
283
+ ```
284
+
285
+ You still review the generated IDs, actors, and ownership before using it in CI.
286
+
287
+ ## What It Catches
288
+
289
+ AuthzTrace currently covers:
290
+
291
+ - horizontal IDOR/BOLA
292
+ - anonymous object access
293
+ - object IDs in path, query, headers, JSON body, and form body
294
+ - denied responses that leak protected markers
295
+ - allowed responses that return the wrong body
296
+ - privileged actors through endpoint `allow` rules
297
+ - unsafe endpoint skipping for CI safety
298
+ - setup failures from broken credentials or invalid fixtures
299
+
300
+ The full pattern roadmap lives in [docs/CORPUS.md](docs/CORPUS.md).
301
+
302
+ ## Why Not A Normal Scanner?
303
+
304
+ | Capability | Generic scanner | AuthzTrace |
305
+ |---|:---:|:---:|
306
+ | Knows object ownership | no | yes |
307
+ | Tests cross-user access | weak | yes |
308
+ | Runs in CI | sometimes | yes |
309
+ | Produces SARIF | sometimes | yes |
310
+ | Keeps the security rule next to code | no | yes |
311
+
312
+ The point is simple: if your API says Bob can read Alice's invoice, AuthzTrace should make the pull request fail.
313
+
314
+ ## Status
315
+
316
+ Alpha. The core engine, contract format, OpenAPI starter generator, terminal/SARIF/JSON/JUnit output, GitHub Action, read-only safety model, and demo API are working end to end.
317
+
318
+ Next priorities:
319
+
320
+ - login-flow auth
321
+ - nested parent-child ownership
322
+ - GraphQL BOLA checks
323
+ - baselines for accepted deviations
324
+
325
+ ## License
326
+
327
+ MIT (c) 2026 Mohamed Taha Slimani ([@Asttr0](https://github.com/Asttr0))
@@ -0,0 +1,303 @@
1
+ # AuthzTrace
2
+
3
+ **Authorization tests for IDOR/BOLA bugs.**
4
+
5
+ AuthzTrace proves that one user cannot access another user's objects by replaying an authorization contract against your real API.
6
+
7
+ ![status](https://img.shields.io/badge/status-alpha-orange)
8
+ ![license](https://img.shields.io/badge/license-MIT-blue)
9
+ ![OWASP API](https://img.shields.io/badge/OWASP_API-%231_BOLA-red)
10
+ ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
11
+
12
+ Most scanners cannot reliably find IDOR/BOLA because they do not know ownership. They can see `/api/invoices/inv_123`, but they do not know that `inv_123` belongs to Alice and must not be readable by Bob.
13
+
14
+ AuthzTrace makes that missing context explicit:
15
+
16
+ ```text
17
+ Alice owns inv_alice_001
18
+ Bob owns inv_bob_002
19
+
20
+ AuthzTrace checks:
21
+ - Alice can read Alice's invoice
22
+ - Bob cannot read Alice's invoice
23
+ - Anonymous users cannot read either invoice
24
+ - The same policy holds for path IDs, query IDs, JSON body IDs, and more
25
+ ```
26
+
27
+ ```mermaid
28
+ flowchart LR
29
+ A[Ownership contract] --> B[Generated actor x object matrix]
30
+ B --> C[Preflight setup checks]
31
+ C --> D[Authorization attack rows]
32
+ D --> E[SARIF, JSON, JUnit, terminal]
33
+ E --> F[Fail the PR on real BOLA]
34
+ ```
35
+
36
+ ## Why It Exists
37
+
38
+ Broken Object Level Authorization is the classic IDOR problem: changing an object ID lets one user reach another user's data. It is still hard to catch automatically because authorization is business logic.
39
+
40
+ AuthzTrace is not a crawler. It is closer to a contract test:
41
+
42
+ | You declare | AuthzTrace proves |
43
+ |---|---|
44
+ | Actors and auth tokens | Each identity can authenticate |
45
+ | Object IDs and owners | Owners can access their own objects |
46
+ | API endpoints | Other users are denied |
47
+ | Response markers | Denied responses do not leak protected data |
48
+
49
+ If credentials or test fixtures are broken, AuthzTrace exits with a setup error instead of giving a false green result.
50
+
51
+ ## Quick Demo
52
+
53
+ Install AuthzTrace:
54
+
55
+ ```bash
56
+ pip install authztrace
57
+ ```
58
+
59
+ For local development from this repo:
60
+
61
+ ```bash
62
+ git clone https://github.com/Asttr0/AuthzTrace.git
63
+ cd AuthzTrace
64
+ pip install -e .
65
+ pip install -r examples/vulnerable-api/requirements.txt
66
+ ```
67
+
68
+ Start the deliberately vulnerable demo API:
69
+
70
+ ```bash
71
+ python examples/vulnerable-api/app.py
72
+ ```
73
+
74
+ In another terminal, run AuthzTrace:
75
+
76
+ ```bash
77
+ export ALICE_TOKEN=alice-token
78
+ export BOB_TOKEN=bob-token
79
+ authztrace run -c examples/authztrace.yaml
80
+ ```
81
+
82
+ You get a real authorization finding:
83
+
84
+ ```text
85
+ RESULT ACTOR TARGET EXPECT STATUS METHOD PATH
86
+ ------------------------------------------------------------------------------------------------
87
+ PASS alice alice allow 200 GET /api/invoices/inv_alice_001
88
+ FAIL bob alice deny 200 GET /api/invoices/inv_alice_001
89
+ -> BOLA: 'bob' accessed alice's invoice (inv_alice_001) - HTTP 200
90
+ PASS anon alice deny 401 GET /api/invoices/inv_alice_001
91
+ ...
92
+ SKIP alice alice allow - DELETE /api/invoices/inv_alice_001
93
+ -> unsafe DELETE skipped in read-only mode
94
+ ...
95
+ 12 passed, 6 failed, 0 warnings, 0 errors, 6 skipped, 24 checks
96
+ categories: bola=6, unsafe_skipped=6
97
+ ```
98
+
99
+ Stop the first server with `Ctrl+C`, then run the fixed API:
100
+
101
+ ```bash
102
+ SECURE=1 python examples/vulnerable-api/app.py
103
+ authztrace run -c examples/authztrace.yaml
104
+ ```
105
+
106
+ The same contract becomes green:
107
+
108
+ ```text
109
+ 18 passed, 0 failed, 0 warnings, 0 errors, 6 skipped, 24 checks
110
+ categories: unsafe_skipped=6
111
+ ```
112
+
113
+ ## Minimal Contract
114
+
115
+ ```yaml
116
+ base_url: http://localhost:3000
117
+
118
+ actors:
119
+ alice: { auth: { type: bearer, token: "${ALICE_TOKEN}" } }
120
+ bob: { auth: { type: bearer, token: "${BOB_TOKEN}" } }
121
+ anon: { auth: { type: none } }
122
+
123
+ resources:
124
+ invoice:
125
+ ids:
126
+ alice: inv_alice_001
127
+ bob: inv_bob_002
128
+ markers:
129
+ alice: "Alice private invoice"
130
+ bob: "Bob private invoice"
131
+ endpoints:
132
+ - name: read invoice
133
+ request: GET /api/invoices/{id}
134
+ assertions:
135
+ allow_contains: ["{marker}"]
136
+ deny_not_contains: ["{marker}"]
137
+
138
+ policy:
139
+ default: owner-only
140
+ deny_status: [401, 403, 404]
141
+ ```
142
+
143
+ From this, AuthzTrace generates the full matrix:
144
+
145
+ ```text
146
+ alice -> alice invoice should allow
147
+ bob -> alice invoice should deny
148
+ anon -> alice invoice should deny
149
+ alice -> bob invoice should deny
150
+ bob -> bob invoice should allow
151
+ anon -> bob invoice should deny
152
+ ```
153
+
154
+ ## Endpoint Shapes
155
+
156
+ Object IDs can live almost anywhere:
157
+
158
+ ```yaml
159
+ endpoints:
160
+ - request: GET /api/invoices/{id}
161
+
162
+ - method: GET
163
+ path: /api/invoices
164
+ query:
165
+ id: "{id}"
166
+
167
+ - method: POST
168
+ path: /api/invoices/lookup
169
+ safe: true
170
+ json:
171
+ invoice_id: "{id}"
172
+ ```
173
+
174
+ Exact placeholders preserve their type. If an ID is numeric, `invoice_id: "{id}"` sends a number, not a string.
175
+
176
+ For endpoints that are intentionally shared, override the default owner-only rule:
177
+
178
+ ```yaml
179
+ - request: GET /api/admin/invoices/{id}
180
+ allow: [owner, admin]
181
+
182
+ - request: GET /api/team/invoices/{id}
183
+ allow: [authenticated]
184
+ ```
185
+
186
+ ## Safe By Default
187
+
188
+ AuthzTrace is designed for CI, so it does not execute mutating endpoints by default.
189
+
190
+ | Method | Default |
191
+ |---|---|
192
+ | `GET`, `HEAD`, `OPTIONS` | executed |
193
+ | `POST`, `PUT`, `PATCH`, `DELETE` | skipped |
194
+
195
+ Mark read-like POST endpoints as safe:
196
+
197
+ ```yaml
198
+ - method: POST
199
+ path: /api/search
200
+ safe: true
201
+ ```
202
+
203
+ Run unsafe endpoints only when you have disposable test data:
204
+
205
+ ```bash
206
+ authztrace run -c authztrace.yaml --include-unsafe
207
+ ```
208
+
209
+ Skipped endpoints are reported as `SKIP`, not counted as passes.
210
+
211
+ ## CI Usage
212
+
213
+ AuthzTrace returns clear exit codes:
214
+
215
+ | Code | Meaning |
216
+ |---:|---|
217
+ | `0` | clean |
218
+ | `1` | real finding, or warning in `--strict` mode |
219
+ | `2` | setup or operational failure |
220
+
221
+ Generate SARIF for GitHub code scanning:
222
+
223
+ ```bash
224
+ authztrace run -c authztrace.yaml --sarif authztrace.sarif
225
+ ```
226
+
227
+ Or use the composite GitHub Action:
228
+
229
+ ```yaml
230
+ - uses: Asttr0/AuthzTrace@v0.3.1
231
+ with:
232
+ config: authztrace.yaml
233
+ sarif: authztrace.sarif
234
+ strict: "true"
235
+ include-unsafe: "false"
236
+ ```
237
+
238
+ JSON and JUnit are also available:
239
+
240
+ ```bash
241
+ authztrace run -c authztrace.yaml \
242
+ --json authztrace.json \
243
+ --junit authztrace.xml
244
+ ```
245
+
246
+ ## Generate A Starter Contract
247
+
248
+ You can scaffold a contract from OpenAPI:
249
+
250
+ ```bash
251
+ authztrace init --from openapi.yaml --output authztrace.yaml
252
+ ```
253
+
254
+ The generator is conservative. It detects simple single-object endpoints such as:
255
+
256
+ ```text
257
+ GET /api/invoices/{invoice_id}
258
+ GET /api/invoices?id=...
259
+ ```
260
+
261
+ You still review the generated IDs, actors, and ownership before using it in CI.
262
+
263
+ ## What It Catches
264
+
265
+ AuthzTrace currently covers:
266
+
267
+ - horizontal IDOR/BOLA
268
+ - anonymous object access
269
+ - object IDs in path, query, headers, JSON body, and form body
270
+ - denied responses that leak protected markers
271
+ - allowed responses that return the wrong body
272
+ - privileged actors through endpoint `allow` rules
273
+ - unsafe endpoint skipping for CI safety
274
+ - setup failures from broken credentials or invalid fixtures
275
+
276
+ The full pattern roadmap lives in [docs/CORPUS.md](docs/CORPUS.md).
277
+
278
+ ## Why Not A Normal Scanner?
279
+
280
+ | Capability | Generic scanner | AuthzTrace |
281
+ |---|:---:|:---:|
282
+ | Knows object ownership | no | yes |
283
+ | Tests cross-user access | weak | yes |
284
+ | Runs in CI | sometimes | yes |
285
+ | Produces SARIF | sometimes | yes |
286
+ | Keeps the security rule next to code | no | yes |
287
+
288
+ The point is simple: if your API says Bob can read Alice's invoice, AuthzTrace should make the pull request fail.
289
+
290
+ ## Status
291
+
292
+ Alpha. The core engine, contract format, OpenAPI starter generator, terminal/SARIF/JSON/JUnit output, GitHub Action, read-only safety model, and demo API are working end to end.
293
+
294
+ Next priorities:
295
+
296
+ - login-flow auth
297
+ - nested parent-child ownership
298
+ - GraphQL BOLA checks
299
+ - baselines for accepted deviations
300
+
301
+ ## License
302
+
303
+ MIT (c) 2026 Mohamed Taha Slimani ([@Asttr0](https://github.com/Asttr0))
@@ -0,0 +1,3 @@
1
+ """AuthzTrace — authorization contract testing for IDOR/BOLA."""
2
+
3
+ __version__ = "0.3.1"