api-quality-spectral-ruleset 1.5.0-beta.2 → 1.5.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/apq-spectral.yaml CHANGED
@@ -34,6 +34,17 @@ functions:
34
34
  - apq-query-params-optional
35
35
  - apq-wso2-scopes-valid
36
36
  - apq-numeric-path-param
37
+ - apq-numeric-invalid-format
38
+ - apq-numeric-missing-format
39
+ - apq-numeric-well-defined-format
40
+ - apq-string-parameter-integrity
41
+ - apq-example-schema-types
42
+ - apq-standard-response-schema
43
+ - apq-allowed-http-verbs
44
+ - apq-url-naming-convention
45
+ - apq-mandatory-response-codes
46
+ - apq-rate-limit-response
47
+ - apq-forbidden-query-format
37
48
  extends:
38
49
  - spectral:asyncapi
39
50
  - spectral:oas
@@ -111,12 +122,14 @@ rules:
111
122
  function: truthy
112
123
  apiq:OAR008:
113
124
  description: "HTTP verbs not encouraged."
114
- message: "OAR008: Only the REST standard verbs are allowed (POST, GET, PUT, PATCH and DELETE)."
125
+ message: "{{error}}"
115
126
  documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR008.md"
116
127
  severity: error
117
- given: "$.paths[*][head,options,trace]"
128
+ given: "$.paths[*]"
118
129
  then:
119
- function: falsy
130
+ function: apq-allowed-http-verbs
131
+ functionOptions:
132
+ allowed-verbs: "get,post,put,delete,patch"
120
133
  apiq:OAR009:
121
134
  description: "Default request media type should be defined for operations."
122
135
  message: "OAR009: Default request media type is mandatory."
@@ -132,18 +145,21 @@ rules:
132
145
  given: "$.paths[*][get,post,put,patch,delete]"
133
146
  then:
134
147
  function: apq-response-media-type
148
+ functionOptions:
149
+ default-media-type: "application/json"
150
+ media-type-exceptions: "-"
135
151
  apiq:OAR011:
136
- description: "URLs should follow the specified naming convention: all literal path segments must be in kebab-case."
137
- message: "OAR011: The resource names with more than two words must be compliant with the standard naming convention (kebab-case)."
152
+ description: "URLs should follow the configured naming convention (default kebab-case): all literal path segments must comply."
153
+ message: "{{error}}"
138
154
  documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR011.md"
139
155
  severity: error
140
156
  given:
141
157
  - "$.paths[*]~"
142
158
  - "$.servers[*].url"
143
159
  then:
144
- function: pattern
160
+ function: apq-url-naming-convention
145
161
  functionOptions:
146
- match: "^(https?:\\/\\/[^\\/]+)?(\\/(?:[a-z0-9]+(?:-[a-z0-9]+)*|\\{[^}]+\\}))*\\/?$"
162
+ naming-convention: "kebab-case"
147
163
  apiq:OAR012:
148
164
  description: "Path params, query params, object names and property names should follow the configured naming convention. You can configure snake_case (default), kebab-case, camelCase or UpperCamelCase"
149
165
  message: "OAR012: Path params, query params, object names and property names must follow the configured naming convention."
@@ -196,29 +212,10 @@ rules:
196
212
  message: "OAR016: Numeric types must use a valid format for their type."
197
213
  documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR016.md"
198
214
  severity: error
199
- given: "$..[?(@ && (@.type=='number' || @.type=='integer' || (@.type && @.type.indexOf && (@.type.indexOf('number') > -1 || @.type.indexOf('integer') > -1))))]"
215
+ resolved: false
216
+ given: "$..[?(@ && @.type)]"
200
217
  then:
201
- function: schema
202
- functionOptions:
203
- schema:
204
- required: [format]
205
- allOf:
206
- - if:
207
- properties:
208
- type:
209
- const: integer
210
- then:
211
- properties:
212
- format:
213
- enum: [int32, int64, uint32, uint64]
214
- - if:
215
- properties:
216
- type:
217
- const: number
218
- then:
219
- properties:
220
- format:
221
- enum: [float, double, decimal]
218
+ function: apq-numeric-invalid-format
222
219
  apiq:OAR017:
223
220
  description: "Resource path should alternate static and parametrized parts."
