From 6344749cdc8b6259d97a738b826bd711a80164d8 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 3 Sep 2025 12:39:12 -0500 Subject: [PATCH 1/3] types/v1: add JSON API v1 types and schemas Signed-off-by: Hank Donnay --- .../types/v1/affected_manifests.schema.json | 30 ++++ .../types/v1/bulk_delete.schema.json | 11 ++ httptransport/types/v1/cpe.schema.json | 19 +++ httptransport/types/v1/digest.schema.json | 26 +++ .../types/v1/distribution.schema.json | 49 ++++++ .../types/v1/environment.schema.json | 29 ++++ httptransport/types/v1/error.schema.json | 20 +++ .../types/v1/index_report.schema.json | 62 +++++++ .../types/v1/index_state.schema.json | 16 ++ httptransport/types/v1/layer.schema.json | 39 +++++ httptransport/types/v1/manifest.schema.json | 24 +++ .../types/v1/normalized_severity.schema.json | 14 ++ .../types/v1/notification.schema.json | 34 ++++ .../types/v1/notification_page.schema.json | 44 +++++ httptransport/types/v1/package.schema.json | 55 ++++++ httptransport/types/v1/range.schema.json | 19 +++ httptransport/types/v1/repository.schema.json | 34 ++++ httptransport/types/v1/types.go | 157 ++++++++++++++++++ .../types/v1/update_diff.schema.json | 9 + httptransport/types/v1/updatekind_string.go | 25 +++ .../types/v1/vulnerability.schema.json | 36 ++++ .../types/v1/vulnerability_core.schema.json | 75 +++++++++ .../types/v1/vulnerability_report.schema.json | 77 +++++++++ .../v1/vulnerability_summary.schema.json | 9 + 24 files changed, 913 insertions(+) create mode 100644 httptransport/types/v1/affected_manifests.schema.json create mode 100644 httptransport/types/v1/bulk_delete.schema.json create mode 100644 httptransport/types/v1/cpe.schema.json create mode 100644 httptransport/types/v1/digest.schema.json create mode 100644 httptransport/types/v1/distribution.schema.json create mode 100644 httptransport/types/v1/environment.schema.json create mode 100644 httptransport/types/v1/error.schema.json create mode 100644 httptransport/types/v1/index_report.schema.json create mode 100644 httptransport/types/v1/index_state.schema.json create mode 100644 httptransport/types/v1/layer.schema.json create mode 100644 httptransport/types/v1/manifest.schema.json create mode 100644 httptransport/types/v1/normalized_severity.schema.json create mode 100644 httptransport/types/v1/notification.schema.json create mode 100644 httptransport/types/v1/notification_page.schema.json create mode 100644 httptransport/types/v1/package.schema.json create mode 100644 httptransport/types/v1/range.schema.json create mode 100644 httptransport/types/v1/repository.schema.json create mode 100644 httptransport/types/v1/types.go create mode 100644 httptransport/types/v1/update_diff.schema.json create mode 100644 httptransport/types/v1/updatekind_string.go create mode 100644 httptransport/types/v1/vulnerability.schema.json create mode 100644 httptransport/types/v1/vulnerability_core.schema.json create mode 100644 httptransport/types/v1/vulnerability_report.schema.json create mode 100644 httptransport/types/v1/vulnerability_summary.schema.json diff --git a/httptransport/types/v1/affected_manifests.schema.json b/httptransport/types/v1/affected_manifests.schema.json new file mode 100644 index 0000000000..1f12fee324 --- /dev/null +++ b/httptransport/types/v1/affected_manifests.schema.json @@ -0,0 +1,30 @@ +{ + "$id": "https://clairproject.org/api/http/v1/affected_manifests.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Affected Manifests", + "type": "object", + "description": "**This is an internal type, documented for completeness.**\n\nManifests affected by the specified vulnerability objects.", + "properties": { + "vulnerabilities": { + "type": "object", + "description": "Vulnerability objects.", + "additionalProperties": { + "$ref": "vulnerability.schema.json" + } + }, + "vulnerable_manifests": { + "type": "object", + "description": "Mapping of manifest digests to vulnerability identifiers.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string", + "description": "An identifier to be used in the \"#/vulnerabilities\" object." + } + } + } + }, + "required": [ + "vulnerable_manifests" + ] +} diff --git a/httptransport/types/v1/bulk_delete.schema.json b/httptransport/types/v1/bulk_delete.schema.json new file mode 100644 index 0000000000..9306e2c893 --- /dev/null +++ b/httptransport/types/v1/bulk_delete.schema.json @@ -0,0 +1,11 @@ +{ + "$id": "https://clairproject.org/api/http/v1/bulk_delete.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Bulk Delete", + "type": "array", + "description": "Array of manifest digests to delete from the system.", + "items": { + "$ref": "digest.schema.json", + "description": "Manifest digest to delete from the system." + } +} diff --git a/httptransport/types/v1/cpe.schema.json b/httptransport/types/v1/cpe.schema.json new file mode 100644 index 0000000000..9b5a9aed81 --- /dev/null +++ b/httptransport/types/v1/cpe.schema.json @@ -0,0 +1,19 @@ +{ + "$id": "https://clairproject.org/api/http/v1/cpe.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Common Platform Enumeration Name", + "description": "This is a CPE Name in either v2.2 \"URI\" form or v2.3 \"Formatted String\" form.", + "$comment": "Clair only produces v2.3 CPE Names. Any v2.2 Names will be normalized into v2.3 form.", + "oneOf": [ + { + "description": "This is the CPE 2.2 regexp: https://cpe.mitre.org/specification/2.2/cpe-language_2.2.xsd", + "type": "string", + "pattern": "^[c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6}$" + }, + { + "description": "This is the CPE 2.3 regexp: https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd", + "type": "string", + "pattern": "^cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){4}$" + } + ] +} diff --git a/httptransport/types/v1/digest.schema.json b/httptransport/types/v1/digest.schema.json new file mode 100644 index 0000000000..8ca37862ce --- /dev/null +++ b/httptransport/types/v1/digest.schema.json @@ -0,0 +1,26 @@ +{ + "$id": "https://clairproject.org/api/http/v1/digest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Digest", + "description": "A digest acts as a content identifier, enabling content addressability.", + "oneOf": [ + { + "$comment": "SHA256: MUST be implemented", + "description": "SHA256", + "type": "string", + "pattern": "^sha256:[a-f0-9]{64}$" + }, + { + "$comment": "SHA512: MAY be implemented", + "description": "SHA512", + "type": "string", + "pattern": "^sha512:[a-f0-9]{128}$" + }, + { + "$comment": "BLAKE3: MAY be implemented", + "description": "BLAKE3\n\n**Currently not implemented.**", + "type": "string", + "pattern": "^blake3:[a-f0-9]{64}$" + } + ] +} diff --git a/httptransport/types/v1/distribution.schema.json b/httptransport/types/v1/distribution.schema.json new file mode 100644 index 0000000000..fa473bb4f6 --- /dev/null +++ b/httptransport/types/v1/distribution.schema.json @@ -0,0 +1,49 @@ +{ + "$id": "https://clairproject.org/api/http/v1/distribution.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Distribution", + "type": "object", + "description": "Distribution is the accompanying system context of a Package.", + "properties": { + "id": { + "description": "Unique ID for this Distribution. May be unique to the response document, not the whole system.", + "type": "string" + }, + "did": { + "description": "A lower-case string (no spaces or other characters outside of 0–9, a–z, \".\", \"_\", and \"-\") identifying the operating system, excluding any version information and suitable for processing by scripts or usage in generated filenames.", + "type": "string" + }, + "name": { + "description": "A string identifying the operating system.", + "type": "string" + }, + "version": { + "description": "A string identifying the operating system version, excluding any OS name information, possibly including a release code name, and suitable for presentation to the user.", + "type": "string" + }, + "version_code_name": { + "description": "A lower-case string (no spaces or other characters outside of 0–9, a–z, \".\", \"_\", and \"-\") identifying the operating system release code name, excluding any OS name information or release version, and suitable for processing by scripts or usage in generated filenames.", + "type": "string" + }, + "version_id": { + "description": "A lower-case string (mostly numeric, no spaces or other characters outside of 0–9, a–z, \".\", \"_\", and \"-\") identifying the operating system version, excluding any OS name information or release code name.", + "type": "string" + }, + "arch": { + "description": "A string identifying the OS architecture.", + "type": "string" + }, + "cpe": { + "description": "Common Platform Enumeration name.", + "$ref": "cpe.schema.json" + }, + "pretty_name": { + "description": "A pretty operating system name in a format suitable for presentation to the user.", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] +} diff --git a/httptransport/types/v1/environment.schema.json b/httptransport/types/v1/environment.schema.json new file mode 100644 index 0000000000..20d7a5f0b8 --- /dev/null +++ b/httptransport/types/v1/environment.schema.json @@ -0,0 +1,29 @@ +{ + "$id": "https://clairproject.org/api/http/v1/environment.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Environment", + "type": "object", + "description": "Environment describes the surrounding environment a package was discovered in.", + "properties": { + "package_db": { + "description": "The database the associated Package was discovered in.", + "type": "string" + }, + "distribution_id": { + "description": "The ID of the Distribution of the associated Package.", + "type": "string" + }, + "introduced_in": { + "description": "The Layer the associated Package was introduced in.", + "$ref": "digest.schema.json" + }, + "repository_ids": { + "description": "The IDs of the Repositories of the associated Package.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false +} diff --git a/httptransport/types/v1/error.schema.json b/httptransport/types/v1/error.schema.json new file mode 100644 index 0000000000..ffb1209425 --- /dev/null +++ b/httptransport/types/v1/error.schema.json @@ -0,0 +1,20 @@ +{ + "$id": "https://clairproject.org/api/http/v1/error.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Error", + "type": "object", + "description": "A general error response.", + "properties": { + "code": { + "type": "string", + "description": "a code for this particular error" + }, + "message": { + "type": "string", + "description": "a message with further detail" + } + }, + "required": [ + "message" + ] +} diff --git a/httptransport/types/v1/index_report.schema.json b/httptransport/types/v1/index_report.schema.json new file mode 100644 index 0000000000..f6fbb17f3f --- /dev/null +++ b/httptransport/types/v1/index_report.schema.json @@ -0,0 +1,62 @@ +{ + "$id": "https://clairproject.org/api/http/v1/index_report.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Index Report", + "type": "object", + "description": "An index of the contents of a Manifest.", + "properties": { + "manifest_hash": { + "$ref": "digest.schema.json", + "description": "The Manifest's digest." + }, + "state": { + "type": "string", + "description": "The current state of the index operation" + }, + "err": { + "type": "string", + "description": "An error message on event of unsuccessful index" + }, + "success": { + "type": "boolean", + "description": "A bool indicating succcessful index" + }, + "packages": { + "type": "object", + "description": "A map of Package objects indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "package.schema.json" + } + }, + "distributions": { + "type": "object", + "description": "A map of Distribution objects indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "distribution.schema.json" + } + }, + "repository": { + "type": "object", + "description": "A map of Repository objects indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "repository.schema.json" + } + }, + "environments": { + "type": "object", + "description": "A map of Environment arrays indexed by a Package's identifier.", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "environment.schema.json" + } + } + } + }, + "additionalProperties": false, + "required": [ + "manifest_hash", + "state", + "success" + ] +} diff --git a/httptransport/types/v1/index_state.schema.json b/httptransport/types/v1/index_state.schema.json new file mode 100644 index 0000000000..19645519cb --- /dev/null +++ b/httptransport/types/v1/index_state.schema.json @@ -0,0 +1,16 @@ +{ + "$id": "https://clairproject.org/api/http/v1/index_state.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Index State", + "type": "object", + "description": "Information on the state of the indexer system.", + "properties": { + "state": { + "type": "string", + "description": "an opaque token" + } + }, + "required": [ + "state" + ] +} diff --git a/httptransport/types/v1/layer.schema.json b/httptransport/types/v1/layer.schema.json new file mode 100644 index 0000000000..f69f81aac0 --- /dev/null +++ b/httptransport/types/v1/layer.schema.json @@ -0,0 +1,39 @@ +{ + "$id": "https://clairproject.org/api/http/v1/layer.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Layer", + "type": "object", + "description": "Layer is a description of a container layer. It should contain enough information to fetch the layer.", + "properties": { + "hash": { + "$ref": "digest.schema.json", + "description": "Digest of the layer blob." + }, + "uri": { + "type": "string", + "description": "A URI indicating where the layer blob can be downloaded from." + }, + "headers": { + "description": "Any additional HTTP-style headers needed for requesting layers.", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9\\-_]+$": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "media_type": { + "description": "The OCI Layer media type for this layer.", + "type": "string", + "pattern": "^application/vnd\\.oci\\.image\\.layer\\.v1\\.tar(\\+(gzip|zstd))?$" + } + }, + "additionalProperties": false, + "required": [ + "hash", + "uri" + ] +} diff --git a/httptransport/types/v1/manifest.schema.json b/httptransport/types/v1/manifest.schema.json new file mode 100644 index 0000000000..15face0a91 --- /dev/null +++ b/httptransport/types/v1/manifest.schema.json @@ -0,0 +1,24 @@ +{ + "$id": "https://clairproject.org/api/http/v1/manifest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Manifest", + "type": "object", + "description": "A description of an OCI Image Manifest.", + "properties": { + "hash": { + "$ref": "digest.schema.json", + "description": "The OCI Image Manifest's digest.\n\nThis is used as an identifier throughout the system. This **SHOULD** be the same as the OCI Image Manifest's digest, but this is not enforced." + }, + "layers": { + "type": "array", + "description": "The OCI Layers making up the Image, in order.", + "items": { + "$ref": "layer.schema.json" + } + } + }, + "additionalProperties": false, + "required": [ + "hash" + ] +} diff --git a/httptransport/types/v1/normalized_severity.schema.json b/httptransport/types/v1/normalized_severity.schema.json new file mode 100644 index 0000000000..c3ef09d055 --- /dev/null +++ b/httptransport/types/v1/normalized_severity.schema.json @@ -0,0 +1,14 @@ +{ + "$id": "https://clairproject.org/api/http/v1/normalized_severity.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Normalized Severity", + "description": "Standardized severity values.", + "enum": [ + "Unknown", + "Negligible", + "Low", + "Medium", + "High", + "Critical" + ] +} diff --git a/httptransport/types/v1/notification.schema.json b/httptransport/types/v1/notification.schema.json new file mode 100644 index 0000000000..d9deb2d665 --- /dev/null +++ b/httptransport/types/v1/notification.schema.json @@ -0,0 +1,34 @@ +{ + "$id": "https://clairproject.org/api/http/v1/notification.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Notification", + "type": "object", + "description": "A change in a manifest affected by a vulnerability.", + "properties": { + "id": { + "description": "Unique identifier for this notification.", + "type": "string" + }, + "manifest": { + "$ref": "digest.schema.json", + "description": "The digest of the manifest affected by the provided vulnerability." + }, + "reason": { + "description": "The reason for the notifcation.", + "enum": [ + "added", + "removed" + ] + }, + "vulnerability": { + "$ref": "vulnerability_summary.schema.json" + } + }, + "additionalProperties": false, + "required": [ + "id", + "manifest", + "reason", + "vulnerability" + ] +} diff --git a/httptransport/types/v1/notification_page.schema.json b/httptransport/types/v1/notification_page.schema.json new file mode 100644 index 0000000000..8a01866856 --- /dev/null +++ b/httptransport/types/v1/notification_page.schema.json @@ -0,0 +1,44 @@ +{ + "$id": "https://clairproject.org/api/http/v1/notification_page.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Notification Page", + "type": "object", + "description": "A page description and list of notifications.", + "properties": { + "page": { + "description": "An object informing the client the next page to retrieve.", + "type": "object", + "properties": { + "size": { + "type": "integer" + }, + "next": { + "oneOf": [ + { + "type": "string" + }, + { + "const": "-1" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "size" + ] + }, + "notifications": { + "description": "Notifications within this page.", + "type": "array", + "items": { + "$ref": "notification.schema.json" + } + } + }, + "additionalProperties": false, + "required": [ + "page", + "notifications" + ] +} diff --git a/httptransport/types/v1/package.schema.json b/httptransport/types/v1/package.schema.json new file mode 100644 index 0000000000..a4a54cd9af --- /dev/null +++ b/httptransport/types/v1/package.schema.json @@ -0,0 +1,55 @@ +{ + "$id": "https://clairproject.org/api/http/v1/package.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Package", + "type": "object", + "description": "Description of installed software.", + "properties": { + "id": { + "description": "Unique ID for this Package. May be unique to the response document, not the whole system.", + "type": "string" + }, + "name": { + "description": "Identifier of this Package.\n\nThe uniqueness and scoping of this name depends on the packaging system.", + "type": "string" + }, + "version": { + "description": "Version of this Package, as reported by the packaging system.", + "type": "string" + }, + "kind": { + "description": "The \"kind\" of this Package.", + "enum": [ + "BINARY", + "SOURCE" + ], + "default": "BINARY" + }, + "source": { + "$ref": "#", + "description": "Source Package that produced the current binary Package, if known." + }, + "normalized_version": { + "description": "Normalized representation of the discoverd version.\n\nThe format is not specific, but is guarenteed to be forward compatible.", + "type": "string" + }, + "module": { + "description": "An identifier for intra-Repository grouping of packages.\n\nLikely only relevant on rpm-based systems.", + "type": "string" + }, + "arch": { + "description": "Native architecture for the Package.", + "type": "string", + "$comment": "This should become and enum in the future." + }, + "cpe": { + "$ref": "cpe.schema.json", + "description": "CPE Name for the Package." + } + }, + "additionalProperties": false, + "required": [ + "name", + "version" + ] +} diff --git a/httptransport/types/v1/range.schema.json b/httptransport/types/v1/range.schema.json new file mode 100644 index 0000000000..924a430f80 --- /dev/null +++ b/httptransport/types/v1/range.schema.json @@ -0,0 +1,19 @@ +{ + "$id": "https://clairproject.org/api/http/v1/range.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Range", + "type": "object", + "description": "A range of versions.", + "properties": { + "[": { + "type": "string", + "description": "Lower bound, inclusive." + }, + ")": { + "type": "string", + "description": "Upper bound, exclusive." + } + }, + "minProperties": 1, + "additionalProperties": false +} diff --git a/httptransport/types/v1/repository.schema.json b/httptransport/types/v1/repository.schema.json new file mode 100644 index 0000000000..1bb0bd9410 --- /dev/null +++ b/httptransport/types/v1/repository.schema.json @@ -0,0 +1,34 @@ +{ + "$id": "https://clairproject.org/api/http/v1/repository.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Repository", + "type": "object", + "description": "Description of a software repository", + "properties": { + "id": { + "description": "Unique ID for this Repository. May be unique to the response document, not the whole system.", + "type": "string" + }, + "name": { + "description": "Human-relevant name for the Repository.", + "type": "string" + }, + "key": { + "description": "Machine-relevant name for the Repository.", + "type": "string" + }, + "uri": { + "description": "URI describing the Repository.", + "type": "string", + "format": "uri" + }, + "cpe": { + "description": "CPE name for the Repository.", + "$ref": "cpe.schema.json" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] +} diff --git a/httptransport/types/v1/types.go b/httptransport/types/v1/types.go new file mode 100644 index 0000000000..a0f14687bb --- /dev/null +++ b/httptransport/types/v1/types.go @@ -0,0 +1,157 @@ +// Package types provides concrete types for the HTTP API. +package types + +import ( + "embed" + "encoding/json" + "fmt" + "time" +) + +//go:embed *.schema.json +var Schema embed.FS + +// Indexer types +type ( + Manifest struct { + Hash string `json:"hash"` + Layers []Layer `json:"layers,omitempty"` + } + + Layer struct { + Hash string `json:"hash"` + URI string `json:"uri"` + Headers map[string][]string `json:"headers,omitempty"` + } + + IndexReport struct { + Hash string `json:"manifest_hash"` + State string `json:"state"` + Err string `json:"err,omitempty"` + Packages map[string]*Package `json:"packages,omitempty"` + Distributions map[string]*Distribution `json:"distributions,omitempty"` + Repositories map[string]*Repository `json:"repository,omitempty"` + Environments map[string][]*Environment `json:"environments,omitempty"` + Success bool `json:"success"` + } + + Package struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Kind string `json:"kind,omitempty"` + Source *Package `json:"source,omitempty"` + NormalizedVersion string `json:"normalized_version,omitempty"` + Module string `json:"module,omitempty"` + Arch string `json:"arch,omitempty"` + CPE string `json:"cpe,omitempty"` + } + + Distribution struct { + ID string `json:"id"` + DID string `json:"did,omitempty"` + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + VersionCodeName string `json:"version_code_name,omitempty"` + VersionID string `json:"version_id,omitempty"` + Arch string `json:"arch,omitempty"` + CPE string `json:"cpe,omitempty"` + PrettyName string `json:"pretty_name,omitempty"` + } + + Repository struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Key string `json:"key,omitempty"` + URI string `json:"uri,omitempty"` + CPE string `json:"cpe,omitempty"` + } + + Environment struct { + IntroducedIn string `json:"introduced_in"` + PackageDB string `json:"package_db,omitempty"` + DistributionID string `json:"distribution_id,omitempty"` + RepositoryIDs []string `json:"repository_ids,omitempty"` + } + + IndexerState struct { + State string + } + + VulnerabilityBatch struct { + Vulnerabilities []Vulnerability + } +) + +// Matcher types +type ( + VulnerabilityReport struct { + Hash string `json:"manifest_hash"` + Packages map[string]*Package `json:"packages,omitempty"` + Vulnerabilities map[string]*Vulnerability `json:"vulnerabilities,omitempty"` + Environments map[string][]*Environment `json:"environments,omitempty"` + PackageVulnerabilities map[string][]string `json:"package_vulnerabilities,omitempty"` + Distributions map[string]*Distribution `json:"distributions,omitempty"` + Repositories map[string]*Repository `json:"repository,omitempty"` + Enrichments map[string][]json.RawMessage `json:"enrichments,omitempty"` + } + + Vulnerability struct { + ID string `json:"id"` + Updater string `json:"updater,omitempty"` + Name string `json:"name,omitempty"` + Issued time.Time `json:"issued"` + Severity string `json:"severity,omitempty"` + NormalizedSeverity string `json:"normalized_severity,omitempty"` + Description string `json:"description,omitempty"` + Links string `json:"links,omitempty"` + Package *Package `json:"package,omitempty"` + Dist *Distribution `json:"distribution,omitempty"` + Repo *Repository `json:"repository,omitempty"` + FixedInVersion string `json:"fixed_in_version"` + Range *Range `json:"range,omitempty"` + ArchOperation string `json:"arch_op,omitempty"` + } + + Range struct { + Lower string `json:"[,omitempty"` + Upper string `json:"),omitempty"` + } + + UpdateKind int + + UpdateOperation struct { + Ref string `json:"ref"` + Updater string `json:"updater"` + Fingerprint []byte `json:"fingerprint"` + Date time.Time `json:"date"` + Kind UpdateKind `json:"kind"` + } + + UpdateDiff struct { + Prev UpdateOperation `json:"prev"` + Cur UpdateOperation `json:"cur"` + Added []Vulnerability `json:"added"` + Removed []Vulnerability `json:"removed"` + } +) + +//go:generate go run golang.org/x/tools/cmd/stringer@latest -type UpdateKind -linecomment + +const ( + _ UpdateKind = iota + UpdateVulnerability // vulnerability + UpdateEnrichment // enrichment +) + +// API types +type ( + Error struct { + Code int + Message string + } +) + +func (e *Error) Error() string { + return fmt.Sprintf("%s (HTTP %d)", e.Message, e.Code) +} diff --git a/httptransport/types/v1/update_diff.schema.json b/httptransport/types/v1/update_diff.schema.json new file mode 100644 index 0000000000..6986db50b8 --- /dev/null +++ b/httptransport/types/v1/update_diff.schema.json @@ -0,0 +1,9 @@ +{ + "$id": "https://clairproject.org/api/http/v1/update_diff.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Update Difference", + "type": "object", + "description": "**This is an internal type, documented for completeness.**\n\nTKTK", + "additionalProperties": false, + "required": [ ] +} diff --git a/httptransport/types/v1/updatekind_string.go b/httptransport/types/v1/updatekind_string.go new file mode 100644 index 0000000000..39d8627236 --- /dev/null +++ b/httptransport/types/v1/updatekind_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type UpdateKind -linecomment"; DO NOT EDIT. + +package types + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[UpdateVulnerability-1] + _ = x[UpdateEnrichment-2] +} + +const _UpdateKind_name = "vulnerabilityenrichment" + +var _UpdateKind_index = [...]uint8{0, 13, 23} + +func (i UpdateKind) String() string { + i -= 1 + if i < 0 || i >= UpdateKind(len(_UpdateKind_index)-1) { + return "UpdateKind(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _UpdateKind_name[_UpdateKind_index[i]:_UpdateKind_index[i+1]] +} diff --git a/httptransport/types/v1/vulnerability.schema.json b/httptransport/types/v1/vulnerability.schema.json new file mode 100644 index 0000000000..2cb9f7501c --- /dev/null +++ b/httptransport/types/v1/vulnerability.schema.json @@ -0,0 +1,36 @@ +{ + "$id": "https://clairproject.org/api/http/v1/vulnerability.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Vulnerability", + "type": "object", + "description": "Description of a software flaw.", + "$ref": "vulnerability_core.schema.json", + "properties": { + "id": { + "description": "", + "type": "string" + }, + "updater": { + "description": "", + "type": "string" + }, + "description": { + "description": "", + "type": "string" + }, + "issued": { + "description": "", + "type": "string", + "format": "date-time" + }, + "links": { + "description": "", + "type": "string" + } + }, + "unevaluatedProperties": false, + "required": [ + "id", + "updater" + ] +} diff --git a/httptransport/types/v1/vulnerability_core.schema.json b/httptransport/types/v1/vulnerability_core.schema.json new file mode 100644 index 0000000000..b5f665b97d --- /dev/null +++ b/httptransport/types/v1/vulnerability_core.schema.json @@ -0,0 +1,75 @@ +{ + "$id": "https://clairproject.org/api/http/v1/vulnerability_core.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Vulnerability Core", + "type": "object", + "description": "The core elements of vulnerabilities in the Clair system.", + "properties": { + "name": { + "type": "string", + "description": "Human-readable name, as presented in the vendor data." + }, + "fixed_in_version": { + "type": "string", + "description": "Version string, as presented in the vendor data." + }, + "severity": { + "type": "string", + "description": "Severity, as presented in the vendor data." + }, + "normalized_severity": { + "$ref": "normalized_severity.schema.json", + "description": "A well defined set of severity strings guaranteed to be present." + }, + "range": { + "$ref": "range.schema.json", + "description": "Range of versions the vulnerability applies to." + }, + "arch_op": { + "description": "Flag indicating how the referenced package's \"arch\" member should be interpreted.", + "enum": [ + "equals", + "not equals", + "pattern match" + ] + }, + "package": { + "$ref": "package.schema.json", + "description": "A package description" + }, + "distribution": { + "$ref": "distribution.schema.json", + "description": "A distribution description" + }, + "repository": { + "$ref": "repository.schema.json", + "description": "A repository description" + } + }, + "required": [ + "name", + "normalized_severity" + ], + "dependentRequired": { + "package": [ + "arch_op" + ] + }, + "anyOf": [ + { + "required": [ + "package" + ] + }, + { + "required": [ + "repository" + ] + }, + { + "required": [ + "distribution" + ] + } + ] +} diff --git a/httptransport/types/v1/vulnerability_report.schema.json b/httptransport/types/v1/vulnerability_report.schema.json new file mode 100644 index 0000000000..8f2658560d --- /dev/null +++ b/httptransport/types/v1/vulnerability_report.schema.json @@ -0,0 +1,77 @@ +{ + "$id": "https://clairproject.org/api/http/v1/vulnerability_report.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Vulnerability Report", + "type": "object", + "description": "A report with discovered packages, package environments, and package vulnerabilities within a Manifest.", + "properties": { + "manifest_hash": { + "$ref": "digest.schema.json", + "description": "The Manifest's digest." + }, + "packages": { + "type": "object", + "description": "A map of Package objects indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "package.schema.json" + } + }, + "distributions": { + "type": "object", + "description": "A map of Distribution objects indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "distribution.schema.json" + } + }, + "repository": { + "type": "object", + "description": "A map of Repository objects indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "repository.schema.json" + } + }, + "environments": { + "type": "object", + "description": "A map of Environment arrays indexed by a Package's identifier.", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "environment.schema.json" + } + } + }, + "vulnerabilities": { + "type": "object", + "description": "A map of Vulnerabilities indexed by a document-local identifier.", + "additionalProperties": { + "$ref": "vulnerability.schema.json" + } + }, + "package_vulnerabilities": { + "type": "object", + "description": "A mapping of Vulnerability identifier lists indexed by Package identifier.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "enrichments": { + "type": "object", + "description": "A mapping of extra \"enrichment\" data by type", + "additionalProperties": { + "type": "array" + } + } + }, + "additionalProperties": false, + "required": [ + "distributions", + "environments", + "manifest_hash", + "packages", + "package_vulnerabilities", + "vulnerabilities" + ] +} diff --git a/httptransport/types/v1/vulnerability_summary.schema.json b/httptransport/types/v1/vulnerability_summary.schema.json new file mode 100644 index 0000000000..8fac0115e5 --- /dev/null +++ b/httptransport/types/v1/vulnerability_summary.schema.json @@ -0,0 +1,9 @@ +{ + "$id": "https://clairproject.org/api/http/v1/vulnerability_summary.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Vulnerability Summary", + "type": "object", + "description": "A summary of a vulnerability.", + "$ref": "vulnerability_core.schema.json", + "unevaluatedProperties": false +} From d12da47af002a91774fec62ac4d11da2fc71d545 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 15 Sep 2025 16:06:29 -0500 Subject: [PATCH 2/3] openapi: rebuild OpenAPI spec Signed-off-by: Hank Donnay --- .gitattributes | 1 + Documentation/reference/api.md | 2319 +++++++++-------- httptransport/api/.gitattributes | 3 + httptransport/api/lib/oapi.jq | 64 + httptransport/api/openapi.zsh | 96 + .../api/v1/examples/bulk_delete.json | 3 + httptransport/api/v1/examples/cpe.json | 2 + .../api/v1/examples/distribution.json | 9 + .../api/v1/examples/environment.json | 7 + httptransport/api/v1/examples/manifest.json | 14 + .../api/v1/examples/notification_page.json | 43 + httptransport/api/v1/examples/package.json | 17 + .../api/v1/examples/vulnerability.json | 31 + .../v1/examples/vulnerability_summary.json | 24 + httptransport/api/v1/openapi.etag | 1 + httptransport/api/v1/openapi.jq | 437 ++++ httptransport/api/v1/openapi.json | 1613 ++++++++++++ httptransport/api/v1/openapi.yaml | 1146 ++++++++ httptransport/discoveryhandler.go | 47 +- httptransport/discoveryhandler_test.go | 130 +- httptransport/indexer_v1.go | 2 +- httptransport/openapi.etag | 1 - httptransport/openapi.json | 1 - httptransport/openapigen.go | 83 - openapi.yaml | 934 ------- 25 files changed, 4822 insertions(+), 2206 deletions(-) create mode 100644 httptransport/api/.gitattributes create mode 100644 httptransport/api/lib/oapi.jq create mode 100755 httptransport/api/openapi.zsh create mode 100644 httptransport/api/v1/examples/bulk_delete.json create mode 100644 httptransport/api/v1/examples/cpe.json create mode 100644 httptransport/api/v1/examples/distribution.json create mode 100644 httptransport/api/v1/examples/environment.json create mode 100644 httptransport/api/v1/examples/manifest.json create mode 100644 httptransport/api/v1/examples/notification_page.json create mode 100644 httptransport/api/v1/examples/package.json create mode 100644 httptransport/api/v1/examples/vulnerability.json create mode 100644 httptransport/api/v1/examples/vulnerability_summary.json create mode 100644 httptransport/api/v1/openapi.etag create mode 100644 httptransport/api/v1/openapi.jq create mode 100644 httptransport/api/v1/openapi.json create mode 100644 httptransport/api/v1/openapi.yaml delete mode 100644 httptransport/openapi.etag delete mode 100644 httptransport/openapi.json delete mode 100644 httptransport/openapigen.go delete mode 100644 openapi.yaml diff --git a/.gitattributes b/.gitattributes index a2363ebf92..279878b8e4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ cmd/build.go export-subst *.go diff=golang +Documentation/reference/api.md linguist-generated diff --git a/Documentation/reference/api.md b/Documentation/reference/api.md index ebc74ab43e..224226725e 100644 --- a/Documentation/reference/api.md +++ b/Documentation/reference/api.md @@ -1,5 +1,5 @@ --- -title: ClairV4 v1.1 +title: Clair Container Analyzer v1.2.0 language_tabs: - python: Python - go: Golang @@ -8,7 +8,8 @@ language_clients: - python: "" - go: "" - javascript: "" -toc_footers: [] +toc_footers: + - External documentation includes: [] search: false highlight_theme: darkula @@ -18,265 +19,22 @@ headingLevel: 2 -

ClairV4 v1.1

+

Clair Container Analyzer v1.2.0

> Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu. -ClairV4 is a set of cooperating microservices which scan, index, and match your container's content with known vulnerabilities. +Clair is a set of cooperating microservices which can index and match a container image's content with known vulnerabilities. + +**Note:** Any endpoints tagged "internal" are documented for completeness but are considered exempt from versioning. Email: Clair Team Web: Clair Team License: Apache License 2.0 -

Notifier

- -## DeleteNotification - - - -> Code samples - -```python -import requests -headers = { - 'Accept': 'application/json' -} - -r = requests.delete('/notifier/api/v1/notification/{notification_id}', headers = headers) - -print(r.json()) - -``` - -```go -package main - -import ( - "bytes" - "net/http" -) - -func main() { - - headers := map[string][]string{ - "Accept": []string{"application/json"}, - } - - data := bytes.NewBuffer([]byte{jsonReq}) - req, err := http.NewRequest("DELETE", "/notifier/api/v1/notification/{notification_id}", data) - req.Header = headers - - client := &http.Client{} - resp, err := client.Do(req) - // ... -} - -``` - -```javascript - -const headers = { - 'Accept':'application/json' -}; - -fetch('/notifier/api/v1/notification/{notification_id}', -{ - method: 'DELETE', - - headers: headers -}) -.then(function(res) { - return res.json(); -}).then(function(body) { - console.log(body); -}); - -``` - -`DELETE /notifier/api/v1/notification/{notification_id}` - -Issues a delete of the provided notification id and all associated notifications. After this delete clients will no longer be able to retrieve notifications. - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|notification_id|path|string|false|A notification ID returned by a callback| - -> Example responses - -> 400 Response - -```json -{ - "code": "string", - "message": "string" -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|OK|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| -|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| - - - -## Retrieve a paginated result of notifications for the provided id. - - - -> Code samples - -```python -import requests -headers = { - 'Accept': 'application/json' -} - -r = requests.get('/notifier/api/v1/notification/{notification_id}', headers = headers) - -print(r.json()) - -``` - -```go -package main - -import ( - "bytes" - "net/http" -) - -func main() { - - headers := map[string][]string{ - "Accept": []string{"application/json"}, - } - - data := bytes.NewBuffer([]byte{jsonReq}) - req, err := http.NewRequest("GET", "/notifier/api/v1/notification/{notification_id}", data) - req.Header = headers - - client := &http.Client{} - resp, err := client.Do(req) - // ... -} - -``` - -```javascript - -const headers = { - 'Accept':'application/json' -}; - -fetch('/notifier/api/v1/notification/{notification_id}', -{ - method: 'GET', - - headers: headers -}) -.then(function(res) { - return res.json(); -}).then(function(body) { - console.log(body); -}); - -``` - -`GET /notifier/api/v1/notification/{notification_id}` - -By performing a GET with a notification_id as a path parameter, the client will retrieve a paginated response of notification objects. - -

Parameters

- -|Name|In|Type|Required|Description| -|---|---|---|---|---| -|notification_id|path|string|false|A notification ID returned by a callback| -|page_size|query|int|false|The maximum number of notifications to deliver in a single page.| -|next|query|string|false|The next page to fetch via id. Typically this number is provided on initial response in the page.next field. The first GET request may omit this field.| - -> Example responses - -> 200 Response - -```json -{ - "page": { - "size": 100, - "next": "1b4d0db2-e757-4150-bbbb-543658144205" - }, - "notifications": [ - { - "id": "5e4b387e-88d3-4364-86fd-063447a6fad2", - "manifest": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", - "reason": "added", - "vulnerability": { - "name": "CVE-2009-5155", - "fixed_in_version": "v0.0.1", - "links": "http://link-to-advisory", - "description": "In the GNU C Library (aka glibc or libc6) before 2.28, parse_reg_exp in posix/regcomp.c misparses alternatives, which allows attackers to cause a denial of service (assertion failure and application exit) or trigger an incorrect result by attempting a regular-expression match.\"", - "normalized_severity": "Unknown", - "package": { - "id": "10", - "name": "libapt-pkg5.0", - "version": "1.6.11", - "kind": "binary", - "normalized_version": "", - "arch": "x86", - "module": "", - "cpe": "", - "source": { - "id": "9", - "name": "apt", - "version": "1.6.11", - "kind": "source", - "source": null - } - }, - "distribution": { - "id": "1", - "did": "ubuntu", - "name": "Ubuntu", - "version": "18.04.3 LTS (Bionic Beaver)", - "version_code_name": "bionic", - "version_id": "18.04", - "arch": "", - "cpe": "", - "pretty_name": "Ubuntu 18.04.3 LTS" - }, - "repository": { - "id": "string", - "name": "string", - "key": "string", - "uri": "string", - "cpe": "string" - } - } - } - ] -} -``` - -

Responses

- -|Status|Meaning|Description|Schema| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A paginated list of notifications|[PagedNotifications](#schemapagednotifications)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| -|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| +# Authentication - +- HTTP Authentication, scheme: bearer Clair's authentication scheme. -

Indexer

+

indexer

## Index the contents of a Manifest @@ -287,8 +45,8 @@ This operation does not require authentication ```python import requests headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json' + 'Content-Type': 'application/vnd.clair.manifest.v1+json', + 'Accept': 'application/vnd.clair.index_report.v1+json' } r = requests.post('/indexer/api/v1/index_report', headers = headers) @@ -308,8 +66,8 @@ import ( func main() { headers := map[string][]string{ - "Content-Type": []string{"application/json"}, - "Accept": []string{"application/json"}, + "Content-Type": []string{"application/vnd.clair.manifest.v1+json"}, + "Accept": []string{"application/vnd.clair.index_report.v1+json"}, } data := bytes.NewBuffer([]byte{jsonReq}) @@ -328,22 +86,19 @@ const inputBody = '{ "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", "layers": [ { - "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "hash": "sha256:2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36", "uri": "https://storage.example.com/blob/2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36", "headers": { - "property1": [ - "string" - ], - "property2": [ - "string" + "Authoriztion": [ + "Bearer hunter2" ] } } ] }'; const headers = { - 'Content-Type':'application/json', - 'Accept':'application/json' + 'Content-Type':'application/vnd.clair.manifest.v1+json', + 'Accept':'application/vnd.clair.index_report.v1+json' }; fetch('/indexer/api/v1/index_report', @@ -371,14 +126,11 @@ By submitting a Manifest object to this endpoint Clair will fetch the layers, sc "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", "layers": [ { - "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "hash": "sha256:2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36", "uri": "https://storage.example.com/blob/2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36", "headers": { - "property1": [ - "string" - ], - "property2": [ - "string" + "Authoriztion": [ + "Bearer hunter2" ] } } @@ -390,7 +142,7 @@ By submitting a Manifest object to this endpoint Clair will fetch the layers, sc |Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|[Manifest](#schemamanifest)|true|none| +|body|body|[manifest](#schemamanifest)|true|Manifest to index.| > Example responses @@ -398,51 +150,17 @@ By submitting a Manifest object to this endpoint Clair will fetch the layers, sc ```json { - "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", - "state": "IndexFinished", - "packages": { - "10": { - "id": "10", - "name": "libapt-pkg5.0", - "version": "1.6.11", - "kind": "binary", - "normalized_version": "", - "arch": "x86", - "module": "", - "cpe": "", - "source": { - "id": "9", - "name": "apt", - "version": "1.6.11", - "kind": "source", - "source": null - } - } - }, - "distributions": { - "1": { - "id": "1", - "did": "ubuntu", - "name": "Ubuntu", - "version": "18.04.3 LTS (Bionic Beaver)", - "version_code_name": "bionic", - "version_id": "18.04", - "arch": "", - "cpe": "", - "pretty_name": "Ubuntu 18.04.3 LTS" - } - }, - "environments": { - "10": [ - { - "package_db": "var/lib/dpkg/status", - "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", - "distribution_id": "1" - } - ] - }, + "manifest_hash": null, + "state": "string", + "err": "string", "success": true, - "err": "" + "packages": {}, + "distributions": {}, + "repository": {}, + "environments": { + "property1": [], + "property2": [] + } } ``` @@ -450,16 +168,26 @@ By submitting a Manifest object to this endpoint Clair will fetch the layers, sc |Status|Meaning|Description|Schema| |---|---|---|---| -|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|IndexReport Created|[IndexReport](#schemaindexreport)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| -|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|IndexReport created. + +Clients may want to avoid reading the body if simply submitting the manifest for later vulnerability reporting.|[index_report](#schemaindex_report)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[error](#schemaerror)| +|412|[Precondition Failed](https://tools.ietf.org/html/rfc7232#section-4.2)|Precondition Failed|None| +|415|[Unsupported Media Type](https://tools.ietf.org/html/rfc7231#section-6.5.13)|Unsupported Media Type|[error](#schemaerror)| +|default|Default|Internal Server Error|[error](#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|201|Location|string||HTTP [Location header](https://httpwg.org/specs/rfc9110.html#field.location)| +|201|Link|string||Web Linking [Link header](https://httpwg.org/specs/rfc8288.html#header)| -## Delete the IndexReport and associated information for the given Manifest hashes, if they exist. +## Delete the referenced manifests. @@ -468,8 +196,8 @@ This operation does not require authentication ```python import requests headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json' + 'Content-Type': 'application/vnd.clair.bulk_delete.v1+json', + 'Accept': 'application/vnd.clair.bulk_delete.v1+json' } r = requests.delete('/indexer/api/v1/index_report', headers = headers) @@ -489,8 +217,8 @@ import ( func main() { headers := map[string][]string{ - "Content-Type": []string{"application/json"}, - "Accept": []string{"application/json"}, + "Content-Type": []string{"application/vnd.clair.bulk_delete.v1+json"}, + "Accept": []string{"application/vnd.clair.bulk_delete.v1+json"}, } data := bytes.NewBuffer([]byte{jsonReq}) @@ -509,8 +237,8 @@ const inputBody = '[ "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3" ]'; const headers = { - 'Content-Type':'application/json', - 'Accept':'application/json' + 'Content-Type':'application/vnd.clair.bulk_delete.v1+json', + 'Accept':'application/vnd.clair.bulk_delete.v1+json' }; fetch('/indexer/api/v1/index_report', @@ -539,11 +267,11 @@ Given a Manifest's content addressable hash, any data related to it will be remo ] ``` -

Parameters

+

Parameters

|Name|In|Type|Required|Description| |---|---|---|---|---| -|body|body|[BulkDelete](#schemabulkdelete)|true|none| +|body|body|[bulk_delete](#schemabulk_delete)|true|Array of manifest digests to delete.| > Example responses @@ -555,19 +283,26 @@ Given a Manifest's content addressable hash, any data related to it will be remo ] ``` -

Responses

+

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|OK|[BulkDelete](#schemabulkdelete)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successfully deleted manifests.|[bulk_delete](#schemabulk_delete)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[error](#schemaerror)| +|415|[Unsupported Media Type](https://tools.ietf.org/html/rfc7231#section-6.5.13)|Unsupported Media Type|[error](#schemaerror)| +|default|Default|Internal Server Error|[error](#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|200|Clair-Error|string||This is a trailer containing any errors encountered while writing the response.| -## Delete the IndexReport and associated information for the given Manifest hash, if exists. +## Delete the referenced manifest. @@ -576,10 +311,10 @@ This operation does not require authentication ```python import requests headers = { - 'Accept': 'application/json' + 'Accept': 'application/vnd.clair.error.v1+json' } -r = requests.delete('/indexer/api/v1/index_report/{manifest_hash}', headers = headers) +r = requests.delete('/indexer/api/v1/index_report/{digest}', headers = headers) print(r.json()) @@ -596,11 +331,11 @@ import ( func main() { headers := map[string][]string{ - "Accept": []string{"application/json"}, + "Accept": []string{"application/vnd.clair.error.v1+json"}, } data := bytes.NewBuffer([]byte{jsonReq}) - req, err := http.NewRequest("DELETE", "/indexer/api/v1/index_report/{manifest_hash}", data) + req, err := http.NewRequest("DELETE", "/indexer/api/v1/index_report/{digest}", data) req.Header = headers client := &http.Client{} @@ -613,10 +348,10 @@ func main() { ```javascript const headers = { - 'Accept':'application/json' + 'Accept':'application/vnd.clair.error.v1+json' }; -fetch('/indexer/api/v1/index_report/{manifest_hash}', +fetch('/indexer/api/v1/index_report/{digest}', { method: 'DELETE', @@ -630,15 +365,15 @@ fetch('/indexer/api/v1/index_report/{manifest_hash}', ``` -`DELETE /indexer/api/v1/index_report/{manifest_hash}` +`DELETE /indexer/api/v1/index_report/{digest}` Given a Manifest's content addressable hash, any data related to it will be removed it it exists. -

Parameters

+

Parameters

|Name|In|Type|Required|Description| |---|---|---|---|---| -|manifest_hash|path|[Digest](#schemadigest)|true|A digest of a manifest that has been indexed previous to this request.| +|digest|path|[digest](#schemadigest)|true|OCI-compatible digest of a referred object.| > Example responses @@ -651,19 +386,20 @@ Given a Manifest's content addressable hash, any data related to it will be remo } ``` -

Responses

+

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| -|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|OK|None| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Success|None| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[error](#schemaerror)| +|415|[Unsupported Media Type](https://tools.ietf.org/html/rfc7231#section-6.5.13)|Unsupported Media Type|[error](#schemaerror)| +|default|Default|Internal Server Error|[error](#schemaerror)| -## Retrieve an IndexReport for the given Manifest hash if exists. +## Retrieve the IndexReport for the referenced manifest. @@ -672,10 +408,10 @@ This operation does not require authentication ```python import requests headers = { - 'Accept': 'application/json' + 'Accept': 'application/vnd.clair.index_report.v1+json' } -r = requests.get('/indexer/api/v1/index_report/{manifest_hash}', headers = headers) +r = requests.get('/indexer/api/v1/index_report/{digest}', headers = headers) print(r.json()) @@ -692,11 +428,11 @@ import ( func main() { headers := map[string][]string{ - "Accept": []string{"application/json"}, + "Accept": []string{"application/vnd.clair.index_report.v1+json"}, } data := bytes.NewBuffer([]byte{jsonReq}) - req, err := http.NewRequest("GET", "/indexer/api/v1/index_report/{manifest_hash}", data) + req, err := http.NewRequest("GET", "/indexer/api/v1/index_report/{digest}", data) req.Header = headers client := &http.Client{} @@ -709,10 +445,10 @@ func main() { ```javascript const headers = { - 'Accept':'application/json' + 'Accept':'application/vnd.clair.index_report.v1+json' }; -fetch('/indexer/api/v1/index_report/{manifest_hash}', +fetch('/indexer/api/v1/index_report/{digest}', { method: 'GET', @@ -726,15 +462,15 @@ fetch('/indexer/api/v1/index_report/{manifest_hash}', ``` -`GET /indexer/api/v1/index_report/{manifest_hash}` +`GET /indexer/api/v1/index_report/{digest}` -Given a Manifest's content addressable hash an IndexReport will be retrieved if exists. +Given a Manifest's content addressable hash, an IndexReport will be retrieved if it exists. -

Parameters

+

Parameters

|Name|In|Type|Required|Description| |---|---|---|---|---| -|manifest_hash|path|[Digest](#schemadigest)|true|A digest of a manifest that has been indexed previous to this request.| +|digest|path|[digest](#schemadigest)|true|OCI-compatible digest of a referred object.| > Example responses @@ -742,63 +478,35 @@ Given a Manifest's content addressable hash an IndexReport will be retrieved if ```json { - "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", - "state": "IndexFinished", - "packages": { - "10": { - "id": "10", - "name": "libapt-pkg5.0", - "version": "1.6.11", - "kind": "binary", - "normalized_version": "", - "arch": "x86", - "module": "", - "cpe": "", - "source": { - "id": "9", - "name": "apt", - "version": "1.6.11", - "kind": "source", - "source": null - } - } - }, - "distributions": { - "1": { - "id": "1", - "did": "ubuntu", - "name": "Ubuntu", - "version": "18.04.3 LTS (Bionic Beaver)", - "version_code_name": "bionic", - "version_id": "18.04", - "arch": "", - "cpe": "", - "pretty_name": "Ubuntu 18.04.3 LTS" - } - }, - "environments": { - "10": [ - { - "package_db": "var/lib/dpkg/status", - "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", - "distribution_id": "1" - } - ] - }, + "manifest_hash": null, + "state": "string", + "err": "string", "success": true, - "err": "" + "packages": {}, + "distributions": {}, + "repository": {}, + "environments": { + "property1": [], + "property2": [] + } } ``` -

Responses

+

Responses

|Status|Meaning|Description|Schema| |---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|IndexReport retrieved|[IndexReport](#schemaindexreport)| -|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found|[Error](#schemaerror)| -|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| -|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|IndexReport retrieved|[index_report](#schemaindex_report)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[error](#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found|[error](#schemaerror)| +|415|[Unsupported Media Type](https://tools.ietf.org/html/rfc7231#section-6.5.13)|Unsupported Media Type|[error](#schemaerror)| +|default|Default|Internal Server Error|[error](#schemaerror)| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|200|Clair-Error|string||This is a trailer containing any errors encountered while writing the response.|