Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_library(cs STATIC
parser-json-sarif.cc
parser-json-shchk.cc
parser-json-simple.cc
parser-json-zap.cc
parser-xml.cc
parser-xml-valgrind.cc
shared-string.cc
Expand Down
4 changes: 4 additions & 0 deletions src/lib/parser-common.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
#define RE_CHECKER_NAME_MISRA "(?:MISRA C(?:\\+\\+)?-[0-9]+ (?:Directive|Rule) [0-9.-]+)"
#define RE_CHECKER_NAME RE_CHECKER_NAME_SA "|" RE_CHECKER_NAME_CERT "|" RE_CHECKER_NAME_MISRA

#define RE_PATH_LOCAL "[^:]+"
#define RE_PATH_URL "http(?:s)?://[^:]+(?::[0-9]+)?[^:]+"
#define RE_PATH RE_PATH_LOCAL "|" RE_PATH_URL

#define RE_EVENT_GCC "(?:(?:(?:fatal|internal) )?[A-Za-z][A-Za-z0-9_-]+)(?:\\[[^ \\]]+\\])?"
#define RE_EVENT_PROSPECTOR "(?:[A-Z]+[0-9]+\\[[a-z0-9-]+\\])"
#define RE_EVENT RE_EVENT_GCC "|" RE_EVENT_PROSPECTOR
Expand Down
5 changes: 4 additions & 1 deletion src/lib/parser-cov.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class ErrFileLexer {
RE("^Error: *(" RE_CHECKER_NAME ")( *\\([^)]+\\))? *:(?: \\[#def[0-9]+\\])?$");

const RE reEvent_ =
RE(/* location */ "^([^:]+)(?::([0-9]+|<[Uu]nknown>))?(?::([0-9]+))?"
RE(/* location */ "^(" RE_PATH ")(?::([0-9]+|<[Uu]nknown>))?(?::([0-9]+))?"
/* evt/mesg */ ": (" RE_EVENT "): (.*)$");
};

Expand Down Expand Up @@ -279,6 +279,9 @@ KeyEventDigger::KeyEventDigger():
d->hMap["GCC_ANALYZER_WARNING"] .insert("warning");
d->hMap["GCC_ANALYZER_WARNING"] .insert("fatal error");

// OWASP ZAP uses "alert" as the key event
d->hMap["OWASP_ZAP_WARNING"] .insert("alert");

// events that should never be used as key events (excluding trace events)
d->denyList.insert("another_instance");
d->denyList.insert("comparison_remediation");
Expand Down
210 changes: 210 additions & 0 deletions src/lib/parser-json-zap.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* Copyright (C) 2022 Red Hat, Inc.
*
* This file is part of csdiff.
*
* csdiff is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* csdiff 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with csdiff. If not, see <http://www.gnu.org/licenses/>.
*/

#include "parser-json-zap.hh"

struct ZapTreeDecoder::Private {
std::string timeStamp;
Defect sitePrototype;
Defect alertPrototype;
const pt::ptree *alertList = nullptr;
const pt::ptree *instList = nullptr;
pt::ptree::const_iterator alertIter;
pt::ptree::const_iterator instIter;

Private()
{
this->sitePrototype.checker = "OWASP_ZAP_WARNING";
this->sitePrototype.tool = "owasp-zap";
}

void readSiteProto(const pt::ptree &siteNode);
void readAlertProto(const pt::ptree &alertNode);
void readAlertInst(Defect *pDef, const pt::ptree &instNode);
};

template <typename TPropList>
void readNonEmptyProps(
TEvtList *pDst,
const pt::ptree &node,
const DefEvent &evtProto,
const TPropList &propList)
{
// make our own copy of the given prototype event
DefEvent evt = evtProto;

for (const auto &evtName : propList) {
evt.event = evtName;
evt.msg = valueOf<std::string>(node, evtName);
if (!evt.msg.empty())
pDst->push_back(evt);
}
}

void ZapTreeDecoder::Private::readSiteProto(const pt::ptree &siteNode)
{
this->sitePrototype.events.clear();
const auto siteName = valueOf<std::string>(siteNode, "@name");
if (siteName.empty() || this->timeStamp.empty())
return;

// create a prototype "note" event
DefEvent siteEvt("note");
siteEvt.fileName = std::move(siteName);
siteEvt.msg = "dynamically analyzed on " + this->timeStamp;
siteEvt.verbosityLevel = /* info event */ 1;
this->sitePrototype.events.push_back(std::move(siteEvt));
}

void ZapTreeDecoder::Private::readAlertProto(const pt::ptree &alertNode)
{
// read per-alert properties
this->alertPrototype = this->sitePrototype;
this->alertPrototype.cwe = valueOf<int>(alertNode, "cweid");
this->alertPrototype.imp = (1 < valueOf<int>(alertNode, "riskcode"));

// initialize key event
DefEvent evt("alert");

// get "uri" from the prototype event
TEvtList &events = this->alertPrototype.events;
if (!events.empty())
evt.fileName = events.front().fileName;

// read "alertRef" if available
const auto alertRef = valueOf<std::string>(alertNode, "alertRef");
if (!alertRef.empty())
evt.event += "[" + alertRef + "]";

// read "alert" if available
evt.msg = valueOf<std::string>(alertNode, "alert");

// append the key event
this->alertPrototype.keyEventIdx = events.size();
events.push_back(evt);

// read other per-alert events if available
evt.verbosityLevel = /* info event */ 1;
const auto defProps = { "desc", "solution", "otherinfo", "reference" };
readNonEmptyProps(&events, alertNode, evt, defProps);
}

void ZapTreeDecoder::Private::readAlertInst(
Defect *pDef,
const pt::ptree &instNode)
{
// start with the prototype initialized by readAlertProto()
*pDef = this->alertPrototype;
TEvtList &events = pDef->events;

// reinitialize events with "uri" specific for this instance (if available)
const std::string uri = valueOf<std::string>(instNode, "uri");
if (!uri.empty())
for (DefEvent &evt : events)
evt.fileName = uri;

// use the key event as a prototype for instance-specific events
DefEvent evtProto = events[pDef->keyEventIdx];
evtProto.verbosityLevel = /* info event */ 1;

// read per-instance properties
const auto instProps = { "method", "param", "attack", "evidence" };
readNonEmptyProps(&events, instNode, evtProto, instProps);
}

ZapTreeDecoder::ZapTreeDecoder():
d(new Private)
{
}

ZapTreeDecoder::~ZapTreeDecoder() = default;

void ZapTreeDecoder::readScanProps(
TScanProps *pDst,
const pt::ptree *root)
{
const auto version = valueOf<std::string>(*root, "@version");
if (!version.empty())
(*pDst)["analyzer-version-owasp-zap"] = version;

d->timeStamp = valueOf<std::string>(*root, "@generated");
}

const pt::ptree* ZapTreeDecoder::nextAlert()
{
// iterate over sites unless we are processing a site already
while (!d->alertList || d->alertList->end() == d->alertIter) {
const pt::ptree *siteNode = this->nextNode();
if (!siteNode)
// failed initialization or EOF
return nullptr;

if (!findChildOf(&d->alertList, *siteNode, "alerts")) {
// "alerts" node missing for this site
d->alertList = nullptr;
continue;
}

// initialize iteration over alerts
d->alertIter = d->alertList->begin();
d->instList = nullptr;

if (!d->alertList->empty())
// site with alerts found --> update site prototype
d->readSiteProto(*siteNode);
}

// get the current alert and move to the next one
const auto itAlertNow = d->alertIter++;
return &itAlertNow->second;
}

bool ZapTreeDecoder::readNode(Defect *pDef)
{
if (!d->instList || d->instList->end() == d->instIter) {
// iterate over alerts
const pt::ptree *alertNode = this->nextAlert();
if (!alertNode)
// failed initialization or EOF
return false;

// process the current alert
d->readAlertProto(*alertNode);

// read the list of instances
if (!findChildOf(&d->instList, *alertNode, "instances")
|| d->instList->empty())
{
// no instances for this alert --> emit the prototype
d->instList = nullptr;
*pDef = d->alertPrototype;
return true;
}

// initialize iteration over instances
d->instIter = d->instList->begin();
}

// get the current instance and move to the next one
const auto itInstNow = d->instIter++;

// process the current instance
d->readAlertInst(pDef, itInstNow->second);
return true;
}
44 changes: 44 additions & 0 deletions src/lib/parser-json-zap.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2012-2022 Red Hat, Inc.
*
* This file is part of csdiff.
*
* csdiff is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* csdiff 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with csdiff. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef H_GUARD_PARSER_JSON_ZAP_H
#define H_GUARD_PARSER_JSON_ZAP_H

#include "abstract-tree.hh"

/// tree decoder of the OWASP ZAP JSON format
class ZapTreeDecoder: public AbstractTreeDecoder {
public:
ZapTreeDecoder();
~ZapTreeDecoder() override;

void readScanProps(
TScanProps *pDst,
const pt::ptree *root)
override;

bool readNode(Defect *def) override;

private:
struct Private;
std::unique_ptr<Private> d;
const pt::ptree* nextAlert();
};

#endif /* H_GUARD_PARSER_JSON_ZAP_H */
4 changes: 4 additions & 0 deletions src/lib/parser-json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "parser-json-sarif.hh"
#include "parser-json-shchk.hh"
#include "parser-json-simple.hh"
#include "parser-json-zap.hh"

#include <boost/property_tree/json_parser.hpp>

Expand Down Expand Up @@ -83,6 +84,9 @@ JsonParser::JsonParser(InStream &input):
else if (findChildOf(&node, d->root, "comments"))
// ShellCheck JSON format
d->decoder.reset(new ShellCheckTreeDecoder);
else if (findChildOf(&node, d->root, "site"))
// OWASP ZAP JSON format
d->decoder.reset(new ZapTreeDecoder);
else if (first.not_found() != first.find("kind"))
// GCC JSON format
d->decoder.reset(new GccTreeDecoder);
Expand Down
1 change: 1 addition & 0 deletions tests/csgrep/0103-json-parser-zap-args.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--mode=json
Loading