





















@@ -15,6 +15,7 @@ import {
1515hasAvailableAuthForProvider,
1616resolveApiKeyForProvider,
1717resolveEnvApiKey,
18+resolveModelAuthMode,
1819} from "./model-auth.js";
19202021async function expectVertexAdcEnvApiKey(params: {
@@ -90,6 +91,17 @@ vi.mock("./provider-auth-aliases.js", () => ({
9091}));
91929293vi.mock("./model-auth-env-vars.js", () => {
94+const hasAllowedPlugin = (config: unknown, pluginId: string): boolean => {
95+if (!config || typeof config !== "object") {
96+return false;
97+}
98+const plugins = (config as { plugins?: unknown }).plugins;
99+if (!plugins || typeof plugins !== "object") {
100+return false;
101+}
102+const allow = (plugins as { allow?: unknown }).allow;
103+return Array.isArray(allow) && allow.includes(pluginId);
104+};
93105const candidates = {
94106anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
95107google: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
@@ -110,19 +122,35 @@ vi.mock("./model-auth-env-vars.js", () => {
110122PROVIDER_ENV_API_KEY_CANDIDATES: candidates,
111123listKnownProviderEnvApiKeyNames: () => [...new Set(Object.values(candidates).flat())],
112124resolveProviderEnvApiKeyCandidates: () => candidates,
113-resolveProviderEnvAuthEvidence: () => ({
114-"google-vertex": [
115-{
116-type: "local-file-with-env",
117-fileEnvVar: "GOOGLE_APPLICATION_CREDENTIALS",
118-fallbackPaths: ["${HOME}/.config/gcloud/application_default_credentials.json"],
119-requiresAnyEnv: ["GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT"],
120-requiresAllEnv: ["GOOGLE_CLOUD_LOCATION"],
121-credentialMarker: "gcp-vertex-credentials",
122-source: "gcloud adc",
123-},
124-],
125-}),
125+resolveProviderEnvAuthEvidence: (params?: { config?: OpenClawConfig }) => {
126+const evidence = {
127+"google-vertex": [
128+{
129+type: "local-file-with-env",
130+fileEnvVar: "GOOGLE_APPLICATION_CREDENTIALS",
131+fallbackPaths: ["${HOME}/.config/gcloud/application_default_credentials.json"],
132+requiresAnyEnv: ["GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT"],
133+requiresAllEnv: ["GOOGLE_CLOUD_LOCATION"],
134+credentialMarker: "gcp-vertex-credentials",
135+source: "gcloud adc",
136+},
137+],
138+} satisfies Record<string, readonly unknown[]>;
139+if (!hasAllowedPlugin(params?.config, "workspace-cloud")) {
140+return evidence;
141+}
142+return {
143+ ...evidence,
144+"workspace-cloud": [
145+{
146+type: "local-file-with-env",
147+fileEnvVar: "WORKSPACE_CLOUD_CREDENTIALS",
148+credentialMarker: "workspace-cloud-local-credentials",
149+source: "workspace cloud credentials",
150+},
151+],
152+};
153+},
126154};
127155});
128156@@ -436,6 +464,67 @@ describe("getApiKeyForModel", () => {
436464});
437465});
438466467+it("uses trusted workspace manifest auth evidence in runtime auth checks", async () => {
468+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-workspace-cloud-auth-"));
469+const credentialsPath = path.join(tempDir, "credentials.json");
470+await fs.writeFile(credentialsPath, "{}", "utf8");
471+472+const cfg: OpenClawConfig = {
473+plugins: {
474+allow: ["workspace-cloud"],
475+},
476+};
477+478+try {
479+await withEnvAsync({ WORKSPACE_CLOUD_CREDENTIALS: credentialsPath }, async () => {
480+const store = { version: 1 as const, profiles: {} };
481+const resolved = await resolveApiKeyForProvider({
482+provider: "workspace-cloud",
483+ cfg,
484+ store,
485+});
486+487+expect(resolved).toEqual({
488+apiKey: "workspace-cloud-local-credentials",
489+source: "workspace cloud credentials",
490+mode: "api-key",
491+});
492+expect(resolveModelAuthMode("workspace-cloud", cfg, store)).toBe("api-key");
493+await expect(
494+hasAvailableAuthForProvider({
495+provider: "workspace-cloud",
496+ cfg,
497+ store,
498+}),
499+).resolves.toBe(true);
500+});
501+} finally {
502+await fs.rm(tempDir, { recursive: true, force: true });
503+}
504+});
505+506+it("ignores untrusted workspace manifest auth evidence in runtime auth checks", async () => {
507+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-workspace-cloud-auth-"));
508+const credentialsPath = path.join(tempDir, "credentials.json");
509+await fs.writeFile(credentialsPath, "{}", "utf8");
510+511+try {
512+await withEnvAsync({ WORKSPACE_CLOUD_CREDENTIALS: credentialsPath }, async () => {
513+const store = { version: 1 as const, profiles: {} };
514+expect(resolveModelAuthMode("workspace-cloud", { plugins: {} }, store)).toBe("unknown");
515+await expect(
516+hasAvailableAuthForProvider({
517+provider: "workspace-cloud",
518+cfg: { plugins: {} },
519+ store,
520+}),
521+).resolves.toBe(false);
522+});
523+} finally {
524+await fs.rm(tempDir, { recursive: true, force: true });
525+}
526+});
527+439528it("hasAvailableAuthForProvider('google') accepts GOOGLE_API_KEY fallback", async () => {
440529await withEnvAsync(
441530{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。