logshield-cli 0.4.4 → 0.6.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +76 -21
  3. package/dist/cli/index.cjs +112 -18
  4. package/package.json +36 -8
  5. package/dist/android-chrome-192x192.png +0 -0
  6. package/dist/android-chrome-512x512.png +0 -0
  7. package/dist/apple-touch-icon.png +0 -0
  8. package/dist/assets/index-B3qxIuiz.css +0 -1
  9. package/dist/assets/index-DDJ1Wxio.js +0 -29
  10. package/dist/engine/applyRules.js +0 -27
  11. package/dist/engine/engine/applyRules.d.ts +0 -5
  12. package/dist/engine/engine/applyRules.js +0 -30
  13. package/dist/engine/engine/guard.d.ts +0 -1
  14. package/dist/engine/engine/guard.js +0 -12
  15. package/dist/engine/engine/sanitizeLog.d.ts +0 -11
  16. package/dist/engine/engine/sanitizeLog.js +0 -18
  17. package/dist/engine/guard.js +0 -9
  18. package/dist/engine/rules/cloud.d.ts +0 -2
  19. package/dist/engine/rules/cloud.js +0 -15
  20. package/dist/engine/rules/credentials.d.ts +0 -2
  21. package/dist/engine/rules/credentials.js +0 -15
  22. package/dist/engine/rules/creditCard.d.ts +0 -2
  23. package/dist/engine/rules/creditCard.js +0 -15
  24. package/dist/engine/rules/custom.d.ts +0 -2
  25. package/dist/engine/rules/custom.js +0 -14
  26. package/dist/engine/rules/index.d.ts +0 -11
  27. package/dist/engine/rules/index.js +0 -38
  28. package/dist/engine/rules/tokens.d.ts +0 -2
  29. package/dist/engine/rules/tokens.js +0 -20
  30. package/dist/engine/rules/types.d.ts +0 -9
  31. package/dist/engine/rules/types.js +0 -2
  32. package/dist/engine/rules/urls.d.ts +0 -2
  33. package/dist/engine/rules/urls.js +0 -10
  34. package/dist/engine/sanitizeLog.js +0 -15
  35. package/dist/engine/utils/luhn.d.ts +0 -1
  36. package/dist/engine/utils/luhn.js +0 -19
  37. package/dist/favicon-16x16.png +0 -0
  38. package/dist/favicon-32x32.png +0 -0
  39. package/dist/favicon.ico +0 -0
  40. package/dist/features.jsx +0 -462
  41. package/dist/robots.txt +0 -4
  42. package/dist/rules/cloud.js +0 -12
  43. package/dist/rules/credentials.js +0 -12
  44. package/dist/rules/creditCard.js +0 -12
  45. package/dist/rules/custom.js +0 -11
  46. package/dist/rules/index.js +0 -35
  47. package/dist/rules/tokens.js +0 -17
  48. package/dist/rules/types.js +0 -1
  49. package/dist/rules/urls.js +0 -7
  50. package/dist/site.webmanifest +0 -20
  51. package/dist/utils/luhn.js +0 -16
  52. package/dist/vite.svg +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.6.0
4
+
5
+ ### Added
6
+
7
+ - Redact modern platform tokens (GitHub, Slack, npm, PyPI, SendGrid)
8
+ - Redact npmrc auth tokens (`:_authToken=...`)
9
+ - Redact private key blocks (PEM/OpenSSH, including ENCRYPTED PRIVATE KEY)
10
+
11
+ ### Docs
12
+
13
+ - Clarified redaction coverage by mode (Default vs Strict) in README and docs
14
+ - Documented the 200KB input safety cap
15
+
16
+ ### Notes
17
+
18
+ - No CLI flag changes
19
+ - This release may redact more secrets in default mode (intended)
20
+
21
+ ## v0.5.0
22
+
23
+ ### Security
24
+
25
+ - Hardened dry-run result shape: dry-run no longer returns the raw input in `output` (safe to serialize and avoids re-leakage in programmatic contexts)
26
+ - `--dry-run` can be combined with `--json` for machine-readable detection without leaking log content (`output` is intentionally empty)
27
+
28
+ ### Added
29
+
30
+ - Added detection-only helper `scanLog(input)` (internal only; safe to serialize)
31
+
32
+ ### Compatibility
33
+
34
+ - CLI behavior is unchanged: dry-run still prints a human report and never echoes log content
35
+ - Programmatic consumers: `dryRun` now returns `output: ""` (intentional)
36
+ - npm package ships CLI only; no supported JS API surface is published
37
+
3
38
  ## v0.4.4
