From 883dd9795bcf4f309222f4f62038537c24d0791f Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Mon, 5 May 2014 22:55:50 +0100 Subject: [PATCH] Error if a .furl config entry contains an unescaped '#'. fixes #2128 Author: Andrew Miller Signed-off-by: Daira Hopwood --- src/allmydata/node.py | 23 ++++++++++++++++++++++- src/allmydata/test/test_client.py | 27 ++++++++++++++++++++++++++- src/allmydata/test/test_node.py | 11 +++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/allmydata/node.py b/src/allmydata/node.py index 8873e5c7..47c8ac3b 100644 --- a/src/allmydata/node.py +++ b/src/allmydata/node.py @@ -55,6 +55,11 @@ class OldConfigError(Exception): class OldConfigOptionError(Exception): pass +class UnescapedHashError(Exception): + def __str__(self): + return ("The configuration entry %s contained an unescaped '#' character." + % quote_output(self.args[0])) + class Node(service.MultiService): # this implements common functionality of both Client nodes and Introducer @@ -101,11 +106,27 @@ class Node(service.MultiService): test_name = tempfile.mktemp() _assert(os.path.dirname(test_name) == tempdir, test_name, tempdir) + @staticmethod + def _contains_unescaped_hash(item): + characters = iter(item) + for c in characters: + if c == '\\': + characters.next() + elif c == '#': + return True + + return False + def get_config(self, section, option, default=_None, boolean=False): try: if boolean: return self.config.getboolean(section, option) - return self.config.get(section, option) + + item = self.config.get(section, option) + if option.endswith(".furl") and self._contains_unescaped_hash(item): + raise UnescapedHashError(item) + + return item except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): if default is _None: fn = os.path.join(self.basedir, u"tahoe.cfg") diff --git a/src/allmydata/test/test_client.py b/src/allmydata/test/test_client.py index 796971b2..df3691f3 100644 --- a/src/allmydata/test/test_client.py +++ b/src/allmydata/test/test_client.py @@ -3,7 +3,7 @@ from twisted.trial import unittest from twisted.application import service import allmydata -from allmydata.node import OldConfigError, OldConfigOptionError, MissingConfigEntry +from allmydata.node import Node, OldConfigError, OldConfigOptionError, MissingConfigEntry, UnescapedHashError from allmydata import client from allmydata.storage_client import StorageFarmBroker from allmydata.util import base32, fileutil @@ -30,6 +30,31 @@ class Basic(testutil.ReallyEqualMixin, unittest.TestCase): BASECONFIG) client.Client(basedir) + def test_comment(self): + dummy = "pb://wl74cyahejagspqgy4x5ukrvfnevlknt@127.0.0.1:58889/bogus" + + should_fail = [r"test#test", r"#testtest", r"test\\#test"] + should_not_fail = [r"test\#test", r"test\\\#test", r"testtest"] + + basedir = "test_client.Basic.test_comment" + os.mkdir(basedir) + + def write_config(shouldfail, s): + config = ("[client]\n" + "introducer.furl = %s\n" % s) + fileutil.write(os.path.join(basedir, "tahoe.cfg"), config) + + for s in should_fail: + self.failUnless(Node._contains_unescaped_hash(s)) + write_config(s) + self.failUnlessRaises(UnescapedHashError, client.Client, basedir) + + for s in should_not_fail: + self.failIf(Node._contains_unescaped_hash(s)) + write_config(s) + client.Client(basedir) + + @mock.patch('twisted.python.log.msg') def test_error_on_old_config_files(self, mock_log_msg): basedir = "test_client.Basic.test_error_on_old_config_files" diff --git a/src/allmydata/test/test_node.py b/src/allmydata/test/test_node.py index 72d6ef8c..88303f2a 100644 --- a/src/allmydata/test/test_node.py +++ b/src/allmydata/test/test_node.py @@ -87,6 +87,17 @@ class TestCase(testutil.SignalMixin, unittest.TestCase): u"\u2621")) return d + def test_tahoe_cfg_hash_in_name(self): + basedir = "test_node/test_cfg_hash_in_name" + nickname = "Hash#Bang!" # a clever nickname containing a hash + fileutil.make_dirs(basedir) + f = open(os.path.join(basedir, 'tahoe.cfg'), 'wt') + f.write("[node]\n") + f.write("nickname = %s\n" % (nickname,)) + f.close() + n = TestNode(basedir) + self.failUnless(n.nickname == nickname) + def test_private_config(self): basedir = "test_node/test_private_config" privdir = os.path.join(basedir, "private") -- 2.45.2