Compare commits
170 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f271aabc8 | ||
|
|
ced0a4d7d8 | ||
|
|
567d457e68 | ||
|
|
1c3d2c28ba | ||
|
|
fd20efde48 | ||
|
|
f0fd9a5051 | ||
|
|
8417fdffb0 | ||
|
|
14a00affa4 | ||
|
|
0a1b1df300 | ||
|
|
3dfed03f73 | ||
|
|
bcc44c71c4 | ||
|
|
ce9c4e847a | ||
|
|
a3c87f4a9b | ||
|
|
42b463e317 | ||
|
|
0a1ae23970 | ||
|
|
33e21284c4 | ||
|
|
550bc01b4d | ||
|
|
0f4fc83848 | ||
|
|
5cecf1ff6b | ||
|
|
6e419416b9 | ||
|
|
e46e4668a2 | ||
|
|
7f360e7b3c | ||
|
|
9e39f29044 | ||
|
|
858ad57fdb | ||
|
|
897ae3cf94 | ||
|
|
7169046f19 | ||
|
|
762347b476 | ||
|
|
2e9f937736 | ||
|
|
5690509887 | ||
|
|
8ed04c780d | ||
|
|
377d5da59c | ||
|
|
2fb6b487c4 | ||
|
|
6aac65e1e7 | ||
|
|
32c69668a4 | ||
|
|
5049af2ce0 | ||
|
|
4fd4d6632f | ||
|
|
1f183c80ff | ||
|
|
f1fe10d312 | ||
|
|
f5a888f655 | ||
|
|
9dc5cf4ba2 | ||
|
|
5b172b7e74 | ||
|
|
fad2178670 | ||
|
|
c886e2ec96 | ||
|
|
0e0e212fab | ||
|
|
51b9e6d740 | ||
|
|
f9080246c8 | ||
|
|
05c8ef5bdd | ||
|
|
15d3d333fa | ||
|
|
c1932eb469 | ||
|
|
09b4ed5bb7 | ||
|
|
e37f42d300 | ||
|
|
d3ff29342a | ||
|
|
490b1a1aff | ||
|
|
24606b6a92 | ||
|
|
327306ae2e | ||
|
|
2c9d1cf420 | ||
|
|
2be17bbdf3 | ||
|
|
f992b0e38c | ||
|
|
bd6ee24eb1 | ||
|
|
366a64530e | ||
|
|
45a175957e | ||
|
|
d390b609a4 | ||
|
|
4c9488091e | ||
|
|
45987efeea | ||
|
|
7c8e4f2585 | ||
|
|
f9f4facbb8 | ||
|
|
ad5a3f55af | ||
|
|
09f5831e43 | ||
|
|
56a7b66650 | ||
|
|
111d332609 | ||
|
|
5405b5659e | ||
|
|
b7aa1c7dc6 | ||
|
|
6ad19f8688 | ||
|
|
8cde19b479 | ||
|
|
21d8b0ecb2 | ||
|
|
e93451de32 | ||
|
|
14716a090b | ||
|
|
b3afe35fe3 | ||
|
|
20361064a2 | ||
|
|
c7dc059d0c | ||
|
|
99d71f815b | ||
|
|
3e5d2eeac2 | ||
|
|
dd296eb982 | ||
|
|
24c3dfab38 | ||
|
|
61f2f2ce9c | ||
|
|
d7546aa1e5 | ||
|
|
d280cc5ae2 | ||
|
|
eec026e5c7 | ||
|
|
779f735117 | ||
|
|
e120b28880 | ||
|
|
05dd6386dd | ||
|
|
523abc1ecc | ||
|
|
e7f0dc33b8 | ||
|
|
7e917e2ac9 | ||
|
|
538c9e0008 | ||
|
|
e4d404388b | ||
|
|
4a6056d081 | ||
|
|
ea86defc8b | ||
|
|
4e53b91d8b | ||
|
|
67275f6906 | ||
|
|
77761d86fe | ||
|
|
0871d652fe | ||
|
|
d4220cffe2 | ||
|
|
ccd940cc54 | ||
|
|
5ffaddc1c6 | ||
|
|
d73d13a22c | ||
|
|
d3026154a1 | ||
|
|
a415b027e8 | ||
|
|
2d994d0e53 | ||
|
|
f22673226d | ||
|
|
e4d59c7dd6 | ||
|
|
61f5843da9 | ||
|
|
086a484f39 | ||
|
|
f40f12c782 | ||
|
|
3f95bef0f4 | ||
|
|
fd391fa5b1 | ||
|
|
c709c52211 | ||
|
|
57554a6ca1 | ||
|
|
437a4b24af | ||
|
|
94f17bfe39 | ||
|
|
ff91bd8f3c | ||
|
|
0bf5de39c9 | ||
|
|
9783578c33 | ||
|
|
061104d31f | ||
|
|
9aacbc14f3 | ||
|
|
91e10ec19a | ||
|
|
200dfb9ba2 | ||
|
|
5e2e75b6e2 | ||
|
|
2b8cadb7a4 | ||
|
|
d99ef04cf3 | ||
|
|
d5a8819db5 | ||
|
|
e98fa14b6f | ||
|
|
4f45333677 | ||
|
|
9fc53e2732 | ||
|
|
a3393f1eb3 | ||
|
|
db1ef55b30 | ||
|
|
e5d2670577 | ||
|
|
fc45502a6a | ||
|
|
dee5019888 | ||
|
|
51e750845a | ||
|
|
d0a1620f57 | ||
|
|
151b64da27 | ||
|
|
90eaac3cd1 | ||
|
|
13401133a4 | ||
|
|
c262369fd7 | ||
|
|
b8bd25968d | ||
|
|
32c6821d36 | ||
|
|
498d116245 | ||
|
|
9d6742fab6 | ||
|
|
a79ff303d5 | ||
|
|
138e075946 | ||
|
|
567dbcd6ec | ||
|
|
26cab99562 | ||
|
|
ded344cd7f | ||
|
|
49b3c8d5d4 | ||
|
|
f1699505da | ||
|
|
1f998ce2ea | ||
|
|
e5f932adff | ||
|
|
37f72ba51f | ||
|
|
1a8c175c48 | ||
|
|
18ab2f1010 | ||
|
|
3bde97627d | ||
|
|
478a22015d | ||
|
|
0768508060 | ||
|
|
a8e2b1b248 | ||
|
|
d8183bd8d8 | ||
|
|
13f6265498 | ||
|
|
60e155b230 | ||
|
|
a57188692f | ||
|
|
f07ed2b30d |
30
.github/ISSUE_TEMPLATE/1feature-request.yaml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: 功能建议
|
||||
# Feature request
|
||||
description: 新功能或现有能力的优化建议
|
||||
title: "[建议] "
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
提示:创建前请搜索一下是否有重复问题。一个 issue 尽量只描述一个问题。简洁、准确的描述有助于集中大家的意见,推进问题尽快解决
|
||||
# Please check for duplicate issue first.
|
||||
# 尽量描述需求的背景、原始问题,避免出现 X-Y 问题,参考: https://coolshell.cn/articles/10804.html
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: 背景与遇到的问题
|
||||
# Background and the problem that frustrates you
|
||||
placeholder: |
|
||||
例如:我的业务有xxx特性,当我在使用xxx功能的时候,会遇到xxx情况...
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: 建议的解决方案
|
||||
# Describe the solution you'd like
|
||||
placeholder: |
|
||||
例如:建议增加xxx功能;将xxx改为xxx...
|
||||
validations:
|
||||
required: false
|
||||
31
.github/ISSUE_TEMPLATE/2bug-report.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Bug
|
||||
description: 明确的软件故障或缺陷
|
||||
# Create a report to help us improve
|
||||
title: "[Bug] "
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
提示:提问前请先搜索一下是否存在重复问题
|
||||
# Please check for duplicate issue first.
|
||||
- type: textarea
|
||||
id: Description
|
||||
attributes:
|
||||
label: 问题描述
|
||||
# Describe the bug
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: Reproduce
|
||||
attributes:
|
||||
label: 复现方法
|
||||
# To Reproduce
|
||||
placeholder: |
|
||||
1. ...
|
||||
2. ...
|
||||
- type: textarea
|
||||
id: Expected
|
||||
attributes:
|
||||
label: 期望的结果
|
||||
# Expected behavior. Descript what you expected to happen.
|
||||
14
.github/ISSUE_TEMPLATE/3other.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: 其他问题与反馈
|
||||
description: 文档、部署失败等其他问题。
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
提示:创建前请先搜索一下是否存在重复问题
|
||||
# Please check for duplicate issue first.
|
||||
# 尽量描述需求的背景、原始问题,避免出现 X-Y 问题,参考: https://coolshell.cn/articles/10804.html
|
||||
# 提問的智慧: https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md
|
||||
- type: textarea
|
||||
id: content
|
||||
attributes:
|
||||
label: 反馈内容
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: Ture
|
||||
contact_links:
|
||||
- name: 绕过反馈
|
||||
url: https://stack.chaitin.com/security-challenge/safeline/index
|
||||
about: Waf 绕过可在 CT Stack 安全挑战赛提交细节
|
||||
9
.gitmodules
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[submodule "blazehttp"]
|
||||
path = blazehttp
|
||||
url = https://github.com/chaitin/blazehttp
|
||||
[submodule "lua-resty-t1k"]
|
||||
path = lua-resty-t1k
|
||||
url = https://github.com/chaitin/lua-resty-t1k
|
||||
[submodule "plugins"]
|
||||
path = plugins
|
||||
url = https://github.com/chaitin/safeline-open-platform
|
||||
212
CHANGELOG.md
@@ -1,9 +1,217 @@
|
||||
SAFELINE-CE CHANGELOG
|
||||
===
|
||||
|
||||
## [Unreleased]
|
||||
### [3.2.0] - 2023-08-31
|
||||
|
||||
- 告警
|
||||
#### 新增
|
||||
|
||||
- 证书管理支持上传 PKCS1、PKCS8、ECC 私钥格式的证书([#257](https://github.com/chaitin/SafeLine/issues/257))
|
||||
- 站点详情新增近 30 天请求数,支持批量删除
|
||||
|
||||
#### 优化
|
||||
|
||||
- 优化站点详情页面,提高加载速度、优化界面样式和逻辑
|
||||
- 限频的生效阈值由 “超过 xx 次” 改为 “达到 xx 次”([#245](https://github.com/chaitin/SafeLine/issues/245))
|
||||
- 修复频率限制所拦截的请求,没有被计入站点列表的 “今日访问/拦截量” 的问题
|
||||
- 修复证书管理的证书有时候无法正常删除的问题([#268](https://github.com/chaitin/SafeLine/issues/268))
|
||||
- 修复证书管理中,有时候一个证书内会解析出几个重复的域名的问题
|
||||
- 去掉对 docker 的 seccomp 特性的依赖([#255](https://github.com/chaitin/SafeLine/issues/255))
|
||||
- 优化大量界面样式、交互和操作提示细节
|
||||
|
||||
## [3.1.1] - 2023-08-28
|
||||
|
||||
### 优化
|
||||
|
||||
- 修复创建站点时直接上传 SSL 证书显示创建失败的问题
|
||||
|
||||
## [3.1.0] - 2023-08-25
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增证书管理。自动判断域名与过期状态,配置站点时可以直接选择([#111](https://github.com/chaitin/SafeLine/issues/111)),还可以修改管理后台的证书([#201](https://github.com/chaitin/SafeLine/issues/201))
|
||||
- 新增系统信息
|
||||
|
||||
### 优化
|
||||
|
||||
- 修复站点详情全局只支持 250 个资源的问题。现在是每个站点各支持 250 个
|
||||
- 修复站点详情中,路径太长样式会错位的问题
|
||||
- 修复站点从维护切换成其他状态时,微信会缓存维护页面的问题([#221](https://github.com/chaitin/SafeLine/issues/221))
|
||||
- 修复手机和平板上不能输入动态口令的问题([#234](https://github.com/chaitin/SafeLine/issues/234))
|
||||
- 优化导航栏和若干 UI 交互细节
|
||||
|
||||
## [3.0.1] - 2023-08-18
|
||||
|
||||
- 更新底层语义分析引擎,加强基础防护能力
|
||||
- 修复站点详情中,不同域名的资源会重复记录问题
|
||||
- 修复攻击事件有小概率时间排序错误的问题
|
||||
- 修复关闭再打开频率限制时,配置不会重置到默认值的问题
|
||||
- 修复首次创建站点且配置 SSL 端口的情况下提示“端口被占用”的问题
|
||||
|
||||
## [3.0.0] - 2023-08-18
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增站点详情,能自动从流量中记录网站资源,一览资源的存活、访问情况
|
||||
- PS. 考虑机器资源消耗问题,当前版本每个站点下最多记录 250 个资源
|
||||
- 站点支持输入多个域名、端口([#162](https://github.com/chaitin/safeline/issues/162))
|
||||
- 通用配置中新增 “站点通用配置”,支持一键开启:
|
||||
- 强制 HTTPS([#67](https://github.com/chaitin/safeline/issues/67))
|
||||
- 使用 HTTP/2([#161](https://github.com/chaitin/safeline/issues/161))
|
||||
- 监听 IPv6([#166](https://github.com/chaitin/safeline/issues/166))
|
||||
- 传递客户端连接的 Host 和协议,方便后续服务器处理
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化限频配置的默认值
|
||||
- 增加 HTTP 497 错误重定向。当以 HTTP 协议访问 HTTPS 端口时,将重定向到 HTTPS([#186](https://github.com/chaitin/safeline/issues/186))
|
||||
- 默认拒绝 IP 和其他非指定域名的访问。如果需要通过 IP 访问站点,可以给站点添加一个 "*" 域名([#58](https://github.com/chaitin/safeline/issues/58))
|
||||
- 优化若干 UI 交互细节和文字提示
|
||||
|
||||
## [2.6.0] - 2023-08-10
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增高频攻击封禁,即多次攻击后自动封禁 IP ([#29](https://github.com/chaitin/safeline/issues/29))
|
||||
- 频率限制日志显示封禁 IP 的地理位置 ([#198](https://github.com/chaitin/safeline/issues/198))
|
||||
|
||||
### 优化
|
||||
|
||||
- 攻击检测的原始日志支持搜索端口 ([#193](https://github.com/chaitin/safeline/issues/193))
|
||||
- 修复编辑站点后,“维护模式” 会失效的问题
|
||||
- 修改默认占用的网段,避免跟腾讯云默认网段冲突 ([#40](https://github.com/chaitin/safeline/issues/40))
|
||||
- 优化安装脚本 ([#194](https://github.com/chaitin/safeline/issues/194)),优化界面一些 UI 交互、错误提示
|
||||
|
||||
## [2.5.0] - 2023-08-03
|
||||
|
||||
### 新增
|
||||
|
||||
- 请求频率限制([#29](https://github.com/chaitin/safeline/issues/29))
|
||||
|
||||
### 优化
|
||||
|
||||
- 支持筛选攻击检测日志的 ID([#74](https://github.com/chaitin/safeline/issues/74)),优化筛选的交互
|
||||
- 证书支持 .cer 格式([#181](https://github.com/chaitin/safeline/issues/181))
|
||||
- 优化人机校验页面,适配移动端([#184](https://github.com/chaitin/safeline/issues/184))
|
||||
- 界面增加一些配置提示:添加站点时 “域名” 的格式、黑白名单/人机验证中匹配条件的生效逻辑
|
||||
|
||||
## [2.4.0] - 2023-07-27
|
||||
|
||||
### 新增
|
||||
|
||||
- IP 组支持注释 [#143](https://github.com/chaitin/safeline/issues/143)
|
||||
|
||||
### 优化
|
||||
|
||||
- 优化编辑 IP 组和相关规则时的性能
|
||||
- 优化一些界面 UI 交互细节
|
||||
|
||||
## [2.3.2] - 2023-07-24
|
||||
|
||||
### 修复
|
||||
- 修复了攻击事件 - 原始日志中,请求报文没有格式化的问题
|
||||
- 优化了一些已知问题
|
||||
|
||||
## [2.3.1] - 2023-07-20
|
||||
|
||||
### 新增
|
||||
|
||||
- 检测日志升级为**攻击事件** ,自动聚合同一攻击 IP 短时间内的所有攻击日志,方便管理员进行监控和处置
|
||||
- 日志支持按时间([#102](https://github.com/chaitin/safeline/issues/102))、动作筛选
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复添加/编辑站点时,上传证书处未翻译中文的问题
|
||||
- 修复数据统计中,“访问来源地区” 小概率出现的部分地区显示不正确的问题
|
||||
|
||||
## [2.2.0] - 2023-07-14
|
||||
|
||||
### 新增
|
||||
|
||||
- IP 组中新增长亭社区恶意 IP 情报,内容来自社区版共享的攻击 IP,每日自动更新
|
||||
|
||||
### 优化
|
||||
|
||||
- 升级核心检测引擎,修复一些绕过和误报
|
||||
- 管理界面增加浏览器版本检查,如果版本过旧,会提示升级浏览器
|
||||
- 优化一些界面的 UI 交互细节
|
||||
- 修复一些中英文翻译的问题
|
||||
|
||||
## [2.1.2] - 2023-07-07
|
||||
|
||||
- 修复了日志详情中防护策略模块没有翻译的问题
|
||||
|
||||
## [2.1.1] - 2023-07-06
|
||||
|
||||
- 修复了防护策略模块没有翻译的问题
|
||||
|
||||
## [2.1.0] - 2023-07-06
|
||||
|
||||
### 新增
|
||||
|
||||
- 添加/编辑站点时,自动检查端口占用情况,避免保存后配置不生效
|
||||
- 支持自定义站点的 nginx conf,详情可见[官网文档](https://waf-ce.chaitin.cn/posts/faq_other#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%AB%99%E7%82%B9-nginx-conf)
|
||||
- [站点列表支持按域名、端口或访问量进行排序](https://github.com/chaitin/safeline/issues/14)
|
||||
- [绑定 TOTP 密钥时,支持直接复制密钥;登录时的 6 位动态密码输入框,支持粘贴](https://github.com/chaitin/safeline/issues/30)
|
||||
|
||||
### 优化
|
||||
|
||||
- [黑白名单和人机验证列表中,可以鼠标悬浮查看 “复合条件” 的具体内容](https://github.com/chaitin/safeline/issues/120)
|
||||
- 修复人机验证列表中,“源 IP 不属于 IP 组” 的规则,IP 组名称显示成了 id 的问题
|
||||
- [优化人机验证启用/禁用交互](https://github.com/chaitin/safeline/issues/130)
|
||||
- [优化描述文字](https://github.com/chaitin/safeline/issues/122)
|
||||
- 优化一些界面 UI 交互、提示文字
|
||||
- 修复一些已知问题
|
||||
|
||||
## [2.0.1] - 2023-06-30
|
||||
|
||||
- 调整了人机验证的策略,降低误拦的情况
|
||||
- 修复了人机验证启动/禁用规则不会自动刷新状态的问题
|
||||
- 修复其他一些已知问题
|
||||
|
||||
## [2.0.0] - 2023-06-29
|
||||
|
||||
### 新增
|
||||
|
||||
- 人机验证
|
||||
- 支持嵌入式部署,可以通过 [t1k 协议](https://github.com/chaitin/lua-resty-t1k) 直接把流量转发到雷池进行检测
|
||||
- 把日志详情中的源 IP 加入到 IP 组时,支持调整 IP 为任意 IP 或网段
|
||||
|
||||
### 优化
|
||||
- 日志列表按照时间和 ID 排序,防止出现小范围的时间乱序
|
||||
- 转发流量时,自动设置请求头 X-Forwarded-Proto,适配更多代理场景
|
||||
- 优化界面 UI,修复其他一些已知问题
|
||||
|
||||
## [1.10.0] - 2023-06-21
|
||||
|
||||
### 新增
|
||||
|
||||
- 防护站点新增 “运行模式”,可以一键将站点设为 观察 或 维护 模式了
|
||||
|
||||
### 优化
|
||||
|
||||
- 修复了站点列表没有分页器的问题
|
||||
- 修复了窗口水平滚动时导航栏会错位的问题
|
||||
- 修复了黑白名单配置 “不属于 IP 组” 的条件时,列表显示组 ID,而未显示组名称的问题
|
||||
- 优化了界面的 UI 与交互
|
||||
|
||||
## [1.9.0] - 2023-06-16
|
||||
|
||||
### 新增
|
||||
|
||||
- 界面 UI 改造,信息层级更清晰
|
||||
- 黑白名单支持添加 源 IP - 不属于 IP 组 的条件
|
||||
|
||||
### 优化
|
||||
|
||||
- 检测日志的路由进一步完善
|
||||
- 数据统计页面更加紧凑,现在可以在 1920*1080 的屏幕上完全显示了
|
||||
- 黑白名单的展示优化
|
||||
- 修复 通用配置-防护模块配置 中,“批量配置为” 按钮有时候不生效的问题
|
||||
- 修复一些已知问题
|
||||
|
||||
## [1.8.2] - 2023-06-12
|
||||
|
||||
- 修复了「30 天访问情况」和「30 天拦截情况」显示相同数据的问题
|
||||
|
||||
## [1.8.1] - 2023-06-09
|
||||
|
||||
|
||||
6
FAQ.md
@@ -14,7 +14,7 @@ refers also: [https://stackoverflow.com/questions/66514436/difference-between-do
|
||||
|
||||
### ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
|
||||
|
||||
As shown, you shall start docker first. Try `systemctl start docker` or manually start your `Docker Desktop` for MacOS/Windows users.
|
||||
As shown, you shall start docker first. Try `systemctl start docker`.
|
||||
|
||||
### docker not found, unable to deploy
|
||||
|
||||
@@ -76,9 +76,9 @@ TOTP is calculated and verified according to time. So check your server time.
|
||||
|
||||
### ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
|
||||
|
||||
如描述,你需要启动 docker daemon 才能执行相关的命令。尝试 `systemctl start docker` 或者手动启动 `Docker Desktop` (MacOS 或者 Windows 用户)
|
||||
如描述,你需要启动 docker daemon 才能执行相关的命令。尝试 `systemctl start docker`
|
||||
|
||||
As shown, you shall start docker first. Try `systemctl start docker` or manually start your docker desktop for MacOS/Windows users.
|
||||
As shown, you shall start docker first. Try `systemctl start docker`.
|
||||
|
||||
### docker not found, unable to deploy
|
||||
|
||||
|
||||
144
README.md
@@ -1,8 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/veinmind/safeline-assets/safeline_logo.png" width="120">
|
||||
<img src="https://waf-ce.chaitin.cn/images/403.svg" width="120">
|
||||
</p>
|
||||
<h1 align="center">SafeLine Community Edition</h1>
|
||||
<h3 align="center">Keep hackers at bay</h3>
|
||||
<h1 align="center">雷池 - 广受好评的社区 WAF</h1>
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/SafeLine-BEST_WAF-blue">
|
||||
@@ -12,101 +11,108 @@
|
||||
<img src="https://img.shields.io/github/stars/chaitin/safeline?style=social">
|
||||
</p>
|
||||
|
||||
<p align="center"> <a href="https://waf-ce.chaitin.cn/">Official Website</a> </p>
|
||||
<p align="center"> English | <a href="README_CN.md">中文文档</a> </p>
|
||||
<p align="center">
|
||||
<a href="https://waf-ce.chaitin.cn/">官方网站</a> |
|
||||
<a href="https://demo.waf-ce.chaitin.cn:9443/dashboard">在线 Demo</a> |
|
||||
<a href="https://waf-ce.chaitin.cn/posts/guide_introduction">技术文档</a> |
|
||||
<a href="README_EN.md">For English</a>
|
||||
</p>
|
||||
|
||||
A simple and easy to use WAF tool. Built on [Chaitin Technology](https://www.chaitin.cn/en/)'s ace 🤖️Intelligent Semantic Analysis algorithm🤖️, designed for the community.
|
||||
一款足够简单、足够好用、足够强的免费 WAF。基于业界领先的语义引擎检测技术,作为反向代理接入,保护你的网站不受黑客攻击。
|
||||
|
||||
## ✨ Demo
|
||||
核心检测能力由智能语义分析算法驱动,专为社区而生,不让黑客越雷池半步。
|
||||
|
||||
### 🔥🔥🔥 Online Demo: https://demo.waf-ce.chaitin.cn:9443/
|
||||
<img src="https://waf-ce.chaitin.cn/images/album/0.png" />
|
||||
|
||||
There is a simple http server, listened on `http://127.0.0.1:8889`, can be used as for testing.
|
||||
<h4 align="center">相关源码仓库</h4>
|
||||
<p align="center">
|
||||
<a href="https://github.com/chaitin/yanshi">语义分析自动机引擎</a> |
|
||||
<a href="https://github.com/chaitin/safeline-open-platform">流量分析插件</a> |
|
||||
<a href="https://github.com/chaitin/lua-resty-t1k">T1K 协议</a> |
|
||||
<a href="https://github.com/chaitin/blazehttp">测试工具</a>
|
||||
</p>
|
||||
|
||||

|
||||
## 相关特性
|
||||
|
||||

|
||||
#### 便捷性
|
||||
|
||||
## 🚀 Installation
|
||||
采用容器化部署,一条命令即可完成安装,0 成本上手。安全配置开箱即用,无需人工维护,可实现安全躺平式管理。
|
||||
|
||||
### 1. Make sure [Docker](https://docs.docker.com/engine/install/) and [Compose V2](https://docs.docker.com/compose/install/) are installed correctly on the machine
|
||||
```shell
|
||||
docker info # >= 20.10.6
|
||||
docker compose version # >= 2.0.0
|
||||
#### 安全性
|
||||
|
||||
首创业内领先的智能语义分析算法,精准检测、低误报、难绕过。语义分析算法无规则,面对未知特征的 0day 攻击不再手足无措。
|
||||
|
||||
#### 高性能
|
||||
|
||||
无规则引擎,线性安全检测算法,平均请求检测延迟在 1 毫秒级别。并发能力强,单核轻松检测 2000+ TPS,只要硬件足够强,可支撑的流量规模无上限。
|
||||
|
||||
#### 高可用
|
||||
|
||||
流量处理引擎基于 Nginx 开发,性能与稳定性均可得到保障。内置完善的健康检查机制,服务可用性高达 99.99%。
|
||||
|
||||
## 🚀 安装
|
||||
|
||||
### 配置需求
|
||||
|
||||
- 操作系统:Linux
|
||||
- 指令架构:x86_64
|
||||
- 软件依赖:Docker 20.10.6 版本以上
|
||||
- 软件依赖:Docker Compose 2.0.0 版本以上
|
||||
- 最小化环境:1 核 CPU / 1 GB 内存 / 10 GB 磁盘
|
||||
|
||||
|
||||
### 一键安装
|
||||
|
||||
```
|
||||
bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
|
||||
### 2. Setup and deploy
|
||||
> 更多安装方式请参考 <a href="https://waf-ce.chaitin.cn/posts/guide_install">安装雷池</a>
|
||||
|
||||
```shell
|
||||
mkdir -p safeline && cd safeline
|
||||
# setup
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/setup.sh | bash
|
||||
## 🕹️ 快速使用
|
||||
|
||||
# launch
|
||||
sudo docker compose up -d
|
||||
```
|
||||
### 登录
|
||||
|
||||
#### Upgrade
|
||||
浏览器打开后台管理页面 `https://<waf-ip>:9443`。根据界面提示,使用 **支持 TOTP 的认证软件** 扫描二维码,然后输入动态口令登录:
|
||||
|
||||
**WARN: SafeLine will be restarted and your traffic will be unavailable for a short period of time. You may need to choose a proper time for upgration.**
|
||||

|
||||
|
||||
```shell
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
### 配置防护站点
|
||||
|
||||
# replace with `docker-compose` if necessary.
|
||||
docker compose down && docker compose pull && docker compose up -d
|
||||
```
|
||||
雷池以反向代理方式接入,优先于网站服务器接收流量,对流量中的攻击行为进行检测和清洗,将清洗过后的流量转发给网站服务器。
|
||||
|
||||
## 🕹️ Quick Start
|
||||

|
||||
|
||||
### 1. Login
|
||||
<font color=grey>💡 TIPS: 添加后,执行 `curl -H "Host: <域名>" http://<WAF IP>:<端口>` 应能获取到业务网站的响应。</font>
|
||||
|
||||
Open admin page `https://<waf-ip>:9443` and scan qrcode with any authenticator Apps that support TOTP, enter the code to login.
|
||||
### 测试效果
|
||||
|
||||

|
||||
使用以下方式尝试模拟黑客攻击,看看雷池的防护效果如何
|
||||
|
||||
### 2. Create website
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/?id=1%20AND%201=1`
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/?a=<script>alert(1)</script>`
|
||||
|
||||

|
||||

|
||||
|
||||
<font color=grey>💡 TIPS: After creating website,execute `curl -H "Host: <Domain>" http://<WAF IP>:<Port>` to check if you can get correct response from web server.</font>
|
||||
> 如果你需要进行深度测试,请参考 <a href="https://waf-ce.chaitin.cn/posts/guide_test">测试防护效果</a>
|
||||
|
||||
### 3. Deploy your website to SafeLine
|
||||
### FAQ
|
||||
|
||||
- If your website is hosted by DNS, just modify your DNS record to WAF
|
||||
- If your website is behind any reverse-proxy like nginx, you can modify your nginx conf and set upstream to WAF
|
||||
- [安装问题](https://waf-ce.chaitin.cn/posts/faq_install)
|
||||
- [登录问题](https://waf-ce.chaitin.cn/posts/faq_login)
|
||||
- [网站无法访问](https://waf-ce.chaitin.cn/posts/faq_access)
|
||||
- [配置问题](https://waf-ce.chaitin.cn/posts/faq_config)
|
||||
- [其他问题](https://waf-ce.chaitin.cn/posts/faq_other)
|
||||
|
||||
### 4. Protected!👌
|
||||
## 🏘️ 联系我们
|
||||
|
||||
Try these:
|
||||
1. 可以通过 GitHub Issue 直接进行 Bug 反馈和功能建议
|
||||
2. 可以扫描下方二维码加入雷池社区版用户讨论群
|
||||
|
||||
- `http://<IP or Domain>:<Port>/webshell.php`
|
||||
- `http://<IP or Domain>:<Port>/?id=1%20AND%201=1`
|
||||
- `http://<IP or Domain>:<Port>/?a=<script>alert(1)</script>`
|
||||
|
||||
## 📖 FAQ
|
||||
|
||||
Please refer to our [FAQ](FAQ.md) first if you have any questions.
|
||||
|
||||
For examples:
|
||||
- [docker compose or docker-compose?](FAQ.md#docker-compose-or-docker-compose)
|
||||
- [website configurations](FAQ.md#站点配置问题)
|
||||
- [website not working / not correctly response](FAQ.md#配置完成之后还是没有成功访问到上游服务器)
|
||||
|
||||
## 🏘️ Contact Us
|
||||
|
||||
1. You can make bug feedback and feature suggestions directly through GitHub Issues.
|
||||
2. By scanning the QR code below (use wechat or qq), you can join the discussion group of SafeLine users for detailed discussions.
|
||||
|
||||
<img src="https://waf-ce.chaitin.cn/images/wechat-light.png" width="30%" />
|
||||
|
||||
## ✨ CTStack
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/CT%20Stack-2.png" width="30%" />
|
||||
|
||||
SafeLine has already joined [CTStack](https://stack.chaitin.com/tool/detail?id=717) community.
|
||||
<img src="https://waf-ce.chaitin.cn/images/wechat-230825.png" width="30%" />
|
||||
|
||||
## Star History <a name="star-history"></a>
|
||||
|
||||
<a href="https://github.com/chaitin/safeline/stargazers">
|
||||
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=chaitin/safeline&type=Date">
|
||||
</a>
|
||||
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=chaitin/safeline&type=Date">
|
||||
</a>
|
||||
|
||||
141
README_CN.md
@@ -1,141 +0,0 @@
|
||||
<p align="center">
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/veinmind/safeline-assets/safeline_logo.png" width="120">
|
||||
</p>
|
||||
<h1 align="center">雷池 SafeLine 社区版</h1>
|
||||
<h3 align="center">不让黑客越雷池半步</h3>
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/SafeLine-BEST_WAF-blue">
|
||||
<img src="https://img.shields.io/github/release/chaitin/safeline.svg?color=blue" />
|
||||
<img src="https://img.shields.io/github/release-date/chaitin/safeline.svg?color=blue&label=update" />
|
||||
<img src="https://img.shields.io/docker/v/chaitin/safeline-mgt-api?color=blue">
|
||||
<img src="https://img.shields.io/github/stars/chaitin/safeline?style=social">
|
||||
</p>
|
||||
|
||||
<p align="center"> <a href="https://waf-ce.chaitin.cn/">官方网站</a> </p>
|
||||
<p align="center"> 中文文档 | <a href="README.md">English</a> </p>
|
||||
|
||||
一款简单、好用的 WAF 工具。基于[长亭科技](https://www.chaitin.cn)王牌的 🤖️智能语义分析算法🤖️ 打造,专为社区设计。
|
||||
|
||||
## ✨ Demo
|
||||
|
||||
### 🔥🔥🔥 体验地址:https://demo.waf-ce.chaitin.cn:9443/
|
||||
|
||||
有一台运行在环境上 `http://127.0.0.1:8889` 的服务可以作为上游服务器测试使用。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 🚀 安装
|
||||
|
||||
### 1. 确保机器上正确安装 [Docker](https://docs.docker.com/engine/install/) 和 [Compose V2](https://docs.docker.com/compose/install/)
|
||||
```shell
|
||||
docker info # >= 20.10.6
|
||||
docker compose version # >= 2.0.0
|
||||
```
|
||||
|
||||
### 2. 部署安装
|
||||
|
||||
```shell
|
||||
mkdir -p safeline && cd safeline
|
||||
# 下载并执行 setup
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/setup.sh | bash
|
||||
|
||||
# 运行
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
#### 升级
|
||||
|
||||
##### 自动一键更新
|
||||
|
||||
**WARN: 雷池 SafeLine 服务会重启,流量会中断一小段时间,根据业务情况选择合适的时间来执行升级操作。**
|
||||
|
||||
```shell
|
||||
# 请到 compose.yaml 同级目录下执行下面脚本
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
```
|
||||
**有部分环境的默认 SafeLine 安装路径是在 `/data/safeline-ce`,安装之后可能会发现需要重新绑定 OTP、配置丢失等情况,可以修改 .env 的 `SAFELINE_DIR` 变量,指向 `/data/safeline-ce`**
|
||||
|
||||
##### 手动更新镜像
|
||||
|
||||
**适用于 docker hub 拉取镜像失败的场景,手动更新镜像,注意还是要执行 `upgrade.sh` 来处理 `.env` 的更新,否则有可能会因为缺少参数而启动失败。**
|
||||
|
||||
###### 1. 在一台能够从 docker hub 拉取镜像的机器上执行
|
||||
|
||||
```shell
|
||||
|
||||
# 拉取镜像
|
||||
docker pull chaitin/safeline-tengine:latest
|
||||
docker pull chaitin/safeline-mgt-api:latest
|
||||
docker pull chaitin/safeline-mario:latest
|
||||
docker pull chaitin/safeline-detector:latest
|
||||
docker pull postgres:15.2
|
||||
|
||||
# 打包镜像
|
||||
docker save -o image.tar chaitin/safeline-tengine:latest chaitin/safeline-mgt-api:latest chaitin/safeline-mario:latest chaitin/safeline-detector:latest postgres:15.2
|
||||
|
||||
# 传输到 SafeLine 要部署的目标服务器
|
||||
# scp image.tar <target-server>:/root/
|
||||
```
|
||||
###### 2. 在目标服务器 load 镜像
|
||||
|
||||
```shell
|
||||
docker load -i image.tar
|
||||
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
```
|
||||
|
||||
## 🕹️ 快速使用
|
||||
|
||||
### 1. 登录
|
||||
|
||||
浏览器打开后台管理页面 `https://<waf-ip>:9443`。根据界面提示,使用 **支持 TOTP 的认证软件** 扫描二维码,然后输入动态口令登录:
|
||||
|
||||

|
||||
|
||||
### 2. 添加站点
|
||||
|
||||

|
||||
|
||||
<font color=grey>💡 TIPS: 添加后,执行 `curl -H "Host: <域名>" http://<WAF IP>:<端口>` 应能获取到业务网站的响应。</font>
|
||||
|
||||
### 3. 将网站流量切到雷池
|
||||
|
||||
- 若网站通过域名访问,则可将域名的 DNS 解析指向雷池所在设备
|
||||
- 若网站前有 nginx 、负载均衡等代理设备,则可将雷池部署在代理设备和业务服务器之间,然后将代理设备的 upstream 指向雷池
|
||||
|
||||
### 4. 开始防护👌
|
||||
|
||||
试试这些攻击方式:
|
||||
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/webshell.php`
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/?id=1%20AND%201=1`
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/?a=<script>alert(1)</script>`
|
||||
|
||||
## 📖 FAQ
|
||||
|
||||
有任何问题请先查阅我们的 [FAQ 文档](FAQ.md)。
|
||||
|
||||
比如:
|
||||
- [docker compose or docker-compose?](FAQ.md#docker-compose-还是-docker-compose)
|
||||
- [站点如何配置](FAQ.md#站点配置问题)
|
||||
- [配置完成之后,还是没有成功访问到上游服务器](FAQ.md#配置完成之后还是没有成功访问到上游服务器)
|
||||
|
||||
## 🏘️ 联系我们
|
||||
1. 您可以通过 GitHub Issue 直接进行 Bug 反馈和功能建议。
|
||||
2. 扫描下方二维码可以加入雷池社区版用户讨论群进行详细讨论
|
||||
|
||||
<img src="https://waf-ce.chaitin.cn/images/wechat-light.png" width="30%" />
|
||||
|
||||
## ✨ CTStack
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/CT%20Stack-2.png" width="30%" />
|
||||
|
||||
雷池 SafeLine 现已加入 [CTStack](https://stack.chaitin.com/tool/detail?id=717) 社区
|
||||
|
||||
## Star History <a name="star-history"></a>
|
||||
|
||||
<a href="https://github.com/chaitin/safeline/stargazers">
|
||||
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=chaitin/safeline&type=Date">
|
||||
</a>
|
||||
112
README_EN.md
Normal file
@@ -0,0 +1,112 @@
|
||||
<p align="center">
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/veinmind/safeline-assets/safeline_logo.png" width="120">
|
||||
</p>
|
||||
<h1 align="center">SafeLine Community Edition</h1>
|
||||
<h3 align="center">Keep hackers at bay</h3>
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/SafeLine-BEST_WAF-blue">
|
||||
<img src="https://img.shields.io/github/release/chaitin/safeline.svg?color=blue" />
|
||||
<img src="https://img.shields.io/github/release-date/chaitin/safeline.svg?color=blue&label=update" />
|
||||
<img src="https://img.shields.io/docker/v/chaitin/safeline-mgt-api?color=blue">
|
||||
<img src="https://img.shields.io/github/stars/chaitin/safeline?style=social">
|
||||
</p>
|
||||
|
||||
<p align="center"> <a href="https://waf-ce.chaitin.cn/">Official Website</a> </p>
|
||||
<p align="center"> English | <a href="README_CN.md">中文文档</a> </p>
|
||||
|
||||
A simple and easy to use WAF tool. Built on [Chaitin Technology](https://www.chaitin.cn/en/)'s ace 🤖️Intelligent Semantic Analysis algorithm🤖️, designed for the community.
|
||||
|
||||
## ✨ Demo
|
||||
|
||||
### 🔥🔥🔥 Online Demo: https://demo.waf-ce.chaitin.cn:9443/
|
||||
|
||||
There is a simple http server, listened on `http://127.0.0.1:8889`, can be used as for testing.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 🚀 Installation
|
||||
|
||||
### 1. Make sure [Docker](https://docs.docker.com/engine/install/) and [Compose V2](https://docs.docker.com/compose/install/) are installed correctly on the machine
|
||||
```shell
|
||||
docker info # >= 20.10.6
|
||||
docker compose version # >= 2.0.0
|
||||
```
|
||||
|
||||
### 2. Setup and deploy
|
||||
|
||||
```shell
|
||||
mkdir -p safeline && cd safeline
|
||||
# setup
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/setup.sh | bash
|
||||
|
||||
# launch
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
#### Upgrade
|
||||
|
||||
**WARN: SafeLine will be restarted and your traffic will be unavailable for a short period of time. You may need to choose a proper time for upgration.**
|
||||
|
||||
```shell
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
|
||||
# delete the old used image layers if necessary.
|
||||
docker rmi $(docker images | grep "safeline" | grep "none" | awk '{print $3}')
|
||||
```
|
||||
|
||||
## 🕹️ Quick Start
|
||||
|
||||
### 1. Login
|
||||
|
||||
Open admin page `https://<waf-ip>:9443` and scan qrcode with any authenticator Apps that support TOTP, enter the code to login.
|
||||
|
||||

|
||||
|
||||
### 2. Create website
|
||||
|
||||

|
||||
|
||||
<font color=grey>💡 TIPS: After creating website,execute `curl -H "Host: <Domain>" http://<WAF IP>:<Port>` to check if you can get correct response from web server.</font>
|
||||
|
||||
### 3. Deploy your website to SafeLine
|
||||
|
||||
- If your website is hosted by DNS, just modify your DNS record to WAF
|
||||
- If your website is behind any reverse-proxy like nginx, you can modify your nginx conf and set upstream to WAF
|
||||
|
||||
### 4. Protected!👌
|
||||
|
||||
Try these:
|
||||
|
||||
- `http://<IP or Domain>:<Port>/webshell.php`
|
||||
- `http://<IP or Domain>:<Port>/?id=1%20AND%201=1`
|
||||
- `http://<IP or Domain>:<Port>/?a=<script>alert(1)</script>`
|
||||
|
||||
## 📖 FAQ
|
||||
|
||||
Please refer to our [FAQ](FAQ.md) first if you have any questions.
|
||||
|
||||
For examples:
|
||||
- [docker compose or docker-compose?](FAQ.md#docker-compose-or-docker-compose)
|
||||
- [website configurations](FAQ.md#站点配置问题)
|
||||
- [website not working / not correctly response](FAQ.md#配置完成之后还是没有成功访问到上游服务器)
|
||||
|
||||
## 🏘️ Contact Us
|
||||
|
||||
1. You can make bug feedback and feature suggestions directly through GitHub Issues.
|
||||
2. By scanning the QR code below (use wechat or qq), you can join the discussion group of SafeLine users for detailed discussions.
|
||||
|
||||
<img src="https://waf-ce.chaitin.cn/images/wechat-230825.png" width="30%" />
|
||||
|
||||
## ✨ CTStack
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/CT%20Stack-2.png" width="30%" />
|
||||
|
||||
SafeLine has already joined [CTStack](https://stack.chaitin.com/tool/detail?id=717) community.
|
||||
|
||||
## Star History <a name="star-history"></a>
|
||||
|
||||
<a href="https://github.com/chaitin/safeline/stargazers">
|
||||
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=chaitin/safeline&type=Date">
|
||||
</a>
|
||||
1
blazehttp
Submodule
@@ -305,7 +305,7 @@
|
||||
console.log(e);
|
||||
}
|
||||
if (event_id) {
|
||||
document.getElementById("EventID").innerText = "EventID: " + event_id;
|
||||
document.getElementById("EventID").innerText = "ID: " + event_id;
|
||||
}
|
||||
if (type) {
|
||||
document.getElementById("TYPE").innerText = "TYPE: " + type;
|
||||
|
||||
287
blockpage/limited.html
Normal file
@@ -0,0 +1,287 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>请求存在威胁,已被拦截</title>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAC8VBMVEUAAAAqKipISEgnJydDQkIDAwMcHBw8PDw8PDwWFhYVFRUqKionJycLCws+Pz8ICAgmJiYICAhHR0cNDQ0QEBApKSksLCwbGxs9PT1HR0cjIyNDQ0MwMDA5OTlHR0cHBwdGRkYyMjIVFRUODg5AQEAYGBgxMTEDAwNJSUkfHx8lJSVBQUELCwsHBwdHR0cwMDAcHBxAQEALCws6OzsDAwMyMjL///89Pj79/v0DAgInt3UnuncoKCgFBAQICQk2NzcWFxYPDw9FRUUlvHhBQUE6PTsLCgtGSEY5OTohISESEhIuLy4slnoyl3grlHM+o21htI0rk3dIqXRCpnEzMzMwoFlgsJNEqWwroFMxMTH2+vg4oWQonUwkJiUeHR02mXxPrHotlnQrlW8ynWqEwqZnuolitoY9nX84oGs9o2o/p2Y6pmEtLCz5/Pvf7+g7nngpkHVLrXA5pGg2o10jm0xLS0selUkLAADu9fKx1sJnt5BMpoZLqXgwmnIzoGE4QT3C3dBSqYpYsINIpn1AoHtBpHc2nHU6o3AnrG4qqGw0PjkQGxYIEA5ksZZdtYFSq4FDoYBZs3xTsHc0lWEqnFo2glkvFSsaHBvn8+7b6+K43Mx5vaFasIhHooVVr344m20smmQaWEZEMT8xST642san0rtxuZhZrI4+oXIbmGEAjlRFTEg3MDgwOTRasZNvvI49sn4iuXQor3EdqGkkfGYipWEsm18npE4tV0Q5SUFDOkBFKj05IDQnAR0KFBF0v5cniG80jF0dmlUAlTiwzrqezrdpt5cns3MvoWksmmkyf1g6dFQvdVMCl1AwbVA9ZE4oZUoxY0EVAAHX5t3S5t3L39Kix66Sx6yWwKVNqIFIsHcpjm8yl24VmlosiUoAjkaBvZuBtZBZo3osk2IwpFohalMvekwvckE/LToZSTg2KDQwIC0UMyceLSUOKiFWsI92rY1IrX4rhVoAkiwiABZxr4Ihw3xfqnMmgGAMmkcAjgBUQVAtAAAANnRSTlMABP4G/v0bGjcoNyEKyEbs4NPMvEMR/fz78uzYzcG7bmxsbEsq/PT05sy4rKuVk5OTfn3v202D2tR1AAAI+UlEQVRYw7SSP2uDQBjGeyqc0OIgFBKk4pChEAiEDkIcAv7B+wqd+xk635p0Le7FjtrFqf5ZMiVoiN8ihHyAzH3vbEmTJk069IeeD+/r85x33sU/Il7jjgx08LX4dzNWjJ6uareApuo9Q8HnhyCElZ7afmQIABdttadghM6xi5eGDm5hD8jQjUsRnZ7dUAXBF+DmRFHzFHhRNU58BbpSdF+wfcm3OVEeBHnEJS8Kvq5coV/8uN+2GXUmRWz6MEoSP+RSymreavcxOurvdG1Jkmw7rJ6zPAzzsB5Z1qgOmc6eqxC60O52jiSIsgpvuIx8Np1UVTAbWAO4ZkFVTaaznPcgQZXFw34N7A2ZlGwcy3EGHAfkJpEyaDQR2qEEJGtuy/zEzdIH8ANgZ1gPaeZ+dVuuJqOf678B/xayqoOgLOcvL/OyDIJ6RaC4TbjZ3weEu2bL80xCKIycVVHE8XA8juO4KFZNzfMoITC2zC5Gu/+/b3oAWaRrSqnHoGlK38eOM35nyuNQuk4XhCmzv3MekHJHAFosR4u3++ETg5DXMrFgD5LyddiUhvdvi9GyoAS4U9D3BXxwRT4hiYRhGGcJ+gvRpQ6hBy8eunRqCBM8SKc5CObgjuNCB2dAHYUcEV0WkU5jB5fJ2cEM2oO6ECjuJmqHlkhb3ENBErWLkBRFQd6KiPa07/fNuMQ+Mwzf9877/Ob53tGj2mKxHalakw8nu7lcbvfoWrSCH/6jVbw+wqWTh6S1GmkXF1G3/tUhBgwWpGIuHAaC1dQSxZ3WAtg1wbK1I4otkxX84XCuiNsNA/8CjOlw5b5bRwTTApLpP6lF5K9373G7buxNH2CwOODau6u73eFIpCFCN9Y3TdrWJDYikbDbXb/bwwZDHzCktzgcDrrkrruBgBDVSiWZrFS+alJ3VWQHP7SVaDBY9EMaYM4I25ICAd6CKEqW5drn7e2Pmx6PZxM9vny4qKVrUKZQA4qglMBinNNGOAufRwGwW/D7fc4VZ/J9IZvN5rOJRDYPix9c0m73+fyCQCEIRKAhxKw6xkEdDVLaYL8RkFuSOuV8ECkTPD/MZPAixnYkacUJDIRoKzRJ07pBDBg3kiCle0Nh+3pKKuej8y6XiwgknpvNqw0i4HIF5qMxVkqtq4ibroI8xnEMMJBTJMmv9e3sfixDEPNwn141XwSq+XIehQ0UMoUy20es8STYDOoIyCme7rX9mr/sCWI/4fr9p8FQMJamcOhSETG2zEmA8PvbPRoAeAiTM/wUT36/FdQEEseysaiKOH16bAgyxciPz2cEKuURAGe4VWiwzUwCYExnNvN4BHEmpIZI7Z8F8CcDeV+TYeJx5rh5FX0NCH1SSN5s1o0BYGTCbDaTvYO4LEOn12Zf2eLY1K8NNXT0/PjCG4cXjzBLIpZCALvTGzrokWCbGAHAMALwfDrExOUahPA67curLMcWtNEFL3/amDjTOIR1ocOltpYRIM3zCDAMgNFpM4hMg9PG1BDCtmRfTrHcfgLOQQQ2LpfsNi/DHD+dErEOt6oBUIB306MY8JfJ8glJJAzDOLEgRHQqCDoaHbp1Wjp4mFMwgczgwBxcnNVRajq0wl5aRwX/ewhJiDLTssP6F9ZzF5FglRUqxLVSMgw7BUm1dOi4z/epu72n+eR7fj7PO++LmkywAIDgMhuA+MQhh+toVV29jm/E0+XyqtkgcF/Xb7tX1wOAQBxADwCNwBLCDv3iI59LuT1f58izj1cTaZutklB5HxFxCtphBoDgdoieRQTSRJY16fZOw5zgAoB/abW6eU4hJ75crsQ3mjZe5Y9ckHHK8bFiMLsELny6pzOxLG3ixALLsrrS7j+A4+Nl71wBwWC4axrJRB1keDW57RIETrECIQCwW9JBtjBBB4kCzsK5oGvbl+Rf0pBc5RWrIvy6Gw1xhedVHxC5MEFYlfAZBdBB+rAoIUPphAKSfCbpwAuPd3NWqzWXr42G+DfJsd32B0EgiJMSEkiLdJ+10oxkYn+kckE/AajfMXL7z918CoRUoXe5RAlGD83hD+YI4r4vsZBp6TZOz8YkWDgNvgNgk3uFWqpWS+UL2Ge6Gt/SqsoPEOE/r6wkxWanKUCzEIOF1/oQUHUMTBt/3hdSHYLotowDxBdbpsr7kCNYfzXBwIIGctKE2HxMkvode8Df9vLVjGPUulahkIKFZ6wWtXCQqIrJtj9g7+gkCSK0gNbcDMMwscZFKASEMys+JTyj1j0XCj0PHmioxJOYdUIeCl00YpDMzEFMM0wx8zj3ZXfIDoRXjBRtTbSOIDyj19C0FSOitx2A3i33GQaSKc0QMKZlqIW6uwNEIOr0imJxtM/DlayIouh1RiHHpToMoLRjI8D4JI4rTOPNLbuHiIgYSe9DjEJ4R1WMZAdyXHlrMCsQTI7//3HVrqCY5ccbWbZY9EAcOrORYpV0k0xAogj5YdQe0rstsnzzuMyQ+1q0cGRBM0U+2dp8kFEWvf7zWvTQCwS6uUTDw/2aXa+3yKiHzS1ye0rz/g/C39rLl7dBIA7DaxaaUCCEQDUCUQOG1CCOBHOCXJtgapuqWRx6Gr5CBapuCk0w+wwT1YgmDfsKe48jpQtJm4m9Cclx93ue+6fO0qM4jvKsPGyRQYHT/P76/Nj1+B54P1ZmOa/VLfCjQPXjCIFhe+BVveLIT3PHV/828BgEj8I48tV7ATbhopMbLmd2WwQUp9P7cZyenS8D7942MN5EhNA8u1YsSdMkSViv4Ph+g1/0seqa5TRCNBn877wuDBpRSussK8+DYrOHAjgTeFNmWU0pyowFbmBisIShqIOubZhwbIALmjVtF9SF4C3BT9aghZSGYViTIC+rlN0lrco8IDUGUaEN808NsosCXgRF0JVtVTVIVbVlFwDnQ/hcecKPd+HrJERIWBQBDwC4kKIIeSSi+/NHTxbVMQmRJIkQOACBHhpDt+mos8ePrrltYEIUc1YELU6j27Dns9nTZ59sayvuQAQvCXql2fLriD9chePBcR/QnjPO/jyqYtmeaax1ZG2Ynm0p6stfoy4VeYHIyhLwv+UH4kmftbvi1LgAAAAASUVORK5CYII="
|
||||
/>
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
word-break: keep-all;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: white;
|
||||
font-size: 12px;
|
||||
min-height: 450px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intercepted {
|
||||
margin-top: 3.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.intercepted-item {
|
||||
margin: 8px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 32px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: #a8a8a8;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-waflink {
|
||||
color: #27b876;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<table class="content">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="logo">
|
||||
<svg
|
||||
width="200px"
|
||||
height="200px"
|
||||
viewBox="0 0 396 407"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<title>编组 12</title>
|
||||
<defs>
|
||||
<linearGradient
|
||||
x1="50%"
|
||||
y1="0%"
|
||||
x2="50%"
|
||||
y2="100%"
|
||||
id="linearGradient-1"
|
||||
>
|
||||
<stop stop-color="#4B4B4B" offset="0%"></stop>
|
||||
<stop stop-color="#000000" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<filter
|
||||
x="-3.0%"
|
||||
y="-2.8%"
|
||||
width="106.1%"
|
||||
height="105.6%"
|
||||
filterUnits="objectBoundingBox"
|
||||
id="filter-2"
|
||||
>
|
||||
<feGaussianBlur
|
||||
stdDeviation="3"
|
||||
in="SourceGraphic"
|
||||
></feGaussianBlur>
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="50%"
|
||||
y1="0%"
|
||||
x2="50%"
|
||||
y2="100%"
|
||||
id="linearGradient-3"
|
||||
>
|
||||
<stop
|
||||
stop-color="#24BC43"
|
||||
stop-opacity="0.8"
|
||||
offset="0%"
|
||||
></stop>
|
||||
<stop
|
||||
stop-color="#3ACBAB"
|
||||
stop-opacity="0.7"
|
||||
offset="100%"
|
||||
></stop>
|
||||
</linearGradient>
|
||||
<path
|
||||
d="M110.049657,49.667649 C110.049657,49.667649 81.1358702,46.2263115 76.8,26.7636364 C72.4880848,46.2263115 43.5503431,49.667649 43.5503431,49.667649 C14.2053649,53.3001718 0,36.4567369 0,36.4567369 C13.941859,65.8036979 38.4,64.7712967 38.4,64.7712967 L115.2,64.7712967 C115.2,64.7712967 139.634186,65.8036979 153.6,36.4567369 C153.6,36.4567369 139.394635,53.3192904 110.049657,49.667649 Z"
|
||||
id="path-4"
|
||||
></path>
|
||||
<filter
|
||||
x="-16.9%"
|
||||
y="-57.9%"
|
||||
width="133.9%"
|
||||
height="236.8%"
|
||||
filterUnits="objectBoundingBox"
|
||||
id="filter-5"
|
||||
>
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="4"
|
||||
in="SourceAlpha"
|
||||
result="shadowOffsetOuter1"
|
||||
></feOffset>
|
||||
<feGaussianBlur
|
||||
stdDeviation="8"
|
||||
in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"
|
||||
></feGaussianBlur>
|
||||
<feColorMatrix
|
||||
values="0 0 0 0 0 0 0 0 0 0.490319293 0 0 0 0 0.292243323 0 0 0 1 0"
|
||||
type="matrix"
|
||||
in="shadowBlurOuter1"
|
||||
></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
id="页面-1"
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
>
|
||||
<g id="编组-12" transform="translate(49.000000, 38.000000)">
|
||||
<path
|
||||
d="M292.40836,59.04 C290.927217,51.9634286 285.002646,46.6971429 277.761503,46.368 C222.13636,44.8868571 176.385503,16.5805714 157.953503,3.08571429 C152.358074,-1.02857143 144.95236,-1.02857143 139.356931,3.08571429 C120.431217,16.5805714 75.1740742,44.8868571 19.5489314,46.368 C12.4723599,46.6971429 6.21864565,51.9634286 4.90207422,59.04 C-3.98478292,103.474286 -19.2899258,254.057143 148.902074,324 C316.60036,253.892571 300.966074,103.474286 292.40836,59.04 Z"
|
||||
id="路径"
|
||||
fill="url(#linearGradient-1)"
|
||||
fill-rule="nonzero"
|
||||
></path>
|
||||
<path
|
||||
d="M292.40836,59.04 C290.927217,51.9634286 285.002646,46.6971429 277.761503,46.368 C222.13636,44.8868571 176.385503,16.5805714 157.953503,3.08571429 C152.358074,-1.02857143 144.95236,-1.02857143 139.356931,3.08571429 C120.431217,16.5805714 75.1740742,44.8868571 19.5489314,46.368 C12.4723599,46.6971429 6.21864565,51.9634286 4.90207422,59.04 C-3.98478292,103.474286 -19.2899258,254.057143 148.902074,324 C316.60036,253.892571 300.966074,103.474286 292.40836,59.04 Z"
|
||||
id="路径"
|
||||
fill="url(#linearGradient-1)"
|
||||
fill-rule="nonzero"
|
||||
filter="url(#filter-2)"
|
||||
></path>
|
||||
<path
|
||||
d="M149,261.4 C205.553958,261.4 251.4,215.553958 251.4,159 C251.4,131.275004 240.381593,106.123494 222.484813,87.6855068 C209.900749,96.0964568 185.81512,106.024178 175.564259,100.853688 C166.334879,96.1984273 157.476591,88.4505652 148.989396,77.610101 C142.047769,88.5334102 134.670586,95.5517221 126.857848,98.6650367 C120.689419,101.123107 98.2592604,102.915695 75.4419467,87.761039 C57.5883513,106.192154 46.6,131.312844 46.6,159 C46.6,215.553958 92.4460416,261.4 149,261.4 Z"
|
||||
id="椭圆形备份-26"
|
||||
fill="url(#linearGradient-3)"
|
||||
></path>
|
||||
<g
|
||||
id="编组-5备份-6"
|
||||
transform="translate(91.771423, 102.101722)"
|
||||
fill="#FFFFFF"
|
||||
>
|
||||
<polygon
|
||||
id="路径-130备份-29"
|
||||
transform="translate(57.217971, 95.920999) rotate(-180.000000) translate(-57.217971, -95.920999) "
|
||||
points="56.6651511 64.9496372 -7.57241738e-17 97.1108413 50.6084036 126.892361 68.8016729 117.264704 34.3433228 97.1108413 56.6651511 84.5503086 96.9001091 107.376711 96.9001091 114.88399 114.435942 125.435553 114.435942 97.1108413"
|
||||
></polygon>
|
||||
<polygon
|
||||
id="路径-130备份-30"
|
||||
transform="translate(57.217971, 30.971362) rotate(-360.000000) translate(-57.217971, -30.971362) "
|
||||
points="56.6651511 2.84217094e-14 -7.57241738e-17 32.1612041 50.6084036 61.9427239 68.8016729 52.3150668 34.3433228 32.1612041 56.6651511 19.6006714 96.9001091 42.4270741 96.9001091 49.9343528 114.435942 60.4859155 114.435942 32.1612041"
|
||||
></polygon>
|
||||
<polygon
|
||||
id="路径-130备份-29"
|
||||
opacity="0.40499442"
|
||||
transform="translate(57.217971, 95.920999) rotate(-180.000000) translate(-57.217971, -95.920999) "
|
||||
points="56.6651511 64.9496372 -7.57241738e-17 97.1108413 50.6084036 126.892361 68.8016729 117.264704 34.3433228 97.1108413 56.6651511 84.5503086 96.9001091 107.376711 96.9001091 114.88399 114.435942 125.435553 114.435942 97.1108413"
|
||||
></polygon>
|
||||
<polygon
|
||||
id="路径-130备份-30"
|
||||
opacity="0.40499442"
|
||||
transform="translate(57.217971, 30.971362) rotate(-360.000000) translate(-57.217971, -30.971362) "
|
||||
points="56.6651511 4.8316906e-13 -7.57241738e-17 32.1612041 50.6084036 61.9427239 68.8016729 52.3150668 34.3433228 32.1612041 56.6651511 19.6006714 96.9001091 42.4270741 96.9001091 49.9343528 114.435942 60.4859155 114.435942 32.1612041"
|
||||
></polygon>
|
||||
</g>
|
||||
<g
|
||||
id="长亭logo备份-18"
|
||||
transform="translate(72.200000, 45.222222)"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<g id="编组-7">
|
||||
<path
|
||||
d="M96.7632666,18.0061837 C96.7632666,18.0061837 79.3862969,15.2966085 76.7907961,0 C74.1952953,15.2966085 56.8183256,18.0061837 56.8183256,18.0061837 C39.1836466,20.8694936 30.6424242,7.60987058 30.6424242,7.60987058 C39.0363842,30.6893013 53.7258141,29.862977 53.7258141,29.862977 L99.8741859,29.862977 C99.8741859,29.862977 114.563616,30.6700845 122.957576,7.60987058 C122.957576,7.60987058 114.416353,20.8694936 96.7816744,18.0061837 L96.7632666,18.0061837 Z"
|
||||
id="路径"
|
||||
fill="#27B876"
|
||||
></path>
|
||||
<g id="路径">
|
||||
<use
|
||||
fill="black"
|
||||
fill-opacity="1"
|
||||
filter="url(#filter-5)"
|
||||
xlink:href="#path-4"
|
||||
></use>
|
||||
<use fill="#27B876" xlink:href="#path-4"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="intercepted" class="intercepted">
|
||||
请求频率过高,存在威胁,已被拦截
|
||||
</div>
|
||||
<div class="intercepted-item">
|
||||
<span id="intercepted-at">拦截时间</span>: <span id="now"></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="footer" class="footer">
|
||||
安全检测能力由
|
||||
<a class="footer-waflink" href="https://waf-ce.chaitin.cn"
|
||||
>长亭雷池 WAF</a
|
||||
>
|
||||
驱动
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// 显示当前时间
|
||||
function timestring() {
|
||||
var d = new Date();
|
||||
function p(d) {
|
||||
return d < 10 ? "0" + d : d;
|
||||
}
|
||||
return (
|
||||
d.getFullYear() +
|
||||
"-" +
|
||||
p(d.getMonth() + 1) +
|
||||
"-" +
|
||||
p(d.getDate()) +
|
||||
" " +
|
||||
p(d.getHours()) +
|
||||
":" +
|
||||
p(d.getMinutes())
|
||||
);
|
||||
}
|
||||
document.getElementById("now").innerText = timestring();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if (navigator.language.startsWith("en")) {
|
||||
document.title = "Requested too frequently";
|
||||
|
||||
document.getElementById("intercepted").innerText =
|
||||
"Requested too frequently, please try again later";
|
||||
document.getElementById("intercepted-at").innerText = "Intercepted at";
|
||||
document.getElementById("footer").style.display = "none";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
212
blockpage/maintaining.html
Normal file
@@ -0,0 +1,212 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>网站维护中</title>
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
word-break: keep-all;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: white;
|
||||
font-size: 12px;
|
||||
min-height: 450px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intercepted {
|
||||
margin-top: 3.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.intercepted-item {
|
||||
margin: 8px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 32px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: #a8a8a8;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-waflink {
|
||||
color: #27b876;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<table class="content">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="logo">
|
||||
<svg
|
||||
width="300px"
|
||||
height="112px"
|
||||
viewBox="0 0 300 112"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<title>编组 65</title>
|
||||
<defs>
|
||||
<linearGradient
|
||||
x1="50%"
|
||||
y1="0%"
|
||||
x2="50%"
|
||||
y2="100%"
|
||||
id="linearGradient-1"
|
||||
>
|
||||
<stop
|
||||
stop-color="#0FC6C2"
|
||||
stop-opacity="0.1"
|
||||
offset="0%"
|
||||
></stop>
|
||||
<stop
|
||||
stop-color="#0FC6C2"
|
||||
stop-opacity="0"
|
||||
offset="100%"
|
||||
></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="50%"
|
||||
y1="0%"
|
||||
x2="50%"
|
||||
y2="100%"
|
||||
id="linearGradient-2"
|
||||
>
|
||||
<stop stop-color="#A9D6D3" offset="0%"></stop>
|
||||
<stop stop-color="#8EC6C4" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="17.048305%"
|
||||
y1="8.1635079%"
|
||||
x2="76.1348779%"
|
||||
y2="89.9397366%"
|
||||
id="linearGradient-3"
|
||||
>
|
||||
<stop stop-color="#ECF7F7" offset="0%"></stop>
|
||||
<stop stop-color="#CEEFEE" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g
|
||||
id="问脉"
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
>
|
||||
<g id="编组-65" transform="translate(0.000000, 0.656106)">
|
||||
<path
|
||||
d="M158.105468,46.4305012 C230.638275,48.8882768 280.048484,89.2493521 291.411562,97.5896283 C298.986948,103.149812 301.288867,107.707461 298.317319,111.262574 L0,111.262574 C57.0484407,66.4026752 109.750263,44.7919842 158.105468,46.4305012 Z"
|
||||
id="路径-14备份"
|
||||
fill="url(#linearGradient-1)"
|
||||
opacity="0.6"
|
||||
></path>
|
||||
<path
|
||||
d="M133.744707,59.2516792 C128.220216,62.212914 142.271748,72.3438938 152.603912,72.3438938 C162.936076,72.3438938 170.384509,66.1002168 166.415224,63.7091176 C162.445939,61.3180184 139.269199,56.2904444 133.744707,59.2516792 Z"
|
||||
id="路径-23备份"
|
||||
fill="#CEEEED"
|
||||
></path>
|
||||
<g
|
||||
id="维护备份"
|
||||
transform="translate(125.841376, 4.000000)"
|
||||
fill="url(#linearGradient-2)"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<path
|
||||
d="M53.4407915,17.8309532 C49.850381,20.6412105 45.048111,20.2807339 42.738148,17.0275722 C40.428185,13.7744105 41.4704292,8.83897736 45.0608397,6.02872002 L48.4302392,3.3908402 C50.2893442,1.93421992 49.6588911,0.00928311389 46.813125,0.241953931 C43.8131226,0.486558233 40.8354442,1.6843377 38.3140089,3.66074708 C34.2763868,6.82838427 31.8308675,11.6191742 31.8089552,16.4041942 L3.55036904,38.5271177 C-0.0421309333,41.3487126 -1.07876034,46.2575577 1.22851314,49.522012 C2.33804924,51.0840803 4.09016048,52.0468265 6.09769574,52.1975197 C8.105231,52.3482129 10.2029338,51.6744483 11.9272901,50.3251027 L40.1897428,28.2024695 C43.7454976,29.2914901 47.8019094,28.7846954 51.4034487,26.8014661 C55.004988,24.8182368 57.8332485,21.5339069 59.2220045,17.7221341 C60.2487908,14.9244912 58.6731626,13.7367433 56.810191,15.1930733 L53.4407915,17.8309532 Z M5.56827104,46.1206895 C4.79626286,45.0334702 5.14228458,43.3949223 6.34222301,42.4557247 C7.541301,41.5714856 9.09674694,41.7145817 9.85296424,42.7787018 C10.6091815,43.842822 10.2920189,45.4421908 9.13710773,46.3885799 C7.9371693,47.3277774 6.34027921,47.2079088 5.56827104,46.1206895 Z M13.7446583,12.4428814 L20.5888502,22.087378 L24.9316389,18.6903037 L18.087447,9.04580702 L16.9394989,5.05163029 L11.6667257,0 L7.32393706,3.39707438 L10.4272492,10.147387 L13.7446583,12.4428814 Z M38.1657743,33.2576704 C37.8311845,33.2325548 37.4815663,33.3450613 37.1943929,33.5702592 L30.6813074,38.6699737 C30.0831963,39.1405877 29.9105495,39.9581375 30.2943314,40.5024561 L40.1788997,54.4239027 C41.7237519,56.5943835 44.9213986,56.8344109 47.3165731,54.9596835 C49.7156141,53.0852463 50.4093292,49.8002347 48.8653128,47.6257961 L38.976878,33.7040592 C38.7922954,33.4434496 38.5003642,33.282786 38.1657743,33.2576704 Z"
|
||||
id="形状"
|
||||
></path>
|
||||
</g>
|
||||
<g
|
||||
id="维护备份-2"
|
||||
transform="translate(123.841376, 0.000000)"
|
||||
fill="url(#linearGradient-3)"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<path
|
||||
d="M53.4407915,17.8309532 C49.850381,20.6412105 45.048111,20.2807339 42.738148,17.0275722 C40.428185,13.7744105 41.4704292,8.83897736 45.0608397,6.02872002 L48.4302392,3.3908402 C50.2893442,1.93421992 49.6588911,0.00928311389 46.813125,0.241953931 C43.8131226,0.486558233 40.8354442,1.6843377 38.3140089,3.66074708 C34.2763868,6.82838427 31.8308675,11.6191742 31.8089552,16.4041942 L3.55036904,38.5271177 C-0.0421309333,41.3487126 -1.07876034,46.2575577 1.22851314,49.522012 C2.33804924,51.0840803 4.09016048,52.0468265 6.09769574,52.1975197 C8.105231,52.3482129 10.2029338,51.6744483 11.9272901,50.3251027 L40.1897428,28.2024695 C43.7454976,29.2914901 47.8019094,28.7846954 51.4034487,26.8014661 C55.004988,24.8182368 57.8332485,21.5339069 59.2220045,17.7221341 C60.2487908,14.9244912 58.6731626,13.7367433 56.810191,15.1930733 L53.4407915,17.8309532 Z M5.56827104,46.1206895 C4.79626286,45.0334702 5.14228458,43.3949223 6.34222301,42.4557247 C7.541301,41.5714856 9.09674694,41.7145817 9.85296424,42.7787018 C10.6091815,43.842822 10.2920189,45.4421908 9.13710773,46.3885799 C7.9371693,47.3277774 6.34027921,47.2079088 5.56827104,46.1206895 Z M13.7446583,12.4428814 L20.5888502,22.087378 L24.9316389,18.6903037 L18.087447,9.04580702 L16.9394989,5.05163029 L11.6667257,0 L7.32393706,3.39707438 L10.4272492,10.147387 L13.7446583,12.4428814 Z M38.1657743,33.2576704 C37.8311845,33.2325548 37.4815663,33.3450613 37.1943929,33.5702592 L30.6813074,38.6699737 C30.0831963,39.1405877 29.9105495,39.9581375 30.2943314,40.5024561 L40.1788997,54.4239027 C41.7237519,56.5943835 44.9213986,56.8344109 47.3165731,54.9596835 C49.7156141,53.0852463 50.4093292,49.8002347 48.8653128,47.6257961 L38.976878,33.7040592 C38.7922954,33.4434496 38.5003642,33.282786 38.1657743,33.2576704 Z"
|
||||
id="形状"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="intercepted" id="intercepted">
|
||||
网站维护中,暂时无法访问
|
||||
</div>
|
||||
<div class="intercepted-item"><span id="now"></span></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="footer" id="footer">
|
||||
安全检测能力由
|
||||
<a class="footer-waflink" href="https://waf-ce.chaitin.cn"
|
||||
>长亭雷池 WAF</a
|
||||
>
|
||||
驱动
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// 显示当前时间
|
||||
function timestring() {
|
||||
var d = new Date();
|
||||
function p(d) {
|
||||
return d < 10 ? "0" + d : d;
|
||||
}
|
||||
return (
|
||||
d.getFullYear() +
|
||||
"-" +
|
||||
p(d.getMonth() + 1) +
|
||||
"-" +
|
||||
p(d.getDate()) +
|
||||
" " +
|
||||
p(d.getHours()) +
|
||||
":" +
|
||||
p(d.getMinutes())
|
||||
);
|
||||
}
|
||||
document.getElementById("now").innerText = timestring();
|
||||
</script>
|
||||
<script>
|
||||
if (navigator.language.startsWith("en")) {
|
||||
document.title = "Site is Maintaining";
|
||||
|
||||
document.getElementById("intercepted").innerText =
|
||||
"The site is on maintaining, please try again later";
|
||||
document.getElementById("footer").style.display = "none";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
14
compose.yaml
@@ -30,11 +30,11 @@ services:
|
||||
redis:
|
||||
container_name: safeline-redis
|
||||
restart: always
|
||||
image: redis:7.0.11
|
||||
image: redis:7.0.10
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/redis/data:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
command: redis-server --appendonly yes --requirepass ${POSTGRES_PASSWORD}
|
||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.3
|
||||
@@ -56,8 +56,12 @@ services:
|
||||
environment:
|
||||
- MANAGEMENT_RESOURCES_DIR=/resources/management
|
||||
- NGINX_RESOURCES_DIR=/resources/nginx
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@127.0.0.1/safeline-ce
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
|
||||
- MARIO_URL=http://safeline-mario:3335
|
||||
- DETECTOR_URL=http://safeline-detector:8001
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
|
||||
- MANAGEMENT_LOGS_DIR=/logs/management
|
||||
dns: 223.5.5.5
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.4
|
||||
@@ -90,7 +94,7 @@ services:
|
||||
- LOG_DIR=/logs/mario
|
||||
- GOGC=100
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
|
||||
- REDIS_URL=redis://:${POSTGRES_PASSWORD}@safeline-redis:6379/0
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.6
|
||||
@@ -112,4 +116,4 @@ services:
|
||||
- MGT_ADDR=${SUBNET_PREFIX}.4:9002
|
||||
ulimits:
|
||||
nofile: 131072
|
||||
network_mode: host
|
||||
network_mode: host
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
35
homepage/.gitignore
vendored
@@ -1,35 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -1,2 +0,0 @@
|
||||
build:
|
||||
npm i && npm run build
|
||||
@@ -1,35 +0,0 @@
|
||||
const isProduction = process.env.NODE_ENV == "production";
|
||||
const isDevelopment = process.env.NODE_ENV == "development";
|
||||
|
||||
const prodConfig = {
|
||||
output: "export",
|
||||
};
|
||||
|
||||
const devConfig = {
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/api/poc/:path*",
|
||||
destination: "https://waf-ce.chaitin.cn/api/poc/:path*",
|
||||
},
|
||||
{
|
||||
source: "/api/count",
|
||||
destination: "https://waf-ce.chaitin.cn/api/count",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: false,
|
||||
images: { unoptimized: true },
|
||||
};
|
||||
|
||||
Object.assign(
|
||||
nextConfig,
|
||||
isProduction && prodConfig,
|
||||
isDevelopment && devConfig
|
||||
);
|
||||
|
||||
module.exports = nextConfig;
|
||||
6049
homepage/package-lock.json
generated
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "blog",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"icon": "node ./script/downLoadIcon.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.6",
|
||||
"@emotion/styled": "^11.10.6",
|
||||
"@jsdevtools/rehype-toc": "^3.0.2",
|
||||
"@mui/icons-material": "^5.11.16",
|
||||
"@mui/lab": "5.0.0-alpha.128",
|
||||
"@mui/material": "^5.12.0",
|
||||
"@types/node": "18.15.11",
|
||||
"@types/react": "18.0.35",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"ahooks": "^3.7.6",
|
||||
"axios": "^1.3.6",
|
||||
"countup.js": "2.6.2",
|
||||
"eslint": "8.38.0",
|
||||
"eslint-config-next": "13.3.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"highlight.js": "11.8.0",
|
||||
"next": "13.3.0",
|
||||
"next-mdx-remote": "^4.4.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-responsive-carousel": "^3.2.23",
|
||||
"remark-external-links": "^9.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-prism": "^1.3.6",
|
||||
"remark-slug": "^7.0.1",
|
||||
"typescript": "5.0.4"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 989 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 595 KiB |
|
Before Width: | Height: | Size: 686 KiB |
|
Before Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="61px" height="68px" viewBox="0 0 61 68" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>编组 4备份 4</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="-1.15518706e-11%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#744BE4" offset="0%"></stop>
|
||||
<stop stop-color="#E44BD3" offset="99.9153191%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="9.5%" y1="7.78543896e-13%" x2="90.5%" y2="100%" id="linearGradient-2">
|
||||
<stop stop-color="#FFFFFF" offset="0%"></stop>
|
||||
<stop stop-color="#8F00FE" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M25.4341763,6.37416481 L15.0880707,0.440979948 C14.1181233,-0.146993316 12.8787461,-0.146993316 11.8549127,0.440979948 L1.61657901,6.37416481 C0.646631602,6.96213807 0,7.97772828 0,9.15367481 L0,20.9131403 C0,22.0356347 0.592745625,23.1046771 1.61657901,23.6926503 L11.8549127,29.5723831 C12.3398864,29.8396437 12.932632,30 13.4714917,30 C14.0103514,30 14.603097,29.8396437 15.0880707,29.5723831 L25.3264044,23.6391982 C26.2963518,23.051225 26.9429834,22.0356347 26.9429834,20.9131403 L26.9968694,9.15367481 C27.0507554,7.97772825 26.4041238,6.90868595 25.4341763,6.37416481 Z" id="path-3"></path>
|
||||
<filter x="-103.7%" y="-60.0%" width="307.4%" height="286.7%" filterUnits="objectBoundingBox" id="filter-4">
|
||||
<feMorphology radius="0.5" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
|
||||
<feOffset dx="0" dy="10" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="7.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
|
||||
<feColorMatrix values="0 0 0 0 0.560784314 0 0 0 0 0 0 0 0 0 0.996078431 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="编组-4备份-4" transform="translate(1.000000, 0.000000)">
|
||||
<g id="编组-3备份" fill="#D8D8D8" fill-opacity="0">
|
||||
<rect id="矩形" x="0" y="0" width="60" height="60"></rect>
|
||||
</g>
|
||||
<rect id="矩形" fill-opacity="0" fill="#D8D8D8" x="0" y="0" width="60" height="60"></rect>
|
||||
<path d="M24.4561176,11.7839344 L17.5587138,7.79637281 C16.9120822,7.40120906 16.0858307,7.40120906 15.4032751,7.79637281 L8.57771934,11.7839344 C7.93108773,12.1790981 7.5,12.8616537 7.5,13.6519812 L7.5,21.5552563 C7.5,22.3096599 7.89516375,23.0281394 8.57771934,23.4233032 L15.4032751,27.3749408 C15.7265909,27.5545606 16.1217547,27.6623326 16.4809945,27.6623326 C16.8402342,27.6623326 17.235398,27.5545606 17.5587138,27.3749408 L24.3842696,23.3873792 C25.0309012,22.9922155 25.4619889,22.3096599 25.4619889,21.5552563 L25.4979129,13.6519812 C25.5338369,12.8616537 25.1027492,12.1431741 24.4561176,11.7839344 Z" id="路径" fill="url(#linearGradient-1)" fill-rule="nonzero"></path>
|
||||
<g id="编组-13备份-4" transform="translate(15.000000, 12.000000)" fill-rule="nonzero">
|
||||
<g id="路径">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-3"></use>
|
||||
<use stroke="url(#linearGradient-2)" stroke-width="1" fill-opacity="0.3" fill="#8F00FE" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M34.3321474,21.1810911 C34.9778424,20.8082989 35.8034894,21.0295304 36.1762816,21.6752254 C36.5490739,22.3209205 36.3278424,23.1465675 35.6821474,23.5193597 L35.6821474,23.5193597 L29.778,26.928 L29.7786864,33.4502254 C29.7786864,34.1958098 29.1742708,34.8002254 28.4286864,34.8002254 C27.683102,34.8002254 27.0786864,34.1958098 27.0786864,33.4502254 L27.078,26.927 L21.1752254,23.5193597 C20.5295304,23.1465675 20.3082989,22.3209205 20.6810911,21.6752254 C21.0538833,21.0295304 21.8795304,20.8082989 22.5252254,21.1810911 L28.429,24.589 Z" id="形状结合" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 122 KiB |
@@ -1 +0,0 @@
|
||||
../../../../compose.yaml
|
||||
@@ -1 +0,0 @@
|
||||
../../../../setup.sh
|
||||
@@ -1 +0,0 @@
|
||||
../../../../upgrade.sh
|
||||
@@ -1 +0,0 @@
|
||||
../../../../version.json
|
||||
@@ -1,19 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const Axios = require("axios");
|
||||
|
||||
async function downloadFile(url) {
|
||||
const iconPath = path.resolve(__dirname, "../src/static/fonts/iconfont.js");
|
||||
const writer = fs.createWriteStream(iconPath);
|
||||
const response = await Axios({
|
||||
url: "https:" + url,
|
||||
method: "GET",
|
||||
responseType: "stream",
|
||||
});
|
||||
response.data.pipe(writer);
|
||||
writer.on("finish", () => console.log("下载成功"));
|
||||
writer.on("error", (err) => console.log(err));
|
||||
}
|
||||
let argument = process.argv.splice(2);
|
||||
downloadFile(argument[0]);
|
||||
@@ -1,42 +0,0 @@
|
||||
export { submitSampleSet, getSampleSet, getSampleSetResult, getSampleDetail };
|
||||
|
||||
const BASE_API = "/api/poc/";
|
||||
|
||||
function submitSampleSet(data: {
|
||||
pocs: Array<{ content: string; tag: string }>;
|
||||
record_it: boolean;
|
||||
}) {
|
||||
return fetch(BASE_API + "list", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
mode: "cors",
|
||||
// credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
}).then((res) => res.json());
|
||||
}
|
||||
|
||||
function getSampleSet(id: string) {
|
||||
return fetch(BASE_API + "list?id=" + id).then((res) => res.json());
|
||||
}
|
||||
|
||||
function getSampleDetail(id: string) {
|
||||
return fetch(BASE_API + "detail?id=" + id).then((res) => res.json());
|
||||
}
|
||||
|
||||
async function getSampleSetResult(id: string, timeout: number = 60) {
|
||||
const startAt = new Date().getTime();
|
||||
const isTimeout = () => new Date().getTime() - startAt > timeout * 1000;
|
||||
const maxRetry = 20;
|
||||
|
||||
for (let i = 0; i < maxRetry; i++) {
|
||||
const res = await fetch(BASE_API + "results?id=" + id).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
if (res.code == 0 && res.data.data) {
|
||||
return { data: res.data.data, timeout: false };
|
||||
}
|
||||
if (isTimeout()) break;
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
}
|
||||
return { data: [], timeout: true };
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export { getSetupCount };
|
||||
|
||||
function getSetupCount() {
|
||||
return fetch("/api/count").then((res) => res.json());
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import React, { type FC, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { Box, type SxProps, Tooltip } from "@mui/material";
|
||||
|
||||
interface EllipsisProps {
|
||||
children: React.ReactNode;
|
||||
sx?: SxProps;
|
||||
}
|
||||
|
||||
const Ellipsis: FC<EllipsisProps> = (props) => {
|
||||
const { children, sx } = props;
|
||||
const content = useRef<HTMLSpanElement>(null);
|
||||
const [ellipsis, setEllipsis] = useState<boolean>(false);
|
||||
|
||||
//判断文字内容是否超出div宽度
|
||||
const onResize = () => {
|
||||
if (content.current) {
|
||||
setEllipsis(
|
||||
(content.current.parentNode! as HTMLDivElement).offsetWidth <
|
||||
content.current.offsetWidth
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onResize();
|
||||
}, [children]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={ellipsis ? children : null}
|
||||
arrow
|
||||
followCursor
|
||||
componentsProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "text.primary",
|
||||
backgroundColor: "background.paper0",
|
||||
boxShadow: " 0px 0px 20px 0px rgba(0,0,0,0.1)",
|
||||
padding: "16px",
|
||||
lineHeight: "24px",
|
||||
maxWidth: "500px",
|
||||
fontSize: "14px",
|
||||
},
|
||||
},
|
||||
arrow: { sx: { color: "background.paper0" } },
|
||||
}}
|
||||
>
|
||||
<Box ref={content} component="span" sx={{ maxWidth: "100%" }}>
|
||||
{children as React.ReactElement}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Ellipsis;
|
||||
@@ -1,58 +0,0 @@
|
||||
import { Grid, Box } from "@mui/material";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
const LINK_LIST = [
|
||||
{
|
||||
name: "北京长亭科技有限公司",
|
||||
url: "https://chaitin.cn/",
|
||||
},
|
||||
{
|
||||
name: "CT Stack 安全社区",
|
||||
url: "https://stack.chaitin.cn/",
|
||||
},
|
||||
{
|
||||
name: "长亭百川云平台",
|
||||
url: "https://rivers.chaitin.cn/",
|
||||
},
|
||||
{
|
||||
name: "长亭 GitHub 主页",
|
||||
url: "https://github.com/chaitin",
|
||||
},
|
||||
{
|
||||
name: "长亭 B 站主页",
|
||||
url: "https://space.bilibili.com/521870525",
|
||||
},
|
||||
{
|
||||
name: "长亭合作伙伴论坛",
|
||||
url: "https://bbs.chaitin.cn/",
|
||||
},
|
||||
];
|
||||
|
||||
const FriendlyLinks = () => {
|
||||
return (
|
||||
<Grid container>
|
||||
{LINK_LIST.map((item) => (
|
||||
<Grid item xs={6} sm={4} md={3} key={item.name}>
|
||||
<Box
|
||||
component={Link}
|
||||
target="_blank"
|
||||
href={item.url}
|
||||
sx={{
|
||||
color: "rgba(0,0,0,0.7)",
|
||||
fontSize: "14px",
|
||||
lineHeight: "24px",
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default FriendlyLinks;
|
||||
@@ -1,255 +0,0 @@
|
||||
import { type FC, useEffect, useRef, useState } from "react";
|
||||
import { Ellipsis } from "@/components";
|
||||
import { useRouter } from "next/router";
|
||||
import { Box } from "@mui/material";
|
||||
import Link from "next/link";
|
||||
import { useDebounceFn, useThrottleFn } from "ahooks";
|
||||
import { slug } from "github-slugger";
|
||||
|
||||
interface MarkdownNavbarProps {
|
||||
containerId?: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface Heading {
|
||||
dataId: string;
|
||||
listNo: string;
|
||||
offsetTop: number;
|
||||
}
|
||||
|
||||
interface Nav {
|
||||
index: number;
|
||||
level: number;
|
||||
text: string;
|
||||
listNo: string;
|
||||
}
|
||||
|
||||
const MarkdownNavbar: FC<MarkdownNavbarProps> = (props) => {
|
||||
const router = useRouter();
|
||||
let { containerId, source } = props;
|
||||
let container: HTMLElement;
|
||||
if (typeof window !== "undefined") {
|
||||
container =
|
||||
(containerId && document.getElementById(containerId)) || document.body;
|
||||
}
|
||||
|
||||
const [currentListNo, setCurrentListNo] = useState<string>("");
|
||||
const [navStructure, setNavStructure] = useState<Nav[]>([]);
|
||||
const scrollEventLock = useRef(false);
|
||||
const timer = useRef<any>();
|
||||
|
||||
const trimArrZero = (arr: number[]) => {
|
||||
let start, end;
|
||||
for (start = 0; start < arr.length; start++) {
|
||||
if (arr[start]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (end = arr.length - 1; end >= 0; end--) {
|
||||
if (arr[end]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return arr.slice(start, end + 1);
|
||||
};
|
||||
|
||||
const getHeadingList = (navStructure: Nav[]) => {
|
||||
const headingList: Heading[] = [];
|
||||
navStructure.forEach((t) => {
|
||||
const curHeading = document.getElementById(slug(t.text));
|
||||
if (curHeading) {
|
||||
headingList.push({
|
||||
dataId: `heading-${t.index}`,
|
||||
listNo: t.listNo,
|
||||
offsetTop: curHeading.offsetTop - 179,
|
||||
});
|
||||
}
|
||||
});
|
||||
return headingList;
|
||||
};
|
||||
const initHeadingsId = (navStructure: any[]) => {
|
||||
navStructure.forEach((t) => {
|
||||
const headings = document.querySelectorAll(`h${t.level}`);
|
||||
const curHeading = Array.prototype.slice
|
||||
.apply(headings)
|
||||
.find(
|
||||
(h) =>
|
||||
h.innerText.trim() === t.text.trim() &&
|
||||
(!h.dataset || !h.dataset.id)
|
||||
);
|
||||
|
||||
if (curHeading) {
|
||||
curHeading.dataset.id = `heading-${t.index}`;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getNavStructure: (source: string) => Nav[] = (source) => {
|
||||
const contentWithoutCode = source
|
||||
.replace(/```[^`\n]*\n+[^```]+```\n+/g, "")
|
||||
.replace(/^[^#]+\n/g, "")
|
||||
.replace(/(?:[^\n#]+)#+\s([^#\n]+)\n*/g, "") // 匹配行内出现 # 号的情况
|
||||
.replace(/^#\s[^#\n]*\n+/, "")
|
||||
.replace(/`([^`\n]+)`/g, "$1")
|
||||
.replace(/\*\*?([^*\n]+)\*\*?/g, "$1")
|
||||
.replace(/__?([^_\n]+)__?/g, "$1")
|
||||
.trim();
|
||||
|
||||
const pattOfTitle = /#+\s([^#\n]+)\n*/g;
|
||||
const matchResult = contentWithoutCode.match(pattOfTitle);
|
||||
|
||||
if (!matchResult) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const navData = matchResult.map((r, i) => ({
|
||||
index: i,
|
||||
level: r.match(/^#+/g)![0].length,
|
||||
text: r.replace(pattOfTitle, "$1"),
|
||||
listNo: "",
|
||||
}));
|
||||
|
||||
let maxLevel = 0;
|
||||
navData.forEach((t) => {
|
||||
if (t.level > maxLevel) {
|
||||
maxLevel = t.level;
|
||||
}
|
||||
});
|
||||
let matchStack = [];
|
||||
|
||||
for (let i = 0; i < navData.length; i++) {
|
||||
const t = navData[i];
|
||||
const { level } = t;
|
||||
while (
|
||||
matchStack.length &&
|
||||
matchStack[matchStack.length - 1].level > level
|
||||
) {
|
||||
matchStack.pop();
|
||||
}
|
||||
if (matchStack.length === 0) {
|
||||
const arr = new Array(maxLevel).fill(0);
|
||||
arr[level - 1] += 1;
|
||||
matchStack.push({
|
||||
level,
|
||||
arr,
|
||||
});
|
||||
t.listNo = trimArrZero(arr).join(".");
|
||||
continue;
|
||||
}
|
||||
const arr: number[] = matchStack[matchStack.length - 1].arr;
|
||||
const newArr = arr.slice();
|
||||
newArr[level - 1] += 1;
|
||||
matchStack.push({
|
||||
level,
|
||||
arr: newArr,
|
||||
});
|
||||
t.listNo = trimArrZero(newArr).join(".");
|
||||
}
|
||||
return navData;
|
||||
};
|
||||
|
||||
const refreshNav = (source: string) => {
|
||||
// TODO: 只需要 2 3 级标题
|
||||
const navStructure = getNavStructure(source).filter((nav) =>
|
||||
[2, 3].includes(nav.level)
|
||||
);
|
||||
setNavStructure(navStructure);
|
||||
let hash = router.asPath.split("#")[1];
|
||||
let listNo = navStructure[0]?.listNo;
|
||||
if (hash) {
|
||||
const hashText = decodeURIComponent(hash);
|
||||
let findNav = navStructure.find((nav) => hashText === slug(nav.text));
|
||||
if (findNav) {
|
||||
listNo = findNav.listNo;
|
||||
const target = document.getElementById(hashText);
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: "smooth" });
|
||||
scrollEventLock.current = true;
|
||||
setTimeout(() => {
|
||||
scrollEventLock.current = false;
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
setCurrentListNo(listNo);
|
||||
initHeadingsId(navStructure);
|
||||
|
||||
setTimeout(() => {
|
||||
container?.addEventListener(
|
||||
"scroll",
|
||||
() => winScroll(getHeadingList(navStructure)),
|
||||
false
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const { run: winScroll } = useThrottleFn(
|
||||
(headingList: Heading[]) => {
|
||||
clearTimeout(timer.current);
|
||||
if (scrollEventLock.current) return;
|
||||
|
||||
var curHeading: Heading | null = null;
|
||||
headingList.forEach((h) => {
|
||||
if (h.offsetTop <= container.scrollTop) {
|
||||
curHeading = h;
|
||||
}
|
||||
});
|
||||
|
||||
if (curHeading) {
|
||||
timer.current = setTimeout(() => {
|
||||
setCurrentListNo(curHeading!.listNo);
|
||||
});
|
||||
}
|
||||
},
|
||||
{ wait: 300 }
|
||||
);
|
||||
|
||||
const { run: scrollToTarget } = useDebounceFn(
|
||||
(dataId: string) => {
|
||||
const target = document.querySelector(`[data-id="${dataId}"]`);
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: "smooth" });
|
||||
scrollEventLock.current = true;
|
||||
setTimeout(() => {
|
||||
scrollEventLock.current = false;
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
{ wait: 0 }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
refreshNav(source);
|
||||
return () => {
|
||||
container?.removeEventListener("scroll", winScroll, false);
|
||||
};
|
||||
}, [source]);
|
||||
|
||||
const tBlocks = navStructure.map((t, index) => {
|
||||
const cls = `title-anchor title-level${t.level}`;
|
||||
const anchor = slug(t.text);
|
||||
return (
|
||||
<Box
|
||||
className={cls}
|
||||
component={Link}
|
||||
href={`#${anchor}`}
|
||||
style={{ width: "100%" }}
|
||||
key={`title_anchor_${Math.random().toString(36).substring(2)}`}
|
||||
>
|
||||
<Ellipsis
|
||||
sx={{
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
color: currentListNo === t.listNo ? "primary.main" : "inherit",
|
||||
}}
|
||||
>
|
||||
{t.text}
|
||||
</Ellipsis>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
return <div className={`markdown-navigation`}>{tBlocks}</div>;
|
||||
};
|
||||
|
||||
export default MarkdownNavbar;
|
||||
@@ -1,46 +0,0 @@
|
||||
import React, { type FC } from "react";
|
||||
|
||||
import ErrorIcon from "@mui/icons-material/Error";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import { ThemeProvider } from "@/components";
|
||||
|
||||
import Modal, { type ModalProps } from "./Modal";
|
||||
|
||||
export interface ConfirmDialogProps extends ModalProps {
|
||||
content?: React.ReactNode;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
const ConfirmDialog: FC<ConfirmDialogProps> = (props) => {
|
||||
const { title = "提示", content, width = "480px", ...rest } = props;
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<Modal
|
||||
title={
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
lineHeight: "22px",
|
||||
color: "text.main",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
<ErrorIcon
|
||||
sx={{ color: "#FFBF00", mr: "16px", fontSize: "24px" }}
|
||||
/>
|
||||
{title}
|
||||
</Box>
|
||||
}
|
||||
closable={false}
|
||||
{...rest}
|
||||
sx={{ width }}
|
||||
>
|
||||
<Box sx={{ color: "text.main", pl: "40px" }}>{content}</Box>
|
||||
</Modal>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmDialog;
|
||||
@@ -1,61 +0,0 @@
|
||||
import { useMemo, useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
ThemeProvider as MUIThemeProvider,
|
||||
useMediaQuery,
|
||||
} from "@mui/material";
|
||||
import { useLocalStorageState } from "ahooks";
|
||||
|
||||
import themes from "../../themes";
|
||||
import ThemeContext, { type ThemeMode } from "../../themes/themeContext";
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
||||
// const [mode, setMode] = useLocalStorageState<ThemeMode>("themeMode", {
|
||||
// defaultValue: "system",
|
||||
// });
|
||||
const [mode, setMode] = useState<ThemeMode>("light");
|
||||
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
||||
|
||||
const theme = useMemo(() => {
|
||||
let newMode = mode;
|
||||
if (mode === "system") {
|
||||
newMode = prefersDarkMode ? "dark" : "light";
|
||||
}
|
||||
return themes(newMode as "dark" | "light");
|
||||
}, [mode, prefersDarkMode]);
|
||||
|
||||
const themeMode = useMemo(() => {
|
||||
return {
|
||||
mode,
|
||||
setThemeMode: (mode: ThemeMode) => {
|
||||
setMode(mode);
|
||||
},
|
||||
};
|
||||
}, [mode]);
|
||||
|
||||
useEffect(() => {
|
||||
const bodyStyle = document.body.style;
|
||||
const body = document.body;
|
||||
let newMode = mode;
|
||||
if (mode === "system") {
|
||||
newMode = prefersDarkMode ? "dark" : "light";
|
||||
}
|
||||
body.className = newMode;
|
||||
// @ts-ignore
|
||||
bodyStyle.backgroundColor = theme.palette.background.paper0;
|
||||
bodyStyle.color = theme.palette.text.primary;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={themeMode}>
|
||||
<MUIThemeProvider theme={theme}>{children}</MUIThemeProvider>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeProvider;
|
||||
@@ -1,107 +0,0 @@
|
||||
const BuiltinAttackSamples = [
|
||||
`
|
||||
POST /doUpload.action HTTP/1.1
|
||||
Host: localhost:8080
|
||||
Content-Length: 1000000000
|
||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXd004BVJN9pBYBL2
|
||||
|
||||
------WebKitFormBoundaryXd004BVJN9pBYBL2
|
||||
Content-Disposition: form-data; name="upload"; filename="ok"
|
||||
|
||||
|
||||
<%eval request("sb")%>
|
||||
------WebKitFormBoundaryXd004BVJN9pBYBL2--
|
||||
`,
|
||||
`GET /fe/Channel/25545911?tabNum=cat%20%2Fetc%2Fhosts HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: Chrome`,
|
||||
`GET /scripts/%2e%2e/%2e/Windows/System32/cmd.exe?/c+dir+c HTTP/1.1
|
||||
Host: a.cn
|
||||
User-Agent: Chrome
|
||||
Cookie: 2333;`,
|
||||
`GET /?s=/../../../etc/login\0.img HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: Chrome`,
|
||||
`POST / HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: Chrome
|
||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4JGjXRl94NnI4Og7
|
||||
|
||||
------WebKitFormBoundary4JGjXRl94NnI4Og7
|
||||
Content-Disposition: form-data; name="file"; filename="hack.asp%00.png"
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
<%@codepage=65000%><%response.codepage=65001:eval(request("key"))%>
|
||||
------WebKitFormBoundary4JGjXRl94NnI4Og7--"`,
|
||||
`GET /?s=%ac%ed%00%05%73%72%00%1a%63%6f%6d%2e%63%74%2e%61%72%61%6c%65%69%69%2e%74%65%73%74%2e%50%65%72%73%6f%6e%00%00%00%00%00%00%00%01%02%00%08%49%00%03%61%61%61%43%00%03%63%63%63%42%00%03%64%64%64%5a%00%03%65%65%65%4a%00%03%66%66%66%46%00%03%67%67%67%44%00%03%68%68%68%4c%00%03%62%62%62%74%00%12%4c%6a%61%76%61%2f%6c%61%6e%67%2f%53%74%72%69%6e%67%3b%78%70%00%00%00%01%00%62%65%01%00%00%00%00%00%00%00%01%3f%80%00%00%40%00%00%00%00%00%00%00%74%00%03%61%61%61 HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: Chrome`,
|
||||
`GET / HTTP/1.1
|
||||
Host: a.cn
|
||||
Content-Type: a
|
||||
Origin: b
|
||||
User-Agent: c
|
||||
X-Forwarded-For: d
|
||||
Cookie: FOOID=1;
|
||||
A: AAAAAAAAA
|
||||
Referer: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ifconfig').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}`,
|
||||
`GET / HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: Chrome
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 55
|
||||
|
||||
<?php echo $a; ?>`,
|
||||
`GET / HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: Chrome
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 55
|
||||
|
||||
a=O:9:"FileClass":1:{s:8:"filename";s:10:"config.php";}`,
|
||||
`GET / HTTP/1.1
|
||||
Host: monster
|
||||
User-Agent: () { :;}; echo; echo $(/bin/ls -al)
|
||||
Referer: Chrome
|
||||
Cookie: 2333;`,
|
||||
`GET /somepath?q=1'%20or%201=1 HTTP/1.1`,
|
||||
`GET /hello?content=hello&user=ftp://a.com HTTP/1.1
|
||||
Host: www.baidu.com
|
||||
User-Agent: Chrome
|
||||
Referer: http://www.qq.com/abc.html`,
|
||||
`GET /?flag=%7B%7Bconfig.__class__.__init__.__globals__%5B'os'%5D.popen('id').read()%7D%7D HTTP/1.1
|
||||
Host: 1.2.3.4
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
|
||||
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
|
||||
Accept-Encoding: gzip, deflate
|
||||
Connection: close`,
|
||||
`POST / HTTP/1.1
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:template match="/fruits">
|
||||
<xsl:value-of select="system-property('xsl:vendor')"/>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>`,
|
||||
`GET /somepath?q=<script>alert('XSS')</script> HTTP/1.1`,
|
||||
`POST / HTTP/1.1
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE data [
|
||||
<!ELEMENT data (#ANY)>
|
||||
<!ENTITY file SYSTEM "file:///etc/passwd">
|
||||
]>
|
||||
<data>&file;</data>`,
|
||||
];
|
||||
|
||||
const BuiltinNonAttackSamples = [
|
||||
`GET / HTTP/1.1
|
||||
Host: 1.2.3.4`,
|
||||
|
||||
`POST / HTTP/1.1
|
||||
|
||||
name=haha&submit=submit`,
|
||||
];
|
||||
|
||||
export { BuiltinAttackSamples, BuiltinNonAttackSamples };
|
||||
@@ -1,3 +0,0 @@
|
||||
.detection-samples-accordion::before {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export { default as ThemeProvider } from "./ThemeProvider";
|
||||
export { default as Icon } from "./Icon";
|
||||
export { default as Message } from "./Message";
|
||||
export { default as MarkdownNavbar } from "./MarkdownNavbar";
|
||||
export { default as Modal } from "./Modal";
|
||||
export { default as Ellipsis } from "./Ellipsis";
|
||||
@@ -1,259 +0,0 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
AppBar,
|
||||
Box,
|
||||
CssBaseline,
|
||||
Divider,
|
||||
Drawer,
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Button,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
interface Props {
|
||||
/**
|
||||
* Injected by the documentation to work in an iframe.
|
||||
* You won't need it on your project.
|
||||
*/
|
||||
window?: () => Window;
|
||||
}
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
export default function DrawerAppBar(props: Props) {
|
||||
const { window } = props;
|
||||
const router = useRouter();
|
||||
const { pathname, asPath } = router;
|
||||
const [mobileOpen, setMobileOpen] = React.useState(false);
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
setMobileOpen((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const drawer = (
|
||||
<Box onClick={handleDrawerToggle} sx={{ textAlign: "center" }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
component="div"
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
my: 2,
|
||||
ml: 2,
|
||||
}}
|
||||
onClick={() => router.push("/")}
|
||||
>
|
||||
<Image
|
||||
src="/images/logo.png"
|
||||
alt="Logo"
|
||||
width={120}
|
||||
height={34}
|
||||
priority
|
||||
/>
|
||||
<Image
|
||||
src="/images/safeline.png"
|
||||
alt="Logo"
|
||||
width={34}
|
||||
height={34}
|
||||
style={{ marginLeft: "24px" }}
|
||||
priority
|
||||
/>
|
||||
</Typography>
|
||||
<Divider />
|
||||
<List>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
sx={{ textAlign: "center" }}
|
||||
selected={pathname === "/"}
|
||||
component={Link}
|
||||
href="/"
|
||||
>
|
||||
<ListItemText primary="主页" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
sx={{ textAlign: "center" }}
|
||||
selected={pathname.startsWith("/posts/")}
|
||||
component={Link}
|
||||
href="/posts/guide_introduction/"
|
||||
>
|
||||
<ListItemText primary="技术文档" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
sx={{ textAlign: "center" }}
|
||||
component={Link}
|
||||
href="https://www.bilibili.com/medialist/detail/ml2342694989"
|
||||
target="_blank"
|
||||
>
|
||||
<ListItemText primary="教学视频" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
sx={{ textAlign: "center" }}
|
||||
component={Link}
|
||||
href="https://demo.waf-ce.chaitin.cn:9443/"
|
||||
target="_blank"
|
||||
>
|
||||
<ListItemText primary="演示环境" />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const container =
|
||||
window !== undefined ? () => window().document.body : undefined;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<CssBaseline />
|
||||
<AppBar
|
||||
component="nav"
|
||||
sx={{
|
||||
backgroundColor: "#0F1935",
|
||||
boxShadow: "none",
|
||||
color: "text.primary",
|
||||
pr: "0 !important",
|
||||
}}
|
||||
>
|
||||
<Toolbar sx={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
component="div"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
onClick={() => router.push("/")}
|
||||
>
|
||||
<Image
|
||||
src="/images/logo.png"
|
||||
alt="Logo"
|
||||
width={120}
|
||||
height={34}
|
||||
priority
|
||||
/>
|
||||
|
||||
<Image
|
||||
src="/images/403.svg"
|
||||
alt="Logo"
|
||||
width={34}
|
||||
height={34}
|
||||
style={{ marginLeft: "24px" }}
|
||||
priority
|
||||
/>
|
||||
</Typography>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={handleDrawerToggle}
|
||||
sx={{ display: { sm: "none", color: "#fff" } }}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: "none", sm: "block" },
|
||||
".MuiButtonBase-root": {
|
||||
ml: "16px",
|
||||
},
|
||||
"a.MuiBox-root": {
|
||||
mr: 5,
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
color: pathname === "/" ? "primary.main" : "#fff",
|
||||
}}
|
||||
component={Link}
|
||||
href="/"
|
||||
>
|
||||
主页
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
color: pathname.startsWith("/posts/") ? "primary.main" : "#fff",
|
||||
}}
|
||||
component={Link}
|
||||
href="/posts/guide_introduction/"
|
||||
>
|
||||
技术文档
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
color: pathname.startsWith("/detection")
|
||||
? "primary.main"
|
||||
: "#fff",
|
||||
}}
|
||||
component={Link}
|
||||
href="/detection"
|
||||
>
|
||||
效果对比
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
color: "#fff",
|
||||
}}
|
||||
component={Link}
|
||||
href="https://www.bilibili.com/medialist/detail/ml2342694989"
|
||||
target="_blank"
|
||||
>
|
||||
教学视频
|
||||
</Box>
|
||||
<Button
|
||||
sx={{ ml: "0 !important" }}
|
||||
component={Link}
|
||||
variant="contained"
|
||||
href="https://demo.waf-ce.chaitin.cn:9443/"
|
||||
target="_blank"
|
||||
>
|
||||
演示环境
|
||||
</Button>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<Box component="nav">
|
||||
<Drawer
|
||||
container={container}
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onClose={handleDrawerToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: "block", sm: "none" },
|
||||
"& .MuiDrawer-paper": {
|
||||
boxSizing: "border-box",
|
||||
width: drawerWidth,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { type FC } from "react";
|
||||
import Head from "next/head";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import Header from "./Header";
|
||||
|
||||
interface IProps {
|
||||
redirect?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
const MainLayout: FC<IProps> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>长亭雷池 WAF 社区版</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="长亭雷池 Web 应用防火墙 | 长亭雷池 WAF"
|
||||
/>
|
||||
<meta name="keywords" content="WAF,雷池,长亭,社区版" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/images/safeline.png" />
|
||||
</Head>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "100vh",
|
||||
}}
|
||||
>
|
||||
<Header />
|
||||
<Box component="main" sx={{ flexGrow: 1 }}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainLayout;
|
||||
@@ -1,159 +0,0 @@
|
||||
import { type FC, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
AppBar,
|
||||
List,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
ListSubheader,
|
||||
ListItem,
|
||||
} from "@mui/material";
|
||||
import { type GroupItem } from "@/utils/posts";
|
||||
interface SideLayoutProps {
|
||||
list: GroupItem[];
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SideLayout: FC<SideLayoutProps> = ({ children, list }) => {
|
||||
const router = useRouter();
|
||||
const { asPath } = router;
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: { xs: "block", sm: "flex" }, height: "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 240,
|
||||
pt: "80px",
|
||||
pb: 3,
|
||||
height: "100%",
|
||||
overflow: "auto",
|
||||
backgroundColor: "#f8f9fc",
|
||||
// boxShadow: "inset 0px 0px 16px 0px rgba(0, 145, 255, 1)",
|
||||
display: { xs: "none", sm: "block" },
|
||||
borderRight: "1px solid hsla(210, 18%, 87%, 1)",
|
||||
"&::-webkit-scrollbar": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{list.map((group) => (
|
||||
<Box sx={{ pl: "40px", lineHeight: "32px" }} key={group.title}>
|
||||
<Box sx={{ color: "text.auxiliary", mb: "6px", mt: "20px" }}>
|
||||
{group.title}
|
||||
</Box>
|
||||
{group.list.map((nav) => (
|
||||
<Box
|
||||
key={nav.title}
|
||||
component={Link}
|
||||
href={`/posts/${nav.id}`}
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
display: "block",
|
||||
textDecoration: "none",
|
||||
color: asPath.startsWith(`/posts/${nav.id}`)
|
||||
? "primary.main"
|
||||
: "inherit",
|
||||
fontWeight: asPath.startsWith(`/posts/${nav.id}`) ? 700 : 400,
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{nav.title}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<AppBar
|
||||
sx={{
|
||||
top: 72,
|
||||
backgroundColor: "#fff",
|
||||
display: {
|
||||
xs: "block",
|
||||
sm: "none",
|
||||
boxShadow: "0 12px 25px -12px rgba(93,99,112, 0.2)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItem onClick={handleClick}>
|
||||
<ListItemIcon>{open ? <ExpandLess /> : <ExpandMore />}</ListItemIcon>
|
||||
<ListItemText primary="目录" sx={{ color: "#000" }} />
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<List
|
||||
sx={{
|
||||
width: "100%",
|
||||
bgcolor: "background.paper",
|
||||
position: "relative",
|
||||
overflow: "auto",
|
||||
maxHeight: 300,
|
||||
"& ul": { padding: 0 },
|
||||
}}
|
||||
subheader={<li />}
|
||||
>
|
||||
{list.map((group) => (
|
||||
<li key={`section-${group.title}`}>
|
||||
<ul>
|
||||
<ListSubheader sx={{ color: "text.auxiliary" }}>
|
||||
{group.title}
|
||||
</ListSubheader>
|
||||
{group.list.map((nav) => (
|
||||
<ListItem
|
||||
key={nav.title}
|
||||
onClick={() => {
|
||||
router.push(`/posts/${nav.id}`);
|
||||
handleClick();
|
||||
}}
|
||||
sx={{
|
||||
color: asPath.startsWith(`/posts/${nav.id}`)
|
||||
? "primary.main"
|
||||
: "text.primary",
|
||||
fontWeight: asPath.startsWith(`/posts/${nav.id}`)
|
||||
? 700
|
||||
: 400,
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={nav.title} />
|
||||
</ListItem>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
</AppBar>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
pt: { xs: 18, sm: 13 },
|
||||
pb: 3,
|
||||
px: 3,
|
||||
flexGrow: 1,
|
||||
height: "100%",
|
||||
width: { xs: "100%", sm: "calc(100% - 240px)" },
|
||||
marginLeft: { xs: 0, sm: "240px" },
|
||||
backgroundColor: "#F8F9FC",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideLayout;
|
||||
@@ -1,35 +0,0 @@
|
||||
import "@/styles/globals.css";
|
||||
import "@/styles/markdown.css";
|
||||
import type { AppProps } from "next/app";
|
||||
import { ThemeProvider } from "@/components";
|
||||
import MainLayout from "@/layout/MainLayout";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import type { NextPage } from "next";
|
||||
import Script from "next/script";
|
||||
import "@/components/detection/detection.css"
|
||||
|
||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (page: ReactElement) => ReactNode;
|
||||
};
|
||||
|
||||
type AppPropsWithLayout = AppProps & {
|
||||
Component: NextPageWithLayout;
|
||||
};
|
||||
|
||||
export default function App({ Component, pageProps }: AppPropsWithLayout) {
|
||||
const getLayout = Component.getLayout || ((page) => page);
|
||||
|
||||
return (
|
||||
<>
|
||||
{process.env.NODE_ENV === "production" && (
|
||||
<Script
|
||||
type="text/javascript"
|
||||
src="https://v1.cnzz.com/z_stat.php?id=1281262430&web_id=1281262430"
|
||||
/>
|
||||
)}
|
||||
<ThemeProvider>
|
||||
<MainLayout>{getLayout(<Component {...pageProps} />)}</MainLayout>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Container from "@mui/material/Container";
|
||||
import Result from "@/components/detection/Result";
|
||||
import { useRouter } from "next/router";
|
||||
import "highlight.js/styles/a11y-light.css";
|
||||
import { getSampleSet, getSampleSetResult } from "@/api/detection";
|
||||
import { Message } from "@/components";
|
||||
import type {
|
||||
RecordSamplesType,
|
||||
ResultRowsType,
|
||||
} from "@/components/detection/types";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import SampleList from "@/components/detection/SampleList";
|
||||
import { Typography } from "@mui/material";
|
||||
import Head from "next/head";
|
||||
|
||||
export default Detection;
|
||||
|
||||
function Detection() {
|
||||
const router = useRouter();
|
||||
const [samples, setSamples] = useState<RecordSamplesType>([]);
|
||||
const [result, setResult] = useState<ResultRowsType>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// useRouter 中获取 参数会有延迟,所以先判断有没有 id 参数
|
||||
const realSetId =
|
||||
new URLSearchParams(location.search).get("id") || "default";
|
||||
const setId = (router.query.id as string) || "default";
|
||||
if (setId !== realSetId) return;
|
||||
|
||||
// 查询样本集合
|
||||
getSampleSet(setId).then((res) => {
|
||||
if (res.code != 0) {
|
||||
Message.error("测试集合 " + setId + ": " + res.msg);
|
||||
return;
|
||||
}
|
||||
if (!res.data.data) {
|
||||
Message.error("测试集合 " + setId + ": 获取结果为空");
|
||||
return;
|
||||
}
|
||||
setSamples(
|
||||
res.data.data?.map((i: any) => ({
|
||||
id: i.id,
|
||||
summary: i.summary,
|
||||
size: i.length,
|
||||
isAttack: i.tag == "black",
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
// 查询样本集合结果
|
||||
getSampleSetResult(setId).then(({ data, timeout }) => {
|
||||
if (timeout) {
|
||||
Message.error("获取检测集结果超时");
|
||||
return;
|
||||
}
|
||||
setResult(
|
||||
data.map((i: any) => ({
|
||||
engine: i.engine,
|
||||
version: i.version,
|
||||
detectionRate: percent(i.recall),
|
||||
failedRate: percent(i.fdr),
|
||||
accuracy: percent(i.accuracy),
|
||||
cost: i.elapsed > 0 ? i.elapsed + " 毫秒" : "小于 1 毫秒",
|
||||
}))
|
||||
);
|
||||
});
|
||||
}, [router.query.id]);
|
||||
|
||||
const handleSetId = (id: string) => {
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { id },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>效果对比 - 长亭雷池 WAF 社区版</title>
|
||||
</Head>
|
||||
<Container sx={{ mb: 2 }}>
|
||||
<div style={{ height: "100px" }}></div>
|
||||
<SampleList value={samples} onSetIdChange={handleSetId} />
|
||||
<Result rows={result} />
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{ mt: 3, mb: 3, color: "text.auxiliary" }}
|
||||
>
|
||||
<Grid item md={3}>
|
||||
<Typography>TP: 正确识别到攻击样本的数量</Typography>
|
||||
<br />
|
||||
<Typography>检出率 = TP / (TP + FN)</Typography>
|
||||
</Grid>
|
||||
<Grid item md={3}>
|
||||
<Typography>TN: 正确识别到普通样本的数量</Typography>
|
||||
<br />
|
||||
<Typography>误报率 = FP / (TP + FP)</Typography>
|
||||
</Grid>
|
||||
<Grid item md={3}>
|
||||
<Typography>FP: 将普通样本误报为攻击的数量</Typography>
|
||||
<br />
|
||||
<Typography>准确率 = (TP + TN) / (TP + TN + FP + FN)</Typography>
|
||||
</Grid>
|
||||
<Grid item md={3}>
|
||||
<Typography>FN: 未识别到攻击样本的数量</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function percent(v: number) {
|
||||
return Math.round(v * 10000) / 100 + "%";
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Box, Grid, Button, Typography, Container, Stack } from "@mui/material";
|
||||
import { Carousel } from "react-responsive-carousel";
|
||||
import Version from "@/components/Home/Version";
|
||||
import FriendlyLinks from "@/components/Home/FriendlyLinks";
|
||||
import Features from "@/components/Home/Features";
|
||||
import Title from "@/components/Home/Title";
|
||||
import { getSetupCount } from "@/api/home";
|
||||
// import countUpModule from "countup.js";
|
||||
|
||||
import "react-responsive-carousel/lib/styles/carousel.min.css";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
const IMAGE_LIST = [
|
||||
{
|
||||
name: "可视化仪表盘",
|
||||
url: "/images/album/0.png",
|
||||
},
|
||||
{
|
||||
name: "登录页",
|
||||
url: "/images/album/5.png",
|
||||
},
|
||||
{
|
||||
name: "攻击检测列表",
|
||||
url: "/images/album/1.png",
|
||||
},
|
||||
{
|
||||
name: "攻击检测详情",
|
||||
url: "/images/album/2.png",
|
||||
},
|
||||
{
|
||||
name: "防护站点列表",
|
||||
url: "/images/album/3.png",
|
||||
},
|
||||
{
|
||||
name: "自定义规则列表",
|
||||
url: "/images/album/3.png",
|
||||
},
|
||||
{
|
||||
name: "攻击阻断页面",
|
||||
url: "/images/album/block.png",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
const totalRef = useRef(null);
|
||||
|
||||
const initTotal = async (n: number) => {
|
||||
const countUpModule = await import("countup.js");
|
||||
const anim = new countUpModule.CountUp(totalRef.current!, Math.max(0, n), {
|
||||
duration: 2,
|
||||
});
|
||||
anim.start();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getSetupCount().then((d) => {
|
||||
initTotal(d.total);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<Box sx={{ backgroundColor: "#F8F9FC" }}>
|
||||
<Box sx={{ pt: 16, pb: 18, backgroundColor: "#0F1935", color: "#fff" }}>
|
||||
<Container maxWidth="lg">
|
||||
<Box sx={{ display: { xs: "block", sm: "flex" }, flexWrap: "wrap" }}>
|
||||
<Grid container sx={{ flex: 1 }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h3" sx={{ pb: 3 }}>
|
||||
雷池 Web 应用防火墙{" "}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" sx={{ pb: 3 }}>
|
||||
耗时近 10 年,长亭科技倾情打造,核心检测能力由
|
||||
<Box component="span" color="primary.main" sx={{ px: 1 }}>
|
||||
智能语义分析算法
|
||||
</Box>
|
||||
驱动,专为社区而生,不让黑客越雷池半步。
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={2}>
|
||||
<Button
|
||||
variant="contained"
|
||||
component={Link}
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "auto" },
|
||||
ml: { xs: 2, sm: 2 },
|
||||
mb: { xs: 2, sm: 0 },
|
||||
}}
|
||||
href="https://stack.chaitin.com/tool/detail?id=717"
|
||||
>
|
||||
免费使用
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="neutral"
|
||||
component={Link}
|
||||
target="_blank"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
backgroundColor: "#fff",
|
||||
color: "#000",
|
||||
ml: { xs: 2, sm: 2 },
|
||||
mb: { xs: 2, sm: 0 },
|
||||
width: { xs: "100%", sm: "auto" },
|
||||
"&:hover": {
|
||||
fontWeight: "500",
|
||||
},
|
||||
}}
|
||||
href="https://github.com/chaitin/safeline"
|
||||
startIcon={
|
||||
<Image
|
||||
src="/images/github.png"
|
||||
alt="Logo"
|
||||
width={16}
|
||||
height={16}
|
||||
priority
|
||||
/>
|
||||
}
|
||||
>
|
||||
GitHub
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="neutral"
|
||||
sx={{
|
||||
backgroundColor: "#fff",
|
||||
color: "#000",
|
||||
ml: { xs: 2, sm: 2 },
|
||||
width: { xs: "100%", sm: "auto" },
|
||||
}}
|
||||
component={Link}
|
||||
href="/#groupchat"
|
||||
startIcon={
|
||||
<Image
|
||||
src="/images/wechat-logo.png"
|
||||
alt="Logo"
|
||||
width={16}
|
||||
height={16}
|
||||
priority
|
||||
/>
|
||||
}
|
||||
>
|
||||
讨论组
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: { xs: "center", sm: "right" },
|
||||
pt: { xs: 3, sm: 0 },
|
||||
ml: { xs: 0, sm: 3 },
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/images/403.svg"
|
||||
alt="Logo"
|
||||
width={196}
|
||||
height={196}
|
||||
priority
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
<Container sx={{ mt: -10, color: "#000", pb: 3 }}>
|
||||
<Features />
|
||||
</Container>
|
||||
<Container>
|
||||
<Stack sx={{ pt: 15 }} spacing={3} alignItems="center">
|
||||
<Title title="装机量" />
|
||||
<Typography
|
||||
sx={{
|
||||
color: "primary.main",
|
||||
fontSize: "96px",
|
||||
letterSpacing: "10px",
|
||||
}}
|
||||
ref={totalRef}
|
||||
>-</Typography>
|
||||
</Stack>
|
||||
</Container>
|
||||
<Container
|
||||
sx={{
|
||||
color: "#000",
|
||||
".carousel .control-dots .dot": {
|
||||
backgroundColor: "#000",
|
||||
},
|
||||
".carousel .control-prev.control-arrow": {
|
||||
padding: "20px",
|
||||
borderRadius: "12px 0 0 12px",
|
||||
},
|
||||
".carousel .control-next.control-arrow": {
|
||||
padding: "20px",
|
||||
borderRadius: "0 12px 12px 0",
|
||||
},
|
||||
".carousel .control-prev.control-arrow:before": {
|
||||
borderRightColor: "rgba(0,0,0,0.5)",
|
||||
},
|
||||
|
||||
".carousel .control-next.control-arrow:before": {
|
||||
borderLeftColor: "rgba(0,0,0,0.5)",
|
||||
},
|
||||
".carousel .slide .legend": {
|
||||
width: "30%",
|
||||
marginLeft: "-15%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack sx={{ pt: 15 }} spacing={6} alignItems="center">
|
||||
<Title title="产品展示" />
|
||||
<Box sx={{ boxShadow: "0 12px 25px -12px rgba(93,99,112, 0.2)" }}>
|
||||
<Carousel
|
||||
interval={2000}
|
||||
infiniteLoop
|
||||
autoPlay
|
||||
showStatus={false}
|
||||
showThumbs={false}
|
||||
>
|
||||
{IMAGE_LIST.map((item) => (
|
||||
<Box
|
||||
key={item.url}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<Box component="img" src={item.url} alt={item.name} />
|
||||
<Box
|
||||
className="legend"
|
||||
sx={{
|
||||
opacity: "0.40 !important",
|
||||
py: "4px !important",
|
||||
borderRadius: "4px !important",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" sx={{ fontSize: "14px" }}>
|
||||
{item.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Carousel>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Container>
|
||||
<Container sx={{ color: "#000", pb: 3 }}>
|
||||
<Stack sx={{ pt: 15 }} spacing={3} alignItems="center">
|
||||
<Title title="版本对比" />
|
||||
<Version />
|
||||
</Stack>
|
||||
|
||||
<Stack sx={{ pt: 15 }} id="groupchat" spacing={6} alignItems="center">
|
||||
<Title title="加入讨论组" />
|
||||
<Image
|
||||
src="/images/wechat-light.png"
|
||||
alt="wechat"
|
||||
width={300}
|
||||
height={300}
|
||||
priority
|
||||
/>
|
||||
</Stack>
|
||||
<Box sx={{ pt: 12 }}>
|
||||
<FriendlyLinks />
|
||||
</Box>
|
||||
<Stack sx={{ pt: 6, color: "rgba(0,0,0,0.5)", textAlign: "center" }}>
|
||||
© 2023 北京长亭未来科技有限公司京 ICP 备 19035216 号京公网安备
|
||||
11010802020947 号
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import { type GetStaticProps } from "next";
|
||||
import { useRef } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import Head from "next/head";
|
||||
import SideLayout from "@/layout/SideLayout";
|
||||
import { MarkdownNavbar } from "@/components";
|
||||
import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";
|
||||
|
||||
import {
|
||||
getPostsIds,
|
||||
getPostsGroup,
|
||||
getPostData,
|
||||
type GroupItem,
|
||||
} from "@/utils/posts";
|
||||
|
||||
interface PostsProps {
|
||||
group: GroupItem[];
|
||||
postData: MDXRemoteProps;
|
||||
content: string;
|
||||
headTitle: string;
|
||||
// tocElement: any;
|
||||
}
|
||||
|
||||
const Posts: React.FC<PostsProps> = ({
|
||||
group,
|
||||
postData,
|
||||
content,
|
||||
headTitle,
|
||||
// tocElement,
|
||||
}) => {
|
||||
const domRef = useRef<HTMLElement>();
|
||||
return (
|
||||
<SideLayout list={group}>
|
||||
<Head>
|
||||
<title>{headTitle}</title>
|
||||
</Head>
|
||||
<Box
|
||||
id="markdown-body-warp"
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "calc(100vh - 128px)",
|
||||
overflow: "auto",
|
||||
justifyContent: "space-between",
|
||||
// "&::-webkit-scrollbar": {
|
||||
// display: "none",
|
||||
// },
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
ref={domRef}
|
||||
id="markdown-body"
|
||||
className="markdown-body"
|
||||
sx={{ width: { xs: "100%", sm: "77%" } }}
|
||||
>
|
||||
<MDXRemote {...postData} />
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: "none", sm: "block" },
|
||||
width: "20%",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
}}
|
||||
>
|
||||
<MarkdownNavbar source={content} containerId="markdown-body-warp" />
|
||||
</Box>
|
||||
</Box>
|
||||
</SideLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const paths = getPostsIds();
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
};
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<PostsProps> = async ({
|
||||
params,
|
||||
}) => {
|
||||
const { postData, content } = await getPostData(params?.id as string);
|
||||
const group = getPostsGroup();
|
||||
|
||||
let idToTextMap: Record<string, string> = {};
|
||||
group.forEach((g) => {
|
||||
g.list.forEach((nav) => {
|
||||
idToTextMap[nav.id] = nav.title;
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
group,
|
||||
content,
|
||||
postData,
|
||||
headTitle: idToTextMap[params?.id as string] + " - 长亭雷池 WAF 社区版",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default Posts;
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: "社区版 vs 企业版"
|
||||
group:
|
||||
title: "关于雷池"
|
||||
order: 3
|
||||
order: 1
|
||||
---
|
||||
# 社区版 vs 企业版
|
||||
@@ -1,118 +0,0 @@
|
||||
---
|
||||
title: "版本更新记录"
|
||||
group: "关于雷池"
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 版本更新记录
|
||||
|
||||
[版本升级方法](https://waf-ce.chaitin.cn/posts/guide_upgrade)
|
||||
|
||||
## [1.8.1] - 2023-06-09
|
||||
|
||||
- 修复了「全部请求」和「仅拦截」数据一样的问题
|
||||
|
||||
## [1.8.0] - 2023-06-09
|
||||
|
||||
### 新增
|
||||
|
||||
- 数据统计页面增加访问来源地区、流量统计,更好把控网站运营情况
|
||||
|
||||

|
||||
|
||||
### 优化
|
||||
|
||||
- 更新语义引擎版本,优化了一大批检测逻辑,降低误报
|
||||
- 优化了部分操作提示信息:
|
||||
- IP 组正在使用时,无法被删除的提示
|
||||
- 未创建 IP 组时,在黑白名单中无法选择属于 IP 组的提示
|
||||
- 添加站点时,域名格式错误的提示
|
||||
|
||||
## [1.7.1] - 2023-06-05
|
||||
|
||||
### 修复
|
||||
- 部分情况下无法打开日志详情页面的问题
|
||||
- 部分情况下页面查询数量只有 10 条的问题
|
||||
|
||||
## [1.7.0] - 2023-06-01
|
||||
|
||||
### 新增
|
||||
- 新增 “IP 组” 功能,可以快速配置大量 IP 的黑/白名单了
|
||||
- 防护策略增加 “仅观察” 配置
|
||||
- 防护策略增加 “批量配置为” 按钮,可以快速切换所有模块的防护策略
|
||||
|
||||
### 优化
|
||||
- 自定义规则列表增加翻页
|
||||
- 优化规则生效顺序,现在会优先执行完所有白名单,再执行黑名单
|
||||
|
||||
## [1.6.0] - 2023-05-25
|
||||
|
||||
### 新增
|
||||
- 自定义规则支持匹配 Header 和 Body
|
||||
- 检测日志支持按域名搜索
|
||||
- 支持命令行清理检测日志和统计信息
|
||||
|
||||
## [1.5.1] - 2023-05-18
|
||||
|
||||
- 修复了自定义规则切换白名单之后,无法创建/编辑的问题
|
||||
|
||||
## [1.5.0] - 2023-05-18
|
||||
|
||||
### 新增
|
||||
- 支持 i18n
|
||||
- 数据统计新增 “今日请求错误情况”
|
||||
- 检测日志的筛选条件现在会显示在 URL,方便保存
|
||||
|
||||
### 优化
|
||||
- 修复自定义规则的编辑表单,有时候会丢失编辑中数据的问题
|
||||
- 修复 Safari 浏览器上的一些显示问题
|
||||
- 修复 Payload 中存在非 Unicode 编码时,检测日志会入库失败的问题(不影响拦截)
|
||||
- 修复新增的 “HTTP 请求走私” 攻击类型会被错误地展示成 “未知” 攻击类型的问题
|
||||
|
||||
## [1.4.0] - 2023-05-12
|
||||
|
||||
### 新增
|
||||
- 自定义规则支持匹配域名
|
||||
- 支持在一条自定义规则内,设置多个匹配条件
|
||||
- 站点列表新增 “今日访问 / 拦截量”
|
||||
|
||||
### 优化
|
||||
- 优化交互和提示文案、修复已知问题
|
||||
|
||||
## [1.3.0] - 2023-05-05
|
||||
|
||||
### 新增
|
||||
- 支持按照源 IP、攻击类型、URL 筛选检测日志
|
||||
|
||||
### 修复
|
||||
- 修复 dashboard 在部分低版本浏览器下的兼容问题
|
||||
- 修复按源 IP 添加自定义规则时,添加不了 /8 和更大的网段的问题
|
||||
|
||||
## [1.2.0] - 2023-04-27
|
||||
|
||||
### 新增
|
||||
- 新增了数据统计页面,可以直观的看到流量大小
|
||||
- 支持配置源 IP 提取方式,解决了源 IP 获取不对的问题
|
||||
- 支持自定义检测策略,可以动态调整检测引擎
|
||||
|
||||
## [1.1.0] - 2023-04-20
|
||||
|
||||
### 新增
|
||||
- 支持根据 IP 和 URL 特征配置黑白名单
|
||||
- 默认开启高防模式
|
||||
|
||||
### 优化
|
||||
- 支持在日志详情中展示响应报文
|
||||
- 服务器时间不准导致 TOTP 无法登录时增加了提示语
|
||||
- 修复了上游服务器填 HTTPS 时端口解析不正确的问题
|
||||
- 优化了 SSL 上传逻辑,体验更好
|
||||
|
||||
## [1.0.0] - 2023-04-13
|
||||
|
||||
- 站点配置
|
||||
|
||||
## [0.9.0] - 2023-03-20
|
||||
|
||||
- OTP 登录
|
||||
- 攻击检测日志
|
||||
- 默认防护策略
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: "网站无法访问"
|
||||
group: "常见问题排查"
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 网站无法访问
|
||||
|
||||
为了方便讨论问题, 我们假设:
|
||||
|
||||
在没有 SafeLine 的时候,假设小明的域名 `xiaoming.com` 通过 DNS 解析到自己主机 `192.168.1.111`,上面在 `:8888` 端口监听了自己的服务(网站/博客/靶场)等等。
|
||||
|
||||
小明通过 `http://xiaoming.com:8888` 或者 `192.168.1.111:8888` 来访问自己的服务。
|
||||
|
||||
## 如果返回502
|
||||

|
||||
如果出现类似相应,请检查上游服务器地址是否配置正确或者雷池是否能够访问能访问到上游服务器
|
||||
|
||||
## 请求返回缓慢
|
||||
1. 首先检查服务器负载情况
|
||||
2. 检查雷池服务器与上游服务器的网络状况,检查命令`curl -H "Host: <SafeLine-IP>" -vv -o /dev/null -s -w 'time_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n' http://xiaoming.com:8888` <br/>
|
||||
如果 `time_namelookup` 时间过大,请检查dns server配置
|
||||
如果 `time_connect` 时间过大,请检查与上游服务器之间的网络状态
|
||||
如果 `time_starttransfer` 时间过大,请检查上游服务器状态,是否出现资源过载情况
|
||||
@@ -1,77 +0,0 @@
|
||||
---
|
||||
title: "配置问题"
|
||||
group: "常见问题排查"
|
||||
order: 10
|
||||
---
|
||||
|
||||
# 配置问题
|
||||
|
||||
## 站点配置问题
|
||||
|
||||
在没有 SafeLine 的时候,假设小明的域名 `xiaoming.com` 通过 DNS 解析到自己主机 `192.168.1.111`,上面在 `:8888` 端口监听了自己的服务(网站/博客/靶场)等等。
|
||||
|
||||
小明通过 `http://xiaoming.com:8888` 或者 `192.168.1.111:8888` 来访问自己的服务。
|
||||
|
||||
### 我该如何配置?/ 域名填什么?/ 端口怎么写?/ 上游服务器是什么?
|
||||
|
||||
目前社区版 SafeLine 支持的是反向代理的方式接入站点,也就是类似于一台 nginx 服务。这时候小明需要做的就是让流量先抵达 SafeLine,然后经过 SafeLine 检测之后,再转发给自己原先的业务。
|
||||
|
||||
小明只需要按照如下方式创建站点即可:
|
||||
|
||||
- `xiaoming.com` 填入页面的「域名」
|
||||
- `:7777` 填入「端口」;或者别的任意非 `:8888`和 `:9443`(被 SafeLine 后台管理页面占用)端口
|
||||
- `http://192.168.1.111:8888` 填入「上游服务器」
|
||||
|
||||
创建之后,就可以通过 `http://xiaoming.com:7777` 或者 `192.168.1.111:7777` 访问自己的服务了,这时候请求到 `http://xiaoming.com:7777` 的流量都会被 SafeLine 检测。经过 SafeLine 过滤后,安全的流量会被透传到原先的 `:8888` 业务服务器(即上游服务器)。
|
||||
|
||||
**注:直接访问 `http://xiaoming.com:8888` 的流量,仍然不会被 SafeLine 检测,因为流量并没有经过 SafeLine,而是绕过 SafeLine 直接打到了上游服务器上**
|
||||
|
||||
**如果按照如上配置,还是无法成功访问到我的上游服务器,接着往下看,尝试逐项进行问题排查。**
|
||||
|
||||
## 配置完成之后,还是没有成功访问到上游服务器
|
||||
|
||||
下面例子都还按照上面小明的环境情况介绍。
|
||||
|
||||
#### 1. netstat/ss/lsof 查看端口占用情况
|
||||
|
||||
先确认下 `0.0.0.0:7777` 端口是否有服务在监听。SafeLine 使用 Tengine 来作为代理服务,所以正常来说,应该有一个 nginx 进程监听在 `:7777` 端口。如果没有的话,可能是 SafeLine 的问题,请通过社群或者 Github issue 提交反馈。
|
||||
|
||||
如果有的话,继续往下排查。
|
||||
|
||||
#### 2. 是否是被非 SafeLine 的 nginx 监听
|
||||
|
||||
基于第一步,已经能确认 `:7777` 是被某个 nginx 进程监听了,但是并不能确认是被 SafeLine 自己的 nginx 监听。排查是否自己原先有 nginx conf 中配置了 server 监听 `:7777`。如果有的话,手动解决冲突。要么修改自己原先的 nginx conf,要么修改 SafeLine 的站点配置。
|
||||
|
||||
也可以直接通过 `docker logs -f safeline-tengine` 确认 SafeLine 是否有 nginx 报错说端口冲突。
|
||||
|
||||
*常见的情况就是自己原先有一个服务监听在 `:80`,SafeLine 上配置了站点也监听 `:80` 端口,就产生了冲突。*
|
||||
|
||||
如果没有的话,继续往下排查。
|
||||
|
||||
#### 3. 是否被防火墙拦截
|
||||
|
||||
有操作系统本身的防火墙,还有可能是云服务商的防火墙。根据实际情况逐项排查,配置开放端口的 TCP 访问。
|
||||
|
||||
出现如下情况,可能就是被中间某防火墙拦截了:
|
||||
|
||||
1. 在 `192.168.1.111` 上 curl -vv `127.0.0.1:7777` 能访问到业务,有 HTTP 返回码。
|
||||
2. 在本机 curl -vv `192.168.1.111:7777` 不通,没有 HTTP 响应;`telnet 192.168.1.111 7777` 返回 `Unable to connect to remote host: Connection refused`
|
||||
|
||||
#### 4. SafeLine 是否能访问到上游服务器
|
||||
|
||||
小明的情况是 SafeLine 和业务在同一台机器,一般不会有不同机器之间的网络问题,但是也建议在 SafeLine 部署的机器上测试一下。如果是两台机器的情况下,需要考虑是否互相之间能正常通信。
|
||||
|
||||
直接 `curl -H "Host: <SafeLine-IP>" -vv http://xiaoming.com:8888` 测一下是否能访问到。如果不行,需要自行排查为什么 SafeLine 的机器没法访问到。
|
||||
|
||||
注:这里需要 -H 指定 Host `Host: <SafeLine-IP>` 进行连通性测试。收到比较多的反馈,在 WAF 上直接配置上游服务器为 HTTPS 的域名,比如 `https://xiaoming.com`。实际场景是希望先测试 WAF 能力正常后再把域名解析切到 WAF 进行上线。这种本地测试的场景,需要修改本机 host,把 `xiaoming.com` 解析到 `SafeLine-IP`,否则可能会无法成功代理。因为 SafeLine 向上游服务器转发时,代理请求中的 Host 使用的是原始 HTTP 请求中的 Host,此时需要自行判断上游业务服务器能够正确处理该代理请求(例如上游业务服务器在 Host 没有匹配自己的站点名称时,是否能够处理)
|
||||
|
||||
#### 5. 其他情况
|
||||
|
||||
如果执行了 1-4:
|
||||
|
||||
1. 确认有 nginx 进程监听了 SafeLine 机器的 `0.0.0.0:7777` 端口
|
||||
2. 确认 SafeLine tengine 无端口冲突报错
|
||||
3. 确认主机和云服务商的防火墙都没有限制 `:7777` 端口的 TCP 访问
|
||||
4. 确认在 SafeLine 上能访问到「上游服务器」
|
||||
|
||||
问题还是没有解决,可能是 SafeLine 产品的问题,请通过社群或者 Github issue 提交反馈。
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
title: "其他问题"
|
||||
group: "常见问题排查"
|
||||
order: 10
|
||||
---
|
||||
|
||||
# 其他问题
|
||||
### 为什么我的检测日志中的攻击IP显示的是负载均衡的IP
|
||||
因为雷池的源 IP 获取方式默认为**从 Socket 中获取**,如需更改请至 通用配置 -> 源 IP 获取方式 处更改
|
||||

|
||||
|
||||
### 如何清理数据库中的统计信息和检测日志
|
||||
|
||||
***注意:该操作会清除所有日志信息,且不可恢复***
|
||||
|
||||
```shell
|
||||
docker exec -it safeline-mgt-api cleanlogs
|
||||
```
|
||||
|
||||
|
||||
### 如何记录所有访问雷池的请求
|
||||
默认情况下雷池是并不会保存请求记录的,如果需要保存请求记录,可以修改**resources/nginx/nginx.conf**
|
||||

|
||||
如图所示,去掉文件第99行的注释,删除第100行的内容,保存后运行命令检查配置文件
|
||||
```shell
|
||||
docker exec safeline-tengine nginx -t
|
||||
```
|
||||
检查应显示
|
||||
```shell
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
最后应用配置文件
|
||||
```shell
|
||||
docker exec safeline-tengine nginx -s reload
|
||||
```
|
||||
配置生效后,访问日志将会保存至**logs/nginx**
|
||||
|
||||
***注意:该操作会会加快对硬盘的消耗,请定时清理访问日志***
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: "防护不生效"
|
||||
group: "常见问题排查"
|
||||
order: 4
|
||||
---
|
||||
|
||||
# 防护不生效
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: "配置防护站点"
|
||||
group: "上手指南"
|
||||
order: 4
|
||||
---
|
||||
|
||||
# 配置防护站点
|
||||
|
||||
|
||||

|
||||
|
||||
💡 TIPS: 添加后,执行 `curl -H "Host: <域名>" http://<WAF IP>:<端口>` 应能获取到业务网站的响应。
|
||||
|
||||
## 将网站流量切到雷池
|
||||
|
||||
- 若网站通过域名访问,则可将域名的 DNS 解析指向雷池所在设备
|
||||

|
||||
- 若网站前有 nginx 、负载均衡等代理设备,则可将雷池部署在代理设备和业务服务器之间,然后将代理设备的 upstream 指向雷池
|
||||

|
||||
|
||||
## 如何配置https
|
||||

|
||||
|
||||
## 测试防护效果
|
||||
|
||||
[测试防护效果](/posts/guide_test)
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
title: "安装雷池"
|
||||
group: "上手指南"
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 安装雷池
|
||||
|
||||
## 配置需求
|
||||
|
||||
- 操作系统:Linux
|
||||
- 指令架构:x86_64
|
||||
- 软件依赖:Docker 20.10.6 版本以上
|
||||
- 软件依赖:Docker Compose 2.0.0 版本以上
|
||||
- 最小化环境:1 核 CPU / 1 GB 内存 / 10 GB 磁盘
|
||||
|
||||
可以逐行执行以下命令来确认服务器配置
|
||||
|
||||
```shell
|
||||
uname -m # 查看指令架构
|
||||
docker version # 查看 Docker 版本
|
||||
docker compose version # 查看 Docker Compose 版本
|
||||
docker-compose version # 同上(兼容老版本 Docker Compose)
|
||||
cat /proc/cpuinfo # 查看 CPU 信息
|
||||
cat /proc/meminfo # 查看内存信息
|
||||
df -h # 查看磁盘信息
|
||||
```
|
||||
|
||||
有三种安装方式供选择
|
||||
|
||||
- [在线安装](#在线安装) : 推荐安装方式
|
||||
- [离线安装](#离线安装) : 服务器无法联网时选择
|
||||
- [一键安装](#使用牧云助手安装) : 最简单的安装方式
|
||||
|
||||
## 在线安装
|
||||
|
||||
***如果服务器可以访问互联网环境,推荐使用在线安装方式***
|
||||
|
||||
### 在线安装 Docker
|
||||
|
||||
> 如果已经安装 Docker 和 Docker Compose,可跳过这一步直接进入 [在线安装雷池](#在线安装雷池)
|
||||
|
||||
执行以下命令来安装 Docker 和 Docker Compose
|
||||
|
||||
```bash
|
||||
curl -fsSLk https://get.docker.com/ | bash
|
||||
```
|
||||
|
||||
安装命令结束后,可以执行以下命令来确认 Docker 和 Docker Compose 是否安装成功
|
||||
|
||||
```
|
||||
docker version # 查看 Docker 版本
|
||||
docker compose version # 查看 Docker Compose 版本
|
||||
```
|
||||
|
||||
### 在线安装雷池
|
||||
|
||||
执行以下命令创建并进入雷池安装目录
|
||||
|
||||
```
|
||||
mkdir -p safeline # 创建 safeline 目录
|
||||
cd safeline # 进入 safeline 目录
|
||||
```
|
||||
|
||||
执行以下命令,将会自动下载镜像,并完成环境的初始化
|
||||
|
||||
```
|
||||
curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh | bash
|
||||
```
|
||||
|
||||
执行以下命令启动雷池
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
经过以上步骤,你的雷池已经安装好了,下一步请参考 [登录雷池](/posts/guide_login)
|
||||
|
||||
## 离线安装
|
||||
|
||||
如果你的服务器无法连接互联网环境,可以使用离线安装方式
|
||||
|
||||
> 这里忽略 Docker 安装的过程
|
||||
|
||||
首先,你要找一台能够访问互联网的服务器,执行以下命令拉取镜像,打包到 image.tar 文件中
|
||||
```
|
||||
docker pull chaitin/safeline-tengine:latest
|
||||
docker pull chaitin/safeline-mgt-api:latest
|
||||
docker pull chaitin/safeline-mario:latest
|
||||
docker pull chaitin/safeline-detector:latest
|
||||
docker pull postgres:15.2
|
||||
docker pull redis:7.0.11
|
||||
|
||||
docker save -o image.tar chaitin/safeline-tengine:latest chaitin/safeline-mgt-api:latest chaitin/safeline-mario:latest chaitin/safeline-detector:latest postgres:15.2 redis:7.0.11
|
||||
```
|
||||
|
||||
将 image.tar 文件传输到需要安装雷池的服务器上,执行以下命令加载镜像
|
||||
|
||||
```
|
||||
docker load -i image.tar
|
||||
```
|
||||
|
||||
执行以下命令创建并进入雷池安装目录
|
||||
|
||||
```
|
||||
mkdir -p safeline # 创建 safeline 目录
|
||||
cd safeline # 进入 safeline 目录
|
||||
```
|
||||
|
||||
下载 [编排脚本](https://waf-ce.chaitin.cn/release/latest/compose.yaml) 并传输到 safeline 目录中
|
||||
|
||||
|
||||
执行以下命令,生成雷池运行所需的相关环境变量
|
||||
|
||||
```
|
||||
echo "SAFELINE_DIR=$(pwd)" > .env
|
||||
echo "IMAGE_TAG=latest" >> .env
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=169.254.0" >> .env
|
||||
```
|
||||
|
||||
执行以下命令启动雷池
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
经过以上步骤,你的雷池已经安装好了,下一步请参考 [登录雷池](/posts/guide_login)
|
||||
|
||||
## 使用牧云助手安装
|
||||
|
||||
也可以使用 [牧云主机管理助手](https://collie.chaitin.cn/) 进行一键安装
|
||||
|
||||

|
||||
|
||||
参考视频教程 [用 “白嫖的云主机” 一键安装 “开源的Web防火墙”](https://www.bilibili.com/video/BV1sh4y1t7Pk/)
|
||||
|
||||
## 常见安装问题
|
||||
|
||||
请参考 [安装问题](/posts/faq_install)
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "登录雷池"
|
||||
group: "上手指南"
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 登录雷池
|
||||
|
||||
浏览器打开后台管理页面 `https://<waf-ip>:9443`。根据界面提示,使用 **支持 TOTP 的认证软件** 扫描二维码,然后输入动态口令登录:
|
||||
|
||||

|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
title: "升级雷池"
|
||||
group: "上手指南"
|
||||
order: 6
|
||||
---
|
||||
|
||||
# 升级雷池
|
||||
|
||||
## 自动一键更新
|
||||
|
||||
**WARN: 雷池 SafeLine 服务会重启,流量会中断一小段时间,根据业务情况选择合适的时间来执行升级操作。**
|
||||
|
||||
```shell
|
||||
# 请到 compose.yaml 同级目录下执行下面脚本
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
```
|
||||
**有部分环境的默认 SafeLine 安装路径是在 `/data/safeline-ce`,安装之后可能会发现需要重新绑定 OTP、配置丢失等情况,可以修改 .env 的 `SAFELINE_DIR` 变量,指向 `/data/safeline-ce`**
|
||||
|
||||
## 手动更新镜像
|
||||
|
||||
适用于 docker hub 拉取镜像失败的场景,手动更新镜像,注意还是要执行 `upgrade.sh` 来处理 `.env` 的更新,否则有可能会因为缺少参数而启动失败。
|
||||
|
||||
### 1. 在一台能够从 docker hub 拉取镜像的机器上执行
|
||||
|
||||
```shell
|
||||
|
||||
# 拉取镜像
|
||||
docker pull chaitin/safeline-tengine:latest
|
||||
docker pull chaitin/safeline-mgt-api:latest
|
||||
docker pull chaitin/safeline-mario:latest
|
||||
docker pull chaitin/safeline-detector:latest
|
||||
docker pull postgres:15.2
|
||||
|
||||
# 打包镜像
|
||||
docker save -o image.tar chaitin/safeline-tengine:latest chaitin/safeline-mgt-api:latest chaitin/safeline-mario:latest chaitin/safeline-detector:latest postgres:15.2
|
||||
|
||||
# 传输到 SafeLine 要部署的目标服务器
|
||||
# scp image.tar <target-server>:/root/
|
||||
```
|
||||
### 2. 在目标服务器 load 镜像
|
||||
|
||||
```shell
|
||||
docker load -i image.tar
|
||||
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
```
|
||||
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
1. Use a more-intuitive box-sizing model.
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/*
|
||||
2. Remove default margin
|
||||
*/
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
3. Allow percentage-based heights in the application
|
||||
*/
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
Typographic tweaks!
|
||||
4. Add accessible line-height
|
||||
5. Improve text rendering
|
||||
*/
|
||||
body {
|
||||
line-height: 1.5;
|
||||
scrollbar-width: 5px;
|
||||
scrollbar-color: #9b9b9b #363636;
|
||||
}
|
||||
|
||||
/*
|
||||
6. Improve media defaults
|
||||
*/
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
7. Remove built-in form typography styles
|
||||
*/
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
8. Avoid text overflows
|
||||
*/
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/*
|
||||
9. Create a root stacking context
|
||||
*/
|
||||
#root,
|
||||
#__next {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Ping Fang SC', 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a,
|
||||
a:link,
|
||||
a:visited,
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
51% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wings {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
|
||||
0%,
|
||||
20%,
|
||||
53%,
|
||||
to {
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
40%,
|
||||
43% {
|
||||
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
transform: translate3d(0, -5px, 0);
|
||||
}
|
||||
|
||||
70% {
|
||||
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
transform: translate3d(0, -7px, 0);
|
||||
}
|
||||
|
||||
80% {
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translate3d(0, -2px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideY {
|
||||
|
||||
0%,
|
||||
50%,
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideX {
|
||||
|
||||
0%,
|
||||
50%,
|
||||
100% {
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes scale {
|
||||
0% {
|
||||
transform: scale(1.5) rotate(90deg);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1) rotate(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.dark ::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
/* 纵向滚动条*/
|
||||
height: 5px;
|
||||
/* 横向滚动条 */
|
||||
background-color: #363636;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/*定义滚动条轨道 内阴影*/
|
||||
.dark ::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
|
||||
background-color: #363636;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/*定义滑块 内阴影*/
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
|
||||
background-color: #9b9b9b;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
.light ::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
/* 纵向滚动条*/
|
||||
height: 5px;
|
||||
/* 横向滚动条 */
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/*定义滚动条轨道 内阴影*/
|
||||
.light ::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
|
||||
background-color: #afafaf;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/*定义滑块 内阴影*/
|
||||
.light ::-webkit-scrollbar-thumb {
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
|
||||
background-color: #666;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* @font-face {
|
||||
font-family: 'HYXIAOBOZHEZHITIJ';
|
||||
src: url('../fonts/HYXIAOBOZHEZHITIJ.TTF');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
} */
|
||||
|
||||
/* @font-face {
|
||||
font-family: 'Mono-Bold';
|
||||
src: url('../fonts/JetBrainsMono-Bold.ttf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
} */
|
||||
|
||||
|
||||
.markdown-navigation {
|
||||
font-size: 14px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Helvetica", "Arial", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-left: 2px solid #E9E9E9;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-anchor {
|
||||
display: block;
|
||||
color: #bbb;
|
||||
transition: all 0.2s;
|
||||
margin: 0.8em 0;
|
||||
/* font-weight: lighter; */
|
||||
line-height: 2em;
|
||||
padding-right: 1.8em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-anchor:hover,
|
||||
.markdown-navigation .title-anchor.active {
|
||||
background-color: #f8f8f8;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-anchor.active {
|
||||
color: #007fff;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-anchor small {
|
||||
/* margin: 0 0.8em; */
|
||||
margin-right: 0.8em;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-level1 {
|
||||
display: none;
|
||||
color: #000;
|
||||
font-size: 1.2em;
|
||||
padding-left: 0em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-level2 {
|
||||
color: #000;
|
||||
font-size: 1em;
|
||||
/* padding-left: 2em; */
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-level3 {
|
||||
color: #666;
|
||||
font-size: 0.8em;
|
||||
/* padding-left: 3em; */
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.markdown-navigation .title-level4 {
|
||||
color: #222;
|
||||
font-size: 0.72em;
|
||||
/* padding-left: 4em; */
|
||||
}
|
||||
|
||||
.markdown-navigation .title-level5 {
|
||||
color: #999;
|
||||
font-size: 0.72em;
|
||||
/* padding-left: 5em; */
|
||||
}
|
||||
|
||||
.markdown-navigation .title-level6 {
|
||||
color: #bbb;
|
||||
font-size: 0.72em;
|
||||
/* padding-left: 6em; */
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
export const dark = {
|
||||
primary: {
|
||||
main: '#7267EF',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
secondary: {
|
||||
lighter: '#D6E4FF',
|
||||
light: '#84A9FF',
|
||||
main: '#2196F3',
|
||||
dark: '#1939B7',
|
||||
darker: '#091A7A',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
info: {
|
||||
lighter: '#D0F2FF',
|
||||
light: '#74CAFF',
|
||||
main: '#1890FF',
|
||||
dark: '#0C53B7',
|
||||
darker: '#04297A',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
|
||||
success: {
|
||||
lighter: '#E9FCD4',
|
||||
light: '#AAF27F',
|
||||
main: '#02BFA5',
|
||||
dark: '#229A16',
|
||||
darker: '#08660D',
|
||||
contrastText: 'rgba(0,0,0,0.7)',
|
||||
},
|
||||
|
||||
warning: {
|
||||
lighter: '#FFF7CD',
|
||||
light: '#FFE16A',
|
||||
main: '#FFBF00',
|
||||
dark: '#B78103',
|
||||
darker: '#7A4F01',
|
||||
contrastText: 'rgba(0,0,0,0.7)',
|
||||
},
|
||||
neutral: {
|
||||
main: '#232C59',
|
||||
contrastText: 'rgba(255, 255, 255, 0.60)',
|
||||
},
|
||||
error: {
|
||||
lighter: '#FFE7D9',
|
||||
light: '#FFA48D',
|
||||
main: '#FF1844',
|
||||
dark: '#B72136',
|
||||
darker: '#7A0C2E',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
text: {
|
||||
primary: '#fff',
|
||||
secondary: 'rgba(255,255,255,0.7)',
|
||||
auxiliary: 'rgba(255,255,255,0.5)',
|
||||
slave: 'rgba(255,255,255,0.05)',
|
||||
disabled: 'rgba(255,255,255,0.15)',
|
||||
inversePrimary: '#000',
|
||||
inverseAuxiliary: 'rgba(255,255,255,0.5)',
|
||||
inverseDisabled: 'rgba(255,255,255,0.15)',
|
||||
},
|
||||
divider: '#38405D',
|
||||
background: {
|
||||
paper0: '#111936',
|
||||
paper: '#1A223F',
|
||||
paper2: '#212A46',
|
||||
default: 'rgba(255,255,255,0.6)',
|
||||
disabled: '#2E375F',
|
||||
chip: '#232D4F',
|
||||
circle: '#3B476A',
|
||||
},
|
||||
common: {},
|
||||
shadowColor: '#0B1233',
|
||||
table: {
|
||||
head: {
|
||||
backgroundColor: '#232D4F',
|
||||
color: 'rgba(255,255,255,0.7)',
|
||||
},
|
||||
row: {
|
||||
hoverColor: '#212A46',
|
||||
},
|
||||
cell: {
|
||||
borderColor: '#232D4F',
|
||||
},
|
||||
},
|
||||
|
||||
charts: {
|
||||
color: ['#7267EF', '#02BFA5'],
|
||||
},
|
||||
} as const
|
||||
|
||||
export const light = {
|
||||
primary: {
|
||||
main: '#7635DC',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
secondary: {
|
||||
lighter: '#D6E4FF',
|
||||
light: '#84A9FF',
|
||||
main: '#3366FF',
|
||||
dark: '#1939B7',
|
||||
darker: '#091A7A',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
info: {
|
||||
lighter: '#D0F2FF',
|
||||
light: '#74CAFF',
|
||||
main: '#1890FF',
|
||||
dark: '#0C53B7',
|
||||
darker: '#04297A',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
|
||||
success: {
|
||||
lighter: '#E9FCD4',
|
||||
light: '#AAF27F',
|
||||
main: '#02BFA5',
|
||||
mainShadow: '#02BFA5',
|
||||
dark: '#229A16',
|
||||
darker: '#08660D',
|
||||
contrastText: 'rgba(0,0,0,0.7)',
|
||||
},
|
||||
|
||||
warning: {
|
||||
lighter: '#FFF7CD',
|
||||
light: '#FFE16A',
|
||||
main: '#FFBF00',
|
||||
dark: '#B78103',
|
||||
darker: '#7A4F01',
|
||||
contrastText: 'rgba(0,0,0,0.7)',
|
||||
},
|
||||
neutral: {
|
||||
main: '#F4F6F8',
|
||||
contrastText: 'rgba(0, 0, 0, 0.60)',
|
||||
},
|
||||
error: {
|
||||
lighter: '#FFE7D9',
|
||||
light: '#FFA48D',
|
||||
main: '#FF1844',
|
||||
dark: '#B72136',
|
||||
darker: '#7A0C2E',
|
||||
contrastText: '#fff',
|
||||
},
|
||||
divider: '#E3E8EF',
|
||||
text: {
|
||||
primary: '#000',
|
||||
secondary: 'rgba(0,0,0,0.7)',
|
||||
auxiliary: 'rgba(0,0,0,0.5)',
|
||||
slave: 'rgba(0,0,0,0.05)',
|
||||
disabled: 'rgba(0,0,0,0.15)',
|
||||
inversePrimary: '#fff',
|
||||
inverseAuxiliary: 'rgba(255,255,255,0.5)',
|
||||
inverseDisabled: 'rgba(255,255,255,0.15)',
|
||||
},
|
||||
background: {
|
||||
paper0: '#fff',
|
||||
paper: '#fff',
|
||||
paper2: '#f6f8fa',
|
||||
default: '#fff',
|
||||
chip: '#F4F6F8',
|
||||
circle: '#E6E8EC',
|
||||
},
|
||||
|
||||
shadowColor: 'rgba(145,158,171,0.2)',
|
||||
table: {
|
||||
head: {
|
||||
backgroundColor: '#F4F6F8',
|
||||
color: 'rgba(0,0,0,0.7)',
|
||||
},
|
||||
row: {
|
||||
hoverColor: '#F9FAFB',
|
||||
},
|
||||
cell: {
|
||||
borderColor: '#F3F4F5',
|
||||
},
|
||||
},
|
||||
charts: {
|
||||
color: ['#673AB7', '#02BFA5'],
|
||||
},
|
||||
}
|
||||
|
||||
export type Color = typeof dark
|
||||
@@ -1,204 +0,0 @@
|
||||
import { type Color } from './color'
|
||||
export default function componentStyleOverrides(color: Color) {
|
||||
return {
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: ({ ownerState, theme }: any) => {
|
||||
return {
|
||||
boxShadow: 'none',
|
||||
'&:hover': {
|
||||
boxShadow: 'none',
|
||||
...(ownerState.color === 'neutral' && {
|
||||
color: color.primary.main,
|
||||
fontWeight: 700,
|
||||
}),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiLoadingButton: {
|
||||
styleOverrides: {
|
||||
root: ({ ownerState, theme }: any) => {
|
||||
return {
|
||||
// position: 'relative',
|
||||
// ...(ownerState.loading && {
|
||||
// backgroundImage: 'linear-gradient(263deg, #3892EE 0%, #2E7AE9 100%)',
|
||||
// boxShadow: '0px 2px 8px 0px rgba(133,190,245,0.5)',
|
||||
// '&.MuiLoadingButton-loading': {
|
||||
// color: '#fff',
|
||||
// },
|
||||
// '&:before': {
|
||||
// content: "''",
|
||||
// position: 'absolute',
|
||||
// top: '-1px',
|
||||
// right: '-1px',
|
||||
// bottom: '-1px',
|
||||
// left: '-1px',
|
||||
// zIndex: 1,
|
||||
// background: '#fff',
|
||||
// opacity: 0.35,
|
||||
// transition: 'opacity .2s',
|
||||
// pointerEvents: 'none',
|
||||
// },
|
||||
// }),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
MuiListItemButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
// paddingTop: '10px',
|
||||
// paddingBottom: '10px',
|
||||
// borderRadius: '8px',
|
||||
// '&.Mui-selected': {
|
||||
// color: theme.menuSelected,
|
||||
// backgroundColor: theme.menuSelectedBack,
|
||||
// '&:hover': {
|
||||
// backgroundColor: theme.menuSelectedBack,
|
||||
// },
|
||||
// '& .MuiListItemIcon-root': {
|
||||
// color: theme.menuSelected,
|
||||
// },
|
||||
// },
|
||||
// '&:hover': {
|
||||
// backgroundColor: 'transparent',
|
||||
// color: theme.menuSelected,
|
||||
// '& .MuiListItemIcon-root': {
|
||||
// color: theme.menuSelected,
|
||||
// },
|
||||
// },
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiListItemIcon: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
// minWidth: '36px',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiFormControlLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
// marginRight: '24px',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
// '.MuiInputBase-colorPrimary&:hover:not(.Mui-disabled):before': {
|
||||
// borderColor: theme.colors?.primaryMain,
|
||||
// },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
MuiFormControl: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'.MuiFormLabel-asterisk': {
|
||||
color: color.error.main,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableRow: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&:hover': {
|
||||
'.MuiTableCell-root': {
|
||||
// backgroundColor: color.table.row.hoverColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableBody: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'.MuiTableRow-root:hover': {
|
||||
'.MuiTableCell-root': {
|
||||
backgroundColor: color.table.row.hoverColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
MuiTableCell: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
background: color.background.paper,
|
||||
lineHeight: 1.5,
|
||||
fontSize: '14px',
|
||||
paddingTop: '24px',
|
||||
paddingBottom: '24px',
|
||||
borderColor: color.table.cell.borderColor,
|
||||
paddingLeft: 0,
|
||||
'&:first-of-type': {
|
||||
paddingLeft: '32px',
|
||||
},
|
||||
},
|
||||
head: {
|
||||
backgroundColor: color.table.head.backgroundColor,
|
||||
color: color.table.head.color,
|
||||
fontSize: '12px',
|
||||
height: '24px',
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
defaultProps: {
|
||||
elevation: 0,
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
defaultProps: {
|
||||
elevation: 1,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ ownerState }: any) => {
|
||||
return {
|
||||
...(ownerState.elevation === 0 && {
|
||||
backgroundColor: color.background.paper0,
|
||||
}),
|
||||
...(ownerState.elevation === 2 && {
|
||||
backgroundColor: color.background.paper2,
|
||||
}),
|
||||
backgroundImage: 'none',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiChip: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: '4px'
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
defaultProps: {
|
||||
elevation: 1,
|
||||
},
|
||||
},
|
||||
MuiOutlinedInput: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: color.primary.main,
|
||||
},
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: color.divider,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { zhCN } from "@mui/material/locale";
|
||||
import { createTheme } from "@mui/material/styles";
|
||||
|
||||
import * as colors from "./color";
|
||||
import { type Color } from "./color";
|
||||
import componentStyleOverrides from "./componentStyleOverrides";
|
||||
import themePalette from "./palette";
|
||||
import shadows from "./shadows";
|
||||
import themeTypography from "./typography";
|
||||
|
||||
declare module "@mui/material/styles" {
|
||||
interface Palette {
|
||||
neutral: Palette["primary"];
|
||||
}
|
||||
|
||||
// allow configuration using `createTheme`
|
||||
interface PaletteOptions {
|
||||
neutral: PaletteOptions["primary"];
|
||||
}
|
||||
}
|
||||
declare module "@mui/material/Button" {
|
||||
interface ButtonPropsColorOverrides {
|
||||
neutral: true;
|
||||
}
|
||||
}
|
||||
|
||||
export const theme = (mode: "light" | "dark") => {
|
||||
const color: Color = colors[mode] as Color;
|
||||
const themeOptions = {
|
||||
palette: themePalette(color, mode),
|
||||
shadows: shadows(color),
|
||||
breakpoints: {
|
||||
values: {
|
||||
xs: 0,
|
||||
sm: 680,
|
||||
md: 900,
|
||||
lg: 1200,
|
||||
xl: 1536,
|
||||
},
|
||||
},
|
||||
mixins: {
|
||||
toolbar: {
|
||||
minHeight: "48px",
|
||||
padding: "16px",
|
||||
"@media (min-width: 600px)": {
|
||||
minHeight: "48px",
|
||||
},
|
||||
},
|
||||
},
|
||||
typography: themeTypography(color),
|
||||
};
|
||||
|
||||
const themes = createTheme(themeOptions as any, zhCN);
|
||||
themes.components = componentStyleOverrides(color);
|
||||
|
||||
return themes;
|
||||
};
|
||||
|
||||
export default theme;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { type Color } from './color'
|
||||
import { type ThemeMode } from './themeContext'
|
||||
export default function themePalette(color: Color, mode: Omit<ThemeMode, 'system'>) {
|
||||
return {
|
||||
mode,
|
||||
common: { black: '#000', white: '#fff' },
|
||||
primary: color.primary,
|
||||
secondary: color.secondary,
|
||||
info: color.info,
|
||||
success: color.success,
|
||||
warning: color.warning,
|
||||
error: color.error,
|
||||
neutral: color.neutral,
|
||||
divider: color.divider,
|
||||
text: color.text,
|
||||
background: color.background,
|
||||
shadowColor: color.shadowColor,
|
||||
charts: color.charts,
|
||||
action: {
|
||||
selectedOpacity: 0.1,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { type Color } from './color'
|
||||
export default function shadows(color: Color) {
|
||||
return [
|
||||
`0px 12px 24px -4px ${color.shadowColor},0px 0px 2px 0px ${color.shadowColor}`,
|
||||
`0px 12px 24px -4px ${color.shadowColor},0px 0px 2px 0px ${color.shadowColor}`,
|
||||
`0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.12),0px 1px 5px 0px rgba(0,0,0,0.12)`,
|
||||
`0px 3px 3px -2px rgba(0,0,0,0.2),0px 3px 4px 0px rgba(0,0,0,0.12),0px 1px 8px 0px rgba(0,0,0,0.12)`,
|
||||
`0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.12),0px 1px 10px 0px rgba(0,0,0,0.12)`,
|
||||
`0px 3px 5px -1px rgba(0,0,0,0.2),0px 5px 8px 0px rgba(0,0,0,0.12),0px 1px 14px 0px rgba(0,0,0,0.12)`,
|
||||
`0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.12),0px 1px 18px 0px rgba(0,0,0,0.12)`,
|
||||
`0px 4px 5px -2px rgba(0,0,0,0.2),0px 7px 10px 1px rgba(0,0,0,0.12),0px 2px 16px 1px rgba(0,0,0,0.12)`,
|
||||
`0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.12),0px 3px 14px 2px rgba(0,0,0,0.12)`,
|
||||
`0px 5px 6px -3px rgba(0,0,0,0.2),0px 9px 12px 1px rgba(0,0,0,0.12),0px 3px 16px 2px rgba(0,0,0,0.12)`,
|
||||
`0px 6px 6px -3px rgba(0,0,0,0.2),0px 10px 14px 1px rgba(0,0,0,0.12),0px 4px 18px 3px rgba(0,0,0,0.12)`,
|
||||
`0px 6px 7px -4px rgba(0,0,0,0.2),0px 11px 15px 1px rgba(0,0,0,0.12),0px 4px 20px 3px rgba(0,0,0,0.12)`,
|
||||
`0px 7px 8px -4px rgba(0,0,0,0.2),0px 12px 17px 2px rgba(0,0,0,0.12),0px 5px 22px 4px rgba(0,0,0,0.12)`,
|
||||
`0px 7px 8px -4px rgba(0,0,0,0.2),0px 13px 19px 2px rgba(0,0,0,0.12),0px 5px 24px 4px rgba(0,0,0,0.12)`,
|
||||
`0px 7px 9px -4px rgba(0,0,0,0.2),0px 14px 21px 2px rgba(0,0,0,0.12),0px 5px 26px 4px rgba(0,0,0,0.12)`,
|
||||
`0px 8px 9px -5px rgba(0,0,0,0.2),0px 15px 22px 2px rgba(0,0,0,0.12),0px 6px 28px 5px rgba(0,0,0,0.12)`,
|
||||
`0px 8px 10px -5px rgba(0,0,0,0.2),0px 16px 24px 2px rgba(0,0,0,0.12),0px 6px 30px 5px rgba(0,0,0,0.12)`,
|
||||
`0px 8px 11px -5px rgba(0,0,0,0.2),0px 17px 26px 2px rgba(0,0,0,0.12),0px 6px 32px 5px rgba(0,0,0,0.12)`,
|
||||
`0px 9px 11px -5px rgba(0,0,0,0.2),0px 18px 28px 2px rgba(0,0,0,0.12),0px 7px 34px 6px rgba(0,0,0,0.12)`,
|
||||
`0px 9px 12px -6px rgba(0,0,0,0.2),0px 19px 29px 2px rgba(0,0,0,0.12),0px 7px 36px 6px rgba(0,0,0,0.12)`,
|
||||
`0px 10px 13px -6px rgba(0,0,0,0.2),0px 20px 31px 3px rgba(0,0,0,0.12),0px 8px 38px 7px rgba(0,0,0,0.12)`,
|
||||
`0px 10px 13px -6px rgba(0,0,0,0.2),0px 21px 33px 3px rgba(0,0,0,0.12),0px 8px 40px 7px rgba(0,0,0,0.12)`,
|
||||
`0px 10px 14px -6px rgba(0,0,0,0.2),0px 22px 35px 3px rgba(0,0,0,0.12),0px 8px 42px 7px rgba(0,0,0,0.12)`,
|
||||
`0px 11px 14px -7px rgba(0,0,0,0.2),0px 23px 36px 3px rgba(0,0,0,0.12),0px 9px 44px 8px rgba(0,0,0,0.12)`,
|
||||
`0px 11px 15px -7px rgba(0,0,0,0.2),0px 24px 38px 3px rgba(0,0,0,0.12),0px 9px 46px 8px rgba(0,0,0,0.12)`,
|
||||
]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { createContext } from 'react'
|
||||
export type ThemeMode = 'dark' | 'light' | 'system'
|
||||
|
||||
const ThemeContext = createContext({ mode: 'dark' as ThemeMode, setThemeMode: (mode: ThemeMode) => {} })
|
||||
|
||||
export default ThemeContext
|
||||
@@ -1,8 +0,0 @@
|
||||
export default function themeTypography(color: any) {
|
||||
return {
|
||||
fontFamily: 'inherit',
|
||||
body1: {
|
||||
fontSize: '14px',
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
export * from "./render";
|
||||
|
||||
export { sampleLength, sizeLength, sampleSummary };
|
||||
|
||||
function sampleLength(s: string) {
|
||||
const l = new Blob([s]).size;
|
||||
return l > 1024 * 2 ? Math.round(l / 1024) + "KB" : l + "B";
|
||||
}
|
||||
|
||||
function sizeLength(l: number) {
|
||||
return l > 1024 * 2 ? Math.round(l / 1024) + "KB" : l + "B";
|
||||
}
|
||||
|
||||
function sampleSummary(s: string) {
|
||||
return s.split("\n").slice(0, 2).join(" ").slice(0, 60);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import matter from "gray-matter";
|
||||
import { serialize } from "next-mdx-remote/serialize";
|
||||
// import prism from "remark-prism";
|
||||
import slug from "remark-slug";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import externalLinks from "remark-external-links";
|
||||
|
||||
const postsDir = path.join(process.cwd(), "./src/static/md/");
|
||||
|
||||
const fileNames = fs.readdirSync(postsDir);
|
||||
|
||||
export const getPostsIds = () => {
|
||||
return fileNames.map((fileName) => ({
|
||||
params: { id: fileName.replace(/\.md$/, "") },
|
||||
}));
|
||||
};
|
||||
|
||||
type Group = string | { title: string; order?: number };
|
||||
export interface NavItem {
|
||||
title: string;
|
||||
group: Group;
|
||||
order?: number;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface GroupItem {
|
||||
title: string;
|
||||
order?: number;
|
||||
list: NavItem[];
|
||||
}
|
||||
|
||||
export const sortComparer = <
|
||||
T extends { order?: number; id?: string; title: string }
|
||||
>(
|
||||
a: T,
|
||||
b: T
|
||||
) => {
|
||||
return (
|
||||
("order" in a && "order" in b ? a.order! - b.order! : 0) ||
|
||||
("id" in a && "id" in b ? a.id!.localeCompare(b.id!) : 0) ||
|
||||
(a.title ? a.title.localeCompare(b.title) : -1)
|
||||
);
|
||||
};
|
||||
|
||||
const dealWithData: (data: NavItem[]) => GroupItem[] = (data) => {
|
||||
let list: GroupItem[] = [];
|
||||
let cache: { [key: string]: NavItem } = {};
|
||||
data.forEach((ele) => {
|
||||
let groupTitle: string, groupOrder: number | undefined;
|
||||
|
||||
if (typeof ele.group === "object") {
|
||||
groupTitle = ele.group.title;
|
||||
groupOrder = ele.group.order;
|
||||
} else if (typeof ele.group === "string") {
|
||||
groupTitle = ele.group;
|
||||
}
|
||||
|
||||
if (!cache[groupTitle!]) {
|
||||
let group: GroupItem = {
|
||||
title: groupTitle!,
|
||||
list: [ele],
|
||||
};
|
||||
if (groupOrder) group.order = groupOrder;
|
||||
list.push(group);
|
||||
cache[groupTitle!] = ele;
|
||||
} else {
|
||||
const cur = list.find((item) => item.title === groupTitle);
|
||||
if (groupOrder && cur) {
|
||||
cur.order = groupOrder;
|
||||
}
|
||||
cur?.list.push(ele);
|
||||
}
|
||||
});
|
||||
|
||||
return list.sort(sortComparer).map((group) => ({
|
||||
...group,
|
||||
list: group.list.sort(sortComparer),
|
||||
}));
|
||||
};
|
||||
|
||||
export const getPostsGroup = () => {
|
||||
const dataList: NavItem[] = fileNames.map((fileName) => {
|
||||
// 去除文件名的md后缀,使其作为文章id使用
|
||||
const id = fileName.replace(/\.md$/, "");
|
||||
|
||||
// 获取md文件路径
|
||||
const fullPath = path.join(postsDir, fileName);
|
||||
|
||||
// 读取md文件内容
|
||||
const fileContents = fs.readFileSync(fullPath, "utf8");
|
||||
|
||||
// 使用matter提取md文件元数据:{data:{//元数据},content:'内容'}
|
||||
const matterResult = matter(fileContents);
|
||||
return {
|
||||
id,
|
||||
...(matterResult.data as Omit<NavItem, "id">),
|
||||
};
|
||||
});
|
||||
return dealWithData(dataList);
|
||||
};
|
||||
|
||||
export async function getPostData(id: string) {
|
||||
// 文章路径
|
||||
const fullPath = path.join(postsDir, `${id}.md`);
|
||||
// const toc = require("@jsdevtools/rehype-toc");
|
||||
// 读取文章内容
|
||||
const fileContents = fs.readFileSync(fullPath, "utf8");
|
||||
|
||||
// 使用matter解析markdown元数据和内容
|
||||
const matterResult = await matter(fileContents);
|
||||
// let tocElement;
|
||||
const res = await serialize(matterResult.content, {
|
||||
mdxOptions: {
|
||||
remarkPlugins: [externalLinks, remarkGfm, slug],
|
||||
// rehypePlugins: [
|
||||
// [
|
||||
// toc,
|
||||
// {
|
||||
// headings: ["h1", "h2", "h3", "h4"],
|
||||
// customizeTOC: (tocAll: any) => {
|
||||
// tocElement = tocAll;
|
||||
// return false;
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// ],
|
||||
},
|
||||
});
|
||||
// console.log(tocElement, "tocElement--");
|
||||
|
||||
return { postData: res, content: matterResult.content };
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import ReactDOM, { type Root } from 'react-dom/client'
|
||||
|
||||
const MARK = '__ct_react_root__'
|
||||
|
||||
type ContainerType = (Element | DocumentFragment) & {
|
||||
[MARK]?: Root
|
||||
}
|
||||
|
||||
export function render(node: React.ReactElement, container: ContainerType) {
|
||||
const root = container[MARK] || ReactDOM.createRoot(container)
|
||||
|
||||
root.render(node)
|
||||
|
||||
container[MARK] = root
|
||||
}
|
||||
|
||||
export async function unmount(container: ContainerType) {
|
||||
return Promise.resolve().then(() => {
|
||||
container[MARK]?.unmount()
|
||||
delete container[MARK]
|
||||
})
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
1
homepage/type.d.ts
vendored
@@ -1 +0,0 @@
|
||||
declare module "remark-prism";
|
||||
1
lua-resty-t1k
Submodule
1
plugins
Submodule
1578
seccomp.json
Normal file
228
setup.sh
@@ -1,11 +1,4 @@
|
||||
#! /bin/bash
|
||||
set -eE
|
||||
|
||||
abort()
|
||||
{
|
||||
echo $1
|
||||
exit 1
|
||||
}
|
||||
#!/bin/bash
|
||||
|
||||
echo "
|
||||
____ __ _ _
|
||||
@@ -15,32 +8,209 @@ echo "
|
||||
|____/ \__,_| |_| \___| |_____| |_| |_| |_| \___|
|
||||
"
|
||||
|
||||
if [[ "$#" -ne "0" ]]; then
|
||||
echo "Usage: run "$0" to set up Safeline CE at current working directory."
|
||||
exit 0
|
||||
qrcode() {
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "████ ▄▄▄▄▄ █▀ █▀▀██▀▄▀▀▄▀▄▀▄██ ▄▄▄▄▄ ████"
|
||||
echo "████ █ █ █▀ ▄ █▀▄▄▀▀ ▄█▄ ▀█ █ █ ████"
|
||||
echo "████ █▄▄▄█ █▀█ █▄█▄▀▀▄▀▄ ▀▀▄▄█ █▄▄▄█ ████"
|
||||
echo "████▄▄▄▄▄▄▄█▄█▄█ █▄▀ █ ▀▄▀ █▄█▄▄▄▄▄▄▄████"
|
||||
echo "████▄ ▄▄ █▄▄ ▄█▄▄▄▄▀▄▀▀▄██ ▄▄▀▄█▄▀ ▀████"
|
||||
echo "████▄ ▄▀▄ ▄▀▄ ▀ ▄█▀ ▀▄ █▀▀ ▀█▀▄██▄▀▄█████"
|
||||
echo "█████ ▀▄█ ▄ ▄▄▀▄▀▀█▄▀▄▄▀▄▀▄ ▄ ▀▄▄▄█▀▀████"
|
||||
echo "████ █▀▄▀ ▄▀▄▄▀█▀ ▄▄ █▄█▀▀▄▀▀█▄█▄█▀▄█████"
|
||||
echo "████ █ ▀ ▄▀▀ ██▄█▄▄▄▄▄▀▄▀▀▀▄▄▀█▄▀█ ▀████"
|
||||
echo "████ █ ▀▄ ▄██▀▀ ▄█▀ ▀███▄ ▀▄▀▄▄ ▄▀▄█████"
|
||||
echo "████▀▄▄█ ▄▀▄▀ ▄▀▀▀▄▀▄▀ ▄▀▄ ▄▀ ▄▀█ ▀████"
|
||||
echo "████ █ █ █▄▀ █▄█▀ ▄▄███▀▀▀▄█▀▄ ▀ ▀▄█████"
|
||||
echo "████▄███▄█▄▄▀▄ █▄█▄▄▄▄▀▀▄█▀▀ ▄▄▄ ▀█ ████"
|
||||
echo "████ ▄▄▄▄▄ █▄▀█ ▄█▀▄ █▀█▄ ▀ █▄█ ▀▄▀████"
|
||||
echo "████ █ █ █ █▄▀▀▀▄▄▄▀▀▀▀▀▀ ▄▄ ▀█ ████"
|
||||
echo "████ █▄▄▄█ █ ▀█▀ ▄▄▄▄ ▀█ ▀▀▄▀ ▀▀ ▀██████"
|
||||
echo "████▄▄▄▄▄▄▄█▄▄██▄█▄▄█▄██▄██▄▄█▄▄█▄█▄█████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
|
||||
echo
|
||||
echo "微信扫描上方二维码加入雷池项目讨论组"
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=`dirname "$dir"`;
|
||||
done
|
||||
echo `df -h "$dir" --output='avail' | tail -n 1`
|
||||
}
|
||||
|
||||
start_docker() {
|
||||
systemctl start docker && systemctl enable docker
|
||||
}
|
||||
|
||||
confirm() {
|
||||
echo -e -n "\033[34m[SafeLine] $* \033[1;36m(Y/n)\033[0m"
|
||||
read -n 1 -s opt
|
||||
|
||||
[[ "$opt" == $'\n' ]] || echo
|
||||
|
||||
case "$opt" in
|
||||
'y' | 'Y' ) return 0;;
|
||||
'n' | 'N' ) return 1;;
|
||||
*) confirm "$1";;
|
||||
esac
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "\033[37m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "\033[33m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
abort() {
|
||||
qrcode
|
||||
echo -e "\033[31m[SafeLine] $*\033[0m"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'onexit' INT
|
||||
onexit() {
|
||||
echo
|
||||
abort "用户手动结束安装"
|
||||
}
|
||||
|
||||
# CPU ssse3 指令集检查
|
||||
lscpu | grep ssse3 > /dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "雷池需要运行在支持 ssse3 指令集的 CPU 上,虚拟机请自行配置开启 CPU ssse3 指令集支持"
|
||||
fi
|
||||
|
||||
command -v docker > /dev/null || abort "docker not found, unable to deploy"
|
||||
compose_plugin=true
|
||||
safeline_path='/data/safeline'
|
||||
|
||||
if [ -z "$BASH" ]; then
|
||||
abort "请用 bash 执行本脚本,请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ ! -t 0 ]; then
|
||||
abort "STDIN 不是标准的输入设备,请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$#" -ne "0" ]; then
|
||||
abort "当前脚本无需任何参数,请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$EUID" -ne "0" ]; then
|
||||
abort "请以 root 权限运行"
|
||||
fi
|
||||
info "脚本调用方式确认正常"
|
||||
|
||||
if [ -z `command_exists docker` ]; then
|
||||
warning "缺少 Docker 环境"
|
||||
if confirm "是否需要自动安装 Docker"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 安装失败"
|
||||
fi
|
||||
info "Docker 安装完成"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
fi
|
||||
info "发现 Docker 环境: '`command -v docker`'"
|
||||
|
||||
start_docker
|
||||
docker version > /dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 服务工作异常"
|
||||
fi
|
||||
info "Docker 工作状态正常"
|
||||
|
||||
compose_command="docker compose"
|
||||
docker --help | grep compose | grep v2 > /dev/null || compose_plugin=false || compose_command="docker-compose"
|
||||
|
||||
if [[ "x${compose_plugin}" = "xfalse" ]]; then
|
||||
command -v docker-compose > /dev/null && docker-compose --version | grep v2 > /dev/null || abort "docker compose v2 not found, unable to deploy"
|
||||
if $compose_command version; then
|
||||
info "发现 Docker Compose Plugin"
|
||||
else
|
||||
warning "未发现 Docker Compose Plugin"
|
||||
compose_command="docker-compose"
|
||||
if [ -z `command_exists "docker-compose"` ]; then
|
||||
warning "未发现 docker-compose 组件"
|
||||
if confirm "是否需要自动安装 Docker Compose Plugin"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker Compose Plugin 安装失败"
|
||||
fi
|
||||
info "Docker Compose Plugin 安装完成"
|
||||
compose_command="docker compose"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
else
|
||||
info "发现 docker-compose 组件: '`command -v docker-compose`'"
|
||||
fi
|
||||
fi
|
||||
|
||||
COMPOSE_YAML="compose.yaml"
|
||||
wget https://waf-ce.chaitin.cn/release/latest/compose.yaml --no-check-certificate -O ${COMPOSE_YAML}
|
||||
while true; do
|
||||
echo -e -n "\033[34m[SafeLine] 雷池安装目录 (留空则为 '$safeline_path'): \033[0m"
|
||||
read input_path
|
||||
[[ -z "$input_path" ]] && input_path=$safeline_path
|
||||
|
||||
ENV_FILE=".env"
|
||||
if [[ ! -f ${ENV_FILE} ]]; then
|
||||
echo "SAFELINE_DIR=$(pwd)" >> .env
|
||||
echo "IMAGE_TAG=latest" >> .env
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=169.254.0" >> .env
|
||||
if [[ ! $input_path == /* ]]; then
|
||||
warning "'$input_path' 不是合法的绝对路径"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -f "$input_path" ] || [ -d "$input_path" ]; then
|
||||
warning "'$input_path' 路径已经存在,请换一个"
|
||||
continue
|
||||
fi
|
||||
|
||||
safeline_path=$input_path
|
||||
|
||||
if confirm "目录 '$safeline_path' 当前剩余存储空间为 `space_left \"$safeline_path\"` ,雷池至少需要 5G,是否确定"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$safeline_path"
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "创建安装目录 '$safeline_path' 失败"
|
||||
fi
|
||||
info "创建安装目录 '$safeline_path' 成功"
|
||||
cd "$safeline_path"
|
||||
|
||||
curl -sS "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -o compose.yaml
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
fi
|
||||
info "下载 compose.yaml 脚本成功"
|
||||
|
||||
touch ".env"
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "创建 .env 脚本失败"
|
||||
fi
|
||||
info "创建 .env 脚本成功"
|
||||
|
||||
echo "SAFELINE_DIR=$safeline_path" >> .env
|
||||
echo "IMAGE_TAG=latest" >> .env
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=172.22.222" >> .env
|
||||
|
||||
info "即将开始下载 Docker 镜像"
|
||||
|
||||
$compose_command up -d
|
||||
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "启动 Docker 容器失败"
|
||||
fi
|
||||
|
||||
echo "Setup success!"
|
||||
echo "Run '$compose_command up -d' to start SafeLine."
|
||||
echo "And then visit https://<SafeLine-IP>:9443."
|
||||
qrcode
|
||||
|
||||
warning "雷池 WAF 社区版安装成功,请访问以下地址访问控制台"
|
||||
warning "https://0.0.0.0:9443/"
|
||||
|
||||
|
||||
227
upgrade.sh
@@ -1,11 +1,4 @@
|
||||
#! /bin/bash
|
||||
set -eE
|
||||
|
||||
abort()
|
||||
{
|
||||
echo $1
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "
|
||||
____ __ _ _
|
||||
@@ -15,28 +8,214 @@ echo "
|
||||
|____/ \__,_| |_| \___| |_____| |_| |_| |_| \___|
|
||||
"
|
||||
|
||||
if [[ "$#" -ne "0" ]]; then
|
||||
echo "Usage: run "$0" to set up Safeline CE at current working directory."
|
||||
exit 0
|
||||
echo $1
|
||||
|
||||
qrcode() {
|
||||
echo
|
||||
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "████ ▄▄▄▄▄ █▀ █▀▀██▀▄▀▀▄▀▄▀▄██ ▄▄▄▄▄ ████"
|
||||
echo "████ █ █ █▀ ▄ █▀▄▄▀▀ ▄█▄ ▀█ █ █ ████"
|
||||
echo "████ █▄▄▄█ █▀█ █▄█▄▀▀▄▀▄ ▀▀▄▄█ █▄▄▄█ ████"
|
||||
echo "████▄▄▄▄▄▄▄█▄█▄█ █▄▀ █ ▀▄▀ █▄█▄▄▄▄▄▄▄████"
|
||||
echo "████▄ ▄▄ █▄▄ ▄█▄▄▄▄▀▄▀▀▄██ ▄▄▀▄█▄▀ ▀████"
|
||||
echo "████▄ ▄▀▄ ▄▀▄ ▀ ▄█▀ ▀▄ █▀▀ ▀█▀▄██▄▀▄█████"
|
||||
echo "█████ ▀▄█ ▄ ▄▄▀▄▀▀█▄▀▄▄▀▄▀▄ ▄ ▀▄▄▄█▀▀████"
|
||||
echo "████ █▀▄▀ ▄▀▄▄▀█▀ ▄▄ █▄█▀▀▄▀▀█▄█▄█▀▄█████"
|
||||
echo "████ █ ▀ ▄▀▀ ██▄█▄▄▄▄▄▀▄▀▀▀▄▄▀█▄▀█ ▀████"
|
||||
echo "████ █ ▀▄ ▄██▀▀ ▄█▀ ▀███▄ ▀▄▀▄▄ ▄▀▄█████"
|
||||
echo "████▀▄▄█ ▄▀▄▀ ▄▀▀▀▄▀▄▀ ▄▀▄ ▄▀ ▄▀█ ▀████"
|
||||
echo "████ █ █ █▄▀ █▄█▀ ▄▄███▀▀▀▄█▀▄ ▀ ▀▄█████"
|
||||
echo "████▄███▄█▄▄▀▄ █▄█▄▄▄▄▀▀▄█▀▀ ▄▄▄ ▀█ ████"
|
||||
echo "████ ▄▄▄▄▄ █▄▀█ ▄█▀▄ █▀█▄ ▀ █▄█ ▀▄▀████"
|
||||
echo "████ █ █ █ █▄▀▀▀▄▄▄▀▀▀▀▀▀ ▄▄ ▀█ ████"
|
||||
echo "████ █▄▄▄█ █ ▀█▀ ▄▄▄▄ ▀█ ▀▀▄▀ ▀▀ ▀██████"
|
||||
echo "████▄▄▄▄▄▄▄█▄▄██▄█▄▄█▄██▄██▄▄█▄▄█▄█▄█████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
|
||||
echo
|
||||
echo "微信扫描上方二维码加入雷池项目讨论组"
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=`dirname "$dir"`;
|
||||
done
|
||||
echo `df -h "$dir" --output='avail' | tail -n 1`
|
||||
}
|
||||
|
||||
confirm() {
|
||||
echo -e -n "\033[34m[SafeLine] $* \033[1;36m(Y/n)\033[0m"
|
||||
read -n 1 -s opt
|
||||
|
||||
[[ "$opt" == $'\n' ]] || echo
|
||||
|
||||
case "$opt" in
|
||||
'y' | 'Y' ) return 0;;
|
||||
'n' | 'N' ) return 1;;
|
||||
*) confirm "$1";;
|
||||
esac
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "\033[37m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "\033[33m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
abort() {
|
||||
qrcode
|
||||
echo -e "\033[31m[SafeLine] $*\033[0m"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'onexit' INT
|
||||
onexit() {
|
||||
echo
|
||||
abort "用户手动结束升级"
|
||||
}
|
||||
|
||||
# CPU ssse3 指令集检查
|
||||
lscpu | grep ssse3 > /dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "雷池需要运行在支持 ssse3 指令集的 CPU 上,虚拟机请自行配置开启 CPU ssse3 指令集支持"
|
||||
fi
|
||||
|
||||
command -v docker > /dev/null || abort "docker not found, unable to deploy"
|
||||
compose_plugin=true
|
||||
if [ -z "$BASH" ]; then
|
||||
abort "请用 bash 执行本脚本, 请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ ! -t 0 ]; then
|
||||
abort "STDIN 不是标准的输入设备, 请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$#" -ne "0" ]; then
|
||||
abort "当前脚本无需任何参数, 请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$EUID" -ne "0" ]; then
|
||||
abort "请以 root 权限运行"
|
||||
fi
|
||||
info "脚本调用方式确认正常"
|
||||
|
||||
if [ -z `command_exists docker` ]; then
|
||||
warning "缺少 Docker 环境"
|
||||
if confirm "是否需要自动安装 Docker"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 安装失败"
|
||||
fi
|
||||
info "Docker 安装完成"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
fi
|
||||
info "发现 Docker 环境: '`command -v docker`'"
|
||||
|
||||
docker version > /dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 服务工作异常"
|
||||
fi
|
||||
info "Docker 工作状态正常"
|
||||
|
||||
compose_command="docker compose"
|
||||
docker --help | grep compose | grep v2 > /dev/null || compose_plugin=false || compose_command="docker-compose"
|
||||
|
||||
if [[ "x${compose_plugin}" = "xfalse" ]]; then
|
||||
command -v docker-compose > /dev/null && docker-compose --version | grep v2 > /dev/null || abort "docker compose v2 not found, unable to deploy"
|
||||
if $compose_command version; then
|
||||
info "发现 Docker Compose Plugin"
|
||||
else
|
||||
warning "未发现 Docker Compose Plugin"
|
||||
compose_command="docker-compose"
|
||||
if [ -z `command_exists "docker-compose"` ]; then
|
||||
warning "未发现 docker-compose 组件"
|
||||
if confirm "是否需要自动安装 Docker Compose Plugin"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker Compose Plugin 安装失败"
|
||||
fi
|
||||
info "Docker Compose Plugin 安装完成"
|
||||
compose_command="docker compose"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
else
|
||||
info "发现 docker-compose 组件: '`command -v docker-compose`'"
|
||||
fi
|
||||
fi
|
||||
|
||||
COMPOSE_YAML="compose.yaml"
|
||||
wget https://waf-ce.chaitin.cn/release/latest/compose.yaml --no-check-certificate -O ${COMPOSE_YAML}
|
||||
container_id=`docker ps --filter ancestor=chaitin/safeline-mgt-api --format '{{.ID}}'`
|
||||
mount_path=`docker inspect --format '{{range .Mounts}}{{if eq .Destination "/logs"}}{{.Source}}{{end}}{{end}}' $container_id`
|
||||
safeline_path=`dirname $mount_path`
|
||||
|
||||
ENV_FILE=".env"
|
||||
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=latest/g" ${ENV_FILE}
|
||||
while [ -z "$safeline_path" ]; do
|
||||
echo -e -n "\033[34m[SafeLine] 未发现正在运行的雷池,请输入雷池安装路径 (留空则为 '`pwd`'): \033[0m"
|
||||
read input_path
|
||||
[[ -z "$input_path" ]] && input_path=`pwd`
|
||||
|
||||
grep "SAFELINE_DIR" ${ENV_FILE} > /dev/null || echo "SAFELINE_DIR=$(pwd)" >> ${ENV_FILE}
|
||||
grep "SUBNET_PREFIX" ${ENV_FILE} > /dev/null || echo "SUBNET_PREFIX=169.254.0" >> ${ENV_FILE}
|
||||
if [[ ! $input_path == /* ]]; then
|
||||
warning "'$input_path' 不是合法的绝对路径"
|
||||
continue
|
||||
fi
|
||||
|
||||
safeline_path=$input_path
|
||||
done
|
||||
|
||||
cd "$safeline_path"
|
||||
|
||||
compose_name=`ls docker-compose.yaml compose.yaml 2>/dev/null`
|
||||
compose_path=$safeline_path/$compose_name
|
||||
|
||||
if [ -f "$compose_path" ]; then
|
||||
info "发现位于 '$safeline_path' 的雷池环境"
|
||||
else
|
||||
abort "没有发现位于 $safeline_path 的雷池环境"
|
||||
fi
|
||||
|
||||
|
||||
mv $compose_name $compose_name.old
|
||||
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -sSLk -o $compose_name
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
fi
|
||||
info "下载 compose.yaml 脚本成功"
|
||||
|
||||
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=latest/g" ".env"
|
||||
|
||||
grep "SAFELINE_DIR" ".env" > /dev/null || echo "SAFELINE_DIR=$(pwd)" >> ".env"
|
||||
grep "IMAGE_TAG" ".env" > /dev/null || echo "IMAGE_TAG=latest" >> ".env"
|
||||
grep "MGT_PORT" ".env" > /dev/null || echo "MGT_PORT=9443" >> ".env"
|
||||
grep "POSTGRES_PASSWORD" ".env" > /dev/null || echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> ".env"
|
||||
grep "REDIS_PASSWORD" ".env" > /dev/null || echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> ".env"
|
||||
grep "SUBNET_PREFIX" ".env" > /dev/null || echo "SUBNET_PREFIX=172.22.222" >> ".env"
|
||||
|
||||
info "升级 .env 脚本成功"
|
||||
|
||||
info "即将开始下载新版本 Docker 镜像"
|
||||
|
||||
$compose_command pull
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载新版本 Docker 镜像失败"
|
||||
fi
|
||||
info "下载新版本 Docker 镜像成功"
|
||||
|
||||
info "即将开始替换 Docker 容器"
|
||||
|
||||
$compose_command down && $compose_command up -d
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "替换 Docker 容器失败"
|
||||
fi
|
||||
info "雷池升级成功"
|
||||
|
||||
qrcode
|
||||
|
||||
warning "雷池 WAF 社区版安装成功, 请访问以下地址访问控制台"
|
||||
warning "https://0.0.0.0:9443/"
|
||||
|
||||
$compose_command pull && $compose_command down && $compose_command up -d
|
||||
echo "Upgrade success!"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"latest_version": "v1.8.1",
|
||||
"rec_version": "v1.7.1"
|
||||
}
|
||||
"latest_version": "v3.2.0",
|
||||
"rec_version": "v3.1.1"
|
||||
}
|
||||
|
||||
3
website/.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
build
|
||||
.docusaurus
|
||||
20
website/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -1,3 +1,4 @@
|
||||
strict-ssl=false
|
||||
save-prefix=""
|
||||
engine-strict=true
|
||||
registry="https://registry.npmmirror.com"
|
||||
7
website/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
CMD npm run serve -- --port 80 --host 0.0.0.0
|
||||
79
website/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Website
|
||||
|
||||
使用 [Docusaurus 2](https://docusaurus.io/), 作为基础框架。
|
||||
|
||||
### 开发
|
||||
|
||||
```sh
|
||||
# 代码变动后可以自动更新,但是不能
|
||||
npm start
|
||||
# 支持搜索功能,但是无法自动更新
|
||||
npm run preview
|
||||
```
|
||||
|
||||
### 部署
|
||||
|
||||
手动本地构建 docker 镜像,然后运行
|
||||
|
||||
```sh
|
||||
docker build -t website:latest .
|
||||
docker run --name site -p 3000:80 -d website:latest
|
||||
```
|
||||
|
||||
### 链接替换
|
||||
|
||||
使用 nginx rewrite 把更改地址的链接记录下,运行旧链接访问到新地址
|
||||
|
||||
```nginx
|
||||
|
||||
location / {
|
||||
|
||||
rewrite /posts/guide_introduction /docs/ permanent;
|
||||
rewrite /posts/guide_install /docs/guide/install permanent;
|
||||
rewrite /docs/上手指南/guide_install /docs/guide/install permanent;
|
||||
|
||||
rewrite /posts/guide_login /docs/guide/login permanent;
|
||||
rewrite /docs/上手指南/guide_login /docs/guide/login permanent;
|
||||
|
||||
rewrite /posts/guide_config /docs/guide/config permanent;
|
||||
rewrite /docs/上手指南/guide_config /docs/guide/config permanent;
|
||||
|
||||
rewrite /posts/guide_test /docs/guide/test permanent;
|
||||
rewrite /docs/上手指南/guide_test /docs/guide/test permanent;
|
||||
|
||||
rewrite /posts/guide_upgrade /docs/guide/upgrade permanent;
|
||||
rewrite /docs/上手指南/guide_upgrade /docs/guide/upgrade permanent;
|
||||
|
||||
rewrite /posts/faq_install /docs/faq/install permanent;
|
||||
rewrite /docs/常见问题排查/faq_install /docs/faq/install permanent;
|
||||
|
||||
rewrite /posts/faq_login /docs/faq/login permanent;
|
||||
rewrite /docs/常见问题排查/faq_login /docs/faq/login permanent;
|
||||
|
||||
rewrite /posts/faq_access /docs/guide/config permanent;
|
||||
rewrite /docs/常见问题排查/faq_access /docs/guide/config permanent;
|
||||
|
||||
rewrite /posts/faq_config /docs/faq/config permanent;
|
||||
rewrite /docs/常见问题排查/faq_config /docs/faq/config permanent;
|
||||
|
||||
rewrite /posts/faq_other /docs/faq/other permanent;
|
||||
rewrite /docs/常见问题排查/faq_other /docs/faq/other permanent;
|
||||
|
||||
rewrite /posts/about_syntaxanalysis /docs/about/syntaxanalysis permanent;
|
||||
rewrite /docs/关于雷池/about_syntaxanalysis /docs/about/syntaxanalysis permanent;
|
||||
|
||||
rewrite /posts/about_challenge /docs/about/challenge permanent;
|
||||
rewrite /docs/关于雷池/about_challenge /docs/about/challenge permanent;
|
||||
|
||||
rewrite /posts/about_changelog /docs/about/changelog permanent;
|
||||
rewrite /docs/关于雷池/about_changelog /docs/about/changelog permanent;
|
||||
|
||||
rewrite /posts/about_chaitin /docs/about/chaitin permanent;
|
||||
rewrite /docs/关于雷池/about_chaitin /docs/about/chaitin permanent;
|
||||
|
||||
rewrite /docs/faq/access /docs/guide/config permanent;
|
||||
rewrite /docs/faq/config /docs/guide/config permanent;
|
||||
|
||||
proxy_pass http://upstream;
|
||||
}
|
||||
```
|
||||
3
website/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
||||
3
website/blog/2023-08-04-website.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Website
|
||||
|
||||
为了支持文档检索,采用 https://docusaurus.io/ 作为新的网站框架。
|
||||
@@ -1,21 +1,19 @@
|
||||
---
|
||||
title: "雷池简介"
|
||||
group:
|
||||
title: "上手指南"
|
||||
order: 1
|
||||
order: 1
|
||||
slug: /
|
||||
---
|
||||
|
||||
# 雷池简介
|
||||
|
||||
## 什么是 WAF
|
||||
|
||||
WAF 是 Web Application Firewall 的缩写,也被称为 Web 应用防火墙。区别于传统防火墙,WAF 工作在应用层,对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果,使其免于收到黑客的攻击。
|
||||
WAF 是 Web Application Firewall 的缩写,也被称为 Web 应用防火墙。区别于传统防火墙,WAF 工作在应用层,对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果,使其免于受到黑客的攻击。
|
||||
|
||||
## 什么是雷池
|
||||
|
||||
雷池是长亭科技耗时近 10 年倾情打造的 WAF,核心检测能力由智能语义分析算法驱动。
|
||||
|
||||
Slogon:不让黑客越雷池半步。
|
||||
Slogan: 不让黑客越雷池半步。
|
||||
|
||||
## 为什么是雷池
|
||||
|
||||
99
website/docs/02-guide/01-install.md
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: "安装雷池"
|
||||
---
|
||||
|
||||
# 安装雷池
|
||||
|
||||
## 配置需求
|
||||
|
||||
- 操作系统:Linux
|
||||
- 指令架构:x86_64
|
||||
- 软件依赖:Docker 20.10.14 版本以上
|
||||
- 软件依赖:Docker Compose 2.0.0 版本以上
|
||||
- 最小化环境:1 核 CPU / 1 GB 内存 / 5 GB 磁盘
|
||||
|
||||
可以逐行执行以下命令来确认服务器配置
|
||||
|
||||
```shell
|
||||
uname -m # 查看指令架构
|
||||
docker version # 查看 Docker 版本
|
||||
docker compose version # 查看 Docker Compose 版本
|
||||
docker-compose version # 同上(兼容老版本 Docker Compose)
|
||||
cat /proc/cpuinfo # 查看 CPU 信息
|
||||
cat /proc/meminfo # 查看内存信息
|
||||
df -h # 查看磁盘信息
|
||||
|
||||
lscpu | grep ssse3 # 确认 CPU 是否支持 ssse3 指令集
|
||||
```
|
||||
|
||||
有三种安装方式供选择
|
||||
|
||||
- [在线安装](#在线安装) : 推荐安装方式
|
||||
- [离线安装](#离线安装) : 服务器无法连接 Docker Hub 时选择
|
||||
- [一键安装](#使用牧云助手安装) : 最简单的安装方式
|
||||
|
||||
## 在线安装
|
||||
|
||||
**_如果服务器可以访问互联网环境,推荐使用该方式_**
|
||||
|
||||
执行以下命令,即可开始安装
|
||||
|
||||
```
|
||||
bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
|
||||
> 如果连接 Docker Hub 网络不稳,导致镜像下载失败,可以采用 [离线安装](#离线安装) 方式
|
||||
|
||||
经过以上步骤,你的雷池已经安装好了,下一步请参考 [登录雷池](/docs/guide/login)
|
||||
|
||||
## 离线安装
|
||||
|
||||
如果你的服务器无法连接互联网环境,或连接 Docker Hub 网络不稳,可以使用镜像包安装方式
|
||||
|
||||
> 这里忽略 Docker 安装的过程
|
||||
|
||||
首先,下载 [雷池社区版镜像包](https://demo.waf-ce.chaitin.cn/image.tar.gz) 并传输到需要安装雷池的服务器上,执行以下命令加载镜像
|
||||
|
||||
```
|
||||
cat image.tar.gz | gzip -d | docker load
|
||||
```
|
||||
|
||||
执行以下命令创建并进入雷池安装目录
|
||||
|
||||
```
|
||||
mkdir -p safeline # 创建 safeline 目录
|
||||
cd safeline # 进入 safeline 目录
|
||||
```
|
||||
|
||||
下载 [编排脚本](https://waf-ce.chaitin.cn/release/latest/compose.yaml) 并传输到 safeline 目录中
|
||||
|
||||
执行以下命令,生成雷池运行所需的相关环境变量
|
||||
|
||||
```
|
||||
echo "SAFELINE_DIR=$(pwd)" >> .env
|
||||
echo "IMAGE_TAG=latest" >> .env
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=172.22.222" >> .env
|
||||
```
|
||||
|
||||
执行以下命令启动雷池
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
经过以上步骤,你的雷池已经安装好了,下一步请参考 [登录雷池](/docs/guide/login)
|
||||
|
||||
## 使用牧云助手安装
|
||||
|
||||
也可以使用 [牧云主机管理助手](https://collie.chaitin.cn/) 进行一键安装
|
||||
|
||||

|
||||
|
||||
参考视频教程 [用 “白嫖的云主机” 一键安装 “开源的 Web 防火墙”](https://www.bilibili.com/video/BV1sh4y1t7Pk/)
|
||||
|
||||
## 常见安装问题
|
||||
|
||||
请参考 [安装问题](/docs/faq/install)
|
||||
9
website/docs/02-guide/02-login.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: "登录雷池"
|
||||
---
|
||||
|
||||
# 登录雷池
|
||||
|
||||
浏览器打开后台管理页面 `https://<waf-ip>:9443`。根据界面提示,使用 **支持 TOTP 的认证软件或者小程序** 扫描二维码,然后输入动态口令登录:
|
||||
|
||||

|
||||
94
website/docs/02-guide/03-config.md
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: "配置防护站点"
|
||||
---
|
||||
|
||||
# 配置防护站点
|
||||
|
||||
## 界面操作
|
||||

|
||||
|
||||
添加后,在客户端执行 `curl -H "Host: <域名>" http://<雷池 IP>:<雷池监听端口>` ,若能获取到业务网站的响应,并且站点上 “今日访问量” 增加,则代表配置成功。
|
||||
|
||||
## 如何配置域名、端口、上游服务器
|
||||
### 工作原理
|
||||
|
||||
雷池社区版主要以 **反向代理** 的方式工作,类似于一台 nginx 服务。部署时,需要让网站流量先抵达雷池,经过雷池检测和过滤后,再转给原来的网站业务。
|
||||
|
||||
如果你不了解反向代理的工作原理,可以通过以下几种雷池常见的工作场景,来了解如何配置站点。
|
||||
|
||||
假设你的网站域名为 `example.com`,如图:
|
||||
|
||||

|
||||
|
||||
### 在单独设备上部署雷池(推荐)
|
||||
|
||||
如果你可以提供一台独立设备部署雷池,那么你需要:
|
||||
1. **将网站流量指向雷池**。例如将域名解析到雷池
|
||||
2. 禁止网站服务器上,所有除了雷池之外的访问。例如配置防火墙,或者直接把网站服务器放到内网
|
||||
|
||||
效果大致如下:
|
||||
|
||||

|
||||
|
||||
雷池上相应的站点配置为:
|
||||
* 域名:公网域名 `example.com`
|
||||
* 端口:80 或 443/ssl
|
||||
* 上游服务器:网站服务器的地址 `http://192.168.10.10`
|
||||
|
||||
### 直接在网站服务器上部署雷池
|
||||
|
||||
提示:不建议这样部署,因为这样单机的负载更高、设备宕机的概率更大。非纯净的环境还会提高安装失败的概率,故障排查也会比较困难。
|
||||
|
||||
如果能接受这些风险,雷池也可以直接部署在网站服务器上。你需要:
|
||||
1. 将原本监听 80 或 443/ssl 端口的网站服务改到其他端口,**让雷池监听设备的 80 或 443/ssl 端口**
|
||||
2. 使网站服务仅允许本机访问。例如配置系统防火墙、Iptables
|
||||
|
||||
效果大致如图:
|
||||
|
||||

|
||||
|
||||
此时雷池上的站点配置为:
|
||||
* 域名:公网域名 `example.com`
|
||||
* 端口:80 或 443/ssl
|
||||
* 上游服务器:`http://127.0.0.1:<网站服务改后的端口>`
|
||||
|
||||
### 和其他反代设备一起部署的情况
|
||||
|
||||
雷池作为反代设备,可以在任意位置接入主链路。只要将接入位置的流量指向雷池,并在雷池的 “上游服务器” 处填写请求的下一跳服务器地址即可。例如:
|
||||
|
||||

|
||||
|
||||
## 配置后网站无法访问,如何排查
|
||||
|
||||
如果按照上文指引部署雷池、配置了站点,但网站仍无法访问,建议按照以下步骤排查:
|
||||
|
||||
1. 明确 “网站无法访问” 的具体表现:
|
||||
* 如果 `502 Bad Gateway tengine`:
|
||||
|
||||

|
||||
|
||||
大概率是是雷池的上游服务器配置不正确,或者雷池无法访问到上游服务器。请继续按下面步骤排查,重点排查步骤 6、7
|
||||
* 如果请求能够返回但是十分缓慢
|
||||
* 首先确认服务器负载是否正常
|
||||
* 在客户端执行命令,检查雷池服务器与上游服务器的网络:`curl -H "Host: <SafeLine-IP>" -vv -o /dev/null -s -w 'time_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n' http://<上游服务器地址>`
|
||||
* 如果 time_namelookup 时间过大,请检查 dns server 配置
|
||||
* 如果 time_connect 时间过大,请检查雷池与上游服务器之间的网络状态
|
||||
* 如果 time_starttransfer 时间过大,请检查上游服务器状态,是否出现资源过载情况
|
||||
* 如果不是以上情况,继续下一步
|
||||
2. 在客户端执行 `curl -H "Host: <域名>" http://<雷池 IP>:<雷池监听端口>` 。正常情况下,应能获取到业务网站的响应,并且站点的 “今日访问量” +1
|
||||
* 如果浏览器无法访问,但这一步正常获取到响应,大概率是因为:
|
||||
* 测试过程中,网站域名还没有切到雷池,浏览器测试时访问的是 `http(s)://<雷池 IP>`,恰好业务服务上有 Host 验证,所以拒绝了该请求。这种情况需要修改本机 host,把域名解析到雷池 IP,再访问 `http(s)://<域名>`,才能准确测试
|
||||
* 网站业务做了其他一些特殊处理。例如访问后 301 跳转到了其他地址,需要具体排查网站业务的响应内容
|
||||
* 如果不能获取到响应,继续下一步
|
||||
3. 在雷池设备上执行 `curl -H "Host: <域名>" http://<雷池 IP>:<雷池监听端口>`。正常情况下,应能获取到业务网站的响应,并且站点上 “今日访问量” +1
|
||||
* 如果步骤 2 失败而这里成功,说明客户端到雷池之间的网络存在问题。请排查网络,保证客户端可访问到雷池
|
||||
* 如果不能获取到响应,继续下一步
|
||||
4. 在雷池设备上执行 `curl -H "Host: <域名>" http://127.0.0.1:<雷池监听端口>`。正常情况下,应能获取到业务网站的响应,并且站点的 “今日访问量” +1
|
||||
* 如果步骤 3 失败而这里成功,且 `telnet <雷池 IP> <雷池监听端口>` 返回 `Unable to connect to remote host: Connection refused`,大概率是被雷池设备上的防火墙拦截了。可能是操作系统本身的防火墙,还有可能是云服务商的防火墙。请根据实际情况逐项排查,开放雷池监听端口的访问
|
||||
* 如果不能获取到响应,继续下一步
|
||||
5. 在雷池设备上执行 `netstat -anp | grep <雷池监听端口>` 确认端口监听情况。正常情况下,应该有一个 nginx 进程监听在 `0.0.0.0:<雷池监听端口>`。没有的话请通过社群或者 Github issue 提交反馈,附上排查过程。有的话继续下一步
|
||||
6. 在雷池设备上 `curl -H "Host: <域名>" <上游服务器地址>`。正常情况下,应能获取到业务网站的响应
|
||||
* 如果步骤 4 失败而这里成功,请通过社群或者 Github issue 提交反馈,附上排查过程
|
||||
* 如果这步失败,说明雷池和上游服务器之间的网络存在问题。请排查网络,确保雷池可以访问到上游服务器
|
||||
|
||||
如果排查后问题还是没有解决,请通过社群或者 Github issue 提交反馈,并附上排查的过程和截图。
|
||||
@@ -1,22 +1,20 @@
|
||||
---
|
||||
title: "测试防护效果"
|
||||
group: "上手指南"
|
||||
order: 5
|
||||
---
|
||||
|
||||
# 测试防护效果
|
||||
|
||||
## 确认网站可以正常访问
|
||||
|
||||
根据雷池 WAF 配置的网站参数访问你的网站
|
||||
根据雷池 WAF 配置的网站参数访问你的网站。
|
||||
|
||||
打开浏览器访问 `http://<IP或域名>:<端口>/`
|
||||
打开浏览器访问 `http://<IP或域名>:<端口>/`。
|
||||
|
||||
> 网站协议默认是 http,勾选 ssl 则为 https
|
||||
> 网站协议默认是 http,勾选 ssl 则为 https
|
||||
> 主机名可以是雷池的 IP,也可以是网站的域名(确保域名已经解析到雷池)
|
||||
> 端口是你在雷池页面中配置的网站端口
|
||||
> 端口是你在雷池页面中配置的网站端口
|
||||
|
||||
若网站访问不正常,请参考 [网站无法访问](/posts/faq_access)
|
||||
若网站访问不正常,请参考 [网站无法访问](/docs/02-guide/03-config.md)。
|
||||
|
||||
## 尝试手动模拟攻击
|
||||
|
||||
@@ -27,7 +25,7 @@ order: 5
|
||||
|
||||
通过浏览器,你将会看到雷池已经发现并阻断了攻击请求。
|
||||
|
||||
若请求没有被阻断,请参考 [防护不生效](/posts/faq_protection)
|
||||
若请求没有被阻断,请参考 [防护不生效](/docs/03-faq/03-other.md)
|
||||
|
||||
## 自动化测试防护效果
|
||||
|
||||
@@ -35,16 +33,16 @@ order: 5
|
||||
|
||||
#### 下载测试工具
|
||||
|
||||
- [Windows 版本](/blazehttp/blazehttp_windows.exe)
|
||||
- [Mac 版本(x64)](/blazehttp/blazehttp_mac_x64)
|
||||
- [Mac 版本(M1)](/blazehttp/blazehttp_mac_m1)
|
||||
- [Linux 版本(x64)](/blazehttp/blazehttp_linux_x64)
|
||||
- [Linux 版本(ARM)](/blazehttp/blazehttp_linux_arm64)
|
||||
- [Windows 版本](https://waf-ce.chaitin.cn/blazehttp/blazehttp_windows.exe)
|
||||
- [Mac 版本(x64)](https://waf-ce.chaitin.cn/blazehttp/blazehttp_mac_x64)
|
||||
- [Mac 版本(M1)](https://waf-ce.chaitin.cn/blazehttp/blazehttp_mac_m1)
|
||||
- [Linux 版本(x64)](https://waf-ce.chaitin.cn/blazehttp/blazehttp_linux_x64)
|
||||
- [Linux 版本(ARM)](https://waf-ce.chaitin.cn/blazehttp/blazehttp_linux_arm64)
|
||||
- [源码仓库](https://github.com/chaitin/blazehttp)
|
||||
|
||||
#### 准备测试样本
|
||||
|
||||
- [测试样本](/blazehttp/testcases.zip)
|
||||
- [测试样本](https://waf-ce.chaitin.cn/blazehttp/testcases.zip)
|
||||
|
||||
下载请求样本后解压到 `testcases` 目录
|
||||
|
||||
@@ -55,16 +53,16 @@ order: 5
|
||||
3. 使用以下请求开始测试
|
||||
|
||||
```
|
||||
./blazehttp -t http://<IP或域名>:<端口> -g './testcases/*.http'
|
||||
./blazehttp -t http://<IP或域名>:<端口> -g './testcases/**/*.http'
|
||||
```
|
||||
|
||||
#### 测试效果展示
|
||||
|
||||
```
|
||||
# 测试请求
|
||||
./blazehttp -t http://192.168.0.1:8080 -g './testcases/*.http'
|
||||
./blazehttp -t http://192.168.0.1:8080 -g './testcases/**/*.http'
|
||||
|
||||
sending 100% |██████████████████████████████████████████| (18/18, 86 it/s)
|
||||
sending 100% |██████████████████████████████████████████| (18/18, 86 it/s)
|
||||
Total http file: 18, success: 18 failed: 0
|
||||
Stat http response code
|
||||
|
||||