nodebb-plugin-pdf-secure 1.2.5 → 1.2.6

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/library.js CHANGED
@@ -26,6 +26,14 @@ plugin.init = async (params) => {
26
26
  console.error('[PDF-Secure] Failed to cache viewer template:', err.message);
27
27
  }
28
28
 
29
+ // Double slash bypass protection - catches /uploads//files/ attempts
30
+ router.use((req, res, next) => {
31
+ if (req.path.includes('//') && req.path.toLowerCase().includes('.pdf')) {
32
+ return res.status(403).json({ error: 'Invalid path' });
33
+ }
34
+ next();
35
+ });
36
+
29
37
  // PDF direct access blocker middleware
30
38
  // Intercepts requests to uploaded PDF files and returns 403
31
39
  router.get('/assets/uploads/files/:filename', (req, res, next) => {
@@ -115,4 +123,35 @@ plugin.addAdminNavigation = (header) => {
115
123
  return header;
116
124
  };
117
125
 
126
+ // Filter meta tags to hide PDF URLs and filenames
127
+ plugin.filterMetaTags = async (hookData) => {
128
+ if (!hookData || !hookData.tags) {
129
+ return hookData;
130
+ }
131
+
132
+ // Filter out PDF-related meta tags
133
+ hookData.tags = hookData.tags.filter(tag => {
134
+ // Remove og:image if it contains .pdf
135
+ if (tag.property === 'og:image' && tag.content && tag.content.toLowerCase().includes('.pdf')) {
136
+ return false;
137
+ }
138
+ // Remove twitter:image if it contains .pdf
139
+ if (tag.name === 'twitter:image' && tag.content && tag.content.toLowerCase().includes('.pdf')) {
140
+ return false;
141
+ }
142
+ return true;
143
+ });
144
+
145
+ // Sanitize description to hide .pdf extensions
146
+ hookData.tags = hookData.tags.map(tag => {
147
+ if ((tag.name === 'description' || tag.property === 'og:description') && tag.content) {
148
+ // Replace .pdf extension with empty string in description
149
+ tag.content = tag.content.replace(/\.pdf/gi, '');
150
+ }
151
+ return tag;
152
+ });
153
+
154
+ return hookData;
155
+ };
156
+
118
157
  module.exports = plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-pdf-secure",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "Secure PDF viewer plugin for NodeBB - prevents downloading, enables canvas-only rendering with Premium group support",
5
5
  "main": "library.js",
6
6
  "repository": {
package/plugin.json CHANGED
@@ -14,6 +14,10 @@
14
14
  {
15
15
  "hook": "filter:admin.header.build",
16
16
  "method": "addAdminNavigation"
17
+ },
18
+ {
19
+ "hook": "filter:meta.getMetaTags",
20
+ "method": "filterMetaTags"
17
21
  }
18
22
  ],
19
23
  "staticDirs": {
@@ -1334,6 +1334,34 @@
1334
1334
  (function () {
1335
1335
  'use strict';
1336
1336
 
1337
+ // ============================================
1338
+ // CANVAS EXPORT PROTECTION
1339
+ // Block toDataURL/toBlob for PDF render canvas only
1340
+ // Allows: thumbnails, annotations, other canvases
1341
+ // ============================================
1342
+ const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
1343
+ const originalToBlob = HTMLCanvasElement.prototype.toBlob;
1344
+
1345
+ HTMLCanvasElement.prototype.toDataURL = function () {
1346
+ // Block only main PDF page canvases (inside .page elements in #viewerContainer)
1347
+ if (this.closest && this.closest('.page') && this.closest('#viewerContainer')) {
1348
+ console.warn('[Security] Canvas toDataURL blocked for PDF page');
1349
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='; // 1x1 transparent
1350
+ }
1351
+ return originalToDataURL.apply(this, arguments);
1352
+ };
1353
+
1354
+ HTMLCanvasElement.prototype.toBlob = function (callback) {
1355
+ // Block only main PDF page canvases
1356
+ if (this.closest && this.closest('.page') && this.closest('#viewerContainer')) {
1357
+ console.warn('[Security] Canvas toBlob blocked for PDF page');
1358
+ // Return empty blob
1359
+ if (callback) callback(new Blob([], { type: 'image/png' }));
1360
+ return;
1361
+ }
1362
+ return originalToBlob.apply(this, arguments);
1363
+ };
1364
+
1337
1365
  pdfjsLib.GlobalWorkerOptions.workerSrc = '';
1338
1366
 
1339
1367
  // State - now private, not accessible from console
@@ -1520,6 +1548,10 @@
1520
1548
  // Security: Delete config containing sensitive data (nonce, key)
1521
1549
  delete window.PDF_SECURE_CONFIG;
1522
1550
 
1551
+ // Security: Remove PDF.js globals to prevent console manipulation
1552
+ delete window.pdfjsLib;
1553
+ delete window.pdfjsViewer;
1554
+
1523
1555
  // Security: Block dangerous PDF.js methods
1524
1556
  if (pdfDoc) {
1525
1557
  pdfDoc.getData = function () {