Skip to content
This repository was archived by the owner on Jan 22, 2026. It is now read-only.

Commit 70d7167

Browse files
committed
Rebuild enrichment branch on top of 0.6.0 changes
1 parent e3c1e88 commit 70d7167

9 files changed

Lines changed: 324 additions & 0 deletions

File tree

lib/git/pkgs.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
require_relative "pkgs/ecosystems"
1212
require_relative "pkgs/osv_client"
1313

14+
require_relative "pkgs/purl_helper"
1415
require_relative "pkgs/models/branch"
1516
require_relative "pkgs/models/branch_commit"
1617
require_relative "pkgs/models/commit"
1718
require_relative "pkgs/models/manifest"
1819
require_relative "pkgs/models/dependency_change"
1920
require_relative "pkgs/models/dependency_snapshot"
2021
require_relative "pkgs/models/package"
22+
require_relative "pkgs/models/version"
2123
require_relative "pkgs/models/vulnerability"
2224
require_relative "pkgs/models/vulnerability_package"
2325

lib/git/pkgs/database.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def self.refresh_models
8484
Git::Pkgs::Models::DependencyChange,
8585
Git::Pkgs::Models::DependencySnapshot,
8686
Git::Pkgs::Models::Package,
87+
Git::Pkgs::Models::Version,
8788
Git::Pkgs::Models::Vulnerability,
8889
Git::Pkgs::Models::VulnerabilityPackage
8990
].each do |model|
@@ -201,6 +202,21 @@ def self.create_schema(with_indexes: true)
201202
index [:ecosystem, :name]
202203
end
203204

205+
@db.create_table?(:versions) do
206+
primary_key :id
207+
String :purl, null: false
208+
String :package_purl, null: false
209+
String :license
210+
DateTime :published_at
211+
String :integrity, text: true
212+
String :source
213+
DateTime :enriched_at
214+
DateTime :created_at
215+
DateTime :updated_at
216+
index :purl, unique: true
217+
index :package_purl
218+
end
219+
204220
# Core vulnerability data (one row per CVE/GHSA)
205221
@db.create_table?(:vulnerabilities) do
206222
String :id, primary_key: true # CVE-2024-1234, GHSA-xxxx, etc.

lib/git/pkgs/models/dependency_change.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ def for_platform(platform)
2828
where(ecosystem: platform)
2929
end
3030
end
31+
32+
def purl(with_version: true)
33+
version = nil
34+
if with_version && manifest&.kind == "lockfile"
35+
version = requirement
36+
end
37+
PurlHelper.build_purl(ecosystem: ecosystem, name: name, version: version)
38+
end
3139
end
3240
end
3341
end

lib/git/pkgs/models/dependency_snapshot.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ def self.current_for_branch(branch)
2929

3030
where(commit: commit)
3131
end
32+
33+
def purl(with_version: true)
34+
version = nil
35+
if with_version && manifest&.kind == "lockfile"
36+
version = requirement
37+
end
38+
PurlHelper.build_purl(ecosystem: ecosystem, name: name, version: version)
39+
end
3240
end
3341
end
3442
end

lib/git/pkgs/models/package.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ module Models
66
class Package < Sequel::Model
77
STALE_THRESHOLD = 86400 # 24 hours
88

9+
one_to_many :versions, key: :package_purl, primary_key: :purl
10+
911
dataset_module do
1012
def by_ecosystem(ecosystem)
1113
where(ecosystem: ecosystem)
@@ -20,6 +22,18 @@ def synced
2022
end
2123
end
2224

25+
def parsed_purl
26+
@parsed_purl ||= Purl.parse(purl)
27+
end
28+
29+
def registry_url
30+
parsed_purl.registry_url
31+
end
32+
33+
def enriched?
34+
!enriched_at.nil?
35+
end
36+
2337
def needs_vuln_sync?
2438
vulns_synced_at.nil? || vulns_synced_at < Time.now - STALE_THRESHOLD
2539
end

lib/git/pkgs/models/version.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
module Git
4+
module Pkgs
5+
module Models
6+
class Version < Sequel::Model
7+
many_to_one :package, key: :package_purl, primary_key: :purl
8+
9+
def parsed_purl
10+
@parsed_purl ||= Purl.parse(purl)
11+
end
12+
13+
def version_string
14+
parsed_purl.version
15+
end
16+
17+
def registry_url
18+
parsed_purl.registry_url
19+
end
20+
21+
def enriched?
22+
!enriched_at.nil?
23+
end
24+
end
25+
end
26+
end
27+
end

