From 194e2ede4da18eb83d56c68c09b7459d521a9864 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Mon, 23 Jun 2025 20:54:30 +0200 Subject: [PATCH] Add some telemetry in order to know what are the certificates used in pdfs (bug 1973573) --- src/core/document.js | 62 ++++++++++++++++++++++++++++++++++++++++++++ web/app.js | 7 +++++ 2 files changed, 69 insertions(+) diff --git a/src/core/document.js b/src/core/document.js index b26acd3dd..5234f5ed4 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -1099,6 +1099,49 @@ class PDFDocument { }); } + #collectSignatureCertificates( + fields, + collectedSignatureCertificates, + visited = new RefSet() + ) { + if (!Array.isArray(fields)) { + return; + } + for (let field of fields) { + if (field instanceof Ref) { + if (visited.has(field)) { + continue; + } + visited.put(field); + } + field = this.xref.fetchIfRef(field); + if (!(field instanceof Dict)) { + continue; + } + if (field.has("Kids")) { + this.#collectSignatureCertificates( + field.get("Kids"), + collectedSignatureCertificates, + visited + ); + continue; + } + const isSignature = isName(field.get("FT"), "Sig"); + if (!isSignature) { + continue; + } + const value = field.get("V"); + if (!(value instanceof Dict)) { + continue; + } + const subFilter = value.get("SubFilter"); + if (!(subFilter instanceof Name)) { + continue; + } + collectedSignatureCertificates.add(subFilter.name); + } + } + get _xfaStreams() { const { acroForm } = this.catalog; if (!acroForm) { @@ -1414,6 +1457,20 @@ class PDFDocument { // specification). const sigFlags = acroForm.get("SigFlags"); const hasSignatures = !!(sigFlags & 0x1); + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { + if (hasSignatures) { + const collectedSignatureCertificates = new Set(); + this.#collectSignatureCertificates( + fields, + collectedSignatureCertificates + ); + if (collectedSignatureCertificates.size > 0) { + formInfo.collectedSignatureCertificates = Array.from( + collectedSignatureCertificates + ); + } + } + } const hasOnlyDocumentSignatures = hasSignatures && this.#hasOnlyDocumentSignatures(fields); formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures; @@ -1441,6 +1498,11 @@ class PDFDocument { IsSignaturesPresent: formInfo.hasSignatures, }; + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { + docInfo.collectedSignatureCertificates = + formInfo.collectedSignatureCertificates ?? null; + } + let infoDict; try { infoDict = xref.trailer.get("Info"); diff --git a/web/app.js b/web/app.js index bd8cf0068..084f981d0 100644 --- a/web/app.js +++ b/web/app.js @@ -1637,6 +1637,13 @@ const PDFViewerApplication = { if (pdfDocument !== this.pdfDocument) { return; // The document was closed while the metadata resolved. } + if (info.collectedSignatureCertificates) { + this.externalServices.reportTelemetry({ + type: "signatureCertificates", + data: info.collectedSignatureCertificates, + }); + } + this.documentInfo = info; this.metadata = metadata; this._contentDispositionFilename ??= contentDispositionFilename;