Skip to content

feat: add nuget ecosystem to blast (CM-1358) - #4443

Merged
ulemons merged 7 commits into
mainfrom
feat/add-nuget-ecosystem-to-blast
Aug 5, 2026
Merged

feat: add nuget ecosystem to blast (CM-1358)#4443
ulemons merged 7 commits into
mainfrom
feat/add-nuget-ecosystem-to-blast

Conversation

@ulemons

@ulemons ulemons commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds NuGet as the fifth supported ecosystem in the blast-radius pipeline (after npm, Go,
Maven and Cargo), so advisories affecting NuGet packages can be analyzed end-to-end:
intel → dependents → reachability.

NuGet has two quirks the existing ecosystems don't: OSV spells the ecosystem NuGet
(mixed case) while our DB stores nuget, and deps.dev never resolves a concrete version
for NuGet dependency edges — so matching goes purely through version_constraint and the
reachability stage falls back to the dependent's highest listed version.

Changes

  • New stage files under blast-radius/stages/nuget/: intelNuGet.ts, dependentsNuGet.ts,
    dependentsScanNuGet.ts, reachabilityConfig.ts, plus agent prompts in
    blast-radius/agent/nugetPrompts.ts.
  • nugetConstraint.ts implements NuGet's interval notation ([1.0,2.0), comma-joined
    alternatives). Unmatched brackets or trailing text return null rather than a partial
    parse: accepting a valid-looking prefix could silently drop the tail and wrongly exclude
    a vulnerable version, so it falls through to unparseable-included.
  • clients/nugetSource.ts resolves a dependent's GitHub source from the nuspec
    <repository> element (url + commit — the registration API never exposes it) and
    extracts the C# source at that commit for the reachability agent.
  • nuget/client.ts: added fetchVersionList — thin wrapper for callers that only need
    version strings, not the full registration payload.
  • Registered nuget in SUPPORTED_ECOSYSTEMS (blast-radius/ecosystemSupport.ts),
    SUPPORTED_BLAST_RADIUS_ECOSYSTEMS (public API schema) and the ECOSYSTEMS dispatch
    record in stages/ecosystems.ts.
  • toBareNuGetId in packageIdentifier.ts. Deliberately does not lowercase:
    findPackageId/findPackageIdsByName compare against canonical casing.

Type of change

  • [] Bug fix
  • New feature
  • Refactor / cleanup
  • Performance improvement
  • Chore / dependency update
  • Documentation

JIRA ticket

CM-1358

@ulemons ulemons self-assigned this Aug 5, 2026
@ulemons ulemons added the Feature Created by Linear-GitHub Sync label Aug 5, 2026
Copilot AI balanced review requested due to automatic review settings August 5, 2026 11:00

Copilot AI left a comment

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.

Pull request overview

Adds NuGet support to the blast-radius analysis pipeline.

Changes:

  • Adds NuGet intel, dependent scanning, source resolution, and reachability analysis.
  • Introduces shared ecosystem-version handling and NuGet constraint parsing.
  • Extends API validation, dispatch, prompts, and tests for NuGet.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
