Summary
maven-build-test.yaml and gradle-build-test.yaml both upload and download an SBOM artefact named exactly sbom-files, with no lane or run discriminator:
maven-build-test.yaml: uploads at L658, downloads at L699
gradle-build-test.yaml: uploads at L592, downloads at L633
testing.yaml calls both reusable workflows from a single caller, so they execute in one workflow run and share one artifact namespace. Each lane's grype job therefore races to download whichever sbom-files it resolves to, and can scan the other lane's SBOM.
Evidence
Two workflow_dispatch runs of testing.yaml, same fixtures, same pins, differing only in which run they were:
In the second run, the Gradle lane's Grype output is Maven's dependency tree:
velocity 1.5 java-archive GHSA-59j4-wjwp-mw9m High
jetty 6.1.4 java-archive GHSA-9986-w5h5-vw59 Medium
maven-core 2.0-SNAPSHOT java-archive GHSA-2f88-5hg8-9x2x Critical
plexus-utils 1.1 java-archive GHSA-8vhq-qq4p-grq3 Critical
maven-core and plexus-utils are not dependencies of the Gradle fixture (onap/portal-ng-preferences). The Gradle lane scanned the Maven SBOM.
Impact
Confined to the self-test, since a real consumer instantiates one lane per caller and hits no collision. But within testing.yaml it means:
- Grype results are non-deterministic between runs.
- A lane can report green having scanned the other lane's SBOM, so the self-test can pass while proving nothing about the lane it claims to cover.
- Conversely a clean lane can fail on the other lane's findings, as above.
The same applies to the sbom-files artefact consumed anywhere downstream.
Suggested fix
Give the artefact a per-lane name, and keep the upload and download in step. For example sbom-files-maven / sbom-files-gradle, or derive a suffix from the build tool the lane represents. Sibling families already do something equivalent — docker-workflows suffixes with ${{ needs.docker-metadata.outputs.build_id }}.
Provenance
Surfaced while validating the Grype composite-action migration (#29). The migration does not touch the SBOM artefact names or the upload/download steps; it only changes the scan steps, so this is pre-existing. It became visible because the run timing shifted slightly and the collision resolved the other way.
Summary
maven-build-test.yamlandgradle-build-test.yamlboth upload and download an SBOM artefact named exactlysbom-files, with no lane or run discriminator:maven-build-test.yaml: uploads at L658, downloads at L699gradle-build-test.yaml: uploads at L592, downloads at L633testing.yamlcalls both reusable workflows from a single caller, so they execute in one workflow run and share one artifact namespace. Each lane'sgrypejob therefore races to download whicheversbom-filesit resolves to, and can scan the other lane's SBOM.Evidence
Two
workflow_dispatchruns oftesting.yaml, same fixtures, same pins, differing only in which run they were:main32874334329In the second run, the Gradle lane's Grype output is Maven's dependency tree:
maven-coreandplexus-utilsare not dependencies of the Gradle fixture (onap/portal-ng-preferences). The Gradle lane scanned the Maven SBOM.Impact
Confined to the self-test, since a real consumer instantiates one lane per caller and hits no collision. But within
testing.yamlit means:The same applies to the
sbom-filesartefact consumed anywhere downstream.Suggested fix
Give the artefact a per-lane name, and keep the upload and download in step. For example
sbom-files-maven/sbom-files-gradle, or derive a suffix from the build tool the lane represents. Sibling families already do something equivalent —docker-workflowssuffixes with${{ needs.docker-metadata.outputs.build_id }}.Provenance
Surfaced while validating the Grype composite-action migration (#29). The migration does not touch the SBOM artefact names or the upload/download steps; it only changes the scan steps, so this is pre-existing. It became visible because the run timing shifted slightly and the collision resolved the other way.