@dreamhorizonorg/sentinel
Version:
Open-source, zero-dependency tool that blocks compromised packages BEFORE download. Built to counter supply chain and credential theft attacks like Shai-Hulud.
104 lines (86 loc) • 2.96 kB
JavaScript
/**
* Data source path resolution utilities
* Pure functions for resolving compromised packages data source paths
*/
import fs from 'fs';
import path from 'path';
import { COMPROMISED_PACKAGES_FILENAME, USER_INSTALL_DIR } from '../constants/app.constants.mjs';
import { isDirectory, pathExists } from './file.utils.mjs';
import { getProjectRoot, resolveUserPath } from './path.utils.mjs';
/**
* Get data source path from config
* Smart handling: if path is a folder, looks for compromised-packages.json inside
*/
export const getDataSourcePath = (config = {}) => {
// Priority 1: Explicit local data source (file or folder)
if (config.dataSourcePath) {
const resolvedPath = path.resolve(config.dataSourcePath);
// If it's a directory, look for standard filename
if (isDirectory(resolvedPath)) {
const jsonPath = path.join(resolvedPath, COMPROMISED_PACKAGES_FILENAME);
if (pathExists(jsonPath)) {
return jsonPath;
}
// If folder exists but JSON not found, return folder (will show error later)
return resolvedPath;
}
// If it's a file or doesn't exist yet, return as-is
return resolvedPath;
}
// Priority 2: HTTP endpoint
if (config.endpoint) {
return config.endpoint;
}
// Priority 3: Local folder (legacy support, but treat same as dataSourcePath)
if (config.localFolder) {
const resolvedPath = path.resolve(config.localFolder);
const jsonPath = path.join(resolvedPath, COMPROMISED_PACKAGES_FILENAME);
if (pathExists(jsonPath)) {
return jsonPath;
}
return resolvedPath;
}
// Priority 4: Default locations (root file pattern)
const JSON_REPO_CONFIG = path.join(getProjectRoot(), 'config', COMPROMISED_PACKAGES_FILENAME);
const JSON_USER_CONFIG = resolveUserPath(USER_INSTALL_DIR, 'config', COMPROMISED_PACKAGES_FILENAME);
if (pathExists(JSON_REPO_CONFIG)) {
return JSON_REPO_CONFIG;
}
if (pathExists(JSON_USER_CONFIG)) {
return JSON_USER_CONFIG;
}
return JSON_REPO_CONFIG; // Will show error if not found
};
/**
* Load JSON from file
*/
export const loadJsonFromFile = (filePath) => {
const content = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(content);
};
/**
* Convert JSON records to compromised map
*/
export const jsonToMap = (packages) => {
const compromisedMap = new Map();
if (!Array.isArray(packages)) {
packages = [packages];
}
for (const pkg of packages) {
const packageName = pkg.name?.trim();
if (!packageName) continue;
if (!compromisedMap.has(packageName)) {
compromisedMap.set(packageName, new Set());
}
const versions = pkg.compromisedVersions ?? [];
if (Array.isArray(versions)) {
versions.forEach(v => {
const version = String(v).trim();
if (version) {
compromisedMap.get(packageName).add(version);
}
});
}
}
return compromisedMap;
};