4
39
 
5
40
  ### Fixed
@@ -18,7 +53,7 @@
18
53
 
19
54
  - Prevented API key redaction from corrupting header names (`x-api-key`)
20
55
  - Preserved key labels when redacting `api_key=...` values
21
- - Corrected CLI exit code for invalid flag combinations (`--json --dry-run` now exits with code 2)
56
+ - Corrected CLI exit code for invalid flag combinations (e.g., `--summary --json` exits with code 2)
22
57
 
23
58
  ### Improved
24
59
 
package/README.md CHANGED
@@ -3,6 +3,8 @@
3
3
  [![npm version](https://img.shields.io/npm/v/logshield-cli)](https://www.npmjs.com/package/logshield-cli)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/logshield-cli)](https://www.npmjs.com/package/logshield-cli)
5
5
  [![CI](https://github.com/afria85/LogShield/actions/workflows/ci.yml/badge.svg)](https://github.com/afria85/LogShield/actions/workflows/ci.yml)
6
+ [![license](https://img.shields.io/badge/license-Apache--2.0-3a3a3a)](https://github.com/afria85/LogShield/blob/main/LICENSE)
7
+ [![Sponsor](https://img.shields.io/badge/sponsor-GitHub%20Sponsors-3a3a3a)](https://github.com/sponsors/afria85)
6
8
 
7
9
  Your logs already contain secrets. You just don't see them.
8
10
 
@@ -44,7 +46,7 @@ After LogShield, the same logs are safe to share.
44
46
 
45
47
  Use LogShield whenever logs leave your system:
46
48
 
47
- - Before pasting logs into CI
49
+ - Before logs end up in CI output or artifacts
48
50
  - Before attaching logs to GitHub issues
49
51
  - Before sending logs to third-party support
50
52
  - Before sharing logs in Slack or email
@@ -70,9 +72,9 @@ Use without --dry-run to apply.
70
72
 
71
73
  Notes:
72
74
 
73
- - The report is printed to stdout
75
+ - The report is printed to **stdout** (human-readable)
74
76
  - No log content is echoed
75
- - Output is deterministic and CI-safe
77
+ - In non-`--dry-run` mode, sanitized logs/JSON are written to stdout; summaries/errors go to stderr
76
78
 
77
79
  ```bash
78
80
  # Enforce redaction (sanitized output)
@@ -82,10 +84,6 @@ echo "email=test@example.com Authorization: Bearer abcdefghijklmnop" | logshield
82
84
  - Prefer `--dry-run` first in CI to verify you are not over-redacting.
83
85
  - Then switch to enforced mode once you are satisfied with the preview.
84
86
 
85
- LogShield is a CLI tool that scans logs and redacts **real secrets**
86
- (API keys, tokens, credentials) before logs are shared with others,
87
- AI tools, CI systems, or public channels.
88
-
89
87
  It is designed to be **predictable, conservative, and safe for production pipelines**.
90
88
 
91
89
  ---
@@ -95,7 +93,12 @@ It is designed to be **predictable, conservative, and safe for production pipeli
95
93
  The website and documentation live in the `/docs` directory.
96
94
  They are deployed to **https://logshield.dev** via Vercel.
97
95
 
98
- ---
96
+ ## Project links
97
+
98
+ - Website: https://logshield.dev
99
+ - Docs: https://logshield.dev/docs.html
100
+ - GitHub: https://github.com/afria85/LogShield
101
+ - Sponsor: https://github.com/sponsors/afria85
99
102
 
100
103
  ## Why LogShield exists
101
104
 
@@ -129,7 +132,7 @@ The same input always produces the same output.
129
132
  - No environment-dependent behavior
130
133
  - Safe for CI, audits, and reproducibility
131
134
 
132
- ### 2. Zero false-positive fatality
135
+ ### 2. Prefer precision over recall
133
136
 
134
137
  LogShield must **not** redact non-secrets.
135
138
 
@@ -193,6 +196,23 @@ logshield scan [file]
193
196
 
194
197
  If a file is not provided and input is piped, LogShield automatically reads from **STDIN**.
195
198
 
199
+ Note: the npm package ships the CLI only; there is no supported JS API surface.
200
+
201
+ ### Limits
202
+
203
+ - Maximum input size: **200KB** (safety cap). Oversized input exits with code `2`.
204
+
205
+
206
+ ### Windows note
207
+
208
+ If `logshield` is not found after a global install, ensure your npm global bin directory is on `PATH`.
209
+
210
+ Check it with:
211
+
212
+ - `npm prefix -g` (add this directory to PATH)
213
+
214
+ Restart your terminal after updating PATH.
215
+
196
216
  ---
197
217
 
198
218
  ## CLI Flags
@@ -213,7 +233,7 @@ If a file is not provided and input is piped, LogShield automatically reads from
213
233
  Print a compact redaction summary
214
234
 
215
235
  - `--json`
216
- JSON output (cannot be combined with `--dry-run`)
236
+ JSON output (can be combined with `--dry-run`; output is empty in dry-run)
217
237
 
218
238
  - `--version`
219
239
  Print CLI version
@@ -237,7 +257,7 @@ logshield scan app.log
237
257
  cat app.log | logshield scan
238
258
  ```
239
259
 
240
- `--stdin` is optional; piped input is auto-detected.
260
+ `--stdin` is optional; piped input is auto-detected. Use `--stdin` to force reading from stdin in TTY/paste workflows.
241
261
 
242
262
  ---
243
263
 
@@ -306,7 +326,7 @@ jobs:
306
326
 
307
327
  - uses: actions/setup-node@v4
308
328
  with:
309
- node-version: 18
329
+ node-version: 22
310
330
 
311
331
  - run: npm install -g logshield-cli
312
332
 
@@ -371,9 +391,16 @@ Structured output for tooling and automation:
371
391
  logshield scan --json < logs.txt
372
392
  ```
373
393
 
394
+ Detection-only JSON (safe to serialize; no log content):
395
+
396
+ ```bash
397
+ logshield scan --json --dry-run < logs.txt
398
+ ```
399
+
374
400
  Notes:
375
401
 
376
- - `--json` **cannot** be combined with `--dry-run`
402
+ - `--json` can be combined with `--dry-run` for machine-readable detection
403
+ - In `--dry-run` JSON mode, `output` is intentionally an empty string
377
404
  - Usage errors exit with code `2`
378
405
  - Output is always newline-terminated
379
406
 
@@ -391,16 +418,32 @@ Notes:
391
418
 
392
419
  ## What gets redacted
393
420
 
394
- Depending on rules and mode:
421
+ LogShield uses a fixed, deterministic rule set. The exact coverage depends on the selected mode.
395
422
 
396
- - Passwords (supports quoted and JSON forms)
397
- - API key headers
398
- - Authorization bearer tokens
423
+ ### Default mode (recommended)
424
+
425
+ - Passwords (quoted and JSON forms)
426
+ - API key headers (e.g. `x-api-key: ...`)
427
+ - API keys (e.g. `api_key=...`, `api-key: ...`)
428
+ - Authorization Bearer tokens
399
429
  - JWTs
430
+ - GitHub tokens (`ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_`, and `github_pat_...`)
431
+ - Slack tokens (`xox*` and `xapp-...`)
432
+ - npm access tokens (`npm_...`)
433
+ - npmrc auth tokens (`:_authToken=...`)
434
+ - PyPI API tokens (`pypi-...`)
435
+ - SendGrid API keys (`SG.<...>.<...>`)
436
+ - Private key blocks (PEM/OpenSSH)
400
437
  - Emails
401
438
  - URLs with embedded credentials
402
- - Database credentials (including redis:// and mssql://)
403
- - Cloud provider credentials
439
+ - Database URL credentials (including `redis://...` and `mssql://...`)
440
+ - Cloud credentials in explicit labeled forms (e.g. `AWS_SECRET_ACCESS_KEY=...`)
441
+
442
+ ### Strict mode (adds)
443
+
444
+ - Stripe secret keys (e.g. `sk_live_...`, `sk_test_...`)
445
+ - AWS access keys (`AKIA...`)
446
+ - AWS secret keys (strict fallback)
404
447
  - Credit card numbers (Luhn-validated)
405
448
 
406
449
  ---
@@ -426,7 +469,7 @@ Depending on rules and mode:
426
469
  LogShield guarantees:
427
470
 
428
471
  - Deterministic output
429
- - Stable behavior within **v0.4.x**
472
+ - Stable behavior within the current minor line **v0.6.x**
430
473
  - No runtime dependencies
431
474
  - Snapshot-tested and contract-tested
432
475
  - No telemetry
@@ -456,4 +499,16 @@ It is a **last-line safety net**, not a primary defense.
456
499
 
457
500
  ## License
458
501
 
459
- Apache-2.0
502
+ Apache-2.0 &mdash; see `LICENSE`.
503
+
504
+ ## Contributing
505
+
506
+ See `CONTRIBUTING.md`.
507
+
508
+ ## Security
509
+
510
+ See `SECURITY.md`.
511
+
512
+ ## Support
513
+
514
+ See `SUPPORT.md`.
@@ -36,22 +36,24 @@ var readInput_exports = {};
36
36
  __export(readInput_exports, {
37
37
  readInput: () => readInput
38
38
  });
39
- async function readInput(file) {
39
+ async function readInput(file, opts) {
40
40
  if (file) {
41
41
  if (!import_node_fs.default.existsSync(file)) {
42
42
  throw new Error(`File not found: ${file}`);
43
43
  }
44
44
  return import_node_fs.default.readFileSync(file, "utf8");
45
45
  }
46
- if (!process.stdin.isTTY) {
46
+ const stdin = opts?.stdin ?? process.stdin;
47
+ const forceStdin = Boolean(opts?.forceStdin);
48
+ if (!stdin.isTTY || forceStdin) {
47
49
  return new Promise((resolve, reject) => {
48
50
  let data = "";
49
- process.stdin.setEncoding("utf8");
50
- process.stdin.on("data", (chunk) => {
51
+ stdin.setEncoding?.("utf8");
52
+ stdin.on("data", (chunk) => {
51
53
  data += chunk;
52
54
  });
53
- process.stdin.on("end", () => resolve(data));
54
- process.stdin.on("error", reject);
55
+ stdin.on("end", () => resolve(data));
56
+ stdin.on("error", reject);
55
57
  });
56
58
  }
57
59
  throw new Error("No input provided");
@@ -155,6 +157,66 @@ var tokenRules;
155
157
  var init_tokens = __esm({
156
158
  "src/rules/tokens.ts"() {
157
159
  tokenRules = [
160
+ // Multi-line private key blocks (PEM/OpenSSH). Run early to avoid leaking
161
+ // partial material through other rules.
162
+ {
163
+ name: "PRIVATE_KEY_BLOCK",
164
+ 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,
165
+ replace: () => "<REDACTED_PRIVATE_KEY_BLOCK>"
166
+ },
167
+ {
168
+ name: "OPENSSH_PRIVATE_KEY_BLOCK",
169
+ pattern: /-----BEGIN OPENSSH PRIVATE KEY-----[\s\S]*?-----END OPENSSH PRIVATE KEY-----/g,
170
+ replace: () => "<REDACTED_PRIVATE_KEY_BLOCK>"
171
+ },
172
+ {
173
+ name: "PRIVATE_KEY_HEADER",
174
+ pattern: /-----BEGIN\s+(?:(?:RSA|EC|DSA)\s+)?(?:ENCRYPTED\s+)?PRIVATE\s+KEY-----|-----BEGIN OPENSSH PRIVATE KEY-----/gi,
175
+ replace: () => "<REDACTED_PRIVATE_KEY_HEADER>"
176
+ },
177
+ // Modern platform tokens (prefix-anchored, low false-positive).
178
+ {
179
+ name: "GITHUB_TOKEN",
180
+ pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\b/g,
181
+ replace: () => "<REDACTED_GITHUB_TOKEN>"
182
+ },
183
+ {
184
+ name: "GITHUB_FINE_GRAINED_TOKEN",
185
+ pattern: /\bgithub_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}\b/g,
186
+ replace: () => "<REDACTED_GITHUB_TOKEN>"
187
+ },
188
+ {
189
+ name: "SLACK_TOKEN",
190
+ pattern: /\bxox(?:b|p|a|s|r)-[A-Za-z0-9-]{10,250}\b/g,
191
+ replace: () => "<REDACTED_SLACK_TOKEN>"
192
+ },
193
+ {
194
+ name: "SLACK_APP_TOKEN",
195
+ pattern: /\bxapp-\d-[A-Za-z0-9-]{10,250}\b/g,
196
+ replace: () => "<REDACTED_SLACK_TOKEN>"
197
+ },
198
+ {
199
+ name: "NPM_TOKEN",
200
+ pattern: /\bnpm_[A-Za-z0-9]{36}\b/g,
201
+ replace: () => "<REDACTED_NPM_TOKEN>"
202
+ },
203
+ {
204
+ name: "NPMRC_AUTH_TOKEN",
205
+ // .npmrc-style auth token, commonly seen as:
206
+ // //registry.npmjs.org/:_authToken=...
207
+ pattern: /(:_authToken\s*=\s*)([^\s\r\n]+)/gi,
208
+ replace: (_match, _ctx, groups) => `${groups[0]}<REDACTED_NPM_TOKEN>`
209
+ },
210
+ {
211
+ name: "PYPI_TOKEN",
212
+ pattern: /\bpypi-[A-Za-z0-9_-]{85,200}\b/g,
213
+ replace: () => "<REDACTED_PYPI_TOKEN>"
214
+ },
215
+ {
216
+ name: "SENDGRID_API_KEY",
217
+ pattern: /\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g,
218
+ replace: () => "<REDACTED_SENDGRID_KEY>"
219
+ },
158
220
  {
159
221
  name: "JWT",
160
222
  pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
@@ -503,7 +565,7 @@ function sanitizeLog(input, options) {
503
565
  const matches = [];
504
566
  if (ctx.dryRun) {
505
567
  applyRules(input, allRules, ctx, matches);
506
- return { output: input, matches };
568
+ return { output: "", matches };
507
569
  }
508
570
  const output = applyRules(input, allRules, ctx, matches);
509
571
  return { output, matches };
@@ -522,8 +584,18 @@ var { writeOutput: writeOutput2 } = (init_writeOutput(), __toCommonJS(writeOutpu
522
584
  var { printSummary: printSummary2 } = (init_summary(), __toCommonJS(summary_exports));
523
585
  var { sanitizeLog: sanitizeLog2 } = (init_sanitizeLog(), __toCommonJS(sanitizeLog_exports));
524
586
  var rawArgs = process.argv.slice(2).map((arg) => arg === "-h" ? "--help" : arg);
587
+ var ALLOWED_FLAGS = /* @__PURE__ */ new Set([
588
+ "--strict",
589
+ "--dry-run",
590
+ "--stdin",
591
+ "--fail-on-detect",
592
+ "--json",
593
+ "--summary",
594
+ "--version",
595
+ "--help"
596
+ ]);
525
597
  function getVersion() {
526
- return true ? "0.4.4" : "unknown";
598
+ return true ? "0.6.0" : "unknown";
527
599
  }
528
600
  function printHelp() {
529
601
  process.stdout.write(`Usage: logshield scan [file]
@@ -550,14 +622,23 @@ function writeErr(message) {
550
622
  function parseArgs(args) {
551
623
  const flags = /* @__PURE__ */ new Set();
552
624
  const positionals = [];
625
+ const unknownFlags = [];
553
626
  for (const arg of args) {
554
- if (arg.startsWith("--")) {
555
- flags.add(arg);
627
+ if (arg.startsWith("-")) {
628
+ if (arg.startsWith("--")) {
629
+ if (!ALLOWED_FLAGS.has(arg)) {
630
+ unknownFlags.push(arg);
631
+ } else {
632
+ flags.add(arg);
633
+ }
634
+ } else {
635
+ unknownFlags.push(arg);
636
+ }
556
637
  } else {
557
638
  positionals.push(arg);
558
639
  }
559
640
  }
560
- return { flags, positionals };
641
+ return { flags, positionals, unknownFlags };
561
642
  }
562
643
  function isStdinPiped() {
563
644
  return !process.stdin.isTTY;
@@ -596,16 +677,23 @@ function exitUsageError(message) {
596
677
  process.exit(2);
597
678
  }
598
679
  async function main() {
599
- if (rawArgs.length === 0 || rawArgs.includes("--help")) {
680
+ if (rawArgs.length === 0) {
600
681
  printHelp();
601
682
  process.exit(0);
602
683
  }
603
- if (rawArgs.includes("--version")) {
684
+ const { flags, positionals, unknownFlags } = parseArgs(rawArgs);
685
+ if (unknownFlags.length > 0) {
686
+ exitUsageError(`Unknown flag: ${unknownFlags[0]}`);
687
+ }
688
+ if (flags.has("--help")) {
689
+ printHelp();
690
+ process.exit(0);
691
+ }
692
+ if (flags.has("--version")) {
604
693
  process.stdout.write(`logshield v${getVersion()}
605
694
  `);
606
695
  process.exit(0);
607
696
  }
608
- const { flags, positionals } = parseArgs(rawArgs);
609
697
  const command = positionals[0];
610
698
  if (command !== "scan") {
611
699
  exitUsageError("Unknown command");
@@ -622,16 +710,22 @@ async function main() {
622
710
  if (useStdin && file) {
623
711
  exitUsageError("Cannot read from both STDIN and file");
624
712
  }
625
- if (dryRun && json) {
626
- exitUsageError("--dry-run cannot be used with --json");
627
- }
628
713
  if (json && summary) {
629
714
  exitUsageError("--summary cannot be used with --json");
630
715
  }
631
716
  try {
632
- const input = await readInput2(useStdin ? void 0 : file);
717
+ const input = await readInput2(useStdin ? void 0 : file, {
718
+ forceStdin: stdinFlag
719
+ });
633
720
  const result = sanitizeLog2(input, { strict, dryRun });
634
721
  if (dryRun) {
722
+ if (json) {
723
+ writeOutput2(result, { json: true });
724
+ if (failOnDetect && result.matches.length > 0) {
725
+ process.exit(1);
726
+ }
727
+ process.exit(0);
728
+ }
635
729
  renderDryRunReport(result.matches);
636
730
  if (failOnDetect && result.matches.length > 0) {
637
731
  process.exit(1);
package/package.json CHANGED
@@ -1,35 +1,63 @@
1
1
  {
2
2
  "name": "logshield-cli",
3
- "version": "0.4.4",
3
+ "version": "0.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "commonjs",
6
6
  "bin": {
7
7
  "logshield": "dist/cli/index.cjs"
8
8
  },
9
9
  "files": [
10
- "dist",
10
+ "dist/cli",
11
11
  "README.md",
12
12
  "CHANGELOG.md",
13
13
  "LICENSE"
14
14
  ],
15
15
  "scripts": {
16
16
  "build": "node scripts/build-cli.cjs",
17
- "build:web": "vite build --outDir dist-web",
18
17
  "build:blog": "node scripts/build-blog.js",
19
- "dev:web": "vite",
20
18
  "typecheck": "tsc -p tsconfig.core.json && tsc -p tsconfig.cli.json --noEmit",
21
19
  "pretest": "npm run build",
22
20
  "test": "vitest",
21
+ "lint": "npm run typecheck",
22
+ "clean:dist": "node scripts/clean-dist.mjs",
23
+ "prepack": "npm run clean:dist && npm run build",
24
+ "pack:check": "npm pack --dry-run",
25
+ "release:check": "npm run prepublish:check && npm run pack:check",
23
26
  "prepublish:check": "npm run typecheck && npm test",
24
- "prepublishOnly": "npm run prepublish:check"
27
+ "prepublishOnly": "npm run prepublish:check",
28
+ "dev": "node scripts/dev-docs-server.mjs",
29
+ "dev:lan": "node scripts/dev-docs-server.mjs --host 0.0.0.0"
25
30
  },
26
31
  "devDependencies": {
27
32
  "@types/node": "^25.0.3",
28
- "autoprefixer": "^10.4.23",
29
33
  "esbuild": "^0.25.0",
30
- "postcss": "^8.5.6",
31
- "tailwindcss": "^3.4.19",
32
34
  "typescript": "^5.9.3",
33
35
  "vitest": "^4.0.0"
36
+ },
37
+ "description": "Deterministic, rule-based CLI to sanitize secrets from logs. No AI. No cloud. No config.",
38
+ "keywords": [
39
+ "log-sanitization",
40
+ "secret-detection",
41
+ "redaction",
42
+ "security",
43
+ "devops",
44
+ "cli",
45
+ "deterministic",
46
+ "no-ai"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/afria85/LogShield.git"
51
+ },
52
+ "homepage": "https://logshield.dev",
53
+ "bugs": {
54
+ "url": "https://github.com/afria85/LogShield/issues"
55
+ },
56
+ "funding": {
57
+ "type": "github",
58
+ "url": "https://github.com/sponsors/afria85"
59
+ },
60
+ "engines": {
61
+ "node": ">=18"
34
62
  }
35
63
  }
Binary file
Binary file
Binary file
@@ -1 +0,0 @@
1
- *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--color-bg-primary: 248 250 252;--color-bg-secondary: 241 245 249;--color-bg-card: 255 255 255;--color-text-primary: 15 23 42;--color-text-secondary: 71 85 105;--color-text-muted: 148 163 184;--color-border: 226 232 240;--color-accent: 59 130 246;--color-accent-hover: 37 99 235;--gradient-start: 239 246 255;--gradient-mid: 238 242 255;--gradient-end: 248 250 252}*{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}@media (prefers-color-scheme: dark){*{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}}html{scroll-behavior:smooth}body{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}@media (prefers-color-scheme: dark){body{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}}body{margin:0;padding:0;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:JetBrains Mono,Fira Code,source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}@media (prefers-color-scheme: dark){::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}}::-webkit-scrollbar-thumb{border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(203 213 225 / var(--tw-bg-opacity, 1))}@media (prefers-color-scheme: dark){::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity, 1))}}::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(148 163 184 / var(--tw-bg-opacity, 1))}@media (prefers-color-scheme: dark){::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity, 1))}}::-moz-selection{background-color:#3b82f64d;--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}::selection{background-color:#3b82f64d;--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}@media (prefers-color-scheme: dark){::-moz-selection{--tw-text-opacity: 1;color:rgb(219 234 254 / var(--tw-text-opacity, 1))}::selection{--tw-text-opacity: 1;color:rgb(219 234 254 / var(--tw-text-opacity, 1))}}@keyframes fadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes pulseGlow{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 20px 5px #3b82f633}}@keyframes gradientShift{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}@keyframes shine{0%{background-position:-200% center}to{background-position:200% center}}@keyframes bounceUp{0%,to{transform:translateY(0)}50%{transform:translateY(-4px)}}@media print{.no-print{display:none!important}body{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}}:root{--bg: #0f1115;--panel: #151922;--border: #252a36;--text: #e6e8eb;--muted: #9aa1ad;--accent: #ff4d4f}*{box-sizing:border-box}body{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;background:var(--bg);color:var(--text)}.ls-root{min-height:100vh;display:flex;flex-direction:column}.ls-header{padding:16px 24px;border-bottom:1px solid var(--border)}.ls-header h1{margin:0;font-size:20px}.ls-header p{margin:4px 0 0;color:var(--muted);font-size:13px}.ls-controls{display:flex;gap:16px;align-items:center;padding:12px 24px;border-bottom:1px solid var(--border);font-size:13px}.ls-controls label{display:flex;align-items:center;gap:6px;cursor:pointer}.ls-controls button{margin-left:auto;background:transparent;border:1px solid var(--border);color:var(--text);padding:6px 12px;cursor:pointer}.ls-controls button:hover{border-color:var(--accent)}.ls-main{flex:1;display:grid;grid-template-columns:1fr 1fr}.ls-input{width:100%;height:100%;resize:none;border:none;outline:none;padding:16px;background:var(--panel);color:var(--text);border-right:1px solid var(--border);font-size:13px;line-height:1.5}.ls-output{padding:16px;background:#0c0f14;font-size:13px;line-height:1.5;white-space:pre-wrap;overflow:auto}.ls-highlight{background:#ff4d4f40;color:#fff}.ls-footer{padding:8px 24px;border-top:1px solid var(--border);font-size:12px;color:var(--muted)}