logshield-cli 0.1.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/README.md +119 -0
- package/dist/cli/cli/index.cjs +54 -0
- package/dist/cli/cli/index.js +57 -0
- package/dist/cli/cli/readInput.js +27 -0
- package/dist/cli/cli/summary.js +13 -0
- package/dist/cli/cli/writeOutput.js +11 -0
- package/dist/cli/engine/applyRules.js +21 -0
- package/dist/cli/engine/guard.js +12 -0
- package/dist/cli/engine/sanitizeLog.js +18 -0
- package/dist/cli/index.js +52 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/readInput.js +15 -0
- package/dist/cli/rules/cloud.js +15 -0
- package/dist/cli/rules/credentials.js +15 -0
- package/dist/cli/rules/creditCard.js +15 -0
- package/dist/cli/rules/custom.js +15 -0
- package/dist/cli/rules/index.js +38 -0
- package/dist/cli/rules/tokens.js +20 -0
- package/dist/cli/rules/types.js +2 -0
- package/dist/cli/rules/urls.js +10 -0
- package/dist/cli/summary.js +10 -0
- package/dist/cli/utils/luhn.js +19 -0
- package/dist/cli/writeOutput.js +8 -0
- package/dist/engine/applyRules.js +27 -0
- package/dist/engine/engine/applyRules.d.ts +5 -0
- package/dist/engine/engine/applyRules.js +30 -0
- package/dist/engine/engine/guard.d.ts +1 -0
- package/dist/engine/engine/guard.js +12 -0
- package/dist/engine/engine/sanitizeLog.d.ts +11 -0
- package/dist/engine/engine/sanitizeLog.js +18 -0
- package/dist/engine/guard.js +9 -0
- package/dist/engine/rules/cloud.d.ts +2 -0
- package/dist/engine/rules/cloud.js +15 -0
- package/dist/engine/rules/credentials.d.ts +2 -0
- package/dist/engine/rules/credentials.js +15 -0
- package/dist/engine/rules/creditCard.d.ts +2 -0
- package/dist/engine/rules/creditCard.js +15 -0
- package/dist/engine/rules/custom.d.ts +2 -0
- package/dist/engine/rules/custom.js +14 -0
- package/dist/engine/rules/index.d.ts +11 -0
- package/dist/engine/rules/index.js +38 -0
- package/dist/engine/rules/tokens.d.ts +2 -0
- package/dist/engine/rules/tokens.js +20 -0
- package/dist/engine/rules/types.d.ts +9 -0
- package/dist/engine/rules/types.js +2 -0
- package/dist/engine/rules/urls.d.ts +2 -0
- package/dist/engine/rules/urls.js +10 -0
- package/dist/engine/sanitizeLog.js +15 -0
- package/dist/engine/utils/luhn.d.ts +1 -0
- package/dist/engine/utils/luhn.js +19 -0
- package/dist/rules/cloud.js +12 -0
- package/dist/rules/credentials.js +12 -0
- package/dist/rules/creditCard.js +12 -0
- package/dist/rules/custom.js +11 -0
- package/dist/rules/index.js +35 -0
- package/dist/rules/tokens.js +17 -0
- package/dist/rules/types.js +1 -0
- package/dist/rules/urls.js +7 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# LogShield
|
|
2
|
+
|
|
3
|
+
**Safe log sanitization for developers.**
|
|
4
|
+
|
|
5
|
+
LogShield is a lightweight, developer-focused utility to automatically redact secrets, tokens, credentials, and sensitive data from logs before they are stored, shared, or shipped.
|
|
6
|
+
|
|
7
|
+
It is designed to be:
|
|
8
|
+
- **Deterministic** – predictable behavior, no AI, no guesswork
|
|
9
|
+
- **Safe by default** – minimal false positives in default mode
|
|
10
|
+
- **Strict when needed** – aggressive redaction via `strict` mode
|
|
11
|
+
- **Composable** – rule-based engine, easy to extend
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why LogShield?
|
|
16
|
+
|
|
17
|
+
Logs are copied everywhere: CI output, bug reports, Slack, tickets, LLM prompts.
|
|
18
|
+
|
|
19
|
+
One leaked key is enough to:
|
|
20
|
+
- Compromise production systems
|
|
21
|
+
- Invalidate compliance (GDPR, SOC2)
|
|
22
|
+
- Burn trust instantly
|
|
23
|
+
|
|
24
|
+
LogShield exists to solve one problem extremely well:
|
|
25
|
+
|
|
26
|
+
> **Make logs safe to share.**
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- Redacts common secrets:
|
|
33
|
+
- API keys
|
|
34
|
+
- Passwords
|
|
35
|
+
- JWT tokens
|
|
36
|
+
- Bearer tokens
|
|
37
|
+
- Stripe keys
|
|
38
|
+
- Cloud credentials (AWS, etc.)
|
|
39
|
+
- Credit cards (Luhn-validated)
|
|
40
|
+
- Two modes:
|
|
41
|
+
- **Default**: conservative, low false positives
|
|
42
|
+
- **Strict**: aggressive, security-first
|
|
43
|
+
- Snapshot-tested, contract-tested
|
|
44
|
+
- Zero runtime dependencies
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install logshield
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Basic
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { sanitizeLog } from "logshield";
|
|
62
|
+
|
|
63
|
+
const result = sanitizeLog("password=supersecret");
|
|
64
|
+
console.log(result.output);
|
|
65
|
+
// password=<REDACTED_PASSWORD>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Strict mode
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
sanitizeLog(input, { strict: true });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## API
|
|
77
|
+
|
|
78
|
+
### `sanitizeLog(input: string, options?)`
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
{
|
|
84
|
+
output: string;
|
|
85
|
+
matches: {
|
|
86
|
+
rule: string;
|
|
87
|
+
match: string;
|
|
88
|
+
}[];
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `output` – sanitized log string
|
|
93
|
+
- `matches` – what was redacted and why (for auditing/debugging)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Design Principles
|
|
98
|
+
|
|
99
|
+
- **No heuristics** – explicit rules only
|
|
100
|
+
- **No mutation magic** – transparent replacements
|
|
101
|
+
- **Locked behavior** – breaking changes require intent
|
|
102
|
+
|
|
103
|
+
This is a boring utility by design.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Roadmap
|
|
108
|
+
|
|
109
|
+
- CLI (`logshield scan file.log`)
|
|
110
|
+
- GitHub Action
|
|
111
|
+
- Pre-commit hook
|
|
112
|
+
- Pro ruleset (enterprise patterns)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
119
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const readInput_1 = require("./readInput");
|
|
5
|
+
const writeOutput_1 = require("./writeOutput");
|
|
6
|
+
const summary_1 = require("./summary");
|
|
7
|
+
const sanitizeLog_1 = require("../engine/sanitizeLog");
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
function hasFlag(flag) {
|
|
10
|
+
return args.includes(flag);
|
|
11
|
+
}
|
|
12
|
+
function getFileArg() {
|
|
13
|
+
return args.find((a) => !a.startsWith("--"));
|
|
14
|
+
}
|
|
15
|
+
async function main() {
|
|
16
|
+
if (hasFlag("--help")) {
|
|
17
|
+
console.log(`Usage: logshield scan [file]
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--strict
|
|
21
|
+
--json
|
|
22
|
+
--summary
|
|
23
|
+
--version
|
|
24
|
+
--help
|
|
25
|
+
`);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
if (hasFlag("--version")) {
|
|
29
|
+
console.log("logshield v0.1.0");
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
const command = args[0];
|
|
33
|
+
if (command !== "scan") {
|
|
34
|
+
console.error("Unknown command");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const strict = hasFlag("--strict");
|
|
38
|
+
const json = hasFlag("--json");
|
|
39
|
+
const summary = hasFlag("--summary");
|
|
40
|
+
const file = getFileArg();
|
|
41
|
+
try {
|
|
42
|
+
const input = await (0, readInput_1.readInput)(file);
|
|
43
|
+
const result = (0, sanitizeLog_1.sanitizeLog)(input, { strict });
|
|
44
|
+
(0, writeOutput_1.writeOutput)(result, { json });
|
|
45
|
+
if (summary) {
|
|
46
|
+
(0, summary_1.printSummary)(result.matches);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error(err.message || "Unexpected error");
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
main();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const readInput_1 = require("./readInput");
|
|
5
|
+
const writeOutput_1 = require("./writeOutput");
|
|
6
|
+
const summary_1 = require("./summary");
|
|
7
|
+
const sanitizeLog_1 = require("../engine/sanitizeLog");
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
function hasFlag(flag) {
|
|
10
|
+
return args.includes(flag);
|
|
11
|
+
}
|
|
12
|
+
function getFileArg() {
|
|
13
|
+
const file = args[1];
|
|
14
|
+
if (!file || file.startsWith("--"))
|
|
15
|
+
return undefined;
|
|
16
|
+
return file;
|
|
17
|
+
}
|
|
18
|
+
async function main() {
|
|
19
|
+
if (hasFlag("--help")) {
|
|
20
|
+
console.log(`Usage: logshield scan [file]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--strict
|
|
24
|
+
--json
|
|
25
|
+
--summary
|
|
26
|
+
--version
|
|
27
|
+
--help
|
|
28
|
+
`);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
if (hasFlag("--version")) {
|
|
32
|
+
console.log("logshield v0.1.0");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
const command = args[0];
|
|
36
|
+
if (command !== "scan") {
|
|
37
|
+
console.error("Unknown command");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const strict = hasFlag("--strict");
|
|
41
|
+
const json = hasFlag("--json");
|
|
42
|
+
const summary = hasFlag("--summary");
|
|
43
|
+
const file = getFileArg();
|
|
44
|
+
try {
|
|
45
|
+
const input = await (0, readInput_1.readInput)(file);
|
|
46
|
+
const result = (0, sanitizeLog_1.sanitizeLog)(input, { strict });
|
|
47
|
+
(0, writeOutput_1.writeOutput)(result, { json });
|
|
48
|
+
if (summary) {
|
|
49
|
+
(0, summary_1.printSummary)(result.matches);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.error(err.message || "Unexpected error");
|
|
54
|
+
process.exit(2);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
main();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.readInput = readInput;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
async function readInput(file) {
|
|
9
|
+
if (file) {
|
|
10
|
+
if (!node_fs_1.default.existsSync(file)) {
|
|
11
|
+
throw new Error(`File not found: ${file}`);
|
|
12
|
+
}
|
|
13
|
+
return node_fs_1.default.readFileSync(file, "utf8");
|
|
14
|
+
}
|
|
15
|
+
if (!process.stdin.isTTY) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
let data = "";
|
|
18
|
+
process.stdin.setEncoding("utf8");
|
|
19
|
+
process.stdin.on("data", chunk => {
|
|
20
|
+
data += chunk;
|
|
21
|
+
});
|
|
22
|
+
process.stdin.on("end", () => resolve(data));
|
|
23
|
+
process.stdin.on("error", reject);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
throw new Error("No input provided");
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printSummary = printSummary;
|
|
4
|
+
function printSummary(matches) {
|
|
5
|
+
const counter = {};
|
|
6
|
+
for (const m of matches) {
|
|
7
|
+
counter[m.rule] = (counter[m.rule] || 0) + 1;
|
|
8
|
+
}
|
|
9
|
+
process.stderr.write("LogShield Summary\n");
|
|
10
|
+
for (const [rule, count] of Object.entries(counter)) {
|
|
11
|
+
process.stderr.write(`${rule}: ${count}\n`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeOutput = writeOutput;
|
|
4
|
+
function writeOutput(result, opts) {
|
|
5
|
+
if (opts.json) {
|
|
6
|
+
process.stdout.write(JSON.stringify(result));
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
process.stdout.write(result.output);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyRules = applyRules;
|
|
4
|
+
function applyRules(input, rules, ctx, matches) {
|
|
5
|
+
let output = input;
|
|
6
|
+
for (const rule of rules) {
|
|
7
|
+
output = output.replace(rule.pattern, (...args) => {
|
|
8
|
+
const match = args[0];
|
|
9
|
+
const groups = args.slice(1, -2);
|
|
10
|
+
const replaced = rule.replace(match, ctx, groups);
|
|
11
|
+
if (replaced !== match) {
|
|
12
|
+
matches.push({
|
|
13
|
+
rule: rule.name,
|
|
14
|
+
value: match,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return replaced;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return output;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.guardInput = guardInput;
|
|
4
|
+
const MAX_SIZE = 200 * 1024; // 200KB
|
|
5
|
+
function guardInput(input) {
|
|
6
|
+
if (!input)
|
|
7
|
+
return "";
|
|
8
|
+
if (input.length > MAX_SIZE) {
|
|
9
|
+
throw new Error("Log size exceeds 200KB limit");
|
|
10
|
+
}
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeLog = sanitizeLog;
|
|
4
|
+
const applyRules_1 = require("./applyRules");
|
|
5
|
+
const guard_1 = require("./guard");
|
|
6
|
+
const rules_1 = require("../rules");
|
|
7
|
+
function sanitizeLog(input, options) {
|
|
8
|
+
(0, guard_1.guardInput)(input);
|
|
9
|
+
if (!input) {
|
|
10
|
+
return { output: "", matches: [] };
|
|
11
|
+
}
|
|
12
|
+
const ctx = {
|
|
13
|
+
strict: Boolean(options?.strict),
|
|
14
|
+
};
|
|
15
|
+
const matches = [];
|
|
16
|
+
const output = (0, applyRules_1.applyRules)(input, rules_1.allRules, ctx, matches);
|
|
17
|
+
return { output, matches };
|
|
18
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readInput } from "./readInput";
|
|
3
|
+
import { writeOutput } from "./writeOutput";
|
|
4
|
+
import { printSummary } from "./summary";
|
|
5
|
+
import { sanitizeLog } from "../engine/sanitizeLog";
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
function hasFlag(flag) {
|
|
8
|
+
return args.includes(flag);
|
|
9
|
+
}
|
|
10
|
+
function getFileArg() {
|
|
11
|
+
return args.find((a) => !a.startsWith("--"));
|
|
12
|
+
}
|
|
13
|
+
async function main() {
|
|
14
|
+
if (hasFlag("--help")) {
|
|
15
|
+
console.log(`Usage: logshield scan [file]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--strict
|
|
19
|
+
--json
|
|
20
|
+
--summary
|
|
21
|
+
--version
|
|
22
|
+
--help
|
|
23
|
+
`);
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
if (hasFlag("--version")) {
|
|
27
|
+
console.log("logshield v0.1.0");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
const command = args[0];
|
|
31
|
+
if (command !== "scan") {
|
|
32
|
+
console.error("Unknown command");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
const strict = hasFlag("--strict");
|
|
36
|
+
const json = hasFlag("--json");
|
|
37
|
+
const summary = hasFlag("--summary");
|
|
38
|
+
const file = getFileArg();
|
|
39
|
+
try {
|
|
40
|
+
const input = await readInput(file);
|
|
41
|
+
const result = sanitizeLog(input, { strict });
|
|
42
|
+
writeOutput(result, { json });
|
|
43
|
+
if (summary) {
|
|
44
|
+
printSummary(result.matches);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
console.error(err.message || "Unexpected error");
|
|
49
|
+
process.exit(2);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
main();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
export function readInput(file) {
|
|
3
|
+
if (file) {
|
|
4
|
+
if (!fs.existsSync(file)) {
|
|
5
|
+
throw new Error(`File not found: ${file}`);
|
|
6
|
+
}
|
|
7
|
+
return Promise.resolve(fs.readFileSync(file, "utf8"));
|
|
8
|
+
}
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
let data = "";
|
|
11
|
+
process.stdin.setEncoding("utf8");
|
|
12
|
+
process.stdin.on("data", (chunk) => (data += chunk));
|
|
13
|
+
process.stdin.on("end", () => resolve(data));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cloudRules = void 0;
|
|
4
|
+
exports.cloudRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "AWS_ACCESS_KEY",
|
|
7
|
+
pattern: /\bAKIA[0-9A-Z]{16,20}\b/g,
|
|
8
|
+
replace: (match, { strict }) => strict ? "<REDACTED_AWS_KEY>" : match,
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "STRIPE_SECRET_KEY",
|
|
12
|
+
pattern: /\b(?:LS_STRIPE_(?:TEST|LIVE)_KEY_[A-Z0-9_]{10,}|sk_(?:test|live)_[A-Za-z0-9]{16,})\b/g,
|
|
13
|
+
replace: (match, ctx) => ctx.strict ? "<REDACTED_STRIPE_KEY>" : match,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.credentialRules = void 0;
|
|
4
|
+
exports.credentialRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "PASSWORD",
|
|
7
|
+
pattern: /\bpassword=([^\s]+)/gi,
|
|
8
|
+
replace: (_match, _value, _ctx) => "password=<REDACTED_PASSWORD>",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "API_KEY",
|
|
12
|
+
pattern: /\bapiKey=([A-Za-z0-9_\-]{16,})\b/g,
|
|
13
|
+
replace: () => "<REDACTED_API_KEY>",
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.creditCardRules = void 0;
|
|
4
|
+
const luhn_1 = require("../utils/luhn");
|
|
5
|
+
exports.creditCardRules = [
|
|
6
|
+
{
|
|
7
|
+
name: "CREDIT_CARD",
|
|
8
|
+
pattern: /\b(?:\d[ -]*?){13,19}\b/g,
|
|
9
|
+
replace: (match, { strict }) => {
|
|
10
|
+
if (!strict)
|
|
11
|
+
return match;
|
|
12
|
+
return (0, luhn_1.isValidLuhn)(match) ? "<REDACTED_CC>" : match;
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.customRules = void 0;
|
|
4
|
+
exports.customRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "GENERIC_SECRET_KV",
|
|
7
|
+
pattern: /"(\w+)":"([^"]{12,})"/g,
|
|
8
|
+
replace: (match, ctx, groups) => {
|
|
9
|
+
if (!ctx.strict)
|
|
10
|
+
return match;
|
|
11
|
+
const key = groups[0] ?? "token";
|
|
12
|
+
return `"${key}":"<REDACTED_SECRET>"`;
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.allRules = void 0;
|
|
4
|
+
const tokens_1 = require("./tokens");
|
|
5
|
+
const credentials_1 = require("./credentials");
|
|
6
|
+
const cloud_1 = require("./cloud");
|
|
7
|
+
const creditCard_1 = require("./creditCard");
|
|
8
|
+
const urls_1 = require("./urls");
|
|
9
|
+
const custom_1 = require("./custom");
|
|
10
|
+
function normalize(rules) {
|
|
11
|
+
return rules.map((rule) => {
|
|
12
|
+
if (typeof rule.replace === "function") {
|
|
13
|
+
return rule;
|
|
14
|
+
}
|
|
15
|
+
const value = rule.replace;
|
|
16
|
+
return {
|
|
17
|
+
...rule,
|
|
18
|
+
replace: () => value,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* ORDER IS CONTRACT:
|
|
24
|
+
* 1. tokenRules ? JWT first
|
|
25
|
+
* 2. credential ? password, api key
|
|
26
|
+
* 3. cloud
|
|
27
|
+
* 4. credit card
|
|
28
|
+
* 5. urls
|
|
29
|
+
* 6. custom
|
|
30
|
+
*/
|
|
31
|
+
exports.allRules = normalize([
|
|
32
|
+
...tokens_1.tokenRules,
|
|
33
|
+
...credentials_1.credentialRules,
|
|
34
|
+
...cloud_1.cloudRules,
|
|
35
|
+
...creditCard_1.creditCardRules,
|
|
36
|
+
...urls_1.urlRules,
|
|
37
|
+
...custom_1.customRules,
|
|
38
|
+
]);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tokenRules = void 0;
|
|
4
|
+
exports.tokenRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "JWT",
|
|
7
|
+
pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
8
|
+
replace: () => "<REDACTED_JWT>",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "AUTH_BEARER",
|
|
12
|
+
pattern: /\bBearer\s+[A-Za-z0-9._-]+\b/g,
|
|
13
|
+
replace: () => "Bearer <REDACTED_TOKEN>",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "EMAIL",
|
|
17
|
+
pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
|
|
18
|
+
replace: () => "<REDACTED_EMAIL>",
|
|
19
|
+
},
|
|
20
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function printSummary(matches) {
|
|
2
|
+
const counter = {};
|
|
3
|
+
for (const m of matches) {
|
|
4
|
+
counter[m.rule] = (counter[m.rule] || 0) + 1;
|
|
5
|
+
}
|
|
6
|
+
process.stderr.write("LogShield Summary\n");
|
|
7
|
+
for (const [rule, count] of Object.entries(counter)) {
|
|
8
|
+
process.stderr.write(`${rule}: ${count}\n`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isValidLuhn = isValidLuhn;
|
|
4
|
+
function isValidLuhn(input) {
|
|
5
|
+
const digits = input.replace(/\D/g, "");
|
|
6
|
+
let sum = 0;
|
|
7
|
+
let alt = false;
|
|
8
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
9
|
+
let n = parseInt(digits[i], 10);
|
|
10
|
+
if (alt) {
|
|
11
|
+
n *= 2;
|
|
12
|
+
if (n > 9)
|
|
13
|
+
n -= 9;
|
|
14
|
+
}
|
|
15
|
+
sum += n;
|
|
16
|
+
alt = !alt;
|
|
17
|
+
}
|
|
18
|
+
return sum % 10 === 0;
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function applyRules(input, rules, ctx, matches) {
|
|
2
|
+
let output = input;
|
|
3
|
+
for (const rule of rules) {
|
|
4
|
+
output = output.replace(rule.pattern, (...args) => {
|
|
5
|
+
const match = args[0];
|
|
6
|
+
const groups = args.slice(1, -2);
|
|
7
|
+
let replaced;
|
|
8
|
+
if (rule.replace.length === 1) {
|
|
9
|
+
replaced = rule.replace(match);
|
|
10
|
+
}
|
|
11
|
+
else if (rule.replace.length === 2) {
|
|
12
|
+
replaced = rule.replace(match, ctx);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
replaced = rule.replace(match, groups[0], ctx);
|
|
16
|
+
}
|
|
17
|
+
if (replaced !== match) {
|
|
18
|
+
matches.push({
|
|
19
|
+
rule: rule.name,
|
|
20
|
+
value: match,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return replaced;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return output;
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyRules = applyRules;
|
|
4
|
+
function applyRules(input, rules, ctx, matches) {
|
|
5
|
+
let output = input;
|
|
6
|
+
for (const rule of rules) {
|
|
7
|
+
output = output.replace(rule.pattern, (...args) => {
|
|
8
|
+
const match = args[0];
|
|
9
|
+
const groups = args.slice(1, -2);
|
|
10
|
+
let replaced;
|
|
11
|
+
if (rule.replace.length === 1) {
|
|
12
|
+
replaced = rule.replace(match);
|
|
13
|
+
}
|
|
14
|
+
else if (rule.replace.length === 2) {
|
|
15
|
+
replaced = rule.replace(match, ctx);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
replaced = rule.replace(match, groups[0], ctx);
|
|
19
|
+
}
|
|
20
|
+
if (replaced !== match) {
|
|
21
|
+
matches.push({
|
|
22
|
+
rule: rule.name,
|
|
23
|
+
value: match,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return replaced;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return output;
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function guardInput(input: string): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.guardInput = guardInput;
|
|
4
|
+
const MAX_SIZE = 200 * 1024; // 200KB
|
|
5
|
+
function guardInput(input) {
|
|
6
|
+
if (!input)
|
|
7
|
+
return "";
|
|
8
|
+
if (input.length > MAX_SIZE) {
|
|
9
|
+
throw new Error("Log size exceeds 200KB limit");
|
|
10
|
+
}
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeLog = sanitizeLog;
|
|
4
|
+
const applyRules_1 = require("./applyRules");
|
|
5
|
+
const guard_1 = require("./guard");
|
|
6
|
+
const rules_1 = require("../rules");
|
|
7
|
+
function sanitizeLog(input, options) {
|
|
8
|
+
(0, guard_1.guardInput)(input);
|
|
9
|
+
if (!input) {
|
|
10
|
+
return { output: "", matches: [] };
|
|
11
|
+
}
|
|
12
|
+
const ctx = {
|
|
13
|
+
strict: Boolean(options?.strict),
|
|
14
|
+
};
|
|
15
|
+
const matches = [];
|
|
16
|
+
const output = (0, applyRules_1.applyRules)(input, rules_1.allRules, ctx, matches);
|
|
17
|
+
return { output, matches };
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cloudRules = void 0;
|
|
4
|
+
exports.cloudRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "AWS_ACCESS_KEY",
|
|
7
|
+
pattern: /\bAKIA[0-9A-Z]{16,20}\b/g,
|
|
8
|
+
replace: (match, { strict }) => strict ? "<REDACTED_AWS_KEY>" : match,
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "STRIPE_SECRET_KEY",
|
|
12
|
+
pattern: /\b(?:LS_STRIPE_(?:TEST|LIVE)_KEY_[A-Z0-9_]{10,}|sk_(?:test|live)_[A-Za-z0-9]{16,})\b/g,
|
|
13
|
+
replace: (match, ctx) => ctx.strict ? "<REDACTED_STRIPE_KEY>" : match,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.credentialRules = void 0;
|
|
4
|
+
exports.credentialRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "PASSWORD",
|
|
7
|
+
pattern: /\bpassword=([^\s]+)/gi,
|
|
8
|
+
replace: (_match, _value, _ctx) => "password=<REDACTED_PASSWORD>",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "API_KEY",
|
|
12
|
+
pattern: /\bapiKey=([A-Za-z0-9_\-]{16,})\b/g,
|
|
13
|
+
replace: () => "<REDACTED_API_KEY>",
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.creditCardRules = void 0;
|
|
4
|
+
const luhn_1 = require("../utils/luhn");
|
|
5
|
+
exports.creditCardRules = [
|
|
6
|
+
{
|
|
7
|
+
name: "CREDIT_CARD",
|
|
8
|
+
pattern: /\b(?:\d[ -]*?){13,19}\b/g,
|
|
9
|
+
replace: (match, { strict }) => {
|
|
10
|
+
if (!strict)
|
|
11
|
+
return match;
|
|
12
|
+
return (0, luhn_1.isValidLuhn)(match) ? "<REDACTED_CC>" : match;
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.customRules = void 0;
|
|
4
|
+
exports.customRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "GENERIC_SECRET_KV",
|
|
7
|
+
pattern: /"(\w+)":"([^"]{12,})"/g,
|
|
8
|
+
replace: (match, value, ctx) => {
|
|
9
|
+
if (!ctx.strict)
|
|
10
|
+
return match;
|
|
11
|
+
return `"${value ? value : "token"}":"<REDACTED_SECRET>"`;
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.allRules = void 0;
|
|
4
|
+
const tokens_1 = require("./tokens");
|
|
5
|
+
const credentials_1 = require("./credentials");
|
|
6
|
+
const cloud_1 = require("./cloud");
|
|
7
|
+
const creditCard_1 = require("./creditCard");
|
|
8
|
+
const urls_1 = require("./urls");
|
|
9
|
+
const custom_1 = require("./custom");
|
|
10
|
+
function normalize(rules) {
|
|
11
|
+
return rules.map((rule) => {
|
|
12
|
+
if (typeof rule.replace === "function") {
|
|
13
|
+
return rule;
|
|
14
|
+
}
|
|
15
|
+
const value = rule.replace;
|
|
16
|
+
return {
|
|
17
|
+
...rule,
|
|
18
|
+
replace: () => value,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* ORDER IS CONTRACT:
|
|
24
|
+
* 1. tokenRules ? JWT first
|
|
25
|
+
* 2. credential ? password, api key
|
|
26
|
+
* 3. cloud
|
|
27
|
+
* 4. credit card
|
|
28
|
+
* 5. urls
|
|
29
|
+
* 6. custom
|
|
30
|
+
*/
|
|
31
|
+
exports.allRules = normalize([
|
|
32
|
+
...tokens_1.tokenRules,
|
|
33
|
+
...credentials_1.credentialRules,
|
|
34
|
+
...cloud_1.cloudRules,
|
|
35
|
+
...creditCard_1.creditCardRules,
|
|
36
|
+
...urls_1.urlRules,
|
|
37
|
+
...custom_1.customRules,
|
|
38
|
+
]);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tokenRules = void 0;
|
|
4
|
+
exports.tokenRules = [
|
|
5
|
+
{
|
|
6
|
+
name: "JWT",
|
|
7
|
+
pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
8
|
+
replace: "<REDACTED_JWT>",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "AUTH_BEARER",
|
|
12
|
+
pattern: /Bearer\s+[A-Za-z0-9._-]+/gi,
|
|
13
|
+
replace: "Bearer <REDACTED_TOKEN>",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "EMAIL",
|
|
17
|
+
pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
|
|
18
|
+
replace: "<REDACTED_EMAIL>",
|
|
19
|
+
},
|
|
20
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { applyRules } from "./applyRules";
|
|
2
|
+
import { guardInput } from "./guard";
|
|
3
|
+
import { allRules } from "../rules";
|
|
4
|
+
export function sanitizeLog(input, options) {
|
|
5
|
+
guardInput(input);
|
|
6
|
+
if (!input) {
|
|
7
|
+
return { output: "", matches: [] };
|
|
8
|
+
}
|
|
9
|
+
const ctx = {
|
|
10
|
+
strict: Boolean(options?.strict),
|
|
11
|
+
};
|
|
12
|
+
const matches = [];
|
|
13
|
+
const output = applyRules(input, allRules, ctx, matches);
|
|
14
|
+
return { output, matches };
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isValidLuhn(input: string): boolean;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isValidLuhn = isValidLuhn;
|
|
4
|
+
function isValidLuhn(input) {
|
|
5
|
+
const digits = input.replace(/\D/g, "");
|
|
6
|
+
let sum = 0;
|
|
7
|
+
let alt = false;
|
|
8
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
9
|
+
let n = parseInt(digits[i], 10);
|
|
10
|
+
if (alt) {
|
|
11
|
+
n *= 2;
|
|
12
|
+
if (n > 9)
|
|
13
|
+
n -= 9;
|
|
14
|
+
}
|
|
15
|
+
sum += n;
|
|
16
|
+
alt = !alt;
|
|
17
|
+
}
|
|
18
|
+
return sum % 10 === 0;
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const cloudRules = [
|
|
2
|
+
{
|
|
3
|
+
name: "AWS_ACCESS_KEY",
|
|
4
|
+
pattern: /\bAKIA[0-9A-Z]{16,20}\b/g,
|
|
5
|
+
replace: (match, { strict }) => strict ? "<REDACTED_AWS_KEY>" : match,
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
name: "STRIPE_SECRET_KEY",
|
|
9
|
+
pattern: /\b(?:LS_STRIPE_(?:TEST|LIVE)_KEY_[A-Z0-9_]{10,}|sk_(?:test|live)_[A-Za-z0-9]{16,})\b/g,
|
|
10
|
+
replace: (match, ctx) => ctx.strict ? "<REDACTED_STRIPE_KEY>" : match,
|
|
11
|
+
},
|
|
12
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const credentialRules = [
|
|
2
|
+
{
|
|
3
|
+
name: "PASSWORD",
|
|
4
|
+
pattern: /\bpassword=([^\s]+)/gi,
|
|
5
|
+
replace: (_match, _value, _ctx) => "password=<REDACTED_PASSWORD>",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
name: "API_KEY",
|
|
9
|
+
pattern: /\bapiKey=([A-Za-z0-9_\-]{16,})\b/g,
|
|
10
|
+
replace: () => "<REDACTED_API_KEY>",
|
|
11
|
+
},
|
|
12
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isValidLuhn } from "../utils/luhn";
|
|
2
|
+
export const creditCardRules = [
|
|
3
|
+
{
|
|
4
|
+
name: "CREDIT_CARD",
|
|
5
|
+
pattern: /\b(?:\d[ -]*?){13,19}\b/g,
|
|
6
|
+
replace: (match, { strict }) => {
|
|
7
|
+
if (!strict)
|
|
8
|
+
return match;
|
|
9
|
+
return isValidLuhn(match) ? "<REDACTED_CC>" : match;
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { tokenRules } from "./tokens";
|
|
2
|
+
import { credentialRules } from "./credentials";
|
|
3
|
+
import { cloudRules } from "./cloud";
|
|
4
|
+
import { creditCardRules } from "./creditCard";
|
|
5
|
+
import { urlRules } from "./urls";
|
|
6
|
+
import { customRules } from "./custom";
|
|
7
|
+
function normalize(rules) {
|
|
8
|
+
return rules.map((rule) => {
|
|
9
|
+
if (typeof rule.replace === "function") {
|
|
10
|
+
return rule;
|
|
11
|
+
}
|
|
12
|
+
const value = rule.replace;
|
|
13
|
+
return {
|
|
14
|
+
...rule,
|
|
15
|
+
replace: () => value,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* ORDER IS CONTRACT:
|
|
21
|
+
* 1. tokenRules ? JWT first
|
|
22
|
+
* 2. credential ? password, api key
|
|
23
|
+
* 3. cloud
|
|
24
|
+
* 4. credit card
|
|
25
|
+
* 5. urls
|
|
26
|
+
* 6. custom
|
|
27
|
+
*/
|
|
28
|
+
export const allRules = normalize([
|
|
29
|
+
...tokenRules,
|
|
30
|
+
...credentialRules,
|
|
31
|
+
...cloudRules,
|
|
32
|
+
...creditCardRules,
|
|
33
|
+
...urlRules,
|
|
34
|
+
...customRules,
|
|
35
|
+
]);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const tokenRules = [
|
|
2
|
+
{
|
|
3
|
+
name: "JWT",
|
|
4
|
+
pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
5
|
+
replace: "<REDACTED_JWT>",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
name: "AUTH_BEARER",
|
|
9
|
+
pattern: /Bearer\s+[A-Za-z0-9._-]+/gi,
|
|
10
|
+
replace: "Bearer <REDACTED_TOKEN>",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: "EMAIL",
|
|
14
|
+
pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
|
|
15
|
+
replace: "<REDACTED_EMAIL>",
|
|
16
|
+
},
|
|
17
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "logshield-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"logshield": "dist/cli/cli/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/cli",
|
|
10
|
+
"dist/engine",
|
|
11
|
+
"dist/rules",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "vite build",
|
|
17
|
+
"test": "vitest"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"vite": "^5.4.0",
|
|
22
|
+
"vitest": "^4.0.15",
|
|
23
|
+
"terser": "^5.31.3"
|
|
24
|
+
}
|
|
25
|
+
}
|