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
30 changes: 18 additions & 12 deletions src/static/js/pad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,12 @@ const getParameters = [
},
{
// showMenuRight accepts 'true' or 'false'. Explicit 'false' hides the
// right-side toolbar (import/export/timeslider/settings/share/users);
// explicit 'true' forces it visible, overriding the readonly
// auto-hide applied further down (issue #5182). Any other value is
// a no-op — the menu stays in its default state.
// right-side toolbar (import/export/timeslider/settings/embed/home/
// users) — useful for iframe-embedded readonly pads where the menu
// is just chrome (issue #5182). Explicit 'true' forces the menu
// visible (a no-op against the default, kept for symmetry and for
// overriding any future caller-applied hide). Any other value is a
// no-op — the menu stays in its default state.
name: 'showMenuRight',
checkVal: null,
callback: (val) => {
Expand Down Expand Up @@ -787,14 +789,18 @@ const pad = {
$('#chaticon').hide();
$('#options-chatandusers').parent().hide();
$('#options-stickychat').parent().hide();
// Hide the right-side toolbar on readonly pads — import/export,
// timeslider, settings, share, users are all noise for viewers
// who can't interact with the pad. Callers who need those
// controls visible on a readonly pad can force them back via
// `?showMenuRight=true`, which runs in getParameters() above.
if (getUrlVars().get('showMenuRight') !== 'true') {
$('#editbar .menu_right').hide();
}
// The right-side toolbar stays visible on readonly pads. The
// server-side `toolbar.menu(buttons, isReadOnly)` (see
// src/node/utils/toolbar.ts) already strips `savedrevision`, and
// `.readonly .acl-write { display: none }` hides the Import column
// inside the import/export popup, so the remaining controls
// (export, timeslider, settings, embed, home, showusers) are all
// safe for readonly viewers — and the userlist is the surface that
// plugins like ep_guest hang their "Log In" button off, so hiding
// it traps guests in readonly with no way out. Iframe-embed use
// cases that want a clean look (issue #5182) opt in to the hide
// via `?showMenuRight=false`, or hide the whole editbar via
// `?showControls=false`.
Comment on lines 789 to +803
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Readonly shows menu_right toolbar 📎 Requirement gap ⛨ Security

The PR changes the default behavior so #editbar .menu_right remains visible on read-only pads.
This violates the requirement that read-only pads (especially iframe/embed) must not expose the
menu_right toolbar options.
Agent Prompt
## Issue description
The compliance requirement states that when a pad is opened in read-only mode (especially embedded via iframe), the `menu_right` toolbar must not be visible. The current PR explicitly keeps `#editbar .menu_right` visible on readonly pads by default, and the frontend test suite was updated to assert that visibility.

## Issue Context
To comply, readonly pads should hide `#editbar .menu_right` by default, while still allowing explicit overrides via the existing `showMenuRight` URL parameter.

## Fix Focus Areas
- src/static/js/pad.ts[785-803]
- src/tests/frontend-new/specs/hide_menu_right.spec.ts[47-67]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

} else if (!settings.hideChat) { $('#chaticon').show(); }

$('body').addClass(window.clientVars.readonly ? 'readonly' : 'readwrite');
Expand Down
17 changes: 16 additions & 1 deletion src/tests/frontend-new/specs/hide_menu_right.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,25 @@ test.describe('showMenuRight URL parameter', function () {
return url;
};

test('readonly pad hides .menu_right by default', async function ({page}) {
test('readonly pad shows .menu_right by default', async function ({page}) {
// The server-side toolbar.menu(..., isReadOnly) strips `savedrevision`
// and `.readonly .acl-write { display: none }` hides the Import
// column of the import/export popup, so the remaining controls
// (export, timeslider, settings, embed, home, showusers) are safe
// for readonly viewers — and ep_guest / other auth plugins depend
// on the userlist being reachable to surface their Log In button.
const readonlyUrl = await getReadonlyUrl(page);
await page.goto(readonlyUrl);
await page.waitForSelector('#editorcontainer.initialized');
await expect(page.locator('#editbar .menu_right')).toBeVisible();
});

test('readonly pad with showMenuRight=false hides the menu', async function ({page}) {
// Iframe-embed use case (issue #5182): the embedder opts in to the
// hide via the URL parameter when they want a clean look.
const readonlyUrl = await getReadonlyUrl(page);
await page.goto(`${readonlyUrl}?showMenuRight=false`);
await page.waitForSelector('#editorcontainer.initialized');
await expect(page.locator('#editbar .menu_right')).toBeHidden();
});

Expand Down
Loading