224
221
  message: "OAR017: Resource path should alternate static and parametrized parts"
@@ -228,7 +225,7 @@ rules:
228
225
  then:
229
226
  function: apq-alternate-paths
230
227
  functionOptions:
231
- except: ["me", "get", "search", "delete"]
228
+ exclude_patterns: "get,me,search,delete"
232
229
  apiq:OAR018:
233
230
  description: Operation not recommended for resource path depending on HTTP verb
234
231
  message: "OAR018: Operation not recommended for resource path: {{path}} ({{verb}})"
@@ -368,46 +365,26 @@ rules:
368
365
  field: "headers.Location.schema.type"
369
366
  function: truthy
370
367
  apiq:OAR028:
371
- description: "$filter must be defined as a query parameter in all collection operations (excluding detail endpoints and health checks)."
372
- message: "OAR028: $filter must be defined as a query parameter in this operation."
368
+ description: "$filter must be defined as a query parameter in the collection GET operations selected by the configured paths."
369
+ message: "{{error}}"
373
370
  severity: warn
374
- given: "$.paths[?(!@property.match(/\\/me(\\/|$)/) && !@property.match(/\\/\\{[^}]+\\}$/) && !@property.match(/status|health|ping/))].get"
371
+ given: "$.paths"
375
372
  then:
376
- function: apq-has-filter-query-param
373
+ function: apq-collection-query-param-required
374
+ functionOptions:
375
+ parameterName: "$filter"
376
+ paths: "/examples"
377
+ pathValidationStrategy: "/include"
377
378
  apiq:OAR029:
378
379
  description: "A response not compliant with the standard may cause application issues."
379
- message: "OAR029: A response not compliant with the standard may cause application issues."
380
+ message: "{{error}}"
380
381
  severity: error
381
- given: "$.components.schemas.errorResponse"
382
+ given: "$.paths"
382
383
  then:
383
- function: schema
384
+ function: apq-standard-response-schema
384
385
  functionOptions:
385
- schema:
386
- type: object
387
- properties:
388
- properties:
389
- type: object
390
- properties:
391
- error:
392
- type: object
393
- properties:
394
- properties:
395
- type: object
396
- properties:
397
- code:
398
- type: string
399
- message:
400
- type: string
401
- details:
402
- type: array
403
- httpStatus:
404
- type: integer
405
- required:
406
- - code
407
- - message
408
- - httpStatus
409
- required:
410
- - properties
386
+ response-schema: '{"type":"object","properties":{"status":{"type":"object","properties":{"code":{"type":"integer"},"description":{"type":"string"},"internal_code":{"type":"string"},"errors":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}}}},"required":["code"]},"payload":{"type":"any"}},"required":["status","payload"]}'
387
+ path-exclusions: "/status"
411
388
  apiq:OAR030:
412
389
  description: "The configured status endpoint must be declared with the configured HTTP method."
413
390
  message: "OAR030: The required status endpoint is not declared or does not have the required method."
@@ -435,13 +412,13 @@ rules:
435
412
  validateProperty: true
436
413
  apiq:OAR032:
437
414
  description: "Ambiguous path parts not encouraged."
438
- message: "OAR032: When reading a URL, it must be possible to interpret which resources it is referring to in a concrete way. Ambiguous names in resources must be avoided."
415
+ message: "{{error}}"
439
416
  severity: error
440
417
  given: "$.paths.*~"
441
418
  then:
442
419
  function: apq-check-ambiguous-path
443
420
  functionOptions:
444
- ambiguous-words: "elements,instances,resources,values,terms,objects,items"
421
+ ambiguous-names: "elementos,instancias,recursos,valores,terminos,objetos,articulos,elements,instances,resources,values,terms,objects,items"
445
422
  apiq:OAR033:
446
423
  description: "Request operation parameters must not include forbidden headers (Accept, Content-Type, Authorization). Note: This validates REQUEST headers, not response headers (see OAR053/114 for response header validation)."
447
424
  message: "OAR033: The request header parameter '{{value}}' is not allowed in operations."
@@ -688,10 +665,10 @@ rules:
688
665
  message: "OAR052: Numeric types requires a format"
689
666
  documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR052.md"
690
667
  severity: warn
