Line data Source code
1 : // Copyright (C) 2020 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.git.validators; 16 : 17 : import com.google.common.collect.ImmutableList; 18 : import com.google.common.collect.Iterables; 19 : import com.google.gerrit.entities.Change; 20 : import com.google.gerrit.entities.Comment; 21 : import com.google.gerrit.entities.Project; 22 : import com.google.gerrit.extensions.validators.CommentForValidation; 23 : import com.google.gerrit.extensions.validators.CommentValidationContext; 24 : import com.google.gerrit.extensions.validators.CommentValidationFailure; 25 : import com.google.gerrit.extensions.validators.CommentValidator; 26 : import com.google.gerrit.server.config.GerritServerConfig; 27 : import com.google.gerrit.server.notedb.ChangeNotes; 28 : import com.google.inject.Inject; 29 : import java.util.stream.Stream; 30 : import org.eclipse.jgit.lib.Config; 31 : 32 : /** 33 : * Limits the total size of all comments and change messages to prevent space/time complexity 34 : * issues. Note that autogenerated change messages are not subject to validation. However, we still 35 : * count autogenerated messages for the limit (which will be notified on a further comment). 36 : */ 37 : public class CommentCumulativeSizeValidator implements CommentValidator { 38 : public static final int DEFAULT_CUMULATIVE_COMMENT_SIZE_LIMIT = 3 << 20; 39 : 40 : private final int maxCumulativeSize; 41 : private final ChangeNotes.Factory notesFactory; 42 : 43 : @Inject 44 : CommentCumulativeSizeValidator( 45 65 : @GerritServerConfig Config serverConfig, ChangeNotes.Factory notesFactory) { 46 65 : this.notesFactory = notesFactory; 47 65 : maxCumulativeSize = 48 65 : serverConfig.getInt( 49 : "change", "cumulativeCommentSizeLimit", DEFAULT_CUMULATIVE_COMMENT_SIZE_LIMIT); 50 65 : } 51 : 52 : @Override 53 : public ImmutableList<CommentValidationFailure> validateComments( 54 : CommentValidationContext ctx, ImmutableList<CommentForValidation> comments) { 55 65 : ChangeNotes notes = 56 65 : notesFactory.createChecked(Project.nameKey(ctx.getProject()), Change.id(ctx.getChangeId())); 57 65 : int existingCumulativeSize = 58 65 : Stream.concat( 59 65 : notes.getHumanComments().values().stream(), 60 65 : notes.getRobotComments().values().stream()) 61 65 : .mapToInt(Comment::getApproximateSize) 62 65 : .sum() 63 65 : + notes.getChangeMessages().stream().mapToInt(cm -> cm.getMessage().length()).sum(); 64 65 : int newCumulativeSize = 65 65 : comments.stream().mapToInt(CommentForValidation::getApproximateSize).sum(); 66 65 : ImmutableList.Builder<CommentValidationFailure> failures = ImmutableList.builder(); 67 65 : if (!comments.isEmpty() && !isEnoughSpace(notes, newCumulativeSize, maxCumulativeSize)) { 68 : // This warning really applies to the set of all comments, but we need to pick one to attach 69 : // the message to. 70 5 : CommentForValidation commentForFailureMessage = Iterables.getLast(comments); 71 : 72 5 : failures.add( 73 5 : commentForFailureMessage.failValidation( 74 5 : String.format( 75 : "Exceeding maximum cumulative size of comments and change messages:" 76 : + " %d (existing) + %d (new) > %d", 77 5 : existingCumulativeSize, newCumulativeSize, maxCumulativeSize))); 78 : } 79 65 : return failures.build(); 80 : } 81 : 82 : /** 83 : * Returns {@code true} if there is available space and the new size that we wish to add is less 84 : * than the maximum allowed size. {@code false} otherwise (if there is not enough space). 85 : */ 86 : public static boolean isEnoughSpace(ChangeNotes notes, int addedBytes, int maxCumulativeSize) { 87 67 : int existingCumulativeSize = 88 67 : Stream.concat( 89 67 : notes.getHumanComments().values().stream(), 90 67 : notes.getRobotComments().values().stream()) 91 67 : .mapToInt(Comment::getApproximateSize) 92 67 : .sum() 93 67 : + notes.getChangeMessages().stream().mapToInt(cm -> cm.getMessage().length()).sum(); 94 67 : return existingCumulativeSize + addedBytes < maxCumulativeSize; 95 : } 96 : }