feat: [CDE-530]:add mask secret (#3047)

* truncated sha256
* add hash value for MaskSecret
* mark secrets for smc creds
* fix nil reference
* fix lint
* fix unmarshall
* implement unmarshall json func
* add mask secret
This commit is contained in:
Deepak Bhatt 2024-11-26 09:32:00 +00:00 committed by Harness
parent e5456d663f
commit deddbc8d68
7 changed files with 90 additions and 16 deletions

View File

@ -71,7 +71,7 @@ func (g *ServiceImpl) SetupCredentials(
) error {
script, err := template.GenerateScriptFromTemplate(
templateSetupGitCredentials, &template.SetupGitCredentialsPayload{
CloneURLWithCreds: resolvedRepoDetails.CloneURL,
CloneURLWithCreds: resolvedRepoDetails.CloneURL.Value(),
})
if err != nil {
return fmt.Errorf(
@ -94,7 +94,7 @@ func (g *ServiceImpl) CloneCode(
defaultBaseImage string,
gitspaceLogger types.GitspaceLogger,
) error {
cloneURL, err := url.Parse(resolvedRepoDetails.CloneURL)
cloneURL, err := url.Parse(resolvedRepoDetails.CloneURL.Value())
if err != nil {
return fmt.Errorf(
"failed to parse clone url %s: %w", resolvedRepoDetails.CloneURL, err)
@ -108,7 +108,7 @@ func (g *ServiceImpl) CloneCode(
}
if resolvedRepoDetails.ResolvedCredentials.Credentials != nil {
data.Email = resolvedRepoDetails.Credentials.Email
data.Name = resolvedRepoDetails.Credentials.Name
data.Name = resolvedRepoDetails.Credentials.Name.Value()
}
script, err := template.GenerateScriptFromTemplate(
templateCloneCode, data)

View File

@ -196,11 +196,11 @@ func (s *GitnessSCM) ResolveCredentials(
}
userInfo := url.UserPassword("harness", jwtToken)
modifiedURL.User = userInfo
resolvedCredentails.CloneURL = modifiedURL.String()
resolvedCredentails.CloneURL = types.NewMaskSecret(modifiedURL.String())
credentials := &Credentials{
Email: user.Email,
Name: user.DisplayName,
Password: jwtToken,
Name: types.NewMaskSecret(user.DisplayName),
Password: types.NewMaskSecret(jwtToken),
}
resolvedCredentails.Credentials = credentials
return resolvedCredentails, nil

View File

@ -126,7 +126,7 @@ func (s *GenericSCM) ResolveCredentials(
) (*ResolvedCredentials, error) {
var resolvedCredentials = &ResolvedCredentials{
Branch: gitspaceConfig.Branch,
CloneURL: gitspaceConfig.CodeRepo.URL,
CloneURL: types.NewMaskSecret(gitspaceConfig.CodeRepo.URL),
}
repoURL, err := url.Parse(gitspaceConfig.CodeRepo.URL)
if err != nil {

View File

@ -70,7 +70,7 @@ func (s *SCM) CheckValidCodeRepo(
return nil, fmt.Errorf("failed to resolve repo credentials and URL: %w", err)
}
if branch, err = s.detectBranch(ctx, resolvedCreds.CloneURL); err == nil {
if branch, err = s.detectBranch(ctx, resolvedCreds.CloneURL.Value()); err == nil {
codeRepositoryResponse.Branch = branch
}
return codeRepositoryResponse, nil

View File

@ -43,13 +43,14 @@ type (
// by an automated login process.
Credentials struct {
Email string
Name string
Password string
Name *types.MaskSecret
Password *types.MaskSecret
}
ResolvedCredentials struct {
Branch string
CloneURL string
Branch string
// CloneURL contains credentials for private repositories in url prefix
CloneURL *types.MaskSecret
Credentials *Credentials
RepoName string
}

73
types/mask_secret.go Normal file
View File

@ -0,0 +1,73 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"crypto/sha256"
"encoding/json"
"fmt"
)
const maxTruncatedLen = 8
// MaskSecret is a wrapper to store decrypted secrets in memory. This is help to prevent them
// from getting prints in logs and fmt.
type MaskSecret struct {
value string
hashedValue string
}
func NewMaskSecret(val string) *MaskSecret {
hash := sha256.New()
hash.Write([]byte(val))
hashedValueStr := fmt.Sprintf("%x", hash.Sum(nil))
return &MaskSecret{
value: val,
hashedValue: hashedValueStr[:maxTruncatedLen],
}
}
// Value returns the unmasked value of the MaskSecret.
// Use cautiously to avoid exposing sensitive data.
func (s *MaskSecret) Value() string {
if s == nil {
return ""
}
return s.value
}
func (s *MaskSecret) String() string {
if s == nil {
return ""
}
return s.hashedValue
}
func (s *MaskSecret) MarshalJSON() ([]byte, error) {
return json.Marshal(s.value)
}
func (s *MaskSecret) UnmarshalJSON(data []byte) error {
var input string
if err := json.Unmarshal(data, &input); err != nil {
return err
}
s.value = input
return nil
}

View File

@ -81,10 +81,10 @@ type PlatformConnectorSpec struct {
type PlatformConnectorAuthSpec struct {
AuthType PlatformConnectorAuthType
// userName can be empty when userName is encrypted.
UserName string
UserName *MaskSecret
// UserNameRef can be empty when userName is not encrypted
UserNameRef string
Password string
Password *MaskSecret
PasswordRef string
}
@ -103,7 +103,7 @@ func (c PlatformConnectorSpec) ExtractRegistryURL() string {
func (c PlatformConnectorAuthSpec) ExtractUserName() string {
if c.AuthType == UserNamePasswordPlatformConnectorAuthType {
return c.UserName
return c.UserName.Value()
}
return ""
@ -127,7 +127,7 @@ func (c PlatformConnectorAuthSpec) ExtractPasswordRef() string {
func (c PlatformConnectorAuthSpec) ExtractPassword() string {
if c.AuthType == UserNamePasswordPlatformConnectorAuthType {
return c.Password
return c.Password.Value()
}
return ""