691
- given: "$..[?(@ && (@.type == 'integer' || @.type == 'number' || (@.type && @.type.indexOf && (@.type.indexOf('integer') > -1 || @.type.indexOf('number') > -1))))]"
668
+ resolved: false
669
+ given: "$..[?(@ && @.type)]"
692
670
  then:
693
- field: format
694
- function: truthy
671
+ function: apq-numeric-missing-format
695
672
  apiq:OAR053:
696
673
  description: "Response headers for API observability and tracing must be defined (excluding 204 responses and health endpoints)."
697
674
  message: "OAR053: Response must include mandatory headers and exclude forbidden headers."
@@ -732,75 +709,59 @@ rules:
732
709
  path-exclusions: "/status"
733
710
  apiq:OAR061:
734
711
  description: "Ensure get have mandatory response codes"
735
- message: "OAR061: Ensure get have the mandatory response codes"
712
+ message: "{{error}}"
736
713
  severity: error
737
- given: "$.paths[?(@property !== '/status')].get.responses"
714
+ given: "$.paths[*][get]"
738
715
  then:
739
- function: schema
716
+ function: apq-mandatory-response-codes
740
717
  functionOptions:
741
- schema:
742
- oneOf:
743
- - required: ['200']
744
- - required: ['202']
745
- - required: ['206']
746
- not:
747
- required: ['201', '204']
718
+ mandatory-response-codes: "200, 202, 206"
719
+ paths: "/status, /another"
720
+ pathValidationStrategy: "/exclude"
748
721
  apiq:OAR062:
749
722
  description: "Ensure post have mandatory response codes"
750
- message: "OAR062: Ensure post have the mandatory response codes"
723
+ message: "{{error}}"
751
724
  severity: error
752
- given: "$.paths[?(@property !== '/status')].post.responses"
725
+ given: "$.paths[*][post]"
753
726
  then:
754
- function: schema
727
+ function: apq-mandatory-response-codes
755
728
  functionOptions:
756
- schema:
757
- oneOf:
758
- - required: ['200']
759
- - required: ['201']
760
- - required: ['202']
761
- - required: ['204']
762
- - required: ['206']
729
+ mandatory-response-codes: "200, 201, 202, 204, 206"
730
+ paths: "/status, /another"
731
+ pathValidationStrategy: "/exclude"
763
732
  apiq:OAR063:
764
733
  description: "Ensure put have mandatory response codes"
765
- message: "OAR063: Ensure put have the mandatory response codes"
734
+ message: "{{error}}"
766
735
  severity: error
767
- given: "$.paths[?(@property !== '/status')].put.responses"
736
+ given: "$.paths[*][put]"
768
737
  then:
769
- function: schema
738
+ function: apq-mandatory-response-codes
770
739
  functionOptions:
771
- schema:
772
- oneOf:
773
- - required: ['200']
774
- - required: ['202']
775
- - required: ['204']
776
- - required: ['206']
740
+ mandatory-response-codes: "200, 202, 204, 206"
741
+ paths: "/status, /another"
742
+ pathValidationStrategy: "/exclude"
777
743
  apiq:OAR064:
778
744
  description: "Ensure patch have mandatory response codes"
779
- message: "OAR064: Ensure patch have the mandatory response codes"
745
+ message: "{{error}}"
780
746
  severity: error
781
- given: "$.paths[?(@property !== '/status')].patch.responses"
747
+ given: "$.paths[*][patch]"
782
748
  then:
783
- function: schema
749
+ function: apq-mandatory-response-codes
784
750
  functionOptions:
785
- schema:
786
- oneOf:
787
- - required: ['200']
788
- - required: ['202']
789
- - required: ['204']
790
- - required: ['206']
751
+ mandatory-response-codes: "200, 202, 204, 206"
752
+ paths: "/status, /another"
753
+ pathValidationStrategy: "/exclude"
791
754
  apiq:OAR065:
792
755
  description: "Ensure delete have mandatory response codes"
793
- message: "OAR065: Ensure delete have the mandatory response codes"
756
+ message: "{{error}}"
794
757
  severity: error
795
- given: "$.paths[?(@property !== '/status')].delete.responses"
758
+ given: "$.paths[*][delete]"
796
759
  then:
