View Javadoc
1   /*
2    * Cloudformation Plugin for SonarQube
3    * Copyright (C) 2019 James Pether Sörling
4    * james@hack23.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 com.hack23.sonar.cloudformation.reports.process;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.nio.file.Files;
25  import java.util.List;
26  import java.util.Optional;
27  
28  import org.apache.commons.lang3.StringUtils;
29  import org.sonar.api.batch.fs.FileSystem;
30  import org.sonar.api.batch.fs.InputFile;
31  import org.sonar.api.batch.fs.TextPointer;
32  import org.sonar.api.batch.rule.ActiveRules;
33  import org.sonar.api.batch.sensor.SensorContext;
34  import org.sonar.api.batch.sensor.issue.NewIssue;
35  import org.sonar.api.batch.sensor.issue.NewIssueLocation;
36  import org.sonar.api.rule.RuleKey;
37  import org.sonar.api.scan.filesystem.PathResolver;
38  import org.sonar.api.utils.log.Logger;
39  import org.sonar.api.utils.log.Loggers;
40  
41  import com.hack23.sonar.cloudformation.reports.checkov.CheckovPassedCheck;
42  import com.hack23.sonar.cloudformation.reports.checkov.CheckovReport;
43  
44  /**
45   * The Class CheckovProcessReports.
46   */
47  public final class CheckovProcessReports extends AbstractProcessReports {
48  
49  	/** The Constant SENSOR_NAME. */
50  	public static final String SENSOR_NAME = "Cloudformation Check";
51  
52  	/** The Constant LOGGER. */
53  	private static final Logger LOGGER = Loggers.get(CheckovProcessReports.class);
54  
55  	/** The checkov report reader. */
56  	private final CheckovReportReader checkovReportReader;
57  
58  	/** The path resolver. */
59  	private final PathResolver pathResolver;
60  
61  	/** The file system. */
62  	private final FileSystem fileSystem;
63  
64  	/**
65  	 * Instantiates a new checkov process reports.
66  	 *
67  	 * @param fileSystem the file system
68  	 * @param pathResolver the path resolver
69  	 */
70  	public CheckovProcessReports(final FileSystem fileSystem,
71  			final PathResolver pathResolver) {
72  		super();
73  		this.checkovReportReader = new CheckovReportReader();
74  		this.fileSystem = fileSystem;
75  		this.pathResolver = pathResolver;
76  	}
77  
78  	/**
79  	 * Process checkov report.
80  	 *
81  	 * @param context the context
82  	 * @param reportFilesProperty the report files property
83  	 * @throws IOException Signals that an I/O exception has occurred.
84  	 */
85  	public void processCheckovReport(final SensorContext context,final Optional<String> reportFilesProperty) throws IOException {
86  		if (reportFilesProperty.isPresent()) {
87  
88  			final String reports = reportFilesProperty.get();
89  			LOGGER.info(CloudformationConstants.CHECKOV_REPORT_FILES_PROPERTY + "=" + reports);
90  			final String[] reportFiles = StringUtils.split(reports, ",");
91  
92  			for (final String report : reportFiles) {
93  				LOGGER.info("Processing  checkov :" + report);
94  				if (pathResolver.relativeFile(fileSystem.baseDir(), report).exists()) {
95  					handleCheckovReports(context, report);
96  				} else {
97  					LOGGER.warn("Processing checkov:" + report + " missing");
98  				}
99  			}
100 		} else {
101 			LOGGER.warn("Missing property:{}", CloudformationConstants.CHECKOV_REPORT_FILES_PROPERTY);
102 		}
103 	}
104 
105 
106 	/**
107 	 * Handle checkov reports.
108 	 *
109 	 * @param context the context
110 	 * @param report the report
111 	 * @throws IOException Signals that an I/O exception has occurred.
112 	 */
113 	private void handleCheckovReports(final SensorContext context, final String report) throws IOException {
114 		LOGGER.info("Reading checkov reports:{}", report);
115 
116 		final List<CheckovReport> checkovReportList = checkovReportReader
117 				.readReport(Files.newInputStream(pathResolver.relativeFile(fileSystem.baseDir(), report).toPath()));
118 
119 
120 		for (final CheckovReport checkovReport : checkovReportList) {
121 
122 			final ActiveRules activeRules = context.activeRules();
123 			for (final CheckovPassedCheck failedChecks : checkovReport.getResults().getFailedChecks()) {
124 				final String filename = failedChecks.getFilePath();
125 				LOGGER.info("Checkov scanned file :{}", filename);
126 
127 				final InputFile templateInputFile = findTemplate(fileSystem,
128 						filename.substring(filename.lastIndexOf(File.separator) + 1), filename);
129 
130 				addCheckovIssue(context, activeRules, checkovReport, failedChecks, templateInputFile);
131 			}
132 		}
133 	}
134 
135 	/**
136 	 * Adds the checkov issue.
137 	 *
138 	 * @param context the context
139 	 * @param activeRules the active rules
140 	 * @param checkovReport the checkov report
141 	 * @param failedChecks the failed checks
142 	 * @param templateInputFile the template input file
143 	 */
144 	private static void addCheckovIssue(final SensorContext context, final ActiveRules activeRules, final CheckovReport checkovReport,
145 			final CheckovPassedCheck failedChecks, final InputFile templateInputFile) {
146 
147 		String repoName = "cloudformation-plugin-cfn";
148 		if (templateInputFile != null && "terraform".equalsIgnoreCase(templateInputFile.language())) {
149 			repoName = "cloudformation-plugin-terraform";
150 		}
151 
152 		final RuleKey ruleKey = RuleKey.of(repoName,checkovReport.getCheckType() + "-" + failedChecks.getCheckId());
153 
154 		if (activeRules.find(ruleKey) == null) {
155 			LOGGER.warn("No active checkov rule detected for:'{}' with key {} detected in {}",failedChecks.getCheckName(), ruleKey,failedChecks.getFilePath());
156 			return;
157 		}
158 
159 		if (templateInputFile == null) {
160 			LOGGER.warn("File not found {} for rule {} issue not created", failedChecks.getFilePath(), ruleKey);
161 			return;
162 		}
163 
164 		final List<Integer> lineNumbers = failedChecks.getFileLineRange();
165 		if(!lineNumbers.isEmpty()) {
166 			final TextPointer startLine = templateInputFile.selectLine(lineNumbers.get(0)).start();
167 			final TextPointer endLine = templateInputFile.selectLine(lineNumbers.get(lineNumbers.size()-1)).end();
168 			final NewIssue newIssue = context.newIssue().forRule(ruleKey);
169 
170 			final NewIssueLocation location = newIssue.newLocation()
171 		            .on(templateInputFile).at(templateInputFile.newRange(startLine, endLine))
172 		            .message(failedChecks.getCheckName());
173 		    newIssue.at(location).save();
174 		}
175 	}
176 
177 
178 }