Skip to content

Commit

Permalink
Add better validation for the "PREFERENCE" kind AppOptions
Browse files Browse the repository at this point in the history
Given that the "PREFERENCE" kind is used e.g. to generate the preference-list for the Firefox PDF Viewer, those options need to be carefully validated.
With this patch we'll now check this unconditionally in development mode, during testing, and when creating the preferences in the gulpfile.
  • Loading branch information
Snuffleupagus committed Feb 19, 2024
1 parent 99fa713 commit 64ca88f
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 28 deletions.
10 changes: 8 additions & 2 deletions gulpfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -863,11 +863,17 @@ async function parseDefaultPreferences(dir) {
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
);

const browserPrefs = AppOptions.getAll(OptionKind.BROWSER);
const browserPrefs = AppOptions.getAll(
OptionKind.BROWSER,
/* defaultOnly = */ true
);
if (Object.keys(browserPrefs).length === 0) {
throw new Error("No browser preferences found.");
}
const prefs = AppOptions.getAll(OptionKind.PREFERENCE);
const prefs = AppOptions.getAll(
OptionKind.PREFERENCE,
/* defaultOnly = */ true
);
if (Object.keys(prefs).length === 0) {
throw new Error("No default preferences found.");
}
Expand Down
31 changes: 31 additions & 0 deletions test/unit/app_options_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Copyright 2024 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { AppOptions, OptionKind } from "../../web/app_options.js";
import { objectSize } from "../../src/shared/util.js";

describe("AppOptions", function () {
it("checks that getAll returns data, for every OptionKind", function () {
const KIND_NAMES = ["BROWSER", "VIEWER", "API", "WORKER", "PREFERENCE"];

for (const name of KIND_NAMES) {
const kind = OptionKind[name];
expect(typeof kind).toEqual("number");

const options = AppOptions.getAll(kind);
expect(objectSize(options)).toBeGreaterThan(0);
}
});
});
1 change: 1 addition & 0 deletions test/unit/clitests.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"annotation_spec.js",
"annotation_storage_spec.js",
"api_spec.js",
"app_options_spec.js",
"bidi_spec.js",
"cff_parser_spec.js",
"cmap_spec.js",
Expand Down
1 change: 1 addition & 0 deletions test/unit/jasmine-boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ async function initializePDFJS(callback) {
"pdfjs-test/unit/annotation_spec.js",
"pdfjs-test/unit/annotation_storage_spec.js",
"pdfjs-test/unit/api_spec.js",
"pdfjs-test/unit/app_options_spec.js",
"pdfjs-test/unit/bidi_spec.js",
"pdfjs-test/unit/cff_parser_spec.js",
"pdfjs-test/unit/cmap_spec.js",
Expand Down
60 changes: 36 additions & 24 deletions web/app_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,34 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {

const userOptions = Object.create(null);

if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING || LIB")) {
// Ensure that the `defaultOptions` are correctly specified.
for (const name in defaultOptions) {
const { value, kind } = defaultOptions[name];

if (kind & OptionKind.PREFERENCE) {
if (kind & OptionKind.BROWSER) {
throw new Error(`Cannot mix "PREFERENCE" and "BROWSER" kind: ${name}`);
}
if (compatibilityParams[name] !== undefined) {
throw new Error(
`Should not have compatibility-value for "PREFERENCE" kind: ${name}`
);
}
const valueType = typeof value;

if (
valueType === "boolean" ||
valueType === "string" ||
(valueType === "number" && Number.isInteger(value))
) {
continue; // Only "simple" values are allowed.
}
throw new Error(`Invalid value for "PREFERENCE" kind: ${name}`);
}
}
}

class AppOptions {
constructor() {
throw new Error("Cannot initialize AppOptions.");
Expand All @@ -421,36 +449,20 @@ class AppOptions {
return undefined;
}

static getAll(kind = null) {
static getAll(kind = null, defaultOnly = false) {
const options = Object.create(null);
for (const name in defaultOptions) {
const defaultOption = defaultOptions[name];
if (kind) {
if (!(kind & defaultOption.kind)) {
continue;
}
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("LIB")) &&
kind === OptionKind.PREFERENCE
) {
if (defaultOption.kind & OptionKind.BROWSER) {
throw new Error(`Invalid kind for preference: ${name}`);
}
const value = defaultOption.value,
valueType = typeof value;

if (
valueType === "boolean" ||
valueType === "string" ||
(valueType === "number" && Number.isInteger(value))
) {
options[name] = value;
continue;
}
throw new Error(`Invalid type for preference: ${name}`);
}
if (kind && !(kind & defaultOption.kind)) {
continue;
}
if (defaultOnly) {
options[name] = defaultOption.value;
continue;
}
const userOption = userOptions[name];

options[name] =
userOption !== undefined
? userOption
Expand Down
4 changes: 2 additions & 2 deletions web/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { AppOptions, OptionKind } from "./app_options.js";
class BasePreferences {
#defaults = Object.freeze(
typeof PDFJSDev === "undefined"
? AppOptions.getAll(OptionKind.PREFERENCE)
? AppOptions.getAll(OptionKind.PREFERENCE, /* defaultOnly = */ true)
: PDFJSDev.eval("DEFAULT_PREFERENCES")
);

Expand All @@ -48,7 +48,7 @@ class BasePreferences {
({ browserPrefs, prefs }) => {
const BROWSER_PREFS =
typeof PDFJSDev === "undefined"
? AppOptions.getAll(OptionKind.BROWSER)
? AppOptions.getAll(OptionKind.BROWSER, /* defaultOnly = */ true)
: PDFJSDev.eval("BROWSER_PREFERENCES");
const options = Object.create(null);

Expand Down

0 comments on commit 64ca88f

Please sign in to comment.