@@ -315,6 +315,118 @@ describe(__filename, function () {
315315 } ) ;
316316 } ) ;
317317
318+ describe ( 'malformed .etherpad files are rejected' , function ( ) {
319+ const makeGoodExport = ( ) => ( {
320+ 'pad:testing' : {
321+ atext : {
322+ text : 'foo\n' ,
323+ attribs : '|1+4' ,
324+ } ,
325+ pool : {
326+ numToAttrib : {
327+ 0 : [ 'author' , 'a.foo' ] ,
328+ } ,
329+ nextNum : 1 ,
330+ } ,
331+ head : 0 ,
332+ savedRevisions : [ ] ,
333+ } ,
334+ 'globalAuthor:a.foo' : {
335+ colorId : '#000000' ,
336+ name : 'author foo' ,
337+ timestamp : 1598747784631 ,
338+ padIDs : 'testing' ,
339+ } ,
340+ 'pad:testing:revs:0' : {
341+ changeset : 'Z:1>3+3$foo' ,
342+ meta : {
343+ author : 'a.foo' ,
344+ timestamp : 1597632398288 ,
345+ pool : {
346+ numToAttrib : { } ,
347+ nextNum : 0 ,
348+ } ,
349+ atext : {
350+ text : 'foo\n' ,
351+ attribs : '|1+4' ,
352+ } ,
353+ } ,
354+ } ,
355+ } ) ;
356+
357+ const importEtherpad = ( records ) => agent . post ( `/p/${ testPadId } /import` )
358+ . attach ( 'file' , Buffer . from ( JSON . stringify ( records ) , 'utf8' ) , {
359+ filename : '/test.etherpad' ,
360+ contentType : 'application/etherpad' ,
361+ } ) ;
362+
363+ before ( async function ( ) {
364+ // makeGoodExport() is assumed to produce good .etherpad records. Verify that assumption so
365+ // that a buggy makeGoodExport() doesn't cause checks to accidentally pass.
366+ const records = makeGoodExport ( ) ;
367+ await importEtherpad ( records )
368+ . expect ( 200 )
369+ . expect ( 'Content-Type' , / j s o n / )
370+ . expect ( ( res ) => assert . deepEqual ( res . body , {
371+ code : 0 ,
372+ message : 'ok' ,
373+ data : { directDatabaseAccess : true } ,
374+ } ) ) ;
375+ await agent . get ( `/p/${ testPadId } /export/txt` )
376+ . expect ( 200 )
377+ . buffer ( true ) . parse ( superagent . parse . text )
378+ . expect ( ( res ) => assert . match ( res . text , / f o o / ) ) ;
379+ } ) ;
380+
381+ it ( 'missing rev' , async function ( ) {
382+ const records = makeGoodExport ( ) ;
383+ delete records [ 'pad:testing:revs:0' ] ;
384+ await importEtherpad ( records ) . expect ( 500 ) ;
385+ } ) ;
386+
387+ it ( 'bad changeset' , async function ( ) {
388+ const records = makeGoodExport ( ) ;
389+ records [ 'pad:testing:revs:0' ] . changeset = 'garbage' ;
390+ await importEtherpad ( records ) . expect ( 500 ) ;
391+ } ) ;
392+
393+ it ( 'missing attrib in pool' , async function ( ) {
394+ const records = makeGoodExport ( ) ;
395+ records [ 'pad:testing' ] . pool . nextNum ++ ;
396+ await importEtherpad ( records ) . expect ( 500 ) ;
397+ } ) ;
398+
399+ it ( 'extra attrib in pool' , async function ( ) {
400+ const records = makeGoodExport ( ) ;
401+ const pool = records [ 'pad:testing' ] . pool ;
402+ pool . numToAttrib [ pool . nextNum ] = [ 'key' , 'value' ] ;
403+ await importEtherpad ( records ) . expect ( 500 ) ;
404+ } ) ;
405+
406+ it ( 'changeset refers to non-existent attrib' , async function ( ) {
407+ const records = makeGoodExport ( ) ;
408+ records [ 'pad:testing:revs:1' ] = {
409+ changeset : 'Z:4>4*1+4$asdf' ,
410+ meta : {
411+ author : 'a.foo' ,
412+ timestamp : 1597632398288 ,
413+ } ,
414+ } ;
415+ records [ 'pad:testing' ] . head = 1 ;
416+ records [ 'pad:testing' ] . atext = {
417+ text : 'asdffoo\n' ,
418+ attribs : '*1+4|1+4' ,
419+ } ;
420+ await importEtherpad ( records ) . expect ( 500 ) ;
421+ } ) ;
422+
423+ it ( 'pad atext does not match' , async function ( ) {
424+ const records = makeGoodExport ( ) ;
425+ records [ 'pad:testing' ] . atext . attribs = `*0${ records [ 'pad:testing' ] . atext . attribs } ` ;
426+ await importEtherpad ( records ) . expect ( 500 ) ;
427+ } ) ;
428+ } ) ;
429+
318430 describe ( 'Import authorization checks' , function ( ) {
319431 let authorize ;
320432
0 commit comments