forked from mirrors/pkg-proxy
* Fix Composer minified metadata expansion and namespaced package routing Packagist serves metadata in a minified format where only the first version entry has all fields and subsequent entries inherit from the previous one. The proxy was passing this through without expanding it, which meant cooldown filtering could break the inheritance chain (losing fields like `name`) and `~dev` sentinel markers were silently dropped. The proxy now expands the minified format before filtering and rewriting, ensuring every version entry is self-contained. Web UI and API routes used single-segment chi URL params for package names, which broke for Composer's `vendor/name` format. `/package/composer/monolog/monolog` would match the version show route instead of the package show route. All `/package/` and related API routes now use wildcard paths with a `resolvePackageName` helper that tries increasingly longer path prefixes as package names via DB lookup, correctly handling namespaced packages across all endpoints (show, version, browse, compare, vulns). Fixes #61, fixes #62 * Add namespaced package routing tests for all affected ecosystems Verifies the wildcard routing handles slashes in package names for npm (@babel/core), Go modules (github.com/stretchr/testify), OCI images (library/nginx), Conda (conda-forge/numpy), and Conan (zlib/1.2.13@demo/stable). * Regenerate swagger docs after route refactor The swagger annotations for the old per-endpoint handlers were removed during the wildcard routing refactor. Regenerate to match current state.
713 lines
No EOL
22 KiB
JSON
713 lines
No EOL
22 KiB
JSON
{
|
|
"swagger": "2.0",
|
|
"info": {
|
|
"description": "HTTP API for package enrichment, vulnerability lookup, cache stats, and source browsing.",
|
|
"title": "git-pkgs proxy API",
|
|
"contact": {},
|
|
"version": "0.1.0"
|
|
},
|
|
"basePath": "/",
|
|
"paths": {
|
|
"/api/browse/{ecosystem}/{name}/{version}": {
|
|
"get": {
|
|
"description": "Lists files from the first cached artifact for a package version.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"browse"
|
|
],
|
|
"summary": "List files inside a cached artifact",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Ecosystem",
|
|
"name": "ecosystem",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Package name",
|
|
"name": "name",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Version",
|
|
"name": "version",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Directory path inside the archive",
|
|
"name": "path",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/server.BrowseListResponse"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not Found",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/browse/{ecosystem}/{name}/{version}/file/{filepath}": {
|
|
"get": {
|
|
"description": "Streams a single file from the cached artifact. The file path may contain slashes.",
|
|
"produces": [
|
|
"application/octet-stream"
|
|
],
|
|
"tags": [
|
|
"browse"
|
|
],
|
|
"summary": "Fetch a file inside a cached artifact",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Ecosystem",
|
|
"name": "ecosystem",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Package name",
|
|
"name": "name",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Version",
|
|
"name": "version",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "File path inside the archive",
|
|
"name": "filepath",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"type": "file"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not Found",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/bulk": {
|
|
"post": {
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"api"
|
|
],
|
|
"summary": "Bulk package lookup by PURL",
|
|
"parameters": [
|
|
{
|
|
"description": "PURLs",
|
|
"name": "request",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/server.BulkRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/server.BulkResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/compare/{ecosystem}/{name}/{fromVersion}/{toVersion}": {
|
|
"get": {
|
|
"description": "Returns a structured diff for two cached versions.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"browse"
|
|
],
|
|
"summary": "Compare two cached versions",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Ecosystem",
|
|
"name": "ecosystem",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Package name",
|
|
"name": "name",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "From version",
|
|
"name": "fromVersion",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "To version",
|
|
"name": "toVersion",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Not Found",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/outdated": {
|
|
"post": {
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"api"
|
|
],
|
|
"summary": "Check outdated packages",
|
|
"parameters": [
|
|
{
|
|
"description": "Packages to check",
|
|
"name": "request",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/server.OutdatedRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/server.OutdatedResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/packages": {
|
|
"get": {
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"api"
|
|
],
|
|
"summary": "List cached packages",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Ecosystem",
|
|
"name": "ecosystem",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"enum": [
|
|
"hits",
|
|
"name",
|
|
"size",
|
|
"cached_at",
|
|
"ecosystem",
|
|
"vulns"
|
|
],
|
|
"type": "string",
|
|
"description": "Sort",
|
|
"name": "sort",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/server.PackagesListResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/search": {
|
|
"get": {
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"api"
|
|
],
|
|
"summary": "Search cached packages",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Query",
|
|
"name": "q",
|
|
"in": "query",
|
|
"required": true
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Ecosystem",
|
|
"name": "ecosystem",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/server.SearchResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad Request",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/health": {
|
|
"get": {
|
|
"produces": [
|
|
"text/plain"
|
|
],
|
|
"tags": [
|
|
"meta"
|
|
],
|
|
"summary": "Health check",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"503": {
|
|
"description": "Service Unavailable",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/stats": {
|
|
"get": {
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"meta"
|
|
],
|
|
"summary": "Cache statistics",
|
|
"responses": {
|
|
"200": {
|
|
"description": "OK",
|
|
"schema": {
|
|
"$ref": "#/definitions/server.StatsResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal Server Error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"definitions": {
|
|
"server.BrowseFileInfo": {
|
|
"type": "object",
|
|
"properties": {
|
|
"is_dir": {
|
|
"type": "boolean"
|
|
},
|
|
"mod_time": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"path": {
|
|
"type": "string"
|
|
},
|
|
"size": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"server.BrowseListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"files": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/server.BrowseFileInfo"
|
|
}
|
|
},
|
|
"path": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"server.BulkRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"purls": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"server.BulkResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"packages": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"$ref": "#/definitions/server.PackageResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"server.OutdatedPackage": {
|
|
"type": "object",
|
|
"properties": {
|
|
"ecosystem": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"version": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"server.OutdatedRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"packages": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/server.OutdatedPackage"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"server.OutdatedResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"results": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/server.OutdatedResult"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"server.OutdatedResult": {
|
|
"type": "object",
|
|
"properties": {
|
|
"ecosystem": {
|
|
"type": "string"
|
|
},
|
|
"is_outdated": {
|
|
"type": "boolean"
|
|
},
|
|
"latest_version": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"version": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"server.PackageListResult": {
|
|
"type": "object",
|
|
"properties": {
|
|
"cached_at": {
|
|
"type": "string"
|
|
},
|
|
"ecosystem": {
|
|
"type": "string"
|
|
},
|
|
"hits": {
|
|
"type": "integer"
|
|
},
|
|
"latest_version": {
|
|
"type": "string"
|
|
},
|
|
"license": {
|
|
"type": "string"
|
|
},
|
|
"license_category": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"size": {
|
|
"type": "integer"
|
|
},
|
|
"vuln_count": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"server.PackageResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"ecosystem": {
|
|
"type": "string"
|
|
},
|
|
"homepage": {
|
|
"type": "string"
|
|
},
|
|
"latest_version": {
|
|
"type": "string"
|
|
},
|
|
"license": {
|
|
"type": "string"
|
|
},
|
|
"license_category": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"registry_url": {
|
|
"type": "string"
|
|
},
|
|
"repository": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"server.PackagesListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"ecosystem": {
|
|
"type": "string"
|
|
},
|
|
"page": {
|
|
"type": "integer"
|
|
},
|
|
"per_page": {
|
|
"type": "integer"
|
|
},
|
|
"results": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/server.PackageListResult"
|
|
}
|
|
},
|
|
"sort_by": {
|
|
"type": "string"
|
|
},
|
|
"total": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"server.SearchPackageResult": {
|
|
"type": "object",
|
|
"properties": {
|
|
"cached_at": {
|
|
"type": "string"
|
|
},
|
|
"ecosystem": {
|
|
"type": "string"
|
|
},
|
|
"hits": {
|
|
"type": "integer"
|
|
},
|
|
"latest_version": {
|
|
"type": "string"
|
|
},
|
|
"license": {
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"size": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"server.SearchResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"query": {
|
|
"type": "string"
|
|
},
|
|
"results": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/server.SearchPackageResult"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"server.StatsResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"cached_artifacts": {
|
|
"type": "integer"
|
|
},
|
|
"database_path": {
|
|
"type": "string"
|
|
},
|
|
"storage_url": {
|
|
"type": "string"
|
|
},
|
|
"total_size": {
|
|
"type": "string"
|
|
},
|
|
"total_size_bytes": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |