|
3 | 3 | # Copyright (c) IPython Development Team. |
4 | 4 | # Distributed under the terms of the Modified BSD License. |
5 | 5 |
|
| 6 | +import base64 |
| 7 | +import os |
6 | 8 | import re |
| 9 | +from tempfile import TemporaryDirectory |
7 | 10 |
|
8 | 11 | import pytest |
9 | 12 | from nbformat import v4 |
@@ -264,6 +267,31 @@ def test_language_code_error(self): |
264 | 267 |
|
265 | 268 | assert '<html lang="en">' in output |
266 | 269 |
|
| 270 | + def test_embed_images_path_traversal_blocked(self): |
| 271 | + """Path traversal in image src should be blocked when embed_images=True""" |
| 272 | + with TemporaryDirectory() as parent_dir: |
| 273 | + # Create a secret file that an attacker would try to exfiltrate |
| 274 | + secret_content = b"SUPERSECRETCONTENT" |
| 275 | + with open(os.path.join(parent_dir, "secret.txt"), "wb") as f: |
| 276 | + f.write(secret_content) |
| 277 | + |
| 278 | + # Create a temp subdirectory to serve as the notebook's working path |
| 279 | + with TemporaryDirectory(dir=parent_dir) as notebook_dir: |
| 280 | + # Build a notebook with path traversal image references |
| 281 | + nb = v4.new_notebook() |
| 282 | + nb.cells.append(v4.new_markdown_cell("")) |
| 283 | + nb.cells.append(v4.new_markdown_cell('<img src="../secret.txt" alt="exfil"/>')) |
| 284 | + |
| 285 | + exporter = HTMLExporter() |
| 286 | + exporter.embed_images = True |
| 287 | + html, _ = exporter.from_notebook_node( |
| 288 | + nb, resources={"metadata": {"path": notebook_dir}} |
| 289 | + ) |
| 290 | + |
| 291 | + # The secret content must NOT appear as base64 in the output |
| 292 | + target_b64 = base64.b64encode(secret_content).decode() |
| 293 | + self.assertNotIn(target_b64, html) |
| 294 | + |
267 | 295 |
|
268 | 296 | @pytest.mark.parametrize( |
269 | 297 | ("lexer_options"), |
|
0 commit comments