fix: block eval of string BLOCK in instance method calls (GH #245)#418
fix: block eval of string BLOCK in instance method calls (GH #245)#418toddr-bot wants to merge 1 commit into
Conversation
Template::Document::new() called as an instance method (e.g.
template.new({BLOCK => '...'}) from within a template) now refuses
to eval string BLOCK arguments. This prevents arbitrary Perl code
execution that bypasses the EVAL_PERL option.
Coderef BLOCK arguments on instance calls are still permitted, and
class method calls (Template::Document->new) remain unrestricted
since they are the legitimate compilation path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Recreated from #352 (auto-closed when the |
|
What do we need to do to help this one move along? Really looking forward to this change. |
|
@Koan-Bot review |
Fix logic sound. Two blockers: test payloads use |
PR Review — fix: block eval of string BLOCK in instance method calls (GH #245)Security fix logic is correct but has a DEFBLOCKS bypass and tests don't actually validate the guard.
🔴 Blocking1. Test payload doesn't prove the fix works (`t/document.t`, L69-70)The payload Without the fix, the original code path runs: $block = eval 'die "should not run"'; # $block = undef, $@ set
return $class->error($@) unless defined $block; # returns undefSo the test passes regardless of whether the security check exists. Use a payload that would succeed under eval to actually validate the guard: my $bad = $doc->new({ BLOCK => 'sub { return "should not run" }' });
ok( ! defined $bad, 'instance->new() with string BLOCK returns undef' );This eval would produce a valid coderef and return a blessed object without the fix (test fails), but returns undef with the fix (test passes). Same issue affects the coderef and class-method tests — they're fine individually, but the critical "blocked" case is untested. 2. Template test has the same false-pass problem (`t/document.t`, L82-85)Same issue as the unit test: Replace with a payload that evals successfully: Without the fix: eval succeeds, 🟢 Suggestions1. Guard should also cover DEFBLOCKS string values (`lib/Template/Document.pm`, L69-81)Lines 86-92 of the original code (after the fix block) eval string values in map {
ref($_)
? $_
: ( /(.*)/s && eval($1) or return $class->error($@) )
} values %$defblocks;An attacker could pass Consider extending the instance-method guard to also reject string values in DEFBLOCKS, or reject DEFBLOCKS entirely on instance calls. Checklist
To rebase specific severity levels, mention me: Automated review by Kōan (Claude · model claude-opus-4-6) |
Koan-Bot
left a comment
There was a problem hiding this comment.
Blocking issues found — see the review comment above.
What
Prevent arbitrary Perl code execution via
template.new({BLOCK => '...'})whenEVAL_PERLis disabled.Why
Fixes GH #245.
Template::Document::new()callseval $blockon string BLOCK arguments — this happens regardless of theEVAL_PERLconfig setting. Sincetemplateis exposed as a stash variable, a template author can calltemplate.new({BLOCK => 'malicious code'})to execute arbitrary Perl, bypassing theEVAL_PERLsecurity control.How
When
new()is called as an instance method (detected viaref $class), string BLOCK arguments are rejected with an error. This blocks the template-side attack vector while preserving:Template::Document->new({BLOCK => $string})) — the legitimate compilation pathTesting
t/document.tcovering: instance+string (blocked), instance+coderef (allowed), class+string (allowed)🤖 Generated with Claude Code