Skip to content

Commit 6689097

Browse files
Merge pull request #21543 from Snuffleupagus/FakeSignatureVerifier
Add a `FakeSignatureVerifier` for development mode and TESTING builds (PR 21247 follow-up)
2 parents 98ccc87 + c188012 commit 6689097

4 files changed

Lines changed: 143 additions & 2 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* Copyright 2026 Mozilla Foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import { closePages, FSI, loadAndWait, PDI } from "./test_utils.mjs";
17+
18+
describe("Digital signatures", () => {
19+
describe("Document without signatures", () => {
20+
let pages;
21+
22+
beforeEach(async () => {
23+
pages = await loadAndWait("tracemonkey.pdf", ".textLayer .endOfContent");
24+
});
25+
26+
afterEach(async () => {
27+
await closePages(pages);
28+
});
29+
30+
it("check that the signatureProperties button is hidden", async () => {
31+
await Promise.all(
32+
pages.map(async ([browserName, page]) => {
33+
const buttonHidden = await page.$eval(
34+
"#signatureProperties",
35+
el => el.hidden
36+
);
37+
expect(buttonHidden).withContext(`In ${browserName}`).toBeTrue();
38+
39+
const separatorHidden = await page.$eval(
40+
"#signaturePropertiesSeparator",
41+
el => el.hidden
42+
);
43+
expect(separatorHidden).withContext(`In ${browserName}`).toBeTrue();
44+
})
45+
);
46+
});
47+
});
48+
49+
describe("Document with signatures", () => {
50+
let pages;
51+
52+
beforeEach(async () => {
53+
pages = await loadAndWait("issue20433.pdf", ".textLayer .endOfContent");
54+
});
55+
56+
afterEach(async () => {
57+
await closePages(pages);
58+
});
59+
60+
it("check that the signatureProperties button is visible", async () => {
61+
await Promise.all(
62+
pages.map(async ([browserName, page]) => {
63+
const buttonHidden = await page.$eval(
64+
"#signatureProperties",
65+
el => el.hidden
66+
);
67+
expect(buttonHidden).withContext(`In ${browserName}`).toBeFalse();
68+
69+
const separatorHidden = await page.$eval(
70+
"#signaturePropertiesSeparator",
71+
el => el.hidden
72+
);
73+
expect(separatorHidden).withContext(`In ${browserName}`).toBeFalse();
74+
})
75+
);
76+
});
77+
78+
it("check that the signatureProperties panel contains two signatures", async () => {
79+
await Promise.all(
80+
pages.map(async ([browserName, page]) => {
81+
await page.click("#signatureProperties");
82+
await page.waitForSelector("#signaturePropertiesPanel", {
83+
hidden: false,
84+
});
85+
86+
await page.waitForFunction(
87+
`document.getElementById("signaturePropertiesBanner").textContent !== ""`
88+
);
89+
const bannerMsg = await page.$eval(
90+
"#signaturePropertiesBanner",
91+
el => el.textContent
92+
);
93+
expect(bannerMsg)
94+
.withContext(`In ${browserName}`)
95+
.toEqual(
96+
`Document signed but ${FSI}2${PDI} digital signatures could not be verified`
97+
);
98+
99+
await page.keyboard.press("Escape");
100+
await page.waitForSelector("#signaturePropertiesPanel", {
101+
hidden: true,
102+
});
103+
})
104+
);
105+
});
106+
});
107+
});

test/integration/jasmine-boot.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ async function runTests(results) {
3434
"comment_spec.mjs",
3535
"copy_paste_spec.mjs",
3636
"cursor_tools_spec.mjs",
37+
"digital_signature_spec.mjs",
3738
"document_properties_spec.mjs",
3839
"find_spec.mjs",
3940
"freetext_editor_spec.mjs",

web/external_services.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ class BaseExternalServices {
6161
* Properties UI should subclass `BaseExternalServices` and return
6262
* an object exposing `verify(signature)` (and optionally
6363
* `viewCertificate(certificate)`) that resolves to a
64-
* `VerificationResult` — see `web/firefoxcom.js` for the exact
65-
* shape.
64+
* `VerificationResult` — see `web/firefoxcom.js` for the exact shape.
6665
*
6766
* @returns {Object|null}
6867
*/

web/genericcom.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ class ExternalServices extends BaseExternalServices {
6666
createSignatureStorage(eventBus, signal) {
6767
return new SignatureStorage(eventBus, signal);
6868
}
69+
70+
createSignatureVerifier() {
71+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
72+
return new FakeSignatureVerifier();
73+
}
74+
return null;
75+
}
6976
}
7077

7178
class MLManager {
@@ -166,6 +173,33 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
166173
this.enableGuessAltText = enabled;
167174
}
168175
};
176+
177+
// eslint-disable-next-line no-var
178+
var FakeSignatureVerifier = class {
179+
async verify(signature) {
180+
if (signature.signatureType === null) {
181+
return {
182+
status: "unknown",
183+
errorCode: "SUBFILTER_NOT_SUPPORTED",
184+
message: signature.subFilter,
185+
certificate: null,
186+
documentModifiedAfterSigning: !signature.coversWholeDocument,
187+
};
188+
}
189+
190+
return {
191+
status: "unknown",
192+
errorCode: "EMPTY_RESPONSE",
193+
message: null,
194+
certificate: null,
195+
documentModifiedAfterSigning: !signature.coversWholeDocument,
196+
};
197+
}
198+
199+
async viewCertificate(certificate) {
200+
return false;
201+
}
202+
};
169203
}
170204

171205
export { ExternalServices, initCom, MLManager, Preferences };

0 commit comments

Comments
 (0)