backend/src/api/public/v1/packages/blastRadius.ts Allows NuGet requests.
services/apps/packages_worker/src/nuget/client.ts Adds version-list fetching.
services/apps/packages_worker/src/blast-radius/ecosystemSupport.ts Registers NuGet support.
services/apps/packages_worker/src/blast-radius/packageIdentifier.ts Normalizes NuGet identifiers.
services/apps/packages_worker/src/blast-radius/__tests__/packageIdentifier.test.ts Tests NuGet identifiers.
services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts Adds C# analysis prompts.
services/apps/packages_worker/src/blast-radius/clients/nugetSource.ts Resolves GitHub source archives.
services/apps/packages_worker/src/blast-radius/clients/__tests__/nugetSource.test.ts Tests source resolution.
services/apps/packages_worker/src/blast-radius/clients/osvClient.ts Simplifies duplicate merging.
services/apps/packages_worker/src/blast-radius/stages/ecosystemVersions.ts Shares ecosystem range logic.
services/apps/packages_worker/src/blast-radius/stages/__tests__/ecosystemVersions.test.ts Tests shared version logic.
services/apps/packages_worker/src/blast-radius/stages/ecosystems.ts Registers NuGet stage handlers.
services/apps/packages_worker/src/blast-radius/stages/__tests__/dispatch.test.ts Tests NuGet dispatch.
services/apps/packages_worker/src/blast-radius/stages/selectAdvisoryEntry.ts Defers affected-name construction.
services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts Implements NuGet intel.
services/apps/packages_worker/src/blast-radius/stages/nuget/dependentsNuGet.ts Persists NuGet dependents.
services/apps/packages_worker/src/blast-radius/stages/nuget/dependentsScanNuGet.ts Scans reverse dependencies.
services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts Configures source analysis.
services/apps/packages_worker/src/blast-radius/stages/nuget/nugetConstraint.ts Parses dependency constraints.
services/apps/packages_worker/src/blast-radius/stages/nuget/__tests__/nugetConstraint.test.ts Tests constraint matching.
services/apps/packages_worker/src/blast-radius/stages/maven/mavenVersions.ts Uses shared version utilities.
services/apps/packages_worker/src/blast-radius/stages/maven/intelMaven.ts Updates request handling.
services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts Updates request handling.
services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts Updates request handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts Outdated
Comment thread services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts Outdated
Comment thread services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts Outdated
Comment thread services/apps/packages_worker/src/blast-radius/stages/ecosystemVersions.ts Outdated
Comment thread services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts Outdated
ulemons added 2 commits August 5, 2026 15:46
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings August 5, 2026 14:03
@ulemons
ulemons force-pushed the feat/add-nuget-ecosystem-to-blast branch from caea4df to 07a1485 Compare August 5, 2026 14:03

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Suppressed comments (7)

services/apps/packages_worker/src/blast-radius/stages/npm/intelNpm.ts:64

  • package_name is string | null, so an advisory-wide request reaches toBareNpmName(null) and throws before selectAdvisoryEntry can handle the omitted package. Check for a string rather than only excluding undefined; this also preserves the explicit empty-string behavior.
    const requestedPackage =
      analysisDetail?.package_name !== undefined ? toBareNpmName(analysisDetail.package_name) : null

services/apps/packages_worker/src/blast-radius/stages/go/intelGo.ts:67

  • package_name is string | null, so an advisory-wide request passes null to toBareGoModule and fails on .trim(). Narrow to a string before normalization while retaining empty strings as explicit requests.
    const requestedModule =
      analysisDetail?.package_name !== undefined
        ? toBareGoModule(analysisDetail.package_name)
        : null

services/apps/packages_worker/src/blast-radius/stages/maven/intelMaven.ts:71

  • package_name is nullable in AnalysisDetailRow; checking only undefined sends null into toBareMavenCoordinate, breaking advisory-wide Maven analyses. Narrow to a string before normalizing.
    const requested =
      analysisDetail?.package_name !== undefined
        ? toBareMavenCoordinate(analysisDetail.package_name)
        : null

services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts:66

  • For an advisory-wide NuGet request, package_name is null, not undefined; this therefore calls toBareNuGetId(null) and the intel stage fails before selecting the sole advisory entry. Narrow to a string before normalization.
    const requestedId =
      analysisDetail?.package_name !== undefined ? toBareNuGetId(analysisDetail.package_name) : null

services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts:74

  • NuGet package IDs are case-insensitive (the existing NuGet client already case-folds IDs), but this exact comparison rejects valid requests such as newtonsoft.json when OSV uses Newtonsoft.Json. Match case-insensitively and then use the OSV entry's canonical spelling for the case-sensitive DB lookup.
      (e) => e.package.name === requestedId,
      advisoryOsvId,
    )

    const nugetId = requestedId ?? entry.package.name

services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts:86

  • The promised DB fallback is bypassed for actual network failures and NuGet 5xx responses: fetchRegistration only converts 404/429 into NuGetFetchError and rethrows other Axios errors. Catch those failures here (or classify them in the client) and load getVersionNumbers when dbPackageId exists.
    // The nuget.org registration index is the authoritative version list; fall back to
    // our own ingested `versions` rows (deps.dev) if the registry is unreachable/rate-limited
    // and the package is already known to us.
    const versionListResult = await fetchVersionList(nugetId)

services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts:13

  • This states that dep.version is always null, but scanNuGetDependents persists row.versionNumber, which comes from an inner-joined dependent versions row and is non-null. The code therefore uses that concrete dependent version; describe the highest-version lookup as a defensive fallback instead.
// deps.dev never resolves a concrete version for NuGet edges (see dependentsScanNuGet.ts),
// so dep.version is always null — fall back to the package's current highest listed version.

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings August 5, 2026 14:13
@ulemons
ulemons marked this pull request as ready for review August 5, 2026 14:18
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New vulnerability-analysis surface with external NuGet/GitHub fetches and LLM verdicts; logic is heavily tested but wrong version or constraint handling could mis-rank dependents.

