Revision history for Object::Configure - Runtime Configuration for an Object
0.24 Thu Aug 20 02:41:40 PM EDT 2026
[ Enhancements ]
- configure() now validates the class name against /\A[A-Za-z_]\w*(?:::\w+)*\z/
and croaks on invalid input, preventing taint-mode failures and injection
through malformed package names.
- Added $RE_PATH_TRAVERSAL guard in configure() and _reload_object_config():
any config_file path containing ../ or /.. sequences is rejected with croak
before any filesystem probe, preventing directory traversal attacks.
- disable_hot_reload() now checks PID > 1 && PID != $$ before sending SIGTERM,
preventing accidental signalling of PID 1 (init) or the parent process itself.
- enable_hot_reload() and _run_config_watcher() now use defined-or (//) for the
interval parameter and clamp negative/zero values to $DEFAULT_INTERVAL,
preventing integer-wrap and busy-poll from a caller-supplied zero/negative value.
- Memoised _get_inheritance_chain() with %_chain_cache (keyed by class name):
repeated calls for the same class return immediately, eliminating redundant
mro::get_linear_isa traversals. Benchmarks show ~7× speedup on repeated calls.
- Memoised _find_class_config_file() with %_find_cache (keyed by NUL-joined
class+base+dirs): eliminates repeated filesystem probes for the same ancestor
class. Benchmarks show ~27× speedup on repeated calls.
- Replaced grep { $_ eq 'UNIVERSAL' } @mro with List::Util::any (XS,
short-circuits on first match).
- _find_class_config_file() now copies $dir to a local scalar before stripping
the trailing slash, avoiding aliased mutation of the caller's config_dirs array.
- Added t/cgi_security.t: 17-subtest security pen-test suite covering path
traversal, null-byte injection, command injection, CRLF injection, env-prefix
collision, XSS, config_dirs traversal, DoS, object injection, registry
poisoning, signal safety, arbitrary file read, deep-merge DoS, and input
validation guard clauses.
[ Bug Fixes ]
- Fixed instantiate(): 'class' key was read from %params but not deleted, causing
it to propagate through configure() as a spurious config key and end up in the
blessed object's hash, polluting its namespace. Changed $params->{'class'} to
delete $params->{'class'} (D~ data-flow anomaly — dead store without delete).
- Fixed class-name validation regex: \w+ after :: permitted digit-first components
(e.g. Bad::1Bad was accepted). Corrected to [A-Za-z_]\w* so each :: component
must start with a letter or underscore, matching Perl's actual identifier rules.
- Fixed _reload_object_config(): the $RE_PATH_TRAVERSAL guard was positioned AFTER
the -f filesystem test, meaning traversal paths for non-existent files were silently
ignored rather than rejected. Guard now fires before the -f check.
- Config::Abstraction, Log::Abstraction, and Return::Set all use eval internally
protect the caller's $@ from being clobbered by our internal eval blocks.
- Fix https://github.com/nigelhorne/Object-Configure/issues/7
- configure(): added $RE_PATH_TRAVERSAL guard for config_path (the env-only branch).
Previously, a caller with env-var control could set ClassName__config_path=../../etc/shadow
to force the hot-reload watcher to stat arbitrary system files (mtime side-channel via
SIGUSR1) and cause fatal taint violations under -T. Now rejected with croak before any
filesystem probe, matching the existing config_file guard.
- register_object(): added blessed($obj) guard. The POD has always required a blessed
reference, but the code only checked defined(). An adversary or buggy caller could push
thousands of unblessed entries, causing reload_config() to iterate them on every SIGUSR1
and degrading throughput proportionally (DoS). Unblessed refs now croak immediately.
[ Tests ]
- Expanded t/edge_cases.t from 28 to 62 subtests: added destructive, pathological,
boundary-condition, and security subtests across all public and private functions:
* Param-type hostility: undef params defaults to {} (intentional); arrayref params
surfaces Perl type error (documented hostile-input behaviour).
* Global variable integrity: $@, $_, and alarm() are all verified not to be
clobbered by configure() across its full code path.
* Return-type contracts (Test::Returns): configure() satisfies { type => hashref }
schema; reload_config() satisfies { type => integer } schema.
* Security / injection: null byte, CR, LF, and shell metacharacters (space ; | $)
in class name are rejected; _reload_object_config() traversal guard verified to
fire before -f filesystem check even for non-existent traversal paths.
* Filesystem hostility: directory as config_file (graceful), dangling symlink
(croaks with locale-safe OS error), unreadable file / chmod 000 (locale-safe
EACCES croak), config_dirs entry that is a plain file (ignored), empty-string
config_dirs entry (no crash).
* _deep_merge() boundary: (undef,undef)->undef, (undef,hashref)->overlay,
(hashref,scalar)->scalar, (hashref,arrayref)->arrayref, 50-level nesting no stack
overflow.
* Registry/signal safety: register_object(undef,*) and register_object(*,undef)
croak with usage; unblessed hashref now croaks (security fix S2);
disable_hot_reload() idempotent; enable_hot_reload(0/-9999) clamped to default;
double enable_hot_reload() is a no-op (no double-fork).
* Upstream failure (Mockingbird): Config::Abstraction::new returning 0 causes
carp and continues; configure() returns a valid hashref.
* DoS resilience: 500-key params hash handled without crash.
* Context safety: configure() in list context returns exactly one hashref.
* Regressions: digit-first :: component still rejected; traversal guard fires
before -f for non-existent paths; instantiate() with undef/missing class croaks;
caller-supplied _config_file/_config_files keys preserved in result.
- Added t/data-flow.t: 24 Define-Use (DU) chain subtests covering %stashed_values
stash-restore, $array_logger priority, %_find_cache memoization (both hit and
undef sentinel), %_chain_cache copy semantics, @config_files_to_load sort order,
_deep_merge non-mutation of inputs, carp_on_warn propagation, weak-ref GC
lifecycle, reload_config() dead-ref pruning, caller params non-mutation,
_config_file set-once semantics, %_config_file_stats synchronous population,
env_prefix :: → __ conversion, %tracked_files duplicate-file guard, O~ file
descriptor leak check (/proc/self/fd), $@/$_ non-pollution, _build_logger()
paths (undef/NULL/passthrough), and instantiate() 'class' key non-leakage.
- Expanded t/integration.t from 19 to 27 subtests: added optional-deps subtest
(Test::Without::Module), env-var precedence E2E, GC weak-ref pruning verification
(exercises the reload_config() bug fix), arrayref logger capture, logger isolation
between two concurrent objects, 3-level inheritance merge order, _config_file_stats
population, and security E2E (traversal + invalid class in full constructor workflow).
Key fixes applied during test writing:
- Arrayref loggers must use ->warn() (not ->debug()): 'debug' has syslog priority 7,
above the default Log::Abstraction threshold (warning = 4), so debug calls are
silently dropped; hashref logger specs are also not stashed before the config
merge, so a site-local UNIVERSAL logger config can override an explicit level.
- _config_file_stats is only populated via the primary-file branch (-r $config_file);
the fallback directory scan does not record stats. Tests that verify stat tracking
must supply an absolute config_file path.
- Expanded t/function.t from 35 to 55 subtests: added white-box coverage for the
S2 class-name guard (8 invalid partitions + 2 valid boundary cases), S1 path-traversal
guard, _reconfigure_logger() (2 subtests), _reload_object_config() (3 subtests
including traversal and private-key filtering), register_object() weak-reference
storage, restore_signal_handlers() after synthetic install, enable_hot_reload()
early-return guard, _find_class_config_file() trailing-slash non-mutation,
_get_inheritance_chain() memoisation cache, _deep_merge() arrayref replacement,
configure() env-var merge flow, and Test::Memory::Cycle circular-reference check.
- Expanded t/unit.t from 35 to 48 subtests: added API message ledger (tracks all
POD-documented error/warning messages; final subtest asserts ledger is empty),
coverage for the new invalid-class and path-traversal croak messages, mocked
Config::Abstraction::new to trigger the "Warning: Can't load configuration" carp,
$@ preservation test, alarm() non-interference test, _config_files metadata,
arrayref-logger priority over config-file logger, and instantiate() hot-reload
registration side-effect.
- Added t/domain.t: 41 EP/BVA subtests covering all input domains for all public
and private functions: class-name valid/invalid partitions, config_file/config_path
traversal boundaries, logger spec types (undef/NULL/arrayref/hashref/blessed),
register_object() unblessed-croak boundary (security fix S2), _deep_merge() type
combinations, _build_logger() all spec types, and combinatorial edge cases.
- Added t/path.t: 52 CFG path-coverage subtests exhausting every branching path
through configure(), register_object(), reload_config(), enable_hot_reload(),
disable_hot_reload(), _build_logger(), _find_class_config_file(), _deep_merge(),
_reload_object_config(), and _reconfigure_logger() — including signal-handler
chaining, config_path env-only stat branch, and injected upstream failures.
- Added t/transaction.t: 14 multi-step lifecycle subtests verifying state
consistency at every phase boundary and correct behaviour on mid-flight
failures: configure() full pipeline (T1), C::A failure rollback (T2),
env-override precedence chain file<env (T3), register→reload→update
synchronous hot-reload flow (T4), GC weak-ref pruning on reload (T5),
multi-object reload with partial GC (T6), same-class push semantics (T7),
nonexistent-config resilience (T8), _reload_object_config exception isolation
(T9), instantiate() lifecycle (T10-T11), and enable/disable watcher lifecycle
including idempotency (T12-T14).
0.23 Sun Jun 28 09:55:21 EDT 2026
[ Refactoring ]
- Replaced hand-rolled _walk_isa / _get_inheritance_chain with mro::get_linear_isa
(handles diamond inheritance correctly; UNIVERSAL appended manually when absent).
_walk_isa is removed; callers that called it directly will get "Undefined subroutine".
- Extracted _build_logger() private helper consolidating logger creation from
configure() and _reconfigure_logger(), eliminating the duplication.
- Added Readonly constants for all magic strings: $OS_WINDOWS, $LOGGER_NULL,
$SIG_DEFAULT, $SIG_IGNORE, $POLL_SLEEP, $KILL_TIMEOUT.
- Removed duplicate dead-code block in configure() (second identical
"if ($array && !$params->{'logger'}->{'array'})" check).
- Replaced POSIX::WNOHANG() fully-qualified call with imported WNOHANG.
- Added =head1 LIMITATIONS POD section documenting known design trade-offs.
- Readonly promoted from TEST_REQUIRES to PREREQ_PM (now used in lib/).
[ Bug Fixes ]
- Fixed configure() to give user-supplied arrayref logger priority over
config-file logger (previously the config file could silently override
an explicitly passed logger => \@arr).
- Fixed env-var ancestor merge order in configure() no-config-file path:
was iterating child-first (base-class env vars overrode child-class),
now iterates base-first so child correctly overrides base.
- Fixed _reload_object_config() logger-key match from /^logger/ to eq 'logger':
keys like 'logger.file' are flat config values, not logger specs, and must
not be processed through _reconfigure_logger().
- Fixed warn → carp in reload_config() for package-correct stack traces.
[ CI ]
- Bumped App::Test::Generator to 0.41 in both dashboard.yml and mutate.yml.
- Added --exclude lib/Devel --exclude lib/App/Test/Generator/Sample to the
non-ATG-repo generate-test-dashboard call in dashboard.yml (parity with
mutate.yml).
- Fixed empty-BASE guard in mutate.yml (no-op diff when all commits are bot).
- Added conditional $BASE_SHA_FLAG in mutate.yml (avoids passing empty --base_sha).
- Removed || true from all git pull --rebase lines in both workflow files.
- Fixed coverage snapshot to use pre-captured SNAPSHOT_SHA / SNAPSHOT_TIMESTAMP
instead of recomputing git rev-parse after bot commits moved HEAD.
[ Tests ]
- Added t/locales.t: verifies configure() error strings are locale-consistent
under en_US.UTF-8, de_DE.UTF-8, ja_JP.UTF-8 using local $! = ENOENT pattern.
- Updated t/function.t: replaced _walk_isa() subtests with _get_inheritance_chain()
equivalents to match refactored internals.
0.22
[ Bug Fixes ]
- Fixed https://github.com/nigelhorne/Object-Configure/issues/4
0.21 Thu May 21 07:55:13 EDT 2026
[Bug Fixes]
- Bump minimum version of Params::Get and Config::Abstraction
0.20 Tue May 19 08:51:20 EDT 2026
[Enhancement]
- Improve the method documentation
[Bug Fixes]
- Fixed disable_hot_reload() hanging indefinitely when child process does not exit on SIGTERM
During mutation testing the forked watcher process can be left in a state where it never receives or acts on SIGTERM, causing waitpid to block forever.
The fix replaces the unconditional waitpid with a non-blocking poll loop with a 5-second deadline, then escalates to SIGKILL if the child is still alive, followed by a final waitpid that is safe because SIGKILL cannot be caught or deferred.
- Fixed configure() logger dispatch ignoring NULL and re-wrapping existing Log::Abstraction instances
- Fixed looking in the correct directory for a object class to reload
- Fixed _reload_object_config() to use full config file paths during hot reload.
Previously attempted to reload from basename only (e.g., 'app.yml') instead
of full path stored in _config_files array. This caused -f test to fail and
reload to silently return without updating object properties or calling
_on_config_reload hooks. Now uses the last (most specific) path from
_config_files array when available, falling back to _config_file for
backward compatibility. Added explicit return statement for consistency.
- Sanity check that class is given to configure()
- Fixed register_object() to return void consistently.
Previously returned a coderef on first call (from $SIG{USR1} assignment),
undef on Windows, and false on subsequent calls. Now explicitly returns
nothing via 'return;' at end of function to match API specification.
- Fixed restore_signal_handlers() to return void consistently.
Previously returned undef when handler was defined and empty list when
not defined. Now explicitly returns nothing via 'return;' at end of
function to match API specification.
- Automatically preserve coderefs and blessed objects passed to configure().
Config::Abstraction treats unknown scalar values as config file paths,
which corrupts coderef and object references. The configure() function
now automatically stashes these values before processing and restores
them afterward, eliminating the need for users to implement manual
stash-delete-restore patterns in their constructors. The logger
parameter continues to receive special handling for wrapping in
Log::Abstraction. Added comprehensive test suite (t/coderef.t) to
verify preservation of coderefs and blessed objects.
- Fixed _deep_merge() to correctly return overlay value when overlay is not a hash.
Previously returned base value instead of overlay, violating the principle
that overlay should always take precedence. Changed line to
'return $overlay unless ref($overlay) eq "HASH"'.
- Fixed logger='NULL' handling to prevent unwanted logger creation.
When logger parameter was set to 'NULL', code would skip wrapping but fall
through to else clause which created a default Log::Abstraction anyway.
Restructured logic to explicitly check for 'NULL' first and preserve it
without creating any logger. Also optimized to avoid dereferencing
$params->{'logger'} twice by using the already-assigned $logger variable.
- Comprehensive POD documentation updates for all public methods.
Added detailed documentation for configure(), instantiate(),
enable_hot_reload(), disable_hot_reload(), reload_config(),
register_object(), restore_signal_handlers(), and get_signal_handler_info().
Each method now includes Purpose, Arguments, Returns, Side Effects, Notes,
Usage Example, API Specification (Params::Validate::Strict compatible
input schema and Return::Set compatible output schema), and Formal
Specification (Z notation). Added comprehensive white-box test suite
(t/function.t) covering all public and private functions.
0.19 Fri Dec 12 08:07:32 EST 2025
[Enhancement]
- Added UNIVERSAL configuration inheritance support.
All classes now automatically inherit from a universal.yml (or .conf,
.json, etc.) configuration file if present. This allows application-wide
defaults to be set once and inherited by all classes unless explicitly
overridden. Modified _walk_isa() to explicitly include UNIVERSAL in the
inheritance chain for classes with no explicit parents. Added test suite
(t/universal.t) to verify UNIVERSAL inheritance across multiple classes
and inheritance levels. Updated POD with UNIVERSAL CONFIGURATION section.
0.18 Thu Dec 11 11:58:01 EST 2025
If a parent class hierachy can't be found, ensure we still load the given path/file
0.17 Sat Dec 6 21:31:58 EST 2025
Load in parents classes as well
Don't assume English - fixes https://www.cpantesters.org/cpan/report/b1624b76-ad2b-11f0-852b-311a6e8775ea
0.16 Thu Oct 16 19:10:49 EDT 2025
Set croak_on_error
Latest testing console
0.15 Wed Sep 17 07:28:24 EDT 2025
Allow the logger to be NULL, in which case nothing is set up
Scalar::Utils was used but not imported
instantiate: avoid double blessing
Added testing dashboard on GitHub Pages
Pass schema through to Config::Abstraction
Added configuration hot reloading without restarting the application (not supported on Windows)
0.14 Wed Aug 27 13:16:36 EDT 2025
Allow the params to be undef
0.13 Wed Aug 20 09:39:00 EDT 2025
Ensure it works with Log::Abstraction 0.25 and bump to use that
Also fixes https://github.com/nigelhorne/Object-Configure/issues/2
0.12 Fri Aug 1 08:20:23 EDT 2025
Use Return::Set
Allow carp_on_warn to be read from the configuration file, and change the default to 0
0.11 Mon Jul 21 08:06:15 EDT 2025
Avoid encapsulating Log::Abstraction within Log::Abstraction
0.10 Wed Jun 18 14:54:59 EDT 2025
Use Config::Abstraction 0.10 to test environment settings
0.09 Wed Jun 18 09:09:58 EDT 2025
Fix tests in other languages
0.08 Fri Jun 6 08:35:54 EDT 2025
Removed a bunch of unneeded pre-reqs
Added the instantiate method
0.07 Thu May 29 09:35:38 EDT 2025
Renamed from Class::Debug to Object::Configure
Get the Abstract from the PM file
Give better error message
0.06 Thu May 22 21:11:29 EDT 2025
Ensure loggers aren't lost when it's a simple list
0.05 Tue May 20 21:21:09 EDT 2025
Fix CI
0.04 Tue May 20 21:14:56 EDT 2025
Try harder to avoid a logger hash within a logger hash
0.03 Fri May 16 11:45:00 EDT 2025
Added CI support
Added better reporting on error
Use the features of Config::Abstraction 0.25
0.02 Wed May 7 17:08:29 EDT 2025
Fix the return value
Added testing
0.01 Wed May 7 16:42:04 EDT 2025
First draft