@mokoconsulting/mokomysql-mcp 2.5.4 → 2.6.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/LICENSE +696 -696
- package/README.md +122 -112
- package/config.example.json +27 -26
- package/dist/README.md +7 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/migrate.d.ts +57 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +224 -0
- package/dist/migrate.js.map +1 -0
- package/dist/secrets.d.ts.map +1 -1
- package/dist/secrets.js +8 -2
- package/dist/secrets.js.map +1 -1
- package/package.json +1 -1
- package/source/client.ts +210 -210
- package/source/config.ts +104 -97
- package/source/index.ts +1105 -1100
- package/source/migrate.ts +263 -0
- package/source/secrets.ts +78 -70
- package/source/state.ts +62 -62
- package/source/types.ts +91 -91
- package/tsconfig.json +19 -19
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
*
|
|
3
|
+
* This file is part of a Moko Consulting project.
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
|
+
*
|
|
7
|
+
* FILE INFORMATION
|
|
8
|
+
* DEFGROUP: mokomysql-mcp.Migrate
|
|
9
|
+
* INGROUP: mokomysql-mcp
|
|
10
|
+
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokomysql-mcp
|
|
11
|
+
* PATH: /source/migrate.ts
|
|
12
|
+
* BRIEF: Self-heal inline secrets in the MySQL MCP config into file-backed refs.
|
|
13
|
+
*
|
|
14
|
+
* No-inline-secrets policy (MokoCLI #1043). Any secret found INLINE in the
|
|
15
|
+
* on-disk `~/.mcp_mokomysql.json` is migrated to `~/.ssh/tokens/<slug>.token`
|
|
16
|
+
* and the config is rewritten so the field becomes `file:<path>`.
|
|
17
|
+
*
|
|
18
|
+
* mysql-specific: secret files are written as RAW values (NOT JSON). A MySQL
|
|
19
|
+
* password is an opaque string; `resolveRef()` reads the file verbatim, so a
|
|
20
|
+
* JSON wrapper would be read back literally and corrupt the credential. This
|
|
21
|
+
* intentionally diverges from the GitHub/OAuth JSON token-file shape used by
|
|
22
|
+
* token-based MCPs.
|
|
23
|
+
*/
|
|
24
|
+
import { constants as FS } from 'node:fs';
|
|
25
|
+
import { access, chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
26
|
+
import { homedir } from 'node:os';
|
|
27
|
+
import { resolve } from 'node:path';
|
|
28
|
+
/** Directory that holds file-backed secrets. */
|
|
29
|
+
function tokensDir() {
|
|
30
|
+
return resolve(homedir(), '.ssh', 'tokens');
|
|
31
|
+
}
|
|
32
|
+
/** True when the value is already a `file:<path>` reference (nothing to migrate). */
|
|
33
|
+
function isFileRef(value) {
|
|
34
|
+
return typeof value === 'string' && /^file:/.test(value.trim());
|
|
35
|
+
}
|
|
36
|
+
/** True when the value is a non-empty inline literal that must be migrated. */
|
|
37
|
+
function isInlineSecret(value) {
|
|
38
|
+
return typeof value === 'string' && value.trim() !== '' && !isFileRef(value);
|
|
39
|
+
}
|
|
40
|
+
/** Sanitize a slug component to the `[A-Za-z0-9._-]` alphabet. */
|
|
41
|
+
function sanitize(part) {
|
|
42
|
+
return part.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
43
|
+
}
|
|
44
|
+
/** stderr only — stdout is the MCP stdio channel and must never carry logs. */
|
|
45
|
+
function warn(message) {
|
|
46
|
+
process.stderr.write(`[mokomysql] WARNING: ${message}\n`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Enumerate every inline secret across all connections. The ONLY top-level
|
|
50
|
+
* connection secret in this config shape is `password`; an inline PEM private
|
|
51
|
+
* key under `ssl.key` is also a secret value (a key *value*, not a path) and is
|
|
52
|
+
* migrated too. A `file:` ref or empty value is skipped.
|
|
53
|
+
*/
|
|
54
|
+
function findInlineSecrets(connections) {
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const [key, raw] of Object.entries(connections)) {
|
|
57
|
+
if (!raw || typeof raw !== 'object')
|
|
58
|
+
continue;
|
|
59
|
+
const conn = raw;
|
|
60
|
+
const connSlug = sanitize(key) || 'connection';
|
|
61
|
+
if (isInlineSecret(conn.password)) {
|
|
62
|
+
out.push({
|
|
63
|
+
connectionKey: key,
|
|
64
|
+
label: `${key}.password`,
|
|
65
|
+
slug: `mysql-${connSlug}-password`,
|
|
66
|
+
value: conn.password,
|
|
67
|
+
apply: (ref) => {
|
|
68
|
+
conn.password = ref;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const ssl = conn.ssl;
|
|
73
|
+
if (ssl && typeof ssl === 'object') {
|
|
74
|
+
const sslRec = ssl;
|
|
75
|
+
// An inline PEM private-key *value* (begins with a PEM header) is a
|
|
76
|
+
// secret; a short reference/path is left alone.
|
|
77
|
+
if (isInlineSecret(sslRec.key) && /BEGIN[^\n]*PRIVATE KEY/.test(sslRec.key)) {
|
|
78
|
+
out.push({
|
|
79
|
+
connectionKey: key,
|
|
80
|
+
label: `${key}.ssl.key`,
|
|
81
|
+
slug: `mysql-${connSlug}-ssl-key`,
|
|
82
|
+
value: sslRec.key,
|
|
83
|
+
apply: (ref) => {
|
|
84
|
+
sslRec.key = ref;
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/** True if the path exists. */
|
|
93
|
+
async function exists(path) {
|
|
94
|
+
try {
|
|
95
|
+
await access(path, FS.F_OK);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Write the raw secret to `~/.ssh/tokens/<slug>.token` (mode 0600). If the file
|
|
104
|
+
* already exists it is NOT overwritten — the existing file is reused. Returns
|
|
105
|
+
* the absolute token path. mysql reads this file RAW, so the content is the
|
|
106
|
+
* verbatim secret with no trailing newline and no JSON wrapper.
|
|
107
|
+
*/
|
|
108
|
+
async function writeTokenFile(slug, secret) {
|
|
109
|
+
const dir = tokensDir();
|
|
110
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
111
|
+
// Best-effort tighten perms if the dir pre-existed with looser bits.
|
|
112
|
+
await chmod(dir, 0o700).catch(() => { });
|
|
113
|
+
const tokenPath = resolve(dir, `${slug}.token`);
|
|
114
|
+
if (await exists(tokenPath)) {
|
|
115
|
+
warn(`secret file already exists, reusing without overwrite: ${tokenPath}`);
|
|
116
|
+
return tokenPath;
|
|
117
|
+
}
|
|
118
|
+
// Atomic: write temp in the same dir, chmod, rename into place.
|
|
119
|
+
const tmp = `${tokenPath}.tmp-${process.pid}`;
|
|
120
|
+
await writeFile(tmp, secret, { encoding: 'utf-8', mode: 0o600 });
|
|
121
|
+
await chmod(tmp, 0o600).catch(() => { });
|
|
122
|
+
await rename(tmp, tokenPath);
|
|
123
|
+
return tokenPath;
|
|
124
|
+
}
|
|
125
|
+
/** Collapse an absolute path under $HOME back to a portable `~/...` form. */
|
|
126
|
+
function tildeify(absPath) {
|
|
127
|
+
const home = homedir();
|
|
128
|
+
if (absPath === home)
|
|
129
|
+
return '~';
|
|
130
|
+
const prefix = home.endsWith('/') || home.endsWith('\\') ? home : home + '/';
|
|
131
|
+
const prefixBack = home.endsWith('/') || home.endsWith('\\') ? home : home + '\\';
|
|
132
|
+
if (absPath.startsWith(prefix))
|
|
133
|
+
return '~/' + absPath.slice(prefix.length).replace(/\\/g, '/');
|
|
134
|
+
if (absPath.startsWith(prefixBack))
|
|
135
|
+
return '~/' + absPath.slice(prefixBack.length).replace(/\\/g, '/');
|
|
136
|
+
return absPath;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Atomically rewrite the config file with the migrated `file:` refs. Backs the
|
|
140
|
+
* original up to `<config>.bak` once (only if no `.bak` exists yet). The config
|
|
141
|
+
* is written mode 0600.
|
|
142
|
+
*/
|
|
143
|
+
async function rewriteConfig(configPath, parsed) {
|
|
144
|
+
const bak = `${configPath}.bak`;
|
|
145
|
+
if (!(await exists(bak))) {
|
|
146
|
+
const original = await readFile(configPath, 'utf-8');
|
|
147
|
+
await writeFile(bak, original, { encoding: 'utf-8', mode: 0o600 });
|
|
148
|
+
await chmod(bak, 0o600).catch(() => { });
|
|
149
|
+
}
|
|
150
|
+
const serialized = JSON.stringify(parsed, null, '\t') + '\n';
|
|
151
|
+
const tmp = `${configPath}.tmp-${process.pid}`;
|
|
152
|
+
await writeFile(tmp, serialized, { encoding: 'utf-8', mode: 0o600 });
|
|
153
|
+
await chmod(tmp, 0o600).catch(() => { });
|
|
154
|
+
await rename(tmp, configPath);
|
|
155
|
+
await chmod(configPath, 0o600).catch(() => { });
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Self-heal inline secrets in the parsed config, mutating `parsed` in place so
|
|
159
|
+
* the subsequent `resolveSecretRefs` pass picks up the new `file:` refs.
|
|
160
|
+
*
|
|
161
|
+
* Behavior (no-inline-secrets policy):
|
|
162
|
+
* - For each inline secret: warn to stderr (never echo the value), write a RAW
|
|
163
|
+
* token file under `~/.ssh/tokens/`, and rewrite the config so the field is a
|
|
164
|
+
* `file:` ref with the inline literal removed.
|
|
165
|
+
* - Existing token files are reused, never overwritten.
|
|
166
|
+
* - Config is backed up once to `<config>.bak`; writes are atomic; mode 0600.
|
|
167
|
+
* - On ANY error (read-only FS, perms, unwritable config) → warn only and keep
|
|
168
|
+
* the inline value for this run. Never throws, never loses the credential.
|
|
169
|
+
*
|
|
170
|
+
* Only runs against the real on-disk config path (the resolved
|
|
171
|
+
* `~/.mcp_mokomysql.json` or its `MYSQL_MCP_CONFIG` override).
|
|
172
|
+
*/
|
|
173
|
+
export async function migrateInlineSecrets(parsed, configPath) {
|
|
174
|
+
try {
|
|
175
|
+
if (!parsed.connections || typeof parsed.connections !== 'object')
|
|
176
|
+
return;
|
|
177
|
+
const inline = findInlineSecrets(parsed.connections);
|
|
178
|
+
if (inline.length === 0)
|
|
179
|
+
return;
|
|
180
|
+
// Only touch the config if it is the real on-disk file we can read.
|
|
181
|
+
if (!(await exists(configPath))) {
|
|
182
|
+
warn(`${inline.length} inline secret(s) found but config path is not a readable file ` +
|
|
183
|
+
`(${configPath}); leaving inline for this run.`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
let migratedAny = false;
|
|
187
|
+
for (const field of inline) {
|
|
188
|
+
try {
|
|
189
|
+
warn(`inline secret detected at ${field.label}; migrating to a file-backed secret ` +
|
|
190
|
+
`(no-inline-secrets policy, MokoCLI #1043). The value is NOT shown.`);
|
|
191
|
+
const tokenPath = await writeTokenFile(field.slug, field.value);
|
|
192
|
+
field.apply(`file:${tildeify(tokenPath)}`);
|
|
193
|
+
migratedAny = true;
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
197
|
+
warn(`failed to migrate ${field.label} to a file (${msg}); ` +
|
|
198
|
+
`keeping the inline value for this run.`);
|
|
199
|
+
// Revert any ref we may have set on this field so resolution uses the literal.
|
|
200
|
+
field.apply(field.value);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (migratedAny) {
|
|
204
|
+
try {
|
|
205
|
+
await rewriteConfig(configPath, parsed);
|
|
206
|
+
warn(`config rewritten with file-backed secret ref(s): ${configPath} ` +
|
|
207
|
+
`(backup at ${configPath}.bak).`);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
211
|
+
warn(`token file(s) written but rewriting the config failed (${msg}); ` +
|
|
212
|
+
`the config still references the secret value(s) inline. ` +
|
|
213
|
+
`The file-backed ref(s) are used for this run only.`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
// Absolute backstop — migration must never crash the server.
|
|
219
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
220
|
+
warn(`secret auto-migration aborted (${msg}); continuing with config as-is.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
export const __test = { sanitize, isInlineSecret, isFileRef, findInlineSecrets, tildeify };
|
|
224
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../source/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,SAAS,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAW,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,gDAAgD;AAChD,SAAS,SAAS;IACjB,OAAO,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED,qFAAqF;AACrF,SAAS,SAAS,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,+EAA+E;AAC/E,SAAS,cAAc,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,kEAAkE;AAClE,SAAS,QAAQ,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,+EAA+E;AAC/E,SAAS,IAAI,CAAC,OAAe;IAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,OAAO,IAAI,CAAC,CAAC;AAC3D,CAAC;AAiBD;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,WAAoC;IAC9D,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAS;QAC9C,MAAM,IAAI,GAAG,GAA8B,CAAC;QAC5C,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC;QAE/C,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,IAAI,CAAC;gBACR,aAAa,EAAE,GAAG;gBAClB,KAAK,EAAE,GAAG,GAAG,WAAW;gBACxB,IAAI,EAAE,SAAS,QAAQ,WAAW;gBAClC,KAAK,EAAE,IAAI,CAAC,QAAQ;gBACpB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;oBACd,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;gBACrB,CAAC;aACD,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,GAA8B,CAAC;YAC9C,oEAAoE;YACpE,gDAAgD;YAChD,IAAI,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7E,GAAG,CAAC,IAAI,CAAC;oBACR,aAAa,EAAE,GAAG;oBAClB,KAAK,EAAE,GAAG,GAAG,UAAU;oBACvB,IAAI,EAAE,SAAS,QAAQ,UAAU;oBACjC,KAAK,EAAE,MAAM,CAAC,GAAG;oBACjB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;wBACd,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;oBAClB,CAAC;iBACD,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,+BAA+B;AAC/B,KAAK,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,MAAc;IACzD,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnD,qEAAqE;IACrE,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAC;IAChD,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,0DAA0D,SAAS,EAAE,CAAC,CAAC;QAC5E,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,gEAAgE;IAChE,MAAM,GAAG,GAAG,GAAG,SAAS,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IAC9C,MAAM,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC7B,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,6EAA6E;AAC7E,SAAS,QAAQ,CAAC,OAAe;IAChC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;IAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC;IAClF,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/F,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvG,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,MAAe;IAC/D,MAAM,GAAG,GAAG,GAAG,UAAU,MAAM,CAAC;IAChC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7D,MAAM,GAAG,GAAG,GAAG,UAAU,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/C,MAAM,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC9B,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,MAAiD,EACjD,UAAkB;IAElB,IAAI,CAAC;QACJ,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;YAAE,OAAO;QAE1E,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEhC,oEAAoE;QACpE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YACjC,IAAI,CACH,GAAG,MAAM,CAAC,MAAM,iEAAiE;gBAChF,IAAI,UAAU,iCAAiC,CAChD,CAAC;YACF,OAAO;QACR,CAAC;QAED,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACJ,IAAI,CACH,6BAA6B,KAAK,CAAC,KAAK,sCAAsC;oBAC7E,oEAAoE,CACrE,CAAC;gBACF,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAChE,KAAK,CAAC,KAAK,CAAC,QAAQ,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC3C,WAAW,GAAG,IAAI,CAAC;YACpB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7D,IAAI,CACH,qBAAqB,KAAK,CAAC,KAAK,eAAe,GAAG,KAAK;oBACtD,wCAAwC,CACzC,CAAC;gBACF,+EAA+E;gBAC/E,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YACjB,IAAI,CAAC;gBACJ,MAAM,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBACxC,IAAI,CACH,oDAAoD,UAAU,GAAG;oBAChE,cAAc,UAAU,QAAQ,CACjC,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7D,IAAI,CACH,0DAA0D,GAAG,KAAK;oBACjE,0DAA0D;oBAC1D,oDAAoD,CACrD,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,6DAA6D;QAC7D,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,kCAAkC,GAAG,kCAAkC,CAAC,CAAC;IAC/E,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/secrets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../source/secrets.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../source/secrets.ts"],"names":[],"mappings":"AAwDA;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAenE"}
|
package/dist/secrets.js
CHANGED
|
@@ -29,21 +29,27 @@ function expandHome(p) {
|
|
|
29
29
|
* - anything else -> returned unchanged, so inline literals keep working.
|
|
30
30
|
*
|
|
31
31
|
* A missing/unreadable file throws (fail loud) — a secret must never silently
|
|
32
|
-
* degrade to the literal "file:..." string.
|
|
32
|
+
* degrade to the literal "file:..." string. An empty or whitespace-only file
|
|
33
|
+
* also throws, so a blank secret file never goes out as an empty value.
|
|
33
34
|
*/
|
|
34
35
|
async function resolveRef(value) {
|
|
35
36
|
const m = /^file:(.+)$/s.exec(value.trim());
|
|
36
37
|
if (!m)
|
|
37
38
|
return value;
|
|
38
39
|
const path = expandHome(m[1].trim());
|
|
40
|
+
let trimmed;
|
|
39
41
|
try {
|
|
40
42
|
const raw = await readFile(path, 'utf-8');
|
|
41
|
-
|
|
43
|
+
trimmed = raw.replace(/\r?\n+$/, '');
|
|
42
44
|
}
|
|
43
45
|
catch (err) {
|
|
44
46
|
const msg = err instanceof Error ? err.message : String(err);
|
|
45
47
|
throw new Error(`Failed to read secret file "${path}" (referenced as "${value}"): ${msg}`);
|
|
46
48
|
}
|
|
49
|
+
if (trimmed.trim() === '') {
|
|
50
|
+
throw new Error(`Secret file "${path}" (referenced as "${value}") is empty or whitespace-only; refusing to use an empty secret value.`);
|
|
51
|
+
}
|
|
52
|
+
return trimmed;
|
|
47
53
|
}
|
|
48
54
|
/**
|
|
49
55
|
* Recursively resolve every "file:" secret reference in a parsed config object,
|
package/dist/secrets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../source/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,mFAAmF;AACnF,SAAS,UAAU,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../source/secrets.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED