Jeremiah Kasuzumira
Client Risk Scorecard
SQL
R
Case Study 05

Client Risk Scorecard

Three years of paid security audit work, turned into a repeatable scoring system instead of a one-off PDF per client. Every engagement produces the same structured inputs — vulnerability severity, patch velocity, authentication hygiene, exposure surface — so risk becomes a number you can track over time, not a report you file away.

⚠ Client names anonymized — engaged clients did not respond to a request to feature their audit publicly. Findings, scores, and the breach-monitoring panel below use illustrative data structured on real audit categories, not verbatim client findings.
Audits Conducted
3
Fintech, e-commerce, SaaS clients
Avg Risk Score
62 / 100
Medium risk, before remediation
Critical Findings Open
4
Across all engagements
Avg Time to Remediate
18 days
From finding to fix, critical items
Client Scorecards anonymized
Real methodology, generic labels — see the anonymization note above
Fintech Client — Payments Platform
Web + API audit · 2025
48
High Risk
No MFA enforced on admin-level accounts
Outdated TLS configuration on a payment callback endpoint
Verbose error messages exposing internal stack traces
E-commerce Client — Retail Platform
Web application audit · 2025
67
Medium Risk
Publicly accessible staging environment with production data
Session tokens not invalidated on password reset
Weak default admin credentials — resolved during engagement
SaaS Client — B2B Platform
Infrastructure + app audit · 2024
71
Medium Risk
No documented incident response plan
Unpatched dependency with known CVE — resolved during engagement
Open database port on cloud instance — resolved during engagement
Findings by Severity all clients, pre-remediation
Critical
4
High
8
Medium
11
Low
6
Risk Score Simulator
The same weighted model behind every scorecard above. Move the sliders to see how each factor moves the final score — this is the R logic running live, not a static illustration.
54
out of 100
High Risk
Methodology — SQL & R
Every audit produces the same structured findings table. SQL aggregates severity counts and remediation timing per engagement; R applies the weighted model that turns those inputs into the 0–100 score shown on each scorecard and in the simulator above.
SQLper-engagement severity rollup
-- One row per audit engagement: severity
-- counts and remediation timing
SELECT
  e.engagement_id,
  e.client_label,
  SUM(CASE WHEN f.severity = 'critical'
      THEN 1 ELSE 0 END) AS critical_count,
  SUM(CASE WHEN f.severity = 'high'
      THEN 1 ELSE 0 END) AS high_count,
  AVG(
    CASE WHEN f.resolved_date IS NOT NULL
    THEN f.resolved_date - f.found_date END
  ) AS avg_days_to_remediate,
  AVG(CAST(a.mfa_enforced AS INT)) AS mfa_coverage,
  BOOL_OR(a.has_incident_response_plan) AS has_irp
FROM engagements e
JOIN findings f ON f.engagement_id = e.engagement_id
JOIN account_hygiene a ON a.engagement_id = e.engagement_id
GROUP BY e.engagement_id, e.client_label;
Rweighted risk score
# Lower is riskier for each raw input, so
# everything is normalized to a 0-1 "good" scale
weights <- c(
  mfa_coverage   = 0.30,
  critical_vulns = 0.30,
  patch_velocity = 0.25,
  irp_documented = 0.15
)

risk_score <- function(mfa, crit_count, days_to_patch, has_irp) {
  s_mfa   <- mfa / 100
  s_crit  <- 1 - pmin(crit_count / 6, 1)
  s_patch <- 1 - pmin(days_to_patch / 60, 1)
  s_irp   <- as.numeric(has_irp)

  raw <- s_mfa * weights["mfa_coverage"] +
         s_crit * weights["critical_vulns"] +
         s_patch * weights["patch_velocity"] +
         s_irp * weights["irp_documented"]

  round(raw * 100)
}
Scope & Anonymization Note

This scorecard is built on the real scoring methodology used across three paid audit engagements (fintech, e-commerce, SaaS). Client names are withheld — they did not respond to a request to be named publicly, and audit engagements are confidential by default without explicit sign-off. Findings shown here are illustrative examples structured on the same categories the real audits covered (authentication hygiene, TLS configuration, exposure surface, incident response readiness), not verbatim client findings.

No live breach-monitoring data is shown for these clients: a domain-level Have I Been Pwned lookup requires verified ownership of the domain, which isn't available for confidential client engagements. A live version of this panel is straightforward to add for any domain Digital Kuwala directly controls.