Compare commits

..

183 Commits

Author SHA1 Message Date
刘祥超
6255573e2e 版本号修改为1.3.8 2024-04-30 19:07:25 +08:00
刘祥超
26bcb3b590 可以在集群设置中修改节点最大并发读/写数 2024-04-30 19:07:17 +08:00
刘祥超
fd2ae5d445 修改Docker文件版本号为1.3.7 2024-04-29 23:15:24 +08:00
刘祥超
d51370d443 版本修改为1.3.7 2024-04-29 23:12:21 +08:00
刘祥超
2d711408c6 管理平台界面设置中增加“使用的DNS解析库”选项 2024-04-29 15:07:09 +08:00
刘祥超
e911039e33 更新依赖库 2024-04-29 14:24:40 +08:00
刘祥超
e915deffb3 安装MySQL过程中测试MySQL是否能够连接后关闭连接 2024-04-28 11:41:07 +08:00
刘祥超
3b61871399 完善添加/修改源站时“回源主机名”的提示文字 2024-04-25 10:24:20 +08:00
刘祥超
cb33d58685 Docker版本号修改为1.3.6 2024-04-22 11:17:20 +08:00
刘祥超
1135bfafd5 版本号变更为1.3.6 2024-04-22 10:55:55 +08:00
刘祥超
ac9716c355 升级依赖库 2024-04-21 20:56:13 +08:00
刘祥超
f501907264 修改Dockerfile中的版本号为1.3.5 2024-04-21 19:58:05 +08:00
刘祥超
39774542a5 修复otp相关编译问题 2024-04-21 14:30:13 +08:00
刘祥超
783b81bc45 版本号修改为1.3.5 2024-04-20 22:46:48 +08:00
刘祥超
af57cb0fed 优化文字提示 2024-04-20 10:20:10 +08:00
刘祥超
6470fbe9f7 提交components.js 2024-04-20 10:15:39 +08:00
刘祥超
19f3be2895 OTP认证二维码增加下载链接 2024-04-20 10:15:31 +08:00
刘祥超
8404dde243 更新依赖库/修复用户OTP中的产品名称 2024-04-20 10:00:15 +08:00
刘祥超
aa52d695e6 集群设置--网站设置中增加“自动gzip回源”选项 2024-04-18 16:15:40 +08:00
刘祥超
4c83d2cbc3 优化源站列表显示 2024-04-18 15:27:08 +08:00
刘祥超
014973a575 优化源站列表 2024-04-18 15:17:31 +08:00
刘祥超
84c8cb3450 源站地址填写为HTTP时,自动检测HTTPS是否可用 2024-04-18 11:31:47 +08:00
刘祥超
9aba52c276 防盗链功能增加“例外URL“和“限制URL”设置 2024-04-17 15:45:06 +08:00
刘祥超
bbfeb5ec16 UA名单支持批量添加关键词 2024-04-17 15:30:24 +08:00
刘祥超
25f2c68738 UA名单功能增加“例外URL“和“限制URL”设置 2024-04-17 15:10:12 +08:00
刘祥超
cbd2419f63 例外和限制URL中增加常见图片、常见音频、常见视频等规则 2024-04-17 14:46:40 +08:00
刘祥超
b61ffef9e8 内容压缩功能增加“例外URL“和“限制URL”设置 2024-04-17 13:54:55 +08:00
刘祥超
18fcc43326 CC规则默认忽略常用文件 2024-04-16 14:14:31 +08:00
刘祥超
dd63154bf7 版本号修改为1.3.4.4 2024-04-16 13:52:58 +08:00
刘祥超
fd2fce8a8d 取消内容压缩中的级别设置 2024-04-16 11:19:01 +08:00
刘祥超
48c90786e9 优化“预热超时时间”输入框长度 2024-04-15 21:15:33 +08:00
刘祥超
152c679565 版本号修改为1.3.4.3 2024-04-15 09:26:25 +08:00
刘祥超
1e428c6a71 删除一直未实现的Unix协议相关内容 2024-04-14 17:12:42 +08:00
刘祥超
2b26064c7d 限制TCP和UDP网站操作HTTP网站的功能 2024-04-14 16:45:17 +08:00
刘祥超
c25c713ac1 源站增加快速停用/启用功能 2024-04-14 16:29:57 +08:00
刘祥超
141fb7bc61 优化WAF CC规则的文字提示 2024-04-13 19:22:45 +08:00
刘祥超
436ee75709 简化IP名单中创建IP操作/支持IP以CIDR方式显示 2024-04-13 16:45:32 +08:00
刘祥超
939b925dfa 优化修改IP名单中IP信息 2024-04-13 10:07:45 +08:00
刘祥超
9fff1f4d03 版本号修改为1.3.4.2 2024-04-09 10:05:28 +08:00
刘祥超
68d2586120 增加URL通配符示例 2024-04-09 09:46:02 +08:00
刘祥超
6050df45e8 提交components.js 2024-04-08 14:57:17 +08:00
刘祥超
ad664f6d60 将版本号修改为1.3.4.1 2024-04-08 14:56:43 +08:00
刘祥超
0bc2894700 增加"edge-admin security.reset"命令来使用命令行重置安全设置 2024-04-08 11:38:04 +08:00
刘祥超
b0d63c0d5b 系统安全设置中增加“仅从自定义报头中获取IP”选项 2024-04-08 11:21:56 +08:00
刘祥超
3fa1cf5a2c 用户使用反向代理访问系统时主动引导用户设置“自定义客户端IP报头“ 2024-04-08 11:07:51 +08:00
刘祥超
33a5c86beb 使用本地SID二次校验增强管理系统安全性 2024-04-08 10:24:10 +08:00
刘祥超
37441b26f1 优化WAF策略默认设置
* 增加JSCookie动作选项
* 拦截动作增加“失败全局封禁”选项
* 人机识别动作增加“失败全局封禁”选项
* IP名单中的“服务”文字改为“网站”
2024-04-07 14:20:17 +08:00
刘祥超
9f123b9a26 优化IP名单相关文字提示 2024-04-06 19:33:53 +08:00
刘祥超
8b6b6319b5 正在执行中的缓存任务状态从“执行中”改为“部分完成” 2024-04-06 15:27:39 +08:00
刘祥超
5bce566988 IP检查结果也可以显示IPv6范围 2024-04-06 15:14:51 +08:00
刘祥超
ca0c845759 IP名单支持IPv6范围 2024-04-06 14:55:29 +08:00
刘祥超
a60a7879a7 优化批量删除IP名单文字提示 2024-04-06 10:38:21 +08:00
刘祥超
1b131b26ee 更好地支持IPv6 2024-04-06 10:07:53 +08:00
刘祥超
16f4fa6c3f 缓存策略类型从“内存缓存”切换到“文件缓存“时增加默认设置 2024-04-05 11:24:05 +08:00
刘祥超
fa94426133 优化节点CPU线程数文字提示 2024-04-04 20:31:29 +08:00
刘祥超
90484aaa24 集群设置增加自动硬盘TRIM选项 2024-04-04 17:04:28 +08:00
刘祥超
20fae556d7 “磁盘”文字改为“硬盘” 2024-04-04 16:48:49 +08:00
刘祥超
14e0610af8 更新依赖库 2024-04-03 08:14:33 +08:00
刘祥超
8ae708a682 取消MMAP相关选项修改限制 2024-03-30 08:43:38 +08:00
刘祥超
fc27d210bb 使用MMAP提升缓存读取性能 2024-03-29 19:30:12 +08:00
刘祥超
49da63c146 修复glibc版本过低无法启动的问题 2024-03-25 16:50:19 +08:00
刘祥超
e45361d9d3 修复手机下菜单太宽的问题 2024-03-25 11:47:21 +08:00
刘祥超
f7941c2251 更新docker版本 2024-03-24 20:43:02 +08:00
刘祥超
b72eeb29a9 将版本号修改为1.3.4 2024-03-24 20:08:01 +08:00
刘祥超
b4dace1253 修复非gcc下编译错误 2024-03-21 17:38:24 +08:00
刘祥超
4edec8b20b 提交components.js 2024-03-18 15:59:46 +08:00
刘祥超
797abbd4e6 编译脚本支持GCC 2024-03-18 15:59:29 +08:00
刘祥超
bce8176866 管理系统增加XSS和SQL注入攻击防御 2024-03-18 12:42:49 +08:00
刘祥超
005e25c1b8 增强安全性 2024-03-18 11:45:13 +08:00
刘祥超
711e36d0bf 节点SSH密码和私钥均以掩码方式显示 2024-03-18 10:51:14 +08:00
刘祥超
78b52e7b35 DNS服务商中的密钥数据以掩码方式显示 2024-03-18 10:21:15 +08:00
刘祥超
89638a5473 将节点IP地址中的“名称”改为“备注“ 2024-03-18 08:50:14 +08:00
刘祥超
3755a1c970 修改CookieID 2024-03-18 08:20:39 +08:00
刘祥超
21963ac738 恢复默认检查区域 2024-03-18 08:13:47 +08:00
刘祥超
b67ea18471 安全设置中自定义客户端IP报头增加True-Client-IP 2024-03-16 10:03:28 +08:00
刘祥超
6e27530dec 管理系统安全设置CheckClientRegion参数默认改为false 2024-03-16 09:57:55 +08:00
刘祥超
c9d5e38db2 集群设置--网站设置中“处理未绑定域名方式”支持跳转到网址 2024-03-16 08:59:24 +08:00
刘祥超
996adc37ff 智能DNS中国家/地区线路下支持省/州的细分 2024-03-14 20:41:36 +08:00
刘祥超
4f6e0f9c65 检测本地数据库时,主机地址增加尝试localhost、172.20.0.2 2024-03-12 17:07:03 +08:00
刘祥超
5d5d129604 将ACME申请证书功能中部分“服务商”文字改为“证书服务商”/没有DNS服务商时提示用户添加 2024-03-12 10:41:19 +08:00
刘祥超
dcf02f4509 优化安全设置中“自定义客户端IP报头”中默认报头交互 2024-03-10 12:02:41 +08:00
刘祥超
cbaa344467 管理员登录时默认不启用“检查客户端区域”选项,避免因为设置了反向代理导致登录异常 2024-03-10 11:55:59 +08:00
刘祥超
7da5feada0 在缓存任务键值中增加集群信息,以便于调试问题 2024-03-10 11:25:18 +08:00
刘祥超
d901de0063 优化滚动条下的菜单 2024-03-10 08:47:11 +08:00
刘祥超
9b730e2a71 优化CSS代码 2024-03-09 22:57:21 +08:00
刘祥超
facd386cf7 优化自动安装的MySQL服务 2024-03-08 20:02:47 +08:00
刘祥超
f72902cd8d 优化systemd服务配置 2024-03-08 19:00:14 +08:00
刘祥超
70e1ef0fdb 由于密码初始化界面文字提示 2024-01-29 19:02:09 +08:00
刘祥超
74680a83a9 部分doctype改为DOCTYPE 2024-01-29 18:57:46 +08:00
刘祥超
1d9b15ad90 单体应用初始化的时候自动进入修改用户名密码界面 2024-01-29 18:57:33 +08:00
刘祥超
9c9132ad72 版本号修改为1.3.3.1 2024-01-29 17:58:47 +08:00
刘祥超
69ec57f1bf 开源版本网站默认跳入设置页面 2024-01-29 17:55:57 +08:00
刘祥超
d11adf3fe6 删除不需要的代码 2024-01-28 20:05:21 +08:00
刘祥超
faa34bc216 修复创建网站时不启用访问日志导致Websocket、统计等选项失效的问题 2024-01-28 15:03:29 +08:00
刘祥超
76451c9d27 优化编译脚本 2024-01-22 18:48:51 +08:00
刘祥超
7e5802dcd1 优化没有网站时的提示 2024-01-21 21:17:42 +08:00
刘祥超
2b6d1c70a8 集群节点列表页增加停用/启用操作 2024-01-21 17:42:56 +08:00
刘祥超
faaef3b353 Dockerfile版本号修改为1.3.3 2024-01-21 16:58:01 +08:00
刘祥超
6ffa1d4a1d 版本号修改为1.3.3 2024-01-21 16:55:55 +08:00
刘祥超
10f0e0dcca 提交components.js 2024-01-21 16:55:31 +08:00
刘祥超
0afad2e192 增加创建本地节点命令 2024-01-21 16:55:22 +08:00
刘祥超
6a1e7266b9 优化缓存文件句柄缓存的文字提示 2024-01-21 14:31:05 +08:00
刘祥超
6ffc552bd8 WAF允许ALLOW动作增加有效范围 2024-01-20 21:23:59 +08:00
刘祥超
8d8245971a WAF策略增加显示页面动作默认设置 2024-01-20 16:17:28 +08:00
刘祥超
a2f730d57e 修复部分内置页面没有<head>标签的问题 2024-01-20 10:15:05 +08:00
刘祥超
bf282c41fd 优化创建策略规则集提示 2024-01-19 11:24:02 +08:00
刘祥超
3e3f7b2cc6 WAF动作默认设置为“显示网页(page)”,减少因错误规则导致的IP封禁 2024-01-19 11:13:01 +08:00
刘祥超
0580e063be 优化网站列表批量操作按钮,防止误操作 2024-01-19 10:56:31 +08:00
刘祥超
2924aad25e 修改版本号为1.3.2.2 2024-01-16 20:59:07 +08:00
刘祥超
39907299cd 提交components.js 2024-01-16 20:58:58 +08:00
刘祥超
77705895d5 优化添加规则界面 2024-01-16 20:42:06 +08:00
刘祥超
3fb85edf0b 提交components.js 2024-01-15 08:51:21 +08:00
刘祥超
0e7a968cac 版本号修改为1.3.2.1 2024-01-15 08:40:14 +08:00
刘祥超
195a9dc771 优化网站设置和路由设置中的左侧菜单 2024-01-14 20:33:07 +08:00
刘祥超
ca227a846a 优化代码 2024-01-13 19:31:52 +08:00
刘祥超
e1057fc76e 字符编码设置增加“强制替换”选项 2024-01-13 16:28:37 +08:00
刘祥超
b5168c3174 优化文字 2024-01-13 15:46:26 +08:00
刘祥超
4ced00de50 修改API节点时提示用户需要重启edge-api进程 2024-01-12 16:44:02 +08:00
刘祥超
750c929506 实现批量删除网站功能 2024-01-11 18:40:29 +08:00
刘祥超
8b46a5a5af 自定义页面跳转支持使用变量 2024-01-11 15:37:31 +08:00
刘祥超
10d606a101 增加带宽相关组件 2024-01-11 15:17:52 +08:00
刘祥超
45d36dadcb 修复“迁移”功能中无法远程修改API节点访问地址的问题 2024-01-09 10:39:09 +08:00
刘祥超
61484d5cb8 增加用户系统文章相关管理 2024-01-09 10:21:05 +08:00
刘祥超
b7775a99ef 优化添加缓存条件和不缓存条件交互 2023-12-28 15:50:19 +08:00
刘祥超
c45ba38ffa 修复参数匹配不区分大小写选项无法保存的问题 2023-12-28 15:31:40 +08:00
刘祥超
e97d9fb8a0 优化可执行文件程序 2023-12-26 09:09:21 +08:00
刘祥超
bb56ec9ec5 修改Dockerfile版本 2023-12-24 17:35:30 +08:00
刘祥超
6d8f659558 版本号修改为1.3.2 2023-12-24 11:14:31 +08:00
刘祥超
7e9047c8fe 优化IP名单管理 2023-12-24 11:05:38 +08:00
刘祥超
b5af560b67 IP名单中可以批量删除最多10000个IP 2023-12-24 10:53:03 +08:00
刘祥超
5622636870 优化请求脚本相关代码 2023-12-23 20:54:37 +08:00
刘祥超
372c276afc 优化英文菜单样式 2023-12-23 09:41:33 +08:00
刘祥超
250b5fdd10 取消“通用设置”菜单 2023-12-22 16:58:16 +08:00
刘祥超
d1fb0bf8b7 优化数据库节点相关文字提示 2023-12-21 17:18:04 +08:00
刘祥超
490c273d36 改进部分文字 2023-12-20 15:06:19 +08:00
刘祥超
0773dbaa28 在网站设置中显示流量相关限制状态 2023-12-19 14:56:05 +08:00
刘祥超
4d04964740 修复缓存条件简单和复杂切换时可能导致复杂条件失效的问题 2023-12-19 09:26:18 +08:00
刘祥超
537e6c9a5e 修复一处编译问题 2023-12-18 10:12:20 +08:00
刘祥超
082a6721f3 版本号修改为1.3.1.2 2023-12-18 08:51:16 +08:00
刘祥超
d515a20129 提升Go模板安全性 2023-12-14 19:57:02 +08:00
刘祥超
a66d3ef61a 更新相关库 2023-12-14 19:53:43 +08:00
刘祥超
3b05b1a933 优化修改网站域名时套餐相关检查 2023-12-13 19:02:12 +08:00
刘祥超
105479ce4b 网站所属用户没有套餐时提示用户 2023-12-13 18:53:08 +08:00
刘祥超
1a950e3aad 非商业版隐藏网站配置复制图标 2023-12-13 18:46:16 +08:00
刘祥超
79fa76e039 缓存设置中可以设置缓存主域名,用来复用多域名下的缓存 2023-12-13 18:33:48 +08:00
刘祥超
9db9a70a68 优化文字 2023-12-13 11:08:35 +08:00
刘祥超
632862c742 优化多语言相关代码 2023-12-13 10:29:50 +08:00
刘祥超
76d0783f6f 切换语言时保存语言设置 2023-12-12 22:39:42 +08:00
刘祥超
fc6a0e9813 切换语言时保存更长时间 2023-12-12 22:28:17 +08:00
刘祥超
038e289057 更新components.js 2023-12-12 22:28:04 +08:00
刘祥超
4b2a4af1e7 优化极验相关代码 2023-12-12 15:00:38 +08:00
刘祥超
0fb2555bea Update en-us.js 2023-12-12 12:11:13 +08:00
刘祥超
bd8a809065 优化英文版样式 2023-12-12 11:54:20 +08:00
刘祥超
fc6fb7dce2 部分菜单实现中英文切换 2023-12-12 11:47:41 +08:00
刘祥超
bacb94abe6 优化WebP相关提示和代码 2023-12-11 11:07:02 +08:00
刘祥超
eb04421412 WebP转换质量转移到WebP策略配置 2023-12-11 10:00:22 +08:00
刘祥超
2fbd5a785e 改进部分表单文字提示 2023-12-10 10:46:50 +08:00
刘祥超
d8ae1525be 管理系统安全设置增加“自定义客户端IP报头” 2023-12-10 10:46:35 +08:00
刘祥超
daaa165d25 优化集群设置--WAF策略选择窗口尺寸 2023-12-10 08:55:12 +08:00
刘祥超
27ea02be5e 优化WAF动作“显示网页”显示 2023-12-09 15:55:17 +08:00
刘祥超
8eac1ef307 优化文字提示 2023-12-08 16:00:48 +08:00
刘祥超
1181974ea5 WAF规则集列表中参数和操作符增加悬停提示 2023-12-08 12:01:36 +08:00
刘祥超
5f57fa0470 WAF规则集列表中参数显示中文 2023-12-08 11:20:32 +08:00
刘祥超
c0ad74ab7b WAF操作符对应的对比值可以不填写 2023-12-07 20:24:26 +08:00
刘祥超
c27905e1cc 优化WAF多行对比值的文字提示 2023-12-07 11:44:37 +08:00
刘祥超
cadb65a85f 业务域名CNAME检查DNS主机地址增加Google公共DNS 2023-12-06 09:42:15 +08:00
刘祥超
b3c152685a 优化检查域名解析文字提示 2023-12-05 11:33:32 +08:00
刘祥超
16ce0726f8 将部分MB、GB...改成MiB、GiB... 2023-12-03 11:41:04 +08:00
刘祥超
5be306e087 将部分MB、GB...改成MiB、GiB... 2023-12-03 11:30:02 +08:00
刘祥超
0d21fc27ab 增加“极验-行为验”验证码集成支持 2023-11-29 16:57:58 +08:00
刘祥超
7deaa678bc 创建集群时默认生成子域名 2023-11-27 11:28:01 +08:00
刘祥超
0d230a9237 <domain-box>组件自动分割单个域名 2023-11-27 10:31:41 +08:00
刘祥超
8453d754f1 添加域名时自动分割用逗号、顿号连接的域名列表 2023-11-27 10:20:54 +08:00
刘祥超
05f77f7a0d 修复go.mod 2023-11-24 10:21:07 +08:00
刘祥超
4334b6f148 修复若干测试用例 2023-11-24 09:13:18 +08:00
刘祥超
72955759d7 更新go.sum 2023-11-24 08:53:40 +08:00
刘祥超
18dc52996d 更新Dockerfile中版本 2023-11-23 17:40:28 +08:00
刘祥超
0b16d09330 版本号修改为1.3.1 2023-11-23 17:26:24 +08:00
刘祥超
027d81a8cd 优化SSH认证sudo设置 2023-11-23 16:12:41 +08:00
刘祥超
d833158784 DNSPod改名为腾讯云DNSPod/DNSPod 支持腾讯云API密钥 2023-11-23 15:14:34 +08:00
刘祥超
75024c9364 优化X-Cache报头设置文字提示 2023-11-22 19:02:18 +08:00
刘祥超
1dd5db3c42 优化第三方域名解析azure dns提示文字 2023-11-22 10:30:29 +08:00
刘祥超
faba80e315 GRPC增加Keepalive参数 2023-11-20 09:56:35 +08:00
355 changed files with 44471 additions and 2361 deletions

View File

