Skip to content

Commit 1a395af

Browse files
CLOUDP-304964: IPA-114: Errors (check for 401 and 403 codes for authorized endpoints)
1 parent 461762a commit 1a395af

File tree

4 files changed

+265
-0
lines changed

4 files changed

+265
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-114-authenticated-endpoints-have-auth-errors', [
5+
{
6+
name: 'valid authenticated endpoint with 401 and 403 responses',
7+
document: {
8+
paths: {
9+
'/resources': {
10+
get: {
11+
responses: {
12+
200: { description: 'Success' },
13+
401: { description: 'Unauthorized' },
14+
403: { description: 'Forbidden' },
15+
},
16+
},
17+
},
18+
},
19+
},
20+
errors: [],
21+
},
22+
{
23+
name: 'invalid authenticated endpoint missing both auth error responses',
24+
document: {
25+
paths: {
26+
'/resources': {
27+
get: {
28+
responses: {
29+
200: { description: 'Success' },
30+
},
31+
},
32+
},
33+
},
34+
},
35+
errors: [
36+
{
37+
code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors',
38+
message: 'Authenticated endpoint must define a 401 response.',
39+
path: ['paths', '/resources', 'get'],
40+
severity: DiagnosticSeverity.Warning,
41+
},
42+
{
43+
code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors',
44+
message: 'Authenticated endpoint must define a 403 response.',
45+
path: ['paths', '/resources', 'get'],
46+
severity: DiagnosticSeverity.Warning,
47+
},
48+
],
49+
},
50+
{
51+
name: 'invalid authenticated endpoint missing 401 response',
52+
document: {
53+
paths: {
54+
'/resources': {
55+
get: {
56+
responses: {
57+
200: { description: 'Success' },
58+
403: { description: 'Forbidden' },
59+
},
60+
},
61+
},
62+
},
63+
},
64+
errors: [
65+
{
66+
code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors',
67+
message: 'Authenticated endpoint must define a 401 response.',
68+
path: ['paths', '/resources', 'get'],
69+
severity: DiagnosticSeverity.Warning,
70+
},
71+
],
72+
},
73+
{
74+
name: 'invalid authenticated endpoint missing 403 response',
75+
document: {
76+
paths: {
77+
'/resources': {
78+
get: {
79+
responses: {
80+
200: { description: 'Success' },
81+
401: { description: 'Unauthorized' },
82+
},
83+
},
84+
},
85+
},
86+
},
87+
errors: [
88+
{
89+
code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors',
90+
message: 'Authenticated endpoint must define a 403 response.',
91+
path: ['paths', '/resources', 'get'],
92+
severity: DiagnosticSeverity.Warning,
93+
},
94+
],
95+
},
96+
{
97+
name: 'unauthenticated endpoint with empty security array',
98+
document: {
99+
paths: {
100+
'/resources': {
101+
get: {
102+
security: [],
103+
responses: {
104+
200: { description: 'Success' },
105+
},
106+
},
107+
},
108+
},
109+
},
110+
errors: [],
111+
},
112+
{
113+
name: 'unauthenticated endpoint with /unauth in path',
114+
document: {
115+
paths: {
116+
'/unauth/resources': {
117+
get: {
118+
responses: {
119+
200: { description: 'Success' },
120+
},
121+
},
122+
},
123+
},
124+
},
125+
errors: [],
126+
},
127+
{
128+
name: 'unauthenticated endpoint with both /unauth and empty security',
129+
document: {
130+
paths: {
131+
'/unauth/resources': {
132+
get: {
133+
security: [],
134+
responses: {
135+
200: { description: 'Success' },
136+
},
137+
},
138+
},
139+
},
140+
},
141+
errors: [],
142+
},
143+
{
144+
name: 'edge case with no responses object',
145+
document: {
146+
paths: {
147+
'/resources': {
148+
get: {},
149+
},
150+
},
151+
},
152+
errors: [
153+
{
154+
code: 'xgen-IPA-114-authenticated-endpoints-have-auth-errors',
155+
message: 'Authenticated endpoint must define a 401 and 403 responses.',
156+
path: ['paths', '/resources', 'get'],
157+
severity: DiagnosticSeverity.Warning,
158+
},
159+
],
160+
},
161+
]);

