Skip to content

Commit

Permalink
make cpanminus secure by default
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo82148 committed Sep 1, 2024
1 parent ddb1436 commit 40aebd9
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 23 deletions.
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.

0 comments on commit 40aebd9

Please sign in to comment.