Skip to content

Commit e6e6c0f

Browse files
committed
TC-2150 TC-2160 TC-2187 TC-2212 TC-2213 License export issues
Signed-off-by: mrizzi <[email protected]>
1 parent b8ca41e commit e6e6c0f

File tree

2 files changed

+248
-120
lines changed

2 files changed

+248
-120
lines changed

spog/api/src/license/license_exporter.rs

Lines changed: 147 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::utils::get_sanitize_filename;
33
use actix_web::body::BoxBody;
44
use actix_web::http::header::ContentType;
55
use actix_web::{HttpResponse, ResponseError};
6-
use csv::WriterBuilder;
6+
use core::time::Duration;
7+
use csv::{Writer, WriterBuilder};
78
use flate2::write::GzEncoder;
89
use flate2::Compression;
910
use http::StatusCode;
@@ -12,6 +13,8 @@ use trustification_common::error::ErrorInformation;
1213

1314
extern crate sanitize_filename;
1415

16+
type CSVs = (Writer<Vec<u8>>, Writer<Vec<u8>>);
17+
1518
pub struct LicenseExporter {
1619
sbom_license: SbomLicense,
1720
extracted_licensing_infos: Vec<ExtractedLicensingInfos>,
@@ -71,6 +74,65 @@ impl LicenseExporter {
7174
}
7275

7376
pub fn generate(&self) -> Result<Vec<u8>, LicenseExporterError> {
77+
let (wtr_sbom, wtr_license_ref) = self.generate_csvs()?;
78+
79+
let sbom_csv = wtr_sbom
80+
.into_inner()
81+
.map_err(|err| LicenseExporterError::CsvIntoInnerError(format!("csv into inner error: {}", err)))?;
82+
let license_ref_csv = wtr_license_ref
83+
.into_inner()
84+
.map_err(|err| LicenseExporterError::CsvIntoInnerError(format!("csv into inner error: {}", err)))?;
85+
86+
let mut compressed_data = Vec::new();
87+
{
88+
let encoder = GzEncoder::new(&mut compressed_data, Compression::default());
89+
90+
let mut archive = Builder::new(encoder);
91+
92+
let mut header = tar::Header::new_gnu();
93+
header.set_size(sbom_csv.len() as u64);
94+
header.set_mode(0o644);
95+
header.set_cksum();
96+
header.set_mtime(
97+
std::time::UNIX_EPOCH
98+
.elapsed()
99+
.unwrap_or(Duration::from_secs(0))
100+
.as_secs(),
101+
);
102+
archive.append_data(
103+
&mut header,
104+
format!(
105+
"{}_sbom_licenses.csv",
106+
&get_sanitize_filename(String::from(&self.sbom_license.sbom_name))
107+
),
108+
&*sbom_csv,
109+
)?;
110+
111+
let mut header = tar::Header::new_gnu();
112+
header.set_size(license_ref_csv.len() as u64);
113+
header.set_mode(0o644);
114+
header.set_cksum();
115+
header.set_mtime(
116+
std::time::UNIX_EPOCH
117+
.elapsed()
118+
.unwrap_or(Duration::from_secs(0))
119+
.as_secs(),
120+
);
121+
archive.append_data(
122+
&mut header,
123+
format!(
124+
"{}_license_ref.csv",
125+
&get_sanitize_filename(String::from(&self.sbom_license.sbom_name))
126+
),
127+
&*license_ref_csv,
128+
)?;
129+
130+
archive.finish()?;
131+
}
132+
Ok(compressed_data)
133+
}
134+
135+
fn generate_csvs(&self) -> Result<CSVs, LicenseExporterError> {
74136
let mut wtr_sbom = WriterBuilder::new()
75137
.delimiter(b'\t')
76138
.quote_style(csv::QuoteStyle::Always)
@@ -104,76 +166,35 @@ impl LicenseExporter {
104166
])?;
105167
}
106168

107-
for pl in &self.sbom_license.packages {
108-
let alternate_package_reference = pl
169+
for package in &self.sbom_license.packages {
170+
let alternate_package_reference = package
109171
.other_reference
110172
.iter()
111173
.map(|reference| reference.as_str())
112174
.collect::<Vec<_>>()
113175
.join("\n");
114176

115-
let spdx_licenses = pl
177+
let spdx_licenses = package
116178
.spdx_licenses
117179
.iter()
118180
.map(|reference| reference.as_str())
119181
.collect::<Vec<_>>()
120182
.join("\n");
121183

122184
wtr_sbom.write_record([
123-
&pl.name,
185+
// &package.name,
186+
&self.sbom_license.sbom_name,
124187
&self.sbom_license.sbom_namespace,
125188
&self.sbom_license.component_group,
126189
&self.sbom_license.component_version,
127-
&pl.purl,
190+
&package.purl,
128191
&spdx_licenses,
129-
&pl.license_name,
130-
&pl.license_text,
192+
&package.license_name,
193+
&package.license_text,
131194
alternate_package_reference.as_str(),
132195
])?;
133196
}
134-
135-
let sbom_csv = wtr_sbom
136-
.into_inner()
137-
.map_err(|err| LicenseExporterError::CsvIntoInnerError(format!("csv into inner error: {}", err)))?;
138-
let license_ref_csv = wtr_license_ref
139-
.into_inner()
140-
.map_err(|err| LicenseExporterError::CsvIntoInnerError(format!("csv into inner error: {}", err)))?;
141-
142-
let mut compressed_data = Vec::new();
143-
{
144-
let encoder = GzEncoder::new(&mut compressed_data, Compression::default());
145-
146-
let mut archive = Builder::new(encoder);
147-
148-
let mut header = tar::Header::new_gnu();
149-
header.set_size(sbom_csv.len() as u64);
150-
header.set_mode(0o644);
151-
header.set_cksum();
152-
archive.append_data(
153-
&mut header,
154-
format!(
155-
"{}_sbom_licenses.csv",
156-
&get_sanitize_filename(String::from(&self.sbom_license.sbom_name))
157-
),
158-
&*sbom_csv,
159-
)?;
160-
161-
let mut header = tar::Header::new_gnu();
162-
header.set_size(license_ref_csv.len() as u64);
163-
header.set_mode(0o644);
164-
header.set_cksum();
165-
archive.append_data(
166-
&mut header,
167-
format!(
168-
"{}_license_ref.csv",
169-
&get_sanitize_filename(String::from(&self.sbom_license.sbom_name))
170-
),
171-
&*license_ref_csv,
172-
)?;
173-
174-
archive.finish()?;
175-
}
176-
Ok(compressed_data)
197+
Ok((wtr_sbom, wtr_license_ref))
177198
}
178199
}
179200

