mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +00:00
Improve OpenAPI server URL validation (#3937)
This commit is contained in:
@@ -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 { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||||
import type { OpenAPIOperationData } from './types';
|
import type { OpenAPIOperationData } from './types';
|
||||||
import { mergeHeaders } from './util/headers';
|
import { mergeHeaders } from './util/headers';
|
||||||
import { getDefaultServerURL } from './util/server';
|
import { getDefaultServerURL, hasValidServerHost } from './util/server';
|
||||||
import {
|
import {
|
||||||
resolvePrefillCodePlaceholderFromSecurityScheme,
|
resolvePrefillCodePlaceholderFromSecurityScheme,
|
||||||
resolveURLWithPrefillCodePlaceholdersFromServer,
|
resolveURLWithPrefillCodePlaceholdersFromServer,
|
||||||
@@ -216,11 +216,14 @@ function OpenAPICodeSampleFooter(props: {
|
|||||||
const hasMultipleMediaTypes =
|
const hasMultipleMediaTypes =
|
||||||
renderers.length > 1 || renderers.some((renderer) => renderer.examples.length > 0);
|
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) {
|
if (hideTryItPanel && !hasMultipleMediaTypes) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateHttpMethod(method) || (!hasMultipleMediaTypes && servers.length === 0)) {
|
if (!validateHttpMethod(method) || (!hasMultipleMediaTypes && !hasValidHost)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +240,7 @@ function OpenAPICodeSampleFooter(props: {
|
|||||||
) : (
|
) : (
|
||||||
<span />
|
<span />
|
||||||
)}
|
)}
|
||||||
{!hideTryItPanel && servers.length > 0 && (
|
{!hideTryItPanel && hasValidHost && (
|
||||||
<ScalarApiButton
|
<ScalarApiButton
|
||||||
context={getOpenAPIClientContext(context)}
|
context={getOpenAPIClientContext(context)}
|
||||||
method={method}
|
method={method}
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ export function OpenAPIMediaTypeExamplesSelector(props: {
|
|||||||
const state = useSelectState(stateKey, renderers[0].mediaType);
|
const state = useSelectState(stateKey, renderers[0].mediaType);
|
||||||
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
|
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 (
|
return (
|
||||||
<div className="openapi-codesample-selectors">
|
<div className="openapi-codesample-selectors">
|
||||||
<MediaTypeSelector selectIcon={selectIcon} stateKey={stateKey} renderers={renderers} />
|
<MediaTypeSelector selectIcon={selectIcon} stateKey={stateKey} renderers={renderers} />
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from './contentTypeChecks';
|
} from './contentTypeChecks';
|
||||||
import { json2xml } from './json2xml';
|
import { json2xml } from './json2xml';
|
||||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||||
|
import { isValidServerHost } from './util/server';
|
||||||
|
|
||||||
export interface CodeSampleInput {
|
export interface CodeSampleInput {
|
||||||
method: string;
|
method: string;
|
||||||
@@ -69,9 +70,12 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
|
|||||||
|
|
||||||
const bodyString = body ? `\n${body}` : '';
|
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
|
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
|
||||||
Host: ${origin.replaceAll(/https*:\/\//g, '')}
|
${hostLine}${headerString}${bodyString}`;
|
||||||
${headerString}${bodyString}`;
|
|
||||||
|
|
||||||
return httpRequest;
|
return httpRequest;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -45,3 +45,31 @@ function parseServerURL(url: string) {
|
|||||||
}
|
}
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user