Skip to content

Commit 97ea941

Browse files
committed
json-parser-zap: support JSON format produced by OWASP ZAP
1 parent 4effa83 commit 97ea941

8 files changed

+531
-0
lines changed

src/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ add_library(cs STATIC
3636
parser-json-sarif.cc
3737
parser-json-shchk.cc
3838
parser-json-simple.cc
39+
parser-json-zap.cc
3940
parser-xml.cc
4041
parser-xml-valgrind.cc
4142
shared-string.cc

src/lib/parser-json-zap.cc

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (C) 2022 Red Hat, Inc.
3+
*
4+
* This file is part of csdiff.
5+
*
6+
* csdiff is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* any later version.
10+
*
11+
* csdiff 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
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with csdiff. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "parser-json-zap.hh"
21+
22+
struct ZapTreeDecoder::Private {
23+
std::string timeStamp;
24+
Defect defPrototype = Defect("OWASP_ZAP_WARNING");
25+
const pt::ptree *alertList = nullptr;
26+
pt::ptree::const_iterator alertIter;
27+
28+
Private()
29+
{
30+
this->defPrototype.tool = "owasp-zap";
31+
}
32+
33+
void readAlert(Defect *pDef, const pt::ptree &alertNode);
34+
};
35+
36+
template <typename TPropList>
37+
void readNonEmptyProps(
38+
TEvtList *pDst,
39+
const pt::ptree &node,
40+
const DefEvent &evtProto,
41+
const TPropList &propList)
42+
{
43+
// make our own copy of the given prototype event
44+
DefEvent evt = evtProto;
45+
46+
for (const auto &evtName : propList) {
47+
evt.event = evtName;
48+
evt.msg = valueOf<std::string>(node, evtName);
49+
if (!evt.msg.empty())
50+
pDst->push_back(evt);
51+
}
52+
}
53+
54+
void ZapTreeDecoder::Private::readAlert(Defect *pDef, const pt::ptree &alertNode)
55+
{
56+
// read per-defect properties
57+
*pDef = this->defPrototype;
58+
pDef->cwe = valueOf<int>(alertNode, "cweid");
59+
pDef->imp = (1 < valueOf<int>(alertNode, "riskcode"));
60+
61+
// get "uri" for the key event
62+
std::string uri;
63+
const pt::ptree *instList = nullptr;
64+
if (findChildOf(&instList, alertNode, "instances")) {
65+
for (const auto &item : *instList) {
66+
uri = valueOf<std::string>(item.second, "uri");
67+
if (!uri.empty())
68+
// found!
69+
break;
70+
}
71+
}
72+
73+
TEvtList &events = pDef->events;
74+
if (uri.empty() && !events.empty())
75+
// fallback to "uri" from the prototype event
76+
uri = events.front().fileName;
77+
78+
// initialize key event
79+
DefEvent evt("alert");
80+
evt.fileName = uri;
81+
82+
// read "alertRef" if available
83+
const auto alertRef = valueOf<std::string>(alertNode, "alertRef");
84+
if (!alertRef.empty())
85+
evt.event += "[" + alertRef + "]";
86+
87+
// read "alert" if available
88+
evt.msg = valueOf<std::string>(alertNode, "alert");
89+
90+
// append the key event
91+
pDef->keyEventIdx = events.size();
92+
events.push_back(evt);
93+
94+
// read other per-alert events if available
95+
evt.verbosityLevel = /* info event */ 1;
96+
const auto defProps = { "desc", "solution", "otherinfo", "reference" };
97+
readNonEmptyProps(&events, alertNode, evt, defProps);
98+
99+
if (!instList)
100+
// no instances to go through
101+
return;
102+
103+
// read per-instance properties
104+
const auto instProps = { "method", "param", "attack", "evidence" };
105+
for (const auto &item : *instList) {
106+
const pt::ptree &instNode = item.second;
107+
evt.fileName = valueOf<std::string>(instNode, "uri");
108+
if (evt.fileName.empty())
109+
// no "uri" for this instance
110+
continue;
111+
112+
readNonEmptyProps(&events, instNode, evt, instProps);
113+
}
114+
}
115+
116+
ZapTreeDecoder::ZapTreeDecoder():
117+
d(new Private)
118+
{
119+
}
120+
121+
ZapTreeDecoder::~ZapTreeDecoder() = default;
122+
123+
void ZapTreeDecoder::readScanProps(
124+
TScanProps *pDst,
125+
const pt::ptree *root)
126+
{
127+
const auto version = valueOf<std::string>(*root, "@version");
128+
if (!version.empty())
129+
(*pDst)["analyzer-version-owasp-zap"] = version;
130+
131+
d->timeStamp = valueOf<std::string>(*root, "@generated");
132+
}
133+
134+
bool ZapTreeDecoder::readNode(Defect *pDef)
135+
{
136+
// iterate over sites unless we are processing a site already
137+
while (!d->alertList || d->alertList->end() == d->alertIter) {
138+
const pt::ptree *siteNode = this->nextNode();
139+
if (!siteNode)
140+
// failed initialization or EOF
141+
return false;
142+
143+
if (!findChildOf(&d->alertList, *siteNode, "alerts")) {
144+
// "alerts" node missing for this site
145+
d->alertList = nullptr;
146+
continue;
147+
}
148+
149+
// initialize iteration over alerts
150+
d->alertIter = d->alertList->begin();
151+
152+
if (d->alertList->end() != d->alertIter) {
153+
// site with alerts found -> update defect prototype based on site
154+
d->defPrototype.events.clear();
155+
const auto siteName = valueOf<std::string>(*siteNode, "@name");
156+
if (!siteName.empty() && !d->timeStamp.empty()) {
157+
// create a prototype "note" event
158+
DefEvent siteEvt("note");
159+
siteEvt.fileName = std::move(siteName);
160+
siteEvt.msg = "dynamically analyzed on " + d->timeStamp;
161+
siteEvt.verbosityLevel = /* info event */ 1;
162+
d->defPrototype.events.push_back(std::move(siteEvt));
163+
}
164+
165+
break;
166+
}
167+
}
168+
169+
// get the current alert and move to the next one
170+
const auto itNow = d->alertIter++;
171+
172+
// process the current alert
173+
d->readAlert(pDef, itNow->second);
174+
175+
return true;
176+
}

src/lib/parser-json-zap.hh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) 2012-2022 Red Hat, Inc.
3+
*
4+
* This file is part of csdiff.
5+
*
6+
* csdiff is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* any later version.
10+
*
11+
* csdiff 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
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with csdiff. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef H_GUARD_PARSER_JSON_ZAP_H
21+
#define H_GUARD_PARSER_JSON_ZAP_H
22+
23+
#include "abstract-tree.hh"
24+
25+
/// tree decoder of the OWASP ZAP JSON format
26+
class ZapTreeDecoder: public AbstractTreeDecoder {
27+
public:
28+
ZapTreeDecoder();
29+
~ZapTreeDecoder() override;
30+
31+
void readScanProps(
32+
TScanProps *pDst,
33+
const pt::ptree *root)
34+
override;
35+
36+
bool readNode(Defect *def) override;
37+
38+
private:
39+
struct Private;
40+
std::unique_ptr<Private> d;
41+
};
42+
43+
#endif /* H_GUARD_PARSER_JSON_ZAP_H */

src/lib/parser-json.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "parser-json-sarif.hh"
2525
#include "parser-json-shchk.hh"
2626
#include "parser-json-simple.hh"
27+
#include "parser-json-zap.hh"
2728

2829
#include <boost/property_tree/json_parser.hpp>
2930

@@ -83,6 +84,9 @@ JsonParser::JsonParser(InStream &input):
8384
else if (findChildOf(&node, d->root, "comments"))
8485
// ShellCheck JSON format
8586
d->decoder.reset(new ShellCheckTreeDecoder);
87+
else if (findChildOf(&node, d->root, "site"))
88+
// OWASP ZAP JSON format
89+
d->decoder.reset(new ZapTreeDecoder);
8690
else if (first.not_found() != first.find("kind"))
8791
// GCC JSON format
8892
d->decoder.reset(new GccTreeDecoder);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--mode=json
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
{
2+
"@version": "2.11.1",
3+
"@generated": "Tue, 9 Aug 2022 14:38:31",
4+
"site":[
5+
{
6+
"@name": "http://rhos-fedora-devel.usersys.redhat.com:5000",
7+
"@host": "rhos-fedora-devel.usersys.redhat.com",
8+
"@port": "5000",
9+
"@ssl": "false",
10+
"alerts": [
11+
{
12+
"pluginid": "90022",
13+
"alertRef": "90022",
14+
"alert": "Application Error Disclosure",
15+
"name": "Application Error Disclosure",
16+
"riskcode": "1",
17+
"confidence": "2",
18+
"riskdesc": "Low (Medium)",
19+
"desc": "<p>This page contains an error/warning message that may disclose sensitive information like the location of the file that produced the unhandled exception. This information can be used to launch further attacks against the web application. The alert could be a false positive if the error message is found inside a documentation page.</p>",
20+
"instances":[
21+
{
22+
"uri": "http://rhos-fedora-devel.usersys.redhat.com:5000/pets/id/pet_id",
23+
"method": "GET",
24+
"param": "",
25+
"attack": "",
26+
"evidence": "HTTP/1.1 500 INTERNAL SERVER ERROR"
27+
}
28+
],
29+
"count": "1",
30+
"solution": "<p>Review the source code of this page. Implement custom error pages. Consider implementing a mechanism to provide a unique error reference/identifier to the client (browser) while logging the details on the server side and not exposing them to the user.</p>",
31+
"otherinfo": "",
32+
"reference": "",
33+
"cweid": "200",
34+
"wascid": "13",
35+
"sourceid": "7"
36+
},
37+
{
38+
"pluginid": "10023",
39+
"alertRef": "10023",
40+
"alert": "Information Disclosure - Debug Error Messages",
41+
"name": "Information Disclosure - Debug Error Messages",
42+
"riskcode": "1",
43+
"confidence": "2",
44+
"riskdesc": "Low (Medium)",
45+
"desc": "<p>The response appeared to contain common error messages returned by platforms such as ASP.NET, and Web-servers such as IIS and Apache. You can configure the list of common debug messages.</p>",
46+
"instances":[
47+
{
48+
"uri": "http://rhos-fedora-devel.usersys.redhat.com:5000/pets/id/pet_id",
49+
"method": "GET",
50+
"param": "",
51+
"attack": "",
52+
"evidence": "Internal Server Error"
53+
}
54+
],
55+
"count": "1",
56+
"solution": "<p>Disable debugging messages before pushing to production.</p>",
57+
"otherinfo": "",
58+
"reference": "",
59+
"cweid": "200",
60+
"wascid": "13",
61+
"sourceid": "7"
62+
},
63+
{
64+
"pluginid": "10021",
65+
"alertRef": "10021",
66+
"alert": "X-Content-Type-Options Header Missing",
67+
"name": "X-Content-Type-Options Header Missing",
68+
"riskcode": "1",
69+
"confidence": "2",
70+
"riskdesc": "Low (Medium)",
71+
"desc": "<p>The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.</p>",
72+
"instances":[
73+
{
74+
"uri": "http://rhos-fedora-devel.usersys.redhat.com:5000/docs/openapi.json",
75+
"method": "GET",
76+
"param": "X-Content-Type-Options",
77+
"attack": "",
78+
"evidence": ""
79+
},
80+
{
81+
"uri": "http://rhos-fedora-devel.usersys.redhat.com:5000/pets/",
82+
"method": "GET",
83+
"param": "X-Content-Type-Options",
84+
"attack": "",
85+
"evidence": ""
86+
},
87+
{
88+
"uri": "http://rhos-fedora-devel.usersys.redhat.com:5000/pets/name/pet_name",
89+
"method": "GET",
90+
"param": "X-Content-Type-Options",
91+
"attack": "",
92+
"evidence": ""
93+
}
94+
],
95+
"count": "3",
96+
"solution": "<p>Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages.</p><p>If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.</p>",
97+
"otherinfo": "<p>This issue still applies to error type pages (401, 403, 500, etc.) as those pages are often still affected by injection issues, in which case there is still concern for browsers sniffing pages away from their actual content type.</p><p>At \"High\" threshold this scan rule will not alert on client or server error responses.</p>",
98+
"reference": "<p>http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx</p><p>https://owasp.org/www-community/Security_Headers</p>",
99+
"cweid": "693",
100+
"wascid": "15",
101+
"sourceid": "1"
102+
}
103+
]
104+
}
105+
]
106+
}

0 commit comments

Comments
 (0)