fix_: ignore log partial API request (#5865)

* fix_: ignore log partial sensitive API request

* chore_: use validator

* chore_: rebase
This commit is contained in:
frank 2024-09-27 18:48:51 +08:00 committed by GitHub
parent 38308d48f2
commit 7a23ac59c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 530 additions and 18 deletions

View File

@ -281,8 +281,28 @@ func verifyAccountPassword(keyStoreDir, address, password string) string {
return makeJSONResponse(err) return makeJSONResponse(err)
} }
func VerifyDatabasePasswordV2(requestJSON string) string {
return callWithResponse(verifyDatabasePasswordV2, requestJSON)
}
func verifyDatabasePasswordV2(requestJSON string) string {
var request requests.VerifyDatabasePassword
err := json.Unmarshal([]byte(requestJSON), &request)
if err != nil {
return makeJSONResponse(err)
}
err = request.Validate()
if err != nil {
return makeJSONResponse(err)
}
err = statusBackend.VerifyDatabasePassword(request.KeyUID, request.Password)
return makeJSONResponse(err)
}
// Deprecated: use VerifyDatabasePasswordV2 instead
func VerifyDatabasePassword(keyUID, password string) string { func VerifyDatabasePassword(keyUID, password string) string {
return callWithResponse(verifyDatabasePassword, keyUID, password) return verifyDatabasePassword(keyUID, password)
} }
// verifyDatabasePassword verifies database password. // verifyDatabasePassword verifies database password.
@ -291,8 +311,29 @@ func verifyDatabasePassword(keyUID, password string) string {
return makeJSONResponse(err) return makeJSONResponse(err)
} }
func MigrateKeyStoreDirV2(requestJSON string) string {
return callWithResponse(migrateKeyStoreDirV2, requestJSON)
}
func migrateKeyStoreDirV2(requestJSON string) string {
var request requests.MigrateKeystoreDir
err := json.Unmarshal([]byte(requestJSON), &request)
if err != nil {
return makeJSONResponse(err)
}
err = request.Validate()
if err != nil {
return makeJSONResponse(err)
}
err = statusBackend.MigrateKeyStoreDir(request.Account, request.Password, request.OldDir, request.NewDir)
return makeJSONResponse(err)
}
// Deprecated: Use MigrateKeyStoreDirV2 instead
func MigrateKeyStoreDir(accountData, password, oldDir, newDir string) string { func MigrateKeyStoreDir(accountData, password, oldDir, newDir string) string {
return callWithResponse(migrateKeyStoreDir, accountData, password, oldDir, newDir) return migrateKeyStoreDir(accountData, password, oldDir, newDir)
} }
// migrateKeyStoreDir migrates key files to a new directory // migrateKeyStoreDir migrates key files to a new directory
@ -517,8 +558,29 @@ func deleteMultiaccount(keyUID, keyStoreDir string) string {
return makeJSONResponse(err) return makeJSONResponse(err)
} }
func DeleteImportedKeyV2(requestJSON string) string {
return callWithResponse(deleteImportedKeyV2, requestJSON)
}
func deleteImportedKeyV2(requestJSON string) string {
var request requests.DeleteImportedKey
err := json.Unmarshal([]byte(requestJSON), &request)
if err != nil {
return makeJSONResponse(err)
}
err = request.Validate()
if err != nil {
return makeJSONResponse(err)
}
err = statusBackend.DeleteImportedKey(request.Address, request.Password, request.KeyStoreDir)
return makeJSONResponse(err)
}
// Deprecated: Use DeleteImportedKeyV2 instead
func DeleteImportedKey(address, password, keyStoreDir string) string { func DeleteImportedKey(address, password, keyStoreDir string) string {
return callWithResponse(deleteImportedKey, address, password, keyStoreDir) return deleteImportedKey(address, password, keyStoreDir)
} }
// deleteImportedKey // deleteImportedKey
@ -629,9 +691,9 @@ func signMessage(rpcParams string) string {
// SignTypedData unmarshall data into TypedData, validate it and signs with selected account, // SignTypedData unmarshall data into TypedData, validate it and signs with selected account,
// if password matches selected account. // if password matches selected account.
// //
//export SignTypedData // Deprecated: Use SignTypedDataV2 instead.
func SignTypedData(data, address, password string) string { func SignTypedData(data, address, password string) string {
return callWithResponse(signTypedData, data, address, password) return signTypedData(data, address, password)
} }
func signTypedData(data, address, password string) string { func signTypedData(data, address, password string) string {
@ -647,6 +709,26 @@ func signTypedData(data, address, password string) string {
return prepareJSONResponse(result.String(), err) return prepareJSONResponse(result.String(), err)
} }
func SignTypedDataV2(requestJSON string) string {
return callWithResponse(signTypedDataV2, requestJSON)
}
func signTypedDataV2(requestJSON string) string {
var request requests.SignTypedData
err := json.Unmarshal([]byte(requestJSON), &request)
if err != nil {
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
}
err = request.Validate()
if err != nil {
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
}
result, err := statusBackend.SignTypedData(request.TypedData, request.Address, request.Password)
return prepareJSONResponse(result.String(), err)
}
// HashTypedData unmarshalls data into TypedData, validates it and hashes it. // HashTypedData unmarshalls data into TypedData, validates it and hashes it.
// //
//export HashTypedData //export HashTypedData
@ -672,7 +754,7 @@ func hashTypedData(data string) string {
// //
//export SignTypedDataV4 //export SignTypedDataV4
func SignTypedDataV4(data, address, password string) string { func SignTypedDataV4(data, address, password string) string {
return callWithResponse(signTypedDataV4, data, address, password) return signTypedDataV4(data, address, password)
} }
func signTypedDataV4(data, address, password string) string { func signTypedDataV4(data, address, password string) string {
@ -718,8 +800,9 @@ func recoverWithRPCParams(rpcParams string) string {
return prepareJSONResponse(addr.String(), err) return prepareJSONResponse(addr.String(), err)
} }
// SendTransactionWithChainID converts RPC args and calls backend.SendTransactionWithChainID.
func SendTransactionWithChainID(chainID int, txArgsJSON, password string) string { func SendTransactionWithChainID(chainID int, txArgsJSON, password string) string {
return callWithResponse(sendTransactionWithChainID, chainID, txArgsJSON, password) return sendTransactionWithChainID(chainID, txArgsJSON, password)
} }
// sendTransactionWithChainID converts RPC args and calls backend.SendTransactionWithChainID. // sendTransactionWithChainID converts RPC args and calls backend.SendTransactionWithChainID.
@ -738,7 +821,7 @@ func sendTransactionWithChainID(chainID int, txArgsJSON, password string) string
} }
func SendTransaction(txArgsJSON, password string) string { func SendTransaction(txArgsJSON, password string) string {
return callWithResponse(sendTransaction, txArgsJSON, password) return sendTransaction(txArgsJSON, password)
} }
// sendTransaction converts RPC args and calls backend.SendTransaction. // sendTransaction converts RPC args and calls backend.SendTransaction.
@ -1035,7 +1118,7 @@ func colorID(pk string) string {
} }
func ValidateMnemonic(mnemonic string) string { func ValidateMnemonic(mnemonic string) string {
return callWithResponse(validateMnemonic, mnemonic) return validateMnemonic(mnemonic)
} }
func validateMnemonic(mnemonic string) string { func validateMnemonic(mnemonic string) string {
@ -1134,7 +1217,7 @@ func multiformatDeserializePublicKey(key, outBase string) string {
} }
func ExportUnencryptedDatabase(accountData, password, databasePath string) string { func ExportUnencryptedDatabase(accountData, password, databasePath string) string {
return callWithResponse(exportUnencryptedDatabase, accountData, password, databasePath) return exportUnencryptedDatabase(accountData, password, databasePath)
} }
// exportUnencryptedDatabase exports the database unencrypted to the given path // exportUnencryptedDatabase exports the database unencrypted to the given path
@ -1149,7 +1232,7 @@ func exportUnencryptedDatabase(accountData, password, databasePath string) strin
} }
func ImportUnencryptedDatabase(accountData, password, databasePath string) string { func ImportUnencryptedDatabase(accountData, password, databasePath string) string {
return callWithResponse(importUnencryptedDatabase, accountData, password, databasePath) return importUnencryptedDatabase(accountData, password, databasePath)
} }
// importUnencryptedDatabase imports the database unencrypted to the given directory // importUnencryptedDatabase imports the database unencrypted to the given directory
@ -1163,18 +1246,18 @@ func importUnencryptedDatabase(accountData, password, databasePath string) strin
return makeJSONResponse(err) return makeJSONResponse(err)
} }
func ChangeDatabasePassword(KeyUID, password, newPassword string) string { func ChangeDatabasePassword(keyUID, password, newPassword string) string {
return callWithResponse(changeDatabasePassword, KeyUID, password, newPassword) return changeDatabasePassword(keyUID, password, newPassword)
} }
// changeDatabasePassword changes the password of the database // changeDatabasePassword changes the password of the database
func changeDatabasePassword(KeyUID, password, newPassword string) string { func changeDatabasePassword(keyUID, password, newPassword string) string {
err := statusBackend.ChangeDatabasePassword(KeyUID, password, newPassword) err := statusBackend.ChangeDatabasePassword(keyUID, password, newPassword)
return makeJSONResponse(err) return makeJSONResponse(err)
} }
func ConvertToKeycardAccount(accountData, settingsJSON, keycardUID, password, newPassword string) string { func ConvertToKeycardAccount(accountData, settingsJSON, keycardUID, password, newPassword string) string {
return callWithResponse(convertToKeycardAccount, accountData, settingsJSON, keycardUID, password, newPassword) return convertToKeycardAccount(accountData, settingsJSON, keycardUID, password, newPassword)
} }
// convertToKeycardAccount converts the account to a keycard account // convertToKeycardAccount converts the account to a keycard account
@ -1195,7 +1278,7 @@ func convertToKeycardAccount(accountData, settingsJSON, keycardUID, password, ne
} }
func ConvertToRegularAccount(mnemonic, currPassword, newPassword string) string { func ConvertToRegularAccount(mnemonic, currPassword, newPassword string) string {
return callWithResponse(convertToRegularAccount, mnemonic, currPassword, newPassword) return convertToRegularAccount(mnemonic, currPassword, newPassword)
} }
// convertToRegularAccount converts the account to a regular account // convertToRegularAccount converts the account to a regular account

