Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ When defining the script, you can specify one of the following placeholders that

When no placeholder was specified, then the exact command as given is being executed.

### Security
Do not wrap placeholders in quotes, as they are already quoted by the app. Wrapping them in quotes can neutralize this and may allow command execution via filenames. Files containing `$(` or `` ` `` in their filename will be skipped.

### Hints

Events for files and folders are triggered by file system operations. An operation like
Expand Down
25 changes: 22 additions & 3 deletions lib/BackgroundJobs/Launcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace OCA\WorkflowScript\BackgroundJobs;

use Exception;
use InvalidArgumentException;
use OC\Files\View;
use OCA\WorkflowScript\AppInfo\Application;
use OCP\AppFramework\Utility\ITimeFactory;
Expand All @@ -31,16 +32,14 @@ protected function run($argument): void {
if (strpos($command, '%f')) {
$path = isset($argument['path']) ? (string)$argument['path'] : '';
try {
$view = new View(dirname($path));
$tmpFile = $view->toTmpFile(basename($path));
$command = str_replace('%f', escapeshellarg($this->resolveLocalPath($path)), $command);
} catch (Exception $e) {
$this->logger->warning($e->getMessage(), [
'app' => Application::APPID,
'exception' => $e
]);
return;
}
$command = str_replace('%f', escapeshellarg($tmpFile), $command);
}

// with wrapping sh around the command, we leave any redirects intact,
Expand All @@ -52,4 +51,24 @@ protected function run($argument): void {
);
shell_exec($wrapper);
}

/**
* @throws InvalidArgumentException
*/
private function resolveLocalPath(string $path): string {
try {
$view = new View();
$localFile = $view->getLocalFile($path);
if ($localFile !== false && file_exists($localFile)) {
return $localFile;
}
$tmpFile = $view->toTmpFile($path);
if ($tmpFile === false) {
throw new InvalidArgumentException();
}
return $tmpFile;
} catch (Exception) {
throw new InvalidArgumentException('Could not resolve local path for: ' . $path);
}
}
}
33 changes: 15 additions & 18 deletions lib/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@

namespace OCA\WorkflowScript;

use Exception;
use InvalidArgumentException;
use OC\Files\View;
use OC\User\NoUserException;
use OCA\Files_Sharing\SharedStorage;
use OCA\GroupFolders\Mount\GroupFolderStorage;
Expand Down Expand Up @@ -134,6 +131,21 @@ public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatch
}

$matches = $ruleMatcher->getFlows(false);
if (empty($matches)) {
return;
}

if (preg_match('/\$\(|`/', $node->getName())) {
$this->logger->warning(
'Potentially dangerous characters in filename, skipping workflow',
[
'app' => Application::APPID,
'file' => $node->getPath(),
]
);
return;
}

foreach ($matches as $match) {
try {
$command = $this->buildCommand($match['operation'], $node, $eventName, $extra);
Expand Down Expand Up @@ -177,21 +189,6 @@ protected function buildCommand(string $template, Node $node, string $event, arr
unset($ncRelPath);
}

if (strpos($command, '%f')) {
try {
$view = new View();
if ($node instanceof FileNode) {
$fullPath = $view->getLocalFile($node->getPath());
}
if (!isset($fullPath) || $fullPath === false) {
throw new InvalidArgumentException();
}
$command = str_replace('%f', escapeshellarg($fullPath), $command);
} catch (Exception) {
throw new InvalidArgumentException('Could not determine full path');
}
}

if (strpos($command, '%i')) {
$nodeID = -1;
try {
Expand Down
Loading