Is Web Maker safe?

Medium risk

Web Maker syncs user code to Firebase and a multiplayer backend, and sends behavioral telemetry to a Google Cloud Run endpoint.

When a user is logged in, Web Maker automatically saves their HTML, CSS, and JavaScript workspace to Google Firestore under the project 'web-maker-app'. During multiplayer collaboration sessions, the full editor content is streamed keystroke-by-keystroke over a WebSocket to a Railway-hosted backend. Separately, the extension tracks approximately 80 categories of user interactions—including settings changes, keyboard shortcuts, and login events—and forwards them as JSON to a Google Cloud Run endpoint.

Kushagra Gourv6.9.0Chrome Web Store
48Risk
Who publishes it

Kushagra Gour - no other listings under this identity, 6 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Kushagra Gour
Registered address
Greater Noida, Greater Noida, Uttar Pradesh 201310, India
Registered contact
kushagra gour

Shared hosts - 6 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

acss.io
Also called by 1 other listing: ZenUML Sequence
cdn77.com
Also called by 1 other listing: ZenUML Sequence
straker.github.io
Also called by 1 other listing: ZenUML Sequence
maxcdn.com
Also called by 2 other listings: ZenUML Sequence, Smartling Context Capture
openmobilealliance.org
Also called by 3 other listings
wzrd.in
Also called by 5 other listings, including 中古車の価格変動が丸わかり - 中古車チェッカー, ショッピングリサーチャー

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Code syncs to WebSocket during collaboration

Starting or joining a Web Maker multiplayer session places the editor's HTML, CSS, and JS into a Yjs doc connected to web-maker-backend-production.up.railway.app over WebSocket.

Edits are sent as binary Yjs update frames for that room.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-319
Source
Dynamic sandbox
What actually happens
You did this

You start a multiplayer collaboration session or open a shared collaboration link.

The extension did this

The extension connects your editor document to a remote WebSocket room and syncs the project code for that session.

Fields placed into the collaboration document
  • Editor HTML
    <main class="demo"><button id="save">Save</button></main>

    Your page structure is included in the collaboration room when the session starts.

  • Editor CSS
    .demo { display: grid; gap: 12px; color: #243b53; }

    Your styling rules are included alongside the HTML so collaborators can see and edit them.

  • Editor JavaScript
    document.querySelector('#save').addEventListener('click', saveDraft);

    Your script content is part of the shared room and can include logic you are actively developing.

  • Room identifier
    A7kP2zQ9

    The room value separates one collaboration session from another and is included in the WebSocket path.

  • Collaboration display identity
    Swift Panda / #1E88E5

    The session broadcasts a generated display name and color for cursor presence.

Captured request
GETwss://web-maker-backend-production.up.railway.app/A7kP2zQ9

WebSocket upgrade; subsequent messages are binary Yjs sync, awareness, and update frames.

The code that does this

The shipped bundle creates a WebSocket-backed Yjs room from the current project code

Readable version

Readable production endpoint constant

constants.js
// Multiplayer collaboration server URL// For development: npm run start:collab (runs on ws://localhost:4444)// For production: Deploy collab-server/ to Railway and update the URL belowexport const COLLAB_SERVER_URL =	window.location.hostname === 'localhost'		? 'ws://localhost:4444'		: 'wss://web-maker-backend-production.up.railway.app';

Readable createSession function from the source map

multiplayerService.js
export function createSession(initialContent = { html: '', css: '', js: '' }) {	const roomId = generateRoomId();	const ydoc = new Y.Doc();	// Initialize Y.Text instances with current content	const yHtml = ydoc.getText('html');	const yCss = ydoc.getText('css');	const yJs = ydoc.getText('js');	// Insert initial content into Y.Text	if (initialContent.html) {		yHtml.insert(0, initialContent.html);	}	if (initialContent.css) {		yCss.insert(0, initialContent.css);	}	if (initialContent.js) {		yJs.insert(0, initialContent.js);	}	// Connect to WebSocket server	const provider = new WebsocketProvider(COLLAB_SERVER_URL, roomId, ydoc);	// Set up local user awareness	const userIdentity = generateUserIdentity();	provider.awareness.setLocalStateField('user', userIdentity);	// Update URL with room ID	addRoomIdToUrl(roomId);	return {		ydoc,		provider,		awareness: provider.awareness,		roomId,		url: generateSessionUrl(roomId),		userIdentity,		isHost: true	};}
The code that does this

The editors are bound to Yjs text objects, so edits become sync updates

Readable version

Readable ContentWrap editor bindings

components/ContentWrap.jsx
yText={	this.props.multiplayerSession?.ydoc?.getText('html') ||	null}awareness={this.props.multiplayerSession?.awareness || null}yText={	this.props.multiplayerSession?.ydoc?.getText('css') ||	null}awareness={this.props.multiplayerSession?.awareness || null}yText={	this.props.multiplayerSession?.ydoc?.getText('js') || null}awareness={this.props.multiplayerSession?.awareness || null}

Readable CodeEditor Yjs binding setup

components/CodeEditor.jsx
async setupYjsBinding(yText, awareness) {	// Destroy existing binding first	this.destroyYjsBinding();	if (!yText || !awareness) {		return;	}	await this.editorReadyDeferred.promise;	if (this.props.type === 'monaco') {		// Dynamically load y-monaco if not loaded		if (!MonacoBinding) {			const yMonacoModule = await import('y-monaco');			MonacoBinding = yMonacoModule.MonacoBinding;		}		this.yjsBinding = new MonacoBinding(			yText,			this.instance.getModel(),			new Set([this.instance]),			awareness		);	} else {		// CodeMirror 5 binding		this.yjsBinding = new CodemirrorBinding(yText, this.instance, awareness);	}}

Readable y-websocket update handler

node_modules/y-websocket/src/y-websocket.js
this._updateHandler = (update, origin) => {  if (origin !== this) {    const encoder = encoding.createEncoder()    encoding.writeVarUint(encoder, messageSync)    syncProtocol.writeUpdate(encoder, update)    broadcastMessage(this, encoding.toUint8Array(encoder))  }}this.doc.on('update', this._updateHandler)
How session actions map to data movement
  1. At session start

    You did

    You start a new collaboration room.

    The extension did

    The extension creates an 8-character room ID and inserts the current HTML, CSS, and JavaScript into the shared document.

  2. During editing

    You did

    You type in the HTML, CSS, or JavaScript editor while the session is active.

    The extension did

    The Yjs binding updates the shared document, and the WebSocket provider sends a binary update for the room.

  3. On share

    You did

    You copy or share the collaboration link.

    The extension did

    The link includes the room parameter so another browser can join the same shared document.

External service used for collaboration sync
    • web-maker-backend-production.up.railway.app

    Railway-hosted WebSocket endpoint that receives the room connection and binary Yjs collaboration updates.

What it can do

Permissions this extension asks for, as declared in version 6.9.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

  • Run hidden pages in the background

    offscreen

Where it sends data

Destinations our analysis observed Web Maker contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • firestore.googleapis.com

    Web Maker sends data to firestore.googleapis.com. 16 other extensions we have analysed send data here.

  • event-ajhkrtmkaq-uc.a.run.app

    Web Maker sends data to event-ajhkrtmkaq-uc.a.run.app. No other extension we have analysed sends data here.

  • web-maker-backend-production.up.railway.app

    Web Maker sends data to web-maker-backend-production.up.railway.app. No other extension we have analysed sends data here.

Updated 30 September 2026lkfkkhfhhdkiemehlpkgjeojomhpccnh