File Coverage

blib/lib/SBOM/CycloneDX/Component.pm
Criterion Covered Total %
statement 91 95 95.7
branch 46 72 63.8
condition n/a
subroutine 16 17 94.1
pod 1 1 100.0
total 154 185 83.2


line stmt bran cond sub pod time code
1             package SBOM::CycloneDX::Component;
2              
3 16     16   241 use 5.010001;
  16         51  
4 16     16   63 use strict;
  16         24  
  16         279  
5 16     16   46 use warnings;
  16         21  
  16         610  
6 16     16   59 use utf8;
  16         43  
  16         91  
7              
8 16     16   6481 use SBOM::CycloneDX::BomRef;
  16         91  
  16         596  
9 16     16   6590 use SBOM::CycloneDX::Enum;
  16         75  
  16         1951  
10 16     16   108 use SBOM::CycloneDX::List;
  16         26  
  16         345  
11              
12 16     16   55 use JSON::PP;
  16         25  
  16         1125  
13 16     16   90 use Types::Standard qw(Str StrMatch Bool Enum InstanceOf HashRef);
  16         47  
  16         151  
14 16     16   29019 use Types::TypeTiny qw(ArrayLike);
  16         28  
  16         96  
15 16     16   14305 use URI::PackageURL;
  16         169667  
  16         1019  
16 16     16   6875 use URI::VersionRange;
  16         227086  
  16         1112  
17              
18 16     16   124 use Moo;
  16         27  
  16         157  
19 16     16   6384 use namespace::autoclean;
  16         32  
  16         173  
