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 admin/src/pages/PadPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export const PadPage = () => {
</button>
<button
className="pm-btn pm-btn-primary pm-btn--sm"
onClick={() => window.open(`../../p/${pad.padName}`, '_blank')}
onClick={() => window.open(`../../p/${encodeURIComponent(pad.padName)}`, '_blank', 'noopener,noreferrer')}
>
<Eye size={13}/> <Trans i18nKey="admin_pads.open"/>
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/static/js/pad_userlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ const paduserlist = (() => {
if (localStorage.getItem('recentPads') != null) {
const recentPadsList = JSON.parse(localStorage.getItem('recentPads'));
const pathSegments = window.location.pathname.split('/');
const padName = pathSegments[pathSegments.length - 1];
const padName = decodeURIComponent(pathSegments[pathSegments.length - 1]);
const existingPad = recentPadsList.find((pad) => pad.name === padName);
if (existingPad) {
existingPad.members = online;
Expand Down
13 changes: 11 additions & 2 deletions src/static/skins/colibris/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,21 @@ window.customStart = () => {
li.style.cursor = 'pointer';

li.className = 'recent-pad';
const padPath = `${window.location.href}p/${pad.name}`;
// Normalize the stored name to its decoded form. Entries saved by older
// versions may already be URL-encoded; decoding first avoids
// double-encoding (e.g. %2F -> %252F) when we build the href below.
let padName = pad.name;
try {
padName = decodeURIComponent(pad.name);
} catch {
// pad.name is not valid percent-encoding; use it as-is.
}
const padPath = `${window.location.href}p/${encodeURIComponent(padName)}`;
const link = document.createElement('a');
link.style.textDecoration = 'none';

link.href = padPath;
link.innerText = pad.name;
link.innerText = padName;
li.appendChild(link);


Expand Down
2 changes: 1 addition & 1 deletion src/static/skins/colibris/pad.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ window.customStart = () => {
$('.buttonicon').on('mouseup', function () { $(this).parent().removeClass('pressed'); });

const pathSegments = window.location.pathname.split('/');
const padName = pathSegments[pathSegments.length - 1];
const padName = decodeURIComponent(pathSegments[pathSegments.length - 1]);
const recentPads = localStorage.getItem('recentPads');
if (recentPads == null) {
localStorage.setItem('recentPads', JSON.stringify([]));
Expand Down
13 changes: 13 additions & 0 deletions src/tests/frontend-new/specs/embed_value.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,17 @@ test.describe('embed links', function () {
await checkiFrameCode(embedCode, true, page);
});
})

test.describe('URL encoding of pad names', function () {
test('share link encodes special characters in pad name', async function ({page}) {
const padName = 'test%2Fencoding';
await page.goto(`http://localhost:9001/p/${padName}`);

const shareButton = page.locator('.buttonicon-embed');
await shareButton.click();

const shareLink = await page.locator('#linkinput').inputValue();
expect(shareLink).toContain(padName);
});
})
})
30 changes: 30 additions & 0 deletions src/tests/frontend-new/specs/recent_pads.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { test, expect } from '@playwright/test';

test.describe('Recent Pads', () => {
test('should display correctly encoded URLs for recent pads', async ({ page }) => {
const padName = 'test pad with spaces & / chars';
const recentPads = [
{
name: padName,
timestamp: new Date().toISOString(),
members: 1,
},
];

await page.addInitScript((data) => {
window.localStorage.setItem('recentPads', data);
}, JSON.stringify(recentPads));

await page.goto('http://localhost:9001/');

const recentPad = page.locator('.recent-pad').first();
await expect(recentPad).toBeVisible();

const link = recentPad.locator('a');
await expect(link).toHaveText(padName);

const expectedEncodedName = encodeURIComponent(padName);
const expectedHrefRegex = new RegExp(`p/${expectedEncodedName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`);
await expect(link).toHaveAttribute('href', expectedHrefRegex);
});
});
Loading