nodebb-plugin-pdf-secure 1.2.25 → 1.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/controllers.js +2 -6
- package/package.json +1 -1
- package/static/viewer.html +50 -9
package/lib/controllers.js
CHANGED
|
@@ -45,12 +45,8 @@ Controllers.servePdfBinary = async function (req, res) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
pdfBuffer = await pdfHandler.getFullPdf(data.file);
|
|
51
|
-
} else {
|
|
52
|
-
pdfBuffer = await pdfHandler.getSinglePagePdf(data.file);
|
|
53
|
-
}
|
|
48
|
+
// Always send full PDF - page restriction is handled client-side
|
|
49
|
+
const pdfBuffer = await pdfHandler.getFullPdf(data.file);
|
|
54
50
|
|
|
55
51
|
// Apply partial XOR encryption with dynamic key from nonce
|
|
56
52
|
const encodedBuffer = partialXorEncode(pdfBuffer, data.xorKey);
|
package/package.json
CHANGED
package/static/viewer.html
CHANGED
|
@@ -2262,7 +2262,7 @@
|
|
|
2262
2262
|
}
|
|
2263
2263
|
|
|
2264
2264
|
function resetOverlayCSS(overlay) {
|
|
2265
|
-
if (!isOverlayTampered(overlay)) return;
|
|
2265
|
+
if (!isOverlayTampered(overlay)) return;
|
|
2266
2266
|
overlay.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;display:flex;z-index:15;opacity:1;visibility:visible;pointer-events:auto;';
|
|
2267
2267
|
}
|
|
2268
2268
|
|
|
@@ -2271,15 +2271,12 @@
|
|
|
2271
2271
|
pages.forEach(function (page) {
|
|
2272
2272
|
var pageNum = parseInt(page.dataset.pageNumber || '0', 10);
|
|
2273
2273
|
if (pageNum > 1) {
|
|
2274
|
-
// Ensure page is visible (undo any old display:none)
|
|
2275
2274
|
if (page.style.display === 'none') {
|
|
2276
2275
|
page.style.display = '';
|
|
2277
2276
|
}
|
|
2278
2277
|
injectPageLock(page);
|
|
2279
|
-
// CSS integrity: reset overlay styles in case of tampering
|
|
2280
2278
|
var existing = page.querySelector('.page-lock-overlay');
|
|
2281
2279
|
if (existing) resetOverlayCSS(existing);
|
|
2282
|
-
// Ensure blur class is present
|
|
2283
2280
|
if (!page.classList.contains('page-locked-blur')) {
|
|
2284
2281
|
page.classList.add('page-locked-blur');
|
|
2285
2282
|
}
|
|
@@ -2326,13 +2323,11 @@
|
|
|
2326
2323
|
for (var i = 0; i < mutations.length; i++) {
|
|
2327
2324
|
var target = mutations[i].target;
|
|
2328
2325
|
if (!target || !target.classList) continue;
|
|
2329
|
-
// Overlay CSS tampered - reset only if values actually differ
|
|
2330
2326
|
if (target.classList.contains('page-lock-overlay')) {
|
|
2331
2327
|
if (isOverlayTampered(target)) {
|
|
2332
2328
|
resetOverlayCSS(target);
|
|
2333
2329
|
}
|
|
2334
2330
|
}
|
|
2335
|
-
// Blur class removed from locked page - re-add it
|
|
2336
2331
|
if (target.classList.contains('page') && !target.classList.contains('page-locked-blur')) {
|
|
2337
2332
|
var pageNum = parseInt(target.dataset.pageNumber || '0', 10);
|
|
2338
2333
|
if (pageNum > 1) {
|
|
@@ -2343,6 +2338,47 @@
|
|
|
2343
2338
|
}).observe(viewerEl, { subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
|
|
2344
2339
|
}
|
|
2345
2340
|
|
|
2341
|
+
// ============================================
|
|
2342
|
+
// LITE MODE: Hide all tools except fullscreen and zoom
|
|
2343
|
+
// Lite users can view full PDF but cannot use annotations,
|
|
2344
|
+
// sidebar, rotate, sepia, overflow menu, etc.
|
|
2345
|
+
// ============================================
|
|
2346
|
+
function applyLiteMode() {
|
|
2347
|
+
// Hide sidebar button
|
|
2348
|
+
var sidebarBtn = document.getElementById('sidebarBtn');
|
|
2349
|
+
if (sidebarBtn) sidebarBtn.style.display = 'none';
|
|
2350
|
+
|
|
2351
|
+
// Close sidebar if open
|
|
2352
|
+
var sidebarEl = document.getElementById('sidebar');
|
|
2353
|
+
if (sidebarEl) sidebarEl.classList.remove('open');
|
|
2354
|
+
|
|
2355
|
+
// Hide entire annotation tools group (highlight, draw, eraser, select, undo/redo, text, shapes)
|
|
2356
|
+
var annotationGroup = document.querySelector('#toolbar > .toolbarGroup:nth-child(3)');
|
|
2357
|
+
if (annotationGroup) annotationGroup.style.display = 'none';
|
|
2358
|
+
|
|
2359
|
+
// In the zoom/utility group, hide everything except zoomIn and zoomOut
|
|
2360
|
+
var keepIds = new Set(['zoomIn', 'zoomOut']);
|
|
2361
|
+
var utilityGroup = document.querySelector('#toolbar > .toolbarGroup:nth-child(5)');
|
|
2362
|
+
if (utilityGroup) {
|
|
2363
|
+
Array.from(utilityGroup.children).forEach(function (child) {
|
|
2364
|
+
if (!keepIds.has(child.id)) {
|
|
2365
|
+
child.style.display = 'none';
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// Hide bottom toolbar annotation tools (mobile), keep only fullscreen button
|
|
2371
|
+
var bottomToolbarInner = document.getElementById('bottomToolbarInner');
|
|
2372
|
+
if (bottomToolbarInner) bottomToolbarInner.style.display = 'none';
|
|
2373
|
+
|
|
2374
|
+
// Hide all top-level separators between groups
|
|
2375
|
+
document.querySelectorAll('#toolbar > .separator').forEach(function (sep) {
|
|
2376
|
+
sep.style.display = 'none';
|
|
2377
|
+
});
|
|
2378
|
+
|
|
2379
|
+
console.log('[PDF-Secure] Lite mode applied - restricted toolbar');
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2346
2382
|
// Auto-load PDF if config is present (injected by NodeBB plugin)
|
|
2347
2383
|
async function autoLoadSecurePDF() {
|
|
2348
2384
|
if (!_cfg || !_cfg.filename) {
|
|
@@ -2423,7 +2459,7 @@
|
|
|
2423
2459
|
}
|
|
2424
2460
|
|
|
2425
2461
|
// Send buffer to parent for caching (premium only - non-premium must not leak decoded buffer)
|
|
2426
|
-
if (_cfg.isPremium !== false && window.parent && window.parent !== window) {
|
|
2462
|
+
if ((_cfg.isPremium !== false || _cfg.isLite) && window.parent && window.parent !== window) {
|
|
2427
2463
|
// Clone buffer for parent (we keep original)
|
|
2428
2464
|
const bufferCopy = pdfBuffer.slice(0);
|
|
2429
2465
|
window.parent.postMessage({
|
|
@@ -2439,8 +2475,8 @@
|
|
|
2439
2475
|
// Step 4: Load into viewer
|
|
2440
2476
|
await loadPDFFromBuffer(pdfBuffer);
|
|
2441
2477
|
|
|
2442
|
-
// Premium Gate: Client-side page restriction for non-premium users
|
|
2443
|
-
if (config.isPremium === false && pdfDoc && pdfDoc.numPages > 1) {
|
|
2478
|
+
// Premium Gate: Client-side page restriction for non-premium, non-lite users
|
|
2479
|
+
if (config.isPremium === false && !config.isLite && pdfDoc && pdfDoc.numPages > 1) {
|
|
2444
2480
|
premiumInfo = Object.freeze({ isPremium: false, totalPages: pdfDoc.numPages });
|
|
2445
2481
|
applyPageLocks();
|
|
2446
2482
|
startPeriodicCheck();
|
|
@@ -2449,6 +2485,11 @@
|
|
|
2449
2485
|
premiumInfo = Object.freeze({ isPremium: true, totalPages: pdfDoc ? pdfDoc.numPages : 1 });
|
|
2450
2486
|
}
|
|
2451
2487
|
|
|
2488
|
+
// Lite Mode: Full PDF access but restricted toolbar
|
|
2489
|
+
if (config.isLite) {
|
|
2490
|
+
applyLiteMode();
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2452
2493
|
// Step 5: Moved to pagerendered event for proper timing
|
|
2453
2494
|
|
|
2454
2495
|
// Step 6: Security - clear references to prevent extraction
|