1package main23import (4 "strings"56 "golang.org/x/tools/go/analysis"7 "golang.org/x/tools/go/analysis/passes/inspect"8)910// hostenvPurity keeps the application one pure http.Handler. Everything11// OS-shaped sits behind the small interfaces of app/hostenv, which is what12// keeps a third host (a WASM Service Worker PWA) reachable at near-zero cost13// later (issue #5 §3.3).14var hostenvPurity = &analysis.Analyzer{15 Name: "hostenv",16 Doc: `forbid OS and dialog access outside app/hostenv (issue #5 §3.3)1718The handler never touches os, file paths, or dialogs directly: it reaches them19through Hostenv's VolumeStore, SessionStore and PickFile. Implementations of20those interfaces — including the Wails host — live under internal/app/hostenv.2122The rule is about the handler, so it is scoped to the app lane. The two host23entries, cmd/atlas and the desktop shell at the module root, are where the OS24is supposed to be reached: theirs is the wiring that decides which directories25and which window the handler is mounted over, and they are exempt by being26outside the lane the rule is written about.`,27 Requires: []*analysis.Analyzer{inspect.Analyzer},28 Run: runImportRule(hostenvEdge),29}3031// hostenvImplementations is the one place OS calls are in contract.32const hostenvImplementations = "internal/app/hostenv"3334// osShaped names the standard-library packages that put the handler on a35// particular machine. io/fs is deliberately absent: an fs.FS is the portable36// shape the stores hand out.37var osShaped = map[string]bool{38 "os": true,39 "os/exec": true,40 "os/signal": true,41 "os/user": true,42 "path/filepath": true,43 "syscall": true,44}4546func hostenvEdge(from Lane, fromRel, importPath string) string {47 if from != LaneApp || under(fromRel, hostenvImplementations) {48 return ""49 }50 switch {51 case osShaped[importPath]:52 return contractf(53 "the application handler must not import "+quote(importPath)+" outside "+hostenvImplementations,54 "3.3",55 "the app is one pure http.Handler: volumes, sessions and the import dialog arrive through the Hostenv interfaces, which is what lets a Wails webview, a headless atlas serve, and a future WASM service worker all mount the same code",56 )57 case dialogShaped(importPath):58 return contractf(59 "the application handler must not import the host toolkit "+quote(importPath)+" outside "+hostenvImplementations,60 "3.3",61 "a native dialog is a Hostenv.PickFile concern; the handler that renders hypermedia must not know which window system it is running under",62 )63 }64 return ""65}6667func dialogShaped(importPath string) bool {68 if isStdlib(importPath) {69 return false70 }71 for _, marker := range []string{"dialog", "zenity", "wails", "webview", "systray"} {72 if strings.Contains(strings.ToLower(importPath), marker) {73 return true74 }75 }76 return false77}