fix(bluebubbles): refresh client cache on network policy changes

This commit is contained in:
Peter Steinberger
2026-04-20 13:09:35 +01:00
parent 20c88ef5db
commit 9429b0976a
2 changed files with 35 additions and 5 deletions

View File

@@ -581,6 +581,26 @@ describe("client cache", () => {
});
expect(a).not.toBe(b);
});
it("private-network config changes rebuild the client without explicit invalidation", () => {
const cfg = {
channels: {
bluebubbles: {
serverUrl: "http://192.168.1.50:1234",
password: "s3cret",
network: { dangerouslyAllowPrivateNetwork: true },
},
},
};
const allowed = createBlueBubblesClient({ cfg: cfg as never });
expect(allowed.getSsrfPolicy()).toEqual({ allowPrivateNetwork: true });
cfg.channels.bluebubbles.network.dangerouslyAllowPrivateNetwork = false;
const denied = createBlueBubblesClient({ cfg: cfg as never });
expect(denied).not.toBe(allowed);
expect(denied.getSsrfPolicy()).toEqual({});
});
});
describe("client construction", () => {

View File

@@ -458,7 +458,7 @@ export class BlueBubblesClient {
type CachedClientEntry = {
client: BlueBubblesClient;
/** Fingerprint of {baseUrl, password, authStrategy.id} — cache hit requires full match. */
/** Fingerprint of auth + SSRF-policy inputs — cache hit requires full match. */
fingerprint: string;
};
const clientFingerprints = new Map<string, CachedClientEntry>();
@@ -467,11 +467,19 @@ function buildClientFingerprint(params: {
baseUrl: string;
password: string;
authStrategyId: string;
allowPrivateNetwork: boolean;
allowPrivateNetworkConfig?: boolean;
}): string {
// authStrategyId is included so two clients for the same account + credential
// that differ only in auth strategy do not silently share a cached instance.
// (Greptile #68234 P2)
return `${params.baseUrl}|${params.password}|${params.authStrategyId}`;
// Keep every construction-time behavior input here. The client stores auth
// and SSRF policy immutably, so config flips must rebuild without requiring
// a process restart or an explicit cache invalidation call.
return JSON.stringify({
baseUrl: params.baseUrl,
password: params.password,
authStrategyId: params.authStrategyId,
allowPrivateNetwork: params.allowPrivateNetwork,
allowPrivateNetworkConfig: params.allowPrivateNetworkConfig ?? null,
});
}
/**
@@ -495,6 +503,8 @@ export function createBlueBubblesClient(opts: BlueBubblesClientOptions = {}): Bl
baseUrl: resolved.baseUrl,
password: resolved.password,
authStrategyId: authStrategy.id,
allowPrivateNetwork: resolved.allowPrivateNetwork,
allowPrivateNetworkConfig: resolved.allowPrivateNetworkConfig,
});
const cached = clientFingerprints.get(cacheKey);
if (cached && cached.fingerprint === fingerprint) {