Repository: Redocly/redocly-cli (ts)

Issue:
no-server-example.com
**Describe the bug**

https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/oas3/no-server-example.com.ts#L6

This only detects an exact match `localhost` or `example.com` but the intention is for it to match https://example.com and http://foo.example.com too (etc...).

**To Reproduce**


**Expected behavior**

Match: https://example.com and https://foo.example.com.

Perhaps we can change it to this:
```js
const pattern = /^(.*[\./])?(example\.com|localhost)/;

if (pattern.test(server.url)) {
    // ... rest of code ...
}
```


<!-- A clear and concise description of what you expected to happen. -->

**Logs**

n/a

**OpenAPI description**

n/a


**Redocly Version(s)**

Latest (1.2)

**`Node.js` Version(s)**

18

**Additional context**

<!-- Add any other context about the problem here. -->


Pull request description:
fix: improve detection accuracy on no-server-example.com rule
## What/Why/How?

Improves the detection accuracy for the `no-server-example.com` rule.

## Reference

Closes #1269 

## Testing

Added tests and had to adjust multiple tests which used variants of example.com.

## Screenshots (optional)

## Check yourself

- [ ] Code is linted
- [ ] Tested with redoc/reference-docs/workflows (internal)
- [x] All new/updated code is covered with tests

## Security

- [x] Security impact of change has been considered
- [x] Code follows company security practices and guidelines


Patch:
diff --git a/.changeset/plenty-ears-allow.md b/.changeset/plenty-ears-allow.md
new file mode 100644
index 00000000..ffc1d0b0
--- /dev/null
+++ b/.changeset/plenty-ears-allow.md
@@ -0,0 +1,6 @@
+---
+'@redocly/openapi-core': patch
+'@redocly/cli': patch
+---
+
+Fixed pattern for no-server-example.com rule to improve detection accuracy.
diff --git a/packages/core/src/rules/oas3/no-server-example.com.ts b/packages/core/src/rules/oas3/no-server-example.com.ts
index 9b7999b7..955bbb80 100644
--- a/packages/core/src/rules/oas3/no-server-example.com.ts
+++ b/packages/core/src/rules/oas3/no-server-example.com.ts
@@ -3,9 +3,10 @@ import { Oas3Rule } from '../../visitors';
 export const NoServerExample: Oas3Rule = () => {
   return {
     Server(server, { report, location }) {
-      if (['example.com', 'localhost'].indexOf(server.url) !== -1) {
+      const pattern = /^(.*[\/.])?(example\.com|localhost)([\/:?].*|$)/;
+      if (server.url && pattern.test(server.url)) {
         report({
-          message: 'Server `url` should not point at example.com.',
+          message: 'Server `url` should not point to example.com or localhost.',
           location: location.child(['url']),
         });
       }


Test patch:
diff --git a/__tests__/build-docs/build-docs-with-config-option/nested/openapi.yaml b/__tests__/build-docs/build-docs-with-config-option/nested/openapi.yaml
index aa98c7e1..4375c769 100644
--- a/__tests__/build-docs/build-docs-with-config-option/nested/openapi.yaml
+++ b/__tests__/build-docs/build-docs-with-config-option/nested/openapi.yaml
@@ -1,6 +1,6 @@
 openapi: 3.1.0
 servers:
-  - url: http://localhost
+  - url: http://redocly-example.com
 info:
   title: Sample API
   version: 1.0.0
diff --git a/__tests__/bundle/bundle-lint-format/openapi.yaml b/__tests__/bundle/bundle-lint-format/openapi.yaml
index 2e8fa09c..0bf93633 100644
--- a/__tests__/bundle/bundle-lint-format/openapi.yaml
+++ b/__tests__/bundle/bundle-lint-format/openapi.yaml
@@ -1,6 +1,6 @@
 openapi: 3.1.0
 servers:
-  - url: https://api.example.com/v1
+  - url: https://api.redocly-example.com/v1
 security: []
 info:
   title: Title
diff --git a/__tests__/bundle/primitive-types/openapi.yaml b/__tests__/bundle/primitive-types/openapi.yaml
index 24e9d2ef..1e7cb6bf 100644
--- a/__tests__/bundle/primitive-types/openapi.yaml
+++ b/__tests__/bundle/primitive-types/openapi.yaml
@@ -1,6 +1,6 @@
 openapi: 3.1.0
 servers:
-  - url: https://api.example.com/v1
+  - url: https://api.redocly-example.com/v1
 security: []
 
 info:
diff --git a/__tests__/bundle/primitive-types/snapshot.js b/__tests__/bundle/primitive-types/snapshot.js
index 0f752896..d2f057f2 100644
--- a/__tests__/bundle/primitive-types/snapshot.js
+++ b/__tests__/bundle/primitive-types/snapshot.js
@@ -10,7 +10,7 @@ info:
   description: Description
   version: 1.0.0
 servers:
-  - url: https://api.example.com/v1
+  - url: https://api.redocly-example.com/v1
 security: []
 paths:
   /my_post:
diff --git a/__tests__/commands.test.ts b/__tests__/commands.test.ts
index 6563433a..a94a8345 100644
--- a/__tests__/commands.test.ts
+++ b/__tests__/commands.test.ts
@@ -397,7 +397,7 @@ describe('E2E', () => {
       (<any>expect(result)).toMatchSpecificSnapshot(join(testPath, 'snapshot.js'));
 
       expect(fs.existsSync(join(testPath, 'nested/redoc-static.html'))).toEqual(true);
-      expect(fs.statSync(join(testPath, 'nested/redoc-static.html')).size).toEqual(32996);
+      expect(fs.statSync(join(testPath, 'nested/redoc-static.html')).size).toEqual(33016);
     });
   });
 });