lib/git/pkgs/purl_helper.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
require "purl"
4+
5+
module Git
6+
module Pkgs
7+
module PurlHelper
8+
# Mapping from Bibliothecary/ecosyste.ms ecosystem names to PURL types
9+
# Source: https://packages.ecosyste.ms/api/v1/registries/
10+
ECOSYSTEM_TO_PURL_TYPE = {
11+
"npm" => "npm",
12+
"go" => "golang",
13+
"docker" => "docker",
14+
"pypi" => "pypi",
15+
"nuget" => "nuget",
16+
"maven" => "maven",
17+
"packagist" => "composer",
18+
"cargo" => "cargo",
19+
"rubygems" => "gem",
20+
"cocoapods" => "cocoapods",
21+
"pub" => "pub",
22+
"bower" => "bower",
23+
"cpan" => "cpan",
24+
"alpine" => "alpine",
25+
"actions" => "githubactions",
26+
"cran" => "cran",
27+
"clojars" => "clojars",
28+
"conda" => "conda",
29+
"hex" => "hex",
30+
"hackage" => "hackage",
31+
"julia" => "julia",
32+
"swiftpm" => "swift",
33+
"openvsx" => "openvsx",
34+
"spack" => "spack",
35+
"homebrew" => "brew",
36+
"puppet" => "puppet",
37+
"deno" => "deno",
38+
"elm" => "elm",
39+
"vcpkg" => "vcpkg",
40+
"racket" => "racket",
41+
"bioconductor" => "bioconductor",
42+
"carthage" => "carthage",
43+
"elpa" => "melpa"
44+
}.freeze
45+
46+
def self.purl_type_for(ecosystem)
47+
ECOSYSTEM_TO_PURL_TYPE.fetch(ecosystem, ecosystem)
48+
end
49+
50+
def self.build_purl(ecosystem:, name:, version: nil)
51+
type = purl_type_for(ecosystem)
52+
Purl::PackageURL.new(type: type, name: name, version: version)
53+
end
54+
end
55+
end
56+
end

test/git/pkgs/test_models.rb

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,150 @@ def test_branch_commit_associations
111111
assert_includes branch.commits, commit
112112
assert_includes commit.branches, branch
113113
end
114+
115+
def test_dependency_change_purl_from_lockfile
116+
repo = Git::Pkgs::Repository.new(@test_dir)
117+
rugged_commit = repo.walk("main").first
118+
commit = Git::Pkgs::Models::Commit.find_or_create_from_rugged(rugged_commit)
119+
120+
manifest = Git::Pkgs::Models::Manifest.find_or_create(
121+
path: "Gemfile.lock",
122+
ecosystem: "rubygems",
123+
kind: "lockfile"
124+
)
125+
126+
change = Git::Pkgs::Models::DependencyChange.create(
127+
commit: commit,
128+
manifest: manifest,
129+
name: "rails",
130+
ecosystem: "rubygems",
131+
change_type: "added",
132+
requirement: "7.0.0"
133+
)
134+
135+
assert_equal "pkg:gem/rails@7.0.0", change.purl.to_s
136+
assert_equal "pkg:gem/rails", change.purl(with_version: false).to_s
137+
end
138+
139+
def test_dependency_change_purl_from_manifest_omits_version
140+
repo = Git::Pkgs::Repository.new(@test_dir)
141+
rugged_commit = repo.walk("main").first
142+
commit = Git::Pkgs::Models::Commit.find_or_create_from_rugged(rugged_commit)
143+
144+
manifest = Git::Pkgs::Models::Manifest.find_or_create(
145+
path: "Gemfile",
146+
ecosystem: "rubygems",
147+
kind: "manifest"
148+
)
149+
150+
change = Git::Pkgs::Models::DependencyChange.create(
151+
commit: commit,
152+
manifest: manifest,
153+
name: "rails",
154+
ecosystem: "rubygems",
155+
change_type: "added",
156+
requirement: "~> 7.0"
157+
)
158+
159+
assert_equal "pkg:gem/rails", change.purl.to_s
160+
end
161+
162+
def test_dependency_snapshot_purl_from_lockfile
163+
repo = Git::Pkgs::Repository.new(@test_dir)
164+
rugged_commit = repo.walk("main").first
165+
commit = Git::Pkgs::Models::Commit.find_or_create_from_rugged(rugged_commit)
166+
167+
manifest = Git::Pkgs::Models::Manifest.find_or_create(
168+
path: "package-lock.json",
169+
ecosystem: "npm",
170+
kind: "lockfile"
171+
)
172+
173+
snapshot = Git::Pkgs::Models::DependencySnapshot.create(
174+
commit: commit,
175+
manifest: manifest,
176+
name: "lodash",
177+
ecosystem: "npm",
178+
requirement: "4.17.21"
179+
)
180+
181+
assert_equal "pkg:npm/lodash@4.17.21", snapshot.purl.to_s
182+
assert_equal "pkg:npm/lodash", snapshot.purl(with_version: false).to_s
183+
end
184+
185+
def test_package_creation
186+
package = Git::Pkgs::Models::Package.create(
187+
purl: "pkg:gem/rails",
188+
latest_version: "7.1.0",
189+
license: "MIT",
190+
description: "Full-stack web framework",
191+
source: "ecosystems"
192+
)
193+
194+
assert_equal "pkg:gem/rails", package.purl
195+
assert_equal "7.1.0", package.latest_version
196+
assert_equal "MIT", package.license
197+
assert_equal "ecosystems", package.source
198+
end
199+
200+
def test_package_parsed_purl
201+
package = Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
202+
203+
assert_equal "gem", package.parsed_purl.type
204+
assert_equal "rails", package.parsed_purl.name
205+
end
206+
207+
def test_package_enriched
208+
package = Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
209+
refute package.enriched?
210+
211+
package.update(enriched_at: Time.now)
212+
assert package.enriched?
213+
end
214+
215+
def test_version_creation
216+
Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
217+
218+
version = Git::Pkgs::Models::Version.create(
219+
purl: "pkg:gem/rails@7.0.0",
220+
package_purl: "pkg:gem/rails",
221+
license: "MIT",
222+
published_at: Time.parse("2021-12-15"),
223+
integrity: "sha256:abc123",
224+
source: "ecosystems"
225+
)
226+
227+
assert_equal "pkg:gem/rails@7.0.0", version.purl
228+
assert_equal "pkg:gem/rails", version.package_purl
229+
assert_equal "7.0.0", version.version_string
230+
end
231+
232+
def test_version_belongs_to_package
233+
package = Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
234+
235+
version = Git::Pkgs::Models::Version.create(
236+
purl: "pkg:gem/rails@7.0.0",
237+
package_purl: "pkg:gem/rails"
238+
)
239+
240+
assert_equal package.id, version.package.id
241+
assert_includes package.versions.map(&:id), version.id
242+
end
243+
244+
def test_package_purl_uniqueness
245+
Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
246+
247+
assert_raises(Sequel::UniqueConstraintViolation) do
248+
Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
249+
end
250+
end
251+
252+
def test_version_purl_uniqueness
253+
Git::Pkgs::Models::Package.create(purl: "pkg:gem/rails")
254+
Git::Pkgs::Models::Version.create(purl: "pkg:gem/rails@7.0.0", package_purl: "pkg:gem/rails")
255+
256+
assert_raises(Sequel::UniqueConstraintViolation) do
257+
Git::Pkgs::Models::Version.create(purl: "pkg:gem/rails@7.0.0", package_purl: "pkg:gem/rails")
258+
end
259+
end
114260
end

