diff --git a/internal/api/sample.go b/internal/api/sample.go new file mode 100644 index 0000000..8093c93 --- /dev/null +++ b/internal/api/sample.go @@ -0,0 +1,43 @@ +package api + +import ( + "net/http" + "os" + "path/filepath" +) + +// The bundled "try our sample" artifact is the aegis security probe. It is +// served publicly so the web UI empty state can drop it into the validate form +// and run a real compatibility matrix. aegis uses a bloom-filter map +// (BPF_MAP_TYPE_BLOOM_FILTER, kernel 5.16+), so it loads on >= 5.16 and fails +// below — a precise, real compatibility boundary. See examples/aegis-live/. + +// sampleArtifactPath returns the path to the bundled sample .bpf.o, overridable +// with BPFCOMPAT_SAMPLE_ARTIFACT for non-repo-root deployments (e.g. the demo). +func sampleArtifactPath() string { + if p := os.Getenv("BPFCOMPAT_SAMPLE_ARTIFACT"); p != "" { + return p + } + return filepath.FromSlash("examples/aegis-live/aegis.bpf.o") +} + +// handleSampleArtifact serves the bundled aegis sample object for the +// "Try our aegis sample" empty-state button. Intentionally public (no auth): +// it is a published sample artifact, not user data. +func (s *Server) handleSampleArtifact(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + writeError(w, http.StatusMethodNotAllowed, "method not allowed") + return + } + // Fixed, operator-controlled sample path (default or BPFCOMPAT_SAMPLE_ARTIFACT). + path := filepath.Clean(sampleArtifactPath()) + data, err := os.ReadFile(path) // #nosec G304 -- fixed sample path, not user input + if err != nil { + writeError(w, http.StatusNotFound, "sample artifact not available") + return + } + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", `attachment; filename="aegis.bpf.o"`) + w.Header().Set("Cache-Control", "public, max-age=3600") + _, _ = w.Write(data) +} diff --git a/internal/api/server.go b/internal/api/server.go index b5f432f..219f061 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -488,6 +488,7 @@ func (s *Server) serve(ctx context.Context) error { registerAPIRoute(mux, "/health", s.handleHealth) registerAPIRoute(mux, "/config", s.handleConfig) registerAPIRoute(mux, "/profiles", s.handleProfiles) + registerAPIRoute(mux, "/sample/aegis/artifact", s.handleSampleArtifact) registerAPIRoute(mux, "/validate/start", s.handleValidateStart) registerAPIRoute(mux, "/validate/status", s.handleValidateStatus) registerAPIRoute(mux, "/validate", s.handleValidate) diff --git a/internal/api/ui.go b/internal/api/ui.go index cf7dd9c..2b2aea1 100644 --- a/internal/api/ui.go +++ b/internal/api/ui.go @@ -282,9 +282,8 @@ const uiHTML = ` .results { padding: 12px 14px; display: grid; - grid-template-rows: auto auto auto 1fr auto; + grid-template-rows: repeat(5, auto); gap: 10px; - height: calc(100% - 52px); box-sizing: border-box; } .progress-wrap { @@ -854,6 +853,10 @@ const uiHTML = `
+
+ Don't have a .bpf.o? + +