Skip to content

Commit 2ea18ad

Browse files
committed
writer-json: support ShellCheck SARIF rule metadata
Fixes: #68
1 parent 3f3742a commit 2ea18ad

7 files changed

+16446
-23
lines changed

src/lib/writer-json.cc

+92-21
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,14 @@ class SarifTreeEncoder: public AbstractTreeEncoder {
153153

154154
private:
155155
void initToolVersion();
156-
void serializeCweMap();
156+
void serializeRules();
157157

158-
typedef std::map<std::string, int> TCweMap;
158+
using TCweMap = std::map<std::string, int>;
159159
TCweMap cweMap_;
160+
161+
using TShellCheckMap = std::map<std::string, std::string>;
162+
TShellCheckMap shellCheckMap_;
163+
160164
TScanProps scanProps_;
161165
PTree driver_;
162166
PTree results_;
@@ -215,31 +219,87 @@ void SarifTreeEncoder::initToolVersion()
215219
driver_.put<std::string>("informationUri", uri);
216220
}
217221

218-
void SarifTreeEncoder::serializeCweMap()
222+
static void sarifEncodeShellCheckRule(PTree *rule, const std::string &ruleID)
219223
{
220-
PTree ruleList;
224+
// name
225+
rule->put<std::string>("name", ruleID);
221226

222-
for (const auto &item : cweMap_) {
223-
PTree rule;
224-
const auto &id = item.first;
225-
rule.put<std::string>("id", id);
227+
// properties.tags[]
228+
PTree tagList;
229+
appendNode(&tagList, PTree({"ShellCheck"}));
230+
231+
PTree props;
232+
props.put_child("tags", tagList);
233+
rule->put_child("properties", props);
234+
235+
// help.text && help.markdown
236+
PTree help;
237+
const auto helpURI =
238+
"https://github.com/koalaman/shellcheck/wiki/" + ruleID;
239+
help.put<std::string>("text", "Defect reference: " + helpURI);
240+
241+
const auto helpMarkdown =
242+
"Defect reference: [" + ruleID +"](" + helpURI + ")";
243+
help.put<std::string>("markdown", helpMarkdown);
226244

227-
PTree cweList;
228-
const auto cwe = item.second;
229-
const auto cweStr = std::to_string(cwe);
230-
appendNode(&cweList, PTree("CWE-" + cweStr));
245+
rule->put_child("help", help);
246+
}
247+
248+
static void sarifEncodeCweRule(PTree *rule, const int cwe, bool append = false)
249+
{
250+
PTree cweList;
251+
const auto cweStr = std::to_string(cwe);
252+
appendNode(&cweList, PTree("CWE-" + cweStr));
231253

232-
// properties.cwe[]
254+
// properties.cwe[]
255+
if (append) {
256+
PTree &props = rule->get_child("properties");
257+
props.put_child("cwe", cweList);
258+
} else {
233259
PTree props;
234260
props.put_child("cwe", cweList);
235-
rule.put_child("properties", props);
261+
rule->put_child("properties", props);
262+
}
236263

237-
// help.text
264+
// help.text
265+
const auto helpText =
266+
"https://cwe.mitre.org/data/definitions/" + cweStr + ".html";
267+
268+
if (append) {
269+
PTree &help = rule->get_child("help");
270+
const auto &originalHelpText = help.get_child("text").get_value("");
271+
help.put<std::string>("text", originalHelpText + '\n' + helpText);
272+
} else {
238273
PTree help;
239-
const auto helpText =
240-
"https://cwe.mitre.org/data/definitions/" + cweStr + ".html";
241274
help.put<std::string>("text", helpText);
242-
rule.put_child("help", help);
275+
rule->put_child("help", help);
276+
}
277+
}
278+
279+
void SarifTreeEncoder::serializeRules()
280+
{
281+
PTree ruleList;
282+
283+
for (const auto &item : shellCheckMap_) {
284+
PTree rule;
285+
const auto &id = item.first;
286+
rule.put<std::string>("id", id);
287+
288+
sarifEncodeShellCheckRule(&rule, item.second);
289+
if (1U == cweMap_.count(id))
290+
sarifEncodeCweRule(&rule, cweMap_[id], /*append =*/ true);
291+
292+
appendNode(&ruleList, rule);
293+
}
294+
295+
for (const auto &item : cweMap_) {
296+
const auto &id = item.first;
297+
if (1U == shellCheckMap_.count(id))
298+
continue;
299+
300+
PTree rule;
301+
rule.put<std::string>("id", id);
302+
sarifEncodeCweRule(&rule, item.second);
243303

244304
appendNode(&ruleList, rule);
245305
}
@@ -259,7 +319,8 @@ static void sarifEncodeMsg(PTree *pDst, const std::string& text)
259319
pDst->put_child("message", msg);
260320
}
261321

262-
static void sarifEncodeLevel(PTree *result, const std::string &event) {
322+
static void sarifEncodeLevel(PTree *result, const std::string &event)
323+
{
263324
std::string level = event;
264325

265326
// cut the [...] suffix from event if present
@@ -350,6 +411,16 @@ void SarifTreeEncoder::appendDef(const Defect &def)
350411
// checker (FIXME: suboptimal mapping to SARIF)
351412
const std::string ruleId = def.checker + ": " + keyEvt.event;
352413
result.put<std::string>("ruleId", ruleId);
414+
415+
if (def.checker == "SHELLCHECK_WARNING") {
416+
boost::smatch sm;
417+
static const RE reShellCheckMsg("(\\[)?(SC[0-9]+)(\\])?$");
418+
boost::regex_search(keyEvt.event, sm, reShellCheckMsg);
419+
420+
// update ShellCheck rule map
421+
shellCheckMap_[ruleId] = sm[2];
422+
}
423+
353424
if (def.cwe)
354425
// update CWE map
355426
cweMap_[ruleId] = def.cwe;
@@ -423,9 +494,9 @@ void SarifTreeEncoder::writeTo(std::ostream &str)
423494

424495
this->initToolVersion();
425496

426-
if (!cweMap_.empty())
497+
if (!cweMap_.empty() || !shellCheckMap_.empty())
427498
// needs to run before we pick driver_
428-
this->serializeCweMap();
499+
this->serializeRules();
429500

430501
PTree tool;
431502
tool.put_child("driver", driver_);

0 commit comments

Comments
 (0)