Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare release 0.2.1 #1193

Merged
merged 41 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
0693b5c
feat: allow locating by CPE
ctron Jan 14, 2025
52fce44
feat: add a way to find related SBOMs by CPE
ctron Jan 14, 2025
3f16a24
chore: cleanup
ctron Jan 16, 2025
75e5190
test: add or amend some test for CPE
ctron Jan 16, 2025
280ad9e
chore: add purl and cpe identity ingestion for cdx
ctron Jan 17, 2025
7233612
chore: rework cdx ingestion to also ingest some pedigree information
ctron Jan 17, 2025
99002aa
test: add test for aliases on ancestor
ctron Jan 17, 2025
3f0d86a
chore: relax urlencoding on purl qualifier values
ctron Jan 17, 2025
c7be980
test: enable one more test after not having a global instance
ctron Jan 17, 2025
2d16653
allow more relationships in analysis graph
JimFuller-RedHat Jan 17, 2025
e3783b2
OSV loader: set properly VersionScheme in version_range
mrizzi Jan 17, 2025
a4b12cd
fix: /api/v2/purl/{purl} now honors qualifiers, or lack thereof
jcrossley3 Jan 17, 2025
9573043
Refactor the purl_by's a bit
jcrossley3 Jan 17, 2025
38740d1
Add unit test affirming TC-2051
jcrossley3 Jan 20, 2025
63f9ada
feat: create VariantOf relationship from cdx pedigree/variants
jcrossley3 Jan 20, 2025
7d39379
refactor: re-use some code, add endpoint for single component
ctron Jan 20, 2025
31acd1f
chore: regenerate api spec
ctron Jan 20, 2025
532a12a
feat: create AncestorOf relationship from cdx pedigree/ancestors
jcrossley3 Jan 20, 2025
a532dd8
Clarify the parent and child purl's in the unit test
jcrossley3 Jan 21, 2025
009295b
Add unit test affirming TC-2053
jcrossley3 Jan 21, 2025
3096472
test: add test for load performance
ctron Jan 21, 2025
843b26b
chore: start working on performance
ctron Jan 21, 2025
40d1c42
chore: work on graph loading performance
ctron Jan 20, 2025
e0c5ba7
chore: fix up tests after graph load changes
ctron Jan 21, 2025
cd155b7
chore: make clippy happy
ctron Jan 21, 2025
3e46969
Revert exposing relationships via /api/v2/purl endpoint
jcrossley3 Jan 21, 2025
4c4e8be
Rename tests to reflect their use instead of downstream issue #'s
jcrossley3 Jan 21, 2025
372228f
Update openapi.yaml after removing relationships from purl endpoint
jcrossley3 Jan 21, 2025
6470938
clippiness
jcrossley3 Jan 21, 2025
c9b7fe4
fix: infinite loop when invalid file passed to Format::from_bytes
jcrossley3 Jan 21, 2025
1c1f900
clipilicious
jcrossley3 Jan 21, 2025
ca799fa
Add support for spdx "relationshipType": "PACKAGE_OF"
chirino Jan 21, 2025
f54d171
Apply suggestions from code review
chirino Jan 21, 2025
b048e4b
fix: sbom license migration to update nulls
dejanb Jan 21, 2025
efc14da
fix: report CPE parsing as "bad request"
ctron Jan 22, 2025
cdb4d9d
Affirm AncestorOf relationship for SPDX files
jcrossley3 Jan 22, 2025
27e5482
feat: Seeing OTEL traces at development time
helio-frota Jan 22, 2025
feb530a
fix(analysis): aggregate in way that we don't have duplicate purls/cpes
ctron Jan 23, 2025
616bbf0
chore: update Rust to 1.84
ctron Jan 23, 2025
3ba71e4
chore: prepare release 0.2.1
ctron Jan 23, 2025
1cdbc77
test: fix up license test after dependency update
ctron Jan 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
920 changes: 475 additions & 445 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ members = [
]

[workspace.package]
version = "0.2.0"
version = "0.2.1"
edition = "2021"
publish = false
license = "Apache-2.0"
Expand Down Expand Up @@ -96,7 +96,8 @@ packageurl = "0.3.0"
parking_lot = "0.12"
peak_alloc = "0.2.0"
pem = "3"
petgraph = { version = "0.6.5", features = ["serde-1"] }
percent-encoding = "2.3.1"
petgraph = { version = "0.7.1", features = ["serde-1"] }
prometheus = "0.13.3"
quick-xml = "0.37.0"
rand = "0.8.5"
Expand Down
4 changes: 4 additions & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ log = { workspace = true }
native-tls = { workspace = true }
packageurl = { workspace = true }
pem = { workspace = true }
percent-encoding = { workspace = true }
postgresql_embedded = { workspace = true, features = ["blocking", "tokio"] }
regex = { workspace = true }
reqwest = { workspace = true, features = ["native-tls"] }
ring = { workspace = true }
sbom-walker = { workspace = true }
schemars = { workspace = true }
sea-orm = { workspace = true, features = ["sea-query-binder", "sqlx-postgres", "runtime-tokio-rustls", "macros"] }
sea-query = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
spdx-expression = { workspace = true }
spdx-rs = { workspace = true }
sqlx = { workspace = true }
strum = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion common/infrastructure/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mime = { workspace = true }
openssl = { workspace = true }
opentelemetry = { workspace = true }
opentelemetry-otlp = { workspace = true }
opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] }
opentelemetry_sdk = { workspace = true, features = ["rt-tokio-current-thread"] }
parking_lot = { workspace = true }
prometheus = { workspace = true }
reqwest = { workspace = true }
Expand Down
5 changes: 3 additions & 2 deletions common/infrastructure/src/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ fn init_otlp(name: &str) {
"service.name",
name.to_string(),
)]))
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::TokioCurrentThread)
.with_sampler(opentelemetry_sdk::trace::Sampler::ParentBased(Box::new(
sampler(),
)))
.build();

