Fix SbomValidator.ValidateSbomAsync false positive when outputPath is directory
Problem
When SbomValidator.ValidateSbomAsync is called with a directory path as the outputPath parameter, it incorrectly returns IsSuccessful = true even though validation fails. This happens because:
-
FileOutputWriter.WriteAsync()throws an exception when trying to create a FileStream with a directory path - The exception is caught and recorded via
recorder.RecordException(e) - However,
SbomValidator.ValidateSbomAsynconly checksrecorder.Errorsto determine success, ignoring recorded exceptions - This results in a false positive where
IsSuccessful = truedespite the validation actually failing
Solution
Added an Exceptions property to the IRecorder interface and updated SbomValidator to consider both errors and exceptions when determining validation success:
// Before
var errors = recorder.Errors.Select(error => error.ToEntityError()).ToList();
return new SbomValidationResult(!errors.Any(), errors);
// After
var errors = recorder.Errors.Select(error => error.ToEntityError()).ToList();
var hasExceptions = recorder.Exceptions.Any();
return new SbomValidationResult(!errors.Any() && !hasExceptions, errors);
Changes Made
-
IRecorder.cs: Added
Exceptionsproperty to expose recorded exceptions -
TelemetryRecorder.cs: Implemented
Exceptionsproperty to return the privateexceptionscollection - SbomValidator.cs: Modified validation result logic to check both errors and exceptions
-
Added comprehensive tests covering all scenarios:
- No errors, no exceptions →
IsSuccess = true - Errors, no exceptions →
IsSuccess = false - No errors, with exceptions →
IsSuccess = false(key fix) - Both errors and exceptions →
IsSuccess = false
- No errors, no exceptions →
Testing
All existing tests pass (370 tests) plus 6 new tests specifically for this fix. The solution maintains backward compatibility while properly handling the exception scenario.
Example
var result = await validator.ValidateSbomAsync(
buildDropPath: "/path/to/build",
outputPath: "/path/to/directory", // Directory, not file
specifications: specs);
// Before: result.IsSuccessful == true (false positive)
// After: result.IsSuccessful == false (correct)
Fixes #1093.
💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.
This PR changes files in the API project. Does it change any of the API interfaces in any way? Please note that this includes the following types of changes:
- Changing the signature of an existing interface method
- Adding a new method to an existing interface
- Adding a required data member to a class that an existing interface method consumes
Because any of these changes can potentially break a downstream consumer with customized interface implementations, these changes need to be treated as breaking changes. Please do one of the following:
Option 1 - Publish this as a breaking change
- Update the documentation to show the new functionality
- Bump the major version in the next release
- Be sure to highlight the breaking changes in the release notes
Option 2 - Refactor the changes to be non-breaking
- Review this commit, which adds a new interface in a backward-compatible way
- Refactor the change to follow this pattern so that existing interfaces are left completely intact
- Bump the minor version in the next release
This PR changes files in the API project. Does it change any of the API interfaces in any way? Please note that this includes the following types of changes:
- Changing the signature of an existing interface method
- Adding a new method to an existing interface
- Adding a required data member to a class that an existing interface method consumes
Because any of these changes can potentially break a downstream consumer with customized interface implementations, these changes need to be treated as breaking changes. Please do one of the following:
Option 1 - Publish this as a breaking change
- Update the documentation to show the new functionality
- Bump the major version in the next release
- Be sure to highlight the breaking changes in the release notes
Option 2 - Refactor the changes to be non-breaking
- Review this commit, which adds a new interface in a backward-compatible way
- Refactor the change to follow this pattern so that existing interfaces are left completely intact
- Bump the minor version in the next release
This PR changes files in the API project. Does it change any of the API interfaces in any way? Please note that this includes the following types of changes:
- Changing the signature of an existing interface method
- Adding a new method to an existing interface
- Adding a required data member to a class that an existing interface method consumes
Because any of these changes can potentially break a downstream consumer with customized interface implementations, these changes need to be treated as breaking changes. Please do one of the following:
Option 1 - Publish this as a breaking change
- Update the documentation to show the new functionality
- Bump the major version in the next release
- Be sure to highlight the breaking changes in the release notes
Option 2 - Refactor the changes to be non-breaking
- Review this commit, which adds a new interface in a backward-compatible way
- Refactor the change to follow this pattern so that existing interfaces are left completely intact
- Bump the minor version in the next release
This PR changes files in the API project. Does it change any of the API interfaces in any way? Please note that this includes the following types of changes:
- Changing the signature of an existing interface method
- Adding a new method to an existing interface
- Adding a required data member to a class that an existing interface method consumes
Because any of these changes can potentially break a downstream consumer with customized interface implementations, these changes need to be treated as breaking changes. Please do one of the following:
Option 1 - Publish this as a breaking change
- Update the documentation to show the new functionality
- Bump the major version in the next release
- Be sure to highlight the breaking changes in the release notes
Option 2 - Refactor the changes to be non-breaking
- Review this commit, which adds a new interface in a backward-compatible way
- Refactor the change to follow this pattern so that existing interfaces are left completely intact
- Bump the minor version in the next release
/azp run
This PR changes files in the API project. Does it change any of the API interfaces in any way? Please note that this includes the following types of changes:
- Changing the signature of an existing interface method
- Adding a new method to an existing interface
- Adding a required data member to a class that an existing interface method consumes
Because any of these changes can potentially break a downstream consumer with customized interface implementations, these changes need to be treated as breaking changes. Please do one of the following:
Option 1 - Publish this as a breaking change
- Update the documentation to show the new functionality
- Bump the major version in the next release
- Be sure to highlight the breaking changes in the release notes
Option 2 - Refactor the changes to be non-breaking
- Review this commit, which adds a new interface in a backward-compatible way
- Refactor the change to follow this pattern so that existing interfaces are left completely intact
- Bump the minor version in the next release
This PR changes files in the API project. Does it change any of the API interfaces in any way? Please note that this includes the following types of changes:
- Changing the signature of an existing interface method
- Adding a new method to an existing interface
- Adding a required data member to a class that an existing interface method consumes
Because any of these changes can potentially break a downstream consumer with customized interface implementations, these changes need to be treated as breaking changes. Please do one of the following:
Option 1 - Publish this as a breaking change
- Update the documentation to show the new functionality
- Bump the major version in the next release
- Be sure to highlight the breaking changes in the release notes
Option 2 - Refactor the changes to be non-breaking
- Review this commit, which adds a new interface in a backward-compatible way
- Refactor the change to follow this pattern so that existing interfaces are left completely intact
- Bump the minor version in the next release