openvibers/openvibe.community Commits

A paste link that picked up trailing punctuation (a full stop, a bracket, Markdown's **) redirects to the paste instead of answering Paste not found (#146)

OP OpenVibers committed 17 days ago · c8adb3e89d · 2 files, +36 −0

Changes

ddiff --git a/server/app.js b/server/app.js
iindex 7291a11..2e116de 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -359,6 +359,23 @@ async function createApp(opts = {}) {
// Old Live-style /pastes/<slug> links and bare slugs land on the paste page.
app.get('/pastes/:slug', (req, res) => res.redirect(301, `/p/${encodeURIComponent(req.params.slug)}`));
++ // A paste link copied out of a chat or a document often carries what followed it: a full stop, a bracket, Markdown's
++ // ** around it. Characters no slug can hold are dropped from its end with a redirect, not answered "Paste not found".
++ app.get(/^\/p\/[^/]+(?:\/(?:raw|screenshot|download))?\/?$/, (req, res, next) => {
++ const [, , raw, rest = ''] = req.path.match(/^\/(p)\/([^/]+)((?:\/[a-z]+)?)\/?$/) || [];
++ let seg;
++ try {
++ seg = decodeURIComponent(raw || '');
++ } catch {
++ return next();
++ }
++ if (SLUG_RE.test(seg)) return next();
++ const clean = seg.replace(/[^A-Za-z0-9_-]+$/, '');
++ if (!SLUG_RE.test(clean)) return next();
++ const q = req.originalUrl.indexOf('?');
++ res.redirect(301, `/p/${clean}${rest}${q >= 0 ? req.originalUrl.slice(q) : ''}`);
++ });
++
app.get('/p/:slug', withUser, wrap(async (req, res) => {
const { slug } = req.params;
if (!SLUG_RE.test(slug)) return html(res, pages.errorPage({ status: 404, title: 'Paste not found', message: 'That link does not point at a paste.' }), 404);
ddiff --git a/test/ssr-community.test.js b/test/ssr-community.test.js
iindex 9cb3b72..31d6bee 100644
---- a/test/ssr-community.test.js
++++ b/test/ssr-community.test.js
@@@ -93,6 +93,25 @@ const { boot, check, done } = require('./helpers/app');
assert.ok(!lang.text.includes(`href="/p/${js}"`));
});
++ await check('a link that picked up trailing punctuation (**, a full stop, a bracket) redirects to the paste', async () => {
++ for (const [path, to] of [
++ [`/p/${js}**`, `/p/${js}`],
++ [`/p/${js}.`, `/p/${js}`],
++ [`/p/${js})`, `/p/${js}`],
++ [`/p/${js}%2A%2A`, `/p/${js}`],
++ [`/p/${js}),/raw?dl=1`, `/p/${js}/raw?dl=1`],
++ [`/p/${js}./download`, `/p/${js}/download`],
++ ]) {
++ const r = await t.get(path);
++ assert.strictEqual(r.status, 301, path);
++ assert.strictEqual(r.headers.get('location'), to, path);
++ }
++ // Nothing left to point at, or junk in the middle: still "Paste not found".
++ assert.strictEqual((await t.get('/p/**')).status, 404);
++ assert.strictEqual((await t.get(`/p/${js.slice(0, 4)}*${js.slice(4)}`)).status, 404);
++ assert.strictEqual((await t.get(`/p/${js}`)).status, 200);
++ });
++
await check('/my lists the signed-in person\'s pastes, private and unlisted included', async () => {
const mine = await t.get('/my', { cookies: [`ov_token=${token}`] });
assert.strictEqual(mine.status, 200);