The signing tool keeps no signature on the device (#1, bau-pm BAU-260)
PDF.js's signature dialog saved every signature in localStorage under pdfjs.signature, "save" ticked on every open, for this origin rather than for a bau-pm user. Measured on v0.1.0: a typed and an uploaded signature were offered to a fresh page on the same browser, the way the next person on a shared tablet would get it, a Redmine logout included. Another browser profile saw nothing; the server never had anything. The glue's fifth duty, in every window before PDF.js's modules: delete the store (a device that saved signatures loses them on its first visit), refuse writes to that key, hold the save box unticked through a property on the element and hide it, and hide the dialog's Bild tab and its file picker, so a scanned signature never enters the dialog. test/signatures.mjs is the second gate, in CI and the release workflow: a browser seeded with a real saved entry (captured from v0.1.0's own store) is offered none, the dialog shows neither the box nor the tab, a typed signature is placed and not stored, a fresh page is offered nothing. Fed v0.1.0: 9 findings, exit 1. Against this image: 0, and the zero-host gate drove 117 tool pages with nothing leaving the origin. A signed PDF still exports with the signature on the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ac6d57562b
commit
4248bc791a
8 files changed
+197
-5
No files matched your search
+51
-1
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* Loaded by every page of this image, the PDF.js viewers' pages inside the
|
||||
* tools included, as a classic script in <head>, so it runs before BentoPDF's
|
||||
* own modules. It does four things:
|
||||
* own modules. It does five things:
|
||||
*
|
||||
* 1. A link to this image's source on every page, because the AGPL asks the
|
||||
* source of a modified version served over a network to be offered
|
||||
@@ -20,6 +20,14 @@
|
||||
* blob is read inside click(), because the next line revokes its URL.
|
||||
* 4. For the signing tool, BentoPDF's "flatten" switched on and locked, so a
|
||||
* signature becomes part of the page and cannot be moved later.
|
||||
* 5. No signature kept on the device. PDF.js's signature editor saves up to
|
||||
* five signatures in localStorage, ticked by default, for this origin and
|
||||
* not for a bau-pm user, so the next person on a shared tablet would find
|
||||
* the last one's signature one click away, a Redmine logout included
|
||||
* (bau-pm PLAN.md §2.77, Gitea #1). Every window deletes what is stored,
|
||||
* refuses the store's writes, and in the viewer the "save" box is held off
|
||||
* and hidden. The dialog's image tab is hidden too, so a scanned
|
||||
* signature never enters the tool: signing means drawing or typing it.
|
||||
*
|
||||
* Nothing here talks to anything but this origin. The words of the hand-off
|
||||
* come from bau-pm in the user's language; only the source link has its own.
|
||||
@@ -31,6 +39,7 @@
|
||||
var COOKIE = '__Host-bau_pm_handoff';
|
||||
var API = '/bau-pm/handoff';
|
||||
var SOURCE_URL = '__BAU_PM_SOURCE_URL__';
|
||||
var SIGNATURES = 'pdfjs.signature';
|
||||
var isTop = window.top === window;
|
||||
|
||||
// --- shared state --------------------------------------------------------
|
||||
@@ -72,6 +81,9 @@
|
||||
return nativeClick.apply(this, arguments);
|
||||
};
|
||||
|
||||
forgetSignatures();
|
||||
ready(signatureDialog);
|
||||
|
||||
if (!isTop) {
|
||||
signFrame();
|
||||
return;
|
||||
@@ -294,6 +306,44 @@
|
||||
})();
|
||||
}
|
||||
|
||||
// --- 5. no signature kept on the device ---------------------------------
|
||||
|
||||
// Runs before PDF.js's modules in every window, so the viewer reads an empty
|
||||
// store, and a device that saved signatures before this glue loses them on
|
||||
// its first visit. The write is refused rather than undone afterwards: a
|
||||
// signature that is never stored cannot be read by the next page either.
|
||||
function forgetSignatures() {
|
||||
try {
|
||||
window.localStorage.removeItem(SIGNATURES);
|
||||
var nativeSetItem = Storage.prototype.setItem;
|
||||
Storage.prototype.setItem = function (key) {
|
||||
if (String(key) === SIGNATURES) { return undefined; }
|
||||
return nativeSetItem.apply(this, arguments);
|
||||
};
|
||||
} catch (e) { /* storage blocked: then nothing can be kept either */ }
|
||||
}
|
||||
|
||||
// The dialog sets the box to "save" each time it opens; a property of the
|
||||
// element's own shadows the prototype's, so it reads unticked whatever is
|
||||
// assigned to it. Only PDF.js's viewer has these elements.
|
||||
function signatureDialog() {
|
||||
var save = document.getElementById('addSignatureSaveCheckbox');
|
||||
if (!save) { return; }
|
||||
Object.defineProperty(save, 'checked', {
|
||||
configurable: true,
|
||||
get: function () { return false; },
|
||||
set: function () {}
|
||||
});
|
||||
hide(document.getElementById('addSignatureSaveContainer'));
|
||||
hide(document.getElementById('addSignatureImageButton'));
|
||||
var picker = document.getElementById('addSignatureFilePicker');
|
||||
if (picker) { picker.disabled = true; }
|
||||
}
|
||||
|
||||
function hide(element) {
|
||||
if (element) { element.style.setProperty('display', 'none', 'important'); }
|
||||
}
|
||||
|
||||
// --- 3. the hand-back's sheet --------------------------------------------
|
||||
|
||||
function button(label, kind) {
|
||||
|
||||
Reference in new issue
Block a user