797
- function: schema
760
+ function: apq-mandatory-response-codes
798
761
  functionOptions:
799
- schema:
800
- oneOf:
801
- - required: ['200']
802
- - required: ['202']
803
- - required: ['204']
762
+ mandatory-response-codes: "200, 202, 204"
763
+ paths: "/status, /another"
764
+ pathValidationStrategy: "/exclude"
804
765
  apiq:OAR066:
805
766
  description: "RequestBody and Responses schema property names must be compliant with the snake_case naming convention."
806
767
  message: "OAR066: RequestBody and Responses schema property names must be compliant with the snake_case naming convention."
@@ -871,10 +832,12 @@ rules:
871
832
  description: "API should include a 429 response to indicate rate limiting, except for health check paths like /status, /health, /health-check, /ping, /liveness, /readiness."
872
833
  message: "OAR073: API should include a 429 response to indicate rate limiting."
873
834
  severity: error
874
- given: "$.paths[?(@property !== '/status' && @property !== '/health' && @property !== '/health-check' && @property !== '/ping' && @property !== '/liveness' && @property !== '/readiness')][*].responses"
835
+ given: "$.paths[*][get,post,put,patch,delete]"
875
836
  then:
876
- field: "429"
877
- function: truthy
837
+ function: apq-rate-limit-response
838
+ functionOptions:
839
+ paths: "/status, /health, /health-check, /ping, /liveness, /readiness"
840
+ pathValidationStrategy: "/exclude"
878
841
  apiq:OAR074:
879
842
  description: "Numeric parameters should define minimum and maximum, or a format restriction."
880
843
  message: "OAR074: Numeric parameter should define both 'minimum' and 'maximum', or a 'format' restriction."
@@ -889,47 +852,25 @@ rules:
889
852
  description: "String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction."
890
853
  message: "OAR075: String parameters should have minLength, maxLength, pattern, or enum restriction."
891
854
  severity: error
892
- given: "$.paths[*][get,post,put,patch,delete].parameters[?(@ && @.schema && (@.schema.type == 'string' || (@.schema.type && @.schema.type.indexOf && @.schema.type.indexOf('string') > -1)) && @.in == 'path')]"
855
+ resolved: false
856
+ given:
857
+ - "$.paths[*][*].parameters[*]"
858
+ - "$.paths[*].parameters[*]"
859
+ - "$.components.parameters[*]"
860
+ - "$.parameters[*]"
893
861
  then:
894
- function: schema
862
+ function: apq-string-parameter-integrity
895
863
  functionOptions:
896
- schema:
897
- type: object
898
- properties:
899
- schema:
900
- anyOf:
901
- - required: ["minLength"]
902
- - required: ["maxLength"]
903
- - required: ["pattern"]
904
- - required: ["enum"]
864
+ parameter_integrity: "minLength,maxLength,pattern,enum"
905
865
  apiq:OAR076:
906
866
  description: "Schema should use well-defined type and format."
907
867
  message: "OAR076: Schema should use well-defined type and format."
908
868
  documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR076.md"
909
869
  severity: error
910
- given: "$..[?(@ && (@.type=='number' || @.type=='integer' || (@.type && @.type.indexOf && (@.type.indexOf('number') > -1 || @.type.indexOf('integer') > -1))))]"
870
+ resolved: false
871
+ given: "$..[?(@ && @.type)]"
911
872
  then:
912
- function: schema
913
- functionOptions:
914
- schema:
915
- required: [format]
916
- allOf:
917
- - if:
918
- properties:
919
- type:
920
- const: integer
921
- then:
922
- properties:
923
- format:
924
- enum: [int32, int64]
925
- - if:
926
- properties:
927
- type:
928
- const: number
929
- then:
930
- properties:
931
- format:
932
- enum: [float, double]
873
+ function: apq-numeric-well-defined-format
933
874
  apiq:OAR077:
934
875
  description: "All parameters in query must be snake_case."
935
876
  message: "OAR077: All parameters in query must be snake_case."
@@ -957,7 +898,8 @@ rules:
957
898
  then:
958
899
  function: apq-require-response-on-path-params
959
900
  functionOptions:
960
- response: "404"
901
+ paths: "/status"
902
+ pathValidationStrategy: "/exclude"
961
903
  apiq:OAR080:
