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)
}
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 {
return callWithResponse(verifyDatabasePassword, keyUID, password)
return verifyDatabasePassword(keyUID, password)
}
// verifyDatabasePassword verifies database password.
@ -291,8 +311,29 @@ func verifyDatabasePassword(keyUID, password string) string {
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 {
return callWithResponse(migrateKeyStoreDir, accountData, password, oldDir, newDir)
return migrateKeyStoreDir(accountData, password, oldDir, newDir)
}
// migrateKeyStoreDir migrates key files to a new directory
@ -517,8 +558,29 @@ func deleteMultiaccount(keyUID, keyStoreDir string) string {
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 {
return callWithResponse(deleteImportedKey, address, password, keyStoreDir)
return deleteImportedKey(address, password, keyStoreDir)
}
// deleteImportedKey
@ -629,9 +691,9 @@ func signMessage(rpcParams string) string {
// SignTypedData unmarshall data into TypedData, validate it and signs with selected account,
// if password matches selected account.
//
//export SignTypedData
// Deprecated: Use SignTypedDataV2 instead.
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 {
@ -647,6 +709,26 @@ func signTypedData(data, address, password string) string {
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.
//
//export HashTypedData
@ -672,7 +754,7 @@ func hashTypedData(data string) string {
//
//export SignTypedDataV4
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 {
@ -718,8 +800,9 @@ func recoverWithRPCParams(rpcParams string) string {
return prepareJSONResponse(addr.String(), err)
}
// SendTransactionWithChainID converts RPC args and calls backend.SendTransactionWithChainID.
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.
@ -738,7 +821,7 @@ func sendTransactionWithChainID(chainID int, 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.
@ -1035,7 +1118,7 @@ func colorID(pk string) string {
}
func ValidateMnemonic(mnemonic string) string {
return callWithResponse(validateMnemonic, mnemonic)
return validateMnemonic(mnemonic)
}
func validateMnemonic(mnemonic string) string {
@ -1134,7 +1217,7 @@ func multiformatDeserializePublicKey(key, outBase 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
@ -1149,7 +1232,7 @@ func exportUnencryptedDatabase(accountData, password, databasePath string) strin
}
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
@ -1163,18 +1246,18 @@ func importUnencryptedDatabase(accountData, password, databasePath string) strin
return makeJSONResponse(err)
}
func ChangeDatabasePassword(KeyUID, password, newPassword string) string {
return callWithResponse(changeDatabasePassword, KeyUID, password, newPassword)
func ChangeDatabasePassword(keyUID, password, newPassword string) string {
return changeDatabasePassword(keyUID, password, newPassword)
}
// changeDatabasePassword changes the password of the database
func changeDatabasePassword(KeyUID, password, newPassword string) string {
err := statusBackend.ChangeDatabasePassword(KeyUID, password, newPassword)
func changeDatabasePassword(keyUID, password, newPassword string) string {
err := statusBackend.ChangeDatabasePassword(keyUID, password, newPassword)
return makeJSONResponse(err)
}
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
@ -1195,7 +1278,7 @@ func convertToKeycardAccount(accountData, settingsJSON, keycardUID, password, ne
}
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

View File

@ -13,7 +13,28 @@ import (
"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 {
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)
}
})
}
}