Skip to content

Commit

Permalink
Check incoming X-Forwarded-Proto and -Host
Browse files Browse the repository at this point in the history
  • Loading branch information
jirik committed Sep 26, 2023
1 parent 199bbfc commit 15bab3c
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 1 deletion.
26 changes: 26 additions & 0 deletions src/layman/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
HEADER_X_FORWARDED_PROTO_KEY = 'X-Forwarded-Proto'
HEADER_X_FORWARDED_HOST_KEY = 'X-Forwarded-Host'
HEADER_X_FORWARDED_PREFIX_KEY = 'X-Forwarded-Prefix'
HOST_NAME_PATTERN = r'^(?=.{1,253}\.?$)(?:(?!-|[^.]+_)[a-z0-9-_]{1,63}(?<!-)(?:\.|$))+$'


class SimpleStorage:
Expand Down Expand Up @@ -622,6 +623,31 @@ def ensure_home_dir():


def get_x_forwarded_items(request_headers):
proto_key = HEADER_X_FORWARDED_PROTO_KEY
if proto_key in request_headers:
proto = request_headers[proto_key]
allowed_proto_values = ['http', 'https']
if proto not in allowed_proto_values:
raise LaymanError(54,
{'header': proto_key,
'message': f'Optional header {proto_key} contains unsupported value.',
'expected': f'One of {allowed_proto_values}',
'found': proto,
}
)

host_key = HEADER_X_FORWARDED_HOST_KEY
if host_key in request_headers:
host = request_headers[host_key]
if not re.match(HOST_NAME_PATTERN, host):
raise LaymanError(54,
{'header': host_key,
'message': f'Optional header {host_key} contains unsupported value.',
'expected': f'Expected header matching regular expression {HOST_NAME_PATTERN}',
'found': host,
}
)

prefix_key = HEADER_X_FORWARDED_PREFIX_KEY
if prefix_key in request_headers:
prefix = request_headers[prefix_key]
Expand Down
50 changes: 49 additions & 1 deletion src/layman/util_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,55 @@ def test_get_x_forwarded_prefix(headers, exp_result):
'expected': 'Expected header matching regular expression ^(?:/[a-z0-9_-]+)*$',
'found': 'layman-proxy',
},
}, id='without_slash'),
}, id='prefix-without-slash'),
pytest.param(
{'X-Forwarded-Proto': ''},
{
'http_code': 400,
'code': 54,
'data': {
'header': 'X-Forwarded-Proto',
'message': 'Optional header X-Forwarded-Proto contains unsupported value.',
'expected': "One of ['http', 'https']",
'found': '',
},
}, id='empty-proto'),
pytest.param(
{'X-Forwarded-Proto': 'ftp'},
{
'http_code': 400,
'code': 54,
'data': {
'header': 'X-Forwarded-Proto',
'message': 'Optional header X-Forwarded-Proto contains unsupported value.',
'expected': "One of ['http', 'https']",
'found': 'ftp',
},
}, id='unsupported-proto'),
pytest.param(
{'X-Forwarded-Host': 'ABZ.COM'},
{
'http_code': 400,
'code': 54,
'data': {
'header': 'X-Forwarded-Host',
'message': 'Optional header X-Forwarded-Host contains unsupported value.',
'expected': r'Expected header matching regular expression ^(?=.{1,253}\.?$)(?:(?!-|[^.]+_)[a-z0-9-_]{1,63}(?<!-)(?:\.|$))+$',
'found': 'ABZ.COM',
},
}, id='uppercase-host'),
pytest.param(
{'X-Forwarded-Host': ''},
{
'http_code': 400,
'code': 54,
'data': {
'header': 'X-Forwarded-Host',
'message': 'Optional header X-Forwarded-Host contains unsupported value.',
'expected': r'Expected header matching regular expression ^(?=.{1,253}\.?$)(?:(?!-|[^.]+_)[a-z0-9-_]{1,63}(?<!-)(?:\.|$))+$',
'found': '',
},
}, id='empty-host'),
])
def test_get_x_forwarded_prefix_raises(headers, exp_error):
with pytest.raises(LaymanError) as exc_info:
Expand Down

0 comments on commit 15bab3c

Please sign in to comment.