doclyft
Version:
CLI for DocLyft - Interactive documentation generator with hosted documentation support
127 lines (126 loc) • 5.59 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateRepositoryName = validateRepositoryName;
exports.validateGitHubToken = validateGitHubToken;
exports.validateAPIKey = validateAPIKey;
exports.validateFilePath = validateFilePath;
exports.validateSections = validateSections;
exports.validateOutputDirectory = validateOutputDirectory;
const errors_1 = require("./errors");
const path_1 = require("path");
function validateRepositoryName(repo) {
if (!repo || typeof repo !== 'string') {
throw new errors_1.ValidationError('Repository name is required');
}
if (!repo.includes('/')) {
throw new errors_1.ValidationError('Repository must be in format owner/name (e.g., facebook/react)', 'repo');
}
const parts = repo.split('/');
if (parts.length !== 2) {
throw new errors_1.ValidationError('Repository must be in format owner/name (e.g., facebook/react)', 'repo');
}
const [owner, name] = parts;
// Validate owner and name format (GitHub rules)
const validPattern = /^[a-zA-Z0-9._-]+$/;
if (!validPattern.test(owner)) {
throw new errors_1.ValidationError('Repository owner contains invalid characters. Only alphanumeric characters, dots, hyphens, and underscores are allowed.', 'owner');
}
if (!validPattern.test(name)) {
throw new errors_1.ValidationError('Repository name contains invalid characters. Only alphanumeric characters, dots, hyphens, and underscores are allowed.', 'name');
}
if (owner.length === 0 || name.length === 0) {
throw new errors_1.ValidationError('Both owner and repository name must be non-empty');
}
if (owner.length > 39 || name.length > 100) {
throw new errors_1.ValidationError('Repository owner or name is too long');
}
return { owner, name };
}
function validateGitHubToken(token) {
if (!token || typeof token !== 'string') {
throw new errors_1.ValidationError('GitHub token is required');
}
// GitHub tokens should start with specific prefixes
const validPrefixes = ['ghp_', 'gho_', 'ghu_', 'ghs_', 'ghr_', 'github_pat_'];
const hasValidPrefix = validPrefixes.some(prefix => token.startsWith(prefix));
if (!hasValidPrefix) {
throw new errors_1.ValidationError('Invalid GitHub token format. Make sure you\'re using a valid personal access token. Expected format: ghp_*, gho_*, ghu_*, ghs_*, ghr_*, or github_pat_*');
}
if (token.length < 40) {
throw new errors_1.ValidationError('GitHub token appears to be too short');
}
}
function validateAPIKey(apiKey) {
if (!apiKey || typeof apiKey !== 'string') {
throw new errors_1.ValidationError('API key is required');
}
if (!apiKey.startsWith('dk_prod_')) {
throw new errors_1.ValidationError('Invalid API key format. API keys should start with "dk_prod_"');
}
if (apiKey.length < 30) {
throw new errors_1.ValidationError('API key appears to be too short');
}
}
function validateFilePath(filePath) {
if (!filePath || typeof filePath !== 'string') {
throw new errors_1.ValidationError('File path is required');
}
// Basic path validation
const invalidChars = /[<>:"|?*]/;
if (invalidChars.test(filePath)) {
throw new errors_1.ValidationError('File path contains invalid characters');
}
}
function validateSections(sections) {
if (!sections || typeof sections !== 'string') {
throw new errors_1.ValidationError('Sections must be provided as a comma-separated string');
}
const sectionList = sections.split(',').map(s => s.trim()).filter(s => s.length > 0);
if (sectionList.length === 0) {
throw new errors_1.ValidationError('At least one section must be specified');
}
const validSections = [
'overview',
'installation',
'usage',
'apiReference',
'testing',
'projectStructure',
'deployment',
'troubleshooting',
'contributing',
'changelog',
'license'
];
const invalidSections = sectionList.filter(section => !validSections.includes(section));
if (invalidSections.length > 0) {
throw new errors_1.ValidationError(`Invalid sections: ${invalidSections.join(', ')}. Valid sections are: ${validSections.join(', ')}`);
}
return sectionList;
}
function validateOutputDirectory(outputDir) {
if (!outputDir || typeof outputDir !== 'string') {
throw new errors_1.ValidationError('Output directory is required');
}
// Basic directory name validation
const invalidChars = /[<>:"|?*]/;
if (invalidChars.test(outputDir)) {
throw new errors_1.ValidationError('Output directory contains invalid characters');
}
// Enhanced path traversal protection
const normalizedPath = (0, path_1.normalize)(outputDir);
const resolvedPath = (0, path_1.resolve)(normalizedPath);
const currentDir = process.cwd();
// Check for parent directory references in various forms
if (normalizedPath.includes('..') ||
outputDir.includes('%2e%2e') ||
outputDir.includes('..\\') ||
outputDir.includes('../') ||
!resolvedPath.startsWith(currentDir)) {
throw new errors_1.ValidationError('Output directory must be within current working directory');
}
// Additional security checks
if (outputDir.startsWith('/') || outputDir.startsWith('\\') || outputDir.match(/^[a-zA-Z]:/)) {
throw new errors_1.ValidationError('Output directory must be a relative path');
}
}