File Coverage

blib/lib/SBOM/CycloneDX/License.pm
Criterion Covered Total %
statement 72 82 87.8
branch 20 34 58.8
condition 8 14 57.1
subroutine 19 20 95.0
pod 1 2 50.0
total 120 152 78.9


line stmt bran cond sub pod time code
1             package SBOM::CycloneDX::License;
2              
3 16     16   377 use 5.010001;
  16         95  
4 16     16   105 use strict;
  16         67  
  16         508  
5 16     16   81 use warnings;
  16         36  
  16         992  
6 16     16   117 use utf8;
  16         31  
  16         202  
7              
8 16     16   626 use SBOM::CycloneDX::BomRef;
  16         37  
  16         614  
9 16     16   84 use SBOM::CycloneDX::Enum;
  16         37  
  16         1098  
10 16     16   110 use SBOM::CycloneDX::List;
  16         45  
  16         489  
11 16     16   9440 use SBOM::CycloneDX::License::Licensing;
  16         89  
  16         891  
12              
13 16     16   181 use Carp;
  16         43  
  16         1719  
14 16     16   172 use List::Util qw(first);
  16         58  
  16         1275  
15 16     16   109 use Types::Standard qw(Str Enum InstanceOf);
  16         39  
  16         162  
16 16     16   54690 use Types::TypeTiny qw(ArrayLike);
  16         45  
  16         97  
17              
18 16     16   8697 use Moo;
  16         36  
  16         107  
19 16     16   7795 use namespace::autoclean;
  16         42  
  16         139  
20              
21 16   50 16   1745 use constant DEBUG => $ENV{SBOM_DEBUG} || 0;
  16         38  
  16         23443  