Overview
NuGet is now a supported blast-radius ecosystem on the public API and worker, with full intel → dependents → reachability routing alongside npm, Go, Maven, and Cargo.

NuGet-specific behavior includes ECOSYSTEM-typed OSV ranges and a dedicated four-part version comparator (shared with refactored Maven range logic in ecosystemVersions.ts), interval-style dependency constraints for dependents scanning, case-sensitive toBareNuGetId with lowercase only at DB lookup, and GitHub source fetch from nuspec <repository> (commit-first, then tag guesses) for C# agent analysis. New NuGet agent prompts, fetchVersionList on the NuGet client, and small fixes (OSV merge key by package name only; null-safe package_name checks in other intel stages) round out the change.

Reviewed by Cursor Bugbot for commit 32b0bcb. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.

Suppressed comments (5)

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:53

  • This section-header comment violates the repository rule that explicitly disallows section headers; the exported prompt names already make this boundary clear. Remove it.
// ---------- STAGE 3: REACHABILITY ----------

services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts:83

  • This exact-name lookup breaks NuGet's case-insensitive identity semantics. findPackageId uses name = $(name), and real GHSA/OSV package casing can differ from the NuGet Gallery/DB casing; then stage 1 stores a null package ID and stage 2 fails even though the registry fetch succeeded. Resolve the registry's canonical ID first or add a NuGet-specific case-insensitive DAL lookup.
    const dbPackageId = await findPackageId(qx, { ecosystem, namespace: null, name: nugetId })

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetConstraint.ts:104

  • Returning true converts a comparison failure into matched, rather than unparseable-included. For example, the bare constraint >=1.0 passes the floor parser, fails version comparison, and is persisted as a successful match, contradicting the result contract and diagnostics. Propagate an indeterminate result and return unparseable-included when no definite match exists but a comparison failed.
    if (c === null) return true // unparseable bound — over-inclusive

