Skip to content

Commit bc076e8

Browse files
committed
Merge branch 'develop' into main
2 parents 3ddf15d + a53f6b6 commit bc076e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+835
-561
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
![Coverage](coverage_badge.svg)
21
# Bompare, a tool to compare the Software Bill-of-Materials from multiple sources
32

43
## Usage
@@ -31,6 +30,13 @@ E.g.:
3130
- OSX (Mac) using brew: `brew tap dart-lang/dart` and then `brew install dart`
3231
- Windows using [Chocolatey](https://chocolatey.org): `choco install dart-sdk`
3332
- With docker ` docker run -it --rm -v $(pwd):/work -w /work google/dart ./build.sh`
34-
2. Run `build.sh` to run all tests and build a native executable
35-
called `bompare`.
33+
1. Globally install the coverage helper tooling: `dart pub global activate coverage`.
34+
1. Globally install the flutter_coverage_badge: `dart pub global activate flutter_coverage_badge`.
35+
1. Install "lcov" coverage visualization tooling.
36+
1. Run `build.sh` to run all tests and build a native executable
37+
called `bompare`.
38+
39+
If the coverage tools are installed, the build results in an update of the
40+
coverage badge and a [static web site](coverage/index.html) with coverage
41+
details.
3642

analysis_options.yaml

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1 @@
1-
# Defines a default set of lint rules enforced for
2-
# projects at Google. For details and rationale,
3-
# see https://github.com/dart-lang/pedantic#enabled-lints.
4-
include: package:pedantic/analysis_options.yaml
5-
6-
# For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
7-
# Uncomment to specify additional rules.
8-
# linter:
9-
# rules:
10-
# - camel_case_types
11-
12-
analyzer:
13-
# exclude:
14-
# - path/to/excluded/files/**
1+
include: package:lints/recommended.yaml

build.sh

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,14 @@ dart analyze "test"
1111
dart format "bin" "lib" "test"
1212

1313
# Calculate coverage by unit tests
14-
dart run test test --coverage coverage
14+
dart run test --coverage coverage
1515

16-
if hash format_coverage >/dev/null && hash genhtml >/dev/null; then
17-
format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.packages --report-on=lib
16+
if type format_coverage && type genhtml &> /dev/null ; then
17+
dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.packages --report-on=lib
1818
genhtml -o coverage coverage/lcov.info
19-
echo "Coverage report is found in /coverage/index.html"
20-
21-
if hash flutter_coverage_badge >/dev/null; then
22-
flutter_coverage_badge
23-
echo "Coverage badge generated."
24-
else
25-
echo "(Install dart package:flutter_coverage_badge globally to generate coverage badge.)"
26-
fi
19+
echo "Coverage report is found in ./coverage/index.html"
2720
else
28-
echo "(Install dart package:coverage globally and genhtml to generate a full coverage overview.)"
21+
echo "(Install dart package:coverage globally and lcov (genhtml) to generate a full coverage overview.)"
2922
fi
3023

3124
# Build command line executable

coverage_badge.svg

Lines changed: 0 additions & 20 deletions
This file was deleted.

lib/command/abstract_command.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import 'dart:io';
77

88
import 'package:args/command_runner.dart';
9+
import 'package:bompare/service/purl.dart';
910
import 'package:glob/glob.dart';
1011

1112
import '../service/bom_service.dart';
@@ -20,6 +21,7 @@ abstract class AbstractCommand extends Command {
2021
static const option_spdx = 'spdx-tag-value';
2122
static const option_white_source = 'whitesource';
2223
static const option_black_duck = 'blackduck';
24+
static const option_default_type = 'default-type';
2325
static const option_output = 'out';
2426
static const option_diff_only = 'diffOnly';
2527
static const option_verbose = 'verbose';
@@ -56,6 +58,9 @@ abstract class AbstractCommand extends Command {
5658
abbr: 'b',
5759
help: 'Scan result in Black Duck "report" (ZIP/directory) format',
5860
valueHelp: 'filename or directory glob pattern')
61+
..addOption(option_default_type,
62+
help: 'Overrides the default package type (from "generic")',
63+
valueHelp: 'type')
5964
..addOption(option_spdx_mapping,
6065
help: 'Convert license texts using SPDX mapping CSV file, '
6166
'formatted as: "<license text>","<SPDX identifier>"',
@@ -94,6 +99,11 @@ abstract class AbstractCommand extends Command {
9499
await service.loadSpdxMapping(File(spdxMapping));
95100
}
96101

102+
final defaultType = argResults![option_default_type];
103+
if (defaultType != null) {
104+
Purl.defaultType = defaultType;
105+
}
106+
97107
await Future.wait([
98108
_loadTypedResults(option_reference, ScannerType.reference),
99109
_loadTypedResults(

lib/persistence/parser/blackduck_result_parser.dart

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import 'dart:io';
99
import 'package:archive/archive.dart';
1010
import 'package:path/path.dart' as path;
1111

12-
import '../../service/domain/item_id.dart';
12+
import '../../service/domain/bom_item.dart';
1313
import '../../service/domain/scan_result.dart';
1414
import '../../service/domain/spdx_mapper.dart';
15+
import '../../service/purl.dart';
1516
import '../persistence_exception.dart';
1617
import '../result_parser.dart';
1718
import 'csv_parser.dart';
@@ -103,17 +104,18 @@ class BlackDuckResultParser implements ResultParser {
103104
stream.transform(utf8.decoder).transform(LineSplitter());
104105
}
105106

106-
/// Dictionary to lookup licenses per [ItemId].
107+
/// Dictionary to lookup licenses per [BomItem].
107108
class _LicenseDictionary {
108-
final _dict = <ItemId, Set<String>>{};
109+
/// Version id -> Licenses
110+
final _dict = <String, Set<String>>{};
109111

110-
void addLicenses(ItemId id, Iterable<String> values) {
111-
final licenses = _dict[id] ?? {};
112+
void addLicenses(String versionId, Iterable<String> values) {
113+
final licenses = _dict[versionId] ?? {};
112114
licenses.addAll(values);
113-
_dict[id] = licenses;
115+
_dict[versionId] = licenses;
114116
}
115117

116-
Set<String>? operator [](ItemId id) => _dict[id];
118+
Set<String>? operator [](String versionId) => _dict[versionId];
117119
}
118120

119121
/// Extracts license info per component from the Black Duck components CSV file.
@@ -122,40 +124,37 @@ class _BlackDuckComponentsCsvParser extends CsvParser {
122124
final _LicenseDictionary _dictionary;
123125
final SpdxMapper mapper;
124126

125-
var _componentNameIndex = -1;
126-
var _componentVersionIndex = -1;
127-
var _licensesIndex = -1;
127+
late final int _licensesIndex;
128+
late final int _componentVersionIdIndex;
128129

129130
_BlackDuckComponentsCsvParser(this._dictionary, this.mapper);
130131

131132
@override
132133
void headerRow(List<String> columns) {
133-
_componentNameIndex = columnIndexOf('Component name', columns);
134-
_componentVersionIndex = columnIndexOf('Component version name', columns);
135134
_licensesIndex = columnIndexOf('License names', columns);
135+
_componentVersionIdIndex = columnIndexOf('Version id', columns);
136136
}
137137

138138
@override
139139
void dataRow(List<String> columns) {
140-
final component = columns[_componentNameIndex];
141-
final version = columns[_componentVersionIndex];
142-
final id = ItemId(component, version);
143140
final license = columns[_licensesIndex];
144-
_dictionary.addLicenses(id, mapper[license]);
141+
final versionId = columns[_componentVersionIdIndex];
142+
_dictionary.addLicenses(versionId, mapper[license]);
145143
}
146144
}
147145

148146
/// Extracts dependencies from a Black Duck source CSV.
149147
class _BlackDuckSourceCsvParser extends CsvParser {
150148
final ScanResult result;
151149
final _LicenseDictionary licenseDictionary;
152-
final assumed = <ItemId>{};
150+
final assumed = <BomItem>{};
153151

154-
var _versionIndex = -1;
155-
var _originIndex = -1;
156-
var _nameIndex = -1;
157-
var _componentNameIndex = -1;
158-
var _componentVersionIndex = -1;
152+
late final int _versionIndex;
153+
late final int _originIndex;
154+
late final int _nameIndex;
155+
late final int _componentNameIndex;
156+
late final int _componentVersionIndex;
157+
late final int _componentVersionIdIndex;
159158

160159
_BlackDuckSourceCsvParser(this.result, this.licenseDictionary);
161160

@@ -166,62 +165,68 @@ class _BlackDuckSourceCsvParser extends CsvParser {
166165
_nameIndex = columnIndexOf('Origin name id', columns);
167166
_componentNameIndex = columnIndexOf('Component name', columns);
168167
_componentVersionIndex = columnIndexOf('Component version name', columns);
168+
_componentVersionIdIndex = columnIndexOf('Version id', columns);
169169
}
170170

171171
@override
172172
void dataRow(List<String> columns) {
173-
final type = columns[_originIndex];
173+
final origin = columns[_originIndex];
174174
final nameColumn = columns[_nameIndex];
175175
final versionColumn = columns[_versionIndex];
176+
final versionId = columns[_componentVersionIdIndex];
177+
final type = _purlType[origin] ?? origin;
176178

177-
var itemId;
178-
switch (type) {
179+
late BomItem item;
180+
switch (origin) {
179181
case '': // Signature scan result
180182
final component = columns[_componentNameIndex];
181183
final componentVersion = columns[_componentVersionIndex];
182-
itemId = ItemId(component, componentVersion);
184+
item = BomItem(Purl.of(
185+
type: 'generic', name: component, version: componentVersion));
183186
break;
184187
case 'maven':
185188
case 'github':
186189
final name2 = _stripFromLast(nameColumn, ':').replaceAll(':', '/');
187-
itemId = ItemId(name2, versionColumn);
190+
item =
191+
BomItem(Purl.of(type: type, name: name2, version: versionColumn));
188192
break;
189193
case 'npmjs':
190194
case 'nuget':
191195
final name2 = _stripFromLast(nameColumn, '/');
192-
itemId = ItemId(name2, versionColumn);
196+
item =
197+
BomItem(Purl.of(type: type, name: name2, version: versionColumn));
193198
break;
194199
case 'alpine':
195200
final name = _stripSeparatedPostFix(nameColumn, versionColumn);
196201
final version = _stripFromLast(versionColumn, '/');
197-
itemId = ItemId(name, version);
202+
item = BomItem(Purl.of(type: type, name: name, version: version));
198203
break;
199204
case 'centos':
200205
final name = _stripFrom(nameColumn, '/');
201206
final temp = versionColumn.substring(versionColumn.indexOf(':') + 1);
202207
final version = _stripFrom(temp, '-');
203-
itemId = ItemId(name, version);
208+
item = BomItem(Purl.of(type: type, name: name, version: version));
204209
break;
205210
case 'debian':
206211
final version = _stripFrom(versionColumn, '/');
207212
final name = _stripSeparatedPostFix(nameColumn, version);
208-
itemId = ItemId(name, version);
213+
item = BomItem(Purl.of(type: type, name: name, version: version));
209214
break;
210215
case 'long_tail':
211216
final name = _stripFromLast(nameColumn, '#');
212-
itemId = ItemId(name, versionColumn);
217+
item = BomItem(Purl.of(type: type, name: name, version: versionColumn));
213218
break;
214219
default:
215220
final name = _stripFromLast(nameColumn, '/');
216-
itemId = ItemId(name, versionColumn);
217-
if (!assumed.contains(itemId)) {
221+
item = BomItem(Purl.of(type: type, name: name, version: versionColumn));
222+
if (!assumed.contains(item)) {
218223
print(
219-
'Warning: Assumed name=${itemId.package}, version=${itemId.version} for Black Duck type "$type"');
220-
assumed.add(itemId);
224+
'Warning: Assumed ${item.purl} for Black Duck type "$origin" -> "$nameColumn"');
225+
assumed.add(item);
221226
}
222227
}
223-
_addLicenses(columns, itemId);
224-
result.addItem(itemId);
228+
item.addLicenses(licenseDictionary[versionId] ?? {});
229+
result.addItem(item);
225230
}
226231

227232
String _stripFrom(String string, Pattern pattern) {
@@ -238,11 +243,19 @@ class _BlackDuckSourceCsvParser extends CsvParser {
238243
final index = string.lastIndexOf(postfix);
239244
return (index < 0) ? string : string.substring(0, index - 1);
240245
}
241-
242-
void _addLicenses(List<String> columns, ItemId itemId) {
243-
final componentName = columns[_componentNameIndex];
244-
final componentVersion = columns[_componentVersionIndex];
245-
final component = ItemId(componentName, componentVersion);
246-
itemId.addLicenses(licenseDictionary[component] ?? {});
247-
}
248246
}
247+
248+
const _purlType = {
249+
'arch_linux': 'arch',
250+
'centos': 'rpm',
251+
'fedora': 'rpm',
252+
'redhat': 'rpm',
253+
'opensuse': 'rpm',
254+
'crates': 'cargo',
255+
'dart': 'pub',
256+
'debian': 'deb',
257+
'ubuntu': 'deb',
258+
'long_tail': 'generic',
259+
'npmjs': 'npm',
260+
'rubygems': 'gem',
261+
};

lib/persistence/parser/jk1_result_parser.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import 'dart:io';
88

99
import 'package:path/path.dart' as path;
1010

11-
import '../../service/domain/item_id.dart';
11+
import '../../service/domain/bom_item.dart';
1212
import '../../service/domain/scan_result.dart';
1313
import '../../service/domain/spdx_mapper.dart';
14+
import '../../service/purl.dart';
1415
import '../persistence_exception.dart';
1516
import '../result_parser.dart';
1617

@@ -40,7 +41,8 @@ class Jk1ResultParser implements ResultParser {
4041
final package = (obj[field_module_name] as String).replaceAll(':', '/');
4142
final version = obj[field_module_version];
4243
var license = obj[field_module_license];
43-
return ItemId(package, version)..addLicenses(mapper[license]);
44+
return BomItem(Purl.of(type: 'maven', name: package, version: version))
45+
..addLicenses(mapper[license]);
4446
}).forEach((id) => result.addItem(id));
4547

4648
return Future.value(result);

lib/persistence/parser/license_checker_result_parser.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import 'dart:io';
88

99
import 'package:path/path.dart' as path;
1010

11-
import '../../service/domain/item_id.dart';
11+
import '../../service/domain/bom_item.dart';
1212
import '../../service/domain/scan_result.dart';
1313
import '../../service/domain/spdx_mapper.dart';
14+
import '../../service/purl.dart';
1415
import '../persistence_exception.dart';
1516
import '../result_parser.dart';
1617
import 'csv_parser.dart';
@@ -60,7 +61,8 @@ class _LicenseFileParser extends CsvParser {
6061
final version = module.substring(pos + 1);
6162
final name = module.substring(0, pos);
6263
final licenses = mapper[columns[_license_column]];
63-
final item = ItemId(name, version)..addLicenses(licenses);
64+
final item = BomItem(Purl.of(type: 'npm', name: name, version: version))
65+
..addLicenses(licenses);
6466
result.addItem(item);
6567
}
6668

0 commit comments

Comments
 (0)