#!/usr/bin/env node
/**
* Config Validation CLI Tool
*
* Validates configuration files for correctness and completeness.
* Can be run via: npm run validate-config
*/
import { readFileSync } from 'fs';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = resolve(__filename, '..');
/**
* Validate JSON file
* @param {string} filePath - Path to JSON file
* @returns {Object} Parsed JSON or null
*/
function validateJsonFile(filePath) {
try {
const content = readFileSync(filePath, 'utf8');
return JSON.parse(content);
} catch (error) {
console.error(`❌ Failed to parse ${filePath}: ${error.message}`);
return null;
}
}
/**
* Validate tools manifest
* @param {Object} manifest - Tools manifest
* @returns {boolean} Valid
*/
function validateToolsManifest(manifest) {
if (!manifest.tools || !Array.isArray(manifest.tools)) {
console.error('❌ Invalid tools manifest: missing tools array');
return false;
}
const toolNames = new Set();
for (const tool of manifest.tools) {
if (!tool.name) {
console.error('❌ Tool missing name');
return false;
}
if (toolNames.has(tool.name)) {
console.error(`❌ Duplicate tool name: ${tool.name}`);
return false;
}
toolNames.add(tool.name);
if (!tool.category) {
console.warn(`⚠️ Tool '${tool.name}' missing category`);
}
if (!tool.description) {
console.warn(`⚠️ Tool '${tool.name}' missing description`);
}
if (!tool.requiredPermissions || !Array.isArray(tool.requiredPermissions)) {
console.warn(`⚠️ Tool '${tool.name}' missing requiredPermissions`);
}
if (tool.inputSchema) {
const schemaErrors = validateJsonSchema(tool.inputSchema);
if (schemaErrors.length > 0) {
console.error(`❌ Tool '${tool.name}' has invalid input schema:`);
schemaErrors.forEach(err => console.error(` - ${err}`));
return false;
}
}
}
console.log(`✅ Tools manifest valid: ${manifest.tools.length} tools`);
return true;
}
/**
* Validate JSON Schema
* @param {Object} schema - JSON schema
* @returns {Array<string>} Errors
*/
function validateJsonSchema(schema) {
const errors = [];
if (!schema.type) {
errors.push('Missing type');
}
if (schema.properties && typeof schema.properties !== 'object') {
errors.push('properties must be an object');
}
if (schema.required && !Array.isArray(schema.required)) {
errors.push('required must be an array');
}
if (schema.enum && !Array.isArray(schema.enum)) {
errors.push('enum must be an array');
}
if (schema.minimum !== undefined && typeof schema.minimum !== 'number') {
errors.push('minimum must be a number');
}
if (schema.maximum !== undefined && typeof schema.maximum !== 'number') {
errors.push('maximum must be a number');
}
if (schema.minLength !== undefined && typeof schema.minLength !== 'number') {
errors.push('minLength must be a number');
}
if (schema.maxLength !== undefined && typeof schema.maxLength !== 'number') {
errors.push('maxLength must be a number');
}
if (schema.pattern && typeof schema.pattern !== 'string') {
errors.push('pattern must be a string');
}
return errors;
}
/**
* Validate policy rules
* @param {Object} policies - Policy rules
* @returns {boolean} Valid
*/
function validatePolicyRules(policies) {
if (!policies.policies || !Array.isArray(policies.policies)) {
console.error('❌ Invalid policy rules: missing policies array');
return false;
}
const policyNames = new Set();
for (const policy of policies.policies) {
if (!policy.name) {
console.error('❌ Policy missing name');
return false;
}
if (policyNames.has(policy.name)) {
console.error(`❌ Duplicate policy name: ${policy.name}`);
return false;
}
policyNames.add(policy.name);
if (!policy.tools || !Array.isArray(policy.tools)) {
console.warn(`⚠️ Policy '${policy.name}' missing tools array`);
}
if (!policy.conditions) {
console.warn(`⚠️ Policy '${policy.name}' missing conditions`);
}
}
console.log(`✅ Policy rules valid: ${policies.policies.length} policies`);
return true;
}
/**
* Validate secret patterns
* @param {Object} patterns - Secret patterns
* @returns {boolean} Valid
*/
function validateSecretPatterns(patterns) {
if (!patterns.patterns || !Array.isArray(patterns.patterns)) {
console.error('❌ Invalid secret patterns: missing patterns array');
return false;
}
const patternNames = new Set();
for (const pattern of patterns.patterns) {
if (!pattern.name) {
console.error('❌ Pattern missing name');
return false;
}
if (patternNames.has(pattern.name)) {
console.error(`❌ Duplicate pattern name: ${pattern.name}`);
return false;
}
patternNames.add(pattern.name);
if (!pattern.regex) {
console.error(`❌ Pattern '${pattern.name}' missing regex`);
return false;
}
if (!pattern.severity) {
console.warn(`⚠️ Pattern '${pattern.name}' missing severity`);
}
if (pattern.regex) {
try {
new RegExp(pattern.regex);
} catch (error) {
console.error(`❌ Pattern '${pattern.name}' has invalid regex: ${error.message}`);
return false;
}
}
}
console.log(`✅ Secret patterns valid: ${patterns.patterns.length} patterns`);
return true;
}
/**
* Main validation function
*/
async function main() {
console.log('🔍 Validating configuration files...\n');
let allValid = true;
// Validate tools manifest
console.log('Validating tools-manifest.json...');
const toolsManifest = validateJsonFile(`${__dirname}/../config/tools-manifest.json`);
if (toolsManifest) {
if (!validateToolsManifest(toolsManifest)) {
allValid = false;
}
} else {
allValid = false;
}
console.log();
// Validate policy rules
console.log('Validating policy-rules.json...');
const policyRules = validateJsonFile(`${__dirname}/../config/policy-rules.json`);
if (policyRules) {
if (!validatePolicyRules(policyRules)) {
allValid = false;
}
} else {
allValid = false;
}
console.log();
// Validate secret patterns
console.log('Validating secret-patterns.json...');
const secretPatterns = validateJsonFile(`${__dirname}/../config/secret-patterns.json`);
if (secretPatterns) {
if (!validateSecretPatterns(secretPatterns)) {
allValid = false;
}
} else {
allValid = false;
}
console.log();
// Final result
if (allValid) {
console.log('✅ All configuration files are valid!');
process.exit(0);
} else {
console.log('❌ Some configuration files have errors.');
process.exit(1);
}
}
// Run validation
main().catch(error => {
console.error('❌ Validation failed with error:', error);
process.exit(1);
});