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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hygraph/preview-sdk",
"version": "1.0.4",
"version": "1.0.5",
"description": "Content preview SDK for seamless real-time content editing in Hygraph CMS",
"publishConfig": {
"access": "public"
Expand Down
71 changes: 67 additions & 4 deletions src/core/MessageBridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('MessageBridge', () => {
expect(bridge.isConnectedToStudio()).toBe(true);
});

it('broadcasts ready message to all allowed origins', () => {
it('sends ready message to any parent window using wildcard origin', () => {
bridge = new MessageBridge({
allowedOrigins,
debug: false,
Expand All @@ -69,9 +69,9 @@ describe('MessageBridge', () => {

bridge.sendReadyMessage(readyMessage);

expect(mockBridge.postMessage).toHaveBeenCalledTimes(allowedOrigins.length);
const targets = mockBridge.messages.map(({ targetOrigin }) => targetOrigin);
expect(targets).toEqual(expect.arrayContaining(allowedOrigins));
// Should send once with wildcard origin to reach any parent
expect(mockBridge.postMessage).toHaveBeenCalledTimes(1);
expect(mockBridge.postMessage).toHaveBeenCalledWith(readyMessage, '*');
});

it('ignores messages from disallowed origins', () => {
Expand Down Expand Up @@ -120,5 +120,68 @@ describe('MessageBridge', () => {

expect(onMessage).toHaveBeenCalledWith(validMessage);
});

it('accepts messages from origins matching wildcard patterns', () => {
bridge = new MessageBridge({
allowedOrigins: ['https://*.hygraph.com', 'http://localhost:*'],
debug: false,
onMessage,
});

// Test wildcard domain pattern
mockBridge.dispatch(
{
type: 'init',
studioOrigin: 'https://app.hygraph.com',
timestamp: Date.now(),
},
'https://app.hygraph.com'
);

expect(onMessage).toHaveBeenCalled();
expect(bridge.isConnectedToStudio()).toBe(true);
onMessage.mockClear();

// Test wildcard port pattern
const bridge2 = new MessageBridge({
allowedOrigins: ['http://localhost:*'],
debug: false,
onMessage,
});

mockBridge.dispatch(
{
type: 'init',
studioOrigin: 'http://localhost:3000',
timestamp: Date.now(),
},
'http://localhost:3000'
);

expect(onMessage).toHaveBeenCalled();
expect(bridge2.isConnectedToStudio()).toBe(true);

bridge2.destroy();
});

it('rejects messages from origins not matching wildcard patterns', () => {
bridge = new MessageBridge({
allowedOrigins: ['https://*.hygraph.com'],
debug: false,
onMessage,
});

mockBridge.dispatch(
{
type: 'init',
studioOrigin: 'https://malicious.example.com',
timestamp: Date.now(),
},
'https://malicious.example.com'
);

expect(onMessage).not.toHaveBeenCalled();
expect(bridge.isConnectedToStudio()).toBe(false);
});
});

30 changes: 12 additions & 18 deletions src/core/MessageBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,20 @@ export class MessageBridge {
sendReadyMessage(message: SDKMessage & { type: 'ready' }): boolean {
if (this.isDestroyed) return false;

let sentSuccessfully = false;

// Try sending to each allowed origin
for (const origin of this.config.allowedOrigins) {
try {
window.parent.postMessage(message, origin);

if (this.config.debug) {
console.log('[MessageBridge] Sent ready message to origin:', origin, 'message:', message);
}

sentSuccessfully = true;
} catch (error) {
if (this.config.debug) {
console.log('[MessageBridge] Failed to send ready message to origin:', origin, 'error:', error);
}
try {
// Use "*" as targetOrigin to send to any parent window
// Security is maintained by validating incoming messages via isOriginAllowed()
window.parent.postMessage(message, '*');

if (this.config.debug) {
console.log('[MessageBridge] Sent ready message to parent window, message:', message);
}
}

return sentSuccessfully;
return true;
} catch (error) {
console.error('[MessageBridge] Failed to send ready message to origin: * error:', error);
return false;
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/core/Preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class Preview {
* Get current SDK version
*/
getVersion(): string {
return '2.0.0';
return '1.0.5';
}

/**
Expand Down