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 cpanm secure by default #674

Open
wants to merge 5 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
8 changes: 4 additions & 4 deletions App-cpanminus/lib/App/cpanminus.pm
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,16 @@ Module::Build (core in 5.10)

=head2 How does cpanm get/parse/update the CPAN index?

It queries the CPAN Meta DB site at L<http://cpanmetadb.plackperl.org/>.
It queries the CPAN Meta DB site at L<https://cpanmetadb.plackperl.org/>.
The site is updated at least every hour to reflect the latest changes
from fast syncing mirrors. The script then also falls back to query the
module at L<http://metacpan.org/> using its search API.
module at L<https://metacpan.org/> using its search API.

Upon calling these API hosts, cpanm (1.6004 or later) will send the
local perl versions to the server in User-Agent string by default. You
can turn it off with C<--no-report-perl-version> option. Read more
about the option with L<cpanm>, and read more about the privacy policy
about this data collection at L<http://cpanmetadb.plackperl.org/#privacy>
about this data collection at L<https://cpanmetadb.plackperl.org/#privacy>

Fetched files are unpacked in C<~/.cpanm> and automatically cleaned up
periodically. You can configure the location of this with the
Expand Down Expand Up @@ -270,7 +270,7 @@ Arnfjord Bjarmason, Eric Wilhelm, Florian Ragwitz and xaicron.

=over 4

=item L<http://github.com/miyagawa/cpanminus> - source code repository, issue tracker
=item L<https://github.com/miyagawa/cpanminus> - source code repository, issue tracker

=item L<irc://irc.perl.org/#cpanm> - discussions about cpanm and its related tools

Expand Down
23 changes: 15 additions & 8 deletions App-cpanminus/script/cpanm.PL
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,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 @@ -60,7 +60,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 @@ -213,7 +213,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 @@ -224,7 +224,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 @@ -256,7 +256,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 @@ -555,9 +555,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
2 changes: 1 addition & 1 deletion Menlo-Legacy/META.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"url" : "https://metacpan.org/pod/CPAN::Meta::Spec",
"version" : 2
},
"name" : "Menlo-Legacy",
Expand Down
74 changes: 57 additions & 17 deletions Menlo-Legacy/lib/Menlo/CLI/Compat.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -453,7 +455,7 @@ sub search_common {
$self->chat("Found $found->{module} $found->{module_version} which doesn't satisfy $want_version.\n");
}
}

return;
}

Expand Down Expand Up @@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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:///!;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You say "at your own risk" a couple of times...

Using the whole tool is and has always been at my own risk/liability. "at your own risk" sounds like legal terms and seems to suggest there might be a time where someone else accepts the legal liability for the use of this tool.

If the aim is to say non-https is more risky than https it's worth saying "with the risks of non-secure http" or "but be aware of the risks of non-https"... Given how long cpan was non-http and non-https I'd say it might even be worth including a discussion of how risky it actually is to fetch tar balls without host verification or a secret communication channel.

There's a difference between the risks of usint the cpan cdn without https+verification and the risks of a 192 mirror without https+verification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. Could you please check if the new message properly addresses your concerns? Thank you!

DIE
}
}

return $reply;
}

sub untar { $_[0]->{_backends}{untar}->(@_) };
Expand Down Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion Menlo/Changes
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
See http://github.com/miyagawa/cpanminus/ for the latest development.
See https://github.com/miyagawa/cpanminus/ for the latest development.

{{$NEXT}}

Expand Down
2 changes: 1 addition & 1 deletion Menlo/META.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"url" : "https://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "Menlo",
Expand Down
2 changes: 1 addition & 1 deletion Menlo/lib/Menlo/Index/MetaCPAN.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
}

Expand Down
4 changes: 2 additions & 2 deletions Menlo/lib/Menlo/Index/MetaDB.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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{/?$}{/};
Expand Down Expand Up @@ -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 {
Expand Down