create-dwy 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/create.js +50 -3
- package/src/utils.js +4 -0
- package/templates/claude-global/CLAUDE.md +81 -6
- package/templates/claude-global/rules/dwy-postgres.md +89 -0
- package/templates/claude-global/rules/dwy-python-backend.md +492 -0
- package/templates/claude-global/rules/dwy-python-core.md +94 -634
- package/templates/claude-global/rules/dwy-python-testing.md +115 -13
- package/templates/claude-global/rules/dwy-redis.md +88 -0
- package/templates/claude-global/rules/dwy-vue-core.md +341 -645
- package/templates/claude-global/rules/dwy-vue-pinia.md +47 -34
- package/templates/claude-global/rules/dwy-vue-testing.md +120 -55
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +1 -1
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +1 -1
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +22 -15
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +23 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +100 -1
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +142 -276
- package/templates/claude-global/skills/dwy-eui/SKILL.md +264 -182
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +117 -129
- package/templates/claude-global/skills/dwy-git-commit/SKILL.md +125 -0
- package/templates/claude-global/skills/dwy-publish/SKILL.md +1 -1
- package/templates/claude-global/{rules/dwy-tech-stack.md → skills/dwy-tech-stack/SKILL.md} +4 -3
- package/templates/project/fullstack-monorepo/CLAUDE.md.hbs +81 -0
- package/templates/project/fullstack-monorepo/backend/.python-version +1 -0
- package/templates/project/fullstack-monorepo/backend/CLAUDE.md.hbs +68 -0
- package/templates/project/fullstack-monorepo/backend/Dockerfile.dev +23 -0
- package/templates/project/fullstack-monorepo/backend/alembic/env.py.hbs +68 -0
- package/templates/project/fullstack-monorepo/backend/alembic/script.py.mako +26 -0
- package/templates/project/fullstack-monorepo/backend/alembic.ini.hbs +43 -0
- package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/pyproject.toml +16 -0
- package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/src/dwy_shared/__init__.py +12 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/pyproject.toml.hbs +20 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/__init__.py.hbs +40 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/config.py +9 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/models.py.hbs +17 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/protocols.py +14 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/router.py.hbs +22 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/schemas.py +21 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/service.py +11 -0
- package/templates/project/fullstack-monorepo/backend/pyproject.toml.hbs +84 -0
- package/templates/project/fullstack-monorepo/backend/src/app/__init__.py +3 -0
- package/templates/project/fullstack-monorepo/backend/src/app/config.py.hbs +21 -0
- package/templates/project/fullstack-monorepo/backend/src/app/exceptions.py +26 -0
- package/templates/project/fullstack-monorepo/backend/src/app/factories.py +16 -0
- package/templates/project/fullstack-monorepo/backend/src/app/infra/__init__.py +1 -0
- package/templates/project/fullstack-monorepo/backend/src/app/main.py.hbs +65 -0
- package/templates/project/fullstack-monorepo/backend/src/app/middleware/__init__.py +1 -0
- package/templates/project/fullstack-monorepo/backend/tests/__init__.py +0 -0
- package/templates/project/fullstack-monorepo/backend/tests/conftest.py.hbs +42 -0
- package/templates/project/fullstack-monorepo/dev.sh.hbs +83 -0
- package/templates/project/fullstack-monorepo/docker-compose.dev.yml.hbs +36 -0
- package/templates/project/fullstack-monorepo/frontend/CLAUDE.md.hbs +35 -0
- package/templates/project/fullstack-monorepo/frontend/Dockerfile +18 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/index.html.hbs +13 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/package.json.hbs +31 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/App.vue.hbs +10 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/api/client.ts.hbs +11 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/features/{{initialDomain}}/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/index.css +12 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/main.ts +17 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/router/index.ts.hbs +24 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/stores/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/types/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/views/HomeView.vue.hbs +36 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/tsconfig.json +7 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/vite.config.ts.hbs +27 -0
- package/templates/project/fullstack-monorepo/frontend/nginx.conf +11 -0
- package/templates/project/fullstack-monorepo/frontend/package.json.hbs +13 -0
- package/templates/project/fullstack-monorepo/frontend/packages/core/package.json.hbs +15 -0
- package/templates/project/fullstack-monorepo/frontend/packages/core/src/index.ts +3 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/package.json.hbs +14 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/index.ts.hbs +16 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/pages/IndexPage.vue.hbs +9 -0
- package/templates/project/fullstack-monorepo/frontend/packages/ui/package.json.hbs +15 -0
- package/templates/project/fullstack-monorepo/frontend/packages/ui/src/index.ts +3 -0
- package/templates/project/fullstack-monorepo/frontend/pnpm-workspace.yaml.hbs +23 -0
- package/templates/project/fullstack-monorepo/frontend/tsconfig.base.json.hbs +23 -0
- package/templates/claude-global/rules/dwy-api-design.md +0 -110
- package/templates/claude-global/rules/dwy-git-commit-convention.md +0 -82
- package/templates/claude-global/rules/dwy-git-security.md +0 -53
- package/templates/claude-global/rules/dwy-payload-limits.md +0 -186
- package/templates/claude-global/rules/dwy-prefer-open-source.md +0 -32
- package/templates/claude-global/rules/dwy-project-structure.md +0 -181
- package/templates/claude-global/rules/dwy-python-fastapi.md +0 -272
- package/templates/claude-global/rules/dwy-python-orm.md +0 -61
- package/templates/claude-global/rules/dwy-python-security.md +0 -636
- package/templates/claude-global/rules/dwy-server-security.md +0 -518
- package/templates/claude-global/rules/dwy-vue-api.md +0 -146
- package/templates/claude-global/skills/dwy-ekit/references/data.md +0 -135
- package/templates/claude-global/skills/dwy-ekit/references/form.md +0 -68
- package/templates/claude-global/skills/dwy-ekit/references/request.md +0 -183
- package/templates/claude-global/skills/dwy-ekit/references/ui.md +0 -109
- package/templates/claude-global/skills/dwy-vue-core/SKILL.md +0 -890
- /package/templates/{claude-global/commands → project/fullstack-monorepo/backend/alembic/versions}/.gitkeep +0 -0
|
@@ -1,518 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 服务器/容器/Nginx/SSH/CORS/部署安全规范
|
|
3
|
-
paths:
|
|
4
|
-
- "**/docker-compose*.yml"
|
|
5
|
-
- "**/Dockerfile*"
|
|
6
|
-
- "**/nginx*.conf"
|
|
7
|
-
- "**/deploy*.sh"
|
|
8
|
-
- "**/.env.example"
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# 服务器安全部署规则
|
|
12
|
-
|
|
13
|
-
所有项目部署到云服务器时必须遵循以下规则。如果任何操作违反这些规则,必须**立即提示用户并获得明确确认**后才能继续。
|
|
14
|
-
|
|
15
|
-
## 一、端口与网络隔离
|
|
16
|
-
|
|
17
|
-
### 强制规则
|
|
18
|
-
- 数据库(PostgreSQL/MySQL/Redis 等)**禁止暴露到宿主机端口**,仅通过 Docker 内部网络访问
|
|
19
|
-
- 后端应用端口绑定 `127.0.0.1`,不允许 `0.0.0.0`
|
|
20
|
-
- 仅 Nginx/反向代理的 80/443 端口绑定 `0.0.0.0`
|
|
21
|
-
|
|
22
|
-
### docker-compose 示例
|
|
23
|
-
```yaml
|
|
24
|
-
# ✅ 正确
|
|
25
|
-
backend:
|
|
26
|
-
ports:
|
|
27
|
-
- "127.0.0.1:{port}:{port}"
|
|
28
|
-
|
|
29
|
-
db:
|
|
30
|
-
# 不写 ports,仅内部网络
|
|
31
|
-
|
|
32
|
-
# ❌ 违规 — 需要提示用户
|
|
33
|
-
db:
|
|
34
|
-
ports:
|
|
35
|
-
- "{db_port}:{db_port}"
|
|
36
|
-
backend:
|
|
37
|
-
ports:
|
|
38
|
-
- "{port}:{port}"
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## 二、防火墙
|
|
42
|
-
|
|
43
|
-
### 强制规则
|
|
44
|
-
- 生产服务器**必须启用防火墙**(ufw 或 iptables)
|
|
45
|
-
- **只开放业务必需端口**,其余全部关闭
|
|
46
|
-
|
|
47
|
-
### 最小端口策略
|
|
48
|
-
|
|
49
|
-
| 端口 | 用途 | 开放方式 |
|
|
50
|
-
|------|------|---------|
|
|
51
|
-
| 80 | HTTP(Nginx) | `ufw allow 80/tcp` |
|
|
52
|
-
| 443 | HTTPS(Nginx) | `ufw allow 443/tcp` |
|
|
53
|
-
| SSH 非标端口 | SSH 远程管理 | `ufw allow {ssh_port}/tcp` |
|
|
54
|
-
|
|
55
|
-
**禁止开放的端口:**
|
|
56
|
-
- 5432(PostgreSQL)
|
|
57
|
-
- 6379(Redis)
|
|
58
|
-
- 9000(MinIO)
|
|
59
|
-
- 8000-9999(后端应用)
|
|
60
|
-
|
|
61
|
-
### 配置示例
|
|
62
|
-
```bash
|
|
63
|
-
ufw default deny incoming
|
|
64
|
-
ufw default allow outgoing
|
|
65
|
-
ufw allow 80/tcp
|
|
66
|
-
ufw allow 443/tcp
|
|
67
|
-
ufw allow {ssh_port}/tcp
|
|
68
|
-
ufw enable
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### 违规检测
|
|
72
|
-
如果在部署脚本或命令中发现 `ufw allow 5432` 等数据库端口开放命令,必须立即提示用户。
|
|
73
|
-
|
|
74
|
-
## 三、凭证与密钥管理
|
|
75
|
-
|
|
76
|
-
### 强制规则
|
|
77
|
-
- **禁止在代码中硬编码**数据库密码、API Key、Secret 等凭证
|
|
78
|
-
- 所有凭证通过 `.env` 文件或环境变量注入
|
|
79
|
-
- `.env` 文件必须在 `.gitignore` 中排除
|
|
80
|
-
- 数据库密码最少 24 位,包含大小写字母和数字
|
|
81
|
-
- 提供 `.env.example` 模板文件(仅含占位符,不含真实值)
|
|
82
|
-
|
|
83
|
-
### 违规检测
|
|
84
|
-
如果在代码中发现以下内容,必须提示用户:
|
|
85
|
-
- 硬编码的数据库连接字符串含真实密码
|
|
86
|
-
- API Key 出现在非 `.env` 文件中
|
|
87
|
-
- SSH 密码、私钥路径出现在代码或文档中
|
|
88
|
-
|
|
89
|
-
## 四、Nginx 安全配置
|
|
90
|
-
|
|
91
|
-
### 核心原则
|
|
92
|
-
**只开放业务必需的路径和方法,其余一律拒绝。**
|
|
93
|
-
|
|
94
|
-
### 强制规则
|
|
95
|
-
|
|
96
|
-
每个项目的 Nginx 配置必须包含以下安全基线(具体路径、端口、超时时间根据项目在项目级 CLAUDE.md 中配置):
|
|
97
|
-
|
|
98
|
-
**1. 频率限制(http 块)**
|
|
99
|
-
```nginx
|
|
100
|
-
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
101
|
-
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=3r/m;
|
|
102
|
-
limit_req_zone $binary_remote_addr zone=page_limit:10m rate=30r/s;
|
|
103
|
-
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
注意:`limit_req_zone` 和 `limit_conn_zone` **必须放在 `http` 块**,不能放在 `server` 块。
|
|
107
|
-
|
|
108
|
-
**2. 安全响应头(server 块)**
|
|
109
|
-
```nginx
|
|
110
|
-
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
111
|
-
add_header X-Content-Type-Options "nosniff" always;
|
|
112
|
-
add_header X-XSS-Protection "1; mode=block" always;
|
|
113
|
-
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
114
|
-
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
|
|
115
|
-
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**3. HTTPS 强制(server 块)**
|
|
119
|
-
```nginx
|
|
120
|
-
# HTTP → HTTPS 重定向
|
|
121
|
-
server {
|
|
122
|
-
listen 80;
|
|
123
|
-
server_name example.com;
|
|
124
|
-
return 301 https://$server_name$request_uri;
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
生产环境**必须启用 HTTPS**,使用 certbot 自动续期:
|
|
129
|
-
```bash
|
|
130
|
-
certbot --nginx -d example.com
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**4. 全局限制(server 块)**
|
|
134
|
-
```nginx
|
|
135
|
-
client_body_timeout 10s;
|
|
136
|
-
client_header_timeout 10s;
|
|
137
|
-
limit_conn conn_limit 20;
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
**5. 敏感文件屏蔽(最高优先级)**
|
|
141
|
-
```nginx
|
|
142
|
-
location ~ /\. {
|
|
143
|
-
deny all;
|
|
144
|
-
return 404;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
location ~* \.(env|git|xlsx|sql|log|bak|dump|tar|gz|zip|csv)$ {
|
|
148
|
-
deny all;
|
|
149
|
-
return 404;
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**6. API 路径最小化**
|
|
154
|
-
```nginx
|
|
155
|
-
location /api {
|
|
156
|
-
limit_except GET POST {
|
|
157
|
-
deny all;
|
|
158
|
-
}
|
|
159
|
-
limit_req zone=api_limit burst=20 nodelay;
|
|
160
|
-
# proxy_pass 等具体配置由项目决定
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**7. 登录接口专用限流**
|
|
165
|
-
```nginx
|
|
166
|
-
location /api/auth/login {
|
|
167
|
-
limit_req zone=login_limit burst=5 nodelay;
|
|
168
|
-
proxy_pass http://127.0.0.1:{backend_port};
|
|
169
|
-
proxy_set_header Host $host;
|
|
170
|
-
proxy_set_header X-Real-IP $remote_addr;
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**8. 攻击路径屏蔽**
|
|
175
|
-
```nginx
|
|
176
|
-
location ~ ^/(admin|phpmyadmin|wp-admin|cgi-bin|actuator|console|debug|\.well-known/security\.txt) {
|
|
177
|
-
deny all;
|
|
178
|
-
return 404;
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**9. 扫描器 User-Agent 屏蔽(http 块)**
|
|
183
|
-
```nginx
|
|
184
|
-
map $http_user_agent $is_scanner {
|
|
185
|
-
default 0;
|
|
186
|
-
~*sqlmap 1;
|
|
187
|
-
~*nmap 1;
|
|
188
|
-
~*nikto 1;
|
|
189
|
-
~*dirbuster 1;
|
|
190
|
-
~*gobuster 1;
|
|
191
|
-
~*masscan 1;
|
|
192
|
-
~*zgrab 1;
|
|
193
|
-
~*python-requests 1;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
# 在 server 块中
|
|
197
|
-
if ($is_scanner) {
|
|
198
|
-
return 403;
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 违规检测
|
|
203
|
-
如果 Nginx 配置中出现以下情况,必须提示用户:
|
|
204
|
-
- 缺少 `limit_req` 频率限制
|
|
205
|
-
- API 路径允许 PUT/DELETE 但业务不需要
|
|
206
|
-
- 缺少 `location ~ /\.` 敏感文件屏蔽
|
|
207
|
-
- 使用 `proxy_pass` 但未设置 `proxy_connect_timeout`
|
|
208
|
-
- 生产环境未启用 HTTPS
|
|
209
|
-
- 缺少登录接口专用限流
|
|
210
|
-
|
|
211
|
-
### 部署后必须验证
|
|
212
|
-
```bash
|
|
213
|
-
# 1. 敏感文件返回 404
|
|
214
|
-
curl -s -o /dev/null -w "%{http_code}" http://{server}/.git/config
|
|
215
|
-
curl -s -o /dev/null -w "%{http_code}" http://{server}/.env
|
|
216
|
-
|
|
217
|
-
# 2. 安全头存在
|
|
218
|
-
curl -sI https://{server}/ | grep -i "x-frame-options"
|
|
219
|
-
curl -sI https://{server}/ | grep -i "x-content-type-options"
|
|
220
|
-
curl -sI https://{server}/ | grep -i "strict-transport-security"
|
|
221
|
-
|
|
222
|
-
# 3. 非法方法被拒绝
|
|
223
|
-
curl -s -o /dev/null -w "%{http_code}" -X DELETE http://{server}/api/health
|
|
224
|
-
|
|
225
|
-
# 4. 攻击路径被屏蔽
|
|
226
|
-
curl -s -o /dev/null -w "%{http_code}" http://{server}/phpmyadmin
|
|
227
|
-
|
|
228
|
-
# 5. 扫描器 UA 被拒绝
|
|
229
|
-
curl -s -o /dev/null -w "%{http_code}" -A "sqlmap/1.0" http://{server}/
|
|
230
|
-
|
|
231
|
-
# 6. 登录限流生效
|
|
232
|
-
for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" -X POST http://{server}/api/auth/login; done
|
|
233
|
-
|
|
234
|
-
# 7. 正常服务可用
|
|
235
|
-
curl -s http://{server}/api/health
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
## 五、Docker 容器安全
|
|
239
|
-
|
|
240
|
-
### 强制规则
|
|
241
|
-
- Dockerfile **必须使用非 root 用户**运行应用
|
|
242
|
-
- 必须创建 `.dockerignore` 排除 `.env`、`.git`、`cache/`、`__pycache__`
|
|
243
|
-
- Python 项目设置 `ENV PYTHONUNBUFFERED=1`
|
|
244
|
-
- Node.js 项目设置 `ENV NODE_ENV=production`
|
|
245
|
-
- **使用最小化基础镜像**(`python:3.11-slim`,不用 `python:3.11`)
|
|
246
|
-
- **禁止在镜像中安装不必要的工具**(ssh、telnet、curl 等调试工具生产不需要)
|
|
247
|
-
- 容器文件系统**建议只读挂载**(`read_only: true`),需要写入的目录用 `tmpfs` 或 volume
|
|
248
|
-
|
|
249
|
-
### Dockerfile 安全示例
|
|
250
|
-
```dockerfile
|
|
251
|
-
FROM python:3.11-slim
|
|
252
|
-
|
|
253
|
-
# 只安装必要的编译依赖,安装后清理
|
|
254
|
-
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
|
|
255
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
256
|
-
|
|
257
|
-
WORKDIR /app
|
|
258
|
-
COPY pyproject.toml .
|
|
259
|
-
RUN pip install --no-cache-dir .
|
|
260
|
-
|
|
261
|
-
COPY src/ src/
|
|
262
|
-
|
|
263
|
-
# 创建非 root 用户
|
|
264
|
-
RUN useradd -m appuser
|
|
265
|
-
USER appuser
|
|
266
|
-
|
|
267
|
-
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## 六、CORS 配置
|
|
271
|
-
|
|
272
|
-
### 强制规则
|
|
273
|
-
- **禁止生产环境使用 `*` 通配符**
|
|
274
|
-
- 必须明确列出允许的域名/IP
|
|
275
|
-
- 通过环境变量配置,便于不同环境切换
|
|
276
|
-
|
|
277
|
-
```
|
|
278
|
-
# ✅ 正确 — 明确列出
|
|
279
|
-
ALLOWED_ORIGINS=["https://example.com","https://www.example.com"]
|
|
280
|
-
|
|
281
|
-
# ❌ 违规
|
|
282
|
-
ALLOWED_ORIGINS=*
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## 七、数据安全
|
|
286
|
-
|
|
287
|
-
### 强制规则
|
|
288
|
-
- 手机号在返回前必须脱敏(如 `138****5678`)
|
|
289
|
-
- 身份证号在返回前必须脱敏(如 `420***********1234`)
|
|
290
|
-
- 查询日志中的 PII(姓名、手机号等)建议加密存储
|
|
291
|
-
- 数据源文件(Excel/CSV 等)不可通过 Web 直接访问
|
|
292
|
-
- 数据库数据仅通过应用层访问,不暴露数据库管理端口
|
|
293
|
-
|
|
294
|
-
## 八、Git 仓库安全
|
|
295
|
-
|
|
296
|
-
### 强制规则
|
|
297
|
-
`.gitignore` 必须包含:
|
|
298
|
-
```
|
|
299
|
-
.env
|
|
300
|
-
*.env
|
|
301
|
-
__pycache__/
|
|
302
|
-
*.pyc
|
|
303
|
-
node_modules/
|
|
304
|
-
dist/
|
|
305
|
-
*.log
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### 违规检测
|
|
309
|
-
如果发现以下文件被 `git add`,必须立即阻止并提示用户:
|
|
310
|
-
- `.env` 文件
|
|
311
|
-
- 含真实密码/密钥的配置文件
|
|
312
|
-
- SSH 私钥文件
|
|
313
|
-
- 数据库备份文件(`.sql`、`.dump`)
|
|
314
|
-
- 含敏感数据的原始数据文件(`.xlsx`、`.csv`)
|
|
315
|
-
|
|
316
|
-
## 九、SSH 安全
|
|
317
|
-
|
|
318
|
-
### 强制规则
|
|
319
|
-
- **禁止密码登录**,仅允许密钥认证
|
|
320
|
-
- **禁止 root 密码登录**(`PermitRootLogin prohibit-password` 或 `without-password`)
|
|
321
|
-
- **禁止使用默认 22 端口**,改为非标端口(如 66)
|
|
322
|
-
- SSH 私钥文件(`id_rsa`、`id_ed25519` 等)**禁止出现在代码仓库、文档、聊天记录中**
|
|
323
|
-
- 服务器 IP、SSH 端口、用户名**禁止写入代码仓库**(包括 README、部署文档)
|
|
324
|
-
|
|
325
|
-
### 违规检测
|
|
326
|
-
如果在任何文件中发现以下内容,必须立即提示用户:
|
|
327
|
-
- SSH 连接命令含完整 IP + 端口 + 用户名
|
|
328
|
-
- 服务器凭证信息(IP、端口、密码、密钥路径)
|
|
329
|
-
- `sshd_config` 中 `PasswordAuthentication yes`
|
|
330
|
-
|
|
331
|
-
### 安全基线配置(`/etc/ssh/sshd_config`)
|
|
332
|
-
```
|
|
333
|
-
Port 66 # 非标端口
|
|
334
|
-
PermitRootLogin prohibit-password # 禁止 root 密码登录
|
|
335
|
-
PasswordAuthentication no # 禁止所有密码登录
|
|
336
|
-
PubkeyAuthentication yes # 启用密钥认证
|
|
337
|
-
MaxAuthTries 3 # 最大尝试次数
|
|
338
|
-
LoginGraceTime 30 # 登录超时 30 秒
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### 配合 fail2ban
|
|
342
|
-
服务器**必须配置** fail2ban 防暴力破解:
|
|
343
|
-
```
|
|
344
|
-
# /etc/fail2ban/jail.local
|
|
345
|
-
[sshd]
|
|
346
|
-
enabled = true
|
|
347
|
-
port = 66
|
|
348
|
-
maxretry = 5
|
|
349
|
-
bantime = 3600
|
|
350
|
-
|
|
351
|
-
# Nginx 暴力请求封禁
|
|
352
|
-
[nginx-limit-req]
|
|
353
|
-
enabled = true
|
|
354
|
-
filter = nginx-limit-req
|
|
355
|
-
logpath = /var/log/nginx/error.log
|
|
356
|
-
maxretry = 10
|
|
357
|
-
bantime = 600
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### 部署操作规范
|
|
361
|
-
- 通过 SSH 连接服务器时,连接信息从本地 `~/.ssh/config` 读取,不在命令行暴露
|
|
362
|
-
- 推荐在 `~/.ssh/config` 中配置 Host 别名:
|
|
363
|
-
```
|
|
364
|
-
Host myserver
|
|
365
|
-
HostName <ip>
|
|
366
|
-
Port <port>
|
|
367
|
-
User root
|
|
368
|
-
IdentityFile ~/.ssh/id_rsa
|
|
369
|
-
```
|
|
370
|
-
- 部署脚本中使用 `ssh myserver` 而非完整连接命令
|
|
371
|
-
|
|
372
|
-
## 十、系统安全加固
|
|
373
|
-
|
|
374
|
-
### 自动安全更新
|
|
375
|
-
生产服务器**必须启用自动安全更新**:
|
|
376
|
-
```bash
|
|
377
|
-
apt install -y unattended-upgrades
|
|
378
|
-
dpkg-reconfigure -plow unattended-upgrades
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### 文件权限
|
|
382
|
-
| 文件 | 权限 | 命令 |
|
|
383
|
-
|------|------|------|
|
|
384
|
-
| `.env` | 600(仅所有者读写) | `chmod 600 .env` |
|
|
385
|
-
| SSH 私钥 | 600 | `chmod 600 ~/.ssh/id_*` |
|
|
386
|
-
| `authorized_keys` | 644 | `chmod 644 ~/.ssh/authorized_keys` |
|
|
387
|
-
| 应用目录 | 755 | `chmod 755 /opt/myapp` |
|
|
388
|
-
|
|
389
|
-
**禁止出现 777 权限。** 如果在部署脚本中发现 `chmod 777`,必须立即提示用户。
|
|
390
|
-
|
|
391
|
-
### 日志监控
|
|
392
|
-
生产服务器必须定期检查以下日志:
|
|
393
|
-
```bash
|
|
394
|
-
# SSH 登录记录
|
|
395
|
-
journalctl -u sshd --since "1 hour ago"
|
|
396
|
-
|
|
397
|
-
# Nginx 错误日志
|
|
398
|
-
tail -100 /var/log/nginx/error.log
|
|
399
|
-
|
|
400
|
-
# 被 fail2ban 封禁的 IP
|
|
401
|
-
fail2ban-client status sshd
|
|
402
|
-
|
|
403
|
-
# Docker 容器日志
|
|
404
|
-
docker compose logs --tail 100 backend
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
## 十一、数据持久化与防删除
|
|
408
|
-
|
|
409
|
-
### 核心原则
|
|
410
|
-
**所有业务数据必须通过 Docker Named Volume 持久化。任何可能导致数据丢失的操作,必须提前说明原因并获得用户确认。**
|
|
411
|
-
|
|
412
|
-
### 强制规则 — Docker Volume
|
|
413
|
-
|
|
414
|
-
- 数据库容器**必须挂载 Named Volume**到数据目录
|
|
415
|
-
- **禁止使用匿名卷或仅容器内存储**
|
|
416
|
-
- 重启策略必须设为 `unless-stopped` 或 `always`
|
|
417
|
-
|
|
418
|
-
```yaml
|
|
419
|
-
# ✅ 正确 — Named Volume 持久化
|
|
420
|
-
db:
|
|
421
|
-
volumes:
|
|
422
|
-
- dbdata:/var/lib/postgresql/data
|
|
423
|
-
restart: unless-stopped
|
|
424
|
-
|
|
425
|
-
volumes:
|
|
426
|
-
dbdata:
|
|
427
|
-
|
|
428
|
-
# ❌ 违规 — 数据在容器内,删除即丢失
|
|
429
|
-
db:
|
|
430
|
-
# 没有 volumes 配置
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### 危险操作清单
|
|
434
|
-
|
|
435
|
-
以下命令**执行前必须停止、说明原因、说明影响、等待用户确认**:
|
|
436
|
-
|
|
437
|
-
| 命令 | 风险等级 | 后果 |
|
|
438
|
-
|---|---|---|
|
|
439
|
-
| `docker compose down -v` | **致命** | 删除所有 Volume,数据永久丢失 |
|
|
440
|
-
| `docker volume rm <name>` | **致命** | 删除指定 Volume,数据永久丢失 |
|
|
441
|
-
| `docker volume prune` | **致命** | 删除所有未使用的 Volume |
|
|
442
|
-
| `docker system prune -a --volumes` | **致命** | 清除所有镜像、容器、Volume |
|
|
443
|
-
| `rm -rf /var/lib/docker/volumes/` | **致命** | 删除所有 Docker 数据 |
|
|
444
|
-
| `DROP TABLE` / `DROP DATABASE` | **致命** | 删除数据库表或整个数据库 |
|
|
445
|
-
| `DELETE FROM <table>` 无 WHERE | **高危** | 清空整张表数据 |
|
|
446
|
-
| `TRUNCATE TABLE` | **高危** | 清空表数据且无法回滚 |
|
|
447
|
-
| `rm -rf` 含数据目录 | **高危** | 删除文件系统数据 |
|
|
448
|
-
| `git clean -fdx` | **高危** | 删除所有未跟踪文件(含数据文件) |
|
|
449
|
-
|
|
450
|
-
### 违规处理
|
|
451
|
-
当 AI 需要执行以上任何操作时,必须:
|
|
452
|
-
|
|
453
|
-
1. **立即停止**,不执行该命令
|
|
454
|
-
2. **说明原因**:为什么需要执行这个操作
|
|
455
|
-
3. **说明影响**:会删除哪些数据、多少条记录
|
|
456
|
-
4. **提供替代方案**(如有):是否有不删除数据的方式达到目的
|
|
457
|
-
5. **等待用户明确回复"确认"**后才能执行
|
|
458
|
-
|
|
459
|
-
### 备份
|
|
460
|
-
生产环境**必须配置定期备份**:
|
|
461
|
-
```bash
|
|
462
|
-
# PostgreSQL 每日备份(建议 cron 每天凌晨执行)
|
|
463
|
-
docker exec <db_container> pg_dump -U <user> <database> > backup_$(date +%Y%m%d).sql
|
|
464
|
-
|
|
465
|
-
# 备份文件不得放在 Web 可访问目录
|
|
466
|
-
# 建议放在 /var/backups/ 下,定期清理超过 30 天的备份
|
|
467
|
-
find /var/backups/ -name "backup_*.sql" -mtime +30 -delete
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
## 十二、部署后检查清单
|
|
471
|
-
|
|
472
|
-
每次部署完成后,必须执行以下验证:
|
|
473
|
-
|
|
474
|
-
```bash
|
|
475
|
-
# 1. 防火墙状态
|
|
476
|
-
ufw status | grep -E "80|443|{ssh_port}"
|
|
477
|
-
|
|
478
|
-
# 2. 数据库端口不可外部访问
|
|
479
|
-
timeout 3 nc -z {server_ip} 5432 && echo "FAIL" || echo "PASS"
|
|
480
|
-
timeout 3 nc -z {server_ip} 6379 && echo "FAIL" || echo "PASS"
|
|
481
|
-
|
|
482
|
-
# 3. 后端端口不可外部直连
|
|
483
|
-
timeout 3 nc -z {server_ip} {backend_port} && echo "FAIL" || echo "PASS"
|
|
484
|
-
|
|
485
|
-
# 4. 敏感文件不可下载
|
|
486
|
-
curl -s -o /dev/null -w "%{http_code}" https://{server}/.git/config # 必须 404
|
|
487
|
-
curl -s -o /dev/null -w "%{http_code}" https://{server}/.env # 必须 404
|
|
488
|
-
|
|
489
|
-
# 5. HTTPS 工作正常
|
|
490
|
-
curl -sI https://{server}/ | grep "strict-transport-security" # 必须有输出
|
|
491
|
-
|
|
492
|
-
# 6. 安全头完整
|
|
493
|
-
curl -sI https://{server}/ | grep -i "x-frame-options"
|
|
494
|
-
curl -sI https://{server}/ | grep -i "x-content-type-options"
|
|
495
|
-
curl -sI https://{server}/ | grep -i "content-security-policy"
|
|
496
|
-
|
|
497
|
-
# 7. 非法方法被拒绝
|
|
498
|
-
curl -s -o /dev/null -w "%{http_code}" -X DELETE https://{server}/api/health # 必须 403/405
|
|
499
|
-
|
|
500
|
-
# 8. Volume 存在且数据完整
|
|
501
|
-
docker volume ls | grep {volume_name} # 必须存在
|
|
502
|
-
|
|
503
|
-
# 9. fail2ban 运行中
|
|
504
|
-
fail2ban-client status sshd
|
|
505
|
-
|
|
506
|
-
# 10. 正常服务可用
|
|
507
|
-
curl -s https://{server}/api/health # 必须返回 200
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
## 十三、违规处理流程
|
|
511
|
-
|
|
512
|
-
当 AI 检测到操作违反以上任何规则时:
|
|
513
|
-
|
|
514
|
-
1. **立即停止**当前操作
|
|
515
|
-
2. **明确告知**用户违反了哪条规则
|
|
516
|
-
3. **说明风险**(可能导致的安全后果)
|
|
517
|
-
4. **等待用户确认**后才能继续执行
|
|
518
|
-
5. 如果用户坚持违规操作,在代码中添加 `# SECURITY WARNING: 用户已确认此操作` 注释
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Vue 前端网络层(ekit createRequest + 拦截器插件)
|
|
3
|
-
paths:
|
|
4
|
-
- "**/api/**/*.ts"
|
|
5
|
-
- "**/api/**/*.vue"
|
|
6
|
-
- "**/api/client.ts"
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Vue 前端网络层规范
|
|
10
|
-
|
|
11
|
-
## 六、API 请求规范
|
|
12
|
-
|
|
13
|
-
### 强制规则
|
|
14
|
-
|
|
15
|
-
- API 文件按**业务域拆分**(`api/user.ts`、`api/exam.ts`)
|
|
16
|
-
- 每个 API 函数**独立导出**,不使用 default export
|
|
17
|
-
- 请求参数和响应使用**类型标注**
|
|
18
|
-
- **禁止**在组件中直接创建 axios 实例或调用 `fetch`
|
|
19
|
-
|
|
20
|
-
### API 文件结构
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
// api/user.ts
|
|
24
|
-
import request from '@/utils/request'
|
|
25
|
-
import type { UserInfo, UserCreate, UserUpdate } from '@/types/user'
|
|
26
|
-
|
|
27
|
-
export function getUsers(params?: { page?: number; page_size?: number }) {
|
|
28
|
-
return request.get<{ items: UserInfo[]; total: number }>('/users', { params })
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function getUser(id: number) {
|
|
32
|
-
return request.get<UserInfo>(`/users/${id}`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function createUser(data: UserCreate) {
|
|
36
|
-
return request.post<UserInfo>('/users', data)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function updateUser(id: number, data: UserUpdate) {
|
|
40
|
-
return request.put<UserInfo>(`/users/${id}`, data)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function deleteUser(id: number) {
|
|
44
|
-
return request.delete(`/users/${id}`)
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### 请求实例配置
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
// utils/request.ts
|
|
52
|
-
import axios from 'axios'
|
|
53
|
-
|
|
54
|
-
const request = axios.create({
|
|
55
|
-
baseURL: '/api',
|
|
56
|
-
timeout: 30000,
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
// Token 注入
|
|
60
|
-
request.interceptors.request.use((config) => {
|
|
61
|
-
const token = localStorage.getItem('token')
|
|
62
|
-
if (token) {
|
|
63
|
-
config.headers.Authorization = `Bearer ${token}`
|
|
64
|
-
}
|
|
65
|
-
return config
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
// 401 处理
|
|
69
|
-
request.interceptors.response.use(
|
|
70
|
-
(response) => response,
|
|
71
|
-
(error) => {
|
|
72
|
-
if (error.response?.status === 401) {
|
|
73
|
-
localStorage.removeItem('token')
|
|
74
|
-
window.location.href = '/login'
|
|
75
|
-
}
|
|
76
|
-
return Promise.reject(error)
|
|
77
|
-
},
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
export default request
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### 禁止的写法
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
// ❌ 在组件中直接调用
|
|
87
|
-
const res = await axios.get('/api/users')
|
|
88
|
-
|
|
89
|
-
// ✅ 通过 API 模块调用
|
|
90
|
-
import { getUsers } from '@/api/user'
|
|
91
|
-
const res = await getUsers()
|
|
92
|
-
|
|
93
|
-
// ❌ API 函数使用 default export
|
|
94
|
-
export default { getUsers, createUser }
|
|
95
|
-
|
|
96
|
-
// ✅ 每个函数独立导出
|
|
97
|
-
export function getUsers() { ... }
|
|
98
|
-
export function createUser() { ... }
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## 网络层规范(ekit 统一方案)
|
|
104
|
-
|
|
105
|
-
### Vue 前端
|
|
106
|
-
|
|
107
|
-
**唯一方式:** ekit `createRequest()` + 插件。
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
// api/client.ts
|
|
111
|
-
import { createRequest } from '@dwydev/ekit'
|
|
112
|
-
|
|
113
|
-
export const request = createRequest({
|
|
114
|
-
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
115
|
-
plugins: [
|
|
116
|
-
tokenPlugin(), // 注入 Authorization header
|
|
117
|
-
refreshTokenPlugin(), // 401 自动刷新
|
|
118
|
-
unwrapPlugin(), // 解包响应 → data
|
|
119
|
-
],
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
// api/user.ts — 按业务域拆分
|
|
123
|
-
export function getUsers(params: { page: number; page_size: number }) {
|
|
124
|
-
return request.get('/users', { params })
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function createUser(data: UserCreate) {
|
|
128
|
-
return request.post('/users', data)
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**禁止:** `axios.create()`、`fetch()`、`new XMLHttpRequest()`
|
|
133
|
-
|
|
134
|
-
### 响应解包
|
|
135
|
-
|
|
136
|
-
前端的解包逻辑必须与后端 eapi response 格式对齐:
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
后端返回: { code: "SUCCESS", message: "success", data: {...}, timestamp: ... }
|
|
140
|
-
↓ unwrap
|
|
141
|
-
前端拿到: {...} (直接是 data 的内容)
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
分页响应解包后拿到 `{ items, total, page, page_size }`(保持 snake_case 对齐后端 JSON)。
|
|
145
|
-
|
|
146
|
-
业务失败时(`code !== "SUCCESS"`),unwrapPlugin 抛出的 HttpError 携带 `businessCode` 和 `apiResponse`。业务 catch 中用 `isApiBusinessError(err)` 做类型守卫后按 `err.businessCode` 分支(如 `NOT_FOUND` / `VALIDATION_ERROR` / `PERMISSION_DENIED`);`VALIDATION_ERROR` 场景可用 `extractValidationErrors(err)` 直接给 vee-validate 的 `setErrors` 回填。
|