Skip to content

Commit 0104c80

Browse files
committed
refactor(server)!: remove legacy URL redirect subsystem (Phase-3 cutover done)
The /p/, /__zp/, /sw.js -> /zp/ compatibility redirects existed only to keep pre-cutover URLs working during the Phase-3 migration (PHASE3_PLAN.md:66 -- "may exist during migration only to redirect... not accepted steady-state surfaces"). No instance was deployed under the old scheme (confirmed by the operator), and no live code generates those spellings: the SW registers /zp/sw.js and shareurl emits /zp/p/. The redirects served no real consumers. Removed the 3 route entries, the redirectLegacyPage/redirectLegacySW/ redirectLegacy/legacyZP handlers, and the legacyControlRedirects/ legacyAssetNames allowlists. Legacy paths now fail closed (POLICY_BLOCKED 403) through the existing handle() default-deny -- a STRONGER posture than the prior redirect, with the security boundary fully preserved. routing_test now pins the new contract (legacy paths denied, not redirected). Build + go test ./... + golangci native+wasm all clean; no orphaned symbols, imports, or generators. BREAKING CHANGE: /p/*, /__zp/*, and root /sw.js now return 403 instead of a 307 redirect to the canonical /zp/ path. Op: compress
1 parent d194d2f commit 0104c80

2 files changed

Lines changed: 7 additions & 58 deletions

File tree

cmd/zeroproxy-server/main.go

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@ var routes = []route{
9393
{pat: controlPrefix + "error/", prefix: true, handler: serveControlError},
9494
{pat: assetPrefix, prefix: true, handler: serveAssetRoute},
9595
{pat: controlPrefix + "worker-bootstrap.js", handler: (*server).workerBootstrap},
96-
{pat: "/p/", prefix: true, handler: redirectLegacyPage},
97-
{pat: "/__zp/", prefix: true, handler: (*server).legacyZP},
98-
{pat: "/sw.js", handler: redirectLegacySW},
9996
}
10097

10198
func (s *server) handle(w http.ResponseWriter, r *http.Request) {
@@ -129,56 +126,6 @@ func serveAssetRoute(s *server, w http.ResponseWriter, r *http.Request) {
129126
s.serveAsset(w, r, strings.TrimPrefix(r.URL.Path, assetPrefix))
130127
}
131128

132-
func redirectLegacyPage(_ *server, w http.ResponseWriter, r *http.Request) {
133-
redirectLegacy(w, r, controlPrefix+"p/"+strings.TrimPrefix(r.URL.Path, "/p/"))
134-
}
135-
136-
func redirectLegacySW(_ *server, w http.ResponseWriter, r *http.Request) {
137-
redirectLegacy(w, r, controlPrefix+"sw.js")
138-
}
139-
140-
func redirectLegacy(w http.ResponseWriter, r *http.Request, nextPath string) {
141-
u := *r.URL
142-
u.Path = nextPath
143-
http.Redirect(w, r, u.String(), http.StatusTemporaryRedirect)
144-
}
145-
146-
// legacyControlRedirects maps legacy /__zp/ control paths to their canonical
147-
// /zp/ targets.
148-
var legacyControlRedirects = map[string]string{
149-
"/__zp/ws-pipe": controlPrefix + "ws-pipe",
150-
"/__zp/kernel.wasm": controlPrefix + "kernel.wasm",
151-
"/__zp/worker-bootstrap.js": controlPrefix + "worker-bootstrap.js",
152-
}
153-
154-
// legacyAssetNames is the allowlist of legacy /__zp/<name> asset paths that map
155-
// to the canonical /zp/assets/ prefix. Anything else is default-denied.
156-
var legacyAssetNames = map[string]struct{}{
157-
"zp-core.js": {},
158-
"runtime-prelude.js": {},
159-
"rust-rewriter.js": {},
160-
"wasm_exec.js": {},
161-
"worker-prelude.js": {},
162-
}
163-
164-
func (s *server) legacyZP(w http.ResponseWriter, r *http.Request) {
165-
path := r.URL.Path
166-
if next, ok := legacyControlRedirects[path]; ok {
167-
redirectLegacy(w, r, next)
168-
return
169-
}
170-
if strings.HasPrefix(path, "/__zp/error/") {
171-
redirectLegacy(w, r, controlPrefix+"error/"+strings.TrimPrefix(path, "/__zp/error/"))
172-
return
173-
}
174-
name := strings.TrimPrefix(path, "/__zp/")
175-
if _, ok := legacyAssetNames[name]; ok {
176-
redirectLegacy(w, r, assetPrefix+name)
177-
return
178-
}
179-
s.safeError(w, r, "POLICY_BLOCKED", http.StatusForbidden)
180-
}
181-
182129
func (s *server) serveWeb(w http.ResponseWriter, r *http.Request, name string) {
183130
s.serveFile(w, r, filepath.Join(s.webDir, name), mime.TypeByExtension(filepath.Ext(name)))
184131
}

cmd/zeroproxy-server/routing_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ var routeCases = []routeCase{
2222
// Redirects to the canonical control prefix.
2323
{"root redirects to control", "/", http.StatusFound, controlPrefix, ""},
2424
{"index.html redirects to control", "/index.html", http.StatusFound, controlPrefix, ""},
25-
{"legacy sw redirects", "/sw.js", http.StatusTemporaryRedirect, controlPrefix + "sw.js", ""},
26-
{"legacy page redirects", "/p/abc", http.StatusTemporaryRedirect, controlPrefix + "p/abc", ""},
27-
{"legacy zp ws-pipe redirects", "/__zp/ws-pipe", http.StatusTemporaryRedirect, controlPrefix + "ws-pipe", ""},
28-
{"legacy zp asset redirects", "/__zp/zp-core.js", http.StatusTemporaryRedirect, assetPrefix + "zp-core.js", ""},
29-
{"legacy zp error redirects", "/__zp/error/BAD_HMAC", http.StatusTemporaryRedirect, controlPrefix + "error/BAD_HMAC", ""},
25+
// Legacy /p/, /__zp/, /sw.js spellings were removed at the Phase-3 cutover;
26+
// they now fail closed (default-deny) instead of redirecting to controlPrefix.
27+
{"legacy root sw is denied", "/sw.js", http.StatusForbidden, "", "POLICY_BLOCKED"},
28+
{"legacy page path is denied", "/p/abc", http.StatusForbidden, "", "POLICY_BLOCKED"},
29+
{"legacy zp control path is denied", "/__zp/ws-pipe", http.StatusForbidden, "", "POLICY_BLOCKED"},
30+
{"legacy zp asset path is denied", "/__zp/zp-core.js", http.StatusForbidden, "", "POLICY_BLOCKED"},
31+
{"legacy zp error path is denied", "/__zp/error/BAD_HMAC", http.StatusForbidden, "", "POLICY_BLOCKED"},
3032

3133
// Serve handlers fail closed (503) because the asset tree is absent, but
3234
// the key point is they routed to a serve path rather than default-deny.

0 commit comments

Comments
 (0)