a7dc917021
ISSUES CLOSED: #319
206 lines
9.1 KiB
Gherkin
206 lines
9.1 KiB
Gherkin
Feature: Secure template rendering
|
|
As a security-conscious developer
|
|
I want templates to be sandboxed and validated
|
|
So that template injection attacks are prevented
|
|
|
|
Background:
|
|
Given a secure template test environment
|
|
|
|
# -- Safe rendering -------------------------------------------------------
|
|
|
|
Scenario: Render a simple placeholder
|
|
Given the secure template text "{greeting} {name}"
|
|
And a secure template context key "greeting" with value "Hello"
|
|
And a secure template context key "name" with value "World"
|
|
When I render the secure template
|
|
Then the secure template output should be "Hello World"
|
|
|
|
Scenario: Render with missing placeholder leaves it intact
|
|
Given the secure template text "Hello {name}, your role is {role}"
|
|
And a secure template context key "name" with value "Alice"
|
|
When I render the secure template
|
|
Then the secure template output should be "Hello Alice, your role is {role}"
|
|
|
|
Scenario: Render with empty context returns template as-is
|
|
Given the secure template text "No placeholders here"
|
|
When I render the secure template
|
|
Then the secure template output should be "No placeholders here"
|
|
|
|
Scenario: Render with numeric values converts to string
|
|
Given the secure template text "Count: {count}"
|
|
And a secure template context key "count" with value "42"
|
|
When I render the secure template
|
|
Then the secure template output should be "Count: 42"
|
|
|
|
# -- Security rejection ---------------------------------------------------
|
|
|
|
Scenario: Reject attribute access in template
|
|
Given the secure template text "{obj.__class__.__name__}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
Scenario: Reject index access in template
|
|
Given the secure template text "{items[0]}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
Scenario: Reject format spec in template
|
|
Given the secure template text "{value:>10}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
Scenario: Reject conversion flag in template
|
|
Given the secure template text "{value!r}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
Scenario: Reject function call in template
|
|
Given the secure template text "{fn()}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
Scenario: Reject Jinja2 expression delimiters
|
|
Given the secure template text "{{ config }}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
Scenario: Reject Jinja2 block delimiters
|
|
Given the secure template text "{% for x in items %}x{% endfor %}"
|
|
When I render the secure template expecting a security error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
# -- Size limits -----------------------------------------------------------
|
|
|
|
Scenario: Reject template exceeding max length
|
|
Given a secure template that is 11000 characters long
|
|
When I render the secure template expecting a size error
|
|
Then the secure template error should mention "exceeds maximum"
|
|
|
|
Scenario: Reject output exceeding max length
|
|
Given the secure template text "{big}"
|
|
And a secure template config with max output length of 20
|
|
And a secure template context key "big" with value "This value is definitely longer than twenty characters"
|
|
When I render the secure template expecting a size error
|
|
Then the secure template error should mention "exceeds maximum"
|
|
|
|
# -- Allowlist enforcement ------------------------------------------------
|
|
|
|
Scenario: Reject template key not in allowlist
|
|
Given the secure template text "{name} {secret}"
|
|
And a secure template with allowed keys "name"
|
|
And a secure template context key "name" with value "Alice"
|
|
And a secure template context key "secret" with value "hidden"
|
|
When I render the secure template expecting a validation error
|
|
Then the secure template error should mention "disallowed"
|
|
|
|
Scenario: Accept template key that is in allowlist
|
|
Given the secure template text "{name}"
|
|
And a secure template with allowed keys "name"
|
|
And a secure template context key "name" with value "Alice"
|
|
When I render the secure template
|
|
Then the secure template output should be "Alice"
|
|
|
|
Scenario: Open allowlist allows any simple key
|
|
Given the secure template text "{anything}"
|
|
And the secure template has no key restrictions
|
|
And a secure template context key "anything" with value "works"
|
|
When I render the secure template
|
|
Then the secure template output should be "works"
|
|
|
|
# -- Pre-validation -------------------------------------------------------
|
|
|
|
Scenario: Validate clean template returns no errors
|
|
Given the secure template text "{name} {role}"
|
|
When I validate the secure template
|
|
Then the secure template validation should be empty
|
|
|
|
Scenario: Validate unsafe template returns errors
|
|
Given the secure template text "{obj.__class__}"
|
|
When I validate the secure template
|
|
Then the secure template validation should not be empty
|
|
And the secure template validation should mention "Unsafe"
|
|
|
|
Scenario: Validate template with unknown keys returns errors
|
|
Given the secure template text "{name} {secret}"
|
|
And a secure template with allowed keys "name"
|
|
When I validate the secure template
|
|
Then the secure template validation should not be empty
|
|
And the secure template validation should mention "Unknown"
|
|
|
|
Scenario: Validate oversized template returns errors
|
|
Given a secure template that is 11000 characters long
|
|
When I validate the secure template
|
|
Then the secure template validation should not be empty
|
|
And the secure template validation should mention "exceeds"
|
|
|
|
# -- Convenience function -------------------------------------------------
|
|
|
|
Scenario: render_template_secure convenience function works
|
|
Given the secure template text "{greeting}"
|
|
And a secure template context key "greeting" with value "Hi"
|
|
When I render via the secure convenience function
|
|
Then the secure template output should be "Hi"
|
|
|
|
Scenario: render_template_secure rejects unsafe input
|
|
Given the secure template text "{obj.attr}"
|
|
When I render via the secure convenience function expecting an error
|
|
Then the secure template error should mention "Unsafe"
|
|
|
|
# -- Config property access ------------------------------------------------
|
|
|
|
Scenario: Renderer exposes active configuration
|
|
Given a secure template with allowed keys "name"
|
|
When I create a renderer with those settings
|
|
Then the renderer config allowed keys should contain "name"
|
|
|
|
# -- Validate with matching allowlist (no unknown keys) -------------------
|
|
|
|
Scenario: Validate template where all keys match allowlist
|
|
Given the secure template text "{name}"
|
|
And a secure template with allowed keys "name"
|
|
When I validate the secure template
|
|
Then the secure template validation should be empty
|
|
|
|
# -- Rejected keys without logging ----------------------------------------
|
|
|
|
Scenario: Reject unknown key without logging when log_rejected is false
|
|
Given the secure template text "{name} {secret}"
|
|
And a secure template config with logging disabled
|
|
And a secure template context key "name" with value "Alice"
|
|
And a secure template context key "secret" with value "hidden"
|
|
When I render the secure template expecting a validation error
|
|
Then the secure template error should mention "disallowed"
|
|
|
|
# -- reject_unknown_keys=False path ----------------------------------------
|
|
|
|
Scenario: Allow unknown keys when reject_unknown_keys is disabled
|
|
Given the secure template text "{name} {extra}"
|
|
And a secure template config that permits unknown keys
|
|
And a secure template context key "name" with value "Alice"
|
|
And a secure template context key "extra" with value "data"
|
|
When I render the secure template
|
|
Then the secure template output should be "Alice data"
|
|
|
|
# -- Legacy renderer with config passthrough --------------------------------
|
|
|
|
Scenario: Legacy TemplateRenderer accepts config passthrough
|
|
Given the secure template text "{name}"
|
|
And a secure template with allowed keys "name"
|
|
And a secure template context key "name" with value "Carol"
|
|
When I render via the legacy TemplateRenderer with config
|
|
Then the secure template output should be "Carol"
|
|
|
|
# -- Legacy renderer backward compat --------------------------------------
|
|
|
|
Scenario: Legacy TemplateRenderer returns raw on unsafe template
|
|
Given the secure template text "{obj.__class__}"
|
|
And a secure template context key "obj" with value "test"
|
|
When I render via the legacy TemplateRenderer
|
|
Then the secure template output should be "{obj.__class__}"
|
|
|
|
Scenario: Legacy TemplateRenderer renders safe templates
|
|
Given the secure template text "{name}"
|
|
And a secure template context key "name" with value "Bob"
|
|
When I render via the legacy TemplateRenderer
|
|
Then the secure template output should be "Bob"
|