Issues (1495)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

laraview/text_layer_builder.js (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* Copyright 2012 Mozilla Foundation
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
/* globals CustomStyle, PDFJS */
17
18
'use strict';
19
20
var MAX_TEXT_DIVS_TO_RENDER = 100000;
21
22
var NonWhitespaceRegexp = /\S/;
23
24
function isAllWhitespace(str) {
25
  return !NonWhitespaceRegexp.test(str);
26
}
27
28
/**
29
 * @typedef {Object} TextLayerBuilderOptions
30
 * @property {HTMLDivElement} textLayerDiv - The text layer container.
31
 * @property {number} pageIndex - The page index.
32
 * @property {PageViewport} viewport - The viewport of the text layer.
33
 * @property {PDFFindController} findController
34
 */
35
36
/**
37
 * TextLayerBuilder provides text-selection functionality for the PDF.
38
 * It does this by creating overlay divs over the PDF text. These divs
39
 * contain text that matches the PDF text they are overlaying. This object
40
 * also provides a way to highlight text that is being searched for.
41
 * @class
42
 */
43
var TextLayerBuilder = (function TextLayerBuilderClosure() {
44
  function TextLayerBuilder(options) {
45
    this.textLayerDiv = options.textLayerDiv;
46
    this.renderingDone = false;
47
    this.divContentDone = false;
48
    this.pageIdx = options.pageIndex;
49
    this.pageNumber = this.pageIdx + 1;
50
    this.matches = [];
51
    this.viewport = options.viewport;
52
    this.textDivs = [];
53
    this.findController = options.findController || null;
54
  }
55
56
  TextLayerBuilder.prototype = {
57
    _finishRendering: function TextLayerBuilder_finishRendering() {
58
      this.renderingDone = true;
59
60
      var event = document.createEvent('CustomEvent');
61
      event.initCustomEvent('textlayerrendered', true, true, {
62
        pageNumber: this.pageNumber
63
      });
64
      this.textLayerDiv.dispatchEvent(event);
65
    },
66
67
    renderLayer: function TextLayerBuilder_renderLayer() {
68
      var textLayerFrag = document.createDocumentFragment();
69
      var textDivs = this.textDivs;
70
      var textDivsLength = textDivs.length;
71
      var canvas = document.createElement('canvas');
72
      var ctx = canvas.getContext('2d');
73
74
      // No point in rendering many divs as it would make the browser
75
      // unusable even after the divs are rendered.
76
      if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
77
        this._finishRendering();
78
        return;
79
      }
80
81
      var lastFontSize;
82
      var lastFontFamily;
83
      for (var i = 0; i < textDivsLength; i++) {
84
        var textDiv = textDivs[i];
85
        if (textDiv.dataset.isWhitespace !== undefined) {
86
          continue;
87
        }
88
89
        var fontSize = textDiv.style.fontSize;
90
        var fontFamily = textDiv.style.fontFamily;
91
92
        // Only build font string and set to context if different from last.
93
        if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) {
0 ignored issues
show
The variable lastFontSize seems to not be initialized for all possible execution paths.
Loading history...
The variable lastFontFamily seems to not be initialized for all possible execution paths.
Loading history...
94
          ctx.font = fontSize + ' ' + fontFamily;
95
          lastFontSize = fontSize;
96
          lastFontFamily = fontFamily;
97
        }
98
99
        var width = ctx.measureText(textDiv.textContent).width;
100
        if (width > 0) {
101
          textLayerFrag.appendChild(textDiv);
102
          var transform;
103
          if (textDiv.dataset.canvasWidth !== undefined) {
104
            // Dataset values come of type string.
105
            var textScale = textDiv.dataset.canvasWidth / width;
106
            transform = 'scaleX(' + textScale + ')';
107
          } else {
108
            transform = '';
109
          }
110
          var rotation = textDiv.dataset.angle;
111
          if (rotation) {
112
            transform = 'rotate(' + rotation + 'deg) ' + transform;
113
          }
114
          if (transform) {
115
            CustomStyle.setProp('transform' , textDiv, transform);
116
          }
117
        }
118
      }
119
120
      this.textLayerDiv.appendChild(textLayerFrag);
121
      this._finishRendering();
122
      this.updateMatches();
123
    },
124
125
    /**
126
     * Renders the text layer.
127
     * @param {number} timeout (optional) if specified, the rendering waits
128
     *   for specified amount of ms.
129
     */
130
    render: function TextLayerBuilder_render(timeout) {
131
      if (!this.divContentDone || this.renderingDone) {
132
        return;
133
      }
134
135
      if (this.renderTimer) {
136
        clearTimeout(this.renderTimer);
137
        this.renderTimer = null;
138
      }
139
140
      if (!timeout) { // Render right away
141
        this.renderLayer();
142
      } else { // Schedule
143
        var self = this;
144
        this.renderTimer = setTimeout(function() {
145
          self.renderLayer();
146
          self.renderTimer = null;
147
        }, timeout);
148
      }
149
    },
150
151
    appendText: function TextLayerBuilder_appendText(geom, styles) {
152
      var style = styles[geom.fontName];
153
      var textDiv = document.createElement('div');
154
      this.textDivs.push(textDiv);
155
      if (isAllWhitespace(geom.str)) {
156
        textDiv.dataset.isWhitespace = true;
157
        return;
158
      }
159
      var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
160
      var angle = Math.atan2(tx[1], tx[0]);
161
      if (style.vertical) {
162
        angle += Math.PI / 2;
163
      }
164
      var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
165
      var fontAscent = fontHeight;
166
      if (style.ascent) {
167
        fontAscent = style.ascent * fontAscent;
168
      } else if (style.descent) {
169
        fontAscent = (1 + style.descent) * fontAscent;
170
      }
171
172
      var left;
173
      var top;
174
      if (angle === 0) {
175
        left = tx[4];
176
        top = tx[5] - fontAscent;
177
      } else {
178
        left = tx[4] + (fontAscent * Math.sin(angle));
179
        top = tx[5] - (fontAscent * Math.cos(angle));
180
      }
181
      textDiv.style.left = left + 'px';
182
      textDiv.style.top = top + 'px';
183
      textDiv.style.fontSize = fontHeight + 'px';
184
      textDiv.style.fontFamily = style.fontFamily;
185
186
      textDiv.textContent = geom.str;
187
      // |fontName| is only used by the Font Inspector. This test will succeed
188
      // when e.g. the Font Inspector is off but the Stepper is on, but it's
189
      // not worth the effort to do a more accurate test.
190
      if (PDFJS.pdfBug) {
191
        textDiv.dataset.fontName = geom.fontName;
192
      }
193
      // Storing into dataset will convert number into string.
194
      if (angle !== 0) {
195
        textDiv.dataset.angle = angle * (180 / Math.PI);
196
      }
197
      // We don't bother scaling single-char text divs, because it has very
198
      // little effect on text highlighting. This makes scrolling on docs with
199
      // lots of such divs a lot faster.
200
      if (textDiv.textContent.length > 1) {
201
        if (style.vertical) {
202
          textDiv.dataset.canvasWidth = geom.height * this.viewport.scale;
203
        } else {
204
          textDiv.dataset.canvasWidth = geom.width * this.viewport.scale;
205
        }
206
      }
207
    },
208
209
    setTextContent: function TextLayerBuilder_setTextContent(textContent) {
210
      this.textContent = textContent;
211
212
      var textItems = textContent.items;
213
      for (var i = 0, len = textItems.length; i < len; i++) {
214
        this.appendText(textItems[i], textContent.styles);
215
      }
216
      this.divContentDone = true;
217
    },
218
219
    convertMatches: function TextLayerBuilder_convertMatches(matches) {
220
      var i = 0;
221
      var iIndex = 0;
222
      var bidiTexts = this.textContent.items;
223
      var end = bidiTexts.length - 1;
224
      var queryLen = (this.findController === null ?
225
                      0 : this.findController.state.query.length);
226
      var ret = [];
227
228
      for (var m = 0, len = matches.length; m < len; m++) {
229
        // Calculate the start position.
230
        var matchIdx = matches[m];
231
232
        // Loop over the divIdxs.
233
        while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
234
          iIndex += bidiTexts[i].str.length;
235
          i++;
236
        }
237
238
        if (i === bidiTexts.length) {
239
          console.error('Could not find a matching mapping');
240
        }
241
242
        var match = {
243
          begin: {
244
            divIdx: i,
245
            offset: matchIdx - iIndex
246
          }
247
        };
248
249
        // Calculate the end position.
250
        matchIdx += queryLen;
251
252
        // Somewhat the same array as above, but use > instead of >= to get
253
        // the end position right.
254
        while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
255
          iIndex += bidiTexts[i].str.length;
256
          i++;
257
        }
258
259
        match.end = {
260
          divIdx: i,
261
          offset: matchIdx - iIndex
262
        };
263
        ret.push(match);
264
      }
265
266
      return ret;
267
    },