services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts:25

  • This fallback is dead in the NuGet pipeline: dependentsScanNuGet.ts:38 stores row.versionNumber (the dependent package's own concrete version) into dep.version. The missing deps.dev value is the dependency edge's resolved target version, not the dependent source version. Remove the unused registry fallback and correct this explanation so these two concepts are not conflated.
    const version = dep.version ?? (await resolveNuGetVersion(dep.name))

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:11

  • This section-header comment violates the repository rule that explicitly disallows section headers; the schema names already provide the structure. Remove it.

This issue also appears on line 53 of the same file.

// ---------- STAGE 1: INTEL ----------

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings August 5, 2026 14:32

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a8a5b0f. Configure here.

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 1 comment.

Suppressed comments (4)

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:53

  • Remove this section-header comment. Project guidance explicitly prohibits section headers in code (CLAUDE.md:82); the constant name already identifies this block.
// ---------- STAGE 3: REACHABILITY ----------

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetVersionCompare.ts:51

  • NuGet's official VersionComparer compares nonnumeric prerelease labels with OrdinalIgnoreCase. This case-sensitive comparison can falsely exclude equivalent versions at range boundaries (for example, [1.0.0-BETA] versus 1.0.0-beta). Normalize both labels before ordering and add a regression test.
    if (aIsNum !== bIsNum) return aIsNum ? -1 : 1
    if (aParts[i] !== bParts[i]) return aParts[i] < bParts[i] ? -1 : 1

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:13

  • Remove this section-header comment. Project guidance explicitly prohibits section headers in code (CLAUDE.md:82); the constant name already identifies this block.

This issue also appears on line 53 of the same file.

// ---------- STAGE 1: INTEL ----------

services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts:14

  • This comment is factually incorrect: scanNuGetDependents stores row.versionNumber in candidate.version (dependentsScanNuGet.ts:38), so dep.version normally contains the selected dependent version. The registry fallback only applies to null/legacy records; remove the misleading statement.
// deps.dev never resolves a concrete version for NuGet edges (see dependentsScanNuGet.ts),
// so dep.version is always null — fall back to the package's current highest listed version.

Comment thread services/apps/packages_worker/src/blast-radius/clients/nugetSource.ts Outdated
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings August 5, 2026 14:56

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

Suppressed comments (5)

services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts:25

  • This fallback is unreachable in the new pipeline: scanNuGetDependents always sets version from row.versionNumber, which is guaranteed by getReverseDependents' inner join and then persisted. Therefore the implementation never performs the PR-described “highest listed version” fallback, and fetchVersionList is dead on this path. Either use registry resolution when that behavior is intended, or remove the fallback and document that the concrete dependent version from deps.dev is analyzed.
    const version = dep.version ?? (await resolveNuGetVersion(dep.name))

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:11

  • Section-header comments are explicitly disallowed by the project guidance. The exported schema and prompt names already make this boundary clear, so remove this header.
// ---------- STAGE 1: INTEL ----------

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetVersionCompare.ts:51

  • NuGet compares non-numeric prerelease labels case-insensitively (OrdinalIgnoreCase), but this comparison is case-sensitive. As a result, versions such as 1.0.0-Beta and 1.0.0-beta are ordered differently instead of comparing equal, which can misclassify OSV range boundaries and select the wrong highest version. Normalize both labels before comparing and add a mixed-case regression test.
    if (aParts[i] !== bParts[i]) return aParts[i] < bParts[i] ? -1 : 1

services/apps/packages_worker/src/nuget/client.ts:151

  • This comment only restates that the wrapper returns version strings, which is already clear from fetchVersionList and its return type. Project guidance explicitly disallows comments that merely describe what code does; remove it.
// Thin wrapper for callers (blast-radius intel) that only need the version strings,
// not the full registration payload with per-version metadata.

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:53

  • Section-header comments are explicitly disallowed by the project guidance. The following reachability symbols are self-describing, so remove this header.
// ---------- STAGE 3: REACHABILITY ----------

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings August 5, 2026 15:14

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

Suppressed comments (9)

services/apps/packages_worker/src/blast-radius/stages/nuget/intelNuGet.ts:83

  • This lookup is case-sensitive (findPackageId uses name = $(name)), while NuGet IDs are case-insensitive and nugetId comes from OSV rather than the packages row. If OSV and deps.dev preserve different casing, package_id remains null and the dependents stage fails with “package_id not resolved.” Resolve NuGet package IDs case-insensitively through an indexed DAL lookup while preserving canonical display casing.
    const dbPackageId = await findPackageId(qx, { ecosystem, namespace: null, name: nugetId })

services/apps/packages_worker/src/blast-radius/stages/nuget/dependentsScanNuGet.ts:35

  • NuGet requirements are recorded per target framework, but the ingestion feeding this row collapses duplicate (root, dependency) entries: the full query flattens them and downstream deduplication keeps one, while the incremental query uses MAX(dep.Requirement) (deps-dev/queries/depsSql.ts:25-37,199-211). A dependency can therefore be excluded here even though another target framework has a range containing the vulnerable version. Preserve and evaluate every distinct requirement before filtering candidates.
    const rangeCheck = nugetConstraintMayInclude(row.versionConstraint, vulnerableVersions)

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetVersionCompare.ts:51

  • NuGet.Versioning compares non-numeric prerelease labels with ordinal, case-insensitive semantics. This case-sensitive branch treats 1.0.0-beta and 1.0.0-BETA as different, which can misclassify range boundaries and choose the wrong highest version.
    if (aParts[i] !== bParts[i]) return aParts[i] < bParts[i] ? -1 : 1

services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts:13

  • This comment is incorrect: scanNuGetDependents stores row.versionNumber in dep.version, so normal NuGet rows do not always reach this fallback. Remove the misleading explanation; the fallback only covers a missing persisted dependent version.
// deps.dev never resolves a concrete version for NuGet edges (see dependentsScanNuGet.ts),
// so dep.version is always null — fall back to the package's current highest listed version.

backend/src/api/public/v1/packages/blastRadius.ts:3

  • Add a schema test that submits ecosystem: 'nuget'. This public API enum has dedicated acceptance/rejection tests in blastRadius.test.ts, but the newly exposed value is not covered there.
export const SUPPORTED_BLAST_RADIUS_ECOSYSTEMS = ['npm', 'go', 'maven', 'cargo', 'nuget'] as const

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetConstraint.ts:66

  • Validate both parsed bounds before accepting the interval. Currently a partially malformed constraint such as [2.0,garbage] can exclude vulnerable version 1.5 on the valid lower bound before the invalid upper bound is noticed, contradicting the promised unparseable-included behavior.
  const lower = body.slice(0, comma).trim() || null
  const upper = body.slice(comma + 1).trim() || null
  return { lower, lowerInclusive, upper, upperInclusive }

services/apps/packages_worker/src/nuget/client.ts:151

  • This comment only restates the wrapper’s name, return type, and implementation. Remove it to follow the repository rule against comments that explain what obvious code does (CLAUDE.md:72-83).
// Thin wrapper for callers (blast-radius intel) that only need the version strings,
// not the full registration payload with per-version metadata.

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetVersionCompare.ts:5

  • This external-compatibility explanation exceeds the repository’s two-line limit for permitted comments and does not link the API quirk (CLAUDE.md:72-84). Condense it and link the NuGet versioning behavior.
// node-semver rejects any version with a 4th numeric component (e.g. "1.2.3.4"), but
// NuGetVersion accepts Major.Minor.Patch.Revision — common in Microsoft/BCL packages
// (System.*, Microsoft.NETCore.*). Routing NuGet through the shared semver comparator
// silently drops those versions from range checks; this is a NuGetVersion-compatible
// comparator scoped to blast-radius so it doesn't change the shared OSV sync pipeline.

services/apps/packages_worker/src/blast-radius/stages/tests/ecosystemVersions.test.ts:48

  • This explanation reverses the ordering in its first clause. NuGet’s numeric component comparison places 1.10.0 after 1.9.0; rewrite the comment so it does not contradict the assertion.
      // Semver orders 1.9.0 > 1.10.0's minor is 10 > 9, so unlike the Maven case above,
      // 1.10.0 must sort after 1.9.0 here too, but via a different comparator entirely.

Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings August 5, 2026 15:55

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

Suppressed comments (6)

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:53

  • Remove this section-header comment. The repository convention explicitly prohibits section-header comments (CLAUDE.md:72-84); the IMPORT_STYLE_ENUM declaration already identifies the reachability section.
// ---------- STAGE 3: REACHABILITY ----------

services/apps/packages_worker/src/blast-radius/stages/nuget/nugetVersionCompare.ts:51

  • NuGet compares non-numeric prerelease labels with OrdinalIgnoreCase, but this comparison is case-sensitive. An exact range such as [1.0.0-BETA] therefore excludes vulnerable version 1.0.0-beta, creating a false negative. Compare the ASCII labels case-insensitively and add that case to the regression tests.
    if (aParts[i] !== bParts[i]) return aParts[i] < bParts[i] ? -1 : 1

services/apps/packages_worker/src/blast-radius/stages/nuget/reachabilityConfig.ts:25

  • This fallback is unreachable for NuGet dependents: scanNuGetDependents always assigns the non-null row.versionNumber, and that value is persisted as dep.version. Consequently the implementation does not fall back to the registry's highest listed version as described. Either pass null when that fallback is intended, or remove this resolver and update the description/comments to reflect that the latest ingested dependent version is analyzed.
    const version = dep.version ?? (await resolveNuGetVersion(dep.name))

backend/src/api/public/v1/packages/blastRadius.ts:3

  • Add a schema regression test that parses a request with ecosystem: 'nuget'. backend/src/api/public/v1/packages/blastRadius.test.ts already contracts accepted and rejected ecosystem values, but this new public API value is currently only tested in the worker's separate support list.
export const SUPPORTED_BLAST_RADIUS_ECOSYSTEMS = ['npm', 'go', 'maven', 'cargo', 'nuget'] as const

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:13

  • Remove this section-header comment. The repository convention explicitly prohibits section-header comments (CLAUDE.md:72-84); the IMPORT_SIGNATURE_KEYS declaration already makes the section self-explanatory.

This issue also appears on line 53 of the same file.

// ---------- STAGE 1: INTEL ----------

services/apps/packages_worker/src/blast-radius/agent/nugetPrompts.ts:25

  • The public API now accepts every NuGet package, while this prompt assumes all packages are C#. NuGet also distributes F#, VB, and other .NET-language packages; those advisories would produce the wrong symbol/import patterns and risk false not_affected verdicts. Either detect the source language and select suitable prompts or validate/reject unsupported packages.
export const NUGET_INTEL_SYSTEM_PROMPT = `You are a vulnerability analyst. Your working directory contains the source (fetched from the
package's GitHub repository at the matching commit/tag) of the vulnerable version of a NuGet
package (C#/.NET). You are given the security advisory and the patch (diff) that fixed the
vulnerability.

@ulemons
ulemons merged commit 4a7d9e4 into main Aug 5, 2026
16 checks passed
@ulemons
ulemons deleted the feat/add-nuget-ecosystem-to-blast branch August 5, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Created by Linear-GitHub Sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants