@liuhange/dsh-data-sensitivity-classification 2.0.1 → 2.0.2

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 CHANGED
@@ -1,39 +1,53 @@
1
- {
2
- "name": "@liuhange/dsh-data-sensitivity-classification",
3
- "description": "Data sensitivity classification plugin: auto-identify sensitivity levels (Public/Internal/Confidential/Secret) and recommend masking strategies",
4
- "version": "2.0.1",
5
- "publishConfig": {
6
- "access": "public"
7
- },
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/liuhange789/data-asset-inspector.git",
11
- "directory": "packages/data-asset/data-sensitivity-classification"
12
- },
13
- "type": "module",
14
- "main": "lib/index.js",
15
- "types": "lib/types/index.d.ts",
16
- "exports": {
17
- ".": {
18
- "types": "./lib/types/index.d.ts",
19
- "default": "./lib/index.js"
20
- },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
- "./src/*": "./src/*",
26
- "./package.json": "./package.json"
27
- },
28
- "files": [
29
- "lib/index.js",
30
- "lib/invariant.js",
31
- "lib/types/**/*.d.ts"
32
- ],
33
- "license": "MIT",
34
- "peerDependencies": {
35
- "@deepseek-ai/cordis": "^0.1.0",
36
- "@deepseek-ai/dsh-tools": "^0.1.0",
37
- "@liuhange/dsh-data-asset-shared": "^2.0.0"
38
- }
39
- }
1
+ {
2
+ "name": "@liuhange/dsh-data-sensitivity-classification",
3
+ "description": "Data sensitivity classification plugin: auto-identify sensitivity levels (Public/Internal/Confidential/Secret) and recommend masking strategies Also known as @deepseek-ai/dsh-data-sensitivity-classification.",
4
+ "version": "2.0.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/liuhange789/data-asset-inspector.git",
11
+ "directory": "packages/data-asset/data-sensitivity-classification"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "lib/types/**/*.d.ts"
32
+ ],
33
+ "license": "MIT",
34
+ "scripts": {
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest run",
37
+ "build": "tsc"
38
+ },
39
+ "peerDependencies": {
40
+ "@deepseek-ai/cordis": "^4.0.0",
41
+ "@deepseek-ai/dsh-tools": "0.0.1-rc.1",
42
+ "@liuhange/dsh-data-asset-shared": "^2.0.0"
43
+ },
44
+ "keywords": [
45
+ "deepseek-harness",
46
+ "dsh-plugin",
47
+ "data-asset",
48
+ "data-asset-inspector",
49
+ "shujintong",
50
+ "数据资产"
51
+ ],
52
+ "homepage": "https://github.com/liuhange789/data-asset-inspector#readme"
53
+ }
package/lib/index.js DELETED
@@ -1,345 +0,0 @@
1
- import { defineTool } from "@deepseek-ai/dsh-tools";
2
- import * as fs from "fs";
3
- import * as path from "path";
4
- import { AuditLogger, BusinessRulesLoader, FileFormatAdapter, PathValidator } from "@liuhange/dsh-data-asset-shared";
5
- //#region lib/types/defaultClassificationRules.js
6
- const defaultClassificationRules = {
7
- fieldNameRules: [
8
- {
9
- name: "idCard",
10
- pattern: "id_?card|身份证|identity",
11
- level: "Secret"
12
- },
13
- {
14
- name: "phone",
15
- pattern: "phone|手机|mobile|tel",
16
- level: "Confidential"
17
- },
18
- {
19
- name: "email",
20
- pattern: "email|邮箱|mail",
21
- level: "Confidential"
22
- },
23
- {
24
- name: "bankCard",
25
- pattern: "bank_?card|银行卡|account",
26
- level: "Secret"
27
- },
28
- {
29
- name: "address",
30
- pattern: "address|地址|addr",
31
- level: "Internal"
32
- },
33
- {
34
- name: "name",
35
- pattern: "^name$|姓名|username",
36
- level: "Internal"
37
- },
38
- {
39
- name: "public",
40
- pattern: "status|type|category|label|tag|level",
41
- level: "Public"
42
- }
43
- ],
44
- fieldValueRules: [
45
- {
46
- name: "idCardValue",
47
- pattern: "[1-9]\\d{5}(18|19|20)?\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]",
48
- level: "Secret"
49
- },
50
- {
51
- name: "phoneValue",
52
- pattern: "1[3-9]\\d{9}",
53
- level: "Confidential"
54
- },
55
- {
56
- name: "emailValue",
57
- pattern: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
58
- level: "Confidential"
59
- },
60
- {
61
- name: "bankCardValue",
62
- pattern: "[1-9]\\d{15,18}",
63
- level: "Secret"
64
- }
65
- ],
66
- levelMapping: {
67
- Public: "none",
68
- Internal: "partial",
69
- Confidential: "full",
70
- Secret: "encrypt"
71
- },
72
- defaultLevel: "Internal"
73
- };
74
- //#endregion
75
- //#region lib/types/fieldNameMatcher.js
76
- var FieldNameMatcher = class {
77
- match(fieldName, rules) {
78
- const invalidRules = [];
79
- for (const rule of rules) try {
80
- if (new RegExp(rule.pattern, "i").test(fieldName)) return {
81
- result: {
82
- matched: true,
83
- name: rule.name,
84
- pattern: rule.pattern,
85
- level: rule.level
86
- },
87
- invalidRules
88
- };
89
- } catch {
90
- invalidRules.push({
91
- ruleName: rule.name,
92
- reason: "正则编译失败"
93
- });
94
- }
95
- return {
96
- result: { matched: false },
97
- invalidRules
98
- };
99
- }
100
- };
101
- //#endregion
102
- //#region lib/types/fieldValueMatcher.js
103
- var FieldValueMatcher = class {
104
- match(sampleValues, rules) {
105
- const invalidRules = [];
106
- if (sampleValues.length === 0) return {
107
- result: { matched: false },
108
- invalidRules
109
- };
110
- for (const rule of rules) try {
111
- const regex = new RegExp(rule.pattern);
112
- if (sampleValues.some((v) => v !== null && v !== void 0 && regex.test(String(v)))) return {
113
- result: {
114
- matched: true,
115
- name: rule.name,
116
- pattern: rule.pattern,
117
- level: rule.level
118
- },
119
- invalidRules
120
- };
121
- } catch {
122
- invalidRules.push({
123
- ruleName: rule.name,
124
- reason: "正则编译失败"
125
- });
126
- }
127
- return {
128
- result: { matched: false },
129
- invalidRules
130
- };
131
- }
132
- };
133
- //#endregion
134
- //#region lib/types/sensitivityClassifier.js
135
- var SensitivityClassifier = class {
136
- fieldNameMatcher = new FieldNameMatcher();
137
- fieldValueMatcher = new FieldValueMatcher();
138
- classify(fields, fieldSamples, config) {
139
- const classifications = [];
140
- const allInvalidRules = [];
141
- for (const fieldName of fields) {
142
- const samples = fieldSamples[fieldName] ?? [];
143
- const nameResult = this.fieldNameMatcher.match(fieldName, config.fieldNameRules);
144
- allInvalidRules.push(...nameResult.invalidRules);
145
- let level;
146
- let identifiedBy;
147
- if (nameResult.result.matched) {
148
- level = nameResult.result.level ?? config.defaultLevel;
149
- identifiedBy = {
150
- ruleName: nameResult.result.name ?? "unknown",
151
- matchType: "fieldName"
152
- };
153
- } else {
154
- const valueResult = this.fieldValueMatcher.match(samples, config.fieldValueRules);
155
- allInvalidRules.push(...valueResult.invalidRules);
156
- if (valueResult.result.matched) {
157
- level = valueResult.result.level ?? config.defaultLevel;
158
- identifiedBy = {
159
- ruleName: valueResult.result.name ?? "unknown",
160
- matchType: "fieldValue"
161
- };
162
- } else {
163
- level = config.defaultLevel;
164
- identifiedBy = {
165
- ruleName: "default",
166
- matchType: "default"
167
- };
168
- }
169
- }
170
- classifications.push({
171
- fieldName,
172
- sensitivityLevel: level,
173
- identifiedBy,
174
- recommendedStrategy: "none"
175
- });
176
- }
177
- return {
178
- classifications,
179
- invalidRules: allInvalidRules
180
- };
181
- }
182
- };
183
- //#endregion
184
- //#region lib/types/strategyRecommender.js
185
- var StrategyRecommender = class {
186
- recommend(level, levelMapping) {
187
- return levelMapping[level] ?? "partial";
188
- }
189
- };
190
- //#endregion
191
- //#region lib/types/classificationReportGenerator.js
192
- var ClassificationReportGenerator = class {
193
- async writeJson(filePath, data) {
194
- const dir = path.dirname(filePath);
195
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
196
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
197
- }
198
- async writeMarkdown(filePath, data, invalidRules) {
199
- const dir = path.dirname(filePath);
200
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
201
- const lines = [];
202
- lines.push("【数据敏感度分级报告】");
203
- lines.push("");
204
- lines.push(`> 分类时间: ${data.classifiedAt}`);
205
- lines.push(`> 字段数: ${data.fields.length}`);
206
- lines.push("");
207
- lines.push("## 字段分级");
208
- lines.push("");
209
- lines.push("| 字段名 | 敏感度等级 | 识别依据 | 推荐策略 |");
210
- lines.push("|--------|-----------|---------|---------|");
211
- for (const field of data.fields) lines.push(`| ${field.fieldName} | ${field.sensitivityLevel} | ${field.identifiedBy.matchType}(${field.identifiedBy.ruleName}) | ${field.recommendedStrategy} |`);
212
- lines.push("");
213
- if (invalidRules.length > 0) {
214
- lines.push("## 无效规则");
215
- lines.push("");
216
- for (const rule of invalidRules) lines.push(`- ${rule.ruleName}: ${rule.reason}`);
217
- lines.push("");
218
- }
219
- lines.push("## 配置状态");
220
- lines.push("");
221
- lines.push(`- 配置状态: ${data.configStatus}`);
222
- lines.push(`- 无效规则数: ${invalidRules.length}`);
223
- fs.writeFileSync(filePath, lines.join("\n"), "utf-8");
224
- }
225
- };
226
- //#endregion
227
- //#region lib/types/index.js
228
- const name = "data-sensitivity-classification";
229
- const inject = ["tools"];
230
- function parseRecords(lines, format) {
231
- if (format === "csv" && lines.length > 0) {
232
- const header = lines[0].split(",");
233
- const records = [];
234
- for (let i = 1; i < lines.length; i++) {
235
- const values = lines[i].split(",");
236
- const record = {};
237
- for (let j = 0; j < header.length; j++) record[header[j]] = values[j] ?? "";
238
- records.push(record);
239
- }
240
- return records;
241
- }
242
- if (format === "json") {
243
- const records = [];
244
- for (const line of lines) try {
245
- const parsed = JSON.parse(line);
246
- if (typeof parsed === "object" && parsed !== null) records.push(parsed);
247
- } catch {}
248
- return records;
249
- }
250
- return [];
251
- }
252
- function apply(ctx) {
253
- const loader = new BusinessRulesLoader();
254
- const formatAdapter = new FileFormatAdapter();
255
- const pathValidator = new PathValidator();
256
- const auditLogger = new AuditLogger();
257
- const classifier = new SensitivityClassifier();
258
- const recommender = new StrategyRecommender();
259
- const reportGenerator = new ClassificationReportGenerator();
260
- ctx.tools.register(defineTool({
261
- name: "classify_sensitivity",
262
- description: "自动识别数据字段敏感度等级(公开/内部/机密/绝密),并推荐相应脱敏策略",
263
- parameters: {
264
- filePath: {
265
- type: "string",
266
- required: true,
267
- description: "待分级数据文件的路径"
268
- },
269
- outputPathDir: {
270
- type: "string",
271
- description: "报告输出目录(默认数据文件所在目录)"
272
- },
273
- sampleSize: {
274
- type: "string",
275
- description: "样本大小(默认100)"
276
- }
277
- },
278
- output: {
279
- schema: { type: "string" },
280
- render: (_args, value) => [{
281
- type: "text",
282
- text: value
283
- }]
284
- },
285
- async execute(args) {
286
- const { config, status: configStatus } = loader.load();
287
- const workingDir = process.cwd();
288
- const filePath = args.filePath;
289
- const outputPathDir = args.outputPathDir ?? path.dirname(path.resolve(filePath));
290
- const sampleSize = parseInt(args.sampleSize ?? "100", 10);
291
- try {
292
- pathValidator.validate(filePath, workingDir);
293
- } catch {
294
- return `错误:路径不合法 - ${filePath}`;
295
- }
296
- const fullPath = path.resolve(filePath);
297
- if (!fs.existsSync(fullPath)) return `错误:文件不存在 - ${fullPath}`;
298
- const classificationConfig = config.sensitivityClassification ?? defaultClassificationRules;
299
- const readResult = await formatAdapter.read(fullPath);
300
- const records = parseRecords(readResult.lines, readResult.format);
301
- const fieldNames = records.length > 0 ? Object.keys(records[0]) : [];
302
- const fieldSamples = {};
303
- for (const fieldName of fieldNames) {
304
- const samples = [];
305
- for (let i = 0; i < Math.min(records.length, sampleSize); i++) {
306
- const value = records[i][fieldName];
307
- if (value !== null && value !== void 0 && String(value).trim() !== "") samples.push(value);
308
- }
309
- fieldSamples[fieldName] = samples;
310
- }
311
- const { classifications, invalidRules } = classifier.classify(fieldNames, fieldSamples, classificationConfig);
312
- const fieldsWithStrategy = classifications.map((c) => ({
313
- ...c,
314
- recommendedStrategy: recommender.recommend(c.sensitivityLevel, classificationConfig.levelMapping)
315
- }));
316
- const reportJson = {
317
- fields: fieldsWithStrategy,
318
- configStatus,
319
- classifiedAt: (/* @__PURE__ */ new Date()).toISOString()
320
- };
321
- const jsonReportPath = path.join(outputPathDir, "classification_report.json");
322
- const markdownReportPath = path.join(outputPathDir, "classification_report.md");
323
- await reportGenerator.writeJson(jsonReportPath, reportJson);
324
- await reportGenerator.writeMarkdown(markdownReportPath, reportJson, invalidRules);
325
- auditLogger.log({
326
- pluginName: "data-sensitivity-classification",
327
- operation: "classify_sensitivity",
328
- inputPath: fullPath,
329
- outputPath: jsonReportPath,
330
- result: "SUCCESS"
331
- });
332
- return JSON.stringify({
333
- jsonReportPath,
334
- markdownReportPath,
335
- report: reportJson,
336
- fields: fieldsWithStrategy,
337
- status: "SUCCESS",
338
- configStatus
339
- }, null, 2);
340
- }
341
- }));
342
- console.log("[data-sensitivity-classification] 数据敏感度分级插件已加载");
343
- }
344
- //#endregion
345
- export { apply, inject, name };
package/lib/invariant.js DELETED
@@ -1,5 +0,0 @@
1
- //#region lib/types/invariant.js
2
- const invariant = "data-sensitivity-classification";
3
- function install() {}
4
- //#endregion
5
- export { install, invariant };
@@ -1,6 +0,0 @@
1
- import type { ClassificationReportJson, InvalidRule } from './types.js';
2
- export declare class ClassificationReportGenerator {
3
- writeJson(filePath: string, data: ClassificationReportJson): Promise<void>;
4
- writeMarkdown(filePath: string, data: ClassificationReportJson, invalidRules: InvalidRule[]): Promise<void>;
5
- }
6
- //# sourceMappingURL=classificationReportGenerator.d.ts.map
@@ -1,3 +0,0 @@
1
- import type { SensitivityClassificationConfig } from '@liuhange/dsh-data-asset-shared';
2
- export declare const defaultClassificationRules: SensitivityClassificationConfig;
3
- //# sourceMappingURL=defaultClassificationRules.d.ts.map
@@ -1,9 +0,0 @@
1
- import type { SensitivityClassificationConfig } from '@liuhange/dsh-data-asset-shared';
2
- import type { MatchResult, InvalidRule } from './types.js';
3
- export declare class FieldNameMatcher {
4
- match(fieldName: string, rules: SensitivityClassificationConfig['fieldNameRules']): {
5
- result: MatchResult;
6
- invalidRules: InvalidRule[];
7
- };
8
- }
9
- //# sourceMappingURL=fieldNameMatcher.d.ts.map
@@ -1,9 +0,0 @@
1
- import type { SensitivityClassificationConfig } from '@liuhange/dsh-data-asset-shared';
2
- import type { MatchResult, InvalidRule } from './types.js';
3
- export declare class FieldValueMatcher {
4
- match(sampleValues: unknown[], rules: SensitivityClassificationConfig['fieldValueRules']): {
5
- result: MatchResult;
6
- invalidRules: InvalidRule[];
7
- };
8
- }
9
- //# sourceMappingURL=fieldValueMatcher.d.ts.map
@@ -1,5 +0,0 @@
1
- import type { Context } from '@deepseek-ai/cordis';
2
- export declare const name = "data-sensitivity-classification";
3
- export declare const inject: string[];
4
- export declare function apply(ctx: Context): void;
5
- //# sourceMappingURL=index.d.ts.map
@@ -1,3 +0,0 @@
1
- export declare const invariant = "data-sensitivity-classification";
2
- export declare function install(): void;
3
- //# sourceMappingURL=invariant.d.ts.map
@@ -1,11 +0,0 @@
1
- import type { SensitivityClassificationConfig } from '@liuhange/dsh-data-asset-shared';
2
- import type { FieldClassification, InvalidRule } from './types.js';
3
- export declare class SensitivityClassifier {
4
- private fieldNameMatcher;
5
- private fieldValueMatcher;
6
- classify(fields: string[], fieldSamples: Record<string, unknown[]>, config: SensitivityClassificationConfig): {
7
- classifications: FieldClassification[];
8
- invalidRules: InvalidRule[];
9
- };
10
- }
11
- //# sourceMappingURL=sensitivityClassifier.d.ts.map
@@ -1,6 +0,0 @@
1
- import type { SensitivityClassificationConfig } from '@liuhange/dsh-data-asset-shared';
2
- import type { SensitivityLevel, RecommendedStrategy } from './types.js';
3
- export declare class StrategyRecommender {
4
- recommend(level: SensitivityLevel, levelMapping: SensitivityClassificationConfig['levelMapping']): RecommendedStrategy;
5
- }
6
- //# sourceMappingURL=strategyRecommender.d.ts.map
@@ -1,40 +0,0 @@
1
- export type SensitivityLevel = 'Public' | 'Internal' | 'Confidential' | 'Secret';
2
- export type RecommendedStrategy = 'none' | 'partial' | 'full' | 'encrypt';
3
- export interface ClassifySensitivityParams {
4
- filePath: string;
5
- outputPathDir?: string;
6
- sampleSize?: number;
7
- }
8
- export interface MatchResult {
9
- matched: boolean;
10
- name?: string;
11
- pattern?: string;
12
- level?: string;
13
- }
14
- export interface InvalidRule {
15
- ruleName: string;
16
- reason: string;
17
- }
18
- export interface FieldClassification {
19
- fieldName: string;
20
- sensitivityLevel: SensitivityLevel;
21
- identifiedBy: {
22
- ruleName: string;
23
- matchType: string;
24
- };
25
- recommendedStrategy: RecommendedStrategy;
26
- }
27
- export interface ClassificationReportJson {
28
- fields: FieldClassification[];
29
- configStatus: string;
30
- classifiedAt: string;
31
- }
32
- export interface ClassifySensitivityResult {
33
- jsonReportPath: string;
34
- markdownReportPath: string;
35
- report: ClassificationReportJson;
36
- fields: FieldClassification[];
37
- status: 'SUCCESS' | 'FAILED';
38
- configStatus: string;
39
- }
40
- //# sourceMappingURL=types.d.ts.map