Skip to content

Commit 8544cff

Browse files
authored
Merge pull request github#7836 from geoffw0/clrtxt9
C++: Fix more FPs in cpp/cleartext-transmission
2 parents c8bc5cf + 8031c3f commit 8544cff

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @kind path-problem
66
* @problem.severity warning
77
* @security-severity 7.5
8-
* @precision medium
8+
* @precision high
99
* @id cpp/cleartext-transmission
1010
* @tags security
1111
* external/cwe/cwe-319
@@ -14,8 +14,8 @@
1414
import cpp
1515
import semmle.code.cpp.security.SensitiveExprs
1616
import semmle.code.cpp.dataflow.TaintTracking
17-
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
1817
import semmle.code.cpp.models.interfaces.FlowSource
18+
import semmle.code.cpp.commons.File
1919
import DataFlow::PathGraph
2020

2121
/**
@@ -121,24 +121,32 @@ abstract class NetworkSendRecv extends FunctionCall {
121121
NetworkSendRecv() {
122122
this.getTarget() = target and
123123
// exclude calls based on the socket...
124-
not exists(GVN g |
125-
g = globalValueNumber(target.getSocketExpr(this)) and
124+
not exists(DataFlow::Node src, DataFlow::Node dest |
125+
DataFlow::localFlow(src, dest) and
126+
dest.asExpr() = target.getSocketExpr(this) and
126127
(
127128
// literal constant
128-
globalValueNumber(any(Literal l)) = g
129+
src.asExpr() instanceof Literal
129130
or
130131
// variable (such as a global) initialized to a literal constant
131132
exists(Variable v |
132133
v.getInitializer().getExpr() instanceof Literal and
133-
g = globalValueNumber(v.getAnAccess())
134+
src.asExpr() = v.getAnAccess()
134135
)
135136
or
136137
// result of a function call with literal inputs (likely constant)
138+
forex(Expr arg | arg = src.asExpr().(FunctionCall).getAnArgument() | arg instanceof Literal)
139+
or
140+
// variable called `stdin`, `stdout` or `stderr`
141+
src.asExpr().(VariableAccess).getTarget().getName() = ["stdin", "stdout", "stderr"]
142+
or
143+
// open of `"/dev/tty"`
137144
exists(FunctionCall fc |
138-
forex(Expr arg | arg = fc.getAnArgument() | arg instanceof Literal) and
139-
g = globalValueNumber(fc)
145+
fopenCall(fc) and
146+
fc.getAnArgument().getValue() = "/dev/tty" and
147+
src.asExpr() = fc
140148
)
141-
// (this is far from exhaustive)
149+
// (this is not exhaustive)
142150
)
143151
)
144152
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been further improved to reduce false positive results, and upgraded from `medium` to `high` precision.

cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ edges
9191
| test3.cpp:398:18:398:25 | password | test3.cpp:400:16:400:23 | password |
9292
| test3.cpp:398:18:398:25 | password | test3.cpp:400:33:400:40 | password |
9393
| test3.cpp:421:21:421:28 | password | test3.cpp:421:3:421:17 | call to decrypt_inplace |
94+
| test3.cpp:429:7:429:14 | password | test3.cpp:431:8:431:15 | password |
9495
| test.cpp:41:23:41:43 | cleartext password! | test.cpp:48:21:48:27 | call to encrypt |
9596
| test.cpp:41:23:41:43 | cleartext password! | test.cpp:48:29:48:39 | thePassword |
9697
| test.cpp:66:23:66:43 | cleartext password! | test.cpp:76:21:76:27 | call to encrypt |
@@ -218,6 +219,8 @@ nodes
218219
| test3.cpp:421:3:421:17 | call to decrypt_inplace | semmle.label | call to decrypt_inplace |
219220
| test3.cpp:421:21:421:28 | password | semmle.label | password |
220221
| test3.cpp:421:21:421:28 | password | semmle.label | password |
222+
| test3.cpp:429:7:429:14 | password | semmle.label | password |
223+
| test3.cpp:431:8:431:15 | password | semmle.label | password |
221224
| test.cpp:41:23:41:43 | cleartext password! | semmle.label | cleartext password! |
222225
| test.cpp:48:21:48:27 | call to encrypt | semmle.label | call to encrypt |
223226
| test.cpp:48:29:48:39 | thePassword | semmle.label | thePassword |
@@ -250,3 +253,4 @@ subpaths
250253
| test3.cpp:388:3:388:6 | call to recv | test3.cpp:386:8:386:15 | password | test3.cpp:388:15:388:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:386:8:386:15 | password | password |
251254
| test3.cpp:414:3:414:6 | call to recv | test3.cpp:414:17:414:24 | password | test3.cpp:414:17:414:24 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:414:17:414:24 | password | password |
252255
| test3.cpp:420:3:420:6 | call to recv | test3.cpp:420:17:420:24 | password | test3.cpp:420:17:420:24 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:420:17:420:24 | password | password |
256+
| test3.cpp:431:2:431:6 | call to fgets | test3.cpp:429:7:429:14 | password | test3.cpp:431:8:431:15 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:429:7:429:14 | password | password |

cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,56 @@ void test_member_password()
421421
decrypt_inplace(p.password); // proof that `password` was in fact encrypted
422422
}
423423
}
424+
425+
extern FILE *stdin;
426+
427+
void test_stdin_param(FILE *stream)
428+
{
429+
char password[128];
430+
431+
fgets(password, 128, stream); // GOOD: from standard input (see call below) [FALSE POSITIVE]
432+
}
433+
434+
void test_stdin()
435+
{
436+
char password[128];
437+
FILE *f = stdin;
438+
439+
fgets(password, 128, stdin); // GOOD: from standard input
440+
fgets(password, 128, f); // GOOD: from standard input
441+
test_stdin_param(stdin);
442+
}
443+
444+
int open(const char *filename, int b);
445+
446+
void test_tty()
447+
{
448+
{
449+
char password[256];
450+
int f;
451+
452+
f = open("/dev/tty", val());
453+
recv(f, password, 256, val()); // GOOD: from terminal
454+
}
455+
456+
{
457+
char password[256];
458+
int f;
459+
460+
f = STDIN_FILENO;
461+
recv(f, password, 256, val()); // GOOD: from stdin
462+
}
463+
464+
{
465+
char password[256];
466+
int f;
467+
468+
f = open("/dev/tty", val());
469+
if (f == -1)
470+
{
471+
f = STDIN_FILENO;
472+
}
473+
474+
recv(f, password, 256, val()); // GOOD: from terminal or stdin
475+
}
476+
}

0 commit comments

Comments
 (0)