webssh

Web based ssh client https://github.com/huashengdun/webssh webssh.huashengdun.org/
git clone http://git.hanabi.in/repos/webssh.git
Log | Files | Refs | README | LICENSE

commit 37299468a9f2e50f5b5bd5266953ba4420ecc7fd
parent f610020758d140a6049ec58eaa9f31f7e3e2b24d
Author: Sheng <webmaster0115@gmail.com>
Date:   Mon, 20 Aug 2018 21:20:55 +0800

Added is_valid_hostname to utils

Diffstat:
Mtests/test_app.py | 8++++++++
Mtests/test_utils.py | 13++++++++++++-
Mwebssh/handler.py | 15++++++++++++---
Mwebssh/utils.py | 19+++++++++++++++++++
4 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/tests/test_app.py b/tests/test_app.py @@ -70,6 +70,14 @@ class TestApp(AsyncHTTPTestCase): response = self.fetch('/', method='POST', body=body) self.assertIn(b'"status": "The port field is required"', response.body) + body = 'hostname=127.0.0&port=22&username=&password' + response = self.fetch('/', method='POST', body=body) + self.assertIn(b'"status": "Invalid hostname', response.body) + + body = 'hostname=http://www.googe.com&port=22&username=&password' + response = self.fetch('/', method='POST', body=body) + self.assertIn(b'"status": "Invalid hostname', response.body) + body = 'hostname=127.0.0.1&port=port&username=&password' response = self.fetch('/', method='POST', body=body) self.assertIn(b'"status": "Invalid port', response.body) diff --git a/tests/test_utils.py b/tests/test_utils.py @@ -1,7 +1,7 @@ import unittest from webssh.utils import (is_valid_ipv4_address, is_valid_ipv6_address, - is_valid_port, to_str, to_bytes) + is_valid_port, is_valid_hostname, to_str, to_bytes) class TestUitls(unittest.TestCase): @@ -34,3 +34,14 @@ class TestUitls(unittest.TestCase): self.assertTrue(is_valid_port(80)) self.assertFalse(is_valid_port(0)) self.assertFalse(is_valid_port(65536)) + + def test_is_valid_hostname(self): + self.assertTrue(is_valid_hostname('google.com')) + self.assertTrue(is_valid_hostname('google.com.')) + self.assertTrue(is_valid_hostname('www.google.com')) + self.assertTrue(is_valid_hostname('www.google.com.')) + self.assertFalse(is_valid_hostname('.www.google.com')) + self.assertFalse(is_valid_hostname('http://www.google.com')) + self.assertFalse(is_valid_hostname('https://www.google.com')) + self.assertFalse(is_valid_hostname('127.0.0.1')) + self.assertFalse(is_valid_hostname('::1')) diff --git a/webssh/handler.py b/webssh/handler.py @@ -11,8 +11,10 @@ import tornado.web from tornado.ioloop import IOLoop from webssh.worker import Worker, recycle_worker, workers -from webssh.utils import (is_valid_ipv4_address, is_valid_ipv6_address, - is_valid_port, to_bytes, to_str, UnicodeType) +from webssh.utils import ( + is_valid_ipv4_address, is_valid_ipv6_address, is_valid_port, + is_valid_hostname, to_bytes, to_str, UnicodeType +) try: from concurrent.futures import Future @@ -98,6 +100,13 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler): 'wrong password for decrypting the private key.') return pkey + def get_hostname(self): + value = self.get_value('hostname') + if not (is_valid_hostname(value) | is_valid_ipv4_address(value) | + is_valid_ipv6_address(value)): + raise ValueError('Invalid hostname {}'.format(value)) + return value + def get_port(self): value = self.get_value('port') try: @@ -117,7 +126,7 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler): return value def get_args(self): - hostname = self.get_value('hostname') + hostname = self.get_hostname() port = self.get_port() username = self.get_value('username') password = self.get_argument('password') diff --git a/webssh/utils.py b/webssh/utils.py @@ -1,4 +1,6 @@ import ipaddress +import re + try: from types import UnicodeType @@ -38,3 +40,20 @@ def is_valid_ipv6_address(ipstr): def is_valid_port(port): return 0 < port < 65536 + + +def is_valid_hostname(hostname): + if hostname[-1] == ".": + # strip exactly one dot from the right, if present + hostname = hostname[:-1] + if len(hostname) > 253: + return False + + labels = hostname.split(".") + + # the TLD must be not all-numeric + if re.match(r"[0-9]+$", labels[-1]): + return False + + allowed = re.compile(r"(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE) + return all(allowed.match(label) for label in labels)