R-839: the deploy API refuses an HDD_PATH that is a folder inside a registered drive

Diagnosed: 9202's paperless-ngx app.yaml named <drive>/userdata/paperless-ngx, written by the
2026-09-22 drill harness through POST /api/stacks/<n>/deploy, which accepted it (RefuseAsAppNamespace
matches by prefix). The boot sweep then asked a folder whether it was a mountpoint and HELD the app.
The deploy page offers drive roots only, so a household could not reach this; every other caller
is now refused with a sentence naming the drive (settings.DriveOfSubPath).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-05 21:52:05 +02:00
parent 0b93e1ad1e
commit 4867ec1804
6 changed files with 101 additions and 0 deletions
+8
View File
@@ -509,6 +509,14 @@ func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name stri
return
}
// R-839: HDD_PATH must name the drive itself, never a folder inside it — see
// settings.DriveOfSubPath. The deploy page only offers drive roots; this guards every other caller.
if drive, inside := r.sett.DriveOfSubPath(body.Values["HDD_PATH"]); inside {
r.logger.Printf("[WARN] [api] Deploy refused for %s: HDD_PATH %s is a folder inside the drive %s, not the drive (R-839)", name, body.Values["HDD_PATH"], drive)
writeJSON(w, http.StatusConflict, apiResponse{OK: false, Error: r.msg(req, "api.deploy.hdd_path_inside_drive", drive)})
return
}
// `09` §3 decision 36: the app's drive folder already holds its old data → the household chooses.
if handled := r.keptDataAtInstall(w, req, name, body.Values["HDD_PATH"], body.KeptData); handled {
return