Skip to content

Commit 3eb58a0

Browse files
committed
fix(gcp): Correctly handle the fact that GCP returns binary values
Updates to docs and tests help clarify how to get to the value, in the form the user prefers/needs.
1 parent 0abc009 commit 3eb58a0

3 files changed

Lines changed: 16 additions & 9 deletions

File tree

plugin/source/gcp/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ Retrieve a specific version of a secret:
3939
gcp://projects/1234567890/secrets/my-api-key/versions/2
4040
```
4141

42-
Using modifiers (e.g., extracting JSON path) safely ignores trailing slashes in the path:
42+
Using modifiers (e.g., extracting JSON path) safely ignores trailing slashes in the path, but keep in mind that since GCP Secret Manager payloads are returned as Base64 encoded strings, you might need to decode them first before parsing:
4343

4444
```text
45-
gcp://projects/my-project-123/secrets/my-json-secret/?jp=$.password
45+
gcp://projects/my-project-123/secrets/my-json-secret/?b64d&jp=$.password
4646
```
4747

4848
## Configuration
@@ -80,7 +80,7 @@ func main() {
8080
- Any trailing slash (e.g., when the URI contains query parameters like `/?jp=$.password`) is stripped automatically.
8181
- If no `/versions/` suffix is present, `/versions/latest` is automatically appended to the request.
8282
2. **Retrieval**: Uses `gcpClient.AccessSecretVersion` to fetch the payload of the secret.
83-
3. **Extraction**: Returns the decoded payload data as a string.
83+
3. **Extraction**: Because GCP Secret Manager payloads are strictly binary (`[]byte`), the source converts and returns the payload data as a **Base64-encoded string**. It is up to the user to decode it using the `?b64d` modifier (or handle it in their application) if plain text or JSON is required.
8484
4. **Errors**:
8585
- Returns `ErrSecretSourceGCPInvalidLocation` if the location format is invalid.
8686
- Returns `ErrCouldNotFetchSecret` if the API call fails for other reasons.

plugin/source/gcp/gcp.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gcp
22

33
import (
44
"context"
5+
"encoding/base64"
56
"fmt"
67
"regexp"
78

@@ -38,6 +39,11 @@ var (
3839
//
3940
// If the version is omitted, a "/versions/latest" suffix is appended.
4041
//
42+
// Because GCP Secret Manager payloads are strictly binary (`[]byte`), this source explicitly
43+
// converts and returns the payload data as a base64-encoded string. It is up to the user
44+
// to decode it using the `?b64d` modifier (or handle it in their application) if plain text
45+
// or JSON parsing is required.
46+
//
4147
// Expected format of `<PROJECT_ID_OR_NUM>` is documented at: https://google.aip.dev/cloud/2510.
4248
// Expected format of `<SECRET_NAME>` is documented at: https://cloud.google.com/security/products/secret-manager.
4349
//
@@ -92,13 +98,13 @@ func (s *SecretSourceGCP) DigUp(ctx context.Context, coord types.SecretCoord) (s
9298
return "", fmt.Errorf("%w (%q): %w", types.ErrCouldNotFetchSecret, coord.Location, err)
9399
}
94100

95-
// Extract and return payload, or error if missing
101+
// Extract and return payload encoded in base64, or error if missing
96102
if res.Payload == nil || res.Payload.Data == nil {
97103
return "", fmt.Errorf(
98104
"%w (%q): secret contains no data",
99105
types.ErrSecretNotFound,
100106
coord.Location,
101107
)
102108
}
103-
return string(res.Payload.Data), nil
109+
return base64.StdEncoding.EncodeToString(res.Payload.Data), nil
104110
}

plugin/source/gcp/gcp_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gcp_test
22

33
import (
4+
"encoding/base64"
45
"fmt"
56
"testing"
67

@@ -53,12 +54,12 @@ func TestSecretSourceGCP_DigUp_Integration(t *testing.T) {
5354
{
5455
name: "valid secret latest",
5556
coordStr: fmt.Sprintf("gcp://projects/%s/secrets/%s", projectID, secretName),
56-
want: secretValue,
57+
want: base64.StdEncoding.EncodeToString([]byte(secretValue)),
5758
},
5859
{
5960
name: "valid secret specific version",
6061
coordStr: fmt.Sprintf("gcp://projects/%s/secrets/%s/versions/1", projectID, secretName),
61-
want: secretValue,
62+
want: base64.StdEncoding.EncodeToString([]byte(secretValue)),
6263
},
6364
{
6465
name: "valid secret specific latest version",
@@ -67,12 +68,12 @@ func TestSecretSourceGCP_DigUp_Integration(t *testing.T) {
6768
projectID,
6869
secretName,
6970
),
70-
want: secretValue,
71+
want: base64.StdEncoding.EncodeToString([]byte(secretValue)),
7172
},
7273
{
7374
name: "valid secret via jp modifier",
7475
coordStr: fmt.Sprintf(
75-
"gcp://projects/%s/secrets/%s/?jp=$.password",
76+
"gcp://projects/%s/secrets/%s/?b64d&jp=$.password",
7677
projectID,
7778
jsonSecretName,
7879
),

0 commit comments

Comments
 (0)