diff --git a/lib/interfaces/swagger-custom-options.interface.ts b/lib/interfaces/swagger-custom-options.interface.ts index a28e6d3aa..60cea025d 100644 --- a/lib/interfaces/swagger-custom-options.interface.ts +++ b/lib/interfaces/swagger-custom-options.interface.ts @@ -2,14 +2,29 @@ import { SwaggerUiOptions } from './swagger-ui-options.interface'; import { SwaggerDocumentOptions } from './swagger-document-options.interface'; import { OpenAPIObject } from './open-api-spec.interface'; +export interface CustomCssUrl { + customCssUrl: string | string[]; + customCssUrlIntegrity?: string | string[]; +} + +export interface CustomJs { + customJs: string | string[]; + customJsIntegrity?: string | string[]; +} + +export interface CustomJsStr { + customJsStr: string | string[]; + customJsStrIntegrity?: string | string[]; +} + export interface SwaggerCustomOptions { useGlobalPrefix?: boolean; explorer?: boolean; swaggerOptions?: SwaggerUiOptions; customCss?: string; - customCssUrl?: string | string[]; - customJs?: string | string[]; - customJsStr?: string | string[]; + customCssUrl?: CustomCssUrl; + customJs?: CustomJs; + customJsStr?: CustomJsStr; customfavIcon?: string; customSwaggerUiPath?: string; swaggerUrl?: string; @@ -19,5 +34,9 @@ export interface SwaggerCustomOptions { urls?: Record<'url' | 'name', string>[]; jsonDocumentUrl?: string; yamlDocumentUrl?: string; - patchDocumentOnRequest?: (req: TRequest, res: TResponse, document: OpenAPIObject) => OpenAPIObject; + patchDocumentOnRequest?: ( + req: TRequest, + res: TResponse, + document: OpenAPIObject + ) => OpenAPIObject; } diff --git a/lib/swagger-ui/swagger-ui.ts b/lib/swagger-ui/swagger-ui.ts index 01f8645df..99c8c4152 100644 --- a/lib/swagger-ui/swagger-ui.ts +++ b/lib/swagger-ui/swagger-ui.ts @@ -17,6 +17,7 @@ export function buildSwaggerInitJS( }; const jsInitOptions = buildJSInitOptions(swaggerInitOptions); + // Cannot dynamically compute integrity: https://github.com/nestjs/swagger/issues/2667#issuecomment-1780515512 return jsTemplateString.replace('<% swaggerOptions %>', jsInitOptions); } @@ -34,20 +35,30 @@ export function getSwaggerAssetsAbsoluteFSPath() { return swaggerAssetsAbsoluteFSPath; } -function toExternalScriptTag(url: string) { - return ``; +function toExternalScriptTag(url: string, integrity?: string) { + // Cannot dynamically compute integrity: https://github.com/nestjs/swagger/issues/2667#issuecomment-1780515512 + return ``.replace(/\s+>/g, '>'); } -function toInlineScriptTag(jsCode: string) { - return ``; +function toInlineScriptTag(jsCode: string, integrity?: string) { + // Cannot dynamically compute integrity: https://github.com/nestjs/swagger/issues/2667#issuecomment-1780515512 + return ``.replace(/\s+>/g, '>'); } -function toExternalStylesheetTag(url: string) { - return ``; +function toExternalStylesheetTag(url: string, integrity?: string) { + // Cannot dynamically compute integrity: https://github.com/nestjs/swagger/issues/2667#issuecomment-1780515512 + return ``.replace(/\s+>/g, '>'); } function toTags( customCode: string | string[] | undefined, + integrity: typeof customCode, toScript: (url: string) => string ) { if (!customCode) { @@ -71,11 +82,11 @@ export function buildSwaggerHTML( ) { const { customCss = '', - customJs = '', - customJsStr = '', + customJs: { customJs = '', customJsIntegrity = null }, + customJsStr: { customJsStr = '', customJsStrIntegrity = null }, customfavIcon = false, customSiteTitle = 'Swagger UI', - customCssUrl = '', + customCssUrl: { customCssUrl = '', customCssUrlIntegrity = null }, explorer = false } = customOptions; @@ -91,11 +102,17 @@ export function buildSwaggerHTML( .replace('<% explorerCss %>', explorerCss) .replace('<% favIconString %>', favIconString) .replace(/<% baseUrl %>/g, baseUrl) - .replace('<% customJs %>', toTags(customJs, toExternalScriptTag)) - .replace('<% customJsStr %>', toTags(customJsStr, toInlineScriptTag)) + .replace( + '<% customJs %>', + toTags(customJs, customJsIntegrity, toExternalScriptTag) + ) + .replace( + '<% customJsStr %>', + toTags(customJsStr, customJsStrIntegrity, toInlineScriptTag) + ) .replace( '<% customCssUrl %>', - toTags(customCssUrl, toExternalStylesheetTag) + toTags(customCssUrl, customCssUrlIntegrity, toExternalStylesheetTag) ) .replace('<% title %>', customSiteTitle); }