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.
Files changed (3) hide show
  1. package/VirtualKeyboard.js +131 -2
  2. package/main.js +43 -43
  3. package/package.json +1 -1
@@ -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
- // Fallback: store plaintext if crypto is unavailable (not recommended)
494
- input.dataset.encrypted = plaintext;
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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js_lis",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"