The TryingToScale PHP framework.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
tts_framework/src/bootstrap/requires.php

185 lines
6.2 KiB

<?php
declare(strict_types=1);
/**
* @author Robert Strutts <Robert@TryingToScale.com>
* @copyright Copyright (c) 2022, Robert Strutts.
* @license MIT
*/
namespace bs_tts;
final class requires {
public static function is_valid_file(string $filename): bool {
if (is_string($filename) && strlen($filename) < 64) {
return (self::is_dangerous($filename) === false) ? true : false;
}
return false;
}
public static function is_dangerous(string $file): bool {
// Make sure the file does not contain null bytes to avoid PHAR file attacks
if (strpos($file, "\x00") !== false) {
return true;
}
// Remove non-visible characters
$file = preg_replace('/[\x00-\x1F\x7F]/u', '', $file);
if (strpos($file, "..") !== false || strpos($file, "./") !== false) {
return true; // .. Too dangerious, up path attack
}
/*
* :// Too dangerious, PHAR file execution of serialized code injection, etc...
* Also, prevent remote code execution from http://, ftp://
*/
if (strpos($file, "://") !== false) {
return true;
}
return false;
}
public static function filter_file_name(string $file): string {
return preg_replace('/[^-a-z0-9._\\/\s]+/i', "", $file);
}
/**
* Do not allow periods in folder names!
* @param string $dir
* @return string
*/
public static function filter_dir_path(string $dir): string {
return preg_replace('/[^a-z0-9_\\/]+/i', "", $dir);
}
private static function get_PHP_Version_for_file(string $file): string|bool {
if (file_exists($file . PHP_MAJOR_VERSION . PHP_MINOR_VERSION) && is_readable($file . PHP_MAJOR_VERSION . PHP_MINOR_VERSION)) {
return $file . PHP_MAJOR_VERSION . PHP_MINOR_VERSION;
} elseif (file_exists($file . PHP_MAJOR_VERSION) && is_readable($file . PHP_MAJOR_VERSION)) {
return $file . PHP_MAJOR_VERSION;
} elseif (file_exists($file) && is_readable($file)) {
return $file;
} else {
return false;
}
}
/**
* @return Cleaned DIR or False
*/
public static function safer_dir_exists(string $dir): string|bool {
if (self::is_dangerous($dir) || empty($dir)) {
return false;
}
$dir = str_replace('_DS_', '/', $dir);
$dir = self::filter_dir_path($dir);
$realpath = realpath($dir);
if ($realpath === false) {
return false;
}
$dir = escapeshellcmd($realpath);
return (file_exists($dir)) ? $dir : false;
}
/**
* @return Cleaned FileName or False
*/
public static function safer_file_exists(string $file, string $dir=""): string|bool {
if (self::is_dangerous($file)) {
return false;
}
if (self::is_dangerous($dir)) {
return false;
}
if (empty($file)) {
return false;
}
$pos = \bs_tts\common::string_last_position($file, ".");
if ($pos === false) {
$file_type = ".php";
$file_name = $file;
} else {
$file_name = \bs_tts\common::get_string_left($file, $pos);
$file_kind = \bs_tts\common::get_string_right($file, \bs_tts\common::string_length($file) - $pos);
$file_type = match ($file_kind) {
".tpl" => ".tpl",
default => ".php",
};
}
$filtered_file = self::filter_file_name($file_name);
if (empty($dir)) {
$file_plus_dir = $filtered_file;
} else {
$filtered_dir = rtrim(self::filter_dir_path($dir), '/') . '/';
$file_plus_dir = $filtered_dir . $filtered_file;
}
$escaped_file = escapeshellcmd($file_plus_dir . $file_type);
return self::get_PHP_Version_for_file($escaped_file);
}
private static function ob_starter() {
if (extension_loaded('mbstring')) {
ob_start('mb_output_handler');
} else {
ob_start();
}
}
private static function secure_file(bool $return_contents, string $file, string $path, $local = null, array $args = array(), bool $load_once = true) {
$dir = match ($path) {
"dir_fixed" => "",
"framework", "tts" => \main_tts\TTS_FRAMEWORK,
"on_error" => \bs_tts\site_helper::get_root() . \bs_tts\site_helper::get_project() . "views/on_error/",
default => \bs_tts\site_helper::get_root(), // project
};
$versioned_file = self::safer_file_exists($file, $dir);
if ($versioned_file === false) {
return false;
}
if (is_array($args)) {
if (isset($args["return_contents"])
|| isset($args["script_output"])
|| isset($args["versioned_file"])
) {
return false; // Args are Dangerious!! Abort
}
extract($args, EXTR_PREFIX_SAME, "dup");
}
if ($return_contents) {
$script_output = (string) ob_get_clean();
self::ob_starter();
include $versioned_file;
$script_output .= (string) ob_get_clean();
return $script_output;
} else {
return ($load_once) ? include_once($versioned_file) : include($versioned_file);
}
}
/**
* Use secure_include instead of include.
* @param string $file script
* @param string $path (framework or project)
* @param type $local ($this)
* @param array $args ($var to pass along)
* @param bool $load_once (default true)
* @return results of include or false if failed
*/
public static function secure_include(string $file, string $path = 'project', $local = null, array $args = array(), bool $load_once = true) {
return self::secure_file(false, $file, $path, $local, $args, $load_once);
}
public static function secure_get_content(string $file, string $path = 'project', $local = null, array $args = array(), bool $load_once = true) {
return self::secure_file(true, $file, $path, $local, $args, $load_once);
}
}