openhands / src /api /mcp-service /mcp-redacted-credentials.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
3d700dd verified
Raw History Blame Contribute Delete
9.3 kB
import { beforeEach, describe, expect, it, vi } from "vitest";
import SettingsService, {
type SettingsApiResponse,
} from "#/api/settings-service/settings-service.api";
import { substituteRedactedMcpCredentials } from "./mcp-redacted-credentials";
import { REDACTED_MCP_SECRET_VALUE } from "#/utils/mcp-config";
describe("substituteRedactedMcpCredentials", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("preserves encrypted stdio env when the server is renamed", async () => {
// Regression: renaming a stdio server left a redacted env value unchanged,
// so the lookup by the new display name missed the stored entry and the
// literal redaction placeholder overwrote the stored encrypted secret.
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
"old-name": {
command: "npx",
env: { API_KEY: "gAAAAA-encrypted-api-key" },
},
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "old-name",
type: "stdio",
name: "new-name",
command: "npx",
env: { API_KEY: REDACTED_MCP_SECRET_VALUE },
});
expect(result.env).toEqual({ API_KEY: "gAAAAA-encrypted-api-key" });
expect(result.name).toBe("new-name");
});
it("does not restore another server's secret when renamed onto an existing name", async () => {
// Renaming "alpha" onto "beta"'s name must still resolve alpha's stored
// entry by stable key, not beta's entry.
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
alpha: { command: "npx", env: { TOKEN: "gAAAAA-alpha-token" } },
beta: { command: "npx", env: { TOKEN: "gAAAAA-beta-token" } },
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "alpha",
type: "stdio",
name: "beta",
command: "npx",
env: { TOKEN: REDACTED_MCP_SECRET_VALUE },
});
expect(result.env).toEqual({ TOKEN: "gAAAAA-alpha-token" });
});
it("leaves typed (non-redacted) env values untouched", async () => {
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
"my-server": {
command: "npx",
env: { API_KEY: "gAAAAA-encrypted", REGION: "us-east-1" },
},
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "my-server",
type: "stdio",
name: "my-server",
command: "npx",
env: {
API_KEY: REDACTED_MCP_SECRET_VALUE,
REGION: "eu-west-1",
},
});
expect(result.env).toEqual({
API_KEY: "gAAAAA-encrypted",
REGION: "eu-west-1",
});
});
it("returns the server unchanged when no env value is redacted", async () => {
const server = {
id: "stdio-0",
type: "stdio" as const,
name: "my-server",
command: "npx",
env: { API_KEY: "plaintext" },
};
const result = await substituteRedactedMcpCredentials(server);
expect(result).toBe(server);
expect(SettingsService.fetchSettingsFromApi).not.toHaveBeenCalled();
});
it("keeps the placeholder when the stored stdio entry is missing", async () => {
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: { mcp_config: {} },
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "stdio-0",
type: "stdio",
name: "new-name",
command: "npx",
env: { API_KEY: REDACTED_MCP_SECRET_VALUE },
});
expect(result.env).toEqual({ API_KEY: REDACTED_MCP_SECRET_VALUE });
});
it("replaces redacted OAuth state with the encrypted stored subtree", async () => {
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
"superhuman-mail": {
url: "https://mcp.mail.superhuman.com/mcp",
auth: {
strategy: "oauth2",
state: {
tokens: {
access_token: "gAAAAA-encrypted-access-token",
refresh_token: "gAAAAA-encrypted-refresh-token",
},
client_info: {
client_id: "superhuman-client",
client_secret: "gAAAAA-encrypted-client-secret",
},
},
},
},
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "superhuman-mail",
type: "shttp",
name: "superhuman-mail",
url: "https://mcp.mail.superhuman.com/mcp",
auth: {
strategy: "oauth2",
state: {
tokens: {
access_token: REDACTED_MCP_SECRET_VALUE,
refresh_token: REDACTED_MCP_SECRET_VALUE,
},
client_info: {
client_id: "superhuman-client",
client_secret: REDACTED_MCP_SECRET_VALUE,
},
},
},
});
expect(result.auth).toEqual({
strategy: "oauth2",
state: {
tokens: {
access_token: "gAAAAA-encrypted-access-token",
refresh_token: "gAAAAA-encrypted-refresh-token",
},
client_info: {
client_id: "superhuman-client",
client_secret: "gAAAAA-encrypted-client-secret",
},
},
});
});
it("preserves fresh OAuth edits while substituting only redacted leaves", async () => {
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
"superhuman-mail": {
url: "https://mcp.mail.superhuman.com/mcp",
auth: {
strategy: "oauth2",
state: {
tokens: {
access_token: "gAAAAA-encrypted-access-token",
refresh_token: "gAAAAA-encrypted-refresh-token",
},
client_info: {
client_id: "old-client-id",
client_secret: "gAAAAA-encrypted-client-secret",
},
},
},
},
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "superhuman-mail",
type: "shttp",
name: "superhuman-mail",
url: "https://mcp.mail.superhuman.com/mcp",
auth: {
strategy: "oauth2",
state: {
tokens: {
access_token: REDACTED_MCP_SECRET_VALUE,
refresh_token: REDACTED_MCP_SECRET_VALUE,
},
client_info: {
client_id: "new-client-id",
client_secret: REDACTED_MCP_SECRET_VALUE,
},
},
},
});
expect(result.auth).toEqual({
strategy: "oauth2",
state: {
tokens: {
access_token: "gAAAAA-encrypted-access-token",
refresh_token: "gAAAAA-encrypted-refresh-token",
},
client_info: {
client_id: "new-client-id",
client_secret: "gAAAAA-encrypted-client-secret",
},
},
});
});
it("preserves fresh header-auth edits while substituting only redacted leaves", async () => {
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
mail: {
url: "https://mail.example/mcp",
auth: {
strategy: "header",
headers: {
"X-API-Key": "gAAAAA-encrypted-api-key",
"X-Region": "us-east-1",
},
},
},
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "mail",
type: "shttp",
name: "mail",
url: "https://mail.example/mcp",
auth: {
strategy: "header",
headers: {
"X-API-Key": REDACTED_MCP_SECRET_VALUE,
"X-Region": "eu-west-1",
},
},
});
expect(result.auth).toEqual({
strategy: "header",
headers: {
"X-API-Key": "gAAAAA-encrypted-api-key",
"X-Region": "eu-west-1",
},
});
});
it("replaces redacted raw remote headers with encrypted stored values", async () => {
vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({
agent_settings: {
mcp_config: {
github: {
url: "https://github.example/mcp",
headers: {
Authorization: "Bearer gAAAAA-encrypted-github-token",
},
},
},
},
} as unknown as SettingsApiResponse);
const result = await substituteRedactedMcpCredentials({
id: "github",
type: "shttp",
name: "github",
url: "https://github.example/mcp",
headers: { Authorization: REDACTED_MCP_SECRET_VALUE },
});
expect(result.headers).toEqual({
Authorization: "Bearer gAAAAA-encrypted-github-token",
});
});
});