From 5de5f88b0b39117ffd81b448b7dd3f0d25774b45 Mon Sep 17 00:00:00 2001 From: Ryder Sun Date: Sun, 8 Nov 2026 00:47:11 +0800 Subject: [PATCH] Track project-relative native credentials during verification Include relative configuration and certificate files from the captured native environment so changed credentials invalidate cached readiness. Verified with the main security and agent catalog test suites. --- desktop/src/main.cjs | 10 +++++++++- desktop/test/security/main-security.test.cjs | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/desktop/src/main.cjs b/desktop/src/main.cjs index e5c1e61..68f0c23 100644 --- a/desktop/src/main.cjs +++ b/desktop/src/main.cjs @@ -4,6 +4,7 @@ const fs = require('node:fs') const path = require('node:path') const { redactSecrets } = require('./security/secret-redaction.cjs') const { ensurePrivateDirectory } = require('./security/private-file.cjs') +const { projectVerificationIdentity } = require('./workspace/agent-verification-identity.cjs') const { NATIVE_CONFIG_DIRECTORIES, NATIVE_CONFIG_IDENTITY_DIRECTORY, NATIVE_ENVIRONMENT_IDENTITY, NATIVE_AUTH_IDENTITY_FILES, NATIVE_PROJECT_CONFIG_PATHS, nativeConfigEnvironment, nativeIdentityDirectory, nativeEnvironmentIdentity, nativeAuthIdentityFiles, nativeProjectConfigPaths, nativeNetworkIdentityFiles, validateNativeConfigDirectory } = require('./agents/native-config-directory.cjs') const { AttachmentStore } = require('./attachments/attachment-store.cjs') @@ -414,11 +415,18 @@ async function resolveProjectEnvironmentIdentities(workdir, kinds) { } const keys = nativeDeclaredCredentialKeys(kind, captured) const env = nativeConfigEnvironment(kind, workspace?.state.agentPreferences, captured.env) - result[kind] = nativeEnvironmentIdentity({ + const environmentIdentity = nativeEnvironmentIdentity({ ...(env.PATH ? { PATH: env.PATH } : {}), ...networkEnvironment(env), ...configDirectoryEnvironment(env), ...nativeCredentialEnvironment(kind, env, keys), }) + const projectPaths = nativeProjectConfigPaths(kind, env) + if (projectPaths.length) { + const agent = { [NATIVE_PROJECT_CONFIG_PATHS]: projectPaths } + result[kind] = projectVerificationIdentity(kind, workdir, agent, environmentIdentity) + } else { + result[kind] = environmentIdentity + } } return result } diff --git a/desktop/test/security/main-security.test.cjs b/desktop/test/security/main-security.test.cjs index 1399725..39bc9b1 100644 --- a/desktop/test/security/main-security.test.cjs +++ b/desktop/test/security/main-security.test.cjs @@ -908,6 +908,24 @@ test('native project identity changes when the CLI tool search path changes', as assert.doesNotMatch(JSON.stringify([first, second]), /synthetic|tool-bin/) }) +test('native project identity follows relative credential files captured for the project', async t => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-native-relative-identity-')) + t.after(() => fs.rmSync(directory, { recursive: true, force: true })) + const credentialFile = path.join(directory, 'credentials.json') + fs.writeFileSync(credentialFile, 'first synthetic credential') + const { harness } = loadMain(directory, { + nativeShellEnvironment: () => ({ source: 'native-shell', env: { GOOGLE_APPLICATION_CREDENTIALS: 'credentials.json' } }), + }) + await harness.ready() + const resolve = harness.workspaceInstances[0].input.resolveProjectEnvironmentIdentities + const first = await resolve(directory, ['hermes']) + fs.writeFileSync(credentialFile, 'replacement synthetic credential') + const second = await resolve(directory, ['hermes']) + assert.match(first.hermes, /^[a-f0-9]{64}$/) + assert.notEqual(first.hermes, second.hermes) + assert.doesNotMatch(JSON.stringify([first, second]), /credentials|synthetic/) +}) + test('Codex and Hermes project identities track only their own native credentials', async t => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-native-project-identities-')) t.after(() => fs.rmSync(directory, { recursive: true, force: true }))