Gentoo Archives: gentoo-commits

From: Georgy Yakovlev <gyakovlev@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/cargo-ebuild:master commit in: /, src/
Date: Thu, 26 Aug 2021 07:51:45
Message-Id: 1629963636.1f8c7eb03ace33caba40822651c822d140f2840c.gyakovlev@gentoo
1 commit: 1f8c7eb03ace33caba40822651c822d140f2840c
2 Author: Leonardo Neumann <leonardo <AT> neumann <DOT> dev <DOT> br>
3 AuthorDate: Sat Aug 14 21:05:37 2021 +0000
4 Commit: Georgy Yakovlev <gyakovlev <AT> gentoo <DOT> org>
5 CommitDate: Thu Aug 26 07:40:36 2021 +0000
6 URL: https://gitweb.gentoo.org/proj/cargo-ebuild.git/commit/?id=1f8c7eb0
7
8 Implement audit functionality using rustsec
9
10 Closes: https://github.com/gentoo/cargo-ebuild/pull/15
11 Closes: https://github.com/gentoo/cargo-ebuild/issues/2
12 Signed-off-by: Leonardo Neumann <leonardo <AT> neumann.dev.br>
13 Signed-off-by: Georgy Yakovlev <gyakovlev <AT> gentoo.org>
14
15 Cargo.lock | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
16 Cargo.toml | 1 +
17 src/audit.rs | 96 ++++++++++++++++++++++
18 src/lib.rs | 8 +-
19 src/main.rs | 7 +-
20 5 files changed, 368 insertions(+), 3 deletions(-)
21
22 diff --git a/Cargo.lock b/Cargo.lock
23 index 431eb9d..06293a2 100644
24 --- a/Cargo.lock
25 +++ b/Cargo.lock
26 @@ -37,6 +37,12 @@ dependencies = [
27 "winapi",
28 ]
29
30 +[[package]]
31 +name = "autocfg"
32 +version = "1.0.1"
33 +source = "registry+https://github.com/rust-lang/crates.io-index"
34 +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
35 +
36 [[package]]
37 name = "base-x"
38 version = "0.2.8"
39 @@ -115,6 +121,7 @@ dependencies = [
40 "cargo_metadata",
41 "itertools",
42 "phf",
43 + "rustsec",
44 "serde",
45 "structopt",
46 "tera",
47 @@ -155,6 +162,15 @@ dependencies = [
48 "serde_json",
49 ]
50
51 +[[package]]
52 +name = "cc"
53 +version = "1.0.69"
54 +source = "registry+https://github.com/rust-lang/crates.io-index"
55 +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
56 +dependencies = [
57 + "jobserver",
58 +]
59 +
60 [[package]]
61 name = "cfg-if"
62 version = "1.0.0"
63 @@ -182,6 +198,24 @@ version = "0.4.8"
64 source = "registry+https://github.com/rust-lang/crates.io-index"
65 checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
66
67 +[[package]]
68 +name = "crates-index"
69 +version = "0.17.0"
70 +source = "registry+https://github.com/rust-lang/crates.io-index"
71 +checksum = "8ad4af5c8dd9940a497ef4473e6e558b660a4a1b6e5ce2cb9d85454e2aaaf947"
72 +dependencies = [
73 + "git2",
74 + "glob",
75 + "hex",
76 + "home",
77 + "memchr",
78 + "semver 1.0.4",
79 + "serde",
80 + "serde_derive",
81 + "serde_json",
82 + "smartstring",
83 +]
84 +
85 [[package]]
86 name = "crossbeam-utils"
87 version = "0.8.5"
88 @@ -192,6 +226,15 @@ dependencies = [
89 "lazy_static",
90 ]
91
92 +[[package]]
93 +name = "cvss"
94 +version = "1.0.2"
95 +source = "registry+https://github.com/rust-lang/crates.io-index"
96 +checksum = "829862dabeab142ae0efd558d42d8fd874659268ccd810809ac6f1ee6bfcbd3f"
97 +dependencies = [
98 + "serde",
99 +]
100 +
101 [[package]]
102 name = "digest"
103 version = "0.8.1"
104 @@ -235,6 +278,12 @@ dependencies = [
105 "percent-encoding",
106 ]
107
108 +[[package]]
109 +name = "fs-err"
110 +version = "2.6.0"
111 +source = "registry+https://github.com/rust-lang/crates.io-index"
112 +checksum = "5ebd3504ad6116843b8375ad70df74e7bfe83cac77a1f3fe73200c844d43bfe0"
113 +
114 [[package]]
115 name = "generic-array"
116 version = "0.12.4"
117 @@ -255,6 +304,27 @@ dependencies = [
118 "wasi",
119 ]
120
121 +[[package]]
122 +name = "git2"
123 +version = "0.13.21"
124 +source = "registry+https://github.com/rust-lang/crates.io-index"
125 +checksum = "659cd14835e75b64d9dba5b660463506763cf0aa6cb640aeeb0e98d841093490"
126 +dependencies = [
127 + "bitflags",
128 + "libc",
129 + "libgit2-sys",
130 + "log",
131 + "openssl-probe",
132 + "openssl-sys",
133 + "url",
134 +]
135 +
136 +[[package]]
137 +name = "glob"
138 +version = "0.3.0"
139 +source = "registry+https://github.com/rust-lang/crates.io-index"
140 +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
141 +
142 [[package]]
143 name = "globset"
144 version = "0.4.8"
145 @@ -297,6 +367,40 @@ dependencies = [
146 "libc",
147 ]
148
149 +[[package]]
150 +name = "hex"
151 +version = "0.4.3"
152 +source = "registry+https://github.com/rust-lang/crates.io-index"
153 +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
154 +dependencies = [
155 + "serde",
156 +]
157 +
158 +[[package]]
159 +name = "home"
160 +version = "0.5.3"
161 +source = "registry+https://github.com/rust-lang/crates.io-index"
162 +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
163 +dependencies = [
164 + "winapi",
165 +]
166 +
167 +[[package]]
168 +name = "humantime"
169 +version = "2.1.0"
170 +source = "registry+https://github.com/rust-lang/crates.io-index"
171 +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
172 +
173 +[[package]]
174 +name = "humantime-serde"
175 +version = "1.0.1"
176 +source = "registry+https://github.com/rust-lang/crates.io-index"
177 +checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058"
178 +dependencies = [
179 + "humantime",
180 + "serde",
181 +]
182 +
183 [[package]]
184 name = "idna"
185 version = "0.2.3"
186 @@ -341,6 +445,15 @@ version = "0.4.8"
187 source = "registry+https://github.com/rust-lang/crates.io-index"
188 checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
189
190 +[[package]]
191 +name = "jobserver"
192 +version = "0.1.24"
193 +source = "registry+https://github.com/rust-lang/crates.io-index"
194 +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
195 +dependencies = [
196 + "libc",
197 +]
198 +
199 [[package]]
200 name = "lazy_static"
201 version = "1.4.0"
202 @@ -353,6 +466,46 @@ version = "0.2.101"
203 source = "registry+https://github.com/rust-lang/crates.io-index"
204 checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
205
206 +[[package]]
207 +name = "libgit2-sys"
208 +version = "0.12.22+1.1.0"
209 +source = "registry+https://github.com/rust-lang/crates.io-index"
210 +checksum = "89c53ac117c44f7042ad8d8f5681378dfbc6010e49ec2c0d1f11dfedc7a4a1c3"
211 +dependencies = [
212 + "cc",
213 + "libc",
214 + "libssh2-sys",
215 + "libz-sys",
216 + "openssl-sys",
217 + "pkg-config",
218 +]
219 +
220 +[[package]]
221 +name = "libssh2-sys"
222 +version = "0.2.21"
223 +source = "registry+https://github.com/rust-lang/crates.io-index"
224 +checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
225 +dependencies = [
226 + "cc",
227 + "libc",
228 + "libz-sys",
229 + "openssl-sys",
230 + "pkg-config",
231 + "vcpkg",
232 +]
233 +
234 +[[package]]
235 +name = "libz-sys"
236 +version = "1.1.3"
237 +source = "registry+https://github.com/rust-lang/crates.io-index"
238 +checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
239 +dependencies = [
240 + "cc",
241 + "libc",
242 + "pkg-config",
243 + "vcpkg",
244 +]
245 +
246 [[package]]
247 name = "log"
248 version = "0.4.14"
249 @@ -392,6 +545,25 @@ version = "0.2.3"
250 source = "registry+https://github.com/rust-lang/crates.io-index"
251 checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
252
253 +[[package]]
254 +name = "openssl-probe"
255 +version = "0.1.4"
256 +source = "registry+https://github.com/rust-lang/crates.io-index"
257 +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
258 +
259 +[[package]]
260 +name = "openssl-sys"
261 +version = "0.9.66"
262 +source = "registry+https://github.com/rust-lang/crates.io-index"
263 +checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82"
264 +dependencies = [
265 + "autocfg",
266 + "cc",
267 + "libc",
268 + "pkg-config",
269 + "vcpkg",
270 +]
271 +
272 [[package]]
273 name = "percent-encoding"
274 version = "2.1.0"
275 @@ -485,6 +657,21 @@ dependencies = [
276 "siphasher",
277 ]
278
279 +[[package]]
280 +name = "pkg-config"
281 +version = "0.3.19"
282 +source = "registry+https://github.com/rust-lang/crates.io-index"
283 +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
284 +
285 +[[package]]
286 +name = "platforms"
287 +version = "1.1.0"
288 +source = "registry+https://github.com/rust-lang/crates.io-index"
289 +checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325"
290 +dependencies = [
291 + "serde",
292 +]
293 +
294 [[package]]
295 name = "ppv-lite86"
296 version = "0.2.10"
297 @@ -605,6 +792,29 @@ dependencies = [
298 "semver 0.9.0",
299 ]
300
301 +[[package]]
302 +name = "rustsec"
303 +version = "0.24.2"
304 +source = "registry+https://github.com/rust-lang/crates.io-index"
305 +checksum = "c29c220a60ceaeedb2c5bf51826b3d3c5d77b2523693f0579c8a85dd03f11947"
306 +dependencies = [
307 + "cargo-lock",
308 + "crates-index",
309 + "cvss",
310 + "fs-err",
311 + "git2",
312 + "home",
313 + "humantime",
314 + "humantime-serde",
315 + "platforms",
316 + "semver 1.0.4",
317 + "serde",
318 + "smol_str",
319 + "thiserror",
320 + "toml",
321 + "url",
322 +]
323 +
324 [[package]]
325 name = "ryu"
326 version = "1.0.5"
327 @@ -699,6 +909,22 @@ version = "0.3.6"
328 source = "registry+https://github.com/rust-lang/crates.io-index"
329 checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1"
330
331 +[[package]]
332 +name = "smartstring"
333 +version = "0.2.9"
334 +source = "registry+https://github.com/rust-lang/crates.io-index"
335 +checksum = "31aa6a31c0c2b21327ce875f7e8952322acfcfd0c27569a6e18a647281352c9b"
336 +dependencies = [
337 + "serde",
338 + "static_assertions",
339 +]
340 +
341 +[[package]]
342 +name = "smol_str"
343 +version = "0.1.17"
344 +source = "registry+https://github.com/rust-lang/crates.io-index"
345 +checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
346 +
347 [[package]]
348 name = "standback"
349 version = "0.2.17"
350 @@ -708,6 +934,12 @@ dependencies = [
351 "version_check",
352 ]
353
354 +[[package]]
355 +name = "static_assertions"
356 +version = "1.1.0"
357 +source = "registry+https://github.com/rust-lang/crates.io-index"
358 +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
359 +
360 [[package]]
361 name = "stdweb"
362 version = "0.4.20"
363 @@ -823,6 +1055,26 @@ dependencies = [
364 "unicode-width",
365 ]
366
367 +[[package]]
368 +name = "thiserror"
369 +version = "1.0.26"
370 +source = "registry+https://github.com/rust-lang/crates.io-index"
371 +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
372 +dependencies = [
373 + "thiserror-impl",
374 +]
375 +
376 +[[package]]
377 +name = "thiserror-impl"
378 +version = "1.0.26"
379 +source = "registry+https://github.com/rust-lang/crates.io-index"
380 +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
381 +dependencies = [
382 + "proc-macro2",
383 + "quote",
384 + "syn",
385 +]
386 +
387 [[package]]
388 name = "thread_local"
389 version = "1.1.3"
390 @@ -999,8 +1251,15 @@ dependencies = [
391 "idna",
392 "matches",
393 "percent-encoding",
394 + "serde",
395 ]
396
397 +[[package]]
398 +name = "vcpkg"
399 +version = "0.2.15"
400 +source = "registry+https://github.com/rust-lang/crates.io-index"
401 +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
402 +
403 [[package]]
404 name = "vec_map"
405 version = "0.8.2"
406
407 diff --git a/Cargo.toml b/Cargo.toml
408 index ef4be19..84e7883 100644
409 --- a/Cargo.toml
410 +++ b/Cargo.toml
411 @@ -27,6 +27,7 @@ anyhow = "^1"
412 cargo-lock = "^7.0"
413 cargo_metadata = "^0.14"
414 itertools = "^0.10"
415 +rustsec = "0.24.2"
416 structopt = "^0.3"
417 serde = { version = "1.0", features = ["derive"] }
418 time = "^0.2"
419
420 diff --git a/src/audit.rs b/src/audit.rs
421 new file mode 100644
422 index 0000000..8ea8e43
423 --- /dev/null
424 +++ b/src/audit.rs
425 @@ -0,0 +1,96 @@
426 +use std::env;
427 +use std::path::Path;
428 +use std::process::Command;
429 +
430 +use anyhow::{format_err, Context, Result};
431 +use rustsec::lockfile::Lockfile;
432 +use rustsec::report::{Settings, VulnerabilityInfo};
433 +use rustsec::{Database, Report, Vulnerability};
434 +
435 +fn generate_lockfile(workspace_root: &Path, manifest_path: Option<&Path>) -> Result<Lockfile> {
436 + let lockfile = workspace_root.join("Cargo.lock");
437 + let mut command = Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()));
438 +
439 + if lockfile.exists() {
440 + return Lockfile::load(lockfile).context("Failed to load lockfile");
441 + }
442 +
443 + command.arg("generate-lockfile");
444 +
445 + if let Some(path) = manifest_path {
446 + command.arg("--manifest-path");
447 + command.arg(path.as_os_str());
448 + }
449 +
450 + let status = command
451 + .status()
452 + .context("Failed to run `cargo generate-lockfile`")?;
453 +
454 + match status.code() {
455 + Some(0) => Lockfile::load(lockfile).context("Failed to load lockfile"),
456 + Some(code) => Err(format_err!(
457 + "Non-zero status ({}) on `cargo generate-lockfile`",
458 + code,
459 + )),
460 + None => Err(format_err!(
461 + "Unexpected termination on `cargo generate-lockfile`",
462 + )),
463 + }
464 +}
465 +
466 +pub fn audit_package(workspace_root: &Path, manifest_path: Option<&Path>) -> Result<()> {
467 + let database = Database::fetch().context("Failed to fetch security advisory database")?;
468 + let lockfile = generate_lockfile(workspace_root, manifest_path)?;
469 + let settings = Settings::default();
470 + let report = Report::generate(&database, &lockfile, &settings);
471 +
472 + if report.vulnerabilities.found {
473 + let VulnerabilityInfo { count, list, .. } = report.vulnerabilities;
474 +
475 + let mut message = match count {
476 + 1 => format!("Found {} vulnerability:\n", count),
477 + _ => format!("Found {} vulnerabilities:\n", count),
478 + };
479 +
480 + for Vulnerability {
481 + package,
482 + versions,
483 + advisory,
484 + ..
485 + } in list
486 + {
487 + message.push('\n');
488 + message.push_str(&format!("Crate: {}\n", package.name));
489 + message.push_str(&format!("Version: {}\n", package.version.to_string()));
490 + message.push_str(&format!("Title: {}\n", advisory.title));
491 + message.push_str(&format!("Date: {}\n", advisory.date.as_str()));
492 + message.push_str(&format!("ID: {}\n", advisory.id));
493 +
494 + if let Some(url) = advisory.id.url() {
495 + message.push_str(&format!("URL: {}\n", url));
496 + } else if let Some(url) = &advisory.url {
497 + message.push_str(&format!("URL: {}\n", url));
498 + }
499 +
500 + if versions.patched().is_empty() {
501 + message.push_str("Solution: No solution available\n");
502 + } else {
503 + let patched = versions
504 + .patched()
505 + .iter()
506 + .map(ToString::to_string)
507 + .collect::<Vec<_>>()
508 + .as_slice()
509 + .join(" or ");
510 +
511 + message.push_str(&format!("Solution: Upgrade to {}\n", patched));
512 + }
513 + }
514 +
515 + message.push_str("\nPlease fix the issues or use \"--noaudit\" flag.\n");
516 +
517 + return Err(format_err!(message));
518 + }
519 +
520 + Ok(())
521 +}
522
523 diff --git a/src/lib.rs b/src/lib.rs
524 index 6b2d2d3..f478bc0 100644
525 --- a/src/lib.rs
526 +++ b/src/lib.rs
527 @@ -8,6 +8,7 @@
528 * except according to those terms.
529 */
530
531 +mod audit;
532 mod license;
533 mod metadata;
534
535 @@ -18,10 +19,11 @@ use std::collections::BTreeSet;
536 use std::fs::OpenOptions;
537 use std::path::Path;
538
539 +use audit::audit_package;
540 use license::{normalize_license, split_spdx_license};
541 use metadata::EbuildConfig;
542
543 -pub fn gen_ebuild_data(manifest_path: Option<&Path>) -> Result<EbuildConfig> {
544 +pub fn gen_ebuild_data(manifest_path: Option<&Path>, audit: bool) -> Result<EbuildConfig> {
545 let mut cmd = MetadataCommand::new();
546
547 cmd.features(CargoOpt::AllFeatures);
548 @@ -44,6 +46,10 @@ pub fn gen_ebuild_data(manifest_path: Option<&Path>) -> Result<EbuildConfig> {
549 .as_ref()
550 .ok_or_else(|| format_err!("cargo metadata failed to resolve the root package"))?;
551
552 + if audit {
553 + audit_package(metadata.workspace_root.as_ref(), manifest_path)?;
554 + }
555 +
556 let mut licenses = BTreeSet::new();
557 let mut crates = Vec::new();
558 let mut root_pkg = None;
559
560 diff --git a/src/main.rs b/src/main.rs
561 index 12dd0e0..1072094 100644
562 --- a/src/main.rs
563 +++ b/src/main.rs
564 @@ -22,9 +22,13 @@ struct Args {
565 #[structopt(name = "PATH", long = "manifest-path", parse(from_os_str))]
566 /// Path to Cargo.toml.
567 manifest_path: Option<PathBuf>,
568 +
569 #[structopt(name = "TEMPLATE", long = "template-path", short)]
570 /// Non-standard template
571 template_path: Option<PathBuf>,
572 +
573 + #[structopt(long)]
574 + noaudit: bool,
575 }
576
577 #[derive(StructOpt, Debug)]
578 @@ -45,8 +49,7 @@ fn main() -> Result<()> {
579 let Opt::Ebuild(opt) = Opt::from_args();
580
581 // compute the data from the package that the build needs
582 - let ebuild_data = gen_ebuild_data(opt.manifest_path.as_deref())?;
583 -
584 + let ebuild_data = gen_ebuild_data(opt.manifest_path.as_deref(), !opt.noaudit)?;
585 let ebuild_path = format!("{}-{}.ebuild", ebuild_data.name, ebuild_data.version);
586
587 write_ebuild(ebuild_data, ebuild_path.as_ref(), opt.template_path.as_deref())?;