Class: Sxn::Templates::TemplateSecurity
- Inherits:
-
Object
- Object
- Sxn::Templates::TemplateSecurity
- Defined in:
- lib/sxn/templates/template_security.rb
Overview
TemplateSecurity provides security validation and sanitization for templates. It ensures that templates cannot execute arbitrary code, access the filesystem, or perform other potentially dangerous operations.
Security Features:
-
Whitelist-based variable validation
-
Content sanitization to prevent injection
-
Path traversal prevention
-
Size and complexity limits
-
Execution time limits
Constant Summary collapse
- MAX_TEMPLATE_DEPTH =
Maximum allowed template complexity (nested structures)
10- MAX_VARIABLE_COUNT =
Maximum number of variables allowed in a template
1000- DANGEROUS_PATTERNS =
Dangerous patterns that should not appear in templates
[ # Ruby code execution - look for actual method calls, not just words /\b(?:eval|exec|system|spawn|fork)\s*[\(\[]/, /\b(?:require|load|autoload)\s*[\(\['"]/, # File/IO operations - look for actual usage, not just the words /\b(?:File|Dir|IO|Kernel|Process|Thread)\s*\./, /\b(?:File|Dir|IO)\.(?:open|read|write|delete)/, # Shell injection patterns - be very specific # Removed backtick check as it causes too many false positives with markdown # Liquid doesn't execute Ruby code directly anyway /%x\{[^}]*\}/, # %x{} command execution /\bsystem\s*\(/, # Direct system calls /%x[{\[]/, # Alternative command execution syntax (removed \b since % is not a word char) # Web security patterns /<script\b[^>]*>/i, # Script tags /javascript:/i, # JavaScript protocols /on\w+\s*=/i, # Event handlers # Liquid-specific dangerous patterns /\{\{.*\|\s*(?:eval|exec|system)\s*\}\}/, # Piped to dangerous filters /\{\{\s*(?:eval|exec|system)\s*\(/, # Direct calls to dangerous functions /\{%\s*(?:eval|exec)\b/, # Liquid eval/exec commands # Ruby metaprogramming that could be dangerous /\bsend\s*\(/, /\b__send__\s*\(/, /\bpublic_send\s*\(/, /\binstance_eval\b/, /\bclass_eval\b/, /\bmodule_eval\b/, # File system access patterns /\{\{\s*file\.(?:read|write|delete)\b/i, /\{%\s*(?:write_file|delete)\b/i, /\{\{\s*delete\s*\(/i ].freeze
- ALLOWED_VARIABLE_NAMESPACES =
Whitelisted variable namespaces
%w[ session git project environment user timestamp ruby rails node database os ].freeze
- SAFE_FILTERS =
Whitelisted filters (subset of Liquid’s standard filters)
%w[ upcase downcase capitalize strip lstrip rstrip size length first last join split sort sort_natural reverse uniq compact date default escape escape_once truncate truncatewords replace replace_first remove remove_first plus minus times divided_by modulo abs ceil floor round at_least at_most ].freeze
Instance Method Summary collapse
-
#clear_cache! ⇒ Object
Clear validation cache (useful for testing).
-
#initialize ⇒ TemplateSecurity
constructor
A new instance of TemplateSecurity.
-
#safe_filter?(filter_name) ⇒ Boolean
Validate that a filter is safe to use.
-
#sanitize_variables(variables) ⇒ Hash
Sanitize template variables to remove potentially dangerous content.
-
#validate_template(template_content, variables = {}) ⇒ Boolean
Validate template content for security issues.
-
#validate_template_content(template_content) ⇒ Object
Validate template content for dangerous patterns (public version for tests).
-
#validate_template_path(template_path) ⇒ Boolean
Validate template path for security issues.
Constructor Details
#initialize ⇒ TemplateSecurity
Returns a new instance of TemplateSecurity.
98 99 100 |
# File 'lib/sxn/templates/template_security.rb', line 98 def initialize @validation_cache = {} end |
Instance Method Details
#clear_cache! ⇒ Object
Clear validation cache (useful for testing)
168 169 170 |
# File 'lib/sxn/templates/template_security.rb', line 168 def clear_cache! @validation_cache.clear end |
#safe_filter?(filter_name) ⇒ Boolean
Validate that a filter is safe to use
163 164 165 |
# File 'lib/sxn/templates/template_security.rb', line 163 def safe_filter?(filter_name) SAFE_FILTERS.include?(filter_name.to_s) end |
#sanitize_variables(variables) ⇒ Hash
Sanitize template variables to remove potentially dangerous content
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/sxn/templates/template_security.rb', line 138 def sanitize_variables(variables) # First check total variable count before processing total_variables = count_total_variables(variables) if total_variables > MAX_VARIABLE_COUNT raise Errors::TemplateSecurityError, "Too many variables: #{total_variables} exceeds limit of #{MAX_VARIABLE_COUNT}" end sanitized = {} variables.each do |key, value| sanitized_key = sanitize_variable_key(key) next unless valid_variable_namespace?(sanitized_key) sanitized_value = sanitize_variable_value(value, depth: 0) sanitized[sanitized_key] = sanitized_value end sanitized end |
#validate_template(template_content, variables = {}) ⇒ Boolean
Validate template content for security issues
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/sxn/templates/template_security.rb', line 108 def validate_template(template_content, variables = {}) # Check cache first cache_key = generate_cache_key(template_content, variables) if @validation_cache.key?(cache_key) cached_result = @validation_cache[cache_key] raise Errors::TemplateSecurityError, "Cached validation error for template" if cached_result == false # Re-raise cached error without re-validating return cached_result end begin result = validate_template_content(template_content) validate_template_variables(variables) validate_template_complexity(template_content) @validation_cache[cache_key] = result result rescue Errors::TemplateSecurityError => e @validation_cache[cache_key] = false raise e end end |
#validate_template_content(template_content) ⇒ Object
Validate template content for dangerous patterns (public version for tests)
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/sxn/templates/template_security.rb', line 173 def validate_template_content(template_content) DANGEROUS_PATTERNS.each do |pattern| next unless template_content.match?(pattern) raise Errors::TemplateSecurityError, "Template contains dangerous pattern: #{pattern.source}" end # Check for path traversal attempts if template_content.include?("../") || template_content.include?("..\\") raise Errors::TemplateSecurityError, "Template contains path traversal attempt" end # Check for file system access attempts - be more specific # Look for actual File/Dir method calls, not just the words if template_content.match?(/\{\{\s*.*(?:File|Dir|IO)\.(?:read|write|delete|create|open).*\s*\}\}/) raise Errors::TemplateSecurityError, "Template attempts file system access" end true end |
#validate_template_path(template_path) ⇒ Boolean
Validate template path for security issues
394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/sxn/templates/template_security.rb', line 394 def validate_template_path(template_path) path = Pathname.new(template_path) # Check for path traversal attempts normalized_path = path..to_s if normalized_path.include?("..") || normalized_path.include?("~") raise Errors::TemplateSecurityError, "Template path contains traversal attempt: #{template_path}" end # Check that the file exists and is readable raise Errors::TemplateSecurityError, "Template path is not accessible: #{template_path}" unless path.exist? && path.readable? true end |