builtins: Add StringOperandByteSlice helper

Many built-in functions immediatley convert an `ast.String` into
`[]byte` which comes with some cost. This change introduces a
`StringOperandByteSlice` function, which IMHO better helps communicate
purpose, as well as it makes these operations a little cheaper. Nothing
major here, but a small improvement.

Also:
- Cleaner `sync.Pool` helper module
- Add benchmarks for YAML marshalling/unmarshalling

Signed-off-by: Anders Eknert <anders@eknert.com>
This commit is contained in:
Anders Eknert
2025-11-11 22:56:26 +01:00
committed by Stephan Renatus
parent 066ca43b74
commit fd75706d7c
8 changed files with 181 additions and 177 deletions
+49 -60
View File
@@ -1,32 +1,63 @@
package ast
import (
"bytes"
"strings"
"sync"
)
type termPtrPool struct {
pool sync.Pool
var (
TermPtrPool = NewSyncPool[Term]()
BytesReaderPool = NewSyncPool[bytes.Reader]()
IndexResultPool = NewSyncPool[IndexResult]()
// Needs custom pool because of custom Put logic.
sbPool = &stringBuilderPool{
pool: sync.Pool{
New: func() any {
return &strings.Builder{}
},
},
}
// Needs custom pool because of custom Put logic.
varVisitorPool = &vvPool{
pool: sync.Pool{
New: func() any {
return NewVarVisitor()
},
},
}
)
type (
syncPool[T any] struct {
pool sync.Pool
}
stringBuilderPool struct {
pool sync.Pool
}
vvPool struct {
pool sync.Pool
}
)
func NewSyncPool[T any]() *syncPool[T] {
return &syncPool[T]{
pool: sync.Pool{
New: func() any {
return new(T)
},
},
}
}
type stringBuilderPool struct {
pool sync.Pool
func (p *syncPool[T]) Get() *T {
return p.pool.Get().(*T)
}
type indexResultPool struct {
pool sync.Pool
}
type vvPool struct {
pool sync.Pool
}
func (p *termPtrPool) Get() *Term {
return p.pool.Get().(*Term)
}
func (p *termPtrPool) Put(t *Term) {
p.pool.Put(t)
func (p *syncPool[T]) Put(x *T) {
if x != nil {
p.pool.Put(x)
}
}
func (p *stringBuilderPool) Get() *strings.Builder {
@@ -38,16 +69,6 @@ func (p *stringBuilderPool) Put(sb *strings.Builder) {
p.pool.Put(sb)
}
func (p *indexResultPool) Get() *IndexResult {
return p.pool.Get().(*IndexResult)
}
func (p *indexResultPool) Put(x *IndexResult) {
if x != nil {
p.pool.Put(x)
}
}
func (p *vvPool) Get() *VarVisitor {
return p.pool.Get().(*VarVisitor)
}
@@ -58,35 +79,3 @@ func (p *vvPool) Put(vv *VarVisitor) {
p.pool.Put(vv)
}
}
var TermPtrPool = &termPtrPool{
pool: sync.Pool{
New: func() any {
return &Term{}
},
},
}
var sbPool = &stringBuilderPool{
pool: sync.Pool{
New: func() any {
return &strings.Builder{}
},
},
}
var varVisitorPool = &vvPool{
pool: sync.Pool{
New: func() any {
return NewVarVisitor()
},
},
}
var IndexResultPool = &indexResultPool{
pool: sync.Pool{
New: func() any {
return &IndexResult{}
},
},
}
+12 -2
View File
@@ -209,8 +209,7 @@ func SetOperand(x ast.Value, pos int) (ast.Set, error) {
return s, nil
}
// StringOperand converts x to a string. If the cast fails, a descriptive error is
// returned.
// StringOperand returns x as [ast.String], or a descriptive error if the conversion fails.
func StringOperand(x ast.Value, pos int) (ast.String, error) {
s, ok := x.(ast.String)
if !ok {
@@ -219,6 +218,17 @@ func StringOperand(x ast.Value, pos int) (ast.String, error) {
return s, nil
}
// StringOperandByteSlice returns x a []byte, assuming x is [ast.String], or a descriptive error
// if that is not the case. The returned byte slice points directly at the underlying array backing
// the string, and should not be modified.
func StringOperandByteSlice(x ast.Value, pos int) ([]byte, error) {
s, err := StringOperand(x, pos)
if err != nil {
return nil, err
}
return util.StringToByteSlice(string(s)), nil
}
// ObjectOperand converts x to an object. If the cast fails, a descriptive
// error is returned.
func ObjectOperand(x ast.Value, pos int) (ast.Object, error) {
+20 -48
View File
@@ -255,17 +255,17 @@ func extractVerifyOpts(options ast.Object) (verifyOpt x509.VerifyOptions, err er
}
func builtinCryptoX509ParseKeyPair(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
certificate, err := builtins.StringOperand(operands[0].Value, 1)
certificate, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
key, err := builtins.StringOperand(operands[1].Value, 1)
key, err := builtins.StringOperandByteSlice(operands[1].Value, 1)
if err != nil {
return err
}
certs, err := getTLSx509KeyPairFromString([]byte(certificate), []byte(key))
certs, err := getTLSx509KeyPairFromString(certificate, key)
if err != nil {
return err
}
@@ -326,10 +326,7 @@ func builtinCryptoX509ParseCertificateRequest(_ BuiltinContext, operands []*ast.
}
func builtinCryptoJWKFromPrivateKey(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
var x any
a := operands[0].Value
input, err := builtins.StringOperand(a, 1)
input, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
}
@@ -371,6 +368,7 @@ func builtinCryptoJWKFromPrivateKey(_ BuiltinContext, operands []*ast.Term, iter
return err
}
var x any
if err := util.UnmarshalJSON(jsonKey, &x); err != nil {
return err
}
@@ -430,53 +428,51 @@ func toHexEncodedString(src []byte) string {
}
func builtinCryptoMd5(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
md5sum := md5.Sum([]byte(s))
md5sum := md5.Sum(bs)
return iter(ast.StringTerm(toHexEncodedString(md5sum[:])))
}
func builtinCryptoSha1(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
sha1sum := sha1.Sum([]byte(s))
sha1sum := sha1.Sum(bs)
return iter(ast.StringTerm(toHexEncodedString(sha1sum[:])))
}
func builtinCryptoSha256(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
sha256sum := sha256.Sum256([]byte(s))
sha256sum := sha256.Sum256(bs)
return iter(ast.StringTerm(toHexEncodedString(sha256sum[:])))
}
func hmacHelper(operands []*ast.Term, iter func(*ast.Term) error, h func() hash.Hash) error {
a1 := operands[0].Value
message, err := builtins.StringOperand(a1, 1)
message, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
a2 := operands[1].Value
key, err := builtins.StringOperand(a2, 2)
key, err := builtins.StringOperandByteSlice(operands[1].Value, 2)
if err != nil {
return err
}
mac := hmac.New(h, []byte(key))
mac.Write([]byte(message))
mac := hmac.New(h, key)
mac.Write(message)
messageDigest := mac.Sum(nil)
return iter(ast.StringTerm(hex.EncodeToString(messageDigest)))
@@ -499,21 +495,17 @@ func builtinCryptoHmacSha512(_ BuiltinContext, operands []*ast.Term, iter func(*
}
func builtinCryptoHmacEqual(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
a1 := operands[0].Value
mac1, err := builtins.StringOperand(a1, 1)
mac1, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
a2 := operands[1].Value
mac2, err := builtins.StringOperand(a2, 2)
mac2, err := builtins.StringOperandByteSlice(operands[1].Value, 2)
if err != nil {
return err
}
res := hmac.Equal([]byte(mac1), []byte(mac2))
return iter(ast.InternedTerm(res))
return iter(ast.InternedTerm(hmac.Equal(mac1, mac2)))
}
func init() {
@@ -668,7 +660,7 @@ func addCACertsFromFile(pool *x509.CertPool, filePath string) (*x509.CertPool, e
pool = x509.NewCertPool()
}
caCert, err := readCertFromFile(filePath)
caCert, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
@@ -703,17 +695,7 @@ func addCACertsFromEnv(pool *x509.CertPool, envName string) (*x509.CertPool, err
return nil, fmt.Errorf("could not add CA certificates from envvar %q: %w", envName, err)
}
return pool, err
}
// ReadCertFromFile reads a cert from file
func readCertFromFile(localCertFile string) ([]byte, error) {
// Read in the cert file
certPEM, err := os.ReadFile(localCertFile)
if err != nil {
return nil, err
}
return certPEM, nil
return pool, nil
}
var beginPrefix = []byte("-----BEGIN ")
@@ -771,13 +753,3 @@ func getTLSx509KeyPairFromString(certPemBlock []byte, keyPemBlock []byte) (*tls.
return &cert, nil
}
// ReadKeyFromFile reads a key from file
func readKeyFromFile(localKeyFile string) ([]byte, error) {
// Read in the cert file
key, err := os.ReadFile(localKeyFile)
if err != nil {
return nil, err
}
return key, nil
}
+29 -41
View File
@@ -5,7 +5,6 @@
package topdown
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
@@ -21,7 +20,6 @@ import (
)
func builtinJSONMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
asJSON, err := ast.JSON(operands[0].Value)
if err != nil {
return err
@@ -32,11 +30,10 @@ func builtinJSONMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.T
return err
}
return iter(ast.StringTerm(string(bs)))
return iter(ast.StringTerm(util.ByteSliceToString(bs)))
}
func builtinJSONMarshalWithOpts(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
asJSON, err := ast.JSON(operands[0].Value)
if err != nil {
return err
@@ -101,36 +98,34 @@ func builtinJSONMarshalWithOpts(_ BuiltinContext, operands []*ast.Term, iter fun
}
var bs []byte
if shouldPrettyPrint {
bs, err = json.MarshalIndent(asJSON, prefixWith, indentWith)
} else {
bs, err = json.Marshal(asJSON)
}
if err != nil {
return err
}
s := util.ByteSliceToString(bs)
if shouldPrettyPrint {
// json.MarshalIndent() function will not prefix the first line of emitted JSON
return iter(ast.StringTerm(prefixWith + string(bs)))
return iter(ast.StringTerm(prefixWith + s))
}
return iter(ast.StringTerm(string(bs)))
return iter(ast.StringTerm(s))
}
func builtinJSONUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
var x any
if err := util.UnmarshalJSON([]byte(str), &x); err != nil {
if err := util.UnmarshalJSON(bs, &x); err != nil {
return err
}
v, err := ast.InterfaceToValue(x)
@@ -141,22 +136,21 @@ func builtinJSONUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast
}
func builtinJSONIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return iter(ast.InternedTerm(false))
}
return iter(ast.InternedTerm(json.Valid([]byte(str))))
return iter(ast.InternedTerm(json.Valid(bs)))
}
func builtinBase64Encode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
return iter(ast.StringTerm(base64.StdEncoding.EncodeToString([]byte(str))))
return iter(ast.StringTerm(base64.StdEncoding.EncodeToString(bs)))
}
func builtinBase64Decode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
@@ -183,20 +177,20 @@ func builtinBase64IsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast
}
func builtinBase64UrlEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
return iter(ast.StringTerm(base64.URLEncoding.EncodeToString([]byte(str))))
return iter(ast.StringTerm(base64.URLEncoding.EncodeToString(bs)))
}
func builtinBase64UrlEncodeNoPad(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
return iter(ast.StringTerm(base64.RawURLEncoding.EncodeToString([]byte(str))))
return iter(ast.StringTerm(base64.RawURLEncoding.EncodeToString(bs)))
}
func builtinBase64UrlDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
@@ -306,45 +300,39 @@ func builtinURLQueryDecodeObject(_ BuiltinContext, operands []*ast.Term, iter fu
}
func builtinYAMLMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
asJSON, err := ast.JSON(operands[0].Value)
if err != nil {
return err
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
if err := encoder.Encode(asJSON); err != nil {
return err
}
bs, err := yaml.JSONToYAML(buf.Bytes())
bs, err := yaml.Marshal(asJSON)
if err != nil {
return err
}
return iter(ast.StringTerm(string(bs)))
return iter(ast.StringTerm(util.ByteSliceToString(bs)))
}
func builtinYAMLUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
bs, err := yaml.YAMLToJSON([]byte(str))
js, err := yaml.YAMLToJSON(bs)
if err != nil {
return err
}
buf := bytes.NewBuffer(bs)
decoder := util.NewJSONDecoder(buf)
reader := ast.BytesReaderPool.Get()
defer ast.BytesReaderPool.Put(reader)
reader.Reset(js)
var val any
err = decoder.Decode(&val)
if err != nil {
if err = util.NewJSONDecoder(reader).Decode(&val); err != nil {
return err
}
v, err := ast.InterfaceToValue(val)
if err != nil {
return err
@@ -353,22 +341,22 @@ func builtinYAMLUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast
}
func builtinYAMLIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return iter(ast.InternedTerm(false))
}
var x any
err = yaml.Unmarshal([]byte(str), &x)
err = yaml.Unmarshal(bs, &x)
return iter(ast.InternedTerm(err == nil))
}
func builtinHexEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
str, err := builtins.StringOperand(operands[0].Value, 1)
bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1)
if err != nil {
return err
}
return iter(ast.StringTerm(hex.EncodeToString([]byte(str))))
return iter(ast.StringTerm(hex.EncodeToString(bs)))
}
func builtinHexDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
+49
View File
@@ -0,0 +1,49 @@
package topdown
import (
"testing"
"github.com/open-policy-agent/opa/v1/ast"
)
var (
unmarshalled = ast.ObjectTerm(
ast.Item(ast.InternedTerm("foo"), ast.ObjectTerm(
ast.Item(ast.InternedTerm("bar"), ast.ArrayTerm(ast.InternedTerm("baz"), ast.InternedTerm("qux"))),
ast.Item(ast.InternedTerm("num"), ast.InternedTerm(42)),
)),
)
marshalled = `foo:
bar:
- baz
- qux
num: 42
`
)
// 7447 ns/op 22984 B/op 142 allocs/op
// 7343 ns/op 22872 B/op 140 allocs/op
func BenchmarkYAMLMarshal(b *testing.B) {
expect := ast.InternedTerm(marshalled)
operands := []*ast.Term{unmarshalled}
iter := eqIter(expect)
for b.Loop() {
if err := builtinYAMLMarshal(BuiltinContext{}, operands, iter); err != nil {
b.Fatal(err)
}
}
}
// 5393 ns/op 11066 B/op 146 allocs/op
// 5210 ns/op 10980 B/op 144 allocs/op
func BenchmarkYAMLUnmarshal(b *testing.B) {
operands := []*ast.Term{ast.InternedTerm(marshalled)}
iter := eqIter(unmarshalled)
for b.Loop() {
if err := builtinYAMLUnmarshal(BuiltinContext{}, operands, iter); err != nil {
b.Fatal(err)
}
}
}
+2 -2
View File
@@ -2440,13 +2440,13 @@ func TestHTTPSClient(t *testing.T) {
}
// Set up Environment
clientCert, err := readCertFromFile(localClientCertFile)
clientCert, err := os.ReadFile(localClientCertFile)
if err != nil {
t.Fatal(err)
}
t.Setenv("CLIENT_CERT_ENV", string(clientCert))
clientKey, err := readKeyFromFile(localClientKeyFile)
clientKey, err := os.ReadFile(localClientKeyFile)
if err != nil {
t.Fatal(err)
}
+6 -10
View File
@@ -7,6 +7,7 @@ package topdown
import (
"fmt"
"regexp"
"regexp/syntax"
"sync"
gintersect "github.com/yashtewari/glob-intersection"
@@ -22,18 +23,13 @@ var regexpCacheLock = sync.Mutex{}
var regexpCache map[string]*regexp.Regexp
func builtinRegexIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return iter(ast.InternedTerm(false))
if s, err := builtins.StringOperand(operands[0].Value, 1); err == nil {
if _, err = syntax.Parse(string(s), syntax.Perl); err == nil {
return iter(ast.InternedTerm(true))
}
}
_, err = regexp.Compile(string(s))
if err != nil {
return iter(ast.InternedTerm(false))
}
return iter(ast.InternedTerm(true))
return iter(ast.InternedTerm(false))
}
func builtinRegexMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
+14 -14
View File
@@ -428,7 +428,7 @@ func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hash
// If a match is found, verify using only that key. Only applicable when a JWKS was provided.
if header.kid != "" {
if key := getKeyByKid(header.kid, keys); key != nil {
err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature))
err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), signature)
return done(err == nil)
}
@@ -440,7 +440,7 @@ func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hash
if key.alg == "" {
// No algorithm provided for the key - this is likely a certificate and not a JWKS, so
// we'll need to verify to find out
err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature))
err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), signature)
if err == nil {
return done(true)
}
@@ -448,7 +448,7 @@ func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hash
if header.alg != key.alg {
continue
}
err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature))
err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), signature)
if err == nil {
return done(true)
}
@@ -509,7 +509,7 @@ func builtinJWTVerifyHS(bctx BuiltinContext, operands []*ast.Term, hashF func()
return err
}
valid := hmac.Equal([]byte(signature), mac.Sum(nil))
valid := hmac.Equal(signature, mac.Sum(nil))
putTokenInCache(bctx, jwt, astSecret, nil, nil, valid)
@@ -662,7 +662,7 @@ func (constraints *tokenConstraints) validate() error {
}
// verify verifies a JWT using the constraints and the algorithm from the header
func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature string) error {
func (constraints *tokenConstraints) verify(kid, alg, header, payload string, signature []byte) error {
// Construct the payload
plaintext := append(append([]byte(header), '.'), []byte(payload)...)
@@ -670,7 +670,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature
if constraints.keys != nil {
if kid != "" {
if key := getKeyByKid(kid, constraints.keys); key != nil {
err := jwsbb.Verify(key.key, alg, plaintext, []byte(signature))
err := jwsbb.Verify(key.key, alg, plaintext, signature)
if err != nil {
return errSignatureNotVerified
}
@@ -681,7 +681,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature
verified := false
for _, key := range constraints.keys {
if key.alg == "" {
err := jwsbb.Verify(key.key, alg, plaintext, []byte(signature))
err := jwsbb.Verify(key.key, alg, plaintext, signature)
if err == nil {
verified = true
break
@@ -690,7 +690,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature
if alg != key.alg {
continue
}
err := jwsbb.Verify(key.key, alg, plaintext, []byte(signature))
err := jwsbb.Verify(key.key, alg, plaintext, signature)
if err == nil {
verified = true
break
@@ -704,7 +704,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature
return nil
}
if constraints.secret != "" {
err := jwsbb.Verify([]byte(constraints.secret), alg, plaintext, []byte(signature))
err := jwsbb.Verify([]byte(constraints.secret), alg, plaintext, signature)
if err != nil {
return errSignatureNotVerified
}
@@ -1170,17 +1170,17 @@ func decodeJWT(a ast.Value) (*JSONWebToken, error) {
return &JSONWebToken{header: parts[0], payload: parts[1], signature: parts[2]}, nil
}
func (token *JSONWebToken) decodeSignature() (string, error) {
func (token *JSONWebToken) decodeSignature() ([]byte, error) {
decodedSignature, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.signature))
if err != nil {
return "", err
return nil, err
}
signatureAst, err := builtins.StringOperand(decodedSignature.Value, 1)
signatureBs, err := builtins.StringOperandByteSlice(decodedSignature.Value, 1)
if err != nil {
return "", err
return nil, err
}
return string(signatureAst), err
return signatureBs, nil
}
// Extract, validate and return the JWT header as an ast.Object.