Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/plugins/utils/pluginSettingsValidation.test.ts
T

71 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
PLUGIN_SETTING_ARRAY_MAX_LENGTH,
PLUGIN_SETTING_KEY_MAX_LENGTH,
PLUGIN_SETTING_OBJECT_MAX_KEYS,
PLUGIN_SETTING_VALUE_MAX_BYTES,
isPluginSettingKeyFormatSafe,
isPluginSettingKeyReserved,
isPluginSettingKeySecretLike,
isPluginSettingValue,
isPluginSettingValueShapeSafe,
isPluginSettingValueWithinSize,
isSafePluginSettingKey,
} from "./pluginSettingsValidation";
describe("plugin settings validation", () => {
it("keeps frame and persistence setting keys on the same safe-key contract", () => {
expect(isSafePluginSettingKey("theme.accent:color-1")).toBe(true);
expect(isPluginSettingKeyFormatSafe("k".repeat(PLUGIN_SETTING_KEY_MAX_LENGTH))).toBe(true);
expect(isPluginSettingKeyFormatSafe("k".repeat(PLUGIN_SETTING_KEY_MAX_LENGTH + 1))).toBe(false);
for (const key of [
"../theme",
"__proto__",
"constructor",
"prototype",
"apiKey",
"access-token",
"password",
"secret.value",
"authorization",
"credentials",
"auth.header",
"bearer",
"cookie",
"sessionId",
"private_key",
"clientSecret",
"webhookUrl",
]) {
expect(isSafePluginSettingKey(key)).toBe(false);
}
expect(isPluginSettingKeyReserved("__proto__")).toBe(true);
expect(isPluginSettingKeySecretLike("refresh-token")).toBe(true);
expect(isPluginSettingKeySecretLike("authorization")).toBe(true);
expect(isPluginSettingKeySecretLike("clientSecret")).toBe(true);
});
it("rejects values that cannot cross the iframe-to-persistence boundary safely", () => {
expect(isPluginSettingValue({ theme: "dark", flags: [true, false], count: 2 })).toBe(true);
expect(isPluginSettingValue(Number.NaN)).toBe(false);
expect(isPluginSettingValue(Array.from({ length: PLUGIN_SETTING_ARRAY_MAX_LENGTH + 1 }, () => true))).toBe(false);
expect(isPluginSettingValue(Object.fromEntries(Array.from({ length: PLUGIN_SETTING_OBJECT_MAX_KEYS + 1 }, (_, index) => [`key${index}`, true])))).toBe(
false
);
expect(isPluginSettingValue(JSON.parse('{"theme":"dark","__proto__":{"polluted":true}}'))).toBe(false);
expect(isPluginSettingValue({ theme: { accessToken: "must-not-persist" } })).toBe(false);
expect(isPluginSettingValue({ request: { authorization: "Bearer must-not-persist" } })).toBe(false);
expect(isPluginSettingValue({ endpoint: { webhookUrl: "https://hooks.example.test/secret" } })).toBe(false);
});
it("separates JSON shape errors from serialized size errors for server error mapping", () => {
const oversized = "x".repeat(PLUGIN_SETTING_VALUE_MAX_BYTES);
expect(isPluginSettingValueShapeSafe(oversized)).toBe(true);
expect(isPluginSettingValueWithinSize(oversized)).toBe(false);
expect(isPluginSettingValue(oversized)).toBe(false);
});
});