Both kiosks admit a check-in or check-out with no login when the caller sits in
a configured network. The subtraction that makes that safe was missing.
A reverse proxy normally sits ON the plant network a site enters in the
allowlist. When the proxy forwards nothing - misconfigured, or something
bypassed it - the request arrives wearing the proxy's own address, which matches
the allowlist, while carrying no evidence whatever about who sent it. That is
the one case that has to refuse, and it was being admitted. The previous
fail-closed test only looked closed because the proxy in it happened to sit
outside the range.
Addresses that belong to the path are now subtracted before the allowlist is
consulted: loopback always, since no site's kiosk is the server itself, plus
whatever the site declares as its proxies. USB and printedparts get the same
treatment - they had diverged for no reason anybody chose.
Three more, same surface:
A range that admits everybody can no longer be saved by accident. 0.0.0.0/0 and
::/0 are refused outright, anything wider than a site plausibly needs is refused
unless an explicit override is set, and the refusal says why.
The kiosk token no longer travels in a URL. The provisioning link carries a
single-use setup code redeemed on first open, so the token stays out of browser
history, referrers and proxy logs. The route is declared public deliberately:
the secret is the code, the way a password-reset link is the secret.
The badge-to-name lookup is bounded. It answers without a login and is now
reachable from a whole network rather than one screen, so a source that keeps
missing gets refused rather than being allowed to walk the directory. On by
default, budget and kill switch are settings.
All four new settings carry controls on their settings pages. A setting that can
stop the kiosk, or widen it, must not be reachable only by hand-editing rows.
The installer's client-IP advice is corrected: it described the opposite of what
the shipped waitress flags do, and named only GE-Enforce as the thing that
breaks when the rule is absent, when the two kiosk allowlists depend on it too.
The USB audit answered who had which drive and when, but not whether it came
back wiped or virus-scanned. That is the question the record is kept to answer:
removable-media custody is CMMC evidence, and custody without the state of the
media on return is half the story. Both stores already held the values - the
external checkinoutlog in sanitized and scanned_viruses, the app's own
usbcheckouts in waswiped and scannedviruses - and neither mode selected them.
Both now report them, in the JSON, the CSV and the column list, so the two
directory modes still answer in one voice.
Two things the rendering has to get right:
- Only a return carries them. The wipe and the scan happen when the drive comes
back, so a check-out row shows blanks rather than implying a fact about an
event that had not happened yet.
- Unknown is not "no". Both stores keep these nullable deliberately: a null
means nobody wrote it down, which is a different claim from a drive that came
back unwiped. Rendering an unrecorded return as "No" would put an assertion
in an audit record that no one ever made, so unknown stays blank and reads as
the gap it is. A test holds that, because "no" is the tempting default.
auditflag lives in selfhosted.py rather than routes.py: routes already imports
selfhosted, so the helper had to sit on that side or the import would be
circular.
Flask + Vue rewrite of the classic-ASP shopdb, built as a plugin
framework so sister GE Aerospace sites can adopt it. Product version
0.7.0-pre-0.8.0, plugin contract 0.11.0, 966 backend tests.
Key entry points:
- docs/INSTALL-WINDOWS-IIS.md - tested Windows + IIS install runbook
(own-site and subpath deployment methods)
- docs/PILOT-DEPLOY.md - production pilot: stand up + import + verify
- docs/IMPORT-API.md - schema-agnostic legacy data import contract
- scripts/site_imports/wjf/ - West Jefferson reference import loader
- docs/adr/ - twelve accepted architecture decision records