DBIO

 view release on metacpan or  search on metacpan

t/generate/08-codegen-injection.t  view on Meta::CPAN

    *{"${pkg}::import"} = sub { return; };
  }

  # DBIO::Core: usable both as `use base 'DBIO::Core'` and as the method sink
  # for __PACKAGE__->table(...) etc. Provide no-op methods for everything except
  # result_source_instance / meta, which need to return chainable objects below.
  for my $f (@DSL_FUNCS) {
    next if $f eq 'result_source_instance' || $f eq 'meta';
    *{"DBIO::Core::${f}"} = sub { return; };
  }
  # result_source_instance must return an object that also no-ops view_definition
  *{"DBIO::Core::result_source_instance"} = sub {
    return bless {}, 'DBIO::Core::FakeRSI';
  };
  *{"DBIO::Core::FakeRSI::view_definition"} = sub { return; };
  *{"DBIO::Core::meta"} = sub { return bless {}, 'DBIO::Core::FakeMeta'; };
  *{"DBIO::Core::FakeMeta::make_immutable"} = sub { return; };

  # Prime %INC so `use`/`require` of these never hits the real modules.
  for my $pkg (qw(DBIO::Cake DBIO::Candy DBIO::Core Moo Moose
                  MooX::NonMoose MooseX::NonMoose MooseX::MarkAsMethods)) {
    (my $file = "${pkg}.pm") =~ s{::}{/}g;
    $INC{$file} ||= __FILE__;
  }
}

my %style = (
  vanilla => 'DBIO::Generate::Style::Vanilla',
  cake    => 'DBIO::Generate::Style::Cake',
  candy   => 'DBIO::Generate::Style::Candy',
  moo     => 'DBIO::Generate::Style::Moo',
  moose   => 'DBIO::Generate::Style::Moose',
);

my $n = 0;
sub require_generated {
  my ($code, $tag) = @_;
  $n++;
  my $file = File::Spec->catfile($tmpdir, "Gen${n}.pm");
  open my $fh, '>', $file or die "open $file: $!";
  print $fh $code;
  close $fh;

  # Compile/run the generated module exactly like require would.
  unlink $sentinel if -e $sentinel;
  my $ok = eval { require $file; 1 };
  my $err = $@;
  return ($ok, $err, $file);
}

# --- Hostile specs: injected code must NOT run, for every style -------------
my $i = 0;
for my $name (sort keys %style) {
  my $class = "Injection::Test::" . ucfirst($name) . (++$i);
  my $spec  = hostile_spec($class);

  my $code = eval { $style{$name}->emit($spec) };
  ok defined $code, "$name: emit() produced source for hostile spec"
    or do { diag "emit died: $@"; next };

  my ($ok, $err) = require_generated($code, $name);

  # The generated module may or may not compile cleanly (a poisoned identifier
  # safely embedded as a string literal yields valid Perl; we don't require
  # compilation to fail). What MUST hold: no injected code executed.
  ok !-e $sentinel,
    "$name: hostile identifiers did NOT execute injected code (no sentinel)";

  unlink $sentinel if -e $sentinel;
}

# --- Benign negative control: clean spec loads, creates no sentinel ---------
for my $name (sort keys %style) {
  my $class = "Benign::Test::" . ucfirst($name);
  my $spec  = {
    moniker      => 'User',
    class        => $class,
    table        => 'users',
    column_order => [qw/id name/],
    columns      => {
      id   => { data_type => 'integer', is_auto_increment => 1, is_nullable => 0 },
      name => { data_type => 'varchar', size => 100, is_nullable => 1 },
    },
    pk    => [qw/id/],
    uniq  => [],
    relationships => [],
    extra_statements => [],
    is_view => 0,
    view_definition => undef,
    result_base_class => 'DBIO::Core',
    components => [],
    additional_classes => [],
  };

  my $code = $style{$name}->emit($spec);
  my ($ok, $err) = require_generated($code, "benign-$name");
  ok $ok, "$name: benign spec compiles cleanly" or diag $err;
  ok !-e $sentinel, "$name: benign spec creates no sentinel";

  unlink $sentinel if -e $sentinel;
}

# --- Package-name validator must croak on a hostile class -------------------
{
  my $evil_class = q{Foo; BEGIN { open my $f,'>',$ENV{DBIO_INJECTION_SENTINEL} } package Bar};
  for my $name (sort keys %style) {
    my $spec = hostile_spec($evil_class);
    my $code = eval { $style{$name}->emit($spec) };
    ok( !defined($code) && $@,
      "$name: emit() croaks on a hostile package/class name" )
      or diag "expected croak, got code:\n$code";
  }

  # And the helper itself, directly.
  eval { DBIO::Generate::Util::assert_pkg($evil_class) };
  like $@, qr/package name/, 'assert_pkg croaks on hostile package name';

  eval { DBIO::Generate::Util::assert_pkg('My::Schema::Result::User') };
  is $@, '', 'assert_pkg accepts a well-formed package name';
}

ok !-e $sentinel, 'final: sentinel was never created across the whole test';

done_testing;



( run in 0.479 second using v1.01-cache-2.11-cpan-6aa56a78535 )