nodebb-plugin-pdf-secure 1.1.6 → 1.2.0
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/.claude/settings.local.json +4 -1
- package/lib/controllers.js +2 -26
- package/library.js +8 -2
- package/package.json +1 -1
- package/static/lib/viewer.js +3 -63
- package/static/style.less +1 -136
- package/static/viewer.html +171 -0
package/lib/controllers.js
CHANGED
|
@@ -34,38 +34,14 @@ Controllers.servePdfBinary = async function (req, res) {
|
|
|
34
34
|
pdfBuffer = await pdfHandler.getSinglePagePdf(data.file);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// PARTIAL XOR Encryption: Sadece ilk ve son 8KB'ı şifrele
|
|
38
|
-
// Bu, PDF'yi açılamaz hale getirir ama performans etkisi minimal
|
|
39
|
-
const encryptionKey = Buffer.from(nonce.slice(0, 8));
|
|
40
|
-
const encryptedBuffer = Buffer.from(pdfBuffer); // Copy
|
|
41
|
-
const chunkSize = 8192; // 8KB
|
|
42
|
-
|
|
43
|
-
// İlk 8KB'ı şifrele (PDF header - CRITICAL)
|
|
44
|
-
const headerEnd = Math.min(chunkSize, pdfBuffer.length);
|
|
45
|
-
for (let i = 0; i < headerEnd; i++) {
|
|
46
|
-
encryptedBuffer[i] = pdfBuffer[i] ^ encryptionKey[i % encryptionKey.length];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Son 8KB'ı şifrele (PDF xref table - CRITICAL)
|
|
50
|
-
const footerStart = Math.max(0, pdfBuffer.length - chunkSize);
|
|
51
|
-
for (let i = footerStart; i < pdfBuffer.length; i++) {
|
|
52
|
-
encryptedBuffer[i] = pdfBuffer[i] ^ encryptionKey[i % encryptionKey.length];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
37
|
res.set({
|
|
56
38
|
'Content-Type': 'application/octet-stream',
|
|
57
|
-
'Cache-Control': 'no-store, no-cache, must-revalidate, private
|
|
58
|
-
'Pragma': 'no-cache',
|
|
59
|
-
'Expires': '0',
|
|
39
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, private',
|
|
60
40
|
'X-Content-Type-Options': 'nosniff',
|
|
61
|
-
'X-Download-Options': 'noopen',
|
|
62
|
-
'X-Frame-Options': 'SAMEORIGIN',
|
|
63
|
-
'Content-Security-Policy': "default-src 'none'",
|
|
64
41
|
'Content-Disposition': 'inline',
|
|
65
|
-
'X-PDF-Encrypted': '1', // İşaretleyici
|
|
66
42
|
});
|
|
67
43
|
|
|
68
|
-
return res.send(
|
|
44
|
+
return res.send(pdfBuffer);
|
|
69
45
|
} catch (err) {
|
|
70
46
|
if (err.message === 'File not found') {
|
|
71
47
|
return res.status(404).json({ error: 'PDF not found' });
|
package/library.js
CHANGED
|
@@ -42,11 +42,17 @@ plugin.init = async (params) => {
|
|
|
42
42
|
return res.status(400).send('Invalid file');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// Serve the viewer template with security headers
|
|
45
|
+
// Serve the viewer template with comprehensive security headers
|
|
46
46
|
res.set({
|
|
47
47
|
'X-Frame-Options': 'SAMEORIGIN',
|
|
48
48
|
'X-Content-Type-Options': 'nosniff',
|
|
49
|
-
'
|
|
49
|
+
'X-XSS-Protection': '1; mode=block',
|
|
50
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, private, max-age=0',
|
|
51
|
+
'Pragma': 'no-cache',
|
|
52
|
+
'Expires': '0',
|
|
53
|
+
'Referrer-Policy': 'no-referrer',
|
|
54
|
+
'Permissions-Policy': 'accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()',
|
|
55
|
+
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; img-src 'self' data: blob:; connect-src 'self'; frame-ancestors 'self'",
|
|
50
56
|
});
|
|
51
57
|
|
|
52
58
|
// Read and send the viewer HTML
|
package/package.json
CHANGED
package/static/lib/viewer.js
CHANGED
|
@@ -32,7 +32,6 @@ function interceptPdfLinks() {
|
|
|
32
32
|
|
|
33
33
|
var container = document.createElement('div');
|
|
34
34
|
container.className = 'pdf-secure-inline';
|
|
35
|
-
container.setAttribute('tabindex', '0'); // Focus için gerekli
|
|
36
35
|
container.innerHTML =
|
|
37
36
|
'<div class="pdf-secure-inline-header">' +
|
|
38
37
|
'<i class="fa fa-file-pdf-o"></i> ' +
|
|
@@ -41,10 +40,7 @@ function interceptPdfLinks() {
|
|
|
41
40
|
'<div class="pdf-secure-inline-body">' +
|
|
42
41
|
'<div class="pdf-secure-loading">Loading PDF...</div>' +
|
|
43
42
|
'<div class="pdf-secure-error"></div>' +
|
|
44
|
-
'<
|
|
45
|
-
'<canvas class="pdf-secure-canvas"></canvas>' +
|
|
46
|
-
'<div class="pdf-secure-overlay"></div>' +
|
|
47
|
-
'</div>' +
|
|
43
|
+
'<canvas class="pdf-secure-canvas"></canvas>' +
|
|
48
44
|
'</div>' +
|
|
49
45
|
'<div class="pdf-secure-inline-footer">' +
|
|
50
46
|
'<button class="pdf-secure-prev" disabled>‹ Prev</button>' +
|
|
@@ -110,29 +106,8 @@ async function loadPdf(container, filename) {
|
|
|
110
106
|
return;
|
|
111
107
|
}
|
|
112
108
|
|
|
113
|
-
var
|
|
114
|
-
console.log('[PDF-Secure][Viewer] Step 2 -
|
|
115
|
-
|
|
116
|
-
// PARTIAL XOR Decryption: Sadece ilk ve son 8KB'ı decrypt et
|
|
117
|
-
var encryptedBytes = new Uint8Array(encryptedArrayBuffer);
|
|
118
|
-
var encryptionKey = new TextEncoder().encode(nonce.slice(0, 8));
|
|
119
|
-
var decryptedBytes = new Uint8Array(encryptedBytes); // Copy
|
|
120
|
-
var chunkSize = 8192; // 8KB
|
|
121
|
-
|
|
122
|
-
// İlk 8KB'ı decrypt et (PDF header)
|
|
123
|
-
var headerEnd = Math.min(chunkSize, encryptedBytes.length);
|
|
124
|
-
for (var i = 0; i < headerEnd; i++) {
|
|
125
|
-
decryptedBytes[i] = encryptedBytes[i] ^ encryptionKey[i % encryptionKey.length];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Son 8KB'ı decrypt et (PDF xref table)
|
|
129
|
-
var footerStart = Math.max(0, encryptedBytes.length - chunkSize);
|
|
130
|
-
for (var i = footerStart; i < encryptedBytes.length; i++) {
|
|
131
|
-
decryptedBytes[i] = encryptedBytes[i] ^ encryptionKey[i % encryptionKey.length];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
var pdfArrayBuffer = decryptedBytes.buffer;
|
|
135
|
-
console.log('[PDF-Secure][Viewer] Step 2 - Decrypted PDF:', pdfArrayBuffer.byteLength, 'bytes');
|
|
109
|
+
var pdfArrayBuffer = await pdfRes.arrayBuffer();
|
|
110
|
+
console.log('[PDF-Secure][Viewer] Step 2 - PDF loaded:', pdfArrayBuffer.byteLength, 'bytes');
|
|
136
111
|
|
|
137
112
|
// Step 3: Render PDF
|
|
138
113
|
console.log('[PDF-Secure][Viewer] Step 3 - Rendering...');
|
|
@@ -147,41 +122,6 @@ async function loadPdf(container, filename) {
|
|
|
147
122
|
container.addEventListener('contextmenu', function (e) { e.preventDefault(); });
|
|
148
123
|
container.addEventListener('dragstart', function (e) { e.preventDefault(); });
|
|
149
124
|
container.addEventListener('selectstart', function (e) { e.preventDefault(); });
|
|
150
|
-
container.addEventListener('copy', function (e) { e.preventDefault(); });
|
|
151
|
-
|
|
152
|
-
// Keyboard shortcuts engelleme (Ctrl+S, Ctrl+P, Ctrl+C, Print Screen)
|
|
153
|
-
container.addEventListener('keydown', function (e) {
|
|
154
|
-
// Ctrl/Cmd + S (Save)
|
|
155
|
-
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
156
|
-
e.preventDefault();
|
|
157
|
-
console.log('[PDF-Secure] Save blocked');
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
// Ctrl/Cmd + P (Print)
|
|
161
|
-
if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
console.log('[PDF-Secure] Print blocked');
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
// Ctrl/Cmd + C (Copy)
|
|
167
|
-
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
|
|
168
|
-
e.preventDefault();
|
|
169
|
-
console.log('[PDF-Secure] Copy blocked');
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
// Print Screen
|
|
173
|
-
if (e.key === 'PrintScreen') {
|
|
174
|
-
e.preventDefault();
|
|
175
|
-
console.log('[PDF-Secure] PrintScreen blocked');
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
// F12 (DevTools)
|
|
179
|
-
if (e.key === 'F12') {
|
|
180
|
-
e.preventDefault();
|
|
181
|
-
console.log('[PDF-Secure] F12 blocked');
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
125
|
|
|
186
126
|
var ctx = canvas.getContext('2d');
|
|
187
127
|
var currentPage = 1;
|
package/static/style.less
CHANGED
|
@@ -136,142 +136,7 @@
|
|
|
136
136
|
|
|
137
137
|
/* Anti-print */
|
|
138
138
|
@media print {
|
|
139
|
-
.pdf-secure-embed
|
|
140
|
-
.pdf-secure-inline {
|
|
139
|
+
.pdf-secure-embed {
|
|
141
140
|
display: none !important;
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
|
-
|
|
145
|
-
/* Inline PDF Viewer Styles */
|
|
146
|
-
.pdf-secure-inline {
|
|
147
|
-
margin: 20px 0;
|
|
148
|
-
border: 1px solid #ddd;
|
|
149
|
-
border-radius: 8px;
|
|
150
|
-
overflow: hidden;
|
|
151
|
-
background: #f5f5f5;
|
|
152
|
-
user-select: none;
|
|
153
|
-
-webkit-user-select: none;
|
|
154
|
-
-moz-user-select: none;
|
|
155
|
-
-ms-user-select: none;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.pdf-secure-inline-header {
|
|
159
|
-
display: flex;
|
|
160
|
-
align-items: center;
|
|
161
|
-
gap: 8px;
|
|
162
|
-
padding: 12px 16px;
|
|
163
|
-
background: linear-gradient(to bottom, #fff, #f8f8f8);
|
|
164
|
-
border-bottom: 1px solid #ddd;
|
|
165
|
-
font-weight: 500;
|
|
166
|
-
color: #333;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.pdf-secure-inline-header i {
|
|
170
|
-
color: #e81224;
|
|
171
|
-
font-size: 18px;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.pdf-secure-filename {
|
|
175
|
-
flex: 1;
|
|
176
|
-
overflow: hidden;
|
|
177
|
-
text-overflow: ellipsis;
|
|
178
|
-
white-space: nowrap;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.pdf-secure-inline-body {
|
|
182
|
-
position: relative;
|
|
183
|
-
min-height: 400px;
|
|
184
|
-
background: #fff;
|
|
185
|
-
display: flex;
|
|
186
|
-
align-items: center;
|
|
187
|
-
justify-content: center;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
.pdf-secure-loading {
|
|
191
|
-
position: absolute;
|
|
192
|
-
top: 50%;
|
|
193
|
-
left: 50%;
|
|
194
|
-
transform: translate(-50%, -50%);
|
|
195
|
-
color: #666;
|
|
196
|
-
font-size: 14px;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.pdf-secure-error {
|
|
200
|
-
display: none;
|
|
201
|
-
position: absolute;
|
|
202
|
-
top: 50%;
|
|
203
|
-
left: 50%;
|
|
204
|
-
transform: translate(-50%, -50%);
|
|
205
|
-
color: #e81224;
|
|
206
|
-
font-size: 14px;
|
|
207
|
-
text-align: center;
|
|
208
|
-
padding: 20px;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.pdf-secure-canvas-wrapper {
|
|
212
|
-
position: relative;
|
|
213
|
-
display: inline-block;
|
|
214
|
-
/* Canvas wrapper'ı güvenlik için kapsayıcı */
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.pdf-secure-canvas {
|
|
218
|
-
display: none;
|
|
219
|
-
max-width: 100%;
|
|
220
|
-
height: auto;
|
|
221
|
-
/* Canvas üzerinde sağ tık ve sürüklemeyi engelle */
|
|
222
|
-
pointer-events: auto;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.pdf-secure-overlay {
|
|
226
|
-
position: absolute;
|
|
227
|
-
top: 0;
|
|
228
|
-
left: 0;
|
|
229
|
-
right: 0;
|
|
230
|
-
bottom: 0;
|
|
231
|
-
background: transparent;
|
|
232
|
-
/* Görünmez koruma katmanı - tüm mouse eventlerini yakalar */
|
|
233
|
-
pointer-events: auto;
|
|
234
|
-
cursor: default;
|
|
235
|
-
z-index: 1;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.pdf-secure-inline-footer {
|
|
239
|
-
display: none;
|
|
240
|
-
align-items: center;
|
|
241
|
-
justify-content: center;
|
|
242
|
-
gap: 16px;
|
|
243
|
-
padding: 12px;
|
|
244
|
-
background: #f8f8f8;
|
|
245
|
-
border-top: 1px solid #ddd;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.pdf-secure-prev,
|
|
249
|
-
.pdf-secure-next {
|
|
250
|
-
padding: 6px 12px;
|
|
251
|
-
background: #fff;
|
|
252
|
-
border: 1px solid #ddd;
|
|
253
|
-
border-radius: 4px;
|
|
254
|
-
cursor: pointer;
|
|
255
|
-
font-size: 14px;
|
|
256
|
-
color: #333;
|
|
257
|
-
transition: all 0.2s;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.pdf-secure-prev:hover:not(:disabled),
|
|
261
|
-
.pdf-secure-next:hover:not(:disabled) {
|
|
262
|
-
background: #f0f0f0;
|
|
263
|
-
border-color: #999;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.pdf-secure-prev:disabled,
|
|
267
|
-
.pdf-secure-next:disabled {
|
|
268
|
-
opacity: 0.4;
|
|
269
|
-
cursor: not-allowed;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.pdf-secure-page-info {
|
|
273
|
-
font-size: 14px;
|
|
274
|
-
color: #666;
|
|
275
|
-
min-width: 60px;
|
|
276
|
-
text-align: center;
|
|
277
|
-
}
|
package/static/viewer.html
CHANGED
|
@@ -3047,7 +3047,178 @@
|
|
|
3047
3047
|
|
|
3048
3048
|
console.log('PDF Viewer Ready');
|
|
3049
3049
|
console.log('Keyboard Shortcuts: H=Highlight, P=Pen, E=Eraser, T=Text, R=Shapes, S=Sidebar, M=ReadingMode, Arrows=Navigate');
|
|
3050
|
+
|
|
3051
|
+
// ==========================================
|
|
3052
|
+
// SECURITY FEATURES
|
|
3053
|
+
// ==========================================
|
|
3054
|
+
|
|
3055
|
+
(function initSecurityFeatures() {
|
|
3056
|
+
console.log('[Security] Initializing protection features...');
|
|
3057
|
+
|
|
3058
|
+
// 1. Block dangerous keyboard shortcuts
|
|
3059
|
+
document.addEventListener('keydown', function (e) {
|
|
3060
|
+
// Ctrl+S (Save)
|
|
3061
|
+
if (e.ctrlKey && e.key === 's') {
|
|
3062
|
+
e.preventDefault();
|
|
3063
|
+
console.log('[Security] Ctrl+S blocked');
|
|
3064
|
+
return false;
|
|
3065
|
+
}
|
|
3066
|
+
// Ctrl+P (Print)
|
|
3067
|
+
if (e.ctrlKey && e.key === 'p') {
|
|
3068
|
+
e.preventDefault();
|
|
3069
|
+
console.log('[Security] Ctrl+P blocked');
|
|
3070
|
+
return false;
|
|
3071
|
+
}
|
|
3072
|
+
// Ctrl+Shift+S (Save As)
|
|
3073
|
+
if (e.ctrlKey && e.shiftKey && e.key === 'S') {
|
|
3074
|
+
e.preventDefault();
|
|
3075
|
+
return false;
|
|
3076
|
+
}
|
|
3077
|
+
// F12 (DevTools)
|
|
3078
|
+
if (e.key === 'F12') {
|
|
3079
|
+
e.preventDefault();
|
|
3080
|
+
console.log('[Security] F12 blocked');
|
|
3081
|
+
return false;
|
|
3082
|
+
}
|
|
3083
|
+
// Ctrl+Shift+I (DevTools)
|
|
3084
|
+
if (e.ctrlKey && e.shiftKey && e.key === 'I') {
|
|
3085
|
+
e.preventDefault();
|
|
3086
|
+
return false;
|
|
3087
|
+
}
|
|
3088
|
+
// Ctrl+Shift+J (Console)
|
|
3089
|
+
if (e.ctrlKey && e.shiftKey && e.key === 'J') {
|
|
3090
|
+
e.preventDefault();
|
|
3091
|
+
return false;
|
|
3092
|
+
}
|
|
3093
|
+
// Ctrl+U (View Source)
|
|
3094
|
+
if (e.ctrlKey && e.key === 'u') {
|
|
3095
|
+
e.preventDefault();
|
|
3096
|
+
return false;
|
|
3097
|
+
}
|
|
3098
|
+
// Ctrl+Shift+C (Inspect Element)
|
|
3099
|
+
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
|
|
3100
|
+
e.preventDefault();
|
|
3101
|
+
return false;
|
|
3102
|
+
}
|
|
3103
|
+
}, true);
|
|
3104
|
+
|
|
3105
|
+
// 2. Block context menu (right-click)
|
|
3106
|
+
document.addEventListener('contextmenu', function (e) {
|
|
3107
|
+
// Allow our custom context menu for annotations
|
|
3108
|
+
if (e.target.closest('.annotationLayer') || e.target.closest('.pdfViewer')) {
|
|
3109
|
+
// Keep custom context menu behavior
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
e.preventDefault();
|
|
3113
|
+
return false;
|
|
3114
|
+
}, true);
|
|
3115
|
+
|
|
3116
|
+
// 3. Block copy/cut/paste
|
|
3117
|
+
document.addEventListener('copy', function (e) {
|
|
3118
|
+
e.preventDefault();
|
|
3119
|
+
console.log('[Security] Copy blocked');
|
|
3120
|
+
return false;
|
|
3121
|
+
}, true);
|
|
3122
|
+
|
|
3123
|
+
document.addEventListener('cut', function (e) {
|
|
3124
|
+
e.preventDefault();
|
|
3125
|
+
return false;
|
|
3126
|
+
}, true);
|
|
3127
|
+
|
|
3128
|
+
// 4. Block drag events (prevent dragging content out)
|
|
3129
|
+
document.addEventListener('dragstart', function (e) {
|
|
3130
|
+
e.preventDefault();
|
|
3131
|
+
return false;
|
|
3132
|
+
}, true);
|
|
3133
|
+
|
|
3134
|
+
// 5. DevTools detection (basic)
|
|
3135
|
+
let devToolsOpen = false;
|
|
3136
|
+
const threshold = 160;
|
|
3137
|
+
|
|
3138
|
+
function checkDevTools() {
|
|
3139
|
+
const widthThreshold = window.outerWidth - window.innerWidth > threshold;
|
|
3140
|
+
const heightThreshold = window.outerHeight - window.innerHeight > threshold;
|
|
3141
|
+
|
|
3142
|
+
if (widthThreshold || heightThreshold) {
|
|
3143
|
+
if (!devToolsOpen) {
|
|
3144
|
+
devToolsOpen = true;
|
|
3145
|
+
console.log('[Security] DevTools detected');
|
|
3146
|
+
// Optional: blur content when devtools open
|
|
3147
|
+
document.body.classList.add('devtools-open');
|
|
3148
|
+
}
|
|
3149
|
+
} else {
|
|
3150
|
+
if (devToolsOpen) {
|
|
3151
|
+
devToolsOpen = false;
|
|
3152
|
+
document.body.classList.remove('devtools-open');
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
// Check periodically
|
|
3158
|
+
setInterval(checkDevTools, 1000);
|
|
3159
|
+
window.addEventListener('resize', checkDevTools);
|
|
3160
|
+
|
|
3161
|
+
// 6. Block Print via window.print override
|
|
3162
|
+
window.print = function () {
|
|
3163
|
+
console.log('[Security] Print function blocked');
|
|
3164
|
+
alert('Yazdırma bu belgede engellenmiştir.');
|
|
3165
|
+
return false;
|
|
3166
|
+
};
|
|
3167
|
+
|
|
3168
|
+
// 7. Disable beforeprint event
|
|
3169
|
+
window.addEventListener('beforeprint', function (e) {
|
|
3170
|
+
e.preventDefault();
|
|
3171
|
+
document.body.style.display = 'none';
|
|
3172
|
+
});
|
|
3173
|
+
|
|
3174
|
+
window.addEventListener('afterprint', function () {
|
|
3175
|
+
document.body.style.display = '';
|
|
3176
|
+
});
|
|
3177
|
+
|
|
3178
|
+
// 8. Block screenshot keyboard shortcuts
|
|
3179
|
+
document.addEventListener('keyup', function (e) {
|
|
3180
|
+
// PrintScreen key
|
|
3181
|
+
if (e.key === 'PrintScreen') {
|
|
3182
|
+
navigator.clipboard.writeText('');
|
|
3183
|
+
console.log('[Security] PrintScreen clipboard cleared');
|
|
3184
|
+
}
|
|
3185
|
+
}, true);
|
|
3186
|
+
|
|
3187
|
+
// 9. Visibility change detection (tab switching for screenshots)
|
|
3188
|
+
document.addEventListener('visibilitychange', function () {
|
|
3189
|
+
if (document.hidden) {
|
|
3190
|
+
// User switched tabs - could be for screenshot tools
|
|
3191
|
+
console.log('[Security] Tab hidden');
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
|
|
3195
|
+
console.log('[Security] All protection features initialized');
|
|
3196
|
+
})();
|
|
3050
3197
|
</script>
|
|
3198
|
+
|
|
3199
|
+
<!-- Security CSS for DevTools detection -->
|
|
3200
|
+
<style>
|
|
3201
|
+
.devtools-open #viewerContainer,
|
|
3202
|
+
.devtools-open #viewer,
|
|
3203
|
+
.devtools-open .pdfViewer {
|
|
3204
|
+
filter: blur(20px) !important;
|
|
3205
|
+
pointer-events: none !important;
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
.devtools-open::after {
|
|
3209
|
+
content: 'Geliştirici araçları açıkken içerik görüntülenemez.';
|
|
3210
|
+
position: fixed;
|
|
3211
|
+
top: 50%;
|
|
3212
|
+
left: 50%;
|
|
3213
|
+
transform: translate(-50%, -50%);
|
|
3214
|
+
background: rgba(0, 0, 0, 0.9);
|
|
3215
|
+
color: #fff;
|
|
3216
|
+
padding: 30px 50px;
|
|
3217
|
+
border-radius: 10px;
|
|
3218
|
+
font-size: 18px;
|
|
3219
|
+
z-index: 99999;
|
|
3220
|
+
}
|
|
3221
|
+
</style>
|
|
3051
3222
|
</body>
|
|
3052
3223
|
|
|
3053
3224
|
</html>
|