Skip to content
Bitcoin Roots

v31.1-roots.1 · Commit 8: [PATCH 08/17] chore(roots): integrate v31 identity and maintenance tooling

ci/test/test_prepare_release.py

Before: /dev/null · After: ci/test/test_prepare_release.py

File metadata
new file mode 100755
index 0000000000000000000000000000000000000000..eefc00275c41480c3e1b45768881746ec3d59b15

Download original patch · Release notes

Diff rows 401–457 of 457. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.

ci/test/test_prepare_release.py ADDED
@@ -0,0 +1,457 @@ (continued)
401
+ f"{framework}/Versions/A/QtGui"],
402
+ )
403
+ self.assertEqual(result.returncode, 0, result.stderr)
404
+
405
+ def test_accepts_repeated_noncyclic_symlink_traversal(self):
406
+ result = self.validate_zip_links(
407
+ {"alias": "dir", "link": "alias/../alias/file"}, ["dir/", "dir/file"],
408
+ )
409
+ self.assertEqual(result.returncode, 0, result.stderr)
410
+
411
+ def test_rejects_unsafe_dangling_and_cyclic_zip_symlinks(self):
412
+ cases = [
413
+ {"link": "/tmp/outside"},
414
+ {"link": "../outside"},
415
+ {"link": "C:/outside"},
416
+ {"link": "..\\outside"},
417
+ {"link": "outside\0"},
418
+ {"link": ""},
419
+ {"link": b"\xff"},
420
+ {"link": "x" * 4097},
421
+ {"link": "missing"},
422
+ {"link": "other", "other": "link"},
423
+ # Resolve alias before ..; lexical normalization alone misses this escape.
424
+ {"alias": ".", "link": "alias/../outside"},
425
+ ]
426
+ for links in cases:
427
+ with self.subTest(links=links):
428
+ result = self.validate_zip_links(links, ["bin/bitcoind", "outside"])
429
+ self.assertNotEqual(result.returncode, 0)
430
+
431
+ def test_symlinks_alone_do_not_count_as_release_payload(self):
432
+ result = self.validate_zip_links({"link": "."}, [])
433
+ self.assertNotEqual(result.returncode, 0)
434
+
435
+ def test_rejects_duplicate_and_special_zip_members(self):
436
+ root = "bitcoin-roots-30.3-roots.1"
437
+ with tempfile.TemporaryDirectory() as temporary_dir:
438
+ for kind in ("duplicate", "fifo", "device"):
439
+ with self.subTest(kind=kind):
440
+ archive = Path(temporary_dir) / f"{kind}.zip"
441
+ with zipfile.ZipFile(archive, mode="w") as package:
442
+ package.writestr(f"{root}/bin/bitcoind", b"payload")
443
+ info = zipfile.ZipInfo(f"{root}/bin/bitcoind" if kind == "duplicate" else f"{root}/special")
444
+ info.create_system = 3
445
+ mode = {"duplicate": 0o100755, "fifo": 0o10755, "device": 0o20755}[kind]
446
+ info.external_attr = mode << 16
447
+ package.writestr(info, b"payload")
448
+ result = subprocess.run(
449
+ ["python3", ARCHIVE_TOOL, "validate", archive, root],
450
+ capture_output=True, text=True,
451
+ )
452
+ self.assertNotEqual(result.returncode, 0)
453
+ self.assertIn("Duplicate archive member" if kind == "duplicate" else "Unsupported archive member", result.stderr)
454
+
455
+
456
+ if __name__ == "__main__":
457
+ unittest.main()