Description: VSV00018: defend request URLs in built-in VCL
 .
 Backport to 7.7 without new counters, logging tags or method APIs.
 Retain stable CONNECT pipe policy; omit unrelated release notes.
Author: Nils Goroll <nils.goroll@uplex.de>
Origin: https://code.vinyl-cache.org/vinyl-cache/vinyl-cache/commit/f89df57ab8e30ca5c8d04ba27870d473f17bedd4
Bug: https://vinyl-cache.org/security/VSV00018.html
Forwarded: not-needed
Last-Update: 2026-10-23

Index: varnish-7.7.0/bin/varnishd/builtin.vcl
===================================================================
--- varnish-7.7.0.orig/bin/varnishd/builtin.vcl
+++ varnish-7.7.0/bin/varnishd/builtin.vcl
@@ -41,6 +41,7 @@ sub vcl_recv {
 }
 
 sub vcl_builtin_recv {
+	call vcl_req_url;
 	call vcl_req_host;
 	call vcl_req_method;
 	call vcl_req_authorization;
@@ -58,6 +59,16 @@ sub vcl_req_host {
 		return (synth(400));
 	}
 }
+
+sub vcl_req_url {
+	if (req.url == "*" && req.method == "OPTIONS") {
+		return;
+	}
+	# CONNECT retains the stable vcl_req_method pipe policy.
+	if (req.url !~ "^/" && req.method != "CONNECT") {
+		return (synth(400));
+	}
+}
 
 sub vcl_req_method {
 	if (req.method == "PRI") {
Index: varnish-7.7.0/bin/varnishtest/tests/b00026.vtc
===================================================================
--- varnish-7.7.0.orig/bin/varnishtest/tests/b00026.vtc
+++ varnish-7.7.0/bin/varnishtest/tests/b00026.vtc
@@ -2,13 +2,13 @@ varnishtest "Check the precedence for ti
 
 server s1 {
 	rxreq
-	expect req.url == "from_backend"
+	expect req.url == "/from_backend"
 	delay 1
 	txresp
 } -start
 server s2 {
 	rxreq
-	expect req.url == "from_vcl"
+	expect req.url == "/from_vcl"
 	delay 1.5
 	txresp
 } -start
@@ -26,13 +26,13 @@ varnish v1 -vcl {
 	}
 
 	sub vcl_recv {
-		if (req.url  == "from_backend") {
+		if (req.url  == "/from_backend") {
 			return(pass);
 		}
 	}
 	sub vcl_backend_fetch {
 		set bereq.first_byte_timeout = 2s;
-		if (bereq.url  == "from_backend") {
+		if (bereq.url  == "/from_backend") {
 			set bereq.backend = b1;
 		} else {
 			set bereq.backend = b2;
@@ -42,10 +42,10 @@ varnish v1 -vcl {
 varnish v1 -cliok "param.set first_byte_timeout 0.5"
 
 client c1 {
-	txreq -url "from_backend"
+	txreq -url "/from_backend"
 	rxresp
 	expect resp.status == 200
-	txreq -url "from_vcl"
+	txreq -url "/from_vcl"
 	rxresp
 	expect resp.status == 200
 } -run
Index: varnish-7.7.0/bin/varnishtest/tests/c00005.vtc
===================================================================
--- varnish-7.7.0.orig/bin/varnishtest/tests/c00005.vtc
+++ varnish-7.7.0/bin/varnishtest/tests/c00005.vtc
@@ -5,7 +5,7 @@ server s1 {
 	expect req.url == "/"
 	txresp -body "1111\n"
 	rxreq
-	expect req.url == "foo"
+	expect req.url == "/foo"
 	txresp -body "2222\n"
 } -start
 
@@ -40,7 +40,7 @@ varnish v1 -vcl+backend {
 } -start
 
 client c1 {
-	txreq -url "foo"
+	txreq -url "/foo"
 	rxresp
 	expect resp.status == 200
 	expect resp.http.acl == acl1
Index: varnish-7.7.0/bin/varnishtest/tests/e00009.vtc
===================================================================
--- varnish-7.7.0.orig/bin/varnishtest/tests/e00009.vtc
+++ varnish-7.7.0/bin/varnishtest/tests/e00009.vtc
@@ -40,7 +40,7 @@ varnish v1 -expect MAIN.s_resp_bodybytes
 varnish v1  -cli "param.set feature +esi_disable_xml_check"
 
 client c1 {
-	txreq -url bar
+	txreq -url /bar
 	rxresp
 	expect resp.status == 200
 	expect resp.bodylen == 22
Index: varnish-7.7.0/bin/varnishtest/tests/e00019.vtc
===================================================================
--- varnish-7.7.0.orig/bin/varnishtest/tests/e00019.vtc
+++ varnish-7.7.0/bin/varnishtest/tests/e00019.vtc
@@ -34,19 +34,19 @@ server s1 {
 # Varnish 4
 server s2 {
 	rxreq
-	expect req.url == "bar/foo"
+	expect req.url == "/foo"
 	txresp -body {<INCL>}
 } -start
 
 varnish v1 -vcl+backend {
 	sub vcl_backend_fetch {
-		if (bereq.url != "bar") {
+		if (bereq.url != "/bar") {
 			set bereq.backend = s2;
 		}
 	}
 
 	sub vcl_backend_response {
-		if (bereq.url == "bar") {
+		if (bereq.url == "/bar") {
 			set beresp.do_esi = true;
 		}
 	}
@@ -67,7 +67,7 @@ logexpect l1 -v v1 -g vxid -q "vxid == 1
 } -start
 
 client c1 {
-	txreq  -url bar
+	txreq  -url /bar
 	rxresp
 	expect resp.status == 200
 	expect resp.bodylen == 65856
Index: varnish-7.7.0/bin/varnishtest/tests/r01847.vtc
===================================================================
--- varnish-7.7.0.orig/bin/varnishtest/tests/r01847.vtc
+++ varnish-7.7.0/bin/varnishtest/tests/r01847.vtc
@@ -26,8 +26,7 @@ client c1 {
 
 	txreq -url https://www.example.com/bar
 	rxresp
-	expect resp.http.rxhost == "${localhost}"
-	expect resp.http.rxurl == https://www.example.com/bar
+	expect resp.status == 400
 } -run
 
 varnish v1 -cliok "param.set feature +https_scheme"
Index: varnish-7.7.0/bin/varnishtest/tests/r02339.vtc
===================================================================
--- varnish-7.7.0.orig/bin/varnishtest/tests/r02339.vtc
+++ varnish-7.7.0/bin/varnishtest/tests/r02339.vtc
@@ -11,10 +11,10 @@ varnish v1 -vcl+backend {
 	import purge;
 
 	sub vcl_miss {
-		if (req.url == "miss") { purge.hard(); }
+		if (req.url == "/miss") { purge.hard(); }
 	}
 	sub vcl_hit {
-		if (req.url == "hit") { purge.hard(); }
+		if (req.url == "/hit") { purge.hard(); }
 	}
 } -start
 
@@ -39,15 +39,15 @@ logexpect l1 -v v1 {
 } -start
 
 client c1 {
-	txreq -url hit
+	txreq -url /hit
 	rxresp
 	expect resp.status == 200
 
-	txreq -url hit
+	txreq -url /hit
 	rxresp
 	expect resp.status == 200
 
-	txreq -url miss
+	txreq -url /miss
 	rxresp
 	expect resp.status == 200
 } -run
@@ -59,7 +59,7 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_purge {
-		if (req.url == "purge") { purge.hard(); }
+		if (req.url == "/purge") { purge.hard(); }
 	}
 }
 
@@ -67,7 +67,7 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_pass {
-		if (req.url == "pass") { purge.hard(); }
+		if (req.url == "/pass") { purge.hard(); }
 	}
 }
 
@@ -75,7 +75,7 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_deliver {
-		if (req.url == "deliver") { purge.hard(); }
+		if (req.url == "/deliver") { purge.hard(); }
 	}
 }
 
@@ -83,7 +83,7 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_synth {
-		if (req.url == "synth") { purge.hard(); }
+		if (req.url == "/synth") { purge.hard(); }
 	}
 }
 
@@ -91,7 +91,7 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_backend_fetch {
-		if (bereq.url == "fetch") { purge.hard(); }
+		if (bereq.url == "/fetch") { purge.hard(); }
 	}
 }
 
@@ -99,7 +99,7 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_backend_error {
-		if (bereq.url == "error") { purge.hard(); }
+		if (bereq.url == "/error") { purge.hard(); }
 	}
 }
 
@@ -107,6 +107,6 @@ varnish v1 -errvcl "Not available in sub
 	import purge;
 
 	sub vcl_backend_response {
-		if (bereq.url == "response") { purge.hard(); }
+		if (bereq.url == "/response") { purge.hard(); }
 	}
 }