test/git/pkgs/test_purl_helper.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class Git::Pkgs::TestPurlHelper < Minitest::Test
6+
def test_purl_type_for_rubygems
7+
assert_equal "gem", Git::Pkgs::PurlHelper.purl_type_for("rubygems")
8+
end
9+
10+
def test_purl_type_for_npm
11+
assert_equal "npm", Git::Pkgs::PurlHelper.purl_type_for("npm")
12+
end
13+
14+
def test_purl_type_for_go
15+
assert_equal "golang", Git::Pkgs::PurlHelper.purl_type_for("go")
16+
end
17+
18+
def test_purl_type_for_packagist
19+
assert_equal "composer", Git::Pkgs::PurlHelper.purl_type_for("packagist")
20+
end
21+
22+
def test_purl_type_for_unknown_falls_back_to_ecosystem
23+
assert_equal "unknown", Git::Pkgs::PurlHelper.purl_type_for("unknown")
24+
end
25+
26+
def test_build_purl_without_version
27+
purl = Git::Pkgs::PurlHelper.build_purl(ecosystem: "rubygems", name: "rails")
28+
assert_equal "pkg:gem/rails", purl.to_s
29+
end
30+
31+
def test_build_purl_with_version
32+
purl = Git::Pkgs::PurlHelper.build_purl(ecosystem: "rubygems", name: "rails", version: "7.0.0")
33+
assert_equal "pkg:gem/rails@7.0.0", purl.to_s
34+
end
35+
36+
def test_build_purl_for_npm
37+
purl = Git::Pkgs::PurlHelper.build_purl(ecosystem: "npm", name: "lodash", version: "4.17.21")
38+
assert_equal "pkg:npm/lodash@4.17.21", purl.to_s
39+
end
40+
41+
def test_build_purl_for_go
42+
purl = Git::Pkgs::PurlHelper.build_purl(ecosystem: "go", name: "github.com/gorilla/mux", version: "1.8.0")
43+
assert_equal "golang", purl.type
44+
assert_equal "github.com/gorilla/mux", purl.name
45+
assert_equal "1.8.0", purl.version
46+
end
47+
end

0 commit comments

Comments
 (0)