mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-31 19:02:59 +01:00
export: Don't leak writeable pad ID when exporting
Co-authored-by: Richard Hansen <rhansen@rhansen.org>
This commit is contained in:
parent
58bd96ce8f
commit
dbd76f0c5d
4 changed files with 30 additions and 20 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,5 +1,19 @@
|
|||
# Next Release
|
||||
|
||||
### Security fixes
|
||||
|
||||
* Fixed leak of the writable pad ID when exporting from the pad's read-only ID.
|
||||
This only matters if you treat the writeable pad IDs as secret (e.g., you are
|
||||
not using [ep_padlist2](https://www.npmjs.com/package/ep_padlist2)) and you
|
||||
share the pad's read-only ID with untrusted users. Instead of treating
|
||||
writeable pad IDs as secret, you are encouraged to take advantage of
|
||||
Etherpad's authentication and authorization mechanisms (e.g., use
|
||||
[ep_openid_connect](https://www.npmjs.com/package/ep_openid_connect) with
|
||||
[ep_readonly_guest](https://www.npmjs.com/package/ep_readonly_guest), or write
|
||||
your own
|
||||
[authentication](https://etherpad.org/doc/v1.8.14/#index_authenticate) and
|
||||
[authorization](https://etherpad.org/doc/v1.8.14/#index_authorize) plugins).
|
||||
|
||||
### Compatibility changes
|
||||
|
||||
* For plugin authors:
|
||||
|
|
|
@ -56,14 +56,14 @@ exports.doExport = async (req, res, padId, readOnlyId, type) => {
|
|||
// if this is a plain text export, we can do this directly
|
||||
// We have to over engineer this because tabs are stored as attributes and not plain text
|
||||
if (type === 'etherpad') {
|
||||
const pad = await exportEtherpad.getPadRaw(padId);
|
||||
const pad = await exportEtherpad.getPadRaw(padId, readOnlyId);
|
||||
res.send(pad);
|
||||
} else if (type === 'txt') {
|
||||
const txt = await exporttxt.getPadTXTDocument(padId, req.params.rev);
|
||||
res.send(txt);
|
||||
} else {
|
||||
// render the html document
|
||||
let html = await exporthtml.getPadHTMLDocument(padId, req.params.rev);
|
||||
let html = await exporthtml.getPadHTMLDocument(padId, req.params.rev, readOnlyId);
|
||||
|
||||
// decide what to do with the html export
|
||||
|
||||
|
|
|
@ -19,23 +19,18 @@
|
|||
const db = require('../db/DB');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
|
||||
exports.getPadRaw = async (padId) => {
|
||||
const padKey = `pad:${padId}`;
|
||||
const padcontent = await db.get(padKey);
|
||||
exports.getPadRaw = async (padId, readOnlyId) => {
|
||||
const keyPrefixRead = `pad:${padId}`;
|
||||
const keyPrefixWrite = readOnlyId ? `pad:${readOnlyId}` : keyPrefixRead;
|
||||
const padcontent = await db.get(keyPrefixRead);
|
||||
|
||||
const records = [padKey];
|
||||
for (let i = 0; i <= padcontent.head; i++) {
|
||||
records.push(`${padKey}:revs:${i}`);
|
||||
}
|
||||
|
||||
for (let i = 0; i <= padcontent.chatHead; i++) {
|
||||
records.push(`${padKey}:chat:${i}`);
|
||||
}
|
||||
const keySuffixes = [''];
|
||||
for (let i = 0; i <= padcontent.head; i++) keySuffixes.push(`:revs:${i}`);
|
||||
for (let i = 0; i <= padcontent.chatHead; i++) keySuffixes.push(`:chat:${i}`);
|
||||
|
||||
const data = {};
|
||||
for (const key of records) {
|
||||
// For each piece of info about a pad.
|
||||
const entry = data[key] = await db.get(key);
|
||||
for (const keySuffix of keySuffixes) {
|
||||
const entry = data[keyPrefixWrite + keySuffix] = await db.get(keyPrefixRead + keySuffix);
|
||||
|
||||
// Get the Pad Authors
|
||||
if (entry.pool && entry.pool.numToAttrib) {
|
||||
|
@ -50,7 +45,7 @@ exports.getPadRaw = async (padId) => {
|
|||
if (authorEntry) {
|
||||
data[`globalAuthor:${authorId}`] = authorEntry;
|
||||
if (authorEntry.padIDs) {
|
||||
authorEntry.padIDs = padId;
|
||||
authorEntry.padIDs = readOnlyId || padId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +58,8 @@ exports.getPadRaw = async (padId) => {
|
|||
const prefixes = await hooks.aCallAll('exportEtherpadAdditionalContent');
|
||||
await Promise.all(prefixes.map(async (prefix) => {
|
||||
const key = `${prefix}:${padId}`;
|
||||
data[key] = await db.get(key);
|
||||
const writeKey = readOnlyId ? `${prefix}:${readOnlyId}` : key;
|
||||
data[writeKey] = await db.get(key);
|
||||
}));
|
||||
|
||||
return data;
|
||||
|
|
|
@ -457,7 +457,7 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
|||
return pieces.join('');
|
||||
};
|
||||
|
||||
exports.getPadHTMLDocument = async (padId, revNum) => {
|
||||
exports.getPadHTMLDocument = async (padId, revNum, readOnlyId) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
|
||||
// Include some Styles into the Head for Export
|
||||
|
@ -475,7 +475,7 @@ exports.getPadHTMLDocument = async (padId, revNum) => {
|
|||
|
||||
return eejs.require('ep_etherpad-lite/templates/export_html.html', {
|
||||
body: html,
|
||||
padId: Security.escapeHTML(padId),
|
||||
padId: Security.escapeHTML(readOnlyId || padId),
|
||||
extraCSS: stylesForExportCSS,
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue