Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .clippy.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
absolute-paths-max-segments = 3
38 changes: 38 additions & 0 deletions CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,44 @@ use sea_orm::{ConnectionTrait, TransactionTrait};
This is a manual convention — `rustfmt`'s `imports_granularity = "Crate"` option is not
available on the stable channel. Reviewers should flag un-nested imports during code review.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Consider changing "un-nested imports" to "unnested imports" for more standard wording.

"Unnested imports" (without the hyphen) is the more standard phrasing in technical writing and would make the documentation read more polished.

Suggested change
available on the stable channel. Reviewers should flag un-nested imports during code review.
available on the stable channel. Reviewers should flag unnested imports during code review.


### Import Style — Absolute Paths

Fully-qualified paths with 4+ segments are enforced by the `clippy::absolute_paths` lint
(configured in `.clippy.toml` with `absolute-paths-max-segments = 3`). Paths exceeding the
threshold must be replaced with `use` imports:

```rust
// Good — imported
use crate::graph::db_context::parse_status;
parse_status(value)?;

// Lint error — 4+ segment absolute path
crate::graph::db_context::parse_status(value)?;
```

3-segment paths (e.g. `std::fmt::Result`, `entity::advisory_vulnerability::Model`) are not
flagged by the lint and are acceptable.

### Import Style — Qualified Names for Common Types

Common or ambiguous names should stay qualified with a 2-segment path, even when there is
no conflict in the current file. This keeps the code self-documenting and avoids confusion
when reviewing:

```rust
// Good — qualified with module prefix
let model: advisory_vulnerability::Model = ...;
let result: std::fmt::Result = ...;
let value = serde_json::from_value(...);

// Avoid — bare name is ambiguous across modules
let model: Model = ...;
```

Names that benefit from qualification include `Model`, `Entity`, `Column`, `ActiveModel`,
`Relation`, `from_value`, `info`, and similar names that appear in many modules. This is a
reviewer convention, not lint-enforced.

## Naming Conventions

- Structs: PascalCase (`SbomService`, `AdvisoryService`, `SbomSummary`)
Expand Down
6 changes: 2 additions & 4 deletions common/auth/src/authenticator/user.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Structures to work with users and identities.

use crate::authenticator::error::AuthorizationError;
use crate::authenticator::error::{AuthenticationError, AuthorizationError};

/// Details of an authenticated user.
///
Expand Down Expand Up @@ -101,9 +101,7 @@ impl actix_web::FromRequest for UserDetails {
log::debug!("Anonymous user, returning failure");
core::future::ready(Err(AuthorizationError::Failed.into()))
}
None => core::future::ready(Err(
crate::authenticator::error::AuthenticationError::Failed.into(),
)),
None => core::future::ready(Err(AuthenticationError::Failed.into())),
}
}
}
5 changes: 4 additions & 1 deletion common/auth/src/client/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ impl TokenProvider for String {
}
}

#[cfg(feature = "actix")]
use actix_web_httpauth::extractors::bearer::BearerAuth;

