logshield-cli 0.1.0 → 0.2.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.
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __esm = (fn, res) => function __init() {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
+
35
+ // src/cli/readInput.ts
36
+ var readInput_exports = {};
37
+ __export(readInput_exports, {
38
+ readInput: () => readInput
39
+ });
40
+ async function readInput(file) {
41
+ if (file) {
42
+ if (!import_node_fs.default.existsSync(file)) {
43
+ throw new Error(`File not found: ${file}`);
44
+ }
45
+ return import_node_fs.default.readFileSync(file, "utf8");
46
+ }
47
+ if (!process.stdin.isTTY) {
48
+ return new Promise((resolve, reject) => {
49
+ let data = "";
50
+ process.stdin.setEncoding("utf8");
51
+ process.stdin.on("data", (chunk) => {
52
+ data += chunk;
53
+ });
54
+ process.stdin.on("end", () => resolve(data));
55
+ process.stdin.on("error", reject);
56
+ });
57
+ }
58
+ throw new Error("No input provided");
59
+ }
60
+ var import_node_fs;
61
+ var init_readInput = __esm({
62
+ "src/cli/readInput.ts"() {
63
+ "use strict";
64
+ import_node_fs = __toESM(require("node:fs"));
65
+ }
66
+ });
67
+
68
+ // src/cli/writeOutput.ts
69
+ var writeOutput_exports = {};
70
+ __export(writeOutput_exports, {
71
+ writeOutput: () => writeOutput
72
+ });
73
+ function writeOutput(result, opts) {
74
+ if (opts.json) {
75
+ process.stdout.write(JSON.stringify(result));
76
+ } else {
77
+ process.stdout.write(result.output);
78
+ }
79
+ }
80
+ var init_writeOutput = __esm({
81
+ "src/cli/writeOutput.ts"() {
82
+ "use strict";
83
+ }
84
+ });
85
+
86
+ // src/cli/summary.ts
87
+ var summary_exports = {};
88
+ __export(summary_exports, {
89
+ printSummary: () => printSummary
90
+ });
91
+ function printSummary(matches) {
92
+ const counter = {};
93
+ for (const m of matches) {
94
+ counter[m.rule] = (counter[m.rule] || 0) + 1;
95
+ }
96
+ process.stderr.write("LogShield Summary\n");
97
+ for (const [rule, count] of Object.entries(counter)) {
98
+ process.stderr.write(`${rule}: ${count}
99
+ `);
100
+ }
101
+ }
102
+ var init_summary = __esm({
103
+ "src/cli/summary.ts"() {
104
+ "use strict";
105
+ }
106
+ });
107
+
108
+ // src/engine/applyRules.ts
109
+ function applyRules(input, rules, ctx, matches) {
110
+ let output = input;
111
+ for (const rule of rules) {
112
+ output = output.replace(rule.pattern, (...args2) => {
113
+ const match = args2[0];
114
+ const groups = args2.slice(1, -2);
115
+ const replaced = rule.replace(match, ctx, groups);
116
+ if (replaced !== match) {
117
+ matches.push({
118
+ rule: rule.name,
119
+ value: match
120
+ });
121
+ }
122
+ return replaced;
123
+ });
124
+ }
125
+ return output;
126
+ }
127
+ var init_applyRules = __esm({
128
+ "src/engine/applyRules.ts"() {
129
+ "use strict";
130
+ }
131
+ });
132
+
133
+ // src/engine/guard.ts
134
+ function guardInput(input) {
135
+ if (!input) return "";
136
+ if (input.length > MAX_SIZE) {
137
+ throw new Error("Log size exceeds 200KB limit");
138
+ }
139
+ return input;
140
+ }
141
+ var MAX_SIZE;
142
+ var init_guard = __esm({
143
+ "src/engine/guard.ts"() {
144
+ "use strict";
145
+ MAX_SIZE = 200 * 1024;
146
+ }
147
+ });
148
+
149
+ // src/rules/tokens.ts
150
+ var tokenRules;
151
+ var init_tokens = __esm({
152
+ "src/rules/tokens.ts"() {
153
+ "use strict";
154
+ tokenRules = [
155
+ {
156
+ name: "JWT",
157
+ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
158
+ replace: () => "<REDACTED_JWT>"
159
+ },
160
+ {
161
+ name: "AUTH_BEARER",
162
+ pattern: /\bBearer\s+[A-Za-z0-9._-]+\b/g,
163
+ replace: () => "Bearer <REDACTED_TOKEN>"
164
+ },
165
+ {
166
+ name: "EMAIL",
167
+ pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
168
+ replace: () => "<REDACTED_EMAIL>"
169
+ }
170
+ ];
171
+ }
172
+ });
173
+
174
+ // src/rules/credentials.ts
175
+ var credentialRules;
176
+ var init_credentials = __esm({
177
+ "src/rules/credentials.ts"() {
178
+ "use strict";
179
+ credentialRules = [
180
+ {
181
+ name: "PASSWORD",
182
+ pattern: /\bpassword=([^\s]+)/gi,
183
+ replace: (_match, _value, _ctx) => "password=<REDACTED_PASSWORD>"
184
+ },
185
+ {
186
+ name: "API_KEY",
187
+ pattern: /\bapiKey=([A-Za-z0-9_\-]{16,})\b/g,
188
+ replace: () => "<REDACTED_API_KEY>"
189
+ }
190
+ ];
191
+ }
192
+ });
193
+
194
+ // src/rules/cloud.ts
195
+ var cloudRules;
196
+ var init_cloud = __esm({
197
+ "src/rules/cloud.ts"() {
198
+ "use strict";
199
+ cloudRules = [
200
+ {
201
+ name: "AWS_ACCESS_KEY",
202
+ pattern: /\bAKIA[0-9A-Z]{16,20}\b/g,
203
+ replace: (match, { strict }) => strict ? "<REDACTED_AWS_KEY>" : match
204
+ },
205
+ {
206
+ name: "STRIPE_SECRET_KEY",
207
+ pattern: /\b(?:LS_STRIPE_(?:TEST|LIVE)_KEY_[A-Z0-9_]{10,}|sk_(?:test|live)_[A-Za-z0-9]{16,})\b/g,
208
+ replace: (match, ctx) => ctx.strict ? "<REDACTED_STRIPE_KEY>" : match
209
+ }
210
+ ];
211
+ }
212
+ });
213
+
214
+ // src/utils/luhn.ts
215
+ function isValidLuhn(input) {
216
+ const digits = input.replace(/\D/g, "");
217
+ let sum = 0;
218
+ let alt = false;
219
+ for (let i = digits.length - 1; i >= 0; i--) {
220
+ let n = parseInt(digits[i], 10);
221
+ if (alt) {
222
+ n *= 2;
223
+ if (n > 9) n -= 9;
224
+ }
225
+ sum += n;
226
+ alt = !alt;
227
+ }
228
+ return sum % 10 === 0;
229
+ }
230
+ var init_luhn = __esm({
231
+ "src/utils/luhn.ts"() {
232
+ "use strict";
233
+ }
234
+ });
235
+
236
+ // src/rules/creditCard.ts
237
+ var creditCardRules;
238
+ var init_creditCard = __esm({
239
+ "src/rules/creditCard.ts"() {
240
+ "use strict";
241
+ init_luhn();
242
+ creditCardRules = [
243
+ {
244
+ name: "CREDIT_CARD",
245
+ pattern: /\b(?:\d[ -]*?){13,19}\b/g,
246
+ replace: (match, { strict }) => {
247
+ if (!strict) return match;
248
+ return isValidLuhn(match) ? "<REDACTED_CC>" : match;
249
+ }
250
+ }
251
+ ];
252
+ }
253
+ });
254
+
255
+ // src/rules/urls.ts
256
+ var urlRules;
257
+ var init_urls = __esm({
258
+ "src/rules/urls.ts"() {
259
+ "use strict";
260
+ urlRules = [
261
+ {
262
+ name: "URL",
263
+ pattern: /\bhttps?:\/\/[^\s/$.?#].[^\s]*\b/gi,
264
+ replace: () => "<REDACTED_URL>"
265
+ }
266
+ ];
267
+ }
268
+ });
269
+
270
+ // src/rules/custom.ts
271
+ var customRules;
272
+ var init_custom = __esm({
273
+ "src/rules/custom.ts"() {
274
+ "use strict";
275
+ customRules = [
276
+ {
277
+ name: "GENERIC_SECRET_KV",
278
+ pattern: /"(\w+)":"([^"]{12,})"/g,
279
+ replace: (match, ctx, groups) => {
280
+ if (!ctx.strict) return match;
281
+ const key = groups[0] ?? "token";
282
+ return `"${key}":"<REDACTED_SECRET>"`;
283
+ }
284
+ }
285
+ ];
286
+ }
287
+ });
288
+
289
+ // src/rules/index.ts
290
+ function normalize(rules) {
291
+ return rules.map((rule) => {
292
+ if (typeof rule.replace === "function") {
293
+ return rule;
294
+ }
295
+ const value = rule.replace;
296
+ return {
297
+ ...rule,
298
+ replace: () => value
299
+ };
300
+ });
301
+ }
302
+ var allRules;
303
+ var init_rules = __esm({
304
+ "src/rules/index.ts"() {
305
+ "use strict";
306
+ init_tokens();
307
+ init_credentials();
308
+ init_cloud();
309
+ init_creditCard();
310
+ init_urls();
311
+ init_custom();
312
+ allRules = normalize([
313
+ ...tokenRules,
314
+ ...credentialRules,
315
+ ...cloudRules,
316
+ ...creditCardRules,
317
+ ...urlRules,
318
+ ...customRules
319
+ ]);
320
+ }
321
+ });
322
+
323
+ // src/engine/sanitizeLog.ts
324
+ var sanitizeLog_exports = {};
325
+ __export(sanitizeLog_exports, {
326
+ sanitizeLog: () => sanitizeLog
327
+ });
328
+ function sanitizeLog(input, options) {
329
+ guardInput(input);
330
+ if (!input) {
331
+ return { output: "", matches: [] };
332
+ }
333
+ const ctx = {
334
+ strict: Boolean(options?.strict)
335
+ };
336
+ const matches = [];
337
+ const output = applyRules(input, allRules, ctx, matches);
338
+ return { output, matches };
339
+ }
340
+ var init_sanitizeLog = __esm({
341
+ "src/engine/sanitizeLog.ts"() {
342
+ "use strict";
343
+ init_applyRules();
344
+ init_guard();
345
+ init_rules();
346
+ }
347
+ });
348
+
349
+ // src/cli/index.ts
350
+ var { readInput: readInput2 } = (init_readInput(), __toCommonJS(readInput_exports));
351
+ var { writeOutput: writeOutput2 } = (init_writeOutput(), __toCommonJS(writeOutput_exports));
352
+ var { printSummary: printSummary2 } = (init_summary(), __toCommonJS(summary_exports));
353
+ var { sanitizeLog: sanitizeLog2 } = (init_sanitizeLog(), __toCommonJS(sanitizeLog_exports));
354
+ var args = process.argv.slice(2);
355
+ function hasFlag(flag) {
356
+ return args.includes(flag);
357
+ }
358
+ function getFileArg() {
359
+ const file = args[1];
360
+ if (!file || file.startsWith("--")) return void 0;
361
+ return file;
362
+ }
363
+ async function main() {
364
+ if (hasFlag("--help") || args.length === 0) {
365
+ process.stdout.write(`Usage: logshield scan [file]
366
+
367
+ Options:
368
+ --strict
369
+ --json
370
+ --summary
371
+ --version
372
+ --help
373
+ `);
374
+ process.exit(0);
375
+ }
376
+ if (hasFlag("--version")) {
377
+ process.stdout.write("logshield v0.2.0\n");
378
+ process.exit(0);
379
+ }
380
+ const command = args[0];
381
+ if (command !== "scan") {
382
+ process.stderr.write("Unknown command\n");
383
+ process.exit(1);
384
+ }
385
+ const strict = hasFlag("--strict");
386
+ const json = hasFlag("--json");
387
+ const summary = hasFlag("--summary");
388
+ const file = getFileArg();
389
+ try {
390
+ const input = await readInput2(file);
391
+ const result = sanitizeLog2(input, { strict });
392
+ writeOutput2(result, { json });
393
+ if (summary) {
394
+ printSummary2(result.matches);
395
+ }
396
+ } catch (err) {
397
+ process.stderr.write(err && err.message || "Unexpected error");
398
+ process.stderr.write("\n");
399
+ process.exit(2);
400
+ }
401
+ }
402
+ main();
Binary file
Binary file
Binary file