Skip to content
Merged
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
15 changes: 12 additions & 3 deletions backend/src/api/public/v1/packages/getPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,23 @@ export async function getPackage(req: Request, res: Response): Promise<void> {
const scorecardScore = pkg.scorecardScore != null ? Number(pkg.scorecardScore) : null
const mappingConfidence =
pkg.repoMappingConfidence != null ? Number(pkg.repoMappingConfidence) : null
const healthBandScore = pkg.healthScore !== null ? pkg.healthScore / 10 : scorecardScore

ok(res, {
purl: pkg.purl,
name: pkg.name,
ecosystem: pkg.ecosystem,
latestVersion: pkg.latestVersion ?? null,
general: {
healthScore: scorecardScore !== null ? Math.round(scorecardScore * 10) : null,
healthBand: computeHealthBand(scorecardScore),
healthScore: pkg.healthScore,
healthScoreDetails: {
total: pkg.healthScore,
label: pkg.healthLabel,
maintainerHealth: pkg.maintainerHealthScore,
securitySupplyChain: pkg.securitySupplyChainScore,
developmentActivity: pkg.developmentActivityScore,
},
Comment thread
cursor[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

healthScoreDetails never null

Medium Severity

The handler always builds a healthScoreDetails object with nullable fields, but the updated OpenAPI schema says that property stays null until Tinybird enrichment. Unenriched package responses therefore expose an empty breakdown object instead of null, which breaks clients that gate UI on a missing healthScoreDetails.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 11fa261. Configure here.

healthBand: computeHealthBand(healthBandScore),
impact: {
impactScore:
pkg.criticalityScore != null ? Math.round(Number(pkg.criticalityScore) * 100) : null,
Expand All @@ -58,7 +66,7 @@ export async function getPackage(req: Request, res: Response): Promise<void> {
transitiveReach: pkg.transitiveReach,
},
riskSignals: {
lifecycle: null,
lifecycle: pkg.lifecycleLabel,
maintainerBusFactor: pkg.maintainerCount,
lastRelease: pkg.latestReleaseAt ? pkg.latestReleaseAt.toISOString() : null,
hasSecurityFile: pkg.hasSecurityFile,
Expand All @@ -67,6 +75,7 @@ export async function getPackage(req: Request, res: Response): Promise<void> {
openSSFScorecard: scorecardScore,
},
},
signalCoverageHealth: pkg.signalCoverageHealth,
assessment: null,
security: {
securityContacts: null,
Expand Down
42 changes: 35 additions & 7 deletions backend/src/api/public/v1/packages/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,27 @@ components:
type: object
properties:
healthScore:
type:
- integer
- 'null'
description: Composite health score (0–100). Tinybird-enriched when available, null otherwise.
example: 18
healthScoreDetails:
type:
- object
- 'null'
description: Breakdown of the composite health score. Null until Tinybird enrichment runs.
properties:
total:
type:
- integer
- 'null'
example: 18
label:
type:
- string
- 'null'
example: Good
maintainerHealth:
type:
- integer
Expand All @@ -286,11 +303,13 @@ components:
- integer
- 'null'
example: 6
total:
type:
- integer
- 'null'
example: 18
healthBand:
type:
- string
- 'null'
enum: [healthy, fair, concerning, critical, null]
description: Derived from Tinybird health score when available, OpenSSF Scorecard otherwise.
example: concerning
impact:
type:
- object
Expand Down Expand Up @@ -466,6 +485,12 @@ components:
history:
type: object
description: Package history data. Empty in v1.
signalCoverageHealth:
type:
- object
- 'null'
description: Signal coverage health data enriched by Tinybird. Null until enriched.
additionalProperties: true

# ── Metrics ──────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -672,11 +697,14 @@ paths:
name: lodash
ecosystem: npm
general:
healthScore:
healthScore: 18
healthScoreDetails:
total: 18
label: critical
maintainerHealth: 4
securitySupplyChain: 8
developmentActivity: 6
total: 18
healthBand: critical
impact:
impactScore: 71
downloadsLastMonth: 52142891
Expand Down
15 changes: 15 additions & 0 deletions services/libs/data-access-layer/src/osspckgs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,14 @@ export interface PackageDetailRow {
downloadsLast30d: string | null
maintainerCount: number
transitiveReach: number | null
// Tinybird-enriched health fields
healthScore: number | null
healthLabel: string | null
maintainerHealthScore: number | null
securitySupplyChainScore: number | null
developmentActivityScore: number | null
lifecycleLabel: string | null
signalCoverageHealth: Record<string, unknown> | null
}

export interface AdvisoryRow {
Expand Down Expand Up @@ -672,6 +680,13 @@ export async function getPackageDetailByPurl(
LIMIT 1
) AS "downloadsLast30d",
(SELECT COUNT(*)::int FROM package_maintainers pm WHERE pm.package_id = p.id) AS "maintainerCount",
p.health_score AS "healthScore",
p.health_label AS "healthLabel",
p.maintainer_health_score AS "maintainerHealthScore",
p.security_supply_chain_score AS "securitySupplyChainScore",
p.development_activity_score AS "developmentActivityScore",
p.lifecycle_label AS "lifecycleLabel",
p.signal_coverage_health AS "signalCoverageHealth",
-- TODO: precompute and store in packages.transitive_reach_prank; full window scan is too slow at npm scale (~24s for npm)
-- (
-- SELECT r.prank
Expand Down
Loading