Merge branch 'security/rve-2023-6' into develop

This commit is contained in:
Kijin Sung 2023-11-06 07:47:14 +09:00
commit f4474070e1
12 changed files with 163 additions and 53 deletions

View file

@ -91,8 +91,8 @@
var chunkStatus = true;
var defaultFormData = {
"editor_sequence": data.editorSequence,
"upload_target_srl" : data.uploadTargetSrl,
"mid" : window.current_mid,
"upload_target_srl" : data.uploadTargetSrl ? data.uploadTargetSrl : 0,
"mid" : window.editor_mid ? window.editor_mid : window.current_mid,
"act": 'procFileUpload'
};

View file

@ -293,6 +293,10 @@ class CommunicationView extends communication
{
$option->editor_toolbar_hide = 'Y';
}
if ($option->allow_fileupload)
{
$option->module_srl = MemberView::getInstance()->getMemberModuleSrl();
}
$editor = $oEditorModel->getEditor(getNextSequence(), $option);
$editor = $editor . "\n" . '<input type="hidden" name="temp_srl" value="" />' . "\n";
Context::set('editor', $editor);

View file

@ -328,11 +328,10 @@ class EditorController extends Editor
$editor_sequence = Context::get('editor_sequence');
$primary_key = Context::get('primary_key');
$oEditorModel = getModel('editor');
$oFileController = getController('file');
$saved_doc = $oEditorModel->getSavedDoc(null);
$oFileController->setUploadInfo($editor_sequence, $saved_doc->document_srl);
FileController::setUploadInfo($editor_sequence, $saved_doc->document_srl, intval($saved_doc->module_srl));
$vars = $this->getVariables();
$this->add("editor_sequence", $editor_sequence);
$this->add("key", $primary_key);

View file

@ -251,14 +251,29 @@ class EditorModel extends Editor
}
Context::set('file_config',$file_config);
// Configure upload status such as file size
$upload_status = FileModel::getUploadStatus();
Context::set('upload_status', $upload_status);
// Upload enabled (internally caching)
$oFileController = getController('file');
$oFileController->setUploadInfo($option->editor_sequence, $upload_target_srl);
FileController::setUploadInfo($option->editor_sequence, $upload_target_srl, $option->module_srl ?? 0);
// Set editor mid
if (!empty($option->module_srl))
{
$option->mid = ModuleModel::getModuleInfoByModuleSrl($option->module_srl)->mid ?? null;
}
if (!empty($option->mid))
{
Context::addHtmlFooter('<script> var editor_mid = ' . json_encode($option->mid) . '; </script>');
}
// Check if the file already exists
if($upload_target_srl) $files_count = FileModel::getFilesCount($upload_target_srl);
if ($upload_target_srl)
{
$files_count = FileModel::getFilesCount($upload_target_srl);
}
}
Context::set('files_count', (int)$files_count);
@ -294,6 +309,7 @@ class EditorModel extends Editor
// Initialize options
$option = new stdClass();
$option->module_type = $type;
$option->module_srl = (int)$module_srl;
// Convert configuration keys according to type (document or comment).
if($type == 'document')

View file

