@@ -104,13 +104,11 @@ func (a *app) requireAuth(next http.Handler) http.Handler {
104104 next .ServeHTTP (w , r )
105105 return
106106 }
107- // Helper protocol: requests carrying both identity headers
108- // (X-RSM-Device-Type + X-RSM-Fingerprint) are deferred to the
109- // downstream handler (authorizeHelperSyncRequest in helper_auth.go),
110- // which enforces either app-password validation or the
111- // auto-enroll-window policy. Pre-empting that decision here would
112- // break the bootstrap flow where a fresh helper has no key yet.
113- if hasHelperIdentity (r ) {
107+ // Helper protocol: only endpoints that actually call
108+ // authorizeHelperSyncRequest may defer auth to the downstream handler.
109+ // Checking identity headers alone would let callers spoof those headers
110+ // and bypass AUTH_MODE on unrelated API/static routes.
111+ if isHelperProtocolRequest (r ) {
114112 next .ServeHTTP (w , r )
115113 return
116114 }
@@ -126,10 +124,35 @@ func (a *app) requireAuth(next http.Handler) http.Handler {
126124 })
127125}
128126
127+ // isHelperProtocolRequest reports whether r is one of the helper-facing routes
128+ // whose handler performs authorizeHelperSyncRequest. Identity headers alone are
129+ // not sufficient; non-helper routes must still require normal auth.
130+ func isHelperProtocolRequest (r * http.Request ) bool {
131+ if ! hasHelperIdentity (r ) {
132+ return false
133+ }
134+ p := stripRoutePrefix (r .URL .Path )
135+ switch r .Method {
136+ case http .MethodGet :
137+ if p == "/save/latest" ||
138+ p == "/saves/download" ||
139+ p == "/saves/download_many" ||
140+ p == "/saves/download-many" {
141+ return true
142+ }
143+ return strings .HasPrefix (p , "/saves/" ) && strings .HasSuffix (p , "/download" )
144+ case http .MethodPost :
145+ return p == "/saves" ||
146+ p == "/devices/config/report" ||
147+ p == "/helpers/config/sync" ||
148+ p == "/helpers/heartbeat"
149+ default :
150+ return false
151+ }
152+ }
153+
129154// hasHelperIdentity reports whether r carries both helper-identity headers
130- // (X-RSM-Device-Type + X-RSM-Fingerprint). The middleware uses this to
131- // recognize the helper protocol and defer the auth decision to
132- // authorizeHelperSyncRequest, which knows the auto-enroll-window policy.
155+ // (X-RSM-Device-Type + X-RSM-Fingerprint).
133156func hasHelperIdentity (r * http.Request ) bool {
134157 dt := strings .TrimSpace (r .Header .Get ("X-RSM-Device-Type" ))
135158 fp := strings .TrimSpace (r .Header .Get ("X-RSM-Fingerprint" ))
0 commit comments