@@ -200,6 +221,81 @@ mod tests {
200221
assert!(!result.contains('/'));
201222
}
202223

224+
#[tokio::test]
225+
async fn test_generate_csvs_cyclonedx() {
226+
let sbom =
227+
load_sbom_file("../test-data/application.cdx.json").unwrap_or_else(|_| panic!("failed to parse test data"));
228+
229+
let license_scanner = LicenseScanner::new(sbom);
230+
231+
let (sbom_licenses, extracted_licensing_info) = license_scanner
232+
.scanner()
233+
.unwrap_or_else(|_| panic!("failed to parse test data"));
234+
235+
let export = LicenseExporter::new(sbom_licenses, extracted_licensing_info);
236+
let (writer_sbom_licenses, _license_ref) = export.generate_csvs().unwrap();
237+
let sbom_licenses = String::from_utf8(writer_sbom_licenses.into_inner().unwrap()).unwrap();
238+
// https://issues.redhat.com/browse/TC-2212
239+
assert_eq!(97, sbom_licenses.matches("spring-petclinic").count());
240+
assert_eq!(97, sbom_licenses.matches("org.springframework.samples").count());
241+
assert_eq!(97, sbom_licenses.matches("3.3.0-SNAPSHOT").count());
242+
assert_eq!(96, sbom_licenses.matches("pkg:maven/").count());
243+
// check some PURLs appear multiple times because they have multiple licenses
244+
assert_eq!(
245+
2,
246+
sbom_licenses
247+
.matches("pkg:maven/ch.qos.logback/[email protected]?type=jar")
248+
.count()
249+
);
250+
assert_eq!(
251+
2,
252+
sbom_licenses
253+
.matches("pkg:maven/ch.qos.logback/[email protected]?type=jar")
254+
.count()
255+
);
256+
assert_eq!(
257+
2,
258+
sbom_licenses
259+
.matches("pkg:maven/jakarta.annotation/[email protected]?type=jar")
260+
.count()
261+
);
262+
assert_eq!(
263+
2,
264+
sbom_licenses
265+
.matches("pkg:maven/org.hdrhistogram/[email protected]?type=jar")
266+
.count()
267+
);
268+
assert_eq!(63, sbom_licenses.matches("Apache-2.0").count());
269+
}
270+
271+
#[tokio::test]
272+
async fn test_generate_csvs_spdx() {
273+
let sbom = load_sbom_file("../test-data/mtv-2.6.json").unwrap_or_else(|_| panic!("failed to parse test data"));
274+
275+
let license_scanner = LicenseScanner::new(sbom);
276+
277+
let (sbom_licenses, extracted_licensing_info) = license_scanner
278+
.scanner()
279+
.unwrap_or_else(|_| panic!("failed to parse test data"));
280+
281+
let export = LicenseExporter::new(sbom_licenses, extracted_licensing_info);
282+
let (writer_sbom_licenses, _license_ref) = export.generate_csvs().unwrap();
283+
let sbom_licenses = String::from_utf8(writer_sbom_licenses.into_inner().unwrap()).unwrap();
284+
// https://issues.redhat.com/browse/TC-2212
285+
assert_eq!(10776, sbom_licenses.matches("MTV-2.6").count());
286+
assert_eq!(
287+
5388,
288+
sbom_licenses
289+
.matches("https://access.redhat.com/security/data/sbom/spdx/MTV-2.6")
290+
.count()
291+
);
292+
assert_eq!(28, sbom_licenses.matches("pkg:oci/").count());
293+
assert_eq!(1976, sbom_licenses.matches("pkg:npm/").count());
294+
assert_eq!(2185, sbom_licenses.matches("pkg:golang/").count());
295+
assert_eq!(1191, sbom_licenses.matches("pkg:rpm/").count());
296+
assert_eq!(4664, sbom_licenses.matches("NOASSERTION").count());
297+
}
298+
203299
#[tokio::test]
204300
async fn is_works_cydx() {
205301
let sbom =

0 commit comments

Comments
 (0)