git-spark
Version:
Git repository analytics and reporting tool for analyzing commit patterns and contributor activity
253 lines • 9.13 kB
JavaScript
;
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