@wdyy/skills 0.1.27 → 0.1.30
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 +8 -3
- 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-logging-standard/SKILL.md +16 -13
- package/.well-known/skills/wdyy-logging-standard/agents/openai.yaml +1 -1
- package/.well-known/skills/wdyy-logging-standard/reference/logging-rules.md +17 -8
- package/.well-known/skills/wdyy-logging-standard/scripts/validate-log-entry.mjs +23 -4
- package/.well-known/skills/wdyy-logging-standard/scripts/validate-log-entry.test.mjs +200 -303
- package/.well-known/skills/wdyy-logging-standard/templates/logger.template.ts +167 -89
- package/.well-known/skills/wdyy-logging-standard/templates/nestjs-http-logging.middleware.template.ts +68 -46
- package/.well-known/skills/wdyy-logging-standard/templates/pino-logger.template.ts +38 -0
- package/.well-known/skills/wdyy-logging-standard/templates/security-audit-logger.template.ts +9 -14
- package/.well-known/skills/wdyy-safety-review/SKILL.md +54 -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
|
{
|
|
@@ -17,13 +17,18 @@
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"name": "wdyy-logging-standard",
|
|
20
|
-
"description": "为 NestJS 和 Vue
|
|
21
|
-
"files": ["SKILL.md", "agents/openai.yaml", "reference/logging-rules.md", "scripts/validate-log-entry.mjs", "scripts/validate-log-entry.test.mjs", "templates/frontend-error-report.template.ts", "templates/logger.template.ts", "templates/nestjs-http-logging.middleware.template.ts", "templates/security-audit-logger.template.ts"]
|
|
20
|
+
"description": "为 NestJS 和 Vue 项目基于 Pino 生态实现固定写入项目根目录 ./logs 的请求、响应、操作审计与安全 JSON 日志,通过 nestjs-pino 与 pino-http 记录 HTTP 请求/响应,使用 pino-roll 2MB 轮转,并提供 W3C trace、敏感字段净化、前端异常上报与哈希链校验。Use when 编写或审查日志、错误处理、请求追踪、安全访问记录、安全事件、审计溯源及生产可观测性时。",
|
|
21
|
+
"files": ["SKILL.md", "agents/openai.yaml", "reference/logging-rules.md", "scripts/validate-log-entry.mjs", "scripts/validate-log-entry.test.mjs", "templates/frontend-error-report.template.ts", "templates/logger.template.ts", "templates/pino-logger.template.ts", "templates/nestjs-http-logging.middleware.template.ts", "templates/security-audit-logger.template.ts"]
|
|
22
22
|
},
|
|
23
23
|
{
|
|
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
|
|
@@ -1,39 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wdyy-logging-standard
|
|
3
|
-
description: 为 NestJS 和 Vue
|
|
3
|
+
description: 为 NestJS 和 Vue 项目基于 Pino 生态实现固定写入项目根目录 ./logs 的请求、响应、操作审计与安全 JSON 日志,通过 nestjs-pino 与 pino-http 记录 HTTP 请求/响应,使用 pino-roll 2MB 轮转,并提供 W3C trace、敏感字段净化、前端异常上报与哈希链校验。Use when 编写或审查日志、错误处理、请求追踪、安全访问记录、安全事件、审计溯源及生产可观测性时。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# 企业安全日志与溯源规范(Pino)
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
建立可追踪、可审计、可验证完整性的结构化日志。净化秘密和敏感个人信息后,保留其余完整业务参数结构。
|
|
10
|
+
使用成熟 Node.js 日志框架 Pino 建立可追踪、可审计、可验证完整性的结构化日志。净化秘密和敏感个人信息后,保留其余完整业务参数结构。
|
|
11
11
|
|
|
12
12
|
## 输入与确认项
|
|
13
13
|
|
|
14
14
|
- 输入:服务名、实例标识、环境、HTTP 与业务上下文,以及经工程师确认的业务功能目录;应用从项目根目录启动。
|
|
15
|
+
- 依赖(安装前须核实最新稳定版):`pino`、`pino-http`、`nestjs-pino`、`pino-roll`;已核实版本为 `pino@10.3.1`、`pino-http@11.0.0`、`nestjs-pino@5.2.0`、`pino-roll@4.0.0`。
|
|
15
16
|
- 实施前由工程师确认:项目特有敏感字段、可信代理链、业务功能编码与名称、动作、审计主体映射、留存责任人,以及外部不可变副本的平台与接入参数。
|
|
16
17
|
- 未确认外部平台、地址、凭据时保持 `待确认`,不得虚构已接入、已备份或合规达标。
|
|
17
18
|
|
|
18
19
|
## 执行步骤
|
|
19
20
|
|
|
20
|
-
1. 阅读 [详细日志规则](reference/logging-rules.md)
|
|
21
|
-
2.
|
|
22
|
-
3. 建立显式业务功能目录,再将 [
|
|
21
|
+
1. 阅读 [详细日志规则](reference/logging-rules.md),确定 `request`、`response`、`security`、`audit` 四类日志及事件清单。
|
|
22
|
+
2. 使用 [Pino logger 模板](templates/pino-logger.template.ts) 创建统一日志流:先以 `0700` 预创建 `./logs`,再用 `pino.transport` 接入 `pino-roll`,并在写入前经过哈希链 Transform;业务代码不得直接使用 `console.log`。
|
|
23
|
+
3. 建立显式业务功能目录,再将 [nestjs-pino 集成模板](templates/nestjs-http-logging.middleware.template.ts) 接入入口;`pino-http` 的 `req`/`res` 日志分别映射为 `request`/`response`,每个 `method + route` 必须映射稳定的功能编码、名称和动作。
|
|
23
24
|
4. 将 [安全审计模板](templates/security-audit-logger.template.ts) 接入认证、授权、权限变更、敏感数据导出和配置变更路径。
|
|
24
|
-
5. 将 [前端异常模板](templates/frontend-error-report.template.ts) 接入 Axios、未处理异常和 Promise
|
|
25
|
+
5. 将 [前端异常模板](templates/frontend-error-report.template.ts) 接入 Axios、未处理异常和 Promise 拒绝;前端不使用 Pino。
|
|
25
26
|
6. 写盘或上报前统一递归净化;必须删除凭据、令牌、Cookie、连接串、身份证件、银行卡、病历与健康数据字段,项目确认的附加字段一并删除。
|
|
26
27
|
7. 进入本 Skill 目录,执行 `node --test scripts/validate-log-entry.test.mjs`,再运行目标项目的完整测试。
|
|
27
28
|
|
|
28
29
|
## 核心合同
|
|
29
30
|
|
|
30
|
-
- `logType` 仅为 `
|
|
31
|
+
- `logType` 仅为 `request`、`response`、`security`、`audit`;`access` 与 `application` 已废弃并会被校验器拒绝。
|
|
31
32
|
- 通用字段包含 `timestamp`、`timestampEpochMs`、`timezone: +08:00`、`level`、`service`、`instanceId`、`env`、`logType`、`message`。
|
|
32
|
-
-
|
|
33
|
+
- `request` 日志还必须包含 `traceId`、`spanId`、`method`、`route`、`sourceIp`、`actorId`、`actorType`、`functionCode`、`functionName`、`action` 及净化后的 `query`、`body`。
|
|
34
|
+
- `response` 日志还必须包含同一 `traceId`、`method`、`route`、`statusCode`、`result`、`durationMs`;100–399 映射 `success`,400–599 映射 `failure`。
|
|
33
35
|
- 未认证请求使用 `actorId: anonymous` 与 `actorType: anonymous`;已认证主体仅记录不含直接身份信息的内部标识。
|
|
34
36
|
- 安全与审计日志还必须包含事件、主体、动作、对象和结果;失败事件必须包含原因。请求触发时还必须包含同一 `traceId`、有效 `sourceIp`、`route`、`functionCode` 和 `functionName`。
|
|
35
37
|
- 使用 W3C `traceparent`,兼容 `x-trace-id`;无效或全零上游值必须替换,不能阻断请求。
|
|
36
|
-
- 仅写项目根目录 `./logs`,目录权限 `0700
|
|
38
|
+
- 仅写项目根目录 `./logs`,目录权限 `0700` 且必须预先创建,文件权限 `0600`;单文件 2MB 轮转,文件名遵循 `filename.date.count.log`,禁止自动删除、移动或覆盖历史日志;每条落盘记录包含 `chainId`、`sequence`、`previousHash`、`entryHash`。
|
|
37
39
|
|
|
38
40
|
## 禁止事项
|
|
39
41
|
|
|
@@ -41,12 +43,13 @@ description: 为 NestJS 和 Vue 项目实现固定写入项目根目录 ./logs
|
|
|
41
43
|
- 不得记录带查询串的原始 URL,不得按服务、日期或类型创建日志子目录。
|
|
42
44
|
- 不得用 `unknown`、原始 URL、控制器名或猜测值代替有效来源 IP 及显式业务功能映射。
|
|
43
45
|
- 不得以裁剪全部请求体代替字段级净化,也不得以“完整原始入参”为由绕过净化。
|
|
44
|
-
-
|
|
46
|
+
- 日志初始化或写入失败必须显式失败,不得降级到 console 或静默吞错。
|
|
45
47
|
|
|
46
48
|
## Verification
|
|
47
49
|
|
|
48
|
-
- [ ]
|
|
50
|
+
- [ ] 四类日志必填字段、成功失败映射与失败原因均有行为测试;旧类别、未知功能、无效 IP 和不完整主体会显式失败。
|
|
49
51
|
- [ ] 净化后保留非敏感业务结构,校验器拒绝任何绕过净化的禁止字段。
|
|
50
52
|
- [ ] trace 可跨前后端和内部调用延续,无效输入被安全替换。
|
|
51
|
-
- [ ]
|
|
53
|
+
- [ ] 请求与响应日志共享同一 trace 与业务功能上下文;哈希链篡改检测通过。
|
|
54
|
+
- [ ] `pino-roll` 存储选项固定 2MB、`0600`、仅追加且禁止删除历史日志;`./logs` 以 `0700` 预创建。
|
|
52
55
|
- [ ] 网络安全相关日志至少留存六个月;备份、访问审计、容量告警和外部不可变副本有经确认的生产证据。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "wdyy-logging-standard"
|
|
3
3
|
short_description: "Record IP, time, business function, trace, and audit logs"
|
|
4
|
-
default_prompt: "Use $wdyy-logging-standard to implement
|
|
4
|
+
default_prompt: "Use $wdyy-logging-standard to implement Pino-based request, response, security, and audit logs with explicit business-function mapping, actor and source-IP validation, trace propagation, 2MB pino-roll rotation, and behavioral verification."
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 日志详细规则(Pino)
|
|
2
|
+
|
|
3
|
+
## 框架与依赖
|
|
4
|
+
|
|
5
|
+
- 后端统一使用 `nestjs-pino` 接入 Pino:`pino-http` 的 `req` 日志映射为 `request`,`res` 日志映射为 `response`,二者由同一条 HTTP 日志链路产生并共享 trace 与业务功能上下文。
|
|
6
|
+
- 落盘与轮转使用 `pino-roll`:通过 `pino.transport` 创建,单文件达到 2MB 轮转,文件名遵循 `filename.date.count.log`。
|
|
7
|
+
- 安装前必须从官方渠道核实最新稳定版本;若版本不兼容或存在未修复高危漏洞,停止安装并报告,不得擅自降级或添加豁免。
|
|
8
|
+
- 前端 Vue 不使用 Pino,继续使用本 Skill 的前端异常上报模板。
|
|
2
9
|
|
|
3
10
|
## 分类与字段
|
|
4
11
|
|
|
@@ -6,11 +13,13 @@
|
|
|
6
13
|
|
|
7
14
|
| 类型 | 用途 | 关键字段 |
|
|
8
15
|
|---|---|---|
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
16
|
+
| `request` | HTTP 请求安全访问记录 | traceId、spanId、method、route、sourceIp、actorId、actorType、functionCode、functionName、action、query、body |
|
|
17
|
+
| `response` | HTTP 响应与性能追踪 | traceId、method、route、statusCode、result、durationMs、response |
|
|
11
18
|
| `security` | 认证、授权、攻击与策略事件 | eventId、eventType、actor、action、target、result、reason(失败时) |
|
|
12
19
|
| `audit` | 管理操作和关键数据变更溯源 | eventId、eventType、actor、action、target、before、after、result、reason(失败时) |
|
|
13
20
|
|
|
21
|
+
`access` 与 `application` 已废弃:业务/集成/HTTP 异常归入 `response` 并携带 `errorCode` 与净化上下文;非 HTTP 关键事件按语义归入 `security` 或 `audit`。
|
|
22
|
+
|
|
14
23
|
安全事件至少覆盖:登录成功/失败、访问拒绝、权限或角色变更、账户锁定/解锁、敏感数据查询或导出、日志访问、审计配置变更和完整性校验失败。审计事件至少覆盖关键业务新增、修改、删除、状态流转及管理配置变更。
|
|
15
24
|
|
|
16
25
|
## 安全访问记录
|
|
@@ -19,7 +28,7 @@
|
|
|
19
28
|
- 功能信息必须来自工程师维护的显式目录,以 `method + route` 精确映射;不得从原始 URL、控制器名或日志组件推断。未登记、重复或格式错误的映射必须显式失败。
|
|
20
29
|
- `actorId` 只记录内部不透明标识,`actorType` 记录主体类别。未认证请求固定记录 `anonymous/anonymous`,不得使用姓名、身份证号、工号等直接身份信息,也不得虚构用户。
|
|
21
30
|
- `sourceIp` 必须是有效 IPv4 或 IPv6 地址。Express `request.ip` 只有在可信代理链已由工程师确认并正确配置时才可采用;不得直接信任任意 `X-Forwarded-For`,也不得写入 `unknown`。
|
|
22
|
-
- 请求触发的 `security`、`audit`
|
|
31
|
+
- 请求触发的 `security`、`audit` 事件必须沿用请求日志的 trace、IP、路由和功能上下文;后台事件不得伪造请求字段。
|
|
23
32
|
|
|
24
33
|
## 数据安全
|
|
25
34
|
|
|
@@ -27,7 +36,7 @@
|
|
|
27
36
|
- 固定禁止密码、认证头、Cookie、会话标识、各类 token、API/客户端密钥、私钥、数据库连接信息、身份证件、银行卡、病历号、诊断和健康数据字段;字段名匹配忽略大小写及分隔符。
|
|
28
37
|
- 由工程师补充项目特有敏感字段。除被删除字段外,嵌套对象、数组与其他业务参数必须保留完整结构;不得静默截断或只保留白名单摘要。
|
|
29
38
|
- `route` 只记录框架路由模板,如 `/patients/:patientId`;不得记录原始 URL、查询串或片段。客户端 IP 仅在可信代理配置已确认后读取转发头。
|
|
30
|
-
-
|
|
39
|
+
- 消息与文本值中的 CR/LF 必须由 JSON 序列化编码,防止伪造日志行。前端错误消息与堆栈在上报前也必须清除常见凭据。
|
|
31
40
|
|
|
32
41
|
## 追踪与时间
|
|
33
42
|
|
|
@@ -38,9 +47,9 @@
|
|
|
38
47
|
## 本地存储与完整性
|
|
39
48
|
|
|
40
49
|
- 应用从项目根目录启动,仅可写 `./logs`,不得读取环境变量改写目录或创建子目录。
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
50
|
+
- 初始化 logger 前必须以 `0700` 预创建 `./logs`;`pino-roll` 不得负责创建目录。日志文件以 `0600` 创建、仅追加。
|
|
51
|
+
- `pino-roll` 的 `size` 固定为 `2m`,轮转文件命名遵循 `filename.date.count.log`,保留既有文件;不得启用 `limit.removeOtherLogFiles` 或有限 `limit.count` 等会删除历史日志的策略。
|
|
52
|
+
- 每条落盘记录在进入 `pino-roll` 前经过哈希链 Transform,包含 `chainId`、`sequence`、`previousHash`、`entryHash`。校验器必须能发现内容、顺序或链路被篡改;哈希链是完整性证据,不替代外部不可变存储。
|
|
44
53
|
- 日志目录不可创建、权限不可收紧或写入失败时,服务必须显式失败,不得降级到 console 或吞错。
|
|
45
54
|
|
|
46
55
|
## 留存与生产控制
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { basename } from 'node:path';
|
|
4
5
|
import {
|
|
5
6
|
assertLogEntry,
|
|
7
|
+
assertPinoRollStorageOptions,
|
|
8
|
+
assertRolledLogFileName,
|
|
6
9
|
findForbiddenLogFieldPath,
|
|
7
10
|
} from '../templates/logger.template.ts';
|
|
8
11
|
|
|
9
12
|
const argumentsList = process.argv.slice(2);
|
|
10
13
|
const storedMode = argumentsList[0] === '--stored';
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
|
|
14
|
+
const storageOptionsMode = argumentsList[0] === '--storage-options';
|
|
15
|
+
let inputPath;
|
|
16
|
+
if (storedMode || storageOptionsMode) {
|
|
17
|
+
if (argumentsList.length !== 2 || !argumentsList[1]) {
|
|
18
|
+
throw new Error('Usage: validate-log-entry.mjs [--stored|--storage-options] <file>');
|
|
19
|
+
}
|
|
20
|
+
inputPath = argumentsList[1];
|
|
21
|
+
} else {
|
|
22
|
+
if (argumentsList.length !== 1 || !argumentsList[0] || argumentsList[0].startsWith('--')) {
|
|
23
|
+
throw new Error('Usage: validate-log-entry.mjs [--stored|--storage-options] <file>');
|
|
24
|
+
}
|
|
25
|
+
inputPath = argumentsList[0];
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
const source = await readFile(inputPath, 'utf8');
|
|
18
29
|
|
|
30
|
+
if (storageOptionsMode) {
|
|
31
|
+
assertPinoRollStorageOptions(JSON.parse(source));
|
|
32
|
+
process.stdout.write('valid pino-roll storage options\n');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
const validateEntry = (entry) => {
|
|
20
37
|
assertLogEntry(entry);
|
|
21
38
|
const forbiddenPath = findForbiddenLogFieldPath(entry);
|
|
@@ -30,6 +47,8 @@ if (!storedMode) {
|
|
|
30
47
|
process.exit(0);
|
|
31
48
|
}
|
|
32
49
|
|
|
50
|
+
assertRolledLogFileName(basename(inputPath));
|
|
51
|
+
|
|
33
52
|
const lines = source.split('\n').filter((line) => line !== '');
|
|
34
53
|
if (lines.length === 0) throw new Error('Stored log file is empty');
|
|
35
54
|
|