permissiondiff 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ Copyright 2026 PermissionDiff contributors
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: permissiondiff
3
+ Version: 0.1.0
4
+ Summary: Deterministic differential authorization regression testing
5
+ Keywords: authorization,ci,differential-testing,property-based-testing,security-testing
6
+ Author: Abishek Giri
7
+ License-Expression: Apache-2.0
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: Software Development :: Testing
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: hypothesis>=6.100
21
+ Requires-Dist: pydantic>=2.7
22
+ Requires-Dist: pyyaml>=6.0
23
+ Requires-Dist: rich>=13.7
24
+ Requires-Dist: typer>=0.12
25
+ Requires-Python: >=3.12
26
+ Project-URL: Homepage, https://github.com/abishekgiri/permissiondiff
27
+ Project-URL: Repository, https://github.com/abishekgiri/permissiondiff
28
+ Project-URL: Issues, https://github.com/abishekgiri/permissiondiff/issues
29
+ Project-URL: Changelog, https://github.com/abishekgiri/permissiondiff/blob/main/CHANGELOG.md
30
+ Project-URL: Security, https://github.com/abishekgiri/permissiondiff/security/policy
31
+ Description-Content-Type: text/markdown
32
+
33
+ # PermissionDiff
34
+
35
+ > **Prove your code didn't just hand the wrong person the keys.**
36
+
37
+ [![CI](https://github.com/abishekgiri/permissiondiff/actions/workflows/ci.yml/badge.svg)](https://github.com/abishekgiri/permissiondiff/actions/workflows/ci.yml)
38
+ ![Python](https://img.shields.io/badge/python-3.12%E2%80%933.14-blue)
39
+ ![License](https://img.shields.io/badge/license-Apache%202.0-green)
40
+ [![Ruff](https://img.shields.io/badge/lint-ruff-000000)](https://github.com/astral-sh/ruff)
41
+ ![Typed](https://img.shields.io/badge/typed-mypy%20strict-blue)
42
+
43
+ **PermissionDiff is a CI-native tool that shows exactly how a code change alters effective authorization.** It answers: “Did this pull request accidentally give a user, tenant, role, or service access it did not have before?”
44
+
45
+ PermissionDiff generates meaningful authorization cases from subjects, resources, actions, and context values you declare. It checks explicit invariants and records baseline cases so a candidate authorizer evaluates the **same exact corpus**. Security verdicts are deterministic Python decisions—never LLM judgments.
46
+
47
+ ```text
48
+ CRITICAL — Cross-tenant access must be denied
49
+ support(acme) → read_invoice → invoice(globex)
50
+ actual: ALLOW
51
+ repro: .permissiondiff/failures/PD-0001.json
52
+ ```
53
+
54
+ PermissionDiff is at **v0/alpha** maturity. The core workflow is tested and usable, but public
55
+ interfaces may evolve before 1.0. It supports Python 3.12, 3.13, and 3.14 and is licensed under
56
+ [Apache-2.0](https://github.com/abishekgiri/permissiondiff/blob/main/LICENSE).
57
+
58
+ ## Install
59
+
60
+ Install the CLI with uv:
61
+
62
+ ```bash
63
+ uv tool install permissiondiff
64
+ ```
65
+
66
+ Or install it in an active virtual environment with pip:
67
+
68
+ ```bash
69
+ python -m pip install permissiondiff
70
+ ```
71
+
72
+ To add PermissionDiff to an existing uv project instead, run `uv add permissiondiff`.
73
+
74
+ ## Five-minute quickstart
75
+
76
+ After installation:
77
+
78
+ ```bash
79
+ permissiondiff init demo
80
+ cd demo
81
+ permissiondiff test
82
+ ```
83
+
84
+ `init` creates a runnable config and a deliberately vulnerable authorizer. The test reports a minimized cross-tenant reproduction under `.permissiondiff/failures/` and exits `1` because the tenant-isolation invariant fails.
85
+
86
+ ## Authorizer interface
87
+
88
+ Point PermissionDiff at one ordinary decision function:
89
+
90
+ ```python
91
+ from permissiondiff import Action, Context, Decision, Resource, Subject
92
+
93
+
94
+ def authorize(
95
+ subject: Subject,
96
+ action: Action,
97
+ resource: Resource,
98
+ context: Context,
99
+ ) -> Decision:
100
+ if subject.tenant != resource.tenant:
101
+ return Decision.DENY
102
+ if action.name == "read_invoice" and subject.role in {"support", "admin"}:
103
+ return Decision.ALLOW
104
+ return Decision.DENY
105
+ ```
106
+
107
+ The result must be exactly `Decision.ALLOW` or `Decision.DENY`. Crashes, hangs, and invalid return values are explicit evaluation errors and exit `3`; PermissionDiff never fails open.
108
+
109
+ ## Configuration
110
+
111
+ The YAML describes real entities rather than asking a fuzzer to invent arbitrary application objects:
112
+
113
+ ```yaml
114
+ authorizer: auth:authorize
115
+
116
+ subjects:
117
+ - {id: alice, tenant: acme, role: support}
118
+ - {id: bob, tenant: globex, role: support}
119
+
120
+ resources:
121
+ - {id: invoice-acme, type: invoice, tenant: acme, owner_id: alice}
122
+ - {id: invoice-globex, type: invoice, tenant: globex, owner_id: bob}
123
+
124
+ actions: [read_invoice, refund]
125
+ contexts:
126
+ amounts: {min: 0, max: 1000, boundaries: [499, 500, 501]}
127
+
128
+ invariants:
129
+ - tenant_isolation
130
+ - role_boundary: {action: refund, allowed_roles: [admin]}
131
+ - ownership: {actions: [read_invoice]}
132
+
133
+ fail_on:
134
+ invariant_violation: true
135
+ newly_allowed: true
136
+ newly_denied: false
137
+
138
+ generation: {max_examples: 64}
139
+ execution: {timeout_seconds: 2}
140
+ ```
141
+
142
+ Hypothesis combines declared entities and explores numeric boundaries. With the same PermissionDiff and Python versions, configuration, seed, exact corpus, and deterministic authorizer, findings are semantically reproducible. Canonical JSON, stable case fingerprints, stable finding ordering, and fixed IDs make review practical. A nondeterministic authorizer remains nondeterministic; PermissionDiff does not conceal that.
143
+
144
+ ## Test and invariant workflow
145
+
146
+ ```bash
147
+ uv run permissiondiff test --config permissiondiff.yaml --seed 42
148
+ uv run permissiondiff explain PD-0001
149
+ ```
150
+
151
+ Built-in invariants cover tenant isolation, action-specific role boundaries, and ownership. A custom deterministic invariant can be declared as:
152
+
153
+ ```yaml
154
+ invariants:
155
+ - custom: {name: refund_limit, callable: invariants:refund_limit}
156
+ ```
157
+
158
+ The callable receives `(AuthorizationCase, Decision)` and returns `bool` or `InvariantResult`.
159
+
160
+ ## Baseline and diff workflow
161
+
162
+ Create a baseline on trusted code:
163
+
164
+ ```bash
165
+ uv run permissiondiff snapshot --config permissiondiff.yaml \
166
+ --output .permissiondiff/main.json --seed 42
167
+ ```
168
+
169
+ The snapshot stores every exact input case, its baseline decision, a stable fingerprint, the corpus seed, and schema/package versions. On candidate code, replay it:
170
+
171
+ ```bash
172
+ uv run permissiondiff diff --config permissiondiff.yaml \
173
+ --baseline .permissiondiff/main.json
174
+ ```
175
+
176
+ Classification is exhaustive:
177
+
178
+ | Baseline | Candidate | Result |
179
+ |---|---|---|
180
+ | DENY | DENY | `unchanged_denied` |
181
+ | ALLOW | ALLOW | `unchanged_allowed` |
182
+ | DENY | ALLOW | `newly_allowed` |
183
+ | ALLOW | DENY | `newly_denied` |
184
+
185
+ A newly allowed path is security-sensitive, not automatically a vulnerability. `fail_on` decides what blocks CI. Invariants can still catch a vulnerability already present in the baseline.
186
+
187
+ The complete machine report is `.permissiondiff/report.json` by default.
188
+
189
+ ## CI
190
+
191
+ ```yaml
192
+ name: permissiondiff
193
+ on: [pull_request]
194
+ jobs:
195
+ authorization:
196
+ runs-on: ubuntu-latest
197
+ steps:
198
+ - uses: actions/checkout@v4
199
+ - uses: astral-sh/setup-uv@v6
200
+ - run: uv sync --all-groups
201
+ - run: uv run permissiondiff diff --baseline .permissiondiff/main.json
202
+ ```
203
+
204
+ Exit codes are stable: `0` pass, `1` configured policy/invariant failure, `2` configuration or snapshot error, `3` evaluation/runtime error.
205
+
206
+ The repository's thin composite action invokes the same CLI without duplicating its logic:
207
+
208
+ ```yaml
209
+ - uses: abishekgiri/permissiondiff@v0
210
+ with:
211
+ config: permissiondiff.yaml
212
+ baseline: .permissiondiff/main.json
213
+ ```
214
+
215
+ ## Security and limitations
216
+
217
+ **Only point PermissionDiff at decision logic with no live side effects. Never use an authorizer that issues refunds, deletes data, sends messages, or contacts production.**
218
+
219
+ Authorizers run in a timeout-controlled subprocess to isolate crashes and hangs from the main CLI. **This subprocess is not a security sandbox.** Imported Python code has the operating-system permissions of the user running PermissionDiff and may be malicious. Use trusted code and isolated CI environments.
220
+
221
+ Snapshots and reproductions may contain sensitive identifiers. Treat them accordingly. v0 supports local Python authorizers and explicit domains; it does not include Git worktree orchestration, remote policy engines, live agent/tool interception, a dashboard, or least-privilege mining.
222
+
223
+ Report vulnerabilities privately as described in the
224
+ [security policy](https://github.com/abishekgiri/permissiondiff/security/policy). For bugs and
225
+ feature requests, use [GitHub Issues](https://github.com/abishekgiri/permissiondiff/issues).
226
+
227
+ ## Development
228
+
229
+ ```bash
230
+ uv sync --all-groups
231
+ uv run ruff check .
232
+ uv run ruff format --check .
233
+ uv run mypy src tests
234
+ uv run pytest
235
+ uv run pytest --cov=permissiondiff --cov-report=term-missing
236
+ uv build
237
+ ```
238
+
239
+ The roadmap is deliberately short: prove authorization diffs are useful, then consider policy-engine adapters, Git-aware orchestration, agent principals/delegation, safe shadow interception, and least-privilege reduction with proof.
240
+
241
+ See the [contribution guide](https://github.com/abishekgiri/permissiondiff/blob/main/CONTRIBUTING.md)
242
+ for the contribution workflow and the
243
+ [changelog](https://github.com/abishekgiri/permissiondiff/blob/main/CHANGELOG.md) for release
244
+ history.
@@ -0,0 +1,212 @@
1
+ # PermissionDiff
2
+
3
+ > **Prove your code didn't just hand the wrong person the keys.**
4
+
5
+ [![CI](https://github.com/abishekgiri/permissiondiff/actions/workflows/ci.yml/badge.svg)](https://github.com/abishekgiri/permissiondiff/actions/workflows/ci.yml)
6
+ ![Python](https://img.shields.io/badge/python-3.12%E2%80%933.14-blue)
7
+ ![License](https://img.shields.io/badge/license-Apache%202.0-green)
8
+ [![Ruff](https://img.shields.io/badge/lint-ruff-000000)](https://github.com/astral-sh/ruff)
9
+ ![Typed](https://img.shields.io/badge/typed-mypy%20strict-blue)
10
+
11
+ **PermissionDiff is a CI-native tool that shows exactly how a code change alters effective authorization.** It answers: “Did this pull request accidentally give a user, tenant, role, or service access it did not have before?”
12
+
13
+ PermissionDiff generates meaningful authorization cases from subjects, resources, actions, and context values you declare. It checks explicit invariants and records baseline cases so a candidate authorizer evaluates the **same exact corpus**. Security verdicts are deterministic Python decisions—never LLM judgments.
14
+
15
+ ```text
16
+ CRITICAL — Cross-tenant access must be denied
17
+ support(acme) → read_invoice → invoice(globex)
18
+ actual: ALLOW
19
+ repro: .permissiondiff/failures/PD-0001.json
20
+ ```
21
+
22
+ PermissionDiff is at **v0/alpha** maturity. The core workflow is tested and usable, but public
23
+ interfaces may evolve before 1.0. It supports Python 3.12, 3.13, and 3.14 and is licensed under
24
+ [Apache-2.0](https://github.com/abishekgiri/permissiondiff/blob/main/LICENSE).
25
+
26
+ ## Install
27
+
28
+ Install the CLI with uv:
29
+
30
+ ```bash
31
+ uv tool install permissiondiff
32
+ ```
33
+
34
+ Or install it in an active virtual environment with pip:
35
+
36
+ ```bash
37
+ python -m pip install permissiondiff
38
+ ```
39
+
40
+ To add PermissionDiff to an existing uv project instead, run `uv add permissiondiff`.
41
+
42
+ ## Five-minute quickstart
43
+
44
+ After installation:
45
+
46
+ ```bash
47
+ permissiondiff init demo
48
+ cd demo
49
+ permissiondiff test
50
+ ```
51
+
52
+ `init` creates a runnable config and a deliberately vulnerable authorizer. The test reports a minimized cross-tenant reproduction under `.permissiondiff/failures/` and exits `1` because the tenant-isolation invariant fails.
53
+
54
+ ## Authorizer interface
55
+
56
+ Point PermissionDiff at one ordinary decision function:
57
+
58
+ ```python
59
+ from permissiondiff import Action, Context, Decision, Resource, Subject
60
+
61
+
62
+ def authorize(
63
+ subject: Subject,
64
+ action: Action,
65
+ resource: Resource,
66
+ context: Context,
67
+ ) -> Decision:
68
+ if subject.tenant != resource.tenant:
69
+ return Decision.DENY
70
+ if action.name == "read_invoice" and subject.role in {"support", "admin"}:
71
+ return Decision.ALLOW
72
+ return Decision.DENY
73
+ ```
74
+
75
+ The result must be exactly `Decision.ALLOW` or `Decision.DENY`. Crashes, hangs, and invalid return values are explicit evaluation errors and exit `3`; PermissionDiff never fails open.
76
+
77
+ ## Configuration
78
+
79
+ The YAML describes real entities rather than asking a fuzzer to invent arbitrary application objects:
80
+
81
+ ```yaml
82
+ authorizer: auth:authorize
83
+
84
+ subjects:
85
+ - {id: alice, tenant: acme, role: support}
86
+ - {id: bob, tenant: globex, role: support}
87
+
88
+ resources:
89
+ - {id: invoice-acme, type: invoice, tenant: acme, owner_id: alice}
90
+ - {id: invoice-globex, type: invoice, tenant: globex, owner_id: bob}
91
+
92
+ actions: [read_invoice, refund]
93
+ contexts:
94
+ amounts: {min: 0, max: 1000, boundaries: [499, 500, 501]}
95
+
96
+ invariants:
97
+ - tenant_isolation
98
+ - role_boundary: {action: refund, allowed_roles: [admin]}
99
+ - ownership: {actions: [read_invoice]}
100
+
101
+ fail_on:
102
+ invariant_violation: true
103
+ newly_allowed: true
104
+ newly_denied: false
105
+
106
+ generation: {max_examples: 64}
107
+ execution: {timeout_seconds: 2}
108
+ ```
109
+
110
+ Hypothesis combines declared entities and explores numeric boundaries. With the same PermissionDiff and Python versions, configuration, seed, exact corpus, and deterministic authorizer, findings are semantically reproducible. Canonical JSON, stable case fingerprints, stable finding ordering, and fixed IDs make review practical. A nondeterministic authorizer remains nondeterministic; PermissionDiff does not conceal that.
111
+
112
+ ## Test and invariant workflow
113
+
114
+ ```bash
115
+ uv run permissiondiff test --config permissiondiff.yaml --seed 42
116
+ uv run permissiondiff explain PD-0001
117
+ ```
118
+
119
+ Built-in invariants cover tenant isolation, action-specific role boundaries, and ownership. A custom deterministic invariant can be declared as:
120
+
121
+ ```yaml
122
+ invariants:
123
+ - custom: {name: refund_limit, callable: invariants:refund_limit}
124
+ ```
125
+
126
+ The callable receives `(AuthorizationCase, Decision)` and returns `bool` or `InvariantResult`.
127
+
128
+ ## Baseline and diff workflow
129
+
130
+ Create a baseline on trusted code:
131
+
132
+ ```bash
133
+ uv run permissiondiff snapshot --config permissiondiff.yaml \
134
+ --output .permissiondiff/main.json --seed 42
135
+ ```
136
+
137
+ The snapshot stores every exact input case, its baseline decision, a stable fingerprint, the corpus seed, and schema/package versions. On candidate code, replay it:
138
+
139
+ ```bash
140
+ uv run permissiondiff diff --config permissiondiff.yaml \
141
+ --baseline .permissiondiff/main.json
142
+ ```
143
+
144
+ Classification is exhaustive:
145
+
146
+ | Baseline | Candidate | Result |
147
+ |---|---|---|
148
+ | DENY | DENY | `unchanged_denied` |
149
+ | ALLOW | ALLOW | `unchanged_allowed` |
150
+ | DENY | ALLOW | `newly_allowed` |
151
+ | ALLOW | DENY | `newly_denied` |
152
+
153
+ A newly allowed path is security-sensitive, not automatically a vulnerability. `fail_on` decides what blocks CI. Invariants can still catch a vulnerability already present in the baseline.
154
+
155
+ The complete machine report is `.permissiondiff/report.json` by default.
156
+
157
+ ## CI
158
+
159
+ ```yaml
160
+ name: permissiondiff
161
+ on: [pull_request]
162
+ jobs:
163
+ authorization:
164
+ runs-on: ubuntu-latest
165
+ steps:
166
+ - uses: actions/checkout@v4
167
+ - uses: astral-sh/setup-uv@v6
168
+ - run: uv sync --all-groups
169
+ - run: uv run permissiondiff diff --baseline .permissiondiff/main.json
170
+ ```
171
+
172
+ Exit codes are stable: `0` pass, `1` configured policy/invariant failure, `2` configuration or snapshot error, `3` evaluation/runtime error.
173
+
174
+ The repository's thin composite action invokes the same CLI without duplicating its logic:
175
+
176
+ ```yaml
177
+ - uses: abishekgiri/permissiondiff@v0
178
+ with:
179
+ config: permissiondiff.yaml
180
+ baseline: .permissiondiff/main.json
181
+ ```
182
+
183
+ ## Security and limitations
184
+
185
+ **Only point PermissionDiff at decision logic with no live side effects. Never use an authorizer that issues refunds, deletes data, sends messages, or contacts production.**
186
+
187
+ Authorizers run in a timeout-controlled subprocess to isolate crashes and hangs from the main CLI. **This subprocess is not a security sandbox.** Imported Python code has the operating-system permissions of the user running PermissionDiff and may be malicious. Use trusted code and isolated CI environments.
188
+
189
+ Snapshots and reproductions may contain sensitive identifiers. Treat them accordingly. v0 supports local Python authorizers and explicit domains; it does not include Git worktree orchestration, remote policy engines, live agent/tool interception, a dashboard, or least-privilege mining.
190
+
191
+ Report vulnerabilities privately as described in the
192
+ [security policy](https://github.com/abishekgiri/permissiondiff/security/policy). For bugs and
193
+ feature requests, use [GitHub Issues](https://github.com/abishekgiri/permissiondiff/issues).
194
+
195
+ ## Development
196
+
197
+ ```bash
198
+ uv sync --all-groups
199
+ uv run ruff check .
200
+ uv run ruff format --check .
201
+ uv run mypy src tests
202
+ uv run pytest
203
+ uv run pytest --cov=permissiondiff --cov-report=term-missing
204
+ uv build
205
+ ```
206
+
207
+ The roadmap is deliberately short: prove authorization diffs are useful, then consider policy-engine adapters, Git-aware orchestration, agent principals/delegation, safe shadow interception, and least-privilege reduction with proof.
208
+
209
+ See the [contribution guide](https://github.com/abishekgiri/permissiondiff/blob/main/CONTRIBUTING.md)
210
+ for the contribution workflow and the
211
+ [changelog](https://github.com/abishekgiri/permissiondiff/blob/main/CHANGELOG.md) for release
212
+ history.
@@ -0,0 +1,86 @@
1
+ [project]
2
+ name = "permissiondiff"
3
+ version = "0.1.0"
4
+ description = "Deterministic differential authorization regression testing"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "Apache-2.0"
8
+ license-files = ["LICENSE"]
9
+ keywords = [
10
+ "authorization",
11
+ "ci",
12
+ "differential-testing",
13
+ "property-based-testing",
14
+ "security-testing",
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "Topic :: Security",
26
+ "Topic :: Software Development :: Testing",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "hypothesis>=6.100",
31
+ "pydantic>=2.7",
32
+ "pyyaml>=6.0",
33
+ "rich>=13.7",
34
+ "typer>=0.12",
35
+ ]
36
+
37
+ [[project.authors]]
38
+ name = "Abishek Giri"
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/abishekgiri/permissiondiff"
42
+ Repository = "https://github.com/abishekgiri/permissiondiff"
43
+ Issues = "https://github.com/abishekgiri/permissiondiff/issues"
44
+ Changelog = "https://github.com/abishekgiri/permissiondiff/blob/main/CHANGELOG.md"
45
+ Security = "https://github.com/abishekgiri/permissiondiff/security/policy"
46
+
47
+ [project.scripts]
48
+ permissiondiff = "permissiondiff.cli:app"
49
+
50
+ [dependency-groups]
51
+ dev = [
52
+ "mypy>=1.10",
53
+ "pytest>=8.2",
54
+ "pytest-cov>=5.0",
55
+ "ruff>=0.5",
56
+ "types-pyyaml>=6.0",
57
+ ]
58
+
59
+ [build-system]
60
+ requires = ["uv_build>=0.11.1,<0.12.0"]
61
+ build-backend = "uv_build"
62
+
63
+ [tool.ruff]
64
+ line-length = 100
65
+ target-version = "py312"
66
+
67
+ [tool.ruff.lint]
68
+ select = [
69
+ "E",
70
+ "F",
71
+ "I",
72
+ "UP",
73
+ "B",
74
+ "SIM",
75
+ "RUF",
76
+ ]
77
+
78
+ [tool.mypy]
79
+ python_version = "3.12"
80
+ strict = true
81
+ packages = ["permissiondiff"]
82
+ mypy_path = "src"
83
+
84
+ [tool.pytest.ini_options]
85
+ addopts = "-ra"
86
+ testpaths = ["tests"]
@@ -0,0 +1,76 @@
1
+ [project]
2
+ name = "permissiondiff"
3
+ version = "0.1.0"
4
+ description = "Deterministic differential authorization regression testing"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "Apache-2.0"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Abishek Giri" }]
10
+ keywords = [
11
+ "authorization",
12
+ "ci",
13
+ "differential-testing",
14
+ "property-based-testing",
15
+ "security-testing",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Environment :: Console",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Security",
27
+ "Topic :: Software Development :: Testing",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = [
31
+ "hypothesis>=6.100",
32
+ "pydantic>=2.7",
33
+ "pyyaml>=6.0",
34
+ "rich>=13.7",
35
+ "typer>=0.12",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/abishekgiri/permissiondiff"
40
+ Repository = "https://github.com/abishekgiri/permissiondiff"
41
+ Issues = "https://github.com/abishekgiri/permissiondiff/issues"
42
+ Changelog = "https://github.com/abishekgiri/permissiondiff/blob/main/CHANGELOG.md"
43
+ Security = "https://github.com/abishekgiri/permissiondiff/security/policy"
44
+
45
+ [project.scripts]
46
+ permissiondiff = "permissiondiff.cli:app"
47
+
48
+ [dependency-groups]
49
+ dev = [
50
+ "mypy>=1.10",
51
+ "pytest>=8.2",
52
+ "pytest-cov>=5.0",
53
+ "ruff>=0.5",
54
+ "types-pyyaml>=6.0",
55
+ ]
56
+
57
+ [build-system]
58
+ requires = ["uv_build>=0.11.1,<0.12.0"]
59
+ build-backend = "uv_build"
60
+
61
+ [tool.ruff]
62
+ line-length = 100
63
+ target-version = "py312"
64
+
65
+ [tool.ruff.lint]
66
+ select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
67
+
68
+ [tool.mypy]
69
+ python_version = "3.12"
70
+ strict = true
71
+ packages = ["permissiondiff"]
72
+ mypy_path = "src"
73
+
74
+ [tool.pytest.ini_options]
75
+ addopts = "-ra"
76
+ testpaths = ["tests"]
@@ -0,0 +1,23 @@
1
+ """Public PermissionDiff API."""
2
+
3
+ from permissiondiff.models import (
4
+ Action,
5
+ AuthorizationCase,
6
+ ChangeType,
7
+ Context,
8
+ Decision,
9
+ Resource,
10
+ Subject,
11
+ )
12
+
13
+ __all__ = [
14
+ "Action",
15
+ "AuthorizationCase",
16
+ "ChangeType",
17
+ "Context",
18
+ "Decision",
19
+ "Resource",
20
+ "Subject",
21
+ ]
22
+
23
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Run PermissionDiff as ``python -m permissiondiff``."""
2
+
3
+ from permissiondiff.cli import app
4
+
5
+ if __name__ == "__main__":
6
+ app()