22              
23             # TODO Incomplete
24              
25             around BUILDARGS => sub {
26              
27             my ($orig, $class, @args) = @_;
28              
29             if (@args == 1 && defined $args[0] && !ref $args[0]) {
30              
31             my $license = $args[0];
32             $license =~ s/^\s+|\s+$//g;
33              
34             if ($license =~ /(\bWITH\b|\bOR\b|\bAND\b)/i) {
35             return {expression => $license};
36             }
37             else {
38             return {id => $license};
39             }
40             }
41              
42             return $class->$orig(@args);
43              
44             };
45              
46             extends 'SBOM::CycloneDX::Base';
47              
48             sub BUILD {
49 22     22 0 511 my ($self, $args) = @_;
50 22 50 66     562 Carp::croak('"id" and "name" cannot be used at the same time') if exists $args->{id} && exists $args->{name};
51             }
52              
53             has bom_ref => (
54             is => 'rw',
55             isa => InstanceOf ['SBOM::CycloneDX::BomRef'],
56             coerce => sub { ref($_[0]) ? $_[0] : SBOM::CycloneDX::BomRef->new($_[0]) }
57             );
58              
59             has id => (is => 'rw', isa => Str, trigger => 1);
60             has name => (is => 'rw', isa => Str);
61             has acknowledgement => (is => 'rw', isa => Enum [qw(declared concluded)]);
62             has text => (is => 'rw', isa => InstanceOf ['SBOM::CycloneDX::Attachment']);
63             has url => (is => 'rw', isa => Str, trigger => 1);
64             has expression => (is => 'rw', isa => Str); # TODO check SPDX expression
65              
66             has expression_details => (
67             is => 'rw',
68             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::License::ExpressionDetail']],
69             default => sub { SBOM::CycloneDX::List->new }
70             );
71              
72             has licensing => (
73             is => 'rw',
74             isa => InstanceOf ['SBOM::CycloneDX::License::Licensing'],
75             default => sub { SBOM::CycloneDX::License::Licensing->new }
76             );
77              
78             has properties => (
79             is => 'rw',
80             isa => ArrayLike [InstanceOf ['SBOM::CycloneDX::Property']],
81             default => sub { SBOM::CycloneDX::List->new }
82             );
83              
84              
85             sub _trigger_id {
86              
87 18     18   890 my ($self) = @_;
88              
89 18 50 33     439 if ($self->id && $self->id =~ /(\bWITH\b|\bOR\b|\bAND\b)/i) {
90 0         0 DEBUG and say STDERR '-- Detected SPDX expression';
91 0         0 $self->expression($self->id);
92 0         0 $self->{id} = undef;
93             }
94              
95 18 50 33     1022 if ($self->id && $self->id =~ /^(NOASSERTION|NONE)$/) {
96 0         0 DEBUG and say STDERR "-- Detected $1 license identifier (unset license identifier)";
97 0         0 $self->{id} = undef;
98             }
99              
100             }
101              
102              
103             sub _trigger_url {
104 0     0   0 my ($self) = @_;
105 0 0       0 $self->url('https://opensource.org/license/' . $self->id) if $self->url eq '1';
106             }
107              
108             sub TO_JSON {
109              
110 301     301 1 1024 my $self = shift;
111              
112 301         576 my $json = {};
113              
114 301 50       7178 if ($self->expression) {
115 0         0 $json->{expression} = $self->expression;
116 0 0       0 $json->{acknowledgement} = $self->acknowledgement if $self->acknowledgement;
117 0 0       0 $json->{'bom-ref'} = $self->bom_ref if $self->bom_ref;
118             }
119             else {
120              
121 301         7547 my $spdx_license = $self->id;
122 301         7155 my $license_name = $self->name;
123              
124 301 100 100 118609   3248 if (defined $spdx_license and not first { $_ eq $spdx_license } @{SBOM::CycloneDX::Enum->SPDX_LICENSES()}) {
  118609         161689  
  247         3744  
125 148         271 DEBUG and say STDERR "-- SPDX license not found ($spdx_license)";
126             }
127              
128 301         1966 $json->{license} = {};
129              
130 301 100       1157 $json->{license}->{id} = $spdx_license if $spdx_license;
131 301 100       786 $json->{license}->{name} = $license_name if $license_name;
132              
133 301 100       8376 $json->{license}->{'bom-ref'} = $self->bom_ref if $self->bom_ref;
134 301 100       7820 $json->{license}->{acknowledgement} = $self->acknowledgement if $self->acknowledgement;
135 301 100       7791 $json->{license}->{text} = $self->text if $self->text;
136 301 50       7404 $json->{license}->{url} = $self->url if $self->url;
137 301 50       1993 $json->{license}->{properties} = $self->properties if @{$self->properties};
  301         5952  
138 301 50       584 $json->{license}->{licensing} = $self->licensing if %{$self->licensing->TO_JSON};
  301         6105  
139 301 50       631 $json->{license}->{expressionDetails} = $self->expression_details if @{$self->expression_details};
  301         5949  
140              
141             }
142              
143 301         5805 return $json;
144              
145             }
146              
147             1;
148              
149             =encoding utf-8
150              
151             =head1 NAME
152              
153             SBOM::CycloneDX::License - Specifies the details and attributes related to a software license
154              
155             =head1 SYNOPSIS
156              
157             # SPDX license
158              
159             $license = SBOM::CycloneDX::License->new(
160             id => 'Apache-2.0'
161             );
162              
163             # or
164              
165             $license = SBOM::CycloneDX::License->new('MIT');
166              
167             # SPDX license expression
168              
169             $license = SBOM::CycloneDX::License->new(
170             expression => 'MIT AND (LGPL-2.1-or-later OR BSD-3-Clause)'
171             );
172              
173             # or
174              
175             $license = SBOM::CycloneDX::License->new('MIT AND (LGPL-2.1-or-later OR BSD-3-Clause)');
176              
177             # Non-SPDX license
178              
179             $license = SBOM::CycloneDX::License->new(
180             name => 'Acme Software License'
181             );
182              
183              
184             =head1 DESCRIPTION
185              
186             L specifies the details and attributes related to a software
187             license.
188              
189             It can either include a valid SPDX license identifier or a named license,
190             along with additional properties such as license acknowledgment, comprehensive
191             commercial licensing information, and the full text of the license.
192              
193             =head2 METHODS
194              
195             L inherits all methods from L
196             and implements the following new ones.
197              
198             =over
199              
200             =item SBOM::CycloneDX::License->new( $id | $expression | %PARAMS )
201              
202             Properties:
203              
204             =over
205              
206             =item * C, An identifier which can be used to reference the license
207             elsewhere in the BOM. Every bom-ref must be unique within the BOM.
208              
209             Value SHOULD not start with the BOM-Link intro C to avoid conflicts with BOM-Links.
210              
211             =item * C, A valid SPDX license identifier. If specified, this value must be
212             one of the enumeration of valid SPDX license identifiers defined in L C.
213              
214             =item * C, A tuple of exactly one SPDX License Expression.
215              
216             Refer to L for syntax requirements.
217              
218             =item * C, Details for parts of the C.
219              
220             See L
221              
222             =item * C, The name of the license. This may include the name of a commercial
223             or proprietary license or an open source license that may not be defined by SPDX.
224              
225             =item * C,
226              
227             =item * C, An way to include the textual content of a license.
228             See L
229              
230             =item * C, The URL to the license file. If C<1> is provided, the license URL is
231             automatically generated.
232              
233             =item * C, Declared licenses and concluded licenses represent two different
234             stages in the licensing process within software development. Declared licenses refer
235             to the initial intention of the software authors regarding the licensing terms
236             under which their code is released. On the other hand, concluded licenses are the
237             result of a comprehensive analysis of the project's codebase to identify and confirm
238             the actual licenses of the components used, which may differ from the initially
239             declared licenses. While declared licenses provide an upfront indication of the
240             licensing intentions, concluded licenses offer a more thorough understanding of
241             the actual licensing within a project, facilitating proper compliance and risk
242             management. Observed licenses are defined in C<$bom->evidence->licenses>. Observed
243             licenses form the evidence necessary to substantiate a concluded license.
244              
245             See L
246              
247              
248             =item * C, Provides the ability to document properties in a name-value
249             store. This provides flexibility to include data not officially supported in the
250             standard without having to use additional namespaces or create extensions.
251             Unlike key-value stores, properties support duplicate names, each potentially
252             having different values. Property names of interest to the general public are
253             encouraged to be registered in the CycloneDX Property Taxonomy. Formal
254             registration is optional. See L
255              
256             =back
257              
258             =item $license->bom_ref
259              
260             =item $license->id
261              
262             # SPDX license
263              
264             $license = SBOM::CycloneDX::License->new(
265             id => 'Apache-2.0'
266             );
267              
268             # or
269              
270             $license = SBOM::CycloneDX::License->new('MIT');
271              
272             =item $license->name
273              
274             =item $license->acknowledgement
275              
276             =item $license->text
277              
278             $license->text(SBOM::CycloneDX::Attachment(file => '/path/LICENSE.md'));
279              
280             =item $license->url
281              
282             =item $license->expression
283              
284             # SPDX license expression
285              
286             $license = SBOM::CycloneDX::License->new(
287             expression => 'MIT AND (LGPL-2.1-or-later OR BSD-3-Clause)'
288             );
289              
290             # or
291              
292             $license = SBOM::CycloneDX::License->new('MIT AND (LGPL-2.1-or-later OR BSD-3-Clause)');
293              
294             =item $license->expression_details
295              
296             =item $license->licensing
297              
298             $license->licensing->alt_ids(['acme', 'acme-license']);
299              
300              
301             $licensing = SBOM::CycloneDX::License::Licensing->new(
302             alt_ids => ['acme', 'acme-license'],
303             purchase_order => 'PO-12345',
304             license_types => ['appliance'],
305             );
306              
307             $license->licensing($licensing);
308              
309             =item $license->properties
310              
311             $license->properties->add(SBOM::CycloneDX::Property->new(name => 'foo', value => 'bar'));
312              
313             =back
314              
315             =head1 SUPPORT
316              
317             =head2 Bugs / Feature Requests
318              
319             Please report any bugs or feature requests through the issue tracker
320             at L.
321             You will be notified automatically of any progress on your issue.
322              
323             =head2 Source Code
324              
325             This is open source software. The code repository is available for
326             public review and contribution under the terms of the license.
327              
328             L
329              
330             git clone https://github.com/giterlizzi/perl-SBOM-CycloneDX.git
331              
332              
333             =head1 AUTHOR
334              
335             =over 4
336              
337             =item * Giuseppe Di Terlizzi
338              
339             =back
340              
341              
342             =head1 LICENSE AND COPYRIGHT
343              
344             This software is copyright (c) 2025-2026 by Giuseppe Di Terlizzi.
345              
346             This is free software; you can redistribute it and/or modify it under
347             the same terms as the Perl 5 programming language system itself.
348              
349             =cut