Files
sif/internal/modules/telescope_exposure_test.go
TigahandGitHub 7742b8e731 feat(modules): add laravel telescope dashboard exposure module (#279)
Detect an exposed Laravel Telescope dashboard, which reveals requests, queries,
exceptions and dumped variables. Telescope aborts with 403 before render when
gated, so a 200 carrying both the Telescope title and the Vue mount id proves
the gate is open. The and-condition on the mount id keeps an unrelated page that
merely names telescope, or a soft-404, from matching.
2026-07-22 12:36:10 -07:00

54 lines
2.2 KiB
Go

package modules_test
import "testing"
// laravel-telescope-exposure fires only when the dashboard actually renders: a
// 200 carrying both the Telescope title and the Vue mount id. A 403-gated
// install, a soft-404 that reflects the word, and an unrelated "telescope" page
// must all stay silent.
func TestLaravelTelescopeExposureModule(t *testing.T) {
const telescope = "../../modules/recon/laravel-telescope-exposure.yaml"
dashboard := `<!DOCTYPE html><html><head><title>Telescope - Acme</title></head>` +
`<body><div id="telescope" v-cloak></div></body></html>`
t.Run("an exposed dashboard is flagged and the app name extracted", func(t *testing.T) {
res := runDebugModule(t, telescope, 200, dashboard)
if len(res.Findings) == 0 {
t.Fatal("expected a telescope finding")
}
if v := debugExtract(res, "app_name"); v != "Acme" {
t.Errorf("app_name=%q, want Acme", v)
}
})
t.Run("a dashboard without a configured app name still fires", func(t *testing.T) {
body := `<!DOCTYPE html><html><head><title>Telescope</title></head>` +
`<body><div id="telescope" v-cloak></div></body></html>`
if res := runDebugModule(t, telescope, 200, body); len(res.Findings) == 0 {
t.Fatal("expected a telescope finding without an app name")
}
})
t.Run("a gated install returning 403 is silent", func(t *testing.T) {
if res := runDebugModule(t, telescope, 403, dashboard); len(res.Findings) > 0 {
t.Errorf("403-gated telescope should not match, got %d findings", len(res.Findings))
}
})
t.Run("an unrelated telescope page without the vue mount is silent", func(t *testing.T) {
body := `<html><head><title>Telescope Reviews 2026</title></head>` +
`<body><ul id="telescope-list"></ul></body></html>`
if res := runDebugModule(t, telescope, 200, body); len(res.Findings) > 0 {
t.Errorf("astronomy page should not match, got %d findings", len(res.Findings))
}
})
t.Run("a generic soft-404 shell is silent", func(t *testing.T) {
body := `<html><head><title>404 Not Found</title></head><body>not found</body></html>`
if res := runDebugModule(t, telescope, 200, body); len(res.Findings) > 0 {
t.Errorf("soft-404 should not match, got %d findings", len(res.Findings))
}
})
}