scriptbox

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 12b41cbdf124960e6204ee6a656c7901cfe838a3
parent 326a85625915ed8fe4c29330184d2f81305ccd88
Author: Jan Dankert <develop@jandankert.de>
Date:   Mon, 27 Jun 2022 00:41:50 +0200

Fetched from upstream.

Diffstat:
Mdsl/ast/DslFunctionCall.class.php | 9++-------
Mdsl/ast/DslProperty.class.php | 10++++++----
Mdsl/ast/DslVariable.class.php | 2+-
Adsl/context/BaseScriptableObject.class.php | 21+++++++++++++++++++++
Ddsl/context/DslFunction.class.php | 8--------
Ddsl/context/DslObject.class.php | 9---------
Adsl/context/Scriptable.class.php | 16++++++++++++++++
Mdsl/executor/DslInterpreter.class.php | 2++
Adsl/standard/Date.class.php | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adsl/standard/Helper.class.php | 15+++++++++++++++
Mdsl/standard/Script.class.php | 17+++++++++++++++--
Mdsl/standard/StandardArray.class.php | 17++++++++++++++++-
Mdsl/standard/StandardDate.class.php | 45++++++++++++++++++++++++++++++++++++++++++++-
Mdsl/standard/StandardMath.class.php | 4+++-
Adsl/standard/System.class.php | 36++++++++++++++++++++++++++++++++++++
Mdsl/standard/Write.class.php | 6++----
Mindex.php | 32+++++++++++++++++++-------------
17 files changed, 261 insertions(+), 51 deletions(-)

