mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 19:41:17 +00:00
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
package api
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRouterPublicPathsInventory(t *testing.T) {
|
|
literalPaths, dynamicPaths := parsePublicPaths(t)
|
|
|
|
expectedLiterals := sliceToSet(t, publicPathsAllowlist, "public paths allowlist")
|
|
expectedDynamics := sliceToSet(t, publicPathsDynamicAllowlist, "public dynamic allowlist")
|
|
expectedPublic := sliceToSet(t, publicRouteAllowlist, "public route allowlist")
|
|
|
|
actualLiterals := sliceToSet(t, literalPaths, "router public paths")
|
|
actualDynamics := sliceToSet(t, dynamicPaths, "router public dynamic paths")
|
|
|
|
if missing := setDifference(actualLiterals, expectedLiterals); len(missing) > 0 {
|
|
t.Fatalf("public paths missing from allowlist: %s", strings.Join(sortedKeys(missing), ", "))
|
|
}
|
|
if stale := setDifference(expectedLiterals, actualLiterals); len(stale) > 0 {
|
|
t.Fatalf("public allowlist contains paths not in router.go: %s", strings.Join(sortedKeys(stale), ", "))
|
|
}
|
|
if missing := setDifference(actualDynamics, expectedDynamics); len(missing) > 0 {
|
|
t.Fatalf("public dynamic paths missing from allowlist: %s", strings.Join(sortedKeys(missing), ", "))
|
|
}
|
|
if stale := setDifference(expectedDynamics, actualDynamics); len(stale) > 0 {
|
|
t.Fatalf("public dynamic allowlist contains paths not in router.go: %s", strings.Join(sortedKeys(stale), ", "))
|
|
}
|
|
if missing := setDifference(expectedLiterals, expectedPublic); len(missing) > 0 {
|
|
t.Fatalf("publicPaths entries missing from public route allowlist: %s", strings.Join(sortedKeys(missing), ", "))
|
|
}
|
|
supportedPublic := sliceToSet(t, append(publicPathsAllowlist, authBypassAllowlist...), "supported public routes")
|
|
if stale := setDifference(expectedPublic, supportedPublic); len(stale) > 0 {
|
|
t.Fatalf("public route allowlist contains paths not backed by bypass logic: %s", strings.Join(sortedKeys(stale), ", "))
|
|
}
|
|
}
|
|
|
|
func parsePublicPaths(t *testing.T) ([]string, []string) {
|
|
t.Helper()
|
|
|
|
_, file, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
t.Fatalf("failed to locate test file path")
|
|
}
|
|
routerPath := filepath.Join(filepath.Dir(file), "router.go")
|
|
|
|
fset := token.NewFileSet()
|
|
fileAST, err := parser.ParseFile(fset, routerPath, nil, 0)
|
|
if err != nil {
|
|
t.Fatalf("parse router.go: %v", err)
|
|
}
|
|
|
|
var literalPaths []string
|
|
var dynamicPaths []string
|
|
var found bool
|
|
|
|
ast.Inspect(fileAST, func(node ast.Node) bool {
|
|
assign, ok := node.(*ast.AssignStmt)
|
|
if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
|
|
return true
|
|
}
|
|
ident, ok := assign.Lhs[0].(*ast.Ident)
|
|
if !ok || ident.Name != "publicPaths" {
|
|
return true
|
|
}
|
|
comp, ok := assign.Rhs[0].(*ast.CompositeLit)
|
|
if !ok {
|
|
return true
|
|
}
|
|
found = true
|
|
for _, elt := range comp.Elts {
|
|
switch v := elt.(type) {
|
|
case *ast.BasicLit:
|
|
if v.Kind != token.STRING {
|
|
continue
|
|
}
|
|
unquoted, err := strconv.Unquote(v.Value)
|
|
if err != nil {
|
|
unquoted = strings.Trim(v.Value, "`\"'")
|
|
}
|
|
literalPaths = append(literalPaths, unquoted)
|
|
case *ast.Ident:
|
|
dynamicPaths = append(dynamicPaths, v.Name)
|
|
case *ast.SelectorExpr:
|
|
dynamicPaths = append(dynamicPaths, selectorName(v))
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
|
|
if !found {
|
|
t.Fatalf("publicPaths slice not found in router.go")
|
|
}
|
|
|
|
return literalPaths, dynamicPaths
|
|
}
|
|
|
|
var publicPathsDynamicAllowlist = []string{
|
|
"config.DefaultOIDCCallbackPath",
|
|
}
|
|
|
|
var publicPathsAllowlist = []string{
|
|
"/api/health",
|
|
"/api/security/status",
|
|
"/api/security/validate-bootstrap-token",
|
|
"/api/security/quick-setup",
|
|
"/api/version",
|
|
"/api/login",
|
|
"/api/oidc/login",
|
|
"/install-docker-agent.sh",
|
|
"/install-container-agent.sh",
|
|
"/download/pulse-docker-agent",
|
|
"/install-host-agent.sh",
|
|
"/install-host-agent.ps1",
|
|
"/uninstall-host-agent.sh",
|
|
"/uninstall-host-agent.ps1",
|
|
"/download/pulse-host-agent",
|
|
"/install.sh",
|
|
"/install.ps1",
|
|
"/download/pulse-agent",
|
|
"/api/agent/version",
|
|
"/api/agent/ws",
|
|
"/api/server/info",
|
|
"/api/install/install-docker.sh",
|
|
"/api/ai/oauth/callback",
|
|
}
|