![]()
I think its more of “or something” than just disabling wifi ![]()
![]()
I think its more of “or something” than just disabling wifi ![]()
v1.2.4 - shortcuts via SYSTEM tasks, injected hotkey, kiosk zoom
Bundles three related “make the admin surface actually usable” fixes
plus a small feature, all blockers the kiosk hit in production after
the v1.2.3 elevation fix landed.
Desktop shortcuts now route through pre-registered scheduled tasks
running as NT AUTHORITY\SYSTEM with HighestAvailable. Each .lnk’s
TargetPath is schtasks.exe /Run /TN KioskExitGuard_; the
task body invokes a new --shortcut-handler entry that picks
the active user session and spawns the flag’s existing handler into
it via spawnFlagAsUserInSession (sibling of spawnControllerInSession,
reuses the v1.2.3 elevatedLinkedToken swap). Net effect for split-
token admin kiosks: no UAC on shortcut click, no “Access is denied”
on Resume after pause. Shortcuts also moved from the per-user
desktop to CommonDesktopDirectory so every logged-in user sees them.
Known limitation: true non-admin Standard User accounts still hit
ERROR_ELEVATION_REQUIRED at CreateProcessAsUser; same failure mode
as v1.2.3 for that case, full fix needs a manifest flip + SYSTEM-
side action dispatcher (tracked as future work).
Ctrl+Shift+Alt+K pause hotkey now fires from injected keyboard
input (AnyDesk SendInput, AutoHotkey, etc.). A narrow carve-out at
hookCallback’s top detects the K+all-three-modifiers chord on
key-down regardless of LLKHF_INJECTED state and triggers
promptAndPause(). Every other injected key still falls through to
procCallNextHookEx unmodified - typing/paste/mouse macros via
AnyDesk keep working. GetAsyncKeyState reflects injected modifier
state, so ctrlDown/shiftDown/altDown observe the SendInput-set
modifiers correctly. ourInjection excluded so a re-inject path
carrying the same combo can’t loop.
Kiosk page zoom (50-200%, default 100%) persisted in HKLM as
KioskZoom DWORD. First-run wizard grew a number input next to the
URL field; --set-url shortcut now also prompts for zoom. runWebView-
Kiosk reads the percent and injects an IIFE inside w.Init that
sets document.documentElement.style.zoom on both DOMContentLoaded
and load - SPA frameworks that re-render the root can’t strip the
inline style. Existing installs default to 100%, no behavior change
until an admin sets a value.
Bundles the four queued items from v1.2.8: WebView2 URL/zoom form, six-hour log rotation in a logs/ subdirectory, version-suffixed desktop shortcut filenames, and double-right-click → pause via a new LL mouse hook.
--set-url previously chained two zenity.Entry dialogs (one for URL, one for zoom). Native Windows dialogs center themselves on the screen with no programmatic positioning, so an AnyDesk admin whose session bar overlapped the middle of the viewport couldn’t reliably reach the input. v1.2.9 replaces both with runSetURLAndZoomDialog — a single branded WebView2 form matching the password modal’s color scheme, with URL + zoom inputs, validation messages, and a fixed top-aligned layout (.wrap { align-items: flex-start; padding-top: 4vh }). Fall-back path: if webview2.NewWithOptions returns nil (WebView2 runtime broken), we drop to the legacy zenity prompt so admins on stripped-down boxes still have a path to change the URL.
<install>/logs/initLogging no longer writes to a single growing kiosk-exit-guard.log next to the exe. Instead it creates a logs/ subdirectory and writes per-bucket files named kiosk-exit-guard-YYYY-MM-DD-HH.log where HH ∈ {00, 06, 12, 18} — four files per day. logf checks the bucket label against the currently-open file on every write and rolls over when the boundary crosses. The previous “5 MB → .log.old” size-based rotation handed back a single coarse blob; v1.2.9’s time buckets let an admin investigating a field report jump straight to the relevant slice.
New log lines on rollover: --- rolled into new bucket v%s pid=%d --- so each bucket file is self-describing for the reader.
cleanupInstallDir’s allowed-subdirs list grew logs so uninstall can wipe the rotated files alongside the staging directory.
Each .lnk createDesktopShortcut writes is now named <action> v<version>.lnk — e.g. Pause SK Filter v1.2.9.lnk, Update SK Filter v1.2.9.lnk. A glance at the desktop tells you the installed version. removeStalePerUserShortcuts and removeDesktopShortcuts switched from a fixed seven-name list to two glob patterns per action (<action>.lnk legacy + <action> v*.lnk versioned) so an upgrade cleans up old-version files alongside the pre-v1.2.9 unversioned ones. Remove-Item -ErrorAction SilentlyContinue absorbs missing-file cases.
New mouseCallback installed via SetWindowsHookExW(WH_MOUSE_LL, …) alongside the keyboard hook. Watches WM_RBUTTONDOWN events and on a second right-button-down within 500 ms and ≤30 screen pixels of the previous one, fires promptAndPause — the same flow Ctrl+Shift+Alt+K triggers. Events pass through unmodified (we don’t swallow right-clicks); the prompt opens on top.
Honors both LLMHF_INJECTED (0x01) and LLMHF_LOWER_IL_INJECTED (0x02), so AnyDesk-forwarded right-clicks trigger the prompt the same way local hardware clicks do — the deliberate mouse-side admin escape hatch matching the keyboard side’s K hotkey carve-out.
Hook install failure is non-fatal: a WARN: SetWindowsHookEx (mouse LL) failed: %v line lands in the log and the keyboard hook (the primary enforcement path) continues running.
Admin hotkeys + AnyDesk reliability + top-aligned password modal for the URL-change flow.
Two siblings of the existing Ctrl+Shift+Alt+K pause hotkey. Both spawn the corresponding --update / --set-url invocation as a detached child via the new launchSelfWithFlag helper, so the action runs in the user’s session with its own password modal + GitHub fetch (for update) or password modal + URL/zoom entry (for set-url). All three hotkeys (K, U, C) work from both local physical keyboard and injected input (AnyDesk, AutoHotkey).
v1.2.4 added an injected-key carve-out for the pause hotkey but checked only LLKHF_INJECTED (0x10). AnyDesk’s keyboard-forwarding worker runs at user-medium integrity level while the kiosk runs at user-high IL (it carries a requireAdministrator manifest), so remote-typed keys actually arrive with LLKHF_LOWER_IL_INJECTED (0x02) in the LL hook struct flags — not 0x10. The carve-out silently never fired.
v1.2.8 introduces llkhfAnyInject = llkhfInject | llkhfLowerIlInject and treats either bit as “injected” for the K/U/C carve-outs. New logf lines at the injected-detection site (hook: injected Ctrl+Shift+Alt+<K|U|C> detected (flags=0x%02x) — triggering ...) make the path observable in kiosk-exit-guard.log so a future no-hotkey-from-AnyDesk report can be diagnosed in one log read.
New askPasswordModalTop(title, subtitle) is the top-aligned sibling of askPasswordModal. The child --ask-password process receives the hint via env var KEG_ASK_PASSWORD_TOP=1; the password HTML reads window.__topAlign and adds a .top class on .wrap that switches align-items from center to flex-start with padding-top: 4vh. Used by the --set-url flow so admins driving via AnyDesk can reach the password input even when AnyDesk’s session bar overlays the center of the viewport.
zenity.Entry (native Win32 dialog, no programmatic positioning). Replacing it with a WebView2 form that lays out at the top is queued.logs/ subfolder) not yet implemented — the log file still grows indefinitely as kiosk-exit-guard.log.Disable Win+L (lock workstation) via the canonical Windows registry policy. Win+L is intercepted by winlogon.exe at a layer beneath the LL keyboard hook — the v1.2.6 always-block + Win-modifier swallow can suppress the visible Start menu hop, but the lock initiates anyway because winlogon sees the key combo first. The proper Windows mechanism is the policy HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableLockWorkstation = 1, which disables Win+L and the Start menu’s “Lock” entry at the OS level. v1.2.7 sets this in applyLockdown alongside the existing DisableTaskMgr / NoRun / NoTrayContextMenu / NoViewContextMenu / NoTaskbar policies, and clears it in removeLockdown so an uninstall/reset restores the lock-screen shortcut.
The pre-existing modifier+key block already catches everything else in the Win+key family (Win+R, Win+E, Win+I, Win+D, Win+X, Win+., Win+Tab) — the LL hook swallows them and routes through the password-prompt path. Win+L was the one outlier because of the winlogon-level interception. Ctrl+Alt+Del (Secure Attention Sequence) remains genuinely unblockable from user mode — it requires a kernel-mode filter driver, which is outside scope for kiosk-exit- guard.
Lockdown widening: bare F1–F12, Tab, Escape, AppMenu (right-click keyboard key), PrintScreen, and Insert are now swallowed and route through the password-prompt path, even with no modifier held. Pre- v1.2.6 the LL keyboard hook only caught <modifier> + key combos, so a bare F11 (toggle browser fullscreen), F12 (DevTools), Tab (focus next form field / address bar in normal Edge), or Escape (exit fullscreen / close modals) all fell straight through to the kiosk URL.
isAlwaysBlockedKey(vk) helper in main.go returns true for the keys above. The hook calls it inside the existing filterMode.Load() branch, before the modifier+key block. Match triggers promptAndReinject (the same UX path as Win+R etc.) so an admin can authenticate and let the key through.isAlwaysAllowedCombo lost its bare-F5 case. F5 alone was an intentional v1.0 carve-out for page reload, but it conflicted with the new always-block list. Admins who want a manual reload still have Ctrl+R.sendKeyCombo already handles the bare-key re-injection case (no modifiers in the slice → just press+release the VK with the kiosk-exit-guard marker so the hook lets it through).!injected && !ourInjection branch.UI/UX audit pass for unusual viewports (sideways 4K TVs at 300% display scaling, low-DPI 1080p landscape, etc.), plus a zoom-target fix that prevents kiosk pages from accidentally double-zooming themselves on top of the v1.2.4 admin-configured zoom, plus stale-shortcut cleanup so an upgrade from <v1.2.4 doesn’t leave the old direct-to-exe .lnks alongside the new schtasks-routed ones.
v1.2.4’s passwordPromptHTML and autoUpdateNotifyHTML both declared .card { width: 100% } with no max. On a 1920×1080 landscape kiosk the password input stretched into a tubular 1800px-wide field with the lock icon floating at the far left and action buttons at the far right. v1.2.5 caps both at max-width: 540px. The first-run wizard card grew from 520px → 560px to fit the new zoom field added in v1.2.4 without crowding the help text.
All three modal <wrap> containers switched from height: 100vh + overflow: hidden (which clipped the top of the card when content exceeded viewport height) to min-height: 100vh + overflow-y: auto. The first-run wizard’s 7-field form (pw1, pw2, url, zoom, error, actions, plus header) is the biggest beneficiary — on a tight portrait viewport or with accessibility text scaling, the card now scrolls naturally instead of vanishing off the top edge.
v1.2.4 injected document.documentElement.style.zoom = pct / 100 — i.e. zoom on <html>. A kiosk page that also runs its own document.body.style.zoom = '0.9' fallback (so the page renders at 90% in regular browsers without kiosk-exit-guard installed) would land on <body> while we landed on <html>. CSS zoom compounds across nested elements, so the rendered scale became <html> × <body> — e.g. 0.9 × 0.9 = 0.81 instead of the intended 0.9.
v1.2.5 targets document.body.style.zoom instead. Both code paths now write to the same element, so the values don’t multiply. Admin config still wins: the injection fires at DOMContentLoaded AND window.load, both of which run after any inline <script> in the page body, so an admin-configured 80% overrides a page-side 90% to 80%. A page-side idempotence check (parseFloat(document.body.style.zoom)) now correctly observes our value on SPA re-renders.
v1.2.4 moved the shortcut .lnks from the running user’s desktop to CommonDesktopDirectory (so every logged-in user sees them). But pre-v1.2.4 installs wrote to the per-user desktop with TargetPath = kiosk-exit-guard.exe (direct, UAC-triggering); upgrading to v1.2.4 left those orphans next to the new public-desktop ones, so the user saw double shortcuts and the old direct-to-exe set still fired UAC.
v1.2.5’s removeStalePerUserShortcuts() runs at the top of createDesktopShortcut (which is called on every controller startup that isn’t a Service spawn) and deletes the seven legacy .lnk filenames from [Environment]::GetFolderPath('Desktop'). Then the new schtasks-routed .lnks get written to CommonDesktopDirectory as in v1.2.4. Idempotent — missing files are absorbed by Remove-Item -ErrorAction SilentlyContinue.
Limitation: only the currently-running user’s per-user desktop is cleaned. A multi-user kiosk where a different admin originally ran the wizard still has stale .lnks on that other admin’s desktop until they log in once with v1.2.5+.
Drop-in zoom override file + two diagnostic log lines.
%ProgramData%\KioskExitGuard\zoom.txt — write a single number into it (e.g. 90, 90%, trailing comment/whitespace tolerated) and the kiosk renders at that page-zoom on the next --webview launch, no need to go through the --set-url WebView2 form. Precedence: zoom.txt wins over the registry KioskZoom DWORD AND syncs back into it, so the --set-url form’s pre-fill stays consistent — the file is the source of truth on each launch. loadKioskZoomPercent now resolves zoom.txt → registry → default(100); registryZoomPercent was split out so the sync path can compare without recursing. Only the first whitespace-delimited token is parsed and the value is clamped to 50–200, so a fat-fingered 9000 renders at 200% rather than breaking layout.
kiosk: rendering "<url>" at zoom <n>% — emitted by runWebViewKiosk on every kiosk paint, so the field log shows both the URL and the resolved zoom (after zoom.txt/registry resolution). When zoom.txt drives the value you also get loadKioskZoomPercent: zoom.txt=<n> (raw <r>) synced to registry (was <old>).watchdog: kiosk child not running — relaunching (next tick in 30s) — emitted only on the relaunch transition (child found dead). A tick that finds the child alive stays silent so the 30 s cadence doesn’t flood the log. watchdog: filter active but pause-just-applied marker set — skipping relaunch this tick covers the v1.1.9 kiosk-blink-suppression window.Four field-reported runtime fixes (contributed via Copilot cloud-agent PRs #2–#4). These close out the elevation / SID-mapping / log-path problems chased across the v1.2.x line.
New-ScheduledTaskPrincipal used $env:USERNAME, which evaluates to SYSTEM (or empty) when the function runs in the LocalSystem service context — Task Scheduler can’t map that to an interactive-logon SID and returned HRESULT 0x80070534 (“No mapping between account names and security IDs was done”), the error that flooded the v1.2.3 field logs. Now resolves the real interactive user via os/user.Current(), strips the DOMAIN\ prefix, passes it as KEG_USER, and fails early with a meaningful log line if the user still can’t be resolved.ERROR_GEN_FAILURE (31) (“a device attached to the system is not functioning”) is now absorbed silently like the other expected parent-already-exited errors, instead of logging a scary line every service-spawn.%ProgramData%\KioskExitGuard\logs\. v1.2.9’s <install-dir>/logs/ layout failed once the exe relocated to C:\Program Files\KioskExitGuard\ — standard users can’t create files there. %ProgramData% is user-writable and already hosts the WebView2 data dir. cleanupInstallDir allowed-subdirs gained logs for the transitional case.enableServiceSpawnPrivileges now enables SeAssignPrimaryTokenPrivilege + SeIncreaseQuotaPrivilege on the process token before CreateProcessAsUser. LocalSystem holds these by default but they’re disabled; enabling them is the root- cause fix for the ERROR_ELEVATION_REQUIRED spawn loop the v1.2.3 linked-token swap only treated symptomatically. Applied to both the Service supervisor path and spawnFlagAsUserInSession.Release workflow also gained a workflow_dispatch trigger so a failed build can be re-run from the Actions tab without pushing a new tag.
I’m not sure why you’re posting these walls here. I highly doubt anyone’s reading them. A 2 sentence summery of where the project is holding would probably be more interesting.
If someone is interested they will read them and if not then they won’t
I don’t have paitence to summarize them lol
Update its been in use for a couple weeks now and no one has bypassed it yet
The same AI that you used to write this, can probably summarize it too ![]()
Though technically you could ask AI to summarize it for you yourself without asking him to ask AI for you.
That being said:
Based on the provided changelog, the KioskExitGuard project is currently at v1.3.1 (as of May 15, 2026). It has reached a stable production state and is actively deployed in the field.
Here is a two-sentence summary of where the project stands:
The project is currently stable and in live production, with field data showing zero successful user bypasses over its first few weeks of deployment. The latest updates resolved critical installation, administrative permission, and file-access bugs, while adding a quick zoom-override file (zoom.txt) and streamlined error logging.
Has anyone actually tried this? DM me a list for techniques to block that bypass filters.
I am in no way advocating those who bypass filter
Just sending to the filters creator techniques to Block So that no one will bypass it
Would this be an option to allow someone to block all internet access on a laptop, and only allow one program, https://design.cricut.com/ to access the internet? How practical/challenging would it be to set up? It looks like The project has migrated to a kiosk. Would that mean that I can only allow one specific app to be open always?
I made this as a shul luach screen kiosk - I would recommend using for this my otzar hachochma filter
fork it and modify it to allow more apps and to allow specific urls because now it blocks all urls and also its edge only
@Shalom_Karr - I have a use case very similar to a shul display, I want to put it on a touch screen tablet in a commercial kitchen that shows a locally hosted page with a calendar for that month’s menu, with touch buttons for staff to switch between lunch and supper display. I need it one hundred and fifty thousand percent kid proof (although they are not allowed in the kitchen - but then again, they are not allowed to touch the tablet either.) Would this app + bios password lock work for my case? You seem to know a lot about windows permissions and accounts. Is there any way to force the computer/tablet into recovery mode (E.G. removing the battery or something) and use the boot menu to get a terminal - IOW, how far does the protection go? Ty!
Why Windows?
Sorry I am busy now days - but you can fork my repo and ask claude
I don’t know if this is good enough for you, but the old way to do it is search for internet options in Windows search.
Click on content
Click on settings
In the proxy server section enable use a proxy server for your LAN. If you want to allow connections to your Wi-Fi router, printer and other local network devices, then you can enable bypass proxy server for local address.
Leave Address: at http://127.0.0.1 and Port 8080
Click on advanced
Add your website to exemptions
To include all subdomains put a star example *.yonistech.com
To disable changing this setting through the registry
\Software\Policies\Microsoft\Internet Explorer\Control Panel DWORD Proxy = 1
However, it doesn’t seem to block changing proxy settings in the Windows settings app. Unless if you use group policy editor Prevent changes to the proxy = Enabled it blocks changing in the settings app despite checking with process monitor and all it appears to do is change the same registry key unless if I’m mistaken, would anybody know how to just doing it through the registry to disable changing it through the settings app?
That’s what a lot of stores do for their POS. They make a proxy and the user cant use internet at all.
Talking about the older pos’s
It is mathematically impossible to make a filter impossible to bypass. You can only make the person’s life as miserable as possible trying to bypass it
BIOS passwords are a good idea (it’s not going to easy to bypass in a kitchen without anybody noticing you have to open up the device, takes at least 10 minutes if you’re lucky and special equipment and another computer).
For tablets there isn’t really any bios passwords that you can do but Apple activation lock Google FRP and MDM auto re enrollment is a great idea.
Bit locker drive encryption is a great idea! And it is possible to force devices into recovery mode. But that’s where MDM Google FRP Apple activation lock drive encryption, bios passwords help