bibliothecary 14.2.0 → 14.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/bibliothecary/parsers/conan.rb +243 -0
- data/lib/bibliothecary/purl_util.rb +1 -0
- data/lib/bibliothecary/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6707fc0443320dc26cce55e6adcd048042c13b1648efe3a134952c62e5288f1f
|
|
4
|
+
data.tar.gz: 7e53ee490a0f3eb7ab97470d30332247176c8ea73fdf88182c82da152de50c56
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eea5b5ac599bbd4043a1caf1ff3f89d8356619f9f30a45715a8bb81628673934b58cec80b4234ff863bb41c5b1cfde33a9a00a8ceaa58ce256ec267d99cab292
|
|
7
|
+
data.tar.gz: 7dc7d4532c3da16a34d60e70e10022c773a2b4c748ec15efec8dda0eb3f2718f71f0f1162da28cd886e8e01138f9f945ab16bf8bcf4f4f44ebcf2a292a4a6f54
|
data/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Ported from Go code available at https://github.com/google/osv-scalibr/blob/f37275e81582aee924103d49d9a27c8e353477e7/extractor/filesystem/language/cpp/conanlock/conanlock.go
|
|
4
|
+
# Go code was made available under the Apache License, Version 2.0
|
|
5
|
+
|
|
6
|
+
module Bibliothecary
|
|
7
|
+
module Parsers
|
|
8
|
+
class Conan
|
|
9
|
+
include Bibliothecary::Analyser
|
|
10
|
+
|
|
11
|
+
def self.mapping
|
|
12
|
+
{
|
|
13
|
+
match_filename("conanfile.py") => {
|
|
14
|
+
kind: "manifest",
|
|
15
|
+
parser: :parse_conanfile_py,
|
|
16
|
+
},
|
|
17
|
+
match_filename("conanfile.txt") => {
|
|
18
|
+
kind: "manifest",
|
|
19
|
+
parser: :parse_conanfile_txt,
|
|
20
|
+
},
|
|
21
|
+
match_filename("conan.lock") => {
|
|
22
|
+
kind: "lockfile",
|
|
23
|
+
parser: :parse_lockfile,
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
|
|
29
|
+
add_multi_parser(Bibliothecary::MultiParsers::Spdx)
|
|
30
|
+
add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
|
|
31
|
+
|
|
32
|
+
def self.parse_conanfile_py(file_contents, options: {})
|
|
33
|
+
dependencies = []
|
|
34
|
+
|
|
35
|
+
# Parse self.requires() calls in conanfile.py
|
|
36
|
+
# Pattern matches: self.requires("package/version") or self.requires("package/version", force=True, options={...})
|
|
37
|
+
# Captures only the package spec string; additional keyword arguments are ignored
|
|
38
|
+
file_contents.scan(/self\.requires\(\s*["']([^"']+)["']/).each do |match|
|
|
39
|
+
manifest_dep = match[0]
|
|
40
|
+
reference = parse_conan_reference(manifest_dep)
|
|
41
|
+
|
|
42
|
+
# Skip entries with no name
|
|
43
|
+
next if reference[:name].nil? || reference[:name].empty?
|
|
44
|
+
|
|
45
|
+
dependencies << Dependency.new(
|
|
46
|
+
name: reference[:name],
|
|
47
|
+
requirement: reference[:version],
|
|
48
|
+
type: "runtime",
|
|
49
|
+
source: options.fetch(:filename, nil),
|
|
50
|
+
platform: platform_name
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
ParserResult.new(dependencies: dependencies)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.parse_conanfile_txt(file_contents, options: {})
|
|
58
|
+
dependencies = []
|
|
59
|
+
current_section = nil
|
|
60
|
+
|
|
61
|
+
file_contents.each_line do |line|
|
|
62
|
+
line = line.strip
|
|
63
|
+
|
|
64
|
+
# Skip empty lines and comments
|
|
65
|
+
next if line.empty? || line.start_with?("#")
|
|
66
|
+
|
|
67
|
+
# Check for section headers
|
|
68
|
+
if line.match?(/^\[([^\]]+)\]$/)
|
|
69
|
+
current_section = line[1..-2]
|
|
70
|
+
next
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Parse dependencies in [requires] and [build_requires] sections
|
|
74
|
+
next unless %w[requires build_requires].include?(current_section)
|
|
75
|
+
|
|
76
|
+
reference = parse_conan_reference(manifest_dep)
|
|
77
|
+
next if reference[:name].nil? || reference[:name].empty?
|
|
78
|
+
|
|
79
|
+
dependencies << Dependency.new(
|
|
80
|
+
name: reference[:name],
|
|
81
|
+
requirement: reference[:version],
|
|
82
|
+
type: current_section == "requires" ? "runtime" : "development",
|
|
83
|
+
source: options.fetch(:filename, nil),
|
|
84
|
+
platform: platform_name
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
ParserResult.new(dependencies: dependencies)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.parse_lockfile(file_contents, options: {})
|
|
92
|
+
manifest = JSON.parse(file_contents)
|
|
93
|
+
|
|
94
|
+
# Auto-detect lockfile format version
|
|
95
|
+
if manifest.dig("graph_lock", "nodes")
|
|
96
|
+
parse_v1_lockfile(manifest, options: options)
|
|
97
|
+
else
|
|
98
|
+
parse_v2_lockfile(manifest, options: options)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.parse_v1_lockfile(lockfile, options: {})
|
|
103
|
+
dependencies = []
|
|
104
|
+
|
|
105
|
+
lockfile["graph_lock"]["nodes"].each_value do |node|
|
|
106
|
+
if node["path"] && !node["path"].empty?
|
|
107
|
+
# a local "conanfile.txt", skip
|
|
108
|
+
next
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
reference = nil
|
|
112
|
+
if node["pref"]
|
|
113
|
+
# old format 0.3 (conan 1.27-) lockfiles use "pref" instead of "ref"
|
|
114
|
+
reference = parse_conan_reference(node["pref"])
|
|
115
|
+
elsif node["ref"]
|
|
116
|
+
reference = parse_conan_reference(node["ref"])
|
|
117
|
+
else
|
|
118
|
+
next
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# skip entries with no name, they are most likely consumer's conanfiles
|
|
122
|
+
# and not dependencies to be searched in a database anyway
|
|
123
|
+
next if reference[:name].nil? || reference[:name].empty?
|
|
124
|
+
|
|
125
|
+
type = case node["context"]
|
|
126
|
+
when "build"
|
|
127
|
+
"development"
|
|
128
|
+
else
|
|
129
|
+
"runtime"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
dependencies << Dependency.new(
|
|
133
|
+
name: reference[:name],
|
|
134
|
+
requirement: reference[:version],
|
|
135
|
+
type: type,
|
|
136
|
+
source: options.fetch(:filename, nil),
|
|
137
|
+
platform: platform_name
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
ParserResult.new(dependencies: dependencies)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.parse_v2_lockfile(lockfile, options: {})
|
|
145
|
+
dependencies = []
|
|
146
|
+
|
|
147
|
+
parse_conan_requires(dependencies, lockfile["requires"], "runtime", options)
|
|
148
|
+
parse_conan_requires(dependencies, lockfile["build_requires"], "development", options)
|
|
149
|
+
parse_conan_requires(dependencies, lockfile["python_requires"], "development", options)
|
|
150
|
+
|
|
151
|
+
ParserResult.new(dependencies: dependencies)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Helper method to parse an array of Conan package references
|
|
155
|
+
# Similar to OSV Scalibr's parseConanRequires function
|
|
156
|
+
def self.parse_conan_requires(dependencies, requires, type, options)
|
|
157
|
+
return unless requires && !requires.empty?
|
|
158
|
+
|
|
159
|
+
requires.each do |ref|
|
|
160
|
+
reference = parse_conan_reference(ref)
|
|
161
|
+
|
|
162
|
+
# Skip entries with no name, they are most likely consumer's conanfiles
|
|
163
|
+
# and not dependencies to be searched in a database anyway
|
|
164
|
+
next if reference[:name].nil? || reference[:name].empty?
|
|
165
|
+
|
|
166
|
+
dependencies << Dependency.new(
|
|
167
|
+
name: reference[:name],
|
|
168
|
+
requirement: reference[:version] || "*",
|
|
169
|
+
type: type,
|
|
170
|
+
source: options.fetch(:filename, nil),
|
|
171
|
+
platform: platform_name
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Parse Conan reference
|
|
177
|
+
# Handles the full Conan reference format:
|
|
178
|
+
# name/version[@username[/channel]][#recipe_revision][:package_id[#package_revision]][%timestamp]
|
|
179
|
+
#
|
|
180
|
+
# Based on OSV Scalibr's parseConanReference implementation:
|
|
181
|
+
# https://github.com/google/osv-scalibr/blob/f37275e81582aee924103d49d9a27c8e353477e7/extractor/filesystem/language/cpp/conanlock/conanlock.go
|
|
182
|
+
#
|
|
183
|
+
# Returns a hash with keys: name, version, username, channel, recipe_revision, package_id, package_revision, timestamp
|
|
184
|
+
def self.parse_conan_reference(ref)
|
|
185
|
+
reference = {
|
|
186
|
+
name: nil,
|
|
187
|
+
version: nil,
|
|
188
|
+
username: nil,
|
|
189
|
+
channel: nil,
|
|
190
|
+
recipe_revision: nil,
|
|
191
|
+
package_id: nil,
|
|
192
|
+
package_revision: nil,
|
|
193
|
+
timestamp: nil,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return reference if ref.nil? || ref.empty?
|
|
197
|
+
|
|
198
|
+
# Validate that ref contains "/" (name/version format)
|
|
199
|
+
# This filters out invalid entries like "1.2.3" (version without name)
|
|
200
|
+
return reference unless ref.include?("/")
|
|
201
|
+
|
|
202
|
+
# Strip timestamp: name/version%1234 -> name/version
|
|
203
|
+
parts = ref.split("%", 2)
|
|
204
|
+
if parts.length == 2
|
|
205
|
+
ref = parts[0]
|
|
206
|
+
reference[:timestamp] = parts[1]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Strip package revision: name/version:pkgid#prev -> name/version
|
|
210
|
+
parts = ref.split(":", 2)
|
|
211
|
+
if parts.length == 2
|
|
212
|
+
ref = parts[0]
|
|
213
|
+
pkg_parts = parts[1].split("#", 2)
|
|
214
|
+
reference[:package_id] = pkg_parts[0]
|
|
215
|
+
reference[:package_revision] = pkg_parts[1] if pkg_parts.length == 2
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Strip recipe revision: name/version#rrev -> name/version
|
|
219
|
+
parts = ref.split("#", 2)
|
|
220
|
+
if parts.length == 2
|
|
221
|
+
ref = parts[0]
|
|
222
|
+
reference[:recipe_revision] = parts[1]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Strip username/channel: name/version@user/channel -> name/version
|
|
226
|
+
parts = ref.split("@", 2)
|
|
227
|
+
if parts.length == 2
|
|
228
|
+
ref = parts[0]
|
|
229
|
+
user_channel = parts[1].split("/", 2)
|
|
230
|
+
reference[:username] = user_channel[0]
|
|
231
|
+
reference[:channel] = user_channel[1] if user_channel.length == 2
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Split name/version: name/version -> [name, version]
|
|
235
|
+
parts = ref.split("/", 2)
|
|
236
|
+
reference[:name] = parts[0]
|
|
237
|
+
reference[:version] = parts.length == 2 ? parts[1] : nil
|
|
238
|
+
|
|
239
|
+
reference
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bibliothecary
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 14.
|
|
4
|
+
version: 14.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Nesbitt
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-12 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: commander
|
|
@@ -135,6 +136,7 @@ dependencies:
|
|
|
135
136
|
- - ">="
|
|
136
137
|
- !ruby/object:Gem::Version
|
|
137
138
|
version: '0'
|
|
139
|
+
description:
|
|
138
140
|
email:
|
|
139
141
|
- andrewnez@gmail.com
|
|
140
142
|
executables:
|
|
@@ -181,6 +183,7 @@ files:
|
|
|
181
183
|
- lib/bibliothecary/parsers/bower.rb
|
|
182
184
|
- lib/bibliothecary/parsers/cargo.rb
|
|
183
185
|
- lib/bibliothecary/parsers/cocoapods.rb
|
|
186
|
+
- lib/bibliothecary/parsers/conan.rb
|
|
184
187
|
- lib/bibliothecary/parsers/conda.rb
|
|
185
188
|
- lib/bibliothecary/parsers/cpan.rb
|
|
186
189
|
- lib/bibliothecary/parsers/cran.rb
|
|
@@ -209,6 +212,7 @@ licenses:
|
|
|
209
212
|
- AGPL-3.0
|
|
210
213
|
metadata:
|
|
211
214
|
rubygems_mfa_required: 'true'
|
|
215
|
+
post_install_message:
|
|
212
216
|
rdoc_options: []
|
|
213
217
|
require_paths:
|
|
214
218
|
- lib
|
|
@@ -223,7 +227,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
223
227
|
- !ruby/object:Gem::Version
|
|
224
228
|
version: '0'
|
|
225
229
|
requirements: []
|
|
226
|
-
rubygems_version: 3.
|
|
230
|
+
rubygems_version: 3.4.19
|
|
231
|
+
signing_key:
|
|
227
232
|
specification_version: 4
|
|
228
233
|
summary: Find and parse manifests
|
|
229
234
|
test_files: []
|