Add encoding.HexDecode/Encode

Fixes #15068
See #15060
This commit is contained in:
Bjørn Erik Pedersen
2026-06-28 11:46:26 +02:00
parent 884439b9a2
commit a5ec542393
4 changed files with 137 additions and 0 deletions
+23
View File
@@ -16,6 +16,7 @@ package encoding
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"html/template"
@@ -56,6 +57,28 @@ func (ns *Namespace) Base64Encode(content any) (string, error) {
return base64.StdEncoding.EncodeToString([]byte(conv)), nil
}
// HexDecode returns the hex decoding of the given content.
func (ns *Namespace) HexDecode(content any) (string, error) {
conv, err := cast.ToStringE(content)
if err != nil {
return "", err
}
b, err := hex.DecodeString(conv)
if err != nil {
return "", err
}
return string(b), nil
}
// HexEncode returns the hex encoding of the given content.
func (ns *Namespace) HexEncode(content any) (string, error) {
conv, err := cast.ToStringE(content)
if err != nil {
return "", err
}
return hex.EncodeToString([]byte(conv)), nil
}
// Jsonify encodes a given object to JSON. To pretty print the JSON, pass a map
// or dictionary of options as the first value in args. Supported options are
// "prefix" and "indent". Each JSON element in the output will begin on a new
+54
View File
@@ -77,6 +77,60 @@ func TestBase64Encode(t *testing.T) {
}
}
func TestHexDecode(t *testing.T) {
t.Parallel()
c := qt.New(t)
ns := New()
for _, test := range []struct {
v any
expect any
}{
{"616263313233213f242a2628292d3d407e", "abc123!?$*&()-=@~"},
// errors
{t, false},
} {
result, err := ns.HexDecode(test.v)
if b, ok := test.expect.(bool); ok && !b {
c.Assert(err, qt.Not(qt.IsNil))
continue
}
c.Assert(err, qt.IsNil)
c.Assert(result, qt.Equals, test.expect)
}
}
func TestHexEncode(t *testing.T) {
t.Parallel()
c := qt.New(t)
ns := New()
for _, test := range []struct {
v any
expect any
}{
{"abc123!?$*&()-=@~", "616263313233213f242a2628292d3d407e"},
// errors
{t, false},
} {
result, err := ns.HexEncode(test.v)
if b, ok := test.expect.(bool); ok && !b {
c.Assert(err, qt.Not(qt.IsNil))
continue
}
c.Assert(err, qt.IsNil)
c.Assert(result, qt.Equals, test.expect)
}
}
func TestJsonify(t *testing.T) {
t.Parallel()
c := qt.New(t)
+14
View File
@@ -46,6 +46,20 @@ func init() {
},
)
ns.AddMethodMapping(ctx.HexDecode,
nil,
[][2]string{
{`{{ "48656c6c6f20776f726c64" | encoding.HexDecode }}`, `Hello world`},
},
)
ns.AddMethodMapping(ctx.HexEncode,
nil,
[][2]string{
{`{{ "Hello world" | encoding.HexEncode }}`, `48656c6c6f20776f726c64`},
},
)
ns.AddMethodMapping(ctx.Jsonify,
[]string{"jsonify"},
[][2]string{
@@ -453,3 +453,49 @@ Home. {{ partial "a" }}:Done.
}
}
}
func TestDecoratorFingerprintScript(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org/"
[outputs]
home = ["html", "headers"]
[mediaTypes]
[mediaTypes."text/netlify"]
delimiter = ""
[outputFormats]
[outputFormats.headers]
baseName = "_headers"
isPlainText = true
mediatype = "text/netlify"
notAlternative = true
-- layouts/_partials/script.html --
{{ $s := (templates.Inner .) }}
{{ $r := resources.FromString ($s | xxhash) $s | fingerprint }}
{{ $hash := $s | crypto.SHA256 | encoding.HexDecode | encoding.Base64Encode }}
{{ $sri := (printf "sha256-%s" $hash) }}
{{ if ne $sri $r.Data.Integrity }}
{{ errorf "SRI hash mismatch: %s != %s" $sri $r.Data.Integrity }}
{{ end }}
{{ hugo.Store.SetInMap "srihashes" $sri true }}
<script>{{ $s | safeJS }}</script>
-- layouts/home.html --
Home.
{{ with partial "script.html" . }}
console.log("Hello world");
{{ end }}
{{ with partial "script.html" . }}
console.log("Hugo rocks!");
{{ end }}
-- layouts/home.headers --
{{ $hashes := hugo.Store.Get "srihashes" -}}
Content-Security-Policy: script-src 'self'{{ range $k, $v := $hashes }} '{{ $k }}'{{ end }};
`
b := hugolib.Test(t, files)
b.AssertFileContentExact("public/index.html", "<script>\nconsole.log(\"Hello world\");\n</script>")
b.AssertFileContentExact("public/_headers", "Content-Security-Policy: script-src 'self' 'sha256-t6z9j4/CagmaDDbn6nHbntzt8PRk8HZ1TwrgdYuZjbE=' 'sha256-wVbS8f78ttoSSGU80KghGoPcTO8DXqT0Uc/87a7zMdk=';")
}