268
269
    renderMatches: function TextLayerBuilder_renderMatches(matches) {
270
      // Early exit if there is nothing to render.
271
      if (matches.length === 0) {
272
        return;
273
      }
274
275
      var bidiTexts = this.textContent.items;
276
      var textDivs = this.textDivs;
277
      var prevEnd = null;
278
      var pageIdx = this.pageIdx;
279
      var isSelectedPage = (this.findController === null ?
280
        false : (pageIdx === this.findController.selected.pageIdx));
281
      var selectedMatchIdx = (this.findController === null ?
282
                              -1 : this.findController.selected.matchIdx);
283
      var highlightAll = (this.findController === null ?
284
                          false : this.findController.state.highlightAll);
285
      var infinity = {
286
        divIdx: -1,
287
        offset: undefined
288
      };
289
290
      function beginText(begin, className) {
291
        var divIdx = begin.divIdx;
292
        textDivs[divIdx].textContent = '';
293
        appendTextToDiv(divIdx, 0, begin.offset, className);
294
      }
295
296
      function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
297
        var div = textDivs[divIdx];
298
        var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
299
        var node = document.createTextNode(content);
300
        if (className) {
301
          var span = document.createElement('span');
302
          span.className = className;
303
          span.appendChild(node);
304
          div.appendChild(span);
305
          return;
306
        }
307
        div.appendChild(node);
308
      }
