]> source.dussan.org Git - sonarqube.git/blob
4d1ca46ed98b2f54ea56b2f4933d77ea72229c6e
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.rule;
21
22 import java.util.Objects;
23 import java.util.Optional;
24 import java.util.Set;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
27 import javax.annotation.CheckForNull;
28 import org.sonar.api.server.rule.RulesDefinition;
29 import org.sonar.core.util.UuidFactory;
30 import org.sonar.db.rule.RuleDescriptionSectionDto;
31 import org.sonar.markdown.Markdown;
32
33 import static java.util.Collections.emptySet;
34 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
35 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ASSESS_THE_PROBLEM_SECTION_KEY;
36 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY;
37 import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY;
38 import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
39
40 public class LegacyHotspotRuleDescriptionSectionsGenerator implements RuleDescriptionSectionsGenerator {
41   private final UuidFactory uuidFactory;
42
43   public LegacyHotspotRuleDescriptionSectionsGenerator(UuidFactory uuidFactory) {
44     this.uuidFactory = uuidFactory;
45   }
46
47   @Override
48   public boolean isGeneratorForRule(RulesDefinition.Rule rule) {
49     // To prevent compatibility issues with SonarLint, this Generator is used for all hotspots rules, regardless of if they expose advanced sections or not. See SONAR-16635.
50     // In the future, the generator should not be used for advanced rules (add condition && rule.ruleDescriptionSections().isEmpty())
51     return SECURITY_HOTSPOT.equals(rule.type());
52   }
53
54   @Override
55   public Set<RuleDescriptionSectionDto> generateSections(RulesDefinition.Rule rule) {
56     return getDescriptionInHtml(rule)
57       .map(this::generateSections)
58       .orElse(emptySet());
59   }
60
61   private static Optional<String> getDescriptionInHtml(RulesDefinition.Rule rule) {
62     if (rule.htmlDescription() != null) {
63       return Optional.of(rule.htmlDescription());
64     } else if (rule.markdownDescription() != null) {
65       return Optional.of(Markdown.convertToHtml(rule.markdownDescription()));
66     }
67     return Optional.empty();
68   }
69
70   private Set<RuleDescriptionSectionDto> generateSections(String descriptionInHtml) {
71     if (descriptionInHtml.isEmpty()) {
72       return emptySet();
73     }
74     String[] split = extractSection("", descriptionInHtml);
75     String remainingText = split[0];
76     String ruleDescriptionSection = split[1];
77
78     split = extractSection("<h2>Exceptions</h2>", remainingText);
79     remainingText = split[0];
80     String exceptions = split[1];
81
82     split = extractSection("<h2>Ask Yourself Whether</h2>", remainingText);
83     remainingText = split[0];
84     String askSection = split[1];
85
86     split = extractSection("<h2>Sensitive Code Example</h2>", remainingText);
87     remainingText = split[0];
88     String sensitiveSection = split[1];
89
90     split = extractSection("<h2>Noncompliant Code Example</h2>", remainingText);
91     remainingText = split[0];
92     String noncompliantSection = split[1];
93
94     split = extractSection("<h2>Recommended Secure Coding Practices</h2>", remainingText);
95     remainingText = split[0];
96     String recommendedSection = split[1];
97
98     split = extractSection("<h2>Compliant Solution</h2>", remainingText);
99     remainingText = split[0];
100     String compliantSection = split[1];
101
102     split = extractSection("<h2>See</h2>", remainingText);
103     remainingText = split[0];
104     String seeSection = split[1];
105
106     RuleDescriptionSectionDto rootSection = createSection(ROOT_CAUSE_SECTION_KEY, ruleDescriptionSection, exceptions, remainingText);
107     RuleDescriptionSectionDto assessSection = createSection(ASSESS_THE_PROBLEM_SECTION_KEY, askSection, sensitiveSection, noncompliantSection);
108     RuleDescriptionSectionDto fixSection = createSection(HOW_TO_FIX_SECTION_KEY, recommendedSection, compliantSection, seeSection);
109
110     // For backward compatibility with SonarLint, see SONAR-16635. Should be removed in 10.x
111     RuleDescriptionSectionDto defaultSection = createDefaultRuleDescriptionSection(uuidFactory.create(), descriptionInHtml);
112
113     return Stream.of(rootSection, assessSection, fixSection, defaultSection)
114       .filter(Objects::nonNull)
115       .collect(Collectors.toSet());
116   }
117
118
119   private static String[] extractSection(String beginning, String description) {
120     String endSection = "<h2>";
121     int beginningIndex = description.indexOf(beginning);
122     if (beginningIndex != -1) {
123       int endIndex = description.indexOf(endSection, beginningIndex + beginning.length());
124       if (endIndex == -1) {
125         endIndex = description.length();
126       }
127       return new String[] {
128         description.substring(0, beginningIndex) + description.substring(endIndex),
129         description.substring(beginningIndex, endIndex)
130       };
131     } else {
132       return new String[] {description, ""};
133     }
134
135   }
136
137   @CheckForNull
138   private RuleDescriptionSectionDto createSection(String key, String... contentPieces) {
139     String content = trimToNull(String.join("", contentPieces));
140     if (content == null) {
141       return null;
142     }
143     return RuleDescriptionSectionDto.builder()
144       .uuid(uuidFactory.create())
145       .key(key)
146       .content(content)
147       .build();
148   }
149
150   @CheckForNull
151   private static String trimToNull(String input) {
152     return input.isEmpty() ? null : input;
153   }
154
155 }