From 264008da96acdf5de3bcd33daf533d82447be4f7 Mon Sep 17 00:00:00 2001 From: Jason McNeil Date: Thu, 19 Oct 2023 19:34:03 -0300 Subject: [PATCH] fix: django only ValidIdentifiers (#307) * fix: django only ValidIdentifiers * docs: add note about data binding --- django/README.md | 6 ++++++ django/django.go | 36 +++++++++++++++++++++++++++++------- django/django_test.go | 20 +++++++++++++++++++- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/django/README.md b/django/README.md index c8f33e72..8f65a55f 100644 --- a/django/README.md +++ b/django/README.md @@ -181,3 +181,9 @@ Hello, World!

Greetings from Fiber Team ``` + +### Important Information on Template Data Binding + +When working with Pongo2 and this template engine, it's crucial to understand the specific rules for data binding. Only keys that match the following regular expression are supported: `^[a-zA-Z0-9_]+$`. + +This means that keys with special characters or punctuation, such as `my-key` or `my.key`, are not compatible and will not be bound to the template. This is a restriction imposed by the underlying Pongo2 template engine. Please ensure your keys adhere to these rules to avoid any binding issues. \ No newline at end of file diff --git a/django/django.go b/django/django.go index 4cb50c47..b0bd8125 100644 --- a/django/django.go +++ b/django/django.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "path/filepath" + "regexp" "strings" "github.com/gofiber/fiber/v2" @@ -16,6 +17,8 @@ import ( "github.com/gofiber/utils" ) +var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$") + // Engine struct type Engine struct { core.Engine @@ -155,26 +158,45 @@ func (e *Engine) Load() error { return filepath.Walk(e.Directory, walkFn) } +// getPongoBinding creates a pongo2.Context containing +// only valid identifiers from a binding interface. +// +// It supports the following types: +// - pongo2.Context +// - map[string]interface{} +// - fiber.Map +// +// It returns nil if the binding is not one of the supported types. func getPongoBinding(binding interface{}) pongo2.Context { if binding == nil { return nil } switch binds := binding.(type) { case pongo2.Context: - return binds + return createBindFromMap(binds) case map[string]interface{}: - return binds + return createBindFromMap(binds) case fiber.Map: - bind := make(pongo2.Context) - for key, value := range binds { - bind[key] = value - } - return bind + return createBindFromMap(binds) } return nil } +// createBindFromMap creates a pongo2.Context containing +// only valid identifiers from a map. +func createBindFromMap(binds map[string]interface{}) pongo2.Context { + bind := make(pongo2.Context) + for key, value := range binds { + if !reIdentifiers.MatchString(key) { + // Skip invalid identifiers + continue + } + bind[key] = value + } + return bind +} + // Render will render the template by name func (e *Engine) Render(out io.Writer, name string, binding interface{}, layout ...string) error { if !e.Loaded || e.ShouldReload { diff --git a/django/django_test.go b/django/django_test.go index ab911e06..d5693cd8 100644 --- a/django/django_test.go +++ b/django/django_test.go @@ -4,12 +4,13 @@ package django import ( "bytes" "embed" - "github.com/stretchr/testify/require" "net/http" "os" "regexp" "strings" "testing" + + "github.com/stretchr/testify/require" ) const ( @@ -82,6 +83,23 @@ func Test_Empty_Layout(t *testing.T) { require.Equal(t, expect, result) } +func Test_Invalid_Identifiers(t *testing.T) { + engine := New("./views", ".django") + engine.Debug(true) + require.NoError(t, engine.Load()) + + var buf bytes.Buffer + err := engine.Render(&buf, "index", map[string]interface{}{ + "Title": "Hello, World!", + "Invalid.Identifiers": "Don't return error from checkForValidIdentifiers!", + }, "") + require.NoError(t, err) + + expect := `

Header

Hello, World!

Footer

` + result := trim(buf.String()) + require.Equal(t, expect, result) +} + func Test_FileSystem(t *testing.T) { engine := NewFileSystem(http.Dir("./views"), ".django") engine.Debug(true)