from __future__ import annotations import json import subprocess from pathlib import Path from sbom_nexus.scanner import detect_sources, scan_repository def _git(repo: Path, *args: str) -> None: subprocess.run(["git", *args], cwd=repo, check=True, capture_output=True) def test_scan_derives_provenance_and_copyleft_report(tmp_path: Path) -> None: repo = tmp_path / "example" repo.mkdir() _git(repo, "init") _git(repo, "config", "user.email", "test@example.com") _git(repo, "config", "user.name", "Test") (repo / "uv.lock").write_text( 'version = 1\n[[package]]\nname = "pyyaml"\nversion = "6.0.2"\n', encoding="utf-8", ) (repo / "package-lock.json").write_text( json.dumps( { "packages": { "": {"name": "root", "version": "1.0.0"}, "node_modules/copyleft": { "name": "copyleft", "version": "2.0.0", "license": "GPL-3.0-only", }, "node_modules/dev-only": { "name": "dev-only", "version": "3.0.0", "license": "AGPL-3.0-only", "dev": True, }, } } ), encoding="utf-8", ) _git(repo, "add", ".") _git(repo, "commit", "-m", "seed") result = scan_repository(repo) assert result["ok"] is True assert result["schema"] == "sbom-nexus.snapshot.v1" assert result["source_revision"] assert result["generated_at"].endswith("Z") assert result["entry_count"] == 3 assert result["sources"][0]["sha256"] assert result["licence_report"]["copyleft_direct_count"] == 1 assert result["licence_report"]["copyleft_direct_prod"][0]["package_name"] == "copyleft" def test_detection_covers_sources_and_skips_dependency_directories(tmp_path: Path) -> None: (tmp_path / "go.sum").write_text("example.test/mod v1.2.3 h1:abc\n", encoding="utf-8") terraform = tmp_path / "infra" terraform.mkdir() (terraform / ".terraform.lock.hcl").write_text( 'provider "registry.terraform.io/hashicorp/null" {\n version = "3.2.3"\n}\n', encoding="utf-8", ) ansible = tmp_path / "deploy" / "ansible" ansible.mkdir(parents=True) (ansible / "requirements.yml").write_text( "collections:\n - community.general\n", encoding="utf-8" ) ignored = tmp_path / "node_modules" ignored.mkdir() (ignored / "package-lock.json").write_text("{}", encoding="utf-8") sources = {str(path.relative_to(tmp_path)) for path, _parser in detect_sources(tmp_path)} assert sources == { "deploy/ansible/requirements.yml", "go.sum", "infra/.terraform.lock.hcl", } def test_go_sum_marks_modules_declared_in_go_mod_as_direct(tmp_path: Path) -> None: (tmp_path / "go.mod").write_text( "module example.test/app\n\nrequire example.test/direct v1.2.3\n", encoding="utf-8", ) (tmp_path / "go.sum").write_text( "example.test/direct v1.2.3 h1:abc\n" "example.test/direct v1.2.3/go.mod h1:def\n" "example.test/transitive v2.0.0 h1:ghi\n", encoding="utf-8", ) result = scan_repository(tmp_path) assert result["ok"] is True assert [(entry["package_name"], entry["is_direct"]) for entry in result["entries"]] == [ ("example.test/direct", True), ("example.test/transitive", False), ] def test_invalid_source_is_reported_without_false_success(tmp_path: Path) -> None: (tmp_path / "uv.lock").write_text("not = [valid", encoding="utf-8") result = scan_repository(tmp_path) assert result["ok"] is False assert result["entry_count"] == 0 assert result["errors"][0]["source_path"] == "uv.lock"