mirror of
https://github.com/Ryder-Sun/Meldwork.git
synced 2026-11-07 06:56:03 -10:00
Match Qwen project trust defaults
Resolve project credentials with the same default and explicit denial behavior as Qwen CLI, while treating malformed trust configuration as unverified. Verified focused readiness and security tests and desktop packaging.
This commit is contained in:
1 parent
a7c3d3a84d
commit
335bbec0dc
2 files changed
+33
-5
No files matched your search
@@ -43,14 +43,21 @@ function qwenNativeSelection(options = {}) {
|
||||
const stat = fs.statSync(trustFile)
|
||||
const rules = stat.isFile() && stat.size <= 1024 * 1024 ? JSON5.parse(fs.readFileSync(trustFile, 'utf8')) : null
|
||||
const target = path.resolve(options.workdir)
|
||||
includeProject = Boolean(rules && typeof rules === 'object' && !Array.isArray(rules)
|
||||
&& Object.entries(rules).some(([folder, level]) => {
|
||||
if (!rules || typeof rules !== 'object' || Array.isArray(rules)) return { keys: [], unsupported: true }
|
||||
const entries = Object.entries(rules)
|
||||
const trusted = entries.some(([folder, level]) => {
|
||||
if (!path.isAbsolute(folder) || !['TRUST_FOLDER', 'TRUST_PARENT'].includes(level)) return false
|
||||
const root = path.resolve(level === 'TRUST_PARENT' ? path.dirname(folder) : folder)
|
||||
const relative = path.relative(root, target)
|
||||
return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative))
|
||||
}))
|
||||
} catch { includeProject = false }
|
||||
})
|
||||
const denied = entries.some(([folder, level]) => level === 'DO_NOT_TRUST'
|
||||
&& path.isAbsolute(folder) && path.normalize(folder) === path.normalize(target))
|
||||
includeProject = trusted || !denied
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') return { keys: [], unsupported: true }
|
||||
includeProject = true
|
||||
}
|
||||
}
|
||||
for (const entry of [defaults, user, ...(includeProject && project ? [project] : []), system]) {
|
||||
const settings = configs.get(entry)
|
||||
|
||||
@@ -3,7 +3,7 @@ const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
const { qwenDeclaredCredentialKeys } = require('../../../src/agents/cli/qwen-native-config.cjs')
|
||||
const { qwenDeclaredCredentialKeys, qwenCredentialSelection } = require('../../../src/agents/cli/qwen-native-config.cjs')
|
||||
const { nativeCredentialEnvironment } = require('../../../src/agents/agent-native-environment.cjs')
|
||||
const { childEnvironment } = require('../../../src/agents/cli/cli-process-support.cjs')
|
||||
const { redactChildSecrets } = require('../../../src/agents/cli/cli-runtime-events.cjs')
|
||||
@@ -79,11 +79,32 @@ test('Qwen does not read a project credential declaration when folder trust deni
|
||||
modelProviders: { openai: [{ id: 'chosen', envKey: 'PROJECT_TOKEN' }] },
|
||||
}))
|
||||
const env = { QWEN_CODE_SYSTEM_SETTINGS_PATH: path.join(home, 'missing.json') }
|
||||
fs.writeFileSync(path.join(home, '.qwen', 'trustedFolders.json'), JSON.stringify({ [workdir]: 'DO_NOT_TRUST' }))
|
||||
assert.deepEqual(qwenDeclaredCredentialKeys({ home, workdir, env }), [])
|
||||
fs.writeFileSync(path.join(home, '.qwen', 'trustedFolders.json'), JSON.stringify({ [workdir]: 'TRUST_FOLDER' }))
|
||||
assert.deepEqual(qwenDeclaredCredentialKeys({ home, workdir, env }), ['PROJECT_TOKEN'])
|
||||
})
|
||||
|
||||
test('Qwen follows native folder trust defaults and rejects malformed trust files', t => {
|
||||
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-qwen-default-trust-'))
|
||||
t.after(() => fs.rmSync(home, { recursive: true, force: true }))
|
||||
const workdir = path.join(home, 'project')
|
||||
fs.mkdirSync(path.join(home, '.qwen'))
|
||||
fs.mkdirSync(path.join(workdir, '.qwen'), { recursive: true })
|
||||
fs.writeFileSync(path.join(home, '.qwen', 'settings.json'), JSON.stringify({ security: { folderTrust: { enabled: true } } }))
|
||||
fs.writeFileSync(path.join(workdir, '.qwen', 'settings.json'), JSON.stringify({
|
||||
model: { name: 'chosen' }, security: { auth: { selectedType: 'openai' } },
|
||||
modelProviders: { openai: [{ id: 'chosen', envKey: 'PROJECT_TOKEN' }] },
|
||||
}))
|
||||
const env = { QWEN_CODE_SYSTEM_SETTINGS_PATH: path.join(home, 'missing.json') }
|
||||
const trustFile = path.join(home, '.qwen', 'trustedFolders.json')
|
||||
assert.deepEqual(qwenDeclaredCredentialKeys({ home, workdir, env }), ['PROJECT_TOKEN'])
|
||||
fs.writeFileSync(trustFile, JSON.stringify({ [workdir]: 'DO_NOT_TRUST' }))
|
||||
assert.deepEqual(qwenDeclaredCredentialKeys({ home, workdir, env }), [])
|
||||
fs.writeFileSync(trustFile, '{ malformed')
|
||||
assert.deepEqual(qwenCredentialSelection({ home, workdir, env }), { keys: [], unsupported: true })
|
||||
})
|
||||
|
||||
test('Qwen system configuration takes precedence and unsafe credential names are rejected', t => {
|
||||
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-qwen-env-precedence-'))
|
||||
t.after(() => fs.rmSync(home, { recursive: true, force: true }))
|
||||
|
||||
Reference in new issue
Block a user