Files
agents/plugins/deployment-validation/commands/config-validate.md
2026-01-19 17:07:03 -05:00

492 lines
13 KiB
Markdown

# Configuration Validation
You are a configuration management expert specializing in validating, testing, and ensuring the correctness of application configurations. Create comprehensive validation schemas, implement configuration testing strategies, and ensure configurations are secure, consistent, and error-free across all environments.
## Context
The user needs to validate configuration files, implement configuration schemas, ensure consistency across environments, and prevent configuration-related errors. Focus on creating robust validation rules, type safety, security checks, and automated validation processes.
## Requirements
$ARGUMENTS
## Instructions
### 1. Configuration Analysis
Analyze existing configuration structure and identify validation needs:
```python
import os
import yaml
import json
from pathlib import Path
from typing import Dict, List, Any
class ConfigurationAnalyzer:
def analyze_project(self, project_path: str) -> Dict[str, Any]:
analysis = {
'config_files': self._find_config_files(project_path),
'security_issues': self._check_security_issues(project_path),
'consistency_issues': self._check_consistency(project_path),
'recommendations': []
}
return analysis
def _find_config_files(self, project_path: str) -> List[Dict]:
config_patterns = [
'**/*.json', '**/*.yaml', '**/*.yml', '**/*.toml',
'**/*.ini', '**/*.env*', '**/config.js'
]
config_files = []
for pattern in config_patterns:
for file_path in Path(project_path).glob(pattern):
if not self._should_ignore(file_path):
config_files.append({
'path': str(file_path),
'type': self._detect_config_type(file_path),
'environment': self._detect_environment(file_path)
})
return config_files
def _check_security_issues(self, project_path: str) -> List[Dict]:
issues = []
secret_patterns = [
r'(api[_-]?key|apikey)',
r'(secret|password|passwd)',
r'(token|auth)',
r'(aws[_-]?access)'
]
for config_file in self._find_config_files(project_path):
content = Path(config_file['path']).read_text()
for pattern in secret_patterns:
if re.search(pattern, content, re.IGNORECASE):
if self._looks_like_real_secret(content, pattern):
issues.append({
'file': config_file['path'],
'type': 'potential_secret',
'severity': 'high'
})
return issues
```
### 2. Schema Validation
Implement configuration schema validation with JSON Schema:
```typescript
import Ajv from "ajv";
import ajvFormats from "ajv-formats";
import { JSONSchema7 } from "json-schema";
interface ValidationResult {
valid: boolean;
errors?: Array<{
path: string;
message: string;
keyword: string;
}>;
}
export class ConfigValidator {
private ajv: Ajv;
constructor() {
this.ajv = new Ajv({
allErrors: true,
strict: false,
coerceTypes: true,
});
ajvFormats(this.ajv);
this.addCustomFormats();
}
private addCustomFormats() {
this.ajv.addFormat("url-https", {
type: "string",
validate: (data: string) => {
try {
return new URL(data).protocol === "https:";
} catch {
return false;
}
},
});
this.ajv.addFormat("port", {
type: "number",
validate: (data: number) => data >= 1 && data <= 65535,
});
this.ajv.addFormat("duration", {
type: "string",
validate: /^\d+[smhd]$/,
});
}
validate(configData: any, schemaName: string): ValidationResult {
const validate = this.ajv.getSchema(schemaName);
if (!validate) throw new Error(`Schema '${schemaName}' not found`);
const valid = validate(configData);
if (!valid && validate.errors) {
return {
valid: false,
errors: validate.errors.map((error) => ({
path: error.instancePath || "/",
message: error.message || "Validation error",
keyword: error.keyword,
})),
};
}
return { valid: true };
}
}
// Example schema
export const schemas = {
database: {
type: "object",
properties: {
host: { type: "string", format: "hostname" },
port: { type: "integer", format: "port" },
database: { type: "string", minLength: 1 },
user: { type: "string", minLength: 1 },
password: { type: "string", minLength: 8 },
ssl: {
type: "object",
properties: {
enabled: { type: "boolean" },
},
required: ["enabled"],
},
},
required: ["host", "port", "database", "user", "password"],
},
};
```
### 3. Environment-Specific Validation
```python
from typing import Dict, List, Any
class EnvironmentValidator:
def __init__(self):
self.environments = ['development', 'staging', 'production']
self.environment_rules = {
'development': {
'allow_debug': True,
'require_https': False,
'min_password_length': 8
},
'production': {
'allow_debug': False,
'require_https': True,
'min_password_length': 16,
'require_encryption': True
}
}
def validate_config(self, config: Dict, environment: str) -> List[Dict]:
if environment not in self.environment_rules:
raise ValueError(f"Unknown environment: {environment}")
rules = self.environment_rules[environment]
violations = []
if not rules['allow_debug'] and config.get('debug', False):
violations.append({
'rule': 'no_debug_in_production',
'message': 'Debug mode not allowed in production',
'severity': 'critical'
})
if rules['require_https']:
urls = self._extract_urls(config)
for url_path, url in urls:
if url.startswith('http://') and 'localhost' not in url:
violations.append({
'rule': 'require_https',
'message': f'HTTPS required for {url_path}',
'severity': 'high'
})
return violations
```
### 4. Configuration Testing
```typescript
import { describe, it, expect } from "@jest/globals";
import { ConfigValidator } from "./config-validator";
describe("Configuration Validation", () => {
let validator: ConfigValidator;
beforeEach(() => {
validator = new ConfigValidator();
});
it("should validate database config", () => {
const config = {
host: "localhost",
port: 5432,
database: "myapp",
user: "dbuser",
password: "securepass123",
};
const result = validator.validate(config, "database");
expect(result.valid).toBe(true);
});
it("should reject invalid port", () => {
const config = {
host: "localhost",
port: 70000,
database: "myapp",
user: "dbuser",
password: "securepass123",
};
const result = validator.validate(config, "database");
expect(result.valid).toBe(false);
});
});
```
### 5. Runtime Validation
```typescript
import { EventEmitter } from "events";
import * as chokidar from "chokidar";
export class RuntimeConfigValidator extends EventEmitter {
private validator: ConfigValidator;
private currentConfig: any;
async initialize(configPath: string): Promise<void> {
this.currentConfig = await this.loadAndValidate(configPath);
this.watchConfig(configPath);
}
private async loadAndValidate(configPath: string): Promise<any> {
const config = await this.loadConfig(configPath);
const validationResult = this.validator.validate(
config,
this.detectEnvironment(),
);
if (!validationResult.valid) {
this.emit("validation:error", {
path: configPath,
errors: validationResult.errors,
});
if (!this.isDevelopment()) {
throw new Error("Configuration validation failed");
}
}
return config;
}
private watchConfig(configPath: string): void {
const watcher = chokidar.watch(configPath, {
persistent: true,
ignoreInitial: true,
});
watcher.on("change", async () => {
try {
const newConfig = await this.loadAndValidate(configPath);
if (JSON.stringify(newConfig) !== JSON.stringify(this.currentConfig)) {
this.emit("config:changed", {
oldConfig: this.currentConfig,
newConfig,
});
this.currentConfig = newConfig;
}
} catch (error) {
this.emit("config:error", { error });
}
});
}
}
```
### 6. Configuration Migration
```python
from typing import Dict
from abc import ABC, abstractmethod
import semver
class ConfigMigration(ABC):
@property
@abstractmethod
def version(self) -> str:
pass
@abstractmethod
def up(self, config: Dict) -> Dict:
pass
@abstractmethod
def down(self, config: Dict) -> Dict:
pass
class ConfigMigrator:
def __init__(self):
self.migrations: List[ConfigMigration] = []
def migrate(self, config: Dict, target_version: str) -> Dict:
current_version = config.get('_version', '0.0.0')
if semver.compare(current_version, target_version) == 0:
return config
result = config.copy()
for migration in self.migrations:
if (semver.compare(migration.version, current_version) > 0 and
semver.compare(migration.version, target_version) <= 0):
result = migration.up(result)
result['_version'] = migration.version
return result
```
### 7. Secure Configuration
```typescript
import * as crypto from "crypto";
interface EncryptedValue {
encrypted: true;
value: string;
algorithm: string;
iv: string;
authTag?: string;
}
export class SecureConfigManager {
private encryptionKey: Buffer;
constructor(masterKey: string) {
this.encryptionKey = crypto.pbkdf2Sync(
masterKey,
"config-salt",
100000,
32,
"sha256",
);
}
encrypt(value: any): EncryptedValue {
const algorithm = "aes-256-gcm";
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, this.encryptionKey, iv);
let encrypted = cipher.update(JSON.stringify(value), "utf8", "hex");
encrypted += cipher.final("hex");
return {
encrypted: true,
value: encrypted,
algorithm,
iv: iv.toString("hex"),
authTag: cipher.getAuthTag().toString("hex"),
};
}
decrypt(encryptedValue: EncryptedValue): any {
const decipher = crypto.createDecipheriv(
encryptedValue.algorithm,
this.encryptionKey,
Buffer.from(encryptedValue.iv, "hex"),
);
if (encryptedValue.authTag) {
decipher.setAuthTag(Buffer.from(encryptedValue.authTag, "hex"));
}
let decrypted = decipher.update(encryptedValue.value, "hex", "utf8");
decrypted += decipher.final("utf8");
return JSON.parse(decrypted);
}
async processConfig(config: any): Promise<any> {
const processed = {};
for (const [key, value] of Object.entries(config)) {
if (this.isEncryptedValue(value)) {
processed[key] = this.decrypt(value as EncryptedValue);
} else if (typeof value === "object" && value !== null) {
processed[key] = await this.processConfig(value);
} else {
processed[key] = value;
}
}
return processed;
}
}
```
### 8. Documentation Generation
````python
from typing import Dict, List
import yaml
class ConfigDocGenerator:
def generate_docs(self, schema: Dict, examples: Dict) -> str:
docs = ["# Configuration Reference\n"]
docs.append("## Configuration Options\n")
sections = self._generate_sections(schema.get('properties', {}), examples)
docs.extend(sections)
return '\n'.join(docs)
def _generate_sections(self, properties: Dict, examples: Dict, level: int = 3) -> List[str]:
sections = []
for prop_name, prop_schema in properties.items():
sections.append(f"{'#' * level} {prop_name}\n")
if 'description' in prop_schema:
sections.append(f"{prop_schema['description']}\n")
sections.append(f"**Type:** `{prop_schema.get('type', 'any')}`\n")
if 'default' in prop_schema:
sections.append(f"**Default:** `{prop_schema['default']}`\n")
if prop_name in examples:
sections.append("**Example:**\n```yaml")
sections.append(yaml.dump({prop_name: examples[prop_name]}))
sections.append("```\n")
return sections
````
## Output Format
1. **Configuration Analysis**: Current configuration assessment
2. **Validation Schemas**: JSON Schema definitions
3. **Environment Rules**: Environment-specific validation
4. **Test Suite**: Configuration tests
5. **Migration Scripts**: Version migrations
6. **Security Report**: Issues and recommendations
7. **Documentation**: Auto-generated reference
Focus on preventing configuration errors, ensuring consistency, and maintaining security best practices.