From 4774827982a67f98e88e17b84229181a529b50c7 Mon Sep 17 00:00:00 2001 From: Jacob Bachmeyer Date: Fri, 30 Jun 2023 23:40:20 -0500 Subject: [PATCH] Move remaining main processing code to packet objects --- gatekeeper.pl | 148 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 54 deletions(-) diff --git a/gatekeeper.pl b/gatekeeper.pl index ae19541..abd1194 100755 --- a/gatekeeper.pl +++ b/gatekeeper.pl @@ -1601,6 +1601,8 @@ sub read_directive_from_string { { package Local::Packet; + BEGIN { *throw = \&::throw } + # can be given an arrayref or a file list sub collect { my $class = shift; @@ -1640,6 +1642,67 @@ sub read_directive_from_string { sub parse; sub auth_check; sub upload_check; + + # Operation lists provide a shared abstraction that allow base class + # "install_check" and "install" methods to be generally applicable. + sub install_check { + my $self = shift; + + # Invoking this without first parsing the packet is a very serious bug. + ::abort 'internal error: performing installation check on unparsed packet' + unless $self->{oplist}; + + # If the upload installs a file, check if the final file exists; if so, + # require the 'replace' option to be set. + foreach my $step (@{$self->{oplist}}) { + if ($step->[0] eq 'install') { + my $install_as = $step->[1]; + + my $pubfinal = File::Spec::Unix->catfile + (pub => @{$self->target_directory}, $install_as); + my $final_upload = File::Spec->catfile + (::CONF_DIR_Public, @{$self->target_directory}, $install_as); + my $final_signature = File::Spec->catfile + (::CONF_DIR_Public, @{$self->target_directory}, $install_as.'.sig'); + + if (-e $final_signature || -e $final_upload) { + unless ($self->allow_overwrite) { + throw processing_error => command => $step, + summary => $pubfinal." exists and 'replace' was not selected"; + } + $self->add_notice + ("Archived and overwrote $pubfinal with uploaded version"); + } + } + } + } + + sub install { + my $self = shift; + + # Invoking this without first parsing the packet is a very serious bug. + ::abort 'internal error: installing unparsed packet' + unless $self->{oplist}; + + # skip the header + foreach my $step (@{$self->{oplist}}[1..$#{$self->{oplist}}]) { + if ($step->[0] eq 'install') { + ::execute_install($self->target_directory, $step, + $self->upload_filename); + } elsif ($step->[0] eq 'symlink') { + ::execute_symlink($self->target_directory, $step); + } elsif ($step->[0] eq 'rmsymlink') { + ::execute_rmsymlink($self->target_directory, $step); + } elsif ($step->[0] eq 'archive') { + # We now also allow archiving entire directories + ::archive_filepair($self->target_directory, $step->[1]); + } elsif (::IN_TEST_MODE && $step->[0] eq 'no-op') { + # do nothing + } else { + ::abort "unknown internal operation: $step->[0]"; + } + } + } } { @@ -1715,6 +1778,17 @@ sub read_directive_from_string { sub upload_check { } + sub install { + my $self = shift; + + # Invoking this without first authenticating the packet is a serious bug. + ::abort 'internal error: installing unauthenticated packet' + unless $self->{auth_directive_signature_info} + && ($self->{auth_directive_signature_info}{exitcode} == 0 + && !defined $self->{auth_directive_signature_info}{TILT}); + + $self->SUPER::install; + } } { @@ -1762,6 +1836,23 @@ sub read_directive_from_string { ::check_automake_vulnerabilities (File::Spec->catfile(::CONF_DIR_Scratch, $self->upload_filename)); } + + sub install { + my $self = shift; + + # Invoking this without first authenticating the file is a serious bug. + ::abort 'internal error: installing unauthenticated file' + unless $self->{auth_file_signature_info} + && ($self->{auth_file_signature_info}{exitcode} == 0 + && !defined $self->{auth_file_signature_info}{TILT}); + + # Do we need a subdirectory on CONF_DIR_Staging as well? Can't quite + # picture when we'd have a collision, so skip that for now. + ::move_filepair + (::CONF_DIR_Scratch, $self->upload_filename, ::CONF_DIR_Staging); + + $self->SUPER::install; + } } @@ -3246,22 +3337,19 @@ my @packets; foreach my $packet (@packets) { # variables preserved for the report if an exception is thrown - my $directive_text; my $oplist; + my $directive_text; # full text of directive my $dsig_info; # directive signature information my $fsig_info; # file signature information my $complete = 0; # direct flag to indicate successful processing eval { # trap exceptions encountered while processing a packet local $Phase = 'PS'; - $packet->parse; # scaffolding to be cleaned up later $directive_text = $packet->{directive_text}; - $oplist = $packet->{oplist}; $Phase = 'AA'; - $packet->auth_check; # scaffolding to be cleaned up later @@ -3269,59 +3357,11 @@ foreach my $packet (@packets) { $fsig_info = $packet->{auth_file_signature_info}; $Phase = 'VL'; - $packet->upload_check; $Phase = 'EX'; - # If the upload installs a file, check if the final file exists; if so, - # require the 'replace' option to be set. - foreach my $step (@$oplist) { - if ($step->[0] eq 'install') { - my $install_as = $step->[1]; - - my $pubfinal = File::Spec::Unix->catfile - (pub => @{$packet->target_directory}, $install_as); - my $final_upload = File::Spec->catfile - (CONF_DIR_Public, @{$packet->target_directory}, $install_as); - my $final_signature = File::Spec->catfile - (CONF_DIR_Public, @{$packet->target_directory}, $install_as.'.sig'); - - if (-e $final_signature || -e $final_upload) { - unless ($packet->allow_overwrite) { - throw processing_error => command => $step, - summary => $pubfinal." exists and 'replace' was not selected"; - } - $packet->add_notice - ("Archived and overwrote $pubfinal with uploaded version"); - } - } - } - - # If the upload carries a file, transfer (with signature) to staging area. - if ($packet->has_uploaded_file) { - # Do we need a subdirectory on CONF_DIR_Staging as well? Can't quite - # picture when we'd have a collision, so skip that for now. - move_filepair - (CONF_DIR_Scratch, $packet->upload_filename, CONF_DIR_Staging); - } - - foreach my $step (@{$oplist}[1..$#$oplist]) { # skip the header - if ($step->[0] eq 'install') { - execute_install($packet->target_directory, $step, - $packet->upload_filename); - } elsif ($step->[0] eq 'symlink') { - execute_symlink($packet->target_directory, $step); - } elsif ($step->[0] eq 'rmsymlink') { - execute_rmsymlink($packet->target_directory, $step); - } elsif ($step->[0] eq 'archive') { - # We now also allow archiving entire directories - archive_filepair($packet->target_directory, $step->[1]); - } elsif (IN_TEST_MODE && $step->[0] eq 'no-op') { - # do nothing - } else { - abort "unknown internal operation: $step->[0]"; - } - } + $packet->install_check; + $packet->install; $complete = 1; }; -- 2.25.1