From 01100c9a4aa8aa3e32b51370c6c11acb0e5e44aa Mon Sep 17 00:00:00 2001 From: Ryan Lieu Date: Fri, 14 Apr 2023 11:45:09 +0800 Subject: [PATCH 01/12] Fix: php 8.1 strtolower not allow null value (#1559) --- var/Widget/Archive.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/var/Widget/Archive.php b/var/Widget/Archive.php index 1ee9d39b..22dd29fe 100644 --- a/var/Widget/Archive.php +++ b/var/Widget/Archive.php @@ -1032,7 +1032,7 @@ class Archive extends Contents */ public function related(int $limit = 5, ?string $type = null): Contents { - $type = strtolower($type); + $type = strtolower($type ?? ''); switch ($type) { case 'author': From 83d4d020edb90836be58fb9c72c0586fbce91d45 Mon Sep 17 00:00:00 2001 From: Patrick SAMA Date: Mon, 15 May 2023 18:29:47 +0800 Subject: [PATCH 02/12] Fix an XSS vulnerability in v1.2.1-rc (#1561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #1560 * Update var/Typecho/Validate.php Co-authored-by: 沈唁 <52o@qq52o.cn> --------- Co-authored-by: joyqi Co-authored-by: 沈唁 <52o@qq52o.cn> --- var/Typecho/Validate.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/var/Typecho/Validate.php b/var/Typecho/Validate.php index 4e371172..044726fd 100644 --- a/var/Typecho/Validate.php +++ b/var/Typecho/Validate.php @@ -96,7 +96,7 @@ class Validate */ public static function email(string $str): bool { - return filter_var($str, FILTER_VALIDATE_EMAIL) !== false; + return (bool) preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str); } /** @@ -110,10 +110,14 @@ class Validate */ public static function url(string $str): bool { - return filter_var( - $str, - FILTER_VALIDATE_URL - ) !== false; + $parts = parse_url($str); + if (!$parts) { + return false; + } + + return isset($parts['scheme']) && + in_array($parts['scheme'], ['http', 'https']) && + !preg_match('/(\(|\)|\\\|"|<|>|[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', $str); } /** From c9de1b3b01f50af2cd0c37b3d99ec5eef02f4987 Mon Sep 17 00:00:00 2001 From: Ryan Lieu Date: Mon, 15 May 2023 18:30:44 +0800 Subject: [PATCH 03/12] fix php 8.1 Deprecated: htmlspecialchars(): Passing null to parameter #1 (#1570) * Fix multiple calls returning the same object * fix: strtolower() passing null to parameter #1 ($string) of type string is deprecated * fix: php 8.1 Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated --- var/Typecho/Widget/Helper/Form/Element.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/var/Typecho/Widget/Helper/Form/Element.php b/var/Typecho/Widget/Helper/Form/Element.php index 7389ebdd..984864c5 100644 --- a/var/Typecho/Widget/Helper/Form/Element.php +++ b/var/Typecho/Widget/Helper/Form/Element.php @@ -217,7 +217,7 @@ abstract class Element extends Layout public function value($value): Element { $this->value = $value; - $this->inputValue($value); + $this->inputValue($value ?? ''); return $this; } From 7a41f0d0134f40dfa01ed453c0e2b667a1253db8 Mon Sep 17 00:00:00 2001 From: joyqi Date: Mon, 15 May 2023 19:55:36 +0800 Subject: [PATCH 04/12] fix email validate --- var/Typecho/Validate.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/var/Typecho/Validate.php b/var/Typecho/Validate.php index 044726fd..dbfda38a 100644 --- a/var/Typecho/Validate.php +++ b/var/Typecho/Validate.php @@ -96,7 +96,8 @@ class Validate */ public static function email(string $str): bool { - return (bool) preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str); + $email = filter_var($str, FILTER_SANITIZE_EMAIL); + return filter_var($str, FILTER_VALIDATE_EMAIL) && ($email === $str); } /** From 98e4c1d16dd9dfd20ddc0fbddd8aa031a9d9611a Mon Sep 17 00:00:00 2001 From: joyqi Date: Mon, 15 May 2023 21:44:12 +0800 Subject: [PATCH 05/12] fix url validate --- var/Typecho/Validate.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/var/Typecho/Validate.php b/var/Typecho/Validate.php index dbfda38a..ae8a3a5d 100644 --- a/var/Typecho/Validate.php +++ b/var/Typecho/Validate.php @@ -97,7 +97,7 @@ class Validate public static function email(string $str): bool { $email = filter_var($str, FILTER_SANITIZE_EMAIL); - return filter_var($str, FILTER_VALIDATE_EMAIL) && ($email === $str); + return !!filter_var($str, FILTER_VALIDATE_EMAIL) && ($email === $str); } /** @@ -111,14 +111,8 @@ class Validate */ public static function url(string $str): bool { - $parts = parse_url($str); - if (!$parts) { - return false; - } - - return isset($parts['scheme']) && - in_array($parts['scheme'], ['http', 'https']) && - !preg_match('/(\(|\)|\\\|"|<|>|[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', $str); + $url = Common::safeUrl($str); + return !!filter_var($str, FILTER_VALIDATE_URL) && ($url === $str); } /** From 0433a11c0ad8b72dc065c75a39992d256a29bb5f Mon Sep 17 00:00:00 2001 From: joyqi Date: Mon, 15 May 2023 22:35:25 +0800 Subject: [PATCH 06/12] escape mail string before output --- admin/manage-comments.php | 2 +- var/Widget/Base/Comments.php | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/admin/manage-comments.php b/admin/manage-comments.php index c85e8a3c..22f0d489 100644 --- a/admin/manage-comments.php +++ b/admin/manage-comments.php @@ -131,7 +131,7 @@ $isAllComments = ('on' == $request->get('__typecho_all_comments') || 'on' == \Ty
author(true); ?> mail): ?> -
mail(); ?> +
mail(); ?> ip): ?>
ip(); ?> diff --git a/var/Widget/Base/Comments.php b/var/Widget/Base/Comments.php index f318711b..2c468e08 100644 --- a/var/Widget/Base/Comments.php +++ b/var/Widget/Base/Comments.php @@ -306,6 +306,18 @@ class Comments extends Base implements QueryInterface echo Common::subStr(strip_tags($this->content), 0, $length, $trim); } + /** + * 输出邮箱地址 + * + * @param bool $link + * @return void + */ + public function mail(bool $link = false) + { + $mail = htmlspecialchars($this->mail); + echo $link ? 'mailto:' . $mail : $mail; + } + /** * 获取查询对象 * From 596d1172100d224ed8d4c97dab4f2b5e19887247 Mon Sep 17 00:00:00 2001 From: joyqi Date: Tue, 16 May 2023 12:33:49 +0800 Subject: [PATCH 07/12] fix: when setting CheckBox's default value to null [ref #1568] --- install.php | 3 +-- var/Typecho/Widget/Helper/Form/Element/Checkbox.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/install.php b/install.php index 32e5bb75..ae9cdf9a 100644 --- a/install.php +++ b/install.php @@ -1031,8 +1031,7 @@ function install_step_2_perform() ->addRule('dbFile', 'required', _t('确认您的配置')) ->addRule('dbFile', function (string $path) { $pattern = "/^(\/[._a-z0-9-]+)*[a-z0-9]+\.[a-z0-9]{2,}$/i"; - if (strstr(PHP_OS, 'WIN')) - { + if (strstr(PHP_OS, 'WIN')) { $pattern = "/(\/[._a-z0-9-]+)*[a-z0-9]+\.[a-z0-9]{2,}$/i"; } return !!preg_match($pattern, $path); diff --git a/var/Typecho/Widget/Helper/Form/Element/Checkbox.php b/var/Typecho/Widget/Helper/Form/Element/Checkbox.php index 15353975..b931d692 100644 --- a/var/Typecho/Widget/Helper/Form/Element/Checkbox.php +++ b/var/Typecho/Widget/Helper/Form/Element/Checkbox.php @@ -61,7 +61,7 @@ class Checkbox extends Element */ protected function inputValue($value) { - $values = is_array($value) ? $value : [$value]; + $values = is_null($value) ? [] : (is_array($value) ? $value : [$value]); foreach ($this->options as $option) { $option->removeAttribute('checked'); From c725fec12e994d904c01427c6d41d38c6ea1de08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Tue, 16 May 2023 14:33:26 +0800 Subject: [PATCH 08/12] Add a prompt message for manual database creation (#1348) * Optimize for no database during install * change tips --- install.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index ae9cdf9a..7439890d 100644 --- a/install.php +++ b/install.php @@ -1076,7 +1076,12 @@ function install_step_2_perform() $installDb->addServer($dbConfig, \Typecho\Db::READ | \Typecho\Db::WRITE); $installDb->query('SELECT 1=1'); } catch (\Typecho\Db\Adapter\ConnectionException $e) { - install_raise_error(_t('对不起, 无法连接数据库, 请先检查数据库配置再继续进行安装: "%s"', $e->getMessage())); + $code = $e->getCode(); + if (('Mysql' == $type && 1049 == $code) || ('Pgsql' == $type && 7 == $code)) { + install_raise_error(_t('数据库: "%s"不存在,请手动创建后重试', $config['dbDatabase'])); + } else { + install_raise_error(_t('对不起, 无法连接数据库, 请先检查数据库配置再继续进行安装: "%s"', $e->getMessage())); + } } catch (\Typecho\Db\Exception $e) { install_raise_error(_t('安装程序捕捉到以下错误: "%s". 程序被终止, 请检查您的配置信息.', $e->getMessage())); } From 6f19a24aa3b75106726c4adfddd989d97a94d4ad Mon Sep 17 00:00:00 2001 From: joyqi Date: Tue, 16 May 2023 21:45:41 +0800 Subject: [PATCH 09/12] fix #1574 (#1575) * split multiple values * add a new field type 'json' to handle complex situation * fix json input display --- admin/custom-fields.php | 8 ++++--- var/Widget/Base/Contents.php | 40 ++++++++++++++++--------------- var/Widget/Contents/Post/Edit.php | 12 +++++++--- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/admin/custom-fields.php b/admin/custom-fields.php index 39e52ae8..8e94b0bd 100644 --- a/admin/custom-fields.php +++ b/admin/custom-fields.php @@ -9,8 +9,8 @@ $defaultFields = isset($post) ? $post->getDefaultFieldItems() : $page->getDefaul class="i-caret-right"> - - + + @@ -37,12 +37,14 @@ $defaultFields = isset($post) ? $post->getDefaultFieldItems() : $page->getDefaul value="int" selected> +
+ rows="2"> diff --git a/var/Widget/Base/Contents.php b/var/Widget/Base/Contents.php index e454b1d1..b42c870f 100644 --- a/var/Widget/Base/Contents.php +++ b/var/Widget/Base/Contents.php @@ -345,41 +345,42 @@ class Contents extends Base implements QueryInterface * * @param string $name * @param string $type - * @param string $value + * @param mixed $value * @param integer $cid * @return integer|bool * @throws Exception */ - public function setField(string $name, string $type, string $value, int $cid) + public function setField(string $name, string $type, $value, int $cid) { if ( empty($name) || !$this->checkFieldName($name) - || !in_array($type, ['str', 'int', 'float']) + || !in_array($type, ['str', 'int', 'float', 'json']) ) { return false; } + if ($type === 'json') { + $value = json_encode($value); + } + $exist = $this->db->fetchRow($this->db->select('cid')->from('table.fields') ->where('cid = ? AND name = ?', $cid, $name)); + $rows = [ + 'type' => $type, + 'str_value' => 'str' == $type || 'json' == $type ? $value : null, + 'int_value' => 'int' == $type ? intval($value) : 0, + 'float_value' => 'float' == $type ? floatval($value) : 0 + ]; + if (empty($exist)) { - return $this->db->query($this->db->insert('table.fields') - ->rows([ - 'cid' => $cid, - 'name' => $name, - 'type' => $type, - 'str_value' => 'str' == $type ? $value : null, - 'int_value' => 'int' == $type ? intval($value) : 0, - 'float_value' => 'float' == $type ? floatval($value) : 0 - ])); + $rows['cid'] = $cid; + $rows['name'] = $name; + + return $this->db->query($this->db->insert('table.fields')->rows($rows)); } else { return $this->db->query($this->db->update('table.fields') - ->rows([ - 'type' => $type, - 'str_value' => 'str' == $type ? $value : null, - 'int_value' => 'int' == $type ? intval($value) : 0, - 'float_value' => 'float' == $type ? floatval($value) : 0 - ]) + ->rows($rows) ->where('cid = ? AND name = ?', $cid, $name)); } } @@ -872,7 +873,8 @@ class Contents extends Base implements QueryInterface ->where('cid = ?', $this->cid)); foreach ($rows as $row) { - $fields[$row['name']] = $row[$row['type'] . '_value']; + $value = 'json' == $row['type'] ? json_decode($row['str_value'], true) : $row[$row['type'] . '_value']; + $fields[$row['name']] = $value; } return new Config($fields); diff --git a/var/Widget/Contents/Post/Edit.php b/var/Widget/Contents/Post/Edit.php index fa8c1e5d..1679d7c4 100644 --- a/var/Widget/Contents/Post/Edit.php +++ b/var/Widget/Contents/Post/Edit.php @@ -229,8 +229,14 @@ class Edit extends Contents implements ActionInterface if (preg_match("/^fields\[(.+)\]$/", $name, $matches)) { $name = $matches[1]; } else { + $inputName = 'fields[' . $name . ']'; + if (preg_match("/^(.+)\[\]$/", $name, $matches)) { + $name = $matches[1]; + $inputName = 'fields[' . $name . '][]'; + } + foreach ($item->inputs as $input) { - $input->setAttribute('name', 'fields[' . $name . ']'); + $input->setAttribute('name', $inputName); } } @@ -662,8 +668,8 @@ class Edit extends Contents implements ActionInterface } $customFields = $this->request->getArray('fields'); - if (!empty($customFields)) { - $fields = array_merge($fields, $customFields); + foreach ($customFields as $key => $val) { + $fields[$key] = [is_array($val) ? 'json' : 'str', $val]; } return $fields; From c1753aa04eddca010d362aaa898e12459bf7f546 Mon Sep 17 00:00:00 2001 From: joyqi Date: Wed, 17 May 2023 11:35:18 +0800 Subject: [PATCH 10/12] improve release ci, upload built asset after new release published. (#1576) --- .github/workflows/Typecho-release-Ci.yml | 27 ++++++------------------ 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/.github/workflows/Typecho-release-Ci.yml b/.github/workflows/Typecho-release-Ci.yml index 7a2f65fb..08f4d117 100644 --- a/.github/workflows/Typecho-release-Ci.yml +++ b/.github/workflows/Typecho-release-Ci.yml @@ -1,14 +1,14 @@ name: Typecho Build Release Ci on: - push: - tags: - - 'v*' + release: + types: [published] + jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Build run: | mkdir build @@ -17,27 +17,14 @@ jobs: chmod 755 build/usr/uploads/ rm -rf build/admin/src cd build && zip -q -r typecho.zip * && mv typecho.zip ../ && cd - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - draft: true - prerelease: false - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: shogo82148/actions-upload-release-asset@v1 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ github.event.release.upload_url }} asset_path: ./typecho.zip asset_name: typecho.zip asset_content_type: application/zip - - name: Trigger langs build + - name: Trigger language build run: | curl -XPOST -H "Authorization: token ${{ secrets.WORKFLOW_TOKEN }}" \ -H "Accept: application/vnd.github.everest-preview+json" \ From 206880ba714d80bfb0638aeacb49c58b8fb1b327 Mon Sep 17 00:00:00 2001 From: logdd Date: Mon, 5 Jun 2023 15:15:24 +0800 Subject: [PATCH 11/12] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E7=9A=84=E4=BC=98=E5=8C=96=20(#1586)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用$isNamespace变量,避免重复判断 --- var/Typecho/Common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/var/Typecho/Common.php b/var/Typecho/Common.php index 02937782..7bdb18cc 100644 --- a/var/Typecho/Common.php +++ b/var/Typecho/Common.php @@ -83,7 +83,7 @@ namespace Typecho { $isPlugin = false; // detect if class is predefined - if (strpos($className, '\\') !== false) { + if ($isNamespace) { $isPlugin = strpos(ltrim($className, '\\'), PLUGIN_NAMESPACE . '\\') !== false; if ($isPlugin) { From 9d1b01a8736ab1ce39e9e2e666a2f992eba28b0f Mon Sep 17 00:00:00 2001 From: Kent Liao <2964556627@qq.com> Date: Wed, 14 Jun 2023 08:58:27 +0800 Subject: [PATCH 12/12] fix: adding checkbox options after theme initialization cannot be saved. (#1591) * fix: adding checkbox options after theme initialization cannot be saved. * chore: Use isset to determine if an option exists --- var/Widget/Themes/Config.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/var/Widget/Themes/Config.php b/var/Widget/Themes/Config.php index 02bda8f8..ab97a269 100644 --- a/var/Widget/Themes/Config.php +++ b/var/Widget/Themes/Config.php @@ -71,7 +71,9 @@ class Config extends BaseOptions if (!empty($inputs)) { foreach ($inputs as $key => $val) { - $form->getInput($key)->value($this->options->{$key}); + if (isset($this->options->{$key})) { + $form->getInput($key)->value($this->options->{$key}); + } } }