From 6d8a404a9c3fefd0a2f26389e5a0ca5d1545291d Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 8 Nov 2016 16:38:22 +0100 Subject: [PATCH] [api-minor] Add support for a couple of white-listed `JavaScript` actions that contains valid URLs (issue 3897, bug 843699) By only allowing very specific type of `JavaScript` actions, and also utilizing the existing `URL` validation, this patch shouldn't pose too much risk. Fixes one of the points in issue 3897 (with the PDF file taken from issue 3438). Fixes https://bugzilla.mozilla.org/show_bug.cgi?id=843699 (probably, since that bug doesn't contain a test-case). --- src/core/obj.js | 27 +++++++++++++++ test/unit/annotation_layer_spec.js | 54 ++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/src/core/obj.js b/src/core/obj.js index 19de8dffa..a501d40d4 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -695,6 +695,33 @@ var Catalog = (function CatalogClosure() { } break; + case 'JavaScript': + var jsAction = action.get('JS'), js; + if (isStream(jsAction)) { + js = bytesToString(jsAction.getBytes()); + } else if (isString(jsAction)) { + js = jsAction; + } + + if (js) { + // Attempt to recover valid URLs from 'JS' entries with certain + // white-listed formats, e.g. + // - window.open('http://example.com') + // - app.launchURL('http://example.com', true) + var URL_OPEN_METHODS = [ + 'app.launchURL', + 'window.open' + ]; + var regex = new RegExp('^(?:' + URL_OPEN_METHODS.join('|') + ')' + + '\\((?:\'|\")(\\S+)(?:\'|\")(?:,|\\))'); + + var jsUrl = regex.exec(stringToPDFString(js), 'i'); + if (jsUrl && jsUrl[1]) { + url = jsUrl[1]; + break; + } + } + /* falls through */ default: warn('Catalog_parseDestDictionary: Unrecognized link type "' + linkType + '".'); diff --git a/test/unit/annotation_layer_spec.js b/test/unit/annotation_layer_spec.js index 98354ad5c..75fade444 100644 --- a/test/unit/annotation_layer_spec.js +++ b/test/unit/annotation_layer_spec.js @@ -544,6 +544,60 @@ describe('Annotation layer', function() { expect(data.newWindow).toEqual(true); }); + it('should recover valid URLs from JavaScript actions having certain ' + + 'white-listed formats', function () { + function checkJsAction(params) { + var jsEntry = params.jsEntry; + var expectedUrl = params.expectedUrl; + var expectedUnsafeUrl = params.expectedUnsafeUrl; + + var actionDict = new Dict(); + actionDict.set('Type', Name.get('Action')); + actionDict.set('S', Name.get('JavaScript')); + actionDict.set('JS', jsEntry); + + var annotationDict = new Dict(); + annotationDict.set('Type', Name.get('Annot')); + annotationDict.set('Subtype', Name.get('Link')); + annotationDict.set('A', actionDict); + + var annotationRef = new Ref(46, 0); + var xref = new XRefMock([ + { ref: annotationRef, data: annotationDict, } + ]); + + var annotation = annotationFactory.create(xref, annotationRef, + pdfManagerMock); + var data = annotation.data; + expect(data.annotationType).toEqual(AnnotationType.LINK); + + expect(data.url).toEqual(expectedUrl); + expect(data.unsafeUrl).toEqual(expectedUnsafeUrl); + expect(data.dest).toBeUndefined(); + expect(data.newWindow).toBeFalsy(); + } + + // Check that we reject a 'JS' entry containing arbitrary JavaScript. + checkJsAction({ + jsEntry: 'function someFun() { return "qwerty"; } someFun();', + expectedUrl: undefined, + expectedUnsafeUrl: undefined, + }); + // Check that we accept a white-listed {string} 'JS' entry. + checkJsAction({ + jsEntry: 'window.open(\'http://www.example.com/test.pdf\')', + expectedUrl: new URL('http://www.example.com/test.pdf').href, + expectedUnsafeUrl: 'http://www.example.com/test.pdf', + }); + // Check that we accept a white-listed {Stream} 'JS' entry. + checkJsAction({ + jsEntry: new StringStream( + 'app.launchURL("http://www.example.com/test.pdf", true)'), + expectedUrl: new URL('http://www.example.com/test.pdf').href, + expectedUnsafeUrl: 'http://www.example.com/test.pdf', + }); + }); + it('should correctly parse a Named action', function() { var actionDict = new Dict(); actionDict.set('Type', Name.get('Action'));