962
904
  description: "The security scheme must be among those allowed by the organization and must be complete."
963
905
  message: "OAR080: The security scheme '{{property}}' must be among those allowed by the organization and must be complete."
@@ -995,15 +937,16 @@ rules:
995
937
  functionOptions:
996
938
  notMatch: "^(email|password)$"
997
939
  apiq:OAR084:
998
- description: "The format password should not pass through the querystring."
999
- message: "OAR084: The format password should not be used in querystring parameters."
940
+ description: "Some formats should not pass through this querystring."
941
+ message: "{{error}}"
1000
942
  severity: error
1001
943
  given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')]"
1002
944
  then:
1003
- field: "schema.format"
1004
- function: pattern
945
+ function: apq-forbidden-query-format
1005
946
  functionOptions:
1006
- notMatch: "^password$"
947
+ forbidden-query-formats: "password"
948
+ paths: "/examples"
949
+ pathValidationStrategy: "/include"
1007
950
  apiq:OAR085:
1008
951
  description: "The OpenAPI version must be one of: 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.1.0, 3.1.1, 3.1.2, 3.2.0."
1009
952
  message: "{{error}}"
@@ -1199,19 +1142,10 @@ rules:
1199
1142
  description: "The schemas should match the provided examples."
1200
1143
  message: "OAR108: Schema does not match the provided example."
1201
1144
  severity: "error"
1202
- given:
1203
- - "$.paths['/item'].get.responses['200'].content['application/json'].example"
1204
- - "$.paths['/item'].get.responses['200'].examples['application/json']"
1145
+ resolved: false
1146
+ given: "$.paths[*][*].responses[*]"
1205
1147
  then:
1206
- - function: "schema"
1207
- functionOptions:
1208
- schema:
1209
- type: "object"
1210
- properties:
1211
- id:
1212
- type: "integer"
1213
- nombre:
1214
- type: "string"
1148
+ function: apq-example-schema-types
1215
1149
  apiq:OAR109:
1216
1150
  description: "Use default response instead of directly specifying 5XX codes."
1217
1151
  message: "OAR109: Use default response instead of specifying 5XX codes directly."
@@ -1265,9 +1199,8 @@ rules:
1265
1199
  message: "OAR115: All fields in the required array must be defined in schema properties."
1266
1200
  documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR115.md"
1267
1201
  severity: warn
1268
- given:
1269
- - "$.components.schemas[*]"
1270
- - "$.definitions[*]"
1202
+ resolved: false
1203
+ given: "$..[?(@ && @.required)]"
1271
1204
  then:
1272
1205
  function: apq-required-fields-exist
1273
1206
  apiq:OAR116:
@@ -0,0 +1,31 @@
1
+ const HTTP_VERBS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
2
+
3
+ /**
4
+ * @param {object} given
5
+ * @param {object} options
6
+ * @param {import('@stoplight/spectral-core').RulesetFunctionContext} context
7
+ */
8
+ module.exports = (given, options, context) => {
9
+ if (!given || typeof given !== 'object') {
10
+ return [];
11
+ }
12
+
13
+ const allowedVerbs = ((options && options['allowed-verbs']) || '')
14
+ .split(',')
15
+ .map((verb) => verb.trim().toLowerCase())
16
+ .filter(Boolean);
17
+
18
+ const basePath = context.path || [];
19
+ const errors = [];
20
+
21
+ HTTP_VERBS.forEach((verb) => {
22
+ if (Object.prototype.hasOwnProperty.call(given, verb) && !allowedVerbs.includes(verb)) {
23
+ errors.push({
24
+ message: `HTTP verb '${verb}' is not encouraged. Only the following verbs are allowed: ${allowedVerbs.join(', ')}.`,
25
+ path: [...basePath, verb]
26
+ });
27
+ }
28
+ });
29
+
30
+ return errors;
31
+ };
@@ -1,18 +1,22 @@
1
-
2
1
  const isVariable = (part) => {
3
2
  return (part.startsWith('{') && part.endsWith('}'));
4
3
  }
5
4
 
