carlos / platform Public

validBuildProbe: name the second caller the grammar now protects

The comment explained the mirror but not the consequence. This rule is no
longer only a create-time gate — runDeploy re-checks the console's ECHOED
probe with it before dialing, so loosening it "just for creates" silently
loosens the deploy watch too. Said on BOTH copies, because whoever loosens
one will be looking at whichever they opened.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev c502772423507f78582c0b4e2612b5b696bf8ffa parent 2eb3aaf
2 files changed, +16 −0
  • apps.go +8 −0
  • internal/console/server.go +8 −0
diff --git a/apps.go b/apps.go
index 9ac32f9a5..4629a1950 100644
--- a/apps.go
+++ b/apps.go
@@ -385,6 +385,14 @@ func validPlaceTarget(target string) error {
// path, not protocol-relative ("//host/..."), no query, no fragment, and
// printable ASCII only (no control characters) — the same rule, checked
// twice, never merely "similar" rules that could quietly drift apart.
+//
+// It is no longer only a create-time gate: this is also what keeps the deploy
+// watch on-host. runDeploy re-checks the probe the console ECHOES with this
+// function before dialing it (deploy.go), so loosening the grammar "just for
+// creates" silently loosens the watch too — and the shape that matters there
+// is a value not starting with "/": "https://" + host + "@evil.example/x"
+// parses host as USERINFO and evil.example as the HOST, which is how a watch
+// ends up reporting a DIFFERENT deployment's build as this one's.
func validBuildProbe(probe string) error {
if probe == "" {
return nil
diff --git a/internal/console/server.go b/internal/console/server.go
index 04caade06..80cf29cf3 100644
--- a/internal/console/server.go
+++ b/internal/console/server.go
@@ -2423,6 +2423,14 @@ func normalizeAppName(name string) string {
// against the create verb's own scarce per-day budget, and a typo the
// server ACCEPTS but the client refuses would just be a confusing false
// refusal.
+//
+// This is no longer only a create-time gate: the SAME rule is what keeps the
+// deploy watch on-host. runDeploy re-checks the echoed probe with the client
+// mirror before dialing it (deploy.go), so loosening this "just for creates"
+// silently loosens the watch too — and the shape that matters there is a
+// value not starting with "/": "https://" + host + "@evil.example/x" parses
+// host as USERINFO and evil.example as the HOST, which is this comment's
+// "a DIFFERENT deployment's build" failure arriving by a second route.
func validBuildProbe(p string) bool {
if !strings.HasPrefix(p, "/") || strings.HasPrefix(p, "//") || strings.ContainsAny(p, "?#") {
return false