diff --git a/__tests__/join/decorate/bar.yaml b/__tests__/join/decorate/bar.yaml
index 15c68322..8f24d577 100644
--- a/__tests__/join/decorate/bar.yaml
+++ b/__tests__/join/decorate/bar.yaml
@@ -4,7 +4,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 paths:
   /users/{userId}:
     parameters:
diff --git a/__tests__/join/decorate/foo.yaml b/__tests__/join/decorate/foo.yaml
index 82f43ca3..2ecbd051 100644
--- a/__tests__/join/decorate/foo.yaml
+++ b/__tests__/join/decorate/foo.yaml
@@ -4,7 +4,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 paths:
   /users/{userId}/orders/{orderId}:
     parameters:
diff --git a/__tests__/join/decorate/snapshot.js b/__tests__/join/decorate/snapshot.js
index 46d985db..90d47b88 100644
--- a/__tests__/join/decorate/snapshot.js
+++ b/__tests__/join/decorate/snapshot.js
@@ -8,7 +8,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 tags:
   - name: bar_other
     x-displayName: other
diff --git a/__tests__/join/ignore-decorators/bar.yaml b/__tests__/join/ignore-decorators/bar.yaml
index 15c68322..8f24d577 100644
--- a/__tests__/join/ignore-decorators/bar.yaml
+++ b/__tests__/join/ignore-decorators/bar.yaml
@@ -4,7 +4,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 paths:
   /users/{userId}:
     parameters:
diff --git a/__tests__/join/ignore-decorators/foo.yaml b/__tests__/join/ignore-decorators/foo.yaml
index 82f43ca3..2ecbd051 100644
--- a/__tests__/join/ignore-decorators/foo.yaml
+++ b/__tests__/join/ignore-decorators/foo.yaml
@@ -4,7 +4,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 paths:
   /users/{userId}/orders/{orderId}:
     parameters:
diff --git a/__tests__/join/ignore-decorators/snapshot.js b/__tests__/join/ignore-decorators/snapshot.js
index ea428b97..a331210b 100644
--- a/__tests__/join/ignore-decorators/snapshot.js
+++ b/__tests__/join/ignore-decorators/snapshot.js
@@ -8,7 +8,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 tags:
   - name: foo_other
     x-displayName: other
diff --git a/__tests__/join/references-in-parameters/bar.yaml b/__tests__/join/references-in-parameters/bar.yaml
index b55e685e..ad6263f8 100644
--- a/__tests__/join/references-in-parameters/bar.yaml
+++ b/__tests__/join/references-in-parameters/bar.yaml
@@ -4,7 +4,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 paths:
   /users/{userId}/orders/{orderId}:
     parameters:
diff --git a/__tests__/join/references-in-parameters/foo.yaml b/__tests__/join/references-in-parameters/foo.yaml
index d9a2363c..b2fc7454 100644
--- a/__tests__/join/references-in-parameters/foo.yaml
+++ b/__tests__/join/references-in-parameters/foo.yaml
@@ -4,7 +4,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 paths:
   /users/{userId}/products/{productId}:
     parameters:
diff --git a/__tests__/join/references-in-parameters/snapshot.js b/__tests__/join/references-in-parameters/snapshot.js
index baacacb5..e6531ab5 100644
--- a/__tests__/join/references-in-parameters/snapshot.js
+++ b/__tests__/join/references-in-parameters/snapshot.js
@@ -8,7 +8,7 @@ info:
   description: This is an example API.
   version: 1.0.0
 servers:
