*/
package org.sonar.education;
+import java.nio.charset.StandardCharsets;
+import org.sonar.api.internal.apachecommons.io.IOUtils;
import org.sonar.api.server.rule.RuleDescriptionSection;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.education.sensors.EducationPrinciplesSensor;
import org.sonar.education.sensors.EducationWithContextsSensor;
import org.sonar.education.sensors.EducationWithDetectedContextSensor;
-import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ASSESS_THE_PROBLEM_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.INTRODUCTION_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY;
+import static org.sonar.education.sensors.EducationWith2LinkedCodeSnippetsSensor.EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY;
+import static org.sonar.education.sensors.EducationWith4LinkedCodeSnippetsSensor.EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY;
+import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
public class EducationRulesDefinition implements RulesDefinition {
public static final String EDUCATION_RULE_REPOSITORY_KEY = "edu";
public static final String EDUCATION_KEY = "education";
+ private static final String[] ALL_SECTIONS = {INTRODUCTION_SECTION_KEY, ROOT_CAUSE_SECTION_KEY, ASSESS_THE_PROBLEM_SECTION_KEY,
+ RESOURCES_SECTION_KEY, HOW_TO_FIX_SECTION_KEY};
private static final String IGNORED_FAKE_SECTION = "fake_section_to_be_ignored";
public static final String[] CONTEXTS = {"spring", "hibernate", "apache-commons", "vaadin", "mybatis"};
createRuleWithDescriptionSectionsAndMultipleContexts(repo);
createRuleWithDescriptionSectionsAndMultipleContextsIncludingOneDetected(repo);
+ createRuleWith2LinkedCodeSnippetsInTwoSections(repo);
+
+ createRuleWith4LinkedCodeSnippetsInOneSection(repo);
+
repo.done();
}
ruleWithSingleContext
.setHtmlDescription(String.format("This rule contains description sections. To trigger an issue using this rule you need to " +
- "scan any text file with a line containing %s key word. This rule also contains 2 education principles - %s and %s",
+ "scan any text file with a line containing %s key word. This rule also contains 2 education principles - %s and %s",
ruleKey, educationPrinciples[0], educationPrinciples[1]))
.addEducationPrincipleKeys(educationPrinciples);
private void createRuleWithDescriptionSectionsAndSingleContext(NewRepository repo) {
String ruleKey = EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY;
NewRule ruleWithSingleContext = repo.createRule(ruleKey).setName("Rule with description sections and single " +
- "contexts for 'how to fix'")
+ "contexts for 'how to fix'")
.addTags(EDUCATION_KEY);
ruleWithSingleContext
ruleWithMultipleContexts
.setHtmlDescription(String.format("This rule contains description sections and multiple contexts for 'how to fix' section. To trigger " +
- "an issue using this rule you need to scan any text file with a line containing %s keyword.",
+ "an issue using this rule you need to scan any text file with a line containing %s keyword.",
EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY));
addNonContextualizedDescriptionSections(ruleWithMultipleContexts);
ruleWithMultipleContexts
.setHtmlDescription(String.format("This rule contains description sections and multiple contexts (including one detected) for " +
- "'how to fix' section. To trigger an issue using this rule you need to scan any text file with a line containing %s keyword.",
+ "'how to fix' section. To trigger an issue using this rule you need to scan any text file with a line containing %s keyword.",
EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY));
addNonContextualizedDescriptionSections(ruleWithMultipleContexts);
}
}
+
+ private void createRuleWith2LinkedCodeSnippetsInTwoSections(NewRepository repo) {
+ String ruleKey = EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY;
+ NewRule rule2CodeSnippetsIn2Sections = repo.createRule(ruleKey).setName("Rule with description sections and code snippets " +
+ "linked to each other in 2 different sections in order to display diff between them.")
+ .addTags(EDUCATION_KEY);
+
+ rule2CodeSnippetsIn2Sections
+ .setHtmlDescription(String.format("This rule contains description sections and 2 linked code snippets in 2 sections. To " +
+ "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey));
+
+ String completelyDifferentSnippetsHtml = readResource("2completelyDifferentSnippets.html");
+ String codeSnippetsHtml = readResource("2codeSnippets.html");
+
+ rule2CodeSnippetsIn2Sections.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY, completelyDifferentSnippetsHtml))
+ .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(HOW_TO_FIX_SECTION_KEY, codeSnippetsHtml))
+ .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY));
+ }
+
+ private void createRuleWith4LinkedCodeSnippetsInOneSection(NewRepository repo) {
+ String ruleKey = EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY;
+ NewRule rule2CodeSnippetsInTheSameSection = repo.createRule(ruleKey).setName("Rule with description sections and code snippets " +
+ "linked to each other in the same section in order to display diff between them.")
+ .addTags(EDUCATION_KEY);
+
+ rule2CodeSnippetsInTheSameSection
+ .setHtmlDescription(String.format("This rule contains description sections and 4 linked code snippets in the same section. To " +
+ "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey));
+
+ String codeSnippetsHtml = readResource("4codeSnippets.html");
+
+ rule2CodeSnippetsInTheSameSection.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY, codeSnippetsHtml))
+ .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(HOW_TO_FIX_SECTION_KEY))
+ .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY));
+ }
+
private static void addNonContextualizedDescriptionSections(NewRule newRule) {
newRule.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY))
.addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY))
}
private static RuleDescriptionSection descriptionSection(String sectionKey) {
+ return descriptionSection(sectionKey, HTML_LOREM_IPSUM);
+ }
+
+ private static RuleDescriptionSection descriptionSection(String sectionKey, String htmlContent) {
return RuleDescriptionSection.builder()
.sectionKey(sectionKey)
- .htmlContent(String.format("%s: %s", sectionKey, HTML_LOREM_IPSUM))
+ .htmlContent(htmlContent)
.build();
}
+
+ private String readResource(String file) {
+ try {
+ return IOUtils.toString(getClass().getResource(file), StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.education.sensors;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.rule.ActiveRules;
+
+public class EducationWith2LinkedCodeSnippetsSensor extends EducationRuleSensor {
+
+ public static final String EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY = "2_LINKED_CODE_SNIPPETS";
+
+ public EducationWith2LinkedCodeSnippetsSensor(FileSystem fs, ActiveRules activeRules) {
+ super(fs, activeRules, EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.education.sensors;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.rule.ActiveRules;
+
+public class EducationWith4LinkedCodeSnippetsSensor extends EducationRuleSensor {
+
+ public static final String EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY = "4_LINKED_CODE_SNIPPETS";
+
+ public EducationWith4LinkedCodeSnippetsSensor(FileSystem fs, ActiveRules activeRules) {
+ super(fs, activeRules, EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY);
+ }
+}
--- /dev/null
+<p>An infinite loop is one that will never end while the program is running, i.e., you have to kill the program to get out of the loop. Whether it is
+ by meeting the loop’s end condition or via a <code>break</code>, every loop should have an end condition.</p>
+<h3>Known Limitations</h3>
+<ul>
+ <li> False positives: when <code>yield</code> is used - <a href="https://github.com/SonarSource/SonarJS/issues/674">Issue #674</a>. </li>
+ <li> False positives: when an exception is raised by a function invoked within the loop. </li>
+ <li> False negatives: when a loop condition is based on an element of an array or object. </li>
+</ul>
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-1 diff-noncompliant">for (;;) { // Noncompliant; end condition omitted
+ // ...
+}
+
+var j = 0;
+while (true) { // Noncompliant; constant end condition
+ j++;
+}
+
+var k;
+var b = true;
+while (b) { // Noncompliant; constant end condition
+ k++;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-1 diff-noncompliant">while (true) { // break will potentially allow leaving the loop
+ if (someCondition) {
+ break;
+ }
+}
+
+var k;
+var b = true;
+while (b) {
+ k++;
+ b = k < 10;
+}
+
+outer:
+while(true) {
+ while(true) {
+ break outer;
+ }
+}
+</pre>
\ No newline at end of file
--- /dev/null
+Example with 2 completely different code snippets
+
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-1 diff-noncompliant">
+while (true) { // Noncompliant; constant end condition
+ j++;
+}
+
+var k;
+var b = true;
+while (b) { // Noncompliant; constant end condition
+ k++;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-1 diff-compliant">while (true) { // break will potentially allow leaving the loop
+var x = 3;
+for(let i=2; i<4; i++) {
+ console.log("Hello there");
+}
+alert('Click here');
+thisIsNiceFunction();
+</pre>
\ No newline at end of file
--- /dev/null
+<p>A <code>catch</code> clause that only rethrows the caught exception has the same effect as omitting the <code>catch</code> altogether and letting
+ it bubble up automatically, but with more code and the additional detriment of leaving maintainers scratching their heads.</p>
+<p>Such clauses should either be eliminated or populated with the appropriate logic.</p>
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-1 diff-noncompliant">try {
+ doSomething();
+} catch (ex) { // Noncompliant
+ throw ex;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-1 diff-compliant">try {
+ doSomething();
+} catch (ex) {
+ console.err(ex);
+ throw ex;
+}
+</pre>
+
+<h2>Noncompliant Code Example</h2>
+<pre class="diff-id-2 diff-noncompliant">try {
+ doSomethingElse();
+} catch (ex) { // Noncompliant
+ throw ex;
+}
+</pre>
+<h2>Compliant Solution</h2>
+<pre class="diff-id-2 diff-compliant">try {
+ doSomethingElse();
+} catch (ex) {
+ console.err(ex);
+ throw ex;
+}
+</pre>
\ No newline at end of file
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.education.sensors;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.education.sensors.EducationWith2LinkedCodeSnippetsSensor.EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY;
+
+public class EducationWith2LinkedCodeSnippetsSensorTest extends EducationSensorTest {
+ @Test
+ public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+ DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY);
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+ fs.add(inputFile);
+
+ SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+ var sensor = new EducationWith2LinkedCodeSnippetsSensor(fs, activeRules);
+
+ sensor.execute(sensorContextTester);
+
+ assertThat(sensorContextTester.allIssues()).hasSize(1);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.education.sensors;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.education.sensors.EducationWith4LinkedCodeSnippetsSensor.EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY;
+
+public class EducationWith4LinkedCodeSnippetsSensorTest extends EducationSensorTest {
+
+ @Test
+ public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException {
+ DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY);
+
+ DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder());
+ fs.add(inputFile);
+
+ SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath());
+ var sensor = new EducationWith4LinkedCodeSnippetsSensor(fs, activeRules);
+
+ sensor.execute(sensorContextTester);
+
+ assertThat(sensorContextTester.allIssues()).hasSize(1);
+ }
+}