Improve OpenAPI server URL validation (#3937)

This commit is contained in:
Nolann B.
2026-01-26 13:45:03 +01:00
committed by GitHub
parent 58f0cc8287
commit 3ba9e46f2b
5 changed files with 53 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Improve OpenAPI server URL validation
@@ -11,7 +11,7 @@ import { generateMediaTypeExamples, generateSchemaExample } from './generateSche
import { stringifyOpenAPI } from './stringifyOpenAPI';
import type { OpenAPIOperationData } from './types';
import { mergeHeaders } from './util/headers';
import { getDefaultServerURL } from './util/server';
import { getDefaultServerURL, hasValidServerHost } from './util/server';
import {
resolvePrefillCodePlaceholderFromSecurityScheme,
resolveURLWithPrefillCodePlaceholdersFromServer,
@@ -216,11 +216,14 @@ function OpenAPICodeSampleFooter(props: {
const hasMultipleMediaTypes =
renderers.length > 1 || renderers.some((renderer) => renderer.examples.length > 0);
// Check if any server has a host that can be used in an HTTP request
const hasValidHost = hasValidServerHost(servers);
if (hideTryItPanel && !hasMultipleMediaTypes) {
return null;
}
if (!validateHttpMethod(method) || (!hasMultipleMediaTypes && servers.length === 0)) {
if (!validateHttpMethod(method) || (!hasMultipleMediaTypes && !hasValidHost)) {
return null;
}
@@ -237,7 +240,7 @@ function OpenAPICodeSampleFooter(props: {
) : (
<span />
)}
{!hideTryItPanel && servers.length > 0 && (
{!hideTryItPanel && hasValidHost && (
<ScalarApiButton
context={getOpenAPIClientContext(context)}
method={method}
@@ -18,6 +18,14 @@ export function OpenAPIMediaTypeExamplesSelector(props: {
const state = useSelectState(stateKey, renderers[0].mediaType);
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
const hasMultipleMediaTypes = renderers.length >= 2;
const hasMultipleExamples = selected.examples.length >= 2;
// Only render the wrapper div if at least one selector will render
if (!hasMultipleMediaTypes && !hasMultipleExamples) {
return null;
}
return (
<div className="openapi-codesample-selectors">
<MediaTypeSelector selectIcon={selectIcon} stateKey={stateKey} renderers={renderers} />
+6 -2
View File
@@ -13,6 +13,7 @@ import {
} from './contentTypeChecks';
import { json2xml } from './json2xml';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import { isValidServerHost } from './util/server';
export interface CodeSampleInput {
method: string;
@@ -69,9 +70,12 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
const bodyString = body ? `\n${body}` : '';
// Only include Host header if origin is considered a valid server host
const hasValidHost = isValidServerHost(origin);
const hostLine = hasValidHost ? `Host: ${origin.replaceAll(/https?:\/\//g, '')}\n` : '';
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
Host: ${origin.replaceAll(/https*:\/\//g, '')}
${headerString}${bodyString}`;
${hostLine}${headerString}${bodyString}`;
return httpRequest;
},
+28
View File
@@ -45,3 +45,31 @@ function parseServerURL(url: string) {
}
return result;
}
/**
* Check if any server has a host that can be used in an HTTP request.
* This is used to determine if the "Try it" button should be shown.
*/
export function hasValidServerHost(servers: OpenAPIV3.ServerObject[]): boolean {
if (servers.length === 0) {
return false;
}
return servers.some((server) => {
const url = interpolateServerURL(server);
return isValidServerHost(url);
});
}
/**
* Check if the server host/URL is valid for making direct HTTP requests.
* Accepts both full URLs (with protocol) and hostnames (without protocol).
*/
export function isValidServerHost(url: string): boolean {
// Check if URL starts with http:// or https://
if (url.startsWith('http://') || url.startsWith('https://')) {
return true;
}
return /^(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(url);
}