From 4e7e4e1b194d02b0c389ed96299bffd6695866da Mon Sep 17 00:00:00 2001 From: Anastasiia Derendeeva Date: Mon, 6 May 2024 15:49:45 +0200 Subject: [PATCH] Added language setting option to skip runtime schema compilation --- .../services/yamlSchemaService.ts | 30 +++++++++++++++---- src/languageservice/yamlLanguageService.ts | 5 ++++ test/schemaValidation.test.ts | 18 +++++++++++ test/utils/serviceSetup.ts | 1 + 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/languageservice/services/yamlSchemaService.ts b/src/languageservice/services/yamlSchemaService.ts index 453b74eb9..1ae50391c 100644 --- a/src/languageservice/services/yamlSchemaService.ts +++ b/src/languageservice/services/yamlSchemaService.ts @@ -5,7 +5,7 @@ *--------------------------------------------------------------------------------------------*/ import { JSONSchema, JSONSchemaMap, JSONSchemaRef } from '../jsonSchema'; -import { SchemaPriority, SchemaRequestService, WorkspaceContextService } from '../yamlLanguageService'; +import { LanguageSettings, SchemaPriority, SchemaRequestService, WorkspaceContextService } from '../yamlLanguageService'; import { UnresolvedSchema, ResolvedSchema, @@ -27,7 +27,7 @@ import { getSchemaFromModeline } from './modelineUtil'; import { JSONSchemaDescriptionExt } from '../../requestTypes'; import { SchemaVersions } from '../yamlTypes'; -import Ajv, { DefinedError } from 'ajv'; +import Ajv, { DefinedError, ValidateFunction } from 'ajv'; import { getSchemaTitle } from '../utils/schemaUtils'; const localize = nls.loadMessageBundle(); @@ -37,7 +37,8 @@ const ajv = new Ajv(); // load JSON Schema 07 def to validate loaded schemas // eslint-disable-next-line @typescript-eslint/no-var-requires const jsonSchema07 = require('ajv/dist/refs/json-schema-draft-07.json'); -const schema07Validator = ajv.compile(jsonSchema07); + +var schema07Validator: ValidateFunction; export declare type CustomSchemaProvider = (uri: string) => Promise; @@ -111,6 +112,7 @@ export class YAMLSchemaService extends JSONSchemaService { public schemaPriorityMapping: Map>; private schemaUriToNameAndDescription = new Map(); + private skipSchemaValidation: boolean; constructor( requestService: SchemaRequestService, @@ -121,6 +123,13 @@ export class YAMLSchemaService extends JSONSchemaService { this.customSchemaProvider = undefined; this.requestService = requestService; this.schemaPriorityMapping = new Map(); + this.skipSchemaValidation = false; + } + + configure(languageSettings: LanguageSettings): void { + if (languageSettings) { + this.skipSchemaValidation = languageSettings.skipSchemaValidation; + } } registerCustomSchemaProvider(customSchemaProvider: CustomSchemaProvider): void { @@ -155,6 +164,16 @@ export class YAMLSchemaService extends JSONSchemaService { return result; } + private getSchemaValidator(): ValidateFunction | undefined { + if (this.skipSchemaValidation) { + return undefined; + } + if (!schema07Validator) { + schema07Validator = ajv.compile(jsonSchema07); + } + return schema07Validator; + } + async resolveSchemaContent( schemaToResolve: UnresolvedSchema, schemaURL: string, @@ -164,9 +183,10 @@ export class YAMLSchemaService extends JSONSchemaService { let schema: JSONSchema = schemaToResolve.schema; const contextService = this.contextService; - if (!schema07Validator(schema)) { + const schemaValidator = this.getSchemaValidator(); + if (schemaValidator && !schemaValidator(schema)) { const errs: string[] = []; - for (const err of schema07Validator.errors as DefinedError[]) { + for (const err of schemaValidator.errors as DefinedError[]) { errs.push(`${err.instancePath} : ${err.message}`); } resolveErrors.push(`Schema '${getSchemaTitle(schemaToResolve.schema, schemaURL)}' is not valid:\n${errs.join('\n')}`); diff --git a/src/languageservice/yamlLanguageService.ts b/src/languageservice/yamlLanguageService.ts index 539371d8b..9ec3b40b0 100644 --- a/src/languageservice/yamlLanguageService.ts +++ b/src/languageservice/yamlLanguageService.ts @@ -119,6 +119,10 @@ export interface LanguageSettings { * If set enforce alphabetical ordering of keys in mappings. */ keyOrdering?: boolean; + /** + * If set skip json schema validation. Allows to not use unsafe-eval rule + */ + skipSchemaValidation?: boolean; } export interface WorkspaceContextService { @@ -204,6 +208,7 @@ export function getLanguageService(params: { return { configure: (settings) => { + schemaService.configure(settings); schemaService.clearExternalSchemas(); if (settings.schemas) { schemaService.schemaPriorityMapping = new Map(); diff --git a/test/schemaValidation.test.ts b/test/schemaValidation.test.ts index 71268db7b..3ef482e7e 100644 --- a/test/schemaValidation.test.ts +++ b/test/schemaValidation.test.ts @@ -1847,6 +1847,24 @@ obj: }); }); + describe('Skip json schema validation with enabled skipSchemaValidation', () => { + before(() => { + languageSettingsSetup.languageSettings.skipSchemaValidation = true; + languageService.configure(languageSettingsSetup.languageSettings); + }); + after(() => { + languageSettingsSetup.languageSettings.skipSchemaValidation = false; + }); + it('should handle not valid schema object', async () => { + const schema = 'Foo'; + schemaProvider.addSchema(SCHEMA_ID, schema as JSONSchema); + const content = `foo: bar`; + const result = await parseSetup(content); + expect(result).to.be.empty; + expect(telemetry.messages).to.be.empty; + }); + }); + describe('Enum tests', () => { afterEach(() => { schemaProvider.deleteSchema(SCHEMA_ID); diff --git a/test/utils/serviceSetup.ts b/test/utils/serviceSetup.ts index 1a20386d1..c9cc1403a 100644 --- a/test/utils/serviceSetup.ts +++ b/test/utils/serviceSetup.ts @@ -21,6 +21,7 @@ export class ServiceSetup { yamlVersion: '1.2', flowMapping: 'allow', flowSequence: 'allow', + skipSchemaValidation: false, }; withValidate(): ServiceSetup {