|
11 | 11 | import unittest |
12 | 12 | import zipfile |
13 | 13 |
|
| 14 | +from pythainlp.corpus import corpus_path |
14 | 15 | from pythainlp.corpus.core import ( |
15 | 16 | _is_within_directory, |
16 | 17 | _safe_extract_tar, |
17 | 18 | _safe_extract_zip, |
18 | 19 | ) |
| 20 | +from pythainlp.tools import get_full_data_path, safe_path_join |
| 21 | +from pythainlp.tools.path import get_pythainlp_data_path |
19 | 22 |
|
20 | 23 |
|
21 | 24 | class SecurityTestCase(unittest.TestCase): |
@@ -186,6 +189,47 @@ def test_is_within_directory_with_symlinks(self): |
186 | 189 | # in the _safe_extract_tar and _safe_extract_zip functions, |
187 | 190 | # which check where symlinks actually point to. |
188 | 191 |
|
| 192 | + def test_get_full_data_path_safe(self): |
| 193 | + """Test that get_full_data_path returns a path within the data directory.""" |
| 194 | + result = get_full_data_path("ttc_freq.txt") |
| 195 | + self.assertTrue(_is_within_directory(get_pythainlp_data_path(), result)) |
| 196 | + |
| 197 | + def test_get_full_data_path_rejects_traversal(self): |
| 198 | + """Test that get_full_data_path rejects path traversal attempts.""" |
| 199 | + with self.assertRaises(ValueError) as ctx: |
| 200 | + get_full_data_path("../../etc/passwd") |
| 201 | + self.assertIn("path traversal", str(ctx.exception).lower()) |
| 202 | + |
| 203 | + def test_get_full_data_path_rejects_multiple_traversal(self): |
| 204 | + """Test that get_full_data_path rejects multiple parent directory traversal.""" |
| 205 | + with self.assertRaises(ValueError) as ctx: |
| 206 | + get_full_data_path("../../../root/.ssh/id_rsa") |
| 207 | + self.assertIn("path traversal", str(ctx.exception).lower()) |
| 208 | + |
| 209 | + def test_safe_path_join_bundled_corpus_safe(self): |
| 210 | + """Test safe_path_join with corpus_path() base accepts safe filenames.""" |
| 211 | + result = safe_path_join(corpus_path(), "negations_th.txt") |
| 212 | + self.assertTrue(_is_within_directory(corpus_path(), result)) |
| 213 | + |
| 214 | + def test_safe_path_join_bundled_corpus_rejects_traversal(self): |
| 215 | + """Test safe_path_join with corpus_path() base rejects traversal.""" |
| 216 | + with self.assertRaises(ValueError) as ctx: |
| 217 | + safe_path_join(corpus_path(), "../../etc/passwd") |
| 218 | + self.assertIn("path traversal", str(ctx.exception).lower()) |
| 219 | + |
| 220 | + def test_safe_path_join_with_tmpdir_safe(self): |
| 221 | + """Test safe_path_join accepts safe sub-paths within a temp directory.""" |
| 222 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 223 | + result = safe_path_join(tmpdir, "model.txt") |
| 224 | + self.assertTrue(_is_within_directory(tmpdir, result)) |
| 225 | + |
| 226 | + def test_safe_path_join_with_tmpdir_rejects_traversal(self): |
| 227 | + """Test safe_path_join rejects traversal escape from a temp directory.""" |
| 228 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 229 | + with self.assertRaises(ValueError) as ctx: |
| 230 | + safe_path_join(tmpdir, "../../etc/passwd") |
| 231 | + self.assertIn("path traversal", str(ctx.exception).lower()) |
| 232 | + |
189 | 233 |
|
190 | 234 | if __name__ == "__main__": |
191 | 235 | unittest.main() |
0 commit comments