Punk
view release on metacpan or search on metacpan
t/1200-config.t view on Meta::CPAN
'the public config redacts a secret');
is($cfg->config->{database}{other}, '[redacted]', 'every secret');
is($cfg->config->{database}{dsn}, 'dbi:SQLite:dbname=test.db',
'ordinary values are untouched');
# ... and nothing anywhere in it looks like the real thing
my $dump = do {
require File::Raw::JSON;
File::Raw::JSON::file_json_encode($cfg->config);
};
unlike($dump, qr/from-the-environment|from-a-file|from-a-command/,
'a full dump of the public config leaks no secret');
is_deeply($cfg->secret_paths,
[ sort qw(database.computed database.other database.password
database.plain) ],
'the resolved secrets are listed');
ok($cfg->has_secret('database.password'), 'has_secret');
ok(!$cfg->has_secret('database.dsn'), 'and only for real secrets');
# get() reaches the resolved value, for the boot consumers
is($cfg->get('database.password'), 'from-the-environment',
'get returns the resolved value');
}
# ---- resolver failures are loud ---------------------------------------------
{
write_file('punk.yml', "token: { \$env: PUNK_DEFINITELY_NOT_SET }\n");
my $err = '';
eval { Punk::Config->load(file => "$dir/punk.yml", env => 'none') }
or $err = $@;
like($err, qr/PUNK_DEFINITELY_NOT_SET.*not set/,
'a missing environment variable is fatal, not an empty password');
write_file('punk.yml', "token: { \$file: /no/such/secret }\n");
$err = '';
eval { Punk::Config->load(file => "$dir/punk.yml", env => 'none') }
or $err = $@;
like($err, qr/cannot read/, 'an unreadable secret file is fatal');
write_file('punk.yml', "token: { \$nope: x }\n");
$err = '';
eval { Punk::Config->load(file => "$dir/punk.yml", env => 'none') }
or $err = $@;
like($err, qr/unknown resolver/, 'an unknown resolver croaks, listing them');
}
# ---- a plaintext value is nobody else's business -----------------------------
#
# There used to be a guardrail here: a plaintext value under a key whose NAME
# looked secret-shaped warned, and `secrets => 'strict'` refused to boot. It
# is gone. Whether a password sits in the file is the decision of whoever
# writes the file, and a warning that fires on a key's name rather than on
# anything it knows is a warning people learn to ignore.
#
# These assert the silence, so reintroducing it fails here rather than in
# somebody's boot log.
{
unlink "$dir/punk.local.yml" if -e "$dir/punk.local.yml";
for my $key (qw(password passwd secret token api_key private_key
credentials auth)) {
write_file('punk.yml', "thing:\n $key: something\n");
my @warned;
{
local $SIG{__WARN__} = sub { push @warned, $_[0] };
Punk::Config->load(file => "$dir/punk.yml", env => 'none');
}
is(scalar @warned, 0, "a plaintext '$key' is loaded without comment");
}
write_file('punk.yml', "db:\n dsn: dbi:Pg:dbname=x;password=oops\n");
my @warned;
{
local $SIG{__WARN__} = sub { push @warned, $_[0] };
my $c = Punk::Config->load(file => "$dir/punk.yml", env => 'none');
is($c->get('db.dsn'), 'dbi:Pg:dbname=x;password=oops',
'and a dsn with the password in it is just a dsn');
}
is(scalar @warned, 0, 'no warning for that either');
# The resolvers are untouched: this is about not second-guessing a
# plaintext value, not about taking the alternative away.
local $ENV{PUNK_T_SECRET} = 'from-the-environment';
write_file('punk.yml', "database:\n password: { \$env: PUNK_T_SECRET }\n");
my $cfg = Punk::Config->load(file => "$dir/punk.yml", env => 'none');
is($cfg->secret('database.password'), 'from-the-environment',
'$env still resolves');
is($cfg->config->{database}{password}, '[redacted]',
'and a resolved secret is still redacted out of the public copy');
}
# ---- config drives the DSL ---------------------------------------------------
{
mkdir "$dir/templates";
open my $t, '>', "$dir/templates/hello.tmpl" or die $!;
print $t '<p>{% msg %}</p>';
close $t;
local $ENV{PUNK_TEST_DB_PASSWORD} = 'from-the-env';
write_file('punk.yml', <<"YAML");
app:
name: base
views:
Stencil:
template_dir: $dir/templates
database:
dsn: dbi:SQLite:dbname=$dir/app.db
password: { \$env: PUNK_TEST_DB_PASSWORD }
plugins:
ConfigTestMark: {}
YAML
write_file('punk.staging.yml', "app:\n name: staging\n");
unlink "$dir/punk.local.yml" if -e "$dir/punk.local.yml";
package Punk::Plugin::ConfigTestMark;
our @ISA = ('Punk::Plugin');
sub register {
my ($self, $app) = @_;
$app->helper(config_marked => sub { 'plugin from config' });
}
( run in 0.717 second using v1.01-cache-2.11-cpan-036bef1c656 )