-  - url: https://example.com/api
+  - url: https://redocly-example.com/api
 tags:
   - name: foo_other
     x-displayName: other
diff --git a/__tests__/join/with-metadata/pet.yaml b/__tests__/join/with-metadata/pet.yaml
index 7ab58b1f..d82826ae 100644
--- a/__tests__/join/with-metadata/pet.yaml
+++ b/__tests__/join/with-metadata/pet.yaml
@@ -1,6 +1,6 @@
 openapi: 3.0.0
 servers:
-  - url: http://localhost:8080
+  - url: http://redocly-example.com:8080
 info:
   description: example test
   version: 1.0.0
diff --git a/__tests__/join/with-metadata/snapshot.js b/__tests__/join/with-metadata/snapshot.js
index b7a6dcaf..becd0e2e 100644
--- a/__tests__/join/with-metadata/snapshot.js
+++ b/__tests__/join/with-metadata/snapshot.js
@@ -14,8 +14,8 @@ info:
     name: Apache 2.0
     url: http://www.apache.org/licenses/LICENSE-2.0.html
 servers:
-  - url: http://localhost:8081
-  - url: http://localhost:8080
+  - url: http://redocly-example.com:8081
+  - url: http://redocly-example.com:8080
 tags:
   - name: user
     x-displayName: user
diff --git a/__tests__/join/with-metadata/test.yaml b/__tests__/join/with-metadata/test.yaml
index 9f6c7f90..883c0cfa 100644
--- a/__tests__/join/with-metadata/test.yaml
+++ b/__tests__/join/with-metadata/test.yaml
@@ -1,6 +1,6 @@
 openapi: 3.0.0
 servers:
-  - url: http://localhost:8081
+  - url: http://redocly-example.com:8081
 info:
   description: test
   version: 1.0.0
diff --git a/__tests__/lint/assertions-camel-case-twice/openapi.yaml b/__tests__/lint/assertions-camel-case-twice/openapi.yaml
index d519fddc..90f8b53c 100644
--- a/__tests__/lint/assertions-camel-case-twice/openapi.yaml
+++ b/__tests__/lint/assertions-camel-case-twice/openapi.yaml
@@ -8,7 +8,7 @@ tags:
   - name: sometag
     description: sometag
 servers:
-  - url: https://example.com
+  - url: https://redocly-example.com
 security: []
 paths:
   /someGet:
diff --git a/__tests__/lint/assertions-error/openapi.yaml b/__tests__/lint/assertions-error/openapi.yaml
index de0755f9..9a15b2a4 100644
--- a/__tests__/lint/assertions-error/openapi.yaml
+++ b/__tests__/lint/assertions-error/openapi.yaml
@@ -27,7 +27,7 @@ paths:
       description: summary example
       externalDocs:
         description: 'Find more info here'
-        url: 'https://example.com'
+        url: 'https://redocly-example.com'
       requestBody:
         content:
           application/json:
diff --git a/__tests__/lint/assertions-error/snapshot.js b/__tests__/lint/assertions-error/snapshot.js
index 99d6f877..d4f78253 100644
--- a/__tests__/lint/assertions-error/snapshot.js
+++ b/__tests__/lint/assertions-error/snapshot.js
@@ -188,7 +188,7 @@ rule/anyType failed because the any description didn't meet the assertions: Shou
 28 | externalDocs:
 29 |   description: 'Find more info here'
    |                ^^^^^^^^^^^^^^^^^^^^^
-30 |   url: 'https://example.com'
+30 |   url: 'https://redocly-example.com'
 31 | requestBody:
 
 Error was generated by the rule/anyType rule.
diff --git a/__tests__/lint/assertions/openapi.yaml b/__tests__/lint/assertions/openapi.yaml
index 8df1f488..3b985177 100644
--- a/__tests__/lint/assertions/openapi.yaml
+++ b/__tests__/lint/assertions/openapi.yaml
@@ -25,7 +25,7 @@ paths:
       summary: My resource
       externalDocs:
         description: 'Find more info here'
-        url: 'https://example.com'
+        url: 'https://redocly-example.com'
       requestBody:
         content:
           application/json:
