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   343 use 5.010001;
  16         62  
4 16     16   94 use strict;
  16         34  
  16         498  
5 16     16   79 use warnings;
  16         29  
  16         906  
6 16     16   89 use utf8;
  16         62  
  16         110  
7              
8 16     16   9091 use SBOM::CycloneDX::BomRef;
  16         89  
  16         863  
9 16     16   9617 use SBOM::CycloneDX::Enum;
  16         109  
  16         1518  
10 16     16   133 use SBOM::CycloneDX::List;
  16         36  
  16         655  
11              
12 16     16   108 use JSON::PP;
  16         35  
  16         1614  
13 16     16   108 use Types::Standard qw(Str StrMatch Bool Enum InstanceOf HashRef);
  16         35  
  16         221  
14 16     16   47829 use Types::TypeTiny qw(ArrayLike);
  16         41  
  16         151  
15 16     16   19656 use URI::PackageURL;
  16         246198  
  16         1392  
16 16     16   8845 use URI::VersionRange;
  16         272138  
  16         1432  
17              
18 16     16   169 use Moo;
  16         37  
  16         195  
19 16     16   8300 use namespace::autoclean;
  16         40  
  16         202  
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 => Str); # 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   6 my $purl = shift;
138              
139 2 50       40 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 392     392 1 1512 my $self = shift;
156              
157 392         10920 my $json = {type => $self->type, name => $self->name};
158              
159              
160 392 50       22007 $json->{'mime-type'} = $self->mime_type if $self->mime_type;
161 392 100       11351 $json->{'bom-ref'} = $self->bom_ref if $self->bom_ref;
162 392 50       10063 $json->{supplier} = $self->supplier if $self->supplier;
163 392 50       10408 $json->{manufacturer} = $self->manufacturer if $self->manufacturer;
164 392 50       2665 $json->{authors} = $self->authors if @{$self->authors};
  392         8201  
165 392 100       8528 $json->{author} = $self->author if $self->author;
166 392 100       11555 $json->{publisher} = $self->publisher if $self->publisher;
167 392 100       11638 $json->{group} = $self->group if $self->group;
168 392 100       11373 $json->{version} = $self->version if $self->version;
169 392 50       18396 $json->{versionRange} = $self->version_range->to_string if $self->version_range;
170 392 50       10138 $json->{isExternal} = $self->is_external if JSON::PP::is_bool($self->is_external);
171 392 100       13200 $json->{description} = $self->description if $self->description;
172 392 100       13040 $json->{scope} = $self->scope if $self->scope;
173 392 100       3140 $json->{hashes} = $self->hashes if @{$self->hashes};
  392         8345  
174 392 100       1396 $json->{licenses} = $self->licenses if @{$self->licenses};
  392         8405  
175 392 50       10800 $json->{copyright} = $self->copyright if $self->copyright;
176 392 50       2860 $json->{patentAssertions} = $self->patent_assertions if @{$self->patent_assertions};
  392         8002  
177 392 50       8856 $json->{cpe} = $self->cpe if $self->cpe;
178 392 100       10102 $json->{purl} = $self->purl->to_string if $self->purl;
179 392 50       12229 $json->{omniborId} = $self->omnibor_id if @{$self->omnibor_id};
  392         8986  
180 392 50       766 $json->{swhid} = $self->swhid if @{$self->swhid};
  392         8353  
181 392 100       8184 $json->{swid} = $self->swid if $self->swid;
182 392 50       11509 $json->{modified} = $self->modified if JSON::PP::is_bool($self->modified);
183 392 50       12484 $json->{pedigree} = $self->pedigree if $self->pedigree;
184 392 50       2681 $json->{externalReferences} = $self->external_references if @{$self->external_references};
  392         8300  
185 392 50       885 $json->{components} = $self->components if @{$self->components};
  392         8386  
186 392 50       8405 $json->{evidence} = $self->evidence if $self->evidence;
187 392 50       10102 $json->{releaseNotes} = $self->release_notes if $self->release_notes;
188 392 50       9741 $json->{modelCard} = $self->model_card if $self->model_card;
189 392 50       2670 $json->{data} = $self->data if @{$self->data};
  392         8497  
190 392 50       8533 $json->{cryptoProperties} = $self->crypto_properties if $self->crypto_properties;
191 392 50       2738 $json->{properties} = $self->properties if @{$self->properties};
  392         8309  
192 392 50       726 $json->{tags} = $self->tags if @{$self->tags};
  392         8377  
193 392 50       8142 $json->{signature} = $self->signature if $self->signature;
194              
195 392         7373 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