Config-Abstraction
view release on metacpan or search on metacpan
[ Security ]
- Fix YAML code injection: YAML::XS v0.91 ignores $YAML::XS::DisableCode and
materialises callable CODE refs from !!perl/code tags. New private helper
_sanitize_yaml_values() recursively replaces CODE and GLOB refs with undef
after every LoadFile/Load call. Do not rely on DisableCode for security.
- Fix XML external entity (XXE) injection at all four XMLin() call sites: file
content is now pre-read and checked for <!ENTITY ... SYSTEM|PUBLIC declarations
before parsing; XMLin receives a string reference rather than a filename so no
entity is resolved. Files containing external entity declarations are skipped
with a carp warning.
- Fix XXE bypass via Config::Auto fallback: after the XML XXE guard blocked a
file and set $data to undef, YAML::XS returned a non-HASH scalar (truthy),
causing the if(!$data) fallback chain to run; Config::Auto then re-read and
expanded the entity. Fixed by saving raw file content before the parsers run
and conditioning the entire fallback chain on the absence of entity declarations.
- Fix encryption_key_file silent failure: when encryption_key_file was specified
explicitly in the constructor but the named file did not exist, the module
silently fell back to unencrypted operation. The constructor now croaks
immediately. Files sourced from environment variables still fail silently.
[ Performance ]
t/extended_tests.t view on Meta::CPAN
};
# ===========================================================================
# Config::Abstract hidden via local %INC + Test::Without::Module:
# - line 701 FALSE: _load_driver('Config::Abstract') returns 0
# - line 719 condition A=FALSE,B=TRUE: $data is truthy (scalar from YAML) but
# NOT a HASH ref, which is the one sub-condition path not yet covered.
#
# Content "just a plain string" is parsed by YAML::XS as a truthy scalar.
# XMLin at line 697 fails (not XML) so $data stays as that truthy scalar.
# With Config::Abstract blocked, line 701 is FALSE â $data unchanged â line 719:
# !$data = FALSE (data truthy)
# ref ne HASH = TRUE (ref is "")
# This is the previously-uncovered A=FALSE,B=TRUE condition path.
# ===========================================================================
subtest '_load_config() - 701 FALSE + 719 A=FALSE,B=TRUE via Config::Abstract hidden' => sub {
# Remove Config::Abstract from the %INC cache so that the eval "require"
# inside _load_driver will search @INC (where Test::Without::Module blocks it).
# 'local %INC' restores the cache after the subtest exits.
local %INC = %INC;
t/mutant_killers.t view on Meta::CPAN
my $cfg3 = Config::Abstraction->new(
data => { sentinel2 => 'safe2' },
config_dirs => [File::Spec->curdir()], # '.' â curdir
);
ok(defined($cfg3), 'object created with curdir config_dirs');
is($cfg3->get('sentinel2'), 'safe2', 'sentinel2 present with curdir effective_dir');
};
# ===========================================================================
# COND_INV_1365_8
# _load_config() - XXE blocked in all-parsers (config_file) XML path
# Kills: inverting the entity check would allow XXE to execute
# ===========================================================================
subtest '_load_config() - config_file XML with XXE entity blocked (COND_INV_1365_8)' => sub {
my $dir = tempdir(CLEANUP => 1);
# Use a full XML file with header so early XML detection fires (line 1363)
my $xxe = '<?xml version="1.0"?>' .
'<!DOCTYPE c [<!ENTITY e SYSTEM "file:///etc/passwd">]>' .
'<config><key>&e;</key></config>';
my $path = _write_file($dir, 'myconfig', $xxe);
my $cfg;
_silenced(sub {
eval {
t/mutant_killers.t view on Meta::CPAN
ok(defined($cfg), 'self-closing XML config_file handled');
# If COND_INV_1468_9 were inverted, XML::Simple would be skipped even when present
SKIP: {
skip 'XML::Simple not installed', 2
unless eval { require XML::Simple; 1 };
is($cfg->get('dbhost'), 'fallback-db', 'late XML fallback: attribute key loaded');
is($cfg->get('port'), '3307', 'late XML fallback: port attribute loaded');
}
};
subtest '_load_config() - late XML fallback with XXE entity blocked (COND_INV_1470_10)' => sub {
my $dir = tempdir(CLEANUP => 1);
# Self-closing XML with XXE â no <?xml header, so goes through late path
my $path = _write_file($dir, 'xxe_noheader',
'<!DOCTYPE d [<!ENTITY s SYSTEM "file:///etc/passwd">]><c k="&s;"/>');
my $cfg;
_silenced(sub {
eval {
$cfg = Config::Abstraction->new(
data => { guard => 'present' },
t/mutant_killers.t view on Meta::CPAN
);
is($cfg2->get('v'), 'my_plaintext', 'token from encrypt_value decrypts correctly');
};
# ===========================================================================
# COND_INV_2592_5
# _parse_config_string() - XXE entity check blocks expansion in parsed XML strings
# Kills: inverting !~ to =~ would allow XXE content, block clean XML
# Uses TestProxy to bypass the UNIVERSAL::isa access guard
# ===========================================================================
subtest '_parse_config_string() - XML with XXE entity blocked (COND_INV_2592_5)' => sub {
SKIP: {
skip 'XML::Simple not installed', 2
unless eval { require XML::Simple; 1 };
my $cfg = Config::Abstraction::MutantProxy->new(
data => { _init => 1 },
config_dirs => [],
);
my $xxe_xml = '<?xml version="1.0"?>' .
( run in 1.683 second using v1.01-cache-2.11-cpan-800906f7e73 )