diff --git a/__tests__/lint/no-server-variables-empty-enum-errror/openapi.yaml b/__tests__/lint/no-server-variables-empty-enum-errror/openapi.yaml
index 451385ab..fb6ba995 100644
--- a/__tests__/lint/no-server-variables-empty-enum-errror/openapi.yaml
+++ b/__tests__/lint/no-server-variables-empty-enum-errror/openapi.yaml
@@ -3,14 +3,14 @@ info:
   title: Example OpenAPI 3 definition. Valid.
   version: 1.0
 servers:
-  - url: https://example.com/user/{id}/place/{placeId}
+  - url: https://redocly-example.com/user/{id}/place/{placeId}
     variables:
       id:
         enum: []
       placeId:
         enum: [b]
         default: a
-  - url: https://example.com/{var}
+  - url: https://redocly-example.com/{var}
     variables: {}
 
 paths:
diff --git a/__tests__/lint/no-server-variables-empty-enum-errror/snapshot.js b/__tests__/lint/no-server-variables-empty-enum-errror/snapshot.js
index 4d1bfcfa..b8cce946 100644
--- a/__tests__/lint/no-server-variables-empty-enum-errror/snapshot.js
+++ b/__tests__/lint/no-server-variables-empty-enum-errror/snapshot.js
@@ -11,7 +11,7 @@ Server variable with \`enum\` must be a non-empty array.
 4 |   version: 1.0
 5 | servers:
   | ^^^^^^^
-6 |   - url: https://example.com/user/{id}/place/{placeId}
+6 |   - url: https://redocly-example.com/user/{id}/place/{placeId}
 7 |     variables:
 
 Error was generated by the no-server-variables-empty-enum rule.
@@ -25,7 +25,7 @@ Server variable define \`enum\` and \`default\`. \`enum\` must include default v
 4 |   version: 1.0
 5 | servers:
   | ^^^^^^^
-6 |   - url: https://example.com/user/{id}/place/{placeId}
+6 |   - url: https://redocly-example.com/user/{id}/place/{placeId}
 7 |     variables:
 
 Error was generated by the no-server-variables-empty-enum rule.
diff --git a/__tests__/lint/no-server-variables-empty-enum/openapi.yaml b/__tests__/lint/no-server-variables-empty-enum/openapi.yaml
index 171e9f59..d5b6833b 100644
--- a/__tests__/lint/no-server-variables-empty-enum/openapi.yaml
+++ b/__tests__/lint/no-server-variables-empty-enum/openapi.yaml
@@ -3,14 +3,14 @@ info:
   title: Example OpenAPI 3 definition. Valid.
   version: 1.0
 servers:
-  - url: https://example.com/user/{id}/place/{placeId}
+  - url: https://redocly-example.com/user/{id}/place/{placeId}
     variables:
       id:
         enum: [c]
       placeId:
         enum: [b, a]
         default: a
-  - url: https://example.com/{var}
+  - url: https://redocly-example.com/{var}
     variables: {}
 
 paths:
diff --git a/__tests__/lint/oas3.1-error/openapi.yaml b/__tests__/lint/oas3.1-error/openapi.yaml
index 0eba558d..11a0deb1 100644
--- a/__tests__/lint/oas3.1-error/openapi.yaml
+++ b/__tests__/lint/oas3.1-error/openapi.yaml
@@ -13,7 +13,7 @@ tags:
   - name: Gameplay
     description: gameplay tag
 servers:
-  - url: https://example.com/{var}
+  - url: https://redocly-example.com/{var}
     variables:
       var:
         enum:
diff --git a/__tests__/lint/oas3.1/openapi.yaml b/__tests__/lint/oas3.1/openapi.yaml
index b86c49f6..f6323a40 100644
--- a/__tests__/lint/oas3.1/openapi.yaml
+++ b/__tests__/lint/oas3.1/openapi.yaml
@@ -13,7 +13,7 @@ tags:
   - name: Gameplay
     description: gameplay tag
 servers:
-  - url: https://example.com/{var}
+  - url: https://redocly-example.com/{var}
     variables:
       var:
         enum:
diff --git a/__tests__/miscellaneous/resolve-refs-in-preprocessors/openapi.yaml b/__tests__/miscellaneous/resolve-refs-in-preprocessors/openapi.yaml
index 68665dda..59b8497c 100644
--- a/__tests__/miscellaneous/resolve-refs-in-preprocessors/openapi.yaml
+++ b/__tests__/miscellaneous/resolve-refs-in-preprocessors/openapi.yaml
@@ -1,6 +1,6 @@
 openapi: 3.0.1
 servers:
-  - url: http://localhost:8080
+  - url: http://redocly-example.com:8080
 info:
   title: Test
   version: 1.0.0
diff --git a/__tests__/miscellaneous/resolve-refs-in-preprocessors/snapshot.js b/__tests__/miscellaneous/resolve-refs-in-preprocessors/snapshot.js
index da7b7700..7cde290f 100644
--- a/__tests__/miscellaneous/resolve-refs-in-preprocessors/snapshot.js
+++ b/__tests__/miscellaneous/resolve-refs-in-preprocessors/snapshot.js
@@ -6,7 +6,7 @@ info:
   title: Test
   version: 1.0.0
 servers:
-  - url: http://localhost:8080
+  - url: http://redocly-example.com:8080
 security: []
 paths:
   /items:
@@ -57,7 +57,7 @@ components:
 Info object should contain \`license\` field.
 
 2 | servers:
-3 |   - url: http://localhost:8080
+3 |   - url: http://redocly-example.com:8080
 4 | info:
   | ^^^^
 5 |   title: Test
@@ -82,7 +82,7 @@ validating openapi.yaml...
 Info object should contain \`license\` field.
 
 2 | servers:
-3 |   - url: http://localhost:8080
+3 |   - url: http://redocly-example.com:8080
 4 | info:
   | ^^^^
 5 |   title: Test
diff --git a/__tests__/smoke/openapi.yaml b/__tests__/smoke/openapi.yaml
index aa98c7e1..4375c769 100644
--- a/__tests__/smoke/openapi.yaml
+++ b/__tests__/smoke/openapi.yaml
@@ -1,6 +1,6 @@
 openapi: 3.1.0
 servers:
-  - url: http://localhost
+  - url: http://redocly-example.com
 info:
   title: Sample API
   version: 1.0.0
diff --git a/packages/core/src/__tests__/lint.test.ts b/packages/core/src/__tests__/lint.test.ts
index 56f00c29..c0e4dceb 100644
--- a/packages/core/src/__tests__/lint.test.ts
+++ b/packages/core/src/__tests__/lint.test.ts
@@ -20,7 +20,7 @@ describe('lint', () => {
           license: Fail
 
         servers:
-          - url: http://example.com
+          - url: http://redocly-example.com
         paths: {}
     `,
       config: await loadConfig(),
