VR keyboard extra keys: the helper holds only Ctrl/Alt, checked in allowlist.mjs

down:/up: were checked with MODKEY[arg], which inherited names like
constructor or toString pass (xdotool then got a junk key name). The check
is now allowedMod() in allowlist.mjs, covered by its test.
This commit is contained in:
Pierre Kisters committed 2026-10-01 04:09:13 +02:00
1 parent ad7e4f7678
commit 514f073a6e
3 files changed
+11 -5

No files matched your search

+3 -1
View File
@@ -7,7 +7,7 @@
// shift) with one key; or shift+Tab
// type:<char> one character Steam's key emulation can't produce: non-ASCII
// (äöü߀§°´…) or | @ { [ ] } \ ~ ^ `
// down:<mod> / up:<mod> hold/release ctrl or alt (checked in the helper)
// down:<mod> / up:<mod> hold/release ctrl or alt
const SPECIAL = new Set(['Escape', 'Delete', 'Home', 'End', 'Prior', 'Next', 'Left', 'Right', 'Up', 'Down',
...Array.from({ length: 12 }, (_, i) => `F${i + 1}`)]);
const KEY = /^([a-z0-9]|space|BackSpace|Tab|period|comma|minus|plus|numbersign|less|slash|ssharp|udiaeresis|odiaeresis|adiaeresis)$/;
@@ -27,3 +27,5 @@ export function allowedChar(c) {
const cp = c.codePointAt(0);
return (cp > 0xa0 && !/\s|\p{C}/u.test(c)) || '|@{[]}\\~^`'.includes(c);
}
export const allowedMod = (m) => m === 'ctrl' || m === 'alt';
@@ -3,7 +3,7 @@
import assert from 'node:assert/strict';
import { pathToFileURL } from 'node:url';
const { allowedCombo, allowedChar } = await import(pathToFileURL(process.argv[2]).href);
const { allowedCombo, allowedChar, allowedMod } = await import(pathToFileURL(process.argv[2]).href);
const accept = ['shift+Tab', 'ctrl+Tab', 'ctrl+shift+Tab', 'ctrl+shift+t', 'alt+f',
'shift+Left', 'Escape', 'Delete', 'shift+End', 'ctrl+BackSpace'];
// F-keys (keyboard.vr.functionKeys): any of them with any modifiers.
@@ -14,8 +14,12 @@ const reject = ['Tab', 'shift+a', 'a', 'Return', 'shift+Return', 'ctrl+Return',
'F0', 'F13', 'F24', 'f5', 'super+F4', 'ctrl+ctrl+F5', 'F5+Return', 'ctrl+alt+shift+Return'];
const acceptChar = ['ä', '€', '|', '@', '\\', '`'];
const rejectChar = ['a', 'A', '1', ' ', '\n', '\t', 'ab', ' '];
const acceptMod = ['ctrl', 'alt'];
const rejectMod = ['shift', 'super', 'Control_L', 'constructor', '__proto__', 'toString', ''];
for (const c of accept) assert.equal(allowedCombo(c), true, `accepts key:${c}`);
for (const c of reject) assert.equal(allowedCombo(c), false, `rejects key:${c}`);
for (const c of acceptChar) assert.equal(allowedChar(c), true, `accepts type:${c}`);
for (const c of rejectChar) assert.equal(allowedChar(c), false, `rejects type:${JSON.stringify(c)}`);
console.log(`allowlist: ${accept.length + reject.length + acceptChar.length + rejectChar.length} cases passed`);
for (const m of acceptMod) assert.equal(allowedMod(m), true, `accepts down:${m}`);
for (const m of rejectMod) assert.equal(allowedMod(m), false, `rejects down:${m}`);
console.log(`allowlist: ${accept.length + reject.length + acceptChar.length + rejectChar.length + acceptMod.length + rejectMod.length} cases passed`);
@@ -8,7 +8,7 @@
// usage: node xdotool-helper.mjs <patch.js> <unpatch.js> [xdotool]
import { readFileSync } from 'node:fs';
import { execFile } from 'node:child_process';
import { allowedCombo, allowedChar } from './allowlist.mjs';
import { allowedCombo, allowedChar, allowedMod } from './allowlist.mjs';
const [, , patchPath, unpatchPath, xdotool = 'xdotool'] = process.argv;
const PATCH = readFileSync(patchPath, 'utf8');
@@ -37,7 +37,7 @@ function handle(msg) {
return xdo(['key', '--', [...parts.filter((m) => !held.has(m)), key].join('+')]);
}
if (op === 'type' && allowedChar(arg)) return xdo(['type', '--', arg]);
if ((op === 'down' || op === 'up') && MODKEY[arg]) {
if ((op === 'down' || op === 'up') && allowedMod(arg)) {
if (op === 'down') held.add(arg); else held.delete(arg);
return xdo([op === 'down' ? 'keydown' : 'keyup', MODKEY[arg]]);
}