UNPKG

@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
/** * 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; } }