238 lines
7.5 KiB
Text
238 lines
7.5 KiB
Text
|
|
# Module: Configuration Validators
|
||
|
|
# Purpose: Provides validation functions for configuration integrity, types, and semantic correctness.
|
||
|
|
# Dependencies: None (core utility)
|
||
|
|
|
||
|
|
# Configuration Validation and Detection Engine
|
||
|
|
# Validates configuration structures and detects potential security/dependency issues
|
||
|
|
|
||
|
|
use std log
|
||
|
|
|
||
|
|
# Validate interpolation patterns and detect potential issues
|
||
|
|
export def validate-interpolation [
|
||
|
|
config: record
|
||
|
|
--detailed = false # Show detailed validation results
|
||
|
|
] {
|
||
|
|
mut errors = []
|
||
|
|
mut warnings = []
|
||
|
|
|
||
|
|
# Convert config to JSON for pattern detection
|
||
|
|
let json_str = ($config | to json)
|
||
|
|
|
||
|
|
# Check for unresolved interpolation patterns
|
||
|
|
let unresolved_patterns = (detect-unresolved-patterns $json_str)
|
||
|
|
if ($unresolved_patterns | length) > 0 {
|
||
|
|
$errors = ($errors | append {
|
||
|
|
type: "unresolved_interpolation"
|
||
|
|
severity: "error"
|
||
|
|
patterns: $unresolved_patterns
|
||
|
|
message: $"Unresolved interpolation patterns found: ($unresolved_patterns | str join ', ')"
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for circular dependencies
|
||
|
|
let circular_deps = (detect-circular-dependencies $json_str)
|
||
|
|
if ($circular_deps | length) > 0 {
|
||
|
|
$errors = ($errors | append {
|
||
|
|
type: "circular_dependency"
|
||
|
|
severity: "error"
|
||
|
|
dependencies: $circular_deps
|
||
|
|
message: $"Circular interpolation dependencies detected: ($circular_deps | str join ', ')"
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for unsafe environment variable access
|
||
|
|
let unsafe_env_vars = (detect-unsafe-env-patterns $json_str)
|
||
|
|
if ($unsafe_env_vars | length) > 0 {
|
||
|
|
$warnings = ($warnings | append {
|
||
|
|
type: "unsafe_env_access"
|
||
|
|
severity: "warning"
|
||
|
|
variables: $unsafe_env_vars
|
||
|
|
message: $"Potentially unsafe environment variable access: ($unsafe_env_vars | str join ', ')"
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate git repository context
|
||
|
|
let git_validation = (validate-git-context $json_str)
|
||
|
|
if not $git_validation.valid {
|
||
|
|
$warnings = ($warnings | append {
|
||
|
|
type: "git_context"
|
||
|
|
severity: "warning"
|
||
|
|
message: $git_validation.message
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
let has_errors = ($errors | length) > 0
|
||
|
|
let has_warnings = ($warnings | length) > 0
|
||
|
|
|
||
|
|
if not $detailed and $has_errors {
|
||
|
|
let error_messages = ($errors | each { |err| $err.message })
|
||
|
|
error make {
|
||
|
|
msg: ($error_messages | str join "; ")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
valid: (not $has_errors),
|
||
|
|
errors: $errors,
|
||
|
|
warnings: $warnings,
|
||
|
|
summary: {
|
||
|
|
total_errors: ($errors | length),
|
||
|
|
total_warnings: ($warnings | length),
|
||
|
|
interpolation_patterns_detected: (count-interpolation-patterns $json_str)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Security-hardened interpolation with input validation
|
||
|
|
export def secure-interpolation [
|
||
|
|
config: record
|
||
|
|
--allow-unsafe = false # Allow potentially unsafe patterns
|
||
|
|
--max-depth = 5 # Maximum interpolation depth
|
||
|
|
] {
|
||
|
|
# Security checks before interpolation
|
||
|
|
let security_validation = (validate-interpolation-security $config $allow_unsafe)
|
||
|
|
|
||
|
|
if not $security_validation.valid {
|
||
|
|
error make {
|
||
|
|
msg: $"Security validation failed: ($security_validation.errors | str join '; ')"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Apply interpolation with depth limiting
|
||
|
|
let base_path = ($config | get -o paths.base | default "")
|
||
|
|
if ($base_path | is-not-empty) {
|
||
|
|
interpolate-with-depth-limit $config $base_path $max_depth
|
||
|
|
} else {
|
||
|
|
$config
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Detect unresolved interpolation patterns
|
||
|
|
export def detect-unresolved-patterns [
|
||
|
|
text: string
|
||
|
|
] {
|
||
|
|
# Find patterns that look like interpolation but might not be handled
|
||
|
|
let unknown_patterns = ($text | str replace --regex "\\{\\{([^}]+)\\}\\}" "")
|
||
|
|
|
||
|
|
# Known patterns that should be resolved
|
||
|
|
let known_patterns = [
|
||
|
|
"paths.base" "env\\." "now\\." "git\\." "sops\\." "providers\\." "path\\.join"
|
||
|
|
]
|
||
|
|
|
||
|
|
mut unresolved = []
|
||
|
|
|
||
|
|
# Check for patterns that don't match known types
|
||
|
|
let all_matches = ($text | str replace --regex "\\{\\{([^}]+)\\}\\}" "$1")
|
||
|
|
if ($all_matches | str contains "{{") {
|
||
|
|
# Basic detection - in a real implementation, this would be more sophisticated
|
||
|
|
let potential_unknown = ($text | str replace --regex "\\{\\{(\\w+\\.\\w+)\\}\\}" "")
|
||
|
|
if ($text | str contains "{{unknown.") {
|
||
|
|
$unresolved = ($unresolved | append "unknown.*")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$unresolved
|
||
|
|
}
|
||
|
|
|
||
|
|
# Detect circular interpolation dependencies
|
||
|
|
export def detect-circular-dependencies [
|
||
|
|
text: string
|
||
|
|
] {
|
||
|
|
mut circular_deps = []
|
||
|
|
|
||
|
|
# Simple detection for self-referencing patterns
|
||
|
|
if (($text | str contains "{{paths.base}}") and ($text | str contains "paths.base.*{{paths.base}}")) {
|
||
|
|
$circular_deps = ($circular_deps | append "paths.base -> paths.base")
|
||
|
|
}
|
||
|
|
|
||
|
|
$circular_deps
|
||
|
|
}
|
||
|
|
|
||
|
|
# Detect unsafe environment variable patterns
|
||
|
|
export def detect-unsafe-env-patterns [
|
||
|
|
text: string
|
||
|
|
] {
|
||
|
|
mut unsafe_vars = []
|
||
|
|
|
||
|
|
# Patterns that might be dangerous
|
||
|
|
let dangerous_patterns = ["PATH" "LD_LIBRARY_PATH" "PYTHONPATH" "SHELL" "PS1"]
|
||
|
|
|
||
|
|
for pattern in $dangerous_patterns {
|
||
|
|
if ($text | str contains $"{{env.($pattern)}}") {
|
||
|
|
$unsafe_vars = ($unsafe_vars | append $pattern)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$unsafe_vars
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate git repository context for git interpolations
|
||
|
|
export def validate-git-context [
|
||
|
|
text: string
|
||
|
|
] {
|
||
|
|
if ($text | str contains "{{git.") {
|
||
|
|
# Check if we're in a git repository
|
||
|
|
let git_check = (do { ^git rev-parse --git-dir err> (if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) } | complete)
|
||
|
|
let is_git_repo = ($git_check.exit_code == 0)
|
||
|
|
|
||
|
|
if not $is_git_repo {
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
message: "Git interpolation patterns detected but not in a git repository"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{ valid: true, message: "" }
|
||
|
|
}
|
||
|
|
|
||
|
|
# Count interpolation patterns for metrics
|
||
|
|
export def count-interpolation-patterns [
|
||
|
|
text: string
|
||
|
|
] {
|
||
|
|
# Count all {{...}} patterns by finding matches
|
||
|
|
# Simple approximation: count occurrences of "{{"
|
||
|
|
let pattern_count = ($text | str replace --all "{{" "\n{{" | lines | where ($it | str contains "{{") | length)
|
||
|
|
$pattern_count
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate interpolation security
|
||
|
|
def validate-interpolation-security [
|
||
|
|
config: record
|
||
|
|
allow_unsafe: bool
|
||
|
|
] {
|
||
|
|
mut errors = []
|
||
|
|
let json_str = ($config | to json)
|
||
|
|
|
||
|
|
# Check for code injection patterns
|
||
|
|
let dangerous_patterns = [
|
||
|
|
"\\$\\(" "\\`" "\\;" "\\|\\|" "\\&&" "rm " "sudo " "eval " "exec "
|
||
|
|
]
|
||
|
|
|
||
|
|
for pattern in $dangerous_patterns {
|
||
|
|
if ($json_str =~ $pattern) {
|
||
|
|
$errors = ($errors | append $"Potential code injection pattern detected: ($pattern)")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for unsafe environment variable access
|
||
|
|
if not $allow_unsafe {
|
||
|
|
let unsafe_env_vars = ["PATH" "LD_LIBRARY_PATH" "PYTHONPATH" "PS1" "PROMPT_COMMAND"]
|
||
|
|
for var in $unsafe_env_vars {
|
||
|
|
if ($json_str | str contains $"{{env.($var)}}") {
|
||
|
|
$errors = ($errors | append $"Unsafe environment variable access: ($var)")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check for path traversal attempts
|
||
|
|
if (($json_str | str contains "../") or ($json_str | str contains "..\\")) {
|
||
|
|
$errors = ($errors | append "Path traversal attempt detected")
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
valid: (($errors | length) == 0)
|
||
|
|
errors: $errors
|
||
|
|
}
|
||
|
|
}
|