println!("Using Jaeger tracing.");
println!("Using OTEL Collector with Jaeger as the back-end.");
println!("{:#?}", provider);

let formatting_layer = tracing_subscriber::fmt::Layer::default();
Expand All @@ -142,6 +142,7 @@ fn init_otlp(name: &str) {
{
eprintln!("Error initializing tracing: {:?}", e);
}
opentelemetry::global::set_tracer_provider(provider);
}

fn init_no_tracing() {
Expand Down
81 changes: 79 additions & 2 deletions common/src/cpe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,82 @@ use cpe::{
cpe::Cpe as _,
uri::{OwnedUri, Uri},
};
use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
borrow::Cow,
fmt::{Debug, Display, Formatter},
str::FromStr,
};
use utoipa::{
openapi::{KnownFormat, ObjectBuilder, RefOr, Schema, SchemaFormat, Type},
PartialSchema, ToSchema,
};
use uuid::Uuid;

#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Cpe {
uri: OwnedUri,
}

impl ToSchema for Cpe {
fn name() -> Cow<'static, str> {
"Cpe".into()
}
}

impl PartialSchema for Cpe {
fn schema() -> RefOr<Schema> {
ObjectBuilder::new()
.schema_type(Type::String)
.format(Some(SchemaFormat::KnownFormat(KnownFormat::Uri)))
.into()
}
}

impl Display for Cpe {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.uri, f)
}
}

impl Serialize for Cpe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_str())
}
}

impl<'de> Deserialize<'de> for Cpe {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(CpeVisitor)
}
}

struct CpeVisitor;

impl Visitor<'_> for CpeVisitor {
type Value = Cpe;

fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a CPE")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
v.try_into().map_err(Error::custom)
}
}

const NAMESPACE: Uuid = Uuid::from_bytes([
0x1b, 0xf1, 0x2a, 0xd5, 0x0d, 0x67, 0x41, 0x18, 0xa1, 0x38, 0xb8, 0x9f, 0x19, 0x35, 0xe0, 0xa7,
]);
Expand Down Expand Up @@ -172,6 +233,22 @@ impl FromStr for Cpe {
}
}

impl TryFrom<&str> for Cpe {
type Error = <OwnedUri as FromStr>::Err;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(Self {
uri: OwnedUri::from_str(value)?,
})
}
}

impl TryFrom<String> for Cpe {
type Error = <OwnedUri as FromStr>::Err;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.as_str().try_into()
}
}

pub trait CpeCompare: cpe::cpe::Cpe {
fn is_superset<O: CpeCompare>(&self, other: &O) -> bool {
self.compare(other).superset()
Expand Down
2 changes: 1 addition & 1 deletion common/src/db/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl Query {
}
}

#[derive(Clone, Default, Debug, Deserialize, Serialize, ToSchema, IntoParams)]
#[derive(Clone, Default, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema, IntoParams)]
#[serde(rename_all = "camelCase")]
pub struct Query {
#[serde(default)]
Expand Down
33 changes: 32 additions & 1 deletion common/src/purl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use packageurl::PackageUrl;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
Expand Down Expand Up @@ -115,6 +116,7 @@ impl FromStr for Purl {
.map_err(PurlErr::Package)
}
}

impl<'de> Deserialize<'de> for Purl {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand All @@ -141,6 +143,35 @@ impl Visitor<'_> for PurlVisitor {
}
}

const QUERY_ENCODE_SET: &AsciiSet = &CONTROLS
.add(b' ') // Space must be encoded as %20 or +.
.add(b'"') // Double quote
.add(b'#') // Fragment identifier
.add(b'<') // Less than
.add(b'>') // Greater than
.add(b'[') // Left square bracket
.add(b']') // Right square bracket
.add(b'{') // Left curly brace
.add(b'}') // Right curly brace
.add(b'|') // Pipe
.add(b'\\') // Backslash
.add(b'^') // Caret
.add(b'`') // Backtick
.add(b'~') // Tilde
.add(b'@') // At sign
.add(b'!') // Exclamation mark
.add(b'$') // Dollar sign
.add(b'&') // Ampersand
.add(b'\'') // Single quote
.add(b'(') // Left parenthesis
.add(b')') // Right parenthesis
.add(b'*') // Asterisk
.add(b'+') // Plus
.add(b',') // Comma
.add(b';') // Semicolon
.add(b'=') // Equals
.add(b'%'); // Percent itself.

