@wdyy/skills 0.1.27 → 0.1.28
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/.well-known/skills/index.json +6 -1
- package/.well-known/skills/wdyy-database-standard/SKILL.md +5 -5
- package/.well-known/skills/wdyy-database-standard/reference/database-rules.md +2 -1
- package/.well-known/skills/wdyy-database-standard/scripts/validate-table-design.mjs +10 -3
- package/.well-known/skills/wdyy-database-standard/scripts/validate-table-design.test.mjs +36 -0
- package/.well-known/skills/wdyy-database-standard/templates/table-design.template.md +3 -2
- package/.well-known/skills/wdyy-safety-review/SKILL.md +53 -0
- package/.well-known/skills/wdyy-safety-review/agents/openai.yaml +4 -0
- package/.well-known/skills/wdyy-safety-review/references/security-review-rules.md +51 -0
- package/.well-known/skills/wdyy-safety-review/scripts/validate-security-checklist.mjs +66 -0
- package/.well-known/skills/wdyy-safety-review/scripts/validate-security-checklist.test.mjs +69 -0
- package/.well-known/skills/wdyy-safety-review/templates/security-checklist.template.md +17 -0
- package/README.md +8 -6
- package/lib/wdyy-cli.js +3 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"skills": [
|
|
3
3
|
{
|
|
4
4
|
"name": "wdyy-database-standard",
|
|
5
|
-
"description": "将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF)的 PostgreSQL 18 无外键逻辑关联设计;反范式例外须记录依据与一致性策略并经人工确认。约束业务域表名前缀、自增主键 {前缀}_id、统一状态字段 {前缀}_status、
|
|
5
|
+
"description": "将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF)的 PostgreSQL 18 无外键逻辑关联设计;反范式例外须记录依据与一致性策略并经人工确认。约束业务域表名前缀、自增主键 {前缀}_id、统一状态字段 {前缀}_status、timestamptz(0) 时间类型与 Asia/Shanghai 默认时区、字段可空策略、注释、幂等、审计与向前兼容。Use when 审查 DDL、设计表结构、建立 V001 基线、增加后续迁移或验证生产数据库兼容性时。",
|
|
6
6
|
"files": ["SKILL.md", "agents/openai.yaml", "reference/database-rules.md", "scripts/apply-migrations.sh", "scripts/apply-migrations.test.mjs", "scripts/validate-migration-layout.mjs", "scripts/validate-migration-layout.test.mjs", "scripts/validate-table-design.mjs", "scripts/validate-table-design.test.mjs", "templates/table-design.template.md"]
|
|
7
7
|
},
|
|
8
8
|
{
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"name": "wdyy-ui",
|
|
25
25
|
"description": "统一医院信息系统前端页面的布局、清新生命绿配色、医院 Logo、常用组件和响应式行为。Use when 新建、改造或审查患者管理、药事管理、医生工作站及相近 HIS 页面时;不负责定义业务流程、接口或数据模型。",
|
|
26
26
|
"files": ["SKILL.md", "agents/openai.yaml", "references/ui-standard.md", "assets/template/common-business.html", "assets/template/pharmacy-management.html", "assets/template/clinical-workstation.html", "assets/template/styles.css", "assets/template/logo.png"]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "wdyy-safety-review",
|
|
30
|
+
"description": "对整个软件执行证据驱动的安全审核并生成四列 Markdown 检查清单。Use when 审查或设计身份认证与权限、Web/API、文件上传下载、敏感数据、源码与依赖、安全日志、漏洞扫描或漏洞验证;即使用户只说“安全检查”“代码安全”“接口安全”“扫描漏洞”也应使用本 Skill。",
|
|
31
|
+
"files": ["SKILL.md", "agents/openai.yaml", "references/security-review-rules.md", "templates/security-checklist.template.md", "scripts/validate-security-checklist.mjs", "scripts/validate-security-checklist.test.mjs"]
|
|
27
32
|
}
|
|
28
33
|
]
|
|
29
34
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wdyy-database-standard
|
|
3
|
-
description: 将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF)的 PostgreSQL 18 无外键逻辑关联设计;反范式例外须记录依据与一致性策略并经人工确认。约束业务域表名前缀、自增主键 {前缀}_id、统一状态字段 {前缀}_status、
|
|
3
|
+
description: 将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF)的 PostgreSQL 18 无外键逻辑关联设计;反范式例外须记录依据与一致性策略并经人工确认。约束业务域表名前缀、自增主键 {前缀}_id、统一状态字段 {前缀}_status、timestamptz(0) 时间类型与 Asia/Shanghai 默认时区、字段可空策略、注释、幂等、审计与向前兼容。Use when 审查 DDL、设计表结构、建立 V001 基线、增加后续迁移或验证生产数据库兼容性时。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# 企业数据库规范
|
|
@@ -27,7 +27,7 @@ description: 将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF
|
|
|
27
27
|
|
|
28
28
|
1. 确认 PRD 与 DDL 已通过就绪检查,识别实体、聚合关系、状态机、唯一约束、幂等键和审计责任人。
|
|
29
29
|
2. 为每个表评估第三范式(3NF),识别重复数据、部分依赖和传递依赖,并在表设计说明中记录结论。确需反范式设计时,先记录业务或性能依据、冗余数据权威来源、一致性维护策略、影响和验证方案;未取得工程师明确人工确认前,不得生成或执行相关 DDL。
|
|
30
|
-
3. 为每个表设计小写下划线名称,表名必须添加业务域英文简写前缀(见业务域前缀映射表);若业务实体无对应前缀,须提示用户并给出建议,不得自行决定。首字段固定为 `{业务域前缀}_id`,类型使用自增主键,不得使用 `uuid`;所有时间类型字段统一使用 `
|
|
30
|
+
3. 为每个表设计小写下划线名称,表名必须添加业务域英文简写前缀(见业务域前缀映射表);若业务实体无对应前缀,须提示用户并给出建议,不得自行决定。首字段固定为 `{业务域前缀}_id`,类型使用自增主键,不得使用 `uuid`;所有时间类型字段统一使用 `timestamptz(0)`;业务数据库默认会话时区固定为 `Asia/Shanghai`,在创建或运维配置阶段显式设置并以 `SHOW TimeZone` 核查;统一状态字段 `{业务域前缀}_status`(varchar(1),默认空值,仅使用 `e` 表示启用、`d` 表示停用)。除 `created_at`、`updated_at` 和主键外,所有字段默认允许空值,`deleted_flag` 默认为空。
|
|
31
31
|
4. 建立逻辑关联和必要索引,不创建数据库外键;明确每个枚举、默认值和字段业务含义。
|
|
32
32
|
5. 为每张表编写表注释,为每个字段编写字段注释;迁移 SQL 必须包含 `COMMENT ON TABLE` 与 `COMMENT ON COLUMN`。
|
|
33
33
|
6. 新项目将工程师确认的完整 DDL 固化为 `database/migrations/V001__baseline.sql`;后续变更只新增连续编号的 `VNNN__lower_snake_name.sql`,不得修改已执行文件。
|
|
@@ -45,7 +45,7 @@ description: 将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF
|
|
|
45
45
|
- 不得每次部署重复执行完整 DDL,或以大量 `IF EXISTS` 掩盖迁移状态错误。
|
|
46
46
|
- 不得创建不带业务域前缀的业务表(除非确认为非业务表并已说明理由)。
|
|
47
47
|
- 不得将任何字段设为 NOT NULL(主键、`created_at`、`updated_at` 除外),所有字段默认允许空值。
|
|
48
|
-
-
|
|
48
|
+
- 时间类型字段必须使用 `timestamptz(0)`;不得使用 `timestamp(0)`、无精度 `timestamp` 或无精度 `timestamptz`,不得遗漏 `Asia/Shanghai` 默认会话时区声明。
|
|
49
49
|
- 不得在未记录例外材料并取得工程师明确人工确认的情况下,生成或执行不符合 3NF 的设计 DDL。
|
|
50
50
|
|
|
51
51
|
## Red Flags
|
|
@@ -53,7 +53,7 @@ description: 将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF
|
|
|
53
53
|
- 表使用 remark 字段、无表注释、无字段注释、物理外键或缺少固定审计字段。
|
|
54
54
|
- 主键使用 `uuid`、业务键替代主键、缺少自增策略或字段名不符合 `{前缀}_id` 格式。
|
|
55
55
|
- 表名缺少业务域前缀。
|
|
56
|
-
- 时间类型字段使用 `
|
|
56
|
+
- 时间类型字段使用 `timestamp(0)`、无精度 `timestamp` 或无精度 `timestamptz`,或未声明 `Asia/Shanghai` 默认会话时区。
|
|
57
57
|
- 发布迁移删除字段或改变旧字段语义。
|
|
58
58
|
- 重复存储、派生字段、部分依赖或传递依赖没有 3NF 评估,或反范式例外缺少工程师确认。
|
|
59
59
|
|
|
@@ -64,7 +64,7 @@ description: 将已确认的 PRD 与 DDL 转为原则上符合第三范式(3NF
|
|
|
64
64
|
- [ ] 首字段为 `{前缀}_id`,自增主键,未使用 uuid。
|
|
65
65
|
- [ ] `{前缀}_status` 字段类型为 varchar(1),默认空值,仅使用 `e`(启用)或 `d`(停用)。
|
|
66
66
|
- [ ] 除主键、`created_at`、`updated_at` 外,所有字段默认允许空值,`deleted_flag` 默认为 NULL。
|
|
67
|
-
- [ ] 所有时间类型字段均使用 `
|
|
67
|
+
- [ ] 所有时间类型字段均使用 `timestamptz(0)`;数据库默认会话时区为 `Asia/Shanghai`,并已通过 `SHOW TimeZone` 核查。
|
|
68
68
|
- [ ] 每张表均记录 3NF 评估结论;反范式例外已记录理由、权威来源、一致性维护策略、影响、验证方案和工程师确认结果,未确认的例外未进入 DDL 实施。
|
|
69
69
|
- [ ] 每张表的字段、类型、必填、默认值、索引、唯一约束、逻辑关联和枚举均有说明。
|
|
70
70
|
- [ ] 表尾固定字段顺序符合要求,逻辑关联无外键。
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
|
|
34
34
|
- 表名、字段名采用小写下划线,表名必须带业务域前缀(如 `pat_patient`、`enc_encounter`)。
|
|
35
35
|
- 首字段为 `{业务域前缀}_id`(如 `pat_id`),类型使用自增主键,建议采用 `bigint generated by default as identity`,不得使用 `uuid`。
|
|
36
|
-
- 所有时间类型字段统一使用 `timestamp(0)
|
|
36
|
+
- 所有时间类型字段统一使用 `timestamptz(0)`;不得使用 `timestamp(0)`、无精度 `timestamp` 或无精度 `timestamptz`。
|
|
37
|
+
- 业务数据库默认会话时区必须为 `Asia/Shanghai`。在数据库创建或运维配置阶段显式设置,并在迁移交付前执行 `SHOW TimeZone` 核查;不得在每张业务表迁移中隐式变更数据库级时区配置。
|
|
37
38
|
- 所有字段默认允许空值(nullable),`created_at` 和 `updated_at` 除外(不可为空)。
|
|
38
39
|
- `deleted_flag` 默认为空(`DEFAULT NULL`),允许空值。
|
|
39
40
|
- `created_by`、`updated_by` 允许空值,不设必填约束。
|
|
@@ -21,6 +21,7 @@ const required = [
|
|
|
21
21
|
'- 影响:',
|
|
22
22
|
'- 验证方案:',
|
|
23
23
|
'- 反范式例外确认:',
|
|
24
|
+
'数据库默认会话时区:Asia/Shanghai',
|
|
24
25
|
];
|
|
25
26
|
const missing = required.filter((text) => !content.includes(text));
|
|
26
27
|
if (missing.length) throw new Error(`Missing table design requirements: ${missing.join(', ')}`);
|
|
@@ -46,9 +47,15 @@ if (/\|\s*deleted_flag\s*\|[^|\n]*\|[^|\n]*\|\s*false\s*\|/i.test(content)) {
|
|
|
46
47
|
throw new Error('deleted_flag default must be NULL, not false');
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
//
|
|
50
|
-
if (/\
|
|
51
|
-
throw new Error('Time fields must use timestamp(0)
|
|
50
|
+
// 时间类型字段必须使用 timestamptz(0),并固定默认会话时区。
|
|
51
|
+
if (/\btimestamp\s*\(\s*0\s*\)/i.test(content) || /\btimestamp\b(?!\s*\()/i.test(content)) {
|
|
52
|
+
throw new Error('Time fields must not use timestamp(0) or timestamp without precision');
|
|
53
|
+
}
|
|
54
|
+
if (/\btimestamptz\b(?!\s*\(\s*0\s*\))/i.test(content)) {
|
|
55
|
+
throw new Error('Time fields must not use timestamptz without (0) precision');
|
|
56
|
+
}
|
|
57
|
+
if (!/\btimestamptz\s*\(\s*0\s*\)/i.test(content)) {
|
|
58
|
+
throw new Error('Time fields must use timestamptz(0)');
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
// 禁止通用 remark 字段
|
|
@@ -63,3 +63,39 @@ test('拒绝缺少反范式例外确认记录的表设计', async () => {
|
|
|
63
63
|
},
|
|
64
64
|
);
|
|
65
65
|
});
|
|
66
|
+
|
|
67
|
+
test('拒绝 timestamp(0) 时间字段', async () => {
|
|
68
|
+
await withDesign(
|
|
69
|
+
(content) => content.replaceAll('timestamptz(0)', 'timestamp(0)'),
|
|
70
|
+
async (designPath) => {
|
|
71
|
+
await assert.rejects(
|
|
72
|
+
execFile('node', [script.pathname, designPath]),
|
|
73
|
+
/must not use timestamp\(0\)/,
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('拒绝无精度 timestamptz 时间字段', async () => {
|
|
80
|
+
await withDesign(
|
|
81
|
+
(content) => content.replaceAll('timestamptz(0)', 'timestamptz'),
|
|
82
|
+
async (designPath) => {
|
|
83
|
+
await assert.rejects(
|
|
84
|
+
execFile('node', [script.pathname, designPath]),
|
|
85
|
+
/must not use timestamptz without \(0\) precision/,
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('拒绝缺少 Asia/Shanghai 默认会话时区声明的表设计', async () => {
|
|
92
|
+
await withDesign(
|
|
93
|
+
(content) => content.replace('数据库默认会话时区:Asia/Shanghai(交付前执行 `SHOW TimeZone` 核查)\n', ''),
|
|
94
|
+
async (designPath) => {
|
|
95
|
+
await assert.rejects(
|
|
96
|
+
execFile('node', [script.pathname, designPath]),
|
|
97
|
+
/数据库默认会话时区:Asia\/Shanghai/,
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
});
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- 关联表及关联键:
|
|
12
12
|
- 状态流转:
|
|
13
13
|
- 幂等键:
|
|
14
|
+
- 数据库默认会话时区:Asia/Shanghai(交付前执行 `SHOW TimeZone` 核查)
|
|
14
15
|
|
|
15
16
|
## 3NF 评估与反范式例外确认
|
|
16
17
|
|
|
@@ -32,8 +33,8 @@
|
|
|
32
33
|
| n-5 | `{前缀}_status` | varchar(1) | 否 | NULL | 业务状态,仅使用 e(启用)或 d(停用) | 业务状态 | |
|
|
33
34
|
| n-4 | created_by | varchar(64) | 否 | NULL | 创建人 | 创建人标识 | |
|
|
34
35
|
| n-3 | updated_by | varchar(64) | 否 | NULL | 更新人 | 更新人标识 | |
|
|
35
|
-
| n-2 | created_at |
|
|
36
|
-
| n-1 | updated_at |
|
|
36
|
+
| n-2 | created_at | timestamptz(0) | 是 | now() | 创建时间 | 记录创建时间 | |
|
|
37
|
+
| n-1 | updated_at | timestamptz(0) | 是 | now() | 更新时间 | 记录最后更新时间 | |
|
|
37
38
|
| n | deleted_flag | boolean | 否 | NULL | 删除标记 | 逻辑删除标记 | |
|
|
38
39
|
|
|
39
40
|
## 注释 SQL
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wdyy-safety-review
|
|
3
|
+
description: 对整个软件执行证据驱动的安全审核并生成四列 Markdown 检查清单。Use when 审查或设计身份认证与权限、Web/API、文件上传下载、敏感数据、源码与依赖、安全日志、漏洞扫描或漏洞验证;即使用户只说“安全检查”“代码安全”“接口安全”“扫描漏洞”也应使用本 Skill。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 软件安全审核规范
|
|
7
|
+
|
|
8
|
+
## 审核边界
|
|
9
|
+
|
|
10
|
+
本 Skill 审核整个软件,按六类组织:身份认证与权限、Web/API 安全、文件安全、数据与敏感信息安全、代码与依赖安全、安全日志与漏洞验证。公网接口是 Web/API 类中的检查项,不是唯一或默认主范围。
|
|
11
|
+
|
|
12
|
+
默认仅执行源码、配置、依赖清单、部署文件和已提供报告的只读审查。任何会向运行目标发送流量、改变状态或处理真实数据的扫描、漏洞验证或渗透测试,均须先由工程师明确确认:资产授权、目标、环境、允许技术、速率、时间窗、联系人、停止条件和证据处理规则。任一项缺失时停止主动测试,并在清单中标记 `未检查`。
|
|
13
|
+
|
|
14
|
+
## 输入与确认项
|
|
15
|
+
|
|
16
|
+
- 输入:代码仓库、技术栈、依赖清单与锁文件、部署配置、OpenAPI/接口文档、角色权限模型、数据分类、现有安全报告和运行证据。
|
|
17
|
+
- 确认:审核范围、环境、数据处理边界、主动测试授权,以及发现项的责任人和整改期限。
|
|
18
|
+
- 不得请求、回显或写入真实密码、Token、密钥、患者信息、身份证件或其他敏感数据;发现秘密时仅记录类型和脱敏位置。
|
|
19
|
+
|
|
20
|
+
## 执行流程
|
|
21
|
+
|
|
22
|
+
1. 盘点代码、服务、入口、角色、数据类型、文件处理链路、依赖、部署边界和已有安全控制;先列证据,再给结论。
|
|
23
|
+
2. 完整阅读 [六类审核规则](references/security-review-rules.md),按适用技术栈选择检查项;不适用必须说明技术事实。
|
|
24
|
+
3. 检查身份认证与权限:登录、密码 Hash、Token/JWT、Session、验证码、失效、RBAC、接口权限,以及水平越权、垂直越权和未授权访问。
|
|
25
|
+
4. 检查 Web/API 和文件:注入、XSS、CSRF、SSRF、路径遍历、参数篡改、重放、遍历、限流、异常泄露,及上传、下载、预览、导出的类型、大小、恶意文件、覆盖和越权。
|
|
26
|
+
5. 检查数据与供应链:最小必要、脱敏、HTTPS、敏感字段返回、日志泄露、硬编码秘密、SAST、SCA、npm/Maven 漏洞、过期组件、SBOM、Git 秘密、危险函数和不安全配置。
|
|
27
|
+
6. 检查安全日志与漏洞验证:登录失败、权限拒绝、数据修改、敏感操作的审计证据;日志实现或整改使用 `wdyy-logging-standard`。主动验证只在授权完整时执行,并保存最小化证据和复测结果。
|
|
28
|
+
7. 复制 [安全检查清单模板](templates/security-checklist.template.md),用真实证据填充后运行 `node scripts/validate-security-checklist.mjs <清单路径>`。
|
|
29
|
+
|
|
30
|
+
## 结论规则
|
|
31
|
+
|
|
32
|
+
- 结果只能为 `通过`、`不通过`、`未检查`、`不适用`。
|
|
33
|
+
- `通过`:提供充分证据,修复方案写“无需修复”。
|
|
34
|
+
- `不通过`:给出可执行的修复方案与验证方式。
|
|
35
|
+
- `未检查`:说明缺少的证据、授权或执行条件;不得写成已经通过。
|
|
36
|
+
- `不适用`:说明当前技术栈或功能范围为何不适用。
|
|
37
|
+
- 扫描器结果需要结合可达性、数据流、数据敏感度和业务影响人工复核,不能直接等同最终风险结论。
|
|
38
|
+
|
|
39
|
+
## 交付格式
|
|
40
|
+
|
|
41
|
+
在范围和限制的简短说明后,输出一个 Markdown 表格;该表格只能使用以下四列,且六类检查均至少一项:
|
|
42
|
+
|
|
43
|
+
| 检查类型 | 检查项目 | 检查结果 | 修复方案 |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
|
|
46
|
+
检查项目中写脱敏后的证据位置;检查结果中写简短判定依据。不得增加“风险等级”“责任人”等表格列。
|
|
47
|
+
|
|
48
|
+
## 验证
|
|
49
|
+
|
|
50
|
+
- [ ] 所有结论关联真实、脱敏的证据位置;未执行项目均为 `未检查`。
|
|
51
|
+
- [ ] 清单只有四列,覆盖六类检查,结果值与修复方案符合合同。
|
|
52
|
+
- [ ] 主动扫描、漏洞验证或渗透测试具有完整授权;否则没有主动流量。
|
|
53
|
+
- [ ] `node --test scripts/validate-security-checklist.test.mjs` 与清单校验器通过。
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "wdyy-safety-review"
|
|
3
|
+
short_description: "审核软件安全并生成四列整改清单"
|
|
4
|
+
default_prompt: "Use $wdyy-safety-review to review the whole software across identity and permissions, Web/API, files, sensitive data, dependencies, and security logging; produce an evidence-based four-column Markdown checklist."
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# 六类软件安全审核规则
|
|
2
|
+
|
|
3
|
+
## 证据与合规基线
|
|
4
|
+
|
|
5
|
+
法律法规和标准是审核依据而非自动合规结论。执行时从发布机关或官方标准平台核验现行状态、适用范围和版本;将法定义务、适用标准、工程建议及待确认事实分开记录。
|
|
6
|
+
|
|
7
|
+
- 《网络安全法》要求等级保护、安全管理、攻击和入侵防护、监测记录、网络日志留存不少于六个月、数据分类和加密等措施。
|
|
8
|
+
- 《个人信息保护法》要求最小必要,医疗健康等敏感个人信息需特定目的、充分必要和严格保护;处理者应采取分类管理、加密、去标识化和权限控制等措施。
|
|
9
|
+
- 《数据安全法》要求数据分类分级、全流程管理、风险监测和漏洞补救。
|
|
10
|
+
- 医疗卫生机构适用时,按《医疗卫生机构网络安全管理办法》进行年度文档核验、漏洞扫描、渗透测试和整改。
|
|
11
|
+
- 参考现行 GB/T 22239-2019、GB/T 35273-2020、GB/T 38674-2020、GB/T 40645-2021;数据接口风险监测可参考 GB/T 46796-2025。工程控制不得被误称为法律逐项强制要求。
|
|
12
|
+
|
|
13
|
+
## 1. 身份认证与权限
|
|
14
|
+
|
|
15
|
+
检查登录入口、账号枚举提示、失败次数和验证码抗绕过、密码复杂度与强 Hash、MFA 的适用性、JWT 算法/签名/受众/有效期、Session 固定与失效、Token 撤销、RBAC 和服务端接口授权。
|
|
16
|
+
|
|
17
|
+
对每个受保护操作检查匿名、低权限、高权限和不同数据主体的行为,重点验证:
|
|
18
|
+
|
|
19
|
+
- 水平越权:变更对象 ID、文件 ID 或查询条件不能访问其他主体数据。
|
|
20
|
+
- 垂直越权:低权限角色不能调用管理功能、权限变更、导出或运维接口。
|
|
21
|
+
- 未授权访问:认证、接口、对象、字段和数据范围均由服务端逐层判定。
|
|
22
|
+
|
|
23
|
+
## 2. Web/API 安全
|
|
24
|
+
|
|
25
|
+
检查输入类型、长度、格式、范围、内容类型与请求体大小;数据库使用参数化查询,命令调用使用固定允许列表;输出按上下文编码;浏览器请求有适当 CSRF 与 CORS 边界;服务端出网访问使用地址、协议、DNS 重解析和私网地址限制。
|
|
26
|
+
|
|
27
|
+
检查路径规范化、重定向、错误响应和调试开关。对外接口检查强鉴权、接口/对象/字段授权、请求签名和重放防护、分页上限、IP/账号/调用方凭据维度限流、配额、并发控制、异常检测和告警。签名 profile 必须规范化并覆盖语义相关组件,避免直接字符串拼接歧义;nonce 一次性消费、时间窗、密钥轮换和失败关闭需要服务端证据。
|
|
28
|
+
|
|
29
|
+
## 3. 文件安全
|
|
30
|
+
|
|
31
|
+
检查上传、下载、预览和导出全链路:服务端限制类型、大小、数量和内容特征;随机化存储名;隔离可执行目录;拒绝路径分隔符与保留名称;防止覆盖;预览与下载使用对象授权;响应头防止浏览器错误解释内容。
|
|
32
|
+
|
|
33
|
+
不要把扩展名或前端校验作为唯一控制。发现恶意文件、压缩包、Office/PDF、图片解析或外部预览器时,记录所用解析组件和隔离策略。
|
|
34
|
+
|
|
35
|
+
## 4. 数据与敏感信息安全
|
|
36
|
+
|
|
37
|
+
建立字段清单,标记身份证、手机号、患者信息、认证数据和业务敏感数据。检查采集目的、最小必要、字段允许列表、传输 HTTPS、存储加密或去标识化、密码强 Hash、访问控制、导出控制、缓存与留存。
|
|
38
|
+
|
|
39
|
+
使用只读方式检查代码、模板、受 Git 跟踪配置和历史提交中的硬编码密码、API Key、Token、私钥、数据库连接秘密。发现时不得输出原文;要求撤销轮换、从历史清理并迁移至经确认的秘密管理方式。
|
|
40
|
+
|
|
41
|
+
## 5. 代码与依赖安全
|
|
42
|
+
|
|
43
|
+
先识别项目已有的 SAST、SCA、npm/Maven 审计、SBOM、Git 秘密扫描、容器或 IaC 扫描能力。新增工具前核验官方最新稳定版、兼容性和公开漏洞状态,并等待工程师确认。
|
|
44
|
+
|
|
45
|
+
检查锁文件、直接与传递依赖、EOL 或过期组件、许可证和 SBOM;检查危险函数、反序列化、动态执行、不安全随机数、调试模式、默认账户、宽松 CORS、明文协议和环境变量泄露。扫描结果须关联实际版本、影响路径、可达性和修复版本。
|
|
46
|
+
|
|
47
|
+
## 6. 安全日志与漏洞验证
|
|
48
|
+
|
|
49
|
+
检查登录失败、认证失败、权限拒绝、数据修改、敏感查询/导出、配置与权限变更是否记录时间、主体内部标识、来源、动作、对象、结果和 trace 标识。日志中不得保留密码、Token、签名、密钥、Cookie、身份证、患者信息或完整敏感请求体。
|
|
50
|
+
|
|
51
|
+
日志实现使用 `wdyy-logging-standard`。漏洞扫描、DAST、模糊测试和渗透测试前,确认资产所有权或授权、目标与排除项、环境、允许技术、速率、时窗、联系人、停止条件和证据保存规则。授权不完整时仅复核已有报告或执行本地静态检查,清单标记 `未检查`。
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
|
|
4
|
+
const [inputPath] = process.argv.slice(2);
|
|
5
|
+
if (!inputPath || process.argv.length !== 3) {
|
|
6
|
+
throw new Error('Usage: validate-security-checklist.mjs <checklist.md>');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const requiredHeaders = ['检查类型', '检查项目', '检查结果', '修复方案'];
|
|
10
|
+
const requiredCategories = new Set([
|
|
11
|
+
'身份认证与权限',
|
|
12
|
+
'Web/API 安全',
|
|
13
|
+
'文件安全',
|
|
14
|
+
'数据与敏感信息安全',
|
|
15
|
+
'代码与依赖安全',
|
|
16
|
+
'安全日志与漏洞验证',
|
|
17
|
+
]);
|
|
18
|
+
const validResults = new Set(['通过', '不通过', '未检查', '不适用']);
|
|
19
|
+
|
|
20
|
+
function parseRow(line) {
|
|
21
|
+
const cells = line.trim().split('|').slice(1, -1).map((cell) => cell.trim());
|
|
22
|
+
if (cells.length !== 4) throw new Error(`Checklist row must have exactly four columns: ${line}`);
|
|
23
|
+
return cells;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isDelimiter(cells) {
|
|
27
|
+
return cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const source = await readFile(inputPath, 'utf8');
|
|
31
|
+
const lines = source.split(/\r?\n/);
|
|
32
|
+
const headerIndex = lines.findIndex((line) => line.trim() === `| ${requiredHeaders.join(' | ')} |`);
|
|
33
|
+
if (headerIndex === -1) throw new Error('Checklist table must use the required four headers');
|
|
34
|
+
if (headerIndex + 1 >= lines.length || !isDelimiter(parseRow(lines[headerIndex + 1]))) {
|
|
35
|
+
throw new Error('Checklist table is missing a valid delimiter row');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const categories = new Set();
|
|
39
|
+
let rowCount = 0;
|
|
40
|
+
for (const line of lines.slice(headerIndex + 2)) {
|
|
41
|
+
if (line.trim() === '') break;
|
|
42
|
+
if (!line.trim().startsWith('|')) break;
|
|
43
|
+
const [category, item, result, remediation] = parseRow(line);
|
|
44
|
+
if (!requiredCategories.has(category)) throw new Error(`Unexpected checklist category: ${category}`);
|
|
45
|
+
if (categories.has(category)) throw new Error(`Checklist category is duplicated: ${category}`);
|
|
46
|
+
if (!item || !remediation) throw new Error(`Checklist row is missing item or remediation: ${category}`);
|
|
47
|
+
if (!validResults.has(result)) throw new Error(`Invalid checklist result: ${result}`);
|
|
48
|
+
if (result === '通过' && !/无需修复|无须修复/.test(remediation)) {
|
|
49
|
+
throw new Error(`Passed checklist row must state no remediation is needed: ${category}`);
|
|
50
|
+
}
|
|
51
|
+
if (result !== '通过' && /无需修复|无须修复|已通过/.test(remediation)) {
|
|
52
|
+
throw new Error(`Non-passed checklist row cannot claim completion: ${category}`);
|
|
53
|
+
}
|
|
54
|
+
if (result === '未检查' && !/补充|授权|执行条件|待确认/.test(remediation)) {
|
|
55
|
+
throw new Error(`Unchecked checklist row must state missing evidence or authorization: ${category}`);
|
|
56
|
+
}
|
|
57
|
+
categories.add(category);
|
|
58
|
+
rowCount += 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (rowCount !== requiredCategories.size || categories.size !== requiredCategories.size) {
|
|
62
|
+
const missing = [...requiredCategories].filter((category) => !categories.has(category));
|
|
63
|
+
throw new Error(`Checklist must cover all six categories; missing: ${missing.join('、')}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
process.stdout.write('valid software security checklist\n');
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
|
|
8
|
+
const validator = new URL('./validate-security-checklist.mjs', import.meta.url);
|
|
9
|
+
const temporaryDirectories = [];
|
|
10
|
+
|
|
11
|
+
test.afterEach(async () => {
|
|
12
|
+
await Promise.all(temporaryDirectories.splice(0).map((directory) => rm(directory, { force: true, recursive: true })));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const validChecklist = `# 软件安全检查清单
|
|
16
|
+
|
|
17
|
+
| 检查类型 | 检查项目 | 检查结果 | 修复方案 |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| 身份认证与权限 | 证据:auth/service.ts | 通过 | 无需修复。 |
|
|
20
|
+
| Web/API 安全 | 证据:api/routes.ts | 不通过 | 使用参数化查询并添加回归测试。 |
|
|
21
|
+
| 文件安全 | 证据:upload.ts | 未检查 | 补充经授权的文件上传验证证据。 |
|
|
22
|
+
| 数据与敏感信息安全 | 证据:dto.ts | 不适用 | 当前范围不处理个人数据,保留证据备查。 |
|
|
23
|
+
| 代码与依赖安全 | 证据:package-lock.json | 通过 | 无需修复。 |
|
|
24
|
+
| 安全日志与漏洞验证 | 证据:audit.ts | 未检查 | 补充主动验证授权和脱敏日志证据。 |
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
async function run(source) {
|
|
28
|
+
const directory = await mkdtemp(join(tmpdir(), 'wdyy-safety-checklist-'));
|
|
29
|
+
temporaryDirectories.push(directory);
|
|
30
|
+
const inputPath = join(directory, 'checklist.md');
|
|
31
|
+
await writeFile(inputPath, source, 'utf8');
|
|
32
|
+
return spawnSync(process.execPath, [validator.pathname, inputPath], { encoding: 'utf8' });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test('accepts the required four-column checklist with all six categories', async () => {
|
|
36
|
+
const result = await run(validChecklist);
|
|
37
|
+
assert.equal(result.status, 0, result.stderr);
|
|
38
|
+
assert.match(result.stdout, /valid software security checklist/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('rejects a checklist that omits a required category', async () => {
|
|
42
|
+
const result = await run(validChecklist.replace('| 文件安全 | 证据:upload.ts | 未检查 | 补充经授权的文件上传验证证据。 |\n', ''));
|
|
43
|
+
assert.notEqual(result.status, 0);
|
|
44
|
+
assert.match(result.stderr, /must cover all six categories/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('rejects an extra table column', async () => {
|
|
48
|
+
const result = await run(validChecklist.replace('检查结果 | 修复方案', '检查结果 | 修复方案 | 风险等级'));
|
|
49
|
+
assert.notEqual(result.status, 0);
|
|
50
|
+
assert.match(result.stderr, /required four headers/);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('rejects an invalid result value', async () => {
|
|
54
|
+
const result = await run(validChecklist.replace('| Web/API 安全 | 证据:api/routes.ts | 不通过 |', '| Web/API 安全 | 证据:api/routes.ts | 高风险 |'));
|
|
55
|
+
assert.notEqual(result.status, 0);
|
|
56
|
+
assert.match(result.stderr, /Invalid checklist result/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('rejects a non-passed row that claims it is already passed', async () => {
|
|
60
|
+
const result = await run(validChecklist.replace('补充经授权的文件上传验证证据。', '已通过,无需修复。'));
|
|
61
|
+
assert.notEqual(result.status, 0);
|
|
62
|
+
assert.match(result.stderr, /cannot claim completion/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('rejects an unchecked row with no evidence or authorization action', async () => {
|
|
66
|
+
const result = await run(validChecklist.replace('补充主动验证授权和脱敏日志证据。', '后续处理。'));
|
|
67
|
+
assert.notEqual(result.status, 0);
|
|
68
|
+
assert.match(result.stderr, /must state missing evidence or authorization/);
|
|
69
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 软件安全检查清单
|
|
2
|
+
|
|
3
|
+
## 范围与限制
|
|
4
|
+
|
|
5
|
+
- 审核范围:待填写
|
|
6
|
+
- 审核时间:待填写
|
|
7
|
+
- 证据来源:待填写;不得包含秘密或敏感个人信息原文。
|
|
8
|
+
- 主动测试授权:待确认;未确认时不得执行主动扫描、漏洞验证或渗透测试。
|
|
9
|
+
|
|
10
|
+
| 检查类型 | 检查项目 | 检查结果 | 修复方案 |
|
|
11
|
+
| --- | --- | --- | --- |
|
|
12
|
+
| 身份认证与权限 | 登录、密码 Hash、Token/Session、RBAC、接口/对象/字段授权;证据:待填写 | 未检查 | 补充源码、配置和角色测试证据;必要时取得主动验证授权。 |
|
|
13
|
+
| Web/API 安全 | 注入、XSS、CSRF、SSRF、路径遍历、重放、遍历、限流、异常泄露;证据:待填写 | 未检查 | 补充代码审查和经授权的验证证据。 |
|
|
14
|
+
| 文件安全 | 上传、下载、预览、导出、类型大小、存储隔离、覆盖和越权;证据:待填写 | 未检查 | 补充文件处理链路与对象授权证据。 |
|
|
15
|
+
| 数据与敏感信息安全 | 敏感字段、脱敏、HTTPS、密码 Hash、日志与硬编码秘密;证据:待填写 | 未检查 | 补充字段清单、传输存储和秘密扫描证据。 |
|
|
16
|
+
| 代码与依赖安全 | SAST、SCA、npm/Maven、SBOM、Git 秘密、危险函数和配置;证据:待填写 | 未检查 | 补充依赖清单和经确认工具的扫描结果。 |
|
|
17
|
+
| 安全日志与漏洞验证 | 登录失败、权限拒绝、数据修改、敏感操作日志及漏洞验证授权;证据:待填写 | 未检查 | 补充脱敏审计日志和完整授权边界。 |
|
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
```text
|
|
8
8
|
AGENTS.md 全局基线、技术栈、OpenSpec 流程与 Skill 路由
|
|
9
9
|
OpenSpec 单项变更的需求、设计、任务、实施与归档
|
|
10
|
-
.well-known/skills/wdyy-* 数据库、日志、部署、医院 UI
|
|
10
|
+
.well-known/skills/wdyy-* 数据库、日志、部署、医院 UI、软件安全审核的专业规则
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
当前 `.well-known/skills/index.json` 登记
|
|
13
|
+
当前 `.well-known/skills/index.json` 登记 6 个企业 Skill。
|
|
14
14
|
|
|
15
15
|
| Skill | 职责 |
|
|
16
16
|
|---|---|
|
|
@@ -19,6 +19,7 @@ OpenSpec 单项变更的需求、设计、任务、实
|
|
|
19
19
|
| `wdyy-logging-standard` | 访问、应用、安全与审计日志,递归敏感字段净化、W3C trace、前端异常上报、轮转与哈希链校验。 |
|
|
20
20
|
| `wdyy-deployment-standard` | 在 Linux、macOS Docker Desktop 或 Windows Docker Desktop + WSL2/Linux 容器模式中,使用本地基础镜像构建前后端独立镜像,分别导出并统一压缩交付,远程数据库直连并一键部署整个项目栈。 |
|
|
21
21
|
| `wdyy-ui` | 统一医院信息系统页面的布局、清新生命绿配色、医院 Logo、常用组件和响应式行为,不定义业务流程、接口或数据模型。 |
|
|
22
|
+
| `wdyy-safety-review` | 审核身份认证与权限、Web/API、文件、敏感数据、代码与依赖、安全日志与漏洞验证,并输出四列 Markdown 安全检查清单。 |
|
|
22
23
|
|
|
23
24
|
## 使用方式
|
|
24
25
|
|
|
@@ -35,9 +36,9 @@ wdyy init
|
|
|
35
36
|
|
|
36
37
|
`wdyy init` 启动时会再次显示上述外部依赖安装命令,但不会自动执行 npm 或 npx。命令在写入前集中检查 Node.js 24、pnpm 12.1.0、Git、OpenSpec、CodeGraph,以及规则强制使用的公共 Skill;任一项缺失或版本不符时一次性报告全部问题并退出。
|
|
37
38
|
|
|
38
|
-
预检通过后,`wdyy init` 会在系统用户目录 `.agents/skills/` 安装或更新
|
|
39
|
+
预检通过后,`wdyy init` 会在系统用户目录 `.agents/skills/` 安装或更新 6 个受管理 Skill,并删除其中旧的 `wdyy-api-standard`。它还会初始化 Git、默认目录、环境模板、BUG 模板、OpenSpec 与 CodeGraph。已有环境文件和 BUG 记录保持不变;已有 `AGENTS.md` 时生成 `AGENTS_new.md`。全部步骤成功后生成 `.wdyy/toolchain.json`,记录初始化器与工具版本、实际规则文件以及公共和企业 Skill 的 SHA-256;清单不含用户名、凭据或绝对主目录。
|
|
39
40
|
|
|
40
|
-
生成规则负责项目边界与 Skill 路由:`grill-me` 澄清需求和验收标准,`grill-with-docs` 记录 PRD、术语与 ADR;文档沉淀完成后先使用 `openspec-explore` 探索方案、约束与风险,再使用 `openspec-propose`
|
|
41
|
+
生成规则负责项目边界与 Skill 路由:`grill-me` 澄清需求和验收标准,`grill-with-docs` 记录 PRD、术语与 ADR;文档沉淀完成后先使用 `openspec-explore` 探索方案、约束与风险,再使用 `openspec-propose` 形成可评审计划并进入实施流程。数据库、日志、部署、UI 和软件安全审核的执行细节由对应 Skill 唯一维护。自建接口由 Agent 根据已确认需求生成 OpenAPI 3.1 YAML 并交工程师验收;第三方接口只依据工程师提供的契约或官方文档。
|
|
41
42
|
|
|
42
43
|
初始化后在项目根目录执行以下只读验收;命令集中检查初始化产物、工具版本、Skill 和工具链清单漂移,不会自动修复文件:
|
|
43
44
|
|
|
@@ -55,14 +56,15 @@ wdyy validate
|
|
|
55
56
|
npm install -g @wdyy/skills@latest
|
|
56
57
|
wdyy update-skill wdyy-logging-standard
|
|
57
58
|
wdyy update-skill wdyy-ui
|
|
59
|
+
wdyy update-skill wdyy-safety-review
|
|
58
60
|
wdyy update-skill --all
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
`wdyy update-skill <skill-name>` 只接受
|
|
63
|
+
`wdyy update-skill <skill-name>` 只接受 6 个受管理的 `wdyy-*` Skill,且仅替换系统用户目录 `.agents/skills/` 中的指定目录;`--all` 替换全部 6 个受管理 Skill并删除旧的 `wdyy-api-standard`。两种方式都不会写入调用项目的 `AGENTS.md`、`AGENTS_new.md`、`docs/bug_record.md` 或 `.agents/skills/`,也不会执行 `openspec init`。首次初始化或需要重新生成项目规则时,使用 `wdyy init`。
|
|
62
64
|
|
|
63
65
|
部署 Skill 生成的标准流程为:在 Linux、macOS Docker Desktop,或 Windows Docker Desktop + WSL2/Linux 文件系统的项目根目录执行 `./deploy.sh build`,只使用本地已有的 Node、Nginx 基础镜像构建 `<项目名>_frontend:latest` 与 `<项目名>_backend:latest`,分别导出两个镜像 tar,并统一封装为 `deploy/<项目名>-docker.tar.gz`。工程师人工复制并解压该压缩包,在受支持的 Linux engine 上无参数执行 `./deploy.sh` 即校验、加载并整体部署前后端;macOS/arm64 Docker Desktop 可通过禁止拉取的实际目标镜像探针验证并运行 `linux/amd64` 包,而不是仅按 Server 原生架构拒绝。另可执行 `./deploy.sh stop` 和 `./deploy.sh status`。数据库通过 `.env` 直连远程 PostgreSQL,不生成数据库容器;前后端的宿主机与容器端口都来自 `.env`,容器名与镜像 repository 相同,使用 `unless-stopped`,并验证 24 小时制 CST(UTC+8);日志直接保存在部署根 `logs/`。不支持原生 PowerShell 或 Windows 容器模式。
|
|
64
66
|
|
|
65
|
-
中大型变更先使用 `/opsx:explore` 或 `/opsx:propose <change-name>`,在方案确认后使用 `/opsx:apply`;完成验证后使用 `/opsx:archive
|
|
67
|
+
中大型变更先使用 `/opsx:explore` 或 `/opsx:propose <change-name>`,在方案确认后使用 `/opsx:apply`;完成验证后使用 `/opsx:archive`。涉及数据库、日志、部署、医院信息系统 UI 或软件安全审核时,按 `AGENTS.md` 的路由加载相应企业 Skill。自建接口由 Agent 生成 OpenAPI 3.1 YAML 并交工程师验收,第三方接口依据工程师提供的契约或官方文档。所有 URL、端口和 IP 地址从 `.env` 读取,不得硬编码;不得提交秘密或生产环境 `.env`。项目完成后创建 `README.md`,说明技术栈、目录结构和安装部署。
|
|
66
68
|
|
|
67
69
|
## 固定技术栈
|
|
68
70
|
|
package/lib/wdyy-cli.js
CHANGED
|
@@ -11,7 +11,8 @@ const managedSkillNames = [
|
|
|
11
11
|
'wdyy-bug-record',
|
|
12
12
|
'wdyy-logging-standard',
|
|
13
13
|
'wdyy-deployment-standard',
|
|
14
|
-
'wdyy-ui'
|
|
14
|
+
'wdyy-ui',
|
|
15
|
+
'wdyy-safety-review'
|
|
15
16
|
];
|
|
16
17
|
|
|
17
18
|
const removedManagedSkillNames = ['wdyy-api-standard'];
|
|
@@ -115,6 +116,7 @@ const generatedAgentsContent = `# 项目开发规则
|
|
|
115
116
|
| 架构图、时序图、流程图 | \`archify\`,产出放入 \`docs/\` |
|
|
116
117
|
| PostgreSQL 表、DDL、迁移、数据兼容性 | \`wdyy-database-standard\` |
|
|
117
118
|
| 访问、应用、安全与审计日志、W3C trace、净化后的完整业务参数、前端异常上报 | \`wdyy-logging-standard\` |
|
|
119
|
+
| 身份认证与权限、Web/API、文件、敏感数据、代码与依赖、安全日志与漏洞验证的安全审核 | \`wdyy-safety-review\`;主动扫描、漏洞验证或渗透测试须先确认完整授权边界 |
|
|
118
120
|
| 统一 Docker 交付、Linux/Mac/Windows WSL2 双镜像、远程数据库与整栈一键部署 | \`wdyy-deployment-standard\` |
|
|
119
121
|
| 汇总当前任务已解决的 BUG、漏洞或运行异常 | 手动调用 \`wdyy-bug-record\`,追加写入 \`docs/bug_record.md\` |
|
|
120
122
|
|