From 954147f7d9905197e786ea4b28add18d5b1a4e33 Mon Sep 17 00:00:00 2001 From: chris48s Date: Sat, 18 Jan 2025 19:16:41 +0000 Subject: [PATCH] URL validator tidyup; affects [discourse dynamic endpoint gerrit jira maven nexus osslifecycle python vpm website] securityheaders sonar swagger w3c (#10810) * add a required url validator * replace occurrences of optionalUrl.required() with url * use standard validators in server.js --- core/server/server.js | 9 ++++++--- services/discourse/discourse.service.js | 4 ++-- services/dynamic/dynamic-helpers.js | 4 ++-- services/endpoint/endpoint.service.js | 4 ++-- services/gerrit/gerrit.service.js | 4 ++-- services/jira/jira-issue.service.js | 4 ++-- services/jira/jira-sprint.service.js | 4 ++-- services/maven-metadata/maven-metadata.service.js | 4 ++-- services/nexus/nexus.service.js | 4 ++-- services/osslifecycle/osslifecycle.service.js | 4 ++-- services/python/python-version-from-toml.service.js | 4 ++-- services/security-headers/security-headers.service.js | 4 ++-- services/sonar/sonar-helpers.js | 6 +++--- services/swagger/swagger.service.js | 4 ++-- services/twitter/twitter.service.js | 4 ++-- services/validators.js | 7 +++++++ services/vpm/vpm-version.service.js | 4 ++-- services/w3c/w3c-validation.service.js | 4 ++-- services/website/website.service.js | 4 ++-- 19 files changed, 48 insertions(+), 38 deletions(-) diff --git a/core/server/server.js b/core/server/server.js index 9cf6f3ca55b1e..03e242f14c622 100644 --- a/core/server/server.js +++ b/core/server/server.js @@ -16,7 +16,12 @@ import { makeSend } from '../base-service/legacy-result-sender.js' import { handleRequest } from '../base-service/legacy-request-handler.js' import { clearResourceCache } from '../base-service/resource-cache.js' import { rasterRedirectUrl } from '../badge-urls/make-badge-url.js' -import { fileSize, nonNegativeInteger } from '../../services/validators.js' +import { + fileSize, + nonNegativeInteger, + optionalUrl, + url as requiredUrl, +} from '../../services/validators.js' import log from './log.js' import PrometheusMetrics from './prometheus-metrics.js' import InfluxMetrics from './influx-metrics.js' @@ -54,8 +59,6 @@ const Joi = originalJoi }, })) -const optionalUrl = Joi.string().uri({ scheme: ['http', 'https'] }) -const requiredUrl = optionalUrl.required() const origins = Joi.arrayFromString().items(Joi.string().origin()) const defaultService = Joi.object({ authorizedOrigins: origins }).default({ authorizedOrigins: [], diff --git a/services/discourse/discourse.service.js b/services/discourse/discourse.service.js index c0307ca8cb6dd..7d18db4f0dcd8 100644 --- a/services/discourse/discourse.service.js +++ b/services/discourse/discourse.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { metric } from '../text-formatters.js' -import { nonNegativeInteger, optionalUrl } from '../validators.js' +import { nonNegativeInteger, url } from '../validators.js' import { BaseJsonService, queryParams } from '../index.js' const schemaSingular = Joi.object({ @@ -20,7 +20,7 @@ const schemaPlural = Joi.object({ const schema = Joi.alternatives(schemaSingular, schemaPlural) const queryParamSchema = Joi.object({ - server: optionalUrl.required(), + server: url, }).required() function singular(variant) { diff --git a/services/dynamic/dynamic-helpers.js b/services/dynamic/dynamic-helpers.js index cb3b6c6ebe3d8..269d681128803 100644 --- a/services/dynamic/dynamic-helpers.js +++ b/services/dynamic/dynamic-helpers.js @@ -1,8 +1,8 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' const queryParamSchema = Joi.object({ - url: optionalUrl.required(), + url, query: Joi.string().required(), prefix: Joi.alternatives().try(Joi.string(), Joi.number()), suffix: Joi.alternatives().try(Joi.string(), Joi.number()), diff --git a/services/endpoint/endpoint.service.js b/services/endpoint/endpoint.service.js index eb9d8e6acd1f4..ba7bdfdcfdf39 100644 --- a/services/endpoint/endpoint.service.js +++ b/services/endpoint/endpoint.service.js @@ -1,14 +1,14 @@ import { URL } from 'url' import Joi from 'joi' import { httpErrors } from '../dynamic-common.js' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { fetchEndpointData } from '../endpoint-common.js' import { BaseJsonService, InvalidParameter, queryParams } from '../index.js' const blockedDomains = ['github.com', 'shields.io'] const queryParamSchema = Joi.object({ - url: optionalUrl.required(), + url, }).required() const description = ` diff --git a/services/gerrit/gerrit.service.js b/services/gerrit/gerrit.service.js index 0b0da1167f30c..03904768a1b4b 100644 --- a/services/gerrit/gerrit.service.js +++ b/services/gerrit/gerrit.service.js @@ -1,9 +1,9 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseJsonService, pathParam, queryParam } from '../index.js' const queryParamSchema = Joi.object({ - baseUrl: optionalUrl.required(), + baseUrl: url, }).required() const schema = Joi.object({ diff --git a/services/jira/jira-issue.service.js b/services/jira/jira-issue.service.js index 1f6bb247689e4..2cf6f0eb0ee4b 100644 --- a/services/jira/jira-issue.service.js +++ b/services/jira/jira-issue.service.js @@ -1,10 +1,10 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseJsonService, pathParam, queryParam } from '../index.js' import { authConfig } from './jira-common.js' const queryParamSchema = Joi.object({ - baseUrl: optionalUrl.required(), + baseUrl: url, }).required() const schema = Joi.object({ diff --git a/services/jira/jira-sprint.service.js b/services/jira/jira-sprint.service.js index c35884bb01733..66e31c43b8547 100644 --- a/services/jira/jira-sprint.service.js +++ b/services/jira/jira-sprint.service.js @@ -1,10 +1,10 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseJsonService, pathParam, queryParam } from '../index.js' import { authConfig } from './jira-common.js' const queryParamSchema = Joi.object({ - baseUrl: optionalUrl.required(), + baseUrl: url, }).required() const schema = Joi.object({ diff --git a/services/maven-metadata/maven-metadata.service.js b/services/maven-metadata/maven-metadata.service.js index d167c068d0f2e..bd27c286f35b4 100644 --- a/services/maven-metadata/maven-metadata.service.js +++ b/services/maven-metadata/maven-metadata.service.js @@ -1,11 +1,11 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { renderVersionBadge } from '../version.js' import { BaseXmlService, NotFound, queryParams } from '../index.js' import { description } from './maven-metadata.js' const queryParamSchema = Joi.object({ - metadataUrl: optionalUrl.required(), + metadataUrl: url, versionPrefix: Joi.string().optional(), versionSuffix: Joi.string().optional(), }).required() diff --git a/services/nexus/nexus.service.js b/services/nexus/nexus.service.js index a6bd0cea0cc62..f0b99e40f72e2 100644 --- a/services/nexus/nexus.service.js +++ b/services/nexus/nexus.service.js @@ -1,7 +1,7 @@ import Joi from 'joi' import { renderVersionBadge } from '../version.js' import { - optionalUrl, + url, optionalDottedVersionNClausesWithOptionalSuffix, } from '../validators.js' import { @@ -49,7 +49,7 @@ const nexus2ResolveApiSchema = Joi.object({ }).required() const queryParamSchema = Joi.object({ - server: optionalUrl.required(), + server: url, queryOpt: Joi.string() .regex(/(:[\w.]+=[^:]*)+/i) .optional(), diff --git a/services/osslifecycle/osslifecycle.service.js b/services/osslifecycle/osslifecycle.service.js index 483b3035c3d00..ffb547916df60 100644 --- a/services/osslifecycle/osslifecycle.service.js +++ b/services/osslifecycle/osslifecycle.service.js @@ -1,5 +1,5 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseService, InvalidResponse, queryParam } from '../index.js' const description = ` @@ -12,7 +12,7 @@ can be viewed on the [OSS Tracker repository](https://github.com/Netflix/osstrac ` const queryParamSchema = Joi.object({ - file_url: optionalUrl.required(), + file_url: url, }).required() export default class OssTracker extends BaseService { diff --git a/services/python/python-version-from-toml.service.js b/services/python/python-version-from-toml.service.js index 65a34dfde7e04..9f6b06b43ada1 100644 --- a/services/python/python-version-from-toml.service.js +++ b/services/python/python-version-from-toml.service.js @@ -1,10 +1,10 @@ import Joi from 'joi' import BaseTomlService from '../../core/base-service/base-toml.js' import { queryParams } from '../index.js' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' const queryParamSchema = Joi.object({ - tomlFilePath: optionalUrl.required(), + tomlFilePath: url, }).required() const schema = Joi.object({ diff --git a/services/security-headers/security-headers.service.js b/services/security-headers/security-headers.service.js index e9356f41dbee9..0a19e105288d4 100644 --- a/services/security-headers/security-headers.service.js +++ b/services/security-headers/security-headers.service.js @@ -1,9 +1,9 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseService, NotFound, queryParams } from '../index.js' const queryParamSchema = Joi.object({ - url: optionalUrl.required(), + url, ignoreRedirects: Joi.equal(''), }).required() diff --git a/services/sonar/sonar-helpers.js b/services/sonar/sonar-helpers.js index 8cce905474668..c2cd7da0c4128 100644 --- a/services/sonar/sonar-helpers.js +++ b/services/sonar/sonar-helpers.js @@ -1,7 +1,7 @@ import Joi from 'joi' import { queryParams } from '../index.js' import { colorScale } from '../color-formatters.js' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' const ratingPercentageScaleSteps = [10, 20, 50, 100] const ratingScaleColors = [ @@ -38,7 +38,7 @@ const sonarVersionSchema = Joi.alternatives( const queryParamSchema = Joi.object({ sonarVersion: sonarVersionSchema, - server: optionalUrl.required(), + server: url, }).required() const openApiQueryParams = queryParams( @@ -48,7 +48,7 @@ const openApiQueryParams = queryParams( const queryParamWithFormatSchema = Joi.object({ sonarVersion: sonarVersionSchema, - server: optionalUrl.required(), + server: url, format: Joi.string().allow('short', 'long').optional(), }).required() diff --git a/services/swagger/swagger.service.js b/services/swagger/swagger.service.js index 2b9f97a8e44e1..780f353797a12 100644 --- a/services/swagger/swagger.service.js +++ b/services/swagger/swagger.service.js @@ -1,5 +1,5 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseJsonService, NotFound, queryParams } from '../index.js' const schema = Joi.object() @@ -14,7 +14,7 @@ const schema = Joi.object() .required() const queryParamSchema = Joi.object({ - specUrl: optionalUrl.required(), + specUrl: url, }).required() export default class SwaggerValidatorService extends BaseJsonService { diff --git a/services/twitter/twitter.service.js b/services/twitter/twitter.service.js index ceafdbe51ffa3..6e4a1aacea877 100644 --- a/services/twitter/twitter.service.js +++ b/services/twitter/twitter.service.js @@ -1,9 +1,9 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseService, pathParams, queryParams } from '../index.js' const queryParamSchema = Joi.object({ - url: optionalUrl.required(), + url, }).required() class TwitterUrl extends BaseService { diff --git a/services/validators.js b/services/validators.js index b4455278668b4..dbe86ba43ffb8 100644 --- a/services/validators.js +++ b/services/validators.js @@ -70,6 +70,13 @@ export const optionalDottedVersionNClausesWithOptionalSuffix = */ export const optionalUrl = Joi.string().uri({ scheme: ['http', 'https'] }) +/** + * Joi validator that checks if a value is a URL and the value must be present. + * + * @type {Joi} + */ +export const url = optionalUrl.required() + /** * Joi validator for a file size we are going to pass to bytes.parse * see https://github.com/visionmedia/bytes.js#bytesparsestringnumber-value-numbernull diff --git a/services/vpm/vpm-version.service.js b/services/vpm/vpm-version.service.js index 3e486ac458731..b71bc73a849ca 100644 --- a/services/vpm/vpm-version.service.js +++ b/services/vpm/vpm-version.service.js @@ -1,10 +1,10 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { latest, renderVersionBadge } from '../version.js' import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js' const queryParamSchema = Joi.object({ - repository_url: optionalUrl.required(), + repository_url: url, include_prereleases: Joi.equal(''), }).required() diff --git a/services/w3c/w3c-validation.service.js b/services/w3c/w3c-validation.service.js index 33a5462bef931..1d61715942dc3 100644 --- a/services/w3c/w3c-validation.service.js +++ b/services/w3c/w3c-validation.service.js @@ -1,5 +1,5 @@ import Joi from 'joi' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js' import { description, @@ -25,7 +25,7 @@ const schema = Joi.object({ }).required() const queryParamSchema = Joi.object({ - targetUrl: optionalUrl.required(), + targetUrl: url, preset: Joi.string().regex(presetRegex).allow(''), }).required() diff --git a/services/website/website.service.js b/services/website/website.service.js index 8394db80472b5..fbfefc5e2fe25 100644 --- a/services/website/website.service.js +++ b/services/website/website.service.js @@ -2,7 +2,7 @@ import emojic from 'emojic' import Joi from 'joi' import trace from '../../core/base-service/trace.js' import { BaseService, queryParams } from '../index.js' -import { optionalUrl } from '../validators.js' +import { url } from '../validators.js' import { queryParamSchema, renderWebsiteStatus, @@ -20,7 +20,7 @@ A site will be classified as "down" if it fails to respond within 3.5 seconds. ` const urlQueryParamSchema = Joi.object({ - url: optionalUrl.required(), + url, }).required() export default class Website extends BaseService {