mirror of
https://github.com/mikefarah/yq.git
synced 2026-10-12 02:19:06 +00:00
feat: add @base64url/@base64urld operators (RFC 4648 §5) (#2827)
Add the URL- and filename-safe Base64 variant alongside the existing @base64/@base64d operators. The URL-safe alphabet replaces + and / with - and _ so encoded values can be embedded in URLs, filenames, or other contexts where + and / have special meaning. Adds: - Base64UrlFormat registered in the Formats slice (also exposes base64url as a top-level --input/--output-format) - NewBase64URLEncoder / NewBase64URLDecoder backed by base64.URLEncoding - @base64url / @base64urld lexer tokens (placed before @base64/@base64d so the longer patterns match first under Go's leftmost-first regex) - no_base64.go stubs for the yq_nobase64 build tag - operator tests covering encode, decode, round-trip, and missing-padding decode Closes #2794 Co-authored-by: Pablo Garcia <[email protected]>
This commit is contained in:
@@ -20,6 +20,13 @@ func NewBase64Decoder() Decoder {
|
||||
return &base64Decoder{finished: false, encoding: *base64.StdEncoding}
|
||||
}
|
||||
|
||||
// NewBase64URLDecoder returns a decoder for the URL- and filename-safe
|
||||
// Base64 variant (RFC 4648 §5): `-` and `_` replace `+` and `/`.
|
||||
// The shared padding logic in Init tolerates inputs with or without padding.
|
||||
func NewBase64URLDecoder() Decoder {
|
||||
return &base64Decoder{finished: false, encoding: *base64.URLEncoding}
|
||||
}
|
||||
|
||||
func (dec *base64Decoder) Init(reader io.Reader) error {
|
||||
// Read all data from the reader and strip leading/trailing whitespace
|
||||
// This is necessary because base64 decoding needs to see the complete input
|
||||
|
||||
@@ -514,3 +514,33 @@ coolData:
|
||||
a: apple
|
||||
```
|
||||
|
||||
## Encode a string to base64url
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
coolData: "Works with UTF-16 \U0001F60A"
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '.coolData | @base64url' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
V29ya3Mgd2l0aCBVVEYtMTYg8J-Yig==
|
||||
```
|
||||
|
||||
## Decode a base64url encoded string
|
||||
URL-safe variant uses `-` and `_` in place of `+` and `/` (RFC 4648 §5).
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
coolData: V29ya3Mgd2l0aCBVVEYtMTYg8J-Yig==
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '.coolData | @base64urld' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
Works with UTF-16 😊
|
||||
```
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@ func NewBase64Encoder() Encoder {
|
||||
return &base64Encoder{encoding: *base64.StdEncoding}
|
||||
}
|
||||
|
||||
// NewBase64URLEncoder returns an encoder for the URL- and filename-safe
|
||||
// Base64 variant (RFC 4648 §5): `-` and `_` replace `+` and `/`.
|
||||
func NewBase64URLEncoder() Encoder {
|
||||
return &base64Encoder{encoding: *base64.URLEncoding}
|
||||
}
|
||||
|
||||
func (e *base64Encoder) CanHandleAliases() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -58,6 +58,15 @@ var Base64Format = &Format{"base64", []string{},
|
||||
func() Decoder { return NewBase64Decoder() },
|
||||
}
|
||||
|
||||
// Base64UrlFormat is the URL- and filename-safe Base64 variant defined by
|
||||
// RFC 4648 §5: it uses `-` and `_` in place of `+` and `/`. Padding is kept
|
||||
// (matching the standard Base64Format), so round-tripping with @base64urld
|
||||
// works whether or not the input was padded.
|
||||
var Base64UrlFormat = &Format{"base64url", []string{},
|
||||
func() Encoder { return NewBase64URLEncoder() },
|
||||
func() Decoder { return NewBase64URLDecoder() },
|
||||
}
|
||||
|
||||
var UriFormat = &Format{"uri", []string{},
|
||||
func() Encoder { return NewUriEncoder() },
|
||||
func() Decoder { return NewUriDecoder() },
|
||||
@@ -102,6 +111,7 @@ var Formats = []*Format{
|
||||
TSVFormat,
|
||||
XMLFormat,
|
||||
Base64Format,
|
||||
Base64UrlFormat,
|
||||
UriFormat,
|
||||
ShFormat,
|
||||
TomlFormat,
|
||||
|
||||
@@ -81,6 +81,9 @@ var participleYqRules = []*participleYqRule{
|
||||
{"TSVDecode", `from_?tsv|@tsvd`, decodeOp(TSVFormat), 0},
|
||||
{"TSVEncode", `to_?tsv|@tsv`, encodeWithIndent(TSVFormat, 0), 0},
|
||||
|
||||
{"Base64Urld", `@base64urld`, decodeOp(Base64UrlFormat), 0},
|
||||
{"Base64Url", `@base64url`, encodeWithIndent(Base64UrlFormat, 0), 0},
|
||||
|
||||
{"Base64d", `@base64d`, decodeOp(Base64Format), 0},
|
||||
{"Base64", `@base64`, encodeWithIndent(Base64Format, 0), 0},
|
||||
|
||||
|
||||
@@ -9,3 +9,11 @@ func NewBase64Decoder() Decoder {
|
||||
func NewBase64Encoder() Encoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBase64URLDecoder() Decoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBase64URLEncoder() Encoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -326,6 +326,39 @@ var encoderDecoderOperatorScenarios = []expressionScenario{
|
||||
"D0, P[], (!!str)::cats\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Encode a string to base64url",
|
||||
document: "coolData: Works with UTF-16 😊",
|
||||
expression: ".coolData | @base64url",
|
||||
expected: []string{
|
||||
"D0, P[coolData], (!!str)::V29ya3Mgd2l0aCBVVEYtMTYg8J-Yig==\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Decode a base64url encoded string",
|
||||
subdescription: "URL-safe variant uses `-` and `_` in place of `+` and `/` (RFC 4648 §5).",
|
||||
document: "coolData: V29ya3Mgd2l0aCBVVEYtMTYg8J-Yig==",
|
||||
expression: ".coolData | @base64urld",
|
||||
expected: []string{
|
||||
"D0, P[coolData], (!!str)::Works with UTF-16 😊\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "base64url round trip preserves bytes with + and /",
|
||||
skipDoc: true,
|
||||
expression: `">>>???" | @base64url | @base64urld`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::>>>???\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "base64url missing padding decode",
|
||||
skipDoc: true,
|
||||
expression: `"V29ya3Mgd2l0aCBVVEYtMTYg8J-Yig" | @base64urld`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!str)::Works with UTF-16 😊\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
requiresFormat: "xml",
|
||||
description: "empty xml decode",
|
||||
|
||||
Reference in New Issue
Block a user