mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-04 12:07:56 +00:00
0d8d1e60ff
Extract the secret set, the command maps and the redaction routines from xcatd.pm. Drive the real routines, because the module needs daemon dependencies that the test host does not have. Cover a multi-word value, a quoted value, spaces around the operator, the "+=", ",=", "^=", "!=", "=~" and "!~" operators, the community strings, a secret embedded in a compound argument with and without quotes, each Getopt::Long password option form with bundles, the "?" help letter, abbreviations, "+" prefixes and two-dash letters, values that hold another secret letter, non-secret compact values that a value option absorbs, long names that start with a secret letter, the mkvm case forms and compact values, the chvm positional passwords, the mkvm clone pw= operand, the vCenter cluster passwords, the snmpc site value through chdef and tabch with compound selectors, the authentication tokens, and the product keys. Assert that validate() redacts the argument vector, that the joined result runs through redact_password, and that the dispatch trace builds its text from redacted arguments. Keep every attribute and column pair from Schema.pm, so a removed table-qualified mapping fails the test.
314 lines
19 KiB
Perl
314 lines
19 KiB
Perl
#!/usr/bin/env perl
|
|
use strict;
|
|
use warnings;
|
|
|
|
use FindBin;
|
|
use File::Spec;
|
|
use Test::More;
|
|
|
|
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
|
|
my $xcatd = File::Spec->catfile( $repo_root, 'xCAT-server/lib/perl/xCAT/xcatd.pm' );
|
|
my $daemon = File::Spec->catfile( $repo_root, 'xCAT-server/sbin/xcatd' );
|
|
my $schema = File::Spec->catfile( $repo_root, 'perl-xCAT/xCAT/Schema.pm' );
|
|
|
|
plan skip_all => 'xcatd.pm, xcatd or Schema.pm not found' unless -r $xcatd && -r $daemon && -r $schema;
|
|
|
|
sub slurp {
|
|
open( my $fh, '<', $_[0] ) or die "Unable to read $_[0]: $!";
|
|
my $c = do { local $/; <$fh> };
|
|
close($fh);
|
|
return $c;
|
|
}
|
|
|
|
my $source = slurp($xcatd);
|
|
my $daemon_source = slurp($daemon);
|
|
my $schema_source = slurp($schema);
|
|
|
|
# xcatd.pm cannot be loaded here (dependencies), so lift out the secret set, the
|
|
# command maps, and the redaction routines and evaluate them alone.
|
|
my ($set) = $source =~ /(my \@secret_attributes = qw\(.*?\);\s*my %secret_attribute = map.*?;)/s;
|
|
my ($maps) = $source =~ /(my %secret_command_options = \(.*?my %secret_site_keys = map.*?;)/s;
|
|
my ($arg_sub) = $source =~ /(sub redact_password_arg \{.*?\n\}\n)/s;
|
|
my ($args_sub) = $source =~ /(sub redact_password_args \{.*?\n\}\n)/s;
|
|
my ($cmd_sub) = $source =~ /(sub redact_password \{.*?\n\}\n)/s;
|
|
BAIL_OUT('could not extract the secret set from xcatd.pm') unless $set;
|
|
BAIL_OUT('could not extract the command maps from xcatd.pm') unless $maps;
|
|
BAIL_OUT('could not extract redact_password_arg from xcatd.pm') unless $arg_sub;
|
|
BAIL_OUT('could not extract redact_password_args from xcatd.pm') unless $args_sub;
|
|
BAIL_OUT('could not extract redact_password from xcatd.pm') unless $cmd_sub;
|
|
eval "package RedactUnderTest; $set $maps $arg_sub $args_sub $cmd_sub 1;"
|
|
or BAIL_OUT("could not evaluate the redaction routines: $@");
|
|
|
|
sub arg { return RedactUnderTest::redact_password_arg( 'xCAT::xcatd', $_[0] ); }
|
|
sub cmd { return RedactUnderTest::redact_password( $_[0], $_[1] ); }
|
|
|
|
sub vec_ {
|
|
my ( $command, @args ) = @_;
|
|
my ( $redacted, $changed ) =
|
|
RedactUnderTest->redact_password_args( $command, \@args );
|
|
return ( join( ' ', @$redacted ), $changed );
|
|
}
|
|
|
|
# A secret in an attribute assignment must never survive, however it was written.
|
|
unlike( arg('bmcpassword=SEKRET'), qr/SEKRET/, 'a bare attribute value is redacted' );
|
|
unlike( arg('bmcpassword=SEKRET phrase'), qr/SEKRET|phrase/, 'a value with spaces is redacted whole' );
|
|
unlike( arg("bmcpassword=has'quote"), qr/quote/, 'a value with a quote is redacted whole' );
|
|
unlike( arg('passwd.password+=SEKRET'), qr/SEKRET/, 'a += splice assignment is redacted' );
|
|
unlike( arg('pdu.community=SEKRET'), qr/SEKRET/, 'the SNMP community string is redacted' );
|
|
unlike( arg('community=SEKRET'), qr/SEKRET/, 'a bare community value is redacted' );
|
|
|
|
# The chdef/mkdef parser trims whitespace around '=', so an attribute may be
|
|
# written with spaces. The value must still be redacted.
|
|
unlike( arg('bmcpassword = SEKRET'), qr/SEKRET/, 'spaces around the equals are redacted' );
|
|
unlike( arg('bmcpassword =SEKRET'), qr/SEKRET/, 'a space before the equals is redacted' );
|
|
unlike( arg('bmcpassword= SEKRET'), qr/SEKRET/, 'a space after the equals is redacted' );
|
|
unlike( arg('bmcpassword = SEKRET phrase'), qr/SEKRET|phrase/, 'spaces around the equals with a multi-word value are redacted whole' );
|
|
|
|
# nodech and node selection accept operators other than a bare '='.
|
|
unlike( arg('ipmi.password,=SEKRET'), qr/SEKRET/, 'a ,= append assignment is redacted' );
|
|
unlike( arg('ipmi.password^=SEKRET'), qr/SEKRET/, 'a ^= remove assignment is redacted' );
|
|
unlike( arg('ipmi.password!=SEKRET'), qr/SEKRET/, 'a != selection is redacted' );
|
|
unlike( arg('ipmi.password!~SEKRET'), qr/SEKRET/, 'a !~ selection is redacted' );
|
|
unlike( arg('ipmi.password=~SEKRET'), qr/SEKRET/, 'a =~ selection is redacted' );
|
|
|
|
# Non-secret detail stays, or the log stops being useful.
|
|
is( arg('groups=lab'), 'groups=lab', 'an unrelated attribute is kept' );
|
|
is( arg('key=system'), 'key=system', 'key is kept, it is not a secret' );
|
|
is( arg('sshkeydir=/etc/xcat/keys'), 'sshkeydir=/etc/xcat/keys', 'sshkeydir is kept, it is a directory' );
|
|
is( arg('n1'), 'n1', 'a plain argument is kept' );
|
|
|
|
# The command-flag mechanism still redacts positional password flags.
|
|
unlike( cmd( 'bmcdiscover', ' -s nmap -p SEKRET --range 10.0.0.1' ), qr/SEKRET/,
|
|
'a -p flag value is redacted' );
|
|
|
|
# An assignment embedded in a compound argument, for example an xdsh remote
|
|
# command string, must be masked to the end of the argument, because a shell
|
|
# value may hold quotes and spaces.
|
|
unlike( cmd( 'xdsh', " compute 'echo bmcpassword=SEKRET > /etc/x'" ), qr/SEKRET/,
|
|
'a secret embedded in a compound argument is redacted' );
|
|
unlike( cmd( 'xdsh', q{ compute "export password='SEKRET phrase'; run-app"} ), qr/SEKRET|phrase/,
|
|
'a quoted secret embedded in a compound argument is redacted whole' );
|
|
unlike( arg(q{export password='SEKRET phrase'; run-app}), qr/SEKRET|phrase/,
|
|
'a quoted secret inside one argument is redacted to the end' );
|
|
unlike( arg('usercomment=password=SEKRET'), qr/SEKRET/,
|
|
'a secret glued to a prior assignment is redacted' );
|
|
is( arg('echo groups=lab; ls'), 'echo groups=lab; ls', 'an embedded non-secret assignment is kept' );
|
|
|
|
# A password given through a command option must be redacted in every form
|
|
# Getopt::Long accepts: a separate argument, a compact short option, a long
|
|
# option, and a long option with an equals sign.
|
|
my ( $out, $changed );
|
|
( $out, $changed ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '-p', 'SEKRET phrase' );
|
|
unlike( $out, qr/SEKRET|phrase/, 'a -p value in the next argument is redacted whole' );
|
|
is( $changed, 1, 'the option redaction reports the change' );
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '-pSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a compact -pSEKRET is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '--bmcpasswd', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a --bmcpasswd value in the next argument is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '--bmcpasswd=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a --bmcpasswd=value is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '-n', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a -n new password is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '--newbmcpw', 'SEKRET' );
|
|
is( $out, '--newbmcpw xxxxxxxx', 'the full --newbmcpw name is kept and its value is masked' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-password', 'SEKRET' );
|
|
is( $out, 'zvm02 -password xxxxxxxx', 'a single-dash long -password keeps its name and masks its value' );
|
|
( $out ) = vec_( 'switchdiscover', '--range', '10.0.0.0/24', '-c', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the switchdiscover -c community is redacted' );
|
|
( $out ) = vec_( 'mkhwconn', 'frame', '-p', 'hmc01', '-PSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the mkhwconn -P password is redacted' );
|
|
like( $out, qr/-p hmc01/, 'the mkhwconn -p hardware control point is kept' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '--password', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the mkvm --password value is redacted' );
|
|
( $out ) = vec_( 'rspconfig', 'admin_passwd=SEKRET phrase' );
|
|
unlike( $out, qr/SEKRET|phrase/, 'a rspconfig password assignment is redacted whole' );
|
|
|
|
# Getopt::Long also accepts an abbreviated long option, a bundle of short
|
|
# options, and, for parsers that keep the default configuration, any letter
|
|
# case. Each of those forms must redact too.
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '--bmcp', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'an abbreviated --bmcp is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '--bmcpas=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'an abbreviated --bmcpas=value is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '-zp', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a bundled -zp is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '--range', '10.0.0.1', '-zpSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a bundled compact -zpSEKRET is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '-pApple' );
|
|
is( $out, '-pxxxxxxxx', 'a compact value keeps no leading letters' );
|
|
( $out ) = vec_( 'bmcdiscover', '-pAdmin', '--range', '10.0.0.1' );
|
|
is( $out, '-pxxxxxxxx --range 10.0.0.1', 'a compact value ending in a secret letter is masked, not the next argument' );
|
|
( $out ) = vec_( 'bmcdiscover', '-nstop', '--range', '10.0.0.1' );
|
|
is( $out, '-nxxxxxxxx --range 10.0.0.1', 'a compact -n value is masked whole' );
|
|
( $out ) = vec_( 'bmcdiscover', '-pbanana' );
|
|
is( $out, '-pxxxxxxxx', 'a compact value with a later secret letter is masked from the option' );
|
|
( $out ) = vec_( 'bmcdiscover', '--p=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a double-dash --p=value is redacted' );
|
|
( $out ) = vec_( 'switchdiscover', '--c', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a double-dash --c community is redacted' );
|
|
( $out ) = vec_( 'mkhwconn', 'frame', '--P=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a double-dash --P=value is redacted' );
|
|
( $out, $changed ) = vec_( 'mkhwconn', 'frame', '--p', 'hmc01' );
|
|
is( $changed, 0, 'the double-dash --p control point is kept, the case differs' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '--W', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a double-dash --W is redacted, the parser ignores case' );
|
|
|
|
# The vCenter cluster commands log in with a --password option.
|
|
( $out ) = vec_( 'createvcluster', '--vcenter', 'vc01', '--username', 'admin', '--password', 'SEKRET', 'cluster01' );
|
|
unlike( $out, qr/SEKRET/, 'the createvcluster password is redacted' );
|
|
like( $out, qr/--username admin/, 'the createvcluster username is kept' );
|
|
( $out ) = vec_( 'lsvcluster', '--vcenter', 'vc01', '--password=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the lsvcluster password is redacted' );
|
|
( $out ) = vec_( 'rmvcluster', '--vcenter', 'vc01', '--PASSWORD', 'SEKRET', 'cluster01' );
|
|
unlike( $out, qr/SEKRET/, 'the rmvcluster password is redacted, the parser ignores case' );
|
|
|
|
# In a bundle a non-secret option that takes a value absorbs the rest, so a
|
|
# secret letter inside that value must not redact.
|
|
( $out, $changed ) = vec_( 'bmcdiscover', '-snmap', '--range', '10.0.0.0/24' );
|
|
is( $out, '-snmap --range 10.0.0.0/24', 'the -snmap scan method is kept, n is the value of -s' );
|
|
is( $changed, 0, 'a non-secret compact value reports no change' );
|
|
( $out, $changed ) = vec_( 'bmcdiscover', '-sopenbmc' );
|
|
is( $out, '-sopenbmc', 'the -sopenbmc scan method is kept, p is inside the value' );
|
|
is( $changed, 0, 'the -sopenbmc value reports no change' );
|
|
( $out, $changed ) = vec_( 'mkhwconn', 'frame', '-pPOWERHMC' );
|
|
is( $changed, 0, 'the mkhwconn -p value is kept, P is inside the value of -p' );
|
|
( $out, $changed ) = vec_( 'bmcdiscover', '-zsp', 'SEKRET' );
|
|
is( $changed, 0, 'a bundle stops at a value option, -zsp is the value p of -s' );
|
|
( $out ) = vec_( 'bmcdiscover', '-zps', 'topsecret' );
|
|
is( $out, '-zpxxxxxxxx topsecret', 'a bundle masks from the secret letter, the next argument is positional' );
|
|
( $out ) = vec_( 'bmcdiscover', '-?pSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a bundle with the ? help letter is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '-?n', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a -?n bundle masks the next argument' );
|
|
( $out, $changed ) = vec_( 'mkvm', 'lpar01', '-cpower10', '-pprofile1' );
|
|
is( $out, 'lpar01 -cpower10 -pprofile1', 'the mkvm compact values are kept, w is inside the value of -c' );
|
|
is( $changed, 0, 'the mkvm compact values report no change' );
|
|
( $out, $changed ) = vec_( 'mkvm', 'lpar01', '-p', 'profile1' );
|
|
is( $out, 'lpar01 -p profile1', 'the PPC mkvm profile remains visible' );
|
|
is( $changed, 0, 'the PPC mkvm profile reports no redaction' );
|
|
|
|
# In the z/VM grammar -s is a boolean and -c takes an integer, so a bundle can
|
|
# continue into the real password option.
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-swSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'zVM -s followed by bundled -w is redacted' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-sw', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'zVM bundled -sw masks the next argument' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-c1wSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'zVM resumes after the numeric -c value and redacts -w' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-c1w', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'zVM numeric bundle masks the following password argument' );
|
|
( $out, $changed ) = vec_( 'mkvm', 'zvm02', '-c12' );
|
|
is( $changed, 0, 'a plain numeric cpu bundle is kept' );
|
|
|
|
# Bundled short options keep letter case, so a capital letter is unknown to
|
|
# the parser and the bundle continues into the password option.
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-Rw', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a capital -Rw bundle masks the next argument' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-RwSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a glued capital -RwSEKRET is redacted' );
|
|
|
|
# A bundled integer value may carry a sign or underscores.
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-c+1w', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a signed cpu value still reaches the password option' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-c-1wSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a negative cpu value still reaches the password option' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-c1_0w', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'an underscored cpu value still reaches the password option' );
|
|
( $out, $changed ) = vec_( 'mkvm', 'zvm02', '-rabcw', 'KEEPME' );
|
|
is( $changed, 0, 'a lowercase -r value absorbs the rest of the bundle' );
|
|
|
|
# Getopt::Long compatibility mode also accepts "+" as an option starter.
|
|
( $out ) = vec_( 'bmcdiscover', '+p', 'SEKRET', '--range', '10.0.0.1' );
|
|
unlike( $out, qr/SEKRET/, 'a +p option is redacted' );
|
|
( $out ) = vec_( 'bmcdiscover', '+bmcpasswd=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a +bmcpasswd=value is redacted' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '+w', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a +w option is redacted' );
|
|
( $out ) = vec_( 'switchdiscover', '--range', '10.0.0.0/24', '-xc', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a bundled -xc community is redacted' );
|
|
( $out ) = vec_( 'mkhwconn', 'frame', '-tP', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a bundled -tP password is redacted' );
|
|
( $out, $changed ) = vec_( 'mkhwconn', 'frame', '-p', 'hmc01' );
|
|
is( $out, 'frame -p hmc01', 'the mkhwconn -p stays visible, it is not the password' );
|
|
is( $changed, 0, 'the mkhwconn hardware control point reports no change' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '-W', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the mkvm -W is redacted, the parser ignores case' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '--PASSWORD=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the mkvm --PASSWORD=value is redacted' );
|
|
( $out ) = vec_( 'mkvm', 'zvm02', '--Pass', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the mkvm abbreviated --Pass is redacted' );
|
|
( $out ) = vec_( 'mkvm', 'gpok4', 'gpok3', 'pool=POOL1', 'pw=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the mkvm clone pw= operand is redacted' );
|
|
like( $out, qr/pool=POOL1/, 'the mkvm clone pool= operand is kept' );
|
|
|
|
# chvm carries passwords as positional operands.
|
|
( $out ) = vec_( 'chvm', '--setpassword', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the chvm --setpassword operand is redacted' );
|
|
( $out ) = vec_( 'chvm', '--add3390', 'POOL1', '0101', '3g', 'MR', 'RSEKRET', 'WSEKRET', 'MSEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the chvm --add3390 disk passwords are redacted' );
|
|
like( $out, qr/POOL1 0101 3g MR/, 'the chvm --add3390 disk parameters are kept' );
|
|
( $out ) = vec_( 'chvm', '--formatdisk', '0100', 'SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'the chvm --formatdisk password is redacted' );
|
|
like( $out, qr/--formatdisk 0100/, 'the chvm --formatdisk address is kept' );
|
|
|
|
# The site table stores the global SNMP community string under the snmpc key.
|
|
( $out ) = vec_( 'tabch', 'key=snmpc', 'site.value=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a tabch of the snmpc site value is redacted' );
|
|
( $out ) = vec_( 'tabch', 'key=snmpc', 'site.value+=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a tabch += splice of the snmpc site value is redacted' );
|
|
( $out ) = vec_( 'tabch', 'key=snmpc,key=snmpc', 'site.value=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a compound tabch selector with snmpc is redacted' );
|
|
( $out ) = vec_( 'tabch', 'key=temporary', 'site.key=snmpc', 'site.value=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a site.key=snmpc assignment marks the value secret' );
|
|
( $out ) = vec_( 'tabch', 'site.key=snmpc', 'site.value=SEKRET' );
|
|
unlike( $out, qr/SEKRET/, 'a table-qualified snmpc selector is redacted' );
|
|
( $out, $changed ) = vec_( 'tabch', 'key=domain', 'site.value=lab' );
|
|
is( $out, 'key=domain site.value=lab', 'a tabch of a plain site value is kept' );
|
|
is( $changed, 0, 'a benign vector reports no change' );
|
|
unlike( arg('snmpc=SEKRET'), qr/SEKRET/, 'the snmpc site attribute is redacted' );
|
|
|
|
# Product keys are license secrets.
|
|
unlike( arg('productkey=SEKRET'), qr/SEKRET/, 'a product key is redacted' );
|
|
unlike( arg('prodkey.key=SEKRET'), qr/SEKRET/, 'a table-qualified product key is redacted' );
|
|
|
|
# The token table stores bearer credentials under tokenid.
|
|
unlike( arg('tokenid=SEKRET'), qr/SEKRET/, 'an authentication token is redacted' );
|
|
unlike( arg('token.tokenid=SEKRET'), qr/SEKRET/, 'a table-qualified token is redacted' );
|
|
|
|
# Every attribute and column Schema.pm marks secret must be covered. Keep every
|
|
# (attribute, column) pair so a removed table-qualified column is caught, not
|
|
# masked by another pair that shares the attribute name.
|
|
my @pairs;
|
|
while ( $schema_source =~ /attr_name\s*=>\s*'([^']+)'(.{0,400}?)tabentry\s*=>\s*'([^']+)'/gs ) {
|
|
my ( $attr, $tabentry ) = ( $1, $3 );
|
|
next
|
|
unless $tabentry =~ /\.(password|passwd|authkey|privkey|adminpassword|sshpassword|community)$/i
|
|
or $tabentry eq 'prodkey.key';
|
|
push @pairs, [ $attr, $tabentry ];
|
|
}
|
|
ok( scalar(@pairs) > 0, 'Schema.pm yielded attributes mapped to secret columns' )
|
|
or BAIL_OUT('the Schema.pm mapping could not be parsed, so this test proves nothing');
|
|
|
|
my @uncovered;
|
|
foreach my $pair (@pairs) {
|
|
my ( $attr, $column ) = @$pair;
|
|
push @uncovered, $attr if arg("$attr=SEKRET") =~ /SEKRET/;
|
|
push @uncovered, $column if arg("$column=SEKRET") =~ /SEKRET/;
|
|
}
|
|
is_deeply( \@uncovered, [], 'every attribute and column Schema.pm marks secret is redacted' );
|
|
|
|
# validate() must redact the argument vector and still run the joined result
|
|
# through redact_password, so every secret reaches syslog and the auditlog
|
|
# table redacted.
|
|
like( $source, qr/=\s*xCAT::xcatd->redact_password_args\(\$request->\{command\}->\[0\]/, 'validate() redacts the argument vector' );
|
|
like( $source, qr/\$redacted_arglist\s*=\s*redact_password\b/, 'validate() redacts the arguments through redact_password' );
|
|
|
|
# The debug dispatch trace must not rebuild the command from the raw request.
|
|
like( $daemon_source, qr/\(\$trace_args\)\s*=\s*xCAT::xcatd->redact_password_args\(\$req->\{command\}->\[0\]/,
|
|
'the dispatch trace builds its text from redacted arguments' );
|
|
|
|
# The auditlog table and syslog use the same redacted arguments.
|
|
like( $source, qr/\$rsp->\{args\}->\[0\]\s*=\s*\$redacted_arglist/, 'the auditlog table stores the redacted arguments' );
|
|
|
|
done_testing();
|