Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions scripts/validate_candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ def handle_starttag(self, tag, attrs):
self.links.extend(value for key, value in attrs if key in {"href", "src"} and value)


# Raster-only: SVG and other data: types can carry script or markup, so they stay rejected.
SAFE_INLINE_IMAGE = re.compile(r"(?i)image/(?:png|jpeg|gif|webp);base64,[A-Za-z0-9+/=\s]*\Z")


def validate_links_and_privacy():
checked = 0
files = list((ROOT / "site").rglob("*.html"))
Expand Down Expand Up @@ -106,6 +110,8 @@ def validate_links_and_privacy():
continue
for value in links:
url = urlsplit(html.unescape(value))
if url.scheme == "data" and SAFE_INLINE_IMAGE.match(url.path):
continue # Inline raster image (e.g. the standalone Floorplan's icon); nothing to fetch.
if url.scheme or url.netloc:
if url.scheme not in {"http", "https"} or not url.hostname or url.username or url.password:
raise ValueError("Unsafe public link in " + str(path.relative_to(ROOT)))
Expand Down
9 changes: 8 additions & 1 deletion tests/test_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
from build_candidate import ROOT, NAME, render, build
from validate_candidate import archive_members, extract, validate_links_and_privacy
from validate_candidate import SAFE_INLINE_IMAGE, archive_members, extract, validate_links_and_privacy


def malicious(entries):
Expand Down Expand Up @@ -74,6 +74,13 @@ def test_new_build_cannot_overwrite_candidate(self):
def test_public_safe_links_and_payload(self):
self.assertGreater(validate_links_and_privacy()["local_links_checked"], 0)

def test_only_inline_raster_images_pass_the_link_check(self):
self.assertTrue(SAFE_INLINE_IMAGE.match("image/png;base64,iVBORw0KGgo="))
self.assertTrue(SAFE_INLINE_IMAGE.match("image/webp;base64,UklGRg=="))
for path in ("image/svg+xml;base64,PHN2Zz4=", "text/html;base64,PHNjcmlwdD4=",
"image/png,<script>", "image/png;base64,iVBOR\"onload=x"):
self.assertIsNone(SAFE_INLINE_IMAGE.match(path), path)


if __name__ == "__main__":
unittest.main()
Loading