@@ -53,7 +53,7 @@ function build() {
# generate files
echo "generating files ..."
go run -tags $TAG "$ROOT"/../cmd/edge-admin/main.go generate
env CGO_ENABLED=0 go run -tags $TAG "$ROOT"/../cmd/edge-admin/main.go generate
if [ "$(which uglifyjs)" ]; then
echo "compress to component.js ..."
uglifyjs --compress --mangle -- "${JS_ROOT}"/components.src.js > "${JS_ROOT}"/components.js
@@ -99,9 +99,36 @@ function build() {
rm -f "$(basename "$EDGE_API_ZIP_FILE")"
cd - || exit
# find gcc
GCC_DIR=""
CC_PATH=""
CXX_PATH=""
if [ "${ARCH}" == "amd64" ]; then
GCC_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin"
CC_PATH="x86_64-unknown-linux-gnu-gcc"
CXX_PATH="x86_64-unknown-linux-gnu-g++"
fi
if [ "${ARCH}" == "arm64" ]; then
GCC_DIR="/usr/local/gcc/aarch64-unknown-linux-gnu/bin"
CC_PATH="aarch64-unknown-linux-gnu-gcc"
CXX_PATH="aarch64-unknown-linux-gnu-g++"
fi
# build
echo "building ${NAME} ..."
env GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags $TAG -ldflags="-s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
if [ -f "${GCC_DIR}/${CC_PATH}" ]; then
echo " building ${NAME} with gcc ..."
env CC="${GCC_DIR}/${CC_PATH}" \
CXX="${GCC_DIR}/${CXX_PATH}" \
CGO_ENABLED=1 \
GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags "${TAG} gcc" -ldflags="-linkmode external -extldflags -static -s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
else
GOOS="$OS" GOARCH="$ARCH" go build -trimpath -tags $TAG -ldflags="-s -w" -o "$DIST"/bin/${NAME} "$ROOT"/../cmd/edge-admin/main.go
fi
if [ ! -f "${DIST}/bin/${NAME}" ]; then
echo "build '${NAME}' failed!"
exit
fi
# delete hidden files
find "$DIST" -name ".DS_Store" -delete

View File

@@ -3,7 +3,7 @@
JS_ROOT=../web/public/js
echo "generating component.src.js ..."
go run -tags=community ../cmd/edge-admin/main.go generate
env CGO_ENABLED=0 go run -tags=community ../cmd/edge-admin/main.go generate
if [ "$(which uglifyjs)" ]; then
echo "compress to component.js ..."

View File

@@ -40,7 +40,9 @@ func main() {
Option("demo", "switch to demo mode").
Option("dev", "switch to 'dev' mode").
Option("prod", "switch to 'prod' mode").
Option("upgrade [--url=URL]", "upgrade from official site or an url")
Option("upgrade [--url=URL]", "upgrade from official site or an url").
Option("install-local-node", "install a local node").
Option("security.reset", "reset security config")
app.On("daemon", func() {
nodes.NewAdminNode().Daemon()
@@ -76,7 +78,7 @@ func main() {
fmt.Println("done")
})
app.On("recover", func() {
sock := gosock.NewTmpSock(teaconst.ProcessName)
var sock = gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
@@ -89,7 +91,7 @@ func main() {
fmt.Println("enter recovery mode successfully")
})
app.On("demo", func() {
sock := gosock.NewTmpSock(teaconst.ProcessName)
var sock = gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
@@ -178,6 +180,17 @@ func main() {
log.Println("restarting ...")
app.RunRestart()
})
app.On("security.reset", func() {
var sock = gosock.NewTmpSock(teaconst.ProcessName)
if !sock.IsListening() {
fmt.Println("[ERROR]the service not started yet, you should start the service first")
return
}
_, _ = sock.Send(&gosock.Command{
Code: "security.reset",
})
fmt.Println("ok")
})
app.Run(func() {
var adminNode = nodes.NewAdminNode()
adminNode.Run()

View File

@@ -1,7 +1,7 @@
FROM --platform=linux/amd64 alpine:latest
LABEL maintainer="goedge.cdn@gmail.com"
ENV TZ "Asia/Shanghai"
ENV VERSION 1.3.0
ENV VERSION 1.3.7
ENV ROOT_DIR /usr/local/goedge
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip

43
go.mod
View File

@@ -1,24 +1,24 @@
module github.com/TeaOSLab/EdgeAdmin
go 1.18
go 1.21
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
require (
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
github.com/cespare/xxhash v1.1.0
github.com/cespare/xxhash/v2 v2.2.0
github.com/go-sql-driver/mysql v1.5.0
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f
github.com/iwind/TeaGo v0.0.0-20240429060313-31a7bc8e9cc9
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
github.com/miekg/dns v1.1.43
github.com/quic-go/quic-go v0.36.0
github.com/quic-go/quic-go v0.42.0
github.com/shirou/gopsutil/v3 v3.22.5
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tealeg/xlsx/v3 v3.2.3
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
golang.org/x/crypto v0.10.0
golang.org/x/sys v0.9.0
google.golang.org/grpc v1.45.0
github.com/xlzd/gotp v0.1.0
golang.org/x/crypto v0.22.0
golang.org/x/sys v0.19.0
google.golang.org/grpc v1.63.2
gopkg.in/yaml.v3 v3.0.1
)
@@ -26,30 +26,25 @@ require (
github.com/frankban/quicktest v1.11.3 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/onsi/ginkgo/v2 v2.17.1 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
github.com/tdewolff/minify/v2 v2.12.7 // indirect
github.com/tdewolff/parse/v2 v2.6.6 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/text v0.10.0 // indirect
golang.org/x/tools v0.10.0 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
google.golang.org/protobuf v1.28.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)

284
go.sum
View File

@@ -1,98 +1,39 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iwind/TeaGo v0.0.0-20210411134150-ddf57e240c2f/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f h1:xo6XmXLtveKcwcZAXV6VMxkWNzy/2dStfHEnyowsGAE=
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY=
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf h1:WA9qgiynESu/DDTnLH6npRI5AK6UL9qwJ2YZ5qhJX5E=
github.com/iwind/TeaGo v0.0.0-20240312020455-6f20b5121caf/go.mod h1:SfqVbWyIPdVflyA6lMgicZzsoGS8pyeLiTRe8/CIpGI=
github.com/iwind/TeaGo v0.0.0-20240429060313-31a7bc8e9cc9 h1:lqSd+OeAMzPlejoVtsmHJEeQ6/MWXCr+Ws2eX9/Rewg=
github.com/iwind/TeaGo v0.0.0-20240429060313-31a7bc8e9cc9/go.mod h1:SfqVbWyIPdVflyA6lMgicZzsoGS8pyeLiTRe8/CIpGI=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -101,24 +42,12 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
@@ -127,15 +56,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.36.0 h1:JIrO7p7Ug6hssFcARjWDiqS2RAKJHCiwPxBAA989rbI=
github.com/quic-go/quic-go v0.36.0/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
@@ -144,170 +68,66 @@ github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJ
github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA=
github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws=
github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo=
github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tealeg/xlsx/v3 v3.2.3 h1:MXnVh+9Y8cUglowItTy2HL3Kv6z+q/0aNjeKuTsVqZQ=
github.com/tealeg/xlsx/v3 v3.2.3/go.mod h1:0hGmAEoZ48SS1ZAE6eqZJkJVXgOMY+8a33vjXa8S8HA=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4=
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -174,6 +174,17 @@ func FindAdminLang(adminId int64) string {
return ""
}
// UpdateAdminLang 修改某个管理员选择的语言
func UpdateAdminLang(adminId int64, langCode string) {
locker.Lock()
defer locker.Unlock()
list, ok := sharedAdminModuleMapping[adminId]
if ok {
list.Lang = langCode
}
}
func FindAdminLangForAction(actionPtr actions.ActionWrapper) (langCode langs.LangCode) {
locker.Lock()
defer locker.Unlock()

View File

@@ -93,6 +93,7 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
}
var config = &systemconfigs.AdminUIConfig{}
config.DNSResolver.Type = nodeconfigs.DNSResolverTypeDefault // 默认值
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[UI_MANAGER]" + err.Error())
@@ -108,7 +109,7 @@ func loadAdminUIConfig() (*systemconfigs.AdminUIConfig, error) {
}
func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
return &systemconfigs.AdminUIConfig{
var config = &systemconfigs.AdminUIConfig{
ProductName: langs.DefaultMessage(codes.AdminUI_DefaultProductName),
AdminSystemName: langs.DefaultMessage(codes.AdminUI_DefaultSystemName),
ShowOpenSourceInfo: true,
@@ -117,6 +118,8 @@ func defaultAdminUIConfig() *systemconfigs.AdminUIConfig {
DefaultPageSize: 10,
TimeZone: nodeconfigs.DefaultTimeZoneLocation,
}
config.DNSResolver.Type = nodeconfigs.DNSResolverTypeDefault
return config
}
// 修改时区

View File

@@ -79,7 +79,7 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return nil, err
}
if len(resp.ValueJSON) == 0 {
sharedSecurityConfig = defaultSecurityConfig()
sharedSecurityConfig = NewSecurityConfig()
return sharedSecurityConfig, nil
}
@@ -88,11 +88,13 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
DenySearchEngines: true,
DenySpiders: true,
}
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
logs.Println("[SECURITY_MANAGER]" + err.Error())
sharedSecurityConfig = defaultSecurityConfig()
sharedSecurityConfig = NewSecurityConfig()
return sharedSecurityConfig, nil
}
err = config.Init()
@@ -103,11 +105,14 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return sharedSecurityConfig, nil
}
func defaultSecurityConfig() *systemconfigs.SecurityConfig {
// NewSecurityConfig create new security config
func NewSecurityConfig() *systemconfigs.SecurityConfig {
return &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
DenySearchEngines: true,
DenySpiders: true,
}
}

View File

@@ -1,9 +1,9 @@
package teaconst
const (
Version = "1.3.0"
Version = "1.3.8"
APINodeVersion = "1.3.0"
APINodeVersion = "1.3.8"
ProductName = "Edge Admin"
ProcessName = "edge-admin"
@@ -14,7 +14,7 @@ const (
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
EncryptMethod = "aes-256-cfb"
CookieSID = "edgesid"
CookieSID = "geadsid"
SessionAdminId = "adminId"
SystemdServiceName = "edge-admin"

View File

@@ -14,6 +14,7 @@ import (
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"io"
"os"
"path/filepath"
@@ -116,6 +117,24 @@ func generateComponentsJSFile() error {
buffer.Write([]byte{';', '\n', '\n'})
}
// WAF checkpoints
var wafCheckpointsMaps = []maps.Map{}
for _, checkpoint := range firewallconfigs.AllCheckpoints {
wafCheckpointsMaps = append(wafCheckpointsMaps, maps.Map{
"name": checkpoint.Name,
"prefix": checkpoint.Prefix,
"description": checkpoint.Description,
})
}
wafCheckpointsJSON, err := json.Marshal(wafCheckpointsMaps)
if err != nil {
logs.Println("ComponentsAction marshal waf rule checkpoints failed: " + err.Error())
} else {
buffer.WriteString("window.WAF_RULE_CHECKPOINTS = ")
buffer.Write(wafCheckpointsJSON)
buffer.Write([]byte{';', '\n', '\n'})
}
// WAF操作符
wafOperatorsJSON, err := json.Marshal(firewallconfigs.AllRuleOperators)
if err != nil {

View File

@@ -3,10 +3,12 @@ package nodes
import (
"errors"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/events"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/iwind/TeaGo"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/lists"
@@ -86,6 +88,9 @@ func (this *AdminNode) Run() {
// 启动API节点
this.startAPINode()
// 设置DNS相关
this.setupDNS()
// 启动IP库
this.startIPLibrary()
@@ -253,6 +258,33 @@ func (this *AdminNode) addPortsToFirewall() {
utils.AddPortsToFirewall(ports)
}
// 设置DNS相关
func (this *AdminNode) setupDNS() {
config, loadErr := configloaders.LoadAdminUIConfig()
if loadErr != nil {
// 默认使用go原生
err := os.Setenv("GODEBUG", "netdns=go")
if err != nil {
logs.Println("[DNS_RESOLVER]set env failed: " + err.Error())
}
return
}
var err error
switch config.DNSResolver.Type {
case nodeconfigs.DNSResolverTypeGoNative:
err = os.Setenv("GODEBUG", "netdns=go")
case nodeconfigs.DNSResolverTypeCGO:
err = os.Setenv("GODEBUG", "netdns=cgo")
default:
// 默认使用go原生
err = os.Setenv("GODEBUG", "netdns=go")
}
if err != nil {
logs.Println("[DNS_RESOLVER]set env failed: " + err.Error())
}
}
// 启动API节点
func (this *AdminNode) startAPINode() {
var configPath = Tea.Root + "/edge-api/configs/api.yaml"
@@ -406,6 +438,10 @@ func (this *AdminNode) listenSock() error {
case "prod": // 切换到prod
Tea.Env = Tea.EnvProd
_ = cmd.ReplyOk()
case "security.reset":
var newConfig = configloaders.NewSecurityConfig()
_ = configloaders.UpdateSecurityConfig(newConfig)
_ = cmd.ReplyOk()
}
})

View File

@@ -72,6 +72,9 @@ func (this *SessionManager) Read(sid string) map[string]string {
}
func (this *SessionManager) WriteItem(sid string, key string, value string) bool {
// 删除缓存
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return false
@@ -95,6 +98,9 @@ func (this *SessionManager) WriteItem(sid string, key string, value string) bool
}
func (this *SessionManager) Delete(sid string) bool {
// 删除缓存
defer ttlcache.DefaultCache.Delete( "SESSION@" + sid)
// 忽略OTP
if strings.HasSuffix(sid, "_otp") {
return false

View File

@@ -20,6 +20,7 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"net"
"net/url"
@@ -514,12 +515,15 @@ func (this *RPCClient) init() error {
grpc.MaxCallSendMsgSize(128<<20),
grpc.UseCompressor(gzip.Name),
)
var keepaliveParams = grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 30 * time.Second,
})
if u.Scheme == "http" {
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions)
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(insecure.NewCredentials()), callOptions, keepaliveParams)
} else if u.Scheme == "https" {
conn, err = grpc.Dial(apiHost, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
})), callOptions)
})), callOptions, keepaliveParams)
} else {
return errors.New("parse endpoint failed: invalid scheme '" + u.Scheme + "'")
}

View File

@@ -34,14 +34,9 @@ func TestRPCClient_NodeRPC(t *testing.T) {
func TestRPC_Dial_HTTP(t *testing.T) {
client, err := NewRPCClient(&configs.APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
DisableUpdate bool `yaml:"disableUpdate"`
}{
Endpoints: []string{"http://127.0.0.1:8004"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
RPCEndpoints: []string{"https://127.0.0.1:8003"},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
}, true)
if err != nil {
t.Fatal(err)
@@ -56,14 +51,9 @@ func TestRPC_Dial_HTTP(t *testing.T) {
func TestRPC_Dial_HTTP_2(t *testing.T) {
client, err := NewRPCClient(&configs.APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
DisableUpdate bool `yaml:"disableUpdate"`
}{
Endpoints: []string{"https://127.0.0.1:8003"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
RPCEndpoints: []string{"https://127.0.0.1:8003"},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
}, true)
if err != nil {
t.Fatal(err)
@@ -78,14 +68,9 @@ func TestRPC_Dial_HTTP_2(t *testing.T) {
func TestRPC_Dial_HTTPS(t *testing.T) {
client, err := NewRPCClient(&configs.APIConfig{
RPC: struct {
Endpoints []string `yaml:"endpoints"`
DisableUpdate bool `yaml:"disableUpdate"`
}{
Endpoints: []string{"https://127.0.0.1:8004"},
},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
RPCEndpoints: []string{"https://127.0.0.1:8004"},
NodeId: "a7e55782dab39bce0901058a1e14a0e6",
Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y",
}, true)
if err != nil {
t.Fatal(err)

View File

@@ -1,6 +1,6 @@
package ttlcache
import "github.com/cespare/xxhash"
import "github.com/cespare/xxhash/v2"
func HashKey(key []byte) uint64 {
return xxhash.Sum64(key)

162
internal/utils/exec/cmd.go Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package executils
import (
"bytes"
"context"
"os"
"os/exec"
"strings"
"time"
)
type Cmd struct {
name string
args []string
env []string
dir string
ctx context.Context
timeout time.Duration
cancelFunc func()
captureStdout bool
captureStderr bool
stdout *bytes.Buffer
stderr *bytes.Buffer
rawCmd *exec.Cmd
}
func NewCmd(name string, args ...string) *Cmd {
return &Cmd{
name: name,
args: args,
}
}
func NewTimeoutCmd(timeout time.Duration, name string, args ...string) *Cmd {
return (&Cmd{
name: name,
args: args,
}).WithTimeout(timeout)
}
func (this *Cmd) WithTimeout(timeout time.Duration) *Cmd {
this.timeout = timeout
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
this.ctx = ctx
this.cancelFunc = cancelFunc
return this
}
func (this *Cmd) WithStdout() *Cmd {
this.captureStdout = true
return this
}
func (this *Cmd) WithStderr() *Cmd {
this.captureStderr = true
return this
}
func (this *Cmd) WithEnv(env []string) *Cmd {
this.env = env
return this
}
func (this *Cmd) WithDir(dir string) *Cmd {
this.dir = dir
return this
}
func (this *Cmd) Start() error {
var cmd = this.compose()
return cmd.Start()
}
func (this *Cmd) Wait() error {
var cmd = this.compose()
return cmd.Wait()
}
func (this *Cmd) Run() error {
if this.cancelFunc != nil {
defer this.cancelFunc()
}
var cmd = this.compose()
return cmd.Run()
}
func (this *Cmd) RawStdout() string {
if this.stdout != nil {
return this.stdout.String()
}
return ""
}
func (this *Cmd) Stdout() string {
return strings.TrimSpace(this.RawStdout())
}
func (this *Cmd) RawStderr() string {
if this.stderr != nil {
return this.stderr.String()
}
return ""
}
func (this *Cmd) Stderr() string {
return strings.TrimSpace(this.RawStderr())
}
func (this *Cmd) String() string {
if this.rawCmd != nil {
return this.rawCmd.String()
}
var newCmd = exec.Command(this.name, this.args...)
return newCmd.String()
}
func (this *Cmd) Process() *os.Process {
if this.rawCmd != nil {
return this.rawCmd.Process
}
return nil
}
func (this *Cmd) compose() *exec.Cmd {
if this.rawCmd != nil {
return this.rawCmd
}
if this.ctx != nil {
this.rawCmd = exec.CommandContext(this.ctx, this.name, this.args...)
} else {
this.rawCmd = exec.Command(this.name, this.args...)
}
if this.env != nil {
this.rawCmd.Env = this.env
}
if len(this.dir) > 0 {
this.rawCmd.Dir = this.dir
}
if this.captureStdout {
this.stdout = &bytes.Buffer{}
this.rawCmd.Stdout = this.stdout
}
if this.captureStderr {
this.stderr = &bytes.Buffer{}
this.rawCmd.Stderr = this.stderr
}
return this.rawCmd
}

View File

@@ -0,0 +1,61 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package executils_test
import (
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"testing"
"time"
)
func TestNewTimeoutCmd_Sleep(t *testing.T) {
var cmd = executils.NewTimeoutCmd(1*time.Second, "sleep", "3")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestNewTimeoutCmd_Echo(t *testing.T) {
var cmd = executils.NewTimeoutCmd(10*time.Second, "echo", "-n", "hello")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestNewTimeoutCmd_Echo2(t *testing.T) {
var cmd = executils.NewCmd("echo", "hello")
cmd.WithStdout()
cmd.WithStderr()
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("raw stdout:", cmd.RawStdout())
t.Log("stderr:", cmd.Stderr())
t.Log("raw stderr:", cmd.RawStderr())
}
func TestNewTimeoutCmd_Echo3(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
err := cmd.Run()
t.Log("error:", err)
t.Log("stdout:", cmd.Stdout())
t.Log("stderr:", cmd.Stderr())
}
func TestCmd_Process(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
err := cmd.Run()
t.Log("error:", err)
t.Log(cmd.Process())
}
func TestNewTimeoutCmd_String(t *testing.T) {
var cmd = executils.NewCmd("echo", "-n", "hello")
t.Log("stdout:", cmd.String())
}

View File

@@ -0,0 +1,58 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build linux
package executils
import (
"golang.org/x/sys/unix"
"io/fs"
"os"
"os/exec"
"syscall"
)
// LookPath customize our LookPath() function, to work in broken $PATH environment variable
func LookPath(file string) (string, error) {
result, err := exec.LookPath(file)
if err == nil && len(result) > 0 {
return result, nil
}
// add common dirs contains executable files these may be excluded in $PATH environment variable
var binPaths = []string{
"/usr/sbin",
"/usr/bin",
"/usr/local/sbin",
"/usr/local/bin",
}
for _, binPath := range binPaths {
var fullPath = binPath + string(os.PathSeparator) + file
stat, err := os.Stat(fullPath)
if err != nil {
continue
}
if stat.IsDir() {
return "", syscall.EISDIR
}
var mode = stat.Mode()
if mode.IsDir() {
return "", syscall.EISDIR
}
err = syscall.Faccessat(unix.AT_FDCWD, fullPath, unix.X_OK, unix.AT_EACCESS)
if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) {
return fullPath, err
}
if mode&0111 != 0 {
return fullPath, nil
}
return "", fs.ErrPermission
}
return "", &exec.Error{
Name: file,
Err: exec.ErrNotFound,
}
}

View File

@@ -0,0 +1,10 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !linux
package executils
import "os/exec"
func LookPath(file string) (string, error) {
return exec.LookPath(file)
}

View File

@@ -3,6 +3,7 @@
package utils
import (
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
"os/exec"
@@ -14,7 +15,7 @@ func AddPortsToFirewall(ports []int) {
// Linux
if runtime.GOOS == "linux" {
// firewalld
firewallCmd, _ := exec.LookPath("firewall-cmd")
firewallCmd, _ := executils.LookPath("firewall-cmd")
if len(firewallCmd) > 0 {
err := exec.Command(firewallCmd, "--add-port="+types.String(port)+"/tcp").Run()
if err == nil {

View File

@@ -2,47 +2,65 @@ package utils
import (
"bytes"
"encoding/binary"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/iwind/TeaGo/types"
"math/big"
"net"
"regexp"
"strings"
)
// IP2Long 将IP转换为整型
func IP2Long(ip string) uint64 {
s := net.ParseIP(ip)
if len(s) != 16 {
return 0
// ParseIPValue 解析IP值
func ParseIPValue(value string) (newValue string, ipFrom string, ipTo string, ok bool) {
if len(value) == 0 {
return
}
if strings.Contains(ip, ":") { // IPv6
bigInt := big.NewInt(0)
bigInt.SetBytes(s.To16())
return bigInt.Uint64()
}
return uint64(binary.BigEndian.Uint32(s.To4()))
}
newValue = value
// IsIPv4 判断是否为IPv4
func IsIPv4(ip string) bool {
if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(ip) {
return false
}
if IP2Long(ip) == 0 {
return false
}
return true
}
// ip1-ip2
if strings.Contains(value, "-") {
var pieces = strings.Split(value, "-")
if len(pieces) != 2 {
return
}
// IsIPv6 判断是否为IPv6
func IsIPv6(ip string) bool {
if !strings.Contains(ip, ":") {
return false
ipFrom = strings.TrimSpace(pieces[0])
ipTo = strings.TrimSpace(pieces[1])
if !iputils.IsValid(ipFrom) || !iputils.IsValid(ipTo) {
return
}
if !iputils.IsSameVersion(ipFrom, ipTo) {
return
}
if iputils.CompareIP(ipFrom, ipTo) > 0 {
ipFrom, ipTo = ipTo, ipFrom
newValue = ipFrom + "-" + ipTo
}
ok = true
return
}
return len(net.ParseIP(ip)) == net.IPv6len
// ip/mask
if strings.Contains(value, "/") {
cidr, err := iputils.ParseCIDR(value)
if err != nil {
return
}
return newValue, cidr.From().String(), cidr.To().String(), true
}
// single value
if iputils.IsValid(value) {
ipFrom = value
ok = true
return
}
return
}
// ExtractIP 分解IP

View File

@@ -5,86 +5,6 @@ import (
"testing"
)
func TestIP2Long(t *testing.T) {
for _, ip := range []string{
"0.0.0.1",
"0.0.0.2",
"127.0.0.1",
"192.0.0.2",
"255.255.255.255",
"2001:db8:0:1::101",
"2001:db8:0:1::102",
"2406:8c00:0:3409:133:18:203:0",
"2406:8c00:0:3409:133:18:203:158",
"2406:8c00:0:3409:133:18:203:159",
"2406:8c00:0:3409:133:18:203:160",
} {
t.Log(ip, " -> ", IP2Long(ip))
}
}
func TestIsIPv4(t *testing.T) {
type testIP struct {
ip string
ok bool
}
for _, item := range []testIP{
{
ip: "1",
ok: false,
},
{
ip: "192.168.0.1",
ok: true,
},
{
ip: "1.1.0.1",
ok: true,
},
{
ip: "255.255.255.255",
ok: true,
},
{
ip: "192.168.0.1233",
ok: false,
},
} {
if IsIPv4(item.ip) != item.ok {
t.Fatal(item.ip, "should be", item.ok)
}
}
}
func TestIsIPv6(t *testing.T) {
type testIP struct {
ip string
ok bool
}
for _, item := range []testIP{
{
ip: "1",
ok: false,
},
{
ip: "2406:8c00:0:3409:133:18:203:158",
ok: true,
},
{
ip: "2406::8c00:0:3409:133:18:203:158",
ok: false,
},
{
ip: "2001:db8:0:1::101",
ok: true,
},
} {
if IsIPv6(item.ip) != item.ok {
t.Fatal(item.ip, "should be", item.ok)
}
}
}
func TestExtractIP(t *testing.T) {
t.Log(ExtractIP("192.168.1.100"))
}
@@ -107,4 +27,4 @@ func TestNextIP_Copy(t *testing.T) {
var ip = net.ParseIP("192.168.1.100")
var nextIP = NextIP(ip)
t.Log(ip, nextIP)
}
}

View File

@@ -3,8 +3,10 @@ package utils
import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/miekg/dns"
"sync"
)
var sharedDNSClient *dns.Client
@@ -33,17 +35,47 @@ func LookupCNAME(host string) (string, error) {
m.RecursionDesired = true
var lastErr error
for _, serverAddr := range sharedDNSConfig.Servers {
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
if err != nil {
lastErr = err
continue
}
if len(r.Answer) == 0 {
continue
}
var success = false
var result = ""
return r.Answer[0].(*dns.CNAME).Target, nil
var serverAddrs = sharedDNSConfig.Servers
{
var publicDNSHosts = []string{"8.8.8.8" /** Google **/, "8.8.4.4" /** Google **/}
for _, publicDNSHost := range publicDNSHosts {
if !lists.ContainsString(serverAddrs, publicDNSHost) {
serverAddrs = append(serverAddrs, publicDNSHost)
}
}
}
var wg = &sync.WaitGroup{}
for _, serverAddr := range serverAddrs {
wg.Add(1)
go func(serverAddr string) {
defer wg.Done()
r, _, err := sharedDNSClient.Exchange(m, configutils.QuoteIP(serverAddr)+":"+sharedDNSConfig.Port)
if err != nil {
lastErr = err
return
}
success = true
if len(r.Answer) == 0 {
return
}
result = r.Answer[0].(*dns.CNAME).Target
}(serverAddr)
}
wg.Wait()
if success {
return result, nil
}
return "", lastErr
}

View File

@@ -8,5 +8,8 @@ import (
)
func TestLookupCNAME(t *testing.T) {
t.Log(utils.LookupCNAME("www.yun4s.cn"))
for _, domain := range []string{"www.yun4s.cn", "example.com"} {
result, err := utils.LookupCNAME(domain)
t.Log(domain, "=>", result, err)
}
}

View File

@@ -30,17 +30,17 @@ func FormatBytes(bytes int64) string {
if bytes < Pow1024(1) {
return FormatInt64(bytes) + "B"
} else if bytes < Pow1024(2) {
return TrimZeroSuffix(fmt.Sprintf("%.2fKB", float64(bytes)/float64(Pow1024(1))))
return TrimZeroSuffix(fmt.Sprintf("%.2fKiB", float64(bytes)/float64(Pow1024(1))))
} else if bytes < Pow1024(3) {
return TrimZeroSuffix(fmt.Sprintf("%.2fMB", float64(bytes)/float64(Pow1024(2))))
return TrimZeroSuffix(fmt.Sprintf("%.2fMiB", float64(bytes)/float64(Pow1024(2))))
} else if bytes < Pow1024(4) {
return TrimZeroSuffix(fmt.Sprintf("%.2fGB", float64(bytes)/float64(Pow1024(3))))
return TrimZeroSuffix(fmt.Sprintf("%.2fGiB", float64(bytes)/float64(Pow1024(3))))
} else if bytes < Pow1024(5) {
return TrimZeroSuffix(fmt.Sprintf("%.2fTB", float64(bytes)/float64(Pow1024(4))))
return TrimZeroSuffix(fmt.Sprintf("%.2fTiB", float64(bytes)/float64(Pow1024(4))))
} else if bytes < Pow1024(6) {
return TrimZeroSuffix(fmt.Sprintf("%.2fPB", float64(bytes)/float64(Pow1024(5))))
return TrimZeroSuffix(fmt.Sprintf("%.2fPiB", float64(bytes)/float64(Pow1024(5))))
} else {
return TrimZeroSuffix(fmt.Sprintf("%.2fEB", float64(bytes)/float64(Pow1024(6))))
return TrimZeroSuffix(fmt.Sprintf("%.2fEiB", float64(bytes)/float64(Pow1024(6))))
}
}

View File

@@ -0,0 +1,31 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package otputils
import (
"net/url"
)
// FixIssuer fix issuer in otp provisioning url
func FixIssuer(urlString string) string {
u, err := url.Parse(urlString)
if err != nil {
return urlString
}
var query = u.Query()
if query != nil {
var issuerName = query.Get("issuer")
if len(issuerName) > 0 {
unescapedIssuerName, unescapeErr := url.QueryUnescape(issuerName)
if unescapeErr == nil {
query.Set("issuer", unescapedIssuerName)
u.RawQuery = query.Encode()
}
}
return u.String()
}
return urlString
}

View File

@@ -0,0 +1,18 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package otputils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils/otputils"
"testing"
)
func TestFixIssuer(t *testing.T) {
var beforeURL = "otpauth://totp/GoEdge%25E7%25AE%25A1%25E7%2590%2586%25E5%2591%2598%25E7%25B3%25BB%25E7%25BB%259F:admin?issuer=GoEdge%25E7%25AE%25A1%25E7%2590%2586%25E5%2591%2598%25E7%25B3%25BB%25E7%25BB%259F&secret=Q3J4WNOWBRFLP3HI"
var afterURL = otputils.FixIssuer(beforeURL)
t.Log(afterURL)
if beforeURL == afterURL {
t.Fatal("'afterURL' should not be equal to 'beforeURL'")
}
}

View File

@@ -5,6 +5,7 @@ package utils
import (
"errors"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/files"
"os"
@@ -21,7 +22,7 @@ func (this *ServiceManager) Install(exePath string, args []string) error {
return errors.New("only root users can install the service")
}
systemd, err := exec.LookPath("systemctl")
systemd, err := executils.LookPath("systemctl")
if err != nil {
return this.installInitService(exePath, args)
}
@@ -36,7 +37,7 @@ func (this *ServiceManager) Start() error {
}
if files.NewFile(systemdServiceFile).Exists() {
systemd, err := exec.LookPath("systemctl")
systemd, err := executils.LookPath("systemctl")
if err != nil {
return err
}
@@ -53,7 +54,7 @@ func (this *ServiceManager) Uninstall() error {
}
if files.NewFile(systemdServiceFile).Exists() {
systemd, err := exec.LookPath("systemctl")
systemd, err := executils.LookPath("systemctl")
if err != nil {
return err
}
@@ -93,7 +94,7 @@ func (this *ServiceManager) installInitService(exePath string, args []string) er
return err
}
chkCmd, err := exec.LookPath("chkconfig")
chkCmd, err := executils.LookPath("chkconfig")
if err != nil {
return err
}
@@ -111,6 +112,12 @@ func (this *ServiceManager) installSystemdService(systemd, exePath string, args
shortName := teaconst.SystemdServiceName
longName := "GoEdge Admin" // TODO 将来可以修改
var startCmd = exePath + " daemon"
bashPath, _ := executils.LookPath("bash")
if len(bashPath) > 0 {
startCmd = bashPath + " -c \"" + startCmd + "\""
}
desc := `### BEGIN INIT INFO
# Provides: ` + shortName + `
# Required-Start: $all
@@ -129,7 +136,7 @@ After=network-online.target
Type=simple
Restart=always
RestartSec=5s
ExecStart=` + exePath + ` daemon
ExecStart=` + startCmd + `
ExecStop=` + exePath + ` stop
ExecReload=` + exePath + ` reload

View File

@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
executils "github.com/TeaOSLab/EdgeAdmin/internal/utils/exec"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
@@ -87,10 +88,10 @@ func (this *UpgradeManager) Start() error {
}()
// 检查unzip
unzipExe, _ := exec.LookPath("unzip")
unzipExe, _ := executils.LookPath("unzip")
// 检查cp
cpExe, _ := exec.LookPath("cp")
cpExe, _ := executils.LookPath("cp")
if len(cpExe) == 0 {
return errors.New("can not find 'cp' command")
}

View File

@@ -0,0 +1,32 @@
Copyright (c) 2012-2016, Nick Galbreath
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
https://github.com/client9/libinjection
http://opensource.org/licenses/BSD-3-Clause

View File

@@ -0,0 +1 @@
copy from https://github.com/libinjection/libinjection

View File

@@ -0,0 +1,65 @@
/**
* Copyright 2012-2016 Nick Galbreath
* nickg@client9.com
* BSD License -- see COPYING.txt for details
*
* https://libinjection.client9.com/
*
*/
#ifndef LIBINJECTION_H
#define LIBINJECTION_H
#ifdef __cplusplus
# define LIBINJECTION_BEGIN_DECLS extern "C" {
# define LIBINJECTION_END_DECLS }
#else
# define LIBINJECTION_BEGIN_DECLS
# define LIBINJECTION_END_DECLS
#endif
LIBINJECTION_BEGIN_DECLS
/*
* Pull in size_t
*/
#include <string.h>
/*
* Version info.
*
* This is moved into a function to allow SWIG and other auto-generated
* binding to not be modified during minor release changes. We change
* change the version number in the c source file, and not regenerated
* the binding
*
* See python's normalized version
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
*/
const char* libinjection_version(void);
/**
* Simple API for SQLi detection - returns a SQLi fingerprint or NULL
* is benign input
*
* \param[in] s input string, may contain nulls, does not need to be null-terminated
* \param[in] slen input string length
* \param[out] fingerprint buffer of 8+ characters. c-string,
* \return 1 if SQLi, 0 if benign. fingerprint will be set or set to empty string.
*/
int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
/** ALPHA version of xss detector.
*
* NOT DONE.
*
* \param[in] s input string, may contain nulls, does not need to be null-terminated
* \param[in] slen input string length
* \return 1 if XSS found, 0 if benign
*
*/
int libinjection_xss(const char* s, size_t slen, int strictMode);
LIBINJECTION_END_DECLS
#endif /* LIBINJECTION_H */

View File

@@ -0,0 +1,868 @@
#include "libinjection_html5.h"
#include <string.h>
#include <assert.h>
#ifdef DEBUG
#include <stdio.h>
#define TRACE() printf("%s:%d\n", __FUNCTION__, __LINE__)
#else
#define TRACE()
#endif
#define CHAR_EOF -1
#define CHAR_NULL 0
#define CHAR_BANG 33
#define CHAR_DOUBLE 34
#define CHAR_PERCENT 37
#define CHAR_SINGLE 39
#define CHAR_DASH 45
#define CHAR_SLASH 47
#define CHAR_LT 60
#define CHAR_EQUALS 61
#define CHAR_GT 62
#define CHAR_QUESTION 63
#define CHAR_RIGHTB 93
#define CHAR_TICK 96
/* prototypes */
static int h5_skip_white(h5_state_t* hs);
static int h5_is_white(char ch);
static int h5_state_eof(h5_state_t* hs);
static int h5_state_data(h5_state_t* hs);
static int h5_state_tag_open(h5_state_t* hs);
static int h5_state_tag_name(h5_state_t* hs);
static int h5_state_tag_name_close(h5_state_t* hs);
static int h5_state_end_tag_open(h5_state_t* hs);
static int h5_state_self_closing_start_tag(h5_state_t* hs);
static int h5_state_attribute_name(h5_state_t* hs);
static int h5_state_after_attribute_name(h5_state_t* hs);
static int h5_state_before_attribute_name(h5_state_t* hs);
static int h5_state_before_attribute_value(h5_state_t* hs);
static int h5_state_attribute_value_double_quote(h5_state_t* hs);
static int h5_state_attribute_value_single_quote(h5_state_t* hs);
static int h5_state_attribute_value_back_quote(h5_state_t* hs);
static int h5_state_attribute_value_no_quote(h5_state_t* hs);
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs);
static int h5_state_comment(h5_state_t* hs);
static int h5_state_cdata(h5_state_t* hs);
/* 12.2.4.44 */
static int h5_state_bogus_comment(h5_state_t* hs);
static int h5_state_bogus_comment2(h5_state_t* hs);
/* 12.2.4.45 */
static int h5_state_markup_declaration_open(h5_state_t* hs);
/* 8.2.4.52 */
static int h5_state_doctype(h5_state_t* hs);
/**
* public function
*/
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags flags)
{
memset(hs, 0, sizeof(h5_state_t));
hs->s = s;
hs->len = len;
switch (flags) {
case DATA_STATE:
hs->state = h5_state_data;
break;
case VALUE_NO_QUOTE:
hs->state = h5_state_before_attribute_name;
break;
case VALUE_SINGLE_QUOTE:
hs->state = h5_state_attribute_value_single_quote;
break;
case VALUE_DOUBLE_QUOTE:
hs->state = h5_state_attribute_value_double_quote;
break;
case VALUE_BACK_QUOTE:
hs->state = h5_state_attribute_value_back_quote;
break;
}
}
/**
* public function
*/
int libinjection_h5_next(h5_state_t* hs)
{
assert(hs->state != NULL);
return (*hs->state)(hs);
}
/**
* Everything below here is private
*
*/
static int h5_is_white(char ch)
{
/*
* \t = horizontal tab = 0x09
* \n = newline = 0x0A
* \v = vertical tab = 0x0B
* \f = form feed = 0x0C
* \r = cr = 0x0D
*/
return strchr(" \t\n\v\f\r", ch) != NULL;
}
static int h5_skip_white(h5_state_t* hs)
{
char ch;
while (hs->pos < hs->len) {
ch = hs->s[hs->pos];
switch (ch) {
case 0x00: /* IE only */
case 0x20:
case 0x09:
case 0x0A:
case 0x0B: /* IE only */
case 0x0C:
case 0x0D: /* IE only */
hs->pos += 1;
break;
default:
return ch;
}
}
return CHAR_EOF;
}
static int h5_state_eof(h5_state_t* hs)
{
/* eliminate unused function argument warning */
(void)hs;
return 0;
}
static int h5_state_data(h5_state_t* hs)
{
const char* idx;
TRACE();
assert(hs->len >= hs->pos);
idx = (const char*) memchr(hs->s + hs->pos, CHAR_LT, hs->len - hs->pos);
if (idx == NULL) {
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = DATA_TEXT;
hs->state = h5_state_eof;
if (hs->token_len == 0) {
return 0;
}
} else {
hs->token_start = hs->s + hs->pos;
hs->token_type = DATA_TEXT;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->pos = (size_t)(idx - hs->s) + 1;
hs->state = h5_state_tag_open;
if (hs->token_len == 0) {
return h5_state_tag_open(hs);
}
}
return 1;
}
/**
* 12 2.4.8
*/
static int h5_state_tag_open(h5_state_t* hs)
{
char ch;
TRACE();
if (hs->pos >= hs->len) {
return 0;
}
ch = hs->s[hs->pos];
if (ch == CHAR_BANG) {
hs->pos += 1;
return h5_state_markup_declaration_open(hs);
} else if (ch == CHAR_SLASH) {
hs->pos += 1;
hs->is_close = 1;
return h5_state_end_tag_open(hs);
} else if (ch == CHAR_QUESTION) {
hs->pos += 1;
return h5_state_bogus_comment(hs);
} else if (ch == CHAR_PERCENT) {
/* this is not in spec.. alternative comment format used
by IE <= 9 and Safari < 4.0.3 */
hs->pos += 1;
return h5_state_bogus_comment2(hs);
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
return h5_state_tag_name(hs);
} else if (ch == CHAR_NULL) {
/* IE-ism NULL characters are ignored */
return h5_state_tag_name(hs);
} else {
/* user input mistake in configuring state */
if (hs->pos == 0) {
return h5_state_data(hs);
}
hs->token_start = hs->s + hs->pos - 1;
hs->token_len = 1;
hs->token_type = DATA_TEXT;
hs->state = h5_state_data;
return 1;
}
}
/**
* 12.2.4.9
*/
static int h5_state_end_tag_open(h5_state_t* hs)
{
char ch;
TRACE();
if (hs->pos >= hs->len) {
return 0;
}
ch = hs->s[hs->pos];
if (ch == CHAR_GT) {
return h5_state_data(hs);
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
return h5_state_tag_name(hs);
}
hs->is_close = 0;
return h5_state_bogus_comment(hs);
}
/*
*
*/
static int h5_state_tag_name_close(h5_state_t* hs)
{
TRACE();
hs->is_close = 0;
hs->token_start = hs->s + hs->pos;
hs->token_len = 1;
hs->token_type = TAG_NAME_CLOSE;
hs->pos += 1;
if (hs->pos < hs->len) {
hs->state = h5_state_data;
} else {
hs->state = h5_state_eof;
}
return 1;
}
/**
* 12.2.4.10
*/
static int h5_state_tag_name(h5_state_t* hs)
{
char ch;
size_t pos;
TRACE();
pos = hs->pos;
while (pos < hs->len) {
ch = hs->s[pos];
if (ch == 0) {
/* special non-standard case */
/* allow nulls in tag name */
/* some old browsers apparently allow and ignore them */
pos += 1;
} else if (h5_is_white(ch)) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->token_type = TAG_NAME_OPEN;
hs->pos = pos + 1;
hs->state = h5_state_before_attribute_name;
return 1;
} else if (ch == CHAR_SLASH) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->token_type = TAG_NAME_OPEN;
hs->pos = pos + 1;
hs->state = h5_state_self_closing_start_tag;
return 1;
} else if (ch == CHAR_GT) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
if (hs->is_close) {
hs->pos = pos + 1;
hs->is_close = 0;
hs->token_type = TAG_CLOSE;
hs->state = h5_state_data;
} else {
hs->pos = pos;
hs->token_type = TAG_NAME_OPEN;
hs->state = h5_state_tag_name_close;
}
return 1;
} else {
pos += 1;
}
}
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = TAG_NAME_OPEN;
hs->state = h5_state_eof;
return 1;
}
/**
* 12.2.4.34
*/
static int h5_state_before_attribute_name(h5_state_t* hs)
{
int ch;
TRACE();
/* for manual tail call optimization, see comment below */
tail_call:;
ch = h5_skip_white(hs);
switch (ch) {
case CHAR_EOF: {
return 0;
}
case CHAR_SLASH: {
hs->pos += 1;
/* Logically, We want to call h5_state_self_closing_start_tag(hs) here.
As this function may call us back and the compiler
might not implement automatic tail call optimization,
this might result in a deep recursion.
We detect this case here and start over with the current state.
*/
if (hs->pos < hs->len && hs->s[hs->pos] != CHAR_GT) {
goto tail_call;
}
return h5_state_self_closing_start_tag(hs);
}
case CHAR_GT: {
hs->state = h5_state_data;
hs->token_start = hs->s + hs->pos;
hs->token_len = 1;
hs->token_type = TAG_NAME_CLOSE;
hs->pos += 1;
return 1;
}
default: {
return h5_state_attribute_name(hs);
}
}
}
static int h5_state_attribute_name(h5_state_t* hs)
{
char ch;
size_t pos;
TRACE();
pos = hs->pos + 1;
while (pos < hs->len) {
ch = hs->s[pos];
if (h5_is_white(ch)) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->token_type = ATTR_NAME;
hs->state = h5_state_after_attribute_name;
hs->pos = pos + 1;
return 1;
} else if (ch == CHAR_SLASH) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->token_type = ATTR_NAME;
hs->state = h5_state_self_closing_start_tag;
hs->pos = pos + 1;
return 1;
} else if (ch == CHAR_EQUALS) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->token_type = ATTR_NAME;
hs->state = h5_state_before_attribute_value;
hs->pos = pos + 1;
return 1;
} else if (ch == CHAR_GT) {
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->token_type = ATTR_NAME;
hs->state = h5_state_tag_name_close;
hs->pos = pos;
return 1;
} else {
pos += 1;
}
}
/* EOF */
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = ATTR_NAME;
hs->state = h5_state_eof;
hs->pos = hs->len;
return 1;
}
/**
* 12.2.4.36
*/
static int h5_state_after_attribute_name(h5_state_t* hs)
{
int c;
TRACE();
c = h5_skip_white(hs);
switch (c) {
case CHAR_EOF: {
return 0;
}
case CHAR_SLASH: {
hs->pos += 1;
return h5_state_self_closing_start_tag(hs);
}
case CHAR_EQUALS: {
hs->pos += 1;
return h5_state_before_attribute_value(hs);
}
case CHAR_GT: {
return h5_state_tag_name_close(hs);
}
default: {
return h5_state_attribute_name(hs);
}
}
}
/**
* 12.2.4.37
*/
static int h5_state_before_attribute_value(h5_state_t* hs)
{
int c;
TRACE();
c = h5_skip_white(hs);
if (c == CHAR_EOF) {
hs->state = h5_state_eof;
return 0;
}
if (c == CHAR_DOUBLE) {
return h5_state_attribute_value_double_quote(hs);
} else if (c == CHAR_SINGLE) {
return h5_state_attribute_value_single_quote(hs);
} else if (c == CHAR_TICK) {
/* NON STANDARD IE */
return h5_state_attribute_value_back_quote(hs);
} else {
return h5_state_attribute_value_no_quote(hs);
}
}
static int h5_state_attribute_value_quote(h5_state_t* hs, char qchar)
{
const char* idx;
TRACE();
/* skip initial quote in normal case.
* don't do this "if (pos == 0)" since it means we have started
* in a non-data state. given an input of '><foo
* we want to make 0-length attribute name
*/
if (hs->pos > 0) {
hs->pos += 1;
}
idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
if (idx == NULL) {
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = ATTR_VALUE;
hs->state = h5_state_eof;
} else {
hs->token_start = hs->s + hs->pos;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->token_type = ATTR_VALUE;
hs->state = h5_state_after_attribute_value_quoted_state;
hs->pos += hs->token_len + 1;
}
return 1;
}
static
int h5_state_attribute_value_double_quote(h5_state_t* hs)
{
TRACE();
return h5_state_attribute_value_quote(hs, CHAR_DOUBLE);
}
static
int h5_state_attribute_value_single_quote(h5_state_t* hs)
{
TRACE();
return h5_state_attribute_value_quote(hs, CHAR_SINGLE);
}
static
int h5_state_attribute_value_back_quote(h5_state_t* hs)
{
TRACE();
return h5_state_attribute_value_quote(hs, CHAR_TICK);
}
static int h5_state_attribute_value_no_quote(h5_state_t* hs)
{
char ch;
size_t pos;
TRACE();
pos = hs->pos;
while (pos < hs->len) {
ch = hs->s[pos];
if (h5_is_white(ch)) {
hs->token_type = ATTR_VALUE;
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->pos = pos + 1;
hs->state = h5_state_before_attribute_name;
return 1;
} else if (ch == CHAR_GT) {
hs->token_type = ATTR_VALUE;
hs->token_start = hs->s + hs->pos;
hs->token_len = pos - hs->pos;
hs->pos = pos;
hs->state = h5_state_tag_name_close;
return 1;
}
pos += 1;
}
TRACE();
/* EOF */
hs->state = h5_state_eof;
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = ATTR_VALUE;
return 1;
}
/**
* 12.2.4.41
*/
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs)
{
char ch;
TRACE();
if (hs->pos >= hs->len) {
return 0;
}
ch = hs->s[hs->pos];
if (h5_is_white(ch)) {
hs->pos += 1;
return h5_state_before_attribute_name(hs);
} else if (ch == CHAR_SLASH) {
hs->pos += 1;
return h5_state_self_closing_start_tag(hs);
} else if (ch == CHAR_GT) {
hs->token_start = hs->s + hs->pos;
hs->token_len = 1;
hs->token_type = TAG_NAME_CLOSE;
hs->pos += 1;
hs->state = h5_state_data;
return 1;
} else {
return h5_state_before_attribute_name(hs);
}
}
/**
* 12.2.4.43
*
* WARNING: This function is partially inlined into h5_state_before_attribute_name()
*/
static int h5_state_self_closing_start_tag(h5_state_t* hs)
{
char ch;
TRACE();
if (hs->pos >= hs->len) {
return 0;
}
ch = hs->s[hs->pos];
if (ch == CHAR_GT) {
assert(hs->pos > 0);
hs->token_start = hs->s + hs->pos -1;
hs->token_len = 2;
hs->token_type = TAG_NAME_SELFCLOSE;
hs->state = h5_state_data;
hs->pos += 1;
return 1;
} else {
return h5_state_before_attribute_name(hs);
}
}
/**
* 12.2.4.44
*/
static int h5_state_bogus_comment(h5_state_t* hs)
{
const char* idx;
TRACE();
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
if (idx == NULL) {
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->pos = hs->len;
hs->state = h5_state_eof;
} else {
hs->token_start = hs->s + hs->pos;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->pos = (size_t)(idx - hs->s) + 1;
hs->state = h5_state_data;
}
hs->token_type = TAG_COMMENT;
return 1;
}
/**
* 12.2.4.44 ALT
*/
static int h5_state_bogus_comment2(h5_state_t* hs)
{
const char* idx;
size_t pos;
TRACE();
pos = hs->pos;
while (1) {
idx = (const char*) memchr(hs->s + pos, CHAR_PERCENT, hs->len - pos);
if (idx == NULL || (idx + 1 >= hs->s + hs->len)) {
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->pos = hs->len;
hs->token_type = TAG_COMMENT;
hs->state = h5_state_eof;
return 1;
}
if (*(idx +1) != CHAR_GT) {
pos = (size_t)(idx - hs->s) + 1;
continue;
}
/* ends in %> */
hs->token_start = hs->s + hs->pos;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->pos = (size_t)(idx - hs->s) + 2;
hs->state = h5_state_data;
hs->token_type = TAG_COMMENT;
return 1;
}
}
/**
* 8.2.4.45
*/
static int h5_state_markup_declaration_open(h5_state_t* hs)
{
size_t remaining;
TRACE();
remaining = hs->len - hs->pos;
if (remaining >= 7 &&
/* case insensitive */
(hs->s[hs->pos + 0] == 'D' || hs->s[hs->pos + 0] == 'd') &&
(hs->s[hs->pos + 1] == 'O' || hs->s[hs->pos + 1] == 'o') &&
(hs->s[hs->pos + 2] == 'C' || hs->s[hs->pos + 2] == 'c') &&
(hs->s[hs->pos + 3] == 'T' || hs->s[hs->pos + 3] == 't') &&
(hs->s[hs->pos + 4] == 'Y' || hs->s[hs->pos + 4] == 'y') &&
(hs->s[hs->pos + 5] == 'P' || hs->s[hs->pos + 5] == 'p') &&
(hs->s[hs->pos + 6] == 'E' || hs->s[hs->pos + 6] == 'e')
) {
return h5_state_doctype(hs);
} else if (remaining >= 7 &&
/* upper case required */
hs->s[hs->pos + 0] == '[' &&
hs->s[hs->pos + 1] == 'C' &&
hs->s[hs->pos + 2] == 'D' &&
hs->s[hs->pos + 3] == 'A' &&
hs->s[hs->pos + 4] == 'T' &&
hs->s[hs->pos + 5] == 'A' &&
hs->s[hs->pos + 6] == '['
) {
hs->pos += 7;
return h5_state_cdata(hs);
} else if (remaining >= 2 &&
hs->s[hs->pos + 0] == '-' &&
hs->s[hs->pos + 1] == '-') {
hs->pos += 2;
return h5_state_comment(hs);
}
return h5_state_bogus_comment(hs);
}
/**
* 12.2.4.48
* 12.2.4.49
* 12.2.4.50
* 12.2.4.51
* state machine spec is confusing since it can only look
* at one character at a time but simply it's comments end by:
* 1) EOF
* 2) ending in -->
* 3) ending in -!>
*/
static int h5_state_comment(h5_state_t* hs)
{
char ch;
const char* idx;
size_t pos;
size_t offset;
const char* end = hs->s + hs->len;
TRACE();
pos = hs->pos;
while (1) {
idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
/* did not find anything or has less than 3 chars left */
if (idx == NULL || idx > hs->s + hs->len - 3) {
hs->state = h5_state_eof;
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = TAG_COMMENT;
return 1;
}
offset = 1;
/* skip all nulls */
while (idx + offset < end && *(idx + offset) == 0) {
offset += 1;
}
if (idx + offset == end) {
hs->state = h5_state_eof;
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = TAG_COMMENT;
return 1;
}
ch = *(idx + offset);
if (ch != CHAR_DASH && ch != CHAR_BANG) {
pos = (size_t)(idx - hs->s) + 1;
continue;
}
/* need to test */
#if 0
/* skip all nulls */
while (idx + offset < end && *(idx + offset) == 0) {
offset += 1;
}
if (idx + offset == end) {
hs->state = h5_state_eof;
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = TAG_COMMENT;
return 1;
}
#endif
offset += 1;
if (idx + offset == end) {
hs->state = h5_state_eof;
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = TAG_COMMENT;
return 1;
}
ch = *(idx + offset);
if (ch != CHAR_GT) {
pos = (size_t)(idx - hs->s) + 1;
continue;
}
offset += 1;
/* ends in --> or -!> */
hs->token_start = hs->s + hs->pos;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->pos = (size_t)(idx + offset - hs->s);
hs->state = h5_state_data;
hs->token_type = TAG_COMMENT;
return 1;
}
}
static int h5_state_cdata(h5_state_t* hs)
{
const char* idx;
size_t pos;
TRACE();
pos = hs->pos;
while (1) {
idx = (const char*) memchr(hs->s + pos, CHAR_RIGHTB, hs->len - pos);
/* did not find anything or has less than 3 chars left */
if (idx == NULL || idx > hs->s + hs->len - 3) {
hs->state = h5_state_eof;
hs->token_start = hs->s + hs->pos;
hs->token_len = hs->len - hs->pos;
hs->token_type = DATA_TEXT;
return 1;
} else if ( *(idx+1) == CHAR_RIGHTB && *(idx+2) == CHAR_GT) {
hs->state = h5_state_data;
hs->token_start = hs->s + hs->pos;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->pos = (size_t)(idx - hs->s) + 3;
hs->token_type = DATA_TEXT;
return 1;
} else {
pos = (size_t)(idx - hs->s) + 1;
}
}
}
/**
* 8.2.4.52
* http://www.w3.org/html/wg/drafts/html/master/syntax.html#doctype-state
*/
static int h5_state_doctype(h5_state_t* hs)
{
const char* idx;
TRACE();
hs->token_start = hs->s + hs->pos;
hs->token_type = DOCTYPE;
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
if (idx == NULL) {
hs->state = h5_state_eof;
hs->token_len = hs->len - hs->pos;
} else {
hs->state = h5_state_data;
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
hs->pos = (size_t)(idx - hs->s) + 1;
}
return 1;
}

View File

@@ -0,0 +1,54 @@
#ifndef LIBINJECTION_HTML5
#define LIBINJECTION_HTML5
#ifdef __cplusplus
extern "C" {
#endif
/* pull in size_t */
#include <stddef.h>
enum html5_type {
DATA_TEXT
, TAG_NAME_OPEN
, TAG_NAME_CLOSE
, TAG_NAME_SELFCLOSE
, TAG_DATA
, TAG_CLOSE
, ATTR_NAME
, ATTR_VALUE
, TAG_COMMENT
, DOCTYPE
};
enum html5_flags {
DATA_STATE
, VALUE_NO_QUOTE
, VALUE_SINGLE_QUOTE
, VALUE_DOUBLE_QUOTE
, VALUE_BACK_QUOTE
};
struct h5_state;
typedef int (*ptr_html5_state)(struct h5_state*);
typedef struct h5_state {
const char* s;
size_t len;
size_t pos;
int is_close;
ptr_html5_state state;
const char* token_start;
size_t token_len;
enum html5_type token_type;
} h5_state_t;
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags);
int libinjection_h5_next(h5_state_t* hs);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
/**
* Copyright 2012-2016 Nick Galbreath
* nickg@client9.com
* BSD License -- see `COPYING.txt` for details
*
* https://libinjection.client9.com/
*
*/
#ifndef LIBINJECTION_SQLI_H
#define LIBINJECTION_SQLI_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* Pull in size_t
*/
#include <string.h>
enum sqli_flags {
FLAG_NONE = 0
, FLAG_QUOTE_NONE = 1 /* 1 << 0 */
, FLAG_QUOTE_SINGLE = 2 /* 1 << 1 */
, FLAG_QUOTE_DOUBLE = 4 /* 1 << 2 */
, FLAG_SQL_ANSI = 8 /* 1 << 3 */
, FLAG_SQL_MYSQL = 16 /* 1 << 4 */
};
enum lookup_type {
LOOKUP_WORD = 1
, LOOKUP_TYPE = 2
, LOOKUP_OPERATOR = 3
, LOOKUP_FINGERPRINT = 4
};
struct libinjection_sqli_token {
#ifdef SWIG
%immutable;
#endif
/*
* position and length of token
* in original string
*/
size_t pos;
size_t len;
/* count:
* in type 'v', used for number of opening '@'
* but maybe used in other contexts
*/
int count;
char type;
char str_open;
char str_close;
char val[32];
};
typedef struct libinjection_sqli_token stoken_t;
/**
* Pointer to function, takes c-string input,
* returns '\0' for no match, else a char
*/
struct libinjection_sqli_state;
typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len);
struct libinjection_sqli_state {
#ifdef SWIG
%immutable;
#endif
/*
* input, does not need to be null terminated.
* it is also not modified.
*/
const char *s;
/*
* input length
*/
size_t slen;
/*
* How to lookup a word or fingerprint
*/
ptr_lookup_fn lookup;
void* userdata;
/*
*
*/
int flags;
/*
* pos is the index in the string during tokenization
*/
size_t pos;
#ifndef SWIG
/* for SWIG.. don't use this.. use functional API instead */
/* MAX TOKENS + 1 since we use one extra token
* to determine the type of the previous token
*/
struct libinjection_sqli_token tokenvec[8];
#endif
/*
* Pointer to token position in tokenvec, above
*/
struct libinjection_sqli_token *current;
/*
* fingerprint pattern c-string
* +1 for ending null
* Minimum of 8 bytes to add gcc's -fstack-protector to work
*/
char fingerprint[8];
/*
* Line number of code that said decided if the input was SQLi or
* not. Most of the time it's line that said "it's not a matching
* fingerprint" but there is other logic that sometimes approves
* an input. This is only useful for debugging.
*
*/
int reason;
/* Number of ddw (dash-dash-white) comments
* These comments are in the form of
* '--[whitespace]' or '--[EOF]'
*
* All databases treat this as a comment.
*/
int stats_comment_ddw;
/* Number of ddx (dash-dash-[notwhite]) comments
*
* ANSI SQL treats these are comments, MySQL treats this as
* two unary operators '-' '-'
*
* If you are parsing result returns FALSE and
* stats_comment_dd > 0, you should reparse with
* COMMENT_MYSQL
*
*/
int stats_comment_ddx;
/*
* c-style comments found /x .. x/
*/
int stats_comment_c;
/* '#' operators or MySQL EOL comments found
*
*/
int stats_comment_hash;
/*
* number of tokens folded away
*/
int stats_folds;
/*
* total tokens processed
*/
int stats_tokens;
};
typedef struct libinjection_sqli_state sfilter;
struct libinjection_sqli_token* libinjection_sqli_get_token(
struct libinjection_sqli_state* sql_state, int i);
/*
* Version info.
*
* This is moved into a function to allow SWIG and other auto-generated
* binding to not be modified during minor release changes. We change
* change the version number in the c source file, and not regenerated
* the binding
*
* See python's normalized version
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
*/
const char* libinjection_version(void);
/**
*
*/
void libinjection_sqli_init(struct libinjection_sqli_state *sf,
const char* s, size_t len,
int flags);
/**
* Main API: tests for SQLi in three possible contexts, no quotes,
* single quote and double quote
*
* \param sql_state core data structure
*
* \return 1 (true) if SQLi, 0 (false) if benign
*/
int libinjection_is_sqli(struct libinjection_sqli_state* sql_state);
/* FOR HACKERS ONLY
* provides deep hooks into the decision making process
*/
void libinjection_sqli_callback(struct libinjection_sqli_state *sf,
ptr_lookup_fn fn,
void* userdata);
/*
* Resets state, but keeps initial string and callbacks
*/
void libinjection_sqli_reset(struct libinjection_sqli_state *sf,
int flags);
/**
*
*/
/**
* This detects SQLi in a single context, mostly useful for custom
* logic and debugging.
*
* \param sql_state Main data structure
* \param flags flags to adjust parsing
*
* \returns a pointer to sfilter.fingerprint as convenience
* do not free!
*
*/
const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state *sql_state,
int flags);
/**
* The default "word" to token-type or fingerprint function. This
* uses a ASCII case-insensitive binary tree.
*/
char libinjection_sqli_lookup_word(struct libinjection_sqli_state *sql_state,
int lookup_type,
const char* str,
size_t len);
/* Streaming tokenization interface.
*
* sql_state->current is updated with the current token.
*
* \returns 1, has a token, keep going, or 0 no tokens
*
*/
int libinjection_sqli_tokenize(struct libinjection_sqli_state *sf);
/**
* parses and folds input, up to 5 tokens
*
*/
int libinjection_sqli_fold(struct libinjection_sqli_state *sf);
/** The built-in default function to match fingerprints
* and do false negative/positive analysis. This calls the following
* two functions. With this, you over-ride one part or the other.
*
* return libinjection_sqli_blacklist(sql_state) &&
* libinjection_sqli_not_whitelist(sql_state);
*
* \param sql_state should be filled out after libinjection_sqli_fingerprint is called
*/
int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state * sql_state);
/* Given a pattern determine if it's a SQLi pattern.
*
* \return TRUE if sqli, false otherwise
*/
int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state);
/* Given a positive match for a pattern (i.e. pattern is SQLi), this function
* does additional analysis to reduce false positives.
*
* \return TRUE if SQLi, false otherwise
*/
int libinjection_sqli_not_whitelist(struct libinjection_sqli_state * sql_state);
#ifdef __cplusplus
}
#endif
#endif /* LIBINJECTION_SQLI_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,918 @@
#include "libinjection.h"
#include "libinjection_xss.h"
#include "libinjection_html5.h"
#include <assert.h>
#include <stdio.h>
typedef enum attribute {
TYPE_NONE
, TYPE_BLACK /* ban always */
, TYPE_ATTR_URL /* attribute value takes a URL-like object */
, TYPE_STYLE
, TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
} attribute_t;
static attribute_t is_black_attr(const char* s, size_t len, int strictMode);
static int is_black_tag(const char* s, size_t len, int strictMode);
static int is_black_url(const char* s, size_t len);
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n);
static int html_decode_char_at(const char* src, size_t len, size_t* consumed);
static int htmlencode_startswith(const char *a/* prefix */, const char *b /* src */, size_t n);
typedef struct stringtype {
const char* name;
attribute_t atype;
} stringtype_t;
static const int gsHexDecodeMap[256] = {
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256,
256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
256, 256, 256, 256
};
static int html_decode_char_at(const char* src, size_t len, size_t* consumed)
{
int val = 0;
size_t i;
int ch;
if (len == 0 || src == NULL) {
*consumed = 0;
return -1;
}
*consumed = 1;
if (*src != '&' || len < 2) {
return (unsigned char)(*src);
}
if (*(src+1) != '#') {
/* normally this would be for named entities
* but for this case we don't actually care
*/
return '&';
}
if (*(src+2) == 'x' || *(src+2) == 'X') {
ch = (unsigned char) (*(src+3));
ch = gsHexDecodeMap[ch];
if (ch == 256) {
/* degenerate case '&#[?]' */
return '&';
}
val = ch;
i = 4;
while (i < len) {
ch = (unsigned char) src[i];
if (ch == ';') {
*consumed = i + 1;
return val;
}
ch = gsHexDecodeMap[ch];
if (ch == 256) {
*consumed = i;
return val;
}
val = (val * 16) + ch;
if (val > 0x1000FF) {
return '&';
}
++i;
}
*consumed = i;
return val;
} else {
i = 2;
ch = (unsigned char) src[i];
if (ch < '0' || ch > '9') {
return '&';
}
val = ch - '0';
i += 1;
while (i < len) {
ch = (unsigned char) src[i];
if (ch == ';') {
*consumed = i + 1;
return val;
}
if (ch < '0' || ch > '9') {
*consumed = i;
return val;
}
val = (val * 10) + (ch - '0');
if (val > 0x1000FF) {
return '&';
}
++i;
}
*consumed = i;
return val;
}
}
/*
* These were mostly extracted from: https://raw.githubusercontent.com/WebKit/WebKit/main/Source/WebCore/dom/EventNames.h
*
* view-source:
* data:
* javascript:
* events:
*/
static stringtype_t BLACKATTREVENT[] = {
{ "ABORT", TYPE_BLACK }
, { "ACTIVATE", TYPE_BLACK }
, { "ACTIVE", TYPE_BLACK }
, { "ADDSOURCEBUFFER", TYPE_BLACK }
, { "ADDSTREAM", TYPE_BLACK }
, { "ADDTRACK", TYPE_BLACK }
, { "AFTERPRINT", TYPE_BLACK }
, { "ANIMATIONCANCEL", TYPE_BLACK }
, { "ANIMATIONEND", TYPE_BLACK }
, { "ANIMATIONITERATION", TYPE_BLACK }
, { "ANIMATIONSTART", TYPE_BLACK }
, { "AUDIOEND", TYPE_BLACK }
, { "AUDIOPROCESS", TYPE_BLACK }
, { "AUDIOSTART", TYPE_BLACK }
, { "AUTOCOMPLETEERROR", TYPE_BLACK }
, { "AUTOCOMPLETE", TYPE_BLACK }
, { "BEFOREACTIVATE", TYPE_BLACK }
, { "BEFORECOPY", TYPE_BLACK }
, { "BEFORECUT", TYPE_BLACK }
, { "BEFOREINPUT", TYPE_BLACK }
, { "BEFORELOAD", TYPE_BLACK }
, { "BEFOREPASTE", TYPE_BLACK }
, { "BEFOREPRINT", TYPE_BLACK }
, { "BEFOREUNLOAD", TYPE_BLACK }
, { "BEGINEVENT", TYPE_BLACK }
, { "BLOCKED", TYPE_BLACK }
, { "BLUR", TYPE_BLACK }
, { "BOUNDARY", TYPE_BLACK }
, { "BUFFEREDAMOUNTLOW", TYPE_BLACK }
, { "CACHED", TYPE_BLACK }
, { "CANCEL", TYPE_BLACK }
, { "CANPLAYTHROUGH", TYPE_BLACK }
, { "CANPLAY", TYPE_BLACK }
, { "CHANGE", TYPE_BLACK }
, { "CHARGINGCHANGE", TYPE_BLACK }
, { "CHARGINGTIMECHANGE", TYPE_BLACK }
, { "CHECKING", TYPE_BLACK }
, { "CLICK", TYPE_BLACK }
, { "CLOSE", TYPE_BLACK }
, { "COMPLETE", TYPE_BLACK }
, { "COMPOSITIONEND", TYPE_BLACK }
, { "COMPOSITIONSTART", TYPE_BLACK }
, { "COMPOSITIONUPDATE", TYPE_BLACK }
, { "CONNECTING", TYPE_BLACK }
, { "CONNECTIONSTATECHANGE", TYPE_BLACK }
, { "CONNECT", TYPE_BLACK }
, { "CONTEXTMENU", TYPE_BLACK }
, { "CONTROLLERCHANGE", TYPE_BLACK }
, { "COPY", TYPE_BLACK }
, { "CUECHANGE", TYPE_BLACK }
, { "CUT", TYPE_BLACK }
, { "DATAAVAILABLE", TYPE_BLACK }
, { "DATACHANNEL", TYPE_BLACK }
, { "DBLCLICK", TYPE_BLACK }
, { "DEVICECHANGE", TYPE_BLACK }
, { "DEVICEMOTION", TYPE_BLACK }
, { "DEVICEORIENTATION", TYPE_BLACK }
, { "DISCHARGINGTIMECHANGE", TYPE_BLACK }
, { "DISCONNECT", TYPE_BLACK }
, { "DOMACTIVATE", TYPE_BLACK }
, { "DOMCHARACTERDATAMODIFIED", TYPE_BLACK }
, { "DOMCONTENTLOADED", TYPE_BLACK }
, { "DOMFOCUSIN", TYPE_BLACK }
, { "DOMFOCUSOUT", TYPE_BLACK }
, { "DOMNODEINSERTEDINTODOCUMENT", TYPE_BLACK }
, { "DOMNODEINSERTED", TYPE_BLACK }
, { "DOMNODEREMOVEDFROMDOCUMENT", TYPE_BLACK }
, { "DOMNODEREMOVED", TYPE_BLACK }
, { "DOMSUBTREEMODIFIED", TYPE_BLACK }
, { "DOWNLOADING", TYPE_BLACK }
, { "DRAGEND", TYPE_BLACK }
, { "DRAGENTER", TYPE_BLACK }
, { "DRAGLEAVE", TYPE_BLACK }
, { "DRAGOVER", TYPE_BLACK }
, { "DRAGSTART", TYPE_BLACK }
, { "DRAG", TYPE_BLACK }
, { "DROP", TYPE_BLACK }
, { "DURATIONCHANGE", TYPE_BLACK }
, { "EMPTIED", TYPE_BLACK }
, { "ENCRYPTED", TYPE_BLACK }
, { "ENDED", TYPE_BLACK }
, { "ENDEVENT", TYPE_BLACK }
, { "END", TYPE_BLACK }
, { "ENTERPICTUREINPICTURE", TYPE_BLACK }
, { "ENTER", TYPE_BLACK }
, { "ERROR", TYPE_BLACK }
, { "EXIT", TYPE_BLACK }
, { "FETCH", TYPE_BLACK }
, { "FINISH", TYPE_BLACK }
, { "FOCUSIN", TYPE_BLACK }
, { "FOCUSOUT", TYPE_BLACK }
, { "FOCUS", TYPE_BLACK }
, { "FORMCHANGE", TYPE_BLACK }
, { "FORMINPUT", TYPE_BLACK }
, { "GAMEPADCONNECTED", TYPE_BLACK }
, { "GAMEPADDISCONNECTED", TYPE_BLACK }
, { "GESTURECHANGE", TYPE_BLACK }
, { "GESTUREEND", TYPE_BLACK }
, { "GESTURESCROLLEND", TYPE_BLACK }
, { "GESTURESCROLLSTART", TYPE_BLACK }
, { "GESTURESCROLLUPDATE", TYPE_BLACK }
, { "GESTURESTART", TYPE_BLACK }
, { "GESTURETAPDOWN", TYPE_BLACK }
, { "GESTURETAP", TYPE_BLACK }
, { "GOTPOINTERCAPTURE", TYPE_BLACK }
, { "HASHCHANGE", TYPE_BLACK }
, { "ICECANDIDATEERROR", TYPE_BLACK }
, { "ICECANDIDATE", TYPE_BLACK }
, { "ICECONNECTIONSTATECHANGE", TYPE_BLACK }
, { "ICEGATHERINGSTATECHANGE", TYPE_BLACK }
, { "INACTIVE", TYPE_BLACK }
, { "INPUTSOURCESCHANGE", TYPE_BLACK }
, { "INPUT", TYPE_BLACK }
, { "INSTALL", TYPE_BLACK }
, { "INVALID", TYPE_BLACK }
, { "KEYDOWN", TYPE_BLACK }
, { "KEYPRESS", TYPE_BLACK }
, { "KEYSTATUSESCHANGE", TYPE_BLACK }
, { "KEYUP", TYPE_BLACK }
, { "LANGUAGECHANGE", TYPE_BLACK }
, { "LEAVEPICTUREINPICTURE", TYPE_BLACK }
, { "LEVELCHANGE", TYPE_BLACK }
, { "LOADEDDATA", TYPE_BLACK }
, { "LOADEDMETADATA", TYPE_BLACK }
, { "LOADEND", TYPE_BLACK }
, { "LOADINGDONE", TYPE_BLACK }
, { "LOADINGERROR", TYPE_BLACK }
, { "LOADING", TYPE_BLACK }
, { "LOADSTART", TYPE_BLACK }
, { "LOAD", TYPE_BLACK }
, { "LOSTPOINTERCAPTURE", TYPE_BLACK }
, { "MARK", TYPE_BLACK }
, { "MERCHANTVALIDATION", TYPE_BLACK }
, { "MESSAGEERROR", TYPE_BLACK }
, { "MESSAGE", TYPE_BLACK }
, { "MOUSEDOWN", TYPE_BLACK }
, { "MOUSEENTER", TYPE_BLACK }
, { "MOUSELEAVE", TYPE_BLACK }
, { "MOUSEMOVE", TYPE_BLACK }
, { "MOUSEOUT", TYPE_BLACK }
, { "MOUSEOVER", TYPE_BLACK }
, { "MOUSEUP", TYPE_BLACK }
, { "MOUSEWHEEL", TYPE_BLACK }
, { "MUTE", TYPE_BLACK }
, { "NEGOTIATIONNEEDED", TYPE_BLACK }
, { "NEXTTRACK", TYPE_BLACK }
, { "NOMATCH", TYPE_BLACK }
, { "NOUPDATE", TYPE_BLACK }
, { "OBSOLETE", TYPE_BLACK }
, { "OFFLINE", TYPE_BLACK }
, { "ONLINE", TYPE_BLACK }
, { "OPEN", TYPE_BLACK }
, { "ORIENTATIONCHANGE", TYPE_BLACK }
, { "OVERCONSTRAINED", TYPE_BLACK }
, { "OVERFLOWCHANGED", TYPE_BLACK }
, { "PAGEHIDE", TYPE_BLACK }
, { "PAGESHOW", TYPE_BLACK }
, { "PASTE", TYPE_BLACK }
, { "PAUSE", TYPE_BLACK }
, { "PAYERDETAILCHANGE", TYPE_BLACK }
, { "PAYMENTAUTHORIZED", TYPE_BLACK }
, { "PAYMENTMETHODCHANGE", TYPE_BLACK }
, { "PAYMENTMETHODSELECTED", TYPE_BLACK }
, { "PLAYING", TYPE_BLACK }
, { "PLAY", TYPE_BLACK }
, { "POINTERCANCEL", TYPE_BLACK }
, { "POINTERDOWN", TYPE_BLACK }
, { "POINTERENTER", TYPE_BLACK }
, { "POINTERLEAVE", TYPE_BLACK }
, { "POINTERLOCKCHANGE", TYPE_BLACK }
, { "POINTERLOCKERROR", TYPE_BLACK }
, { "POINTERMOVE", TYPE_BLACK }
, { "POINTEROUT", TYPE_BLACK }
, { "POINTEROVER", TYPE_BLACK }
, { "POINTERUP", TYPE_BLACK }
, { "POPSTATE", TYPE_BLACK }
, { "PREVIOUSTRACK", TYPE_BLACK }
, { "PROCESSORERROR", TYPE_BLACK }
, { "PROGRESS", TYPE_BLACK }
, { "PROPERTYCHANGE", TYPE_BLACK }
, { "RATECHANGE", TYPE_BLACK }
, { "READYSTATECHANGE", TYPE_BLACK }
, { "REJECTIONHANDLED", TYPE_BLACK }
, { "REMOVESOURCEBUFFER", TYPE_BLACK }
, { "REMOVESTREAM", TYPE_BLACK }
, { "REMOVETRACK", TYPE_BLACK }
, { "REMOVE", TYPE_BLACK }
, { "RESET", TYPE_BLACK }
, { "RESIZE", TYPE_BLACK }
, { "RESOURCETIMINGBUFFERFULL", TYPE_BLACK }
, { "RESULT", TYPE_BLACK }
, { "RESUME", TYPE_BLACK }
, { "SCROLL", TYPE_BLACK }
, { "SEARCH", TYPE_BLACK }
, { "SECURITYPOLICYVIOLATION", TYPE_BLACK }
, { "SEEKED", TYPE_BLACK }
, { "SEEKING", TYPE_BLACK }
, { "SELECTEND", TYPE_BLACK }
, { "SELECTIONCHANGE", TYPE_BLACK }
, { "SELECTSTART", TYPE_BLACK }
, { "SELECT", TYPE_BLACK }
, { "SHIPPINGADDRESSCHANGE", TYPE_BLACK }
, { "SHIPPINGCONTACTSELECTED", TYPE_BLACK }
, { "SHIPPINGMETHODSELECTED", TYPE_BLACK }
, { "SHIPPINGOPTIONCHANGE", TYPE_BLACK }
, { "SHOW", TYPE_BLACK }
, { "SIGNALINGSTATECHANGE", TYPE_BLACK }
, { "SLOTCHANGE", TYPE_BLACK }
, { "SOUNDEND", TYPE_BLACK }
, { "SOUNDSTART", TYPE_BLACK }
, { "SOURCECLOSE", TYPE_BLACK }
, { "SOURCEENDED", TYPE_BLACK }
, { "SOURCEOPEN", TYPE_BLACK }
, { "SPEECHEND", TYPE_BLACK }
, { "SPEECHSTART", TYPE_BLACK }
, { "SQUEEZEEND", TYPE_BLACK }
, { "SQUEEZESTART", TYPE_BLACK }
, { "SQUEEZE", TYPE_BLACK }
, { "STALLED", TYPE_BLACK }
, { "STARTED", TYPE_BLACK }
, { "START", TYPE_BLACK }
, { "STATECHANGE", TYPE_BLACK }
, { "STOP", TYPE_BLACK }
, { "STORAGE", TYPE_BLACK }
, { "SUBMIT", TYPE_BLACK }
, { "SUCCESS", TYPE_BLACK }
, { "SUSPEND", TYPE_BLACK }
, { "TEXTINPUT", TYPE_BLACK }
, { "TIMEOUT", TYPE_BLACK }
, { "TIMEUPDATE", TYPE_BLACK }
, { "TOGGLE", TYPE_BLACK }
, { "TOGGLE", TYPE_BLACK }
, { "TONECHANGE", TYPE_BLACK }
, { "TOUCHCANCEL", TYPE_BLACK }
, { "TOUCHEND", TYPE_BLACK }
, { "TOUCHFORCECHANGE", TYPE_BLACK }
, { "TOUCHMOVE", TYPE_BLACK }
, { "TOUCHSTART", TYPE_BLACK }
, { "TRACK", TYPE_BLACK }
, { "TRANSITIONCANCEL", TYPE_BLACK }
, { "TRANSITIONEND", TYPE_BLACK }
, { "TRANSITIONRUN", TYPE_BLACK }
, { "TRANSITIONSTART", TYPE_BLACK }
, { "UNCAPTUREDERROR", TYPE_BLACK }
, { "UNHANDLEDREJECTION", TYPE_BLACK }
, { "UNLOAD", TYPE_BLACK }
, { "UNMUTE", TYPE_BLACK }
, { "UPDATEEND", TYPE_BLACK }
, { "UPDATEFOUND", TYPE_BLACK }
, { "UPDATEREADY", TYPE_BLACK }
, { "UPDATESTART", TYPE_BLACK }
, { "UPDATE", TYPE_BLACK }
, { "UPGRADENEEDED", TYPE_BLACK }
, { "VALIDATEMERCHANT", TYPE_BLACK }
, { "VERSIONCHANGE", TYPE_BLACK }
, { "VISIBILITYCHANGE", TYPE_BLACK }
, { "VOLUMECHANGE", TYPE_BLACK }
, { "WAITINGFORKEY", TYPE_BLACK }
, { "WAITING", TYPE_BLACK }
, { "WEBGLCONTEXTCHANGED", TYPE_BLACK }
, { "WEBGLCONTEXTCREATIONERROR", TYPE_BLACK }
, { "WEBGLCONTEXTLOST", TYPE_BLACK }
, { "WEBGLCONTEXTRESTORED", TYPE_BLACK }
, { "WEBKITANIMATIONEND", TYPE_BLACK }
, { "WEBKITANIMATIONITERATION", TYPE_BLACK }
, { "WEBKITANIMATIONSTART", TYPE_BLACK }
, { "WEBKITBEFORETEXTINSERTED", TYPE_BLACK }
, { "WEBKITBEGINFULLSCREEN", TYPE_BLACK }
, { "WEBKITCURRENTPLAYBACKTARGETISWIRELESSCHANGED", TYPE_BLACK }
, { "WEBKITENDFULLSCREEN", TYPE_BLACK }
, { "WEBKITFULLSCREENCHANGE", TYPE_BLACK }
, { "WEBKITFULLSCREENERROR", TYPE_BLACK }
, { "WEBKITKEYADDED", TYPE_BLACK }
, { "WEBKITKEYERROR", TYPE_BLACK }
, { "WEBKITKEYMESSAGE", TYPE_BLACK }
, { "WEBKITMOUSEFORCECHANGED", TYPE_BLACK }
, { "WEBKITMOUSEFORCEDOWN", TYPE_BLACK }
, { "WEBKITMOUSEFORCEUP", TYPE_BLACK }
, { "WEBKITMOUSEFORCEWILLBEGIN", TYPE_BLACK }
, { "WEBKITNEEDKEY", TYPE_BLACK }
, { "WEBKITNETWORKINFOCHANGE", TYPE_BLACK }
, { "WEBKITPLAYBACKTARGETAVAILABILITYCHANGED", TYPE_BLACK }
, { "WEBKITPRESENTATIONMODECHANGED", TYPE_BLACK }
, { "WEBKITREGIONOVERSETCHANGE", TYPE_BLACK }
, { "WEBKITREMOVESOURCEBUFFER", TYPE_BLACK }
, { "WEBKITSOURCECLOSE", TYPE_BLACK }
, { "WEBKITSOURCEENDED", TYPE_BLACK }
, { "WEBKITSOURCEOPEN", TYPE_BLACK }
, { "WEBKITSPEECHCHANGE", TYPE_BLACK }
, { "WEBKITTRANSITIONEND", TYPE_BLACK }
, { "WEBKITWILLREVEALBOTTOM", TYPE_BLACK }
, { "WEBKITWILLREVEALLEFT", TYPE_BLACK }
, { "WEBKITWILLREVEALRIGHT", TYPE_BLACK }
, { "WEBKITWILLREVEALTOP", TYPE_BLACK }
, { "WHEEL", TYPE_BLACK }
, { "WRITEEND", TYPE_BLACK }
, { "WRITESTART", TYPE_BLACK }
, { "WRITE", TYPE_BLACK }
, { "ZOOM", TYPE_BLACK }
, { NULL, TYPE_NONE }
};
/*
* view-source:
* data:
* javascript:
*/
static stringtype_t STRICT_BLACKATTR[] = {
{ "ACTION", TYPE_ATTR_URL } /* form */
, { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
, { "BY", TYPE_ATTR_URL } /* SVG */
, { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
, { "DATAFORMATAS", TYPE_BLACK } /* IE */
, { "DATASRC", TYPE_BLACK } /* IE */
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
, { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
, { "FROM", TYPE_ATTR_URL } /* SVG */
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
, { "HREF", TYPE_ATTR_URL }
, { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
, { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */
, { "SRC", TYPE_ATTR_URL }
, { "STYLE", TYPE_STYLE }
, { "TO", TYPE_ATTR_URL } /* SVG */
, { "VALUES", TYPE_ATTR_URL } /* SVG */
, { "XLINK:HREF", TYPE_ATTR_URL }
, { NULL, TYPE_NONE }
};
static stringtype_t BLACKATTR[] = {
{ "ACTION", TYPE_ATTR_URL } /* form */
, { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
, { "BY", TYPE_ATTR_URL } /* SVG */
, { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
, { "DATAFORMATAS", TYPE_BLACK } /* IE */
, { "DATASRC", TYPE_BLACK } /* IE */
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
, { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
, { "FROM", TYPE_ATTR_URL } /* SVG */
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
, { "HREF", TYPE_ATTR_URL }
, { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
, { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */
, { "SRC", TYPE_ATTR_URL }
, { "TO", TYPE_ATTR_URL } /* SVG */
, { "VALUES", TYPE_ATTR_URL } /* SVG */
, { "XLINK:HREF", TYPE_ATTR_URL }
, { NULL, TYPE_NONE }
};
/* xmlns */
/* `xml-stylesheet` > <eval>, <if expr=> */
/*
static const char* BLACKATTR[] = {
"ATTRIBUTENAME",
"BACKGROUND",
"DATAFORMATAS",
"HREF",
"SCROLL",
"SRC",
"STYLE",
"SRCDOC",
NULL
};
*/
// GoEdge: change BLACKTAG to STRICT_BLACKTAG
static const char* STRICT_BLACKTAG[] = {
"APPLET"
, "AUDIO"
, "BASE"
, "COMMENT" /* IE http://html5sec.org/#38 */
, "EMBED"
, "FORM"
, "FRAME"
, "FRAMESET"
, "HANDLER" /* Opera SVG, effectively a script tag */
, "IFRAME"
, "IMPORT"
, "ISINDEX"
, "LINK"
, "LISTENER"
/* , "MARQUEE" */
, "META"
, "NOSCRIPT"
, "OBJECT"
, "SCRIPT"
, "STYLE"
, "VIDEO"
, "VMLFRAME"
, "XML"
, "XSS"
, NULL
};
static const char* BLACKTAG[] = {
"APPLET"
/* , "AUDIO" */
, "BASE"
, "COMMENT" /* IE http://html5sec.org/#38 */
, "EMBED"
/* , "FORM" */
, "FRAME"
, "FRAMESET"
, "HANDLER" /* Opera SVG, effectively a script tag */
, "IFRAME"
, "IMPORT"
, "ISINDEX"
, "LINK"
, "LISTENER"
/* , "MARQUEE" */
, "META"
, "NOSCRIPT"
, "OBJECT"
, "SCRIPT"
, "STYLE"
/* , "VIDEO" */
, "VMLFRAME"
, "XSS"
, NULL
};
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n)
{
char ca;
char cb;
/* printf("Comparing to %s %.*s\n", a, (int)n, b); */
while (n-- > 0) {
cb = *b++;
if (cb == '\0') continue;
ca = *a++;
if (cb >= 'a' && cb <= 'z') {
cb -= 0x20;
}
/* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
if (ca != cb) {
return 1;
}
}
if (*a == 0) {
/* printf(" MATCH \n"); */
return 0;
} else {
return 1;
}
}
/*
* Does an HTML encoded binary string (const char*, length) start with
* a all uppercase c-string (null terminated), case insensitive!
*
* also ignore any embedded nulls in the HTML string!
*
* return 1 if match / starts with
* return 0 if not
*/
static int htmlencode_startswith(const char *a, const char *b, size_t n)
{
size_t consumed;
int cb;
int first = 1;
/* printf("Comparing %s with %.*s\n", a,(int)n,b); */
while (n > 0) {
if (*a == 0) {
/* printf("Match EOL!\n"); */
return 1;
}
cb = html_decode_char_at(b, n, &consumed);
b += consumed;
n -= consumed;
if (first && cb <= 32) {
/* ignore all leading whitespace and control characters */
continue;
}
first = 0;
if (cb == 0) {
/* always ignore null characters in user input */
continue;
}
if (cb == 10) {
/* always ignore vertical tab characters in user input */
/* who allows this?? */
continue;
}
if (cb >= 'a' && cb <= 'z') {
/* upcase */
cb -= 0x20;
}
if (*a != (char) cb) {
/* printf(" %c != %c\n", *a, cb); */
/* mismatch */
return 0;
}
a++;
}
return (*a == 0) ? 1 : 0;
}
static int is_black_tag(const char* s, size_t len, int strictMode)
{
const char** black;
if (len < 3) {
return 0;
}
if (strictMode == 1) {
black = STRICT_BLACKTAG;
} else {
black = BLACKTAG;
}
while (*black != NULL) {
if (cstrcasecmp_with_null(*black, s, len) == 0) {
/* printf("Got black tag %s\n", *black); */
return 1;
}
black += 1;
}
/* anything SVG related */
if ((s[0] == 's' || s[0] == 'S') &&
(s[1] == 'v' || s[1] == 'V') &&
(s[2] == 'g' || s[2] == 'G')) {
/* printf("Got SVG tag \n"); */
return 1;
}
/* Anything XSL(t) related */
if ((s[0] == 'x' || s[0] == 'X') &&
(s[1] == 's' || s[1] == 'S') &&
(s[2] == 'l' || s[2] == 'L')) {
/* printf("Got XSL tag\n"); */
return 1;
}
return 0;
}
static attribute_t is_black_attr(const char* s, size_t len, int strictMode)
{
stringtype_t* black;
if (len < 2) {
return TYPE_NONE;
}
if (len >= 5) {
/* JavaScript on.* event handlers */
if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
black = BLACKATTREVENT;
const char *s_without_on = &s[2]; // start comparing from the third char
while (black->name != NULL) {
if (cstrcasecmp_with_null(black->name, s_without_on, strlen(black->name)) == 0) {
/* printf("Got banned attribute name %s\n", black->name); */
return black->atype;
}
black += 1;
}
}
/* XMLNS can be used to create arbitrary tags */
// goedge: commented for photo uploading
//if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) {
/* printf("Got XMLNS and XLINK tags\n"); */
// return TYPE_BLACK;
//}
}
if (strictMode == 1) {
black = STRICT_BLACKATTR;
} else {
black = BLACKATTR;
}
while (black->name != NULL) {
if (cstrcasecmp_with_null(black->name, s, len) == 0) {
/* printf("Got banned attribute name %s\n", black->name); */
return black->atype;
}
black += 1;
}
return TYPE_NONE;
}
static int is_black_url(const char* s, size_t len)
{
static const char* data_url = "DATA";
static const char* viewsource_url = "VIEW-SOURCE";
/* obsolete but interesting signal */
static const char* vbscript_url = "VBSCRIPT";
/* covers JAVA, JAVASCRIPT, + colon */
static const char* javascript_url = "JAVA";
/* skip whitespace */
while (len > 0 && (*s <= 32 || *s >= 127)) {
/*
* HEY: this is a signed character.
* We are intentionally skipping high-bit characters too
* since they are not ASCII, and Opera sometimes uses UTF-8 whitespace.
*
* Also in EUC-JP some of the high bytes are just ignored.
*/
++s;
--len;
}
if (htmlencode_startswith(data_url, s, len)) {
return 1;
}
if (htmlencode_startswith(viewsource_url, s, len)) {
return 1;
}
if (htmlencode_startswith(javascript_url, s, len)) {
return 1;
}
if (htmlencode_startswith(vbscript_url, s, len)) {
return 1;
}
return 0;
}
int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode)
{
h5_state_t h5;
attribute_t attr = TYPE_NONE;
libinjection_h5_init(&h5, s, len, (enum html5_flags) flags);
while (libinjection_h5_next(&h5)) {
if (h5.token_type != ATTR_VALUE) {
attr = TYPE_NONE;
}
if (h5.token_type == DOCTYPE) {
return 1;
} else if (h5.token_type == TAG_NAME_OPEN) {
if (is_black_tag(h5.token_start, h5.token_len, strictMode)) {
return 1;
}
} else if (h5.token_type == ATTR_NAME) {
attr = is_black_attr(h5.token_start, h5.token_len, strictMode);
} else if (h5.token_type == ATTR_VALUE) {
/*
* IE6,7,8 parsing works a bit differently so
* a whole <script> or other black tag might be hiding
* inside an attribute value under HTML 5 parsing
* See http://html5sec.org/#102
* to avoid doing a full reparse of the value, just
* look for "<". This probably need adjusting to
* handle escaped characters
*/
/*
if (memchr(h5.token_start, '<', h5.token_len) != NULL) {
return 1;
}
*/
switch (attr) {
case TYPE_NONE:
break;
case TYPE_BLACK:
return 1;
case TYPE_ATTR_URL:
if (is_black_url(h5.token_start, h5.token_len)) {
return 1;
}
break;
case TYPE_STYLE:
return 1;
case TYPE_ATTR_INDIRECT:
/* an attribute name is specified in a _value_ */
if (is_black_attr(h5.token_start, h5.token_len, strictMode)) {
return 1;
}
break;
/*
default:
assert(0);
*/
}
attr = TYPE_NONE;
} else if (h5.token_type == TAG_COMMENT) {
/* IE uses a "`" as a tag ending char */
// goedge: commented for photo uploading
/**if (memchr(h5.token_start, '`', h5.token_len) != NULL) {
return 1;
}**/
/* IE conditional comment */
if (h5.token_len > 3) {
if (h5.token_start[0] == '[' &&
(h5.token_start[1] == 'i' || h5.token_start[1] == 'I') &&
(h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
return 1;
}
if ((h5.token_start[0] == 'x' || h5.token_start[0] == 'X') &&
(h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
(h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
return 1;
}
}
if (h5.token_len > 5) {
/* IE <?import pseudo-tag */
if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) {
return 1;
}
/* XML Entity definition */
if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) {
return 1;
}
}
}
}
return 0;
}
/*
* wrapper
*
*
* const char* s: input string, may contain nulls, does not need to be null-terminated.
* size_t len: input string length.
*
*
*/
int libinjection_xss(const char* s, size_t slen, int strictMode)
{
if (libinjection_is_xss(s, slen, DATA_STATE, strictMode)) {
return 1;
}
if (libinjection_is_xss(s, slen, VALUE_NO_QUOTE, strictMode)) {
return 1;
}
if (libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE, strictMode)) {
return 1;
}
if (libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE, strictMode)) {
return 1;
}
if (libinjection_is_xss(s, slen, VALUE_BACK_QUOTE, strictMode)) {
return 1;
}
return 0;
}

View File

@@ -0,0 +1,21 @@
#ifndef LIBINJECTION_XSS
#define LIBINJECTION_XSS
#ifdef __cplusplus
extern "C" {
#endif
/**
* HEY THIS ISN'T DONE
*/
/* pull in size_t */
#include <string.h>
int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,313 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "libinjection.h"
#include "libinjection_sqli.h"
#include "libinjection_xss.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static int g_test_ok = 0;
static int g_test_fail = 0;
typedef enum {
MODE_SQLI,
MODE_XSS
} detect_mode_t;
static void usage(const char* program_name);
size_t modp_rtrim(char* str, size_t len);
void modp_toprint(char* str, size_t len);
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
int flag_invert, int flag_true, int flag_quiet);
int urlcharmap(char ch);
size_t modp_url_decode(char* dest, const char* s, size_t len);
int urlcharmap(char ch) {
switch (ch) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default:
return 256;
}
}
size_t modp_url_decode(char* dest, const char* s, size_t len)
{
const char* deststart = dest;
size_t i = 0;
int d = 0;
while (i < len) {
switch (s[i]) {
case '+':
*dest++ = ' ';
i += 1;
break;
case '%':
if (i+2 < len) {
d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
if ( d < 256) {
*dest = (char) d;
dest++;
i += 3; /* loop will increment one time */
} else {
*dest++ = '%';
i += 1;
}
} else {
*dest++ = '%';
i += 1;
}
break;
default:
*dest++ = s[i];
i += 1;
}
}
*dest = '\0';
return (size_t)(dest - deststart); /* compute "strlen" of dest */
}
void modp_toprint(char* str, size_t len)
{
size_t i;
for (i = 0; i < len; ++i) {
if (str[i] < 32 || str[i] > 126) {
str[i] = '?';
}
}
}
size_t modp_rtrim(char* str, size_t len)
{
while (len) {
char c = str[len -1];
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
str[len -1] = '\0';
len -= 1;
} else {
break;
}
}
return len;
}
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
int flag_invert, int flag_true, int flag_quiet)
{
char linebuf[8192];
int issqli = 0;
int linenum = 0;
size_t len;
sfilter sf;
while (fgets(linebuf, sizeof(linebuf), fd)) {
linenum += 1;
len = modp_rtrim(linebuf, strlen(linebuf));
if (len == 0) {
continue;
}
if (linebuf[0] == '#') {
continue;
}
len = modp_url_decode(linebuf, linebuf, len);
switch (mode) {
case MODE_SQLI: {
libinjection_sqli_init(&sf, linebuf, len, 0);
issqli = libinjection_is_sqli(&sf);
break;
}
case MODE_XSS: {
issqli = libinjection_xss(linebuf, len);
break;
}
default:
assert(0);
}
if (issqli) {
g_test_ok += 1;
} else {
g_test_fail += 1;
}
if (!flag_quiet) {
if ((issqli && flag_true && ! flag_invert) ||
(!issqli && flag_true && flag_invert) ||
!flag_true) {
modp_toprint(linebuf, len);
switch (mode) {
case MODE_SQLI: {
/*
* if we didn't find a SQLi and fingerprint from
* sqlstats is is 'sns' or 'snsns' then redo using
* plain context
*/
if (!issqli && (strcmp(sf.fingerprint, "sns") == 0 ||
strcmp(sf.fingerprint, "snsns") == 0)) {
libinjection_sqli_fingerprint(&sf, 0);
}
fprintf(stdout, "%s\t%d\t%s\t%s\t%s\n",
fname, linenum,
(issqli ? "True" : "False"), sf.fingerprint, linebuf);
break;
}
case MODE_XSS: {
fprintf(stdout, "%s\t%d\t%s\t%s\n",
fname, linenum,
(issqli ? "True" : "False"), linebuf);
break;
}
default:
assert(0);
}
}
}
}
}
static void usage(const char* program_name)
{
fprintf(stdout, "usage: %s [flags] [files...]\n", program_name);
fprintf(stdout, "%s\n", "");
fprintf(stdout, "%s\n", "-q --quiet : quiet mode");
fprintf(stdout, "%s\n", "-m --max-fails : number of failed cases need to fail entire test");
fprintf(stdout, "%s\n", "-s INTEGER : repeat each test N time "
"(for performance testing)");
fprintf(stdout, "%s\n", "-t : only print positive matches");
fprintf(stdout, "%s\n", "-x --mode-xss : test input for XSS");
fprintf(stdout, "%s\n", "-i --invert : invert test logic "
"(input is tested for being safe)");
fprintf(stdout, "%s\n", "");
fprintf(stdout, "%s\n", "-? -h -help --help : this page");
fprintf(stdout, "%s\n", "");
}
int main(int argc, const char *argv[])
{
/*
* invert output, by
*/
int flag_invert = FALSE;
/*
* don't print anything.. useful for
* performance monitors, gprof.
*/
int flag_quiet = FALSE;
/*
* only print positive results
* with invert, only print negative results
*/
int flag_true = FALSE;
detect_mode_t mode = MODE_SQLI;
int flag_slow = 1;
int count = 0;
int max = -1;
int i, j;
int offset = 1;
while (offset < argc) {
if (strcmp(argv[offset], "-?") == 0 ||
strcmp(argv[offset], "-h") == 0 ||
strcmp(argv[offset], "-help") == 0 ||
strcmp(argv[offset], "--help") == 0) {
usage(argv[0]);
exit(0);
}
if (strcmp(argv[offset], "-i") == 0) {
offset += 1;
flag_invert = TRUE;
} else if (strcmp(argv[offset], "-q") == 0 ||
strcmp(argv[offset], "--quiet") == 0) {
offset += 1;
flag_quiet = TRUE;
} else if (strcmp(argv[offset], "-t") == 0) {
offset += 1;
flag_true = TRUE;
} else if (strcmp(argv[offset], "-s") == 0) {
offset += 1;
flag_slow = 100;
} else if (strcmp(argv[offset], "-m") == 0 ||
strcmp(argv[offset], "--max-fails") == 0) {
offset += 1;
max = atoi(argv[offset]);
offset += 1;
} else if (strcmp(argv[offset], "-x") == 0 ||
strcmp(argv[offset], "--mode-xss") == 0) {
mode = MODE_XSS;
offset += 1;
} else {
break;
}
}
if (offset == argc) {
test_positive(stdin, "stdin", mode, flag_invert, flag_true, flag_quiet);
} else {
for (j = 0; j < flag_slow; ++j) {
for (i = offset; i < argc; ++i) {
FILE* fd = fopen(argv[i], "r");
if (fd) {
test_positive(fd, argv[i], mode, flag_invert, flag_true, flag_quiet);
fclose(fd);
}
}
}
}
if (!flag_quiet) {
fprintf(stdout, "%s", "\n");
fprintf(stdout, "SQLI : %d\n", g_test_ok);
fprintf(stdout, "SAFE : %d\n", g_test_fail);
fprintf(stdout, "TOTAL : %d\n", g_test_ok + g_test_fail);
}
if (max == -1) {
return 0;
}
count = g_test_ok;
if (flag_invert) {
count = g_test_fail;
}
if (count > max) {
printf("\nThreshold is %d, got %d, failing.\n", max, count);
return 1;
} else {
printf("\nThreshold is %d, got %d, passing.\n", max, count);
return 0;
}
}

View File

@@ -0,0 +1,165 @@
/**
* Copyright 2012, 2013 Nick Galbreath
* nickg@client9.com
* BSD License -- see COPYING.txt for details
*
* This is for testing against files in ../data/ *.txt
* Reads from stdin or a list of files, and emits if a line
* is a SQLi attack or not, and does basic statistics
*
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "libinjection.h"
#include "libinjection_sqli.h"
void print_string(stoken_t* t);
void print_var(stoken_t* t);
void print_token(stoken_t *t);
void usage(void);
void print_string(stoken_t* t)
{
/* print opening quote */
if (t->str_open != '\0') {
printf("%c", t->str_open);
}
/* print content */
printf("%s", t->val);
/* print closing quote */
if (t->str_close != '\0') {
printf("%c", t->str_close);
}
}
void print_var(stoken_t* t)
{
if (t->count >= 1) {
printf("%c", '@');
}
if (t->count == 2) {
printf("%c", '@');
}
print_string(t);
}
void print_token(stoken_t *t) {
printf("%c ", t->type);
switch (t->type) {
case 's':
print_string(t);
break;
case 'v':
print_var(t);
break;
default:
printf("%s", t->val);
}
printf("%s", "\n");
}
void usage(void) {
printf("\n");
printf("libinjection sqli tester\n");
printf("\n");
printf(" -ca parse as ANSI SQL\n");
printf(" -cm parse as MySQL SQL\n");
printf(" -q0 parse as is\n");
printf(" -q1 parse in single-quote mode\n");
printf(" -q2 parse in doiuble-quote mode\n");
printf("\n");
printf(" -f --fold fold results\n");
printf("\n");
printf(" -d --detect detect SQLI. empty reply = not detected\n");
printf("\n");
}
int main(int argc, const char* argv[])
{
size_t slen;
char* copy;
int flags = 0;
int fold = 0;
int detect = 0;
int i;
int count;
int offset = 1;
int issqli;
sfilter sf;
if (argc < 2) {
usage();
return 1;
}
while (1) {
if (strcmp(argv[offset], "-h") == 0 || strcmp(argv[offset], "-?") == 0 || strcmp(argv[offset], "--help") == 0) {
usage();
return 1;
}
if (strcmp(argv[offset], "-m") == 0) {
flags |= FLAG_SQL_MYSQL;
offset += 1;
}
else if (strcmp(argv[offset], "-f") == 0 || strcmp(argv[offset], "--fold") == 0) {
fold = 1;
offset += 1;
} else if (strcmp(argv[offset], "-d") == 0 || strcmp(argv[offset], "--detect") == 0) {
detect = 1;
offset += 1;
} else if (strcmp(argv[offset], "-ca") == 0) {
flags |= FLAG_SQL_ANSI;
offset += 1;
} else if (strcmp(argv[offset], "-cm") == 0) {
flags |= FLAG_SQL_MYSQL;
offset += 1;
} else if (strcmp(argv[offset], "-q0") == 0) {
flags |= FLAG_QUOTE_NONE;
offset += 1;
} else if (strcmp(argv[offset], "-q1") == 0) {
flags |= FLAG_QUOTE_SINGLE;
offset += 1;
} else if (strcmp(argv[offset], "-q2") == 0) {
flags |= FLAG_QUOTE_DOUBLE;
offset += 1;
} else {
break;
}
}
/* ATTENTION: argv is a C-string, null terminated. We copy this
* to it's own location, WITHOUT null byte. This way, valgrind
* can see if we run past the buffer.
*/
slen = strlen(argv[offset]);
copy = (char* ) malloc(slen);
memcpy(copy, argv[offset], slen);
libinjection_sqli_init(&sf, copy, slen, flags);
if (detect == 1) {
issqli = libinjection_is_sqli(&sf);
if (issqli) {
printf("%s\n", sf.fingerprint);
}
} else if (fold == 1) {
count = libinjection_sqli_fold(&sf);
for (i = 0; i < count; ++i) {
print_token(&(sf.tokenvec[i]));
}
} else {
while (libinjection_sqli_tokenize(&sf)) {
print_token(sf.current);
}
}
free(copy);
return 0;
}

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env python3
#
# Copyright 2012, 2013 Nick Galbreath
# nickg@client9.com
# BSD License -- see COPYING.txt for details
#
"""
Converts a libinjection JSON data file to a C header (.h) file
"""
import sys
def toc(obj):
""" main routine """
print("""
#ifndef LIBINJECTION_SQLI_DATA_H
#define LIBINJECTION_SQLI_DATA_H
#include "libinjection.h"
#include "libinjection_sqli.h"
typedef struct {
const char *word;
char type;
} keyword_t;
static size_t parse_money(sfilter * sf);
static size_t parse_other(sfilter * sf);
static size_t parse_white(sfilter * sf);
static size_t parse_operator1(sfilter *sf);
static size_t parse_char(sfilter *sf);
static size_t parse_hash(sfilter *sf);
static size_t parse_dash(sfilter *sf);
static size_t parse_slash(sfilter *sf);
static size_t parse_backslash(sfilter * sf);
static size_t parse_operator2(sfilter *sf);
static size_t parse_string(sfilter *sf);
static size_t parse_word(sfilter * sf);
static size_t parse_var(sfilter * sf);
static size_t parse_number(sfilter * sf);
static size_t parse_tick(sfilter * sf);
static size_t parse_ustring(sfilter * sf);
static size_t parse_qstring(sfilter * sf);
static size_t parse_nqstring(sfilter * sf);
static size_t parse_xstring(sfilter * sf);
static size_t parse_bstring(sfilter * sf);
static size_t parse_estring(sfilter * sf);
static size_t parse_bword(sfilter * sf);
""")
#
# Mapping of character to function
#
fnmap = {
'CHAR_WORD' : 'parse_word',
'CHAR_WHITE': 'parse_white',
'CHAR_OP1' : 'parse_operator1',
'CHAR_UNARY': 'parse_operator1',
'CHAR_OP2' : 'parse_operator2',
'CHAR_BANG' : 'parse_operator2',
'CHAR_BACK' : 'parse_backslash',
'CHAR_DASH' : 'parse_dash',
'CHAR_STR' : 'parse_string',
'CHAR_HASH' : 'parse_hash',
'CHAR_NUM' : 'parse_number',
'CHAR_SLASH': 'parse_slash',
'CHAR_SEMICOLON' : 'parse_char',
'CHAR_COMMA': 'parse_char',
'CHAR_LEFTPARENS': 'parse_char',
'CHAR_RIGHTPARENS': 'parse_char',
'CHAR_LEFTBRACE': 'parse_char',
'CHAR_RIGHTBRACE': 'parse_char',
'CHAR_VAR' : 'parse_var',
'CHAR_OTHER': 'parse_other',
'CHAR_MONEY': 'parse_money',
'CHAR_TICK' : 'parse_tick',
'CHAR_UNDERSCORE': 'parse_underscore',
'CHAR_USTRING' : 'parse_ustring',
'CHAR_QSTRING' : 'parse_qstring',
'CHAR_NQSTRING' : 'parse_nqstring',
'CHAR_XSTRING' : 'parse_xstring',
'CHAR_BSTRING' : 'parse_bstring',
'CHAR_ESTRING' : 'parse_estring',
'CHAR_BWORD' : 'parse_bword'
}
print()
print("typedef size_t (*pt2Function)(sfilter *sf);")
print("static const pt2Function char_parse_map[] = {")
pos = 0
for character in obj['charmap']:
print(" &%s, /* %d */" % (fnmap[character], pos))
pos += 1
print("};")
print()
# keywords
# load them
keywords = obj['keywords']
for fingerprint in list(obj['fingerprints']):
fingerprint = '0' + fingerprint.upper()
keywords[fingerprint] = 'F'
needhelp = []
for key in keywords.keys():
if key != key.upper():
needhelp.append(key)
for key in needhelp:
tmpv = keywords[key]
del keywords[key]
keywords[key.upper()] = tmpv
print("static const keyword_t sql_keywords[] = {")
for k in sorted(keywords.keys()):
if len(k) > 31:
sys.stderr.write("ERROR: keyword greater than 32 chars\n")
sys.exit(1)
print(" {\"%s\", '%s'}," % (k, keywords[k]))
print("};")
print("static const size_t sql_keywords_sz = %d;" % (len(keywords), ))
print("#endif")
return 0
if __name__ == '__main__':
import json
sys.exit(toc(json.load(sys.stdin)))

View File

@@ -0,0 +1,3 @@
#define LIBINJECTION_VERSION "3.9.1"
#include "libinjection/src/libinjection_sqli.c"

View File

@@ -0,0 +1,6 @@
#define LIBINJECTION_VERSION "3.9.1"
#include "libinjection/src/libinjection_xss.c"
#include "libinjection/src/libinjection_html5.c"
#define GOEDGE_VERSION "25" // last version is for GoEdge change

View File

@@ -0,0 +1,91 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build gcc
package injectionutils
/*
#cgo CFLAGS: -O2 -I./libinjection/src
#include <libinjection.h>
#include <stdlib.h>
*/
import "C"
import (
"net/url"
"strings"
"unicode/utf8"
"unsafe"
)
// DetectSQLInjectionCache detect sql injection in string with cache
func DetectSQLInjectionCache(input string, isStrict bool, cacheLife int) bool {
var l = len(input)
if l == 0 {
return false
}
if cacheLife <= 0 || l < 128 || l > MaxCacheDataSize {
return DetectSQLInjection(input, isStrict)
}
var result = DetectSQLInjection(input, isStrict)
return result
}
// DetectSQLInjection detect sql injection in string
func DetectSQLInjection(input string, isStrict bool) bool {
if len(input) == 0 {
return false
}
if !isStrict {
if len(input) > 1024 {
if !utf8.ValidString(input[:1024]) && !utf8.ValidString(input[:1023]) && !utf8.ValidString(input[:1022]) {
return false
}
} else {
if !utf8.ValidString(input) {
return false
}
}
}
if detectSQLInjectionOne(input) {
return true
}
// 兼容 /PATH?URI
if (input[0] == '/' || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://")) && len(input) < 1024 {
var argsIndex = strings.Index(input, "?")
if argsIndex > 0 {
var args = input[argsIndex+1:]
unescapeArgs, err := url.QueryUnescape(args)
if err == nil && args != unescapeArgs {
return detectSQLInjectionOne(args) || detectSQLInjectionOne(unescapeArgs)
} else {
return detectSQLInjectionOne(args)
}
}
} else {
unescapedInput, err := url.QueryUnescape(input)
if err == nil && input != unescapedInput {
return detectSQLInjectionOne(unescapedInput)
}
}
return false
}
func detectSQLInjectionOne(input string) bool {
if len(input) == 0 {
return false
}
var fingerprint [8]C.char
var fingerprintPtr = (*C.char)(unsafe.Pointer(&fingerprint[0]))
var cInput = C.CString(input)
defer C.free(unsafe.Pointer(cInput))
return C.libinjection_sqli(cInput, C.size_t(len(input)), fingerprintPtr) == 1
}

View File

@@ -0,0 +1,21 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !gcc
package injectionutils
// DetectSQLInjectionCache detect sql injection in string with cache
func DetectSQLInjectionCache(input string, isStrict bool, cacheLife int) bool {
// stub
return false
}
// DetectSQLInjection detect sql injection in string
func DetectSQLInjection(input string, isStrict bool) bool {
// stub
return false
}
func detectSQLInjectionOne(input string) bool {
// stub
return false
}

View File

@@ -0,0 +1,130 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build gcc
package injectionutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
"github.com/iwind/TeaGo/assert"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"runtime"
"strings"
"testing"
)
func TestDetectSQLInjection(t *testing.T) {
var a = assert.NewAssertion(t)
for _, isStrict := range []bool{true, false} {
a.IsTrue(injectionutils.DetectSQLInjection("' UNION SELECT * FROM myTable", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=1 ' UNION select * from a", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("' UNION SELECT1 * FROM myTable", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("1234", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=123 OR 1=1&b=2", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=123&b=456&c=1' or 2=2", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("?", isStrict))
a.IsFalse(injectionutils.DetectSQLInjection("/hello?age=22", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/sql/injection?id=123%20or%201=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("id=123%20or%201=1", isStrict))
a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/' or 1=1", isStrict))
}
}
func BenchmarkDetectSQLInjection(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", false)
}
})
}
func BenchmarkDetectSQLInjection_URL(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", false)
}
})
}
func BenchmarkDetectSQLInjection_Normal_Small(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("a/sql/injection?id=1234", false)
}
})
}
func BenchmarkDetectSQLInjection_URL_Normal_Small(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("/sql/injection?id="+types.String(rands.Int64()%10000), false)
}
})
}
func BenchmarkDetectSQLInjection_URL_Normal_Middle(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("/search?q=libinjection+fingerprint&newwindow=1&sca_esv=589290862&sxsrf=AMwHvKnxuLoejn2XlNniffC12E_xc35M7Q%3A1702090118361&ei=htvzzebfFZfo1e8PvLGggAk&ved=0ahUKEwjTsYmnq4GDAxUWdPOHHbwkCJAQ4ddDCBA&uact=5&oq=libinjection+fingerprint&gs_lp=Egxnd3Mtd2l6LXNlcnAiGIxpYmluamVjdGlvbmBmaW5nKXJwcmludTIEEAAYHjIGVAAYCBgeSiEaUPkRWKFZcAJ4AZABAJgBHgGgAfoEqgwDMC40uAEGyAEA-AEBwgIKEAFYTxjWMuiwA-IDBBgAVteIBgGQBgI&sclient=gws-wiz-serp#ip=1", false)
}
})
}
func BenchmarkDetectSQLInjection_URL_Normal_Small_Cache(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjectionCache("/sql/injection?id="+types.String(rands.Int64()%10000), false, 1800)
}
})
}
func BenchmarkDetectSQLInjection_Normal_Large(b *testing.B) {
runtime.GOMAXPROCS(4)
var s = strings.Repeat("A", 512)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s+"&v=%20", false)
}
})
}
func BenchmarkDetectSQLInjection_Normal_Large_Cache(b *testing.B) {
runtime.GOMAXPROCS(4)
var s = strings.Repeat("A", 512)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjectionCache("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s, false, 1800)
}
})
}
func BenchmarkDetectSQLInjection_URL_Unescape(b *testing.B) {
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", false)
}
})
}

