js_lis 2.0.2 → 2.0.3
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/VirtualKeyboard.js +131 -2
- package/main.js +43 -43
- package/package.json +1 -1
package/VirtualKeyboard.js
CHANGED
|
@@ -17,11 +17,29 @@ export class VirtualKeyboard {
|
|
|
17
17
|
// Maintain plaintext buffers per input (not stored in DOM). DOM will store only encrypted text.
|
|
18
18
|
this.inputPlaintextBuffers = new WeakMap();
|
|
19
19
|
|
|
20
|
+
// Security state
|
|
21
|
+
this.security = {
|
|
22
|
+
detectorStatus: "idle",
|
|
23
|
+
suspiciousEvents: [],
|
|
24
|
+
observer: null,
|
|
25
|
+
addSuspicious(event) {
|
|
26
|
+
try {
|
|
27
|
+
this.suspiciousEvents.push({
|
|
28
|
+
time: new Date().toISOString(),
|
|
29
|
+
...event,
|
|
30
|
+
});
|
|
31
|
+
console.warn("[VK Security] Suspicious activity detected:", event);
|
|
32
|
+
} catch (_) {}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
20
36
|
this.initialize();
|
|
21
37
|
}
|
|
22
38
|
|
|
23
39
|
async initialize() {
|
|
24
40
|
try {
|
|
41
|
+
this.applyCSPReportOnly();
|
|
42
|
+
this.startKeyloggerDetector();
|
|
25
43
|
this.render();
|
|
26
44
|
this.initializeInputListeners();
|
|
27
45
|
console.log("VirtualKeyboard initialized successfully.");
|
|
@@ -30,6 +48,113 @@ export class VirtualKeyboard {
|
|
|
30
48
|
}
|
|
31
49
|
}
|
|
32
50
|
|
|
51
|
+
// Inject CSP Report-Only meta for runtime visibility into potential violations
|
|
52
|
+
applyCSPReportOnly() {
|
|
53
|
+
// Disabled: Report-Only CSP must be set via HTTP headers. See server middleware in app.js.
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Start MutationObserver and limited listener-hook to detect potential keylogger injections
|
|
58
|
+
startKeyloggerDetector() {
|
|
59
|
+
try {
|
|
60
|
+
const allowlistedScriptHosts = new Set([
|
|
61
|
+
window.location.host,
|
|
62
|
+
"cdnjs.cloudflare.com",
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
const isSuspiciousScript = (node) => {
|
|
66
|
+
try {
|
|
67
|
+
if (!(node instanceof HTMLScriptElement)) return false;
|
|
68
|
+
const src = node.getAttribute("src") || "";
|
|
69
|
+
if (!src) {
|
|
70
|
+
const code = (node.textContent || "").toLowerCase();
|
|
71
|
+
return /addEventListener\(['\"]key|document\.onkey|keylogger|send\(|fetch\(|xmlhttprequest/i.test(code);
|
|
72
|
+
}
|
|
73
|
+
const url = new URL(src, window.location.href);
|
|
74
|
+
return !Array.from(allowlistedScriptHosts).some((h) => url.host.endsWith(h));
|
|
75
|
+
} catch (_) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const isSuspiciousElement = (node) => {
|
|
81
|
+
try {
|
|
82
|
+
if (!(node instanceof Element)) return false;
|
|
83
|
+
const hasKeyHandlers = node.hasAttribute("onkeydown") || node.hasAttribute("onkeypress") || node.hasAttribute("onkeyup");
|
|
84
|
+
const hiddenFrame = node.tagName === "IFRAME" && (node.getAttribute("sandbox") === null || node.getAttribute("srcdoc") !== null);
|
|
85
|
+
return hasKeyHandlers || hiddenFrame;
|
|
86
|
+
} catch (_) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const quarantineScript = (script) => {
|
|
92
|
+
try {
|
|
93
|
+
script.type = "text/plain";
|
|
94
|
+
script.setAttribute("data-quarantined", "true");
|
|
95
|
+
} catch (_) {}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const observer = new MutationObserver((mutations) => {
|
|
99
|
+
for (const m of mutations) {
|
|
100
|
+
if (m.type === "childList") {
|
|
101
|
+
m.addedNodes.forEach((node) => {
|
|
102
|
+
if (isSuspiciousScript(node)) {
|
|
103
|
+
this.security.addSuspicious({ type: "script", reason: "suspicious source or inline pattern", node });
|
|
104
|
+
quarantineScript(node);
|
|
105
|
+
} else if (isSuspiciousElement(node)) {
|
|
106
|
+
this.security.addSuspicious({ type: "element", reason: "inline key handlers or risky frame", node });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
} else if (m.type === "attributes" && isSuspiciousElement(m.target)) {
|
|
110
|
+
this.security.addSuspicious({ type: "attr", reason: `attribute ${m.attributeName}`, node: m.target });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
observer.observe(document.documentElement, {
|
|
116
|
+
childList: true,
|
|
117
|
+
subtree: true,
|
|
118
|
+
attributes: true,
|
|
119
|
+
attributeFilter: ["onkeydown", "onkeypress", "onkeyup", "src", "type", "sandbox", "srcdoc"],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Hook addEventListener to monitor keyboard listeners registration
|
|
123
|
+
const OriginalAddEventListener = EventTarget.prototype.addEventListener;
|
|
124
|
+
const selfRef = this;
|
|
125
|
+
EventTarget.prototype.addEventListener = function(type, listener, options) {
|
|
126
|
+
try {
|
|
127
|
+
if (type && /^(key(?:down|up|press))$/i.test(type)) {
|
|
128
|
+
const info = {
|
|
129
|
+
type: "event-listener",
|
|
130
|
+
reason: `keyboard listener registered: ${type}`,
|
|
131
|
+
target: this,
|
|
132
|
+
};
|
|
133
|
+
selfRef.security.addSuspicious(info);
|
|
134
|
+
}
|
|
135
|
+
} catch (_) {}
|
|
136
|
+
return OriginalAddEventListener.call(this, type, listener, options);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
this.security.observer = observer;
|
|
140
|
+
this.security.detectorStatus = "running";
|
|
141
|
+
|
|
142
|
+
// Expose minimal security API
|
|
143
|
+
window.VKSecurity = {
|
|
144
|
+
getStatus: () => this.security.detectorStatus,
|
|
145
|
+
getEvents: () => [...this.security.suspiciousEvents],
|
|
146
|
+
stop: () => {
|
|
147
|
+
try { observer.disconnect(); } catch (_) {}
|
|
148
|
+
this.security.detectorStatus = "stopped";
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
console.info("[VK Security] Keylogger detector started");
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this.security.detectorStatus = "error";
|
|
154
|
+
console.warn("[VK Security] Detector error:", err);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
33
158
|
getLayoutName(layout) {
|
|
34
159
|
switch (layout) {
|
|
35
160
|
case "full":
|
|
@@ -486,18 +611,22 @@ export class VirtualKeyboard {
|
|
|
486
611
|
updateDomEncrypted(input, plaintext) {
|
|
487
612
|
try {
|
|
488
613
|
const crypto = window.VKCrypto;
|
|
614
|
+
|
|
489
615
|
if (crypto && typeof crypto.encrypt === "function") {
|
|
490
616
|
const cipher = plaintext.length ? crypto.encrypt(plaintext) : "";
|
|
491
617
|
input.dataset.encrypted = cipher;
|
|
492
618
|
} else {
|
|
493
|
-
//
|
|
494
|
-
|
|
619
|
+
// No encryption function available — don't store anything
|
|
620
|
+
console.warn("Encryption unavailable. Not storing plaintext.");
|
|
621
|
+
input.dataset.encrypted = "";
|
|
495
622
|
}
|
|
623
|
+
|
|
496
624
|
} catch (err) {
|
|
497
625
|
console.error("Failed to encrypt input:", err);
|
|
498
626
|
input.dataset.encrypted = "";
|
|
499
627
|
}
|
|
500
628
|
}
|
|
629
|
+
|
|
501
630
|
|
|
502
631
|
// [4]
|
|
503
632
|
unscrambleKeys() {
|
package/main.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { VirtualKeyboard } from './VirtualKeyboard.js';
|
|
2
|
-
|
|
3
|
-
// Provide simple session-scoped AES crypto utilities backed by CryptoJS (loaded globally via CDN)
|
|
4
|
-
function createSessionCrypto() {
|
|
5
|
-
const sessionKey = CryptoJS.lib.WordArray.random(32); // 256-bit key
|
|
6
|
-
return {
|
|
7
|
-
encrypt(plaintext) {
|
|
8
|
-
const iv = CryptoJS.lib.WordArray.random(16);
|
|
9
|
-
const cipher = CryptoJS.AES.encrypt(plaintext, sessionKey, {
|
|
10
|
-
iv,
|
|
11
|
-
mode: CryptoJS.mode.CBC,
|
|
12
|
-
padding: CryptoJS.pad.Pkcs7,
|
|
13
|
-
});
|
|
14
|
-
const ivHex = CryptoJS.enc.Hex.stringify(iv);
|
|
15
|
-
const ctB64 = cipher.toString();
|
|
16
|
-
return `${ivHex}:${ctB64}`;
|
|
17
|
-
},
|
|
18
|
-
decrypt(payload) {
|
|
19
|
-
if (!payload) return "";
|
|
20
|
-
const [ivHex, ctB64] = String(payload).split(":");
|
|
21
|
-
if (!ivHex || !ctB64) return "";
|
|
22
|
-
const iv = CryptoJS.enc.Hex.parse(ivHex);
|
|
23
|
-
const decrypted = CryptoJS.AES.decrypt(ctB64, sessionKey, {
|
|
24
|
-
iv,
|
|
25
|
-
mode: CryptoJS.mode.CBC,
|
|
26
|
-
padding: CryptoJS.pad.Pkcs7,
|
|
27
|
-
});
|
|
28
|
-
return decrypted.toString(CryptoJS.enc.Utf8);
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
window.onload = () => {
|
|
34
|
-
try {
|
|
35
|
-
// Initialize per-page crypto helper
|
|
36
|
-
window.VKCrypto = createSessionCrypto();
|
|
37
|
-
|
|
38
|
-
window.keyboard = new VirtualKeyboard();
|
|
39
|
-
console.log("VirtualKeyboard initialized successfully.");
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error("Error initializing VirtualKeyboard:", error);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
1
|
+
import { VirtualKeyboard } from './VirtualKeyboard.js';
|
|
2
|
+
|
|
3
|
+
// Provide simple session-scoped AES crypto utilities backed by CryptoJS (loaded globally via CDN)
|
|
4
|
+
function createSessionCrypto() {
|
|
5
|
+
const sessionKey = CryptoJS.lib.WordArray.random(32); // 256-bit key
|
|
6
|
+
return {
|
|
7
|
+
encrypt(plaintext) {
|
|
8
|
+
const iv = CryptoJS.lib.WordArray.random(16);
|
|
9
|
+
const cipher = CryptoJS.AES.encrypt(plaintext, sessionKey, {
|
|
10
|
+
iv,
|
|
11
|
+
mode: CryptoJS.mode.CBC,
|
|
12
|
+
padding: CryptoJS.pad.Pkcs7,
|
|
13
|
+
});
|
|
14
|
+
const ivHex = CryptoJS.enc.Hex.stringify(iv);
|
|
15
|
+
const ctB64 = cipher.toString();
|
|
16
|
+
return `${ivHex}:${ctB64}`;
|
|
17
|
+
},
|
|
18
|
+
decrypt(payload) {
|
|
19
|
+
if (!payload) return "";
|
|
20
|
+
const [ivHex, ctB64] = String(payload).split(":");
|
|
21
|
+
if (!ivHex || !ctB64) return "";
|
|
22
|
+
const iv = CryptoJS.enc.Hex.parse(ivHex);
|
|
23
|
+
const decrypted = CryptoJS.AES.decrypt(ctB64, sessionKey, {
|
|
24
|
+
iv,
|
|
25
|
+
mode: CryptoJS.mode.CBC,
|
|
26
|
+
padding: CryptoJS.pad.Pkcs7,
|
|
27
|
+
});
|
|
28
|
+
return decrypted.toString(CryptoJS.enc.Utf8);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
window.onload = () => {
|
|
34
|
+
try {
|
|
35
|
+
// Initialize per-page crypto helper
|
|
36
|
+
window.VKCrypto = createSessionCrypto();
|
|
37
|
+
|
|
38
|
+
window.keyboard = new VirtualKeyboard();
|
|
39
|
+
console.log("VirtualKeyboard initialized successfully.");
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("Error initializing VirtualKeyboard:", error);
|
|
42
|
+
}
|
|
43
|
+
};
|