20              
21             around BUILDARGS => sub {
22             my ($orig, $class, @args) = @_;
23             return {type => $args[0]} if @args == 1 && !ref $args[0];
24             return $class->$orig(@args);
25             };
26              
27             extends 'SBOM::CycloneDX::Base';
28              
29             has type => (is => 'rw', isa => Enum [SBOM::CycloneDX::Enum->values('COMPONENT_TYPE')], required => 1);
30              
31             has mime_type => (is => 'rw', isa => StrMatch [qr{^[-+a-z0-9.]+/[-+a-z0-9.]+$}]);
32              
33             has bom_ref => (
34             is => 'rw',
35             isa => InstanceOf ['SBOM::CycloneDX::BomRef'],
36             coerce => sub { ref($_[0]) ? $_[0] : SBOM::CycloneDX::BomRef->new($_[0]) }
37             );
38              
39             has supplier => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::OrganizationalEntity']);
40              
41             has manufacturer => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::OrganizationalEntity']);
42              
43             has authors => (
44             is => 'rw',
45             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::OrganizationalContact']],
46             default => sub { SBOM::CycloneDX::List->new }
47             );
48              
49             has author => (is => 'rw', isa => Str); # Deprecated in 1.6
50              
51             has publisher => (is => 'rw', isa => Str);
52              
53             has group => (is => 'rw', isa => Str);
54              
55             has name => (is => 'rw', isa => Str, required => 1);
56              
57             has version => (is => 'rw', isa => Str); # Required in 1.2 and 1.3
58              
59             # Added in 1.7
60             has version_range => (is => 'rw', isa => InstanceOf ['URI::VersionRange'], coerce => sub { _vers_parse($_[0]) });
61              
62             has is_external => (is => 'rw', isa => Bool); # Added in 1.7
63              
64             has description => (is => 'rw', isa => Str);
65              
66             has scope => (is => 'rw', isa => Enum [qw(required optional excluded)]); # Default required
67              
68             has hashes => (
69             is => 'rw',
70             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::Hash']],
71             default => sub { SBOM::CycloneDX::List->new }
72             );
73              
74             has licenses => (
75             is => 'rw',
76             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::License']],
77             default => sub { SBOM::CycloneDX::List->new }
78             );
79              
80             has copyright => (is => 'rw', isa => Str);
81              
82             has patent_assertions => (
83             is => 'rw',
84             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::PatentAssertions']],
85             default => sub { SBOM::CycloneDX::List->new }
86             );
87              
88             has cpe => (is => 'rw', isa => Str);
89              
90             has purl => (is => 'rw', isa => InstanceOf ['URI::PackageURL'], coerce => sub { _purl_parse($_[0]) });
91              
92             has omnibor_id => (is => 'rw', isa => ArrayLike [Str], default => sub { SBOM::CycloneDX::List->new });
93              
94             has swhid => (is => 'rw', isa => ArrayLike [Str], default => sub { SBOM::CycloneDX::List->new });
95              
96             has swid => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::Component::SWID']);
97              
98             has modified => (is => 'rw', isa => Bool); # Deprecated in 1.4
99              
100             has pedigree => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::Component::Pedigree']);
101              
102             has external_references => (
103             is => 'rw',
104             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::ExternalReference']],
105             default => sub { SBOM::CycloneDX::List->new }
106             );
107              
108             has components => (
109             is => 'rw',
110             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::Component']],
111             default => sub { SBOM::CycloneDX::List->new }
112             );
113              
114             has evidence => (is => 'rw', isa => HashRef); # TODO
115              
116             has release_notes => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::ReleaseNotes']);
117              
118             has model_card => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::Component::ModelCard']);
119              
120             has data => (is => 'rw', isa => ArrayLike [Str], default => sub { SBOM::CycloneDX::List->new }); # TODO
121              
122             has crypto_properties => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::CryptoProperties']);
123              
124             has properties => (
125             is => 'rw',
126             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::Property']],
127             default => sub { SBOM::CycloneDX::List->new }
128             );
129              
130             has tags => (is => 'rw', isa => ArrayLike [Str], default => sub { SBOM::CycloneDX::List->new });
131              
132             has signature => (is => 'rw', isa => HashRef); # TODO
133              
134              
135             sub _purl_parse {
136              
137 2     2   5 my $purl = shift;
138              
139 2 50       28 return $purl if (ref $purl eq 'URI::PackageURL');
140 0         0 return URI::PackageURL->from_string($purl);
141              
142             }
143              
144             sub _vers_parse {
145              
146 0     0   0 my $vers = shift;
147              
148 0 0       0 return $vers if (ref $vers eq 'URI::VersionRange');
149 0         0 return URI::VersionRange->from_string($vers);
150              
151             }
152              
153             sub TO_JSON {
154              
155 112     112 1 147 my $self = shift;
156              
157 112         1535 my $json = {type => $self->type, name => $self->name};
158              
159              
160 112 50       3281 $json->{'mime-type'} = $self->mime_type if $self->mime_type;
161 112 100       1712 $json->{'bom-ref'} = $self->bom_ref if $self->bom_ref;
162 112 50       1662 $json->{supplier} = $self->supplier if $self->supplier;
163 112 50       1645 $json->{manufacturer} = $self->manufacturer if $self->manufacturer;
164 112 50       453 $json->{authors} = $self->authors if @{$self->authors};
  112         1289  
165 112 100       1398 $json->{author} = $self->author if $self->author;
166 112 100       1793 $json->{publisher} = $self->publisher if $self->publisher;
167 112 100       1808 $json->{group} = $self->group if $self->group;
168 112 100       1756 $json->{version} = $self->version if $self->version;
169 112 50       2782 $json->{versionRange} = $self->version_range->to_string if $self->version_range;
170 112 50       1485 $json->{isExternal} = $self->is_external if JSON::PP::is_bool($self->is_external);
171 112 100       2122 $json->{description} = $self->description if $self->description;
172 112 100       1625 $json->{scope} = $self->scope if $self->scope;
173 112 100       528 $json->{hashes} = $self->hashes if @{$self->hashes};
  112         1235  
174 112 100       213 $json->{licenses} = $self->licenses if @{$self->licenses};
  112         1287  
175 112 50       1608 $json->{copyright} = $self->copyright if $self->copyright;
176 112 50       446 $json->{patentAssertions} = $self->patent_assertions if @{$self->patent_assertions};
  112         1245  
177 112 50       1262 $json->{cpe} = $self->cpe if $self->cpe;
178 112 100       1537 $json->{purl} = $self->purl->to_string if $self->purl;
179 112 50       2329 $json->{omniborId} = $self->omnibor_id if @{$self->omnibor_id};
  112         1231  
180 112 50       143 $json->{swhid} = $self->swhid if @{$self->swhid};
  112         1269  
181 112 100       1300 $json->{swid} = $self->swid if $self->swid;
182 112 50       1639 $json->{modified} = $self->modified if JSON::PP::is_bool($self->modified);
183 112 50       1940 $json->{pedigree} = $self->pedigree if $self->pedigree;
184 112 50       470 $json->{externalReferences} = $self->external_references if @{$self->external_references};
  112         1190  
185 112 50       158 $json->{components} = $self->components if @{$self->components};
  112         1231  
186 112 50       1252 $json->{evidence} = $self->evidence if $self->evidence;
187 112 50       1471 $json->{releaseNotes} = $self->release_notes if $self->release_notes;
188 112 50       1511 $json->{modelCard} = $self->model_card if $self->model_card;
189 112 50       502 $json->{data} = $self->data if @{$self->data};
  112         1165  
190 112 50       1204 $json->{cryptoProperties} = $self->crypto_properties if $self->crypto_properties;
191 112 50       451 $json->{properties} = $self->properties if @{$self->properties};
  112         1254  
192 112 50       154 $json->{tags} = $self->tags if @{$self->tags};
  112         1285  
193 112 50       1257 $json->{signature} = $self->signature if $self->signature;
194              
195 112         1310 return $json;
196              
197             }
198              
199             1;
200              
201             =encoding utf-8
202              
203             =head1 NAME
204              
205             SBOM::CycloneDX::Component - Component
206              
207             =head1 SYNOPSIS
208              
209             SBOM::CycloneDX::Component->new();
210              
211              
212             =head1 DESCRIPTION
213              
214             L
215              
216             =head2 METHODS
217              
218             L inherits all methods from L
219             and implements the following new ones.
220              
221             =over
222              
223             =item SBOM::CycloneDX::Component->new( %PARAMS )
224              
225             Properties:
226              
227             =over
228              
229             =item * C, [Deprecated] This will be removed in a future version. Use
230             "authors" or "manufacturer" methods instead.
231             The person(s) or organization(s) that authored the component
232              
233             =item * C, The person(s) who created the component.
234             Authors are common in components created through manual processes.
235             Components created through automated means may have "manufacturer" method
236             instead.
237              
238             =item * C, An identifier which can be used to reference the component
239             elsewhere in the BOM. Every bom-ref must be unique within the BOM.
240             Value SHOULD not start with the BOM-Link intro 'urn:cdx:' to avoid conflicts
241             with BOM-Links.
242              
243             =item * C, A list of software and hardware components included in
244             the parent component. This is not a dependency tree. It provides a way to
245             specify a hierarchical representation of component assemblies, similar to
246             system → subsystem → parts assembly in physical supply chains.
247              
248             =item * C, A copyright notice informing users of the underlying
249             claims to copyright ownership in a published work.
250              
251             =item * C, Asserts the identity of the component using CPE. The CPE must
252             conform to the CPE 2.2 or 2.3 specification. See L.
253             Refer to "evidence->identity" method to optionally provide evidence that
254             substantiates the assertion of the component's identity.
255              
256             =item * C, Cryptographic Properties
257              
258             =item * C, This object SHOULD be specified for any component of type
259             `data` and must not be specified for other component types.
260              
261             =item * C, Specifies a description for the component
262              
263             =item * C, Provides the ability to document evidence collected
264             through various forms of extraction or analysis.
265              
266             =item * C, External references provide a way to document
267             systems, sites, and information that may be relevant but are not included
268             with the BOM. They may also establish specific relationships within or
269             external to the BOM.
270              
271             =item * C, The grouping name or identifier. This will often be a
272             shortened, single name of the company or project that produced the
273             component, or the source package or domain name. Whitespace and special
274             characters should be avoided. Examples include: apache, org.apache.commons,
275             and apache.org.
276              
277             =item * C, The hashes of the component.
278              
279             =item * C, Determine whether this component is external.
280             An external component is one that is not part of an assembly, but is expected
281             to be provided by the environment, regardless of the component's C. This
282             setting can be useful for distinguishing which components are bundled with the
283             product and which can be relied upon to be present in the deployment
284             environment.
285             This may be set to C for runtime components only.
286             For L C, it must be set to C.
287              
288             =item * C, Component License(s)
289              
290             =item * C, The organization that created the component.
291             Manufacturer is common in components created through automated processes.
292             Components created through manual means may have `@.authors` instead.
293              
294             =item * C, The mime-type of the component. When used on file
295             components, the mime-type can provide additional context about the kind of file
296             being represented, such as an image, font, or executable. Some library or
297             framework components may also have an associated mime-type.
298              
299             =item * C, AI/ML Model Card
300              
301             =item * C, [Deprecated] This will be removed in a future version.
302             Use the pedigree element instead to supply information on exactly how the
303             component was modified. A boolean value indicating if the component has
304             been modified from the original. A value of true indicates the component is
305             a derivative of the original. A value of false indicates the component has
306             not been modified from the original.
307              
308             =item * C, The name of the component. This will often be a shortened,
309             single name of the component.
310              
311             Examples: commons-lang3 and jquery
312              
313             =item * C, Asserts the identity of the component using the
314             OmniBOR Artifact ID. The OmniBOR, if specified, must be valid and conform
315             to the specification defined at: L.
316             Refer to "evidence->identity" method to optionally provide evidence that
317             substantiates the assertion of the component's identity.
318              
319             =item * C, Component Patent(s).
320              
321             Patent Assertions. A list of assertions made regarding patents associated
322             with this component or service. Assertions distinguish between ownership,
323             licensing, and other relevant interactions with patents.
324              
325             =item * C, Component pedigree is a way to document complex supply
326             chain scenarios where components are created, distributed, modified,
327             redistributed, combined with other components, etc. Pedigree supports
328             viewing this complex chain from the beginning, the end, or anywhere in the
329             middle. It also provides a way to document variants where the exact
330             relation may not be known.
331              
332             =item * C, Provides the ability to document properties in a
333             name-value store. This provides flexibility to include data not officially
334             supported in the standard without having to use additional namespaces or
335             create extensions. Unlike key-value stores, properties support duplicate
336             names, each potentially having different values. Property names of interest
337             to the general public are encouraged to be registered in the CycloneDX
338             Property Taxonomy (L).
339             Formal registration is optional.
340              
341             =item * C, The person(s) or organization(s) that published the
342             component
343              
344             =item * C, Asserts the identity of the component using package-url
345             (purl). The purl, if specified, must be valid and conform to the
346             specification defined at L).
347             Refer to "evidence->identity" method to optionally provide evidence
348             that substantiates the assertion of the component's identity.
349              
350             =item * C, Specifies release notes.
351              
352             =item * C, Specifies the scope of the component. If scope is not
353             specified, 'required' scope SHOULD be assumed by the consumer of the BOM.
354              
355             =item * C, Enveloped signature in JSON Signature Format
356             (JSF) (L).
357              
358             =item * C, The organization that supplied the component. The
359             supplier may often be the manufacturer, but may also be a distributor or
360             repackager.
361              
362             =item * C, Asserts the identity of the component using the Software
363             Heritage persistent identifier (SWHID). The SWHID, if specified, must be
364             valid and conform to the specification defined at:
365             L.
366             Refer to "evidence->identity" method to optionally provide evidence
367             that substantiates the assertion of the component's identity.
368              
369             =item * C, Asserts the identity of the component using ISO-IEC 19770-2
370             Software Identification (SWID) Tags (L).
371             Refer to "evidence->identity" method to optionally provide evidence that substantiates
372             the assertion of the component's identity.
373              
374             =item * C, Tags
375              
376             =item * C, Specifies the type of component. For software components,
377             classify as application if no more specific appropriate classification is
378             available or cannot be determined for the component.
379              
380             =item * C, The component version. The version should ideally comply
381             with semantic versioning but is not enforced.
382              
383             =item * C, Component Version Range. For an external component,
384             this specifies the accepted version range.
385             The value must adhere to the Package URL Version Range syntax (vers), as
386             defined at L
387             May only be used if C is set to C.
388             Must be used exclusively, either C or C, but not both.
389              
390             =back
391              
392             =item $component->author
393              
394             =item $component->authors
395              
396             =item $component->bom_ref
397              
398             =item $component->components
399              
400             =item $component->copyright
401              
402             =item $component->cpe
403              
404             =item $component->crypto_properties
405              
406             =item $component->data
407              
408             =item $component->description
409              
410             =item $component->evidence
411              
412             =item $component->external_references
413              
414             =item $component->group
415              
416             =item $component->is_external
417              
418             =item $component->hashes
419              
420             =item $component->licenses
421              
422             =item $component->manufacturer
423              
424             =item $component->mime_type
425              
426             =item $component->model_card
427              
428             =item $component->modified
429              
430             =item $component->name
431              
432             =item $component->omnibor_id
433              
434             =item $component->patent_assertions
435              
436             =item $component->pedigree
437              
438             =item $component->properties
439              
440             =item $component->publisher
441              
442             =item $component->purl
443              
444             =item $component->release_notes
445              
446             =item $component->scope
447              
448             =item $component->signature
449              
450             =item $component->supplier
451              
452             =item $component->swhid
453              
454             =item $component->swid
455              
456             =item $component->tags
457              
458             =item $component->type
459              
460             =item $component->version
461              
462             =item $component->version_range
463              
464             =back
465              
466              
467             =head1 SUPPORT
468              
469             =head2 Bugs / Feature Requests
470              
471             Please report any bugs or feature requests through the issue tracker
472             at L.
473             You will be notified automatically of any progress on your issue.
474              
475             =head2 Source Code
476              
477             This is open source software. The code repository is available for
478             public review and contribution under the terms of the license.
479              
480             L
481              
482             git clone https://github.com/giterlizzi/perl-SBOM-CycloneDX.git
483              
484              
485             =head1 AUTHOR
486              
487             =over 4
488              
489             =item * Giuseppe Di Terlizzi
490              
491             =back
492              
493              
494             =head1 LICENSE AND COPYRIGHT
495              
496             This software is copyright (c) 2025-2026 by Giuseppe Di Terlizzi.
497              
498             This is free software; you can redistribute it and/or modify it under
499             the same terms as the Perl 5 programming language system itself.
500              
501             =cut