geenforce: harden allowlist + fix share-less kiosk client and display scope

- allowlist auth uses remote_addr, not the spoofable first X-Forwarded-For hop
  (adds _trusted_client_ip + a regression test); rate-limit path unchanged
- client psm1: fix Set-StrictMode crashes reading absent keys in Get-ShopdbConfig
  (token-less mode) and Resolve-ShopdbPayloads (no-payload entries); validate
  the manifest response is JSON before overwriting the last-known-good cache
- runner: pass the engine its required -InstallerRoot/-LogFile; create the log
  directory so enforce logging is not silently lost on a fresh kiosk
- display scope: dispatcher writes an all-users Startup shortcut instead of
  Start-Process (SYSTEM cannot show a window in session 0), resolves the base
  URL from HKLM, and adds an always-on power/no-lock entry; tests updated for
  the 6-entry scope
This commit is contained in:
cproudlock
2026-07-28 17:09:21 -04:00
parent f533af82cd
commit 4c0cc672a2
6 changed files with 229 additions and 46 deletions

View File

@@ -147,6 +147,17 @@ def test_empty_allowlist_keeps_token_required(client, db, app):
assert resp.status_code == 401
def test_spoofed_forwarded_for_does_not_bypass_allowlist(client, db, app):
# SECURITY: the allowlist uses remote_addr, not X-Forwarded-For. A caller
# whose real IP (127.0.0.1) is NOT allowlisted must NOT gain token-less access
# by forging X-Forwarded-For to an allowlisted address.
_seed_and_publish(app)
_set_allowlist(app, '10.134.48.0/23') # test client 127.0.0.1 is NOT in it
resp = client.get('/api/geenforce/manifest?pctype=gea-shopfloor-cmm',
headers={'X-Forwarded-For': '10.134.48.10'})
assert resp.status_code == 401, 'spoofed X-Forwarded-For bypassed the allowlist'
def test_wrong_scope_rejected(client, db, app, auth_headers):
_seed_and_publish(app)
resp = client.post('/api/apitokens',