UNPKG

git-spark

Version:

Git repository analytics and reporting tool for analyzing commit patterns and contributor activity

253 lines 9.13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.validateOptions = validateOptions; exports.validateNodeVersion = validateNodeVersion; exports.validateGitInstallation = validateGitInstallation; exports.sanitizeInput = sanitizeInput; exports.sanitizePath = sanitizePath; exports.sanitizeEmail = sanitizeEmail; exports.validateCommitMessage = validateCommitMessage; const types_1 = require("../types"); const fs_1 = require("fs"); const path_1 = require("path"); const semver = __importStar(require("semver")); function validateOptions(options) { const errors = []; const warnings = []; // Validate repository path if (options.repoPath) { if (!(0, fs_1.existsSync)(options.repoPath)) { errors.push(`Repository path does not exist: ${options.repoPath}`); } else if (!(0, fs_1.statSync)(options.repoPath).isDirectory()) { errors.push(`Repository path is not a directory: ${options.repoPath}`); } else if (!(0, fs_1.existsSync)((0, path_1.resolve)(options.repoPath, '.git'))) { errors.push(`Directory is not a Git repository: ${options.repoPath}`); } } // Validate date formats if (options.since && !isValidDate(options.since)) { errors.push(`Invalid since date format. Use YYYY-MM-DD: ${options.since}`); } if (options.until && !isValidDate(options.until)) { errors.push(`Invalid until date format. Use YYYY-MM-DD: ${options.until}`); } // Validate date range if (options.since && options.until) { const sinceDate = new Date(options.since); const untilDate = new Date(options.until); if (sinceDate >= untilDate) { errors.push('Since date must be before until date'); } } // Validate days parameter if (options.days !== undefined) { if (!Number.isInteger(options.days) || options.days <= 0) { errors.push('Days must be a positive integer'); } if (options.days > 3650) { warnings.push('Analyzing more than 10 years of history may be slow'); } } // Validate output format if (options.format) { const validFormats = ['html', 'json', 'console', 'markdown', 'csv']; if (!validFormats.includes(options.format)) { errors.push(`Invalid format. Must be one of: ${validFormats.join(', ')}`); } } // Validate output directory if (options.output) { const outputDir = (0, path_1.resolve)(options.output); const parentDir = (0, path_1.resolve)(outputDir, '..'); if (!(0, fs_1.existsSync)(parentDir)) { errors.push(`Output directory parent does not exist: ${parentDir}`); } } // Validate config file if (options.config) { if (!(0, fs_1.existsSync)(options.config)) { errors.push(`Config file does not exist: ${options.config}`); } else if (!options.config.endsWith('.json')) { warnings.push('Config file should have .json extension'); } } // Validate log level if (options.logLevel) { const validLevels = ['error', 'warn', 'info', 'debug', 'verbose']; if (!validLevels.includes(options.logLevel)) { errors.push(`Invalid log level. Must be one of: ${validLevels.join(', ')}`); } } // redaction flag requires no validation now; future: ensure not combined with raw mode exports // Validate author pattern if (options.author) { if (options.author.length < 2) { warnings.push('Author filter is very short and may not match any commits'); } } // Validate path pattern if (options.path) { try { new RegExp(options.path); } catch { warnings.push('Path pattern may not be a valid glob or regex pattern'); } } return { isValid: errors.length === 0, errors, warnings, }; } function validateNodeVersion() { const errors = []; const warnings = []; const nodeVersion = process.version; const requiredVersion = '>=18.0.0'; if (!semver.satisfies(nodeVersion, requiredVersion)) { errors.push(`Node.js ${requiredVersion} is required. Current version: ${nodeVersion}`); } return { isValid: errors.length === 0, errors, warnings, }; } function validateGitInstallation() { return new Promise(resolve => { const { spawn } = require('child_process'); const git = spawn('git', ['--version']); let output = ''; git.stdout.on('data', (data) => { output += data.toString(); }); git.on('close', (code) => { if (code === 0) { const versionMatch = output.match(/git version (\d+\.\d+\.\d+)/); if (versionMatch) { const version = versionMatch[1]; if (semver.gte(version, '2.20.0')) { resolve({ isValid: true, errors: [], warnings: [], }); } else { resolve({ isValid: false, errors: [`Git 2.20.0 or higher is required. Current version: ${version}`], warnings: [], }); } } else { resolve({ isValid: false, errors: ['Could not determine Git version'], warnings: [], }); } } else { resolve({ isValid: false, errors: ['Git is not installed or not accessible in PATH'], warnings: [], }); } }); git.on('error', () => { resolve({ isValid: false, errors: ['Git is not installed or not accessible in PATH'], warnings: [], }); }); }); } function sanitizeInput(input) { // Remove potentially dangerous characters return input .replace(/[;&|`$(){}[\]]/g, '') .replace(/\.\./g, '') .trim(); } function sanitizePath(path) { // Prevent directory traversal const normalized = (0, path_1.resolve)(path); if (normalized.includes('..')) { throw new types_1.ValidationError('Path traversal not allowed', 'path'); } return normalized; } function sanitizeEmail(email, redact = false) { if (redact) { const [user, domain] = email.split('@'); if (domain) { return `${user.charAt(0)}***@${domain}`; } return '***'; } return email; } function validateCommitMessage(message) { const conventional = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|build)(\(.+\))?: .+/.test(message); const issueRef = /#\d+/.test(message) || /closes|fixes|resolves/i.test(message); const isWip = /wip|work in progress/i.test(message); const isRevert = /^revert/i.test(message); return { isConventional: conventional, hasIssueReference: issueRef, isWip, isRevert, length: message.length, }; } function isValidDate(dateString) { // Check YYYY-MM-DD format const regex = /^\d{4}-\d{2}-\d{2}$/; if (!regex.test(dateString)) { return false; } const date = new Date(dateString); return date instanceof Date && !isNaN(date.getTime()); } //# sourceMappingURL=validation.js.map