[sf-perl] regex problem: append path only if not in list already

David Christensen dpchrist at holgerdanske.com
Tue Mar 2 21:04:07 PST 2021


On 3/2/21 3:55 PM, Joseph Brenner wrote:

>> Indeed the "\s*" parts are not needed
> 
> Yes, but that's more of a nit that anything.  They don't cause any
> problems, either  (and actually, I don't know the precise rules are
> about what you can encounter in these files).


sudoers(5) describes the syntax.


visudo(8) can check the syntax of the sudoers(5) file or the syntax of a 
specified file.


Here is my script.  It uses a loop to process the input one line at a 
time, concatenates continued lines, uses a match regular expression to 
split the desired lines into a front half (assignment expression) and a 
back half (trailing white space and comment), and inserts the delimiter 
and path component.  I found that '[\\\s]+' and/or '[\\\s]*' were 
required for white space and continued lines, non-greedy '.*?' was 
required for the front half, and non-capturing parentheses and a newline 
were required for the back half.  I do not see how to convert this 
solution into a substitution regular expression that could operate on 
the contents of an entire sudoers(5) file as a string:

2021-03-02 20:41:32 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ cat dpchrist.pl
#!perl
# $Id: dpchrist.pl,v 1.15 2021/03/03 04:26:36 dpchrist Exp $
# By David Paul Christensen dpchrist at holgerdanske.com
# Public Domain.
#
# 
https://github.com/doomvox/raku-study/blob/main/bin/2021feb28/pcre_regex_to_append_path_non_redundantly.t

use strict;
use warnings;

use Data::Dumper;
use File::Basename;
use File::Slurp;
use Getopt::Long;

my $base	= basename $0;
my $debug	= undef;
my $visudo	= '/usr/sbin/visudo';

sub _d { warn((caller(0))[2], $", Data::Dumper->Dump(@_)) if $debug }

GetOptions('debug|d' => \$debug)
     or die "ERROR $base processing arguments";

die "Usage: $base [--debug|-d] FILE\n" unless @ARGV == 1;

my $file = shift;

my @in = read_file($file);
_d [\@in], [qw(*in)];

system $visudo, '-c', '-q', '-s', '-f', $file
     and die "ERROR $base invalid sudoers(5) syntax '$file'";

my @out;

my $delimiter	= ':';
my $newline	= "\n";
my $comp	= '/usr/local/bin';
my $rx		= 
qr(^(\s*Defaults[\\\s]+secure_path[\\\s]*=[\\\s]*.*?)((?:[\\\s]*#.*)?\n$))s;

for (my $i = 0; $i < @in; $i++) {
     my $line = $in[$i];
     _d [$i], [qw(i)]; _d [$line], [qw(line)];
     while ($i < @in && $line =~ m{\\$}s) {
	$line .= $in[++$i];
	_d [$i], [qw(i)]; _d [$line], [qw(line)];
     }
     if ( $line !~ /$comp/s && $line =~ $rx) {
	my $front = $1;		 _d [$front], [qw(front)];
	my $back  = $2;		 _d [$back ], [qw(back )];
	push @out, $front . $delimiter . $comp . $back;
	_d [$out[-1]], [qw(out)];
     } else {
	push @out, $line;
	_d [$out[-1]], [qw(out)];
     }
}
_d [\@out], [qw(*out)];

$file =~ s/\.in/.out.dpchrist/;

write_file($file, @out);

system $visudo, '-c', '-q', '-s', '-f', $file
     and die "ERROR $base invalid sudoers(5) syntax '$file'";


Here is the default Debian sudoers(5) file (with the '#include' 
statement disabled).  It is syntactically correct, and already includes 
the desired path component for 'secure_path'.  When processed, the 
output should match the input:

2021-03-02 19:25:38 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ cat sudoers.0.in
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults	env_reset
Defaults	mail_badpass
Defaults 
secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root	ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo	ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:

##includedir /etc/sudoers.d

2021-03-02 19:25:43 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ /usr/sbin/visudo -c -s -f sudoers.0.in
sudoers.0.in: parsed OK

2021-03-02 19:25:57 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ perl dpchrist.pl sudoers.0.in

2021-03-02 19:26:04 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ diff sudoers.0.in sudoers.0.out.dpchrist


Here is a contrived, but syntactically correct, example that 
demonstrates white space, continued lines, and comments.  The three 
'secure_path' statements do not include the desired path component. 
When processed, the component should be added in all three places and 
the syntax should be valid:

2021-03-02 19:34:43 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ cat sudoers.1.in
# no leading white space
Defaults				\
secure_path				\
=					\
/sbin:/bin:/usr/sbin:/usr/bin		\
					# trailing comment

     # 4 leading spaces
     Defaults				\
     secure_path				\
     =					\
     /sbin:/bin:/usr/sbin:/usr/bin	\
					# trailing comment

	# 1 leading tab
	Defaults			\
	secure_path			\
	=				\
	/sbin:/bin:/usr/sbin:/usr/bin	\
					# trailing comment

2021-03-02 19:34:49 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ /usr/sbin/visudo -c -s -f sudoers.1.in
sudoers.1.in: parsed OK

2021-03-02 19:35:00 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ perl dpchrist.pl sudoers.1.in

2021-03-02 19:35:08 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ cat sudoers.1.out.dpchrist
# no leading white space
Defaults				\
secure_path				\
=					\
/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin		\
					# trailing comment

     # 4 leading spaces
     Defaults				\
     secure_path				\
     =					\
     /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin	\
					# trailing comment

	# 1 leading tab
	Defaults			\
	secure_path			\
	=				\
	/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin	\
					# trailing comment

2021-03-02 19:35:22 dpchrist at tinkywinky 
~/sandbox/perl/sudoers-secure_path-usr_local_bin
$ /usr/sbin/visudo -c -s -f sudoers.1.out.dpchrist
sudoers.1.out.dpchrist: parsed OK


David


More information about the SanFrancisco-pm mailing list