-
Notifications
You must be signed in to change notification settings - Fork 0
/
helpers.go
96 lines (81 loc) · 2.49 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package templiconoir
import (
"fmt"
"html"
"sort"
"strings"
"github.com/a-h/templ"
)
// Reserved attributes for SVG tags that should not be overwritten.
var reservedSVGAttributes = map[string]struct{}{
"xmlns": {},
"viewBox": {},
"width": {},
"height": {},
"stroke-width": {},
"stroke": {},
"fill": {},
}
// sanitizeAttribute ensures that attribute keys and values are safe for inclusion in the SVG tag.
func sanitizeAttribute(key, value string) (string, string, bool) {
// Define allowlist for event attributes
allowedEventAttributes := map[string]struct{}{
"onclick": {},
"onchange": {},
"onhover": {},
}
// Check for unsafe attributes
if _, isEvent := allowedEventAttributes[key]; isEvent {
// For event attributes, only allow simple JS functions (no <script> tags, eval, etc.)
if strings.Contains(strings.ToLower(value), "<script>") || strings.Contains(strings.ToLower(value), "javascript:") {
return "", "", false // Unsafe value
}
}
// Escape any unsafe characters for all attributes
escapedKey := html.EscapeString(key)
escapedValue := html.EscapeString(value)
return escapedKey, escapedValue, true // Safe attribute
}
// addAttributesToSVG adds templ.Attributes to the SVG tag, placing them at the end of the <svg> opening tag.
// Reserved attributes are skipped to avoid overwriting critical SVG settings.
// Attributes are sanitized to prevent XSS or injection attacks.
func addAttributesToSVG(builder *strings.Builder, attrs templ.Attributes) {
if len(attrs) == 0 {
return
}
// Extract keys and sort them for deterministic order
keys := make([]string, 0, len(attrs))
for key := range attrs {
keys = append(keys, key)
}
sort.Strings(keys)
// Process attributes in sorted order
for _, key := range keys {
value, ok := attrs[key].(string) // Ensure value is a string
if !ok {
// Skip attributes with non-string values
continue
}
// Skip reserved attributes
if _, isReserved := reservedSVGAttributes[key]; isReserved {
continue
}
// Sanitize the attribute
sanitizedKey, sanitizedValue, ok := sanitizeAttribute(key, value)
if !ok {
// Skip attributes that are not safe
continue
}
// Add the sanitized attribute to the SVG tag
fmt.Fprintf(builder, ` %s="%v"`, sanitizedKey, sanitizedValue)
}
}
func defaultIfEmpty(value, defaultValue string) string {
if value == "" {
return defaultValue
}
return value
}
func errorSVGComment(err error) string {
return fmt.Sprintf("<!-- Error: %s -->", err)
}