mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-04-02 01:52:10 +09:00
Merge branch 'rhymix:master' into master
This commit is contained in:
commit
25f6d02677
27 changed files with 945 additions and 1153 deletions
|
|
@ -7,8 +7,8 @@ class HTMLDisplayHandler
|
|||
*/
|
||||
public const JQUERY_V2 = '2.2.4';
|
||||
public const JQUERY_V2_MIGRATE = '1.4.1';
|
||||
public const JQUERY_V3 = '3.6.3';
|
||||
public const JQUERY_V3_MIGRATE = '3.4.0';
|
||||
public const JQUERY_V3 = '3.7.1';
|
||||
public const JQUERY_V3_MIGRATE = '3.6.0';
|
||||
|
||||
/**
|
||||
* Default viewport setting
|
||||
|
|
@ -746,7 +746,8 @@ class HTMLDisplayHandler
|
|||
*/
|
||||
private function _loadCommonJSCSS()
|
||||
{
|
||||
if (config('view.jquery_version') === 3)
|
||||
$jquery_version = config('view.jquery_version') ?: 2;
|
||||
if ($jquery_version == 3)
|
||||
{
|
||||
$jquery_version = self::JQUERY_V3;
|
||||
$jquery_migrate_version = self::JQUERY_V3_MIGRATE;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* RX_VERSION is the version number of the Rhymix CMS.
|
||||
*/
|
||||
define('RX_VERSION', '2.1.30');
|
||||
define('RX_VERSION', '2.1.31');
|
||||
|
||||
/**
|
||||
* RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch.
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ return array(
|
|||
'minify_scripts' => 'common',
|
||||
'concat_scripts' => 'none',
|
||||
'delay_compile' => 0,
|
||||
'jquery_version' => 2,
|
||||
'jquery_version' => 3,
|
||||
),
|
||||
'admin' => array(
|
||||
'allow' => array(),
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ class Security
|
|||
if (!utf8_check($input)) return false;
|
||||
return Filters\FilenameFilter::clean($input);
|
||||
|
||||
// Clean up SVG content to prevent various attacks.
|
||||
case 'svg':
|
||||
if (!utf8_check($input)) return false;
|
||||
$sanitizer = new \enshrined\svgSanitize\Sanitizer();
|
||||
return strval($sanitizer->sanitize($input));
|
||||
|
||||
// Unknown filters.
|
||||
default:
|
||||
throw new Exception('Unknown filter type for sanitize: ' . $type);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class FileContentFilter
|
|||
$skip_xml = preg_match('/^(hwpx)$/', $ext);
|
||||
|
||||
// Check SVG files.
|
||||
if (($ext === 'svg' || $is_xml) && !self::_checkSVG($fp, 0, $filesize))
|
||||
if (($ext === 'svg' || $is_xml) && !self::_checkSVG($fp, 0, $filesize, $ext))
|
||||
{
|
||||
fclose($fp);
|
||||
return false;
|
||||
|
|
@ -89,11 +89,12 @@ class FileContentFilter
|
|||
* @param resource $fp
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @param string $ext
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _checkSVG($fp, $from, $to)
|
||||
protected static function _checkSVG($fp, $from, $to, $ext)
|
||||
{
|
||||
if (self::_matchStream('/(?:<|<)(?:script|iframe|foreignObject|object|embed|handler)|javascript:|xlink:href\s*=\s*"(?!data:)/i', $fp, $from, $to))
|
||||
if (self::_matchStream('/(?:<|<|:)(?:script|iframe|foreignObject|object|embed|handler)|javascript:|(?:\s|:)href\s*=\s*"(?!data:)/i', $fp, $from, $to))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ class ConfigParser
|
|||
if (isset($db_info->use_ssl) && in_array($db_info->use_ssl, ['always', 'optional']))
|
||||
{
|
||||
$config['url']['ssl'] = 'always';
|
||||
$config['session']['use_ssl'] = true;
|
||||
$config['session']['use_ssl_cookies'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
2
common/js/jquery-3.6.3.min.js
vendored
2
common/js/jquery-3.6.3.min.js
vendored
File diff suppressed because one or more lines are too long
1953
common/js/jquery-3.6.3.js → common/js/jquery-3.7.1.js
vendored
1953
common/js/jquery-3.6.3.js → common/js/jquery-3.7.1.js
vendored
File diff suppressed because it is too large
Load diff
2
common/js/jquery-3.7.1.min.js
vendored
Normal file
2
common/js/jquery-3.7.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
common/js/plugins/jquery.migrate/jquery-migrate-3.6.0.min.js
vendored
Normal file
2
common/js/plugins/jquery.migrate/jquery-migrate-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -359,6 +359,7 @@ $lang->filter['invalid_alpha_number'] = 'The format of %s is invalid. Please ent
|
|||
$lang->filter['invalid_mid'] = 'The format of %s is invalid. Module ID should be begun with a letter. Subsequent characters may be letters, digits or underscore characters.';
|
||||
$lang->filter['invalid_number'] = 'The format of %s is invalid. Please enter numbers only.';
|
||||
$lang->filter['invalid_float'] = 'The format of %s is invalid. Please enter numbers only.';
|
||||
$lang->filter['invalid_file'] = 'The value of %s is not a valid file upload.';
|
||||
$lang->filter['invalid_extension'] = 'The format of %s is invalid. e.g. gif, jpg, png';
|
||||
$lang->security_warning_embed = 'Due to security concern, administrators are not allowed to view embedded items.<BR /> To view them, please use another non-administrator ID.';
|
||||
$lang->msg_pc_to_mobile = 'View mobile optimized version of this page';
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ $lang->filter['invalid_alpha_number'] = '%s의 형식이 잘못되었습니다.
|
|||
$lang->filter['invalid_mid'] = '%s의 형식이 잘못되었습니다. 첫 글자는 영문으로 시작해야 하며 \'영문+숫자+_\'로만 입력해야 합니다.';
|
||||
$lang->filter['invalid_number'] = '%s의 형식이 잘못되었습니다. 숫자로만 입력해야 합니다.';
|
||||
$lang->filter['invalid_float'] = '%s의 형식이 잘못되었습니다. 숫자로만 입력해야 합니다.';
|
||||
$lang->filter['invalid_file'] = '%s의 값은 올바르게 업로드된 파일이 아닙니다.';
|
||||
$lang->filter['invalid_extension'] = '%s의 형식이 잘못되었습니다. gif, jpg, png 등 쉼표로 구분하여 입력해야 합니다.';
|
||||
$lang->security_invalid_session = '바르지 않은 접근입니다. 인증을 위해 다시 로그인해야 합니다.';
|
||||
$lang->security_warning_embed = '보안 문제로 관리자 아이디로는 embed를 볼 수 없습니다. 확인하려면 다른 아이디로 접속하세요';
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ $lang->cmd_concat_js_only = 'Combine all JS';
|
|||
$lang->cmd_concat_css_js = 'Combine both CSS and JS';
|
||||
$lang->about_concat_scripts = 'Automatically combine CSS and JS scripts into as few files as possible. External scripts are not combined.';
|
||||
$lang->jquery_version = 'jQuery Version';
|
||||
$lang->about_jquery_version = 'You can select the default jQuery version for this site. Please note that jQuery 3.x may not be compatible with older features.';
|
||||
$lang->about_jquery_version = 'You can select the default jQuery version for this site. Older third-party programs may require 2.x, but Rhymix recommends 3.x or higher for security.';
|
||||
$lang->use_gzip = 'gzip Compression';
|
||||
$lang->about_use_gzip = 'This option should be left off unless you know for sure that your webserver doesn\'t compress output by default.';
|
||||
$lang->delay_session = 'Delay session start';
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ $lang->cmd_concat_js_only = 'JS만 합침';
|
|||
$lang->cmd_concat_css_js = 'CSS와 JS를 모두 합침';
|
||||
$lang->about_concat_scripts = 'CSS, JS 파일들을 하나로 합쳐서 전송합니다. 외부에서 로딩하는 스크립트는 합쳐지지 않습니다.';
|
||||
$lang->jquery_version = 'jQuery 버전';
|
||||
$lang->about_jquery_version = '기본으로 사용할 jQuery 버전을 선택합니다. jQuery 3.x는 오래된 기능과 호환되지 않을 수 있습니다.';
|
||||
$lang->about_jquery_version = '기본으로 사용할 jQuery 버전을 선택합니다. 오래된 확장 기능은 jQuery 2.x를 요구할 수 있으나, 보안상 3.x 이상을 추천합니다.';
|
||||
$lang->use_gzip = 'gzip 압축';
|
||||
$lang->about_use_gzip = '웹서버가 gzip을 지원하지 않더라도 페이지를 강제로 압축하는 기능입니다. 대부분의 서버에는 필요하지 않습니다.';
|
||||
$lang->delay_session = '세션 시작 지연';
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class BoardAdminView extends Board {
|
|||
|
||||
// install order (sorting) options
|
||||
foreach($this->order_target as $key) $order_target[$key] = lang($key);
|
||||
$order_target['list_order'] = lang('document_srl');
|
||||
$order_target['list_order'] = lang('default_value');
|
||||
$order_target['update_order'] = lang('last_update');
|
||||
Context::set('order_target', $order_target);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -907,7 +907,7 @@ class DocumentController extends Document
|
|||
}
|
||||
|
||||
// Handle extra vars that support file upload.
|
||||
if ($extra_item->type === 'file' && is_array($value))
|
||||
if ($extra_item->type === 'file' && $value)
|
||||
{
|
||||
$ev_output = $extra_item->uploadFile($value, $obj->document_srl, 'doc');
|
||||
if (!$ev_output->toBool())
|
||||
|
|
@ -1305,16 +1305,20 @@ class DocumentController extends Document
|
|||
if ($extra_item->type === 'file')
|
||||
{
|
||||
// New upload
|
||||
if (is_array($value) && isset($value['name']))
|
||||
if (is_array($value) && isset($value['tmp_name']))
|
||||
{
|
||||
// Delete old file
|
||||
if (isset($old_extra_vars[$idx]->value))
|
||||
{
|
||||
$fc_output = FileController::getInstance()->deleteFile($old_extra_vars[$idx]->value);
|
||||
if (!$fc_output->toBool())
|
||||
$old_file = FileModel::getFile($old_extra_vars[$idx]->value);
|
||||
if ($old_file && $old_file->upload_target_srl == $obj->document_srl)
|
||||
{
|
||||
$oDB->rollback();
|
||||
return $fc_output;
|
||||
$fc_output = FileController::getInstance()->deleteFile($old_file->file_srl);
|
||||
if (!$fc_output->toBool())
|
||||
{
|
||||
$oDB->rollback();
|
||||
return $fc_output;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert new file
|
||||
|
|
@ -1339,21 +1343,22 @@ class DocumentController extends Document
|
|||
return $ev_output;
|
||||
}
|
||||
// Delete old file
|
||||
$fc_output = FileController::getInstance()->deleteFile($old_extra_vars[$idx]->value);
|
||||
if (!$fc_output->toBool())
|
||||
$old_file = FileModel::getFile($old_extra_vars[$idx]->value);
|
||||
if ($old_file && $old_file->upload_target_srl == $obj->document_srl)
|
||||
{
|
||||
$oDB->rollback();
|
||||
return $fc_output;
|
||||
$fc_output = FileController::getInstance()->deleteFile($old_file->file_srl);
|
||||
if (!$fc_output->toBool())
|
||||
{
|
||||
$oDB->rollback();
|
||||
return $fc_output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Leave current file unchanged
|
||||
elseif (!$value)
|
||||
elseif (isset($old_extra_vars[$idx]->value))
|
||||
{
|
||||
if (isset($old_extra_vars[$idx]->value))
|
||||
{
|
||||
$value = $old_extra_vars[$idx]->value;
|
||||
}
|
||||
$value = $old_extra_vars[$idx]->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1650,6 +1650,10 @@ class DocumentItem extends BaseObject
|
|||
return ModuleModel::getModuleInfoByModuleSrl($this->get('module_srl'))->browser_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the module to which the document belongs.
|
||||
* @return string
|
||||
*/
|
||||
function getBrowserTitle()
|
||||
{
|
||||
return $this->getModuleName();
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ class DocumentModel extends Document
|
|||
foreach($GLOBALS['XE_EXTRA_KEYS'][$module_srl] as $idx => $key)
|
||||
{
|
||||
$document_extra_vars[$idx] = clone($key);
|
||||
$document_extra_vars[$idx]->parent_srl = $document_srl;
|
||||
|
||||
// set variable value in user language
|
||||
if(isset($document_extra_values[$idx][$user_lang_code]))
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Value
|
|||
public $input_id = '';
|
||||
public $input_name = '';
|
||||
public $parent_type = 'document';
|
||||
public $parent_srl = null;
|
||||
public $type = 'text';
|
||||
public $value = null;
|
||||
public $name = '';
|
||||
|
|
@ -159,7 +160,7 @@ class Value
|
|||
*/
|
||||
public function getValueHTML(): string
|
||||
{
|
||||
return self::_getTypeValueHTML($this->type, $this->value);
|
||||
return self::_getTypeValueHTML($this->type, $this->value, $this->parent_type, $this->parent_srl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -280,7 +281,7 @@ class Value
|
|||
$values = [$value];
|
||||
}
|
||||
|
||||
// Check if a required value is empty.
|
||||
// Check that a required value is not empty.
|
||||
if ($this->is_required === 'Y')
|
||||
{
|
||||
if ($this->type === 'file' && !$value && $old_value)
|
||||
|
|
@ -298,7 +299,7 @@ class Value
|
|||
}
|
||||
}
|
||||
|
||||
// Check if a strict value is not one of the specified options.
|
||||
// Check that a strict value equals one of the specified options.
|
||||
if ($this->is_strict === 'Y' && $value)
|
||||
{
|
||||
if ($this->canHaveOptions())
|
||||
|
|
@ -321,6 +322,15 @@ class Value
|
|||
}
|
||||
}
|
||||
|
||||
// Check that a file value is actually an uploaded file.
|
||||
if ($this->type === 'file' && $value)
|
||||
{
|
||||
if (!isset($value['tmp_name']) || !is_uploaded_file($value['tmp_name']))
|
||||
{
|
||||
return new BaseObject(-1, sprintf(lang('common.filter.invalid_file'), Context::replaceUserLang($this->name)));
|
||||
}
|
||||
}
|
||||
|
||||
return new BaseObject;
|
||||
}
|
||||
|
||||
|
|
@ -442,9 +452,11 @@ class Value
|
|||
*
|
||||
* @param string $type
|
||||
* @param string|array $value
|
||||
* @param string $parent_type
|
||||
* @param ?int $parent_srl
|
||||
* @return string
|
||||
*/
|
||||
protected static function _getTypeValueHTML(string $type, $value): string
|
||||
protected static function _getTypeValueHTML(string $type, $value, string $parent_type, ?int $parent_srl = null): string
|
||||
{
|
||||
// Return if the value is empty.
|
||||
$value = self::_getTypeValue($type, $value);
|
||||
|
|
@ -511,10 +523,14 @@ class Value
|
|||
if ($value)
|
||||
{
|
||||
$file = FileModel::getFile($value);
|
||||
if ($file)
|
||||
if ($file && $file->upload_target_srl == $parent_srl)
|
||||
{
|
||||
return sprintf('<span><a href="%s">%s</a> (%s)</span>', \RX_BASEURL . ltrim($file->download_url, './'), $file->source_filename, FileHandler::filesize($file->file_size));
|
||||
}
|
||||
elseif ($file)
|
||||
{
|
||||
return sprintf('<span>%s (%s)</span>', $file->source_filename, FileHandler::filesize($file->file_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -551,7 +551,7 @@ class FileController extends File
|
|||
{
|
||||
$download_type = 'inline';
|
||||
}
|
||||
if (Context::get('force_download') === 'Y')
|
||||
if ($mime_type === 'image/svg+xml' || Context::get('force_download') === 'Y')
|
||||
{
|
||||
$download_type = 'attachment';
|
||||
}
|
||||
|
|
@ -936,6 +936,14 @@ class FileController extends File
|
|||
}
|
||||
}
|
||||
|
||||
// Sanitize SVG
|
||||
if(!$manual_insert && !$this->user->isAdmin() && ($file_info['type'] === 'image/svg+xml' || $file_info['extension'] === 'svg'))
|
||||
{
|
||||
$dirty_svg = Rhymix\Framework\Storage::read($file_info['tmp_name']);
|
||||
$clean_svg = Rhymix\Framework\Security::sanitize($dirty_svg, 'svg');
|
||||
Rhymix\Framework\Storage::write($file_info['tmp_name'], $clean_svg);
|
||||
}
|
||||
|
||||
// Adjust
|
||||
if(!$manual_insert)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -204,6 +204,13 @@ class installController extends install
|
|||
// Set the default umask.
|
||||
$config['file']['umask'] = Rhymix\Framework\Storage::recommendUmask();
|
||||
|
||||
// Set default security settings.
|
||||
if ($config['url']['ssl'] === 'always')
|
||||
{
|
||||
$config['session']['use_ssl'] = true;
|
||||
$config['session']['use_ssl_cookies'] = true;
|
||||
}
|
||||
|
||||
// Load the new configuration.
|
||||
Rhymix\Framework\Config::setAll($config);
|
||||
Context::loadDBInfo($config);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ class installView extends install
|
|||
// Specify the template path.
|
||||
$this->setTemplatePath($this->module_path.'tpl');
|
||||
|
||||
// Set default frontend configurations.
|
||||
config('view.jquery_version', 3);
|
||||
config('view.minify_scripts', 'none');
|
||||
config('view.concat_scripts', 'none');
|
||||
|
||||
// Check the environment.
|
||||
$oInstallController = getController('install');
|
||||
self::$checkEnv = $oInstallController->checkInstallEnv();
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ class PageAdminController extends Page
|
|||
$oDocumentController = getController('document');
|
||||
$obj = new stdClass();
|
||||
$obj->module_srl = $module_srl;
|
||||
$obj->list_count = 99999999;
|
||||
$obj->list_count = 0;
|
||||
$output = $oDocumentModel->getDocumentList($obj);
|
||||
if(count($output->data))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<param name="module_srl" target="module_srl" />
|
||||
<param name="mid" target="mid" />
|
||||
<param name="mcontent" target="content" />
|
||||
<parma name="type" target="type" />
|
||||
<param name="type" target="type" />
|
||||
</parameter>
|
||||
<response callback_func="completeInsertMobilePageContent">
|
||||
<tag name="error" />
|
||||
|
|
|
|||
|
|
@ -18,5 +18,6 @@ v.cast('ADD_MESSAGE',['invalid_alpha_number','%s의 형식이 잘못되었습니
|
|||
v.cast('ADD_MESSAGE',['invalid_mid','%s의 형식이 잘못되었습니다. 첫 글자는 영문으로 시작해야 하며 \'영문+숫자+_\'로만 입력해야 합니다.']);
|
||||
v.cast('ADD_MESSAGE',['invalid_number','%s의 형식이 잘못되었습니다. 숫자로만 입력해야 합니다.']);
|
||||
v.cast('ADD_MESSAGE',['invalid_float','%s의 형식이 잘못되었습니다. 숫자로만 입력해야 합니다.']);
|
||||
v.cast('ADD_MESSAGE',['invalid_file','%s의 값은 올바르게 업로드된 파일이 아닙니다.']);
|
||||
v.cast('ADD_MESSAGE',['invalid_extension','%s의 형식이 잘못되었습니다. gif, jpg, png 등 쉼표로 구분하여 입력해야 합니다.']);
|
||||
})(jQuery);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@ class SecurityTest extends \Codeception\Test\Unit
|
|||
|
||||
// Filename (more thorough tests in FilenameFilterTest)
|
||||
$this->assertEquals('foo(bar).xls', Rhymix\Framework\Security::sanitize('foo<bar>.xls', 'filename'));
|
||||
|
||||
// SVG #1
|
||||
$source = '<svg><rect><a href="javascript:alert(0)">Test</a></rect></svg>';
|
||||
$target = '<?xml version="1.0" encoding="UTF-8"?>' . "\n<svg>\n <rect>\n <a>Test</a>\n </rect>\n</svg>\n";
|
||||
$this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'svg'));
|
||||
|
||||
// SVG #2
|
||||
$source = '<svg><rect></rect><script></script></svg>';
|
||||
$target = '<?xml version="1.0" encoding="UTF-8"?>' . "\n<svg>\n <rect></rect>\n</svg>\n";
|
||||
$this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'svg'));
|
||||
}
|
||||
|
||||
public function testEncryption()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue