Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make cpanminus secure by default #1919

Merged
merged 1 commit into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions author/cpanm/cpanm.PL
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ cpanm - get, unpack build and install modules from CPAN

cpanm Test::More # install Test::More
cpanm MIYAGAWA/Plack-0.99_05.tar.gz # full distribution path
cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL
cpanm https://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL
cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # install from a local file
cpanm --interactive Task::Kensho # Configure interactively
cpanm . # install from local directory
cpanm --installdeps . # install all the deps for the current directory
cpanm -L extlib Plack # install Plack and all non-core deps into extlib
cpanm --mirror http://cpan.cpantesters.org/ DBI # use the fast-syncing mirror
cpanm --from https://cpan.metacpan.org/ Plack # use only the HTTPS mirror
cpanm --mirror https://cpan.cpantesters.org/ DBI # use the fast-syncing mirror
cpanm --from https://cpan.metacpan.org/ Plack # use a different mirror

=head1 COMMANDS

Expand All @@ -44,7 +44,7 @@ will all work as you expect.
cpanm Plack/Request.pm
cpanm MIYAGAWA/Plack-1.0000.tar.gz
cpanm /path/to/Plack-1.0000.tar.gz
cpanm http://cpan.metacpan.org/authors/id/M/MI/MIYAGAWA/Plack-0.9990.tar.gz
cpanm https://cpan.metacpan.org/authors/id/M/MI/MIYAGAWA/Plack-0.9990.tar.gz
cpanm git://github.com/plack/Plack.git

Additionally, you can use the notation using C<~> and C<@> to specify
Expand Down Expand Up @@ -197,7 +197,7 @@ the behaviour from before version 1.7023
=item --mirror

Specifies the base URL for the CPAN mirror to use, such as
C<http://cpan.cpantesters.org/> (you can omit the trailing slash). You
C<https://cpan.cpantesters.org/> (you can omit the trailing slash). You
can specify multiple mirror URLs by repeating the command line option.

You can use a local directory that has a CPAN mirror structure
Expand All @@ -208,7 +208,7 @@ scheme), it is considered as a file scheme as well.
cpanm --mirror file:///path/to/mirror
cpanm --mirror ~/minicpan # Because shell expands ~ to /home/user

Defaults to C<http://www.cpan.org/>.
Defaults to C<https://www.cpan.org/>.

=item --mirror-only

Expand Down Expand Up @@ -240,7 +240,7 @@ B<Tip:> It might be useful if you name these options with your shell
aliases, like:

alias minicpanm='cpanm --from ~/minicpan'
alias darkpan='cpanm --from http://mycompany.example.com/DPAN'
alias darkpan='cpanm --from https://mycompany.example.com/DPAN'

=item --mirror-index

Expand Down Expand Up @@ -526,9 +526,16 @@ Defaults to true (man pages generated) unless C<-L|--local-lib-contained>
option is supplied in which case it's set to false. You can disable
it with C<--no-man-pages>.

=item --insecure

By default, cpanm only uses HTTPS. If your environment lacks TLS
support, you can consider at your own risk to use HTTP instead by
using the C<--insecure> flag.
Be aware that when using that argument all requests will use HTTP.

=item --lwp

Uses L<LWP> module to download stuff over HTTP. Defaults to true, and
Uses L<LWP> module to download stuff over HTTPS. Defaults to true, and
you can say C<--no-lwp> to disable using LWP, when you want to upgrade
LWP from CPAN on some broken perl systems.

