diff --git a/docs/diagnostics/DisableSafeModeForExternalDataProcessors.md b/docs/diagnostics/DisableSafeModeForExternalDataProcessors.md index b74f030246f..5b3d50c9ef2 100644 --- a/docs/diagnostics/DisableSafeModeForExternalDataProcessors.md +++ b/docs/diagnostics/DisableSafeModeForExternalDataProcessors.md @@ -22,6 +22,13 @@ Правило проверяет отключение безопасного режима во внешних отчетах и обработках, подключаемых к подсистеме " Дополнительные отчеты и обработки" для БСП-совместимых конфигураций. +Также выдается замечание, если добавляются разрешения на специальные +действия - `Разрешение = РаботаВБезопасномРежиме.РазрешениеНаХХХ` + +- т.к. в случае файловых баз или клиент-серверных систем без установленных профилей безопасности `БСП` подключает + внешние файлы с отключением безопасного режима +- смотрите пример далее + ## Примеры @@ -37,6 +44,12 @@ ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь); // будет выдано замечание ПараметрыРегистрации.Вставить("БезопасныйРежим", Истина); // замечания не будет + Разрешение = РаботаВБезопасномРежиме.РазрешениеНаИспользованиеПривилегированногоРежима(); + ПараметрыРегистрации.Разрешения.Добавить(Разрешение); // будет выдано замечание + ПараметрыРегистрации.Разрешения.Вставить(0, Разрешение); // будет выдано замечание + + КоличествоРазрешений = ПараметрыРегистрации.Разрешения.Количество(); // замечания не будет + Возврат ПараметрыРегистрации; КонецФункции ``` diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.java index 62f82f2d176..62af6cf119f 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.java @@ -64,38 +64,18 @@ public class DisableSafeModeForExternalDataProcessorsDiagnostic extends Abstract private static final String SAFE_MODE_PARAMETER = "БЕЗОПАСНЫЙРЕЖИМ"; private static final String SAFE_MODE_PARAMETER_WITH_QUOTES = "\"" + SAFE_MODE_PARAMETER + "\""; private static final Pattern INSERT_METHOD_PATTERN = CaseInsensitivePattern.compile("вставить|insert"); + private static final Pattern ADD_INSERT_METHOD_PATTERN = CaseInsensitivePattern.compile("add|добавить|вставить|insert"); + private static final Pattern PERMISSIONS_PATTERN = CaseInsensitivePattern.compile("разрешения"); + private final ReferenceIndex referenceIndex; - private final Map> nodes = new HashMap<>(); + private final Map> safeModeUnsetNodes = new HashMap<>(); private MethodSymbol methodSymbol; - private Optional mainCollection = Optional.empty(); + private String returnedVariableNameFromMainMethod = ""; public DisableSafeModeForExternalDataProcessorsDiagnostic(ReferenceIndex referenceIndex) { this.referenceIndex = referenceIndex; } - private static boolean isFalseExpression(Optional expressionContext) { - return expressionContext - .map(BSLParser.ExpressionContext::member) - .filter(memberContexts -> !memberContexts.isEmpty()) - .map(memberContexts -> memberContexts.get(0)) - .map(BSLParser.MemberContext::constValue) - .map(BSLParser.ConstValueContext::FALSE) - .isPresent(); - } - - private static boolean haveSafeModeText(BSLParser.CallParamContext callParamContext) { - return Optional.of(callParamContext) - .map(BSLParser.CallParamContext::expression) - .map(BSLParser.ExpressionContext::member) - .filter(memberContexts -> !memberContexts.isEmpty()) - .map(memberContexts -> memberContexts.get(0)) - .map(BSLParser.MemberContext::constValue) - .map(BSLParser.ConstValueContext::string) - .map(BSLParserRuleContext::getText) - .filter(name -> name.equalsIgnoreCase(SAFE_MODE_PARAMETER_WITH_QUOTES)) - .isPresent(); - } - @Override public ParseTree visitFile(BSLParser.FileContext ctx) { processFile(ctx); @@ -116,9 +96,9 @@ public void processFile(BSLParser.FileContext ctx) { return; } - super.visitFile(ctx); // вычисляю имя возвращаемой переменной в mainCollectionName - if (mainCollection.isPresent() && !nodes.isEmpty()) { - nodes.get(mainCollection.get()) + super.visitFile(ctx); // вычисляю имя возвращаемой переменной в returnedVariablesFromMainMethod + if (!returnedVariableNameFromMainMethod.isEmpty() && !safeModeUnsetNodes.isEmpty()) { + safeModeUnsetNodes.get(returnedVariableNameFromMainMethod) .forEach(diagnosticStorage::addDiagnostic); } } @@ -132,15 +112,16 @@ private Optional getMainMethodSymbol() { @Override public ParseTree visitFunction(BSLParser.FunctionContext ctx) { - // TODO проверить на вхождение range плюс или минус 1 - if (Ranges.containsRange(methodSymbol.getRange(), Ranges.create(ctx)) && - Optional.ofNullable(ctx.funcDeclaration()) + if (Ranges.containsRange(methodSymbol.getRange(), Ranges.create(ctx))) { + final var isMainFunction = Optional.ofNullable(ctx.funcDeclaration()) .map(BSLParser.FuncDeclarationContext::subName) .map(BSLParser.SubNameContext::IDENTIFIER) .map(ParseTree::getText) .filter(s -> s.equalsIgnoreCase(MAIN_METHOD_NAME)) - .isPresent()) { - return super.visitFunction(ctx); + .isPresent(); + if (isMainFunction) { + return super.visitFunction(ctx); + } } return null; // нет смысла анализировать другие методы } @@ -152,15 +133,17 @@ public ParseTree visitProcedure(BSLParser.ProcedureContext ctx) { @Override public ParseTree visitReturnStatement(BSLParser.ReturnStatementContext ctx) { - // TODO проверить на вхождение range плюс или минус 1 - if (mainCollection.isEmpty() && Ranges.containsRange(methodSymbol.getRange(), Ranges.create(ctx))) { - mainCollection = Optional.ofNullable(ctx.expression()) + if (returnedVariableNameFromMainMethod.isEmpty() + && Ranges.containsRange(methodSymbol.getRange(), Ranges.create(ctx))) { + + returnedVariableNameFromMainMethod = Optional.ofNullable(ctx.expression()) .map(BSLParser.ExpressionContext::member) .filter(memberContexts -> !memberContexts.isEmpty()) .map(memberContexts -> memberContexts.get(0)) .map(BSLParser.MemberContext::complexIdentifier) .map(BSLParser.ComplexIdentifierContext::IDENTIFIER) - .map(ParseTree::getText); + .map(ParseTree::getText) + .orElse(""); } return null; //нет смысла идти глубже } @@ -185,7 +168,7 @@ public void processAssignment(BSLParser.AssignmentContext ctx) { .filter(terminalNode1 -> terminalNode1.getText().equalsIgnoreCase(SAFE_MODE_PARAMETER)); if (terminalNodeSafeMode.isPresent() && isFalseExpression(Optional.ofNullable(ctx.expression()))) { - nodes.computeIfAbsent(varName.get(), name -> new ArrayList<>()).add(ctx); + safeModeUnsetNodes.computeIfAbsent(varName.get(), name -> new ArrayList<>()).add(ctx); } } @@ -202,15 +185,48 @@ public void processCallStatement(BSLParser.CallStatementContext ctx) { return; } - final var methodCallContext = Optional.of(ctx) - .map(BSLParser.CallStatementContext::accessCall) - .map(BSLParser.AccessCallContext::methodCall); - final var methodNameContext = methodCallContext - .map(BSLParser.MethodCallContext::methodName) - .filter(methodName -> INSERT_METHOD_PATTERN.matcher(methodName.getText()).matches()); - if (methodNameContext.isEmpty()) { + final var callContext = Optional.of(ctx); + if (isPermissionAdding(callContext)) { + safeModeUnsetNodes.computeIfAbsent(varName.get(), name -> new ArrayList<>()).add(ctx); return; } + + if (isSafeModeUnsetCalling(callContext)) { + safeModeUnsetNodes.computeIfAbsent(varName.get(), name -> new ArrayList<>()).add(ctx); + } + } + + private static boolean isFalseExpression(Optional expressionContext) { + return expressionContext + .map(BSLParser.ExpressionContext::member) + .filter(memberContexts -> !memberContexts.isEmpty()) + .map(memberContexts -> memberContexts.get(0)) + .map(BSLParser.MemberContext::constValue) + .map(BSLParser.ConstValueContext::FALSE) + .isPresent(); + } + + private static boolean isPermissionAdding(Optional callContext) { + final var isPermissionReferenced = callContext + .map(BSLParser.CallStatementContext::modifier) + .filter(modifierContexts -> !modifierContexts.isEmpty()) + .map(modifierContexts -> modifierContexts.get(0)) + .map(BSLParser.ModifierContext::accessProperty) + .map(BSLParser.AccessPropertyContext::IDENTIFIER) + .filter(terminalNode -> PERMISSIONS_PATTERN.matcher(terminalNode.getText()).matches()) + .isPresent(); + if (!isPermissionReferenced) { + return false; + } + return getMethodCallContext(callContext, ADD_INSERT_METHOD_PATTERN) + .isPresent(); + } + + private static boolean isSafeModeUnsetCalling(Optional callContext) { + final var methodCallContext = getMethodCallContext(callContext, INSERT_METHOD_PATTERN); + if (methodCallContext.isEmpty()) { + return false; + } final var expressionContext = methodCallContext .map(BSLParser.MethodCallContext::doCall) .map(BSLParser.DoCallContext::callParamList) @@ -220,8 +236,32 @@ public void processCallStatement(BSLParser.CallStatementContext ctx) { .map(callParamContexts -> callParamContexts.get(1)) .map(BSLParser.CallParamContext::expression); - if (isFalseExpression(expressionContext)) { - nodes.computeIfAbsent(varName.get(), name -> new ArrayList<>()).add(ctx); + return isFalseExpression(expressionContext); + } + + private static Optional getMethodCallContext(Optional callContext, Pattern insertMethodPattern) { + final var methodCallContext = callContext + .map(BSLParser.CallStatementContext::accessCall) + .map(BSLParser.AccessCallContext::methodCall); + final var methodNameContext = methodCallContext + .map(BSLParser.MethodCallContext::methodName) + .filter(methodName -> insertMethodPattern.matcher(methodName.getText()).matches()); + if (methodNameContext.isEmpty()) { + return Optional.empty(); } + return methodCallContext; + } + + private static boolean haveSafeModeText(BSLParser.CallParamContext callParamContext) { + return Optional.of(callParamContext) + .map(BSLParser.CallParamContext::expression) + .map(BSLParser.ExpressionContext::member) + .filter(memberContexts -> !memberContexts.isEmpty()) + .map(memberContexts -> memberContexts.get(0)) + .map(BSLParser.MemberContext::constValue) + .map(BSLParser.ConstValueContext::string) + .map(BSLParserRuleContext::getText) + .filter(name -> name.equalsIgnoreCase(SAFE_MODE_PARAMETER_WITH_QUOTES)) + .isPresent(); } } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnosticTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnosticTest.java index 9d63699f78a..e91a7d27344 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnosticTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnosticTest.java @@ -41,6 +41,8 @@ void test() { assertThat(diagnostics, true) .hasRange(5, 4, 47) .hasRange(7, 4, 58) - .hasSize(2); + .hasRange(12, 4, 56) + .hasRange(13, 4, 59) + .hasSize(4); } } diff --git a/src/test/resources/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.bsl b/src/test/resources/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.bsl index c2881582460..c32837ed524 100644 --- a/src/test/resources/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.bsl +++ b/src/test/resources/diagnostics/DisableSafeModeForExternalDataProcessorsDiagnostic.bsl @@ -8,6 +8,11 @@ ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь); // ошибка ПараметрыРегистрации.Вставить("БезопасныйРежим", Истина); + Разрешение = РаботаВБезопасномРежиме.РазрешениеНаИспользованиеПривилегированногоРежима(); + ПараметрыРегистрации.Очистить(); // нет замечания + ПараметрыРегистрации.Разрешения.Добавить(Разрешение); // ошибка + ПараметрыРегистрации.Разрешения.Вставить(0, Разрешение); // ошибка + Возврат ПараметрыРегистрации; КонецФункции