#[cfg(feature = "actix")]
#[async_trait]
impl TokenProvider for actix_web_httpauth::extractors::bearer::BearerAuth {
impl TokenProvider for BearerAuth {
async fn provide_access_token(&self) -> Result<Option<Credentials>, Error> {
Ok(Some(Credentials::Bearer(self.token().to_string())))
}
Expand Down
11 changes: 5 additions & 6 deletions common/infrastructure/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthent
use futures::{FutureExt, future::LocalBoxFuture};
use opentelemetry_instrumentation_actix_web::{RequestMetrics, RequestTracing};
use std::sync::Arc;
use trustify_auth::{authenticator::Authenticator, authorizer::Authorizer};
use trustify_auth::{
authenticator::{Authenticator, actix::openid_validator},
authorizer::Authorizer,
};
use trustify_common::middleware::StdMiddleware;

#[derive(Default)]
Expand Down Expand Up @@ -42,11 +45,7 @@ pub fn new_auth(
Condition::from_option(auth.map(move |authenticator| {
HttpAuthentication::bearer(move |req, auth| {
let authenticator = authenticator.clone();
Box::pin(async move {
trustify_auth::authenticator::actix::openid_validator(req, auth, authenticator)
.await
})
.boxed_local()
Box::pin(async move { openid_validator(req, auth, authenticator).await }).boxed_local()
})
}))
}
Expand Down
5 changes: 3 additions & 2 deletions common/src/decompress.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use actix_web::http::header;
use anyhow::anyhow;
use async_compression::tokio::bufread::{GzipDecoder, LzmaDecoder};
use bytes::Bytes;
use std::{io::Read, path::Path, pin::Pin};
use tokio::{
Expand Down Expand Up @@ -109,8 +110,8 @@ pub async fn decompress_async_read(
let source = BufReader::new(source);

Ok(match path.extension().and_then(|ext| ext.to_str()) {
Some("xz") => Box::pin(async_compression::tokio::bufread::LzmaDecoder::new(source)),
Some("gz") => Box::pin(async_compression::tokio::bufread::GzipDecoder::new(source)),
Some("xz") => Box::pin(LzmaDecoder::new(source)),
Some("gz") => Box::pin(GzipDecoder::new(source)),
// Anything else could be .sql, .tar, or an unsupported compression format.
// In that case, the following code would fail to understand the compressed content.
None | Some(_) => Box::pin(source),
Expand Down
3 changes: 2 additions & 1 deletion common/src/uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ pub mod serde {

use serde::{Deserialize, Deserializer, Serializer, de::Error};
use uuid::Uuid;
use uuid::serde::urn::serialize as uuid_urn_serialize;

pub fn serialize<S>(value: &Option<Uuid>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(uuid) => uuid::serde::urn::serialize(uuid, serializer),
Some(uuid) => uuid_urn_serialize(uuid, serializer),
None => serializer.serialize_none(),
}
}
Expand Down
3 changes: 2 additions & 1 deletion migration/src/data/document/sbom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sea_orm::{
prelude::*,
sea_query::{Expr, extension::postgres::PgExpr},
};
use serde_cyclonedx::cyclonedx::v_1_6::CycloneDx;
use trustify_entity::{labels::Labels, sbom};
use trustify_module_storage::service::StorageBackend;

Expand All @@ -16,7 +17,7 @@ pub struct Id {

#[allow(clippy::large_enum_variant)]
pub enum Sbom {
CycloneDx(serde_cyclonedx::cyclonedx::v_1_6::CycloneDx),
CycloneDx(CycloneDx),
Spdx(spdx_rs::models::SPDX),
Other(Bytes),
}
Expand Down
6 changes: 4 additions & 2 deletions migration/src/m0002000_add_sbom_properties.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::data::{MigrationTraitWithData, SchemaDataManager, sbom::Sbom as SbomDoc};
use crate::data::{
MigrationTraitWithData, SchemaDataManager, sbom::Id as SbomId, sbom::Sbom as SbomDoc,
};
use sea_orm::{ActiveModelBehavior, ActiveModelTrait, DatabaseTransaction, Set};
use sea_orm_migration::prelude::*;
use trustify_common::advisory::cyclonedx::extract_properties_json;
Expand Down Expand Up @@ -68,7 +70,7 @@ impl MigrationTraitWithData for Migration {
manager
.process(
self,
async |sbom: SbomDoc, id: crate::data::sbom::Id, tx: &DatabaseTransaction| {
async |sbom: SbomDoc, id: SbomId, tx: &DatabaseTransaction| {
let mut model = legacy::ActiveModel::new();
model.sbom_id = Set(id.sbom);
match sbom {
Expand Down
3 changes: 2 additions & 1 deletion modules/analysis/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use cpe::uri::OwnedUri;
use sea_orm::DbErr;
use std::str::FromStr;
use trustify_auth::authenticator::error::AuthorizationError;
use trustify_common::db::query::Error as QueryError;
use trustify_common::db::{DatabaseErrors, DbError};
use trustify_common::error::ErrorInformation;
use trustify_common::id::IdError;
Expand All @@ -17,7 +18,7 @@ pub enum Error {
#[error(transparent)]
Database(DbErr),
#[error(transparent)]
Query(#[from] trustify_common::db::query::Error),
Query(#[from] QueryError),
#[error(transparent)]
Purl(#[from] PurlErr),
#[error(transparent)]
Expand Down
20 changes: 13 additions & 7 deletions modules/fundamental/src/advisory/endpoints/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
advisory::model::{AdvisoryDetails, AdvisorySummary},
test::{caller, caller_with, label::Api},
test::{
caller, caller_with, label::Api, label::update_labels as do_update_labels,
label::update_labels_not_found as do_update_labels_not_found,
},
};
use actix_http::StatusCode;
use actix_web::{body::MessageBody, test::TestRequest};
Expand All @@ -18,7 +21,10 @@ use trustify_common::{
};
use trustify_entity::{advisory_vulnerability_score, labels::Labels};
use trustify_module_ingestor::{
graph::{advisory::AdvisoryInformation, cvss::ScoreCreator},
graph::{
advisory::AdvisoryInformation,
cvss::{ScoreCreator, ScoreInformation},
},
model::IngestResult,
service::Format,
};
Expand Down Expand Up @@ -56,7 +62,7 @@ async fn all_advisories(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {

// Use ScoreCreator to write to advisory_vulnerability_score table
let mut score_creator = ScoreCreator::new(advisory.advisory.id);
score_creator.add(trustify_module_ingestor::graph::cvss::ScoreInformation {
score_creator.add(ScoreInformation {
vulnerability_id: "CVE-123".to_string(),
r#type: advisory_vulnerability_score::ScoreType::V3_0,
vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N".to_string(),
Expand Down Expand Up @@ -180,7 +186,7 @@ async fn one_advisory(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {

// Use ScoreCreator to write to advisory_vulnerability_score table
let mut score_creator = ScoreCreator::new(advisory2.advisory.id);
score_creator.add(trustify_module_ingestor::graph::cvss::ScoreInformation {
score_creator.add(ScoreInformation {
vulnerability_id: "CVE-123".to_string(),
r#type: advisory_vulnerability_score::ScoreType::V3_0,
vector: "CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N".to_string(),
Expand Down Expand Up @@ -286,7 +292,7 @@ async fn one_advisory_by_uuid(ctx: &TrustifyContext) -> Result<(), anyhow::Error

// Use ScoreCreator to write to advisory_vulnerability_score table
let mut score_creator = ScoreCreator::new(advisory.advisory.id);
score_creator.add(trustify_module_ingestor::graph::cvss::ScoreInformation {
score_creator.add(ScoreInformation {
vulnerability_id: "CVE-123".to_string(),
r#type: advisory_vulnerability_score::ScoreType::V3_0,
vector: "CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N".to_string(),
Expand Down Expand Up @@ -595,14 +601,14 @@ async fn download_advisory_by_id(ctx: &TrustifyContext) -> Result<(), anyhow::Er
#[test_context(TrustifyContext)]
#[test(actix_web::test)]
async fn update_labels(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
crate::test::label::update_labels(ctx, Api::Advisory, DOC, "csaf").await
do_update_labels(ctx, Api::Advisory, DOC, "csaf").await
}

/// Test updating labels, for a document that does not exist
#[test_context(TrustifyContext)]
#[test(actix_web::test)]
async fn update_labels_not_found(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
crate::test::label::update_labels_not_found(ctx, Api::Advisory, DOC).await
do_update_labels_not_found(ctx, Api::Advisory, DOC).await
}

/// Test deleing an advisory
Expand Down
8 changes: 5 additions & 3 deletions modules/fundamental/src/advisory/service/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ use trustify_module_ingestor::graph::{
Outcome,
advisory::{
AdvisoryContext, AdvisoryInformation,
advisory_vulnerability::AdvisoryVulnerabilityContext,
version::{VersionInfo, VersionSpec},
},
cvss::{ScoreCreator, ScoreInformation},
error::Error as GraphError,
};
use trustify_test_context::TrustifyContext;

pub async fn ingest_sample_advisory<'a>(
ctx: &'a TrustifyContext,
id: &'a str,
title: &'a str,
) -> Result<AdvisoryContext<'a>, trustify_module_ingestor::graph::error::Error> {
) -> Result<AdvisoryContext<'a>, GraphError> {
ctx.graph
.ingest_advisory(
title,
Expand Down Expand Up @@ -101,8 +103,8 @@ async fn single_advisory(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {

let advisory = ingest_sample_advisory(ctx, "RHSA-1", "RHSA-1").await?;

let advisory_vuln: trustify_module_ingestor::graph::advisory::advisory_vulnerability::AdvisoryVulnerabilityContext<'_> = advisory
.link_to_vulnerability("CVE-123", None,&ctx.db)
let advisory_vuln: AdvisoryVulnerabilityContext<'_> = advisory
.link_to_vulnerability("CVE-123", None, &ctx.db)
.await?;
let mut creator = ScoreCreator::new(advisory_vuln.advisory.advisory.id);
creator.add(ScoreInformation {
Expand Down
25 changes: 15 additions & 10 deletions modules/fundamental/src/endpoints.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use actix_web::web;
use trustify_common::db::{self, pagination_cache::PaginationCache};
use trustify_module_analysis::service::AnalysisService;
use trustify_module_ingestor::common;
use trustify_module_ingestor::graph::Graph;
use trustify_module_ingestor::service::IngestorService;
use trustify_module_storage::service::dispatch::DispatchBackend;
use utoipa::{IntoParams, ToSchema};

use crate::{
advisory, license, organization, product, purl, sbom, sbom_group, vulnerability, weakness,
};

#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct Config {
pub sbom_upload_limit: usize,
Expand All @@ -27,32 +32,32 @@ pub fn configure(
let ingestor_service = IngestorService::new(graph, storage, Some(analysis));
svc.app_data(web::Data::new(ingestor_service.clone()));

crate::advisory::endpoints::configure(
advisory::endpoints::configure(
svc,
db_rw.clone(),
db_ro.clone(),
config.advisory_upload_limit,
cache.clone(),
);
crate::license::endpoints::configure(svc, db_ro.clone());
crate::organization::endpoints::configure(svc, db_ro.clone(), cache.clone());
crate::purl::endpoints::configure(svc, db_ro.clone(), cache.clone());
crate::product::endpoints::configure(svc, db_rw.clone(), db_ro.clone(), cache.clone());
crate::sbom::endpoints::configure(
license::endpoints::configure(svc, db_ro.clone());
organization::endpoints::configure(svc, db_ro.clone(), cache.clone());
purl::endpoints::configure(svc, db_ro.clone(), cache.clone());
product::endpoints::configure(svc, db_rw.clone(), db_ro.clone(), cache.clone());
sbom::endpoints::configure(
svc,
db_rw.clone(),
db_ro.clone(),
config.sbom_upload_limit,
cache.clone(),
);
crate::vulnerability::endpoints::configure(svc, db_ro.clone(), cache.clone());
crate::weakness::endpoints::configure(svc, db_ro.clone(), cache.clone());
crate::sbom_group::endpoints::configure(svc, db_rw, db_ro, config.max_group_name_length, cache);
vulnerability::endpoints::configure(svc, db_ro.clone(), cache.clone());
weakness::endpoints::configure(svc, db_ro.clone(), cache.clone());
sbom_group::endpoints::configure(svc, db_rw, db_ro, config.max_group_name_length, cache);
}

#[derive(Clone, Debug, PartialEq, Eq, Default, ToSchema, serde::Deserialize, IntoParams)]
pub struct Deprecation {
#[serde(default)]
#[param(inline)]
pub deprecated: trustify_module_ingestor::common::Deprecation,
pub deprecated: common::Deprecation,
}
4 changes: 2 additions & 2 deletions modules/fundamental/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sea_orm::DbErr;
use std::borrow::Cow;
use trustify_auth::authenticator::error::AuthorizationError;
use trustify_common::{
db::{DatabaseErrors, DbError, limiter::LimiterError, pagination_cache::LimitError},
db::{DatabaseErrors, DbError, limiter::LimiterError, pagination_cache::LimitError, query},
decompress,
error::ErrorInformation,
id::IdError,
Expand All @@ -22,7 +22,7 @@ pub enum Error {
#[error(transparent)]
Database(DbErr),
#[error(transparent)]
Query(#[from] trustify_common::db::query::Error),
Query(#[from] query::Error),
#[error(transparent)]
Ingestor(#[from] trustify_module_ingestor::service::Error),
#[error(transparent)]
Expand Down
4 changes: 2 additions & 2 deletions modules/fundamental/src/organization/endpoints/test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::organization::service::OrganizationService;
use crate::test::caller;
use actix_web::{cookie::time::OffsetDateTime, test::TestRequest};
use jsonpath_rust::JsonPath;
Expand Down Expand Up @@ -98,8 +99,7 @@ async fn one_organization(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
.link_to_vulnerability("CVE-123", None, &ctx.db)
.await?;

let service =
crate::organization::service::OrganizationService::new(PaginationCache::for_test());
let service = OrganizationService::new(PaginationCache::for_test());

let orgs = service
.fetch_organizations(
Expand Down
4 changes: 2 additions & 2 deletions modules/fundamental/src/organization/service/test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::organization::service::OrganizationService;
use actix_web::cookie::time::OffsetDateTime;
use test_context::test_context;
use test_log::test;
Expand Down Expand Up @@ -29,8 +30,7 @@ async fn all_organizations(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
)
.await?;

let service =
crate::organization::service::OrganizationService::new(PaginationCache::for_test());
let service = OrganizationService::new(PaginationCache::for_test());

let orgs = service
.fetch_organizations(
Expand Down
Loading
Loading