Found a security issue? Please open a GitHub issue (or contact BiswasHost at https://www.biswashost.com). We'll respond promptly.
This project is a macOS keyboard‑layout installer. It has a deliberately small attack surface:
- No network access, no telemetry, no analytics, no auto‑update.
- No credentials handled or stored.
- The only privileged actions are: installing two
.keylayoutfiles + free fonts into/Library, and (optionally) removing them again.
| Component | Runs as | Notes |
|---|---|---|
src/build/scripts/preinstall |
root (during .pkg install) |
Only rm -f of this project's own hard‑coded layout paths. No dynamic input. |
src/build/scripts/postinstall |
root (during .pkg install) |
Only absolute‑path atsutil calls to refresh the font cache. No dynamic input. |
Bangla Keyboard Installer.app (src/build/installer.applescript) |
admin (via osascript … with administrator privileges) |
Install/Reinstall/Uninstall. All filesystem paths are passed to AppleScript as arguments and shell‑escaped with quoted form of — see below. |
The installer app never interpolates a path directly into a privileged shell
string. Paths are passed as osascript arguments and quoted by AppleScript
itself:
on run argv
do shell script "/usr/sbin/installer -pkg " & quoted form of (item 1 of argv) & " -target /" with administrator privileges
end runThis means a path containing quotes, spaces, or shell metacharacters cannot
break out of the command (verified with a '; touch …; ' payload during
development). The uninstall path uses the same pattern for every file it removes.
- The
.keylayoutfiles are XML consumed by macOS's text‑input engine. They contain only the standard system DTD reference (no external entities / XXE) and key→character mappings. The low‑byte�..;outputs in Bangla Classic are legacy font glyph codes — data, never executed. - Fonts are free/libre TrueType files from the projects listed in
fonts-licenses/FONTS.md.
The Windows build (windows/, shipped as bangla-tray.exe) is a tray keyboard
that uses a global low-level keyboard hook (WH_KEYBOARD_LL) — the standard way an
input method sees keystrokes. Audit findings:
- No keystroke logging, storage, or transmission (typing path). For keyboard
typing the app holds only the current syllable in memory and clears it on every
word boundary — no file, network, or telemetry in the hook/engine path. The tray
reads one registry value (
HKCU\Software\BanglaKeyboard\VoiceEnabled) to decide whether to start the optional voice companion, and launches it by a fixed image name (bangla-voice.exe) from its own folder. No auto-update, no analytics. - No DLL-hijacking surface. Both exes call
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32)at startup, so implicit DLL loads resolve fromSystem32only (no app-dir/CWD planting).bangla-tray.exeis statically linked and imports only system DLLs (kernel32,user32,gdi32,shell32,advapi32,msvcrt). The optionalbangla-voice.exeloads exactly one non-system DLL —WebView2Loader.dll, a Microsoft redistributable — and does so by its full install-folder path (not by bare name), so the search order can't be hijacked. - Least privilege. Runs as the normal user (no elevation); the installer is
per-user (
%LocalAppData%), so installing/uninstalling needs no admin. - Exception-safe hook. The hook callback is wrapped so no C++ exception can escape into the OS input dispatch (a crash there would affect every app); on any error it drops its pending state and stays alive.
- Bounded buffers. The in-progress run is capped (1024 chars) so a long burst or stuck auto-repeat can't grow memory/CPU unbounded.
- Engine input safety. The reordering engine is driven by compile-time-constant
tables generated from the macOS
.keylayoutfiles; the only runtime input is the user's own scan codes (bounded to one byte). No untrusted data crosses into it. - Installer. No remote code/downloads; the only external calls are fixed
taskkill /im bangla-tray.exeand/im bangla-voice.exe(constant image names — no injection) to close the running apps before install/uninstall.
Opt-in at install (default on; untick to omit the files entirely). It is a separate process so a WebView2 issue can never disturb the keyboard hook. Audit notes:
- Mic only while listening. The microphone is opened only after you press a voice
shortcut and is released the moment you stop; an off-screen WebView2 (loading only
our local
voice.htmlover a virtual host) does the capture. A tray icon is shown whenever the mic is live. - Nothing recorded or stored. Audio is held in memory just long enough to send one spoken sentence to the speech service; the recognized text is typed and the buffer dropped. No audio or transcript is written to disk, and nothing is logged.
- Network scope is the speech service only. English uses the browser's built-in
Web Speech API; Bangla POSTs each utterance (16 kHz PCM) over HTTPS to Google's
free legacy speech endpoint (the same one the open-source
recognize_googleuses) to get correct Bangladeshi text. No other host is contacted; no account, key entry, or payment is involved. This is a free, unofficial endpoint, so it may be rate-limited — the app then falls back to the browser's Bangla recognizer. - Local page only. The WebView2 loads exactly one file we ship (
voice.html); it browses nothing and renders no remote content.--autoplay-policyis the only browser flag set (so capture starts without a click); web security is not disabled — the cross-origin POST is done from native code, not the page.
The optional TSF IME DLL (windows/tsf/, not shipped in the release) is an
in-proc COM keyboard service; it is experimental and excluded from binary releases
until reviewed and code-signed.
The optional macOS voice companion (macos/voice/) is a menu-bar agent that captures
the mic while a mode is on and types what you say. Audited surfaces:
- No keystroke logging. Unlike the Windows tray app, it installs no keyboard
hook — the macOS keyboard is the passive
.keylayout. The voice app only captures audio (when listening) and injects recognized text. - One network destination, HTTPS only. Bangla audio is POSTed to Google's free
legacy endpoint (
https://www.google.com/speech-api/v2/recognize,STT.swift) — the only host in the code. English uses Apple's on-deviceSFSpeechRecognizer(requiresOnDeviceRecognitionwhen supported → audio never leaves the Mac); it only falls back to the same Googleen-USendpoint if on-device speech is unauthorized/ unavailable. No telemetry, no analytics, no other hosts. - Nothing is stored. No file writes, no
UserDefaults/Keychain/plist data, no logging of audio or transcripts (verified — zeroprint/NSLog/file-write calls). The STT call uses an ephemeralURLSession(no disk cache, no cookies), so neither the audio nor the response touches disk. - Mic is live only while listening. Each mode stop removes the audio tap and stops the engine; a menu-bar mic indicator shows the state (blue listening / green speaking).
- Injection is bounded. Recognized text is typed via
CGEventUnicode events with control characters (< U+0020) stripped, so a bad STT response can't inject Return/Tab/escape sequences. - Least privilege + explicit consent. Microphone, Speech Recognition, and Accessibility (needed to type into other apps) are each requested via the system TCC prompt; if denied, the app shows a clear alert and does nothing — it never proceeds without the grant. The shared Google key is the well-known public Chromium key, not a secret.
The Linux build (linux/, shipped as ibus-engine-bangla) is an IBus input-method
engine that reuses the shared C++ KLEngine, plus an optional voice-typing feature
(mic + online STT, off until you press Ctrl+Alt+S/D). Audit findings:
- No keystroke logging, storage, or transmission. The keyboard path only maps scan codes to Bangla via compile-time-constant tables and commits the result through IBus — no file, network, socket, or process-exec calls anywhere in the typing path. No telemetry. (The only network use is the voice feature below, and only while active.)
- Voice typing (opt-in, off by default). Audio is captured (PulseAudio/PipeWire) and
a short recognized phrase sent — only while you are actively dictating — over TLS
to the same free online speech service the Windows/macOS builds use (
stt_curl.h, native libcurl; TLS peer/host verification is libcurl-default-on, redirects are not followed, and the request URL is a hardcoded constant with no user input → no SSRF). Nothing is recorded to disk; the mic stream stops the moment you toggle voice off, switch away from the engine, or the engine shuts down (the capture thread is joined). The utterance buffer is capped at 30 s so continuous audio can't grow memory or the POST unbounded. Recognized text is only committed as text (never interpreted/exec'd). - No shell in the "voice on/off" toast.
notify-sendis spawned viag_spawn_asyncwith an explicitargv(nog_shell_parse_argv, no/bin/sh), and the message is a fixed literal — so no command/argument injection is possible. - Runs as the user, not root, not setuid.
ibus-daemonlaunches the engine as the logged-in user. The binary at/usr/lib/ibus/ibus-engine-banglahas mode 0755 (not setuid/setgid), so it grants no elevated privilege. - Bounds-safe. IBus passes an untrusted keycode;
scan = keycode - 8can underflow to a large unsigned, butKLEngine::wouldHandle/processreject anyscan > 0xFFbefore indexing, so there is no out-of-bounds read. The in-progress preedit run is capped (1024) against unbounded growth from a stuck key. - Exception-safe. The
process_key_eventhandler (andcommit_run) are wrapped so no C++ exception can unwind into IBus's C dispatch; on any error the engine drops its pending state and stays alive. - Installer.
install.shruns as root but performs only fixed-pathinstall/file writes and emits a static component XML (no dynamic/user input flows into any command); it builds the binary as the invoking non-root user (SUDO_USER), never as root. No downloads, no remote code.
-
Windows: unsigned + keylogger-shaped tech. The tray app is not code-signed yet (SmartScreen warns; More info → Run anyway), and a global keyboard hook is the same Windows API a keylogger uses — so some antivirus may heuristically flag it. It does not exfiltrate anything (see the audit above); a code-signing certificate + reputation removes both the SmartScreen prompt and most AV noise.
-
Voice typing is online + third-party. With voice enabled, spoken audio is sent to a third-party speech service (the browser's Web Speech provider for English; Google's free legacy endpoint for Bangla) to be transcribed — inherent to any cloud STT. Nothing is stored locally, but if you would rather not send audio anywhere, leave the voice option unticked at install (the feature is then fully absent). The Bangla endpoint is unofficial and may be throttled; the app degrades gracefully.
-
Unsigned distribution. The
.pkgand.commandare not code‑signed or notarized, so Gatekeeper shows an "unidentified developer" warning and users must right‑click → Open the first time. If you build a signed/notarized copy with an Apple Developer ID, that warning goes away. This is a trust/UX item, not a code vulnerability. -
Because installation writes to
/Library, an admin password is required — this is expected for system‑wide keyboard layouts and fonts.