@aarunyaapps/env-redact 1.0.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/dist/index.d.mts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +221 -0
- package/dist/index.mjs +189 -0
- package/package.json +31 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type EnvLine = {
|
|
2
|
+
kind: "blank";
|
|
3
|
+
raw: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "comment";
|
|
6
|
+
raw: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "entry";
|
|
9
|
+
raw: string;
|
|
10
|
+
key: string;
|
|
11
|
+
value: string;
|
|
12
|
+
sensitive: boolean;
|
|
13
|
+
sep: "=" | ": ";
|
|
14
|
+
commentPrefix?: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "invalid";
|
|
17
|
+
raw: string;
|
|
18
|
+
};
|
|
19
|
+
type DiffLine = {
|
|
20
|
+
changed: false;
|
|
21
|
+
content: string;
|
|
22
|
+
} | {
|
|
23
|
+
changed: true;
|
|
24
|
+
before: string;
|
|
25
|
+
after: string;
|
|
26
|
+
};
|
|
27
|
+
interface RedactOptions {
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
}
|
|
30
|
+
declare function parseEnv(content: string): EnvLine[];
|
|
31
|
+
declare function buildSanitized(lines: EnvLine[], opts?: RedactOptions): string;
|
|
32
|
+
declare function buildExample(lines: EnvLine[]): string;
|
|
33
|
+
declare function buildDiff(lines: EnvLine[]): DiffLine[];
|
|
34
|
+
declare function countSensitive(lines: EnvLine[]): number;
|
|
35
|
+
declare function countTotal(lines: EnvLine[]): number;
|
|
36
|
+
declare function isSensitiveKey(key: string): boolean;
|
|
37
|
+
declare function isSensitiveValue(value: string): boolean;
|
|
38
|
+
|
|
39
|
+
export { type DiffLine, type EnvLine, type RedactOptions, buildDiff, buildExample, buildSanitized, countSensitive, countTotal, isSensitiveKey, isSensitiveValue, parseEnv };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type EnvLine = {
|
|
2
|
+
kind: "blank";
|
|
3
|
+
raw: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "comment";
|
|
6
|
+
raw: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "entry";
|
|
9
|
+
raw: string;
|
|
10
|
+
key: string;
|
|
11
|
+
value: string;
|
|
12
|
+
sensitive: boolean;
|
|
13
|
+
sep: "=" | ": ";
|
|
14
|
+
commentPrefix?: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "invalid";
|
|
17
|
+
raw: string;
|
|
18
|
+
};
|
|
19
|
+
type DiffLine = {
|
|
20
|
+
changed: false;
|
|
21
|
+
content: string;
|
|
22
|
+
} | {
|
|
23
|
+
changed: true;
|
|
24
|
+
before: string;
|
|
25
|
+
after: string;
|
|
26
|
+
};
|
|
27
|
+
interface RedactOptions {
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
}
|
|
30
|
+
declare function parseEnv(content: string): EnvLine[];
|
|
31
|
+
declare function buildSanitized(lines: EnvLine[], opts?: RedactOptions): string;
|
|
32
|
+
declare function buildExample(lines: EnvLine[]): string;
|
|
33
|
+
declare function buildDiff(lines: EnvLine[]): DiffLine[];
|
|
34
|
+
declare function countSensitive(lines: EnvLine[]): number;
|
|
35
|
+
declare function countTotal(lines: EnvLine[]): number;
|
|
36
|
+
declare function isSensitiveKey(key: string): boolean;
|
|
37
|
+
declare function isSensitiveValue(value: string): boolean;
|
|
38
|
+
|
|
39
|
+
export { type DiffLine, type EnvLine, type RedactOptions, buildDiff, buildExample, buildSanitized, countSensitive, countTotal, isSensitiveKey, isSensitiveValue, parseEnv };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
buildDiff: () => buildDiff,
|
|
24
|
+
buildExample: () => buildExample,
|
|
25
|
+
buildSanitized: () => buildSanitized,
|
|
26
|
+
countSensitive: () => countSensitive,
|
|
27
|
+
countTotal: () => countTotal,
|
|
28
|
+
isSensitiveKey: () => isSensitiveKey,
|
|
29
|
+
isSensitiveValue: () => isSensitiveValue,
|
|
30
|
+
parseEnv: () => parseEnv
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
|
+
var SENSITIVE_KEY_PATTERNS = [
|
|
34
|
+
/SECRET/i,
|
|
35
|
+
/PASSWORD/i,
|
|
36
|
+
/PASSWD/i,
|
|
37
|
+
/PASS$/i,
|
|
38
|
+
/_KEY$/i,
|
|
39
|
+
/^API_KEY/i,
|
|
40
|
+
/TOKEN/i,
|
|
41
|
+
/AUTH/i,
|
|
42
|
+
/CREDENTIAL/i,
|
|
43
|
+
/PRIVATE/i,
|
|
44
|
+
/SIGNING/i,
|
|
45
|
+
/ENCRYPTION/i,
|
|
46
|
+
/DATABASE_URL/i,
|
|
47
|
+
/DB_URL/i,
|
|
48
|
+
/_DSN$/i,
|
|
49
|
+
/CONNECTION_STRING/i,
|
|
50
|
+
/MONGODB_URI/i,
|
|
51
|
+
/REDIS_URL/i,
|
|
52
|
+
/STRIPE_/i,
|
|
53
|
+
/OPENAI_/i,
|
|
54
|
+
/ANTHROPIC_/i,
|
|
55
|
+
/GEMINI_/i,
|
|
56
|
+
/GITHUB_TOKEN/i,
|
|
57
|
+
/GH_TOKEN/i,
|
|
58
|
+
/AWS_SECRET/i,
|
|
59
|
+
/AWS_ACCESS_KEY/i,
|
|
60
|
+
/AZURE_/i,
|
|
61
|
+
/GCP_/i,
|
|
62
|
+
/GOOGLE_/i,
|
|
63
|
+
/TWILIO_/i,
|
|
64
|
+
/SENDGRID_/i,
|
|
65
|
+
/MAILGUN_/i,
|
|
66
|
+
/DISCORD_TOKEN/i,
|
|
67
|
+
/SLACK_TOKEN/i,
|
|
68
|
+
/WEBHOOK_SECRET/i,
|
|
69
|
+
/HMAC/i,
|
|
70
|
+
/SALT$/i,
|
|
71
|
+
/CERTIFICATE/i,
|
|
72
|
+
/CERT$/i,
|
|
73
|
+
/PEM$/i,
|
|
74
|
+
/SSH_/i
|
|
75
|
+
];
|
|
76
|
+
var SENSITIVE_VALUE_PATTERNS = [
|
|
77
|
+
/^[A-Za-z0-9+/]{32,}={0,2}$/,
|
|
78
|
+
/^[0-9a-f]{32,}$/i,
|
|
79
|
+
/^sk_/,
|
|
80
|
+
/^pk_live/,
|
|
81
|
+
/^rk_/,
|
|
82
|
+
/^eyJ/,
|
|
83
|
+
/^ghp_/,
|
|
84
|
+
/^github_pat_/,
|
|
85
|
+
/^gho_/,
|
|
86
|
+
/^ghu_/,
|
|
87
|
+
/^[A-Z0-9]{20,}$/,
|
|
88
|
+
/-----BEGIN/,
|
|
89
|
+
/^[A-Za-z0-9_-]{40,}$/
|
|
90
|
+
];
|
|
91
|
+
function cleanValue(raw) {
|
|
92
|
+
return raw.trim().replace(/,$/, "").replace(/^(['"])(.*)\1$/, "$2");
|
|
93
|
+
}
|
|
94
|
+
function isValueSensitive(key, rawValue) {
|
|
95
|
+
const val = cleanValue(rawValue);
|
|
96
|
+
if (!val) return false;
|
|
97
|
+
for (const p of SENSITIVE_KEY_PATTERNS) if (p.test(key)) return true;
|
|
98
|
+
if (val.length >= 32) {
|
|
99
|
+
for (const p of SENSITIVE_VALUE_PATTERNS) if (p.test(val)) return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
function detectSeparator(lines) {
|
|
104
|
+
let colonCount = 0, equalsCount = 0;
|
|
105
|
+
for (const line of lines.slice(0, 30)) {
|
|
106
|
+
const t = line.trim().replace(/^\/\/\s*/, "");
|
|
107
|
+
if (!t || t.startsWith("#")) continue;
|
|
108
|
+
if (/^[A-Z_][A-Z0-9_]*\s*=/.test(t)) equalsCount++;
|
|
109
|
+
else if (/^[A-Z_][A-Z0-9_]*\s*:/.test(t)) colonCount++;
|
|
110
|
+
}
|
|
111
|
+
return colonCount > equalsCount ? "colon" : "=";
|
|
112
|
+
}
|
|
113
|
+
function tryParseKeyValue(inner) {
|
|
114
|
+
const colonM = inner.match(/^([A-Z_][A-Z0-9_]*)\s*:\s*(.*)$/i);
|
|
115
|
+
if (colonM) return { key: colonM[1], value: colonM[2].trim(), sep: ": " };
|
|
116
|
+
const eqIdx = inner.indexOf("=");
|
|
117
|
+
if (eqIdx !== -1) {
|
|
118
|
+
const key = inner.slice(0, eqIdx).trim();
|
|
119
|
+
if (/^[A-Z_][A-Z0-9_]*$/i.test(key)) return { key, value: inner.slice(eqIdx + 1), sep: "=" };
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
function parseEnv(content) {
|
|
124
|
+
const rawLines = content.split("\n");
|
|
125
|
+
const sep = detectSeparator(rawLines);
|
|
126
|
+
const normalised = [];
|
|
127
|
+
if (sep === "colon") {
|
|
128
|
+
let i = 0;
|
|
129
|
+
while (i < rawLines.length) {
|
|
130
|
+
const line = rawLines[i];
|
|
131
|
+
if (/^[A-Z_][A-Z0-9_]*\s*:\s*$/.test(line.trim()) && i + 1 < rawLines.length) {
|
|
132
|
+
normalised.push({ raw: line + "\n" + rawLines[i + 1], joined: line.trim() + " " + rawLines[i + 1].trim() });
|
|
133
|
+
i += 2;
|
|
134
|
+
} else {
|
|
135
|
+
normalised.push({ raw: line, joined: line });
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
rawLines.forEach((r) => normalised.push({ raw: r, joined: r }));
|
|
141
|
+
}
|
|
142
|
+
return normalised.map(({ raw, joined }) => {
|
|
143
|
+
const line = joined.trim();
|
|
144
|
+
if (line === "") return { kind: "blank", raw };
|
|
145
|
+
if (line.startsWith("#")) return { kind: "comment", raw };
|
|
146
|
+
if (line.startsWith("//")) {
|
|
147
|
+
const m = line.match(/^(\/\/\s*)(.*)$/);
|
|
148
|
+
if (m) {
|
|
149
|
+
const kv = tryParseKeyValue(m[2].trim());
|
|
150
|
+
if (kv && isValueSensitive(kv.key, kv.value)) {
|
|
151
|
+
return { kind: "entry", raw, key: kv.key, value: kv.value, sep: kv.sep, sensitive: true, commentPrefix: m[1] };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { kind: "comment", raw };
|
|
155
|
+
}
|
|
156
|
+
let key, value;
|
|
157
|
+
const entrySep = sep === "colon" ? ": " : "=";
|
|
158
|
+
if (sep === "colon") {
|
|
159
|
+
const m = line.match(/^([A-Z_][A-Z0-9_]*)\s*:\s*(.*)$/i);
|
|
160
|
+
if (!m) return { kind: "invalid", raw };
|
|
161
|
+
key = m[1].trim();
|
|
162
|
+
value = m[2].trim();
|
|
163
|
+
} else {
|
|
164
|
+
const eqIndex = line.indexOf("=");
|
|
165
|
+
if (eqIndex === -1) return { kind: "invalid", raw };
|
|
166
|
+
key = line.slice(0, eqIndex).trim();
|
|
167
|
+
value = line.slice(eqIndex + 1);
|
|
168
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) return { kind: "invalid", raw };
|
|
169
|
+
}
|
|
170
|
+
return { kind: "entry", raw, key, value, sep: entrySep, sensitive: isValueSensitive(key, value) };
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function buildSanitized(lines, opts = {}) {
|
|
174
|
+
const placeholder = opts.placeholder ?? "REDACTED";
|
|
175
|
+
return lines.map((line) => {
|
|
176
|
+
if (line.kind === "entry" && line.sensitive) {
|
|
177
|
+
return `${line.commentPrefix ?? ""}${line.key}${line.sep}${placeholder}`;
|
|
178
|
+
}
|
|
179
|
+
return line.raw;
|
|
180
|
+
}).join("\n");
|
|
181
|
+
}
|
|
182
|
+
function buildExample(lines) {
|
|
183
|
+
return lines.map((line) => {
|
|
184
|
+
if (line.kind === "entry") return `${line.commentPrefix ?? ""}${line.key}${line.sep}`;
|
|
185
|
+
return line.raw;
|
|
186
|
+
}).join("\n");
|
|
187
|
+
}
|
|
188
|
+
function buildDiff(lines) {
|
|
189
|
+
return lines.map((line) => {
|
|
190
|
+
if (line.kind === "entry" && line.sensitive) {
|
|
191
|
+
const prefix = line.commentPrefix ?? "";
|
|
192
|
+
return { changed: true, before: `${prefix}${line.key}${line.sep}${line.value}`, after: `${prefix}${line.key}${line.sep}REDACTED` };
|
|
193
|
+
}
|
|
194
|
+
return { changed: false, content: line.raw };
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function countSensitive(lines) {
|
|
198
|
+
return lines.filter((l) => l.kind === "entry" && l.sensitive).length;
|
|
199
|
+
}
|
|
200
|
+
function countTotal(lines) {
|
|
201
|
+
return lines.filter((l) => l.kind === "entry").length;
|
|
202
|
+
}
|
|
203
|
+
function isSensitiveKey(key) {
|
|
204
|
+
return SENSITIVE_KEY_PATTERNS.some((p) => p.test(key));
|
|
205
|
+
}
|
|
206
|
+
function isSensitiveValue(value) {
|
|
207
|
+
const val = cleanValue(value);
|
|
208
|
+
if (val.length < 32) return false;
|
|
209
|
+
return SENSITIVE_VALUE_PATTERNS.some((p) => p.test(val));
|
|
210
|
+
}
|
|
211
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
212
|
+
0 && (module.exports = {
|
|
213
|
+
buildDiff,
|
|
214
|
+
buildExample,
|
|
215
|
+
buildSanitized,
|
|
216
|
+
countSensitive,
|
|
217
|
+
countTotal,
|
|
218
|
+
isSensitiveKey,
|
|
219
|
+
isSensitiveValue,
|
|
220
|
+
parseEnv
|
|
221
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var SENSITIVE_KEY_PATTERNS = [
|
|
3
|
+
/SECRET/i,
|
|
4
|
+
/PASSWORD/i,
|
|
5
|
+
/PASSWD/i,
|
|
6
|
+
/PASS$/i,
|
|
7
|
+
/_KEY$/i,
|
|
8
|
+
/^API_KEY/i,
|
|
9
|
+
/TOKEN/i,
|
|
10
|
+
/AUTH/i,
|
|
11
|
+
/CREDENTIAL/i,
|
|
12
|
+
/PRIVATE/i,
|
|
13
|
+
/SIGNING/i,
|
|
14
|
+
/ENCRYPTION/i,
|
|
15
|
+
/DATABASE_URL/i,
|
|
16
|
+
/DB_URL/i,
|
|
17
|
+
/_DSN$/i,
|
|
18
|
+
/CONNECTION_STRING/i,
|
|
19
|
+
/MONGODB_URI/i,
|
|
20
|
+
/REDIS_URL/i,
|
|
21
|
+
/STRIPE_/i,
|
|
22
|
+
/OPENAI_/i,
|
|
23
|
+
/ANTHROPIC_/i,
|
|
24
|
+
/GEMINI_/i,
|
|
25
|
+
/GITHUB_TOKEN/i,
|
|
26
|
+
/GH_TOKEN/i,
|
|
27
|
+
/AWS_SECRET/i,
|
|
28
|
+
/AWS_ACCESS_KEY/i,
|
|
29
|
+
/AZURE_/i,
|
|
30
|
+
/GCP_/i,
|
|
31
|
+
/GOOGLE_/i,
|
|
32
|
+
/TWILIO_/i,
|
|
33
|
+
/SENDGRID_/i,
|
|
34
|
+
/MAILGUN_/i,
|
|
35
|
+
/DISCORD_TOKEN/i,
|
|
36
|
+
/SLACK_TOKEN/i,
|
|
37
|
+
/WEBHOOK_SECRET/i,
|
|
38
|
+
/HMAC/i,
|
|
39
|
+
/SALT$/i,
|
|
40
|
+
/CERTIFICATE/i,
|
|
41
|
+
/CERT$/i,
|
|
42
|
+
/PEM$/i,
|
|
43
|
+
/SSH_/i
|
|
44
|
+
];
|
|
45
|
+
var SENSITIVE_VALUE_PATTERNS = [
|
|
46
|
+
/^[A-Za-z0-9+/]{32,}={0,2}$/,
|
|
47
|
+
/^[0-9a-f]{32,}$/i,
|
|
48
|
+
/^sk_/,
|
|
49
|
+
/^pk_live/,
|
|
50
|
+
/^rk_/,
|
|
51
|
+
/^eyJ/,
|
|
52
|
+
/^ghp_/,
|
|
53
|
+
/^github_pat_/,
|
|
54
|
+
/^gho_/,
|
|
55
|
+
/^ghu_/,
|
|
56
|
+
/^[A-Z0-9]{20,}$/,
|
|
57
|
+
/-----BEGIN/,
|
|
58
|
+
/^[A-Za-z0-9_-]{40,}$/
|
|
59
|
+
];
|
|
60
|
+
function cleanValue(raw) {
|
|
61
|
+
return raw.trim().replace(/,$/, "").replace(/^(['"])(.*)\1$/, "$2");
|
|
62
|
+
}
|
|
63
|
+
function isValueSensitive(key, rawValue) {
|
|
64
|
+
const val = cleanValue(rawValue);
|
|
65
|
+
if (!val) return false;
|
|
66
|
+
for (const p of SENSITIVE_KEY_PATTERNS) if (p.test(key)) return true;
|
|
67
|
+
if (val.length >= 32) {
|
|
68
|
+
for (const p of SENSITIVE_VALUE_PATTERNS) if (p.test(val)) return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
function detectSeparator(lines) {
|
|
73
|
+
let colonCount = 0, equalsCount = 0;
|
|
74
|
+
for (const line of lines.slice(0, 30)) {
|
|
75
|
+
const t = line.trim().replace(/^\/\/\s*/, "");
|
|
76
|
+
if (!t || t.startsWith("#")) continue;
|
|
77
|
+
if (/^[A-Z_][A-Z0-9_]*\s*=/.test(t)) equalsCount++;
|
|
78
|
+
else if (/^[A-Z_][A-Z0-9_]*\s*:/.test(t)) colonCount++;
|
|
79
|
+
}
|
|
80
|
+
return colonCount > equalsCount ? "colon" : "=";
|
|
81
|
+
}
|
|
82
|
+
function tryParseKeyValue(inner) {
|
|
83
|
+
const colonM = inner.match(/^([A-Z_][A-Z0-9_]*)\s*:\s*(.*)$/i);
|
|
84
|
+
if (colonM) return { key: colonM[1], value: colonM[2].trim(), sep: ": " };
|
|
85
|
+
const eqIdx = inner.indexOf("=");
|
|
86
|
+
if (eqIdx !== -1) {
|
|
87
|
+
const key = inner.slice(0, eqIdx).trim();
|
|
88
|
+
if (/^[A-Z_][A-Z0-9_]*$/i.test(key)) return { key, value: inner.slice(eqIdx + 1), sep: "=" };
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function parseEnv(content) {
|
|
93
|
+
const rawLines = content.split("\n");
|
|
94
|
+
const sep = detectSeparator(rawLines);
|
|
95
|
+
const normalised = [];
|
|
96
|
+
if (sep === "colon") {
|
|
97
|
+
let i = 0;
|
|
98
|
+
while (i < rawLines.length) {
|
|
99
|
+
const line = rawLines[i];
|
|
100
|
+
if (/^[A-Z_][A-Z0-9_]*\s*:\s*$/.test(line.trim()) && i + 1 < rawLines.length) {
|
|
101
|
+
normalised.push({ raw: line + "\n" + rawLines[i + 1], joined: line.trim() + " " + rawLines[i + 1].trim() });
|
|
102
|
+
i += 2;
|
|
103
|
+
} else {
|
|
104
|
+
normalised.push({ raw: line, joined: line });
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
rawLines.forEach((r) => normalised.push({ raw: r, joined: r }));
|
|
110
|
+
}
|
|
111
|
+
return normalised.map(({ raw, joined }) => {
|
|
112
|
+
const line = joined.trim();
|
|
113
|
+
if (line === "") return { kind: "blank", raw };
|
|
114
|
+
if (line.startsWith("#")) return { kind: "comment", raw };
|
|
115
|
+
if (line.startsWith("//")) {
|
|
116
|
+
const m = line.match(/^(\/\/\s*)(.*)$/);
|
|
117
|
+
if (m) {
|
|
118
|
+
const kv = tryParseKeyValue(m[2].trim());
|
|
119
|
+
if (kv && isValueSensitive(kv.key, kv.value)) {
|
|
120
|
+
return { kind: "entry", raw, key: kv.key, value: kv.value, sep: kv.sep, sensitive: true, commentPrefix: m[1] };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { kind: "comment", raw };
|
|
124
|
+
}
|
|
125
|
+
let key, value;
|
|
126
|
+
const entrySep = sep === "colon" ? ": " : "=";
|
|
127
|
+
if (sep === "colon") {
|
|
128
|
+
const m = line.match(/^([A-Z_][A-Z0-9_]*)\s*:\s*(.*)$/i);
|
|
129
|
+
if (!m) return { kind: "invalid", raw };
|
|
130
|
+
key = m[1].trim();
|
|
131
|
+
value = m[2].trim();
|
|
132
|
+
} else {
|
|
133
|
+
const eqIndex = line.indexOf("=");
|
|
134
|
+
if (eqIndex === -1) return { kind: "invalid", raw };
|
|
135
|
+
key = line.slice(0, eqIndex).trim();
|
|
136
|
+
value = line.slice(eqIndex + 1);
|
|
137
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) return { kind: "invalid", raw };
|
|
138
|
+
}
|
|
139
|
+
return { kind: "entry", raw, key, value, sep: entrySep, sensitive: isValueSensitive(key, value) };
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function buildSanitized(lines, opts = {}) {
|
|
143
|
+
const placeholder = opts.placeholder ?? "REDACTED";
|
|
144
|
+
return lines.map((line) => {
|
|
145
|
+
if (line.kind === "entry" && line.sensitive) {
|
|
146
|
+
return `${line.commentPrefix ?? ""}${line.key}${line.sep}${placeholder}`;
|
|
147
|
+
}
|
|
148
|
+
return line.raw;
|
|
149
|
+
}).join("\n");
|
|
150
|
+
}
|
|
151
|
+
function buildExample(lines) {
|
|
152
|
+
return lines.map((line) => {
|
|
153
|
+
if (line.kind === "entry") return `${line.commentPrefix ?? ""}${line.key}${line.sep}`;
|
|
154
|
+
return line.raw;
|
|
155
|
+
}).join("\n");
|
|
156
|
+
}
|
|
157
|
+
function buildDiff(lines) {
|
|
158
|
+
return lines.map((line) => {
|
|
159
|
+
if (line.kind === "entry" && line.sensitive) {
|
|
160
|
+
const prefix = line.commentPrefix ?? "";
|
|
161
|
+
return { changed: true, before: `${prefix}${line.key}${line.sep}${line.value}`, after: `${prefix}${line.key}${line.sep}REDACTED` };
|
|
162
|
+
}
|
|
163
|
+
return { changed: false, content: line.raw };
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function countSensitive(lines) {
|
|
167
|
+
return lines.filter((l) => l.kind === "entry" && l.sensitive).length;
|
|
168
|
+
}
|
|
169
|
+
function countTotal(lines) {
|
|
170
|
+
return lines.filter((l) => l.kind === "entry").length;
|
|
171
|
+
}
|
|
172
|
+
function isSensitiveKey(key) {
|
|
173
|
+
return SENSITIVE_KEY_PATTERNS.some((p) => p.test(key));
|
|
174
|
+
}
|
|
175
|
+
function isSensitiveValue(value) {
|
|
176
|
+
const val = cleanValue(value);
|
|
177
|
+
if (val.length < 32) return false;
|
|
178
|
+
return SENSITIVE_VALUE_PATTERNS.some((p) => p.test(val));
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
buildDiff,
|
|
182
|
+
buildExample,
|
|
183
|
+
buildSanitized,
|
|
184
|
+
countSensitive,
|
|
185
|
+
countTotal,
|
|
186
|
+
isSensitiveKey,
|
|
187
|
+
isSensitiveValue,
|
|
188
|
+
parseEnv
|
|
189
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aarunyaapps/env-redact",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Parse .env files and detect/redact sensitive values. Zero dependencies. Browser + Node.js compatible.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist", "README.md"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
18
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
19
|
+
"type-check": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["dotenv", "env", "secrets", "redact", "sanitize", "security", "privacy"],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/aarunyaapps/env-redact"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsup": "^8.0.0",
|
|
29
|
+
"typescript": "^5.8.3"
|
|
30
|
+
}
|
|
31
|
+
}
|