Skip to content

Commit f00b1ae

Browse files
committed
Merge branch 'master' into develop
2 parents 777d045 + 142a47c commit f00b1ae

File tree

14 files changed

+608
-350
lines changed

14 files changed

+608
-350
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@
2929
generator function.
3030
* `newOp()`: Deprecated in favor of the new `Op` class.
3131

32+
# 1.8.16
33+
34+
### Security fixes
35+
36+
If you cannot upgrade to v1.8.16 for some reason, you are encouraged to try
37+
cherry-picking the fixes to the version you are running:
38+
39+
```shell
40+
git cherry-pick b7065eb9a0ec..77bcb507b30e
41+
```
42+
43+
* Maliciously crafted `.etherpad` files can no longer overwrite arbitrary
44+
non-pad database records when imported.
45+
* Imported `.etherpad` files are now subject to numerous consistency checks
46+
before any records are written to the database. This should help avoid
47+
denial-of-service attacks via imports of malformed `.etherpad` files.
48+
49+
### Notable enhancements and fixes
50+
51+
* Fixed several `.etherpad` import bugs.
52+
* Improved support for large `.etherpad` imports.
53+
3254
# 1.8.15
3355

3456
### Security fixes

doc/api/hooks_server-side.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,15 @@ Things in context:
175175

176176
This hook gets called when a new pad was created.
177177

178-
## padLoad
179-
Called from: src/node/db/Pad.js
178+
## `padLoad`
180179

181-
Things in context:
180+
Called from: `src/node/db/PadManager.js`
182181

183-
1. pad - the pad instance
182+
Called when a pad is loaded, including after new pad creation.
183+
184+
Context properties:
184185

185-
This hook gets called when a pad was loaded. If a new pad was created and loaded this event will be emitted too.
186+
* `pad`: The Pad object.
186187

187188
## padUpdate
188189
Called from: src/node/db/Pad.js

src/bin/checkAllPads.js

Lines changed: 9 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,79 +10,18 @@ process.on('unhandledRejection', (err) => { throw err; });
1010
if (process.argv.length !== 2) throw new Error('Use: node src/bin/checkAllPads.js');
1111

1212
(async () => {
13-
// initialize the database
14-
require('../node/utils/Settings');
1513
const db = require('../node/db/DB');
1614
await db.init();
17-
18-
// load modules
19-
const Changeset = require('../static/js/Changeset');
2015
const padManager = require('../node/db/PadManager');
21-
22-
let revTestedCount = 0;
23-
24-
// get all pads
25-
const res = await padManager.listAllPads();
26-
for (const padId of res.padIDs) {
16+
await Promise.all((await padManager.listAllPads()).padIDs.map(async (padId) => {
2717
const pad = await padManager.getPad(padId);
28-
29-
// check if the pad has a pool
30-
if (pad.pool == null) {
31-
console.error(`[${pad.id}] Missing attribute pool`);
32-
continue;
33-
}
34-
// create an array with key kevisions
35-
// key revisions always save the full pad atext
36-
const head = pad.getHeadRevisionNumber();
37-
const keyRevisions = [];
38-
for (let rev = 0; rev < head; rev += 100) {
39-
keyRevisions.push(rev);
40-
}
41-
42-
// run through all key revisions
43-
for (const keyRev of keyRevisions) {
44-
// create an array of revisions we need till the next keyRevision or the End
45-
const revisionsNeeded = [];
46-
for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) {
47-
revisionsNeeded.push(rev);
48-
}
49-
50-
// this array will hold all revision changesets
51-
const revisions = [];
52-
53-
// run through all needed revisions and get them from the database
54-
for (const revNum of revisionsNeeded) {
55-
const revision = await db.get(`pad:${pad.id}:revs:${revNum}`);
56-
revisions[revNum] = revision;
57-
}
58-
59-
// check if the revision exists
60-
if (revisions[keyRev] == null) {
61-
console.error(`[${pad.id}] Missing revision ${keyRev}`);
62-
continue;
63-
}
64-
65-
// check if there is a atext in the keyRevisions
66-
let {meta: {atext} = {}} = revisions[keyRev];
67-
if (atext == null) {
68-
console.error(`[${pad.id}] Missing atext in revision ${keyRev}`);
69-
continue;
70-
}
71-
72-
const apool = pad.pool;
73-
for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) {
74-
try {
75-
const cs = revisions[rev].changeset;
76-
atext = Changeset.applyToAText(cs, atext, apool);
77-
revTestedCount++;
78-
} catch (e) {
79-
console.error(`[${pad.id}] Bad changeset at revision ${rev} - ${e.message}`);
80-
}
81-
}
18+
try {
19+
await pad.check();
20+
} catch (err) {
21+
console.error(`Error in pad ${padId}: ${err.stack || err}`);
22+
return;
8223
}
83-
}
84-
if (revTestedCount === 0) {
85-
throw new Error('No revisions tested');
86-
}
87-
console.log(`Finished: Tested ${revTestedCount} revisions`);
24+
console.log(`Pad ${padId}: OK`);
25+
}));
26+
console.log('Finished.');
8827
})();