@ -31,21 +31,24 @@ class FileController extends File
// An error appears if not a normally uploaded file
if(!$file_info || !is_uploaded_file($file_info['tmp_name'])) exit();
// Basic variables setting
// Validate editor_sequence and module_srl.
$editor_sequence = Context::get('editor_sequence');
$module_srl = $this->module_srl;
// Exit a session if there is neither upload permission nor information
if(!$_SESSION['upload_info'][$editor_sequence]->enabled)
if (empty($_SESSION['upload_info'][$editor_sequence]->enabled))
{
throw new Rhymix\Framework\Exceptions\NotPermitted;
throw new Rhymix\Framework\Exceptions\InvalidRequest(sprintf(lang('file.msg_invalid_upload_info'), 'editor_sequence'));
}
if ($_SESSION['upload_info'][$editor_sequence]->module_srl !== $module_srl)
{
throw new Rhymix\Framework\Exceptions\InvalidRequest(sprintf(lang('file.msg_invalid_upload_info'), 'module_srl'));
}
// Get upload_target_srl
$upload_target_srl = intval(Context::get('uploadTargetSrl')) ?: intval(Context::get('upload_target_srl'));
if (!$upload_target_srl)
// Validate upload_target_srl.
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
$submitted_upload_target_srl = intval(Context::get('uploadTargetSrl')) ?: intval(Context::get('upload_target_srl'));
if ($submitted_upload_target_srl && $submitted_upload_target_srl !== intval($upload_target_srl))
{
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
throw new Rhymix\Framework\Exceptions\InvalidRequest(sprintf(lang('file.msg_invalid_upload_info'), 'upload_target_srl'));
}
if (!$upload_target_srl)
{
@ -170,24 +173,39 @@ class FileController extends File
function procFileIframeUpload()
{
// Basic variables setting
$editor_sequence = Context::get('editor_sequence');
$callback = Context::get('callback');
$module_srl = $this->module_srl;
$upload_target_srl = intval(Context::get('uploadTargetSrl'));
if(!$upload_target_srl) $upload_target_srl = intval(Context::get('upload_target_srl'));
// Exit a session if there is neither upload permission nor information
if(!$_SESSION['upload_info'][$editor_sequence]->enabled) exit();
// Extract from session information if upload_target_srl is not specified
if(!$upload_target_srl) $upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
// Create if upload_target_srl is not defined in the session information
if(!$upload_target_srl) $_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl = getNextSequence();
// Validate editor_sequence and module_srl.
$editor_sequence = Context::get('editor_sequence');
$module_srl = $this->module_srl;
if (empty($_SESSION['upload_info'][$editor_sequence]->enabled))
{
throw new Rhymix\Framework\Exceptions\InvalidRequest(sprintf(lang('file.msg_invalid_upload_info'), 'editor_sequence'));
}
if ($_SESSION['upload_info'][$editor_sequence]->module_srl !== $module_srl)
{
throw new Rhymix\Framework\Exceptions\InvalidRequest(sprintf(lang('file.msg_invalid_upload_info'), 'module_srl'));
}
// Get upload_target_srl
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
$submitted_upload_target_srl = intval(Context::get('uploadTargetSrl')) ?: intval(Context::get('upload_target_srl'));
if ($submitted_upload_target_srl && $submitted_upload_target_srl !== intval($upload_target_srl))
{
throw new Rhymix\Framework\Exceptions\InvalidRequest(sprintf(lang('file.msg_invalid_upload_info'), 'upload_target_srl'));
}
if (!$upload_target_srl)
{
$upload_target_srl = getNextSequence();
$_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl;
}
// Delete and then attempt to re-upload if file_srl is requested
$file_srl = Context::get('file_srl');
if($file_srl)
{
$file_info = FileModel::getFile($file_srl);
if($file_info->file_srl == $file_srl && FileModel::isDeletable($file_info))
if($file_info->file_srl == $file_srl && $file_info->upload_target_srl == $upload_target_srl && FileModel::isDeletable($file_info))
{
$this->deleteFile($file_srl);
}
@ -582,10 +600,18 @@ class FileController extends File
$file_srl = Context::get('file_srl');
$file_srls = Context::get('file_srls');
if($file_srls) $file_srl = $file_srls;
// Exit a session if there is neither upload permission nor information
if(!$_SESSION['upload_info'][$editor_sequence]->enabled) exit();
// Exit a session if there is neither upload permission nor information
if (!$_SESSION['upload_info'][$editor_sequence]->enabled)
{
throw new Rhymix\Framework\Exceptions\NotPermitted;
}
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
if (!$upload_target_srl)
{
throw new Rhymix\Framework\Exceptions\TargetNotFound;
}
$module_srl = $_SESSION['upload_info'][$editor_sequence]->module_srl ?? 0;
$srls = explode(',',$file_srl);
if(!count($srls)) return;
@ -601,9 +627,10 @@ class FileController extends File
if(!$output->toBool()) continue;
$file_info = $output->data;
if(!$file_info) continue;
if(!$file_info || $file_info->upload_target_srl != $upload_target_srl) continue;
if($module_srl && $file_info->module_srl != $module_srl) continue;
if(!FileModel::isDeletable($file_info)) continue;
if($upload_target_srl && $file_srl) $output = $this->deleteFile($file_srl);
$output = $this->deleteFile($file_srl);
}
}
@ -709,9 +736,10 @@ class FileController extends File
*
* @param int $editor_sequence
* @param int $upload_target_srl
* @param int $module_srl
* @return int
*/
function setUploadInfo($editor_sequence = 0, $upload_target_srl = 0)
public static function setUploadInfo($editor_sequence = 0, $upload_target_srl = 0, $module_srl = 0)
{
if(!$editor_sequence)
{
@ -721,6 +749,14 @@ class FileController extends File
}
$editor_sequence = ++$_SESSION['_editor_sequence_'];
}
if(!$module_srl)
{
$current_module_info = Context::get('current_module_info');
if (!empty($current_module_info->module_srl))
{
$module_srl = $current_module_info->module_srl;
}
}
if(!isset($_SESSION['upload_info']) || !is_array($_SESSION['upload_info']))
{
$_SESSION['upload_info'] = array();
@ -730,7 +766,12 @@ class FileController extends File
$_SESSION['upload_info'][$editor_sequence] = new stdClass();
}
$_SESSION['upload_info'][$editor_sequence]->enabled = true;
$_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl;
$_SESSION['upload_info'][$editor_sequence]->upload_target_srl = (int)$upload_target_srl;
$_SESSION['upload_info'][$editor_sequence]->module_srl = (int)$module_srl;
if (!$module_srl)
{
trigger_error('No module_srl supplied to setUploadInfo(), and cannot determine automatically', E_USER_WARNING);
}
return $editor_sequence;
}
@ -1740,17 +1781,32 @@ class FileController extends File
public function procFileSetCoverImage()
{
$vars = Context::getRequestVars();
$logged_info = Context::get('logged_info');
if(!$vars->editor_sequence) throw new Rhymix\Framework\Exceptions\InvalidRequest;
$upload_target_srl = $_SESSION['upload_info'][$vars->editor_sequence]->upload_target_srl;
// Exit a session if there is neither upload permission nor information
$editor_sequence = $vars->editor_sequence ?? 0;
if (!$vars->editor_sequence)
{
throw new Rhymix\Framework\Exceptions\InvalidRequest;
}
if (!$_SESSION['upload_info'][$editor_sequence]->enabled)
{
throw new Rhymix\Framework\Exceptions\NotPermitted;
}
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
if (!$upload_target_srl)
{
throw new Rhymix\Framework\Exceptions\TargetNotFound;
}
$file_info = FileModel::getFile($vars->file_srl);
if(!$file_info) throw new Rhymix\Framework\Exceptions\TargetNotFound;
if(!$this->manager && !$file_info->member_srl === $logged_info->member_srl) throw new Rhymix\Framework\Exceptions\NotPermitted;
if (!$file_info || $file_info->upload_target_srl != $upload_target_srl)
{
throw new Rhymix\Framework\Exceptions\TargetNotFound;
}
if(!$this->grant->manager && $file_info->member_srl != $this->user->member_srl)
{
throw new Rhymix\Framework\Exceptions\NotPermitted;
}
$args = new stdClass();
$args->file_srl = $vars->file_srl;
@ -1769,7 +1825,6 @@ class FileController extends File
if($file_info->cover_image != 'Y')
{
$args->cover_image = 'Y';
$output = executeQuery('file.updateCoverImage', $args);
if(!$output->toBool())
@ -1777,7 +1832,6 @@ class FileController extends File
$oDB->rollback();
return $output;
}
}
$oDB->commit();