tools/spectral/ipa/rulesets/IPA-114.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
functions:
55
- IPA114ErrorResponsesReferToApiError
66
- IPA114ApiErrorHasBadRequestDetail
7+
- IPA114AuthenticatedEndpointsHaveAuthErrors
78

89
rules:
910
xgen-IPA-114-error-responses-refer-to-api-error:
@@ -32,3 +33,15 @@ rules:
3233
given: $.components.schemas.ApiError
3334
then:
3435
function: 'IPA114ApiErrorHasBadRequestDetail'
36+
xgen-IPA-114-authenticated-endpoints-have-auth-errors:
37+
description: |
38+
Authenticated endpoints must define 401 and 403 responses.
39+
40+
##### Implementation details
41+
This rule checks that all authenticated endpoints (those without explicit 'security: []'
42+
and not containing '/unauth' in the path) include 401 and 403 responses.
43+
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-114-authenticated-endpoints-have-auth-errors'
44+
severity: warn
45+
given: '$.paths[*][*]'
46+
then:
47+
function: 'IPA114AuthenticatedEndpointsHaveAuthErrors'

tools/spectral/ipa/rulesets/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,15 @@ Rule checks that:
627627
- Each field must include description and field properties
628628
- This rule does not allow exceptions
629629

630+
#### xgen-IPA-114-authenticated-endpoints-have-auth-errors
631+
632+
![warn](https://img.shields.io/badge/warning-yellow)
633+
Authenticated endpoints must define 401 and 403 responses.
634+
635+
##### Implementation details
636+
This rule checks that all authenticated endpoints (those without explicit 'security: []'
637+
and not containing '/unauth' in the path) include 401 and 403 responses.
638+
630639

631640

632641
### IPA-117
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { hasException } from './utils/exceptions.js';
2+
import {
3+
collectAdoption,
4+
collectAndReturnViolation,
5+
collectException,
6+
handleInternalError,
7+
} from './utils/collectionUtils.js';
8+
9+
const RULE_NAME = 'xgen-IPA-114-authenticated-endpoints-have-auth-errors';
10+
11+
/**
12+
* Validates that authenticated endpoints have 401 and 403 responses defined
13+
*
14+
* Endpoints are considered authenticated unless:
15+
* 1. They have explicit "security: []" set
16+
* 2. They contain "/unauth" in the path
17+
*
18+
* @param {object} input - The operation object to check
19+
* @param {object} _ - Rule options (unused)
20+
* @param {object} context - The context object containing path and document information
21+
*/
22+
export default (input, _, { path }) => {
23+
// Path components: [paths, pathName, methodName, ...]
24+
const pathName = path[1];
25+
26+
// Skip validation if the path contains 'unauth'
27+
if (pathName.includes('/unauth/')) {
28+
return;
29+
}
30+
31+
// Skip validation if security is explicitly set to empty array
32+
if (Array.isArray(input.security) && input.security.length === 0) {
33+
return;
34+
}
35+
36+
// Check for exception at operation level
37+
if (hasException(input, RULE_NAME)) {
38+
collectException(input, RULE_NAME, path);
39+
return;
40+
}
41+
42+
const errors = checkViolationsAndReturnErrors(input.responses, path);
43+
if (errors.length > 0) {
44+
return collectAndReturnViolation(path, RULE_NAME, errors);
45+
}
46+
47+
collectAdoption(path, RULE_NAME);
48+
};
49+
50+
function checkViolationsAndReturnErrors(responses, path) {
51+
try {
52+
const errors = [];
53+
54+
if (!responses) {
55+
return [
56+
{
57+
path,
58+
message: `Authenticated endpoint must define a 401 and 403 responses.`,
59+
},
60+
];
61+
}
62+
// Check for 401 Unauthorized response
63+
if (!responses['401']) {
64+
errors.push({
65+
path,
66+
message: `Authenticated endpoint must define a 401 response.`,
67+
});
68+
}
69+
70+
// Check for 403 Forbidden response
71+
if (!responses['403']) {
72+
errors.push({
73+
path,
74+
message: `Authenticated endpoint must define a 403 response.`,
75+
});
76+
}
77+
78+
return errors;
79+
} catch (e) {
80+
handleInternalError(RULE_NAME, path, e);
81+
}
82+
}

0 commit comments

Comments
 (0)