mirror of
https://github.com/Ryder-Sun/Meldwork.git
synced 2026-11-07 06:56:03 -10:00
Redact MiMo native configuration credentials
Use native configuration layers to protect inline provider secrets in final and streaming output without adding them to the subprocess environment. Verified CLI, ACP and workspace tests and desktop packaging.
This commit is contained in:
1 parent
f2d4cf9c64
commit
bea31af3e3
3 files changed
+43
-1
No files matched your search
@@ -6,6 +6,7 @@ const { networkEnvironment } = require('./cli-network-environment.cjs')
|
||||
const { CONFIG_DIRECTORY_ENV_KEYS } = require('./cli-config-directories.cjs')
|
||||
const { SECRET_ENV_KEYS, nativeSecretEnvKeys } = require('./native-secret-environment.cjs')
|
||||
const { nativeCredentialEnvironment } = require('../agent-native-environment.cjs')
|
||||
const { mimoConfigSecrets } = require('./mimo-native-config.cjs')
|
||||
const { redactChildSecrets } = require('./cli-runtime-events.cjs')
|
||||
const {
|
||||
agentRuntimeError,
|
||||
@@ -221,6 +222,11 @@ function childEnvironment(agent, workdir, options, platform) {
|
||||
PATH: searchPath({ platform, env: source }),
|
||||
}
|
||||
Object.defineProperty(childEnv, SECRET_ENV_KEYS, { value: nativeKeys })
|
||||
if (agent.kind === 'mimo') {
|
||||
const home = platform === 'win32' ? (childEnv.USERPROFILE || childEnv.HOME) : childEnv.HOME
|
||||
const secrets = mimoConfigSecrets({ home, env: childEnv, workdir })
|
||||
Object.defineProperty(childEnv, Symbol.for('meldwork.project-secret-values'), { value: secrets })
|
||||
}
|
||||
if (agent.kind === 'claude' && typeof workdir === 'string' && path.isAbsolute(workdir)) {
|
||||
const secrets = new Set()
|
||||
const home = platform === 'win32' ? (childEnv.USERPROFILE || childEnv.HOME) : childEnv.HOME
|
||||
|
||||
@@ -155,4 +155,24 @@ function mimoConfiguredProvider(options = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { mimoConfigurationFingerprint, mimoConfiguredProvider, mimoDeclaredCredentialKeys }
|
||||
function mimoConfigSecrets(options = {}) {
|
||||
const configs = mimoConfigLayers(options)
|
||||
if (!configs) return []
|
||||
const secrets = new Set()
|
||||
const pending = configs.map(value => [value, 0])
|
||||
while (pending.length && secrets.size < 128) {
|
||||
const [value, depth] = pending.pop()
|
||||
if (!value || typeof value !== 'object' || depth > 32) continue
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
if (/(?:api[_-]?key|access[_-]?key|token|secret|password|authorization)$/i.test(key)
|
||||
&& typeof item === 'string' && item && item.length <= 8192 && !/^\{(?:env|file):[^}]+\}$/.test(item)) {
|
||||
secrets.add(item)
|
||||
const bearer = item.match(/^Bearer\s+(\S+)$/i)
|
||||
if (bearer) secrets.add(bearer[1])
|
||||
} else if (item && typeof item === 'object') pending.push([item, depth + 1])
|
||||
}
|
||||
}
|
||||
return [...secrets]
|
||||
}
|
||||
|
||||
module.exports = { mimoConfigurationFingerprint, mimoConfiguredProvider, mimoDeclaredCredentialKeys, mimoConfigSecrets }
|
||||
@@ -39,6 +39,22 @@ test('Claude project credentials are redacted from final and split streaming out
|
||||
assert.doesNotMatch(JSON.stringify(events), /project-private-value|local-private-value/)
|
||||
})
|
||||
|
||||
test('MiMo native provider credentials are redacted from split streaming output', t => {
|
||||
const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-mimo-project-secret-'))
|
||||
t.after(() => fs.rmSync(workdir, { recursive: true, force: true }))
|
||||
fs.writeFileSync(path.join(workdir, 'mimocode.json'), JSON.stringify({
|
||||
model: 'custom/model', provider: { custom: { options: { apiKey: 'mimo-private-value' } } },
|
||||
}))
|
||||
const child = childEnvironment({ kind: 'mimo' }, workdir, { env: { XDG_CONFIG_HOME: workdir } }, process.platform)
|
||||
assert.equal(redactChildSecrets('mimo-private-value', child), '[redacted]')
|
||||
const events = []
|
||||
const emitter = createRuntimeEventEmitter({ onEvent: event => events.push(event) }, child)
|
||||
emitter.emit({ type: 'answer_delta', delta: 'mimo-private-' })
|
||||
emitter.emit({ type: 'answer_delta', delta: 'value' })
|
||||
emitter.finalize({ text: 'mimo-private-value', status: 'completed' })
|
||||
assert.doesNotMatch(JSON.stringify(events), /mimo-private-value/)
|
||||
})
|
||||
|
||||
test('Zcode receives its selected native home without changing other Agent homes', () => {
|
||||
const options = { env: { HOME: '/selected-zcode', USERPROFILE: 'C:\\selected-zcode' } }
|
||||
const unix = childEnvironment({ kind: 'zcode' }, '/tmp', options, 'darwin')
|
||||
|
||||
Reference in new issue
Block a user