fix: SBOM multi-ecosystem support with correct package managers and licenses
Some checks failed
CI / Format (push) Failing after 39s
CI / Clippy (push) Successful in 4m24s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Format (pull_request) Failing after 3s
CI / Clippy (pull_request) Successful in 4m24s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Some checks failed
CI / Format (push) Failing after 39s
CI / Clippy (push) Successful in 4m24s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Format (pull_request) Failing after 3s
CI / Clippy (pull_request) Successful in 4m24s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
- Extract package manager from PURL instead of CycloneDX component type (was showing "library"/"file" instead of "npm"/"cargo"/"pip" etc.) - Generate missing lock files (Cargo.lock, package-lock.json) before Syft scan so repos that gitignore them still get full dependency trees - Enable Syft remote license lookups for Go, JS, Python, and Java - Enrich Cargo entries with license data from cargo metadata - Parse CycloneDX license expressions (e.g. "MIT OR Apache-2.0") - Delete stale SBOM entries on rescan instead of only upserting - Add /api/v1/sbom/filters endpoint for dynamic filter options - Make manager and license dropdowns dynamic from actual DB data - Add cargo, npm, go, php, ruby, composer, bundler to Docker image Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -77,8 +77,32 @@ pub struct SbomDiffResponse {
|
||||
pub data: SbomDiffResultData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct SbomFiltersResponse {
|
||||
pub package_managers: Vec<String>,
|
||||
pub licenses: Vec<String>,
|
||||
}
|
||||
|
||||
// ── Server functions ──
|
||||
|
||||
#[server]
|
||||
pub async fn fetch_sbom_filters() -> Result<SbomFiltersResponse, ServerFnError> {
|
||||
let state: super::server_state::ServerState =
|
||||
dioxus_fullstack::FullstackContext::extract().await?;
|
||||
|
||||
let url = format!("{}/api/v1/sbom/filters", state.agent_api_url);
|
||||
let resp = reqwest::get(&url)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
||||
let text = resp
|
||||
.text()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
||||
let body: SbomFiltersResponse = serde_json::from_str(&text)
|
||||
.map_err(|e| ServerFnError::new(format!("Parse error: {e} — body: {text}")))?;
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn fetch_sbom_filtered(
|
||||
repo_id: Option<String>,
|
||||
|
||||
@@ -36,6 +36,11 @@ pub fn SbomPage() -> Element {
|
||||
.ok()
|
||||
});
|
||||
|
||||
// ── Dynamic filter options (package managers + licenses from DB) ──
|
||||
let sbom_filters = use_resource(|| async {
|
||||
fetch_sbom_filters().await.ok()
|
||||
});
|
||||
|
||||
// ── SBOM list (filtered) ──
|
||||
let sbom = use_resource(move || {
|
||||
let p = page();
|
||||
@@ -132,14 +137,20 @@ pub fn SbomPage() -> Element {
|
||||
class: "sbom-filter-select",
|
||||
onchange: move |e| { pm_filter.set(e.value()); page.set(1); },
|
||||
option { value: "", "All Managers" }
|
||||
option { value: "npm", "npm" }
|
||||
option { value: "cargo", "Cargo" }
|
||||
option { value: "pip", "pip" }
|
||||
option { value: "go", "Go" }
|
||||
option { value: "maven", "Maven" }
|
||||
option { value: "nuget", "NuGet" }
|
||||
option { value: "composer", "Composer" }
|
||||
option { value: "gem", "RubyGems" }
|
||||
{
|
||||
match &*sbom_filters.read() {
|
||||
Some(Some(f)) => rsx! {
|
||||
for pm in &f.package_managers {
|
||||
{
|
||||
let val = pm.clone();
|
||||
let label = pm_display_name(&val);
|
||||
rsx! { option { value: "{val}", "{label}" } }
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => rsx! {},
|
||||
}
|
||||
}
|
||||
}
|
||||
input {
|
||||
class: "sbom-filter-input",
|
||||
@@ -166,14 +177,19 @@ pub fn SbomPage() -> Element {
|
||||
class: "sbom-filter-select",
|
||||
onchange: move |e| { license_filter.set(e.value()); page.set(1); },
|
||||
option { value: "", "All Licenses" }
|
||||
option { value: "MIT", "MIT" }
|
||||
option { value: "Apache-2.0", "Apache 2.0" }
|
||||
option { value: "BSD-3-Clause", "BSD 3-Clause" }
|
||||
option { value: "ISC", "ISC" }
|
||||
option { value: "GPL-3.0", "GPL 3.0" }
|
||||
option { value: "GPL-2.0", "GPL 2.0" }
|
||||
option { value: "LGPL-2.1", "LGPL 2.1" }
|
||||
option { value: "MPL-2.0", "MPL 2.0" }
|
||||
{
|
||||
match &*sbom_filters.read() {
|
||||
Some(Some(f)) => rsx! {
|
||||
for lic in &f.licenses {
|
||||
{
|
||||
let val = lic.clone();
|
||||
rsx! { option { value: "{val}", "{val}" } }
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => rsx! {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Export button ──
|
||||
@@ -633,6 +649,21 @@ pub fn SbomPage() -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
fn pm_display_name(pm: &str) -> &str {
|
||||
match pm {
|
||||
"npm" => "npm",
|
||||
"cargo" => "Cargo",
|
||||
"pip" => "pip",
|
||||
"go" | "golang" => "Go",
|
||||
"maven" => "Maven",
|
||||
"nuget" => "NuGet",
|
||||
"composer" => "Composer",
|
||||
"gem" => "RubyGems",
|
||||
"github" => "GitHub Actions",
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
fn license_css_class(license: Option<&str>) -> &'static str {
|
||||
match license {
|
||||
Some(l) => {
|
||||
|
||||
Reference in New Issue
Block a user