Private
Public Access
1
0

fix: cast compliance_pct to float8 and fix PDF generation crashes
All checks were successful
CI Pipeline / Rust Format Check (push) Successful in 5s
CI Pipeline / Clippy Lints (push) Successful in 56s
CI Pipeline / Rust Unit Tests (push) Successful in 1m16s
CI Pipeline / Security Audit (push) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 15s
CI Pipeline / Build .deb & Release (push) Has been skipped

This commit is contained in:
2026-05-12 20:59:21 +00:00
parent a1852fd55a
commit 4c91e02e58
2 changed files with 38 additions and 10 deletions

View File

@ -29,9 +29,9 @@ SELECT
h.last_patch_at,
COALESCE(jsonb_array_length(pd.installed_packages), 0) AS total_packages,
COALESCE(pd.patch_count, 0) AS pending_patches,
CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages), 0) = 0 THEN 100.0
ELSE ROUND(CAST((1.0 - pd.patch_count::float / NULLIF(jsonb_array_length(pd.installed_packages), 0)) * 100 AS numeric), 1)
END AS compliance_pct,
(CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages), 0) = 0 THEN 100.0
ELSE ROUND(CAST((1.0 - pd.patch_count::float / NULLIF(jsonb_array_length(pd.installed_packages), 0)) * 100 AS numeric), 1)
END)::float8 AS compliance_pct,
COALESCE(string_agg(DISTINCT g.name, ', '), '') AS group_names
FROM hosts h
LEFT JOIN host_patch_data pd ON pd.host_id = h.id
@ -59,9 +59,9 @@ SELECT
h.last_patch_at,
COALESCE(jsonb_array_length(pd.installed_packages), 0) AS total_packages,
COALESCE(pd.patch_count, 0) AS pending_patches,
CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages), 0) = 0 THEN 100.0
ELSE ROUND(CAST((1.0 - pd.patch_count::float / NULLIF(jsonb_array_length(pd.installed_packages), 0)) * 100 AS numeric), 1)
END AS compliance_pct,
(CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages), 0) = 0 THEN 100.0
ELSE ROUND(CAST((1.0 - pd.patch_count::float / NULLIF(jsonb_array_length(pd.installed_packages), 0)) * 100 AS numeric), 1)
END)::float8 AS compliance_pct,
COALESCE(string_agg(DISTINCT g.name, ', '), '') AS group_names
FROM hosts h
LEFT JOIN host_patch_data pd ON pd.host_id = h.id

View File

@ -46,6 +46,16 @@ fn render_bar_chart(
let mut pixel_buf = vec![0u8; (W * H * 3) as usize];
// Guard: skip rendering for empty or mismatched data
if labels.is_empty() || values.is_empty() || labels.len() != values.len() {
anyhow::bail!("cannot render bar chart with empty or mismatched data");
}
// Guard: reject NaN / infinity which would panic in plotters
if values.iter().any(|v| !v.is_finite()) {
anyhow::bail!("bar chart values contain non-finite numbers");
}
{
let root = BitMapBackend::with_buffer(&mut pixel_buf, (W, H)).into_drawing_area();
root.fill(&WHITE)?;
@ -169,6 +179,24 @@ impl PdfBuilder {
scale_x: f32,
scale_y: f32,
) -> anyhow::Result<()> {
// Validate dimensions and buffer size to prevent panics
let expected_len = (img_w as usize) * (img_h as usize) * 3;
if raw_rgb.len() != expected_len || img_w == 0 || img_h == 0 {
anyhow::bail!(
"image buffer size mismatch: expected {} bytes for {}x{} RGB, got {}",
expected_len,
img_w,
img_h,
raw_rgb.len()
);
}
if !scale_x.is_finite() || !scale_y.is_finite() || scale_x <= 0.0 || scale_y <= 0.0 {
anyhow::bail!(
"invalid image scale factors: scale_x={}, scale_y={}",
scale_x,
scale_y
);
}
let xobj = ImageXObject {
width: Px(img_w as usize),
height: Px(img_h as usize),
@ -252,9 +280,9 @@ async fn compliance_pdf(pool: &sqlx::PgPool, params: &ReportParams) -> anyhow::R
SELECT h.display_name, h.fqdn,
COALESCE(jsonb_array_length(pd.installed_packages),0) AS total_packages,
COALESCE(pd.patch_count,0) AS pending_patches,
CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages),0)=0 THEN 100.0
(CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages),0)=0 THEN 100.0
ELSE ROUND(CAST((1.0-pd.patch_count::float/NULLIF(jsonb_array_length(pd.installed_packages),0))*100 AS numeric),1)
END AS compliance_pct,
END)::float8 AS compliance_pct,
h.health_status::text AS health_status
FROM hosts h LEFT JOIN host_patch_data pd ON pd.host_id=h.id
WHERE h.id IN (SELECT host_id FROM host_groups WHERE group_id=$1)
@ -271,9 +299,9 @@ ORDER BY compliance_pct ASC",
SELECT h.display_name, h.fqdn,
COALESCE(jsonb_array_length(pd.installed_packages),0) AS total_packages,
COALESCE(pd.patch_count,0) AS pending_patches,
CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages),0)=0 THEN 100.0
(CASE WHEN COALESCE(jsonb_array_length(pd.installed_packages),0)=0 THEN 100.0
ELSE ROUND(CAST((1.0-pd.patch_count::float/NULLIF(jsonb_array_length(pd.installed_packages),0))*100 AS numeric),1)
END AS compliance_pct,
END)::float8 AS compliance_pct,
h.health_status::text AS health_status
FROM hosts h LEFT JOIN host_patch_data pd ON pd.host_id=h.id
GROUP BY h.id, h.health_status, pd.installed_packages, pd.patch_count