dsh-dbhub-live 5.0.1 → 5.2.0-dev.3
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/AGENTS.md +95 -209
- package/README.en.md +251 -169
- package/README.md +251 -169
- package/doc/ARCHITECTURE.md +108 -0
- package/doc/CLIENT.md +124 -0
- package/doc/DESENSITIZE.md +56 -0
- package/doc/DEVELOPMENT.md +139 -0
- package/doc/IMPACT-MATRIX.md +36 -0
- package/doc/RELEASE.md +93 -0
- package/doc/REQUIREMENTS.md +177 -105
- package/doc/images/chat-query.png +0 -0
- package/doc/images/row-editor.png +0 -0
- package/doc/images/settings-page.png +0 -0
- package/lib/adhoc.mjs +412 -34
- package/lib/capability.mjs +157 -0
- package/lib/client.js +1243 -80
- package/lib/config.mjs +370 -21
- package/lib/i18n.mjs +119 -4
- package/lib/index.mjs +401 -43
- package/lib/mcp.mjs +496 -391
- package/lib/meta.mjs +267 -0
- package/lib/options.mjs +25 -5
- package/lib/policy.mjs +92 -0
- package/lib/toml.mjs +188 -0
- package/lib/tools.mjs +1093 -827
- package/lib/update.mjs +244 -0
- package/package.json +34 -10
- package/scripts/desensitize-check.mjs +10 -3
- package/scripts/desensitize.config.mjs +255 -237
- package/scripts/docs-check.mjs +214 -0
- package/scripts/release-preflight.mjs +180 -0
- package/scripts/render-check.mjs +1366 -843
- package/test/adhoc-toml.test.mjs +365 -0
- package/test/bridge.test.mjs +227 -185
- package/test/capability.test.mjs +231 -0
- package/test/client-format.test.mjs +515 -352
- package/test/configure-options.test.mjs +244 -0
- package/test/configure-ssh.test.mjs +232 -0
- package/test/copy-from.test.mjs +155 -0
- package/test/env-options.test.mjs +456 -0
- package/test/meta.test.mjs +234 -0
- package/test/options.test.mjs +160 -123
- package/test/policy.test.mjs +132 -0
- package/test/row-cache.test.mjs +237 -138
- package/test/ssh-probe.test.mjs +834 -0
- package/test/test-op-fastpath.test.mjs +208 -0
- package/test/toml.test.mjs +352 -0
- package/test/update.test.mjs +270 -0
- package/test/util.test.mjs +17 -0
- package/test/zero-knowledge.test.mjs +120 -78
|
@@ -1,237 +1,255 @@
|
|
|
1
|
-
// dsh-dbhub-live — desensitization slot registry (single source of truth).
|
|
2
|
-
//
|
|
3
|
-
// Every place a real host, account, password, database or project name COULD be
|
|
4
|
-
// written down is a *slot*. The gate `npm run check:secrets` resolves each slot
|
|
5
|
-
// against this registry, so the check is high-precision at the places we know
|
|
6
|
-
// and still has a generic backstop for places nobody registered yet.
|
|
7
|
-
//
|
|
8
|
-
// The registry is verified: a slot whose globs match no file fails the gate
|
|
9
|
-
// (`slot-stale`), so it cannot silently rot while the tree moves.
|
|
10
|
-
//
|
|
11
|
-
// Two rule sets:
|
|
12
|
-
// * backstop — runs on EVERY scanned file (unknown surfaces): IP literals,
|
|
13
|
-
// secret formats, high-entropy strings.
|
|
14
|
-
// * slot rules — run inside the registered slots only (targeted, lower noise):
|
|
15
|
-
// credentialed-URL password/host, secret-keyword assignments, internal host
|
|
16
|
-
// suffixes, and the local denylist of bare names.
|
|
17
|
-
//
|
|
18
|
-
// Rules for editing content (not just for the gate):
|
|
19
|
-
// * example host -> 127.0.0.1, localhost, or an RFC 5737 range
|
|
20
|
-
// (192.0.2.x / 198.51.100.x / 203.0.113.x)
|
|
21
|
-
// * example password -> CHANGE_ME
|
|
22
|
-
// * test fixtures -> short placeholders are fine (`u:p`, `root:secret`),
|
|
23
|
-
// but the host still obeys the example-host rule
|
|
24
|
-
// * real values -> ONLY $DSH_HOME/storages/dsh-dbhub-live/credentials.json
|
|
25
|
-
// (outside the repository) and the local .env denylist
|
|
26
|
-
//
|
|
27
|
-
// Adding a feature that introduces a new example, default, copy string or
|
|
28
|
-
// fixture means adding/updating its slot HERE in the same change (AGENTS.md
|
|
29
|
-
// "质量门" states the obligation).
|
|
30
|
-
|
|
31
|
-
/** Placeholder passwords allowed in an example or fixture DSN. */
|
|
32
|
-
export const PLACEHOLDER_PASSWORDS = [
|
|
33
|
-
'CHANGE_ME',
|
|
34
|
-
'changeme',
|
|
35
|
-
'your-password',
|
|
36
|
-
'<password>',
|
|
37
|
-
'****',
|
|
38
|
-
'p',
|
|
39
|
-
'pass',
|
|
40
|
-
'password',
|
|
41
|
-
'secret',
|
|
42
|
-
'secret.pw',
|
|
43
|
-
'pw',
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
/** Placeholder hostnames allowed in an example or fixture DSN. */
|
|
47
|
-
export const PLACEHOLDER_HOSTS = [
|
|
48
|
-
'localhost',
|
|
49
|
-
'host',
|
|
50
|
-
'h',
|
|
51
|
-
'db',
|
|
52
|
-
'dbhost',
|
|
53
|
-
'mysql',
|
|
54
|
-
'postgres',
|
|
55
|
-
'x',
|
|
56
|
-
'alice',
|
|
57
|
-
'bob',
|
|
58
|
-
'u',
|
|
59
|
-
'user',
|
|
60
|
-
'root',
|
|
61
|
-
'evil',
|
|
62
|
-
'nope',
|
|
63
|
-
'example.com',
|
|
64
|
-
'db.example.com',
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* IP literals that may appear in the repository. Anything else is a real
|
|
69
|
-
* address and must be replaced by a loopback or RFC 5737 documentation range.
|
|
70
|
-
*/
|
|
71
|
-
export const IP_ALLOWLIST = [
|
|
72
|
-
{ label: 'IPv4 loopback 127.0.0.0/8', test: /^127\./ },
|
|
73
|
-
{ label: 'IPv4 bind-any 0.0.0.0', test: /^0\.0\.0\.0$/ },
|
|
74
|
-
{ label: 'RFC 5737 documentation 192.0.2.0/24', test: /^192\.0\.2\./ },
|
|
75
|
-
{ label: 'RFC 5737 documentation 198.51.100.0/24', test: /^198\.51\.100\./ },
|
|
76
|
-
{ label: 'RFC 5737 documentation 203.0.113.0/24', test: /^203\.0\.113\./ },
|
|
77
|
-
{ label: 'IPv6 loopback ::1', test: /^::1$/ },
|
|
78
|
-
{ label: 'IPv6 unspecified ::', test: /^::$/ },
|
|
79
|
-
]
|
|
80
|
-
|
|
81
|
-
/** Hostname suffixes that reveal an internal/private network. */
|
|
82
|
-
export const INTERNAL_HOST_SUFFIXES = [
|
|
83
|
-
'.local',
|
|
84
|
-
'.internal',
|
|
85
|
-
'.intranet',
|
|
86
|
-
'.lan',
|
|
87
|
-
'.corp',
|
|
88
|
-
'.intra',
|
|
89
|
-
'.home',
|
|
90
|
-
'.localdomain',
|
|
91
|
-
]
|
|
92
|
-
|
|
93
|
-
/** Property names whose assigned literal must be a placeholder or a reference. */
|
|
94
|
-
export const SECRET_KEYWORDS = [
|
|
95
|
-
'password',
|
|
96
|
-
'passwd',
|
|
97
|
-
'pwd',
|
|
98
|
-
'secret',
|
|
99
|
-
'token',
|
|
100
|
-
'auth_token',
|
|
101
|
-
'access_token',
|
|
102
|
-
'api_key',
|
|
103
|
-
'apikey',
|
|
104
|
-
'access_key',
|
|
105
|
-
'private_key',
|
|
106
|
-
'client_secret',
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
/** Values that are obviously not a real secret (references or placeholders). */
|
|
110
|
-
export const PLACEHOLDER_VALUE_RE =
|
|
111
|
-
/^(\$\{?[A-Za-z_][A-Za-z0-9_]*\}?|process\.env\.[A-Za-z0-9_]+|env\.[A-Za-z0-9_]+|CHANGE_ME|changeme|your-password|<[^>]+>|\*+|-+|''|""|)$/
|
|
112
|
-
|
|
113
|
-
/** Well-known credential formats: never legitimate in this repository. */
|
|
114
|
-
export const SECRET_FORMATS = [
|
|
115
|
-
{ label: 'GitHub token', re: /\bgh[pousr]_[A-Za-z0-9]{20,}/ },
|
|
116
|
-
{ label: 'npm token', re: /\bnpm_[A-Za-z0-9]{30,}/ },
|
|
117
|
-
{ label: 'OpenAI-style key', re: /\bsk-[A-Za-z0-9]{20,}/ },
|
|
118
|
-
{ label: 'Slack token', re: /\bxox[baprs]-[A-Za-z0-9-]{10,}/ },
|
|
119
|
-
{ label: 'AWS access key id', re: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
120
|
-
{ label: 'JWT', re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\./ },
|
|
121
|
-
{ label: 'PEM private key', re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
|
|
122
|
-
{ label: 'Google API key', re: /\bAIza[0-9A-Za-z_-]{35}\b/ },
|
|
123
|
-
]
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* High-entropy literal detection (catches random passwords nobody listed, e.g.
|
|
127
|
-
* the short random strings this repository used to keep in its blacklist).
|
|
128
|
-
* Only quoted single tokens are considered; hashes and ids are allowlisted.
|
|
129
|
-
*/
|
|
130
|
-
export const ENTROPY = {
|
|
131
|
-
minLength: 12,
|
|
132
|
-
minBitsPerChar: 3.5,
|
|
133
|
-
/**
|
|
134
|
-
* Structural requirement before entropy even applies: a random credential
|
|
135
|
-
* mixes letter case or digits, while ordinary code identifiers and copy
|
|
136
|
-
* strings are single-case words (`dbhub_configure`, `dbhub_search_objects`,
|
|
137
|
-
* `/plugins/dsh-dbhub-live/client.js`).
|
|
138
|
-
*/
|
|
139
|
-
requireMixed: true,
|
|
140
|
-
/** Tokens that are structurally not secrets. */
|
|
141
|
-
allowPatterns: [
|
|
142
|
-
/^[0-9a-fA-F]{16,}$/, // hex digest / sha fragment
|
|
143
|
-
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, // uuid
|
|
144
|
-
/^[A-Za-z0-9]+\.(mjs|js|json|md|yml|yaml|ts|tsx|png|tgz)$/, // file name
|
|
145
|
-
/^[a-z0-9]+(?:[-_][a-z0-9]+)+$/, // lowercase slug / identifier (kebab or snake)
|
|
146
|
-
/^Icon[A-Za-z0-9]*$/, // platform icon component name (IconCopyOutline16, IconChevronDownOutline14)
|
|
147
|
-
/^[A-Za-z0-9_.-]+=[^\s]*$/, // `KEY=value` fragment (env/config line)
|
|
148
|
-
/^[A-Za-z0-9_-]+:[A-Za-z0-9_./-]+$/, // `key: value` fragment
|
|
149
|
-
/^https?/, // url fragment
|
|
150
|
-
/^[A-Za-z0-9_./-]*[./][A-Za-z0-9_./-]*$/, // path-like token (contains . or /)
|
|
151
|
-
/^[A-Z][A-Z0-9_]{7,}$/, // SCREAMING_SNAKE env/const name
|
|
152
|
-
/^[A-Za-z]+$/, // a single word
|
|
153
|
-
],
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** Inline escape hatch: a line carrying this marker is skipped. */
|
|
157
|
-
export const PRAGMA = 'desensitize:allow'
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
*
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
]
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
1
|
+
// dsh-dbhub-live — desensitization slot registry (single source of truth).
|
|
2
|
+
//
|
|
3
|
+
// Every place a real host, account, password, database or project name COULD be
|
|
4
|
+
// written down is a *slot*. The gate `npm run check:secrets` resolves each slot
|
|
5
|
+
// against this registry, so the check is high-precision at the places we know
|
|
6
|
+
// and still has a generic backstop for places nobody registered yet.
|
|
7
|
+
//
|
|
8
|
+
// The registry is verified: a slot whose globs match no file fails the gate
|
|
9
|
+
// (`slot-stale`), so it cannot silently rot while the tree moves.
|
|
10
|
+
//
|
|
11
|
+
// Two rule sets:
|
|
12
|
+
// * backstop — runs on EVERY scanned file (unknown surfaces): IP literals,
|
|
13
|
+
// secret formats, high-entropy strings.
|
|
14
|
+
// * slot rules — run inside the registered slots only (targeted, lower noise):
|
|
15
|
+
// credentialed-URL password/host, secret-keyword assignments, internal host
|
|
16
|
+
// suffixes, absolute local paths, and the local denylist of bare names.
|
|
17
|
+
//
|
|
18
|
+
// Rules for editing content (not just for the gate):
|
|
19
|
+
// * example host -> 127.0.0.1, localhost, or an RFC 5737 range
|
|
20
|
+
// (192.0.2.x / 198.51.100.x / 203.0.113.x)
|
|
21
|
+
// * example password -> CHANGE_ME
|
|
22
|
+
// * test fixtures -> short placeholders are fine (`u:p`, `root:secret`),
|
|
23
|
+
// but the host still obeys the example-host rule
|
|
24
|
+
// * real values -> ONLY $DSH_HOME/storages/dsh-dbhub-live/credentials.json
|
|
25
|
+
// (outside the repository) and the local .env denylist
|
|
26
|
+
//
|
|
27
|
+
// Adding a feature that introduces a new example, default, copy string or
|
|
28
|
+
// fixture means adding/updating its slot HERE in the same change (AGENTS.md
|
|
29
|
+
// "质量门" states the obligation).
|
|
30
|
+
|
|
31
|
+
/** Placeholder passwords allowed in an example or fixture DSN. */
|
|
32
|
+
export const PLACEHOLDER_PASSWORDS = [
|
|
33
|
+
'CHANGE_ME',
|
|
34
|
+
'changeme',
|
|
35
|
+
'your-password',
|
|
36
|
+
'<password>',
|
|
37
|
+
'****',
|
|
38
|
+
'p',
|
|
39
|
+
'pass',
|
|
40
|
+
'password',
|
|
41
|
+
'secret',
|
|
42
|
+
'secret.pw',
|
|
43
|
+
'pw',
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
/** Placeholder hostnames allowed in an example or fixture DSN. */
|
|
47
|
+
export const PLACEHOLDER_HOSTS = [
|
|
48
|
+
'localhost',
|
|
49
|
+
'host',
|
|
50
|
+
'h',
|
|
51
|
+
'db',
|
|
52
|
+
'dbhost',
|
|
53
|
+
'mysql',
|
|
54
|
+
'postgres',
|
|
55
|
+
'x',
|
|
56
|
+
'alice',
|
|
57
|
+
'bob',
|
|
58
|
+
'u',
|
|
59
|
+
'user',
|
|
60
|
+
'root',
|
|
61
|
+
'evil',
|
|
62
|
+
'nope',
|
|
63
|
+
'example.com',
|
|
64
|
+
'db.example.com',
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* IP literals that may appear in the repository. Anything else is a real
|
|
69
|
+
* address and must be replaced by a loopback or RFC 5737 documentation range.
|
|
70
|
+
*/
|
|
71
|
+
export const IP_ALLOWLIST = [
|
|
72
|
+
{ label: 'IPv4 loopback 127.0.0.0/8', test: /^127\./ },
|
|
73
|
+
{ label: 'IPv4 bind-any 0.0.0.0', test: /^0\.0\.0\.0$/ },
|
|
74
|
+
{ label: 'RFC 5737 documentation 192.0.2.0/24', test: /^192\.0\.2\./ },
|
|
75
|
+
{ label: 'RFC 5737 documentation 198.51.100.0/24', test: /^198\.51\.100\./ },
|
|
76
|
+
{ label: 'RFC 5737 documentation 203.0.113.0/24', test: /^203\.0\.113\./ },
|
|
77
|
+
{ label: 'IPv6 loopback ::1', test: /^::1$/ },
|
|
78
|
+
{ label: 'IPv6 unspecified ::', test: /^::$/ },
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
/** Hostname suffixes that reveal an internal/private network. */
|
|
82
|
+
export const INTERNAL_HOST_SUFFIXES = [
|
|
83
|
+
'.local',
|
|
84
|
+
'.internal',
|
|
85
|
+
'.intranet',
|
|
86
|
+
'.lan',
|
|
87
|
+
'.corp',
|
|
88
|
+
'.intra',
|
|
89
|
+
'.home',
|
|
90
|
+
'.localdomain',
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
/** Property names whose assigned literal must be a placeholder or a reference. */
|
|
94
|
+
export const SECRET_KEYWORDS = [
|
|
95
|
+
'password',
|
|
96
|
+
'passwd',
|
|
97
|
+
'pwd',
|
|
98
|
+
'secret',
|
|
99
|
+
'token',
|
|
100
|
+
'auth_token',
|
|
101
|
+
'access_token',
|
|
102
|
+
'api_key',
|
|
103
|
+
'apikey',
|
|
104
|
+
'access_key',
|
|
105
|
+
'private_key',
|
|
106
|
+
'client_secret',
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
/** Values that are obviously not a real secret (references or placeholders). */
|
|
110
|
+
export const PLACEHOLDER_VALUE_RE =
|
|
111
|
+
/^(\$\{?[A-Za-z_][A-Za-z0-9_]*\}?|process\.env\.[A-Za-z0-9_]+|env\.[A-Za-z0-9_]+|CHANGE_ME|changeme|your-password|<[^>]+>|\*+|-+|''|""|)$/
|
|
112
|
+
|
|
113
|
+
/** Well-known credential formats: never legitimate in this repository. */
|
|
114
|
+
export const SECRET_FORMATS = [
|
|
115
|
+
{ label: 'GitHub token', re: /\bgh[pousr]_[A-Za-z0-9]{20,}/ },
|
|
116
|
+
{ label: 'npm token', re: /\bnpm_[A-Za-z0-9]{30,}/ },
|
|
117
|
+
{ label: 'OpenAI-style key', re: /\bsk-[A-Za-z0-9]{20,}/ },
|
|
118
|
+
{ label: 'Slack token', re: /\bxox[baprs]-[A-Za-z0-9-]{10,}/ },
|
|
119
|
+
{ label: 'AWS access key id', re: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
120
|
+
{ label: 'JWT', re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\./ },
|
|
121
|
+
{ label: 'PEM private key', re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
|
|
122
|
+
{ label: 'Google API key', re: /\bAIza[0-9A-Za-z_-]{35}\b/ },
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* High-entropy literal detection (catches random passwords nobody listed, e.g.
|
|
127
|
+
* the short random strings this repository used to keep in its blacklist).
|
|
128
|
+
* Only quoted single tokens are considered; hashes and ids are allowlisted.
|
|
129
|
+
*/
|
|
130
|
+
export const ENTROPY = {
|
|
131
|
+
minLength: 12,
|
|
132
|
+
minBitsPerChar: 3.5,
|
|
133
|
+
/**
|
|
134
|
+
* Structural requirement before entropy even applies: a random credential
|
|
135
|
+
* mixes letter case or digits, while ordinary code identifiers and copy
|
|
136
|
+
* strings are single-case words (`dbhub_configure`, `dbhub_search_objects`,
|
|
137
|
+
* `/plugins/dsh-dbhub-live/client.js`).
|
|
138
|
+
*/
|
|
139
|
+
requireMixed: true,
|
|
140
|
+
/** Tokens that are structurally not secrets. */
|
|
141
|
+
allowPatterns: [
|
|
142
|
+
/^[0-9a-fA-F]{16,}$/, // hex digest / sha fragment
|
|
143
|
+
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, // uuid
|
|
144
|
+
/^[A-Za-z0-9]+\.(mjs|js|json|md|yml|yaml|ts|tsx|png|tgz)$/, // file name
|
|
145
|
+
/^[a-z0-9]+(?:[-_][a-z0-9]+)+$/, // lowercase slug / identifier (kebab or snake)
|
|
146
|
+
/^Icon[A-Za-z0-9]*$/, // platform icon component name (IconCopyOutline16, IconChevronDownOutline14)
|
|
147
|
+
/^[A-Za-z0-9_.-]+=[^\s]*$/, // `KEY=value` fragment (env/config line)
|
|
148
|
+
/^[A-Za-z0-9_-]+:[A-Za-z0-9_./-]+$/, // `key: value` fragment
|
|
149
|
+
/^https?/, // url fragment
|
|
150
|
+
/^[A-Za-z0-9_./-]*[./][A-Za-z0-9_./-]*$/, // path-like token (contains . or /)
|
|
151
|
+
/^[A-Z][A-Z0-9_]{7,}$/, // SCREAMING_SNAKE env/const name
|
|
152
|
+
/^[A-Za-z]+$/, // a single word
|
|
153
|
+
],
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Inline escape hatch: a line carrying this marker is skipped. */
|
|
157
|
+
export const PRAGMA = 'desensitize:allow'
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Absolute local filesystem paths (a developer machine layout: drive-letter
|
|
161
|
+
* paths and POSIX home directories). The repository ships publicly, so no real
|
|
162
|
+
* machine path may appear in it. Applied as a SLOT rule rather than a backstop:
|
|
163
|
+
* test fixtures legitimately use synthetic drive paths (`C:\\ws\\good`).
|
|
164
|
+
*/
|
|
165
|
+
export const LOCAL_PATH_RE = /(?<![A-Za-z])[A-Za-z]:[\\/]|\/(?:Users|home)\/[A-Za-z0-9._-]+\//
|
|
166
|
+
|
|
167
|
+
/** Rules the checker understands. */
|
|
168
|
+
export const ALL_RULES = ['ip', 'secret', 'entropy', 'password', 'dsn', 'host', 'name', 'localpath']
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Backstop rules: applied to every scanned file, including files no slot
|
|
172
|
+
* registers yet — they are cheap and high-signal (IP literals, credential
|
|
173
|
+
* formats, entropy, internal hostnames, the local denylist of bare names).
|
|
174
|
+
*/
|
|
175
|
+
export const BACKSTOP_RULES = ['ip', 'secret', 'entropy', 'host', 'name']
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Registered slots. `globs` are repository-relative and must match at least one
|
|
179
|
+
* file. `rules` are the targeted rules applied inside the slot.
|
|
180
|
+
*/
|
|
181
|
+
export const SLOTS = [
|
|
182
|
+
{
|
|
183
|
+
id: 'host-copy',
|
|
184
|
+
description: '宿主侧模型可见文案:工具描述、i18n 结果与标签(会打进 npm 包、直接进模型上下文)',
|
|
185
|
+
globs: ['lib/i18n.mjs', 'lib/tools.mjs', 'lib/policy.mjs', 'lib/index.mjs', 'lib/mcp.mjs', 'lib/state.mjs', 'lib/bridge.mjs'],
|
|
186
|
+
rules: ['password', 'dsn', 'host', 'name'],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'client-copy',
|
|
190
|
+
description: '浏览器半区文案与 UI 占位符(设置页/插件页展示,会打进 npm 包)',
|
|
191
|
+
globs: ['lib/client.js'],
|
|
192
|
+
rules: ['password', 'dsn', 'host', 'name'],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 'connection-code',
|
|
196
|
+
description: '连接构造与解析代码:不得有硬编码 host/user/password 默认值',
|
|
197
|
+
globs: ['lib/config.mjs', 'lib/adhoc.mjs', 'lib/options.mjs', 'lib/runtime.mjs', 'lib/toml.mjs', 'lib/capability.mjs'],
|
|
198
|
+
rules: ['password', 'dsn', 'host', 'name'],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'scan-code',
|
|
202
|
+
description: '项目文件扫描器:只允许文件名模式,不得出现具体路径/库名',
|
|
203
|
+
globs: ['lib/collect.mjs'],
|
|
204
|
+
rules: ['password', 'host', 'name'],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: 'tests',
|
|
208
|
+
description: '测试夹具:DSN/IP/账号只能是回环或 RFC 5737 文档段 + 占位符',
|
|
209
|
+
globs: ['test/**/*.mjs'],
|
|
210
|
+
rules: ['password', 'dsn', 'host', 'name'],
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'docs',
|
|
214
|
+
description: '用户文档示例:安装、配置、故障排查里的连接示例(不得含真实机器路径)',
|
|
215
|
+
globs: ['README.md', 'README.en.md', 'doc/**/*.md'],
|
|
216
|
+
rules: ['password', 'dsn', 'host', 'name', 'localpath'],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'package-meta',
|
|
220
|
+
description: '包元数据与 bundle patch:description/keywords 不得含内部项目名或机器路径',
|
|
221
|
+
globs: ['package.json', 'cordis.patch.yml'],
|
|
222
|
+
rules: ['password', 'host', 'name', 'localpath'],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'agent-prompt',
|
|
226
|
+
description: '开发规范(提示词):只写规则与变量名,绝不写真实值或本机绝对路径',
|
|
227
|
+
globs: ['AGENTS.md'],
|
|
228
|
+
rules: ['password', 'dsn', 'host', 'name', 'localpath'],
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: 'tooling',
|
|
232
|
+
description: '仓库维护脚本自身(不含黑名单值;.env 由 .gitignore 排除、不参与扫描)',
|
|
233
|
+
globs: ['scripts/*.mjs'],
|
|
234
|
+
rules: ['password', 'host'],
|
|
235
|
+
},
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
/** The local denylist variable read from the gitignored `.env`. */
|
|
239
|
+
export const DENYLIST_VAR = 'DSH_DBHUB_DESENSITIZE_RE'
|
|
240
|
+
|
|
241
|
+
/** Paths the walk never enters and files it never reads. */
|
|
242
|
+
export const SKIP_DIRS = ['.git', 'node_modules', '.dsh-test', '.release-test', 'dist', 'coverage']
|
|
243
|
+
// The two local-dev documents are never committed and never published (see
|
|
244
|
+
// package.json → files), so the gate has no reason to scan them: they legitimately
|
|
245
|
+
// carry plan-era placeholders (`REAL_PASSWORD`, `host:port`) that are not shipped
|
|
246
|
+
// anywhere. The published REQUIREMENTS.md stays in scope. NOTE: this list is
|
|
247
|
+
// matched against the BASENAME (see the walker), so entries are file names.
|
|
248
|
+
export const SKIP_FILES = [
|
|
249
|
+
'.env',
|
|
250
|
+
'.env.local',
|
|
251
|
+
'PLAN-5.1-readonly-and-ssh-tunnel.md',
|
|
252
|
+
'implementation-notes.md',
|
|
253
|
+
]
|
|
254
|
+
export const SKIP_EXT_RE = /\.(tgz|png|jpe?g|gif|webp|ico|zip|gz|exe|dll|node|woff2?|pdf)$/i
|
|
255
|
+
export const MAX_FILE_BYTES = 2 * 1024 * 1024
|