Config Validate

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.

Published by @Seth Hobson·0 agent reads / 30d·0 saves·

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:

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:

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

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

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

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

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

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

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.

Bundled with this artifact

1 file

Reference files that ship alongside this artifact. Agents pull these in only when the task needs them.

More on the bench

SKILL0

Xlsx

Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.

software-engineering+2
0
SKILL0

Docx

Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). Triggers include: any mention of 'Word doc', 'word document', '.docx', or requests to produce professional documents with formatting like tables of contents, headings, page numbers, or letterheads. Also use when extracting or reorganizing content from .docx files, inserting or replacing images in documents, performing find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a 'report', 'memo', 'letter', 'template', or similar deliverable as a Word or .docx file, use this skill. Do NOT use for PDFs, spreadsheets, Google Docs, or general coding tasks unrelated to document generation.

software-engineering+1
0
SKILL0

Ticket Triage

Triage incoming support tickets by categorizing issues, assigning priority (P1-P4), and recommending routing. Use when a new ticket or customer issue comes in, when assessing severity, or when deciding which team should handle an issue.

customer-success+2
0