|
13 | 13 |
|
14 | 14 | from codechecker_server.routing import split_client_GET_request |
15 | 15 | from codechecker_server.routing import split_client_POST_request |
| 16 | +from codechecker_server.routing import is_valid_postgresql_db_name |
16 | 17 |
|
17 | 18 |
|
18 | 19 | def get(path, host="http://localhost:8001/"): |
@@ -61,3 +62,64 @@ def test_post(self): |
61 | 62 |
|
62 | 63 | self.assertEqual(post('/DummyProduct/v6.0/FoobarService'), |
63 | 64 | ('DummyProduct', '6.0', 'FoobarService')) |
| 65 | + |
| 66 | + |
| 67 | +class PostgresqlDbNameValidationTest(unittest.TestCase): |
| 68 | + """ |
| 69 | + Tests for is_valid_postgresql_db_name, which guards against dangerous |
| 70 | + or unusable PostgreSQL database names before CodeChecker issues a |
| 71 | + CREATE DATABASE statement. |
| 72 | + """ |
| 73 | + |
| 74 | + def test_accepts_plain_names(self): |
| 75 | + """Names that are legal even as unquoted identifiers are allowed.""" |
| 76 | + self.assertTrue(is_valid_postgresql_db_name('myapp')) |
| 77 | + self.assertTrue(is_valid_postgresql_db_name('codechecker_test')) |
| 78 | + self.assertTrue(is_valid_postgresql_db_name('db2')) |
| 79 | + self.assertTrue(is_valid_postgresql_db_name('_internal')) |
| 80 | + |
| 81 | + def test_accepts_names_needing_quoting(self): |
| 82 | + """ |
| 83 | + Names that are legal only as quoted identifiers are allowed, since |
| 84 | + CodeChecker always quotes the identifier when creating the database. |
| 85 | + These are the cases reported in the bug ticket. |
| 86 | + """ |
| 87 | + self.assertTrue(is_valid_postgresql_db_name('test-product')) |
| 88 | + self.assertTrue(is_valid_postgresql_db_name('1team')) |
| 89 | + self.assertTrue(is_valid_postgresql_db_name('my-app-prod')) |
| 90 | + # "user" is a PostgreSQL reserved word but valid when quoted. |
| 91 | + self.assertTrue(is_valid_postgresql_db_name('user')) |
| 92 | + |
| 93 | + def test_rejects_empty_or_none(self): |
| 94 | + """Empty string and non-string inputs are rejected.""" |
| 95 | + self.assertFalse(is_valid_postgresql_db_name('')) |
| 96 | + self.assertFalse(is_valid_postgresql_db_name(None)) |
| 97 | + self.assertFalse(is_valid_postgresql_db_name(123)) |
| 98 | + |
| 99 | + def test_rejects_dangerous_characters(self): |
| 100 | + """ |
| 101 | + Characters that could break out of a quoted identifier or embed |
| 102 | + an SQL statement are rejected. |
| 103 | + """ |
| 104 | + self.assertFalse(is_valid_postgresql_db_name('foo"bar')) |
| 105 | + self.assertFalse(is_valid_postgresql_db_name("foo'bar")) |
| 106 | + self.assertFalse(is_valid_postgresql_db_name('foo;bar')) |
| 107 | + self.assertFalse(is_valid_postgresql_db_name('foo\\bar')) |
| 108 | + self.assertFalse(is_valid_postgresql_db_name('foo\x00bar')) |
| 109 | + |
| 110 | + def test_rejects_whitespace(self): |
| 111 | + """Names containing any whitespace are rejected.""" |
| 112 | + self.assertFalse(is_valid_postgresql_db_name('foo bar')) |
| 113 | + self.assertFalse(is_valid_postgresql_db_name('foo\tbar')) |
| 114 | + self.assertFalse(is_valid_postgresql_db_name('foo\nbar')) |
| 115 | + self.assertFalse(is_valid_postgresql_db_name('foo\rbar')) |
| 116 | + |
| 117 | + def test_rejects_overlong_names(self): |
| 118 | + """ |
| 119 | + PostgreSQL silently truncates identifiers longer than 63 bytes, |
| 120 | + which would produce a database CodeChecker cannot reconnect to. |
| 121 | + """ |
| 122 | + self.assertTrue(is_valid_postgresql_db_name('a' * 63)) |
| 123 | + self.assertFalse(is_valid_postgresql_db_name('a' * 64)) |
| 124 | + # 63 characters but more than 63 bytes due to multi-byte encoding. |
| 125 | + self.assertFalse(is_valid_postgresql_db_name('é' * 32)) |
0 commit comments