MediaWiki  master
Shell.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Shell;
24 
25 use Hooks;
27 
44 class Shell {
45 
52  const NO_ROOT = 1;
53 
60  const SECCOMP = 2;
61 
67  const PRIVATE_DEV = 4;
68 
75  const NO_NETWORK = 8;
76 
83  const NO_EXECVE = 16;
84 
90  const NO_LOCALSETTINGS = 32;
91 
100  const RESTRICT_DEFAULT = self::NO_ROOT | self::SECCOMP | self::PRIVATE_DEV |
101  self::NO_LOCALSETTINGS;
102 
108  const RESTRICT_NONE = 0;
109 
119  public static function command( ...$commands ): Command {
120  if ( count( $commands ) === 1 && is_array( reset( $commands ) ) ) {
121  // If only one argument has been passed, and that argument is an array,
122  // treat it as a list of arguments
123  $commands = reset( $commands );
124  }
126  ->getShellCommandFactory()
127  ->create();
128 
129  return $command->params( $commands );
130  }
131 
137  public static function isDisabled() {
138  static $disabled = null;
139 
140  if ( is_null( $disabled ) ) {
141  if ( !function_exists( 'proc_open' ) ) {
142  wfDebug( "proc_open() is disabled\n" );
143  $disabled = true;
144  } else {
145  $disabled = false;
146  }
147  }
148 
149  return $disabled;
150  }
151 
163  public static function escape( ...$args ) {
164  if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
165  // If only one argument has been passed, and that argument is an array,
166  // treat it as a list of arguments
167  $args = reset( $args );
168  }
169 
170  $first = true;
171  $retVal = '';
172  foreach ( $args as $arg ) {
173  if ( $arg === null ) {
174  continue;
175  }
176  if ( !$first ) {
177  $retVal .= ' ';
178  } else {
179  $first = false;
180  }
181 
182  if ( wfIsWindows() ) {
183  // Escaping for an MSVC-style command line parser and CMD.EXE
184  // Refs:
185  // * https://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
186  // * https://technet.microsoft.com/en-us/library/cc723564.aspx
187  // * T15518
188  // * CR r63214
189  // Double the backslashes before any double quotes. Escape the double quotes.
190  $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
191  $arg = '';
192  $iteration = 0;
193  foreach ( $tokens as $token ) {
194  if ( $iteration % 2 == 1 ) {
195  // Delimiter, a double quote preceded by zero or more slashes
196  $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
197  } elseif ( $iteration % 4 == 2 ) {
198  // ^ in $token will be outside quotes, need to be escaped
199  $arg .= str_replace( '^', '^^', $token );
200  } else { // $iteration % 4 == 0
201  // ^ in $token will appear inside double quotes, so leave as is
202  $arg .= $token;
203  }
204  $iteration++;
205  }
206  // Double the backslashes before the end of the string, because
207  // we will soon add a quote
208  $m = [];
209  if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
210  $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
211  }
212 
213  // Add surrounding quotes
214  $retVal .= '"' . $arg . '"';
215  } else {
216  $retVal .= escapeshellarg( $arg );
217  }
218  }
219  return $retVal;
220  }
221 
236  public static function makeScriptCommand( $script, $parameters, $options = [] ): Command {
237  global $wgPhpCli;
238  // Give site config file a chance to run the script in a wrapper.
239  // The caller may likely want to call wfBasename() on $script.
240  Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
241  $cmd = [ $options['php'] ?? $wgPhpCli ];
242  if ( isset( $options['wrapper'] ) ) {
243  $cmd[] = $options['wrapper'];
244  }
245  $cmd[] = $script;
246 
247  return self::command( $cmd )
248  ->params( $parameters )
249  ->restrict( self::RESTRICT_DEFAULT & ~self::NO_LOCALSETTINGS );
250  }
251 }
const RESTRICT_DEFAULT
Apply a default set of restrictions for improved security out of the box.
Definition: Shell.php:100
static escape(... $args)
Version of escapeshellarg() that works better on Windows.
Definition: Shell.php:163
Executes shell commands.
Definition: Shell.php:44
$command
Definition: cdb.php:65
const NO_ROOT
Disallow any root access.
Definition: Shell.php:52
static getInstance()
Returns the global default instance of the top level service locator.
wfIsWindows()
Check if the operating system is Windows.
if( $line===false) $args
Definition: cdb.php:64
$wgPhpCli
Executable path of the PHP cli binary.
Class used for executing shell commands.
Definition: Command.php:36
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
const NO_LOCALSETTINGS
Deny access to LocalSettings.php (MW_CONFIG_FILE)
Definition: Shell.php:90
const NO_EXECVE
Deny execve syscall with seccomp.
Definition: Shell.php:83
const SECCOMP
Use seccomp to block dangerous syscalls.
Definition: Shell.php:60
const NO_NETWORK
Restrict the request to have no network access.
Definition: Shell.php:75
static command(... $commands)
Returns a new instance of Command class.
Definition: Shell.php:119
const PRIVATE_DEV
Create a private /dev.
Definition: Shell.php:67
Copyright (C) 2017 Kunal Mehta legoktm@member.fsf.org
Definition: Command.php:21
const RESTRICT_NONE
Don&#39;t apply any restrictions.
Definition: Shell.php:108
static isDisabled()
Check if this class is effectively disabled via php.ini config.
Definition: Shell.php:137
static makeScriptCommand( $script, $parameters, $options=[])
Generate a Command object to run a MediaWiki CLI script.
Definition: Shell.php:236
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200