View file

@ -26,12 +26,8 @@ class FileModel extends File
{
$file_list = [];
$attached_size = 0;
$editor_sequence = Context::get('editor_sequence');
$upload_target_srl = Context::get('upload_target_srl') ?: 0;
if(!$upload_target_srl && isset($_SESSION['upload_info'][$editor_sequence]))
{
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
}
$editor_sequence = intval(Context::get('editor_sequence'));
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl ?? 0;
// Get uploaded files
if($upload_target_srl)

View file

@ -49,6 +49,7 @@ $lang->about_save_changelog = 'Keep a log of new and deleted files in the databa
$lang->cmd_delete_checked_file = 'Delete Selected Item(s)';
$lang->cmd_move_to_document = 'Move to Document';
$lang->cmd_download = 'Download';
$lang->msg_invalid_upload_info = 'Invalid upload target information. (%s)';
$lang->msg_not_permitted_download = 'You do not have a permission to download.';
$lang->msg_file_cart_is_null = 'Please select a file(s) to delete.';
$lang->msg_checked_file_is_deleted = '%d attachment(s) was(were) deleted.';

View file

@ -49,6 +49,7 @@ $lang->about_save_changelog = '파일 저장 및 삭제 내역을 DB에 기록
$lang->cmd_delete_checked_file = '선택항목 삭제';
$lang->cmd_move_to_document = '문서로 이동';
$lang->cmd_download = '다운로드';
$lang->msg_invalid_upload_info = '업로드 대상 정보가 일치하지 않습니다. (%s)';
$lang->msg_not_permitted_download = '다운로드할 수 있는 권한이 없습니다.';
$lang->msg_file_cart_is_null = '삭제할 파일을 선택해주세요.';
$lang->msg_checked_file_is_deleted = '%d개의 첨부 파일이 삭제되었습니다.';

View file

@ -433,6 +433,14 @@ class MemberAdminView extends Member
$option->editor_toolbar_hide = 'Y';
$option->editor_skin = $member_config->signature_editor_skin;
$option->sel_editor_colorset = $member_config->sel_editor_colorset;
if (!$option->allow_html)
{
$option->editor_skin = 'textarea';
}
if ($option->allow_fileupload)
{
$option->module_srl = MemberView::getInstance()->getMemberModuleSrl();
}
Context::set('editor', getModel('editor')->getEditor($member_info->member_srl, $option));
}

