Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions clawnify-patches/whatsapp-clawnify-tools.patch
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,71 @@ index cbc2ea2ad4..40acbc7040 100644
const allowListRaw = normalizeStringEntries(params.allowFrom ?? []);
const hasWildcard = allowListRaw.includes("*");
const allowList = allowListRaw
diff --git a/extensions/whatsapp/src/runtime-group-policy.test.ts b/extensions/whatsapp/src/runtime-group-policy.test.ts
new file mode 100644
index 0000000000..d2fbe23dc0
--- /dev/null
+++ b/extensions/whatsapp/src/runtime-group-policy.test.ts
@@ -0,0 +1,34 @@
+import { describe, expect, it } from "vitest";
+import { resolveWhatsAppRuntimeGroupPolicy } from "./runtime-group-policy.js";
+
+describe("resolveWhatsAppRuntimeGroupPolicy (Clawnify fail-closed)", () => {
+ it("falls back to allowlist when channels.whatsapp exists without groupPolicy", () => {
+ // Regression: the open fallback admitted every group when groupPolicy was absent.
+ const result = resolveWhatsAppRuntimeGroupPolicy({ providerConfigPresent: true });
+ expect(result.groupPolicy).toBe("allowlist");
+ });
+
+ it("falls back to allowlist when channels.whatsapp is missing entirely", () => {
+ const result = resolveWhatsAppRuntimeGroupPolicy({ providerConfigPresent: false });
+ expect(result.groupPolicy).toBe("allowlist");
+ expect(result.providerMissingFallbackApplied).toBe(true);
+ });
+
+ it("honors an explicit groupPolicy", () => {
+ for (const groupPolicy of ["open", "allowlist", "disabled"] as const) {
+ const result = resolveWhatsAppRuntimeGroupPolicy({
+ providerConfigPresent: true,
+ groupPolicy,
+ });
+ expect(result.groupPolicy).toBe(groupPolicy);
+ }
+ });
+
+ it("honors channels.defaults.groupPolicy over the fallback", () => {
+ const result = resolveWhatsAppRuntimeGroupPolicy({
+ providerConfigPresent: true,
+ defaultGroupPolicy: "open",
+ });
+ expect(result.groupPolicy).toBe("open");
+ });
+});
diff --git a/extensions/whatsapp/src/runtime-group-policy.ts b/extensions/whatsapp/src/runtime-group-policy.ts
index 15ddee1266..116b4188c3 100644
--- a/extensions/whatsapp/src/runtime-group-policy.ts
+++ b/extensions/whatsapp/src/runtime-group-policy.ts
@@ -1,5 +1,5 @@
// Whatsapp plugin module implements runtime group policy behavior.
-import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
+import { resolveAllowlistProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";

export function resolveWhatsAppRuntimeGroupPolicy(params: {
providerConfigPresent: boolean;
@@ -9,7 +9,12 @@ export function resolveWhatsAppRuntimeGroupPolicy(params: {
groupPolicy: "open" | "allowlist" | "disabled";
providerMissingFallbackApplied: boolean;
} {
- return resolveOpenProviderRuntimeGroupPolicy({
+ // Fail closed: a configured channels.whatsapp without an explicit groupPolicy
+ // should not admit every group the linked number is a member of. Fall back to
+ // "allowlist" instead of "open" — group inbound stays blocked until groups are
+ // explicitly configured (groupAllowFrom senders still pass, and explicit
+ // groupPolicy values are honored unchanged).
+ return resolveAllowlistProviderRuntimeGroupPolicy({
providerConfigPresent: params.providerConfigPresent,
groupPolicy: params.groupPolicy,
defaultGroupPolicy: params.defaultGroupPolicy,
diff --git a/src/config/bundled-channel-config-metadata.generated.ts b/src/config/bundled-channel-config-metadata.generated.ts
index ed3a798ae6..b06632f96d 100644
--- a/src/config/bundled-channel-config-metadata.generated.ts
Expand Down
34 changes: 34 additions & 0 deletions extensions/whatsapp/src/runtime-group-policy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import { resolveWhatsAppRuntimeGroupPolicy } from "./runtime-group-policy.js";

describe("resolveWhatsAppRuntimeGroupPolicy (Clawnify fail-closed)", () => {
it("falls back to allowlist when channels.whatsapp exists without groupPolicy", () => {
// Regression: the open fallback admitted every group when groupPolicy was absent.
const result = resolveWhatsAppRuntimeGroupPolicy({ providerConfigPresent: true });
expect(result.groupPolicy).toBe("allowlist");
});

it("falls back to allowlist when channels.whatsapp is missing entirely", () => {
const result = resolveWhatsAppRuntimeGroupPolicy({ providerConfigPresent: false });
expect(result.groupPolicy).toBe("allowlist");
expect(result.providerMissingFallbackApplied).toBe(true);
});

it("honors an explicit groupPolicy", () => {
for (const groupPolicy of ["open", "allowlist", "disabled"] as const) {
const result = resolveWhatsAppRuntimeGroupPolicy({
providerConfigPresent: true,
groupPolicy,
});
expect(result.groupPolicy).toBe(groupPolicy);
}
});

it("honors channels.defaults.groupPolicy over the fallback", () => {
const result = resolveWhatsAppRuntimeGroupPolicy({
providerConfigPresent: true,
defaultGroupPolicy: "open",
});
expect(result.groupPolicy).toBe("open");
});
});
9 changes: 7 additions & 2 deletions extensions/whatsapp/src/runtime-group-policy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resolveOpenProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
import { resolveAllowlistProviderRuntimeGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";

export function resolveWhatsAppRuntimeGroupPolicy(params: {
providerConfigPresent: boolean;
Expand All @@ -8,7 +8,12 @@ export function resolveWhatsAppRuntimeGroupPolicy(params: {
groupPolicy: "open" | "allowlist" | "disabled";
providerMissingFallbackApplied: boolean;
} {
return resolveOpenProviderRuntimeGroupPolicy({
// Fail closed: a configured channels.whatsapp without an explicit groupPolicy
// should not admit every group the linked number is a member of. Fall back to
// "allowlist" instead of "open" — group inbound stays blocked until groups are
// explicitly configured (groupAllowFrom senders still pass, and explicit
// groupPolicy values are honored unchanged).
return resolveAllowlistProviderRuntimeGroupPolicy({
providerConfigPresent: params.providerConfigPresent,
groupPolicy: params.groupPolicy,
defaultGroupPolicy: params.defaultGroupPolicy,
Expand Down
Loading