test_data_rule_runtime_sbom.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from __future__ import annotations
  2. import hashlib
  3. import json
  4. from pathlib import Path
  5. from jsonschema import Draft7Validator
  6. from referencing import Registry, Resource
  7. ROOT = Path(__file__).resolve().parents[1]
  8. SBOM = ROOT / "docs" / "security" / "data-rule-runtime-sbom.json"
  9. SCHEMAS = ROOT / "docs" / "security" / "cyclonedx-1.5-schema"
  10. def _load(name: str):
  11. return json.loads((SCHEMAS / name).read_text(encoding="utf-8"))
  12. def test_data_rule_runtime_sbom_validates_against_cyclonedx_15_schema():
  13. expected_schema_digests = {
  14. "bom-1.5.schema.json": (
  15. "a00fcba23a44b72179ac1f288be4c3529"
  16. "b59f6e1b3719709a40685f177516b46"
  17. ),
  18. "spdx.schema.json": (
  19. "c87aa7bb5eb503d40b52ec6bf00de8045"
  20. "df15da7a13cea48d290cf6d36a8d2ea"
  21. ),
  22. "jsf-0.82.schema.json": (
  23. "2faf5eb3651f2ae5f46091a131770d8d8"
  24. "47bbd121139d19c85fc7051bfa58c46"
  25. ),
  26. }
  27. for name, expected in expected_schema_digests.items():
  28. assert hashlib.sha256((SCHEMAS / name).read_bytes()).hexdigest() == (
  29. expected
  30. )
  31. document = json.loads(SBOM.read_text(encoding="utf-8"))
  32. schema = _load("bom-1.5.schema.json")
  33. registry = Registry().with_resources(
  34. [
  35. (
  36. "http://cyclonedx.org/schema/spdx.schema.json",
  37. Resource.from_contents(_load("spdx.schema.json")),
  38. ),
  39. (
  40. "http://cyclonedx.org/schema/jsf-0.82.schema.json",
  41. Resource.from_contents(_load("jsf-0.82.schema.json")),
  42. ),
  43. ]
  44. )
  45. Draft7Validator.check_schema(schema)
  46. errors = sorted(
  47. Draft7Validator(
  48. schema,
  49. registry=registry,
  50. ).iter_errors(document),
  51. key=lambda error: list(error.absolute_path),
  52. )
  53. assert errors == []
  54. def test_data_rule_runtime_sbom_pins_official_oss_dependencies():
  55. document = json.loads(SBOM.read_text(encoding="utf-8"))
  56. components = {
  57. component["name"]: component
  58. for component in document["components"]
  59. }
  60. assert document["bomFormat"] == "CycloneDX"
  61. assert document["specVersion"] == "1.5"
  62. assert set(components) == {
  63. "minio",
  64. "polars",
  65. "polars-runtime-32",
  66. "psutil",
  67. "pyarrow",
  68. }
  69. for name, version, license_id, source in (
  70. (
  71. "polars",
  72. "1.42.1",
  73. "MIT",
  74. "https://github.com/pola-rs/polars",
  75. ),
  76. (
  77. "polars-runtime-32",
  78. "1.42.1",
  79. "MIT",
  80. "https://github.com/pola-rs/polars",
  81. ),
  82. (
  83. "pyarrow",
  84. "21.0.0",
  85. "Apache-2.0",
  86. "https://github.com/apache/arrow",
  87. ),
  88. (
  89. "psutil",
  90. "5.9.8",
  91. "BSD-3-Clause",
  92. "https://github.com/giampaolo/psutil",
  93. ),
  94. (
  95. "minio",
  96. "7.2.10",
  97. "Apache-2.0",
  98. "https://github.com/minio/minio-py",
  99. ),
  100. ):
  101. component = components[name]
  102. assert component["version"] == version
  103. assert component["licenses"] == [
  104. {"license": {"id": license_id}}
  105. ]
  106. assert component["externalReferences"] == [
  107. {"type": "vcs", "url": source}
  108. ]
  109. assert component["properties"][0]["name"] == "dataops:purpose"
  110. assert not {"license", "officialSource", "purpose"} & set(component)
  111. requirements = (ROOT / "requirements.txt").read_text(encoding="utf-8")
  112. for requirement in (
  113. "polars==1.42.1",
  114. "pyarrow==21.0.0",
  115. "psutil==5.9.8",
  116. "minio==7.2.10",
  117. ):
  118. assert requirement in requirements