mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(qqbot): avoid memory spikes from oversized client secret files (#110002)
* fix(qqbot): reject oversized client secret files * test(qqbot): derive oversized fixture from shared limit * test(qqbot): track credential fixtures for cleanup * fix(qqbot): keep tests inside plugin boundary --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
co-authored by
Peter Steinberger
parent
1e3b76f26b
commit
e1ca392dfb
@@ -1,7 +1,7 @@
|
||||
// Qqbot helper module supports config behavior.
|
||||
import fs from "node:fs";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
|
||||
import { coerceSecretRef, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
||||
import { getPlatformAdapter } from "../engine/adapter/index.js";
|
||||
import {
|
||||
@@ -135,8 +135,17 @@ export function resolveQQBotAccount(
|
||||
secretSource = "config";
|
||||
} else if (accountConfig.clientSecretFile) {
|
||||
try {
|
||||
clientSecret = fs.readFileSync(accountConfig.clientSecretFile, "utf8").trim();
|
||||
secretSource = "file";
|
||||
const fileSecret = tryReadSecretFileSync(
|
||||
accountConfig.clientSecretFile,
|
||||
"QQ Bot client secret",
|
||||
// Existing clientSecretFile paths may be symlinks or hardlinks. Keep
|
||||
// that contract while gaining the shared credential size limit.
|
||||
{ rejectHardlinks: false },
|
||||
);
|
||||
if (fileSecret) {
|
||||
clientSecret = fileSecret;
|
||||
secretSource = "file";
|
||||
}
|
||||
} catch {
|
||||
secretSource = "none";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Qqbot tests cover config plugin behavior.
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import {
|
||||
type JsonSchemaObject,
|
||||
validateJsonSchemaValue,
|
||||
} from "openclaw/plugin-sdk/json-schema-runtime";
|
||||
import { DEFAULT_SECRET_FILE_MAX_BYTES } from "openclaw/plugin-sdk/secret-file-runtime";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { qqbotSetupAdapterShared } from "./bridge/config-shared.js";
|
||||
import {
|
||||
@@ -240,6 +243,47 @@ describe("qqbot config", () => {
|
||||
expect(resolved.name).toBe("Bot Two");
|
||||
});
|
||||
|
||||
it("rejects oversized client secret files", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "qqbot-client-secret-"));
|
||||
try {
|
||||
const secretFile = path.join(tempDir, "secret");
|
||||
fs.writeFileSync(secretFile, "x".repeat(DEFAULT_SECRET_FILE_MAX_BYTES + 1));
|
||||
const resolved = resolveQQBotAccount({
|
||||
channels: { qqbot: { appId: "123456", clientSecretFile: secretFile } },
|
||||
} as OpenClawConfig);
|
||||
|
||||
expect(resolved.clientSecret).toBe("");
|
||||
expect(resolved.secretSource).toBe("none");
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32").each(["symlink", "hardlink"] as const)(
|
||||
"continues to resolve client secret files through a %s",
|
||||
(linkType) => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "qqbot-client-secret-link-"));
|
||||
try {
|
||||
const targetFile = path.join(tempDir, "secret-target");
|
||||
const linkedFile = path.join(tempDir, "secret-link");
|
||||
fs.writeFileSync(targetFile, " fixture-secret\n");
|
||||
if (linkType === "symlink") {
|
||||
fs.symlinkSync(targetFile, linkedFile);
|
||||
} else {
|
||||
fs.linkSync(targetFile, linkedFile);
|
||||
}
|
||||
const resolved = resolveQQBotAccount({
|
||||
channels: { qqbot: { appId: "123456", clientSecretFile: linkedFile } },
|
||||
} as OpenClawConfig);
|
||||
|
||||
expect(resolved.clientSecret).toBe("fixture-secret");
|
||||
expect(resolved.secretSource).toBe("file");
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("resolves env SecretRefs on runtime resolution", () => {
|
||||
const cfg = makeQqbotSecretRefConfig();
|
||||
const previous = process.env.QQBOT_CLIENT_SECRET;
|
||||
|
||||
Reference in New Issue
Block a user