diff options
Diffstat (limited to 'packages/meshbay-node/tests/test_security_regressions.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_security_regressions.py | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/packages/meshbay-node/tests/test_security_regressions.py b/packages/meshbay-node/tests/test_security_regressions.py index 77d544b..e13dec0 100644 --- a/packages/meshbay-node/tests/test_security_regressions.py +++ b/packages/meshbay-node/tests/test_security_regressions.py @@ -92,12 +92,43 @@ def test_upload_rejects_unsafe_filenames(name): "My Holiday Video.mkv", "report-2026.pdf", "track_01.flac", + # Reported 2026-08-16: an upload refused as "Invalid filename". The rule was + # ASCII-only, so most of the world could not send a file, and — worse — it + # rejected the "name (1).ext" form that _free_name produces itself, so the + # node refused names it had chosen. + "été.txt", + "naïve café.jpg", + "Ich möchte.pdf", + "日本語.mp4", + "rapport (1).pdf", ]) def test_upload_accepts_ordinary_filenames(name): - """The allowlist must not break normal use.""" + """The allowlist must not break normal use, in any script.""" assert _safe_name_re().match(name), f"should be accepted: {name!r}" +@pytest.mark.parametrize("name", [ + "trailing space ", + "ends.with.dot.", + "..", + "a\u202eexe.txt", # right-to-left override: hides the real extension +]) +def test_upload_rejects_names_that_lie_about_themselves(name): + """Widening to Unicode must not admit names that misrepresent the file.""" + assert not _safe_name_re().match(name), f"should be rejected: {name!r}" + + +def test_the_node_never_generates_a_name_it_would_refuse(tmp_path): + """_free_name resolves a collision by appending " (n)"; that has to be legal.""" + from meshbay_node.transport.webrtc_server import _free_name + (tmp_path / "clip.mp4").touch() + (tmp_path / "clip (1).mp4").touch() + chosen = _free_name(tmp_path, "clip.mp4") + assert chosen not in ("clip.mp4", "clip (1).mp4") + assert _safe_name_re().match(chosen), ( + f"the node picked {chosen!r} and would then reject it on the next upload") + + def _session(tmp_path: Path, user_id: str) -> WebRTCPeerSession: """A peer session wired to a real shared root, with sending stubbed out.""" shared_root = tmp_path / "shared" |