6
- module.exports = (given, { except }, context) => {
5
+ module.exports = (given, options, context) => {
7
6
  const result = [];
8
7
  const paths = given || [];
9
8
  if (paths.length === 0) return result;
10
9
 
10
+ const excludePatterns = ((options && options['exclude_patterns']) || '')
11
+ .split(',')
12
+ .map((pattern) => pattern.trim())
13
+ .filter(Boolean);
14
+
11
15
  const parts = paths.substr(1).split('/').filter(p => p.length > 0);
12
16
  if (parts.length === 0) return result;
13
17
  const firstPart = parts.shift();
14
18
  let previousIsVar;
15
- if (except && except.includes(firstPart)) {
19
+ if (excludePatterns.includes(firstPart)) {
16
20
  previousIsVar = true;
17
21
  } else if (isVariable(firstPart)) {
18
22
  return [{ message: context.rule.message }];
@@ -21,7 +25,7 @@ module.exports = (given, { except }, context) => {
21
25
  }
22
26
 
23
27
  for (const part of parts) {
24
- if (except && except.includes(part)) {
28
+ if (excludePatterns.includes(part)) {
25
29
  previousIsVar = true;
26
30
  continue;
27
31
  }
@@ -1,52 +1,38 @@
1
+ const PARAM_REGEX = /\{[^}{]*}/g;
2
+
3
+ const DEFAULT_AMBIGUOUS_NAMES =
4
+ 'elementos,instancias,recursos,valores,terminos,objetos,articulos,elements,instances,resources,values,terms,objects,items';
5
+
1
6
  /**
2
- * Validates that URL path segments use clear, unambiguous names
3
7
  * @param {string} given - The path key (e.g. "/users/{id}/items")
4
- * @param {object} options - Function options
5
- * @param {string} options.ambiguous-words - Comma-separated list of ambiguous words
8
+ * @param {object} options
9
+ * @param {string} options['ambiguous-names']
6
10
  * @param {import('@stoplight/spectral-core').RulesetFunctionContext} context
7
11
  */
8
12
  module.exports = (given, options, context) => {
9
- const results = [];
10
-
11
13
  if (typeof given !== 'string') {
12
- return results;
14
+ return [];
13
15
  }
14
16
 
15
- // Default ambiguous words (from the original regex)
16
- const defaultAmbiguousWords = [
17
- 'elements', 'instances', 'resources', 'values', 'terms', 'objects', 'items'
18
- ];
19
-
20
- // Parse custom ambiguous words from options if provided
21
- let ambiguousWords = defaultAmbiguousWords;
22
- if (options && options['ambiguous-words']) {
23
- ambiguousWords = options['ambiguous-words']
17
+ const forbiddenValues = new Set(
18
+ ((options && options['ambiguous-names']) || DEFAULT_AMBIGUOUS_NAMES)
24
19
  .split(',')
25
- .map(w => w.trim().toLowerCase())
26
- .filter(Boolean);
27
- }
20
+ .map((value) => value.trim())
21
+ .filter(Boolean)
22
+ );
28
23
 
29
- // Split path into segments and filter out parameters
30
- const segments = given
31
- .split('/')
32
- .filter(segment => {
33
- // Keep only static segments (not empty, not {param})
34
- return segment.length > 0 && !segment.startsWith('{');
35
- });
24
+ const pathWithoutParams = given.replace(PARAM_REGEX, '');
25
+ const pathParts = pathWithoutParams.split('/').filter(Boolean);
26
+ const forbidden = pathParts.filter((part) => forbiddenValues.has(part));
36
27
 
37
- // Check each segment for ambiguous words
38
- for (const segment of segments) {
39
- const lowerSegment = segment.toLowerCase();
40
- for (const word of ambiguousWords) {
41
- if (lowerSegment.includes(word)) {
42
- results.push({
43
- message: `${context.rule.message || 'OAR032'}: Path segment '${segment}' is ambiguous. Avoid using words like '${word}' in resource names.`,
44
- path: context.path
45
- });
46
- break; // Report only once per segment
47
- }
48
- }
28
+ if (forbidden.length === 0) {
29
+ return [];
49
30
  }
50
31
 
51
- return results;
32
+ return [
33
+ {
34
+ message: `Ambiguous name(s) found in path: ${forbidden.join(', ')}.`,
35
+ path: context.path
36
+ }
37
+ ];
52
38
  };