templates: escape query value in ping page
elem-go does not escape attribute values, so the raw query reaches the rendered HTML verbatim. Pre-escape with html.EscapeString to prevent reflected XSS. Updates #3157
This commit is contained in:
parent
3a4af8cf87
commit
f3eb9a7bba
@ -361,7 +361,6 @@ func (h *Headscale) debugHTTPServer() *http.Server {
|
|||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
//nolint:gosec // elem-go auto-escapes all attribute values; no XSS risk.
|
|
||||||
_, _ = w.Write([]byte(templates.PingPage(query, result, nodes).Render()))
|
_, _ = w.Write([]byte(templates.PingPage(query, result, nodes).Render()))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package templates
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ func pingForm(query string) *elem.Element {
|
|||||||
elem.Input(attrs.Props{
|
elem.Input(attrs.Props{
|
||||||
attrs.Type: "text",
|
attrs.Type: "text",
|
||||||
attrs.Name: "node",
|
attrs.Name: "node",
|
||||||
attrs.Value: query,
|
attrs.Value: html.EscapeString(query),
|
||||||
attrs.Placeholder: "Node ID, IP, or hostname",
|
attrs.Placeholder: "Node ID, IP, or hostname",
|
||||||
attrs.Autofocus: "true",
|
attrs.Autofocus: "true",
|
||||||
attrs.Style: styles.Props{
|
attrs.Style: styles.Props{
|
||||||
|
|||||||
26
hscontrol/templates/ping_test.go
Normal file
26
hscontrol/templates/ping_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestPingPageEscapesQuery asserts hostile query values cannot break out of
|
||||||
|
// the input's value attribute. elem-go does not escape attribute values, so
|
||||||
|
// the template must escape before rendering.
|
||||||
|
func TestPingPageEscapesQuery(t *testing.T) {
|
||||||
|
payloads := []string{
|
||||||
|
`" autofocus onfocus=alert(1) x="`,
|
||||||
|
`"><script>alert(1)</script>`,
|
||||||
|
`<img src=x onerror=alert(1)>`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range payloads {
|
||||||
|
t.Run(p, func(t *testing.T) {
|
||||||
|
out := PingPage(p, nil, nil).Render()
|
||||||
|
if strings.Contains(out, p) {
|
||||||
|
t.Fatalf("unescaped payload rendered verbatim: %q", p)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user