diff --git a/cmd/signer/rules/rules.go b/cmd/signer/rules/rules.go index b0b40045d1..8eb1a0fbb7 100644 --- a/cmd/signer/rules/rules.go +++ b/cmd/signer/rules/rules.go @@ -19,6 +19,7 @@ package rules import ( "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/cmd/signer/core" "github.com/ethereum/go-ethereum/cmd/signer/rules/deps" "github.com/ethereum/go-ethereum/cmd/signer/storage" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -26,7 +27,6 @@ import ( "github.com/robertkrimen/otto" "os" "strings" - "github.com/ethereum/go-ethereum/cmd/signer/core" ) var ( @@ -47,14 +47,15 @@ func consoleOutput(call otto.FunctionCall) otto.Value { // rulesetUi provides an implementation of SignerUI that evaluates a javascript // file for each defined UI-method type rulesetUi struct { - vm *otto.Otto // The JS vm + vm *otto.Otto // The JS vm next core.SignerUI // The next handler, for manual processing storage storage.Storage } -func NewRuleEvaluator() (*rulesetUi, error) { +func NewRuleEvaluator(next core.SignerUI) (*rulesetUi, error) { c := &rulesetUi{ vm: otto.New(), + next: next, storage: storage.NewEphemeralStorage(), } consoleObj, _ := c.vm.Get("console") @@ -81,32 +82,38 @@ func (r *rulesetUi) Init(javascriptRules string) error { return err } -func (r *rulesetUi) checkApproval(jsfunc string, jsarg []byte, err error) error { +func (r *rulesetUi) checkApproval(jsfunc string, jsarg []byte, err error) (bool, error) { if err != nil { - return err + return false, err } v, err := r.vm.Call(jsfunc, nil, string(jsarg)) - if err != nil { log.Info("error occurred during execution", "error", err) - return err + return false, err } result, err := v.ToString() if err != nil { log.Info("error occurred during response unmarshalling", "error", err) - return err - + return false, err } if result == "Approve" { log.Info("Op approved") - return nil + return true, nil + } else if result == "Reject" { + log.Info("Op rejected") + return false, nil } - return fmt.Errorf("rejected") + return false, fmt.Errorf("Unknown response") } func (r *rulesetUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { jsonreq, err := json.Marshal(request) - if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil { + approved, err := r.checkApproval("ApproveTx", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", "err") + return r.next.ApproveTx(request) + } + if approved { return core.SignTxResponse{Transaction: request.Transaction, Approved: true, Password: ""}, nil } return core.SignTxResponse{Approved: false}, err @@ -114,7 +121,12 @@ func (r *rulesetUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, func (r *rulesetUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { jsonreq, err := json.Marshal(request) - if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil { + approved, err := r.checkApproval("ApproveSignData", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", "err") + return r.next.ApproveSignData(request) + } + if approved { return core.SignDataResponse{Approved: true, Password: ""}, nil } return core.SignDataResponse{Approved: false, Password: ""}, err @@ -122,7 +134,12 @@ func (r *rulesetUi) ApproveSignData(request *core.SignDataRequest) (core.SignDat func (r *rulesetUi) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { jsonreq, err := json.Marshal(request) - if err = r.checkApproval("ApproveTx", jsonreq, err); err == nil { + approved, err := r.checkApproval("ApproveExport", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", "err") + return r.next.ApproveExport(request) + } + if approved { return core.ExportResponse{Approved: true}, nil } return core.ExportResponse{Approved: false}, err @@ -136,7 +153,12 @@ func (r *rulesetUi) ApproveImport(request *core.ImportRequest) (core.ImportRespo func (r *rulesetUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { jsonreq, err := json.Marshal(request) - if err = r.checkApproval("ApproveListing", jsonreq, err); err == nil { + approved, err := r.checkApproval("ApproveListing", jsonreq, err) + if err != nil { + log.Info("Rule-based approval error, going to manual", "error", "err") + return r.next.ApproveListing(request) + } + if approved { return core.ListResponse{Accounts: request.Accounts}, nil } return core.ListResponse{}, err @@ -157,8 +179,8 @@ func (r *rulesetUi) ShowInfo(message string) { log.Info(message) r.next.ShowInfo(message) } -func (r *rulesetUi) OnApprovedTx(tx ethapi.SignTransactionResult) { +func (r *rulesetUi) OnApprovedTx(tx ethapi.SignTransactionResult) { jsonTx, err := json.Marshal(tx) if err != nil { log.Warn("failed marshalling transaction", "tx", tx) @@ -169,5 +191,4 @@ func (r *rulesetUi) OnApprovedTx(tx ethapi.SignTransactionResult) { fmt.Printf("Error in onapprove %v", err) log.Warn("error occurred during execution", "error", err) } - } diff --git a/cmd/signer/rules/rules_test.go b/cmd/signer/rules/rules_test.go index fe738da8c3..ae303e2187 100644 --- a/cmd/signer/rules/rules_test.go +++ b/cmd/signer/rules/rules_test.go @@ -53,9 +53,46 @@ func hexAddr(a string) common.Address { return common.BytesToAddress(common.Hex2 func mixAddr(a string) (*common.MixedcaseAddress, error) { return common.NewMixedcaseAddressFromString(a) } +type alwaysDenyUi struct{} + +func (alwaysDenyUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { + return core.SignTxResponse{request.Transaction, false, ""}, nil +} + +func (alwaysDenyUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { + return core.SignDataResponse{false, ""}, nil +} + +func (alwaysDenyUi) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) { + return core.ExportResponse{false}, nil +} + +func (alwaysDenyUi) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) { + return core.ImportResponse{false, "", ""}, nil +} + +func (alwaysDenyUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { + return core.ListResponse{nil}, nil +} + +func (alwaysDenyUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { + return core.NewAccountResponse{false, ""}, nil +} + +func (alwaysDenyUi) ShowError(message string) { + panic("implement me") +} + +func (alwaysDenyUi) ShowInfo(message string) { + panic("implement me") +} + +func (alwaysDenyUi) OnApprovedTx(tx ethapi.SignTransactionResult) { + panic("implement me") +} func initRuleEngine(js string) (*rulesetUi, error) { - r, err := NewRuleEvaluator() + r, err := NewRuleEvaluator(&alwaysDenyUi{}) if err != nil { return nil, fmt.Errorf("Failed to create js engine: %v", err) } @@ -154,10 +191,13 @@ func TestMissingFunc(t *testing.T) { t.Error("Expected error") } - if r.checkApproval("MissingMethod", nil, nil) == nil { - t.Errorf("Expected error to resolve to 'Reject'") + approved, err := r.checkApproval("MissingMethod", nil, nil); + if err == nil { + t.Errorf("Expected missing method to yield error'") + } + if approved{ + t.Errorf("Expected missing method to cause non-approval") } - fmt.Printf("Err %v", err) }