env-sentinel
Version:
Zero-dependency tool that auto-validates .env files against schema.env, with optional fallback and secure warnings.
57 lines (56 loc) • 1.88 kB
JavaScript
const SHELL_SPECIAL_CHARS = ['$', '`', '!', '*', ';', '|', '&', '>', '<', '?', '(', ')', '{', '}', '[', ']', '='];
export function noUnescapedShellCharsCheck(lineNumber, lineContent) {
const equalIndex = lineContent.indexOf('=');
if (equalIndex === -1)
return;
const rawKey = lineContent.slice(0, equalIndex).trim();
let value = lineContent.slice(equalIndex + 1);
let cleanValue = '';
let inEscape = false;
for (let i = 0; i < value.length; i++) {
if (inEscape) {
cleanValue += value[i];
inEscape = false;
continue;
}
if (value[i] === '\\') {
inEscape = true;
cleanValue += value[i];
continue;
}
if (value[i] === '#') {
break;
}
cleanValue += value[i];
}
value = cleanValue.trim();
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
return;
}
if (value.includes('\\n') || value.includes('\\r')) {
return;
}
const valueWithoutEnvSubs = value.replace(/\$\{[^}]+}/g, (match) => {
return ' '.repeat(match.length);
});
for (let i = 0; i < valueWithoutEnvSubs.length; i++) {
const ch = valueWithoutEnvSubs[i];
if (SHELL_SPECIAL_CHARS.includes(ch)) {
let escapeCount = 0;
let j = i - 1;
while (j >= 0 && valueWithoutEnvSubs[j] === '\\') {
escapeCount++;
j--;
}
if (escapeCount % 2 === 0) {
return {
line: lineNumber,
issue: `Unescaped shell special character '${ch}' in value of "${rawKey}"`,
content: lineContent,
severity: 'error',
};
}
}
}
return;
}