Merge pull request #18026 from Snuffleupagus/validate-more-getArray

Validate even more dictionary properties
This commit is contained in:
Jonas Jenwald 2024-05-06 22:09:42 +02:00 committed by GitHub
commit b6765403a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 140 additions and 85 deletions

View file

@ -43,6 +43,10 @@ import {
getInheritableProperty, getInheritableProperty,
getRotationMatrix, getRotationMatrix,
isAscii, isAscii,
isNumberArray,
lookupMatrix,
lookupNormalRect,
lookupRect,
numberToString, numberToString,
stringToUTF16String, stringToUTF16String,
} from "./core_utils.js"; } from "./core_utils.js";
@ -550,7 +554,7 @@ function getQuadPoints(dict, rect) {
// Each quadrilateral must consist of eight coordinates. // Each quadrilateral must consist of eight coordinates.
const quadPoints = dict.getArray("QuadPoints"); const quadPoints = dict.getArray("QuadPoints");
if ( if (
!Array.isArray(quadPoints) || !isNumberArray(quadPoints, null) ||
quadPoints.length === 0 || quadPoints.length === 0 ||
quadPoints.length % 8 > 0 quadPoints.length % 8 > 0
) { ) {
@ -914,10 +918,7 @@ class Annotation {
* @param {Array} rectangle - The rectangle array with exactly four entries * @param {Array} rectangle - The rectangle array with exactly four entries
*/ */
setRectangle(rectangle) { setRectangle(rectangle) {
this.rectangle = this.rectangle = lookupNormalRect(rectangle, [0, 0, 0, 0]);
Array.isArray(rectangle) && rectangle.length === 4
? Util.normalizeRect(rectangle)
: [0, 0, 0, 0];
} }
/** /**
@ -1150,8 +1151,11 @@ class Annotation {
["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], ["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"],
appearance appearance
); );
const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1]; const bbox = lookupRect(appearanceDict.getArray("BBox"), [0, 0, 1, 1]);
const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0]; const matrix = lookupMatrix(
appearanceDict.getArray("Matrix"),
IDENTITY_MATRIX
);
const transform = getTransformMatrix(rect, bbox, matrix); const transform = getTransformMatrix(rect, bbox, matrix);
const opList = new OperatorList(); const opList = new OperatorList();
@ -1248,10 +1252,13 @@ class Annotation {
if (text.length > 1 || text[0]) { if (text.length > 1 || text[0]) {
const appearanceDict = this.appearance.dict; const appearanceDict = this.appearance.dict;
const bbox = lookupRect(appearanceDict.getArray("BBox"), null);
const matrix = lookupMatrix(appearanceDict.getArray("Matrix"), null);
this.data.textPosition = this._transformPoint( this.data.textPosition = this._transformPoint(
firstPosition, firstPosition,
appearanceDict.getArray("BBox"), bbox,
appearanceDict.getArray("Matrix") matrix
); );
this.data.textContent = text; this.data.textContent = text;
} }
@ -1401,7 +1408,7 @@ class AnnotationBorderStyle {
setWidth(width, rect = [0, 0, 0, 0]) { setWidth(width, rect = [0, 0, 0, 0]) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( assert(
Array.isArray(rect) && rect.length === 4, isNumberArray(rect, 4),
"A valid `rect` parameter must be provided." "A valid `rect` parameter must be provided."
); );
} }
@ -2972,7 +2979,10 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
: this.uncheckedAppearance; : this.uncheckedAppearance;
if (appearance) { if (appearance) {
const savedAppearance = this.appearance; const savedAppearance = this.appearance;
const savedMatrix = appearance.dict.getArray("Matrix") || IDENTITY_MATRIX; const savedMatrix = lookupMatrix(
appearance.dict.getArray("Matrix"),
IDENTITY_MATRIX
);
if (rotation) { if (rotation) {
appearance.dict.set( appearance.dict.set(
@ -3737,12 +3747,7 @@ class PopupAnnotation extends Annotation {
warn("Popup annotation has a missing or invalid parent annotation."); warn("Popup annotation has a missing or invalid parent annotation.");
return; return;
} }
this.data.parentRect = lookupNormalRect(parentItem.getArray("Rect"), null);
const parentRect = parentItem.getArray("Rect");
this.data.parentRect =
Array.isArray(parentRect) && parentRect.length === 4
? Util.normalizeRect(parentRect)
: null;
const rt = parentItem.get("RT"); const rt = parentItem.get("RT");
if (isName(rt, AnnotationReplyType.GROUP)) { if (isName(rt, AnnotationReplyType.GROUP)) {
@ -4030,7 +4035,7 @@ class LineAnnotation extends MarkupAnnotation {
this.data.hasOwnCanvas = this.data.noRotate; this.data.hasOwnCanvas = this.data.noRotate;
this.data.noHTML = false; this.data.noHTML = false;
const lineCoordinates = dict.getArray("L"); const lineCoordinates = lookupRect(dict.getArray("L"), [0, 0, 0, 0]);
this.data.lineCoordinates = Util.normalizeRect(lineCoordinates); this.data.lineCoordinates = Util.normalizeRect(lineCoordinates);
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
@ -4225,7 +4230,7 @@ class PolylineAnnotation extends MarkupAnnotation {
// horizontal and vertical coordinates, respectively, of each vertex. // horizontal and vertical coordinates, respectively, of each vertex.
// Convert this to an array of objects with x and y coordinates. // Convert this to an array of objects with x and y coordinates.
const rawVertices = dict.getArray("Vertices"); const rawVertices = dict.getArray("Vertices");
if (!Array.isArray(rawVertices)) { if (!isNumberArray(rawVertices, null)) {
return; return;
} }
for (let i = 0, ii = rawVertices.length; i < ii; i += 2) { for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {
@ -4314,11 +4319,15 @@ class InkAnnotation extends MarkupAnnotation {
// of each vertex. Convert this to an array of objects with x and y // of each vertex. Convert this to an array of objects with x and y
// coordinates. // coordinates.
this.data.inkLists.push([]); this.data.inkLists.push([]);
if (!Array.isArray(rawInkLists[i])) {
continue;
}
for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) { for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {
this.data.inkLists[i].push({ const x = xref.fetchIfRef(rawInkLists[i][j]),
x: xref.fetchIfRef(rawInkLists[i][j]), y = xref.fetchIfRef(rawInkLists[i][j + 1]);
y: xref.fetchIfRef(rawInkLists[i][j + 1]), if (typeof x === "number" && typeof y === "number") {
}); this.data.inkLists[i].push({ x, y });
}
} }
} }

View file

@ -15,6 +15,7 @@
import { import {
collectActions, collectActions,
isNumberArray,
MissingDataException, MissingDataException,
PDF_VERSION_REGEXP, PDF_VERSION_REGEXP,
recoverJsURL, recoverJsURL,
@ -388,8 +389,7 @@ class Catalog {
// We only need to parse the color when it's valid, and non-default. // We only need to parse the color when it's valid, and non-default.
if ( if (
Array.isArray(color) && isNumberArray(color, 3) &&
color.length === 3 &&
(color[0] !== 0 || color[1] !== 0 || color[2] !== 0) (color[0] !== 0 || color[1] !== 0 || color[2] !== 0)
) { ) {
rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0); rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);

View file

@ -19,6 +19,7 @@ import {
BaseException, BaseException,
objectSize, objectSize,
stringToPDFString, stringToPDFString,
Util,
warn, warn,
} from "../shared/util.js"; } from "../shared/util.js";
import { Dict, isName, Ref, RefSet } from "./primitives.js"; import { Dict, isName, Ref, RefSet } from "./primitives.js";
@ -218,6 +219,21 @@ function isWhiteSpace(ch) {
return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a; return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
} }
/**
* Checks if something is an Array containing only boolean values,
* and (optionally) checks its length.
* @param {any} arr
* @param {number | null} len
* @returns {boolean}
*/
function isBooleanArray(arr, len) {
return (
Array.isArray(arr) &&
(len === null || arr.length === len) &&
arr.every(x => typeof x === "boolean")
);
}
/** /**
* Checks if something is an Array containing only numbers, * Checks if something is an Array containing only numbers,
* and (optionally) checks its length. * and (optionally) checks its length.
@ -233,6 +249,21 @@ function isNumberArray(arr, len) {
); );
} }
// Returns the matrix, or the fallback value if it's invalid.
function lookupMatrix(arr, fallback) {
return isNumberArray(arr, 6) ? arr : fallback;
}
// Returns the rectangle, or the fallback value if it's invalid.
function lookupRect(arr, fallback) {
return isNumberArray(arr, 4) ? arr : fallback;
}
// Returns the normalized rectangle, or the fallback value if it's invalid.
function lookupNormalRect(arr, fallback) {
return isNumberArray(arr, 4) ? Util.normalizeRect(arr) : fallback;
}
/** /**
* AcroForm field names use an array like notation to refer to * AcroForm field names use an array like notation to refer to
* repeated XFA elements e.g. foo.bar[nnn]. * repeated XFA elements e.g. foo.bar[nnn].
@ -652,9 +683,13 @@ export {
getRotationMatrix, getRotationMatrix,
getSizeInBytes, getSizeInBytes,
isAscii, isAscii,
isBooleanArray,
isNumberArray, isNumberArray,
isWhiteSpace, isWhiteSpace,
log2, log2,
lookupMatrix,
lookupNormalRect,
lookupRect,
MissingDataException, MissingDataException,
numberToString, numberToString,
ParserEOFException, ParserEOFException,

View file

@ -40,6 +40,7 @@ import {
getInheritableProperty, getInheritableProperty,
getNewAnnotationsMap, getNewAnnotationsMap,
isWhiteSpace, isWhiteSpace,
lookupNormalRect,
MissingDataException, MissingDataException,
PDF_VERSION_REGEXP, PDF_VERSION_REGEXP,
validateCSSFont, validateCSSFont,
@ -160,10 +161,12 @@ class Page {
if (this.xfaData) { if (this.xfaData) {
return this.xfaData.bbox; return this.xfaData.bbox;
} }
let box = this._getInheritableProperty(name, /* getArray = */ true); const box = lookupNormalRect(
this._getInheritableProperty(name, /* getArray = */ true),
null
);
if (Array.isArray(box) && box.length === 4) { if (box) {
box = Util.normalizeRect(box);
if (box[2] - box[0] > 0 && box[3] - box[1] > 0) { if (box[2] - box[0] > 0 && box[3] - box[1] > 0) {
return box; return box;
} }

View file

@ -53,6 +53,7 @@ import {
import { getTilingPatternIR, Pattern } from "./pattern.js"; import { getTilingPatternIR, Pattern } from "./pattern.js";
import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js"; import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js";
import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js"; import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js";
import { isNumberArray, lookupMatrix, lookupNormalRect } from "./core_utils.js";
import { isPDFFunction, PDFFunctionFactory } from "./function.js"; import { isPDFFunction, PDFFunctionFactory } from "./function.js";
import { Lexer, Parser } from "./parser.js"; import { Lexer, Parser } from "./parser.js";
import { import {
@ -73,7 +74,6 @@ import { getGlyphsUnicode } from "./glyphlist.js";
import { getMetrics } from "./metrics.js"; import { getMetrics } from "./metrics.js";
import { getUnicodeForGlyph } from "./unicode.js"; import { getUnicodeForGlyph } from "./unicode.js";
import { ImageResizer } from "./image_resizer.js"; import { ImageResizer } from "./image_resizer.js";
import { isNumberArray } from "./core_utils.js";
import { MurmurHash3_64 } from "../shared/murmurhash3.js"; import { MurmurHash3_64 } from "../shared/murmurhash3.js";
import { OperatorList } from "./operator_list.js"; import { OperatorList } from "./operator_list.js";
import { PDFImage } from "./image.js"; import { PDFImage } from "./image.js";
@ -460,12 +460,8 @@ class PartialEvaluator {
localColorSpaceCache localColorSpaceCache
) { ) {
const dict = xobj.dict; const dict = xobj.dict;
const matrix = dict.getArray("Matrix"); const matrix = lookupMatrix(dict.getArray("Matrix"), null);
let bbox = dict.getArray("BBox"); const bbox = lookupNormalRect(dict.getArray("BBox"), null);
bbox =
Array.isArray(bbox) && bbox.length === 4
? Util.normalizeRect(bbox)
: null;
let optionalContent, groupOptions; let optionalContent, groupOptions;
if (dict.has("OC")) { if (dict.has("OC")) {
@ -1578,7 +1574,7 @@ class PartialEvaluator {
localShadingPatternCache, localShadingPatternCache,
}); });
if (objId) { if (objId) {
const matrix = dict.getArray("Matrix"); const matrix = lookupMatrix(dict.getArray("Matrix"), null);
operatorList.addOp(fn, ["Shading", objId, matrix]); operatorList.addOp(fn, ["Shading", objId, matrix]);
} }
return undefined; return undefined;
@ -3265,8 +3261,8 @@ class PartialEvaluator {
const currentState = stateManager.state.clone(); const currentState = stateManager.state.clone();
const xObjStateManager = new StateManager(currentState); const xObjStateManager = new StateManager(currentState);
const matrix = xobj.dict.getArray("Matrix"); const matrix = lookupMatrix(xobj.dict.getArray("Matrix"), null);
if (Array.isArray(matrix) && matrix.length === 6) { if (matrix) {
xObjStateManager.transform(matrix); xObjStateManager.transform(matrix);
} }
@ -4244,10 +4240,7 @@ class PartialEvaluator {
if (!descriptor) { if (!descriptor) {
if (isType3Font) { if (isType3Font) {
let bbox = dict.getArray("FontBBox"); const bbox = lookupNormalRect(dict.getArray("FontBBox"), [0, 0, 0, 0]);
if (!isNumberArray(bbox, 4)) {
bbox = [0, 0, 0, 0];
}
// FontDescriptor is only required for Type3 fonts when the document // FontDescriptor is only required for Type3 fonts when the document
// is a tagged pdf. Create a barbebones one to get by. // is a tagged pdf. Create a barbebones one to get by.
descriptor = new Dict(null); descriptor = new Dict(null);
@ -4437,14 +4430,14 @@ class PartialEvaluator {
} }
} }
let fontMatrix = dict.getArray("FontMatrix"); const fontMatrix = lookupMatrix(
if (!isNumberArray(fontMatrix, 6)) { dict.getArray("FontMatrix"),
fontMatrix = FONT_IDENTITY_MATRIX; FONT_IDENTITY_MATRIX
} );
let bbox = descriptor.getArray("FontBBox") || dict.getArray("FontBBox"); const bbox = lookupNormalRect(
if (!isNumberArray(bbox, 4)) { descriptor.getArray("FontBBox") || dict.getArray("FontBBox"),
bbox = undefined; undefined
} );
let ascent = descriptor.get("Ascent"); let ascent = descriptor.get("Ascent");
if (typeof ascent !== "number") { if (typeof ascent !== "number") {
ascent = undefined; ascent = undefined;

View file

@ -16,14 +16,21 @@
import { import {
assert, assert,
FormatError, FormatError,
IDENTITY_MATRIX,
info, info,
unreachable, unreachable,
Util, Util,
warn, warn,
} from "../shared/util.js"; } from "../shared/util.js";
import {
isBooleanArray,
isNumberArray,
lookupMatrix,
lookupNormalRect,
MissingDataException,
} from "./core_utils.js";
import { BaseStream } from "./base_stream.js"; import { BaseStream } from "./base_stream.js";
import { ColorSpace } from "./colorspace.js"; import { ColorSpace } from "./colorspace.js";
import { MissingDataException } from "./core_utils.js";
const ShadingType = { const ShadingType = {
FUNCTION_BASED: 1, FUNCTION_BASED: 1,
@ -106,8 +113,17 @@ class BaseShading {
class RadialAxialShading extends BaseShading { class RadialAxialShading extends BaseShading {
constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) { constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) {
super(); super();
this.coordsArr = dict.getArray("Coords");
this.shadingType = dict.get("ShadingType"); this.shadingType = dict.get("ShadingType");
let coordsLen = 0;
if (this.shadingType === ShadingType.AXIAL) {
coordsLen = 4;
} else if (this.shadingType === ShadingType.RADIAL) {
coordsLen = 6;
}
this.coordsArr = dict.getArray("Coords");
if (!isNumberArray(this.coordsArr, coordsLen)) {
throw new FormatError("RadialAxialShading: Invalid /Coords array.");
}
const cs = ColorSpace.parse({ const cs = ColorSpace.parse({
cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"),
xref, xref,
@ -115,26 +131,20 @@ class RadialAxialShading extends BaseShading {
pdfFunctionFactory, pdfFunctionFactory,
localColorSpaceCache, localColorSpaceCache,
}); });
const bbox = dict.getArray("BBox"); this.bbox = lookupNormalRect(dict.getArray("BBox"), null);
this.bbox =
Array.isArray(bbox) && bbox.length === 4
? Util.normalizeRect(bbox)
: null;
let t0 = 0.0, let t0 = 0.0,
t1 = 1.0; t1 = 1.0;
if (dict.has("Domain")) { const domainArr = dict.getArray("Domain");
const domainArr = dict.getArray("Domain"); if (isNumberArray(domainArr, 2)) {
t0 = domainArr[0]; [t0, t1] = domainArr;
t1 = domainArr[1];
} }
let extendStart = false, let extendStart = false,
extendEnd = false; extendEnd = false;
if (dict.has("Extend")) { const extendArr = dict.getArray("Extend");
const extendArr = dict.getArray("Extend"); if (isBooleanArray(extendArr, 2)) {
extendStart = extendArr[0]; [extendStart, extendEnd] = extendArr;
extendEnd = extendArr[1];
} }
if ( if (
@ -271,8 +281,7 @@ class RadialAxialShading extends BaseShading {
} }
getIR() { getIR() {
const coordsArr = this.coordsArr; const { coordsArr, shadingType } = this;
const shadingType = this.shadingType;
let type, p0, p1, r0, r1; let type, p0, p1, r0, r1;
if (shadingType === ShadingType.AXIAL) { if (shadingType === ShadingType.AXIAL) {
p0 = [coordsArr[0], coordsArr[1]]; p0 = [coordsArr[0], coordsArr[1]];
@ -453,11 +462,7 @@ class MeshShading extends BaseShading {
} }
const dict = stream.dict; const dict = stream.dict;
this.shadingType = dict.get("ShadingType"); this.shadingType = dict.get("ShadingType");
const bbox = dict.getArray("BBox"); this.bbox = lookupNormalRect(dict.getArray("BBox"), null);
this.bbox =
Array.isArray(bbox) && bbox.length === 4
? Util.normalizeRect(bbox)
: null;
const cs = ColorSpace.parse({ const cs = ColorSpace.parse({
cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"),
xref, xref,
@ -983,17 +988,28 @@ class DummyShading extends BaseShading {
} }
function getTilingPatternIR(operatorList, dict, color) { function getTilingPatternIR(operatorList, dict, color) {
const matrix = dict.getArray("Matrix"); const matrix = lookupMatrix(dict.getArray("Matrix"), IDENTITY_MATRIX);
const bbox = Util.normalizeRect(dict.getArray("BBox")); const bbox = lookupNormalRect(dict.getArray("BBox"), null);
const xstep = dict.get("XStep");
const ystep = dict.get("YStep");
const paintType = dict.get("PaintType");
const tilingType = dict.get("TilingType");
// Ensure that the pattern has a non-zero width and height, to prevent errors // Ensure that the pattern has a non-zero width and height, to prevent errors
// in `pattern_helper.js` (fixes issue8330.pdf). // in `pattern_helper.js` (fixes issue8330.pdf).
if (bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) { if (!bbox || bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) {
throw new FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`); throw new FormatError(`Invalid getTilingPatternIR /BBox array.`);
}
const xstep = dict.get("XStep");
if (typeof xstep !== "number") {
throw new FormatError(`Invalid getTilingPatternIR /XStep value.`);
}
const ystep = dict.get("YStep");
if (typeof ystep !== "number") {
throw new FormatError(`Invalid getTilingPatternIR /YStep value.`);
}
const paintType = dict.get("PaintType");
if (!Number.isInteger(paintType)) {
throw new FormatError(`Invalid getTilingPatternIR /PaintType value.`);
}
const tilingType = dict.get("TilingType");
if (!Number.isInteger(tilingType)) {
throw new FormatError(`Invalid getTilingPatternIR /TilingType value.`);
} }
return [ return [

View file

@ -2465,10 +2465,9 @@ class CanvasGraphics {
this.save(); this.save();
this.baseTransformStack.push(this.baseTransform); this.baseTransformStack.push(this.baseTransform);
if (Array.isArray(matrix) && matrix.length === 6) { if (matrix) {
this.transform(...matrix); this.transform(...matrix);
} }
this.baseTransform = getCurrentTransform(this.ctx); this.baseTransform = getCurrentTransform(this.ctx);
if (bbox) { if (bbox) {
@ -2652,7 +2651,7 @@ class CanvasGraphics {
this.ctx.setTransform(...this.baseTransform); this.ctx.setTransform(...this.baseTransform);
} }
if (Array.isArray(rect) && rect.length === 4) { if (rect) {
const width = rect[2] - rect[0]; const width = rect[2] - rect[0];
const height = rect[3] - rect[1]; const height = rect[3] - rect[1];

View file

@ -455,7 +455,7 @@ class TilingPattern {
constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) { constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
this.operatorList = IR[2]; this.operatorList = IR[2];
this.matrix = IR[3] || [1, 0, 0, 1, 0, 0]; this.matrix = IR[3];
this.bbox = IR[4]; this.bbox = IR[4];
this.xstep = IR[5]; this.xstep = IR[5];
this.ystep = IR[6]; this.ystep = IR[6];