View file

@ -93,6 +93,27 @@ class MemberView extends Member
return false;
}
/**
* Get the module_srl for the member mid.
*
* @return int
*/
public function getMemberModuleSrl(): int
{
if (!$this->member_config)
{
$this->member_config = MemberModel::getMemberConfig();
}
if (!empty($this->member_config->mid))
{
return ModuleModel::getModuleInfoByMid($this->member_config->mid)->module_srl ?? 0;
}
else
{
return 0;
}
}
/**
* Module index
*/
@ -334,6 +355,10 @@ class MemberView extends Member
{
$option->editor_skin = 'textarea';
}
if ($option->allow_fileupload)
{
$option->module_srl = $this->getMemberModuleSrl();
}
Context::set('editor', getModel('editor')->getEditor(0, $option));
}
@ -448,6 +473,10 @@ class MemberView extends Member
{
$option->editor_skin = 'textarea';
}
if ($option->allow_fileupload)
{
$option->module_srl = $this->getMemberModuleSrl();
}
Context::set('editor', getModel('editor')->getEditor($member_info->member_srl, $option));
}

View file

@ -5,6 +5,7 @@ class TemplateTest extends \Codeception\Test\Unit
public function _before()
{
Context::init();
$this->baseurl = '/' . basename(dirname(dirname(dirname(__DIR__)))) . '/';
}
public function testIsRelativePath()
@ -48,7 +49,7 @@ class TemplateTest extends \Codeception\Test\Unit
$this->assertEquals($target, $tmpl->convertPath($source));
$source = '^/foo/bar.gif';
$target = '/rhymix/foo/bar.gif';
$target = $this->baseurl . 'foo/bar.gif';
$this->assertEquals($target, $tmpl->convertPath($source));
}

View file

@ -1145,9 +1145,9 @@ class TemplateParserV2Test extends \Codeception\Test\Unit
// Check that resource is loaded
$list = \Context::getJsFile('body');
$this->assertStringContainsString('/rhymix/common/js/plugins/ckeditor/', array_first($list)['file']);
$this->assertStringContainsString('/common/js/plugins/ckeditor/', array_first($list)['file']);
$list = \Context::getCssFile();
$this->assertStringContainsString('/rhymix/tests/_data/template/css/style.scss', array_first($list)['file']);
$this->assertStringContainsString('/tests/_data/template/css/style.scss', array_first($list)['file']);
}
public function testCompileLang()
@ -1240,7 +1240,7 @@ class TemplateParserV2Test extends \Codeception\Test\Unit
);
$list = \Context::getJsFile();
$this->assertStringContainsString('/rhymix/tests/_data/template/js/test.js', array_last($list)['file']);
$this->assertStringContainsString('/tests/_data/template/js/test.js', array_last($list)['file']);
}
/**
@ -1274,6 +1274,7 @@ class TemplateParserV2Test extends \Codeception\Test\Unit
protected function _normalizeWhitespace(string $content): string
{
$content = preg_replace('/<!--#Template(Start|End):.+?-->\n/', '', $content);
$content = preg_replace('!(action|src)="' . preg_quote($this->baseurl, '!') . '!', '$1="/rhymix/', $content);
$result = [];
foreach (explode("\n", $content) as $line)