-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_configure_ldap.py
More file actions
322 lines (262 loc) · 11 KB
/
test_configure_ldap.py
File metadata and controls
322 lines (262 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#!/usr/bin/env python3
"""Tests for configure_ldap.py.
This module tests the LDAP configuration script for Redmine.
IMPORTANT: This script has SQL injection vulnerabilities that should be tested.
"""
import subprocess
from unittest.mock import MagicMock
from pytest_mock import MockFixture
from scripts.redmine.configure_ldap import configure_ldap
class TestConfigureLdap:
"""Tests for configure_ldap function."""
def test_configure_ldap_success(self, mocker: MockFixture) -> None:
"""Test successful LDAP configuration."""
# Mock environment variables
mocker.patch.dict(
"os.environ",
{
"REDMINE_DB_HOST": "localhost",
"REDMINE_DB_PORT": "5432",
"REDMINE_DB_USER": "redmine",
"REDMINE_DB_NAME": "redmine",
"REDMINE_DB_PASSWORD": "secret",
},
)
# Mock file operations for .pgpass
_mock_fdopen = mocker.patch("os.fdopen")
_mock_chmod = mocker.patch("os.chmod")
_mock_unlink = mocker.patch("os.unlink")
mocker.patch("tempfile.mkstemp", return_value=(999, "/tmp/pgpass_test.tmp"))
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", stderr="", returncode=0)
result = configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="uid=admin,cn=users,dc=example,dc=com",
bind_password="secret123",
base_dn="cn=users,dc=example,dc=com",
use_tls=True,
)
assert result is True
mock_run.assert_called_once()
# Verify psql command structure (direct DB connection)
call_args = mock_run.call_args
cmd = call_args[0][0]
assert cmd[0] == "psql"
assert "-h" in cmd
assert "localhost" in cmd
assert "-U" in cmd
assert "redmine" in cmd
def test_configure_ldap_with_tls_disabled(self, mocker: MockFixture) -> None:
"""Test LDAP configuration with TLS disabled."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
result = configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=389,
bind_dn="uid=admin,cn=users,dc=example,dc=com",
bind_password="secret",
base_dn="cn=users,dc=example,dc=com",
use_tls=False,
)
assert result is True
# Verify TLS is set to false in SQL
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
assert "false" in sql_cmd.lower()
def test_configure_ldap_subprocess_failure(self, mocker: MockFixture) -> None:
"""Test handling of subprocess failure."""
mock_run = mocker.patch("subprocess.run")
mock_run.side_effect = subprocess.CalledProcessError(1, ["kubectl"], stderr="connection refused")
result = configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="uid=admin,cn=users,dc=example,dc=com",
bind_password="secret",
base_dn="cn=users,dc=example,dc=com",
)
assert result is False
def test_configure_ldap_sql_structure(self, mocker: MockFixture) -> None:
"""Test that SQL contains required fields."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
configure_ldap(
namespace="dev",
ldap_name="TestLDAP",
ldap_host="ldap.test.com",
ldap_port=636,
bind_dn="cn=admin,dc=test,dc=com",
bind_password="testpass",
base_dn="dc=test,dc=com",
)
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
# Verify SQL contains expected fields
assert "DELETE FROM auth_sources WHERE type = 'AuthSourceLdap'" in sql_cmd
assert "INSERT INTO auth_sources" in sql_cmd
assert "AuthSourceLdap" in sql_cmd
assert "TestLDAP" in sql_cmd
assert "ldap.test.com" in sql_cmd
assert "636" in sql_cmd
assert "cn=admin,dc=test,dc=com" in sql_cmd
assert "dc=test,dc=com" in sql_cmd
def test_configure_ldap_default_attributes(self, mocker: MockFixture) -> None:
"""Test that default LDAP attributes are set correctly."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="uid=admin",
bind_password="secret",
base_dn="dc=example,dc=com",
)
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
# Verify default attribute mappings
assert "uid" in sql_cmd # attr_login
assert "givenName" in sql_cmd # attr_firstname
assert "sn" in sql_cmd # attr_lastname
assert "mail" in sql_cmd # attr_mail
assert "(objectClass=person)" in sql_cmd # filter
class TestConfigureLdapSqlInjection:
"""Tests for SQL injection vulnerabilities.
IMPORTANT: These tests document the SQL injection vulnerability in the current
implementation. The script uses string formatting for SQL queries which is unsafe.
"""
def test_sql_injection_in_ldap_name(self, mocker: MockFixture) -> None:
"""Test that malicious LDAP name could cause SQL injection."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="", returncode=0)
# This malicious input could break out of the SQL string
malicious_name = "FreeIPA'; DROP TABLE auth_sources; --"
configure_ldap(
namespace="dev",
ldap_name=malicious_name,
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="uid=admin",
bind_password="secret",
base_dn="dc=example,dc=com",
)
# Check that the malicious input is in the SQL (demonstrating vulnerability)
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
# This test documents the vulnerability - malicious input goes straight into SQL
assert malicious_name in sql_cmd
def test_sql_injection_in_bind_password(self, mocker: MockFixture) -> None:
"""Test that malicious password could cause SQL injection."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="", returncode=0)
# Password with SQL injection attempt
malicious_password = "secret'; UPDATE users SET admin=true; --"
configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="uid=admin",
bind_password=malicious_password,
base_dn="dc=example,dc=com",
)
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
# Documents vulnerability - password is not escaped
assert malicious_password in sql_cmd
def test_sql_injection_in_ldap_host(self, mocker: MockFixture) -> None:
"""Test that malicious host could cause SQL injection."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="", returncode=0)
malicious_host = (
"evil.com', 636, '', '', '', 'uid', 'givenName', 'sn', 'mail', "
"true, true, '', 15, true); DELETE FROM auth_sources; --"
)
configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host=malicious_host,
ldap_port=636,
bind_dn="uid=admin",
bind_password="secret",
base_dn="dc=example,dc=com",
)
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
# Documents vulnerability
assert malicious_host in sql_cmd
class TestConfigureLdapEdgeCases:
"""Tests for edge cases and boundary conditions."""
def test_configure_ldap_with_special_chars_in_dn(self, mocker: MockFixture) -> None:
"""Test handling of special characters in DN."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
result = configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="uid=admin+cn=backup,ou=special users,dc=example,dc=com",
bind_password="pass=word+special",
base_dn="ou=special users,dc=example,dc=com",
)
assert result is True
def test_configure_ldap_empty_password(self, mocker: MockFixture) -> None:
"""Test with empty bind password (anonymous bind)."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
result = configure_ldap(
namespace="dev",
ldap_name="FreeIPA",
ldap_host="freeipa.example.com",
ldap_port=636,
bind_dn="",
bind_password="",
base_dn="dc=example,dc=com",
)
assert result is True
def test_configure_ldap_non_standard_port(self, mocker: MockFixture) -> None:
"""Test with non-standard LDAP port."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
result = configure_ldap(
namespace="dev",
ldap_name="CustomLDAP",
ldap_host="ldap.custom.com",
ldap_port=3269, # Global Catalog SSL port
bind_dn="cn=admin",
bind_password="secret",
base_dn="dc=custom,dc=com",
)
assert result is True
call_args = mock_run.call_args
cmd = call_args[0][0]
sql_cmd = cmd[-1]
assert "3269" in sql_cmd
def test_configure_ldap_unicode_values(self, mocker: MockFixture) -> None:
"""Test handling of unicode characters."""
mock_run = mocker.patch("subprocess.run")
mock_run.return_value = MagicMock(stdout="INSERT 0 1", returncode=0)
result = configure_ldap(
namespace="dev",
ldap_name="日本語LDAP",
ldap_host="ldap.例え.jp",
ldap_port=636,
bind_dn="uid=管理者,dc=例え,dc=jp",
bind_password="パスワード",
base_dn="dc=例え,dc=jp",
)
assert result is True