diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js index 877bbab06d..dc8aae98b7 100644 --- a/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js +++ b/tools/spectral/ipa/__tests__/IPA117DescriptionEndsWithPeriod.test.js @@ -61,7 +61,7 @@ testRule('xgen-IPA-117-description-ends-with-period', [ errors: [], }, { - name: 'invalid components with exceptions', + name: 'invalid description with exceptions', document: { components: { schemas: { diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionMustNotUseHtml.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionMustNotUseHtml.test.js new file mode 100644 index 0000000000..71656f0272 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA117DescriptionMustNotUseHtml.test.js @@ -0,0 +1,112 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-117-description-must-not-use-html', [ + { + name: 'valid description', + document: { + components: { + schemas: { + Schema: { + properties: { + valid: { + description: 'Description.', + }, + validWithAngleBracket: { + description: 'Must be < 250 characters.', + }, + validWithAngleBrackets: { + description: 'For example :', + }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid descriptions', + document: { + components: { + schemas: { + Schema: { + properties: { + html: { + description: 'Description', + }, + link: { + description: 'To learn more, see MongoDB', + }, + inlineHtml: { + description: 'This is something. Description', + }, + selfClosingHtml: { + description: 'This is something.
With a line break.', + }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-117-description-must-not-use-html', + message: 'Descriptions must not use raw HTML.', + path: ['components', 'schemas', 'Schema', 'properties', 'html'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-117-description-must-not-use-html', + message: + 'Descriptions must not use raw HTML. If you want to link to additional documentation, please use the externalDocumentation property (https://swagger.io/specification/#external-documentation-object).', + path: ['components', 'schemas', 'Schema', 'properties', 'link'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-117-description-must-not-use-html', + message: 'Descriptions must not use raw HTML.', + path: ['components', 'schemas', 'Schema', 'properties', 'inlineHtml'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-117-description-must-not-use-html', + message: 'Descriptions must not use raw HTML.', + path: ['components', 'schemas', 'Schema', 'properties', 'selfClosingHtml'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid descriptions with exceptions', + document: { + components: { + schemas: { + Schema: { + properties: { + html: { + description: 'Description', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-must-not-use-html': 'reason', + }, + }, + inlineHtml: { + description: 'This is something. Description', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-must-not-use-html': 'reason', + }, + }, + selfClosingHtml: { + description: 'This is something.
With a line break.', + 'x-xgen-IPA-exception': { + 'xgen-IPA-117-description-must-not-use-html': 'reason', + }, + }, + }, + }, + }, + }, + }, + errors: [], + }, +]); diff --git a/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js b/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js index e72777c061..071a50ffff 100644 --- a/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js +++ b/tools/spectral/ipa/__tests__/IPA117DescriptionStartsWithUpperCase.test.js @@ -44,7 +44,7 @@ testRule('xgen-IPA-117-description-starts-with-uppercase', [ ], }, { - name: 'invalid components with exceptions', + name: 'invalid description with exceptions', document: { components: { schemas: { diff --git a/tools/spectral/ipa/rulesets/IPA-117.yaml b/tools/spectral/ipa/rulesets/IPA-117.yaml index e2750ff485..3fae95e33f 100644 --- a/tools/spectral/ipa/rulesets/IPA-117.yaml +++ b/tools/spectral/ipa/rulesets/IPA-117.yaml @@ -5,6 +5,7 @@ functions: - IPA117HasDescription - IPA117DescriptionStartsWithUpperCase - IPA117DescriptionEndsWithPeriod + - IPA117DescriptionMustNotUseHtml rules: xgen-IPA-117-description: @@ -81,3 +82,28 @@ rules: - '$.components.parameters[*]' then: function: 'IPA117DescriptionEndsWithPeriod' + xgen-IPA-117-description-must-not-use-html: + description: | + Descriptions must not use raw HTML. + + ##### Implementation details + Rule checks the format of the descriptions for components: + - Info object + - Tags + - Operation objects + - Inline schema properties for operation object requests and responses + - Parameter objects (in operations and components) + - Schema properties + The rule validates that the description content does not include opening and/or closing HTML tags. + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-117-description-must-not-use-html' + severity: warn + given: + - '$.info' + - '$.tags[*]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace].parameters[*]' + - '$.paths[*][get,put,post,delete,options,head,patch,trace]..content..properties[*]' + - '$.components.schemas..properties[*]' + - '$.components.parameters[*]' + then: + function: 'IPA117DescriptionMustNotUseHtml' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index da9199092e..624f7c96e3 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -575,6 +575,21 @@ Rule checks the format of the description property in the following components: - Schema properties The rule ignores descriptions that end with `|`, i.e. inline markdown tables +#### xgen-IPA-117-description-must-not-use-html + + ![warn](https://img.shields.io/badge/warning-yellow) +Descriptions must not use raw HTML. + +##### Implementation details +Rule checks the format of the descriptions for components: + - Info object + - Tags + - Operation objects + - Inline schema properties for operation object requests and responses + - Parameter objects (in operations and components) + - Schema properties +The rule validates that the description content does not include opening and/or closing HTML tags. + ### IPA-123 diff --git a/tools/spectral/ipa/rulesets/functions/IPA117DescriptionMustNotUseHtml.js b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionMustNotUseHtml.js new file mode 100644 index 0000000000..6a315fa35a --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA117DescriptionMustNotUseHtml.js @@ -0,0 +1,51 @@ +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-117-description-must-not-use-html'; +const ERROR_MESSAGE = 'Descriptions must not use raw HTML.'; + +export default (input, opts, { path }) => { + // Ignore missing descriptions + if (!input['description']) { + return; + } + + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const errors = checkViolationsAndReturnErrors(input['description'], path); + if (errors.length !== 0) { + return collectAndReturnViolation(path, RULE_NAME, errors); + } + collectAdoption(path, RULE_NAME); +}; + +function checkViolationsAndReturnErrors(description, path) { + const htmlTagPattern = new RegExp(`<.*>.*`); + const htmlTagSelfClosingPattern = new RegExp(`<.*/>`); + const linkHtmlPattern = new RegExp(`.*`); + + try { + if (htmlTagPattern.test(description) || htmlTagSelfClosingPattern.test(description)) { + if (linkHtmlPattern.test(description)) { + return [ + { + path, + message: `${ERROR_MESSAGE} If you want to link to additional documentation, please use the externalDocumentation property (https://swagger.io/specification/#external-documentation-object).`, + }, + ]; + } + return [{ path, message: ERROR_MESSAGE }]; + } + return []; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +}