From e48cb55df66df829af53deb3e2ac1c27d9e542c6 Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Mon, 22 Jul 2024 19:40:25 -0400 Subject: [PATCH 1/2] Implement getSbom using a generated api client. * Initial step in implementing #119 The generated code lived in to the `client/src/app/client`. The goal should be to replace all the api calls with client calls. Added `npm run generate` and `npm run prettier` sub commands. Signed-off-by: Hiram Chirino --- client/.gitignore | 2 + client/config/openapi-ts.config.ts | 7 + client/openapi/trustd.yaml | 2359 +++++++++++++++++ client/package.json | 3 + client/src/app/api/rest.ts | 4 +- client/src/app/axios-config/apiInit.ts | 8 + client/src/app/client/index.ts | 4 + client/src/app/client/schemas.gen.ts | 1660 ++++++++++++ client/src/app/client/services.gen.ts | 555 ++++ client/src/app/client/types.gen.ts | 1892 +++++++++++++ .../src/app/pages/sbom-details/overview.tsx | 3 +- .../sbom-details/vulnerabilities-by-sbom.tsx | 19 +- client/src/app/queries/sboms.ts | 7 +- client/src/app/utils/utils.ts | 2 +- package-lock.json | 539 ++++ package.json | 4 +- 16 files changed, 7050 insertions(+), 18 deletions(-) create mode 100644 client/.gitignore create mode 100644 client/config/openapi-ts.config.ts create mode 100644 client/openapi/trustd.yaml create mode 100644 client/src/app/client/index.ts create mode 100644 client/src/app/client/schemas.gen.ts create mode 100644 client/src/app/client/services.gen.ts create mode 100644 client/src/app/client/types.gen.ts diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 00000000..b75ff4cb --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,2 @@ +# generating openapi client can generate error logs. +*.log diff --git a/client/config/openapi-ts.config.ts b/client/config/openapi-ts.config.ts new file mode 100644 index 00000000..46e9f118 --- /dev/null +++ b/client/config/openapi-ts.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "@hey-api/openapi-ts"; + +export default defineConfig({ + client: "@hey-api/client-axios", + input: "./openapi/trustd.yaml", + output: "src/app/client", +}); diff --git a/client/openapi/trustd.yaml b/client/openapi/trustd.yaml new file mode 100644 index 00000000..8bddaa86 --- /dev/null +++ b/client/openapi/trustd.yaml @@ -0,0 +1,2359 @@ +openapi: 3.0.3 +info: + title: Trustify + description: Software Supply-Chain Security API + license: + name: '' + version: 0.1.0-alpha.11 +paths: + /api/v1/advisory: + get: + tags: + - advisory + operationId: listAdvisories + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Matching vulnerabilities + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedAdvisorySummary' + post: + tags: + - advisory + summary: Upload a new advisory + operationId: uploadAdvisory + parameters: + - name: issuer + in: query + description: Optional issuer if it cannot be determined from advisory contents. + required: false + schema: + type: string + nullable: true + - name: labels + in: query + description: |- + Optional labels. + + Only use keys with a prefix of `labels.` + required: true + schema: + $ref: '#/components/schemas/Labels' + requestBody: + content: + application/octet-stream: + schema: + type: string + format: binary + required: true + responses: + '201': + description: Upload a file + '400': + description: The file could not be parsed as an advisory + /api/v1/advisory/{id}/label: + put: + tags: + - advisory + summary: Replace the labels of an advisory + operationId: updateAdvisoryLabels + parameters: + - name: id + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + $ref: '#/components/schemas/Id' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Labels' + required: true + responses: + '204': + description: Replaced the labels of the advisory + '404': + description: The advisory could not be found + patch: + tags: + - advisory + summary: Modify existing labels of an advisory + operationId: patchAdvisoryLabels + parameters: + - name: id + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + $ref: '#/components/schemas/Id' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Labels' + required: true + responses: + '204': + description: Modified the labels of the advisory + '404': + description: The advisory could not be found + /api/v1/advisory/{key}: + get: + tags: + - advisory + operationId: getAdvisory + parameters: + - name: key + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + type: string + responses: + '200': + description: Matching advisory + content: + application/json: + schema: + $ref: '#/components/schemas/AdvisoryDetails' + '404': + description: Matching advisory not found + /api/v1/advisory/{key}/download: + get: + tags: + - advisory + operationId: downloadAdvisory + parameters: + - name: key + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' + required: true + schema: + type: string + responses: + '200': + description: Download a an advisory + content: + application/octet-stream: + schema: + type: string + format: binary + '404': + description: The document could not be found + /api/v1/importer: + get: + tags: + - importer + summary: List importer configurations + operationId: listImporters + responses: + '200': + description: List importer configurations + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Importer' + /api/v1/importer/{name}: + get: + tags: + - importer + summary: Get an importer configuration + operationId: getImporter + parameters: + - name: name + in: path + description: The name of the importer + required: true + schema: + type: string + responses: + '200': + description: Retrieved importer configuration + headers: + etag: + schema: + type: string + description: Revision ID + content: + application/json: + schema: + $ref: '#/components/schemas/RevisionedImporter' + '404': + description: An importer with that name could not be found + post: + tags: + - importer + summary: Create a new importer configuration + operationId: createImporter + parameters: + - name: name + in: path + description: The name of the importer + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImporterConfiguration' + required: true + responses: + '201': + description: Created a new importer configuration + '409': + description: An importer with that name already exists + put: + tags: + - importer + summary: Update an existing importer configuration + operationId: updateImporter + parameters: + - name: name + in: path + description: The name of the importer + required: true + schema: + type: string + - name: if-match + in: header + description: The revision to update + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImporterConfiguration' + required: true + responses: + '201': + description: Created a new importer configuration + '409': + description: An importer with that name does not exist + '412': + description: The provided if-match header did not match the stored revision + delete: + tags: + - importer + summary: Delete an importer configuration + operationId: deleteImporter + parameters: + - name: name + in: path + description: The name of the importer + required: true + schema: + type: string + - name: if-match + in: header + description: The revision to delete + required: true + schema: + type: string + responses: + '201': + description: Delete the importer configuration + /api/v1/importer/{name}/enabled: + put: + tags: + - importer + summary: Update an existing importer configuration + operationId: enableImporter + parameters: + - name: name + in: path + description: The name of the importer + required: true + schema: + type: string + - name: if-match + in: header + description: The revision to update + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + type: boolean + required: true + responses: + '201': + description: Updated the enable state + '404': + description: An importer with that name does not exist + '412': + description: The provided if-match header did not match the stored revision + /api/v1/importer/{name}/force: + post: + tags: + - importer + summary: Force an importer to run as soon as possible + operationId: forceRunImporter + parameters: + - name: name + in: path + description: The name of the importer + required: true + schema: + type: string + - name: if-match + in: header + description: The revision to update + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + type: boolean + required: true + responses: + '201': + description: Updated the state + '404': + description: An importer with that name does not exist + '412': + description: The provided if-match header did not match the stored revision + /api/v1/importer/{name}/report: + get: + tags: + - importer + summary: Get reports for an importer + operationId: getImporterReports + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + '200': + description: Retrieved importer reports + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedImporterReport' + /api/v1/organization: + get: + tags: + - organization + operationId: listOrganizations + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Matching organizations + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedOrganizationSummary' + /api/v1/organization/{id}: + get: + tags: + - organization + operationId: getOrganization + parameters: + - name: id + in: path + description: Opaque ID of the organization + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Matching organization + content: + application/json: + schema: + $ref: '#/components/schemas/OrganizationDetails' + '404': + description: Matching organization not found + /api/v1/product: + get: + tags: + - product + operationId: listProducts + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Matching products + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedProductSummary' + /api/v1/product/{id}: + get: + tags: + - product + operationId: getProducts + parameters: + - name: id + in: path + description: Opaque ID of the product + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Matching product + content: + application/json: + schema: + $ref: '#/components/schemas/ProductDetails' + '404': + description: Matching product not found + /api/v1/purl: + get: + tags: + - purl + operationId: listPurl + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: All relevant matching qualified PURLs + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedPurlSummary' + /api/v1/purl/base: + get: + tags: + - purl + operationId: listBasePurls + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: All relevant matching versionless base PURL + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedBasePurlSummary' + /api/v1/purl/base/{key}: + get: + tags: + - purl + operationId: getBasePurl + parameters: + - name: key + in: path + description: opaque identifier for a base PURL, or a URL-encoded pURL itself + required: true + schema: + type: string + responses: + '200': + description: Details for the versionless base PURL + content: + application/json: + schema: + $ref: '#/components/schemas/BasePurlDetails' + /api/v1/purl/type: + get: + tags: + - purl + operationId: listPurlTypes + responses: + '200': + description: List of all known PURL types + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TypeSummary' + /api/v1/purl/type/{type}: + get: + tags: + - purl + operationId: getPurlType + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: type + in: path + description: PURL identifier of a type + required: true + schema: + type: string + responses: + '200': + description: Information regarding PURLs within an type + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedBasePurlSummary' + /api/v1/purl/type/{type}/{namespace_and_name}: + get: + tags: + - purl + operationId: getBasePurlOfType + parameters: + - name: type + in: path + description: PURL identifier of a type + required: true + schema: + type: string + - name: namespace_and_name + in: path + description: name of the package optionally preceded by its namespace + required: true + schema: + type: string + responses: + '200': + description: Matching vulnerabilities + content: + application/json: + schema: + $ref: '#/components/schemas/BasePurlDetails' + /api/v1/purl/type/{type}/{namespace_and_name}@{version}: + get: + tags: + - purl + operationId: getVersionedPurlOfType + parameters: + - name: type + in: path + description: PURL identifier of a type + required: true + schema: + type: string + - name: namespace_and_name + in: path + description: name of the package optionally preceded by its namespace + required: true + schema: + type: string + - name: version + in: path + description: version of the package + required: true + schema: + type: string + responses: + '200': + description: Matching vulnerabilities + content: + application/json: + schema: + $ref: '#/components/schemas/VersionedPurlDetails' + /api/v1/purl/version/{key}: + get: + tags: + - purl + operationId: getVersionedPurl + parameters: + - name: key + in: path + description: opaque ID identifier for a package version, or URL-ecnoded pURL itself + required: true + schema: + type: string + responses: + '200': + description: Details for the version of a PURL + content: + application/json: + schema: + $ref: '#/components/schemas/VersionedPurlDetails' + /api/v1/purl/{key}: + get: + tags: + - purl + operationId: getPurl + parameters: + - name: key + in: path + description: opaque identifier for a fully-qualified PURL, or URL-encoded pURL itself + required: true + schema: + type: string + responses: + '200': + description: Details for the qualified PURL + content: + application/json: + schema: + $ref: '#/components/schemas/PurlDetails' + /api/v1/sbom: + get: + tags: + - sbom + operationId: listSboms + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Matching SBOMs + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedSbomSummary' + post: + tags: + - sbom + summary: Upload a new SBOM + operationId: uploadSbom + parameters: + - name: labels + in: query + description: |- + Optional labels. + + Only use keys with a prefix of `labels.` + required: true + schema: + $ref: '#/components/schemas/Labels' + - name: location + in: query + description: Source the document came from + required: true + schema: + type: string + requestBody: + content: + application/octet-stream: + schema: + type: string + format: binary + required: true + responses: + '201': + description: Upload an SBOM + '400': + description: The file could not be parsed as an advisory + /api/v1/sbom/by-package: + get: + tags: + - sbom + summary: Find all SBOMs containing the provided package. + description: |- + The package can be provided either via a PURL or using the ID of a package as returned by + other APIs, but not both. + operationId: listRelatedSboms + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: purl + in: query + description: Find by PURL + required: false + schema: + allOf: + - $ref: '#/components/schemas/Purl' + nullable: true + - name: id + in: query + description: Find by a ID of a package + required: false + schema: + type: string + format: uuid + nullable: true + responses: + '200': + description: Matching SBOMs + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedSbomSummary' + /api/v1/sbom/{id}: + get: + tags: + - sbom + operationId: getSbom + parameters: + - name: id + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + type: string + responses: + '200': + description: Matching SBOM + content: + application/json: + schema: + $ref: '#/components/schemas/SbomDetails' + '404': + description: Matching SBOM not found + delete: + tags: + - sbom + operationId: deleteSbom + parameters: + - name: id + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + type: string + responses: + '200': + description: Matching SBOM + content: + application/json: + schema: + $ref: '#/components/schemas/SbomDetails' + '404': + description: Matching SBOM not found + /api/v1/sbom/{id}/label: + put: + tags: + - sbom + summary: Replace the labels of an SBOM + operationId: updateSbomLabels + parameters: + - name: id + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + $ref: '#/components/schemas/Id' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Labels' + required: true + responses: + '204': + description: Replaced the labels of the SBOM + '404': + description: The SBOM could not be found + patch: + tags: + - sbom + summary: Modify existing labels of an SBOM + operationId: patchSbomLabels + parameters: + - name: id + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + required: true + schema: + $ref: '#/components/schemas/Id' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Labels' + required: true + responses: + '204': + description: Modified the labels of the SBOM + '404': + description: The SBOM could not be found + /api/v1/sbom/{id}/packages: + get: + tags: + - sbom + summary: Search for packages of an SBOM + operationId: listPackages + parameters: + - name: id + in: path + description: ID of the SBOM to get packages for + required: true + schema: + type: string + format: uuid + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Packages + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedSbomPackage' + /api/v1/sbom/{id}/related: + get: + tags: + - sbom + summary: Search for related packages in an SBOM + operationId: listRelatedPackages + parameters: + - name: id + in: path + description: ID of SBOM to search packages in + required: true + schema: + type: string + format: uuid + - name: reference + in: query + description: The Package to use as reference + required: false + schema: + type: string + nullable: true + - name: which + in: query + description: Which side the reference should be on + required: false + schema: + $ref: '#/components/schemas/Which' + - name: relationship + in: query + description: Optional relationship filter + required: false + schema: + allOf: + - $ref: '#/components/schemas/Relationship' + nullable: true + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Packages + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedSbomPackageRelation' + /api/v1/sbom/{key}/download: + get: + tags: + - sbom + operationId: downloadSbom + parameters: + - name: key + in: path + description: Digest/hash of the document, prefixed by hash type, such as 'sha256:' + required: true + schema: + type: string + responses: + '200': + description: Download a an SBOM + content: + application/octet-stream: + schema: + type: string + format: binary + '404': + description: The document could not be found + /api/v1/vulnerability: + get: + tags: + - vulnerability + operationId: listVulnerabilities + parameters: + - name: q + in: query + required: false + schema: + type: string + - name: sort + in: query + required: false + schema: + type: string + - name: offset + in: query + description: |- + The first item to return, skipping all that come before it. + + NOTE: The order of items is defined by the API being called. + required: false + schema: + type: integer + format: int64 + minimum: 0 + - name: limit + in: query + description: |- + The maximum number of entries to return. + + Zero means: no limit + required: false + schema: + type: integer + format: int64 + minimum: 0 + responses: + '200': + description: Matching vulnerabilities + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedVulnerabilitySummary' + /api/v1/vulnerability/{id}: + get: + tags: + - vulnerability + operationId: getVulnerability + parameters: + - name: id + in: path + description: ID of the vulnerability + required: true + schema: + type: string + responses: + '200': + description: Specified vulnerability + content: + application/json: + schema: + $ref: '#/components/schemas/VulnerabilityDetails' + '404': + description: Specified vulnerability not found +components: + schemas: + AdvisoryDetails: + allOf: + - $ref: '#/components/schemas/AdvisoryHead' + - type: object + required: + - vulnerabilities + properties: + average_score: + type: number + format: double + description: Average (arithmetic mean) score of the advisory aggregated from *all* related vulnerability assertions. + nullable: true + average_severity: + type: string + description: Average (arithmetic mean) severity of the advisory aggregated from *all* related vulnerability assertions. + nullable: true + vulnerabilities: + type: array + items: + $ref: '#/components/schemas/AdvisoryVulnerabilitySummary' + description: Vulnerabilities addressed within this advisory. + AdvisoryHead: + type: object + required: + - uuid + - identifier + properties: + hashes: + type: array + items: + $ref: '#/components/schemas/Id' + description: Hashes of the underlying original document as ingested. + identifier: + type: string + description: The identifier of the advisory, as assigned by the issuing organization. + issuer: + allOf: + - $ref: '#/components/schemas/OrganizationSummary' + nullable: true + labels: + $ref: '#/components/schemas/Labels' + modified: + type: string + format: date-time + description: The date (in RFC3339 format) of when the advisory was last modified, if any. + nullable: true + published: + type: string + format: date-time + description: The date (in RFC3339 format) of when the advisory was published, if any. + nullable: true + title: + type: string + description: The title of the advisory as assigned by the issuing organization. + nullable: true + uuid: + type: string + format: uuid + description: The opaque UUID of the advisory. + withdrawn: + type: string + format: date-time + description: The date (in RFC3339 format) of when the advisory was withdrawn, if any. + nullable: true + AdvisorySummary: + allOf: + - $ref: '#/components/schemas/AdvisoryHead' + - type: object + required: + - vulnerabilities + properties: + average_score: + type: number + format: double + description: Average (arithmetic mean) score of the advisory aggregated from *all* related vulnerability assertions. + nullable: true + average_severity: + type: string + description: Average (arithmetic mean) severity of the advisory aggregated from *all* related vulnerability assertions. + nullable: true + vulnerabilities: + type: array + items: + $ref: '#/components/schemas/AdvisoryVulnerabilityHead' + description: Vulnerabilities addressed within this advisory. + AdvisoryVulnerabilityAssertions: + type: object + required: + - assertions + properties: + assertions: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Assertion' + AdvisoryVulnerabilityHead: + allOf: + - $ref: '#/components/schemas/VulnerabilityHead' + - type: object + required: + - severity + - score + properties: + score: + type: number + format: double + description: |- + The average (arithmetic mean) score this advisory assigns to + the particular vulnerability. + severity: + type: string + description: |- + The English-language word description of the severity of the given + vulnerability, as asserted by the advisory, using the CVSS bucketing + ranges. + + Critical: 9.0–10.0 + High: 7.0–8.9 + Medium: 4.0–6.9 + Low: 0.1–3.9 + None: 0 + AdvisoryVulnerabilitySummary: + allOf: + - $ref: '#/components/schemas/AdvisoryVulnerabilityHead' + - type: object + required: + - cvss3_scores + properties: + cvss3_scores: + type: array + items: + type: string + description: |- + All CVSS3 scores from the advisory for the given vulnerability. + May include several, varying by minor version of the CVSS3 vector. + description: Summary of information from this advisory regarding a single specific vulnerability. + Assertion: + oneOf: + - type: object + required: + - affected + properties: + affected: + type: object + required: + - start_version + - end_version + properties: + end_version: + type: string + start_version: + type: string + - type: object + required: + - not_affected + properties: + not_affected: + type: object + required: + - version + properties: + version: + type: string + - type: object + required: + - fixed + properties: + fixed: + type: object + required: + - version + properties: + version: + type: string + BasePurlDetails: + allOf: + - $ref: '#/components/schemas/BasePurlHead' + - type: object + properties: + versions: + type: array + items: + $ref: '#/components/schemas/VersionedPurlSummary' + BasePurlHead: + type: object + required: + - uuid + - purl + properties: + purl: + $ref: '#/components/schemas/Purl' + uuid: + type: string + format: uuid + description: The ID of the base PURL + BasePurlSummary: + allOf: + - $ref: '#/components/schemas/BasePurlHead' + - type: object + CommonImporter: + type: object + required: + - period + properties: + description: + type: string + description: A description for users. + nullable: true + disabled: + type: boolean + description: A flag to disable the importer, without deleting it. + labels: + $ref: '#/components/schemas/Labels' + period: + type: string + description: The period the importer should be run. + CsafImporter: + allOf: + - $ref: '#/components/schemas/CommonImporter' + - type: object + required: + - source + properties: + onlyPatterns: + type: array + items: + type: string + source: + type: string + v3Signatures: + type: boolean + CveImporter: + allOf: + - $ref: '#/components/schemas/CommonImporter' + - type: object + properties: + source: + type: string + startYear: + type: integer + format: int32 + nullable: true + minimum: 0 + years: + type: array + items: + type: integer + format: int32 + minimum: 0 + uniqueItems: true + Id: + type: string + description: A hash/digest prefixed with its type. + example: sha256:dc60aeb735c16a71b6fc56e84ddb8193e3a6d1ef0b7e958d77e78fc039a5d04e + Importer: + allOf: + - $ref: '#/components/schemas/ImporterData' + - type: object + required: + - name + properties: + name: + type: string + ImporterConfiguration: + oneOf: + - type: object + required: + - sbom + properties: + sbom: + $ref: '#/components/schemas/SbomImporter' + - type: object + required: + - csaf + properties: + csaf: + $ref: '#/components/schemas/CsafImporter' + - type: object + required: + - osv + properties: + osv: + $ref: '#/components/schemas/OsvImporter' + - type: object + required: + - cve + properties: + cve: + $ref: '#/components/schemas/CveImporter' + ImporterData: + type: object + required: + - configuration + - state + - lastChange + properties: + configuration: + $ref: '#/components/schemas/ImporterConfiguration' + continuation: + description: The continuation token of the importer. + lastChange: + type: string + format: date-time + description: The last state change + lastError: + type: string + description: The error of the last run (empty if successful) + nullable: true + lastRun: + type: string + format: date-time + description: The last run (successful or not) + nullable: true + lastSuccess: + type: string + format: date-time + description: The last successful run + nullable: true + state: + $ref: '#/components/schemas/State' + ImporterReport: + type: object + required: + - id + - importer + - creation + - report + properties: + creation: + type: string + format: date-time + error: + type: string + nullable: true + id: + type: string + importer: + type: string + report: {} + Labels: + type: object + additionalProperties: + type: string + OrganizationDetails: + allOf: + - $ref: '#/components/schemas/OrganizationHead' + - type: object + required: + - advisories + properties: + advisories: + type: array + items: + $ref: '#/components/schemas/AdvisoryHead' + description: Advisories issued by the organization, if any. + OrganizationHead: + type: object + description: |- + An organization who may issue advisories, product SBOMs, or + otherwise be involved in supply-chain evidence. + required: + - id + - name + properties: + cpe_key: + type: string + description: The `CPE` key of the organization, if known. + nullable: true + id: + type: string + format: uuid + description: The opaque UUID of the organization. + name: + type: string + description: The name of the organization. + website: + type: string + description: The website of the organization, if known. + nullable: true + OrganizationSummary: + allOf: + - $ref: '#/components/schemas/OrganizationHead' + - type: object + OsvImporter: + allOf: + - $ref: '#/components/schemas/CommonImporter' + - type: object + required: + - source + properties: + path: + type: string + nullable: true + source: + type: string + PaginatedAdvisorySummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/AdvisorySummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedBasePurlSummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/BasePurlSummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedImporterReport: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/ImporterReport' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedOrganizationSummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/OrganizationSummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedProductSummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/ProductSummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedPurlSummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/PurlSummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedSbomPackage: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/SbomPackage' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedSbomPackageRelation: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/SbomPackageRelation' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedSbomSummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/SbomSummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + PaginatedVulnerabilitySummary: + type: object + description: Paginated returned items + required: + - items + - total + properties: + items: + type: array + items: + $ref: '#/components/schemas/VulnerabilitySummary' + description: Returned items + total: + type: integer + format: int64 + description: Total number of items found + minimum: 0 + ProductDetails: + allOf: + - $ref: '#/components/schemas/ProductHead' + - type: object + properties: + vendor: + allOf: + - $ref: '#/components/schemas/OrganizationSummary' + nullable: true + versions: + type: array + items: + $ref: '#/components/schemas/ProductVersionHead' + ProductHead: + type: object + required: + - id + - name + properties: + id: + type: string + format: uuid + name: + type: string + ProductSummary: + allOf: + - $ref: '#/components/schemas/ProductHead' + - type: object + properties: + vendor: + allOf: + - $ref: '#/components/schemas/OrganizationSummary' + nullable: true + versions: + type: array + items: + $ref: '#/components/schemas/ProductVersionHead' + ProductVersionHead: + type: object + required: + - id + - version + properties: + id: + type: string + format: uuid + sbom_id: + type: string + format: uuid + nullable: true + version: + type: string + Purl: + type: string + format: uri + PurlAdvisory: + allOf: + - $ref: '#/components/schemas/AdvisoryHead' + - type: object + required: + - status + properties: + status: + type: array + items: + $ref: '#/components/schemas/PurlStatus' + PurlDetails: + allOf: + - $ref: '#/components/schemas/PurlHead' + - type: object + required: + - version + - base + properties: + advisories: + type: array + items: + $ref: '#/components/schemas/PurlAdvisory' + base: + $ref: '#/components/schemas/BasePurlHead' + version: + $ref: '#/components/schemas/VersionedPurlHead' + PurlHead: + type: object + required: + - uuid + - purl + properties: + purl: + $ref: '#/components/schemas/Purl' + uuid: + type: string + format: uuid + description: The ID of the qualified PURL + PurlStatus: + type: object + required: + - vulnerability + - status + properties: + context: + allOf: + - $ref: '#/components/schemas/StatusContext' + nullable: true + status: + type: string + vulnerability: + $ref: '#/components/schemas/VulnerabilityHead' + PurlSummary: + allOf: + - $ref: '#/components/schemas/PurlHead' + - type: object + required: + - base + - version + - qualifiers + properties: + base: + $ref: '#/components/schemas/BasePurlHead' + qualifiers: + type: object + additionalProperties: + type: string + version: + $ref: '#/components/schemas/VersionedPurlHead' + Relationship: + type: string + enum: + - contained_by + - dependency_of + - dev_dependency_of + - optional_dependency_of + - provided_dependency_of + - test_dependency_of + - runtime_dependency_of + - example_of + - generated_from + - ancestor_of + - variant_of + - build_tool_of + - dev_tool_of + - described_by + RevisionedImporter: + type: object + description: |- + A struct wrapping an item with a revision. + + If the revision should not be part of the payload, but e.g. an HTTP header (like `ETag`), this + struct can help carrying both pieces. + required: + - value + - revision + properties: + revision: + type: string + description: |- + The revision. + + An opaque string that should have no meaning to the user, only to the backend. + value: + $ref: '#/components/schemas/Importer' + SbomAdvisory: + allOf: + - $ref: '#/components/schemas/AdvisoryHead' + - type: object + properties: + status: + type: array + items: + $ref: '#/components/schemas/SbomStatus' + SbomDetails: + allOf: + - $ref: '#/components/schemas/SbomHead' + - type: object + required: + - advisories + properties: + advisories: + type: array + items: + $ref: '#/components/schemas/SbomAdvisory' + authors: + type: array + items: + type: string + described_by: + type: array + items: + $ref: '#/components/schemas/SbomPackage' + published: + type: string + format: date-time + nullable: true + SbomHead: + type: object + required: + - id + - document_id + - name + properties: + document_id: + type: string + hashes: + type: array + items: + $ref: '#/components/schemas/Id' + id: + type: string + format: uuid + labels: + $ref: '#/components/schemas/Labels' + name: + type: string + SbomImporter: + allOf: + - $ref: '#/components/schemas/CommonImporter' + - type: object + required: + - source + properties: + keys: + type: array + items: + type: string + format: uri + onlyPatterns: + type: array + items: + type: string + source: + type: string + v3Signatures: + type: boolean + SbomPackage: + type: object + required: + - id + - name + properties: + cpe: + type: array + items: + type: string + id: + type: string + name: + type: string + purl: + type: array + items: + $ref: '#/components/schemas/PurlSummary' + version: + type: string + nullable: true + SbomPackageRelation: + type: object + required: + - relationship + - package + properties: + package: + $ref: '#/components/schemas/SbomPackage' + relationship: + $ref: '#/components/schemas/Relationship' + SbomStatus: + type: object + required: + - vulnerability_id + - status + properties: + context: + allOf: + - $ref: '#/components/schemas/StatusContext' + nullable: true + packages: + type: array + items: + $ref: '#/components/schemas/SbomPackage' + status: + type: string + vulnerability_id: + type: string + SbomSummary: + allOf: + - $ref: '#/components/schemas/SbomHead' + - type: object + properties: + authors: + type: array + items: + type: string + described_by: + type: array + items: + $ref: '#/components/schemas/SbomPackage' + published: + type: string + format: date-time + nullable: true + State: + type: string + enum: + - waiting + - running + StatusContext: + oneOf: + - type: object + required: + - purl + properties: + purl: + $ref: '#/components/schemas/Purl' + - type: object + required: + - cpe + properties: + cpe: + type: string + TypeCounts: + type: object + required: + - base + - version + - package + properties: + base: + type: integer + format: int64 + package: + type: integer + format: int64 + version: + type: integer + format: int64 + TypeHead: + type: object + required: + - name + properties: + name: + type: string + TypeSummary: + allOf: + - $ref: '#/components/schemas/TypeHead' + - type: object + required: + - counts + properties: + counts: + $ref: '#/components/schemas/TypeCounts' + Uuid: + type: string + description: a UUID + VersionedPurlAdvisory: + allOf: + - $ref: '#/components/schemas/AdvisoryHead' + - type: object + required: + - status + properties: + status: + type: array + items: + $ref: '#/components/schemas/VersionedPurlStatus' + VersionedPurlDetails: + allOf: + - $ref: '#/components/schemas/VersionedPurlHead' + - type: object + required: + - base + properties: + advisories: + type: array + items: + $ref: '#/components/schemas/VersionedPurlAdvisory' + base: + $ref: '#/components/schemas/BasePurlHead' + purls: + type: array + items: + $ref: '#/components/schemas/PurlHead' + VersionedPurlHead: + type: object + required: + - uuid + - purl + - version + properties: + purl: + $ref: '#/components/schemas/Purl' + uuid: + type: string + format: uuid + description: The ID of the versioned PURL + version: + type: string + description: The version from the PURL + VersionedPurlStatus: + type: object + required: + - vulnerability + - status + properties: + status: + type: string + vulnerability: + $ref: '#/components/schemas/VulnerabilityHead' + VersionedPurlSummary: + allOf: + - $ref: '#/components/schemas/VersionedPurlHead' + - type: object + required: + - base + properties: + base: + $ref: '#/components/schemas/BasePurlHead' + purls: + type: array + items: + $ref: '#/components/schemas/PurlHead' + VulnerabilityAdvisoryHead: + allOf: + - $ref: '#/components/schemas/AdvisoryHead' + - type: object + properties: + score: + type: number + format: double + nullable: true + severity: + type: string + nullable: true + VulnerabilityAdvisoryStatus: + type: object + required: + - base_purl + - version + properties: + base_purl: + $ref: '#/components/schemas/BasePurlHead' + context: + allOf: + - $ref: '#/components/schemas/StatusContext' + nullable: true + version: + type: string + VulnerabilityAdvisorySummary: + allOf: + - $ref: '#/components/schemas/VulnerabilityAdvisoryHead' + - type: object + required: + - cvss3_scores + - purls + - sboms + properties: + cvss3_scores: + type: array + items: + type: string + description: CVSS3 scores from this advisory regarding the vulnerability. + purls: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/VulnerabilityAdvisoryStatus' + sboms: + type: array + items: + $ref: '#/components/schemas/SbomStatus' + description: SBOMs claimed by this advisory to be addressed by this vulnerability. + VulnerabilityDetails: + allOf: + - $ref: '#/components/schemas/VulnerabilityHead' + - type: object + required: + - advisories + properties: + advisories: + type: array + items: + $ref: '#/components/schemas/VulnerabilityAdvisorySummary' + description: Advisories addressing this vulnerability, if any. + average_score: + type: number + format: double + description: Average (arithmetic mean) score of the vulnerability aggregated from *all* related advisories. + nullable: true + average_severity: + type: string + description: Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories. + nullable: true + VulnerabilityHead: + type: object + required: + - identifier + properties: + cwe: + type: string + description: Associated CWE, if any. + nullable: true + description: + type: string + description: The description of the vulnerability, if known. + nullable: true + discovered: + type: string + format: date-time + description: The date (in RFC3339 format) of when the vulnerability was discovered, if any. + nullable: true + identifier: + type: string + description: |- + The globally-unique identifier for the vulnerability. + Traditionally (but not required) refers to the assigned + CVE identifier. + modified: + type: string + format: date-time + description: The date (in RFC3339 format) of when the vulnerability was last modified, if any. + nullable: true + normative: + type: boolean + published: + type: string + format: date-time + description: The date (in RFC3339 format) of when the vulnerability was published, if any. + nullable: true + released: + type: string + format: date-time + description: The date (in RFC3339 format) of when software containing the vulnerability first released, if known. + nullable: true + title: + type: string + description: The title of the vulnerability, if known. + nullable: true + withdrawn: + type: string + format: date-time + description: The date (in RFC3339 format) of when the vulnerability was last withdrawn, if any. + nullable: true + VulnerabilitySummary: + allOf: + - $ref: '#/components/schemas/VulnerabilityHead' + - type: object + properties: + advisories: + type: array + items: + $ref: '#/components/schemas/VulnerabilityAdvisoryHead' + average_score: + type: number + format: double + description: Average (arithmetic mean) score of the vulnerability aggregated from *all* related advisories. + nullable: true + average_severity: + type: string + description: Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories. + nullable: true + Which: + type: string + enum: + - left + - right +tags: [] diff --git a/client/package.json b/client/package.json index 1a10e1f8..52cf88ce 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ "build:dev": "NODE_ENV=development webpack --config ./config/webpack.dev.ts", "start:dev": "NODE_ENV=development webpack serve --config ./config/webpack.dev.ts", "test": "NODE_ENV=test jest --rootDir=. --config=./config/jest.config.ts", + "generate": "openapi-ts -f ./config/openapi-ts.config.ts && npx prettier --write './**/*.{ts,js,json}'", "lint": "eslint .", "tsc": "tsc -p ./tsconfig.json" }, @@ -19,6 +20,7 @@ "*.{css,json,md,yaml,yml}": "prettier --write" }, "dependencies": { + "@hey-api/client-axios": "^0.1.1", "@hookform/resolvers": "^2.9.11", "@patternfly/patternfly": "^5.2.1", "@patternfly/react-charts": "^7.2.1", @@ -53,6 +55,7 @@ "yup": "^0.32.11" }, "devDependencies": { + "@hey-api/openapi-ts": "^0.50.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", diff --git a/client/src/app/api/rest.ts b/client/src/app/api/rest.ts index 2bc9e12a..c24dae42 100644 --- a/client/src/app/api/rest.ts +++ b/client/src/app/api/rest.ts @@ -76,7 +76,9 @@ export const getAdvisories = (params: HubRequestParams = {}) => getHubPaginatedResult(ADVISORIES, params); export const getAdvisoryById = (id: number | string) => - axios.get(`${ADVISORIES}/${id}`).then((response) => response.data); + axios + .get(`${ADVISORIES}/${id}`) + .then((response) => response.data); export const getAdvisorySourceById = (id: number | string) => axios diff --git a/client/src/app/axios-config/apiInit.ts b/client/src/app/axios-config/apiInit.ts index c5b6ace7..083b40cb 100644 --- a/client/src/app/axios-config/apiInit.ts +++ b/client/src/app/axios-config/apiInit.ts @@ -3,6 +3,14 @@ import { User, UserManager } from "oidc-client-ts"; import { OIDC_CLIENT_ID, OIDC_SERVER_URL, oidcClientSettings } from "@app/oidc"; +import { createClient } from "@hey-api/client-axios"; + +createClient({ + // set default base url for requests + baseURL: "/", + axios: axios, +}); + function getUser() { const oidcStorage = sessionStorage.getItem( `oidc.user:${OIDC_SERVER_URL}:${OIDC_CLIENT_ID}` diff --git a/client/src/app/client/index.ts b/client/src/app/client/index.ts new file mode 100644 index 00000000..1cb041de --- /dev/null +++ b/client/src/app/client/index.ts @@ -0,0 +1,4 @@ +// This file is auto-generated by @hey-api/openapi-ts +export * from "./schemas.gen"; +export * from "./services.gen"; +export * from "./types.gen"; diff --git a/client/src/app/client/schemas.gen.ts b/client/src/app/client/schemas.gen.ts new file mode 100644 index 00000000..657036ba --- /dev/null +++ b/client/src/app/client/schemas.gen.ts @@ -0,0 +1,1660 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export const $AdvisoryDetails = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryHead", + }, + { + type: "object", + required: ["vulnerabilities"], + properties: { + average_score: { + type: "number", + format: "double", + description: + "Average (arithmetic mean) score of the advisory aggregated from *all* related vulnerability assertions.", + nullable: true, + }, + average_severity: { + type: "string", + description: + "Average (arithmetic mean) severity of the advisory aggregated from *all* related vulnerability assertions.", + nullable: true, + }, + vulnerabilities: { + type: "array", + items: { + $ref: "#/components/schemas/AdvisoryVulnerabilitySummary", + }, + description: "Vulnerabilities addressed within this advisory.", + }, + }, + }, + ], +} as const; + +export const $AdvisoryHead = { + type: "object", + required: ["uuid", "identifier"], + properties: { + hashes: { + type: "array", + items: { + $ref: "#/components/schemas/Id", + }, + description: "Hashes of the underlying original document as ingested.", + }, + identifier: { + type: "string", + description: + "The identifier of the advisory, as assigned by the issuing organization.", + }, + issuer: { + allOf: [ + { + $ref: "#/components/schemas/OrganizationSummary", + }, + ], + nullable: true, + }, + labels: { + $ref: "#/components/schemas/Labels", + }, + modified: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the advisory was last modified, if any.", + nullable: true, + }, + published: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the advisory was published, if any.", + nullable: true, + }, + title: { + type: "string", + description: + "The title of the advisory as assigned by the issuing organization.", + nullable: true, + }, + uuid: { + type: "string", + format: "uuid", + description: "The opaque UUID of the advisory.", + }, + withdrawn: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the advisory was withdrawn, if any.", + nullable: true, + }, + }, +} as const; + +export const $AdvisorySummary = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryHead", + }, + { + type: "object", + required: ["vulnerabilities"], + properties: { + average_score: { + type: "number", + format: "double", + description: + "Average (arithmetic mean) score of the advisory aggregated from *all* related vulnerability assertions.", + nullable: true, + }, + average_severity: { + type: "string", + description: + "Average (arithmetic mean) severity of the advisory aggregated from *all* related vulnerability assertions.", + nullable: true, + }, + vulnerabilities: { + type: "array", + items: { + $ref: "#/components/schemas/AdvisoryVulnerabilityHead", + }, + description: "Vulnerabilities addressed within this advisory.", + }, + }, + }, + ], +} as const; + +export const $AdvisoryVulnerabilityAssertions = { + type: "object", + required: ["assertions"], + properties: { + assertions: { + type: "object", + additionalProperties: { + type: "array", + items: { + $ref: "#/components/schemas/Assertion", + }, + }, + }, + }, +} as const; + +export const $AdvisoryVulnerabilityHead = { + allOf: [ + { + $ref: "#/components/schemas/VulnerabilityHead", + }, + { + type: "object", + required: ["severity", "score"], + properties: { + score: { + type: "number", + format: "double", + description: `The average (arithmetic mean) score this advisory assigns to +the particular vulnerability.`, + }, + severity: { + type: "string", + description: `The English-language word description of the severity of the given +vulnerability, as asserted by the advisory, using the CVSS bucketing +ranges. + +Critical: 9.0–10.0 +High: 7.0–8.9 +Medium: 4.0–6.9 +Low: 0.1–3.9 +None: 0`, + }, + }, + }, + ], +} as const; + +export const $AdvisoryVulnerabilitySummary = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryVulnerabilityHead", + }, + { + type: "object", + required: ["cvss3_scores"], + properties: { + cvss3_scores: { + type: "array", + items: { + type: "string", + }, + description: `All CVSS3 scores from the advisory for the given vulnerability. +May include several, varying by minor version of the CVSS3 vector.`, + }, + }, + }, + ], + description: + "Summary of information from this advisory regarding a single specific vulnerability.", +} as const; + +export const $Assertion = { + oneOf: [ + { + type: "object", + required: ["affected"], + properties: { + affected: { + type: "object", + required: ["start_version", "end_version"], + properties: { + end_version: { + type: "string", + }, + start_version: { + type: "string", + }, + }, + }, + }, + }, + { + type: "object", + required: ["not_affected"], + properties: { + not_affected: { + type: "object", + required: ["version"], + properties: { + version: { + type: "string", + }, + }, + }, + }, + }, + { + type: "object", + required: ["fixed"], + properties: { + fixed: { + type: "object", + required: ["version"], + properties: { + version: { + type: "string", + }, + }, + }, + }, + }, + ], +} as const; + +export const $BasePurlDetails = { + allOf: [ + { + $ref: "#/components/schemas/BasePurlHead", + }, + { + type: "object", + properties: { + versions: { + type: "array", + items: { + $ref: "#/components/schemas/VersionedPurlSummary", + }, + }, + }, + }, + ], +} as const; + +export const $BasePurlHead = { + type: "object", + required: ["uuid", "purl"], + properties: { + purl: { + $ref: "#/components/schemas/Purl", + }, + uuid: { + type: "string", + format: "uuid", + description: "The ID of the base PURL", + }, + }, +} as const; + +export const $BasePurlSummary = { + allOf: [ + { + $ref: "#/components/schemas/BasePurlHead", + }, + { + type: "object", + }, + ], +} as const; + +export const $CommonImporter = { + type: "object", + required: ["period"], + properties: { + description: { + type: "string", + description: "A description for users.", + nullable: true, + }, + disabled: { + type: "boolean", + description: "A flag to disable the importer, without deleting it.", + }, + labels: { + $ref: "#/components/schemas/Labels", + }, + period: { + type: "string", + description: "The period the importer should be run.", + }, + }, +} as const; + +export const $CsafImporter = { + allOf: [ + { + $ref: "#/components/schemas/CommonImporter", + }, + { + type: "object", + required: ["source"], + properties: { + onlyPatterns: { + type: "array", + items: { + type: "string", + }, + }, + source: { + type: "string", + }, + v3Signatures: { + type: "boolean", + }, + }, + }, + ], +} as const; + +export const $CveImporter = { + allOf: [ + { + $ref: "#/components/schemas/CommonImporter", + }, + { + type: "object", + properties: { + source: { + type: "string", + }, + startYear: { + type: "integer", + format: "int32", + nullable: true, + minimum: 0, + }, + years: { + type: "array", + items: { + type: "integer", + format: "int32", + minimum: 0, + }, + uniqueItems: true, + }, + }, + }, + ], +} as const; + +export const $Id = { + type: "string", + description: "A hash/digest prefixed with its type.", + example: + "sha256:dc60aeb735c16a71b6fc56e84ddb8193e3a6d1ef0b7e958d77e78fc039a5d04e", +} as const; + +export const $Importer = { + allOf: [ + { + $ref: "#/components/schemas/ImporterData", + }, + { + type: "object", + required: ["name"], + properties: { + name: { + type: "string", + }, + }, + }, + ], +} as const; + +export const $ImporterConfiguration = { + oneOf: [ + { + type: "object", + required: ["sbom"], + properties: { + sbom: { + $ref: "#/components/schemas/SbomImporter", + }, + }, + }, + { + type: "object", + required: ["csaf"], + properties: { + csaf: { + $ref: "#/components/schemas/CsafImporter", + }, + }, + }, + { + type: "object", + required: ["osv"], + properties: { + osv: { + $ref: "#/components/schemas/OsvImporter", + }, + }, + }, + { + type: "object", + required: ["cve"], + properties: { + cve: { + $ref: "#/components/schemas/CveImporter", + }, + }, + }, + ], +} as const; + +export const $ImporterData = { + type: "object", + required: ["configuration", "state", "lastChange"], + properties: { + configuration: { + $ref: "#/components/schemas/ImporterConfiguration", + }, + continuation: { + description: "The continuation token of the importer.", + }, + lastChange: { + type: "string", + format: "date-time", + description: "The last state change", + }, + lastError: { + type: "string", + description: "The error of the last run (empty if successful)", + nullable: true, + }, + lastRun: { + type: "string", + format: "date-time", + description: "The last run (successful or not)", + nullable: true, + }, + lastSuccess: { + type: "string", + format: "date-time", + description: "The last successful run", + nullable: true, + }, + state: { + $ref: "#/components/schemas/State", + }, + }, +} as const; + +export const $ImporterReport = { + type: "object", + required: ["id", "importer", "creation", "report"], + properties: { + creation: { + type: "string", + format: "date-time", + }, + error: { + type: "string", + nullable: true, + }, + id: { + type: "string", + }, + importer: { + type: "string", + }, + report: {}, + }, +} as const; + +export const $Labels = { + type: "object", + additionalProperties: { + type: "string", + }, +} as const; + +export const $OrganizationDetails = { + allOf: [ + { + $ref: "#/components/schemas/OrganizationHead", + }, + { + type: "object", + required: ["advisories"], + properties: { + advisories: { + type: "array", + items: { + $ref: "#/components/schemas/AdvisoryHead", + }, + description: "Advisories issued by the organization, if any.", + }, + }, + }, + ], +} as const; + +export const $OrganizationHead = { + type: "object", + description: `An organization who may issue advisories, product SBOMs, or +otherwise be involved in supply-chain evidence.`, + required: ["id", "name"], + properties: { + cpe_key: { + type: "string", + description: "The `CPE` key of the organization, if known.", + nullable: true, + }, + id: { + type: "string", + format: "uuid", + description: "The opaque UUID of the organization.", + }, + name: { + type: "string", + description: "The name of the organization.", + }, + website: { + type: "string", + description: "The website of the organization, if known.", + nullable: true, + }, + }, +} as const; + +export const $OrganizationSummary = { + allOf: [ + { + $ref: "#/components/schemas/OrganizationHead", + }, + { + type: "object", + }, + ], +} as const; + +export const $OsvImporter = { + allOf: [ + { + $ref: "#/components/schemas/CommonImporter", + }, + { + type: "object", + required: ["source"], + properties: { + path: { + type: "string", + nullable: true, + }, + source: { + type: "string", + }, + }, + }, + ], +} as const; + +export const $PaginatedAdvisorySummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/AdvisorySummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedBasePurlSummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/BasePurlSummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedImporterReport = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/ImporterReport", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedOrganizationSummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/OrganizationSummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedProductSummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/ProductSummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedPurlSummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/PurlSummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedSbomPackage = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/SbomPackage", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedSbomPackageRelation = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/SbomPackageRelation", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedSbomSummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/SbomSummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $PaginatedVulnerabilitySummary = { + type: "object", + description: "Paginated returned items", + required: ["items", "total"], + properties: { + items: { + type: "array", + items: { + $ref: "#/components/schemas/VulnerabilitySummary", + }, + description: "Returned items", + }, + total: { + type: "integer", + format: "int64", + description: "Total number of items found", + minimum: 0, + }, + }, +} as const; + +export const $ProductDetails = { + allOf: [ + { + $ref: "#/components/schemas/ProductHead", + }, + { + type: "object", + properties: { + vendor: { + allOf: [ + { + $ref: "#/components/schemas/OrganizationSummary", + }, + ], + nullable: true, + }, + versions: { + type: "array", + items: { + $ref: "#/components/schemas/ProductVersionHead", + }, + }, + }, + }, + ], +} as const; + +export const $ProductHead = { + type: "object", + required: ["id", "name"], + properties: { + id: { + type: "string", + format: "uuid", + }, + name: { + type: "string", + }, + }, +} as const; + +export const $ProductSummary = { + allOf: [ + { + $ref: "#/components/schemas/ProductHead", + }, + { + type: "object", + properties: { + vendor: { + allOf: [ + { + $ref: "#/components/schemas/OrganizationSummary", + }, + ], + nullable: true, + }, + versions: { + type: "array", + items: { + $ref: "#/components/schemas/ProductVersionHead", + }, + }, + }, + }, + ], +} as const; + +export const $ProductVersionHead = { + type: "object", + required: ["id", "version"], + properties: { + id: { + type: "string", + format: "uuid", + }, + sbom_id: { + type: "string", + format: "uuid", + nullable: true, + }, + version: { + type: "string", + }, + }, +} as const; + +export const $Purl = { + type: "string", + format: "uri", +} as const; + +export const $PurlAdvisory = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryHead", + }, + { + type: "object", + required: ["status"], + properties: { + status: { + type: "array", + items: { + $ref: "#/components/schemas/PurlStatus", + }, + }, + }, + }, + ], +} as const; + +export const $PurlDetails = { + allOf: [ + { + $ref: "#/components/schemas/PurlHead", + }, + { + type: "object", + required: ["version", "base"], + properties: { + advisories: { + type: "array", + items: { + $ref: "#/components/schemas/PurlAdvisory", + }, + }, + base: { + $ref: "#/components/schemas/BasePurlHead", + }, + version: { + $ref: "#/components/schemas/VersionedPurlHead", + }, + }, + }, + ], +} as const; + +export const $PurlHead = { + type: "object", + required: ["uuid", "purl"], + properties: { + purl: { + $ref: "#/components/schemas/Purl", + }, + uuid: { + type: "string", + format: "uuid", + description: "The ID of the qualified PURL", + }, + }, +} as const; + +export const $PurlStatus = { + type: "object", + required: ["vulnerability", "status"], + properties: { + context: { + allOf: [ + { + $ref: "#/components/schemas/StatusContext", + }, + ], + nullable: true, + }, + status: { + type: "string", + }, + vulnerability: { + $ref: "#/components/schemas/VulnerabilityHead", + }, + }, +} as const; + +export const $PurlSummary = { + allOf: [ + { + $ref: "#/components/schemas/PurlHead", + }, + { + type: "object", + required: ["base", "version", "qualifiers"], + properties: { + base: { + $ref: "#/components/schemas/BasePurlHead", + }, + qualifiers: { + type: "object", + additionalProperties: { + type: "string", + }, + }, + version: { + $ref: "#/components/schemas/VersionedPurlHead", + }, + }, + }, + ], +} as const; + +export const $Relationship = { + type: "string", + enum: [ + "contained_by", + "dependency_of", + "dev_dependency_of", + "optional_dependency_of", + "provided_dependency_of", + "test_dependency_of", + "runtime_dependency_of", + "example_of", + "generated_from", + "ancestor_of", + "variant_of", + "build_tool_of", + "dev_tool_of", + "described_by", + ], +} as const; + +export const $RevisionedImporter = { + type: "object", + description: `A struct wrapping an item with a revision. + +If the revision should not be part of the payload, but e.g. an HTTP header (like \`ETag\`), this +struct can help carrying both pieces.`, + required: ["value", "revision"], + properties: { + revision: { + type: "string", + description: `The revision. + +An opaque string that should have no meaning to the user, only to the backend.`, + }, + value: { + $ref: "#/components/schemas/Importer", + }, + }, +} as const; + +export const $SbomAdvisory = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryHead", + }, + { + type: "object", + properties: { + status: { + type: "array", + items: { + $ref: "#/components/schemas/SbomStatus", + }, + }, + }, + }, + ], +} as const; + +export const $SbomDetails = { + allOf: [ + { + $ref: "#/components/schemas/SbomHead", + }, + { + type: "object", + required: ["advisories"], + properties: { + advisories: { + type: "array", + items: { + $ref: "#/components/schemas/SbomAdvisory", + }, + }, + authors: { + type: "array", + items: { + type: "string", + }, + }, + described_by: { + type: "array", + items: { + $ref: "#/components/schemas/SbomPackage", + }, + }, + published: { + type: "string", + format: "date-time", + nullable: true, + }, + }, + }, + ], +} as const; + +export const $SbomHead = { + type: "object", + required: ["id", "document_id", "name"], + properties: { + document_id: { + type: "string", + }, + hashes: { + type: "array", + items: { + $ref: "#/components/schemas/Id", + }, + }, + id: { + type: "string", + format: "uuid", + }, + labels: { + $ref: "#/components/schemas/Labels", + }, + name: { + type: "string", + }, + }, +} as const; + +export const $SbomImporter = { + allOf: [ + { + $ref: "#/components/schemas/CommonImporter", + }, + { + type: "object", + required: ["source"], + properties: { + keys: { + type: "array", + items: { + type: "string", + format: "uri", + }, + }, + onlyPatterns: { + type: "array", + items: { + type: "string", + }, + }, + source: { + type: "string", + }, + v3Signatures: { + type: "boolean", + }, + }, + }, + ], +} as const; + +export const $SbomPackage = { + type: "object", + required: ["id", "name"], + properties: { + cpe: { + type: "array", + items: { + type: "string", + }, + }, + id: { + type: "string", + }, + name: { + type: "string", + }, + purl: { + type: "array", + items: { + $ref: "#/components/schemas/PurlSummary", + }, + }, + version: { + type: "string", + nullable: true, + }, + }, +} as const; + +export const $SbomPackageRelation = { + type: "object", + required: ["relationship", "package"], + properties: { + package: { + $ref: "#/components/schemas/SbomPackage", + }, + relationship: { + $ref: "#/components/schemas/Relationship", + }, + }, +} as const; + +export const $SbomStatus = { + type: "object", + required: ["vulnerability_id", "status"], + properties: { + context: { + allOf: [ + { + $ref: "#/components/schemas/StatusContext", + }, + ], + nullable: true, + }, + packages: { + type: "array", + items: { + $ref: "#/components/schemas/SbomPackage", + }, + }, + status: { + type: "string", + }, + vulnerability_id: { + type: "string", + }, + }, +} as const; + +export const $SbomSummary = { + allOf: [ + { + $ref: "#/components/schemas/SbomHead", + }, + { + type: "object", + properties: { + authors: { + type: "array", + items: { + type: "string", + }, + }, + described_by: { + type: "array", + items: { + $ref: "#/components/schemas/SbomPackage", + }, + }, + published: { + type: "string", + format: "date-time", + nullable: true, + }, + }, + }, + ], +} as const; + +export const $State = { + type: "string", + enum: ["waiting", "running"], +} as const; + +export const $StatusContext = { + oneOf: [ + { + type: "object", + required: ["purl"], + properties: { + purl: { + $ref: "#/components/schemas/Purl", + }, + }, + }, + { + type: "object", + required: ["cpe"], + properties: { + cpe: { + type: "string", + }, + }, + }, + ], +} as const; + +export const $TypeCounts = { + type: "object", + required: ["base", "version", "package"], + properties: { + base: { + type: "integer", + format: "int64", + }, + package: { + type: "integer", + format: "int64", + }, + version: { + type: "integer", + format: "int64", + }, + }, +} as const; + +export const $TypeHead = { + type: "object", + required: ["name"], + properties: { + name: { + type: "string", + }, + }, +} as const; + +export const $TypeSummary = { + allOf: [ + { + $ref: "#/components/schemas/TypeHead", + }, + { + type: "object", + required: ["counts"], + properties: { + counts: { + $ref: "#/components/schemas/TypeCounts", + }, + }, + }, + ], +} as const; + +export const $Uuid = { + type: "string", + description: "a UUID", +} as const; + +export const $VersionedPurlAdvisory = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryHead", + }, + { + type: "object", + required: ["status"], + properties: { + status: { + type: "array", + items: { + $ref: "#/components/schemas/VersionedPurlStatus", + }, + }, + }, + }, + ], +} as const; + +export const $VersionedPurlDetails = { + allOf: [ + { + $ref: "#/components/schemas/VersionedPurlHead", + }, + { + type: "object", + required: ["base"], + properties: { + advisories: { + type: "array", + items: { + $ref: "#/components/schemas/VersionedPurlAdvisory", + }, + }, + base: { + $ref: "#/components/schemas/BasePurlHead", + }, + purls: { + type: "array", + items: { + $ref: "#/components/schemas/PurlHead", + }, + }, + }, + }, + ], +} as const; + +export const $VersionedPurlHead = { + type: "object", + required: ["uuid", "purl", "version"], + properties: { + purl: { + $ref: "#/components/schemas/Purl", + }, + uuid: { + type: "string", + format: "uuid", + description: "The ID of the versioned PURL", + }, + version: { + type: "string", + description: "The version from the PURL", + }, + }, +} as const; + +export const $VersionedPurlStatus = { + type: "object", + required: ["vulnerability", "status"], + properties: { + status: { + type: "string", + }, + vulnerability: { + $ref: "#/components/schemas/VulnerabilityHead", + }, + }, +} as const; + +export const $VersionedPurlSummary = { + allOf: [ + { + $ref: "#/components/schemas/VersionedPurlHead", + }, + { + type: "object", + required: ["base"], + properties: { + base: { + $ref: "#/components/schemas/BasePurlHead", + }, + purls: { + type: "array", + items: { + $ref: "#/components/schemas/PurlHead", + }, + }, + }, + }, + ], +} as const; + +export const $VulnerabilityAdvisoryHead = { + allOf: [ + { + $ref: "#/components/schemas/AdvisoryHead", + }, + { + type: "object", + properties: { + score: { + type: "number", + format: "double", + nullable: true, + }, + severity: { + type: "string", + nullable: true, + }, + }, + }, + ], +} as const; + +export const $VulnerabilityAdvisoryStatus = { + type: "object", + required: ["base_purl", "version"], + properties: { + base_purl: { + $ref: "#/components/schemas/BasePurlHead", + }, + context: { + allOf: [ + { + $ref: "#/components/schemas/StatusContext", + }, + ], + nullable: true, + }, + version: { + type: "string", + }, + }, +} as const; + +export const $VulnerabilityAdvisorySummary = { + allOf: [ + { + $ref: "#/components/schemas/VulnerabilityAdvisoryHead", + }, + { + type: "object", + required: ["cvss3_scores", "purls", "sboms"], + properties: { + cvss3_scores: { + type: "array", + items: { + type: "string", + }, + description: + "CVSS3 scores from this advisory regarding the vulnerability.", + }, + purls: { + type: "object", + additionalProperties: { + type: "array", + items: { + $ref: "#/components/schemas/VulnerabilityAdvisoryStatus", + }, + }, + }, + sboms: { + type: "array", + items: { + $ref: "#/components/schemas/SbomStatus", + }, + description: + "SBOMs claimed by this advisory to be addressed by this vulnerability.", + }, + }, + }, + ], +} as const; + +export const $VulnerabilityDetails = { + allOf: [ + { + $ref: "#/components/schemas/VulnerabilityHead", + }, + { + type: "object", + required: ["advisories"], + properties: { + advisories: { + type: "array", + items: { + $ref: "#/components/schemas/VulnerabilityAdvisorySummary", + }, + description: "Advisories addressing this vulnerability, if any.", + }, + average_score: { + type: "number", + format: "double", + description: + "Average (arithmetic mean) score of the vulnerability aggregated from *all* related advisories.", + nullable: true, + }, + average_severity: { + type: "string", + description: + "Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories.", + nullable: true, + }, + }, + }, + ], +} as const; + +export const $VulnerabilityHead = { + type: "object", + required: ["identifier"], + properties: { + cwe: { + type: "string", + description: "Associated CWE, if any.", + nullable: true, + }, + description: { + type: "string", + description: "The description of the vulnerability, if known.", + nullable: true, + }, + discovered: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the vulnerability was discovered, if any.", + nullable: true, + }, + identifier: { + type: "string", + description: `The globally-unique identifier for the vulnerability. +Traditionally (but not required) refers to the assigned +CVE identifier.`, + }, + modified: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the vulnerability was last modified, if any.", + nullable: true, + }, + normative: { + type: "boolean", + }, + published: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the vulnerability was published, if any.", + nullable: true, + }, + released: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when software containing the vulnerability first released, if known.", + nullable: true, + }, + title: { + type: "string", + description: "The title of the vulnerability, if known.", + nullable: true, + }, + withdrawn: { + type: "string", + format: "date-time", + description: + "The date (in RFC3339 format) of when the vulnerability was last withdrawn, if any.", + nullable: true, + }, + }, +} as const; + +export const $VulnerabilitySummary = { + allOf: [ + { + $ref: "#/components/schemas/VulnerabilityHead", + }, + { + type: "object", + properties: { + advisories: { + type: "array", + items: { + $ref: "#/components/schemas/VulnerabilityAdvisoryHead", + }, + }, + average_score: { + type: "number", + format: "double", + description: + "Average (arithmetic mean) score of the vulnerability aggregated from *all* related advisories.", + nullable: true, + }, + average_severity: { + type: "string", + description: + "Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories.", + nullable: true, + }, + }, + }, + ], +} as const; + +export const $Which = { + type: "string", + enum: ["left", "right"], +} as const; diff --git a/client/src/app/client/services.gen.ts b/client/src/app/client/services.gen.ts new file mode 100644 index 00000000..ef0dd09a --- /dev/null +++ b/client/src/app/client/services.gen.ts @@ -0,0 +1,555 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { client, type Options } from "@hey-api/client-axios"; +import type { + ListAdvisoriesData, + ListAdvisoriesError, + ListAdvisoriesResponse, + UploadAdvisoryData, + UploadAdvisoryError, + UploadAdvisoryResponse, + UpdateAdvisoryLabelsData, + UpdateAdvisoryLabelsError, + UpdateAdvisoryLabelsResponse, + PatchAdvisoryLabelsData, + PatchAdvisoryLabelsError, + PatchAdvisoryLabelsResponse, + GetAdvisoryData, + GetAdvisoryError, + GetAdvisoryResponse, + DownloadAdvisoryData, + DownloadAdvisoryError, + DownloadAdvisoryResponse, + ListImportersError, + ListImportersResponse, + GetImporterData, + GetImporterError, + GetImporterResponse, + CreateImporterData, + CreateImporterError, + CreateImporterResponse, + UpdateImporterData, + UpdateImporterError, + UpdateImporterResponse, + DeleteImporterData, + DeleteImporterError, + DeleteImporterResponse, + EnableImporterData, + EnableImporterError, + EnableImporterResponse, + ForceRunImporterData, + ForceRunImporterError, + ForceRunImporterResponse, + GetImporterReportsData, + GetImporterReportsError, + GetImporterReportsResponse, + ListOrganizationsData, + ListOrganizationsError, + ListOrganizationsResponse, + GetOrganizationData, + GetOrganizationError, + GetOrganizationResponse, + ListProductsData, + ListProductsError, + ListProductsResponse, + GetProductsData, + GetProductsError, + GetProductsResponse, + ListPurlData, + ListPurlError, + ListPurlResponse, + ListBasePurlsData, + ListBasePurlsError, + ListBasePurlsResponse, + GetBasePurlData, + GetBasePurlError, + GetBasePurlResponse, + ListPurlTypesError, + ListPurlTypesResponse, + GetPurlTypeData, + GetPurlTypeError, + GetPurlTypeResponse, + GetBasePurlOfTypeData, + GetBasePurlOfTypeError, + GetBasePurlOfTypeResponse, + GetVersionedPurlOfTypeData, + GetVersionedPurlOfTypeError, + GetVersionedPurlOfTypeResponse, + GetVersionedPurlData, + GetVersionedPurlError, + GetVersionedPurlResponse, + GetPurlData, + GetPurlError, + GetPurlResponse, + ListSbomsData, + ListSbomsError, + ListSbomsResponse, + UploadSbomData, + UploadSbomError, + UploadSbomResponse, + ListRelatedSbomsData, + ListRelatedSbomsError, + ListRelatedSbomsResponse, + GetSbomData, + GetSbomError, + GetSbomResponse, + DeleteSbomData, + DeleteSbomError, + DeleteSbomResponse, + UpdateSbomLabelsData, + UpdateSbomLabelsError, + UpdateSbomLabelsResponse, + PatchSbomLabelsData, + PatchSbomLabelsError, + PatchSbomLabelsResponse, + ListPackagesData, + ListPackagesError, + ListPackagesResponse, + ListRelatedPackagesData, + ListRelatedPackagesError, + ListRelatedPackagesResponse, + DownloadSbomData, + DownloadSbomError, + DownloadSbomResponse, + ListVulnerabilitiesData, + ListVulnerabilitiesError, + ListVulnerabilitiesResponse, + GetVulnerabilityData, + GetVulnerabilityError, + GetVulnerabilityResponse, +} from "./types.gen"; + +export const listAdvisories = (options?: Options) => { + return (options?.client ?? client).get< + ListAdvisoriesResponse, + ListAdvisoriesError + >({ + ...options, + url: "/api/v1/advisory", + }); +}; + +/** + * Upload a new advisory + */ +export const uploadAdvisory = (options: Options) => { + return (options?.client ?? client).post< + UploadAdvisoryResponse, + UploadAdvisoryError + >({ + ...options, + url: "/api/v1/advisory", + }); +}; + +/** + * Replace the labels of an advisory + */ +export const updateAdvisoryLabels = ( + options: Options +) => { + return (options?.client ?? client).put< + UpdateAdvisoryLabelsResponse, + UpdateAdvisoryLabelsError + >({ + ...options, + url: "/api/v1/advisory/{id}/label", + }); +}; + +/** + * Modify existing labels of an advisory + */ +export const patchAdvisoryLabels = ( + options: Options +) => { + return (options?.client ?? client).patch< + PatchAdvisoryLabelsResponse, + PatchAdvisoryLabelsError + >({ + ...options, + url: "/api/v1/advisory/{id}/label", + }); +}; + +export const getAdvisory = (options: Options) => { + return (options?.client ?? client).get( + { + ...options, + url: "/api/v1/advisory/{key}", + } + ); +}; + +export const downloadAdvisory = (options: Options) => { + return (options?.client ?? client).get< + DownloadAdvisoryResponse, + DownloadAdvisoryError + >({ + ...options, + url: "/api/v1/advisory/{key}/download", + }); +}; + +/** + * List importer configurations + */ +export const listImporters = (options?: Options) => { + return (options?.client ?? client).get< + ListImportersResponse, + ListImportersError + >({ + ...options, + url: "/api/v1/importer", + }); +}; + +/** + * Get an importer configuration + */ +export const getImporter = (options: Options) => { + return (options?.client ?? client).get( + { + ...options, + url: "/api/v1/importer/{name}", + } + ); +}; + +/** + * Create a new importer configuration + */ +export const createImporter = (options: Options) => { + return (options?.client ?? client).post< + CreateImporterResponse, + CreateImporterError + >({ + ...options, + url: "/api/v1/importer/{name}", + }); +}; + +/** + * Update an existing importer configuration + */ +export const updateImporter = (options: Options) => { + return (options?.client ?? client).put< + UpdateImporterResponse, + UpdateImporterError + >({ + ...options, + url: "/api/v1/importer/{name}", + }); +}; + +/** + * Delete an importer configuration + */ +export const deleteImporter = (options: Options) => { + return (options?.client ?? client).delete< + DeleteImporterResponse, + DeleteImporterError + >({ + ...options, + url: "/api/v1/importer/{name}", + }); +}; + +/** + * Update an existing importer configuration + */ +export const enableImporter = (options: Options) => { + return (options?.client ?? client).put< + EnableImporterResponse, + EnableImporterError + >({ + ...options, + url: "/api/v1/importer/{name}/enabled", + }); +}; + +/** + * Force an importer to run as soon as possible + */ +export const forceRunImporter = (options: Options) => { + return (options?.client ?? client).post< + ForceRunImporterResponse, + ForceRunImporterError + >({ + ...options, + url: "/api/v1/importer/{name}/force", + }); +}; + +/** + * Get reports for an importer + */ +export const getImporterReports = ( + options: Options +) => { + return (options?.client ?? client).get< + GetImporterReportsResponse, + GetImporterReportsError + >({ + ...options, + url: "/api/v1/importer/{name}/report", + }); +}; + +export const listOrganizations = (options?: Options) => { + return (options?.client ?? client).get< + ListOrganizationsResponse, + ListOrganizationsError + >({ + ...options, + url: "/api/v1/organization", + }); +}; + +export const getOrganization = (options: Options) => { + return (options?.client ?? client).get< + GetOrganizationResponse, + GetOrganizationError + >({ + ...options, + url: "/api/v1/organization/{id}", + }); +}; + +export const listProducts = (options?: Options) => { + return (options?.client ?? client).get< + ListProductsResponse, + ListProductsError + >({ + ...options, + url: "/api/v1/product", + }); +}; + +export const getProducts = (options: Options) => { + return (options?.client ?? client).get( + { + ...options, + url: "/api/v1/product/{id}", + } + ); +}; + +export const listPurl = (options?: Options) => { + return (options?.client ?? client).get({ + ...options, + url: "/api/v1/purl", + }); +}; + +export const listBasePurls = (options?: Options) => { + return (options?.client ?? client).get< + ListBasePurlsResponse, + ListBasePurlsError + >({ + ...options, + url: "/api/v1/purl/base", + }); +}; + +export const getBasePurl = (options: Options) => { + return (options?.client ?? client).get( + { + ...options, + url: "/api/v1/purl/base/{key}", + } + ); +}; + +export const listPurlTypes = (options?: Options) => { + return (options?.client ?? client).get< + ListPurlTypesResponse, + ListPurlTypesError + >({ + ...options, + url: "/api/v1/purl/type", + }); +}; + +export const getPurlType = (options: Options) => { + return (options?.client ?? client).get( + { + ...options, + url: "/api/v1/purl/type/{type}", + } + ); +}; + +export const getBasePurlOfType = (options: Options) => { + return (options?.client ?? client).get< + GetBasePurlOfTypeResponse, + GetBasePurlOfTypeError + >({ + ...options, + url: "/api/v1/purl/type/{type}/{namespace_and_name}", + }); +}; + +export const getVersionedPurlOfType = ( + options: Options +) => { + return (options?.client ?? client).get< + GetVersionedPurlOfTypeResponse, + GetVersionedPurlOfTypeError + >({ + ...options, + url: "/api/v1/purl/type/{type}/{namespace_and_name}@{version}", + }); +}; + +export const getVersionedPurl = (options: Options) => { + return (options?.client ?? client).get< + GetVersionedPurlResponse, + GetVersionedPurlError + >({ + ...options, + url: "/api/v1/purl/version/{key}", + }); +}; + +export const getPurl = (options: Options) => { + return (options?.client ?? client).get({ + ...options, + url: "/api/v1/purl/{key}", + }); +}; + +export const listSboms = (options?: Options) => { + return (options?.client ?? client).get({ + ...options, + url: "/api/v1/sbom", + }); +}; + +/** + * Upload a new SBOM + */ +export const uploadSbom = (options: Options) => { + return (options?.client ?? client).post({ + ...options, + url: "/api/v1/sbom", + }); +}; + +/** + * Find all SBOMs containing the provided package. + * The package can be provided either via a PURL or using the ID of a package as returned by + * other APIs, but not both. + */ +export const listRelatedSboms = (options?: Options) => { + return (options?.client ?? client).get< + ListRelatedSbomsResponse, + ListRelatedSbomsError + >({ + ...options, + url: "/api/v1/sbom/by-package", + }); +}; + +export const getSbom = (options: Options) => { + return (options?.client ?? client).get({ + ...options, + url: "/api/v1/sbom/{id}", + }); +}; + +export const deleteSbom = (options: Options) => { + return (options?.client ?? client).delete< + DeleteSbomResponse, + DeleteSbomError + >({ + ...options, + url: "/api/v1/sbom/{id}", + }); +}; + +/** + * Replace the labels of an SBOM + */ +export const updateSbomLabels = (options: Options) => { + return (options?.client ?? client).put< + UpdateSbomLabelsResponse, + UpdateSbomLabelsError + >({ + ...options, + url: "/api/v1/sbom/{id}/label", + }); +}; + +/** + * Modify existing labels of an SBOM + */ +export const patchSbomLabels = (options: Options) => { + return (options?.client ?? client).patch< + PatchSbomLabelsResponse, + PatchSbomLabelsError + >({ + ...options, + url: "/api/v1/sbom/{id}/label", + }); +}; + +/** + * Search for packages of an SBOM + */ +export const listPackages = (options: Options) => { + return (options?.client ?? client).get< + ListPackagesResponse, + ListPackagesError + >({ + ...options, + url: "/api/v1/sbom/{id}/packages", + }); +}; + +/** + * Search for related packages in an SBOM + */ +export const listRelatedPackages = ( + options: Options +) => { + return (options?.client ?? client).get< + ListRelatedPackagesResponse, + ListRelatedPackagesError + >({ + ...options, + url: "/api/v1/sbom/{id}/related", + }); +}; + +export const downloadSbom = (options: Options) => { + return (options?.client ?? client).get< + DownloadSbomResponse, + DownloadSbomError + >({ + ...options, + url: "/api/v1/sbom/{key}/download", + }); +}; + +export const listVulnerabilities = ( + options?: Options +) => { + return (options?.client ?? client).get< + ListVulnerabilitiesResponse, + ListVulnerabilitiesError + >({ + ...options, + url: "/api/v1/vulnerability", + }); +}; + +export const getVulnerability = (options: Options) => { + return (options?.client ?? client).get< + GetVulnerabilityResponse, + GetVulnerabilityError + >({ + ...options, + url: "/api/v1/vulnerability/{id}", + }); +}; diff --git a/client/src/app/client/types.gen.ts b/client/src/app/client/types.gen.ts new file mode 100644 index 00000000..a0ed8a49 --- /dev/null +++ b/client/src/app/client/types.gen.ts @@ -0,0 +1,1892 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AdvisoryDetails = AdvisoryHead & { + /** + * Average (arithmetic mean) score of the advisory aggregated from *all* related vulnerability assertions. + */ + average_score?: number | null; + /** + * Average (arithmetic mean) severity of the advisory aggregated from *all* related vulnerability assertions. + */ + average_severity?: string | null; + /** + * Vulnerabilities addressed within this advisory. + */ + vulnerabilities: Array; +}; + +export type AdvisoryHead = { + /** + * Hashes of the underlying original document as ingested. + */ + hashes?: Array; + /** + * The identifier of the advisory, as assigned by the issuing organization. + */ + identifier: string; + issuer?: OrganizationSummary | null; + labels?: Labels; + /** + * The date (in RFC3339 format) of when the advisory was last modified, if any. + */ + modified?: string | null; + /** + * The date (in RFC3339 format) of when the advisory was published, if any. + */ + published?: string | null; + /** + * The title of the advisory as assigned by the issuing organization. + */ + title?: string | null; + /** + * The opaque UUID of the advisory. + */ + uuid: string; + /** + * The date (in RFC3339 format) of when the advisory was withdrawn, if any. + */ + withdrawn?: string | null; +}; + +export type AdvisorySummary = AdvisoryHead & { + /** + * Average (arithmetic mean) score of the advisory aggregated from *all* related vulnerability assertions. + */ + average_score?: number | null; + /** + * Average (arithmetic mean) severity of the advisory aggregated from *all* related vulnerability assertions. + */ + average_severity?: string | null; + /** + * Vulnerabilities addressed within this advisory. + */ + vulnerabilities: Array; +}; + +export type AdvisoryVulnerabilityAssertions = { + assertions: { + [key: string]: Array; + }; +}; + +export type AdvisoryVulnerabilityHead = VulnerabilityHead & { + /** + * The average (arithmetic mean) score this advisory assigns to + * the particular vulnerability. + */ + score: number; + /** + * The English-language word description of the severity of the given + * vulnerability, as asserted by the advisory, using the CVSS bucketing + * ranges. + * + * Critical: 9.0–10.0 + * High: 7.0–8.9 + * Medium: 4.0–6.9 + * Low: 0.1–3.9 + * None: 0 + */ + severity: string; +}; + +/** + * Summary of information from this advisory regarding a single specific vulnerability. + */ +export type AdvisoryVulnerabilitySummary = AdvisoryVulnerabilityHead & { + /** + * All CVSS3 scores from the advisory for the given vulnerability. + * May include several, varying by minor version of the CVSS3 vector. + */ + cvss3_scores: Array; +}; + +export type Assertion = + | { + affected: { + end_version: string; + start_version: string; + }; + } + | { + not_affected: { + version: string; + }; + } + | { + fixed: { + version: string; + }; + }; + +export type BasePurlDetails = BasePurlHead & { + versions?: Array; +}; + +export type BasePurlHead = { + purl: Purl; + /** + * The ID of the base PURL + */ + uuid: string; +}; + +export type BasePurlSummary = BasePurlHead & { + [key: string]: unknown; +}; + +export type CommonImporter = { + /** + * A description for users. + */ + description?: string | null; + /** + * A flag to disable the importer, without deleting it. + */ + disabled?: boolean; + labels?: Labels; + /** + * The period the importer should be run. + */ + period: string; +}; + +export type CsafImporter = CommonImporter & { + onlyPatterns?: Array; + source: string; + v3Signatures?: boolean; +}; + +export type CveImporter = CommonImporter & { + source?: string; + startYear?: number | null; + years?: Array; +}; + +/** + * A hash/digest prefixed with its type. + */ +export type Id = string; + +export type Importer = ImporterData & { + name: string; +}; + +export type ImporterConfiguration = + | { + sbom: SbomImporter; + } + | { + csaf: CsafImporter; + } + | { + osv: OsvImporter; + } + | { + cve: CveImporter; + }; + +export type ImporterData = { + configuration: ImporterConfiguration; + /** + * The continuation token of the importer. + */ + continuation?: unknown; + /** + * The last state change + */ + lastChange: string; + /** + * The error of the last run (empty if successful) + */ + lastError?: string | null; + /** + * The last run (successful or not) + */ + lastRun?: string | null; + /** + * The last successful run + */ + lastSuccess?: string | null; + state: State; +}; + +export type ImporterReport = { + creation: string; + error?: string | null; + id: string; + importer: string; + report: unknown; +}; + +export type Labels = { + [key: string]: string; +}; + +export type OrganizationDetails = OrganizationHead & { + /** + * Advisories issued by the organization, if any. + */ + advisories: Array; +}; + +/** + * An organization who may issue advisories, product SBOMs, or + * otherwise be involved in supply-chain evidence. + */ +export type OrganizationHead = { + /** + * The `CPE` key of the organization, if known. + */ + cpe_key?: string | null; + /** + * The opaque UUID of the organization. + */ + id: string; + /** + * The name of the organization. + */ + name: string; + /** + * The website of the organization, if known. + */ + website?: string | null; +}; + +export type OrganizationSummary = OrganizationHead & { + [key: string]: unknown; +}; + +export type OsvImporter = CommonImporter & { + path?: string | null; + source: string; +}; + +/** + * Paginated returned items + */ +export type PaginatedAdvisorySummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedBasePurlSummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedImporterReport = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedOrganizationSummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedProductSummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedPurlSummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedSbomPackage = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedSbomPackageRelation = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedSbomSummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +/** + * Paginated returned items + */ +export type PaginatedVulnerabilitySummary = { + /** + * Returned items + */ + items: Array; + /** + * Total number of items found + */ + total: number; +}; + +export type ProductDetails = ProductHead & { + vendor?: OrganizationSummary | null; + versions?: Array; +}; + +export type ProductHead = { + id: string; + name: string; +}; + +export type ProductSummary = ProductHead & { + vendor?: OrganizationSummary | null; + versions?: Array; +}; + +export type ProductVersionHead = { + id: string; + sbom_id?: string | null; + version: string; +}; + +export type Purl = string; + +export type PurlAdvisory = AdvisoryHead & { + status: Array; +}; + +export type PurlDetails = PurlHead & { + advisories?: Array; + base: BasePurlHead; + version: VersionedPurlHead; +}; + +export type PurlHead = { + purl: Purl; + /** + * The ID of the qualified PURL + */ + uuid: string; +}; + +export type PurlStatus = { + context?: StatusContext | null; + status: string; + vulnerability: VulnerabilityHead; +}; + +export type PurlSummary = PurlHead & { + base: BasePurlHead; + qualifiers: { + [key: string]: string; + }; + version: VersionedPurlHead; +}; + +export type Relationship = + | "contained_by" + | "dependency_of" + | "dev_dependency_of" + | "optional_dependency_of" + | "provided_dependency_of" + | "test_dependency_of" + | "runtime_dependency_of" + | "example_of" + | "generated_from" + | "ancestor_of" + | "variant_of" + | "build_tool_of" + | "dev_tool_of" + | "described_by"; + +/** + * A struct wrapping an item with a revision. + * + * If the revision should not be part of the payload, but e.g. an HTTP header (like `ETag`), this + * struct can help carrying both pieces. + */ +export type RevisionedImporter = { + /** + * The revision. + * + * An opaque string that should have no meaning to the user, only to the backend. + */ + revision: string; + value: Importer; +}; + +export type SbomAdvisory = AdvisoryHead & { + status?: Array; +}; + +export type SbomDetails = SbomHead & { + advisories: Array; + authors?: Array; + described_by?: Array; + published?: string | null; +}; + +export type SbomHead = { + document_id: string; + hashes?: Array; + id: string; + labels?: Labels; + name: string; +}; + +export type SbomImporter = CommonImporter & { + keys?: Array; + onlyPatterns?: Array; + source: string; + v3Signatures?: boolean; +}; + +export type SbomPackage = { + cpe?: Array; + id: string; + name: string; + purl?: Array; + version?: string | null; +}; + +export type SbomPackageRelation = { + package: SbomPackage; + relationship: Relationship; +}; + +export type SbomStatus = { + context?: StatusContext | null; + packages?: Array; + status: string; + vulnerability_id: string; +}; + +export type SbomSummary = SbomHead & { + authors?: Array; + described_by?: Array; + published?: string | null; +}; + +export type State = "waiting" | "running"; + +export type StatusContext = + | { + purl: Purl; + } + | { + cpe: string; + }; + +export type TypeCounts = { + base: number; + package: number; + version: number; +}; + +export type TypeHead = { + name: string; +}; + +export type TypeSummary = TypeHead & { + counts: TypeCounts; +}; + +/** + * a UUID + */ +export type Uuid = string; + +export type VersionedPurlAdvisory = AdvisoryHead & { + status: Array; +}; + +export type VersionedPurlDetails = VersionedPurlHead & { + advisories?: Array; + base: BasePurlHead; + purls?: Array; +}; + +export type VersionedPurlHead = { + purl: Purl; + /** + * The ID of the versioned PURL + */ + uuid: string; + /** + * The version from the PURL + */ + version: string; +}; + +export type VersionedPurlStatus = { + status: string; + vulnerability: VulnerabilityHead; +}; + +export type VersionedPurlSummary = VersionedPurlHead & { + base: BasePurlHead; + purls?: Array; +}; + +export type VulnerabilityAdvisoryHead = AdvisoryHead & { + score?: number | null; + severity?: string | null; +}; + +export type VulnerabilityAdvisoryStatus = { + base_purl: BasePurlHead; + context?: StatusContext | null; + version: string; +}; + +export type VulnerabilityAdvisorySummary = VulnerabilityAdvisoryHead & { + /** + * CVSS3 scores from this advisory regarding the vulnerability. + */ + cvss3_scores: Array; + purls: { + [key: string]: Array; + }; + /** + * SBOMs claimed by this advisory to be addressed by this vulnerability. + */ + sboms: Array; +}; + +export type VulnerabilityDetails = VulnerabilityHead & { + /** + * Advisories addressing this vulnerability, if any. + */ + advisories: Array; + /** + * Average (arithmetic mean) score of the vulnerability aggregated from *all* related advisories. + */ + average_score?: number | null; + /** + * Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories. + */ + average_severity?: string | null; +}; + +export type VulnerabilityHead = { + /** + * Associated CWE, if any. + */ + cwe?: string | null; + /** + * The description of the vulnerability, if known. + */ + description?: string | null; + /** + * The date (in RFC3339 format) of when the vulnerability was discovered, if any. + */ + discovered?: string | null; + /** + * The globally-unique identifier for the vulnerability. + * Traditionally (but not required) refers to the assigned + * CVE identifier. + */ + identifier: string; + /** + * The date (in RFC3339 format) of when the vulnerability was last modified, if any. + */ + modified?: string | null; + normative?: boolean; + /** + * The date (in RFC3339 format) of when the vulnerability was published, if any. + */ + published?: string | null; + /** + * The date (in RFC3339 format) of when software containing the vulnerability first released, if known. + */ + released?: string | null; + /** + * The title of the vulnerability, if known. + */ + title?: string | null; + /** + * The date (in RFC3339 format) of when the vulnerability was last withdrawn, if any. + */ + withdrawn?: string | null; +}; + +export type VulnerabilitySummary = VulnerabilityHead & { + advisories?: Array; + /** + * Average (arithmetic mean) score of the vulnerability aggregated from *all* related advisories. + */ + average_score?: number | null; + /** + * Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories. + */ + average_severity?: string | null; +}; + +export type Which = "left" | "right"; + +export type ListAdvisoriesData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListAdvisoriesResponse = PaginatedAdvisorySummary; + +export type ListAdvisoriesError = unknown; + +export type UploadAdvisoryData = { + body: Blob | File; + query: { + /** + * Optional issuer if it cannot be determined from advisory contents. + */ + issuer?: string | null; + /** + * Optional labels. + * + * Only use keys with a prefix of `labels.` + */ + labels: Labels; + }; +}; + +export type UploadAdvisoryResponse = unknown; + +export type UploadAdvisoryError = unknown; + +export type UpdateAdvisoryLabelsData = { + body: Labels; + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + id: Id; + }; +}; + +export type UpdateAdvisoryLabelsResponse = void; + +export type UpdateAdvisoryLabelsError = unknown; + +export type PatchAdvisoryLabelsData = { + body: Labels; + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + id: Id; + }; +}; + +export type PatchAdvisoryLabelsResponse = void; + +export type PatchAdvisoryLabelsError = unknown; + +export type GetAdvisoryData = { + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + key: string; + }; +}; + +export type GetAdvisoryResponse = AdvisoryDetails; + +export type GetAdvisoryError = unknown; + +export type DownloadAdvisoryData = { + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' + */ + key: string; + }; +}; + +export type DownloadAdvisoryResponse = Blob | File; + +export type DownloadAdvisoryError = unknown; + +export type ListImportersResponse = Array; + +export type ListImportersError = unknown; + +export type GetImporterData = { + path: { + /** + * The name of the importer + */ + name: string; + }; +}; + +export type GetImporterResponse = RevisionedImporter; + +export type GetImporterError = unknown; + +export type CreateImporterData = { + body: ImporterConfiguration; + path: { + /** + * The name of the importer + */ + name: string; + }; +}; + +export type CreateImporterResponse = unknown; + +export type CreateImporterError = unknown; + +export type UpdateImporterData = { + body: ImporterConfiguration; + headers: { + /** + * The revision to update + */ + "if-match": string; + }; + path: { + /** + * The name of the importer + */ + name: string; + }; +}; + +export type UpdateImporterResponse = unknown; + +export type UpdateImporterError = unknown; + +export type DeleteImporterData = { + headers: { + /** + * The revision to delete + */ + "if-match": string; + }; + path: { + /** + * The name of the importer + */ + name: string; + }; +}; + +export type DeleteImporterResponse = unknown; + +export type DeleteImporterError = unknown; + +export type EnableImporterData = { + body: boolean; + headers: { + /** + * The revision to update + */ + "if-match": string; + }; + path: { + /** + * The name of the importer + */ + name: string; + }; +}; + +export type EnableImporterResponse = unknown; + +export type EnableImporterError = unknown; + +export type ForceRunImporterData = { + body: boolean; + headers: { + /** + * The revision to update + */ + "if-match": string; + }; + path: { + /** + * The name of the importer + */ + name: string; + }; +}; + +export type ForceRunImporterResponse = unknown; + +export type ForceRunImporterError = unknown; + +export type GetImporterReportsData = { + path: { + name: string; + }; +}; + +export type GetImporterReportsResponse = PaginatedImporterReport; + +export type GetImporterReportsError = unknown; + +export type ListOrganizationsData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListOrganizationsResponse = PaginatedOrganizationSummary; + +export type ListOrganizationsError = unknown; + +export type GetOrganizationData = { + path: { + /** + * Opaque ID of the organization + */ + id: string; + }; +}; + +export type GetOrganizationResponse = OrganizationDetails; + +export type GetOrganizationError = unknown; + +export type ListProductsData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListProductsResponse = PaginatedProductSummary; + +export type ListProductsError = unknown; + +export type GetProductsData = { + path: { + /** + * Opaque ID of the product + */ + id: string; + }; +}; + +export type GetProductsResponse = ProductDetails; + +export type GetProductsError = unknown; + +export type ListPurlData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListPurlResponse = PaginatedPurlSummary; + +export type ListPurlError = unknown; + +export type ListBasePurlsData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListBasePurlsResponse = PaginatedBasePurlSummary; + +export type ListBasePurlsError = unknown; + +export type GetBasePurlData = { + path: { + /** + * opaque identifier for a base PURL, or a URL-encoded pURL itself + */ + key: string; + }; +}; + +export type GetBasePurlResponse = BasePurlDetails; + +export type GetBasePurlError = unknown; + +export type ListPurlTypesResponse = Array; + +export type ListPurlTypesError = unknown; + +export type GetPurlTypeData = { + path: { + /** + * PURL identifier of a type + */ + type: string; + }; + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type GetPurlTypeResponse = PaginatedBasePurlSummary; + +export type GetPurlTypeError = unknown; + +export type GetBasePurlOfTypeData = { + path: { + /** + * name of the package optionally preceded by its namespace + */ + namespace_and_name: string; + /** + * PURL identifier of a type + */ + type: string; + }; +}; + +export type GetBasePurlOfTypeResponse = BasePurlDetails; + +export type GetBasePurlOfTypeError = unknown; + +export type GetVersionedPurlOfTypeData = { + path: { + /** + * name of the package optionally preceded by its namespace + */ + namespace_and_name: string; + /** + * PURL identifier of a type + */ + type: string; + /** + * version of the package + */ + version: string; + }; +}; + +export type GetVersionedPurlOfTypeResponse = VersionedPurlDetails; + +export type GetVersionedPurlOfTypeError = unknown; + +export type GetVersionedPurlData = { + path: { + /** + * opaque ID identifier for a package version, or URL-ecnoded pURL itself + */ + key: string; + }; +}; + +export type GetVersionedPurlResponse = VersionedPurlDetails; + +export type GetVersionedPurlError = unknown; + +export type GetPurlData = { + path: { + /** + * opaque identifier for a fully-qualified PURL, or URL-encoded pURL itself + */ + key: string; + }; +}; + +export type GetPurlResponse = PurlDetails; + +export type GetPurlError = unknown; + +export type ListSbomsData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListSbomsResponse = PaginatedSbomSummary; + +export type ListSbomsError = unknown; + +export type UploadSbomData = { + body: Blob | File; + query: { + /** + * Optional labels. + * + * Only use keys with a prefix of `labels.` + */ + labels: Labels; + /** + * Source the document came from + */ + location: string; + }; +}; + +export type UploadSbomResponse = unknown; + +export type UploadSbomError = unknown; + +export type ListRelatedSbomsData = { + query?: { + /** + * Find by a ID of a package + */ + id?: string | null; + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + /** + * Find by PURL + */ + purl?: Purl | null; + q?: string; + sort?: string; + }; +}; + +export type ListRelatedSbomsResponse = PaginatedSbomSummary; + +export type ListRelatedSbomsError = unknown; + +export type GetSbomData = { + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + id: string; + }; +}; + +export type GetSbomResponse = SbomDetails; + +export type GetSbomError = unknown; + +export type DeleteSbomData = { + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + id: string; + }; +}; + +export type DeleteSbomResponse = SbomDetails; + +export type DeleteSbomError = unknown; + +export type UpdateSbomLabelsData = { + body: Labels; + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + id: Id; + }; +}; + +export type UpdateSbomLabelsResponse = void; + +export type UpdateSbomLabelsError = unknown; + +export type PatchSbomLabelsData = { + body: Labels; + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' or 'urn:uuid:' + */ + id: Id; + }; +}; + +export type PatchSbomLabelsResponse = void; + +export type PatchSbomLabelsError = unknown; + +export type ListPackagesData = { + path: { + /** + * ID of the SBOM to get packages for + */ + id: string; + }; + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListPackagesResponse = PaginatedSbomPackage; + +export type ListPackagesError = unknown; + +export type ListRelatedPackagesData = { + path: { + /** + * ID of SBOM to search packages in + */ + id: string; + }; + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + /** + * The Package to use as reference + */ + reference?: string | null; + /** + * Optional relationship filter + */ + relationship?: Relationship | null; + sort?: string; + /** + * Which side the reference should be on + */ + which?: Which; + }; +}; + +export type ListRelatedPackagesResponse = PaginatedSbomPackageRelation; + +export type ListRelatedPackagesError = unknown; + +export type DownloadSbomData = { + path: { + /** + * Digest/hash of the document, prefixed by hash type, such as 'sha256:' + */ + key: string; + }; +}; + +export type DownloadSbomResponse = Blob | File; + +export type DownloadSbomError = unknown; + +export type ListVulnerabilitiesData = { + query?: { + /** + * The maximum number of entries to return. + * + * Zero means: no limit + */ + limit?: number; + /** + * The first item to return, skipping all that come before it. + * + * NOTE: The order of items is defined by the API being called. + */ + offset?: number; + q?: string; + sort?: string; + }; +}; + +export type ListVulnerabilitiesResponse = PaginatedVulnerabilitySummary; + +export type ListVulnerabilitiesError = unknown; + +export type GetVulnerabilityData = { + path: { + /** + * ID of the vulnerability + */ + id: string; + }; +}; + +export type GetVulnerabilityResponse = VulnerabilityDetails; + +export type GetVulnerabilityError = unknown; + +export type $OpenApiTs = { + "/api/v1/advisory": { + get: { + req: ListAdvisoriesData; + res: { + /** + * Matching vulnerabilities + */ + "200": PaginatedAdvisorySummary; + }; + }; + post: { + req: UploadAdvisoryData; + res: { + /** + * Upload a file + */ + "201": unknown; + /** + * The file could not be parsed as an advisory + */ + "400": unknown; + }; + }; + }; + "/api/v1/advisory/{id}/label": { + put: { + req: UpdateAdvisoryLabelsData; + res: { + /** + * Replaced the labels of the advisory + */ + "204": void; + /** + * The advisory could not be found + */ + "404": unknown; + }; + }; + patch: { + req: PatchAdvisoryLabelsData; + res: { + /** + * Modified the labels of the advisory + */ + "204": void; + /** + * The advisory could not be found + */ + "404": unknown; + }; + }; + }; + "/api/v1/advisory/{key}": { + get: { + req: GetAdvisoryData; + res: { + /** + * Matching advisory + */ + "200": AdvisoryDetails; + /** + * Matching advisory not found + */ + "404": unknown; + }; + }; + }; + "/api/v1/advisory/{key}/download": { + get: { + req: DownloadAdvisoryData; + res: { + /** + * Download a an advisory + */ + "200": Blob | File; + /** + * The document could not be found + */ + "404": unknown; + }; + }; + }; + "/api/v1/importer": { + get: { + res: { + /** + * List importer configurations + */ + "200": Array; + }; + }; + }; + "/api/v1/importer/{name}": { + get: { + req: GetImporterData; + res: { + /** + * Retrieved importer configuration + */ + "200": RevisionedImporter; + /** + * An importer with that name could not be found + */ + "404": unknown; + }; + }; + post: { + req: CreateImporterData; + res: { + /** + * Created a new importer configuration + */ + "201": unknown; + /** + * An importer with that name already exists + */ + "409": unknown; + }; + }; + put: { + req: UpdateImporterData; + res: { + /** + * Created a new importer configuration + */ + "201": unknown; + /** + * An importer with that name does not exist + */ + "409": unknown; + /** + * The provided if-match header did not match the stored revision + */ + "412": unknown; + }; + }; + delete: { + req: DeleteImporterData; + res: { + /** + * Delete the importer configuration + */ + "201": unknown; + }; + }; + }; + "/api/v1/importer/{name}/enabled": { + put: { + req: EnableImporterData; + res: { + /** + * Updated the enable state + */ + "201": unknown; + /** + * An importer with that name does not exist + */ + "404": unknown; + /** + * The provided if-match header did not match the stored revision + */ + "412": unknown; + }; + }; + }; + "/api/v1/importer/{name}/force": { + post: { + req: ForceRunImporterData; + res: { + /** + * Updated the state + */ + "201": unknown; + /** + * An importer with that name does not exist + */ + "404": unknown; + /** + * The provided if-match header did not match the stored revision + */ + "412": unknown; + }; + }; + }; + "/api/v1/importer/{name}/report": { + get: { + req: GetImporterReportsData; + res: { + /** + * Retrieved importer reports + */ + "200": PaginatedImporterReport; + }; + }; + }; + "/api/v1/organization": { + get: { + req: ListOrganizationsData; + res: { + /** + * Matching organizations + */ + "200": PaginatedOrganizationSummary; + }; + }; + }; + "/api/v1/organization/{id}": { + get: { + req: GetOrganizationData; + res: { + /** + * Matching organization + */ + "200": OrganizationDetails; + /** + * Matching organization not found + */ + "404": unknown; + }; + }; + }; + "/api/v1/product": { + get: { + req: ListProductsData; + res: { + /** + * Matching products + */ + "200": PaginatedProductSummary; + }; + }; + }; + "/api/v1/product/{id}": { + get: { + req: GetProductsData; + res: { + /** + * Matching product + */ + "200": ProductDetails; + /** + * Matching product not found + */ + "404": unknown; + }; + }; + }; + "/api/v1/purl": { + get: { + req: ListPurlData; + res: { + /** + * All relevant matching qualified PURLs + */ + "200": PaginatedPurlSummary; + }; + }; + }; + "/api/v1/purl/base": { + get: { + req: ListBasePurlsData; + res: { + /** + * All relevant matching versionless base PURL + */ + "200": PaginatedBasePurlSummary; + }; + }; + }; + "/api/v1/purl/base/{key}": { + get: { + req: GetBasePurlData; + res: { + /** + * Details for the versionless base PURL + */ + "200": BasePurlDetails; + }; + }; + }; + "/api/v1/purl/type": { + get: { + res: { + /** + * List of all known PURL types + */ + "200": Array; + }; + }; + }; + "/api/v1/purl/type/{type}": { + get: { + req: GetPurlTypeData; + res: { + /** + * Information regarding PURLs within an type + */ + "200": PaginatedBasePurlSummary; + }; + }; + }; + "/api/v1/purl/type/{type}/{namespace_and_name}": { + get: { + req: GetBasePurlOfTypeData; + res: { + /** + * Matching vulnerabilities + */ + "200": BasePurlDetails; + }; + }; + }; + "/api/v1/purl/type/{type}/{namespace_and_name}@{version}": { + get: { + req: GetVersionedPurlOfTypeData; + res: { + /** + * Matching vulnerabilities + */ + "200": VersionedPurlDetails; + }; + }; + }; + "/api/v1/purl/version/{key}": { + get: { + req: GetVersionedPurlData; + res: { + /** + * Details for the version of a PURL + */ + "200": VersionedPurlDetails; + }; + }; + }; + "/api/v1/purl/{key}": { + get: { + req: GetPurlData; + res: { + /** + * Details for the qualified PURL + */ + "200": PurlDetails; + }; + }; + }; + "/api/v1/sbom": { + get: { + req: ListSbomsData; + res: { + /** + * Matching SBOMs + */ + "200": PaginatedSbomSummary; + }; + }; + post: { + req: UploadSbomData; + res: { + /** + * Upload an SBOM + */ + "201": unknown; + /** + * The file could not be parsed as an advisory + */ + "400": unknown; + }; + }; + }; + "/api/v1/sbom/by-package": { + get: { + req: ListRelatedSbomsData; + res: { + /** + * Matching SBOMs + */ + "200": PaginatedSbomSummary; + }; + }; + }; + "/api/v1/sbom/{id}": { + get: { + req: GetSbomData; + res: { + /** + * Matching SBOM + */ + "200": SbomDetails; + /** + * Matching SBOM not found + */ + "404": unknown; + }; + }; + delete: { + req: DeleteSbomData; + res: { + /** + * Matching SBOM + */ + "200": SbomDetails; + /** + * Matching SBOM not found + */ + "404": unknown; + }; + }; + }; + "/api/v1/sbom/{id}/label": { + put: { + req: UpdateSbomLabelsData; + res: { + /** + * Replaced the labels of the SBOM + */ + "204": void; + /** + * The SBOM could not be found + */ + "404": unknown; + }; + }; + patch: { + req: PatchSbomLabelsData; + res: { + /** + * Modified the labels of the SBOM + */ + "204": void; + /** + * The SBOM could not be found + */ + "404": unknown; + }; + }; + }; + "/api/v1/sbom/{id}/packages": { + get: { + req: ListPackagesData; + res: { + /** + * Packages + */ + "200": PaginatedSbomPackage; + }; + }; + }; + "/api/v1/sbom/{id}/related": { + get: { + req: ListRelatedPackagesData; + res: { + /** + * Packages + */ + "200": PaginatedSbomPackageRelation; + }; + }; + }; + "/api/v1/sbom/{key}/download": { + get: { + req: DownloadSbomData; + res: { + /** + * Download a an SBOM + */ + "200": Blob | File; + /** + * The document could not be found + */ + "404": unknown; + }; + }; + }; + "/api/v1/vulnerability": { + get: { + req: ListVulnerabilitiesData; + res: { + /** + * Matching vulnerabilities + */ + "200": PaginatedVulnerabilitySummary; + }; + }; + }; + "/api/v1/vulnerability/{id}": { + get: { + req: GetVulnerabilityData; + res: { + /** + * Specified vulnerability + */ + "200": VulnerabilityDetails; + /** + * Specified vulnerability not found + */ + "404": unknown; + }; + }; + }; +}; diff --git a/client/src/app/pages/sbom-details/overview.tsx b/client/src/app/pages/sbom-details/overview.tsx index 1e8e8753..805663de 100644 --- a/client/src/app/pages/sbom-details/overview.tsx +++ b/client/src/app/pages/sbom-details/overview.tsx @@ -13,9 +13,10 @@ import { import { compareBySeverityFn, severityList } from "@app/api/model-utils"; import { SBOM, Severity } from "@app/api/models"; import { formatDate } from "@app/utils/utils"; +import {SbomDetails} from "@app/client"; interface InfoProps { - sbom: SBOM; + sbom: SbomDetails; } export const Overview: React.FC = ({ sbom }) => { diff --git a/client/src/app/pages/sbom-details/vulnerabilities-by-sbom.tsx b/client/src/app/pages/sbom-details/vulnerabilities-by-sbom.tsx index c2bbec95..2dbca847 100644 --- a/client/src/app/pages/sbom-details/vulnerabilities-by-sbom.tsx +++ b/client/src/app/pages/sbom-details/vulnerabilities-by-sbom.tsx @@ -38,13 +38,14 @@ import { VulnerabilityInDrawerInfo } from "@app/components/VulnerabilityInDrawer import { useLocalTableControls } from "@app/hooks/table-controls"; import { useFetchSBOMById } from "@app/queries/sboms"; import { useWithUiId } from "@app/utils/query-utils"; +import {SbomPackage} from "@app/client"; interface TableData { vulnerabilityId: string; advisory: AdvisoryWithinSbom; status: VulnerabilityStatus; context: { cpe: string }; - packages: { id: string; name: string; version: string }[]; + packages: SbomPackage[]; vulnerability?: VulnerabilityIndex; } @@ -82,17 +83,17 @@ export const VulnerabilitiesBySbom: React.FC = ({ >(new Set()); React.useEffect(() => { - const vulnerabilities: TableData[] = (sbom?.advisories ?? []) + const vulnerabilities = (sbom?.advisories ?? []) .flatMap((advisory) => { - return advisory.status.map((status) => ({ + return (advisory.status ?? []).map((status) => ({ vulnerabilityId: status.vulnerability_id, status: status.status, context: { ...status.context }, - packages: [...status.packages], + packages: status.packages || [], advisory: { ...advisory }, - })); + } as TableData)); }) - .reduce((prev, current) => { + .reduce((prev, current)=> { const exists = prev.find( (item) => item.vulnerabilityId === current.vulnerabilityId && @@ -378,11 +379,7 @@ export const VulnerabilitiesBySbom: React.FC = ({ }; interface VulnerabilitiesExpandedAreaProps { - packages: { - id: string; - name: string; - version: string; - }[]; + packages: SbomPackage[]; } export const VulnerabilitiesExpandedArea: React.FC< diff --git a/client/src/app/queries/sboms.ts b/client/src/app/queries/sboms.ts index b3ee7bf2..32def3be 100644 --- a/client/src/app/queries/sboms.ts +++ b/client/src/app/queries/sboms.ts @@ -11,6 +11,7 @@ import { uploadSbom, } from "@app/api/rest"; import { useUpload } from "@app/hooks/useUpload"; +import { getSbom } from "@app/client"; export const SBOMsQueryKey = "sboms"; @@ -35,11 +36,11 @@ export const useFetchSBOMs = ( }; }; -export const useFetchSBOMById = (id?: number | string) => { +export const useFetchSBOMById = (id?: string) => { const { data, isLoading, error } = useQuery({ queryKey: [SBOMsQueryKey, id], - queryFn: () => - id === undefined ? Promise.resolve(undefined) : getSBOMById(id), + queryFn: async () => + id === undefined ? undefined : (await getSbom({ path: { id } })).data, enabled: id !== undefined, }); diff --git a/client/src/app/utils/utils.ts b/client/src/app/utils/utils.ts index e1df5ebf..849daa14 100644 --- a/client/src/app/utils/utils.ts +++ b/client/src/app/utils/utils.ts @@ -34,7 +34,7 @@ export const getToolbarChipKey = (value: string | ToolbarChip) => { // Dates -export const formatDate = (value?: string) => { +export const formatDate = (value?: string | null) => { return value ? dayjs(value).format(RENDER_DATE_FORMAT) : null; }; diff --git a/package-lock.json b/package-lock.json index d1cca5e8..a919eeff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "version": "0.1.0", "license": "Apache-2.0", "dependencies": { + "@hey-api/client-axios": "^0.1.1", "@hookform/resolvers": "^2.9.11", "@patternfly/patternfly": "^5.2.1", "@patternfly/react-charts": "^7.2.1", @@ -91,6 +92,7 @@ "yup": "^0.32.11" }, "devDependencies": { + "@hey-api/openapi-ts": "^0.50.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", @@ -193,6 +195,23 @@ "node": ">=6.0.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.6.4", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.6.4.tgz", + "integrity": "sha512-9K6xOqeevacvweLGik6LnZCb1fBtCOSIWQs8d096XGeqoLKC33UVMGz9+77Gw44KvbH4pKcQPWo4ZpxkXYj05w==", + "dev": true, + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -909,6 +928,57 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@hey-api/client-axios": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@hey-api/client-axios/-/client-axios-0.1.1.tgz", + "integrity": "sha512-cBveZnvbmpXFKuEjZw+9IbjVYdc+9jEQ0F6kLXQWckbDCKAGrpvnDThKwIPeEhTQdR4MgRabckHERN6d5DTtDA==", + "peerDependencies": { + "axios": ">= 1.0.0 < 2" + } + }, + "node_modules/@hey-api/openapi-ts": { + "version": "0.50.1", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.50.1.tgz", + "integrity": "sha512-9xc5TZP7srz2R90B2qDOPgRYHLjnrcpKKMDcGQo90T4gYLgBupus8cKY/s+gps7AVtxYeI7M9IDvHOc3t+5UFA==", + "dev": true, + "dependencies": { + "@apidevtools/json-schema-ref-parser": "11.6.4", + "c12": "1.11.1", + "camelcase": "8.0.0", + "commander": "12.1.0", + "handlebars": "4.7.8" + }, + "bin": { + "openapi-ts": "bin/index.cjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@hookform/resolvers": { "version": "2.9.11", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.11.tgz", @@ -1505,6 +1575,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -7095,6 +7171,46 @@ "node": ">= 0.8" } }, + "node_modules/c12": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/c12/-/c12-1.11.1.tgz", + "integrity": "sha512-KDU0TvSvVdaYcQKQ6iPHATGz/7p/KiVjPg4vQrB6Jg/wX9R0yl5RZxWm9IoZqaIHD2+6PZd81+KMGwRr/lRIUg==", + "dev": true, + "dependencies": { + "chokidar": "^3.6.0", + "confbox": "^0.1.7", + "defu": "^6.1.4", + "dotenv": "^16.4.5", + "giget": "^1.2.3", + "jiti": "^1.21.6", + "mlly": "^1.7.1", + "ohash": "^1.1.3", + "pathe": "^1.1.2", + "perfect-debounce": "^1.0.0", + "pkg-types": "^1.1.1", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.4" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -7268,6 +7384,15 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -7292,6 +7417,15 @@ "node": ">=8" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/cjs-module-lexer": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", @@ -7627,6 +7761,12 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", @@ -7636,6 +7776,15 @@ "node": ">=0.8" } }, + "node_modules/consola": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -8623,6 +8772,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true + }, "node_modules/delaunator": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", @@ -8671,6 +8826,12 @@ "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz", + "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==", + "dev": true + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -10300,6 +10461,36 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/fs-monkey": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", @@ -10435,6 +10626,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/giget": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.3.tgz", + "integrity": "sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==", + "dev": true, + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.2.3", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.3", + "nypm": "^0.3.8", + "ohash": "^1.1.3", + "pathe": "^1.1.2", + "tar": "^6.2.0" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob": { "version": "10.4.2", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", @@ -10607,6 +10817,27 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -12772,6 +13003,15 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", @@ -14274,6 +14514,37 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -14286,6 +14557,18 @@ "node": ">=10" } }, + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } + }, "node_modules/monaco-editor": { "version": "0.34.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", @@ -14480,6 +14763,12 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz", + "integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==", + "dev": true + }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14581,6 +14870,160 @@ "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", "dev": true }, + "node_modules/nypm": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.3.9.tgz", + "integrity": "sha512-BI2SdqqTHg2d4wJh8P9A1W+bslg33vOE9IZDY6eR2QC+Pu1iNBVZUqczrd43rJb+fMzHU7ltAYKsEFY/kHMFcw==", + "dev": true, + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.2.3", + "execa": "^8.0.1", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/nypm/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/nypm/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/nypm/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nypm/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/obj-case": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/obj-case/-/obj-case-0.2.1.tgz", @@ -14704,6 +15147,12 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "node_modules/ohash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", + "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", + "dev": true + }, "node_modules/oidc-client-ts": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.4.0.tgz", @@ -15094,6 +15543,18 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -15196,6 +15657,17 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", + "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -16021,6 +16493,16 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -17961,6 +18443,38 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/temp": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", @@ -18708,6 +19222,25 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", + "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -19970,6 +20503,12 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index 46eef8e3..652c4082 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "start:dev": "concurrently -n common,client -c 'white.bold.inverse,green.bold.inverse,blue.bold.inverse' 'npm:start:dev:common' 'npm:start:dev:client'", "start": "npm run build -w common -w client && npm run start -w server", "test": "npm run test -ws --if-present --", - "lint": "npm run lint -ws --if-present --" + "lint": "npm run lint -ws --if-present --", + "generate": "npm run generate -w client", + "prettier": "npx prettier --write './**/*.{ts,js,json}'" }, "lint-staged": { "package-lock.json": "./scripts/verify_lock.mjs", From cb118a7fb22ee4cd12da942c08f425f7d94d07dc Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Tue, 6 Aug 2024 08:48:16 -0400 Subject: [PATCH 2/2] Make sure that api failures throw the Axios error so react query can deal with them as expected. Signed-off-by: Hiram Chirino --- client/package.json | 4 ++-- client/src/app/axios-config/apiInit.ts | 3 ++- client/src/app/client/services.gen.ts | 8 +++++++- client/src/app/queries/sboms.ts | 5 ++++- package-lock.json | 16 ++++++++-------- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/client/package.json b/client/package.json index 52cf88ce..bebedd3c 100644 --- a/client/package.json +++ b/client/package.json @@ -20,7 +20,7 @@ "*.{css,json,md,yaml,yml}": "prettier --write" }, "dependencies": { - "@hey-api/client-axios": "^0.1.1", + "@hey-api/client-axios": "^0.2.0", "@hookform/resolvers": "^2.9.11", "@patternfly/patternfly": "^5.2.1", "@patternfly/react-charts": "^7.2.1", @@ -55,7 +55,7 @@ "yup": "^0.32.11" }, "devDependencies": { - "@hey-api/openapi-ts": "^0.50.1", + "@hey-api/openapi-ts": "^0.52.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", diff --git a/client/src/app/axios-config/apiInit.ts b/client/src/app/axios-config/apiInit.ts index 083b40cb..8484e673 100644 --- a/client/src/app/axios-config/apiInit.ts +++ b/client/src/app/axios-config/apiInit.ts @@ -5,10 +5,11 @@ import { OIDC_CLIENT_ID, OIDC_SERVER_URL, oidcClientSettings } from "@app/oidc"; import { createClient } from "@hey-api/client-axios"; -createClient({ +export const client = createClient({ // set default base url for requests baseURL: "/", axios: axios, + throwOnError: true, }); function getUser() { diff --git a/client/src/app/client/services.gen.ts b/client/src/app/client/services.gen.ts index ef0dd09a..7e82f306 100644 --- a/client/src/app/client/services.gen.ts +++ b/client/src/app/client/services.gen.ts @@ -1,6 +1,10 @@ // This file is auto-generated by @hey-api/openapi-ts -import { client, type Options } from "@hey-api/client-axios"; +import { + createClient, + createConfig, + type Options, +} from "@hey-api/client-axios"; import type { ListAdvisoriesData, ListAdvisoriesError, @@ -119,6 +123,8 @@ import type { GetVulnerabilityResponse, } from "./types.gen"; +export const client = createClient(createConfig()); + export const listAdvisories = (options?: Options) => { return (options?.client ?? client).get< ListAdvisoriesResponse, diff --git a/client/src/app/queries/sboms.ts b/client/src/app/queries/sboms.ts index 32def3be..56c03c43 100644 --- a/client/src/app/queries/sboms.ts +++ b/client/src/app/queries/sboms.ts @@ -12,6 +12,7 @@ import { } from "@app/api/rest"; import { useUpload } from "@app/hooks/useUpload"; import { getSbom } from "@app/client"; +import { client } from "@app/axios-config/apiInit"; export const SBOMsQueryKey = "sboms"; @@ -40,7 +41,9 @@ export const useFetchSBOMById = (id?: string) => { const { data, isLoading, error } = useQuery({ queryKey: [SBOMsQueryKey, id], queryFn: async () => - id === undefined ? undefined : (await getSbom({ path: { id } })).data, + id === undefined + ? undefined + : (await getSbom({ client, path: { id } })).data, enabled: id !== undefined, }); diff --git a/package-lock.json b/package-lock.json index a919eeff..4e692424 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "version": "0.1.0", "license": "Apache-2.0", "dependencies": { - "@hey-api/client-axios": "^0.1.1", + "@hey-api/client-axios": "^0.2.0", "@hookform/resolvers": "^2.9.11", "@patternfly/patternfly": "^5.2.1", "@patternfly/react-charts": "^7.2.1", @@ -92,7 +92,7 @@ "yup": "^0.32.11" }, "devDependencies": { - "@hey-api/openapi-ts": "^0.50.1", + "@hey-api/openapi-ts": "^0.52.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", @@ -929,17 +929,17 @@ } }, "node_modules/@hey-api/client-axios": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@hey-api/client-axios/-/client-axios-0.1.1.tgz", - "integrity": "sha512-cBveZnvbmpXFKuEjZw+9IbjVYdc+9jEQ0F6kLXQWckbDCKAGrpvnDThKwIPeEhTQdR4MgRabckHERN6d5DTtDA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@hey-api/client-axios/-/client-axios-0.2.0.tgz", + "integrity": "sha512-U8Uon1dF5S+xwPaSLsKP3QrjinDqEOyc/z67vqyXB2tQS0QlnkVMr0X3doySuhOOSEbNVSqsCAkR289ItMabtw==", "peerDependencies": { "axios": ">= 1.0.0 < 2" } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.50.1", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.50.1.tgz", - "integrity": "sha512-9xc5TZP7srz2R90B2qDOPgRYHLjnrcpKKMDcGQo90T4gYLgBupus8cKY/s+gps7AVtxYeI7M9IDvHOc3t+5UFA==", + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.52.0.tgz", + "integrity": "sha512-DA3Zf5ONxMK1PUkK88lAuYbXMgn5BvU5sjJdTAO2YOn6Eu/9ovilBztMzvu8pyY44PmL3n4ex4+f+XIwvgfhvw==", "dev": true, "dependencies": { "@apidevtools/json-schema-ref-parser": "11.6.4",