logshield-cli 0.5.0 → 0.7.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/CHANGELOG.md +41 -0
- package/README.md +46 -37
- package/dist/cli/index.cjs +75 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.7.0
|
|
4
|
+
|
|
5
|
+
### Security
|
|
6
|
+
|
|
7
|
+
- Added a per-line safety cap (`64KB`) alongside the existing `200KB` total input cap
|
|
8
|
+
- Overlong single-line input now fails with a deterministic bounded error instead of flowing into rule evaluation
|
|
9
|
+
- Hardened the strict-mode credit card detector to reduce ambiguous separator-heavy matching on pathological near-miss input
|
|
10
|
+
|
|
11
|
+
### Improved
|
|
12
|
+
|
|
13
|
+
- Added adversarial regression coverage for line-length boundaries, multiline line reporting, and regex near-miss cases
|
|
14
|
+
- Added bounded-failure contract coverage for CLI and detection-only code paths
|
|
15
|
+
|
|
16
|
+
### Docs
|
|
17
|
+
|
|
18
|
+
- Synced README limits documentation with the final bounded input behavior and error contract
|
|
19
|
+
|
|
20
|
+
### Notes
|
|
21
|
+
|
|
22
|
+
- No new CLI flags
|
|
23
|
+
- No breaking changes to normal successful scan output
|
|
24
|
+
- Input/usage bounded failures continue to exit with code `2`
|
|
25
|
+
|
|
26
|
+
## v0.6.0
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Redact modern platform tokens (GitHub, Slack, npm, PyPI, SendGrid)
|
|
31
|
+
- Redact npmrc auth tokens (`:_authToken=...`)
|
|
32
|
+
- Redact private key blocks (PEM/OpenSSH, including ENCRYPTED PRIVATE KEY)
|
|
33
|
+
|
|
34
|
+
### Docs
|
|
35
|
+
|
|
36
|
+
- Clarified redaction coverage by mode (Default vs Strict) in README and docs
|
|
37
|
+
- Documented the 200KB input safety cap
|
|
38
|
+
|
|
39
|
+
### Notes
|
|
40
|
+
|
|
41
|
+
- No CLI flag changes
|
|
42
|
+
- This release may redact more secrets in default mode (intended)
|
|
43
|
+
|
|
3
44
|
## v0.5.0
|
|
4
45
|
|
|
5
46
|
### Security
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ After LogShield, the same logs are safe to share.
|
|
|
46
46
|
|
|
47
47
|
Use LogShield whenever logs leave your system:
|
|
48
48
|
|
|
49
|
-
- Before
|
|
49
|
+
- Before logs end up in CI output or artifacts
|
|
50
50
|
- Before attaching logs to GitHub issues
|
|
51
51
|
- Before sending logs to third-party support
|
|
52
52
|
- Before sharing logs in Slack or email
|
|
@@ -72,9 +72,9 @@ Use without --dry-run to apply.
|
|
|
72
72
|
|
|
73
73
|
Notes:
|
|
74
74
|
|
|
75
|
-
- The report is printed to stdout
|
|
75
|
+
- The report is printed to **stdout** (human-readable)
|
|
76
76
|
- No log content is echoed
|
|
77
|
-
-
|
|
77
|
+
- In non-`--dry-run` mode, sanitized logs/JSON are written to stdout; summaries/errors go to stderr
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
# Enforce redaction (sanitized output)
|
|
@@ -84,10 +84,6 @@ echo "email=test@example.com Authorization: Bearer abcdefghijklmnop" | logshield
|
|
|
84
84
|
- Prefer `--dry-run` first in CI to verify you are not over-redacting.
|
|
85
85
|
- Then switch to enforced mode once you are satisfied with the preview.
|
|
86
86
|
|
|
87
|
-
LogShield is a CLI tool that scans logs and redacts **real secrets**
|
|
88
|
-
(API keys, tokens, credentials) before logs are shared with others,
|
|
89
|
-
AI tools, CI systems, or public channels.
|
|
90
|
-
|
|
91
87
|
It is designed to be **predictable, conservative, and safe for production pipelines**.
|
|
92
88
|
|
|
93
89
|
---
|
|
@@ -104,24 +100,6 @@ They are deployed to **https://logshield.dev** via Vercel.
|
|
|
104
100
|
- GitHub: https://github.com/afria85/LogShield
|
|
105
101
|
- Sponsor: https://github.com/sponsors/afria85
|
|
106
102
|
|
|
107
|
-
## Local preview (website)
|
|
108
|
-
|
|
109
|
-
To preview the website on your computer:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npm run dev
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
To preview on a phone/tablet on the same Wi-Fi:
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
npm run dev:lan
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Then open the printed LAN URL on your device.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
103
|
## Why LogShield exists
|
|
126
104
|
|
|
127
105
|
Logs are frequently copied into:
|
|
@@ -154,7 +132,7 @@ The same input always produces the same output.
|
|
|
154
132
|
- No environment-dependent behavior
|
|
155
133
|
- Safe for CI, audits, and reproducibility
|
|
156
134
|
|
|
157
|
-
### 2.
|
|
135
|
+
### 2. Prefer precision over recall
|
|
158
136
|
|
|
159
137
|
LogShield must **not** redact non-secrets.
|
|
160
138
|
|
|
@@ -220,6 +198,22 @@ If a file is not provided and input is piped, LogShield automatically reads from
|
|
|
220
198
|
|
|
221
199
|
Note: the npm package ships the CLI only; there is no supported JS API surface.
|
|
222
200
|
|
|
201
|
+
### Limits
|
|
202
|
+
|
|
203
|
+
- Maximum input size: **200KB** (safety cap). Oversized input exits with code `2`.
|
|
204
|
+
- Maximum line length: **64KB** per line. If any single line exceeds the cap, LogShield exits with code `2` and a deterministic `Log line <n> exceeds 64KB limit` error.
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
### Windows note
|
|
208
|
+
|
|
209
|
+
If `logshield` is not found after a global install, ensure your npm global bin directory is on `PATH`.
|
|
210
|
+
|
|
211
|
+
Check it with:
|
|
212
|
+
|
|
213
|
+
- `npm prefix -g` (add this directory to PATH)
|
|
214
|
+
|
|
215
|
+
Restart your terminal after updating PATH.
|
|
216
|
+
|
|
223
217
|
---
|
|
224
218
|
|
|
225
219
|
## CLI Flags
|
|
@@ -264,7 +258,7 @@ logshield scan app.log
|
|
|
264
258
|
cat app.log | logshield scan
|
|
265
259
|
```
|
|
266
260
|
|
|
267
|
-
`--stdin` is optional; piped input is auto-detected.
|
|
261
|
+
`--stdin` is optional; piped input is auto-detected. Use `--stdin` to force reading from stdin in TTY/paste workflows.
|
|
268
262
|
|
|
269
263
|
---
|
|
270
264
|
|
|
@@ -333,7 +327,7 @@ jobs:
|
|
|
333
327
|
|
|
334
328
|
- uses: actions/setup-node@v4
|
|
335
329
|
with:
|
|
336
|
-
node-version:
|
|
330
|
+
node-version: 22
|
|
337
331
|
|
|
338
332
|
- run: npm install -g logshield-cli
|
|
339
333
|
|
|
@@ -425,16 +419,32 @@ Notes:
|
|
|
425
419
|
|
|
426
420
|
## What gets redacted
|
|
427
421
|
|
|
428
|
-
|
|
422
|
+
LogShield uses a fixed, deterministic rule set. The exact coverage depends on the selected mode.
|
|
423
|
+
|
|
424
|
+
### Default mode (recommended)
|
|
429
425
|
|
|
430
|
-
- Passwords (
|
|
431
|
-
- API key headers
|
|
432
|
-
-
|
|
426
|
+
- Passwords (quoted and JSON forms)
|
|
427
|
+
- API key headers (e.g. `x-api-key: ...`)
|
|
428
|
+
- API keys (e.g. `api_key=...`, `api-key: ...`)
|
|
429
|
+
- Authorization Bearer tokens
|
|
433
430
|
- JWTs
|
|
431
|
+
- GitHub tokens (`ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_`, and `github_pat_...`)
|
|
432
|
+
- Slack tokens (`xox*` and `xapp-...`)
|
|
433
|
+
- npm access tokens (`npm_...`)
|
|
434
|
+
- npmrc auth tokens (`:_authToken=...`)
|
|
435
|
+
- PyPI API tokens (`pypi-...`)
|
|
436
|
+
- SendGrid API keys (`SG.<...>.<...>`)
|
|
437
|
+
- Private key blocks (PEM/OpenSSH)
|
|
434
438
|
- Emails
|
|
435
439
|
- URLs with embedded credentials
|
|
436
|
-
- Database credentials (including redis
|
|
437
|
-
- Cloud
|
|
440
|
+
- Database URL credentials (including `redis://...` and `mssql://...`)
|
|
441
|
+
- Cloud credentials in explicit labeled forms (e.g. `AWS_SECRET_ACCESS_KEY=...`)
|
|
442
|
+
|
|
443
|
+
### Strict mode (adds)
|
|
444
|
+
|
|
445
|
+
- Stripe secret keys (e.g. `sk_live_...`, `sk_test_...`)
|
|
446
|
+
- AWS access keys (`AKIA...`)
|
|
447
|
+
- AWS secret keys (strict fallback)
|
|
438
448
|
- Credit card numbers (Luhn-validated)
|
|
439
449
|
|
|
440
450
|
---
|
|
@@ -460,7 +470,7 @@ Depending on rules and mode:
|
|
|
460
470
|
LogShield guarantees:
|
|
461
471
|
|
|
462
472
|
- Deterministic output
|
|
463
|
-
- Stable behavior within the current minor line **v0.
|
|
473
|
+
- Stable behavior within the current minor line **v0.6.x**
|
|
464
474
|
- No runtime dependencies
|
|
465
475
|
- Snapshot-tested and contract-tested
|
|
466
476
|
- No telemetry
|
|
@@ -490,8 +500,7 @@ It is a **last-line safety net**, not a primary defense.
|
|
|
490
500
|
|
|
491
501
|
## License
|
|
492
502
|
|
|
493
|
-
Apache-2.0
|
|
494
|
-
---
|
|
503
|
+
Apache-2.0 — see `LICENSE`.
|
|
495
504
|
|
|
496
505
|
## Contributing
|
|
497
506
|
|
package/dist/cli/index.cjs
CHANGED
|
@@ -140,15 +140,23 @@ var init_applyRules = __esm({
|
|
|
140
140
|
// src/engine/guard.ts
|
|
141
141
|
function guardInput(input) {
|
|
142
142
|
if (!input) return "";
|
|
143
|
-
if (input.length >
|
|
143
|
+
if (input.length > MAX_INPUT_SIZE) {
|
|
144
144
|
throw new Error("Log size exceeds 200KB limit");
|
|
145
145
|
}
|
|
146
|
+
const lines = input.split(/\r?\n/);
|
|
147
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
148
|
+
if (lines[i].length > MAX_LINE_LENGTH) {
|
|
149
|
+
const lineNumber = i + 1;
|
|
150
|
+
throw new Error(`Log line ${lineNumber} exceeds 64KB limit`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
146
153
|
return input;
|
|
147
154
|
}
|
|
148
|
-
var
|
|
155
|
+
var MAX_INPUT_SIZE, MAX_LINE_LENGTH;
|
|
149
156
|
var init_guard = __esm({
|
|
150
157
|
"src/engine/guard.ts"() {
|
|
151
|
-
|
|
158
|
+
MAX_INPUT_SIZE = 200 * 1024;
|
|
159
|
+
MAX_LINE_LENGTH = 64 * 1024;
|
|
152
160
|
}
|
|
153
161
|
});
|
|
154
162
|
|
|
@@ -157,6 +165,66 @@ var tokenRules;
|
|
|
157
165
|
var init_tokens = __esm({
|
|
158
166
|
"src/rules/tokens.ts"() {
|
|
159
167
|
tokenRules = [
|
|
168
|
+
// Multi-line private key blocks (PEM/OpenSSH). Run early to avoid leaking
|
|
169
|
+
// partial material through other rules.
|
|
170
|
+
{
|
|
171
|
+
name: "PRIVATE_KEY_BLOCK",
|
|
172
|
+
pattern: /-----BEGIN\s+(?:(?:RSA|EC|DSA)\s+)?(?:ENCRYPTED\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END\s+(?:(?:RSA|EC|DSA)\s+)?(?:ENCRYPTED\s+)?PRIVATE\s+KEY-----/gi,
|
|
173
|
+
replace: () => "<REDACTED_PRIVATE_KEY_BLOCK>"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: "OPENSSH_PRIVATE_KEY_BLOCK",
|
|
177
|
+
pattern: /-----BEGIN OPENSSH PRIVATE KEY-----[\s\S]*?-----END OPENSSH PRIVATE KEY-----/g,
|
|
178
|
+
replace: () => "<REDACTED_PRIVATE_KEY_BLOCK>"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "PRIVATE_KEY_HEADER",
|
|
182
|
+
pattern: /-----BEGIN\s+(?:(?:RSA|EC|DSA)\s+)?(?:ENCRYPTED\s+)?PRIVATE\s+KEY-----|-----BEGIN OPENSSH PRIVATE KEY-----/gi,
|
|
183
|
+
replace: () => "<REDACTED_PRIVATE_KEY_HEADER>"
|
|
184
|
+
},
|
|
185
|
+
// Modern platform tokens (prefix-anchored, low false-positive).
|
|
186
|
+
{
|
|
187
|
+
name: "GITHUB_TOKEN",
|
|
188
|
+
pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\b/g,
|
|
189
|
+
replace: () => "<REDACTED_GITHUB_TOKEN>"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "GITHUB_FINE_GRAINED_TOKEN",
|
|
193
|
+
pattern: /\bgithub_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}\b/g,
|
|
194
|
+
replace: () => "<REDACTED_GITHUB_TOKEN>"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "SLACK_TOKEN",
|
|
198
|
+
pattern: /\bxox(?:b|p|a|s|r)-[A-Za-z0-9-]{10,250}\b/g,
|
|
199
|
+
replace: () => "<REDACTED_SLACK_TOKEN>"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "SLACK_APP_TOKEN",
|
|
203
|
+
pattern: /\bxapp-\d-[A-Za-z0-9-]{10,250}\b/g,
|
|
204
|
+
replace: () => "<REDACTED_SLACK_TOKEN>"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "NPM_TOKEN",
|
|
208
|
+
pattern: /\bnpm_[A-Za-z0-9]{36}\b/g,
|
|
209
|
+
replace: () => "<REDACTED_NPM_TOKEN>"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "NPMRC_AUTH_TOKEN",
|
|
213
|
+
// .npmrc-style auth token, commonly seen as:
|
|
214
|
+
// //registry.npmjs.org/:_authToken=...
|
|
215
|
+
pattern: /(:_authToken\s*=\s*)([^\s\r\n]+)/gi,
|
|
216
|
+
replace: (_match, _ctx, groups) => `${groups[0]}<REDACTED_NPM_TOKEN>`
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "PYPI_TOKEN",
|
|
220
|
+
pattern: /\bpypi-[A-Za-z0-9_-]{85,200}\b/g,
|
|
221
|
+
replace: () => "<REDACTED_PYPI_TOKEN>"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: "SENDGRID_API_KEY",
|
|
225
|
+
pattern: /\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g,
|
|
226
|
+
replace: () => "<REDACTED_SENDGRID_KEY>"
|
|
227
|
+
},
|
|
160
228
|
{
|
|
161
229
|
name: "JWT",
|
|
162
230
|
pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
@@ -327,7 +395,9 @@ var init_creditCard = __esm({
|
|
|
327
395
|
creditCardRules = [
|
|
328
396
|
{
|
|
329
397
|
name: "CREDIT_CARD",
|
|
330
|
-
|
|
398
|
+
// Keep separators simple and bounded between digits to avoid ambiguous
|
|
399
|
+
// repetition on long near-miss inputs.
|
|
400
|
+
pattern: /\b\d(?:[ -]?\d){12,18}\b/g,
|
|
331
401
|
replace: (match, { strict }) => {
|
|
332
402
|
if (!strict) return match;
|
|
333
403
|
return isValidLuhn(match) ? "<REDACTED_CC>" : match;
|
|
@@ -535,7 +605,7 @@ var ALLOWED_FLAGS = /* @__PURE__ */ new Set([
|
|
|
535
605
|
"--help"
|
|
536
606
|
]);
|
|
537
607
|
function getVersion() {
|
|
538
|
-
return true ? "0.
|
|
608
|
+
return true ? "0.7.0" : "unknown";
|
|
539
609
|
}
|
|
540
610
|
function printHelp() {
|
|
541
611
|
process.stdout.write(`Usage: logshield scan [file]
|