diff --git a/src/Codeception/Command/Console.php b/src/Codeception/Command/Console.php index a124de459c..1d95cca00e 100644 --- a/src/Codeception/Command/Console.php +++ b/src/Codeception/Command/Console.php @@ -54,9 +54,19 @@ public function getDescription() public function execute(InputInterface $input, OutputInterface $output) { $suiteName = $input->getArgument('suite'); - $this->output = $output; + // Validate suite name before using it + if (!preg_match('/^[a-zA-Z0-9_]+$/', $suiteName)) { + $output->writeln("Invalid suite name."); + return; + } - $config = Configuration::config($input->getOption('config')); + $this->output = $output; + $configPath = $input->getOption('config'); + if ($configPath && !preg_match('/^[a-zA-Z0-9_\-\/\.]+$/', $configPath)) { + $output->writeln("Invalid config file path."); + return; + } + $config = Configuration::config($configPath); $settings = Configuration::suiteSettings($suiteName, $config); $options = $input->getOptions(); @@ -112,8 +122,13 @@ protected function executeCommands(InputInterface $input, OutputInterface $outpu { $dialog = new QuestionHelper(); - if (file_exists($bootstrap)) { - require $bootstrap; + $projectRoot = realpath(__DIR__ . '/../../../'); + $bootstrapPath = realpath($bootstrap); + + if ($bootstrapPath && strpos($bootstrapPath, $projectRoot) === 0 && file_exists($bootstrapPath)) { + require $bootstrapPath; + } else { + $output->writeln("Invalid bootstrap file path."); } do { diff --git a/src/Codeception/Command/Shared/Config.php b/src/Codeception/Command/Shared/Config.php index 64c59ab483..7d389ae38e 100644 --- a/src/Codeception/Command/Shared/Config.php +++ b/src/Codeception/Command/Shared/Config.php @@ -6,6 +6,13 @@ trait Config { protected function getSuiteConfig($suite, $conf) { + // Allow only alphanumeric, underscore, and dash + if (!preg_match('/^[\w\-]+$/', $suite)) { + throw new \InvalidArgumentException("Invalid suite name."); + } + if (!preg_match('/^[\w\-\.\/]+$/', $conf)) { + throw new \InvalidArgumentException("Invalid config path."); + } $config = Configuration::config($conf); return Configuration::suiteSettings($suite, $config); } @@ -21,4 +28,4 @@ protected function getSuites($conf) return Configuration::suites(); } -} \ No newline at end of file +} diff --git a/src/Codeception/Lib/Driver/PostgreSql.php b/src/Codeception/Lib/Driver/PostgreSql.php index 07e7eb748d..0299591c00 100644 --- a/src/Codeception/Lib/Driver/PostgreSql.php +++ b/src/Codeception/Lib/Driver/PostgreSql.php @@ -45,22 +45,46 @@ public function load($sql) public function cleanup() { + // Get all tables in the public schema and generate DROP statements $tables = $this->dbh - ->query("SELECT 'DROP TABLE IF EXISTS \"' || tablename || '\" cascade;' FROM pg_tables WHERE schemaname = 'public';") + ->query(" + SELECT 'DROP TABLE IF EXISTS ' || quote_ident(tablename) || ' CASCADE;' + FROM pg_tables + WHERE schemaname = 'public'; + ") ->fetchAll(); + // Get all sequences and generate DROP statements $sequences = $this->dbh - ->query("SELECT 'DROP SEQUENCE IF EXISTS \"' || relname || '\" cascade;' FROM pg_class WHERE relkind = 'S';") + ->query(" + SELECT 'DROP SEQUENCE IF EXISTS ' || quote_ident(relname) || ' CASCADE;' + FROM pg_class + WHERE relkind = 'S'; + ") ->fetchAll(); - $types = $this->dbh - ->query("SELECT 'DROP TYPE IF EXISTS \"' || pg_type.typname || '\" cascade;' FROM pg_type JOIN pg_enum ON pg_enum.enumtypid = pg_type.oid GROUP BY pg_type.typname;") + // Get all enum types and generate DROP statements + $types = $this->dbh + ->query(" + SELECT 'DROP TYPE IF EXISTS ' || quote_ident(pg_type.typname) || ' CASCADE;' + FROM pg_type + JOIN pg_enum ON pg_enum.enumtypid = pg_type.oid + GROUP BY pg_type.typname; + ") ->fetchAll(); + // Merge all DROP statements into a single array $drops = array_merge($tables, $sequences, $types); + + // Execute each DROP statement safely if ($drops) { foreach ($drops as $drop) { - $this->dbh->exec($drop[0]); + try { + $this->dbh->exec($drop[0]); + } catch (\PDOException $e) { + // Log the error but continue with the next DROP + error_log("Failed to execute: " . $drop[0] . " - " . $e->getMessage()); + } } } }