Dist-Zilla-Plugin-CycloneDX
view release on metacpan or search on metacpan
CPAN-META/Dist-Zilla-Plugin-CycloneDX-0.001.cdx.json view on Meta::CPAN
{
"type": "vcs",
"url": "https://github.com/Leont/dist-zilla-plugin-cyclonedx"
}
],
"group": "CPAN",
"name": "Dist-Zilla-Plugin-CycloneDX",
"type": "library"
},
{
"bom-ref": "SBOM-CycloneDX@1.09",
"description": "Perl distribution for CycloneDX",
"externalReferences": [
{
"type": "website",
"url": "https://metacpan.org/pod/SBOM::CycloneDX"
},
{
"type": "documentation",
"url": "https://metacpan.org/dist/SBOM-CycloneDX"
},
{
"type": "vcs",
"url": "https://github.com/giterlizzi/perl-SBOM-CycloneDX"
},
{
"type": "issue-tracker",
"url": "https://github.com/giterlizzi/perl-SBOM-CycloneDX/issues"
},
{
"type": "license",
"url": "https://github.com/giterlizzi/perl-SBOM-CycloneDX/blob/main/LICENSE"
},
{
"type": "release-notes",
"url": "https://github.com/giterlizzi/perl-SBOM-CycloneDX/blob/main/Changes"
},
{
"type": "distribution",
"url": "https://metacpan.org/dist/SBOM-CycloneDX"
}
],
"group": "CPAN",
"licenses": [
{
"license": {
"id": "Artistic-2.0"
}
}
],
"name": "SBOM-CycloneDX",
"type": "library",
"version": "1.09"
}
]
}
},
"serialNumber": "urn:uuid:a489e437-c1e7-4f3e-847c-6fb7ce1a0ee5",
"specVersion": "1.7",
"version": 1
}
},
"runtime" : {
"requires" : {
"Carp" : "0",
"Dist::Zilla::File::InMemory" : "0",
"Dist::Zilla::Role::FileGatherer" : "0",
"Dist::Zilla::Role::Plugin" : "0",
"Moose" : "0",
"Moose::Role" : "0",
"MooseX::Types::Moose" : "0",
"SBOM::CycloneDX" : "1.08",
"SBOM::CycloneDX::Component" : "0",
"SBOM::CycloneDX::ExternalReference" : "0",
"SBOM::CycloneDX::License" : "0",
"SBOM::CycloneDX::Metadata" : "0",
"SBOM::CycloneDX::Metadata::Lifecycle" : "0",
"SBOM::CycloneDX::OrganizationalContact" : "0",
"SBOM::CycloneDX::Tools" : "0",
"SBOM::CycloneDX::Util" : "0",
"Time::Piece" : "0",
"URI::PackageURL" : "0",
"autovivification" : "0",
"experimental" : "0",
"namespace::autoclean" : "0",
"perl" : "5.020"
}
},
"test" : {
"requires" : {
file: lib/Dist/Zilla/Role/CycloneDXSource.pm
version: '0.001'
requires:
Carp: '0'
Dist::Zilla::File::InMemory: '0'
Dist::Zilla::Role::FileGatherer: '0'
Dist::Zilla::Role::Plugin: '0'
Moose: '0'
Moose::Role: '0'
MooseX::Types::Moose: '0'
SBOM::CycloneDX: '1.08'
SBOM::CycloneDX::Component: '0'
SBOM::CycloneDX::ExternalReference: '0'
SBOM::CycloneDX::License: '0'
SBOM::CycloneDX::Metadata: '0'
SBOM::CycloneDX::Metadata::Lifecycle: '0'
SBOM::CycloneDX::OrganizationalContact: '0'
SBOM::CycloneDX::Tools: '0'
SBOM::CycloneDX::Util: '0'
Time::Piece: '0'
URI::PackageURL: '0'
autovivification: '0'
experimental: '0'
namespace::autoclean: '0'
perl: '5.020'
resources:
IRC:
url: irc://irc.perl.org/#distzilla
bugtracker: https://github.com/Leont/dist-zilla-plugin-cyclonedx/issues
lib/Dist/Zilla/Plugin/CycloneDX.pm view on Meta::CPAN
use MooseX::Types::Moose 'Str';
use namespace::autoclean;
use experimental qw/signatures postderef lexical_subs/;
no autovivification;
use Carp;
use Dist::Zilla::File::InMemory;
use Time::Piece;
use URI::PackageURL;
use SBOM::CycloneDX 1.08;
use SBOM::CycloneDX::Component;
use SBOM::CycloneDX::ExternalReference;
use SBOM::CycloneDX::License;
use SBOM::CycloneDX::Metadata;
use SBOM::CycloneDX::Metadata::Lifecycle;
use SBOM::CycloneDX::OrganizationalContact;
use SBOM::CycloneDX::Tools;
use SBOM::CycloneDX::Util qw/cpan_meta_to_spdx_license cyclonedx_component/;
has filename => (
is => 'ro',
isa => Str,
lazy => 1,
default => sub($self) {
my $name = $self->zilla->name;
my $version = $self->zilla->version;
return "CPAN-META/$name-$version.cdx.json";
}
lib/Dist/Zilla/Plugin/CycloneDX.pm view on Meta::CPAN
homepage => 'website',
x_mailing_list => 'mailing-list',
x_MailingList => 'mailing-list',
x_mailinglist => 'mailing-list',
x_mastodon => 'social',
x_twitter => 'social',
x_wiki => 'documentation',
);
my sub make_externs($module, $resources) {
my @externs = SBOM::CycloneDX::ExternalReference->new(type => 'documentation', url => "https://metacpan.org/pod/$module");
if (my $tracker = $resources->{bugtracker}{web}) {
push @externs, SBOM::CycloneDX::ExternalReference->new(type => 'issue-tracker', url => $tracker);
}
if (my $repo = $resources->{repository}{web}) {
push @externs, SBOM::CycloneDX::ExternalReference->new(type => 'vcs', url => $repo);
}
if (my $irc = $resources->{x_IRC}) {
my $url = ref $irc ? $irc->{url} : $irc;
push @externs, SBOM::CycloneDX::ExternalReference->new(type => 'chat', url => $url);
}
for my $key (keys %simple_resource) {
if (my $url = $resources->{$key}) {
push @externs, SBOM::CycloneDX::ExternalReference->new(type => $simple_resource{$key}, url => $url);
}
}
return sort { $a->type cmp $b->type } @externs;
}
sub make_purl($name, $version) {
return URI::PackageURL->new(
type => 'cpan',
namespace => '',
name => $name,
version => $version,
);
}
my sub make_root_component($meta) {
my $purl = make_purl($meta->{name}, $meta->{version});
my $bom_ref = sprintf '%s@%s', $meta->{name}, $meta->{version};
my $module = $meta->{name} =~ s/-/::/gr;
my @externs = make_externs($module, $meta->{resources});
return SBOM::CycloneDX::Component->new(
type => 'library',
group => 'CPAN',
name => $meta->{name},
version => $meta->{version},
description => $meta->{abstract},
bom_ref => $bom_ref,
purl => $purl,
external_references => \@externs,
);
}
my sub to_organization($author) {
my ($name, $email) = $author =~ /([^<]+?)\s+<([^>]+)>/ or croak "Could not parse author '$author'";
return SBOM::CycloneDX::OrganizationalContact->new(name => $name, email => $email);
}
my sub make_licenses($meta) {
if (my $spdx_expr = $meta->{x_spdx_expression}) {
return SBOM::CycloneDX::License->new($spdx_expr);
} else {
my @raw = map { cpan_meta_to_spdx_license($_) } $meta->{license}->@*;
return map { SBOM::CycloneDX::License->new($_) } @raw;
}
}
my %self_resources = (
bugtracker => {
web => 'https://github.com/Leont/dist-zilla-plugin-cyclonedx/issues',
},
repository => {
web => 'https://github.com/Leont/dist-zilla-plugin-cyclonedx',
},
lib/Dist/Zilla/Plugin/CycloneDX.pm view on Meta::CPAN
my %args = (
type => 'library',
group => 'CPAN',
name => $dist,
external_references => \@externs,
);
if ($version) {
$args{version} = $version;
$args{purl} = make_purl($dist, $version);
}
return SBOM::CycloneDX::Component->new(%args);
}
my sub make_tools($plugins) {
my $distname = __PACKAGE__ =~ s/::/-/gr;
my @tools = make_tool_component(__PACKAGE__, $distname, \%self_resources);
push @tools, map { make_tool_component(ref($_), $_->cyclonedx_dist, $_->cyclonedx_resources) } $plugins->@*;
push @tools, cyclonedx_component;
return SBOM::CycloneDX::Tools->new(components => \@tools);
}
my sub make_metadata($meta, $plugins) {
my $component = make_root_component($meta);
my @authors = map { to_organization($_) } $meta->{author}->@*;
my $timestamp = SBOM::CycloneDX::Timestamp->new($ENV{TIMESTAMP} // scalar gmtime);
my @licenses = make_licenses($meta);
my @lifecycle = SBOM::CycloneDX::Metadata::Lifecycle->new(phase => 'pre-build');
my $tools = make_tools($plugins);
return SBOM::CycloneDX::Metadata->new(
component => $component,
authors => \@authors,
timestamp => $timestamp,
licenses => \@licenses,
lifecycles => \@lifecycle,
tools => $tools,
);
}
sub gather_files($self) {
my @plugins = $self->zilla->plugins_with(-CycloneDXSource)->@*;
my $metadata = make_metadata($self->zilla->distmeta, \@plugins);
my $bom = SBOM::CycloneDX->new(metadata => $metadata);
for my $plugin (@plugins) {
$plugin->add_to_bom($bom);
}
local $SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /Net::IDN::Encode/ };
if (my @errors = $bom->validate) {
croak "Errors found in BOM: @errors";
}
$self->add_file(Dist::Zilla::File::InMemory->new(
name => $self->filename,
encoded_content => $bom->to_string,
));
return;
}
lib/Dist/Zilla/Plugin/CycloneDX.pm view on Meta::CPAN
=head1 NAME
Dist::Zilla::Plugin::CycloneDX - CycloneDX files in dzil generated dists
=head1 VERSION
version 0.001
=head1 DESCRIPTION
This module adds a CycloneDX SBOM file to your distribution. By default this will only contain information that's already contained in the META files, but this can be extended by other plugins that implement the L<CycloneDXSource|Dist::Zilla::Role::C...
=head1 AUTHOR
Leon Timmermans <fawaka@gmail.com>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Leon Timmermans.
This is free software; you can redistribute it and/or modify it under
lib/Dist/Zilla/Role/CycloneDXSource.pm view on Meta::CPAN
sub cyclonedx_dist($self) {
return ref($self) =~ s/::/-/gr;
}
sub cyclonedx_resources($self) {
return {};
}
1;
# ABSTRACT: A role for plugins that adds information to the CycloneDX SBOM
__END__
=pod
=encoding UTF-8
=head1 NAME
Dist::Zilla::Role::CycloneDXSource - A role for plugins that adds information to the CycloneDX SBOM
=head1 VERSION
version 0.001
=head1 DESCRIPTION
=head1 METHODS
=head2 add_to_bom
This method, taking a single L<SBOM::CycloneDX|SBOM::CycloneDX> object as argument. This can be manipulated, typically to add information to it.
=head1 AUTHOR
Leon Timmermans <fawaka@gmail.com>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Leon Timmermans.
This is free software; you can redistribute it and/or modify it under
( run in 1.814 second using v1.01-cache-2.11-cpan-9581c071862 )