dsh-config-manager 0.1.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/README.md +216 -0
- package/README.zh-CN.md +218 -0
- package/cordis.patch.yml +45 -0
- package/dsh.bundle.patch +21 -0
- package/dsh.client +39 -0
- package/lib/adapters/agent-presets.d.ts +13 -0
- package/lib/adapters/agent-presets.js +14 -0
- package/lib/adapters/agent-presets.js.map +1 -0
- package/lib/adapters/credentials.d.ts +36 -0
- package/lib/adapters/credentials.js +121 -0
- package/lib/adapters/credentials.js.map +1 -0
- package/lib/adapters/file-collection.d.ts +14 -0
- package/lib/adapters/file-collection.js +102 -0
- package/lib/adapters/file-collection.js.map +1 -0
- package/lib/adapters/index.d.ts +38 -0
- package/lib/adapters/index.js +46 -0
- package/lib/adapters/index.js.map +1 -0
- package/lib/adapters/mcp.d.ts +27 -0
- package/lib/adapters/mcp.js +175 -0
- package/lib/adapters/mcp.js.map +1 -0
- package/lib/adapters/plugin-files.d.ts +15 -0
- package/lib/adapters/plugin-files.js +91 -0
- package/lib/adapters/plugin-files.js.map +1 -0
- package/lib/adapters/plugins.d.ts +13 -0
- package/lib/adapters/plugins.js +177 -0
- package/lib/adapters/plugins.js.map +1 -0
- package/lib/adapters/prompts.d.ts +29 -0
- package/lib/adapters/prompts.js +228 -0
- package/lib/adapters/prompts.js.map +1 -0
- package/lib/adapters/providers.d.ts +33 -0
- package/lib/adapters/providers.js +143 -0
- package/lib/adapters/providers.js.map +1 -0
- package/lib/adapters/sessions.d.ts +14 -0
- package/lib/adapters/sessions.js +15 -0
- package/lib/adapters/sessions.js.map +1 -0
- package/lib/adapters/settings.d.ts +23 -0
- package/lib/adapters/settings.js +142 -0
- package/lib/adapters/settings.js.map +1 -0
- package/lib/adapters/skills.d.ts +13 -0
- package/lib/adapters/skills.js +14 -0
- package/lib/adapters/skills.js.map +1 -0
- package/lib/adapters/test-helpers.d.ts +99 -0
- package/lib/adapters/test-helpers.js +192 -0
- package/lib/adapters/test-helpers.js.map +1 -0
- package/lib/adapters/ui.d.ts +26 -0
- package/lib/adapters/ui.js +88 -0
- package/lib/adapters/ui.js.map +1 -0
- package/lib/adapters/workspaces.d.ts +12 -0
- package/lib/adapters/workspaces.js +92 -0
- package/lib/adapters/workspaces.js.map +1 -0
- package/lib/client.d.ts +377 -0
- package/lib/client.js +2598 -0
- package/lib/client.js.map +1 -0
- package/lib/core/analyzer.d.ts +35 -0
- package/lib/core/analyzer.js +563 -0
- package/lib/core/analyzer.js.map +1 -0
- package/lib/core/backup.d.ts +29 -0
- package/lib/core/backup.js +184 -0
- package/lib/core/backup.js.map +1 -0
- package/lib/core/exporter.d.ts +37 -0
- package/lib/core/exporter.js +222 -0
- package/lib/core/exporter.js.map +1 -0
- package/lib/core/importer.d.ts +42 -0
- package/lib/core/importer.js +25 -0
- package/lib/core/importer.js.map +1 -0
- package/lib/core/index.d.ts +12 -0
- package/lib/core/index.js +11 -0
- package/lib/core/index.js.map +1 -0
- package/lib/core/rollback.d.ts +9 -0
- package/lib/core/rollback.js +136 -0
- package/lib/core/rollback.js.map +1 -0
- package/lib/core/types.d.ts +331 -0
- package/lib/core/types.js +18 -0
- package/lib/core/types.js.map +1 -0
- package/lib/core/validator.d.ts +18 -0
- package/lib/core/validator.js +77 -0
- package/lib/core/validator.js.map +1 -0
- package/lib/index.d.ts +51 -0
- package/lib/index.js +875 -0
- package/lib/index.js.map +1 -0
- package/lib/migrations/index.d.ts +29 -0
- package/lib/migrations/index.js +56 -0
- package/lib/migrations/index.js.map +1 -0
- package/lib/migrations/v1-to-v2.d.ts +9 -0
- package/lib/migrations/v1-to-v2.js +10 -0
- package/lib/migrations/v1-to-v2.js.map +1 -0
- package/lib/profiles/index.d.ts +4 -0
- package/lib/profiles/index.js +5 -0
- package/lib/profiles/index.js.map +1 -0
- package/lib/profiles/profile-manager.d.ts +102 -0
- package/lib/profiles/profile-manager.js +522 -0
- package/lib/profiles/profile-manager.js.map +1 -0
- package/lib/schema/config.d.ts +28 -0
- package/lib/schema/config.js +136 -0
- package/lib/schema/config.js.map +1 -0
- package/lib/schema/manifest.d.ts +28 -0
- package/lib/schema/manifest.js +109 -0
- package/lib/schema/manifest.js.map +1 -0
- package/lib/schema/types.d.ts +169 -0
- package/lib/schema/types.js +8 -0
- package/lib/schema/types.js.map +1 -0
- package/lib/schema/versions.d.ts +27 -0
- package/lib/schema/versions.js +50 -0
- package/lib/schema/versions.js.map +1 -0
- package/lib/security/encryption.d.ts +41 -0
- package/lib/security/encryption.js +152 -0
- package/lib/security/encryption.js.map +1 -0
- package/lib/security/index.d.ts +16 -0
- package/lib/security/index.js +17 -0
- package/lib/security/index.js.map +1 -0
- package/lib/security/integrity.d.ts +30 -0
- package/lib/security/integrity.js +78 -0
- package/lib/security/integrity.js.map +1 -0
- package/lib/security/redaction.d.ts +26 -0
- package/lib/security/redaction.js +95 -0
- package/lib/security/redaction.js.map +1 -0
- package/lib/security/secret-scanner.d.ts +48 -0
- package/lib/security/secret-scanner.js +253 -0
- package/lib/security/secret-scanner.js.map +1 -0
- package/lib/security/zip-security.d.ts +27 -0
- package/lib/security/zip-security.js +154 -0
- package/lib/security/zip-security.js.map +1 -0
- package/lib/ui/conflict-view.d.ts +28 -0
- package/lib/ui/conflict-view.js +49 -0
- package/lib/ui/conflict-view.js.map +1 -0
- package/lib/ui/errors.d.ts +17 -0
- package/lib/ui/errors.js +76 -0
- package/lib/ui/errors.js.map +1 -0
- package/lib/ui/export-flow.d.ts +62 -0
- package/lib/ui/export-flow.js +79 -0
- package/lib/ui/export-flow.js.map +1 -0
- package/lib/ui/import-wizard.d.ts +67 -0
- package/lib/ui/import-wizard.js +169 -0
- package/lib/ui/import-wizard.js.map +1 -0
- package/lib/ui/path-mapping.d.ts +38 -0
- package/lib/ui/path-mapping.js +75 -0
- package/lib/ui/path-mapping.js.map +1 -0
- package/lib/ui/progress.d.ts +27 -0
- package/lib/ui/progress.js +76 -0
- package/lib/ui/progress.js.map +1 -0
- package/lib/ui/report.d.ts +21 -0
- package/lib/ui/report.js +146 -0
- package/lib/ui/report.js.map +1 -0
- package/lib/ui/test-helpers.d.ts +62 -0
- package/lib/ui/test-helpers.js +147 -0
- package/lib/ui/test-helpers.js.map +1 -0
- package/lib/ui/types.d.ts +129 -0
- package/lib/ui/types.js +21 -0
- package/lib/ui/types.js.map +1 -0
- package/lib/utils/hashing.d.ts +17 -0
- package/lib/utils/hashing.js +41 -0
- package/lib/utils/hashing.js.map +1 -0
- package/lib/utils/json.d.ts +24 -0
- package/lib/utils/json.js +76 -0
- package/lib/utils/json.js.map +1 -0
- package/lib/utils/logger.d.ts +39 -0
- package/lib/utils/logger.js +82 -0
- package/lib/utils/logger.js.map +1 -0
- package/lib/utils/paths.d.ts +31 -0
- package/lib/utils/paths.js +132 -0
- package/lib/utils/paths.js.map +1 -0
- package/lib/utils/zip.d.ts +54 -0
- package/lib/utils/zip.js +285 -0
- package/lib/utils/zip.js.map +1 -0
- package/package.json +109 -0
- package/src/adapters/agent-presets.ts +14 -0
- package/src/adapters/credentials.test.ts +90 -0
- package/src/adapters/credentials.ts +141 -0
- package/src/adapters/file-collection.ts +110 -0
- package/src/adapters/files.test.ts +113 -0
- package/src/adapters/index.ts +69 -0
- package/src/adapters/mcp.test.ts +96 -0
- package/src/adapters/mcp.ts +181 -0
- package/src/adapters/plugin-files.ts +96 -0
- package/src/adapters/plugins.test.ts +72 -0
- package/src/adapters/plugins.ts +176 -0
- package/src/adapters/prompts.test.ts +110 -0
- package/src/adapters/prompts.ts +226 -0
- package/src/adapters/providers.test.ts +125 -0
- package/src/adapters/providers.ts +161 -0
- package/src/adapters/roundtrip.test.ts +188 -0
- package/src/adapters/sessions.ts +15 -0
- package/src/adapters/settings.test.ts +93 -0
- package/src/adapters/settings.ts +165 -0
- package/src/adapters/skills.ts +14 -0
- package/src/adapters/test-helpers.ts +207 -0
- package/src/adapters/ui.test.ts +56 -0
- package/src/adapters/ui.ts +106 -0
- package/src/adapters/workspaces.test.ts +79 -0
- package/src/adapters/workspaces.ts +95 -0
- package/src/client/CLIENT_DEPENDENCIES.md +101 -0
- package/src/client/ConfigManagerSection.tsx +60 -0
- package/src/client/api.ts +270 -0
- package/src/client/client-types.ts +27 -0
- package/src/client/common/ErrorBanner.tsx +68 -0
- package/src/client/common/ProgressBar.tsx +49 -0
- package/src/client/common/ReportView.tsx +117 -0
- package/src/client/common/ui.tsx +165 -0
- package/src/client/config-manager.module.css +759 -0
- package/src/client/css-modules.d.ts +8 -0
- package/src/client/export/ExportView.tsx +222 -0
- package/src/client/import/ConflictList.tsx +72 -0
- package/src/client/import/ImportWizardView.tsx +406 -0
- package/src/client/import/PathMappingForm.tsx +77 -0
- package/src/client/index.ts +65 -0
- package/src/client/locales.ts +182 -0
- package/src/core/analyzer.ts +631 -0
- package/src/core/backup.ts +212 -0
- package/src/core/exporter.ts +246 -0
- package/src/core/importer.ts +66 -0
- package/src/core/index.ts +39 -0
- package/src/core/rollback.ts +140 -0
- package/src/core/smoke.test.ts +717 -0
- package/src/core/types.ts +343 -0
- package/src/core/validator.ts +84 -0
- package/src/index.ts +942 -0
- package/src/migrations/index.ts +71 -0
- package/src/migrations/v1-to-v2.ts +17 -0
- package/src/profiles/index.ts +8 -0
- package/src/profiles/profile-manager.test.ts +280 -0
- package/src/profiles/profile-manager.ts +606 -0
- package/src/schema/config.ts +137 -0
- package/src/schema/manifest.ts +124 -0
- package/src/schema/types.ts +179 -0
- package/src/schema/versions.ts +55 -0
- package/src/security/encryption.ts +180 -0
- package/src/security/index.ts +16 -0
- package/src/security/integrity.ts +102 -0
- package/src/security/redaction.ts +101 -0
- package/src/security/secret-scanner.ts +275 -0
- package/src/security/security.test.ts +796 -0
- package/src/security/zip-security.ts +180 -0
- package/src/ui/conflict-view.test.ts +62 -0
- package/src/ui/conflict-view.ts +65 -0
- package/src/ui/errors.test.ts +44 -0
- package/src/ui/errors.ts +95 -0
- package/src/ui/export-flow.test.ts +75 -0
- package/src/ui/export-flow.ts +128 -0
- package/src/ui/import-wizard.test.ts +128 -0
- package/src/ui/import-wizard.ts +208 -0
- package/src/ui/path-mapping.test.ts +67 -0
- package/src/ui/path-mapping.ts +103 -0
- package/src/ui/progress.test.ts +42 -0
- package/src/ui/progress.ts +90 -0
- package/src/ui/report.test.ts +93 -0
- package/src/ui/report.ts +140 -0
- package/src/ui/test-helpers.ts +177 -0
- package/src/ui/types.ts +193 -0
- package/src/utils/hashing.ts +53 -0
- package/src/utils/json.ts +77 -0
- package/src/utils/logger.ts +114 -0
- package/src/utils/paths.ts +122 -0
- package/src/utils/zip.ts +332 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 凭据加密(规范 §7 / 设计 §7.3 / core `EncryptionProvider` 契约实现,零依赖 node:crypto)。
|
|
3
|
+
*
|
|
4
|
+
* 方案:scrypt(KDF)+ AES-256-GCM(AEAD),每次导出 salt/iv 全随机,密码绝不落盘。
|
|
5
|
+
* secrets.enc 二进制布局(设计 §7.3 细化):
|
|
6
|
+
*
|
|
7
|
+
* magic "DSC1"(4B) + version(1B) + salt(16B) + iv(12B) + authTag(16B) + ciphertext
|
|
8
|
+
*
|
|
9
|
+
* - magic/version 不符 → `UNSUPPORTED_FORMAT`(不是本插件产物 / 版本过新)
|
|
10
|
+
* - blob 内嵌参数与 manifest 的 EncryptionInfo 不一致 → `TAMPERED`(元数据被篡改)
|
|
11
|
+
* - GCM 认证失败(密码错误或密文被改) → `BAD_PASSWORD`(提示重输密码或核对备份来源)
|
|
12
|
+
*
|
|
13
|
+
* 与 core 契约的关系:
|
|
14
|
+
* - `createEncryptionProvider(password)` 返回 core `EncryptionProvider`(encrypt 无密码参数,
|
|
15
|
+
* 闭包持有导出密码;decrypt 用调用方传入的密码 → 支持「换密码解密」)。
|
|
16
|
+
* - 底层纯函数 encryptCredentials/decryptCredentials 对齐设计 §13.4,供直接调用与测试。
|
|
17
|
+
* - plaintext 语义:`.credentials.yaml` 文件原文(m5 经 HostContext.fs 文件级读取提供;
|
|
18
|
+
* ctx.credentials 永不回读值——研究报告 §3.2 硬约束),本模块只做字节级加解密。
|
|
19
|
+
*/
|
|
20
|
+
import crypto from 'node:crypto';
|
|
21
|
+
import { promisify } from 'node:util';
|
|
22
|
+
const scryptAsync = promisify(crypto.scrypt);
|
|
23
|
+
export const SCHEMA_MAGIC = 'DSC1';
|
|
24
|
+
export const SCHEMA_VERSION = 1;
|
|
25
|
+
export const SCRYPT_PARAMS = { N: 16384, r: 8, p: 1, keyLength: 32 };
|
|
26
|
+
export const SALT_LENGTH = 16;
|
|
27
|
+
export const IV_LENGTH = 12;
|
|
28
|
+
export const TAG_LENGTH = 16;
|
|
29
|
+
/** magic(4) + version(1) + salt(16) + iv(12) + authTag(16) */
|
|
30
|
+
export const HEADER_LENGTH = 4 + 1 + SALT_LENGTH + IV_LENGTH + TAG_LENGTH;
|
|
31
|
+
export class SecurityError extends Error {
|
|
32
|
+
code;
|
|
33
|
+
constructor(code, message) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = 'SecurityError';
|
|
36
|
+
this.code = code;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** KDF 参数值域校验(防 manifest 被篡改成超大 N 导致 DoS;非法即拒绝) */
|
|
40
|
+
export function validateKdfParams(params) {
|
|
41
|
+
if (params === null || typeof params !== 'object')
|
|
42
|
+
return false;
|
|
43
|
+
const p = params;
|
|
44
|
+
if (typeof p['N'] !== 'number' || p['N'] < 2 ** 14 || p['N'] > 2 ** 20)
|
|
45
|
+
return false;
|
|
46
|
+
if (typeof p['r'] !== 'number' || p['r'] < 1 || p['r'] > 32)
|
|
47
|
+
return false;
|
|
48
|
+
if (typeof p['p'] !== 'number' || p['p'] < 1 || p['p'] > 32)
|
|
49
|
+
return false;
|
|
50
|
+
return typeof p['keyLength'] === 'number' && p['keyLength'] === 32;
|
|
51
|
+
}
|
|
52
|
+
/** 派生密钥(scrypt,参数可来自 manifest;默认常量) */
|
|
53
|
+
export async function deriveKey(password, salt, params = SCRYPT_PARAMS) {
|
|
54
|
+
return scryptAsync(password, salt, params.keyLength, { N: params.N, r: params.r, p: params.p });
|
|
55
|
+
}
|
|
56
|
+
/** 加密 .credentials.yaml 原文 → { blob, info }(info 直接进 manifest.security.encryption) */
|
|
57
|
+
export async function encryptCredentials(plaintext, password) {
|
|
58
|
+
if (password === '')
|
|
59
|
+
throw new SecurityError('BAD_PASSWORD', '加密密码不能为空');
|
|
60
|
+
const salt = crypto.randomBytes(SALT_LENGTH);
|
|
61
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
62
|
+
const key = await deriveKey(password, salt);
|
|
63
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
64
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
65
|
+
const authTag = cipher.getAuthTag();
|
|
66
|
+
const header = Buffer.alloc(HEADER_LENGTH);
|
|
67
|
+
header.write(SCHEMA_MAGIC, 0, 'ascii');
|
|
68
|
+
header[4] = SCHEMA_VERSION;
|
|
69
|
+
salt.copy(header, 5);
|
|
70
|
+
iv.copy(header, 5 + SALT_LENGTH);
|
|
71
|
+
authTag.copy(header, 5 + SALT_LENGTH + IV_LENGTH);
|
|
72
|
+
const info = {
|
|
73
|
+
algorithm: 'aes-256-gcm',
|
|
74
|
+
kdf: 'scrypt',
|
|
75
|
+
kdfParams: { ...SCRYPT_PARAMS },
|
|
76
|
+
salt: salt.toString('base64'),
|
|
77
|
+
iv: iv.toString('base64'),
|
|
78
|
+
authTag: authTag.toString('base64'),
|
|
79
|
+
version: SCHEMA_VERSION,
|
|
80
|
+
};
|
|
81
|
+
return { blob: Buffer.concat([header, encrypted]), info };
|
|
82
|
+
}
|
|
83
|
+
/** 解密 secrets.enc(authTag 校验失败抛 BAD_PASSWORD;元数据不一致抛 TAMPERED) */
|
|
84
|
+
export async function decryptCredentials(blob, info, password) {
|
|
85
|
+
if (blob.length < HEADER_LENGTH) {
|
|
86
|
+
throw new SecurityError('TAMPERED', 'secrets.enc 体积过小(截断或损坏)');
|
|
87
|
+
}
|
|
88
|
+
const magic = Buffer.from(blob.subarray(0, 4)).toString('ascii');
|
|
89
|
+
if (magic !== SCHEMA_MAGIC) {
|
|
90
|
+
throw new SecurityError('UNSUPPORTED_FORMAT', `未知文件格式 magic "${magic}",不是本插件产物`);
|
|
91
|
+
}
|
|
92
|
+
const version = blob[4];
|
|
93
|
+
if (version !== SCHEMA_VERSION) {
|
|
94
|
+
throw new SecurityError('UNSUPPORTED_FORMAT', `不支持的 secrets.enc 版本 ${version}`);
|
|
95
|
+
}
|
|
96
|
+
if (info.algorithm !== 'aes-256-gcm' || info.kdf !== 'scrypt' || !validateKdfParams(info.kdfParams)) {
|
|
97
|
+
throw new SecurityError('UNSUPPORTED_FORMAT', 'manifest 加密参数非法或不受支持');
|
|
98
|
+
}
|
|
99
|
+
const blobSalt = blob.subarray(5, 5 + SALT_LENGTH);
|
|
100
|
+
const blobIv = blob.subarray(5 + SALT_LENGTH, 5 + SALT_LENGTH + IV_LENGTH);
|
|
101
|
+
const blobTag = blob.subarray(5 + SALT_LENGTH + IV_LENGTH, HEADER_LENGTH);
|
|
102
|
+
if (info.salt !== Buffer.from(blobSalt).toString('base64') ||
|
|
103
|
+
info.iv !== Buffer.from(blobIv).toString('base64') ||
|
|
104
|
+
info.authTag !== Buffer.from(blobTag).toString('base64')) {
|
|
105
|
+
throw new SecurityError('TAMPERED', 'secrets.enc 与 manifest 加密参数不一致(可能被篡改)');
|
|
106
|
+
}
|
|
107
|
+
let key;
|
|
108
|
+
try {
|
|
109
|
+
key = await deriveKey(password, blobSalt, info.kdfParams);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
throw new SecurityError('BAD_PASSWORD', `密钥派生失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
113
|
+
}
|
|
114
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, blobIv);
|
|
115
|
+
decipher.setAuthTag(blobTag);
|
|
116
|
+
try {
|
|
117
|
+
const decrypted = Buffer.concat([
|
|
118
|
+
decipher.update(blob.subarray(HEADER_LENGTH)),
|
|
119
|
+
decipher.final(),
|
|
120
|
+
]);
|
|
121
|
+
return decrypted.toString('utf8');
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new SecurityError('BAD_PASSWORD', '解密认证失败:密码错误或密文被篡改');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** 密码强度校验(导出 UI 用;≥8 字符,建议 12+ 混合) */
|
|
128
|
+
export function validatePasswordStrength(password) {
|
|
129
|
+
if (password.length < 8) {
|
|
130
|
+
return { ok: false, message: '密码至少 8 个字符(建议 12+ 且含大小写与数字)' };
|
|
131
|
+
}
|
|
132
|
+
const variety = /[a-z]/.test(password) && /[A-Z]/.test(password) && /[0-9]/.test(password);
|
|
133
|
+
if (password.length < 12 && !variety) {
|
|
134
|
+
return { ok: true, message: '密码强度偏弱:建议 12+ 字符且混合大小写与数字' };
|
|
135
|
+
}
|
|
136
|
+
return { ok: true, message: '' };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 创建 core `EncryptionProvider`(对齐 core/types.ts 契约)。
|
|
140
|
+
* encrypt 使用闭包持有密码;decrypt 使用调用方传入的密码(支持换密码解密)。
|
|
141
|
+
*/
|
|
142
|
+
export function createEncryptionProvider(password) {
|
|
143
|
+
return {
|
|
144
|
+
async encrypt(plaintext) {
|
|
145
|
+
return encryptCredentials(plaintext, password);
|
|
146
|
+
},
|
|
147
|
+
async decrypt(blob, info, pw) {
|
|
148
|
+
return decryptCredentials(blob, info, pw);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/security/encryption.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安全模块公共出口(m4-security):
|
|
3
|
+
* secret-scanner / encryption / integrity / zip-security / redaction。
|
|
4
|
+
*
|
|
5
|
+
* 与 core 的注入点对齐:
|
|
6
|
+
* - `createSecretScanner()` → ExporterOptions.scanner(SecretScanner 契约)
|
|
7
|
+
* - `createEncryptionProvider(pw)` → ExporterOptions.encryption(EncryptionProvider 契约)
|
|
8
|
+
* - `createHardenedZipParser()` → ImporterOptions.parseZipOverride((buf, limits?) => ZipArchive)
|
|
9
|
+
* - `safeExtractHardened()` → m5 导入文件类分区落盘通道
|
|
10
|
+
* - `verifyChecksumsJson()` → 完整性校验通道(core analyzer 已有内置校验,本模块供独立调用)
|
|
11
|
+
*/
|
|
12
|
+
export * from './secret-scanner.ts';
|
|
13
|
+
export * from './encryption.ts';
|
|
14
|
+
export * from './integrity.ts';
|
|
15
|
+
export * from './zip-security.ts';
|
|
16
|
+
export * from './redaction.ts';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安全模块公共出口(m4-security):
|
|
3
|
+
* secret-scanner / encryption / integrity / zip-security / redaction。
|
|
4
|
+
*
|
|
5
|
+
* 与 core 的注入点对齐:
|
|
6
|
+
* - `createSecretScanner()` → ExporterOptions.scanner(SecretScanner 契约)
|
|
7
|
+
* - `createEncryptionProvider(pw)` → ExporterOptions.encryption(EncryptionProvider 契约)
|
|
8
|
+
* - `createHardenedZipParser()` → ImporterOptions.parseZipOverride((buf, limits?) => ZipArchive)
|
|
9
|
+
* - `safeExtractHardened()` → m5 导入文件类分区落盘通道
|
|
10
|
+
* - `verifyChecksumsJson()` → 完整性校验通道(core analyzer 已有内置校验,本模块供独立调用)
|
|
11
|
+
*/
|
|
12
|
+
export * from "./secret-scanner.js";
|
|
13
|
+
export * from "./encryption.js";
|
|
14
|
+
export * from "./integrity.js";
|
|
15
|
+
export * from "./zip-security.js";
|
|
16
|
+
export * from "./redaction.js";
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ChecksumVerifyResult } from '../utils/hashing.ts';
|
|
2
|
+
export declare const CHECKSUMS_ALGORITHM: "sha256";
|
|
3
|
+
export declare const SHA256_HEX_RE: RegExp;
|
|
4
|
+
/** 与 core ZIP 条目数上限一致的默认表大小上限 */
|
|
5
|
+
export declare const DEFAULT_MAX_CHECKSUM_ENTRIES = 10000;
|
|
6
|
+
export interface ChecksumsTableOptions {
|
|
7
|
+
maxEntries?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 生成 checksums 表(对齐 core 扁平格式;内部复用 core buildChecksums)。
|
|
11
|
+
* entries.name 必须是 ZIP 内正斜杠相对路径。
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildChecksums(entries: {
|
|
14
|
+
name: string;
|
|
15
|
+
data: Uint8Array;
|
|
16
|
+
}[]): Record<string, string>;
|
|
17
|
+
/**
|
|
18
|
+
* 解析并结构校验 checksums.json 原文。
|
|
19
|
+
* 非法结构 / 危险键 / 值非 SHA-256 hex / 条目数超限 → 抛错(不可信输入,宁可拒绝)。
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseChecksumsTable(raw: string, opts?: ChecksumsTableOptions): Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* 校验:表内每个路径必须在 entries 中存在且 SHA-256 一致;entries 多余文件(manifest/自身)不参与。
|
|
24
|
+
* 返回 { ok, mismatches, missing }(缺失与不符统一列入 mismatches 的展开视图,mismatches 只含不符)。
|
|
25
|
+
*/
|
|
26
|
+
export declare function verifyChecksums(entries: ReadonlyMap<string, Uint8Array>, table: Record<string, string>): ChecksumVerifyResult;
|
|
27
|
+
/** 一步到位:解析 checksums.json 原文并校验(解析失败抛错;不符返回 {ok:false,...}) */
|
|
28
|
+
export declare function verifyChecksumsJson(entries: ReadonlyMap<string, Uint8Array>, raw: string, opts?: ChecksumsTableOptions): ChecksumVerifyResult;
|
|
29
|
+
/** 人类可读的不匹配摘要(给「Backup integrity check failed」错误消息用) */
|
|
30
|
+
export declare function describeMismatches(result: ChecksumVerifyResult): string;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置完整性(规范 §18 / 设计 §9.7 / core utils/hashing.ts 的强化层)。
|
|
3
|
+
*
|
|
4
|
+
* checksums.json 实际格式由 core exporter 定死(`src/core/exporter.ts`):
|
|
5
|
+
* 扁平对象 `{ "<ZIP 内相对路径>": "<sha256 hex>" }`,覆盖除 manifest.json 与自身外的全部条目;
|
|
6
|
+
* core analyzer 导入时按此解析(`as Record<string, string>`)。本模块**对齐该格式**,不擅自改结构。
|
|
7
|
+
*
|
|
8
|
+
* 强化点(在 core hashing 之上):
|
|
9
|
+
* - 解析防线:结构校验(对象、值必须 64 位 hex)、条目数上限、拒绝 `__proto__`/`constructor`/`prototype`
|
|
10
|
+
* 危险键(防 prototype pollution);解析失败抛错 → 导入方归为「Backup integrity check failed」。
|
|
11
|
+
* - 一步到位 `verifyChecksumsJson`:解析 + 校验合并,供 m5/导入管线直接调用。
|
|
12
|
+
* - 校验语义:表内每个路径必须在条目集合中存在且 SHA-256 一致;缺失与不符均计入 mismatches。
|
|
13
|
+
*/
|
|
14
|
+
import { parseJsonSafe } from "../utils/json.js";
|
|
15
|
+
import { buildChecksums as coreBuildChecksums, verifyChecksums as coreVerifyChecksums, } from "../utils/hashing.js";
|
|
16
|
+
export const CHECKSUMS_ALGORITHM = 'sha256';
|
|
17
|
+
export const SHA256_HEX_RE = /^[0-9a-f]{64}$/i;
|
|
18
|
+
/** 与 core ZIP 条目数上限一致的默认表大小上限 */
|
|
19
|
+
export const DEFAULT_MAX_CHECKSUM_ENTRIES = 10_000;
|
|
20
|
+
/** 危险键(原型污染向量):解析时拒绝 */
|
|
21
|
+
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
22
|
+
/**
|
|
23
|
+
* 生成 checksums 表(对齐 core 扁平格式;内部复用 core buildChecksums)。
|
|
24
|
+
* entries.name 必须是 ZIP 内正斜杠相对路径。
|
|
25
|
+
*/
|
|
26
|
+
export function buildChecksums(entries) {
|
|
27
|
+
return coreBuildChecksums(entries);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 解析并结构校验 checksums.json 原文。
|
|
31
|
+
* 非法结构 / 危险键 / 值非 SHA-256 hex / 条目数超限 → 抛错(不可信输入,宁可拒绝)。
|
|
32
|
+
*/
|
|
33
|
+
export function parseChecksumsTable(raw, opts = {}) {
|
|
34
|
+
const maxEntries = opts.maxEntries ?? DEFAULT_MAX_CHECKSUM_ENTRIES;
|
|
35
|
+
const parsed = parseJsonSafe(raw);
|
|
36
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
37
|
+
throw new Error('checksums.json 必须是 JSON 对象');
|
|
38
|
+
}
|
|
39
|
+
const entries = Object.entries(parsed);
|
|
40
|
+
if (entries.length > maxEntries) {
|
|
41
|
+
throw new Error(`checksums.json 条目数 ${entries.length} 超过上限 ${maxEntries}`);
|
|
42
|
+
}
|
|
43
|
+
const table = {};
|
|
44
|
+
for (const [relPath, hash] of entries) {
|
|
45
|
+
if (DANGEROUS_KEYS.has(relPath)) {
|
|
46
|
+
throw new Error(`checksums.json 含危险键 "${relPath}",已拒绝`);
|
|
47
|
+
}
|
|
48
|
+
if (relPath === '' || relPath.includes('\\') || relPath.startsWith('/')) {
|
|
49
|
+
throw new Error(`checksums.json 含非法相对路径 "${relPath}"`);
|
|
50
|
+
}
|
|
51
|
+
if (typeof hash !== 'string' || !SHA256_HEX_RE.test(hash)) {
|
|
52
|
+
throw new Error(`checksums.json["${relPath}"] 不是合法的 SHA-256 hex`);
|
|
53
|
+
}
|
|
54
|
+
table[relPath] = hash;
|
|
55
|
+
}
|
|
56
|
+
return table;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 校验:表内每个路径必须在 entries 中存在且 SHA-256 一致;entries 多余文件(manifest/自身)不参与。
|
|
60
|
+
* 返回 { ok, mismatches, missing }(缺失与不符统一列入 mismatches 的展开视图,mismatches 只含不符)。
|
|
61
|
+
*/
|
|
62
|
+
export function verifyChecksums(entries, table) {
|
|
63
|
+
return coreVerifyChecksums(entries, table);
|
|
64
|
+
}
|
|
65
|
+
/** 一步到位:解析 checksums.json 原文并校验(解析失败抛错;不符返回 {ok:false,...}) */
|
|
66
|
+
export function verifyChecksumsJson(entries, raw, opts = {}) {
|
|
67
|
+
const table = parseChecksumsTable(raw, opts);
|
|
68
|
+
return verifyChecksums(entries, table);
|
|
69
|
+
}
|
|
70
|
+
/** 人类可读的不匹配摘要(给「Backup integrity check failed」错误消息用) */
|
|
71
|
+
export function describeMismatches(result) {
|
|
72
|
+
const parts = [
|
|
73
|
+
...result.mismatches.map((m) => `"${m}" (hash 不符)`),
|
|
74
|
+
...result.missing.map((m) => `"${m}" (缺失)`),
|
|
75
|
+
];
|
|
76
|
+
return parts.join(', ');
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/security/integrity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 日志/文本脱敏(规范 §24 / 设计 §9.8 / core utils/logger.ts 的强化层)。
|
|
3
|
+
*
|
|
4
|
+
* 与 core logger.redact 的分工:core 版按固定字段名单做子串替换(不感知字段边界);
|
|
5
|
+
* 本模块为强化版,两模式:
|
|
6
|
+
* 1. **结构化字段形态**:JSON `"field": "value"` / `field=value` / `field: value`,
|
|
7
|
+
* 字段名命中敏感名单(复用 secret-scanner 的 `isSensitiveFieldName`,单一来源防漂移)→ 值替换。
|
|
8
|
+
* 2. **值形状模式**:与字段名无关,文本任意位置出现 sk- / JWT / AKIA / GitHub PAT /
|
|
9
|
+
* PEM 私钥 / Bearer / URL query 的敏感参数值 → 替换。
|
|
10
|
+
*
|
|
11
|
+
* 幂等性:替换产物 `***REDACTED***` 不匹配任何模式,重复 redact 结果不变;
|
|
12
|
+
* JSON 行的引号结构保留(输出仍是合法 JSON)。
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULT_SECRET_FIELD_NAMES } from './secret-scanner.ts';
|
|
15
|
+
export declare const REDACTED = "***REDACTED***";
|
|
16
|
+
/**
|
|
17
|
+
* 文本脱敏(幂等)。blacklist 为**附加**规范化字段名(小写无分隔符,如 'clientsecret')。
|
|
18
|
+
* 输出仍保留结构化形态(JSON 行合法)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function redact(text: string, blacklist?: readonly string[]): string;
|
|
21
|
+
/** 对象级掩码:字段名命中敏感名单 → 整值替换(返回新对象,不改原对象;结构与类型保留) */
|
|
22
|
+
export declare function redactValue(value: unknown, blacklist?: readonly string[]): unknown;
|
|
23
|
+
/** 预编译脱敏器(多次调用复用;blacklist 为附加规范化字段名) */
|
|
24
|
+
export declare function createRedactor(blacklist?: readonly string[]): (text: string) => string;
|
|
25
|
+
/** 默认名单(文档用;redact 的默认附加名单为空,内置判定已含 DEFAULT_SECRET_FIELD_NAMES) */
|
|
26
|
+
export { DEFAULT_SECRET_FIELD_NAMES };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 日志/文本脱敏(规范 §24 / 设计 §9.8 / core utils/logger.ts 的强化层)。
|
|
3
|
+
*
|
|
4
|
+
* 与 core logger.redact 的分工:core 版按固定字段名单做子串替换(不感知字段边界);
|
|
5
|
+
* 本模块为强化版,两模式:
|
|
6
|
+
* 1. **结构化字段形态**:JSON `"field": "value"` / `field=value` / `field: value`,
|
|
7
|
+
* 字段名命中敏感名单(复用 secret-scanner 的 `isSensitiveFieldName`,单一来源防漂移)→ 值替换。
|
|
8
|
+
* 2. **值形状模式**:与字段名无关,文本任意位置出现 sk- / JWT / AKIA / GitHub PAT /
|
|
9
|
+
* PEM 私钥 / Bearer / URL query 的敏感参数值 → 替换。
|
|
10
|
+
*
|
|
11
|
+
* 幂等性:替换产物 `***REDACTED***` 不匹配任何模式,重复 redact 结果不变;
|
|
12
|
+
* JSON 行的引号结构保留(输出仍是合法 JSON)。
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULT_SECRET_FIELD_NAMES, isSensitiveFieldName } from "./secret-scanner.js";
|
|
15
|
+
export const REDACTED = '***REDACTED***';
|
|
16
|
+
/* ---------------- 结构化字段形态 ---------------- */
|
|
17
|
+
const JSON_FIELD_RE = /"([A-Za-z0-9_.\-]+)"\s*:\s*"([^"]*)"/g;
|
|
18
|
+
/** kv 值到空白/逗号/分号/&(& 截断:避免吞掉 URL query 的后续参数) */
|
|
19
|
+
const KV_FIELD_RE = /([A-Za-z0-9_.\-]+)\s*=\s*([^\s,;&]+)/g;
|
|
20
|
+
/** colon 值允许空格(如 `Authorization: Bearer xyz`),到逗号/分号/右大括号截断 */
|
|
21
|
+
const COLON_FIELD_RE = /([A-Za-z0-9_.\-]+)\s*:\s*([^,;}]+)/g;
|
|
22
|
+
/* ---------------- 值形状模式(包含式,字段名无关) ---------------- */
|
|
23
|
+
const VALUE_PATTERNS = [
|
|
24
|
+
{ name: 'openai-key', re: /sk-[A-Za-z0-9_-]{8,}/g },
|
|
25
|
+
{ name: 'jwt', re: /eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g },
|
|
26
|
+
{ name: 'aws-access-key', re: /AKIA[0-9A-Z]{16}/g },
|
|
27
|
+
{ name: 'github-token', re: /gh[pousr]_[A-Za-z0-9]{20,}/g },
|
|
28
|
+
{ name: 'github-pat', re: /github_pat_[A-Za-z0-9_]{20,}/g },
|
|
29
|
+
{ name: 'pem-private-key', re: /-----BEGIN [A-Za-z0-9 ]*PRIVATE KEY-----/g },
|
|
30
|
+
{ name: 'bearer-token', re: /Bearer [A-Za-z0-9._~+/=-]{8,}/g },
|
|
31
|
+
];
|
|
32
|
+
/** URL query 中的敏感参数值(保留参数名,只替换值) */
|
|
33
|
+
const URL_QUERY_RE = /([?&](?:token|api[_-]?key|key|secret|access[_-]?token|password|auth)=)([^&\s"']+)/gi;
|
|
34
|
+
/* ---------------- redact ---------------- */
|
|
35
|
+
function replaceSensitiveField(text, re, blacklist) {
|
|
36
|
+
return text.replace(re, (match, field, value) => {
|
|
37
|
+
if (!isSensitiveFieldName(field, blacklist))
|
|
38
|
+
return match;
|
|
39
|
+
if (value === '')
|
|
40
|
+
return match;
|
|
41
|
+
// 只替换值部分,保留原分隔符与引号形态(JSON `"f": "v"` / kv `f=v` / colon `f: v`)
|
|
42
|
+
const idx = match.lastIndexOf(value);
|
|
43
|
+
return match.slice(0, idx) + REDACTED + match.slice(idx + value.length);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 文本脱敏(幂等)。blacklist 为**附加**规范化字段名(小写无分隔符,如 'clientsecret')。
|
|
48
|
+
* 输出仍保留结构化形态(JSON 行合法)。
|
|
49
|
+
*/
|
|
50
|
+
export function redact(text, blacklist = []) {
|
|
51
|
+
let out = text;
|
|
52
|
+
// 1. 结构化字段形态(顺序:JSON → kv → colon;JSON 键不会被后续形态二次命中)
|
|
53
|
+
out = replaceSensitiveField(out, JSON_FIELD_RE, blacklist);
|
|
54
|
+
out = replaceSensitiveField(out, KV_FIELD_RE, blacklist);
|
|
55
|
+
out = replaceSensitiveField(out, COLON_FIELD_RE, blacklist);
|
|
56
|
+
// 2. 值形状模式(字段名无关,包含式)
|
|
57
|
+
for (const { re } of VALUE_PATTERNS) {
|
|
58
|
+
re.lastIndex = 0;
|
|
59
|
+
out = out.replace(re, REDACTED);
|
|
60
|
+
}
|
|
61
|
+
out = out.replace(URL_QUERY_RE, `$1${REDACTED}`);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
/** 对象级掩码:字段名命中敏感名单 → 整值替换(返回新对象,不改原对象;结构与类型保留) */
|
|
65
|
+
export function redactValue(value, blacklist = []) {
|
|
66
|
+
if (value === null || typeof value !== 'object')
|
|
67
|
+
return value;
|
|
68
|
+
if (value instanceof Uint8Array)
|
|
69
|
+
return value;
|
|
70
|
+
if (Array.isArray(value))
|
|
71
|
+
return value.map((v) => redactValue(v, blacklist));
|
|
72
|
+
const out = {};
|
|
73
|
+
for (const [k, v] of Object.entries(value)) {
|
|
74
|
+
out[k] = isSensitiveFieldName(k, blacklist) ? REDACTED : redactValue(v, blacklist);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** 预编译脱敏器(多次调用复用;blacklist 为附加规范化字段名) */
|
|
79
|
+
export function createRedactor(blacklist = []) {
|
|
80
|
+
const compiled = { json: JSON_FIELD_RE, kv: KV_FIELD_RE, colon: COLON_FIELD_RE };
|
|
81
|
+
return (text) => {
|
|
82
|
+
let out = replaceSensitiveField(text, new RegExp(compiled.json.source, 'g'), blacklist);
|
|
83
|
+
out = replaceSensitiveField(out, new RegExp(compiled.kv.source, 'g'), blacklist);
|
|
84
|
+
out = replaceSensitiveField(out, new RegExp(compiled.colon.source, 'g'), blacklist);
|
|
85
|
+
for (const { re } of VALUE_PATTERNS) {
|
|
86
|
+
re.lastIndex = 0;
|
|
87
|
+
out = out.replace(re, REDACTED);
|
|
88
|
+
}
|
|
89
|
+
out = out.replace(URL_QUERY_RE, `$1${REDACTED}`);
|
|
90
|
+
return out;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** 默认名单(文档用;redact 的默认附加名单为空,内置判定已含 DEFAULT_SECRET_FIELD_NAMES) */
|
|
94
|
+
export { DEFAULT_SECRET_FIELD_NAMES };
|
|
95
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../src/security/redaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SecretScanner, SensitiveHit } from '../core/types.ts';
|
|
2
|
+
/** 剥离占位(与 core defaultSecretScanner 语义一致:空串 = 需补录提示) */
|
|
3
|
+
export declare const REDACTED_PLACEHOLDER = "";
|
|
4
|
+
/**
|
|
5
|
+
* 敏感字段名单(规范化名:小写、无分隔符)。
|
|
6
|
+
* 覆盖规范 §6 原始清单 + 设计 §7.2 + core logger 名单,并保持克制以防误伤
|
|
7
|
+
* (不收录 `key`/`session` 等过宽子串——`sessionIds`/`monkey` 不得中招)。
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_SECRET_FIELD_NAMES: readonly string[];
|
|
10
|
+
/** 引用字段(值只是「名字」而非秘密):规范化精确命中即豁免 */
|
|
11
|
+
export declare const DEFAULT_REFERENCE_FIELDS: readonly string[];
|
|
12
|
+
/** 值形状强模式(默认开):一眼可辨的密钥/凭据形态(包含式:值内任意位置出现即命中) */
|
|
13
|
+
export interface ValuePattern {
|
|
14
|
+
name: string;
|
|
15
|
+
re: RegExp;
|
|
16
|
+
}
|
|
17
|
+
export declare const SECRET_VALUE_PATTERNS: readonly ValuePattern[];
|
|
18
|
+
export interface SecretScannerOptions {
|
|
19
|
+
/** 附加敏感字段名(任意原始形态,内部会规范化) */
|
|
20
|
+
extraFieldNames?: string[];
|
|
21
|
+
/** 附加引用字段名(规范化后精确命中即豁免) */
|
|
22
|
+
extraReferenceFields?: string[];
|
|
23
|
+
/** 值形状启发式开关(默认 true) */
|
|
24
|
+
valuePatterns?: boolean;
|
|
25
|
+
/** 高熵长串启发式(默认 false:误伤风险高,需显式开启) */
|
|
26
|
+
highEntropy?: boolean;
|
|
27
|
+
/** 最大递归深度(默认 64,与 JSON 深度上限一致) */
|
|
28
|
+
maxDepth?: number;
|
|
29
|
+
}
|
|
30
|
+
/** 规范化字段名:小写 + 去 `_-. ` 分隔符(用于名单匹配) */
|
|
31
|
+
export declare function normalizeFieldName(name: string): string;
|
|
32
|
+
/** 是否为「环境变量名」形态(全大写下划线,如 DEEPSEEK_API_KEY)→ 视为引用名 */
|
|
33
|
+
export declare function isEnvVarName(value: string): boolean;
|
|
34
|
+
/** 字段名是否命中敏感名单(规范化后精确 / 敏感后缀 / 敏感前缀) */
|
|
35
|
+
export declare function isSensitiveFieldName(field: string, extra?: readonly string[]): boolean;
|
|
36
|
+
/** 是否为引用字段(值只是名字,不剥离) */
|
|
37
|
+
export declare function isReferenceField(field: string, extra?: readonly string[]): boolean;
|
|
38
|
+
/** 值是否命中强模式 secret 形状(返回命中模式名;未命中返回 null) */
|
|
39
|
+
export declare function matchSecretValuePattern(value: string): string | null;
|
|
40
|
+
/** 递归扫描 + 剥离(纯函数,不改原数据;深度保护;返回清洗后数据与命中清单) */
|
|
41
|
+
export declare function scanAndRedact(data: unknown, opts?: SecretScannerOptions): {
|
|
42
|
+
sanitized: unknown;
|
|
43
|
+
hits: SensitiveHit[];
|
|
44
|
+
};
|
|
45
|
+
/** 文本级扫描:逐行找「敏感字段名 = 值」与「值形状」命中(只报告,不修改文本) */
|
|
46
|
+
export declare function scanText(text: string, opts?: SecretScannerOptions): SensitiveHit[];
|
|
47
|
+
/** 强化版 SecretScanner(对齐 core SecretScanner 契约,可注入 Exporter.scanner) */
|
|
48
|
+
export declare function createSecretScanner(opts?: SecretScannerOptions): SecretScanner;
|