Skip to content

Instantly share code, notes, and snippets.

@kbanman
Created September 13, 2012 00:02
Show Gist options
  • Save kbanman/3710886 to your computer and use it in GitHub Desktop.
Save kbanman/3710886 to your computer and use it in GitHub Desktop.
Restrish: A restricted shell written in PHP. Allows whitelisting commands.
#!/usr/bin/env php
<?php
$allow = array('cd', 'ls');
$prompt = 'restrish';
$warning = 'Command not allowed: %s';
/****** TODO LIST
- Better parsing of command delimiters (ignore quoted delimiters)
- Use readline for command history
*******/
define('DELIMS', '[;|&><]+');
pcntl_signal(SIGINT, function(){});
function say($str)
{
fwrite(STDOUT, $str);
}
function prompt($continue = false)
{
global $prompt;
say($continue ? ' > ' : $prompt.'$ ');
return trim(fgets(STDIN));
}
function parts($command)
{
return preg_split('/('.DELIMS.')/', $command);
}
function allowed($command)
{
global $allow;
// Find each command
$commands = parts($command);
if (count($commands) > 1)
{
foreach ($commands as $command)
{
if ( ! allowed(trim($command))) return false;
}
return true;
}
$path = (strpos($command, ' ') !== false)
? substr($command, 0, strpos($command, ' '))
: $command;
$filename = (strpos($path, '/') !== false)
? substr($path, strrpos($path, '/'))
: $path;
return in_array($filename, $allow);
}
function clean($command)
{
$exp = '/(?:'.DELIMS.'|^)?\s*(.+?)\s*(?:'.DELIMS.'|$)/';
return preg_replace_callback($exp, function($matches)
{
return escapeshellcmd($matches[0]);
}, $command);
}
function run($command)
{
global $warning;
// Get the base command name
if ( ! allowed($command))
{
throw new Exception(sprintf($warning, $command));
}
passthru(clean($command));
}
// Single-command mode
if ($argc > 2 && $argv[1] == '-c')
{
array_shift($argv);
array_shift($argv);
$command = implode(' ', $argv);
try
{
say(run($command));
}
catch (Exception $e)
{
say($e->getMessage());
exit(1);
}
exit(0);
}
// Interactive Mode
while (1)
{
$command = prompt();
while (substr($command, -1) == '\\')
{
$command = trim(substr($command, 0, strlen($command)-1)).' '.prompt($continue = true);
}
if (empty($command)) continue;
if ($command == 'exit') exit(0);
try
{
run($command);
}
catch (Exception $e)
{
say($e->getMessage()."\n");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment