Skip to content

Commit 9faa825

Browse files
committed
C++: Add support for libxml2 in the query.
1 parent 812a24f commit 9faa825

File tree

3 files changed

+75
-5
lines changed

3 files changed

+75
-5
lines changed

cpp/ql/src/Security/CWE/CWE-611/XXE.ql

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,42 @@ class CreateLSParser extends Function {
188188
}
189189
}
190190

191+
/**
192+
* A call to a `libxml2` function that parses XML.
193+
*/
194+
class Libxml2ParseCall extends FunctionCall {
195+
int optionsArg;
196+
197+
Libxml2ParseCall() {
198+
exists(string fname | this.getTarget().getName() = fname |
199+
fname = ["xmlCtxtUseOptions"] and optionsArg = 1
200+
or
201+
fname = ["xmlReadFile"] and optionsArg = 2
202+
or
203+
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
204+
optionsArg = 3
205+
or
206+
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
207+
or
208+
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
209+
or
210+
fname = ["xmlCtxtReadIO"] and optionsArg = 6
211+
)
212+
}
213+
214+
/**
215+
* Gets the argument that specifies `xmlParserOption`s.
216+
*/
217+
Expr getOptions() { result = this.getArgument(optionsArg) }
218+
}
219+
220+
/**
221+
* An `xmlParserOption` for `libxml2` that is considered unsafe.
222+
*/
223+
class Libxml2BadOption extends EnumConstant {
224+
Libxml2BadOption() { this.getName().matches(["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"]) }
225+
}
226+
191227
/**
192228
* A configuration for tracking XML objects and their states.
193229
*/
@@ -219,6 +255,23 @@ class XXEConfiguration extends DataFlow::Configuration {
219255
call.getThisArgument() and
220256
encodeXercesFlowState(flowstate, 0, 1) // default configuration
221257
)
258+
or
259+
// source is an `options` argument on a `libxml2` parse call that specifies
260+
// at least one unsafe option.
261+
//
262+
// note: we don't need to track an XML object for `libxml2`, so we don't
263+
// really need data flow. Nevertheless we jam it into this configuration,
264+
// with matching sources and sinks. This allows results to be presented by
265+
// the same query, in a consistent way as other results with flow paths.
266+
exists(Libxml2ParseCall call, Expr options |
267+
options = call.getOptions() and
268+
node.asExpr() = options and
269+
flowstate = "libxml2" and
270+
exists(Libxml2BadOption opt |
271+
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
272+
0
273+
)
274+
)
222275
}
223276

224277
override predicate isSink(DataFlow::Node node, string flowstate) {
@@ -229,6 +282,13 @@ class XXEConfiguration extends DataFlow::Configuration {
229282
) and
230283
flowstate instanceof XercesFlowState and
231284
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
285+
or
286+
// sink is the `options` argument on a `libxml2` parse call.
287+
exists(Libxml2ParseCall call, Expr options |
288+
options = call.getOptions() and
289+
node.asExpr() = options and
290+
flowstate = "libxml2"
291+
)
232292
}
233293

234294
override predicate isAdditionalFlowStep(

cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ nodes
3333
| tests2.cpp:22:2:22:2 | p | semmle.label | p |
3434
| tests2.cpp:33:17:33:31 | SAXParser output argument | semmle.label | SAXParser output argument |
3535
| tests2.cpp:37:2:37:2 | p | semmle.label | p |
36+
| tests4.cpp:26:34:26:48 | (int)... | semmle.label | (int)... |
37+
| tests4.cpp:36:34:36:50 | (int)... | semmle.label | (int)... |
38+
| tests4.cpp:46:34:46:68 | ... \| ... | semmle.label | ... \| ... |
39+
| tests4.cpp:77:34:77:38 | flags | semmle.label | flags |
40+
| tests4.cpp:130:39:130:55 | (int)... | semmle.label | (int)... |
3641
| tests.cpp:33:23:33:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
3742
| tests.cpp:35:2:35:2 | p | semmle.label | p |
3843
| tests.cpp:46:23:46:43 | XercesDOMParser output argument | semmle.label | XercesDOMParser output argument |
@@ -74,6 +79,11 @@ subpaths
7479
#select
7580
| tests2.cpp:22:2:22:2 | p | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:20:17:20:31 | SAXParser output argument | XML parser |
7681
| tests2.cpp:37:2:37:2 | p | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:33:17:33:31 | SAXParser output argument | XML parser |
82+
| tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:26:34:26:48 | (int)... | XML parser |
83+
| tests4.cpp:36:34:36:50 | (int)... | tests4.cpp:36:34:36:50 | (int)... | tests4.cpp:36:34:36:50 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:36:34:36:50 | (int)... | XML parser |
84+
| tests4.cpp:46:34:46:68 | ... \| ... | tests4.cpp:46:34:46:68 | ... \| ... | tests4.cpp:46:34:46:68 | ... \| ... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:46:34:46:68 | ... \| ... | XML parser |
85+
| tests4.cpp:77:34:77:38 | flags | tests4.cpp:77:34:77:38 | flags | tests4.cpp:77:34:77:38 | flags | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:77:34:77:38 | flags | XML parser |
86+
| tests4.cpp:130:39:130:55 | (int)... | tests4.cpp:130:39:130:55 | (int)... | tests4.cpp:130:39:130:55 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:130:39:130:55 | (int)... | XML parser |
7787
| tests.cpp:35:2:35:2 | p | tests.cpp:33:23:33:43 | XercesDOMParser output argument | tests.cpp:35:2:35:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:33:23:33:43 | XercesDOMParser output argument | XML parser |
7888
| tests.cpp:49:2:49:2 | p | tests.cpp:46:23:46:43 | XercesDOMParser output argument | tests.cpp:49:2:49:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:46:23:46:43 | XercesDOMParser output argument | XML parser |
7989
| tests.cpp:57:2:57:2 | p | tests.cpp:53:23:53:43 | XercesDOMParser output argument | tests.cpp:57:2:57:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:53:23:53:43 | XercesDOMParser output argument | XML parser |

cpp/ql/test/query-tests/Security/CWE/CWE-611/tests4.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ void xmlFreeDoc(xmlDoc *ptr);
2323
void test4_1(const char *fileName) {
2424
xmlDoc *p;
2525

26-
p = xmlReadFile(fileName, NULL, XML_PARSE_NOENT); // BAD (parser not correctly configured) [NOT DETECTED]
26+
p = xmlReadFile(fileName, NULL, XML_PARSE_NOENT); // BAD (parser not correctly configured)
2727
if (p != NULL)
2828
{
2929
xmlFreeDoc(p);
@@ -33,7 +33,7 @@ void test4_1(const char *fileName) {
3333
void test4_2(const char *fileName) {
3434
xmlDoc *p;
3535

36-
p = xmlReadFile(fileName, NULL, XML_PARSE_DTDLOAD); // BAD (parser not correctly configured) [NOT DETECTED]
36+
p = xmlReadFile(fileName, NULL, XML_PARSE_DTDLOAD); // BAD (parser not correctly configured)
3737
if (p != NULL)
3838
{
3939
xmlFreeDoc(p);
@@ -43,7 +43,7 @@ void test4_2(const char *fileName) {
4343
void test4_3(const char *fileName) {
4444
xmlDoc *p;
4545

46-
p = xmlReadFile(fileName, NULL, XML_PARSE_NOENT | XML_PARSE_DTDLOAD); // BAD (parser not correctly configured) [NOT DETECTED]
46+
p = xmlReadFile(fileName, NULL, XML_PARSE_NOENT | XML_PARSE_DTDLOAD); // BAD (parser not correctly configured)
4747
if (p != NULL)
4848
{
4949
xmlFreeDoc(p);
@@ -74,7 +74,7 @@ void test4_6(const char *fileName) {
7474
xmlDoc *p;
7575
int flags = XML_PARSE_NOENT;
7676

77-
p = xmlReadFile(fileName, NULL, flags); // BAD (parser not correctly configured) [NOT DETECTED]
77+
p = xmlReadFile(fileName, NULL, flags); // BAD (parser not correctly configured)
7878
if (p != NULL)
7979
{
8080
xmlFreeDoc(p);
@@ -127,7 +127,7 @@ void test4_10(const char *ptr, int sz) {
127127
void test4_11(const char *ptr, int sz) {
128128
xmlDoc *p;
129129

130-
p = xmlReadMemory(ptr, sz, "", NULL, XML_PARSE_DTDLOAD); // BAD (parser not correctly configured) [NOT DETECTED]
130+
p = xmlReadMemory(ptr, sz, "", NULL, XML_PARSE_DTDLOAD); // BAD (parser not correctly configured)
131131
if (p != NULL)
132132
{
133133
xmlFreeDoc(p);

0 commit comments

Comments
 (0)