309
310
      var i0 = selectedMatchIdx, i1 = i0 + 1;
311
      if (highlightAll) {
312
        i0 = 0;
313
        i1 = matches.length;
314
      } else if (!isSelectedPage) {
315
        // Not highlighting all and this isn't the selected page, so do nothing.
316
        return;
317
      }
318
319
      for (var i = i0; i < i1; i++) {
320
        var match = matches[i];
321
        var begin = match.begin;
322
        var end = match.end;
323
        var isSelected = (isSelectedPage && i === selectedMatchIdx);
324
        var highlightSuffix = (isSelected ? ' selected' : '');
325
326
        if (this.findController) {
327
          this.findController.updateMatchPosition(pageIdx, i, textDivs,
328
                                                  begin.divIdx, end.divIdx);
329
        }
330
331
        // Match inside new div.
332
        if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
333
          // If there was a previous div, then add the text at the end.
334
          if (prevEnd !== null) {
335
            appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
336
          }
337
          // Clear the divs and set the content until the starting point.
338
          beginText(begin);
339
        } else {
340
          appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
341
        }
342
343
        if (begin.divIdx === end.divIdx) {
344
          appendTextToDiv(begin.divIdx, begin.offset, end.offset,
345
                          'highlight' + highlightSuffix);
346
        } else {
347
          appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
348
                          'highlight begin' + highlightSuffix);
349
          for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
350
            textDivs[n0].className = 'highlight middle' + highlightSuffix;
351
          }
352
          beginText(end, 'highlight end' + highlightSuffix);
353
        }
354
        prevEnd = end;
355
      }
356
357
      if (prevEnd) {
358
        appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
359
      }
360
    },
361
362
    updateMatches: function TextLayerBuilder_updateMatches() {
363
      // Only show matches when all rendering is done.
364
      if (!this.renderingDone) {
365
        return;
366
      }
367
368
      // Clear all matches.
369
      var matches = this.matches;
370
      var textDivs = this.textDivs;
371
      var bidiTexts = this.textContent.items;
372
      var clearedUntilDivIdx = -1;
373
374
      // Clear all current matches.
375
      for (var i = 0, len = matches.length; i < len; i++) {
376
        var match = matches[i];
377
        var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
378
        for (var n = begin, end = match.end.divIdx; n <= end; n++) {
379
          var div = textDivs[n];
380
          div.textContent = bidiTexts[n].str;
381
          div.className = '';
382
        }
383
        clearedUntilDivIdx = match.end.divIdx + 1;
384
      }
385
386
      if (this.findController === null || !this.findController.active) {
387
        return;
388
      }
389
390
      // Convert the matches on the page controller into the match format
391
      // used for the textLayer.
392
      this.matches = this.convertMatches(this.findController === null ?
393
        [] : (this.findController.pageMatches[this.pageIdx] || []));
394
      this.renderMatches(this.matches);
395
    }
396
  };
397
  return TextLayerBuilder;
398
})();
399
400
/**
401
 * @constructor
402
 * @implements IPDFTextLayerFactory
403
 */
404
function DefaultTextLayerFactory() {}
405
DefaultTextLayerFactory.prototype = {
406
  /**
407
   * @param {HTMLDivElement} textLayerDiv
408
   * @param {number} pageIndex
409
   * @param {PageViewport} viewport
410
   * @returns {TextLayerBuilder}
411
   */
412
  createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
413
    return new TextLayerBuilder({
414
      textLayerDiv: textLayerDiv,
415
      pageIndex: pageIndex,
416
      viewport: viewport
417
    });
418
  }
419
};
420