Skip to content

Commit

Permalink
fix: django only ValidIdentifiers (#307)
Browse files Browse the repository at this point in the history
* fix: django only ValidIdentifiers

* docs: add note about data binding
  • Loading branch information
sixcolors authored Oct 19, 2023
1 parent a05c35a commit 264008d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 8 deletions.
6 changes: 6 additions & 0 deletions django/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,9 @@ Hello, World!<br /><br />Greetings from Fiber Team
</body>
</html>
```

### 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.
36 changes: 29 additions & 7 deletions django/django.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/gofiber/fiber/v2"
Expand All @@ -16,6 +17,8 @@ import (
"github.com/gofiber/utils"
)

var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$")

// Engine struct
type Engine struct {
core.Engine
Expand Down Expand Up @@ -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 {
Expand Down
20 changes: 19 additions & 1 deletion django/django_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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 := `<h2>Header</h2><h1>Hello, World!</h1><h2>Footer</h2>`
result := trim(buf.String())
require.Equal(t, expect, result)
}

func Test_FileSystem(t *testing.T) {
engine := NewFileSystem(http.Dir("./views"), ".django")
engine.Debug(true)
Expand Down

0 comments on commit 264008d

Please sign in to comment.