Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/secrets/common/browser_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ func digestHexString(digest [32]byte) string {
// exchanges the code for a short-lived vault JWT, and POSTs the same JSON-RPC body to the gateway with Bearer auth.
// Login tokens in ~/.cre/cre.yaml are not modified; that session stays separate from this vault-only token.
func (h *Handler) executeBrowserUpsert(ctx context.Context, inputs UpsertSecretsInputs, method string) error {
defer ZeroUpsertSecretValues(inputs)

Comment thread
timothyF95 marked this conversation as resolved.
Outdated
if h.Credentials.AuthType == credentials.AuthTypeApiKey {
return fmt.Errorf("this sign-in flow requires an interactive login; API keys are not supported")
}
Expand Down
29 changes: 21 additions & 8 deletions cmd/secrets/common/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,17 @@ type UpsertSecretsInputs []SecretItem
// SecretItem represents a single secret with its ID, value, and optional namespace.
type SecretItem struct {
ID string `json:"id" validate:"required"`
Value string `json:"value" validate:"required"`
Value []byte `json:"value" validate:"required"`
Namespace string `json:"namespace"`
}

// ZeroUpsertSecretValues overwrites secret payloads in memory.
func ZeroUpsertSecretValues(inputs UpsertSecretsInputs) {
for i := range inputs {
clear(inputs[i].Value)
}
}

type SecretsYamlConfig struct {
SecretsNames map[string][]string `yaml:"secretsNames"`
}
Expand Down Expand Up @@ -157,13 +164,13 @@ func (h *Handler) ResolveInputs() (UpsertSecretsInputs, error) {
if !ok {
return nil, fmt.Errorf("environment variable %q for secret %q not found; please export it", envName, id)
}
if !utf8.ValidString(envVal) {
if !utf8.Valid([]byte(envVal)) {
return nil, fmt.Errorf("value for secret %q (env %q) contains invalid UTF-8", id, envName)
}

out = append(out, SecretItem{
ID: id,
Value: envVal,
Value: []byte(envVal),
Namespace: "main",
Comment thread
timothyF95 marked this conversation as resolved.
})

Expand Down Expand Up @@ -331,8 +338,10 @@ func (h *Handler) EncryptSecrets(rawSecrets UpsertSecretsInputs) ([]*vault.Encry
}

encryptedSecrets := make([]*vault.EncryptedSecret, 0, len(rawSecrets))
for _, item := range rawSecrets {
for i := range rawSecrets {
item := &rawSecrets[i]
cipherHex, err := EncryptSecret(item.Value, pubKeyHex, h.OwnerAddress)
clear(item.Value)
if err != nil {
Comment thread
timothyF95 marked this conversation as resolved.
return nil, fmt.Errorf("failed to encrypt secret (key=%s ns=%s): %w", item.ID, item.Namespace, err)
}
Expand Down Expand Up @@ -361,8 +370,10 @@ func (h *Handler) EncryptSecretsForBrowserOrg(rawSecrets UpsertSecretsInputs, or
label := sha256.Sum256([]byte(orgID))

encryptedSecrets := make([]*vault.EncryptedSecret, 0, len(rawSecrets))
for _, item := range rawSecrets {
for i := range rawSecrets {
item := &rawSecrets[i]
cipherHex, err := encryptSecretWithLabel(item.Value, pubKeyHex, label)
clear(item.Value)
if err != nil {
return nil, fmt.Errorf("failed to encrypt secret (key=%s ns=%s): %w", item.ID, item.Namespace, err)
}
Expand All @@ -380,7 +391,7 @@ func (h *Handler) EncryptSecretsForBrowserOrg(rawSecrets UpsertSecretsInputs, or
}

// encryptSecretWithLabel encrypts a secret using the vault master public key and the given label.
func encryptSecretWithLabel(secret, masterPublicKeyHex string, label [32]byte) (string, error) {
func encryptSecretWithLabel(secret []byte, masterPublicKeyHex string, label [32]byte) (string, error) {
masterPublicKey := tdh2easy.PublicKey{}
masterPublicKeyBytes, err := hex.DecodeString(masterPublicKeyHex)
if err != nil {
Expand All @@ -390,7 +401,7 @@ func encryptSecretWithLabel(secret, masterPublicKeyHex string, label [32]byte) (
return "", fmt.Errorf("failed to unmarshal master public key: %w", err)
}

cipher, err := tdh2easy.EncryptWithLabel(&masterPublicKey, []byte(secret), label)
cipher, err := tdh2easy.EncryptWithLabel(&masterPublicKey, secret, label)
if err != nil {
return "", fmt.Errorf("failed to encrypt secret: %w", err)
}
Expand All @@ -402,7 +413,7 @@ func encryptSecretWithLabel(secret, masterPublicKeyHex string, label [32]byte) (
}

// EncryptSecret encrypts for the owner-key / web3 flow using a 32-byte label derived from the EOA (12 zero bytes + 20-byte address).
func EncryptSecret(secret, masterPublicKeyHex string, ownerAddress string) (string, error) {
func EncryptSecret(secret []byte, masterPublicKeyHex string, ownerAddress string) (string, error) {
addr := common.HexToAddress(ownerAddress) // canonical 20-byte address
var label [32]byte
copy(label[12:], addr.Bytes()) // left-pad with 12 zero bytes
Expand Down Expand Up @@ -452,6 +463,8 @@ func (h *Handler) Execute(
duration time.Duration,
secretsAuth string,
) error {
defer ZeroUpsertSecretValues(inputs)

if IsBrowserFlow(secretsAuth) {
return h.executeBrowserUpsert(context.Background(), inputs, method)
}
Expand Down
10 changes: 5 additions & 5 deletions cmd/secrets/common/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ func TestEncryptSecrets(t *testing.T) {
}

raw := UpsertSecretsInputs{
{ID: "test-secret-1", Value: "value1", Namespace: "ns1"},
{ID: "test-secret-2", Value: "another-value", Namespace: "ns2"},
{ID: "test-secret-1", Value: []byte("value1"), Namespace: "ns1"},
{ID: "test-secret-2", Value: []byte("another-value"), Namespace: "ns2"},
}

enc, err := h.EncryptSecrets(raw)
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestEncryptSecrets(t *testing.T) {
},
}

enc, err := h.EncryptSecrets(UpsertSecretsInputs{{ID: "s", Value: "v", Namespace: "n"}})
enc, err := h.EncryptSecrets(UpsertSecretsInputs{{ID: "s", Value: []byte("v"), Namespace: "n"}})
require.Error(t, err)
require.Nil(t, enc)
require.Contains(t, err.Error(), "gateway POST failed")
Expand All @@ -126,7 +126,7 @@ func TestEncryptSecrets(t *testing.T) {
},
}

enc, err := h.EncryptSecrets(UpsertSecretsInputs{{ID: "s", Value: "v", Namespace: "n"}})
enc, err := h.EncryptSecrets(UpsertSecretsInputs{{ID: "s", Value: []byte("v"), Namespace: "n"}})
require.Error(t, err)
require.Nil(t, enc)
require.Contains(t, err.Error(), "vault public key fetch error")
Expand Down Expand Up @@ -248,7 +248,7 @@ func TestEncryptSecrets_OrgOwned(t *testing.T) {
}

raw := UpsertSecretsInputs{
{ID: "secret-1", Value: "val1", Namespace: "main"},
{ID: "secret-1", Value: []byte("val1"), Namespace: "main"},
}
Comment thread
anirudhwarrier marked this conversation as resolved.
Outdated

t.Run("uses orgID as owner when SecretsOrgOwned is true", func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func New(ctx *runtime.Context) *cobra.Command {
if err != nil {
return err
}
defer common.ZeroUpsertSecretValues(inputs)

if err := h.ValidateInputs(inputs); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func New(ctx *runtime.Context) *cobra.Command {
if err != nil {
return err
}
defer common.ZeroUpsertSecretValues(inputs)

if err := h.ValidateInputs(inputs); err != nil {
return err
Expand Down
Loading