View File

@@ -0,0 +1,89 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build gcc
package injectionutils
/*
#cgo CFLAGS: -O2 -I./libinjection/src
#include <libinjection.h>
#include <stdlib.h>
*/
import "C"
import (
"github.com/cespare/xxhash/v2"
"net/url"
"strconv"
"strings"
"unsafe"
)
const MaxCacheDataSize = 1 << 20
func DetectXSSCache(input string, isStrict bool, cacheLife int) bool {
var l = len(input)
if l == 0 {
return false
}
if cacheLife <= 0 || l < 512 || l > MaxCacheDataSize {
return DetectXSS(input, isStrict)
}
var hash = xxhash.Sum64String(input)
var key = "WAF@XSS@" + strconv.FormatUint(hash, 10)
if isStrict {
key += "@1"
}
var result = DetectXSS(input, isStrict)
return result
}
// DetectXSS detect XSS in string
func DetectXSS(input string, isStrict bool) bool {
if len(input) == 0 {
return false
}
if detectXSSOne(input, isStrict) {
return true
}
// 兼容 /PATH?URI
if (input[0] == '/' || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://")) && len(input) < 1024 {
var argsIndex = strings.Index(input, "?")
if argsIndex > 0 {
var args = input[argsIndex+1:]
unescapeArgs, err := url.QueryUnescape(args)
if err == nil && args != unescapeArgs {
return detectXSSOne(args, isStrict) || detectXSSOne(unescapeArgs, isStrict)
} else {
return detectXSSOne(args, isStrict)
}
}
} else {
unescapedInput, err := url.QueryUnescape(input)
if err == nil && input != unescapedInput {
return detectXSSOne(unescapedInput, isStrict)
}
}
return false
}
func detectXSSOne(input string, isStrict bool) bool {
if len(input) == 0 {
return false
}
var cInput = C.CString(input)
defer C.free(unsafe.Pointer(cInput))
var isStrictInt = 0
if isStrict {
isStrictInt = 1
}
return C.libinjection_xss(cInput, C.size_t(len(input)), C.int(isStrictInt)) == 1
}