diff --git a/packages/core/src/rules/oas3/__tests__/no-server-example.com.test.ts b/packages/core/src/rules/oas3/__tests__/no-server-example.com.test.ts
index e4026735..3bb893ab 100644
--- a/packages/core/src/rules/oas3/__tests__/no-server-example.com.test.ts
+++ b/packages/core/src/rules/oas3/__tests__/no-server-example.com.test.ts
@@ -30,7 +30,7 @@ describe('Oas3 oas3-no-server-example.com', () => {
               "source": "foobar.yaml",
             },
           ],
-          "message": "Server \`url\` should not point at example.com.",
+          "message": "Server \`url\` should not point to example.com or localhost.",
           "ruleId": "no-server-example.com",
           "severity": "error",
           "suggest": Array [],
@@ -57,4 +57,39 @@ describe('Oas3 oas3-no-server-example.com', () => {
 
     expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
   });
+
+  it('oas3-no-server-example.com: should report on server object with "foo.example.com" url', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+          openapi: 3.0.0
+          servers:
+            - url: foo.example.com
+        `,
+      'foobar.yaml'
+    );
+
+    const results = await lintDocument({
+      externalRefResolver: new BaseResolver(),
+      document,
+      config: await makeConfig({ 'no-server-example.com': 'error' }),
+    });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "location": Array [
+            Object {
+              "pointer": "#/servers/0/url",
+              "reportOnKey": false,
+              "source": "foobar.yaml",
+            },
+          ],
+          "message": "Server \`url\` should not point to example.com or localhost.",
+          "ruleId": "no-server-example.com",
+          "severity": "error",
+          "suggest": Array [],
+        },
+      ]
+    `);
+  });
 });
diff --git a/packages/core/src/rules/oas3/__tests__/spec/spec.test.ts b/packages/core/src/rules/oas3/__tests__/spec/spec.test.ts
index 09fbcad1..d7c7201c 100644
--- a/packages/core/src/rules/oas3/__tests__/spec/spec.test.ts
+++ b/packages/core/src/rules/oas3/__tests__/spec/spec.test.ts
@@ -17,7 +17,7 @@ describe('Oas3 Structural visitor basic', () => {
             - 25.3
             - test
           servers:
-          - url: 'http://example.com'
+          - url: 'http://redocly-example.com'
             variables:
               a:
                 default: test