Skip to content

Commit

Permalink
Add base64-decode operator
Browse files Browse the repository at this point in the history
  • Loading branch information
riendeau authored and geofffranks committed Sep 15, 2023
1 parent d44852c commit b877834
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
17 changes: 17 additions & 0 deletions doc/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- [awsparam](#-awsparam-)
- [awssecret](#-awssecret-)
- [base64](#-base64-)
- [base64-decode](#-base64-decode-)

Additionally, there are operators that are specific to merging arrays. For more detail
see the [array merging documentation][array-merging]:
Expand Down Expand Up @@ -391,6 +392,22 @@ time base64 encoding of string literals specified directly, or by reference.

[Example][base64-example]

## (( base64-decode ))

Usage: `(( base64-decode LITERAL|REFERENCE ... ))`

The `(( base64-decode ))` operator allows for merge-time decoding of base64-encoded string literals specified
directly, or by reference.

Example:
```yaml
encoded_string_1: Zm9v
decoded_string_1: (( base64-decode encoded_string_1 ))
decoded_string_2: (( base64-decode "YmFy" ))
decoded_properties_file_contents: (( concat "fookey=" decoded_string_1 "\nbarkey=" decoded_string_2 "\n" ))
encoded_properties_file_contents: (( base64 decoded_properties_file_contents ))
```
[array-merging]: https://github.com/geofffranks/spruce/blob/master/doc/array-merging.md
[env-var]: https://github.com/geofffranks/spruce/blob/master/doc/environment-variables-and-defaults.md
[vault]: https://vaultproject.io
Expand Down
98 changes: 98 additions & 0 deletions op_base64_decode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package spruce

import (
"encoding/base64"
"fmt"

"github.com/starkandwayne/goutils/ansi"

"github.com/starkandwayne/goutils/tree"

. "github.com/geofffranks/spruce/log"
)

// Base64DecodeOperator ...
type Base64DecodeOperator struct{}

// Setup ...
func (Base64DecodeOperator) Setup() error {
return nil
}

// Phase ...
func (Base64DecodeOperator) Phase() OperatorPhase {
return EvalPhase
}

// Dependencies ...
func (Base64DecodeOperator) Dependencies(_ *Evaluator, _ []*Expr, _ []*tree.Cursor, auto []*tree.Cursor) []*tree.Cursor {
return auto
}

// Run ...
func (Base64DecodeOperator) Run(ev *Evaluator, args []*Expr) (*Response, error) {
DEBUG("running (( base64-decode ... )) operation at $.%s", ev.Here)
defer DEBUG("done with (( base64-decode ... )) operation at $%s\n", ev.Here)

if len(args) != 1 {
return nil, fmt.Errorf("base64-decode operator requires exactly one string or reference argument")
}

var contents string

arg := args[0]
i := 0
v, err := arg.Resolve(ev.Tree)
if err != nil {
DEBUG(" arg[%d]: failed to resolve expression to a concrete value", i)
DEBUG(" [%d]: error was: %s", i, err)
return nil, err
}

switch v.Type {
case Literal:
DEBUG(" arg[%d]: using string literal '%v'", i, v.Literal)
DEBUG(" [%d]: appending '%v' to resultant string", i, v.Literal)
if fmt.Sprintf("%T", v.Literal) != "string" {
return nil, ansi.Errorf("@R{tried to base64 decode} @c{%v}@R{, which is not a string scalar}", v.Literal)
}
contents = fmt.Sprintf("%v", v.Literal)

case Reference:
DEBUG(" arg[%d]: trying to resolve reference $.%s", i, v.Reference)
s, err := v.Reference.Resolve(ev.Tree)
if err != nil {
DEBUG(" [%d]: resolution failed\n error: %s", i, err)
return nil, fmt.Errorf("unable to resolve `%s`: %s", v.Reference, err)
}

switch s.(type) {
case string:
DEBUG(" [%d]: appending '%s' to resultant string", i, s)
contents = fmt.Sprintf("%v", s)

default:
DEBUG(" arg[%d]: %v is not a string scalar", i, s)
return nil, ansi.Errorf("@R{tried to base64 decode} @c{%v}@R{, which is not a string scalar}", v.Reference)
}

default:
DEBUG(" arg[%d]: I don't know what to do with '%v'", i, arg)
return nil, fmt.Errorf("base64-decode operator only accepts string literals and key reference argument")
}
DEBUG("")

if decoded, err := base64.StdEncoding.DecodeString(contents); err == nil {
DEBUG(" resolved (( base64-decode ... )) operation to the string:\n \"%s\"", string(decoded))
return &Response{
Type: Replace,
Value: string(decoded),
}, nil
} else {
return nil, fmt.Errorf("unable to base64 decode string %s: %s", contents, err)
}
}

func init() {
RegisterOp("base64-decode", Base64DecodeOperator{})
}
34 changes: 34 additions & 0 deletions operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,40 @@ meta:

})

Convey("Base64Decode Operator", t, func() {
op := Base64DecodeOperator{}
ev := &Evaluator{
Tree: YAML(
`meta:
sample: "U2FtcGxlIFRleHQgVG8gQmFzZTY0IEVuY29kZSBGcm9tIFJlZmVyZW5jZQ=="
`),
}

Convey("can decode from a string literal", func() {
r, err := op.Run(ev, []*Expr{
str("U2FtcGxlIFRleHQgVG8gQmFzZTY0IEVuY29kZQ=="),
})
So(err, ShouldBeNil)
So(r, ShouldNotBeNil)

So(r.Type, ShouldEqual, Replace)
So(r.Value.(string), ShouldEqual, "Sample Text To Base64 Encode")
})

Convey("can decode from a reference", func() {
r, err := op.Run(ev, []*Expr{
ref("meta.sample"),
})

So(err, ShouldBeNil)
So(r, ShouldNotBeNil)

So(r.Type, ShouldEqual, Replace)

So(r.Value.(string), ShouldEqual, "Sample Text To Base64 Encode From Reference")
})
})

Convey("awsparam/awssecret operator", t, func() {
op := AwsOperator{variant: "awsparam"}
ev := &Evaluator{
Expand Down

0 comments on commit b877834

Please sign in to comment.