impl Display for Purl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let ns = if let Some(ns) = &self.namespace {
Expand All @@ -156,7 +187,7 @@ impl Display for Purl {
"?{}",
self.qualifiers
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.map(|(k, v)| format!("{}={}", k, utf8_percent_encode(v, QUERY_ENCODE_SET)))
.collect::<Vec<_>>()
.join("&")
)
Expand Down
2 changes: 2 additions & 0 deletions common/src/sbom/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod spdx;

use crate::cpe::Cpe;
use crate::purl::Purl;
use uuid::Uuid;
Expand Down
33 changes: 33 additions & 0 deletions common/src/sbom/spdx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use sbom_walker::report::ReportSink;
use serde_json::Value;
use spdx_rs::models::SPDX;

/// Parse a SPDX document, possibly replacing invalid license expressions.
///
/// Returns the parsed document and a flag indicating if license expressions got replaced.
pub fn parse_spdx(report: &dyn ReportSink, json: Value) -> Result<(SPDX, bool), serde_json::Error> {
let (json, changed) = fix_license(report, json);
Ok((serde_json::from_value(json)?, changed))
}

/// Check the document for invalid SPDX license expressions and replace them with `NOASSERTION`.
pub fn fix_license(report: &dyn ReportSink, mut json: Value) -> (Value, bool) {
let mut changed = false;
if let Some(packages) = json["packages"].as_array_mut() {
for package in packages {
if let Some(declared) = package["licenseDeclared"].as_str() {
if let Err(err) = spdx_expression::SpdxExpression::parse(declared) {
package["licenseDeclared"] = "NOASSERTION".into();
changed = true;

let message =
format!("Replacing faulty SPDX license expression with NOASSERTION: {err}");
log::debug!("{message}");
report.error(message);
}
}
}
}

(json, changed)
}
23 changes: 23 additions & 0 deletions docs/design/log_tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,26 @@ However, not all function variants might expose the same behavior. Just because
mean it will panic. For example, the `Option::unwrap_or` function:

![Screenshot of rustdoc for Option::unwrap_or](drawings/log_tracing_2.png)

## Sending traces to OpenTelemetry Collector (devmode)

Jaeger and OTEL Collector:

```shell
podman compose -f etc/dev-traces/compose.yaml up
```

Database:

```shell
podman compose -f etc/deploy/compose/compose.yaml up
```

Trustify with traces:

```shell
OTEL_TRACES_SAMPLER_ARG=1 OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" cargo run --bin trustd api --db-password trustify --auth-disabled --tracing enabled
```

Access Trustify at [localhost:8080](http://localhost:8080) and analyze the traces using the [Jaeger UI](http://localhost:16686/)

4 changes: 2 additions & 2 deletions entity/src/advisory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct Model {

#[ComplexObject]
impl Model {
async fn organization<'a>(&self, ctx: &Context<'a>) -> Result<organization::Model> {
async fn organization(&self, ctx: &Context<'_>) -> Result<organization::Model> {
let db = ctx.data::<Arc<db::Database>>()?;
if let Some(found) = self
.find_related(organization::Entity)
Expand All @@ -47,7 +47,7 @@ impl Model {
}
}

async fn vulnerabilities<'a>(&self, ctx: &Context<'a>) -> Result<Vec<vulnerability::Model>> {
async fn vulnerabilities(&self, ctx: &Context<'_>) -> Result<Vec<vulnerability::Model>> {
let db = ctx.data::<Arc<db::Database>>()?;
Ok(self
.find_related(vulnerability::Entity)
Expand Down
17 changes: 17 additions & 0 deletions etc/dev-traces/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
jaeger-all-in-one:
hostname: jaeger-all-in-one
image: jaegertracing/all-in-one:1.53.0 # Using this version to align with trustify-helm-charts
ports:
- "16686:16686"
- "14250:14250"
environment:
- COLLECTOR_OTLP_ENABLED=true
collector:
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.115.1 # Using this version to align with trustify-helm-charts https://github.com/TylerHelmuth/opentelemetry-helm-charts/commit/86188fea6022a6424ef6a086e928d0056fb5dfe8#diff-55020f2b796ba5770731a3b4913592732431ff180c7f7473e5f469e92ed00e74R48
command: ["--config=/otel-collector-config.yaml"]
volumes:
- './config.yaml:/otel-collector-config.yaml:z'
ports:
- "4317:4317"
depends_on: [jaeger-all-in-one]
23 changes: 23 additions & 0 deletions etc/dev-traces/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"

exporters:
otlp:
endpoint: jaeger-all-in-one:4317
tls:
insecure: true
debug:
verbosity: detailed

processors:
batch: {}

service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug, otlp]
Loading
Loading