Skip to content

Commit af3f08a

Browse files
authored
DOCSP-40534 Script to scrape licensing info from OM SBOM (#485)
1 parent 5ef1184 commit af3f08a

File tree

9 files changed

+347
-0
lines changed

9 files changed

+347
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ source/figures/
2424
giza.log
2525
backups/*
2626
.vscode/
27+
tools/sbom-tool/sbom.json

tools/sbom-tool/Cargo.lock

Lines changed: 114 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/sbom-tool/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "license-tool"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
iter_tools = "0.18.0"
10+
serde = { version = "1.0.203", features = ["derive"]}
11+
serde_json = "1.0.117"

tools/sbom-tool/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Requirements
2+
3+
Install [Rust](https://www.rust-lang.org/learn/get-started).
4+
5+
# Instructions
6+
7+
1. Download the SBOM (software bill of materials) for Ops Manager from here: https://parsley.mongodb.com/taskFile/mms_ops_manager_main_ssdlc_PACKAGE_SILK_SBOM_21ef545d4cdcde67d2eaa7f709e62a080db80cc6_24_05_30_14_41_10/0/Augmented%20SBOM?bookmarks=0,50069
8+
(This location may change in the future. Ask engineering for latest if you have questions.)
9+
- Click *Details* -> *Raw* to downlaod the raw JSON. Name the file `sbom.json`.
10+
2. Place `sbom.json` in `docs-ops-manager/tools/sbom-tool`.
11+
3. Change to the tool directory: `cd docs-ops-manager/tools/sbom-tool/`.
12+
4. Run `cargo run`
13+
5. Copy to the output file (`out.rst`) to the appropriate section of `docs-ops-manager/source/reference/third-party-licenses.txt`.
14+
15+
# Troubleshooting
16+
17+
If you run into an error with `cargo run`,
18+
try adding the following text to `~/.carg/config`
19+
(create this file if it does not exist):
20+
21+
```
22+
[net]
23+
git-fetch-with-cli = true
24+
```

tools/sbom-tool/example.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.. list-table::
2+
:widths: 50 50
3+
:header-rows: 1
4+
:class: licenses
5+
6+
* - Package
7+
- Copyright
8+
9+
* - `pikaday <https://www.npmjs.com/package/pikaday/v/1.8.2>`__
10+
- Copyright © 2013-2014 David Bushell
11+
12+
* - `tslib <https://www.npmjs.com/package/tslib/v/2.6.2>`__
13+
- Copyright © Microsoft Corporation
14+
15+
* - `github.com/davecgh/go-spew <https://pkg.go.dev/github.com/davecgh/go-spew@v1.1.2-0.20180830191138-d8f796af33cc>`__
16+
-
17+
18+
* - `github.com/decred/dcrd/crypto/blake256 <https://pkg.go.dev/github.com/decred/dcrd/crypto/blake256@v1.0.1>`__
19+
-
20+
21+
* - `github.com/decred/dcrd/dcrec/secp256k1 <https://pkg.go.dev/github.com/decred/dcrd/dcrec/secp256k1@v4.2.0>`__
22+
-
23+
24+
* - `github.com/gogo/protobuf <https://pkg.go.dev/github.com/gogo/protobuf@v1.3.2>`__
25+
-
26+
27+
* - `github.com/davecgh/go-spew <https://pkg.go.dev/github.com/davecgh/go-spew@v1.1.1>`__
28+
-
29+
30+
* - `github.com/howeyc/gopass <https://pkg.go.dev/github.com/howeyc/gopass@v0.0.0-20170109162249-bf9dde6d0d2c>`__
31+
-
32+

tools/sbom-tool/src/constants.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pub const TABLE_HEADER: &str = r#".. list-table::
2+
:widths: 20 20 60
3+
:header-rows: 1
4+
:class: licenses"#;
5+
6+
pub const COLUMN_HEADERS: &str = r#" * - Package
7+
- License
8+
- Copyright"#;

tools/sbom-tool/src/fmt.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use std::fmt;
2+
3+
use crate::sbom::Component;
4+
use iter_tools::Itertools;
5+
6+
// Example:
7+
//
8+
// * - `pikaday <https://www.npmjs.com/package/pikaday/v/1.8.2>`__
9+
// - Copyright © 2013-2014 David Bushell
10+
//
11+
impl fmt::Display for Component {
12+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13+
let name = self.name.clone().unwrap();
14+
15+
// Get a URL for this component.
16+
// Prefer a github repo URL, but otherwise use any given URL.
17+
let mut url = String::new();
18+
let refs = self.clone().external_references;
19+
if refs.is_some() {
20+
// println!("{:#?}", refs.clone().unwrap());
21+
22+
for r in refs.clone().unwrap() {
23+
if r.url.contains("https://github") {
24+
url = r.url.replace("git+", "").replace(".git", "");
25+
} else {
26+
url = r.url;
27+
}
28+
}
29+
}
30+
31+
// Licenses
32+
let mut licenses: Vec<String> = vec![];
33+
let ls = self.clone().licenses;
34+
if ls.is_some() {
35+
for l in ls.unwrap() {
36+
let license = l.to_string();
37+
if license != "Other" && license != "NOASSERTION" {
38+
licenses.push(license);
39+
}
40+
}
41+
}
42+
43+
// Format copyright text into one line
44+
let mut copyright = self.copyright.clone().unwrap_or(String::from(""));
45+
copyright = copyright.replace('\n', "\n ");
46+
47+
// Here is where we actually create the string that will go into
48+
// the RST file, using the data from above.
49+
write!(
50+
f,
51+
" * - `{} <{}>`__\n - {}\n - {}\n\n",
52+
name,
53+
url,
54+
licenses.iter().format(", "),
55+
copyright,
56+
)
57+
}
58+
}

tools/sbom-tool/src/main.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::fs::read_to_string;
2+
3+
use sbom::Component;
4+
5+
use crate::constants::{COLUMN_HEADERS, TABLE_HEADER};
6+
use crate::sbom::Sbom;
7+
8+
mod constants;
9+
mod fmt;
10+
mod sbom;
11+
12+
fn main() {
13+
let file_path = "sbom.json".to_owned();
14+
let contents = read_to_string(file_path).expect("Couldn't find or load that file.");
15+
16+
let sbom: Sbom = serde_json::from_str(&contents).expect("Error unmarshalling JSON");
17+
18+
let mut output = String::new();
19+
output += TABLE_HEADER;
20+
output += "\n\n";
21+
output += COLUMN_HEADERS;
22+
output += "\n\n";
23+
24+
let mut components: Vec<Component> = sbom.components;
25+
components.sort_by(|a, b| {
26+
a.clone()
27+
.name
28+
.unwrap()
29+
.to_lowercase()
30+
.cmp(&b.clone().name.unwrap().to_lowercase())
31+
});
32+
components.sort_by_key(|c| c.clone().licenses);
33+
34+
for comp in &components {
35+
if comp.licenses.is_some() {
36+
output += &format!("{}", comp);
37+
}
38+
}
39+
40+
// println!("{}", output);
41+
std::fs::write("out.rst", output).expect("Error writing output file.");
42+
}

tools/sbom-tool/src/sbom.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use std::collections::BTreeMap;
2+
3+
use serde::Deserialize;
4+
5+
#[derive(Clone, Debug, Deserialize)]
6+
pub struct Sbom {
7+
pub components: Vec<Component>,
8+
}
9+
10+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
11+
pub struct Component {
12+
pub name: Option<String>,
13+
pub copyright: Option<String>,
14+
#[serde(rename = "externalReferences")]
15+
pub external_references: Option<Vec<ExternalReference>>,
16+
pub licenses: Option<Vec<License>>,
17+
}
18+
19+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
20+
#[serde(transparent)]
21+
pub struct License {
22+
pub license_inner: BTreeMap<String, BTreeMap<String, String>>,
23+
}
24+
25+
impl std::fmt::Display for License {
26+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27+
let out = self.license_inner.first_key_value().unwrap();
28+
let (_, out) = out;
29+
let out = out.first_key_value().unwrap();
30+
31+
write!(f, "{}", out.1)
32+
}
33+
}
34+
35+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
36+
pub struct ExternalReference {
37+
#[serde(rename = "type")]
38+
pub _type: ExternalReferenceType,
39+
pub url: String,
40+
}
41+
42+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
43+
#[serde(rename = "type")]
44+
pub enum ExternalReferenceType {
45+
#[serde(rename = "distribution")]
46+
Distribution,
47+
#[serde(rename = "distribution-intake")]
48+
DistributionIntake,
49+
#[serde(rename = "evidence")]
50+
Evidence,
51+
#[serde(rename = "other")]
52+
Other,
53+
#[serde(rename = "vcs")]
54+
Vcs,
55+
#[serde(rename = "website")]
56+
Website,
57+
}

0 commit comments

Comments
 (0)