60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import { normalizeRepositoryRelativePath } from "./repository-file-inventory.ts";
|
|
|
|
export function parseSecretScanIncludedPaths(
|
|
value: unknown,
|
|
): readonly string[] | null {
|
|
if (value === undefined) return null;
|
|
if (
|
|
!Array.isArray(value) ||
|
|
value.length === 0 ||
|
|
value.some(
|
|
(entry) =>
|
|
typeof entry !== "string" ||
|
|
entry.length === 0 ||
|
|
entry.trim() !== entry,
|
|
)
|
|
) {
|
|
throw new TypeError(
|
|
"includedPaths must be a non-empty array of repository-relative POSIX paths",
|
|
);
|
|
}
|
|
const normalized = value.map((entry) =>
|
|
normalizeRepositoryRelativePath(entry as string, "included path"),
|
|
);
|
|
if (new Set(normalized).size !== normalized.length) {
|
|
throw new TypeError("includedPaths must not contain duplicate paths");
|
|
}
|
|
return Object.freeze(normalized);
|
|
}
|
|
|
|
export function selectIncludedInventoryFiles(
|
|
inventoryFiles: readonly string[],
|
|
includedPaths: readonly string[] | null,
|
|
): readonly string[] {
|
|
if (includedPaths === null) return Object.freeze([...inventoryFiles]);
|
|
const validatedIncludedPaths = parseSecretScanIncludedPaths(includedPaths);
|
|
if (validatedIncludedPaths === null) {
|
|
throw new TypeError("includedPaths unexpectedly omitted");
|
|
}
|
|
for (const includedPath of validatedIncludedPaths) {
|
|
if (
|
|
!inventoryFiles.some(
|
|
(file) =>
|
|
file === includedPath || file.startsWith(`${includedPath}/`),
|
|
)
|
|
) {
|
|
throw new Error(
|
|
`secret scan included path matches no inventory file: ${includedPath}`,
|
|
);
|
|
}
|
|
}
|
|
return Object.freeze(
|
|
inventoryFiles.filter((file) =>
|
|
validatedIncludedPaths.some(
|
|
(includedPath) =>
|
|
file === includedPath || file.startsWith(`${includedPath}/`),
|
|
),
|
|
),
|
|
);
|
|
}
|