-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_config_path_security.py
More file actions
252 lines (192 loc) · 9.28 KB
/
test_config_path_security.py
File metadata and controls
252 lines (192 loc) · 9.28 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
"""Security tests for config file path validation.
Tests Pattern 6 (File Path Validation) applied to:
- config.py (EmpathyConfig.to_yaml, to_json)
- workflows/config.py (WorkflowConfig.save)
- config/xml_config.py (EmpathyXMLConfig.save_to_file)
Copyright 2026 Smart-AI-Memory
Licensed under the Apache License, Version 2.0
"""
import os
import tempfile
from pathlib import Path
import pytest
from attune.config import EmpathyConfig
from attune.config.xml_config import EmpathyXMLConfig
from attune.workflows.config import WorkflowConfig
class TestEmpathyConfigPathValidation:
"""Test path validation in EmpathyConfig."""
def test_to_yaml_prevents_path_traversal(self):
"""Test that to_yaml blocks path traversal attacks."""
config = EmpathyConfig(user_id="test")
# Attempt path traversal (may raise ValueError or PermissionError depending on OS)
with pytest.raises((ValueError, PermissionError)):
config.to_yaml("/etc/empathy.yml")
def test_to_yaml_prevents_null_bytes(self):
"""Test that to_yaml blocks null byte injection."""
config = EmpathyConfig(user_id="test")
with pytest.raises(ValueError, match="contains null bytes"):
config.to_yaml("config\x00.yml")
def test_to_yaml_accepts_valid_path(self):
"""Test that to_yaml accepts valid paths."""
config = EmpathyConfig(user_id="test")
with tempfile.TemporaryDirectory() as tmpdir:
valid_path = os.path.join(tmpdir, "config.yml")
config.to_yaml(valid_path)
assert Path(valid_path).exists()
def test_to_json_prevents_path_traversal(self):
"""Test that to_json blocks path traversal attacks."""
config = EmpathyConfig(user_id="test")
with pytest.raises(ValueError, match="Cannot write to system directory"):
config.to_json("/sys/empathy.json")
def test_to_json_prevents_null_bytes(self):
"""Test that to_json blocks null byte injection."""
config = EmpathyConfig(user_id="test")
with pytest.raises(ValueError, match="contains null bytes"):
config.to_json("config\x00.json")
def test_to_json_accepts_valid_path(self):
"""Test that to_json accepts valid paths."""
config = EmpathyConfig(user_id="test")
with tempfile.TemporaryDirectory() as tmpdir:
valid_path = os.path.join(tmpdir, "config.json")
config.to_json(valid_path)
assert Path(valid_path).exists()
def test_rejects_empty_path(self):
"""Test that empty paths are rejected."""
config = EmpathyConfig(user_id="test")
with pytest.raises(ValueError, match="must be a non-empty string"):
config.to_yaml("")
class TestWorkflowConfigPathValidation:
"""Test path validation in WorkflowConfig."""
def test_save_prevents_path_traversal(self):
"""Test that save blocks path traversal attacks."""
config = WorkflowConfig(default_provider="anthropic")
with pytest.raises(ValueError, match="Cannot write to system directory"):
config.save("/proc/empathy_workflows.json")
def test_save_prevents_null_bytes(self):
"""Test that save blocks null byte injection."""
config = WorkflowConfig(default_provider="anthropic")
with pytest.raises(ValueError, match="contains null bytes"):
config.save("workflows\x00.json")
def test_save_accepts_valid_path_str(self):
"""Test that save accepts valid string paths."""
config = WorkflowConfig(default_provider="anthropic")
with tempfile.TemporaryDirectory() as tmpdir:
valid_path = os.path.join(tmpdir, "workflows.json")
config.save(valid_path)
assert Path(valid_path).exists()
def test_save_accepts_valid_path_object(self):
"""Test that save accepts valid Path objects."""
config = WorkflowConfig(default_provider="anthropic")
with tempfile.TemporaryDirectory() as tmpdir:
valid_path = Path(tmpdir) / "workflows.yaml"
config.save(valid_path)
assert valid_path.exists()
def test_save_creates_yaml_for_yml_suffix(self):
"""Test that save creates YAML files with .yml/.yaml suffix."""
config = WorkflowConfig(default_provider="anthropic")
with tempfile.TemporaryDirectory() as tmpdir:
yaml_path = os.path.join(tmpdir, "workflows.yaml")
config.save(yaml_path)
assert Path(yaml_path).exists()
# Verify it's YAML format
content = Path(yaml_path).read_text()
assert "default_provider" in content
def test_save_creates_json_for_other_suffix(self):
"""Test that save creates JSON files for non-.yml suffix."""
config = WorkflowConfig(default_provider="anthropic")
with tempfile.TemporaryDirectory() as tmpdir:
json_path = os.path.join(tmpdir, "workflows.json")
config.save(json_path)
assert Path(json_path).exists()
# Verify it's JSON format
content = Path(json_path).read_text()
assert "{" in content and "}" in content
class TestXMLConfigPathValidation:
"""Test path validation in EmpathyXMLConfig."""
def test_save_to_file_prevents_path_traversal(self):
"""Test that save_to_file blocks path traversal attacks."""
config = EmpathyXMLConfig()
# Use /dev/test.json as the test path (valid path structure, but in /dev)
# Note: /dev/null/empathy.json fails on macOS because /dev/null is a file, not a dir
with pytest.raises(ValueError, match="Cannot write to system directory"):
config.save_to_file("/dev/test.json")
def test_save_to_file_prevents_null_bytes(self):
"""Test that save_to_file blocks null byte injection."""
config = EmpathyXMLConfig()
with pytest.raises(ValueError, match="contains null bytes"):
config.save_to_file("xml_config\x00.json")
def test_save_to_file_accepts_valid_path(self):
"""Test that save_to_file accepts valid paths."""
config = EmpathyXMLConfig()
with tempfile.TemporaryDirectory() as tmpdir:
valid_path = os.path.join(tmpdir, "xml_config.json")
config.save_to_file(valid_path)
assert Path(valid_path).exists()
def test_save_to_file_uses_default_path(self, tmp_path, monkeypatch):
"""Test that save_to_file uses default path when not specified."""
config = EmpathyXMLConfig()
monkeypatch.chdir(tmp_path)
# Use default path relative to current directory
config.save_to_file(".attune/config.json")
assert (tmp_path / ".attune" / "config.json").exists()
class TestCrossModuleConsistency:
"""Test that path validation is consistent across all config modules."""
@pytest.mark.parametrize(
"dangerous_path",
[
"/etc/passwd",
"/sys/kernel/config",
"/proc/self/environ",
"/dev/random",
],
)
def test_all_modules_block_system_paths(self, dangerous_path):
"""Test that all config modules consistently block system paths."""
empathy_config = EmpathyConfig()
workflow_config = WorkflowConfig()
xml_config = EmpathyXMLConfig()
# All should raise ValueError or PermissionError for system paths
with pytest.raises((ValueError, PermissionError)):
empathy_config.to_json(dangerous_path)
with pytest.raises((ValueError, PermissionError)):
workflow_config.save(dangerous_path)
with pytest.raises((ValueError, PermissionError)):
xml_config.save_to_file(dangerous_path)
@pytest.mark.parametrize(
"null_byte_path",
[
"config\x00.json",
"\x00etc/passwd",
"test.json\x00.txt",
],
)
def test_all_modules_block_null_bytes(self, null_byte_path):
"""Test that all config modules consistently block null bytes."""
empathy_config = EmpathyConfig()
workflow_config = WorkflowConfig()
xml_config = EmpathyXMLConfig()
# All should raise ValueError for null bytes
with pytest.raises(ValueError, match="contains null bytes"):
empathy_config.to_json(null_byte_path)
with pytest.raises(ValueError, match="contains null bytes"):
workflow_config.save(null_byte_path)
with pytest.raises(ValueError, match="contains null bytes"):
xml_config.save_to_file(null_byte_path)
def test_all_modules_accept_relative_paths(self):
"""Test that all config modules accept valid relative paths."""
empathy_config = EmpathyConfig()
workflow_config = WorkflowConfig()
xml_config = EmpathyXMLConfig()
original_cwd = os.getcwd()
with tempfile.TemporaryDirectory() as tmpdir:
try:
os.chdir(tmpdir)
# All should accept relative paths
empathy_config.to_json("empathy.json")
assert Path("empathy.json").exists()
workflow_config.save("workflows.json")
assert Path("workflows.json").exists()
xml_config.save_to_file("xml_config.json")
assert Path("xml_config.json").exists()
finally:
os.chdir(original_cwd)