UNPKG

@socketsecurity/lib

Version:

Core utilities and infrastructure for Socket.dev security tools

471 lines (470 loc) 15.1 kB
import type { SpawnOptions } from './spawn'; /** * Options for GitHub API fetch requests. */ export interface GitHubFetchOptions { /** * GitHub authentication token. * If not provided, will attempt to use token from environment variables. */ token?: string | undefined; /** * Additional HTTP headers to include in the request. * Will be merged with default headers (Accept, User-Agent, Authorization). */ headers?: Record<string, string> | undefined; } /** * Error thrown when GitHub API rate limit is exceeded. * Extends the standard Error with additional rate limit information. */ export interface GitHubRateLimitError extends Error { /** HTTP status code (always 403 for rate limit errors) */ status: number; /** * Date when the rate limit will reset. * Undefined if reset time is not available in response headers. */ resetTime?: Date | undefined; } /** * Get GitHub authentication token from environment variables. * Checks multiple environment variable names in priority order. * * Environment variables checked (in order): * 1. `GITHUB_TOKEN` - Standard GitHub token variable * 2. `GH_TOKEN` - Alternative GitHub CLI token variable * 3. `SOCKET_CLI_GITHUB_TOKEN` - Socket-specific token variable * * @returns The first available GitHub token, or `undefined` if none found * * @example * ```ts * const token = getGitHubToken() * if (!token) { * console.warn('No GitHub token found') * } * ``` */ export declare function getGitHubToken(): string | undefined; /** * Fetch data from GitHub API with automatic authentication and rate limit handling. * Makes authenticated requests to the GitHub REST API with proper error handling. * * Features: * - Automatic token injection from environment if not provided * - Rate limit detection with helpful error messages * - Standard GitHub API headers (Accept, User-Agent) * - JSON response parsing * * @template T - Expected response type (defaults to `unknown`) * @param url - Full GitHub API URL (e.g., 'https://api.github.com/repos/owner/repo') * @param options - Fetch options including token and custom headers * @returns Parsed JSON response of type `T` * * @throws {GitHubRateLimitError} When API rate limit is exceeded (status 403) * @throws {Error} For other API errors with status code and message * * @example * ```ts * // Fetch repository information * interface Repo { * name: string * full_name: string * default_branch: string * } * const repo = await fetchGitHub<Repo>( * 'https://api.github.com/repos/owner/repo' * ) * console.log(`Default branch: ${repo.default_branch}`) * ``` * * @example * ```ts * // With custom token and headers * const data = await fetchGitHub( * 'https://api.github.com/user', * { * token: 'ghp_customtoken', * headers: { 'X-Custom-Header': 'value' } * } * ) * ``` * * @example * ```ts * // Handle rate limit errors * try { * await fetchGitHub('https://api.github.com/repos/owner/repo') * } catch (error) { * if (error.status === 403 && error.resetTime) { * console.error(`Rate limited until ${error.resetTime}`) * } * } * ``` */ export declare function fetchGitHub<T = unknown>(url: string, options?: GitHubFetchOptions | undefined): Promise<T>; /** * GitHub ref object returned by the API. * Represents a git reference (tag or branch). */ export interface GitHubRef { /** The object this ref points to */ object: { /** SHA of the commit or tag object */ sha: string; /** Type of object ('commit' or 'tag') */ type: string; /** API URL to fetch the full object details */ url: string; }; /** Full ref path (e.g., 'refs/tags/v1.0.0' or 'refs/heads/main') */ ref: string; /** API URL for this ref */ url: string; } /** * GitHub annotated tag object returned by the API. * Represents a git tag with metadata. */ export interface GitHubTag { /** Tag annotation message */ message: string; /** The commit this tag points to */ object: { /** SHA of the commit */ sha: string; /** Type of object (usually 'commit') */ type: string; /** API URL to fetch the commit details */ url: string; }; /** SHA of this tag object itself */ sha: string; /** Tag name (e.g., 'v1.0.0') */ tag: string; /** * Information about who created the tag. * Undefined for lightweight tags. */ tagger?: { /** Tag creation date in ISO 8601 format */ date: string; /** Tagger's email address */ email: string; /** Tagger's name */ name: string; }; /** API URL for this tag object */ url: string; } /** * GitHub commit object returned by the API. * Represents a git commit with metadata. */ export interface GitHubCommit { /** Full commit SHA */ sha: string; /** API URL for this commit */ url: string; /** Commit details */ commit: { /** Commit message */ message: string; /** Author information */ author: { /** Commit author date in ISO 8601 format */ date: string; /** Author's email address */ email: string; /** Author's name */ name: string; }; }; } /** * Options for resolving git refs to commit SHAs. */ export interface ResolveRefOptions { /** * GitHub authentication token. * If not provided, will attempt to use token from environment variables. */ token?: string | undefined; } /** * Resolve a git ref (tag, branch, or commit SHA) to its full commit SHA. * Handles tags (annotated and lightweight), branches, and commit SHAs. * Results are cached in-memory and on disk (with TTL) to minimize API calls. * * Resolution strategy: * 1. Try as a tag (refs/tags/{ref}) * 2. If tag is annotated, dereference to get the commit SHA * 3. If not a tag, try as a branch (refs/heads/{ref}) * 4. If not a branch, try as a commit SHA directly * * Caching behavior: * - In-memory cache (Map) for immediate lookups * - Persistent disk cache (cacache) for durability across runs * - Default TTL: 5 minutes * - Disable caching with `DISABLE_GITHUB_CACHE` env var * * @param owner - Repository owner (user or organization name) * @param repo - Repository name * @param ref - Git reference to resolve (tag name, branch name, or commit SHA) * @param options - Resolution options including authentication token * @returns The full commit SHA (40-character hex string) * * @throws {Error} When ref cannot be resolved after trying all strategies * @throws {GitHubRateLimitError} When API rate limit is exceeded * * @example * ```ts * // Resolve a tag to commit SHA * const sha = await resolveRefToSha('owner', 'repo', 'v1.0.0') * console.log(sha) // 'a1b2c3d4e5f6...' * ``` * * @example * ```ts * // Resolve a branch to latest commit SHA * const sha = await resolveRefToSha('owner', 'repo', 'main') * console.log(sha) // Latest commit on main branch * ``` * * @example * ```ts * // Resolve with custom token * const sha = await resolveRefToSha( * 'owner', * 'repo', * 'develop', * { token: 'ghp_customtoken' } * ) * ``` * * @example * ```ts * // Commit SHA passes through unchanged (but validates it exists) * const sha = await resolveRefToSha('owner', 'repo', 'a1b2c3d4') * console.log(sha) // Full 40-char SHA * ``` */ export declare function resolveRefToSha(owner: string, repo: string, ref: string, options?: ResolveRefOptions | undefined): Promise<string>; /** * Clear the ref resolution cache (in-memory only). * Clears the in-memory memoization cache without affecting the persistent disk cache. * Useful for testing or when you need fresh data from the API. * * Note: This only clears the in-memory cache. The persistent cacache storage * remains intact and will be used to rebuild the in-memory cache on next access. * * @returns Promise that resolves when cache is cleared * * @example * ```ts * // Clear cache to force fresh API calls * await clearRefCache() * const sha = await resolveRefToSha('owner', 'repo', 'main') * // This will hit the persistent cache or API, not in-memory cache * ``` */ export declare function clearRefCache(): Promise<void>; /** * Get GitHub authentication token from git config. * Reads the `github.token` configuration value from git config. * This is a fallback method when environment variables don't contain a token. * * @param options - Spawn options for git command execution * @returns GitHub token from git config, or `undefined` if not configured * * @example * ```ts * const token = await getGitHubTokenFromGitConfig() * if (token) { * console.log('Found token in git config') * } * ``` * * @example * ```ts * // With custom working directory * const token = await getGitHubTokenFromGitConfig({ * cwd: '/path/to/repo' * }) * ``` */ export declare function getGitHubTokenFromGitConfig(options?: SpawnOptions | undefined): Promise<string | undefined>; /** * Get GitHub authentication token from all available sources. * Checks environment variables first, then falls back to git config. * This is the recommended way to get a GitHub token with maximum compatibility. * * Priority order: * 1. Environment variables (GITHUB_TOKEN, GH_TOKEN, SOCKET_CLI_GITHUB_TOKEN) * 2. Git config (github.token) * * @returns GitHub token from first available source, or `undefined` if none found * * @example * ```ts * const token = await getGitHubTokenWithFallback() * if (!token) { * throw new Error('GitHub token required') * } * ``` */ export declare function getGitHubTokenWithFallback(): Promise<string | undefined>; /** * GitHub Security Advisory (GHSA) details. * Represents a complete security advisory from GitHub's database. */ export interface GhsaDetails { /** GHSA identifier (e.g., 'GHSA-xxxx-yyyy-zzzz') */ ghsaId: string; /** Short summary of the vulnerability */ summary: string; /** Detailed description of the vulnerability */ details: string; /** Severity level ('low', 'moderate', 'high', 'critical') */ severity: string; /** Alternative identifiers (CVE IDs, etc.) */ aliases: string[]; /** ISO 8601 timestamp when advisory was published */ publishedAt: string; /** ISO 8601 timestamp when advisory was last updated */ updatedAt: string; /** * ISO 8601 timestamp when advisory was withdrawn. * `null` if advisory is still active. */ withdrawnAt: string | null; /** External reference URLs for more information */ references: Array<{ url: string; }>; /** Affected packages and version ranges */ vulnerabilities: Array<{ /** Package information */ package: { /** Ecosystem (e.g., 'npm', 'pip', 'maven') */ ecosystem: string; /** Package name */ name: string; }; /** Version range expression for vulnerable versions */ vulnerableVersionRange: string; /** * First patched version that fixes the vulnerability. * `null` if no patched version exists yet. */ firstPatchedVersion: { identifier: string; } | null; }>; /** * CVSS (Common Vulnerability Scoring System) information. * `null` if CVSS score is not available. */ cvss: { /** CVSS score (0.0-10.0) */ score: number; /** CVSS vector string describing the vulnerability characteristics */ vectorString: string; } | null; /** CWE (Common Weakness Enumeration) categories */ cwes: Array<{ /** CWE identifier (e.g., 'CWE-79') */ cweId: string; /** Human-readable CWE name */ name: string; /** Description of the weakness category */ description: string; }>; } /** * Generate GitHub Security Advisory URL from GHSA ID. * Constructs the public advisory URL for a given GHSA identifier. * * @param ghsaId - GHSA identifier (e.g., 'GHSA-xxxx-yyyy-zzzz') * @returns Full URL to the advisory page * * @example * ```ts * const url = getGhsaUrl('GHSA-1234-5678-90ab') * console.log(url) // 'https://github.com/advisories/GHSA-1234-5678-90ab' * ``` */ export declare function getGhsaUrl(ghsaId: string): string; /** * Fetch GitHub Security Advisory details from the API. * Retrieves complete advisory information including severity, affected packages, * CVSS scores, and CWE classifications. * * @param ghsaId - GHSA identifier to fetch (e.g., 'GHSA-xxxx-yyyy-zzzz') * @param options - Fetch options including authentication token * @returns Complete advisory details with normalized field names * * @throws {Error} If advisory cannot be found or API request fails * @throws {GitHubRateLimitError} When API rate limit is exceeded * * @example * ```ts * const advisory = await fetchGhsaDetails('GHSA-1234-5678-90ab') * console.log(`Severity: ${advisory.severity}`) * console.log(`Affects: ${advisory.vulnerabilities.length} packages`) * if (advisory.cvss) { * console.log(`CVSS Score: ${advisory.cvss.score}`) * } * ``` * * @example * ```ts * // Check if vulnerability is patched * const advisory = await fetchGhsaDetails('GHSA-xxxx-yyyy-zzzz') * for (const vuln of advisory.vulnerabilities) { * if (vuln.firstPatchedVersion) { * console.log( * `Patched in ${vuln.package.name}@${vuln.firstPatchedVersion.identifier}` * ) * } * } * ``` */ export declare function fetchGhsaDetails(ghsaId: string, options?: GitHubFetchOptions | undefined): Promise<GhsaDetails>; /** * Fetch GitHub Security Advisory details with caching. * Retrieves advisory information with two-tier caching (in-memory + persistent). * Cached results are stored with the default TTL (5 minutes). * * Caching behavior: * - Checks in-memory cache first for immediate response * - Falls back to persistent disk cache if not in memory * - Fetches from API only if not cached * - Stores result in both cache tiers * - Respects `DISABLE_GITHUB_CACHE` env var * * @param ghsaId - GHSA identifier to fetch * @param options - Fetch options including authentication token * @returns Complete advisory details * * @throws {Error} If advisory cannot be found or API request fails * @throws {GitHubRateLimitError} When API rate limit is exceeded * * @example * ```ts * // First call hits API * const advisory = await cacheFetchGhsa('GHSA-1234-5678-90ab') * * // Second call within 5 minutes returns cached data * const cached = await cacheFetchGhsa('GHSA-1234-5678-90ab') * ``` * * @example * ```ts * // Disable caching for fresh data * process.env.DISABLE_GITHUB_CACHE = '1' * const advisory = await cacheFetchGhsa('GHSA-xxxx-yyyy-zzzz') * ``` */ export declare function cacheFetchGhsa(ghsaId: string, options?: GitHubFetchOptions | undefined): Promise<GhsaDetails>;