src/bin/checkPad.js

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,75 +8,13 @@
88
process.on('unhandledRejection', (err) => { throw err; });
99

1010
if (process.argv.length !== 3) throw new Error('Use: node src/bin/checkPad.js $PADID');
11-
12-
// get the padID
1311
const padId = process.argv[2];
14-
let checkRevisionCount = 0;
15-
1612
(async () => {
17-
// initialize database
18-
require('../node/utils/Settings');
1913
const db = require('../node/db/DB');
2014
await db.init();
21-
22-
// load modules
23-
const Changeset = require('../static/js/Changeset');
2415
const padManager = require('../node/db/PadManager');
25-
26-
const exists = await padManager.doesPadExists(padId);
27-
if (!exists) throw new Error('Pad does not exist');
28-
29-
// get the pad
16+
if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist');
3017
const pad = await padManager.getPad(padId);
31-
32-
// create an array with key revisions
33-
// key revisions always save the full pad atext
34-
const head = pad.getHeadRevisionNumber();
35-
const keyRevisions = [];
36-
for (let rev = 0; rev < head; rev += 100) {
37-
keyRevisions.push(rev);
38-
}
39-
40-
// run through all key revisions
41-
for (let keyRev of keyRevisions) {
42-
keyRev = parseInt(keyRev);
43-
// create an array of revisions we need till the next keyRevision or the End
44-
const revisionsNeeded = [];
45-
for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) {
46-
revisionsNeeded.push(rev);
47-
}
48-
49-
// this array will hold all revision changesets
50-
const revisions = [];
51-
52-
// run through all needed revisions and get them from the database
53-
for (const revNum of revisionsNeeded) {
54-
const revision = await db.get(`pad:${padId}:revs:${revNum}`);
55-
revisions[revNum] = revision;
56-
}
57-
58-
// check if the pad has a pool
59-
if (pad.pool == null) throw new Error('Attribute pool is missing');
60-
61-
// check if there is an atext in the keyRevisions
62-
let {meta: {atext} = {}} = revisions[keyRev] || {};
63-
if (atext == null) {
64-
console.error(`No atext in key revision ${keyRev}`);
65-
continue;
66-
}
67-
68-
const apool = pad.pool;
69-
70-
for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) {
71-
checkRevisionCount++;
72-
try {
73-
const cs = revisions[rev].changeset;
74-
atext = Changeset.applyToAText(cs, atext, apool);
75-
} catch (e) {
76-
console.error(`Bad changeset at revision ${rev} - ${e.message}`);
77-
continue;
78-
}
79-
}
80-
console.log(`Finished: Checked ${checkRevisionCount} revisions`);
81-
}
18+
await pad.check();
19+
console.log('Finished.');
8220
})();

src/bin/checkPadDeltas.js

Lines changed: 0 additions & 103 deletions
This file was deleted.

0 commit comments

Comments
 (0)