Skip to content

Commit 7e94c5e

Browse files
hackaugustoLefterisJP
authored andcommitted
proper validation errors
1 parent 2370c9f commit 7e94c5e

File tree

2 files changed

+132
-12
lines changed

2 files changed

+132
-12
lines changed

raiden/api/v1/encoding.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@
99
SchemaOpts,
1010
)
1111
from webargs import validate
12-
from werkzeug.routing import BaseConverter
13-
14-
from pyethapp.jsonrpc import address_encoder, address_decoder, data_encoder, data_decoder
12+
from werkzeug.routing import (
13+
BaseConverter,
14+
ValidationError,
15+
)
16+
from pyethapp.jsonrpc import (
17+
address_encoder,
18+
data_encoder,
19+
data_decoder,
20+
)
1521

1622
from raiden.api.objects import (
1723
Channel,
@@ -37,22 +43,46 @@
3743

3844
class HexAddressConverter(BaseConverter):
3945
def to_python(self, value):
40-
if value[:2] == '0x':
41-
return value[2:].decode('hex')
46+
if value[:2] != '0x':
47+
raise ValidationError()
48+
49+
try:
50+
value = value[2:].decode('hex')
51+
except TypeError:
52+
raise ValidationError()
53+
54+
if len(value) != 20:
55+
raise ValidationError()
4256

43-
raise Exception('invalid address')
57+
return value
4458

4559
def to_url(self, value):
4660
return address_encoder(value)
4761

4862

4963
class AddressField(fields.Field):
64+
default_error_messages = {
65+
'missing_prefix': 'Not a valid hex encoded address, must be 0x prefixed.',
66+
'invalid_data': 'Not a valid hex encoded address, contains invalid characters.',
67+
'invalid_size': 'Not a valid hex encoded address, decoded address is not 20 bytes long.',
68+
}
5069

5170
def _serialize(self, value, attr, obj):
5271
return address_encoder(value)
5372

54-
def _deserialize(self, value, attr, obj):
55-
return address_decoder(value)
73+
def _deserialize(self, value, attr, data):
74+
if value[:2] != '0x':
75+
self.fail('missing_prefix')
76+
77+
try:
78+
value = value[2:].decode('hex')
79+
except TypeError:
80+
self.fail('invalid_data')
81+
82+
if len(value) != 20:
83+
self.fail('invalid_size')
84+
85+
return value
5686

5787

5888
class DataField(fields.Field):

raiden/tests/api/test_api.py

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# -*- coding: utf-8 -*-
2-
import pytest
3-
import grequests
42
import httplib
53
import json
6-
from flask import url_for
74

5+
import pytest
6+
import grequests
7+
from flask import url_for
88
from pyethapp.jsonrpc import address_encoder, address_decoder
99

10-
from raiden.api.v1.encoding import HexAddressConverter
10+
from raiden.api.v1.encoding import (
11+
AddressField,
12+
HexAddressConverter,
13+
)
1114
from raiden.utils import channel_to_api_dict
1215
from raiden.tests.utils.transfer import channel
1316
from raiden.transfer.state import (
@@ -59,6 +62,28 @@ def test_hex_converter():
5962
assert converter.to_python('0x414d72a6f6e28f4950117696081450d63d56c354') == address
6063

6164

65+
def test_address_field():
66+
# pylint: disable=protected-access
67+
field = AddressField()
68+
attr = 'test'
69+
data = object()
70+
71+
# invalid hex data
72+
with pytest.raises(Exception):
73+
field._deserialize('-', attr, data)
74+
75+
# invalid address, too short
76+
with pytest.raises(Exception):
77+
field._deserialize('1234', attr, data)
78+
79+
# missing prefix 0x
80+
with pytest.raises(Exception):
81+
field._deserialize('414d72a6f6e28f4950117696081450d63d56c354', attr, data)
82+
83+
address = b'AMr\xa6\xf6\xe2\x8fIP\x11v\x96\x08\x14P\xd6=V\xc3T'
84+
assert field._deserialize('0x414d72a6f6e28f4950117696081450d63d56c354', attr, data) == address
85+
86+
6287
@pytest.mark.parametrize('blockchain_type', ['geth'])
6388
@pytest.mark.parametrize('number_of_nodes', [2])
6489
def test_channel_to_api_dict(raiden_network, token_addresses, settle_timeout):
@@ -80,6 +105,71 @@ def test_channel_to_api_dict(raiden_network, token_addresses, settle_timeout):
80105
assert result == expected_result
81106

82107

108+
def test_url_with_invalid_address(rest_api_port_number, api_backend):
109+
""" Addresses required the leading 0x in the urls. """
110+
111+
url_without_prefix = (
112+
'http://localhost:{port}/api/1/'
113+
'channels/ea674fdde714fd979de3edf0f56aa9716b898ec8'
114+
).format(port=rest_api_port_number)
115+
116+
request = grequests.patch(
117+
url_without_prefix,
118+
json={'state': CHANNEL_STATE_SETTLED}
119+
)
120+
response = request.send().response
121+
122+
assert response.status_code == httplib.NOT_FOUND
123+
124+
125+
def test_payload_with_address_without_prefix(api_backend):
126+
""" Addresses required leading 0x in the payload. """
127+
invalid_address = '61c808d82a3ac53231750dadc13c777b59310bd9'
128+
channel_data_obj = {
129+
'partner_address': invalid_address,
130+
'token_address': '0xea674fdde714fd979de3edf0f56aa9716b898ec8',
131+
'settle_timeout': 10,
132+
}
133+
request = grequests.put(
134+
api_url_for(api_backend, 'channelsresource'),
135+
json=channel_data_obj
136+
)
137+
response = request.send().response
138+
assert response.status_code == httplib.BAD_REQUEST
139+
140+
141+
def test_payload_with_address_invalid_chars(api_backend):
142+
""" Addresses cannot have invalid characters in it. """
143+
invalid_address = '0x61c808d82a3ac53231750dadc13c777b59310bdg' # g at the end is invalid
144+
channel_data_obj = {
145+
'partner_address': invalid_address,
146+
'token_address': '0xea674fdde714fd979de3edf0f56aa9716b898ec8',
147+
'settle_timeout': 10,
148+
}
149+
request = grequests.put(
150+
api_url_for(api_backend, 'channelsresource'),
151+
json=channel_data_obj
152+
)
153+
response = request.send().response
154+
assert response.status_code == httplib.BAD_REQUEST
155+
156+
157+
def test_payload_with_address_invalid_length(api_backend):
158+
""" Encoded addresses must have the right length. """
159+
invalid_address = '0x61c808d82a3ac53231750dadc13c777b59310b' # g at the end is invalid
160+
channel_data_obj = {
161+
'partner_address': invalid_address,
162+
'token_address': '0xea674fdde714fd979de3edf0f56aa9716b898ec8',
163+
'settle_timeout': 10,
164+
}
165+
request = grequests.put(
166+
api_url_for(api_backend, 'channelsresource'),
167+
json=channel_data_obj
168+
)
169+
response = request.send().response
170+
assert response.status_code == httplib.BAD_REQUEST
171+
172+
83173
def test_api_query_channels(
84174
api_backend,
85175
api_test_context,

0 commit comments

Comments
 (0)