From 0b93b4f5460ab69d4656eca3f1e5789c2df72f06 Mon Sep 17 00:00:00 2001 From: Georg Heindl Date: Thu, 11 Jun 2026 22:34:52 +0200 Subject: [PATCH] Added registry_url parameter for /osm/api/registry-info endpoint --- docs/api-swagger/docs.go | 6 +++++ docs/api-swagger/swagger.json | 6 +++++ docs/api-swagger/swagger.yaml | 6 +++++ docs/api/install.mdx | 20 ++++++++++++++ pkg/server/handlers/install.go | 17 +++++++++--- test/e2e/api_test.go | 49 ++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 4 deletions(-) diff --git a/docs/api-swagger/docs.go b/docs/api-swagger/docs.go index 5097ad7..4c9b30a 100644 --- a/docs/api-swagger/docs.go +++ b/docs/api-swagger/docs.go @@ -1040,6 +1040,12 @@ const docTemplate = `{ "description": "Registry mode: direct-fetch or nix-build", "name": "registry_mode", "in": "query" + }, + { + "type": "string", + "description": "Custom registry URL or local file path. In direct-fetch mode: source for the full binary list. In nix-build mode: metadata overlay (desc, tags, version).", + "name": "registry_url", + "in": "query" } ], "responses": { diff --git a/docs/api-swagger/swagger.json b/docs/api-swagger/swagger.json index 78a0797..415ef24 100644 --- a/docs/api-swagger/swagger.json +++ b/docs/api-swagger/swagger.json @@ -1029,6 +1029,12 @@ "description": "Registry mode: direct-fetch or nix-build", "name": "registry_mode", "in": "query" + }, + { + "type": "string", + "description": "Custom registry URL or local file path. In direct-fetch mode: source for the full binary list. In nix-build mode: metadata overlay (desc, tags, version).", + "name": "registry_url", + "in": "query" } ], "responses": { diff --git a/docs/api-swagger/swagger.yaml b/docs/api-swagger/swagger.yaml index 7693493..ca1c1c9 100644 --- a/docs/api-swagger/swagger.yaml +++ b/docs/api-swagger/swagger.yaml @@ -1184,6 +1184,12 @@ paths: in: query name: registry_mode type: string + - description: 'Custom registry URL or local file path. In direct-fetch mode: + source for the full binary list. In nix-build mode: metadata overlay (desc, + tags, version).' + in: query + name: registry_url + type: string produces: - application/json responses: diff --git a/docs/api/install.mdx b/docs/api/install.mdx index 6121338..60c242d 100644 --- a/docs/api/install.mdx +++ b/docs/api/install.mdx @@ -16,6 +16,7 @@ Fetch binary registry metadata with installation status. Supports two modes: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `registry_mode` | string | `direct-fetch` | Registry mode: `direct-fetch` or `nix-build` | +| `registry_url` | string | _(embedded)_ | Custom registry URL or local file path. **Direct-fetch mode**: source for the full binary list. **Nix-build mode**: metadata overlay (desc, tags, version). Accepts HTTPS URLs or absolute file paths. | --- @@ -28,6 +29,18 @@ curl http://localhost:8002/osm/api/registry-info \ -H "Authorization: Bearer $TOKEN" ``` +**With a custom registry URL:** +```bash +curl "http://localhost:8002/osm/api/registry-info?registry_url=https://example.com/my-registry.json" \ + -H "Authorization: Bearer $TOKEN" +``` + +**With a local file path:** +```bash +curl "http://localhost:8002/osm/api/registry-info?registry_url=/etc/osmedeus/registry.json" \ + -H "Authorization: Bearer $TOKEN" +``` + **Response:** ```json { @@ -95,6 +108,12 @@ curl "http://localhost:8002/osm/api/registry-info?registry_mode=nix-build" \ -H "Authorization: Bearer $TOKEN" ``` +**With a custom metadata registry:** +```bash +curl "http://localhost:8002/osm/api/registry-info?registry_mode=nix-build®istry_url=/etc/osmedeus/registry.json" \ + -H "Authorization: Bearer $TOKEN" +``` + **Response:** ```json { @@ -144,6 +163,7 @@ curl "http://localhost:8002/osm/api/registry-info?registry_mode=nix-build" \ | Field | Type | Description | |-------|------|-------------| | `registry_mode` | string | Always `"nix-build"` | +| `registry_url` | string | Registry URL or file path used for metadata (empty string = embedded) | | `nix_installed` | boolean | Whether Nix package manager is installed | | `categories` | array | List of tool categories from flake.nix | | `categories[].name` | string | Category name (e.g., "Subdomain", "Vuln") | diff --git a/pkg/server/handlers/install.go b/pkg/server/handlers/install.go index c8df669..ba4d347 100644 --- a/pkg/server/handlers/install.go +++ b/pkg/server/handlers/install.go @@ -16,6 +16,7 @@ import ( // @Tags Install // @Produce json // @Param registry_mode query string false "Registry mode: direct-fetch or nix-build" default(direct-fetch) +// @Param registry_url query string false "Custom registry URL or local file path. In direct-fetch mode: source for the full binary list. In nix-build mode: metadata overlay (desc, tags, version)." // @Success 200 {object} map[string]interface{} "Registry data" // @Failure 500 {object} map[string]interface{} "Failed to load registry" // @Security BearerAuth @@ -37,7 +38,8 @@ func GetRegistryInfo(cfg *config.Config) fiber.Handler { // getDirectFetchRegistry returns the direct-fetch registry (existing behavior) func getDirectFetchRegistry(c *fiber.Ctx) error { - registry, err := installer.LoadRegistry("", nil) + registryPathOrURL := c.Query("registry_url", "") + registry, err := installer.LoadRegistry(registryPathOrURL, nil) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": true, @@ -45,6 +47,11 @@ func getDirectFetchRegistry(c *fiber.Ctx) error { }) } + // Using installer.DefaultRegistryURL as default for query would break LoadRegistry logic + if registryPathOrURL == "" { + registryPathOrURL = installer.DefaultRegistryURL + } + // Build response with installation status for each binary binariesWithStatus := make(map[string]BinaryStatusEntry) for name, entry := range registry { @@ -71,7 +78,7 @@ func getDirectFetchRegistry(c *fiber.Ctx) error { return c.JSON(fiber.Map{ "registry_mode": "direct-fetch", - "registry_url": installer.DefaultRegistryURL, + "registry_url": registryPathOrURL, "binaries": binariesWithStatus, }) } @@ -95,8 +102,9 @@ func getNixBuildRegistry(c *fiber.Ctx) error { }) } - // Load registry for metadata (desc, tags) - registry, _ := installer.LoadRegistry("", nil) + // Load registry for metadata (desc, tags) - use custom registry_url if provided + registryPathOrURL := c.Query("registry_url", "") + registry, _ := installer.LoadRegistry(registryPathOrURL, nil) // Build response with categories and tool metadata categoriesData := make([]map[string]interface{}, 0) @@ -138,6 +146,7 @@ func getNixBuildRegistry(c *fiber.Ctx) error { return c.JSON(fiber.Map{ "registry_mode": "nix-build", + "registry_url": registryPathOrURL, "nix_installed": installer.IsNixInstalled(), "categories": categoriesData, }) diff --git a/test/e2e/api_test.go b/test/e2e/api_test.go index fd08d2d..d458419 100644 --- a/test/e2e/api_test.go +++ b/test/e2e/api_test.go @@ -8,7 +8,10 @@ import ( "io" "net" "net/http" + "net/url" "os/exec" + "path/filepath" + "runtime" "testing" "time" @@ -469,16 +472,62 @@ func testScheduleEndpoints(t *testing.T, log *TestLogger) { log.Success("Schedule endpoints OK") } +// getPresetRegistryPath returns the absolute path to the bundled direct-fetch registry file +func getPresetRegistryPath(t *testing.T) string { + t.Helper() + _, filename, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("Failed to get caller info") + } + return filepath.Join(filepath.Dir(filename), "..", "..", "public", "presets", "registry-metadata-direct-fetch.json") +} + // testRegistryEndpoint tests metadata registry endpoint func testRegistryEndpoint(t *testing.T, log *TestLogger) { log.Info("Testing registry endpoint") + presetRegistryPath := getPresetRegistryPath(t) + + // Default: embedded registry, direct-fetch mode + log.Info("Testing default registry (embedded)") resp := apiGet(t, "/osm/api/registry-info") assert.Equal(t, 200, resp.StatusCode, "GET /osm/api/registry-info should return 200") body := parseJSONResponse(t, resp) + assert.Equal(t, "direct-fetch", body["registry_mode"], "Default mode should be direct-fetch") assert.Contains(t, body, "registry_url", "Should contain registry_url") assert.Contains(t, body, "binaries", "Should contain binaries") + // direct-fetch with local preset registry file https://github.com/j3ssie/osmedeus/blob/d5aa39ba30545102a8c9717801f16879f8197736/public/presets/registry-metadata-direct-fetch.json + log.Info("Testing registry_url with preset file (direct-fetch)") + encodedPath := url.QueryEscape(presetRegistryPath) + resp = apiGet(t, "/osm/api/registry-info?registry_url="+encodedPath) + assert.Equal(t, 200, resp.StatusCode, "GET /osm/api/registry-info with preset file should return 200") + body = parseJSONResponse(t, resp) + assert.Equal(t, "direct-fetch", body["registry_mode"], "Mode should be direct-fetch") + assert.Equal(t, presetRegistryPath, body["registry_url"], "registry_url in response should match the path passed") + binaries, ok := body["binaries"].(map[string]interface{}) + assert.True(t, ok, "Binaries should be a map") + assert.Contains(t, binaries, "amass", "Preset registry should contain amass") + assert.Contains(t, binaries, "nuclei", "Preset registry should contain nuclei") + + // nix-build mode (may or may not have Nix; endpoint must respond regardless) + log.Info("Testing registry_mode=nix-build (no custom registry_url)") + resp = apiGet(t, "/osm/api/registry-info?registry_mode=nix-build") + assert.Equal(t, 200, resp.StatusCode, "GET /osm/api/registry-info?registry_mode=nix-build should return 200") + body = parseJSONResponse(t, resp) + assert.Equal(t, "nix-build", body["registry_mode"], "Mode should be nix-build") + assert.Contains(t, body, "nix_installed", "Should contain nix_installed field") + assert.Contains(t, body, "categories", "Should contain categories") + assert.Contains(t, body, "registry_url", "nix-build response should contain registry_url") + + // nix-build mode with preset registry_url for metadata overlay + log.Info("Testing registry_mode=nix-build with preset registry_url for metadata") + resp = apiGet(t, "/osm/api/registry-info?registry_mode=nix-build®istry_url="+encodedPath) + assert.Equal(t, 200, resp.StatusCode, "GET /osm/api/registry-info nix-build+registry_url should return 200") + body = parseJSONResponse(t, resp) + assert.Equal(t, "nix-build", body["registry_mode"], "Mode should be nix-build") + assert.Equal(t, presetRegistryPath, body["registry_url"], "registry_url in nix-build response should match the path passed") + log.Success("Registry endpoint OK") }