View File

@@ -0,0 +1,22 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !gcc
package injectionutils
const MaxCacheDataSize = 1 << 20
func DetectXSSCache(input string, isStrict bool, cacheLife int) bool {
// stub
return false
}
// DetectXSS detect XSS in string
func DetectXSS(input string, isStrict bool) bool {
// stub
return false
}
func detectXSSOne(input string, isStrict bool) bool {
// stub
return false
}

View File

@@ -0,0 +1,94 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build gcc
package injectionutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
"github.com/iwind/TeaGo/assert"
"runtime"
"testing"
)
func TestDetectXSS(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsFalse(injectionutils.DetectXSS("", true))
a.IsFalse(injectionutils.DetectXSS("abc", true))
a.IsTrue(injectionutils.DetectXSS("<script>", true))
a.IsTrue(injectionutils.DetectXSS("<link>", true))
a.IsFalse(injectionutils.DetectXSS("<html><span>", true))
a.IsFalse(injectionutils.DetectXSS("&lt;script&gt;", true))
a.IsTrue(injectionutils.DetectXSS("/path?onmousedown=a", true))
a.IsTrue(injectionutils.DetectXSS("/path?onkeyup=a", true))
a.IsTrue(injectionutils.DetectXSS("onkeyup=a", true))
a.IsTrue(injectionutils.DetectXSS("<iframe scrolling='no'>", true))
a.IsFalse(injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", true))
a.IsTrue(injectionutils.DetectXSS("name=s&description=%3Cscript+src%3D%22a.js%22%3Edddd%3C%2Fscript%3E", true))
a.IsFalse(injectionutils.DetectXSS(`<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
<tiff:Orientation>1</tiff:Orientation>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>`, true)) // included in some photo files
a.IsFalse(injectionutils.DetectXSS(`<xml></xml>`, false))
}
func TestDetectXSS_Strict(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsFalse(injectionutils.DetectXSS(`<xml></xml>`, false))
a.IsTrue(injectionutils.DetectXSS(`<xml></xml>`, true))
a.IsFalse(injectionutils.DetectXSS(`<img src=\"\"/>`, false))
a.IsFalse(injectionutils.DetectXSS(`<img src=\"test.jpg\"/>`, true))
a.IsFalse(injectionutils.DetectXSS(`<a href="aaaa"></a>`, true))
a.IsFalse(injectionutils.DetectXSS(`<span style="color: red"></span>`, false))
a.IsTrue(injectionutils.DetectXSS(`<span style="color: red"></span>`, true))
a.IsFalse(injectionutils.DetectXSS("https://example.com?style=list", false))
a.IsTrue(injectionutils.DetectXSS("https://example.com?style=list", true))
}
func BenchmarkDetectXSS_MISS(b *testing.B) {
var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
if result {
b.Fatal("'result' should not be 'true'")
}
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
}
})
}
func BenchmarkDetectXSS_MISS_Cache(b *testing.B) {
var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
if result {
b.Fatal("'result' should not be 'true'")
}
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectXSSCache("<html><body><span>RequestId: 1234567890</span></body></html>", false, 1800)
}
})
}
func BenchmarkDetectXSS_HIT(b *testing.B) {
var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
if !result {
b.Fatal("'result' should not be 'false'")
}
runtime.GOMAXPROCS(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
}
})
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
@@ -106,7 +107,7 @@ func (this *ParentAction) CreateLog(level string, messageCode langs.MessageCode,
}
}
}
err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, this.RequestRemoteIP(), messageCode, args)
err := dao.SharedLogDAO.CreateAdminLog(this.AdminContext(), level, this.Request.URL.Path, desc, loginutils.RemoteIP(&this.ActionObject), messageCode, args)
if err != nil {
utils.PrintError(err)
}
@@ -168,3 +169,14 @@ func (this *ParentAction) FailLang(messageCode langs.MessageCode, args ...any) {
func (this *ParentAction) FailFieldLang(field string, messageCode langs.MessageCode, args ...any) {
this.FailField(field, langs.Message(this.LangCode(), messageCode, args...))
}
func (this *ParentAction) FilterHTTPFamily() bool {
if this.Data.GetString("serverFamily") == "http" {
return false
}
this.ResponseWriter.WriteHeader(http.StatusNotFound)
_, _ = this.ResponseWriter.Write([]byte("page not found"))
return true
}

View File

@@ -3,9 +3,11 @@ package admins
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/otputils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/skip2/go-qrcode"
"github.com/xlzd/gotp"
)
@@ -19,7 +21,8 @@ func (this *OtpQrcodeAction) Init() {
}
func (this *OtpQrcodeAction) RunGet(params struct {
AdminId int64
AdminId int64
Download bool
}) {
loginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
AdminId: params.AdminId,
@@ -29,19 +32,19 @@ func (this *OtpQrcodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
login := loginResp.Login
var login = loginResp.Login
if login == nil || !login.IsOn {
this.NotFound("adminLogin", params.AdminId)
return
}
loginParams := maps.Map{}
var loginParams = maps.Map{}
err = json.Unmarshal(login.ParamsJSON, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
secret := loginParams.GetString("secret")
var secret = loginParams.GetString("secret")
// 当前用户信息
adminResp, err := this.RPC().AdminRPC().FindEnabledAdmin(this.AdminContext(), &pb.FindEnabledAdminRequest{AdminId: params.AdminId})
@@ -49,7 +52,7 @@ func (this *OtpQrcodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
admin := adminResp.Admin
var admin = adminResp.Admin
if admin == nil {
this.NotFound("admin", params.AdminId)
return
@@ -60,12 +63,19 @@ func (this *OtpQrcodeAction) RunGet(params struct {
this.ErrorPage(err)
return
}
url := gotp.NewDefaultTOTP(secret).ProvisioningUri(admin.Username, uiConfig.AdminSystemName)
data, err := qrcode.Encode(url, qrcode.Medium, 256)
var url = gotp.NewDefaultTOTP(secret).ProvisioningUri(admin.Username, uiConfig.AdminSystemName)
data, err := qrcode.Encode(otputils.FixIssuer(url), qrcode.Medium, 256)
if err != nil {
this.ErrorPage(err)
return
}
if params.Download {
var filename = "OTP-ADMIN-" + admin.Username + ".png"
this.AddHeader("Content-Disposition", "attachment; filename=\""+filename+"\";")
}
this.AddHeader("Content-Type", "image/png")
this.AddHeader("Content-Length", types.String(len(data)))
_, _ = this.Write(data)
}

View File

@@ -49,6 +49,7 @@ func init() {
Post("/start", new(node.StartAction)).
Post("/stop", new(node.StopAction)).
Post("/up", new(node.UpAction)).
Post("/updateIsOn", new(node.UpdateIsOnAction)).
Get("/detail", new(node.DetailAction)).
GetPost("/updateDNSPopup", new(node.UpdateDNSPopupAction)).
Post("/syncDomain", new(node.SyncDomainAction)).

View File

@@ -3,11 +3,11 @@ package node
import (
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
@@ -178,7 +178,7 @@ func (this *DetailAction) RunGet(params struct {
for _, route := range dnsInfo.Routes {
var recordType = "A"
if utils.IsIPv6(addr.Ip) {
if iputils.IsIPv6(addr.Ip) {
recordType = "AAAA"
}
recordMaps = append(recordMaps, maps.Map{

View File

@@ -0,0 +1,16 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package nodeutils_test
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
_ "github.com/iwind/TeaGo/bootstrap"
"testing"
)
func TestInstallLocalNode(t *testing.T) {
err := nodeutils.InstallLocalNode()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,35 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package node
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdateIsOnAction struct {
actionutils.ParentAction
}
func (this *UpdateIsOnAction) RunPost(params struct {
NodeId int64
IsOn bool
}) {
if params.IsOn {
defer this.CreateLogInfo(codes.Node_LogUpdateNodeOn, params.NodeId)
} else {
defer this.CreateLogInfo(codes.Node_LogUpdateNodeOff, params.NodeId)
}
_, err := this.RPC().NodeRPC().UpdateNodeIsOn(this.AdminContext(), &pb.UpdateNodeIsOnRequest{
NodeId: params.NodeId,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -3,8 +3,8 @@
package dns
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
@@ -110,7 +110,7 @@ func (this *RecordsAction) RunGet(params struct {
var isResolved = false
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
if iputils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
@@ -148,7 +148,7 @@ func (this *RecordsAction) RunGet(params struct {
var isResolved = false
if isInstalled && len(defaultRoute) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
if iputils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{

View File

@@ -46,6 +46,9 @@ func (this *IndexAction) RunGet(params struct {
var httpAllDomainMismatchActionCode = serverconfigs.DomainMismatchActionPage
var httpAllDomainMismatchActionContentHTML string
var httpAllDomainMismatchActionStatusCode = "404"
var httpAllDomainMismatchActionRedirectURL = ""
if config.HTTPAll.DomainMismatchAction != nil {
httpAllDomainMismatchActionCode = config.HTTPAll.DomainMismatchAction.Code
@@ -56,6 +59,10 @@ func (this *IndexAction) RunGet(params struct {
if statusCode > 0 {
httpAllDomainMismatchActionStatusCode = types.String(statusCode)
}
if config.HTTPAll.DomainMismatchAction.Code == serverconfigs.DomainMismatchActionRedirect {
httpAllDomainMismatchActionRedirectURL = config.HTTPAll.DomainMismatchAction.Options.GetString("url")
}
}
} else {
httpAllDomainMismatchActionContentHTML = `<!DOCTYPE html>
@@ -83,6 +90,8 @@ p { color: grey; }
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
this.Data["httpAllDomainMismatchActionStatusCode"] = httpAllDomainMismatchActionStatusCode
this.Data["httpAllDomainMismatchActionRedirectURL"] = httpAllDomainMismatchActionRedirectURL
this.Show()
}
@@ -93,12 +102,14 @@ func (this *IndexAction) RunPost(params struct {
HttpAllDomainMismatchActionCode string
HttpAllDomainMismatchActionContentHTML string
HttpAllDomainMismatchActionStatusCode string
HttpAllDomainMismatchActionRedirectURL string
HttpAllAllowMismatchDomainsJSON []byte
HttpAllAllowNodeIP bool
HttpAllDefaultDomain string
HttpAllNodeIPPageHTML string
HttpAllNodeIPShowPage bool
HttpAllEnableServerAddrVariable bool
HttpAllRequestOriginsWithEncodings bool
HttpAllDomainAuditingIsOn bool
HttpAllDomainAuditingPrompt string
@@ -156,11 +167,28 @@ func (this *IndexAction) RunPost(params struct {
var domainMisMatchStatusCode = types.Int(domainMisMatchStatusCodeString)
config.HTTPAll.MatchDomainStrictly = params.HttpAllMatchDomainStrictly
// validate
if config.HTTPAll.MatchDomainStrictly {
// validate redirect
if params.HttpAllDomainMismatchActionCode == serverconfigs.DomainMismatchActionRedirect {
if len(params.HttpAllDomainMismatchActionRedirectURL) == 0 {
this.FailField("httpAllDomainMismatchActionRedirectURL", "请输入跳转目标网址URL")
return
}
if !regexp.MustCompile(`(?i)(http|https)://`).MatchString(params.HttpAllDomainMismatchActionRedirectURL) {
this.FailField("httpAllDomainMismatchActionRedirectURL", "目标网址URL必须以http://或https://开头")
return
}
}
}
config.HTTPAll.DomainMismatchAction = &serverconfigs.DomainMismatchAction{
Code: params.HttpAllDomainMismatchActionCode,
Options: maps.Map{
"statusCode": domainMisMatchStatusCode,
"contentHTML": params.HttpAllDomainMismatchActionContentHTML,
"statusCode": domainMisMatchStatusCode, // page
"contentHTML": params.HttpAllDomainMismatchActionContentHTML, // page
"url": params.HttpAllDomainMismatchActionRedirectURL, // redirect
},
}
@@ -190,6 +218,7 @@ func (this *IndexAction) RunPost(params struct {
config.HTTPAll.ForceLnRequest = params.HttpAllForceLnRequest
config.HTTPAll.LnRequestSchedulingMethod = params.HttpAllLnRequestSchedulingMethod
config.HTTPAll.EnableServerAddrVariable = params.HttpAllEnableServerAddrVariable
config.HTTPAll.RequestOriginsWithEncodings = params.HttpAllRequestOriginsWithEncodings
// 访问日志
config.HTTPAccessLog.IsOn = params.HttpAccessLogIsOn

View File

@@ -113,6 +113,9 @@ func (this *IndexAction) RunGet(params struct {
"autoRemoteStart": cluster.AutoRemoteStart,
"autoInstallNftables": cluster.AutoInstallNftables,
"autoSystemTuning": cluster.AutoSystemTuning,
"autoTrimDisks": cluster.AutoTrimDisks,
"maxConcurrentReads": cluster.MaxConcurrentReads,
"maxConcurrentWrites": cluster.MaxConcurrentWrites,
"sshParams": sshParams,
"domainName": fullDomainName,
}
@@ -141,6 +144,9 @@ func (this *IndexAction) RunPost(params struct {
AutoRemoteStart bool
AutoInstallNftables bool
AutoSystemTuning bool
AutoTrimDisks bool
MaxConcurrentReads int32
MaxConcurrentWrites int32
Must *actions.Must
}) {
@@ -196,7 +202,10 @@ func (this *IndexAction) RunPost(params struct {
AutoRemoteStart: params.AutoRemoteStart,
AutoInstallNftables: params.AutoInstallNftables,
AutoSystemTuning: params.AutoSystemTuning,
AutoTrimDisks: params.AutoTrimDisks,
SshParamsJSON: sshParamsJSON,
MaxConcurrentReads: params.MaxConcurrentReads,
MaxConcurrentWrites: params.MaxConcurrentWrites,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -45,6 +45,7 @@ func (this *IndexAction) RunGet(params struct {
func (this *IndexAction) RunPost(params struct {
ClusterId int64
IsOn bool
Quality int
RequireCache bool
MinLengthJSON []byte
MaxLengthJSON []byte
@@ -59,6 +60,13 @@ func (this *IndexAction) RunPost(params struct {
RequireCache: params.RequireCache,
}
if params.Quality < 0 {
params.Quality = 0
} else if params.Quality > 100 {
params.Quality = 100
}
config.Quality = params.Quality
if len(params.MinLengthJSON) > 0 {
var minLength = &shared.SizeCapacity{}
err := json.Unmarshal(params.MinLengthJSON, minLength)
@@ -79,6 +87,11 @@ func (this *IndexAction) RunPost(params struct {
config.MaxLength = maxLength
}
err := config.Init()
if err != nil {
this.Fail("配置校验失败:" + err.Error())
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
@@ -87,7 +100,7 @@ func (this *IndexAction) RunPost(params struct {
_, err = this.RPC().NodeClusterRPC().UpdateNodeClusterWebPPolicy(this.AdminContext(), &pb.UpdateNodeClusterWebPPolicyRequest{
NodeClusterId: params.ClusterId,
WebpPolicyJSON: configJSON,
WebPPolicyJSON: configJSON,
})
if err != nil {

View File

@@ -180,7 +180,7 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F
"name": this.Lang(actionPtr, codes.NodeClusterMenu_SettingWebP),
"url": "/clusters/cluster/settings/webp?clusterId=" + clusterId,
"isActive": selectedItem == "webp",
"isOn": info != nil && info.WebpIsOn,
"isOn": info != nil && info.WebPIsOn,
})
items = this.filterMenuItems1(items, info, clusterId, selectedItem, actionPtr)

View File

@@ -1,7 +1,9 @@
package clusters
import (
"crypto/rand"
"encoding/json"
"fmt"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
@@ -9,6 +11,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/rands"
)
type CreateAction struct {
@@ -44,6 +47,18 @@ func (this *CreateAction) RunGet(params struct{}) {
}
this.Data["totalNodes"] = totalNodesResp.Count
// 随机子域名
var defaultDNSName = "g" + rands.HexString(6) + ".cdn"
{
var b = make([]byte, 3)
_, err = rand.Read(b)
if err == nil {
defaultDNSName = fmt.Sprintf("g%x.cdn", b)
}
}
this.Data["defaultDNSName"] = defaultDNSName
this.Data["dnsName"] = defaultDNSName
this.Show()
}
@@ -65,6 +80,9 @@ func (this *CreateAction) RunPost(params struct {
SystemdServiceIsOn bool
AutoInstallNftables bool
AutoSystemTuning bool
AutoTrimDisks bool
MaxConcurrentReads int32
MaxConcurrentWrites int32
// DNS相关
DnsDomainId int64
@@ -134,6 +152,9 @@ func (this *CreateAction) RunPost(params struct {
GlobalServerConfigJSON: globalServerConfigJSON,
AutoInstallNftables: params.AutoInstallNftables,
AutoSystemTuning: params.AutoSystemTuning,
AutoTrimDisks: params.AutoTrimDisks,
MaxConcurrentReads: params.MaxConcurrentReads,
MaxConcurrentWrites: params.MaxConcurrentWrites,
})
if err != nil {
this.ErrorPage(err)

View File

@@ -31,7 +31,14 @@ func (this *GrantAction) RunGet(params struct {
// TODO 处理节点专用的认证
grant := grantResp.NodeGrant
var grant = grantResp.NodeGrant
var privateKey = grant.PrivateKey
const maskLength = 64
if len(privateKey) > maskLength+32 {
privateKey = privateKey[:maskLength] + strings.Repeat("*", len(privateKey)-maskLength)
}
this.Data["grant"] = maps.Map{
"id": grant.Id,
"name": grant.Name,
@@ -39,7 +46,7 @@ func (this *GrantAction) RunGet(params struct {
"methodName": grantutils.FindGrantMethodName(grant.Method, this.LangCode()),
"username": grant.Username,
"password": strings.Repeat("*", len(grant.Password)),
"privateKey": grant.PrivateKey,
"privateKey": privateKey,
"passphrase": strings.Repeat("*", len(grant.Passphrase)),
"description": grant.Description,
"su": grant.Su,

View File

@@ -27,7 +27,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
page := this.NewPage(countResp.Count)
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
grantsResp, err := this.RPC().NodeGrantRPC().ListEnabledNodeGrants(this.AdminContext(), &pb.ListEnabledNodeGrantsRequest{
@@ -39,7 +39,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
grantMaps := []maps.Map{}
var grantMaps = []maps.Map{}
for _, grant := range grantsResp.NodeGrants {
// 集群数
countClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClustersWithNodeGrantId(this.AdminContext(), &pb.CountAllEnabledNodeClustersWithNodeGrantIdRequest{NodeGrantId: grant.Id})
@@ -47,7 +47,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
countClusters := countClustersResp.Count
var countClusters = countClustersResp.Count
// 节点数
countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGrantId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGrantIdRequest{NodeGrantId: grant.Id})
@@ -55,7 +55,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
countNodes := countNodesResp.Count
var countNodes = countNodesResp.Count
grantMaps = append(grantMaps, maps.Map{
"id": grant.Id,

View File

@@ -1,12 +1,14 @@
package grants
import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"golang.org/x/crypto/ssh"
"strings"
)
type UpdateAction struct {
@@ -34,15 +36,23 @@ func (this *UpdateAction) RunGet(params struct {
// TODO 处理节点专用的认证
grant := grantResp.NodeGrant
var grant = grantResp.NodeGrant
// private key
var privateKey = grant.PrivateKey
const maskLength = 64
if len(privateKey) > maskLength+32 {
privateKey = privateKey[:maskLength] + strings.Repeat("*", len(privateKey)-maskLength)
}
this.Data["grant"] = maps.Map{
"id": grant.Id,
"name": grant.Name,
"method": grant.Method,
"methodName": grantutils.FindGrantMethodName(grant.Method, this.LangCode()),
"username": grant.Username,
"password": grant.Password,
"privateKey": grant.PrivateKey,
"password": strings.Repeat("*", len(grant.Password)),
"privateKey": privateKey,
"passphrase": grant.Passphrase,
"description": grant.Description,
"su": grant.Su,
@@ -85,15 +95,17 @@ func (this *UpdateAction) RunPost(params struct {
}
// 验证私钥
var err error
if len(params.Passphrase) > 0 {
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
} else {
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
}
if err != nil {
this.Fail("私钥验证失败,请检查格式:" + err.Error())
return
if !strings.HasSuffix(params.PrivateKey, "******") /* 非掩码 */ {
var err error
if len(params.Passphrase) > 0 {
_, err = ssh.ParsePrivateKeyWithPassphrase([]byte(params.PrivateKey), []byte(params.Passphrase))
} else {
_, err = ssh.ParsePrivateKey([]byte(params.PrivateKey))
}
if err != nil {
this.Fail("私钥验证失败,请检查格式:" + err.Error())
return
}
}
default:
this.Fail("请选择正确的认证方式")

View File

@@ -21,7 +21,7 @@ import (
"strings"
)
// CheckDiskPartitions 检查服务器盘空间
// CheckDiskPartitions 检查服务器盘空间
func CheckDiskPartitions(thresholdPercent float64) (path string, usage uint64, usagePercent float64, shouldWarning bool) {
partitions, err := disk.Partitions(false)
if err != nil {

View File

@@ -1,8 +1,8 @@
package clusters
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
@@ -107,7 +107,7 @@ func (this *ClusterAction) RunGet(params struct {
var isResolved = false
if isInstalled && cluster.DnsDomainId > 0 && len(cluster.DnsName) > 0 && len(node.IpAddr) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
if iputils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
@@ -145,7 +145,7 @@ func (this *ClusterAction) RunGet(params struct {
var isResolved = false
if isInstalled && len(defaultRoute) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
if iputils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{

View File

@@ -1,8 +1,8 @@
package domains
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
@@ -65,7 +65,7 @@ func (this *NodesPopupAction) RunGet(params struct {
var isResolved = false
if len(route.Name) > 0 && len(node.IpAddr) > 0 && len(cluster.DnsName) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
if iputils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{
@@ -99,7 +99,7 @@ func (this *NodesPopupAction) RunGet(params struct {
var isResolved = false
if len(defaultRoute) > 0 {
var recordType = "A"
if utils.IsIPv6(node.IpAddr) {
if iputils.IsIPv6(node.IpAddr) {
recordType = "AAAA"
}
checkResp, err := this.RPC().DNSDomainRPC().ExistDNSDomainRecord(this.AdminContext(), &pb.ExistDNSDomainRecordRequest{

View File

@@ -52,9 +52,13 @@ func (this *CreatePopupAction) RunPost(params struct {
Type string
// DNSPod
ParamId string
ParamToken string
ParamRegion string
ParamDNSPodId string
ParamDNSPodToken string
ParamDNSPodRegion string
ParamDNSPodAPIType string
ParamDNSPodAccessKeyId string
ParamDNSPodAccessKeySecret string
// AliDNS
ParamAliDNSAccessKeyId string
@@ -89,18 +93,31 @@ func (this *CreatePopupAction) RunPost(params struct {
Field("type", params.Type).
Require("请选择服务商厂家")
apiParams := maps.Map{}
var apiParams = maps.Map{}
switch params.Type {
case "dnspod":
params.Must.
Field("paramId", params.ParamId).
Require("请输入密钥ID").
Field("paramToken", params.ParamToken).
Require("请输入密钥Token")
apiParams["apiType"] = params.ParamDNSPodAPIType
switch params.ParamDNSPodAPIType {
case "tencentDNS":
params.Must.
Field("paramDNSPodAccessKeyId", params.ParamDNSPodAccessKeyId).
Require("请输入SecretId").
Field("paramDNSPodAccessKeySecret", params.ParamDNSPodAccessKeySecret).
Require("请输入SecretKey")
apiParams["accessKeyId"] = params.ParamDNSPodAccessKeyId
apiParams["accessKeySecret"] = params.ParamDNSPodAccessKeySecret
apiParams["region"] = params.ParamDNSPodRegion
default:
params.Must.
Field("paramId", params.ParamDNSPodId).
Require("请输入密钥ID").
Field("paramToken", params.ParamDNSPodToken).
Require("请输入密钥Token")
apiParams["id"] = params.ParamId
apiParams["token"] = params.ParamToken
apiParams["region"] = params.ParamRegion
apiParams["id"] = params.ParamDNSPodId
apiParams["token"] = params.ParamDNSPodToken
apiParams["region"] = params.ParamDNSPodRegion
}
case "alidns":
params.Must.
Field("paramAliDNSAccessKeyId", params.ParamAliDNSAccessKeyId).

View File

@@ -24,7 +24,10 @@ func (this *ProviderAction) RunGet(params struct {
this.Data["pageNo"] = params.Page
this.Data["filter"] = params.Filter
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{DnsProviderId: params.ProviderId})
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{
DnsProviderId: params.ProviderId,
MaskParams: true,
})
if err != nil {
this.ErrorPage(err)
return

View File

@@ -22,7 +22,10 @@ func (this *UpdatePopupAction) Init() {
func (this *UpdatePopupAction) RunGet(params struct {
ProviderId int64
}) {
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{DnsProviderId: params.ProviderId})
providerResp, err := this.RPC().DNSProviderRPC().FindEnabledDNSProvider(this.AdminContext(), &pb.FindEnabledDNSProviderRequest{
DnsProviderId: params.ProviderId,
MaskParams: true,
})
if err != nil {
this.ErrorPage(err)
return
@@ -79,9 +82,13 @@ func (this *UpdatePopupAction) RunPost(params struct {
Type string
// DNSPod
ParamId string
ParamToken string
ParamRegion string
ParamDNSPodId string
ParamDNSPodToken string
ParamDNSPodRegion string
ParamDNSPodAPIType string
ParamDNSPodAccessKeyId string
ParamDNSPodAccessKeySecret string
// AliDNS
ParamAliDNSAccessKeyId string
@@ -118,18 +125,31 @@ func (this *UpdatePopupAction) RunPost(params struct {
Field("type", params.Type).
Require("请选择服务商厂家")
apiParams := maps.Map{}
var apiParams = maps.Map{}
switch params.Type {
case "dnspod":
params.Must.
Field("paramId", params.ParamId).
Require("请输入密钥ID").
Field("paramToken", params.ParamToken).
Require("请输入密钥Token")
apiParams["apiType"] = params.ParamDNSPodAPIType
switch params.ParamDNSPodAPIType {
case "tencentDNS":
params.Must.
Field("paramDNSPodAccessKeyId", params.ParamDNSPodAccessKeyId).
Require("请输入SecretId").
Field("paramDNSPodAccessKeySecret", params.ParamDNSPodAccessKeySecret).
Require("请输入SecretKey")
apiParams["accessKeyId"] = params.ParamDNSPodAccessKeyId
apiParams["accessKeySecret"] = params.ParamDNSPodAccessKeySecret
apiParams["region"] = params.ParamDNSPodRegion
default:
params.Must.
Field("paramId", params.ParamDNSPodId).
Require("请输入密钥ID").
Field("paramToken", params.ParamDNSPodToken).
Require("请输入密钥Token")
apiParams["id"] = params.ParamId
apiParams["token"] = params.ParamToken
apiParams["region"] = params.ParamRegion
apiParams["id"] = params.ParamDNSPodId
apiParams["token"] = params.ParamDNSPodToken
apiParams["region"] = params.ParamDNSPodRegion
}
case "alidns":
params.Must.
Field("paramAliDNSAccessKeyId", params.ParamAliDNSAccessKeyId).

View File

@@ -18,8 +18,10 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"net"
@@ -121,6 +123,19 @@ func (this *IndexAction) RunGet(params struct {
// 删除Cookie
loginutils.UnsetCookie(this.Object())
// 检查单体实例是否已经被初始化
{
settingResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeStandaloneInstanceInitialized})
if err != nil {
this.ErrorPage(err)
return
}
if string(settingResp.ValueJSON) == "0" {
this.RedirectURL("/initPassword")
return
}
}
this.Show()
}
@@ -175,7 +190,7 @@ func (this *IndexAction) RunPost(params struct {
})
if err != nil {
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelError, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSystemError, err.Error()), this.RequestRemoteIP(), codes.AdminLogin_LogSystemError, []any{err.Error()})
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelError, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSystemError, err.Error()), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSystemError, []any{err.Error()})
if err != nil {
utils.PrintError(err)
}
@@ -185,7 +200,7 @@ func (this *IndexAction) RunPost(params struct {
}
if !resp.IsOk {
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelWarn, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogFailed, params.Username), this.RequestRemoteIP(), codes.AdminLogin_LogFailed, []any{params.Username})
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(0), oplogs.LevelWarn, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogFailed, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogFailed, []any{params.Username})
if err != nil {
utils.PrintError(err)
}
@@ -222,10 +237,13 @@ func (this *IndexAction) RunPost(params struct {
}
// 写入SESSION
params.Auth.StoreAdmin(adminId, params.Remember)
var localSid = rands.HexString(32)
this.Data["localSid"] = localSid
this.Data["ip"] = loginutils.RemoteIP(&this.ActionObject)
params.Auth.StoreAdmin(adminId, params.Remember, localSid)
// 记录日志
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), this.RequestRemoteIP(), codes.AdminLogin_LogSuccess, []any{params.Username})
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, langs.DefaultMessage(codes.AdminLogin_LogSuccess, params.Username), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogSuccess, []any{params.Username})
if err != nil {
utils.PrintError(err)
}
@@ -235,7 +253,7 @@ func (this *IndexAction) RunPost(params struct {
// 检查登录区域
func (this *IndexAction) checkRegion() bool {
var ip = this.RequestRemoteIP()
var ip = loginutils.RemoteIP(&this.ActionObject)
var result = iplibrary.LookupIP(ip)
if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{9, 10}, result.CountryId()) {
return false

View File

@@ -10,6 +10,7 @@ func init() {
Prefix("").
GetPost("/", new(IndexAction)).
GetPost("/index/otp", new(OtpAction)).
GetPost("/initPassword", new(InitPasswordAction)).
EndAll()
})
}

View File

@@ -0,0 +1,104 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package index
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/actions"
"net/http"
)
type InitPasswordAction struct {
actionutils.ParentAction
}
func (this *InitPasswordAction) Init() {
this.Nav("", "", "")
}
func (this *InitPasswordAction) RunGet(params struct{}) {
isNotInitialized, err := this.isNotInitialized()
if err != nil {
this.ErrorPage(err)
return
}
if !isNotInitialized {
this.RedirectURL("/")
return
}
this.Data["username"] = "admin"
this.Data["password"] = ""
this.Show()
}
func (this *InitPasswordAction) RunPost(params struct {
Username string
Password string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
isNotInitialized, err := this.isNotInitialized()
if err != nil {
this.ErrorPage(err)
return
}
if !isNotInitialized {
this.ResponseWriter.WriteHeader(http.StatusForbidden)
return
}
params.Must.
Field("username", params.Username).
Require("请输入登录用户名").
Match(`^[a-zA-Z0-9_]+$`, "用户名中只能包含英文、数字或下划线").
Field("password", params.Password).
Require("请输入密码")
// 查找ID
adminResp, err := this.RPC().AdminRPC().FindAdminWithUsername(this.AdminContext(), &pb.FindAdminWithUsernameRequest{Username: "admin" /** 固定的 **/})
if err != nil {
this.ErrorPage(err)
return
}
if adminResp.Admin == nil {
this.Fail("数据错误请将数据库中的edgeAdmins表中的用户名修改为admin后再试")
return
}
var adminId = adminResp.Admin.Id
// 修改密码
_, err = this.RPC().AdminRPC().UpdateAdminLogin(this.AdminContext(), &pb.UpdateAdminLoginRequest{
AdminId: adminId,
Username: params.Username,
Password: params.Password, // raw
})
if err != nil {
this.ErrorPage(err)
return
}
// 修改为初始化完成
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
Code: systemconfigs.SettingCodeStandaloneInstanceInitialized,
ValueJSON: []byte("1"),
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}
func (this *InitPasswordAction) isNotInitialized() (bool, error) {
settingResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeStandaloneInstanceInitialized})
if err != nil {
return false, err
}
return string(settingResp.ValueJSON) == "0", nil
}

View File

@@ -3,12 +3,15 @@
package loginutils
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/actions"
stringutil "github.com/iwind/TeaGo/utils/string"
"net"
"net/http"
"regexp"
"strings"
)
// CalculateClientFingerprint 计算客户端指纹
@@ -17,8 +20,30 @@ func CalculateClientFingerprint(action *actions.ActionObject) string {
}
// RemoteIP 获取客户端IP
// TODO 将来增加是否使用代理设置即从X-Real-IP中获取IP
func RemoteIP(action *actions.ActionObject) string {
securityConfig, _ := configloaders.LoadSecurityConfig()
if securityConfig != nil {
if len(securityConfig.ClientIPHeaderNames) > 0 {
var headerNames = regexp.MustCompile(`[,;\s]`).Split(securityConfig.ClientIPHeaderNames, -1)
for _, headerName := range headerNames {
headerName = http.CanonicalHeaderKey(strings.TrimSpace(headerName))
if len(headerName) == 0 {
continue
}
var ipValue = action.Request.Header.Get(headerName)
if net.ParseIP(ipValue) != nil {
return ipValue
}
}
if securityConfig.ClientIPHeaderOnly {
return ""
}
}
}
ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
return ip
}

View File

@@ -12,12 +12,14 @@ import (
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
stringutil "github.com/iwind/TeaGo/utils/string"
"github.com/xlzd/gotp"
"time"
@@ -131,7 +133,10 @@ func (this *OtpAction) RunPost(params struct {
}
// 写入SESSION
params.Auth.StoreAdmin(adminId, params.Remember)
var localSid = rands.HexString(32)
this.Data["localSid"] = localSid
this.Data["ip"] = loginutils.RemoteIP(&this.ActionObject)
params.Auth.StoreAdmin(adminId, params.Remember, localSid)
// 删除OTP SESSION
_, err = this.RPC().LoginSessionRPC().DeleteLoginSession(this.AdminContext(), &pb.DeleteLoginSessionRequest{Sid: sid})
@@ -146,7 +151,7 @@ func (this *OtpAction) RunPost(params struct {
this.ErrorPage(err)
return
}
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, this.Lang(codes.AdminLogin_LogOtpVerifiedSuccess), this.RequestRemoteIP(), codes.AdminLogin_LogOtpVerifiedSuccess, nil)
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, this.Lang(codes.AdminLogin_LogOtpVerifiedSuccess), loginutils.RemoteIP(&this.ActionObject), codes.AdminLogin_LogOtpVerifiedSuccess, nil)
if err != nil {
utils.PrintError(err)
}

View File

@@ -0,0 +1,14 @@
package login
import (
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Prefix("/login").
GetPost("/validate", new(ValidateAction)).
EndAll()
})
}

View File

@@ -0,0 +1,74 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package login
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/rands"
"net"
)
type ValidateAction struct {
actionutils.ParentAction
}
func (this *ValidateAction) Init() {
this.Nav("", "", "")
}
func (this *ValidateAction) RunGet(params struct {
From string
}) {
this.Data["from"] = params.From
this.Show()
}
func (this *ValidateAction) RunPost(params struct {
Must *actions.Must
LocalSid string
Ip string
}) {
var isOk bool
defer func() {
this.Data["isOk"] = isOk
if !isOk {
loginutils.UnsetCookie(&this.ActionObject)
this.Session().Delete()
}
this.Success()
}()
if len(params.LocalSid) == 0 || len(params.LocalSid) != 32 {
return
}
if len(params.Ip) == 0 {
return
}
if net.ParseIP(params.Ip) == nil {
return
}
if params.LocalSid == this.Session().GetString("@localSid") {
isOk = true
// renew ip and local sid
var newIP = loginutils.RemoteIP(&this.ActionObject)
var newLocalSid = rands.HexString(32)
this.Session().Write("@ip", newIP)
this.Session().Write("@localSid", newLocalSid)
this.Data["ip"] = newIP
this.Data["localSid"] = newLocalSid
return
}
}

View File

@@ -36,31 +36,43 @@ func (this *AddServerNamePopupAction) RunPost(params struct {
// 去除空格
serverName = regexp.MustCompile(`\s+`).ReplaceAllString(serverName, "")
// 处理URL
if regexp.MustCompile(`^(?i)(http|https|ftp)://`).MatchString(serverName) {
u, err := url.Parse(serverName)
if err == nil && len(u.Host) > 0 {
serverName = u.Host
// 是否包含了多个域名
var splitReg = regexp.MustCompile(`([,、|,;|])`)
if splitReg.MatchString(serverName) {
params.ServerNames = strings.Join(splitReg.Split(serverName, -1), "\n")
params.Mode = "multiple"
} else {
// 处理URL
if regexp.MustCompile(`^(?i)(http|https|ftp)://`).MatchString(serverName) {
u, err := url.Parse(serverName)
if err == nil && len(u.Host) > 0 {
serverName = u.Host
}
}
}
// 去除端口
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
host, _, err := net.SplitHostPort(serverName)
if err == nil && len(host) > 0 {
serverName = host
// 去除端口
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
host, _, err := net.SplitHostPort(serverName)
if err == nil && len(host) > 0 {
serverName = host
}
}
}
params.Must.
Field("serverName", serverName).
Require("请输入域名")
params.Must.
Field("serverName", serverName).
Require("请输入域名")
this.Data["serverName"] = maps.Map{
"name": serverName,
"type": "full",
this.Data["serverName"] = maps.Map{
"name": serverName,
"type": "full",
}
this.Success()
return
}
} else if params.Mode == "multiple" {
}
if params.Mode == "multiple" {
if len(params.ServerNames) == 0 {
this.FailField("serverNames", "请输入至少域名")
}
@@ -99,8 +111,6 @@ func (this *AddServerNamePopupAction) RunPost(params struct {
"type": "full",
"subNames": serverNames,
}
} else {
this.Fail("错误的mode参数")
}
this.Success()

View File

@@ -99,11 +99,24 @@ func (this *TaskAction) readTask(taskId int64) (ok bool) {
})
}
// 集群信息
var clusterMap = maps.Map{
"id": 0,
"name": "",
}
if key.NodeCluster != nil {
clusterMap = maps.Map{
"id": key.NodeCluster.Id,
"name": key.NodeCluster.Name,
}
}
keyMaps = append(keyMaps, maps.Map{
"key": key.Key,
"isDone": key.IsDone,
"isDoing": key.IsDoing,
"errors": errorMaps,
"cluster": clusterMap,
})
}

View File

@@ -41,6 +41,7 @@ func (this *CreatePopupAction) RunPost(params struct {
MaxSizeJSON []byte
FetchTimeoutJSON []byte
SyncCompressionCache bool
EnableMMAP bool
Description string
IsOn bool
@@ -97,6 +98,7 @@ func (this *CreatePopupAction) RunPost(params struct {
OpenFileCache: openFileCacheConfig,
EnableSendfile: params.FileEnableSendfile,
MinFreeSize: minFreeSize,
EnableMMAP: params.EnableMMAP,
}
case serverconfigs.CachePolicyStorageMemory:
options = &serverconfigs.HTTPMemoryCacheStorage{}

View File

@@ -40,13 +40,23 @@ func (this *UpdateAction) RunGet(params struct {
return
}
// fix min free size
if cachePolicy.Type == serverconfigs.CachePolicyStorageFile && cachePolicy.Options != nil {
_, ok := cachePolicy.Options["minFreeSize"]
if !ok {
cachePolicy.Options["minFreeSize"] = &shared.SizeCapacity{
Count: 0,
Unit: shared.SizeCapacityUnitGB,
// fix min free size
{
_, ok := cachePolicy.Options["minFreeSize"]
if !ok {
cachePolicy.Options["minFreeSize"] = &shared.SizeCapacity{
Count: 0,
Unit: shared.SizeCapacityUnitGB,
}
}
}
// fix enableMMAP
{
_, ok := cachePolicy.Options["enableMMAP"]
if !ok {
cachePolicy.Options["enableMMAP"] = true
}
}
}
@@ -77,6 +87,8 @@ func (this *UpdateAction) RunPost(params struct {
SyncCompressionCache bool
FetchTimeoutJSON []byte
EnableMMAP bool
Description string
IsOn bool
@@ -137,6 +149,7 @@ func (this *UpdateAction) RunPost(params struct {
OpenFileCache: openFileCacheConfig,
EnableSendfile: params.FileEnableSendfile,
MinFreeSize: minFreeSize,
EnableMMAP: params.EnableMMAP,
}
case serverconfigs.CachePolicyStorageMemory:
options = &serverconfigs.HTTPMemoryCacheStorage{}

View File

@@ -54,7 +54,7 @@ func (this *CreateSetPopupAction) RunGet(params struct {
}
// 所有可选的动作
actionMaps := []maps.Map{}
var actionMaps = []maps.Map{}
for _, action := range firewallconfigs.AllActions {
actionMaps = append(actionMaps, maps.Map{
"name": action.Name,
@@ -64,6 +64,9 @@ func (this *CreateSetPopupAction) RunGet(params struct {
}
this.Data["actions"] = actionMaps
// 是否为全局
this.Data["isGlobalPolicy"] = firewallPolicy.ServerId == 0
this.Show()
}

View File

@@ -31,8 +31,8 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
var count = countResp.Count
var page = this.NewPage(count)
listResp, err := this.RPC().HTTPFirewallPolicyRPC().ListEnabledHTTPFirewallPolicies(this.AdminContext(), &pb.ListEnabledHTTPFirewallPoliciesRequest{
NodeClusterId: params.ClusterId,
@@ -44,10 +44,10 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
policyMaps := []maps.Map{}
var policyMaps = []maps.Map{}
for _, policy := range listResp.HttpFirewallPolicies {
countInbound := 0
countOutbound := 0
var countInbound = 0
var countOutbound = 0
if len(policy.InboundJSON) > 0 {
inboundConfig := &firewallconfigs.HTTPFirewallInboundConfig{}
err = json.Unmarshal(policy.InboundJSON, inboundConfig)
@@ -72,7 +72,7 @@ func (this *IndexAction) RunGet(params struct {
this.ErrorPage(err)
return
}
countClusters := countClustersResp.Count
var countClusters = countClustersResp.Count
// mode
if len(policy.Mode) == 0 {

View File

@@ -99,6 +99,7 @@ func (this *ListsAction) RunGet(params struct {
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"value": item.Value,
"ipFrom": item.IpFrom,
"ipTo": item.IpTo,
"createdTime": timeutil.FormatTime("Y-m-d", item.CreatedAt),

View File

@@ -57,6 +57,7 @@ func (this *TestAction) RunPost(params struct {
if resp.IpItem != nil {
resultMap["item"] = maps.Map{
"id": resp.IpItem.Id,
"value": resp.IpItem.Value,
"ipFrom": resp.IpItem.IpFrom,
"ipTo": resp.IpItem.IpTo,
"reason": resp.IpItem.Reason,

View File

@@ -1,6 +1,7 @@
package ipadmin
import ( "github.com/TeaOSLab/EdgeAdmin/internal/utils"
import (
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
@@ -32,6 +33,7 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
this.Data["item"] = maps.Map{
"id": item.Id,
"value": item.Value,
"ipFrom": item.IpFrom,
"ipTo": item.IpTo,
"expiredAt": item.ExpiredAt,
@@ -49,8 +51,7 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
FirewallPolicyId int64
ItemId int64
IpFrom string
IpTo string
Value string
ExpiredAt int64
Reason string
Type string
@@ -62,47 +63,25 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
// 日志
defer this.CreateLogInfo(codes.WAF_LogUpdateIPFromWAFPolicy, params.FirewallPolicyId, params.ItemId)
// TODO 校验ItemId所属用户
switch params.Type {
case "ipv4":
case "ip":
// 校验IP格式
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入开始IP")
Field("value", params.Value).
Require("请输入IP或IP段")
// 校验IP格式ipFrom/ipTo
var ipFromLong uint64
if !utils.IsIPv4(params.IpFrom) {
this.Fail("请输入正确的开始IP")
}
ipFromLong = utils.IP2Long(params.IpFrom)
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
this.Fail("请输入正确的结束IP")
}
ipToLong = utils.IP2Long(params.IpTo)
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
}
case "ipv6":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入IP")
// 校验IP格式ipFrom
if !utils.IsIPv6(params.IpFrom) {
this.Fail("请输入正确的IPv6地址")
_, _, _, ok := utils.ParseIPValue(params.Value)
if !ok {
this.FailField("value", "请输入正确的IP格式")
return
}
case "all":
params.IpFrom = "0.0.0.0"
params.Value = "0.0.0.0"
}
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
IpItemId: params.ItemId,
IpFrom: params.IpFrom,
IpTo: params.IpTo,
Value: params.Value,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,

View File

@@ -95,7 +95,9 @@ func (this *PolicyAction) RunGet(params struct {
"modeInfo": firewallconfigs.FindFirewallMode(firewallPolicy.Mode),
"groups": internalGroups,
"blockOptions": firewallPolicy.BlockOptions,
"pageOptions": firewallPolicy.PageOptions,
"captchaOptions": firewallPolicy.CaptchaOptions,
"jsCookieOptions": firewallPolicy.JSCookieOptions,
"useLocalFirewall": firewallPolicy.UseLocalFirewall,
"synFlood": firewallPolicy.SYNFlood,
"log": firewallPolicy.Log,

View File

@@ -10,7 +10,6 @@ import (
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http"
)
type UpdateAction struct {
@@ -34,13 +33,19 @@ func (this *UpdateAction) RunGet(params struct {
return
}
// block options
if firewallPolicy.BlockOptions == nil {
firewallPolicy.BlockOptions = &firewallconfigs.HTTPFirewallBlockAction{
StatusCode: http.StatusForbidden,
Body: "Blocked By WAF",
URL: "",
Timeout: 60,
}
firewallPolicy.BlockOptions = firewallconfigs.NewHTTPFirewallBlockAction()
}
// page options
if firewallPolicy.PageOptions == nil {
firewallPolicy.PageOptions = firewallconfigs.NewHTTPFirewallPageAction()
}
// jscookie options
if firewallPolicy.JSCookieOptions == nil {
firewallPolicy.JSCookieOptions = firewallconfigs.NewHTTPFirewallJavascriptCookieAction()
}
// mode
@@ -71,7 +76,9 @@ func (this *UpdateAction) RunGet(params struct {
"isOn": firewallPolicy.IsOn,
"mode": firewallPolicy.Mode,
"blockOptions": firewallPolicy.BlockOptions,
"pageOptions": firewallPolicy.PageOptions,
"captchaOptions": firewallPolicy.CaptchaOptions,
"jsCookieOptions": firewallPolicy.JSCookieOptions,
"useLocalFirewall": firewallPolicy.UseLocalFirewall,
"synFloodConfig": firewallPolicy.SYNFlood,
"log": firewallPolicy.Log,
@@ -103,20 +110,22 @@ func (this *UpdateAction) RunGet(params struct {
}
func (this *UpdateAction) RunPost(params struct {
FirewallPolicyId int64
Name string
GroupCodes []string
BlockOptionsJSON []byte
CaptchaOptionsJSON []byte
Description string
IsOn bool
Mode string
UseLocalFirewall bool
SynFloodJSON []byte
LogJSON []byte
MaxRequestBodySize int64
DenyCountryHTML string
DenyProvinceHTML string
FirewallPolicyId int64
Name string
GroupCodes []string
BlockOptionsJSON []byte
PageOptionsJSON []byte
CaptchaOptionsJSON []byte
JsCookieOptionsJSON []byte
Description string
IsOn bool
Mode string
UseLocalFirewall bool
SynFloodJSON []byte
LogJSON []byte
MaxRequestBodySize int64
DenyCountryHTML string
DenyProvinceHTML string
Must *actions.Must
}) {
@@ -128,17 +137,58 @@ func (this *UpdateAction) RunPost(params struct {
Require("请输入策略名称")
// 校验拦截选项JSON
var blockOptions = &firewallconfigs.HTTPFirewallBlockAction{}
var blockOptions = firewallconfigs.NewHTTPFirewallBlockAction()
err := json.Unmarshal(params.BlockOptionsJSON, blockOptions)
if err != nil {
this.Fail("拦截动作参数校验失败:" + err.Error())
return
}
// 校验显示页面选项JSON
var pageOptions = firewallconfigs.NewHTTPFirewallPageAction()
err = json.Unmarshal(params.PageOptionsJSON, pageOptions)
if err != nil {
this.Fail("校验显示页面动作配置失败:" + err.Error())
return
}
if pageOptions.Status < 100 && pageOptions.Status > 999 {
this.Fail("显示页面动作的状态码配置错误:" + types.String(pageOptions.Status))
return
}
// 校验验证码选项JSON
var captchaOptions = &firewallconfigs.HTTPFirewallCaptchaAction{}
var captchaOptions = firewallconfigs.NewHTTPFirewallCaptchaAction()
err = json.Unmarshal(params.CaptchaOptionsJSON, captchaOptions)
if err != nil {
this.Fail("验证码动作参数校验失败:" + err.Error())
return
}
// 检查极验配置
if captchaOptions.CaptchaType == firewallconfigs.CaptchaTypeGeeTest || captchaOptions.GeeTestConfig.IsOn {
if captchaOptions.CaptchaType == firewallconfigs.CaptchaTypeGeeTest && !captchaOptions.GeeTestConfig.IsOn {
this.Fail("人机识别动作配置的默认验证方式为极验-行为验,所以需要选择允许用户使用极验")
return
}
if len(captchaOptions.GeeTestConfig.CaptchaId) == 0 {
this.FailField("geetestCaptchaId", "请输入极验-验证ID")
return
}
if len(captchaOptions.GeeTestConfig.CaptchaKey) == 0 {
this.FailField("geetestCaptchaKey", "请输入极验-验证Key")
return
}
}
// 校验JSCookie选项JSON
var jsCookieOptions = firewallconfigs.NewHTTPFirewallJavascriptCookieAction()
if len(params.JsCookieOptionsJSON) > 0 {
err = json.Unmarshal(params.JsCookieOptionsJSON, jsCookieOptions)
if err != nil {
this.Fail("JSCookie动作参数校验失败" + err.Error())
return
}
}
// 最大内容尺寸
@@ -153,7 +203,9 @@ func (this *UpdateAction) RunPost(params struct {
Description: params.Description,
FirewallGroupCodes: params.GroupCodes,
BlockOptionsJSON: params.BlockOptionsJSON,
PageOptionsJSON: params.PageOptionsJSON,
CaptchaOptionsJSON: params.CaptchaOptionsJSON,
JsCookieOptionsJSON: params.JsCookieOptionsJSON,
Mode: params.Mode,
UseLocalFirewall: params.UseLocalFirewall,
SynFloodJSON: params.SynFloodJSON,

View File

@@ -11,7 +11,6 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"strings"
@@ -101,7 +100,6 @@ func (this *CreateAction) RunPost(params struct {
var httpsConfig *serverconfigs.HTTPSProtocolConfig = nil
var tcpConfig *serverconfigs.TCPProtocolConfig = nil
var tlsConfig *serverconfigs.TLSProtocolConfig = nil
var unixConfig *serverconfigs.UnixProtocolConfig = nil
var udpConfig *serverconfigs.UDPProtocolConfig = nil
var webId int64 = 0
@@ -429,14 +427,6 @@ func (this *CreateAction) RunPost(params struct {
}
req.TlsJSON = data
}
if unixConfig != nil {
data, err := json.Marshal(unixConfig)
if err != nil {
this.ErrorPage(err)
return
}
req.UnixJSON = data
}
if udpConfig != nil {
data, err := json.Marshal(udpConfig)
if err != nil {
@@ -454,15 +444,16 @@ func (this *CreateAction) RunPost(params struct {
// 开启访问日志和Websocket
if params.ServerType == serverconfigs.ServerTypeHTTPProxy {
webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), serverId)
if err != nil {
logs.Error(err)
} else {
// 访问日志
if params.AccessLogIsOn {
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.AdminContext(), &pb.UpdateHTTPWebAccessLogRequest{
HttpWebId: webConfig.Id,
AccessLogJSON: []byte(`{
webConfig, findErr := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), serverId)
if findErr != nil {
this.ErrorPage(findErr)
return
}
// 访问日志
if params.AccessLogIsOn {
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebAccessLog(this.AdminContext(), &pb.UpdateHTTPWebAccessLogRequest{
HttpWebId: webConfig.Id,
AccessLogJSON: []byte(`{
"isPrior": false,
"isOn": true,
"fields": [1, 2, 6, 7],
@@ -477,90 +468,94 @@ func (this *CreateAction) RunPost(params struct {
"firewallOnly": false
}`),
})
if err != nil {
logs.Error(err)
}
})
if err != nil {
this.ErrorPage(err)
return
}
}
// websocket
if params.WebsocketIsOn {
createWebSocketResp, err := this.RPC().HTTPWebsocketRPC().CreateHTTPWebsocket(this.AdminContext(), &pb.CreateHTTPWebsocketRequest{
HandshakeTimeoutJSON: []byte(`{
// websocket
if params.WebsocketIsOn {
createWebSocketResp, err := this.RPC().HTTPWebsocketRPC().CreateHTTPWebsocket(this.AdminContext(), &pb.CreateHTTPWebsocketRequest{
HandshakeTimeoutJSON: []byte(`{
"count": 30,
"unit": "second"
}`),
AllowAllOrigins: true,
AllowedOrigins: nil,
RequestSameOrigin: true,
RequestOrigin: "",
})
if err != nil {
logs.Error(err)
} else {
websocketId := createWebSocketResp.WebsocketId
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.AdminContext(), &pb.UpdateHTTPWebWebsocketRequest{
HttpWebId: webConfig.Id,
WebsocketJSON: []byte(` {
AllowAllOrigins: true,
AllowedOrigins: nil,
RequestSameOrigin: true,
RequestOrigin: "",
})
if err != nil {
this.ErrorPage(err)
return
}
websocketId := createWebSocketResp.WebsocketId
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebWebsocket(this.AdminContext(), &pb.UpdateHTTPWebWebsocketRequest{
HttpWebId: webConfig.Id,
WebsocketJSON: []byte(`{
"isPrior": false,
"isOn": true,
"websocketId": ` + types.String(websocketId) + `
}`),
})
if err != nil {
logs.Error(err)
}
}
})
if err != nil {
this.ErrorPage(err)
return
}
}
// cache
if params.CacheIsOn {
var cacheConfig = &serverconfigs.HTTPCacheConfig{
IsPrior: false,
IsOn: true,
AddStatusHeader: true,
PurgeIsOn: false,
PurgeKey: "",
CacheRefs: []*serverconfigs.HTTPCacheRef{},
}
cacheConfigJSON, err := json.Marshal(cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.AdminContext(), &pb.UpdateHTTPWebCacheRequest{
HttpWebId: webConfig.Id,
CacheJSON: cacheConfigJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// cache
if params.CacheIsOn {
var cacheConfig = &serverconfigs.HTTPCacheConfig{
IsPrior: false,
IsOn: true,
AddStatusHeader: true,
PurgeIsOn: false,
PurgeKey: "",
CacheRefs: []*serverconfigs.HTTPCacheRef{},
}
// waf
if params.WafIsOn {
var firewallRef = &firewallconfigs.HTTPFirewallRef{
IsPrior: false,
IsOn: true,
FirewallPolicyId: 0,
}
firewallRefJSON, err := json.Marshal(firewallRef)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.AdminContext(), &pb.UpdateHTTPWebFirewallRequest{
HttpWebId: webConfig.Id,
FirewallJSON: firewallRefJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
cacheConfigJSON, err := json.Marshal(cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebCache(this.AdminContext(), &pb.UpdateHTTPWebCacheRequest{
HttpWebId: webConfig.Id,
CacheJSON: cacheConfigJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
// remoteAddr
// waf
if params.WafIsOn {
var firewallRef = &firewallconfigs.HTTPFirewallRef{
IsPrior: false,
IsOn: true,
FirewallPolicyId: 0,
}
firewallRefJSON, err := json.Marshal(firewallRef)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebFirewall(this.AdminContext(), &pb.UpdateHTTPWebFirewallRequest{
HttpWebId: webConfig.Id,
FirewallJSON: firewallRefJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
// remoteAddr
{
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
IsOn: true,
Value: "${rawRemoteAddr}",
@@ -583,26 +578,26 @@ func (this *CreateAction) RunPost(params struct {
this.ErrorPage(err)
return
}
}
// 统计
if params.StatIsOn {
var statConfig = &serverconfigs.HTTPStatRef{
IsPrior: false,
IsOn: true,
}
statJSON, err := json.Marshal(statConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.AdminContext(), &pb.UpdateHTTPWebStatRequest{
HttpWebId: webConfig.Id,
StatJSON: statJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
// 统计
if params.StatIsOn {
var statConfig = &serverconfigs.HTTPStatRef{
IsPrior: false,
IsOn: true,
}
statJSON, err := json.Marshal(statConfig)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().HTTPWebRPC().UpdateHTTPWebStat(this.AdminContext(), &pb.UpdateHTTPWebStatRequest{
HttpWebId: webConfig.Id,
StatJSON: statJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package servers
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// DeleteServersAction 删除一组网站
type DeleteServersAction struct {
actionutils.ParentAction
}
func (this *DeleteServersAction) RunPost(params struct {
ServerIds []int64
}) {
defer this.CreateLogInfo(codes.Server_LogDeleteServers)
_, err := this.RPC().ServerRPC().DeleteServers(this.AdminContext(), &pb.DeleteServersRequest{ServerIds: params.ServerIds})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -108,14 +108,6 @@ func (this *IndexAction) RunGet(params struct {
})
}
}
if config.Unix != nil && config.Unix.IsOn {
for _, listen := range config.Unix.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.Host,
})
}
}
if config.UDP != nil && config.UDP.IsOn {
for _, listen := range config.UDP.Listen {
portMaps = append(portMaps, maps.Map{

View File

@@ -53,12 +53,16 @@ func (this *IndexAction) RunPost(params struct {
defer this.CreateLogInfo(codes.ServerCache_LogUpdateCacheSettings, params.WebId)
// 校验配置
cacheConfig := &serverconfigs.HTTPCacheConfig{}
var cacheConfig = &serverconfigs.HTTPCacheConfig{}
err := json.Unmarshal(params.CacheJSON, cacheConfig)
if err != nil {
this.ErrorPage(err)
return
}
// 分组不支持主域名
cacheConfig.Key = nil
err = cacheConfig.Init()
if err != nil {
this.Fail("检查配置失败:" + err.Error())

View File

@@ -109,6 +109,7 @@ func (this *AllowListAction) RunGet(params struct {
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"value": item.Value,
"ipFrom": item.IpFrom,
"ipTo": item.IpTo,
"createdTime": timeutil.FormatTime("Y-m-d", item.CreatedAt),

View File

@@ -109,6 +109,7 @@ func (this *DenyListAction) RunGet(params struct {
itemMaps = append(itemMaps, maps.Map{
"id": item.Id,
"value": item.Value,
"ipFrom": item.IpFrom,
"ipTo": item.IpTo,
"createdTime": timeutil.FormatTime("Y-m-d", item.CreatedAt),

View File

@@ -70,6 +70,7 @@ func (this *TestAction) RunPost(params struct {
if resp.IpItem != nil {
resultMap["item"] = maps.Map{
"id": resp.IpItem.Id,
"value": resp.IpItem.Value,
"ipFrom": resp.IpItem.IpFrom,
"ipTo": resp.IpItem.IpTo,
"reason": resp.IpItem.Reason,

View File

@@ -33,6 +33,7 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
this.Data["item"] = maps.Map{
"id": item.Id,
"value": item.Value,
"ipFrom": item.IpFrom,
"ipTo": item.IpTo,
"expiredAt": item.ExpiredAt,
@@ -49,8 +50,7 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
func (this *UpdateIPPopupAction) RunPost(params struct {
ItemId int64
IpFrom string
IpTo string
Value string
ExpiredAt int64
Reason string
Type string
@@ -62,47 +62,25 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
// 日志
defer this.CreateLogInfo(codes.IPItem_LogUpdateIPItem, params.ItemId)
// TODO 校验ItemId所属用户
switch params.Type {
case "ipv4":
case "ip":
// 校验IP格式
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入开始IP")
Field("value", params.Value).
Require("请输入IP或IP段")
// 校验IP格式ipFrom/ipTo
var ipFromLong uint64
if !utils.IsIPv4(params.IpFrom) {
this.Fail("请输入正确的开始IP")
}
ipFromLong = utils.IP2Long(params.IpFrom)
var ipToLong uint64
if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
this.Fail("请输入正确的结束IP")
}
ipToLong = utils.IP2Long(params.IpTo)
if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
}
case "ipv6":
params.Must.
Field("ipFrom", params.IpFrom).
Require("请输入IP")
// 校验IP格式ipFrom
if !utils.IsIPv6(params.IpFrom) {
this.Fail("请输入正确的IPv6地址")
_, _, _, ok := utils.ParseIPValue(params.Value)
if !ok {
this.FailField("value", "请输入正确的IP格式")
return
}
case "all":
params.IpFrom = "0.0.0.0"
params.Value = "0.0.0.0"
}
_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
IpItemId: params.ItemId,
IpFrom: params.IpFrom,
IpTo: params.IpTo,
Value: params.Value,
ExpiredAt: params.ExpiredAt,
Reason: params.Reason,
Type: params.Type,

View File

@@ -155,14 +155,6 @@ func (this *IndexAction) RunGet(params struct {
})
}
}
if config.Unix != nil && config.Unix.IsOn {
for _, listen := range config.Unix.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.Host,
})
}
}
if config.UDP != nil && config.UDP.IsOn {
for _, listen := range config.UDP.Listen {
portMaps = append(portMaps, maps.Map{

View File

@@ -19,6 +19,7 @@ func init() {
GetPost("/create", new(CreateAction)).
GetPost("/update", new(UpdateAction)).
Post("/nearby", new(NearbyAction)).
Post("/deleteServers", new(DeleteServersAction)).
//
GetPost("/addPortPopup", new(AddPortPopupAction)).

Some files were not shown because too many files have changed in this diff Show More