core/validators/nginx.validator.ts
core/validators/nginx.validator.ts is a file in Project Scripts. 138 lines of code and 24 definitions.
import {
COMMENT_MARK,
ENGINE_DIRECTIVE,
ESCAPE,
HEADER_DIRECTIVE,
HEADER_FLAG,
IMPORT_DIRECTIVE,
POLICY_HEADER,
PRIVATE_REFERRERS,
QUOTES,
REFERRER_HEADER,
REFUSED_SOURCES,
REQUIRED_ENGINE,
TERMINATOR,
TOKEN_BOUNDARIES,
WILDCARD,
} from "#configuration/constants/nginx.constants";
import {
COMMENT_REMEDY,
ENGINE_MISSING,
ENGINE_REMEDY,
HEADERS_REMEDY,
commentsHeading,
commentsHeld,
engineHeading,
engineHeld,
headersHeading,
headersHeld,
openReferrer,
openSource,
otherEngine,
} from "#configuration/strings/nginx.strings";
import type { CheckVerdict } from "#types/validation.types";
import type { ConfigFile } from "#types/nginx.types";
const NEWLINE = "\n";
const SPACE = " ";
const opensComment = function opensComment(line: string, at: number): boolean {
return at === 0 || TOKEN_BOUNDARIES.has(line.charAt(at - 1));
};
const quoteAfter = function quoteAfter(quote: string, char: string): string {
if (quote !== "") {
return char === quote ? "" : quote;
}
return QUOTES.has(char) ? char : "";
};
const hasComment = function hasComment(line: string): boolean {
let quote = "";
for (let at = 0; at < line.length; at += 1) {
const char = line.charAt(at);
if (char === ESCAPE) {
at += 1;
} else if (quote === "" && char === COMMENT_MARK && opensComment(line, at)) {
return true;
} else {
quote = quoteAfter(quote, char);
}
}
return false;
};
export const commentVerdict = function commentVerdict(files: readonly ConfigFile[]): CheckVerdict {
const found = files.flatMap((file) =>
file.text
.split(NEWLINE)
.flatMap((line, index) => (hasComment(line) ? [` ${file.path}:${String(index + 1)}`] : [])),
);
if (found.length === 0) {
return { held: true, text: commentsHeld(files.length) };
}
return { held: false, text: [commentsHeading(found.length), ...found, COMMENT_REMEDY, ""].join(NEWLINE) };
};
const directivesOf = function directivesOf(text: string): readonly (readonly string[])[] {
return text
.split(NEWLINE)
.map((line) => line.trim())
.filter((line) => line.endsWith(TERMINATOR))
.map((line) =>
line
.slice(0, -TERMINATOR.length)
.split(SPACE)
.filter((word) => word.length > 0),
);
};
const engineFinding = function engineFinding(text: string): string | null {
const directives = directivesOf(text);
if (!directives.some((words) => words[0] === IMPORT_DIRECTIVE)) {
return null;
}
const engines = directives.filter((words) => words[0] === ENGINE_DIRECTIVE).map((words) => words[1] ?? "");
if (engines.length === 0) {
return ENGINE_MISSING;
}
const other = engines.find((engine) => engine !== REQUIRED_ENGINE);
return other === undefined ? null : otherEngine(other);
};
const headerValue = function headerValue(words: readonly string[]): string {
const value = words.slice(2).filter((word) => word !== HEADER_FLAG);
const text = value.join(SPACE);
const quoted = text.length > 1 && QUOTES.has(text.charAt(0)) && text.endsWith(text.charAt(0));
return quoted ? text.slice(1, -1) : text;
};
const sourceFindings = function sourceFindings(value: string): readonly string[] {
return value
.split(SPACE)
.map((token) => (token.endsWith(TERMINATOR) ? token.slice(0, -TERMINATOR.length) : token))
.filter((token) => REFUSED_SOURCES.has(token) || token.includes(WILDCARD))
.map(openSource);
};
const headerFindings = function headerFindings(text: string): readonly string[] {
return directivesOf(text)
.filter((words) => words[0] === HEADER_DIRECTIVE)
.flatMap((words) => {
const value = headerValue(words);
if (words[1] === POLICY_HEADER) {
return sourceFindings(value);
}
if (words[1] === REFERRER_HEADER && !PRIVATE_REFERRERS.has(value)) {
return [openReferrer(value)];
}
return [];
});
};
export const headerVerdict = function headerVerdict(files: readonly ConfigFile[]): CheckVerdict {
const found = files.flatMap((file) => headerFindings(file.text).map((finding) => ` ${file.path} ${finding}`));
if (found.length === 0) {
return { held: true, text: headersHeld(files.length) };
}
return { held: false, text: [headersHeading(found.length), ...found, HEADERS_REMEDY, ""].join(NEWLINE) };
};
export const engineVerdict = function engineVerdict(files: readonly ConfigFile[]): CheckVerdict {
const found = files.flatMap((file) => {
const finding = engineFinding(file.text);
return finding === null ? [] : [` ${file.path} ${finding}`];
});
if (found.length === 0) {
return { held: true, text: engineHeld(files.length) };
}
return { held: false, text: [engineHeading(found.length), ...found, ENGINE_REMEDY, ""].join(NEWLINE) };
};