llm-mask 0.3.0
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.
- package/.github/workflows/llm-mask-check.yml +62 -0
- package/LICENSE +21 -0
- package/README.md +549 -0
- package/dist/audit.d.ts +56 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +90 -0
- package/dist/audit.js.map +1 -0
- package/dist/cli-old.d.ts +12 -0
- package/dist/cli-old.d.ts.map +1 -0
- package/dist/cli-old.js +257 -0
- package/dist/cli-old.js.map +1 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +197 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +9 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +121 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/context-detection.d.ts +54 -0
- package/dist/context-detection.d.ts.map +1 -0
- package/dist/context-detection.js +219 -0
- package/dist/context-detection.js.map +1 -0
- package/dist/diff-masking.d.ts +51 -0
- package/dist/diff-masking.d.ts.map +1 -0
- package/dist/diff-masking.js +121 -0
- package/dist/diff-masking.js.map +1 -0
- package/dist/executor.d.ts +105 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +250 -0
- package/dist/executor.js.map +1 -0
- package/dist/executor.test.d.ts +5 -0
- package/dist/executor.test.d.ts.map +1 -0
- package/dist/executor.test.js +500 -0
- package/dist/executor.test.js.map +1 -0
- package/dist/features.test.d.ts +5 -0
- package/dist/features.test.d.ts.map +1 -0
- package/dist/features.test.js +118 -0
- package/dist/features.test.js.map +1 -0
- package/dist/formatter.d.ts +54 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +139 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/language-patterns.d.ts +39 -0
- package/dist/language-patterns.d.ts.map +1 -0
- package/dist/language-patterns.js +177 -0
- package/dist/language-patterns.js.map +1 -0
- package/dist/masker.d.ts +100 -0
- package/dist/masker.d.ts.map +1 -0
- package/dist/masker.js +255 -0
- package/dist/masker.js.map +1 -0
- package/dist/masker.test.d.ts +11 -0
- package/dist/masker.test.d.ts.map +1 -0
- package/dist/masker.test.js +162 -0
- package/dist/masker.test.js.map +1 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +494 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/patterns.d.ts +25 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +184 -0
- package/dist/patterns.js.map +1 -0
- package/dist/scanner.d.ts +75 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +247 -0
- package/dist/scanner.js.map +1 -0
- package/dist/test-utils.d.ts +5 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +6 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/tokenizer.d.ts +62 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +95 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: llm-mask - Check for Leaked Secrets
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
llm-mask-scan:
|
|
12
|
+
name: Scan for secrets
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout code
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Node.js
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '20'
|
|
22
|
+
|
|
23
|
+
- name: Install llm-mask
|
|
24
|
+
run: |
|
|
25
|
+
npm install -g llm-mask
|
|
26
|
+
# Or build from local source if in same repo
|
|
27
|
+
# npm install && npm run build
|
|
28
|
+
|
|
29
|
+
- name: Scan codebase
|
|
30
|
+
id: scan
|
|
31
|
+
continue-on-error: true
|
|
32
|
+
run: |
|
|
33
|
+
npx llm-mask scan . --fail-on-detect --level basic \
|
|
34
|
+
--extensions .ts,.js,.py,.go,.rs,.java,.env,.yaml,.yml,.json \
|
|
35
|
+
--skip-dir node_modules,dist,build,.git,target \
|
|
36
|
+
2>&1 | tee scan-output.txt
|
|
37
|
+
|
|
38
|
+
- name: Upload scan results
|
|
39
|
+
if: always()
|
|
40
|
+
uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: llm-mask-scan-results
|
|
43
|
+
path: scan-output.txt
|
|
44
|
+
retention-days: 30
|
|
45
|
+
|
|
46
|
+
- name: Comment on PR
|
|
47
|
+
if: github.event_name == 'pull_request' && steps.scan.outcome == 'failure'
|
|
48
|
+
uses: actions/github-script@v7
|
|
49
|
+
with:
|
|
50
|
+
script: |
|
|
51
|
+
const fs = require('fs');
|
|
52
|
+
const output = fs.readFileSync('scan-output.txt', 'utf8');
|
|
53
|
+
github.rest.issues.createComment({
|
|
54
|
+
issue_number: context.issue.number,
|
|
55
|
+
owner: context.repo.owner,
|
|
56
|
+
repo: context.repo.repo,
|
|
57
|
+
body: `## 🔐 llm-mask Scan Results\n\n${output}\n\nPlease review and remove any sensitive data before merging.`
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
- name: Fail if secrets found
|
|
61
|
+
if: steps.scan.outcome == 'failure'
|
|
62
|
+
run: exit 1
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dolphy
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
# llm-mask
|
|
2
|
+
|
|
3
|
+
> Mask sensitive data before sending to LLMs. Keep the semantic meaning, lose the secrets.
|
|
4
|
+
|
|
5
|
+
**Version 0.3.0** - Now with exec/kube/ssh commands for credential isolation.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
✅ **23 Built-in Patterns** - API keys, emails, IPs, PII, URLs, UUIDs, hashes, file paths
|
|
10
|
+
✅ **Credential Isolation** - Execute commands with real credentials, output redacted for LLMs
|
|
11
|
+
✅ **Kubernetes Support** - Run kubectl commands safely with automatic redaction
|
|
12
|
+
✅ **SSH Support** - Execute remote commands without exposing credentials
|
|
13
|
+
✅ **Masking Levels** - basic (secrets), standard (+PII), aggressive (everything)
|
|
14
|
+
✅ **Preserve Format** - `j***@a***.com` instead of `[EMAIL_1]`
|
|
15
|
+
✅ **Custom Patterns** - Define your own via config file
|
|
16
|
+
✅ **Scan Codebases** - Find secrets in entire projects
|
|
17
|
+
✅ **Diff Masking** - Safe git diff for LLM code review
|
|
18
|
+
✅ **Context Detection** - Smart masking for SQL, JSON, YAML
|
|
19
|
+
✅ **Language Patterns** - Python, JavaScript, Go, Ruby, Terraform
|
|
20
|
+
✅ **Reversible Tokenization** - Deterministic encryption with salt
|
|
21
|
+
✅ **Audit Logging** - Safe logging without actual values
|
|
22
|
+
✅ **CI/CD Integration** - GitHub Actions workflow included
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g llm-mask
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Basic Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Mask sensitive data
|
|
34
|
+
echo "API key sk-proj-abc123 expired for john@acme.com" | llm-mask
|
|
35
|
+
|
|
36
|
+
# Preserve format for readability
|
|
37
|
+
llm-mask --preserve-format "Contact john@acme.com"
|
|
38
|
+
|
|
39
|
+
# Mask only secrets (not emails)
|
|
40
|
+
llm-mask --level basic "Contact john@acme.com with key sk-abc123"
|
|
41
|
+
|
|
42
|
+
# Scan codebase for secrets
|
|
43
|
+
llm-mask scan ./src
|
|
44
|
+
|
|
45
|
+
# Mask git diff for safe code review
|
|
46
|
+
git diff main | llm-mask diff
|
|
47
|
+
|
|
48
|
+
# Context-aware masking (preserves structure)
|
|
49
|
+
llm-mask --context '{"user": "john@acme.com", "key": "sk-abc123"}'
|
|
50
|
+
|
|
51
|
+
# Execute commands with credential isolation
|
|
52
|
+
llm-mask exec kubectl get secrets -n production
|
|
53
|
+
llm-mask kube -n production get secrets
|
|
54
|
+
llm-mask ssh user@server "cat /etc/secrets/db.conf"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Credential Isolation (NEW!)
|
|
58
|
+
|
|
59
|
+
The core principle: **Credentials work for the command, but output is redacted before the LLM sees it.**
|
|
60
|
+
|
|
61
|
+
### Exec Command
|
|
62
|
+
|
|
63
|
+
Run any command and automatically redact sensitive output:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Show environment variables without exposing actual values
|
|
67
|
+
llm-mask exec env | grep -i key
|
|
68
|
+
|
|
69
|
+
# Run database queries safely
|
|
70
|
+
llm-mask exec psql -c "SELECT * FROM users"
|
|
71
|
+
|
|
72
|
+
# Check AWS credentials without revealing them
|
|
73
|
+
llm-mask exec aws sts get-caller-identity
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Kubernetes Command
|
|
77
|
+
|
|
78
|
+
Execute kubectl commands with automatic redaction:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# List secrets (values are masked)
|
|
82
|
+
llm-mask kube -n production get secrets
|
|
83
|
+
|
|
84
|
+
# Execute into a pod and run commands
|
|
85
|
+
llm-mask kube -n production --pod my-app --exec "env | grep -i password"
|
|
86
|
+
|
|
87
|
+
# Get config with credentials masked
|
|
88
|
+
llm-mask kube -n staging config
|
|
89
|
+
|
|
90
|
+
# Full kubectl command passthrough with redaction
|
|
91
|
+
llm-mask kube --context minikube get pods -o yaml
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### SSH Command
|
|
95
|
+
|
|
96
|
+
Execute commands on remote servers without exposing credentials in LLM context:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Execute remote command with redacted output
|
|
100
|
+
llm-mask ssh user@production-server "cat /etc/secrets/app.conf"
|
|
101
|
+
|
|
102
|
+
# With custom port and identity
|
|
103
|
+
llm-mask ssh -p 2222 -i ~/.ssh/deploy_key deploy@server "systemctl status"
|
|
104
|
+
|
|
105
|
+
# Interactive mode (for debugging, output is still redacted)
|
|
106
|
+
llm-mask ssh user@test-server
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Example Use Case
|
|
110
|
+
|
|
111
|
+
Debugging a Kubernetes issue with an LLM:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Without llm-mask - DANGER!
|
|
115
|
+
kubectl get secrets -n production
|
|
116
|
+
# Output: password=SuperSecret123, api_key=sk-proj-abc123
|
|
117
|
+
|
|
118
|
+
# With llm-mask - SAFE!
|
|
119
|
+
llm-mask kube -n production get secrets
|
|
120
|
+
# Output: password=[CREDENTIALS_1], api_key=[OPENAI_KEY_1]
|
|
121
|
+
# 🔒 Redacted 2 item(s)
|
|
122
|
+
|
|
123
|
+
# Now you can share this with an LLM for debugging
|
|
124
|
+
# without leaking actual credentials!
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Features Deep Dive
|
|
128
|
+
|
|
129
|
+
### 1. Masking Levels
|
|
130
|
+
|
|
131
|
+
Control what gets masked:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
--level basic # Only API keys/secrets (priority 90+)
|
|
135
|
+
--level standard # + PII like emails, phones (priority 40+) [default]
|
|
136
|
+
--level aggressive # Everything including URLs, file paths
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 2. Preserve Format
|
|
140
|
+
|
|
141
|
+
Keep structure visible while protecting values:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
$ llm-mask --preserve-format "Email: john@acme.com"
|
|
145
|
+
Email: j***@a***.com
|
|
146
|
+
|
|
147
|
+
$ llm-mask --preserve-format "Card: 4111 1111 1111 1111"
|
|
148
|
+
Card: 4111 ************ 1111
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 3. Custom Patterns File
|
|
152
|
+
|
|
153
|
+
Create `.llm-mask-rules.json` in your project:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"customPatterns": [
|
|
158
|
+
{
|
|
159
|
+
"name": "employee_id",
|
|
160
|
+
"regex": "\\bEMP-[0-9]{6}\\b",
|
|
161
|
+
"placeholder": "[EMPLOYEE_ID_{i}]"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "internal_ticket",
|
|
165
|
+
"regex": "\\bTICKET-[A-Z]{3}-[0-9]{4}\\b",
|
|
166
|
+
"placeholder": "[TICKET_{i}]"
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
"defaultLevel": "standard",
|
|
170
|
+
"auditLog": true
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 4. Scan & Report
|
|
175
|
+
|
|
176
|
+
Scan entire codebases for leaked secrets:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
$ llm-mask scan ./src
|
|
180
|
+
|
|
181
|
+
🔍 llm-mask Scan Report
|
|
182
|
+
|
|
183
|
+
Scanned: 47 files
|
|
184
|
+
Skipped: 123 files
|
|
185
|
+
Findings: 3 sensitive pattern(s)
|
|
186
|
+
|
|
187
|
+
⚠️ Findings:
|
|
188
|
+
|
|
189
|
+
config.ts:
|
|
190
|
+
❌ Line 15: openai_api_key
|
|
191
|
+
const apiKey = "sk-proj-abc123xyz789abcdef123"
|
|
192
|
+
|
|
193
|
+
.env.example:
|
|
194
|
+
⚠️ Line 8: email
|
|
195
|
+
support@example.com
|
|
196
|
+
|
|
197
|
+
db.ts:
|
|
198
|
+
❌ Line 42: url_with_creds
|
|
199
|
+
postgresql://admin:password@localhost/db
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 5. Diff Masking
|
|
203
|
+
|
|
204
|
+
Safe code review with LLMs:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Mask git diff output
|
|
208
|
+
git diff main | llm-mask diff
|
|
209
|
+
|
|
210
|
+
# Or use the diff mode directly
|
|
211
|
+
llm-mask diff --base main --head feature-branch
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 6. Context Detection
|
|
215
|
+
|
|
216
|
+
Smart masking that preserves structure:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# JSON - preserves keys, masks values
|
|
220
|
+
$ llm-mask --context '{"email": "john@test.com"}'
|
|
221
|
+
{"email": "[EMAIL_1]"}
|
|
222
|
+
|
|
223
|
+
# SQL - preserves identifiers, masks literals
|
|
224
|
+
$ llm-mask --context "SELECT * FROM users WHERE email = 'john@test.com'"
|
|
225
|
+
SELECT * FROM users WHERE email = '[EMAIL_1]'
|
|
226
|
+
|
|
227
|
+
# YAML - preserves keys, masks values
|
|
228
|
+
$ llm-mask --context "api_key: sk-proj-abc123"
|
|
229
|
+
api_key: [OPENAI_KEY_1]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 7. Language-Specific Patterns
|
|
233
|
+
|
|
234
|
+
Automatically detects and masks language-specific patterns:
|
|
235
|
+
|
|
236
|
+
| Language | Patterns |
|
|
237
|
+
|----------|----------|
|
|
238
|
+
| Python | Django SECRET_KEY, SQLAlchemy URLs, boto3 keys |
|
|
239
|
+
| JavaScript/TypeScript | Firebase configs, JWT tokens, MongoDB URIs |
|
|
240
|
+
| Go | AWS SDK configs, env structs |
|
|
241
|
+
| Ruby | Rails secret_key_base, DB passwords |
|
|
242
|
+
| Terraform | AWS access/secret keys |
|
|
243
|
+
|
|
244
|
+
### 8. Reversible Tokenization
|
|
245
|
+
|
|
246
|
+
For production use with deterministic encryption:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { Tokenizer } from 'llm-mask'
|
|
250
|
+
|
|
251
|
+
const tokenizer = new Tokenizer({
|
|
252
|
+
salt: 'your-secure-salt-here' // Generate with: llm-tokenizer-generate-salt
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// Tokenize (deterministic - same input = same output)
|
|
256
|
+
const token = tokenizer.tokenize('john@test.com')
|
|
257
|
+
// Returns: "tok_a1b2c3d4e5f6g7h8"
|
|
258
|
+
|
|
259
|
+
// Verify without storing mappings
|
|
260
|
+
tokenizer.verify(token, 'john@test.com') // true
|
|
261
|
+
tokenizer.verify(token, 'wrong@email.com') // false
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 9. Audit Logging
|
|
265
|
+
|
|
266
|
+
Safe logging without sensitive values:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { AuditLogger } from 'llm-mask'
|
|
270
|
+
|
|
271
|
+
const logger = new AuditLogger({
|
|
272
|
+
file: './audit.log',
|
|
273
|
+
console: true
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
logger.log({
|
|
277
|
+
timestamp: 1234567890,
|
|
278
|
+
patternName: 'email',
|
|
279
|
+
placeholder: '[EMAIL_1]',
|
|
280
|
+
inputLength: 123,
|
|
281
|
+
context: 'user-registration'
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 10. CI/CD Integration
|
|
286
|
+
|
|
287
|
+
GitHub Actions workflow included:
|
|
288
|
+
|
|
289
|
+
```yaml
|
|
290
|
+
# .github/workflows/llm-mask-check.yml
|
|
291
|
+
name: llm-mask - Check for Leaked Secrets
|
|
292
|
+
on: [push, pull_request]
|
|
293
|
+
jobs:
|
|
294
|
+
scan:
|
|
295
|
+
runs-on: ubuntu-latest
|
|
296
|
+
steps:
|
|
297
|
+
- uses: actions/checkout@v4
|
|
298
|
+
- run: npx llm-mask scan . --fail-on-detect
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## CLI Options
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
Usage: llm-mask [options] [text]
|
|
305
|
+
|
|
306
|
+
Options:
|
|
307
|
+
--check, -c Dry run: show what would be masked
|
|
308
|
+
--level, -l Masking level (basic|standard|aggressive)
|
|
309
|
+
--preserve-format, -f Preserve format (j***@a***.com)
|
|
310
|
+
--context Smart context detection (SQL, JSON, etc.)
|
|
311
|
+
--unmask, -u Unmask previously masked text
|
|
312
|
+
--patterns, -p List all available patterns
|
|
313
|
+
--json, -j Output as JSON
|
|
314
|
+
--config <path> Path to config file
|
|
315
|
+
|
|
316
|
+
Commands:
|
|
317
|
+
exec [command...] Execute command with output redaction
|
|
318
|
+
kube [kubectl-args] Execute kubectl with output redaction
|
|
319
|
+
ssh <host> Execute SSH command with output redaction
|
|
320
|
+
scan [path] Scan codebase for secrets
|
|
321
|
+
diff Mask git diff output
|
|
322
|
+
patterns List all available patterns
|
|
323
|
+
|
|
324
|
+
Examples:
|
|
325
|
+
# Pipe mode
|
|
326
|
+
echo "API key sk-proj-123" | llm-mask
|
|
327
|
+
|
|
328
|
+
# Exec commands
|
|
329
|
+
llm-mask exec env | grep -i key
|
|
330
|
+
llm-mask kube -n production get secrets
|
|
331
|
+
llm-mask ssh user@server "cat /etc/secrets/app.conf"
|
|
332
|
+
|
|
333
|
+
# Scan codebase
|
|
334
|
+
llm-mask scan ./src --fail-on-detect
|
|
335
|
+
|
|
336
|
+
# Git diff
|
|
337
|
+
git diff main | llm-mask diff
|
|
338
|
+
|
|
339
|
+
# Preserve format
|
|
340
|
+
llm-mask --preserve-format "Contact john@acme.com"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Built-in Patterns
|
|
344
|
+
|
|
345
|
+
| Category | Patterns | Example |
|
|
346
|
+
|----------|----------|---------|
|
|
347
|
+
| API Keys | OpenAI, Anthropic, Stripe, AWS, GitHub, JWT | `[OPENAI_KEY_1]` |
|
|
348
|
+
| PII | Emails, SSNs, Credit Cards, Phones | `[EMAIL_1]` |
|
|
349
|
+
| URLs | URLs with credentials, internal URLs | `[CREDENTIALS_1]@` |
|
|
350
|
+
| Network | IPv4, IPv6 | `[IP_1]` |
|
|
351
|
+
| Identifiers | UUIDs, SHA256, MD5 hashes | `[UUID_1]` |
|
|
352
|
+
| Files | Unix paths, Windows paths | `[PATH_1]` |
|
|
353
|
+
|
|
354
|
+
## MCP Server
|
|
355
|
+
|
|
356
|
+
Add to Claude Code settings.json:
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"mcpServers": {
|
|
361
|
+
"llm-mask": {
|
|
362
|
+
"command": "node",
|
|
363
|
+
"args": ["/path/to/llm-mask/dist/mcp-server.js"]
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Available tools:
|
|
370
|
+
- `mask_data` - Mask text with options
|
|
371
|
+
- `unmask_data` - Unmask (trusted only)
|
|
372
|
+
- `check_masking` - Dry run
|
|
373
|
+
- `scan_directory` - Scan codebase
|
|
374
|
+
- `mask_diff` - Mask git diff
|
|
375
|
+
- `mask_context` - Context-aware masking
|
|
376
|
+
- `list_patterns` - List patterns
|
|
377
|
+
- `clear_mappings` - Clear memory
|
|
378
|
+
- `exec_redacted` - Execute command with output redaction (NEW!)
|
|
379
|
+
- `kube_exec` - Execute kubectl with redaction (NEW!)
|
|
380
|
+
- `ssh_exec` - Execute SSH command with redaction (NEW!)
|
|
381
|
+
|
|
382
|
+
## Security & Data Protection
|
|
383
|
+
|
|
384
|
+
### How It Works
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
388
|
+
│ Claude │ asks │ llm-mask │ sends │ LLM API │
|
|
389
|
+
│ Code │────────▶│ MCP Server │────────▶│ (Claude) │
|
|
390
|
+
│ (Local) │ │ (Local) │ │ (Remote) │
|
|
391
|
+
└─────────────┘ └─────────────┘ └─────────────┘
|
|
392
|
+
│ │ │
|
|
393
|
+
│ │ masked data │
|
|
394
|
+
│ │ only │
|
|
395
|
+
└───────────────────────┴────────────────────────┘
|
|
396
|
+
Your sensitive data NEVER leaves your machine
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**The key insight**: llm-mask runs **locally on your machine**. The MCP server:
|
|
400
|
+
1. Receives requests from Claude Code
|
|
401
|
+
2. Masks the data **before** anything leaves your machine
|
|
402
|
+
3. Sends only masked data to the LLM
|
|
403
|
+
|
|
404
|
+
### What is Protected ✅
|
|
405
|
+
|
|
406
|
+
| Scenario | Protected? | How |
|
|
407
|
+
|----------|-----------|-----|
|
|
408
|
+
| Explicit masking via MCP tools | ✅ Yes | Data masked locally before sending |
|
|
409
|
+
| Using `exec_redacted` tool | ✅ Yes | Command runs locally, output masked before LLM sees it |
|
|
410
|
+
| Using `kube_exec` tool | ✅ Yes | Credentials work for kubectl, output is masked |
|
|
411
|
+
| Using `ssh_exec` tool | ✅ Yes | Credentials work for SSH, output is masked |
|
|
412
|
+
|
|
413
|
+
### What is NOT Protected ⚠️
|
|
414
|
+
|
|
415
|
+
| Scenario | Risk | Why |
|
|
416
|
+
|----------|------|-----|
|
|
417
|
+
| You paste sensitive data directly in chat | ❌ Not protected | Goes straight to LLM without masking |
|
|
418
|
+
| You share file contents without masking | ❌ Not protected | File content sent directly to LLM |
|
|
419
|
+
| Claude Code reads files independently | ❌ Not protected | MCP doesn't intercept file reads |
|
|
420
|
+
|
|
421
|
+
### Example Usage
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// ❌ BAD - Pasting directly (NOT protected)
|
|
425
|
+
User: "Help me debug this error with API key sk-proj-abc123"
|
|
426
|
+
// → The key goes directly to Claude's API
|
|
427
|
+
|
|
428
|
+
// ✅ GOOD - Using MCP tool (protected)
|
|
429
|
+
User: "Help me debug this error"
|
|
430
|
+
[Claude uses mask_data tool]
|
|
431
|
+
// → Data masked locally, only [OPENAI_KEY_1] sent to LLM
|
|
432
|
+
|
|
433
|
+
// ✅ GOOD - Using exec_redacted (credential isolation)
|
|
434
|
+
User: "Check why my kubernetes secrets are failing"
|
|
435
|
+
[Claude uses kube_exec tool]
|
|
436
|
+
// → kubectl runs with your credentials
|
|
437
|
+
// → Output masked: "password=[CREDENTIALS_1]"
|
|
438
|
+
// → LLM sees structure but NOT actual credentials
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Security Model
|
|
442
|
+
|
|
443
|
+
```
|
|
444
|
+
Your Machine Remote (Anthropic)
|
|
445
|
+
───────────── ────────────────────
|
|
446
|
+
┌─────────────────────────────────────────────────────────┐
|
|
447
|
+
│ Sensitive Data: sk-proj-abc123xyz456 │
|
|
448
|
+
│ │
|
|
449
|
+
│ MCP Server masks it → [OPENAI_KEY_1] │
|
|
450
|
+
│ │
|
|
451
|
+
│ Only this is sent → [OPENAI_KEY_1] ──────────────▶ │
|
|
452
|
+
│ │
|
|
453
|
+
└─────────────────────────────────────────────────────────┘
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Important Limitations
|
|
457
|
+
|
|
458
|
+
1. **You must actively use the MCP tools** - Claude won't automatically mask everything
|
|
459
|
+
2. **File reads bypass MCP** - When Claude reads files directly, that data isn't masked
|
|
460
|
+
3. **MCP mappings are ephemeral** - Stored only in memory, cleared when MCP server restarts
|
|
461
|
+
4. **Trust model** - You're trusting the llm-mask code running locally
|
|
462
|
+
|
|
463
|
+
### Best Practices
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
# 1. Scan your codebase before sharing
|
|
467
|
+
llm-mask scan ./src
|
|
468
|
+
|
|
469
|
+
# 2. Use exec_redacted for command output
|
|
470
|
+
llm-mask exec kubectl get secrets
|
|
471
|
+
|
|
472
|
+
# 3. Mask git diffs before sharing
|
|
473
|
+
git diff main | llm-mask diff
|
|
474
|
+
|
|
475
|
+
# 4. When in doubt, pipe through llm-mask
|
|
476
|
+
cat sensitive-file.json | llm-mask | llm # share masked output
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Summary
|
|
480
|
+
|
|
481
|
+
**Yes, your sensitive data is protected** WHEN you use the MCP tools. But it's not automatic - you (or Claude) need to explicitly use the masking tools. The protection happens **locally before anything leaves your machine**.
|
|
482
|
+
|
|
483
|
+
## Library Usage
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
import {
|
|
487
|
+
mask,
|
|
488
|
+
unmask,
|
|
489
|
+
clearMasker,
|
|
490
|
+
Scanner,
|
|
491
|
+
ContextMasker,
|
|
492
|
+
Tokenizer,
|
|
493
|
+
SecureExecutor
|
|
494
|
+
} from 'llm-mask'
|
|
495
|
+
|
|
496
|
+
// Basic masking
|
|
497
|
+
const { masked, mappings, stats } = mask("Email: john@test.com")
|
|
498
|
+
|
|
499
|
+
// Preserve format
|
|
500
|
+
const { masked } = mask("john@test.com", { preserveFormat: true })
|
|
501
|
+
|
|
502
|
+
// Masking levels
|
|
503
|
+
const { masked } = mask(text, { level: 'basic' })
|
|
504
|
+
|
|
505
|
+
// Context-aware
|
|
506
|
+
const contextMasker = new ContextMasker()
|
|
507
|
+
const { masked, context } = contextMasker.mask(jsonString)
|
|
508
|
+
|
|
509
|
+
// Scan codebase
|
|
510
|
+
const scanner = new Scanner({ failOnDetect: true })
|
|
511
|
+
const report = await scanner.scan('./src')
|
|
512
|
+
|
|
513
|
+
// Tokenization
|
|
514
|
+
const tokenizer = new Tokenizer({ salt: 'secure-salt' })
|
|
515
|
+
const token = tokenizer.tokenize('sensitive-value')
|
|
516
|
+
|
|
517
|
+
// Execute commands with credential isolation
|
|
518
|
+
const executor = new SecureExecutor()
|
|
519
|
+
const result = await executor.exec({
|
|
520
|
+
command: 'kubectl',
|
|
521
|
+
args: ['get', 'secrets', '-n', 'production'],
|
|
522
|
+
level: 'standard'
|
|
523
|
+
})
|
|
524
|
+
// result.stdout - original output with credentials
|
|
525
|
+
// result.redacted.stdout - masked output for LLM
|
|
526
|
+
|
|
527
|
+
// Kubernetes convenience
|
|
528
|
+
const kubeResult = await executor.kubectl({
|
|
529
|
+
namespace: 'production',
|
|
530
|
+
pod: 'my-app',
|
|
531
|
+
execCommand: 'env',
|
|
532
|
+
level: 'standard'
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
// SSH convenience
|
|
536
|
+
const sshResult = await executor.ssh({
|
|
537
|
+
host: 'server.example.com',
|
|
538
|
+
user: 'admin',
|
|
539
|
+
remoteCommand: 'cat /etc/secrets/app.conf',
|
|
540
|
+
level: 'standard'
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
// Clear sensitive data from memory
|
|
544
|
+
clearMasker()
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## License
|
|
548
|
+
|
|
549
|
+
MIT
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit logging for masking operations
|
|
3
|
+
*
|
|
4
|
+
* Logs masking events WITHOUT the actual sensitive values
|
|
5
|
+
* Only records: timestamp, pattern type, placeholder, length
|
|
6
|
+
*/
|
|
7
|
+
export interface AuditEvent {
|
|
8
|
+
timestamp: number;
|
|
9
|
+
patternName: string;
|
|
10
|
+
placeholder: string;
|
|
11
|
+
inputLength: number;
|
|
12
|
+
context?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AuditLoggerOptions {
|
|
15
|
+
/** Log file path */
|
|
16
|
+
file?: string;
|
|
17
|
+
/** Include in console output */
|
|
18
|
+
console?: boolean;
|
|
19
|
+
/** Context to include in each log entry */
|
|
20
|
+
context?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Audit logger - records masking operations safely
|
|
24
|
+
*/
|
|
25
|
+
export declare class AuditLogger {
|
|
26
|
+
private file?;
|
|
27
|
+
private consoleOutput;
|
|
28
|
+
private context?;
|
|
29
|
+
private buffer;
|
|
30
|
+
constructor(options?: AuditLoggerOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Log a masking event
|
|
33
|
+
*/
|
|
34
|
+
log(event: AuditEvent): void;
|
|
35
|
+
/**
|
|
36
|
+
* Write event to file (append)
|
|
37
|
+
*/
|
|
38
|
+
private writeToFile;
|
|
39
|
+
/**
|
|
40
|
+
* Flush buffer and clear
|
|
41
|
+
*/
|
|
42
|
+
flush(): AuditEvent[];
|
|
43
|
+
/**
|
|
44
|
+
* Get buffered events
|
|
45
|
+
*/
|
|
46
|
+
getBuffer(): AuditEvent[];
|
|
47
|
+
/**
|
|
48
|
+
* Clear buffer
|
|
49
|
+
*/
|
|
50
|
+
clear(): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create audit logger from environment or config
|
|
54
|
+
*/
|
|
55
|
+
export declare function createAuditLogger(options?: AuditLoggerOptions): AuditLogger;
|
|
56
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAC,CAAQ;IACrB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAmB;gBAErB,OAAO,GAAE,kBAAuB;IAM5C;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAoB5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,KAAK,IAAI,UAAU,EAAE;IAMrB;;OAEG;IACH,SAAS,IAAI,UAAU,EAAE;IAIzB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAM3E"}
|