From 1a395af10addc193530fc29a3f9e79e6ec7ec845 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Mon, 31 Mar 2025 16:34:14 +0100 Subject: [PATCH 1/2] CLOUDP-304964: IPA-114: Errors (check for 401 and 403 codes for authorized endpoints) --- ...thenticatedEndpointsHaveAuthErrors.test.js | 161 ++++++++++++++++++ tools/spectral/ipa/rulesets/IPA-114.yaml | 13 ++ tools/spectral/ipa/rulesets/README.md | 9 + ...114AuthenticatedEndpointsHaveAuthErrors.js | 82 +++++++++ 4 files changed, 265 insertions(+) create mode 100644 tools/spectral/ipa/__tests__/IPA114AuthenticatedEndpointsHaveAuthErrors.test.js create mode 100644 tools/spectral/ipa/rulesets/functions/IPA114AuthenticatedEndpointsHaveAuthErrors.js diff --git a/tools/spectral/ipa/__tests__/IPA114AuthenticatedEndpointsHaveAuthErrors.test.js b/tools/spectral/ipa/__tests__/IPA114AuthenticatedEndpointsHaveAuthErrors.test.js new file mode 100644 index 0000000000..a514ab9f81 --- /dev/null +++ b/tools/spectral/ipa/__tests__/IPA114AuthenticatedEndpointsHaveAuthErrors.test.js @@ -0,0 +1,161 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-114-authenticated-endpoints-have-auth-errors', [ + { + name: 'valid authenticated endpoint with 401 and 403 responses', + document: { + paths: { + '/resources': { + get: { + responses: { + 200: { description: 'Success' }, + 401: { description: 'Unauthorized' }, + 403: { description: 'Forbidden' }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'invalid authenticated endpoint missing both auth error responses', + document: { + paths: { + '/resources': { + get: { + responses: { + 200: { description: 'Success' }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors', + message: 'Authenticated endpoint must define a 401 response.', + path: ['paths', '/resources', 'get'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors', + message: 'Authenticated endpoint must define a 403 response.', + path: ['paths', '/resources', 'get'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid authenticated endpoint missing 401 response', + document: { + paths: { + '/resources': { + get: { + responses: { + 200: { description: 'Success' }, + 403: { description: 'Forbidden' }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors', + message: 'Authenticated endpoint must define a 401 response.', + path: ['paths', '/resources', 'get'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'invalid authenticated endpoint missing 403 response', + document: { + paths: { + '/resources': { + get: { + responses: { + 200: { description: 'Success' }, + 401: { description: 'Unauthorized' }, + }, + }, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors', + message: 'Authenticated endpoint must define a 403 response.', + path: ['paths', '/resources', 'get'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, + { + name: 'unauthenticated endpoint with empty security array', + document: { + paths: { + '/resources': { + get: { + security: [], + responses: { + 200: { description: 'Success' }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'unauthenticated endpoint with /unauth in path', + document: { + paths: { + '/unauth/resources': { + get: { + responses: { + 200: { description: 'Success' }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'unauthenticated endpoint with both /unauth and empty security', + document: { + paths: { + '/unauth/resources': { + get: { + security: [], + responses: { + 200: { description: 'Success' }, + }, + }, + }, + }, + }, + errors: [], + }, + { + name: 'edge case with no responses object', + document: { + paths: { + '/resources': { + get: {}, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors', + message: 'Authenticated endpoint must define a 401 and 403 responses.', + path: ['paths', '/resources', 'get'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, +]); diff --git a/tools/spectral/ipa/rulesets/IPA-114.yaml b/tools/spectral/ipa/rulesets/IPA-114.yaml index e6fc26815f..8f0df3d6e1 100644 --- a/tools/spectral/ipa/rulesets/IPA-114.yaml +++ b/tools/spectral/ipa/rulesets/IPA-114.yaml @@ -4,6 +4,7 @@ functions: - IPA114ErrorResponsesReferToApiError - IPA114ApiErrorHasBadRequestDetail + - IPA114AuthenticatedEndpointsHaveAuthErrors rules: xgen-IPA-114-error-responses-refer-to-api-error: @@ -32,3 +33,15 @@ rules: given: $.components.schemas.ApiError then: function: 'IPA114ApiErrorHasBadRequestDetail' + xgen-IPA-114-authenticated-endpoints-have-auth-errors: + description: | + Authenticated endpoints must define 401 and 403 responses. + + ##### Implementation details + This rule checks that all authenticated endpoints (those without explicit 'security: []' + and not containing '/unauth' in the path) include 401 and 403 responses. + message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-114-authenticated-endpoints-have-auth-errors' + severity: warn + given: '$.paths[*][*]' + then: + function: 'IPA114AuthenticatedEndpointsHaveAuthErrors' diff --git a/tools/spectral/ipa/rulesets/README.md b/tools/spectral/ipa/rulesets/README.md index 8f1c4a791f..550d7f2c4d 100644 --- a/tools/spectral/ipa/rulesets/README.md +++ b/tools/spectral/ipa/rulesets/README.md @@ -627,6 +627,15 @@ Rule checks that: - Each field must include description and field properties - This rule does not allow exceptions +#### xgen-IPA-114-authenticated-endpoints-have-auth-errors + + ![warn](https://img.shields.io/badge/warning-yellow) +Authenticated endpoints must define 401 and 403 responses. + +##### Implementation details +This rule checks that all authenticated endpoints (those without explicit 'security: []' +and not containing '/unauth' in the path) include 401 and 403 responses. + ### IPA-117 diff --git a/tools/spectral/ipa/rulesets/functions/IPA114AuthenticatedEndpointsHaveAuthErrors.js b/tools/spectral/ipa/rulesets/functions/IPA114AuthenticatedEndpointsHaveAuthErrors.js new file mode 100644 index 0000000000..fd3a809a36 --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/IPA114AuthenticatedEndpointsHaveAuthErrors.js @@ -0,0 +1,82 @@ +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, + handleInternalError, +} from './utils/collectionUtils.js'; + +const RULE_NAME = 'xgen-IPA-114-authenticated-endpoints-have-auth-errors'; + +/** + * Validates that authenticated endpoints have 401 and 403 responses defined + * + * Endpoints are considered authenticated unless: + * 1. They have explicit "security: []" set + * 2. They contain "/unauth" in the path + * + * @param {object} input - The operation object to check + * @param {object} _ - Rule options (unused) + * @param {object} context - The context object containing path and document information + */ +export default (input, _, { path }) => { + // Path components: [paths, pathName, methodName, ...] + const pathName = path[1]; + + // Skip validation if the path contains 'unauth' + if (pathName.includes('/unauth/')) { + return; + } + + // Skip validation if security is explicitly set to empty array + if (Array.isArray(input.security) && input.security.length === 0) { + return; + } + + // Check for exception at operation level + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); + return; + } + + const errors = checkViolationsAndReturnErrors(input.responses, path); + if (errors.length > 0) { + return collectAndReturnViolation(path, RULE_NAME, errors); + } + + collectAdoption(path, RULE_NAME); +}; + +function checkViolationsAndReturnErrors(responses, path) { + try { + const errors = []; + + if (!responses) { + return [ + { + path, + message: `Authenticated endpoint must define a 401 and 403 responses.`, + }, + ]; + } + // Check for 401 Unauthorized response + if (!responses['401']) { + errors.push({ + path, + message: `Authenticated endpoint must define a 401 response.`, + }); + } + + // Check for 403 Forbidden response + if (!responses['403']) { + errors.push({ + path, + message: `Authenticated endpoint must define a 403 response.`, + }); + } + + return errors; + } catch (e) { + handleInternalError(RULE_NAME, path, e); + } +} From 1c13a3d4205f7882df7c00bf8c61ae7ae2cf13af Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Mon, 31 Mar 2025 17:22:44 +0100 Subject: [PATCH 2/2] address the comments --- tools/spectral/ipa/rulesets/IPA-114.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/spectral/ipa/rulesets/IPA-114.yaml b/tools/spectral/ipa/rulesets/IPA-114.yaml index 8f0df3d6e1..fe630188a6 100644 --- a/tools/spectral/ipa/rulesets/IPA-114.yaml +++ b/tools/spectral/ipa/rulesets/IPA-114.yaml @@ -42,6 +42,6 @@ rules: and not containing '/unauth' in the path) include 401 and 403 responses. message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-114-authenticated-endpoints-have-auth-errors' severity: warn - given: '$.paths[*][*]' + given: '$.paths[*][get,put,post,delete,options,head,patch,trace]' then: function: 'IPA114AuthenticatedEndpointsHaveAuthErrors'