contextduty 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.
- contextduty-0.1.0/LICENSE +21 -0
- contextduty-0.1.0/PKG-INFO +250 -0
- contextduty-0.1.0/README.md +222 -0
- contextduty-0.1.0/pyproject.toml +61 -0
- contextduty-0.1.0/setup.cfg +4 -0
- contextduty-0.1.0/src/contextduty/__init__.py +3 -0
- contextduty-0.1.0/src/contextduty/cli.py +127 -0
- contextduty-0.1.0/src/contextduty/detectors.py +36 -0
- contextduty-0.1.0/src/contextduty/engine.py +136 -0
- contextduty-0.1.0/src/contextduty/mcp_server.py +222 -0
- contextduty-0.1.0/src/contextduty/policy.py +146 -0
- contextduty-0.1.0/src/contextduty.egg-info/PKG-INFO +250 -0
- contextduty-0.1.0/src/contextduty.egg-info/SOURCES.txt +19 -0
- contextduty-0.1.0/src/contextduty.egg-info/dependency_links.txt +1 -0
- contextduty-0.1.0/src/contextduty.egg-info/entry_points.txt +3 -0
- contextduty-0.1.0/src/contextduty.egg-info/requires.txt +3 -0
- contextduty-0.1.0/src/contextduty.egg-info/top_level.txt +1 -0
- contextduty-0.1.0/tests/test_cli.py +138 -0
- contextduty-0.1.0/tests/test_detectors.py +115 -0
- contextduty-0.1.0/tests/test_engine.py +129 -0
- contextduty-0.1.0/tests/test_policy.py +180 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContextDuty Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: contextduty
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Policy-driven context firewall for AI workflows — scan and redact sensitive data before prompts, logs, or traces leave your environment.
|
|
5
|
+
Author: ContextDuty Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/SHUBHAGYTA24/contextduty
|
|
8
|
+
Project-URL: Repository, https://github.com/SHUBHAGYTA24/contextduty
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/SHUBHAGYTA24/contextduty/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/SHUBHAGYTA24/contextduty/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: security,pii,redaction,ai,llm,mcp,prompt,privacy,devsecops,secrets
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: System :: Logging
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# ContextDuty
|
|
30
|
+
|
|
31
|
+
> A policy-driven context firewall for AI workflows. Scan and redact sensitive data before prompts, logs, or traces leave your environment — locally, with no cloud calls.
|
|
32
|
+
|
|
33
|
+
[](https://www.python.org/)
|
|
34
|
+
[](LICENSE)
|
|
35
|
+
[](https://modelcontextprotocol.io)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Why ContextDuty
|
|
40
|
+
|
|
41
|
+
AI coding assistants and agent workflows are spreading fast. So is accidental data leakage — API keys, emails, and PII flowing into prompts, logs, and traces that may be stored or sent to third-party services.
|
|
42
|
+
|
|
43
|
+
ContextDuty is a **local-first, policy-layered primitive** that fits into any workflow:
|
|
44
|
+
- **CLI** — pipe files through it in CI or pre-commit hooks
|
|
45
|
+
- **MCP server** — Cursor, VS Code, and any MCP client get automatic redaction
|
|
46
|
+
- **Policy inheritance** — teams extend org-wide baselines without copying rules
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Why not Presidio?
|
|
51
|
+
|
|
52
|
+
[Microsoft Presidio](https://github.com/microsoft/presidio) is great for NER-based PII detection in data pipelines. ContextDuty solves a different problem:
|
|
53
|
+
|
|
54
|
+
| | ContextDuty | Presidio |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| Target use case | AI prompts, logs, agent traces | Data pipelines, analytics |
|
|
57
|
+
| MCP-native | ✅ | ❌ |
|
|
58
|
+
| Policy layering (`extends`) | ✅ | ❌ |
|
|
59
|
+
| `block` mode for CI | ✅ | ❌ |
|
|
60
|
+
| Zero dependencies | ✅ | ❌ (heavy NLP stack) |
|
|
61
|
+
| Custom detectors (no code) | ✅ (regex in JSON) | Partial |
|
|
62
|
+
| Deployment | Local CLI / subprocess | Service / SDK |
|
|
63
|
+
|
|
64
|
+
Use Presidio when you need ML-based entity recognition at scale. Use ContextDuty when you need a lightweight, policy-enforceable firewall close to your AI toolchain.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Detection coverage
|
|
69
|
+
|
|
70
|
+
| Detector | Example input | Masked as |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `email` | `jane@corp.com` | `<EMAIL_a1b2c3d4e5>` |
|
|
73
|
+
| `phone` | `+1 415-555-1212` | `<PHONE_f6g7h8i9j0>` |
|
|
74
|
+
| `api_key` | `sk_live_ABC123...` | `<API_KEY_k1l2m3n4o5>` |
|
|
75
|
+
| `aws_key` | `AKIA1234567890ABCDEF` | `<AWS_KEY_p6q7r8s9t0>` |
|
|
76
|
+
| `bearer_token` | `Bearer eyJhbGci...` | `<BEARER_TOKEN_u1v2w3x4y5>` |
|
|
77
|
+
|
|
78
|
+
Masks are **deterministic** — the same value always produces the same mask, so you can correlate across log lines without exposing the raw value.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Quickstart
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install contextduty
|
|
86
|
+
contextduty init
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then scan and redact:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
contextduty scan sample.txt --report report.json
|
|
93
|
+
contextduty redact --in sample.txt --out clean.txt --report report.json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Commands
|
|
99
|
+
|
|
100
|
+
| Command | Description |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `contextduty init` | Create `.contextduty.json` in the current directory |
|
|
103
|
+
| `contextduty scan <file>` | Scan file, print JSON findings report |
|
|
104
|
+
| `contextduty redact --in <f> --out <f>` | Redact matches, write clean file |
|
|
105
|
+
| `contextduty policy validate --policy <f> [--strict]` | Validate and resolve a layered policy |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## MCP server (Cursor / VS Code / any MCP client)
|
|
110
|
+
|
|
111
|
+
ContextDuty runs as an MCP stdio server — drop it into your editor config and every file your agent touches is scanned automatically.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
contextduty-mcp
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Cursor** — add to `~/.cursor/mcp.json`:
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"contextduty": {
|
|
122
|
+
"command": "contextduty-mcp"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Exposed tools:
|
|
129
|
+
- `contextduty_scan` (`path`, optional `policyPath`)
|
|
130
|
+
- `contextduty_redact` (`inputPath`, `outputPath`, optional `policyPath`)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Policy file
|
|
135
|
+
|
|
136
|
+
Default `.contextduty.json`:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"mode": "redact",
|
|
141
|
+
"detectors": ["email", "phone", "api_key", "aws_key", "bearer_token"],
|
|
142
|
+
"custom_detectors": {}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Add a custom detector without touching code:**
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"mode": "redact",
|
|
151
|
+
"detectors": ["email"],
|
|
152
|
+
"custom_detectors": {
|
|
153
|
+
"employee_id": "\\bEMP-[0-9]{6}\\b",
|
|
154
|
+
"internal_ticket": "\\bTICKET-[A-Z]{3}-[0-9]{4}\\b"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`custom_detectors` are auto-enabled — just add the regex entry.
|
|
160
|
+
|
|
161
|
+
**Policy layering for teams and enterprises:**
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"extends": "../../policies/org-baseline.json",
|
|
166
|
+
"mode": "block",
|
|
167
|
+
"detectors": ["internal_ticket"],
|
|
168
|
+
"custom_detectors": {
|
|
169
|
+
"internal_ticket": "\\bTICKET-[A-Z]{3}-[0-9]{4}\\b"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Rules:
|
|
175
|
+
- `extends` can be a string or list (relative file paths)
|
|
176
|
+
- `detectors` are merged (parent + child)
|
|
177
|
+
- `custom_detectors` are merged (child overrides same key)
|
|
178
|
+
- `mode` is overridden by the child policy
|
|
179
|
+
- Cycles in `extends` are rejected with a clear error
|
|
180
|
+
|
|
181
|
+
**Modes:**
|
|
182
|
+
|
|
183
|
+
| Mode | Behaviour |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `redact` | Replace matched values with deterministic masks |
|
|
186
|
+
| `warn` | Report findings, do not change content |
|
|
187
|
+
| `block` | Exit non-zero if findings exist (CI enforcement) |
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Compliance policy packs
|
|
192
|
+
|
|
193
|
+
Ready-made baselines for common frameworks — extend them in your own policy file:
|
|
194
|
+
|
|
195
|
+
| Pack | Path | Detectors included |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| SOC 2 | `policies/soc2-baseline.json` | email, phone, api_key, aws_key, bearer_token |
|
|
198
|
+
| HIPAA | `policies/hipaa-baseline.json` | email, phone + PHI custom patterns |
|
|
199
|
+
|
|
200
|
+
Usage:
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"extends": "./node_modules/contextduty/policies/soc2-baseline.json",
|
|
204
|
+
"mode": "block"
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## CI integration
|
|
211
|
+
|
|
212
|
+
Add a pre-push check to block accidental secret commits:
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
# .github/workflows/contextduty.yml
|
|
216
|
+
- name: Scan for secrets
|
|
217
|
+
run: |
|
|
218
|
+
pip install contextduty
|
|
219
|
+
contextduty scan . --policy .contextduty.json
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Or use `mode: block` in your policy to make `contextduty scan` exit non-zero on any finding.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Roadmap
|
|
227
|
+
|
|
228
|
+
- [ ] PyPI publish (`pip install contextduty`)
|
|
229
|
+
- [ ] Streaming JSONL mode for multi-GB datasets
|
|
230
|
+
- [ ] VS Code extension
|
|
231
|
+
- [ ] Policy packs for PCI-DSS
|
|
232
|
+
- [ ] GitHub Action (`uses: contextduty/action@v1`)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Open source
|
|
237
|
+
|
|
238
|
+
| File | Purpose |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `LICENSE` | MIT |
|
|
241
|
+
| `SECURITY.md` | Vulnerability reporting |
|
|
242
|
+
| `CONTRIBUTING.md` | How to contribute |
|
|
243
|
+
| `CODE_OF_CONDUCT.md` | Community standards |
|
|
244
|
+
| `CHANGELOG.md` | Version history |
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Contributing
|
|
249
|
+
|
|
250
|
+
Issues, PRs, and policy pack contributions are very welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) to get started.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# ContextDuty
|
|
2
|
+
|
|
3
|
+
> A policy-driven context firewall for AI workflows. Scan and redact sensitive data before prompts, logs, or traces leave your environment — locally, with no cloud calls.
|
|
4
|
+
|
|
5
|
+
[](https://www.python.org/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://modelcontextprotocol.io)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why ContextDuty
|
|
12
|
+
|
|
13
|
+
AI coding assistants and agent workflows are spreading fast. So is accidental data leakage — API keys, emails, and PII flowing into prompts, logs, and traces that may be stored or sent to third-party services.
|
|
14
|
+
|
|
15
|
+
ContextDuty is a **local-first, policy-layered primitive** that fits into any workflow:
|
|
16
|
+
- **CLI** — pipe files through it in CI or pre-commit hooks
|
|
17
|
+
- **MCP server** — Cursor, VS Code, and any MCP client get automatic redaction
|
|
18
|
+
- **Policy inheritance** — teams extend org-wide baselines without copying rules
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why not Presidio?
|
|
23
|
+
|
|
24
|
+
[Microsoft Presidio](https://github.com/microsoft/presidio) is great for NER-based PII detection in data pipelines. ContextDuty solves a different problem:
|
|
25
|
+
|
|
26
|
+
| | ContextDuty | Presidio |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| Target use case | AI prompts, logs, agent traces | Data pipelines, analytics |
|
|
29
|
+
| MCP-native | ✅ | ❌ |
|
|
30
|
+
| Policy layering (`extends`) | ✅ | ❌ |
|
|
31
|
+
| `block` mode for CI | ✅ | ❌ |
|
|
32
|
+
| Zero dependencies | ✅ | ❌ (heavy NLP stack) |
|
|
33
|
+
| Custom detectors (no code) | ✅ (regex in JSON) | Partial |
|
|
34
|
+
| Deployment | Local CLI / subprocess | Service / SDK |
|
|
35
|
+
|
|
36
|
+
Use Presidio when you need ML-based entity recognition at scale. Use ContextDuty when you need a lightweight, policy-enforceable firewall close to your AI toolchain.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Detection coverage
|
|
41
|
+
|
|
42
|
+
| Detector | Example input | Masked as |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `email` | `jane@corp.com` | `<EMAIL_a1b2c3d4e5>` |
|
|
45
|
+
| `phone` | `+1 415-555-1212` | `<PHONE_f6g7h8i9j0>` |
|
|
46
|
+
| `api_key` | `sk_live_ABC123...` | `<API_KEY_k1l2m3n4o5>` |
|
|
47
|
+
| `aws_key` | `AKIA1234567890ABCDEF` | `<AWS_KEY_p6q7r8s9t0>` |
|
|
48
|
+
| `bearer_token` | `Bearer eyJhbGci...` | `<BEARER_TOKEN_u1v2w3x4y5>` |
|
|
49
|
+
|
|
50
|
+
Masks are **deterministic** — the same value always produces the same mask, so you can correlate across log lines without exposing the raw value.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Quickstart
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install contextduty
|
|
58
|
+
contextduty init
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then scan and redact:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
contextduty scan sample.txt --report report.json
|
|
65
|
+
contextduty redact --in sample.txt --out clean.txt --report report.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
| Command | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `contextduty init` | Create `.contextduty.json` in the current directory |
|
|
75
|
+
| `contextduty scan <file>` | Scan file, print JSON findings report |
|
|
76
|
+
| `contextduty redact --in <f> --out <f>` | Redact matches, write clean file |
|
|
77
|
+
| `contextduty policy validate --policy <f> [--strict]` | Validate and resolve a layered policy |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## MCP server (Cursor / VS Code / any MCP client)
|
|
82
|
+
|
|
83
|
+
ContextDuty runs as an MCP stdio server — drop it into your editor config and every file your agent touches is scanned automatically.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
contextduty-mcp
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Cursor** — add to `~/.cursor/mcp.json`:
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"contextduty": {
|
|
94
|
+
"command": "contextduty-mcp"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Exposed tools:
|
|
101
|
+
- `contextduty_scan` (`path`, optional `policyPath`)
|
|
102
|
+
- `contextduty_redact` (`inputPath`, `outputPath`, optional `policyPath`)
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Policy file
|
|
107
|
+
|
|
108
|
+
Default `.contextduty.json`:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mode": "redact",
|
|
113
|
+
"detectors": ["email", "phone", "api_key", "aws_key", "bearer_token"],
|
|
114
|
+
"custom_detectors": {}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Add a custom detector without touching code:**
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mode": "redact",
|
|
123
|
+
"detectors": ["email"],
|
|
124
|
+
"custom_detectors": {
|
|
125
|
+
"employee_id": "\\bEMP-[0-9]{6}\\b",
|
|
126
|
+
"internal_ticket": "\\bTICKET-[A-Z]{3}-[0-9]{4}\\b"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`custom_detectors` are auto-enabled — just add the regex entry.
|
|
132
|
+
|
|
133
|
+
**Policy layering for teams and enterprises:**
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"extends": "../../policies/org-baseline.json",
|
|
138
|
+
"mode": "block",
|
|
139
|
+
"detectors": ["internal_ticket"],
|
|
140
|
+
"custom_detectors": {
|
|
141
|
+
"internal_ticket": "\\bTICKET-[A-Z]{3}-[0-9]{4}\\b"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Rules:
|
|
147
|
+
- `extends` can be a string or list (relative file paths)
|
|
148
|
+
- `detectors` are merged (parent + child)
|
|
149
|
+
- `custom_detectors` are merged (child overrides same key)
|
|
150
|
+
- `mode` is overridden by the child policy
|
|
151
|
+
- Cycles in `extends` are rejected with a clear error
|
|
152
|
+
|
|
153
|
+
**Modes:**
|
|
154
|
+
|
|
155
|
+
| Mode | Behaviour |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `redact` | Replace matched values with deterministic masks |
|
|
158
|
+
| `warn` | Report findings, do not change content |
|
|
159
|
+
| `block` | Exit non-zero if findings exist (CI enforcement) |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Compliance policy packs
|
|
164
|
+
|
|
165
|
+
Ready-made baselines for common frameworks — extend them in your own policy file:
|
|
166
|
+
|
|
167
|
+
| Pack | Path | Detectors included |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| SOC 2 | `policies/soc2-baseline.json` | email, phone, api_key, aws_key, bearer_token |
|
|
170
|
+
| HIPAA | `policies/hipaa-baseline.json` | email, phone + PHI custom patterns |
|
|
171
|
+
|
|
172
|
+
Usage:
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"extends": "./node_modules/contextduty/policies/soc2-baseline.json",
|
|
176
|
+
"mode": "block"
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## CI integration
|
|
183
|
+
|
|
184
|
+
Add a pre-push check to block accidental secret commits:
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
# .github/workflows/contextduty.yml
|
|
188
|
+
- name: Scan for secrets
|
|
189
|
+
run: |
|
|
190
|
+
pip install contextduty
|
|
191
|
+
contextduty scan . --policy .contextduty.json
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Or use `mode: block` in your policy to make `contextduty scan` exit non-zero on any finding.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Roadmap
|
|
199
|
+
|
|
200
|
+
- [ ] PyPI publish (`pip install contextduty`)
|
|
201
|
+
- [ ] Streaming JSONL mode for multi-GB datasets
|
|
202
|
+
- [ ] VS Code extension
|
|
203
|
+
- [ ] Policy packs for PCI-DSS
|
|
204
|
+
- [ ] GitHub Action (`uses: contextduty/action@v1`)
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Open source
|
|
209
|
+
|
|
210
|
+
| File | Purpose |
|
|
211
|
+
|---|---|
|
|
212
|
+
| `LICENSE` | MIT |
|
|
213
|
+
| `SECURITY.md` | Vulnerability reporting |
|
|
214
|
+
| `CONTRIBUTING.md` | How to contribute |
|
|
215
|
+
| `CODE_OF_CONDUCT.md` | Community standards |
|
|
216
|
+
| `CHANGELOG.md` | Version history |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Contributing
|
|
221
|
+
|
|
222
|
+
Issues, PRs, and policy pack contributions are very welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) to get started.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "contextduty"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Policy-driven context firewall for AI workflows — scan and redact sensitive data before prompts, logs, or traces leave your environment."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "ContextDuty Contributors" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"security", "pii", "redaction", "ai", "llm", "mcp",
|
|
17
|
+
"prompt", "privacy", "devsecops", "secrets"
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Security",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
|
+
"Topic :: System :: Logging",
|
|
30
|
+
]
|
|
31
|
+
dependencies = []
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = ["pytest>=7"]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/SHUBHAGYTA24/contextduty"
|
|
38
|
+
Repository = "https://github.com/SHUBHAGYTA24/contextduty"
|
|
39
|
+
"Bug Tracker" = "https://github.com/SHUBHAGYTA24/contextduty/issues"
|
|
40
|
+
Changelog = "https://github.com/SHUBHAGYTA24/contextduty/blob/main/CHANGELOG.md"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
contextduty = "contextduty.cli:main"
|
|
44
|
+
contextduty-mcp = "contextduty.mcp_server:main"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
package-dir = {"" = "src"}
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["src"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
line-length = 100
|
|
58
|
+
target-version = "py310"
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = ["E", "F", "I"] # pycodestyle errors, pyflakes, isort
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""CLI entrypoint for ContextDuty."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from .engine import redact_file, report_to_json, scan_file
|
|
11
|
+
from .policy import load_policy, unknown_detector_names, write_default_policy
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _parser() -> argparse.ArgumentParser:
|
|
15
|
+
from . import __version__
|
|
16
|
+
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="contextduty", description="Protect AI context with policy checks."
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
21
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
22
|
+
|
|
23
|
+
init_parser = subparsers.add_parser("init", help="Create default policy file.")
|
|
24
|
+
init_parser.add_argument("--path", default=".contextduty.json", help="Policy output path.")
|
|
25
|
+
|
|
26
|
+
scan_parser = subparsers.add_parser("scan", help="Scan a text file for risky data.")
|
|
27
|
+
scan_parser.add_argument("target", help="Input file path.")
|
|
28
|
+
scan_parser.add_argument("--policy", default=".contextduty.json", help="Policy path.")
|
|
29
|
+
scan_parser.add_argument("--report", help="Optional report output JSON path.")
|
|
30
|
+
|
|
31
|
+
redact_parser = subparsers.add_parser("redact", help="Redact risky data from an input file.")
|
|
32
|
+
redact_parser.add_argument("--in", dest="input_path", required=True, help="Input file path.")
|
|
33
|
+
redact_parser.add_argument("--out", dest="output_path", required=True, help="Output file path.")
|
|
34
|
+
redact_parser.add_argument("--policy", default=".contextduty.json", help="Policy path.")
|
|
35
|
+
redact_parser.add_argument("--report", help="Optional report output JSON path.")
|
|
36
|
+
|
|
37
|
+
policy_parser = subparsers.add_parser("policy", help="Policy operations.")
|
|
38
|
+
policy_subparsers = policy_parser.add_subparsers(dest="policy_command", required=True)
|
|
39
|
+
validate_parser = policy_subparsers.add_parser(
|
|
40
|
+
"validate", help="Validate and resolve a policy file."
|
|
41
|
+
)
|
|
42
|
+
validate_parser.add_argument("--policy", default=".contextduty.json", help="Policy path.")
|
|
43
|
+
validate_parser.add_argument(
|
|
44
|
+
"--strict",
|
|
45
|
+
action="store_true",
|
|
46
|
+
help="Fail validation when unknown detector names are present.",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return parser
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _load_policy_with_fallback(policy_path: str) -> tuple[Path | None, object]:
|
|
53
|
+
path = Path(policy_path)
|
|
54
|
+
if path.exists():
|
|
55
|
+
return path, load_policy(path)
|
|
56
|
+
return None, load_policy(None)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main() -> None:
|
|
60
|
+
parser = _parser()
|
|
61
|
+
args = parser.parse_args()
|
|
62
|
+
|
|
63
|
+
if args.command == "init":
|
|
64
|
+
out_path = Path(args.path)
|
|
65
|
+
write_default_policy(out_path)
|
|
66
|
+
print(f"Created policy at {out_path}")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if args.command == "scan":
|
|
70
|
+
policy_ref, policy = _load_policy_with_fallback(args.policy)
|
|
71
|
+
result = scan_file(Path(args.target), policy)
|
|
72
|
+
report = report_to_json(result)
|
|
73
|
+
print(report)
|
|
74
|
+
if args.report:
|
|
75
|
+
Path(args.report).write_text(report + "\n", encoding="utf-8")
|
|
76
|
+
print(f"Saved report to {args.report}")
|
|
77
|
+
if result.blocked:
|
|
78
|
+
print(f"BLOCKED by policy ({policy_ref or 'default'})", file=sys.stderr)
|
|
79
|
+
raise SystemExit(2)
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
if args.command == "redact":
|
|
83
|
+
policy_ref, policy = _load_policy_with_fallback(args.policy)
|
|
84
|
+
result = redact_file(Path(args.input_path), Path(args.output_path), policy)
|
|
85
|
+
report = report_to_json(result)
|
|
86
|
+
print(report)
|
|
87
|
+
if args.report:
|
|
88
|
+
Path(args.report).write_text(report + "\n", encoding="utf-8")
|
|
89
|
+
print(f"Saved report to {args.report}")
|
|
90
|
+
if result.blocked:
|
|
91
|
+
print(f"BLOCKED by policy ({policy_ref or 'default'})", file=sys.stderr)
|
|
92
|
+
raise SystemExit(2)
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
if args.command == "policy":
|
|
96
|
+
if args.policy_command == "validate":
|
|
97
|
+
policy_path = Path(args.policy)
|
|
98
|
+
if policy_path.exists():
|
|
99
|
+
policy = load_policy(policy_path)
|
|
100
|
+
source = str(policy_path)
|
|
101
|
+
else:
|
|
102
|
+
policy = load_policy(None)
|
|
103
|
+
source = "default"
|
|
104
|
+
payload = {
|
|
105
|
+
"valid": True,
|
|
106
|
+
"source": source,
|
|
107
|
+
"mode": policy.mode,
|
|
108
|
+
"detectors": sorted(policy.detectors),
|
|
109
|
+
"custom_detectors": sorted(policy.custom_detectors.keys()),
|
|
110
|
+
}
|
|
111
|
+
if args.strict:
|
|
112
|
+
unknown = unknown_detector_names(policy)
|
|
113
|
+
if unknown:
|
|
114
|
+
print(
|
|
115
|
+
f"Unknown detector names in strict mode: {', '.join(unknown)}",
|
|
116
|
+
file=sys.stderr,
|
|
117
|
+
)
|
|
118
|
+
raise SystemExit(2)
|
|
119
|
+
print(json.dumps(payload, indent=2))
|
|
120
|
+
return
|
|
121
|
+
raise SystemExit(1)
|
|
122
|
+
|
|
123
|
+
raise SystemExit(1)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
main()
|