@@ -127,6 +127,9 @@ public async Task TestSbomDetector_SimpleSbomAsync()
127127 sbomComponent . SpdxVersion . Should ( ) . Be ( "SPDX-2.2" ) ;
128128 sbomComponent . Checksum . Should ( ) . Be ( checksum ) ;
129129 sbomComponent . Path . Should ( ) . Be ( Path . Combine ( Path . GetTempPath ( ) , spdxFileName ) ) ;
130+
131+ sbomComponent . CreatorTool . Should ( ) . Be ( "Microsoft.SBOMTool-1.0.0" ) ;
132+ sbomComponent . CreatorOrganization . Should ( ) . Be ( "Microsoft" ) ;
130133 }
131134
132135 [ TestMethod ]
@@ -160,4 +163,213 @@ public async Task TestSbomDetector_InvalidFileAsync()
160163 var components = detectedComponents . ToList ( ) ;
161164 components . Should ( ) . BeEmpty ( ) ;
162165 }
166+
167+ [ TestMethod ]
168+ public async Task TestSbomDetector_ExtractsCreatorToolAndOrganizationAsync ( )
169+ {
170+ var spdxFile = /*lang=json,strict*/ @"{
171+ ""spdxVersion"": ""SPDX-2.2"",
172+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
173+ ""name"": ""TestDoc"",
174+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
175+ ""creationInfo"": {
176+ ""created"": ""2024-01-01T00:00:00Z"",
177+ ""creators"": [
178+ ""Tool: microsoft/sbom-tool-2.2.0"",
179+ ""Organization: Microsoft""
180+ ]
181+ },
182+ ""documentDescribes"": [""SPDXRef-RootPackage""],
183+ ""packages"": [],
184+ ""relationships"": []
185+ }" ;
186+
187+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
188+ . WithFile ( "manifest.spdx.json" , spdxFile )
189+ . ExecuteDetectorAsync ( ) ;
190+
191+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
192+
193+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
194+ sbomComponent . CreatorTool . Should ( ) . Be ( "microsoft/sbom-tool-2.2.0" ) ;
195+ sbomComponent . CreatorOrganization . Should ( ) . Be ( "Microsoft" ) ;
196+ }
197+
198+ [ TestMethod ]
199+ public async Task TestSbomDetector_MissingCreationInfoReturnsNullAsync ( )
200+ {
201+ var spdxFile = /*lang=json,strict*/ @"{
202+ ""spdxVersion"": ""SPDX-2.2"",
203+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
204+ ""name"": ""TestDoc"",
205+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
206+ ""documentDescribes"": [""SPDXRef-RootPackage""],
207+ ""packages"": [],
208+ ""relationships"": []
209+ }" ;
210+
211+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
212+ . WithFile ( "manifest.spdx.json" , spdxFile )
213+ . ExecuteDetectorAsync ( ) ;
214+
215+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
216+
217+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
218+ sbomComponent . CreatorTool . Should ( ) . BeNull ( ) ;
219+ sbomComponent . CreatorOrganization . Should ( ) . BeNull ( ) ;
220+ }
221+
222+ [ TestMethod ]
223+ public async Task TestSbomDetector_WhitespaceOnlyCreatorsReturnNullAsync ( )
224+ {
225+ var spdxFile = /*lang=json,strict*/ @"{
226+ ""spdxVersion"": ""SPDX-2.2"",
227+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
228+ ""name"": ""TestDoc"",
229+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
230+ ""creationInfo"": {
231+ ""created"": ""2024-01-01T00:00:00Z"",
232+ ""creators"": [
233+ ""Tool: "",
234+ ""Organization: ""
235+ ]
236+ },
237+ ""documentDescribes"": [""SPDXRef-RootPackage""],
238+ ""packages"": [],
239+ ""relationships"": []
240+ }" ;
241+
242+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
243+ . WithFile ( "manifest.spdx.json" , spdxFile )
244+ . ExecuteDetectorAsync ( ) ;
245+
246+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
247+
248+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
249+ sbomComponent . CreatorTool . Should ( ) . BeNull ( ) ;
250+ sbomComponent . CreatorOrganization . Should ( ) . BeNull ( ) ;
251+ }
252+
253+ [ TestMethod ]
254+ public async Task TestSbomDetector_MultipleCreatorsPicksFirstToolAndOrgAsync ( )
255+ {
256+ var spdxFile = /*lang=json,strict*/ @"{
257+ ""spdxVersion"": ""SPDX-2.2"",
258+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
259+ ""name"": ""TestDoc"",
260+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
261+ ""creationInfo"": {
262+ ""created"": ""2024-01-01T00:00:00Z"",
263+ ""creators"": [
264+ ""Tool: first-tool-1.0"",
265+ ""Tool: second-tool-2.0"",
266+ ""Organization: FirstOrg"",
267+ ""Organization: SecondOrg""
268+ ]
269+ },
270+ ""documentDescribes"": [""SPDXRef-RootPackage""],
271+ ""packages"": [],
272+ ""relationships"": []
273+ }" ;
274+
275+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
276+ . WithFile ( "manifest.spdx.json" , spdxFile )
277+ . ExecuteDetectorAsync ( ) ;
278+
279+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
280+
281+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
282+ sbomComponent . CreatorTool . Should ( ) . Be ( "first-tool-1.0" ) ;
283+ sbomComponent . CreatorOrganization . Should ( ) . Be ( "FirstOrg" ) ;
284+ }
285+
286+ [ TestMethod ]
287+ public async Task TestSbomDetector_OnlyToolNoOrganizationAsync ( )
288+ {
289+ var spdxFile = /*lang=json,strict*/ @"{
290+ ""spdxVersion"": ""SPDX-2.2"",
291+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
292+ ""name"": ""TestDoc"",
293+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
294+ ""creationInfo"": {
295+ ""created"": ""2024-01-01T00:00:00Z"",
296+ ""creators"": [
297+ ""Tool: my-tool-3.0""
298+ ]
299+ },
300+ ""documentDescribes"": [""SPDXRef-RootPackage""],
301+ ""packages"": [],
302+ ""relationships"": []
303+ }" ;
304+
305+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
306+ . WithFile ( "manifest.spdx.json" , spdxFile )
307+ . ExecuteDetectorAsync ( ) ;
308+
309+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
310+
311+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
312+ sbomComponent . CreatorTool . Should ( ) . Be ( "my-tool-3.0" ) ;
313+ sbomComponent . CreatorOrganization . Should ( ) . BeNull ( ) ;
314+ }
315+
316+ [ TestMethod ]
317+ public async Task TestSbomDetector_OnlyOrganizationNoToolAsync ( )
318+ {
319+ var spdxFile = /*lang=json,strict*/ @"{
320+ ""spdxVersion"": ""SPDX-2.2"",
321+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
322+ ""name"": ""TestDoc"",
323+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
324+ ""creationInfo"": {
325+ ""created"": ""2024-01-01T00:00:00Z"",
326+ ""creators"": [
327+ ""Organization: Contoso""
328+ ]
329+ },
330+ ""documentDescribes"": [""SPDXRef-RootPackage""],
331+ ""packages"": [],
332+ ""relationships"": []
333+ }" ;
334+
335+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
336+ . WithFile ( "manifest.spdx.json" , spdxFile )
337+ . ExecuteDetectorAsync ( ) ;
338+
339+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
340+
341+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
342+ sbomComponent . CreatorTool . Should ( ) . BeNull ( ) ;
343+ sbomComponent . CreatorOrganization . Should ( ) . Be ( "Contoso" ) ;
344+ }
345+
346+ [ TestMethod ]
347+ public async Task TestSbomDetector_CreatorsWithNoToolOrOrgPrefixAsync ( )
348+ {
349+ var spdxFile = /*lang=json,strict*/ @"{
350+ ""spdxVersion"": ""SPDX-2.2"",
351+ ""SPDXID"": ""SPDXRef-DOCUMENT"",
352+ ""name"": ""TestDoc"",
353+ ""documentNamespace"": ""https://sbom.microsoft/test/1.0.0/abc"",
354+ ""creationInfo"": {
355+ ""created"": ""2024-01-01T00:00:00Z"",
356+ ""creators"": [
357+ ""Person: John Doe (john@example.com)""
358+ ]
359+ },
360+ ""documentDescribes"": [""SPDXRef-RootPackage""],
361+ ""packages"": [],
362+ ""relationships"": []
363+ }" ;
364+
365+ var ( scanResult , componentRecorder ) = await this . detectorTestUtility
366+ . WithFile ( "manifest.spdx.json" , spdxFile )
367+ . ExecuteDetectorAsync ( ) ;
368+
369+ scanResult . ResultCode . Should ( ) . Be ( ProcessingResultCode . Success ) ;
370+
371+ var sbomComponent = ( SpdxComponent ) componentRecorder . GetDetectedComponents ( ) . Single ( ) . Component ;
372+ sbomComponent . CreatorTool . Should ( ) . BeNull ( ) ;
373+ sbomComponent . CreatorOrganization . Should ( ) . BeNull ( ) ;
374+ }
163375}
0 commit comments