signer, log: properly escape character sequences (#20987)
* signer: properly handle terminal escape characters * log: use strconv conversion instead of custom escape function * log: remove relection tests for nil
This commit is contained in:
parent
0708b573bc
commit
7f95a85fd4
@ -358,49 +358,19 @@ func formatLogfmtValue(value interface{}, term bool) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stringBufPool = sync.Pool{
|
// escapeString checks if the provided string needs escaping/quoting, and
|
||||||
New: func() interface{} { return new(bytes.Buffer) },
|
// calls strconv.Quote if needed
|
||||||
}
|
|
||||||
|
|
||||||
func escapeString(s string) string {
|
func escapeString(s string) string {
|
||||||
needsQuotes := false
|
needsQuoting := false
|
||||||
needsEscape := false
|
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
if r <= ' ' || r == '=' || r == '"' {
|
// We quote everything below " (0x34) and above~ (0x7E), plus equal-sign
|
||||||
needsQuotes = true
|
if r <= '"' || r > '~' || r == '=' {
|
||||||
}
|
needsQuoting = true
|
||||||
if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' {
|
break
|
||||||
needsEscape = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !needsEscape && !needsQuotes {
|
if !needsQuoting {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
e := stringBufPool.Get().(*bytes.Buffer)
|
return strconv.Quote(s)
|
||||||
e.WriteByte('"')
|
|
||||||
for _, r := range s {
|
|
||||||
switch r {
|
|
||||||
case '\\', '"':
|
|
||||||
e.WriteByte('\\')
|
|
||||||
e.WriteByte(byte(r))
|
|
||||||
case '\n':
|
|
||||||
e.WriteString("\\n")
|
|
||||||
case '\r':
|
|
||||||
e.WriteString("\\r")
|
|
||||||
case '\t':
|
|
||||||
e.WriteString("\\t")
|
|
||||||
default:
|
|
||||||
e.WriteRune(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.WriteByte('"')
|
|
||||||
var ret string
|
|
||||||
if needsQuotes {
|
|
||||||
ret = e.String()
|
|
||||||
} else {
|
|
||||||
ret = string(e.Bytes()[1 : e.Len()-1])
|
|
||||||
}
|
|
||||||
e.Reset()
|
|
||||||
stringBufPool.Put(e)
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,19 @@ func (ui *CommandlineUI) confirm() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitize quotes and truncates 'txt' if longer than 'limit'. If truncated,
|
||||||
|
// and ellipsis is added after the quoted string
|
||||||
|
func sanitize(txt string, limit int) string {
|
||||||
|
if len(txt) > limit {
|
||||||
|
return fmt.Sprintf("%q...", txt[:limit])
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%q", txt)
|
||||||
|
}
|
||||||
|
|
||||||
func showMetadata(metadata Metadata) {
|
func showMetadata(metadata Metadata) {
|
||||||
fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local)
|
fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local)
|
||||||
fmt.Printf("\nAdditional HTTP header data, provided by the external caller:\n")
|
fmt.Printf("\nAdditional HTTP header data, provided by the external caller:\n")
|
||||||
fmt.Printf("\tUser-Agent: %v\n\tOrigin: %v\n", metadata.UserAgent, metadata.Origin)
|
fmt.Printf("\tUser-Agent: %v\n\tOrigin: %v\n", sanitize(metadata.UserAgent, 200), sanitize(metadata.Origin, 100))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApproveTx prompt the user for confirmation to request to sign Transaction
|
// ApproveTx prompt the user for confirmation to request to sign Transaction
|
||||||
@ -113,7 +122,6 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
|||||||
if request.Transaction.Data != nil {
|
if request.Transaction.Data != nil {
|
||||||
d := *request.Transaction.Data
|
d := *request.Transaction.Data
|
||||||
if len(d) > 0 {
|
if len(d) > 0 {
|
||||||
|
|
||||||
fmt.Printf("data: %v\n", hexutil.Encode(d))
|
fmt.Printf("data: %v\n", hexutil.Encode(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,7 +153,7 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp
|
|||||||
for _, nvt := range request.Messages {
|
for _, nvt := range request.Messages {
|
||||||
fmt.Printf("\u00a0\u00a0%v\n", strings.TrimSpace(nvt.Pprint(1)))
|
fmt.Printf("\u00a0\u00a0%v\n", strings.TrimSpace(nvt.Pprint(1)))
|
||||||
}
|
}
|
||||||
fmt.Printf("raw data: \n%q\n", request.Rawdata)
|
fmt.Printf("raw data: \n\t%q\n", request.Rawdata)
|
||||||
fmt.Printf("data hash: %v\n", request.Hash)
|
fmt.Printf("data hash: %v\n", request.Hash)
|
||||||
fmt.Printf("-------------------------------------------\n")
|
fmt.Printf("-------------------------------------------\n")
|
||||||
showMetadata(request.Meta)
|
showMetadata(request.Meta)
|
||||||
|
@ -827,23 +827,23 @@ func (t Types) validate() error {
|
|||||||
}
|
}
|
||||||
for i, typeObj := range typeArr {
|
for i, typeObj := range typeArr {
|
||||||
if len(typeObj.Type) == 0 {
|
if len(typeObj.Type) == 0 {
|
||||||
return fmt.Errorf("type %v:%d: empty Type", typeKey, i)
|
return fmt.Errorf("type %q:%d: empty Type", typeKey, i)
|
||||||
}
|
}
|
||||||
if len(typeObj.Name) == 0 {
|
if len(typeObj.Name) == 0 {
|
||||||
return fmt.Errorf("type %v:%d: empty Name", typeKey, i)
|
return fmt.Errorf("type %q:%d: empty Name", typeKey, i)
|
||||||
}
|
}
|
||||||
if typeKey == typeObj.Type {
|
if typeKey == typeObj.Type {
|
||||||
return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type)
|
return fmt.Errorf("type %q cannot reference itself", typeObj.Type)
|
||||||
}
|
}
|
||||||
if typeObj.isReferenceType() {
|
if typeObj.isReferenceType() {
|
||||||
if _, exist := t[typeObj.typeName()]; !exist {
|
if _, exist := t[typeObj.typeName()]; !exist {
|
||||||
return fmt.Errorf("reference type '%s' is undefined", typeObj.Type)
|
return fmt.Errorf("reference type %q is undefined", typeObj.Type)
|
||||||
}
|
}
|
||||||
if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
|
if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
|
||||||
return fmt.Errorf("unknown reference type '%s", typeObj.Type)
|
return fmt.Errorf("unknown reference type %q", typeObj.Type)
|
||||||
}
|
}
|
||||||
} else if !isPrimitiveTypeValid(typeObj.Type) {
|
} else if !isPrimitiveTypeValid(typeObj.Type) {
|
||||||
return fmt.Errorf("unknown type '%s'", typeObj.Type)
|
return fmt.Errorf("unknown type %q", typeObj.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ var selectorRegexp = regexp.MustCompile(`^([^\)]+)\(([A-Za-z0-9,\[\]]*)\)`)
|
|||||||
|
|
||||||
// parseSelector converts a method selector into an ABI JSON spec. The returned
|
// parseSelector converts a method selector into an ABI JSON spec. The returned
|
||||||
// data is a valid JSON string which can be consumed by the standard abi package.
|
// data is a valid JSON string which can be consumed by the standard abi package.
|
||||||
func parseSelector(selector string) ([]byte, error) {
|
func parseSelector(unescapedSelector string) ([]byte, error) {
|
||||||
// Define a tiny fake ABI struct for JSON marshalling
|
// Define a tiny fake ABI struct for JSON marshalling
|
||||||
type fakeArg struct {
|
type fakeArg struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@ -95,10 +95,10 @@ func parseSelector(selector string) ([]byte, error) {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Inputs []fakeArg `json:"inputs"`
|
Inputs []fakeArg `json:"inputs"`
|
||||||
}
|
}
|
||||||
// Validate the selector and extract it's components
|
// Validate the unescapedSelector and extract it's components
|
||||||
groups := selectorRegexp.FindStringSubmatch(selector)
|
groups := selectorRegexp.FindStringSubmatch(unescapedSelector)
|
||||||
if len(groups) != 3 {
|
if len(groups) != 3 {
|
||||||
return nil, fmt.Errorf("invalid selector %s (%v matches)", selector, len(groups))
|
return nil, fmt.Errorf("invalid selector %q (%v matches)", unescapedSelector, len(groups))
|
||||||
}
|
}
|
||||||
name := groups[1]
|
name := groups[1]
|
||||||
args := groups[2]
|
args := groups[2]
|
||||||
@ -115,7 +115,7 @@ func parseSelector(selector string) ([]byte, error) {
|
|||||||
|
|
||||||
// parseCallData matches the provided call data against the ABI definition and
|
// parseCallData matches the provided call data against the ABI definition and
|
||||||
// returns a struct containing the actual go-typed values.
|
// returns a struct containing the actual go-typed values.
|
||||||
func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
|
func parseCallData(calldata []byte, unescapedAbidata string) (*decodedCallData, error) {
|
||||||
// Validate the call data that it has the 4byte prefix and the rest divisible by 32 bytes
|
// Validate the call data that it has the 4byte prefix and the rest divisible by 32 bytes
|
||||||
if len(calldata) < 4 {
|
if len(calldata) < 4 {
|
||||||
return nil, fmt.Errorf("invalid call data, incomplete method signature (%d bytes < 4)", len(calldata))
|
return nil, fmt.Errorf("invalid call data, incomplete method signature (%d bytes < 4)", len(calldata))
|
||||||
@ -127,9 +127,9 @@ func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
|
|||||||
return nil, fmt.Errorf("invalid call data; length should be a multiple of 32 bytes (was %d)", len(argdata))
|
return nil, fmt.Errorf("invalid call data; length should be a multiple of 32 bytes (was %d)", len(argdata))
|
||||||
}
|
}
|
||||||
// Validate the called method and upack the call data accordingly
|
// Validate the called method and upack the call data accordingly
|
||||||
abispec, err := abi.JSON(strings.NewReader(abidata))
|
abispec, err := abi.JSON(strings.NewReader(unescapedAbidata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid method signature (%s): %v", abidata, err)
|
return nil, fmt.Errorf("invalid method signature (%q): %v", unescapedAbidata, err)
|
||||||
}
|
}
|
||||||
method, err := abispec.MethodById(sigdata)
|
method, err := abispec.MethodById(sigdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,7 +98,7 @@ func (db *Database) ValidateCallData(selector *string, data []byte, messages *co
|
|||||||
if info, err := verifySelector(*selector, data); err != nil {
|
if info, err := verifySelector(*selector, data); err != nil {
|
||||||
messages.Warn(fmt.Sprintf("Transaction contains data, but provided ABI signature could not be matched: %v", err))
|
messages.Warn(fmt.Sprintf("Transaction contains data, but provided ABI signature could not be matched: %v", err))
|
||||||
} else {
|
} else {
|
||||||
messages.Info(info.String())
|
messages.Info(fmt.Sprintf("Transaction invokes the following method: %q", info.String()))
|
||||||
db.AddSelector(*selector, data[:4])
|
db.AddSelector(*selector, data[:4])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -112,6 +112,6 @@ func (db *Database) ValidateCallData(selector *string, data []byte, messages *co
|
|||||||
if info, err := verifySelector(embedded, data); err != nil {
|
if info, err := verifySelector(embedded, data); err != nil {
|
||||||
messages.Warn(fmt.Sprintf("Transaction contains data, but provided ABI signature could not be verified: %v", err))
|
messages.Warn(fmt.Sprintf("Transaction contains data, but provided ABI signature could not be verified: %v", err))
|
||||||
} else {
|
} else {
|
||||||
messages.Info(info.String())
|
messages.Info(fmt.Sprintf("Transaction invokes the following method: %q", info.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user