diff --git a/dsl/ast/DslFunctionCall.class.php b/dsl/ast/DslFunctionCall.class.php @@ -43,12 +43,7 @@ class DslFunctionCall implements DslStatement if ( ! is_array($parameterValues)) $parameterValues = array($parameterValues); - if ( $function instanceof \dsl\context\DslFunction ) { - // call "external" native function - - return call_user_func_array(array($function,'execute'),$parameterValues ); - } - elseif ( $function instanceof DslFunction ) { + if ( $function instanceof DslFunction ) { $parameters = $function->parameters; @@ -56,7 +51,7 @@ class DslFunctionCall implements DslStatement throw new DslRuntimeException('function call has '.sizeof($parameterValues).' parameters but the function has '.sizeof($parameters).' parameters'); // Put all function parameters to the function context. - $parameterContext = array_combine( $parameters, $parameterValues ); + $parameterContext = array_combine( $parameters, array_slice($parameterValues,0,sizeof($parameters)) ); $subContext = array_merge( $context,$parameterContext ); return $function->execute( $subContext ); diff --git a/dsl/ast/DslProperty.class.php b/dsl/ast/DslProperty.class.php @@ -3,6 +3,7 @@ namespace dsl\ast; use cms\generator\dsl\DslObject; +use dsl\context\Scriptable; use dsl\DslRuntimeException; class DslProperty implements DslStatement @@ -40,6 +41,11 @@ class DslProperty implements DslStatement // copy object methods to the object context to make them callable. foreach( get_class_methods( $object ) as $method ) { $objectContext[ $method ] = function() use ($method, $object) { + + // For Security: Do not expose all available objects, they must implement a marker interface. + if ( ! $object instanceof Scriptable ) + throw new DslRuntimeException('security: Object '.get_class($object).' is not scriptable and therefore not available in script context'); + return call_user_func_array( array($object,$method),func_get_args() ); }; } @@ -55,10 +61,6 @@ class DslProperty implements DslStatement $prop = $this->property->execute( $objectContext ); - // TODO: how to recognize objects - // For Security: Do not expose internal objects. - //if ( is_object($prop) && ! $prop instanceof DslObject ) - // $prop = '@'.get_class($prop).'@'; return $prop; } diff --git a/dsl/ast/DslVariable.class.php b/dsl/ast/DslVariable.class.php @@ -20,7 +20,7 @@ class DslVariable implements DslStatement public function execute( & $context ) { if ( ! array_key_exists( $this->name, $context ) ) - throw new DslRuntimeException('variable \''.$this->name.'\' does not exist.'); + throw new DslRuntimeException('\''.$this->name.'\' does not exist'); return $context[ $this->name ]; } diff --git a/dsl/context/BaseScriptableObject.class.php b/dsl/context/BaseScriptableObject.class.php @@ -0,0 +1,20 @@ +<?php + + +namespace dsl\context; + +use dsl\standard\Helper; + +class BaseScriptableObject implements Scriptable +{ + + public function __toString() + { + return "Script object"; + } + + public function help() + { + return Helper::getHelp($this); + } +} +\ No newline at end of file diff --git a/dsl/context/DslFunction.class.php b/dsl/context/DslFunction.class.php @@ -1,7 +0,0 @@ -<?php - -namespace dsl\context; - -interface DslFunction -{ -} -\ No newline at end of file diff --git a/dsl/context/DslObject.class.php b/dsl/context/DslObject.class.php @@ -1,8 +0,0 @@ -<?php - -namespace dsl\context; - -interface DslObject -{ - -} -\ No newline at end of file diff --git a/dsl/context/Scriptable.class.php b/dsl/context/Scriptable.class.php @@ -0,0 +1,15 @@ +<?php + +namespace dsl\context; + +/** + * Class is callable from scripts. + * + * Classes whose methods should be callable from user scripts must implement this interface. + * + * This is a marker interface which do not has methods by design. + * + * If a class do not implement this interface then its methods cannot be called out of a script. + * This is for security reasons: You cannot expose your classes to the user context by mistake. + */ +interface Scriptable {} +\ No newline at end of file diff --git a/dsl/executor/DslInterpreter.class.php b/dsl/executor/DslInterpreter.class.php @@ -9,6 +9,7 @@ use dsl\standard\Script; use dsl\standard\StandardArray; use dsl\standard\StandardDate; use dsl\standard\StandardMath; +use dsl\standard\System; use dsl\standard\Write; class DslInterpreter @@ -38,6 +39,7 @@ class DslInterpreter // Standard-Globals $this->addContext( [ + 'System'=> new System(), 'Math' => new StandardMath(), 'Array' => new StandardArray(), 'Date' => new StandardDate(), diff --git a/dsl/standard/Date.class.php b/dsl/standard/Date.class.php @@ -0,0 +1,62 @@ +<?php + +namespace dsl\standard; + +use dsl\context\BaseScriptableObject; + + +/** + * Date. + * + * Similar to the javascript Date object + */ +class Date extends BaseScriptableObject +{ + private $time; + + /** + * @param integer $time unix timestamp + */ + public function __construct( $time = null ) + { + if ( $time == null ) + $time = time(); + + $this->time = $time; + } + + + public function getDate() { return date('d',$this->time); } + public function getDay() { return date('w',$this->time); } + public function getFullYear() { return date('Y',$this->time); } + public function getHours() { return date('H',$this->time); } + public function getMilliseconds() { return 0; } + public function getMinutes() { return date('i',$this->time); } + public function getMonth() { return date('m',$this->time); } + public function getSeconds() { return date('s',$this->time); } + public function getTime() { return $this->time * 1000; } + public function getTimezoneOffset() { return date('y',$this->time)/60;} + public function getUTCDate() { return date('d',$this->time);} + public function getUTCDay() { return date('w',$this->time);} + public function getUTCFullYear() { return date('Y',$this->time);} + public function getUTCHours() { return date('H',$this->time);} + public function getUTCMilliseconds() { return 0;} + public function getUTCMinutes() { return date('i',$this->time);} + public function getUTCMonth() { return date('m',$this->time);} + public function getUTCSeconds() { return date('s',$this->time);} + public function getYear() { return date('y',$this->time); } + + public function __toString() + { + return date('r'); + } + + + /** + * @return string + */ + public function help() + { + return Helper::getHelp($this); + } +} +\ No newline at end of file diff --git a/dsl/standard/Helper.class.php b/dsl/standard/Helper.class.php @@ -0,0 +1,14 @@ +<?php + + +namespace dsl\standard; + + +class Helper +{ + public static function getHelp( $obj ) { + + return 'Object properties: '.implode(', ',array_map(function($property) { return ''.$property; },get_object_vars($obj))).';'. + ' methods: '.implode(', ',array_map(function($property) { return ''.$property.'()'; },get_class_methods($obj))); + } +} +\ No newline at end of file diff --git a/dsl/standard/Script.class.php b/dsl/standard/Script.class.php @@ -3,10 +3,10 @@ namespace dsl\standard; use dsl\ast\DslStatement; -use dsl\context\DslObject; +use dsl\context\BaseScriptableObject; use dsl\DslToken; -class Script implements DslObject +class Script extends BaseScriptableObject { /** * @var DslToken[] @@ -53,4 +53,17 @@ class Script implements DslObject { return print_r($this->ast,true); } + + public function __toString() + { + return "Script Info, call help() for help."; + } + + /** + * @return string + */ + public function help() + { + return Helper::getHelp($this); + } } \ No newline at end of file diff --git a/dsl/standard/StandardArray.class.php b/dsl/standard/StandardArray.class.php @@ -1,11 +1,26 @@ <?php namespace dsl\standard; -class StandardArray +use dsl\context\BaseScriptableObject; + +class StandardArray extends BaseScriptableObject { public function of() { return func_get_args(); } + + public function __toString() + { + return "Arrays:Object"; + } + + /** + * @return string + */ + public function help() + { + return Helper::getHelp($this); + } } \ No newline at end of file diff --git a/dsl/standard/StandardDate.class.php b/dsl/standard/StandardDate.class.php @@ -1,7 +1,9 @@ <?php namespace dsl\standard; -class StandardDate +use dsl\context\BaseScriptableObject; + +class StandardDate extends BaseScriptableObject { /** * Date.now() @@ -14,4 +16,45 @@ class StandardDate return time(); } + + + + /** + * Gets the current date object. + * @return Date + */ + public function getDate( $date = null ) { + + return new Date( $date ); + } + + + + /** + * Gets the current date object for a given date. + * @return Date + */ + public function getDateFor( $year = 0,$month = 0,$day = 0,$hour = 0,$minute = 0,$second = 0 ) { + + $month++; // month in JS is 0-based, but in PHP not. + + return new Date( mktime( $hour, $minute, $second, $month, $day, $year ) ); + } + + + public function __toString() + { + return "Arrays:Object"; + } + + public function help() + { + return Helper::getHelp($this); + } + + + public function parse( $dateAsString ) { + + return strtotime( $dateAsString ); + } } \ No newline at end of file diff --git a/dsl/standard/StandardMath.class.php b/dsl/standard/StandardMath.class.php @@ -1,7 +1,9 @@ <?php namespace dsl\standard; -class StandardMath +use dsl\context\BaseScriptableObject; + +class StandardMath extends BaseScriptableObject { public $E = M_EULER; public $PI = M_PI; diff --git a/dsl/standard/System.class.php b/dsl/standard/System.class.php @@ -0,0 +1,35 @@ +<?php + +namespace dsl\standard; + +use dsl\context\BaseScriptableObject; + +class System extends BaseScriptableObject +{ + /** + * runtime + * @var string + */ + public $version; + + /** + * Operating system + * @var string + */ + public $os; + + public function __construct() + { + $this->version = PHP_VERSION; + $this->os = PHP_OS; + } + + /** + * @param $name + * @return array|false|string + */ + public function env( $name ) { + + return getenv('SCRIPTBOX_'.$name); + } +} +\ No newline at end of file diff --git a/dsl/standard/Write.class.php b/dsl/standard/Write.class.php @@ -2,13 +2,11 @@ namespace dsl\standard; -use dsl\context\DslFunction; - -class Write implements DslFunction +class Write { public $buffer; - public function execute( $text ) + public function __invoke( $text ) { $this->buffer .= $text; } diff --git a/index.php b/index.php @@ -5,7 +5,7 @@ use dsl\executor\DslInterpreter; require('./autoload.php'); ?><html> -<head><title>Script Sandbox</title> +<head><title>Scriptbox</title> <style> textarea { width: 100%; @@ -16,9 +16,8 @@ require('./autoload.php'); <body> -</body></html> -<h1>Script Sandbox</h1> -<p>Script sandbox for PHP. The syntax is a subset of Javascript. The interpreter is supporting functions, full arithmetic calculations, for-loops, if-else statements. +<h1>Scriptbox</h1> +<p>Scriptbox is a Script sandbox for PHP.</p><p>The syntax is a subset of Javascript.<br/> The interpreter is supporting functions, full arithmetic calculations, for-loops, if-else statements. </p> <?php $code = @$_POST['code'] ?: <<<DEF // Script sandbox @@ -64,12 +63,12 @@ for( name of names ) { DEF ; ?> -<legend title="Script output"> -<pre> - <?php +<fieldset> +<legend>Output</legend> +<pre><?php try { error_reporting( E_ALL ); - $interpreter = new DslInterpreter( DslInterpreter::FLAG_SHOW_ERROR + DslInterpreter::FLAG_SHOW_TRACE ); + $interpreter = new DslInterpreter( DslInterpreter::FLAG_SHOW_ERROR ); $interpreter->runCode( $code ); echo htmlentities( $interpreter->getOutput() ); } catch( Exception $e ) { @@ -77,8 +76,14 @@ DEF echo "Unexcepted error while running the script: \n".htmlentities( $e->getMessage() )."\n"; } ?> -</pre></legend> -<form action="./" method="POST"><textarea name="code"><?php echo htmlentities( $code ) ?> -</textarea> -<input type="submit" value="Execute code"/> -</form> +</pre> +</fieldset> + <fieldset> + <legend>Source</legend> + <form action="./" method="POST"> + <textarea name="code" rows="50" style="border:0;"><?php echo htmlentities( $code ) ?></textarea> + <input type="submit" value="Execute" /> + </form> + </fieldset> + +</body></html> +\ No newline at end of file