@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.
106 lines (88 loc) • 3.69 kB
JavaScript
/**
* GitHub Security Advisories Provider
* GitHub's security advisory database
* API: https://docs.github.com/en/rest/security-advisories
*/
import { VulnerabilityProvider } from './provider.interface.mjs';
import { fetchJsonFromUrl } from '../utils/http.utils.mjs';
import { logVerbose } from '../utils/log.utils.mjs';
import { colors } from '../utils/color.utils.mjs';
import { DEFAULT_TIMEOUT_MS, DEFAULT_SEVERITY } from '../constants/app.constants.mjs';
import { INFO_MESSAGES } from '../constants/validation.constants.mjs';
export class GitHubAdvisoriesProvider extends VulnerabilityProvider {
name = 'GitHub Advisories';
getDefaultConfig() {
return {
...super.getDefaultConfig(),
enabled: true, // Enabled by default, works without token
apiUrl: 'https://api.github.com/advisories',
timeout: DEFAULT_TIMEOUT_MS,
// Optional: GitHub token for higher rate limits
// Public API: 60 requests/hour (no token required)
// Authenticated: 5000 requests/hour (token recommended for high usage)
token: null
};
}
isEnabled(config = {}) {
// GitHub works without token, so just check if explicitly disabled
const providerConfig = { ...this.getDefaultConfig(), ...config };
return providerConfig.enabled !== false;
}
async check(packageName, version = null, config = {}) {
if (!this.isEnabled(config)) {
return { found: false };
}
const providerConfig = { ...this.getDefaultConfig(), ...config };
try {
// GitHub Advisories API: Search by package name
// Note: GitHub API requires authentication for higher rate limits
// Public API: 60 requests/hour, Authenticated: 5000 requests/hour
const searchUrl = `https://api.github.com/search/advisories?q=${encodeURIComponent(`ecosystem:npm package:${packageName}`)}`;
logVerbose(colors.dim(INFO_MESSAGES.PROVIDER_CHECKING('GitHub', packageName, version)), config.logMode);
const headers = {
'Accept': 'application/vnd.github+json'
};
if (providerConfig.token) {
headers['Authorization'] = `Bearer ${providerConfig.token}`;
}
const response = await fetchJsonFromUrl(searchUrl, {
headers,
timeout: providerConfig.timeout
});
if (response && response.items && response.items.length > 0) {
// Check if version is affected
const advisory = response.items[0];
const affected = this.isVersionAffected(version, advisory);
if (affected) {
return {
found: true,
severity: advisory.severity?.toLowerCase() ?? DEFAULT_SEVERITY,
title: advisory.summary ?? advisory.ghsa_id,
source: 'GitHub Advisories',
cve: advisory.cve_id,
url: advisory.html_url ?? `https://github.com/advisories/${advisory.ghsa_id}`
};
}
}
return { found: false };
} catch (error) {
// Fail silently - don't block installation if provider is down
logVerbose(colors.dim(INFO_MESSAGES.PROVIDER_ERROR('GitHub', error.message)), config.logMode);
return { found: false };
}
}
/**
* Check if version is affected by advisory
* Simplified check - GitHub API provides affected_version_range
*/
isVersionAffected(version, advisory) {
if (!version) {
// If no version specified, assume affected
return true;
}
// GitHub provides affected_version_range, but parsing is complex
// For now, if advisory exists, consider it affected
// Note: This is a conservative approach - better to block than miss a vulnerability
return true;
}
}