Expand Down
209 changes: 209 additions & 0 deletions author/cpanm/patches/secure-by-default.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
patch for [CVE-2024-45321](https://github.com/advisories/GHSA-9mmm-86g7-vp9g).
based on https://github.com/miyagawa/cpanminus/pull/674

--- Menlo/CLI/Compat.pm
+++ Menlo/CLI/Compat.pm
@@ -65,7 +65,7 @@ sub new {
mirrors => [],
mirror_only => undef,
mirror_index => undef,
- cpanmetadb => "http://cpanmetadb.plackperl.org/v1.0/",
+ cpanmetadb => "https://cpanmetadb.plackperl.org/v1.0/",
perl => $^X,
argv => [],
local_lib => undef,
@@ -79,6 +79,7 @@ sub new {
try_lwp => 1,
try_wget => 1,
try_curl => 1,
+ use_http => 0,
uninstall_shadows => ($] < 5.012),
skip_installed => 1,
skip_satisfied => 0,
@@ -199,6 +200,7 @@ sub parse_options {
'lwp!' => \$self->{try_lwp},
'wget!' => \$self->{try_wget},
'curl!' => \$self->{try_curl},
+ 'insecure!' => \$self->{use_http},
'auto-cleanup=s' => \$self->{auto_cleanup},
'man-pages!' => \$self->{pod2man},
'scandeps' => \$self->{scandeps},
@@ -453,7 +455,7 @@ sub search_common {
$self->chat("Found $found->{module} $found->{module_version} which doesn't satisfy $want_version.\n");
}
}
-
+
return;
}

@@ -616,7 +618,7 @@ Options:
--installdeps Only install dependencies
--showdeps Only display direct dependencies
--reinstall Reinstall the distribution even if you already have the latest version installed
- --mirror Specify the base URL for the mirror (e.g. http://cpan.cpantesters.org/)
+ --mirror Specify the base URL for the mirror (e.g. https://cpan.cpantesters.org/)
--mirror-only Use the mirror's index file instead of the CPAN Meta DB
-M,--from Use only this mirror base URL and its index file
--prompt Prompt when configure/build/test fails
@@ -636,18 +638,18 @@ Examples:

cpanm Test::More # install Test::More
cpanm MIYAGAWA/Plack-0.99_05.tar.gz # full distribution path
- cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL
+ cpanm https://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL
cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # install from a local file
cpanm --interactive Task::Kensho # Configure interactively
cpanm . # install from local directory
cpanm --installdeps . # install all the deps for the current directory
cpanm -L extlib Plack # install Plack and all non-core deps into extlib
- cpanm --mirror http://cpan.cpantesters.org/ DBI # use the fast-syncing mirror
+ cpanm --mirror https://cpan.cpantesters.org/ DBI # use the fast-syncing mirror
cpanm -M https://cpan.metacpan.org App::perlbrew # use only this secure mirror and its index

You can also specify the default options in PERL_CPANM_OPT environment variable in the shell rc:

- export PERL_CPANM_OPT="--prompt --reinstall -l ~/perl --mirror http://cpan.cpantesters.org"
+ export PERL_CPANM_OPT="--prompt --reinstall -l ~/perl --mirror https://cpan.cpantesters.org"

Type `man cpanm` or `perldoc cpanm` for the more detailed explanation of the options.

@@ -977,7 +979,7 @@ sub append_args {
my($self, $cmd, $phase) = @_;

return $cmd if ref $cmd ne 'ARRAY';
-
+
if (my $args = $self->{build_args}{$phase}) {
$cmd = join ' ', Menlo::Util::shell_quote(@$cmd), $args;
}
@@ -1163,7 +1165,7 @@ sub chdir {
sub configure_mirrors {
my $self = shift;
unless (@{$self->{mirrors}}) {
- $self->{mirrors} = [ 'http://www.cpan.org' ];
+ $self->{mirrors} = [ 'https://www.cpan.org' ];
}
for (@{$self->{mirrors}}) {
s!^/!file:///!;
@@ -1688,7 +1690,7 @@ sub cpan_dist {
sub git_uri {
my ($self, $uri) = @_;

- # similar to http://www.pip-installer.org/en/latest/logic.html#vcs-support
+ # similar to https://www.pip-installer.org/en/latest/logic.html#vcs-support
# git URL has to end with .git when you need to use pin @ commit/tag/branch

($uri, my $commitish) = split /(?<=\.git)@/i, $uri, 2;
@@ -2650,11 +2652,40 @@ sub DESTROY {

sub mirror {
my($self, $uri, $local) = @_;
- if ($uri =~ /^file:/) {
- $self->file_mirror($uri, $local);
- } else {
- $self->{http}->mirror($uri, $local);
+
+ die( "mirror: Undefined URI\n" ) unless defined $uri && length $uri;
+
+ if ( $uri =~ /^file:/) {
+ return $self->file_mirror($uri, $local);
}
+
+ # HTTPTinyish does not provide an option to disable
+ # certificates check, let's switch to http on demand.
+ $uri =~ s/^https:/http:/ if $self->{use_http};
+
+ my $reply = $self->{http}->mirror($uri, $local);
+
+ if ( $uri =~ /^https:/ && ref $reply
+ && $reply->{status} && $reply->{status} == 599
+ && $reply->{content}
+ ) {
+ my $invalid_cert;
+ if ( ref($self->{http}) =~ m{(?:Curl|HTTPTiny|Wget)} ) {
+ $invalid_cert = 1 if $reply->{content} =~ m{certificate}mi;
+ } elsif ( ref($self->{http}) =~ m{LWP} ) {
+ $invalid_cert = 1 if $reply->{content} =~ m{Can't connect.+?:443}mi;
+ }
+ if ( $invalid_cert ) {
+ die <<"DIE";
+TLS issue found while fetching $uri:\n
+$reply->{content}\n
+Please verify your certificates or force an HTTP-only request/mirror
+using --insecure option at your own risk.
+DIE
+ }
+ }
+
+ return $reply;
}

sub untar { $_[0]->{_backends}{untar}->(@_) };
@@ -2698,14 +2729,16 @@ sub configure_http {

require HTTP::Tinyish;

+ my $use_http = $self->{use_http};
+
my @try = qw(HTTPTiny);
unshift @try, 'Wget' if $self->{try_wget};
unshift @try, 'Curl' if $self->{try_curl};
unshift @try, 'LWP' if $self->{try_lwp};

- my @protocol = ('http');
- push @protocol, 'https'
- if grep /^https:/, @{$self->{mirrors}};
+ my @protocol = ( $use_http ? 'http' : 'https' );
+ push @protocol, 'http'
+ if !$use_http && grep /^http:/, @{$self->{mirrors}};

my $backend;
for my $try (map "HTTP::Tinyish::$_", @try) {
@@ -2721,7 +2754,14 @@ sub configure_http {
}
}

- $backend->new(agent => "Menlo/$Menlo::VERSION", verify_SSL => 1);
+ # In case we use https protocol by default
+ # and then later we try to perform non https requests
+ # we still want these requests to succeed
+ # Note: this is disabling the client cache optimization above
+ # and will fail later for SSL requests as no clients support TLS
+ $backend ||= 'HTTP::Tinyish';
+
+ $backend->new(agent => "Menlo/$Menlo::VERSION", $use_http ? () : ( verify_SSL => 1 ) );
}

sub init_tools {
--- Menlo/Index/MetaCPAN.pm
+++ Menlo/Index/MetaCPAN.pm
@@ -55,7 +55,7 @@ sub search_packages {
package => $args->{package},
version => $dist_meta->{version},
uri => "cpan:///distfile/$distfile",
- download_uri => $self->_download_uri("http://cpan.metacpan.org", $distfile),
+ download_uri => $self->_download_uri("https://cpan.metacpan.org", $distfile),
};
}

--- Menlo/Index/MetaDB.pm
+++ Menlo/Index/MetaDB.pm
@@ -19,7 +19,7 @@ use HTTP::Tiny;
sub BUILD {
my $self = shift;
my $uri = $self->uri;
- $uri = "http://cpanmetadb.plackperl.org/v1.0/"
+ $uri = "https://cpanmetadb.plackperl.org/v1.0/"
unless defined $uri;
# ensure URI ends in '/'
$uri =~ s{/?$}{/};
@@ -73,7 +73,7 @@ sub search_packages {
version => $match->{version},
uri => "cpan:///distfile/$file",
($match->{latest} ? () :
- (download_uri => "http://backpan.perl.org/authors/id/$match->{distfile}")),
+ (download_uri => "https://backpan.perl.org/authors/id/$match->{distfile}")),
};
}
} else {
42 changes: 27 additions & 15 deletions bin/cpanm

Large diffs are not rendered by default.