XRAY-156485 - Match violation component ids to SBOM refs case-insensitively - #830
Merged
Jordanh1996 merged 2 commits intoAug 4, 2026
Conversation
…tively Xray lowercases component ids of case-insensitive package types such as NuGet, while the SBOM BOMRef keeps the original case and carries purl qualifiers. The previous match combined a case-sensitive HasPrefix with a full-length EqualFold, so on V3 SBOMs the EqualFold branch could never fire and any case drift dropped the violation from the PR. Strip the purl qualifiers off the BOMRef before comparing, then compare case-insensitively. This also removes the HasPrefix over-match that let version 9.0.131 satisfy a violation on 9.0.13. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Jordanh1996
force-pushed
the
fix/XRAY-156485-nuget-case-insensitive-bomref-match
branch
from
August 4, 2026 13:38
0b30a56 to
a168b30
Compare
Contributor
Author
|
prioject with a simple csproj: <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="6.0.0" />
</ItemGroup>
</Project>
|
Contributor
attiasas
approved these changes
Aug 4, 2026
attiasas
left a comment
Collaborator
There was a problem hiding this comment.
LGTM — this correctly replaces the case-sensitive HasPrefix + full-length EqualFold combo that made EqualFold unreachable on V3 qualified BOMRefs.
Stripping only bomRef (not ref) is right: XrayComponentIdToCdxComponentRef → ToPackageRef produces a bare purl. Type-agnostic EqualFold is also the right call given cpp/Maven case drift (XRAY-135509).
Nice bonus that this also kills the @9.0.131 / @9.0.13 HasPrefix over-match.
…f match test Address review: the matcher strips both purl qualifier separators and folds case for every package type, but the table only exercised ?hash= on NuGet. The subpath row fails if the strip is ever narrowed to '?' alone, and the Maven row (a real BOMRef from a Frogbot V3 scan, whose version case drifts even though Xray does not lower case gav ids) fails if the fold is ever narrowed to the case-insensitive package types. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Jordanh1996
deleted the
fix/XRAY-156485-nuget-case-insensitive-bomref-match
branch
August 4, 2026 15:05
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


devbranch.go vet ./....go fmt ./....Problem
XRAY-156485 — customer case 428503, Critical.
When a git-repository watch is bound, Frogbot correlates each Xray violation's infected component id back to a component in the scan SBOM. For NuGet, that correlation fails and the violation is silently dropped, so the PR is decorated as having no findings even though SCA detected the CVEs:
With
JF_FAIL=truethe build cannot gate on findings that were dropped. The only customer workaround was to unbind the watch, which downgrades PR comments from violations to plain vulnerabilities.Root cause
Two independent facts combine:
lowerCasePackageTypes = {NuGet, Pypi}in Xray'ssbom_utils.go. This is deliberate and correct — nuget.org and PyPI are case-insensitive registries — and Xray cannot echo the original casing back, because only the folded value is persisted. PascalCase package names are the NuGet norm, so NuGet drifts on almost every project.pkg:nuget/Name@9.0.13?hash=5058f1af, whilerefis built from the violation id byXrayComponentIdToCdxComponentRefand is a bare purl.The previous match was:
HasPrefixtolerates the qualifier suffix but is case-sensitive.EqualFoldtolerates case but only on a full-length match, which the qualifier makes impossible. So on V3 theEqualFoldbranch is unreachable and only the case-sensitive comparison is load-bearing. Any case drift drops the violation.V2 was never affected — its BOMRefs are bare, so
EqualFoldstill fired. That is exactly why "unbind the watch" worked as a workaround.Fix
Strip the purl qualifiers (
?qualifiers/#subpath) off the BOMRef, then compare case-insensitively. This restores the case-insensitive intent the original comment described, and drops theHasPrefixover-match that let@9.0.131satisfy a violation on@9.0.13.Notes for the reviewer
bomRefis stripped, notref.refcomes fromXrayComponentIdToCdxComponentRef→ToPackageRef, which callsToPackageUrlwith no qualifier arguments, so it is bare by construction. Stripping both would be symmetric but is dead code today.nuget/pypi(Xray's exact allowlist) looks tighter but regressescpp: Xray'sGetComponentIdFromCycloneDXComponentlowercases the whole purl wholesale, so e.g.pkg:github/FFmpeg/FFmpeg@n6.1.1→cpp://ffmpeg:FFmpeg:n6.1.1— case-drifted despitecppnot being in the allowlist. That is XRAY-135509, still open on the Xray side; the type-agnostic fold is what carries it today.Sirupsen/logrusvssirupsen/logrus). I did not add an exact-match-first pass, because the ambiguity is already unresolvable: the violation id reaching us has been lowercased server-side, so Xray cannot say which of the two it meant either. The realistic outcome is a possibly-mislabeled impact path rather than a dropped violation, which is strictly better than today. Happy to add a prefer-exact pass if you'd rather.locateBomComponentInfobreaks out of the component loop but not the target loop, so a match in a later target silently overwrites an earlier one. Pre-existing, unchanged by this PR.Testing
TestLocateBomComponentInfo_matchesQualifiedAndCaseDriftedRefsis a table test over the real customer refs, covering exact / qualified / case-drifted / qualified+case-drifted matches plus the two negatives (version-prefix collision, different component). It fails ondevand passes here.Worth noting which case fails on
dev: onlyqualified_and_case_drifted_ref.case_drifted_refon its own still passes, because a bare BOMRef lets the oldEqualFoldbranch fire — that is V2, and V2 was never affected. It takes the qualifier that V3 static-SCA adds to push the ref pastEqualFold's full-length match and leave only the case-sensitive comparison load-bearing.Verified in addition by replaying two real Frogbot V3 CycloneDX SBOMs through
locateBomComponentInfowith the lowercased violation ids Xray actually returns:The two WebGoat drops are its only two BOMRefs containing uppercase,
@2025.4-SNAPSHOTand@3.1.3.RELEASE— i.e. Maven is affected too, not just NuGet.Finally, reproduced end to end against a live Xray:
jf audit --static-sca --watches=…on a NuGet project pinningSystem.Security.Cryptography.Xml 6.0.0, run twice with ajfbuilt from this commit and from its parent, everything else identical.