View File

@ -13,7 +13,28 @@ import (
"github.com/status-im/status-go/logutils/requestlog" "github.com/status-im/status-go/logutils/requestlog"
) )
var sensitiveRegex = regexp.MustCompile(`(?i)(".*?(password|mnemonic|openseaAPIKey|poktToken|alchemyArbitrumMainnetToken|raribleTestnetAPIKey|alchemyOptimismMainnetToken|statusProxyBlockchainUser|alchemyEthereumSepoliaToken|alchemyArbitrumSepoliaToken|infuraToken|raribleMainnetAPIKey|alchemyEthereumMainnetToken).*?")\s*:\s*("[^"]*")`) var sensitiveKeys = []string{
"password",
"mnemonic",
"openseaAPIKey",
"poktToken",
"alchemyArbitrumMainnetToken",
"raribleTestnetAPIKey",
"alchemyOptimismMainnetToken",
"statusProxyBlockchainUser",
"alchemyEthereumSepoliaToken",
"alchemyArbitrumSepoliaToken",
"infuraToken",
"raribleMainnetAPIKey",
"alchemyEthereumMainnetToken",
"alchemyOptimismSepoliaToken",
"verifyENSURL",
"verifyTransactionURL",
}
var sensitiveRegexString = fmt.Sprintf(`(?i)(".*?(%s).*?")\s*:\s*("[^"]*")`, strings.Join(sensitiveKeys, "|"))
var sensitiveRegex = regexp.MustCompile(sensitiveRegexString)
func getFunctionName(fn any) string { func getFunctionName(fn any) string {
return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()

View File

@ -0,0 +1,22 @@
package requests
import (
"gopkg.in/go-playground/validator.v9"
)
// DeleteImportedKey represents a request to delete an imported key.
type DeleteImportedKey struct {
// Address is the address of the imported key to delete.
Address string `json:"address" validate:"required"`
// Password is the password used to decrypt the key.
Password string `json:"password" validate:"required"`
// KeyStoreDir is the directory where the key is stored.
KeyStoreDir string `json:"keyStoreDir" validate:"required"`
}
// Validate checks the validity of the DeleteImportedKey request.
func (r *DeleteImportedKey) Validate() error {
return validator.New().Struct(r)
}

View File

@ -0,0 +1,60 @@
package requests
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestDeleteImportedKey_Validate(t *testing.T) {
testCases := []struct {
name string
req DeleteImportedKey
expectedErr string
}{
{
name: "valid request",
req: DeleteImportedKey{
Address: "0x1234567890123456789012345678901234567890",
Password: "password",
KeyStoreDir: "/keystore/dir",
},
},
{
name: "empty address",
req: DeleteImportedKey{
Password: "password",
KeyStoreDir: "/keystore/dir",
},
expectedErr: "Address",
},
{
name: "empty password",
req: DeleteImportedKey{
Address: "0x1234567890123456789012345678901234567890",
KeyStoreDir: "/keystore/dir",
},
expectedErr: "Password",
},
{
name: "empty keystore dir",
req: DeleteImportedKey{
Address: "0x1234567890123456789012345678901234567890",
Password: "password",
},
expectedErr: "KeyStoreDir",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.req.Validate()
if tc.expectedErr != "" {
t.Log("err", err.Error())
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -0,0 +1,27 @@
package requests
import (
"gopkg.in/go-playground/validator.v9"
"github.com/status-im/status-go/multiaccounts"
)
// MigrateKeystoreDir represents a request to migrate keystore directory.
type MigrateKeystoreDir struct {
// Account is the account associated with the keystore.
Account multiaccounts.Account `json:"account"`
// Password is the password for the keystore.
Password string `json:"password" validate:"required"`
// OldDir is the old keystore directory.
OldDir string `json:"oldDir" validate:"required"`
// NewDir is the new keystore directory.
NewDir string `json:"newDir" validate:"required"`
}
// Validate checks the validity of the MigrateKeystoreDir request.
func (r *MigrateKeystoreDir) Validate() error {
return validator.New().Struct(r)
}

View File

@ -0,0 +1,74 @@
package requests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/status-im/status-go/multiaccounts"
)
func TestMigrateKeystoreDir_Validate(t *testing.T) {
testCases := []struct {
name string
req MigrateKeystoreDir
expectedErr string
}{
{
name: "valid request",
req: MigrateKeystoreDir{
Account: multiaccounts.Account{Name: "test-account"},
Password: "test-password",
OldDir: "/old/keystore/dir",
NewDir: "/new/keystore/dir",
},
},
{
name: "empty account",
req: MigrateKeystoreDir{
Password: "test-password",
OldDir: "/old/keystore/dir",
NewDir: "/new/keystore/dir",
},
},
{
name: "empty password",
req: MigrateKeystoreDir{
Account: multiaccounts.Account{Name: "test-account"},
OldDir: "/old/keystore/dir",
NewDir: "/new/keystore/dir",
},
expectedErr: "Password",
},
{
name: "empty old dir",
req: MigrateKeystoreDir{
Account: multiaccounts.Account{Name: "test-account"},
Password: "test-password",
NewDir: "/new/keystore/dir",
},
expectedErr: "OldDir",
},
{
name: "empty new dir",
req: MigrateKeystoreDir{
Account: multiaccounts.Account{Name: "test-account"},
Password: "test-password",
OldDir: "/old/keystore/dir",
},
expectedErr: "NewDir",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.req.Validate()
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -0,0 +1,34 @@
package requests
import (
"gopkg.in/go-playground/validator.v9"
"github.com/status-im/status-go/services/typeddata"
)
// SignTypedData represents a request to sign typed data.
type SignTypedData struct {
// TypedData is the typed data to sign.
TypedData typeddata.TypedData `json:"typedData" validate:"required"`
// Address is the address of the account to sign with.
Address string `json:"address" validate:"required"`
// Password is the password of the account to sign with.
Password string `json:"password" validate:"required"`
}
// Validate checks the validity of the SignTypedData request.
func (r *SignTypedData) Validate() error {
// Use the validator package to validate the struct fields
if err := validator.New().Struct(r); err != nil {
return err
}
// Additional validation logic from the old signTypedData function
if err := r.TypedData.Validate(); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,130 @@
package requests
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/status-im/status-go/services/typeddata"
)
func TestSignTypedData_Validate(t *testing.T) {
testCases := []struct {
name string
req SignTypedData
expectedErr string
}{
{
name: "valid request",
req: SignTypedData{
TypedData: typeddata.TypedData{
Types: typeddata.Types{
"EIP712Domain": []typeddata.Field{
{Name: "name", Type: "string"},
},
},
Domain: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
PrimaryType: "EIP712Domain",
Message: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
},
Address: "0x1234567890123456789012345678901234567890",
Password: "password",
},
expectedErr: "",
},
{
name: "missing typed data",
req: SignTypedData{
TypedData: typeddata.TypedData{
Types: typeddata.Types{},
Domain: map[string]json.RawMessage{},
PrimaryType: "",
Message: map[string]json.RawMessage{},
},
Address: "0x1234567890123456789012345678901234567890",
Password: "password",
},
expectedErr: "`EIP712Domain` must be in `types`",
},
{
name: "missing address",
req: SignTypedData{
TypedData: typeddata.TypedData{
Types: typeddata.Types{
"EIP712Domain": []typeddata.Field{
{Name: "name", Type: "string"},
},
},
Domain: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
PrimaryType: "EIP712Domain",
Message: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
},
Password: "password",
},
expectedErr: "Key: 'SignTypedData.Address' Error:Field validation for 'Address' failed on the 'required' tag",
},
{
name: "missing password",
req: SignTypedData{
TypedData: typeddata.TypedData{
Types: typeddata.Types{
"EIP712Domain": []typeddata.Field{
{Name: "name", Type: "string"},
},
},
Domain: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
PrimaryType: "EIP712Domain",
Message: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
},
Address: "0x1234567890123456789012345678901234567890",
},
expectedErr: "Key: 'SignTypedData.Password' Error:Field validation for 'Password' failed on the 'required' tag",
},
{
name: "invalid typed data",
req: SignTypedData{
TypedData: typeddata.TypedData{
Types: typeddata.Types{
"EIP712Domain": []typeddata.Field{
{Name: "name", Type: "string"},
},
},
Domain: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
PrimaryType: "InvalidType",
Message: map[string]json.RawMessage{
"name": json.RawMessage(`"test"`),
},
},
Address: "0x1234567890123456789012345678901234567890",
Password: "password",
},
expectedErr: "primary type `InvalidType` not defined in types",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.req.Validate()
if tc.expectedErr != "" {
require.EqualError(t, err, tc.expectedErr)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -0,0 +1,19 @@
package requests
import (
"gopkg.in/go-playground/validator.v9"
)
// VerifyDatabasePassword represents a request to verify the database password.
type VerifyDatabasePassword struct {
// KeyUID identifies the specific key in the database.
KeyUID string `json:"keyUID" validate:"required"`
// Password is the password to verify against the database entry.
Password string `json:"password" validate:"required"`
}
// Validate checks the validity of the VerifyDatabasePassword request.
func (v *VerifyDatabasePassword) Validate() error {
return validator.New().Struct(v)
}

View File

@ -0,0 +1,42 @@
package requests
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestVerifyDatabasePassword_Validate(t *testing.T) {
tests := []struct {
name string
request VerifyDatabasePassword
wantErr string
}{
{
name: "Empty KeyUID",
request: VerifyDatabasePassword{KeyUID: "", Password: "password"},
wantErr: "KeyUID",
},
{
name: "Empty Password",
request: VerifyDatabasePassword{KeyUID: "keyuid", Password: ""},
wantErr: "Password",
},
{
name: "Valid Request",
request: VerifyDatabasePassword{KeyUID: "keyuid", Password: "password"},
wantErr: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.request.Validate()
if tt.wantErr != "" {
require.Contains(t, err.Error(), tt.wantErr)
} else {
require.NoError(t, err)
}
})
}
}